Repository: NREL/floris Branch: main Commit: a0775339e325 Files: 284 Total size: 4.1 MB Directory structure: gitextract_8emkfpo1/ ├── .codecov.yml ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── feature_request.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── check-working-examples.yaml │ ├── continuous-integration-workflow.yaml │ ├── deploy-pages.yaml │ ├── python-publish.yml │ └── quality-metrics-workflow.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── benchmarks/ │ └── bench.py ├── docs/ │ ├── .nojekyll │ ├── _config.yml │ ├── _toc.yml │ ├── advanced_concepts.ipynb │ ├── api_docs.md │ ├── architecture.md │ ├── bibliography.md │ ├── code_quality.ipynb │ ├── dev_guide.md │ ├── empirical_gauss_model.md │ ├── floating_wind_turbine.md │ ├── floris_models.ipynb │ ├── heterogeneous_map.ipynb │ ├── index.md │ ├── input_reference_main.md │ ├── input_reference_turbine.md │ ├── installation.md │ ├── intro_concepts.ipynb │ ├── layout_optimization.md │ ├── multidimensional_wind_turbine.ipynb │ ├── operation_models_user.ipynb │ ├── references.bib │ ├── turbine_library.md │ ├── turbine_models.ipynb │ ├── v3_to_v4.md │ ├── wake_models.ipynb │ └── wind_data_user.ipynb ├── examples/ │ ├── 001_opening_floris_computing_power.py │ ├── 002_visualizations.py │ ├── 003_wind_data_objects.py │ ├── 004_set.py │ ├── 005_getting_power.py │ ├── 006_get_farm_aep.py │ ├── 007_sweeping_variables.py │ ├── 008_uncertain_models.py │ ├── 009_parallel_models.py │ ├── 010_compare_farm_power_with_neighbor.py │ ├── _convert_examples_to_notebooks.py │ ├── examples_control_optimization/ │ │ ├── 001_opt_yaw_single_ws.py │ │ ├── 002_opt_yaw_single_ws_uncertain.py │ │ ├── 003_opt_yaw_multiple_ws.py │ │ ├── 004_optimize_yaw_aep.py │ │ ├── 005_optimize_yaw_aep_parallel.py │ │ ├── 006_compare_yaw_optimizers.py │ │ ├── 007_optimize_yaw_with_neighbor_farms.py │ │ └── 008_optimize_yaw_with_disabled_turbines.py │ ├── examples_control_types/ │ │ ├── 001_derating_control.py │ │ ├── 002_disable_turbines.py │ │ ├── 003_setting_yaw_and_disabling.py │ │ ├── 004_helix_active_wake_mixing.py │ │ └── 005_peak_shaving.py │ ├── examples_emgauss/ │ │ ├── 001_empirical_gauss_velocity_deficit_parameters.py │ │ └── 002_empirical_gauss_deflection_parameters.py │ ├── examples_floating/ │ │ ├── 001_floating_turbine_models.py │ │ ├── 002_floating_vs_fixedbottom_farm.py │ │ └── 003_tilt_driven_vertical_wake_deflection.py │ ├── examples_get_flow/ │ │ ├── 001_extract_wind_speed_at_turbines.py │ │ ├── 002_extract_wind_speed_at_points.py │ │ ├── 003_extract_turbulence_intensity_at_points.py │ │ └── 004_plot_velocity_deficit_profiles.py │ ├── examples_heterogeneous/ │ │ ├── 001_heterogeneous_inflow_single.py │ │ ├── 002_heterogeneous_using_wind_data.py │ │ ├── 003_heterogeneous_speedup_by_wd_and_ws.py │ │ └── 004_heterogeneous_2d_and_3d.py │ ├── examples_layout_optimization/ │ │ ├── 001_optimize_layout.py │ │ ├── 002_optimize_layout_with_heterogeneity.py │ │ ├── 003_genetic_random_search.py │ │ ├── 004_generate_gridded_layout.py │ │ └── 005_layout_optimization_complex_boundary.py │ ├── examples_load_optimization/ │ │ ├── 001_lti_and_voc.py │ │ └── 002_row_opt_example.py │ ├── examples_multidim/ │ │ ├── 001_multi_dimensional_cp_ct.py │ │ ├── 002_multi_dimensional_cp_ct_2Hs.py │ │ └── 003_multi_dimensional_cp_ct_TI.py │ ├── examples_operation_models/ │ │ └── 001_compare_yaw_loss.py │ ├── examples_turbine/ │ │ ├── 001_reference_turbines.py │ │ ├── 002_multiple_turbine_types.py │ │ └── 003_specify_turbine_power_curve.py │ ├── examples_turbopark/ │ │ ├── 001_compare_turbopark_implementations.py │ │ └── comparison_data/ │ │ ├── Rowpark_Orsted.csv │ │ └── WindDirection_Sweep_Orsted.csv │ ├── examples_uncertain/ │ │ ├── 001_uncertain_model_params.py │ │ ├── 002_approx_floris_model.py │ │ └── 003_uncertain_model_with_parallelization.py │ ├── examples_visualizations/ │ │ ├── 001_layout_visualizations.py │ │ ├── 002_visualize_y_cut_plane.py │ │ ├── 003_visualize_cross_plane.py │ │ ├── 004_visualize_rotor_values.py │ │ └── 005_visualize_flow_by_sweeping_turbines.py │ ├── examples_wind_data/ │ │ ├── 001_wind_data_comparisons.py │ │ ├── 002_generate_ti.py │ │ └── 003_generate_value.py │ ├── examples_wind_resource_grid/ │ │ ├── 000_generate_example_wrg.py │ │ ├── 001_wind_rose_wrg.py │ │ ├── 002_set_floris_with_wrg.py │ │ ├── 003_wrg_compar_layout_optimization.py │ │ └── wrg_example.wrg │ ├── inputs/ │ │ ├── cc.yaml │ │ ├── emgauss.yaml │ │ ├── emgauss_helix.yaml │ │ ├── gch.yaml │ │ ├── gch_heterogeneous_inflow.yaml │ │ ├── gch_multi_dim_cp_ct.yaml │ │ ├── gch_multi_dim_cp_ct_TI.yaml │ │ ├── gch_multiple_turbine_types.yaml │ │ ├── jensen.yaml │ │ ├── turbine_files/ │ │ │ ├── iea_15MW_multi_dim_TI.csv │ │ │ └── iea_15MW_multi_dim_TI.yaml │ │ ├── turbopark.yaml │ │ ├── turbopark_cubature.yaml │ │ ├── turboparkgauss.yaml │ │ ├── turboparkgauss_cubature.yaml │ │ └── wind_rose.csv │ └── inputs_floating/ │ ├── emgauss_fixed.yaml │ ├── emgauss_floating.yaml │ ├── emgauss_floating_fixedtilt15.yaml │ ├── emgauss_floating_fixedtilt5.yaml │ ├── gch_fixed.yaml │ ├── gch_floating.yaml │ ├── gch_floating_defined_floating.yaml │ └── turbine_files/ │ ├── nrel_5MW_fixed.yaml │ ├── nrel_5MW_floating.yaml │ ├── nrel_5MW_floating_defined_floating.yaml │ ├── nrel_5MW_floating_fixedtilt15.yaml │ └── nrel_5MW_floating_fixedtilt5.yaml ├── floris/ │ ├── __init__.py │ ├── convert_floris_input_v3_to_v4.py │ ├── convert_turbine_v3_to_v4.py │ ├── core/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── core.py │ │ ├── farm.py │ │ ├── flow_field.py │ │ ├── grid.py │ │ ├── rotor_velocity.py │ │ ├── solver.py │ │ ├── turbine/ │ │ │ ├── __init__.py │ │ │ ├── controller_dependent_operation_model.py │ │ │ ├── operation_models.py │ │ │ ├── turbine.py │ │ │ └── unified_momentum_model.py │ │ ├── wake.py │ │ ├── wake_combination/ │ │ │ ├── __init__.py │ │ │ ├── fls.py │ │ │ ├── max.py │ │ │ └── sosfs.py │ │ ├── wake_deflection/ │ │ │ ├── __init__.py │ │ │ ├── empirical_gauss.py │ │ │ ├── gauss.py │ │ │ ├── jimenez.py │ │ │ └── none.py │ │ ├── wake_turbulence/ │ │ │ ├── __init__.py │ │ │ ├── crespo_hernandez.py │ │ │ ├── none.py │ │ │ └── wake_induced_mixing.py │ │ └── wake_velocity/ │ │ ├── __init__.py │ │ ├── cumulative_gauss_curl.py │ │ ├── empirical_gauss.py │ │ ├── gauss.py │ │ ├── jensen.py │ │ ├── none.py │ │ ├── turbopark.py │ │ ├── turbopark_lookup_table.mat │ │ └── turboparkgauss.py │ ├── cut_plane.py │ ├── default_inputs.yaml │ ├── floris_model.py │ ├── flow_visualization.py │ ├── heterogeneous_map.py │ ├── layout_visualization.py │ ├── logging_manager.py │ ├── optimization/ │ │ ├── __init__.py │ │ ├── layout_optimization/ │ │ │ ├── __init__.py │ │ │ ├── layout_optimization_base.py │ │ │ ├── layout_optimization_boundary_grid.py │ │ │ ├── layout_optimization_gridded.py │ │ │ ├── layout_optimization_pyoptsparse.py │ │ │ ├── layout_optimization_pyoptsparse_spread.py │ │ │ ├── layout_optimization_random_search.py │ │ │ └── layout_optimization_scipy.py │ │ ├── load_optimization/ │ │ │ ├── __init__.py │ │ │ └── load_optimization.py │ │ ├── other/ │ │ │ ├── __init__.py │ │ │ └── boundary_grid.py │ │ └── yaw_optimization/ │ │ ├── __init__.py │ │ ├── yaw_optimization_base.py │ │ ├── yaw_optimization_tools.py │ │ ├── yaw_optimizer_geometric.py │ │ ├── yaw_optimizer_scipy.py │ │ └── yaw_optimizer_sr.py │ ├── par_floris_model.py │ ├── parallel_floris_model.py │ ├── turbine_library/ │ │ ├── __init__.py │ │ ├── demo_cp_ct_surfaces/ │ │ │ ├── iea_10MW_demo_cp_ct_surface.npz │ │ │ ├── iea_15MW_demo_cp_ct_surface.npz │ │ │ └── nrel_5MW_demo_cp_ct_surface.npz │ │ ├── iea_10MW.yaml │ │ ├── iea_15MW.yaml │ │ ├── iea_15MW_floating_multi_dim_cp_ct.yaml │ │ ├── iea_15MW_multi_dim_TI_u.csv │ │ ├── iea_15MW_multi_dim_Tp_Hs.csv │ │ ├── iea_15MW_multi_dim_cp_ct.yaml │ │ ├── iea_22MW.yaml │ │ ├── nrel_5MW.yaml │ │ ├── turbine_previewer.py │ │ └── turbine_utilities.py │ ├── type_dec.py │ ├── uncertain_floris_model.py │ ├── utilities.py │ └── wind_data.py ├── profiling/ │ ├── linux_perf.py │ ├── profiling.py │ ├── quality_metrics.py │ ├── serial_vectorize.py │ └── timing.py ├── pyproject.toml └── tests/ ├── __init__.py ├── base_unit_test.py ├── conftest.py ├── controller_dependent_operation_model_unit_test.py ├── convert_v3_to_v4_test.py ├── core_unit_test.py ├── data/ │ ├── __init__.py │ ├── input_full.yaml │ ├── nrel_5MW_custom.yaml │ ├── wind_rose.csv │ ├── wind_ti_rose.csv │ └── wrg_test.wrg ├── farm_unit_test.py ├── floris_model_integration_test.py ├── flow_field_unit_test.py ├── geometric_yaw_unit_test.py ├── heterogeneous_map_integration_test.py ├── layout_optimization_integration_test.py ├── layout_visualization_test.py ├── load_optimization_test.py ├── par_floris_model_unit_test.py ├── parallel_floris_model_integration_test.py ├── reg_tests/ │ ├── cumulative_curl_regression_test.py │ ├── empirical_gauss_regression_test.py │ ├── gauss_regression_test.py │ ├── jensen_jimenez_regression_test.py │ ├── none_regression_test.py │ ├── random_search_layout_opt_regression_test.py │ ├── scipy_layout_opt_regression.py │ ├── turbopark_regression_test.py │ ├── turboparkgauss_regression_test.py │ ├── turbulence_models_regression_test.py │ └── yaw_optimization_regression_test.py ├── rotor_velocity_unit_test.py ├── serial_refine_unit_test.py ├── turbine_grid_unit_test.py ├── turbine_multi_dim_unit_test.py ├── turbine_operation_models_unit_test.py ├── turbine_unit_test.py ├── turbine_utilities_unit_test.py ├── turboparkgauss_unit_test.py ├── type_dec_unit_test.py ├── uncertain_floris_model_integration_test.py ├── unified_momentum_operation_model_unit_test.py ├── utilities_unit_test.py ├── v3_to_v4_convert_test/ │ ├── gch.yaml │ └── nrel_5MW_v3.yaml ├── wake_unit_tests.py ├── wind_data_integration_test.py ├── wind_rose_wrg_test.py └── yaw_optimization_integration_test.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .codecov.yml ================================================ # Codecov configuration file comment: false coverage: range: 0..100 # sets the range for the color bar in the dashboard round: down precision: 2 status: project: default: # allow coverage to drop by this amount and still post success threshold: 10 patch: default: threshold: 10 ignore: - "setup.py" - "tests/" ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Report a bug to help us improve title: 'Bug report' labels: "Type: Bug" --- # Add meaningful title here ## How to reproduce ## Relevant output ## Floris version ## System Information - OS: - Python version: - Library versions - Results of `pip freeze`, for example - matplotlib - numpy - numexpr - scipy - pandas ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Usage question url: https://github.com/NatLabRockies/floris/discussions about: Have any questions about using FLORIS? Post in Discussions to engage with the NLR team and FLORIS community. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: 'Feature request' labels: 'Type: Enhancement' --- # Add meaningful title here ## Proposed solution ## Alternatives considered ## Additional context ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ # Add meaningful title here Describe your feature here. ## Related issue ## Impacted areas of the software ## Additional supporting information ## Test results, if applicable ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "pip" directory: "/" # Location of package manifests target-branch: "develop" schedule: interval: "monthly" labels: - "package" ignore: - dependency-name: "*" update-types: ["version-update:semver-minor", "version-update:semver-patch"] ================================================ FILE: .github/workflows/check-working-examples.yaml ================================================ name: Check Examples APIs on: [push, pull_request] jobs: examples-check: runs-on: ${{ matrix.os }} strategy: matrix: python-version: ["3.10", "3.14"] os: [ubuntu-latest] fail-fast: False steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install project run: | python -m pip install --upgrade pip pip install . pip install nbconvert # For converting Jupyter notebook to python script in the next step - name: Run examples (ubuntu, macos) # Run all examples and test that they finish successfully. Do not evaluate the results. # Copy the examples to a new directory outside of the repo to ensure that there is no # reliance on the repo directory structure. run: | mkdir -p temp1/temp2/temp3 cp -rL examples temp1/temp2/temp3/. cd temp1/temp2/temp3/examples/ error_found=0 # 0 is false error_results="Error in example:" # Now run the examples in root and subdirectories echo "Running examples" for d in . $(find . -type d -name "*examples*"); do cd $d echo "========================= Example directory- $d" for i in *.py; do echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Running example- $i" # If "convert_examples" is in i, skip this script if [[ $i == *"convert_examples"* ]]; then continue fi if ! python $i; then error_results="${error_results}"$'\n'" - ${i}" error_found=1 fi done if [ "$d" != "." ]; then cd .. fi done if [[ $error_found ]]; then echo "${error_results}" fi exit $error_found ================================================ FILE: .github/workflows/continuous-integration-workflow.yaml ================================================ name: Automated tests & code coverage on: [push, pull_request] jobs: code-qa-validation: runs-on: ${{ matrix.os }} strategy: matrix: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: False env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install project run: | python -m pip install --upgrade pip pip install ".[develop]" - uses: pre-commit/action@v3.0.0 - name: Run tests (ubuntu, macos) if: matrix.os != 'windows-latest' && (success() || failure()) # Run this step even if the linting step fails run: | mkdir -p temp1/temp2/temp3 cp -rL tests temp1/temp2/temp3/. cd temp1/temp2/temp3/ # -rA displays the captured output for all tests after they're run # See the docs: https://doc.pytest.org/en/latest/reference/reference.html#command-line-flags pytest -rA tests/ --ignore tests/timing.py --ignore tests/profiling.py - name: Run tests (windows) if: matrix.os == 'windows-latest' && (success() || failure()) # Run this step even if the linting step fails run: | mkdir -p temp1/temp2/temp3 cp -r tests temp1/temp2/temp3/. cd temp1/temp2/temp3/ # -rA displays the captured output for all tests after they're run # See the docs: https://doc.pytest.org/en/latest/reference/reference.html#command-line-flags pytest -rA tests/ --ignore tests/timing.py --ignore tests/profiling.py - name: Generate coverage report # Run these tests on unit tests only so that we avoid inflating our code # coverage through the regression tests if: matrix.os == 'ubuntu-latest' run: | pip install pytest pip install pytest-cov pytest --cov=./ --cov-report=xml tests/ --ignore tests/reg_tests --ignore tests/timing.py --ignore tests/profiling.py - name: Upload coverage to Codecov if: ${{ matrix.os == 'ubuntu-latest' && env.CODECOV_TOKEN }} # Don't attempt to upload if the codecov token is not configured uses: codecov/codecov-action@v3 with: token: ${{ env.CODECOV_TOKEN }} files: ./coverage.xml fail_ci_if_error: true ================================================ FILE: .github/workflows/deploy-pages.yaml ================================================ name: deploy-pages on: push: branches: - develop workflow_dispatch: # Allows manual triggering of the workflow # This job installs dependencies, builds the book, and pushes it to `gh-pages` jobs: deploy-book: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 # Install dependencies - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3" - name: Install dependencies run: | pip install -e ".[docs, develop]" # Make a copy of the examples folder within the docs folder - name: Copy examples to docs working-directory: ${{runner.workspace}}/floris/ run: | rsync -av examples/ docs/examples ls docs/examples # Run the script examples/_convert_examples_to_notebooks.py - name: Convert examples to notebooks working-directory: ${{runner.workspace}}/floris/docs/examples/ run: | pwd ls python _convert_examples_to_notebooks.py # Build the book - name: Build the book working-directory: ${{runner.workspace}}/floris/docs/ run: | jupyter-book build . # Push the book's HTML to github-pages - name: GitHub Pages action uses: peaceiris/actions-gh-pages@v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./docs/_build/html # Stash changes before benchmark action - name: Stash changes working-directory: ${{runner.workspace}}/floris/ run: | git stash - name: Run benchmark working-directory: ${{runner.workspace}}/floris/ run: | ls -lah cd benchmarks pytest bench.py --benchmark-json output.json # Store benchmark result and create the benchmark pages # Update the index.html and data.js files in the # dev/bench folder of the benches branch # dev/bench is the default folder for pytest-benchmark - name: Store benchmark result uses: benchmark-action/github-action-benchmark@v1 with: name: Python Benchmark with pytest-benchmark tool: 'pytest' output-file-path: benchmarks/output.json github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true gh-pages-branch: benches # Add bench mark files to the gh-pages branch - name: Add benchmark files to gh-pages working-directory: ${{runner.workspace}}/floris/ run: | ls -lah git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" git fetch origin benches git checkout benches rsync -av dev/bench /tmp/bench git fetch origin gh-pages git checkout gh-pages mkdir -p dev rsync -av /tmp/bench/ dev/ ls -lah dev/bench git add dev git commit -m "Add bench folder to gh-pages" git push origin gh-pages ================================================ FILE: .github/workflows/python-publish.yml ================================================ # This workflows will upload a Python Package using Twine when a release is created # Published via GitHub Actions as a PyPI Trusted Publisher. # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries # and: https://docs.pypi.org/trusted-publishers/ name: Upload Python Package on: release: types: [published] jobs: deploy: environment: release-pypi if: github.repository_owner == 'NatLabRockies' runs-on: ubuntu-latest permissions: id-token: write steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: python-version: '3.x' - name: Install dependencies and build package run: | python -m pip install --upgrade pip pip install build twine python -m build twine check --strict dist/* - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: verbose: True ================================================ FILE: .github/workflows/quality-metrics-workflow.yaml ================================================ name: Code Quality Check on: [push, pull_request] jobs: code-qa-validation: runs-on: ${{ matrix.os }} strategy: matrix: python-version: ["3.13"] os: [ubuntu-latest] fail-fast: False steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install project run: | python -m pip install --upgrade pip pip install ".[develop]" - name: Run the code quality script run: | cd profiling python quality_metrics.py ================================================ FILE: .gitignore ================================================ # python compiled files __pycache__/ .cache *.ipynb *.ipynb_checkpoints *.pyc *.egg-info dist build .pytest_cache .ruff_cache # pip meta data pip-wheel-metadata # macOS files .DS_Store # IDE settings and files .idea .vscode # Documentation notebooks !docs/*.ipynb # The examples folder within docs docs/examples # Documentation output _site/ .jekyll-cache/ _build _autosummary/ # Output from examples examples/cp_ct_cq_lut.p examples/hist.hist examples/SLSQP.out # Log files *.log # Temp files in convert test tests/v3_to_v4_convert_test/convert_turbine_v3_to_v4.py tests/v3_to_v4_convert_test/convert_floris_input_v3_to_v4.py tests/v3_to_v4_convert_test/gch_v4.yaml tests/v3_to_v4_convert_test/nrel_5MW_v3_v4.yaml ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-executables-have-shebangs - id: check-yaml args: [--unsafe] - id: check-merge-conflict - id: check-symlinks - id: mixed-line-ending - repo: https://github.com/charliermarsh/ruff-pre-commit rev: v0.9.6 hooks: - id: ruff - repo: https://github.com/timothycrosley/isort rev: 5.12.0 hooks: - id: isort name: isort stages: [commit] ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to FLORIS FLORIS is a community model, and we are excited for community contributions! There are a variety of ways in which you can contribute beyond writing code. This document provides a high-level overview of how you can get involved. ## Asking Questions Have a question? Rather than opening an issue directly, please ask questions or post comments in [Q&A Discussions](https://github.com/NatLabRockies/floris/discussions/categories/q-a). The NLR team or other members of the community will assist. Your well-worded question will serve as a resource to others searching for help. ## Providing Feedback Your comments and feedback are very welcome. Please post to [General Discussions](https://github.com/NatLabRockies/floris/discussions/categories/general) with lots of information and detail. It is beneficial to consider how someone else will understand your comments in order to make them most effective. ## Reporting Issues Have you identified a reproducible problem in FLORIS? Have a feature request? We want to hear about it! Here's how you can make reporting your issue as effective as possible. ### Look For an Existing Issue Before you create a new issue, please do a search in [open issues](https://github.com/microsoft/vscode/issues) to see if the issue or feature request has already been filed. If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment: - 👍 - upvote - 👎 - downvote If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below. ### Writing Good Bug Reports and Feature Requests File a single issue per problem and feature request. Do not enumerate multiple bugs or feature requests in the same issue. Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes. The more information you can provide, the more likely someone will be successful at reproducing the issue and finding a fix. Please follow the issue template guidelines to include relevant information that will help in diagnosing the problem. ### Final Checklist Please remember to do the following: - [ ] Search the issue repository to ensure your report is a new issue - [ ] Recreate the issue with a minimally descriptive example - [ ] Simplify your code around the issue to better isolate the problem ## Contributing Fixes If you are interested in writing code to fix an issue or submit a new feature, let us know in [Ideas Discussions](https://github.com/NatLabRockies/floris/discussions/categories/ideas)! We rely heavily on git and GitHub, so be sure to review the contributing guidelines in the [online documentation](https://natlabrockies.github.io/floris/dev_guide.html). ================================================ FILE: LICENSE.txt ================================================ BSD 3-Clause License Copyright (c) 2025, Alliance for Energy Innovation 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. ================================================ FILE: README.md ================================================ # 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 NLR since 2013 and the latest release is [FLORIS v.4.6.4](https://github.com/NatLabRockies/floris/releases/latest). Online documentation is available at https://natlabrockies.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](https://github.com/NatLabRockies/floris/discussions/)! ## WETO software FLORIS is primarily developed with the support from the U.S. Department of Energy and is part of the [WETO Software Stack](https://natlabrockies.github.io/WETOStack). For more information and other integrated modeling software, see: - [Portfolio Overview](https://natlabrockies.github.io/WETOStack/portfolio_analysis/overview.html) - [Entry Guide](https://natlabrockies.github.io/WETOStack/_static/entry_guide/index.html) - [Wind Farm Controls Workshop](https://www.youtube.com/watch?v=f-w6whxIBrA&list=PL6ksUtsZI1dwRXeWFCmJT6cEN1xijsHJz) ## 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](https://mdolab-pyoptsparse.readthedocs-hosted.com/en/latest/) 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: ```bash 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](https://docs.conda.io/en/latest/miniconda.html) in order to maintain a clean and sandboxed environment. The following commands in a terminal or shell will download and install FLORIS. ```bash # Download the source code from the `main` branch git clone -b main https://github.com/NatLabRockies/floris.git # If using conda, be sure to activate your environment prior to installing # conda activate # 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: ```python >>> import floris >>> help(floris) Help on package floris: NAME floris 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.6.4 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](https://natlabrockies.github.io/floris/installation.html)). The typical entry point is [FlorisModel](https://natlabrockies.github.io/floris/_autosummary/floris.floris_model.html) 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](https://natlabrockies.github.io/floris/_autosummary/floris.floris_model.html#floris.floris_model.FlorisModel.set) routine, and the simulation is executed with [FlorisModel.run](https://natlabrockies.github.io/floris/_autosummary/floris.floris_model.html#floris.floris_model.FlorisModel.run). ```python 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](https://natlabrockies.github.io/floris/_autosummary/floris.floris_model.html#floris.floris_model.FlorisModel) such as - [FlorisModel.get_turbine_layout](https://natlabrockies.github.io/floris/_autosummary/floris.floris_model.html#floris.floris_model.FlorisModel.get_turbine_layout) - [FlorisModel.get_turbine_powers](https://natlabrockies.github.io/floris/_autosummary/floris.floris_model.html#floris.floris_model.FlorisModel.get_turbine_powers) - [FlorisModel.get_farm_AEP](https://natlabrockies.github.io/floris/_autosummary/floris.floris_model.html#floris.floris_model.FlorisModel.get_farm_AEP) and in two visualization packages: [layoutviz](https://natlabrockies.github.io/floris/_autosummary/floris.layout_visualization.html) and [flowviz](https://natlabrockies.github.io/floris/_autosummary/floris.flow_visualization.html). A collection of examples describing the creation of simulations as well as analysis and post processing are included in the [repository](https://github.com/NatLabRockies/floris/tree/main/examples). Examples are also listed in the [online documentation](https://natlabrockies.github.io/floris/examples/001_opening_floris_computing_power.html). ## Engaging on GitHub FLORIS leverages the following GitHub features to coordinate support and development efforts: - [Discussions](https://github.com/NatLabRockies/floris/discussions): Collaborate to develop ideas for new use cases, features, and software designs, and get support for usage questions - [Issues](https://github.com/NatLabRockies/floris/issues): Report potential bugs and well-developed feature requests - [Projects](https://github.com/orgs/NREL/projects/96): 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](https://github.com/NatLabRockies/floris/discussions/categories/ideas) is a great spot to develop the details for a feature request. [Q&A](https://github.com/NatLabRockies/floris/discussions/categories/q-a) is where to get usage support. [Show and tell](https://github.com/NatLabRockies/floris/discussions/categories/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) 2025, Alliance for Energy Innovation 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. ================================================ FILE: benchmarks/bench.py ================================================ from pathlib import Path import numpy as np import pytest from floris import ( FlorisModel, TimeSeries, ) from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.heterogeneous_map import HeterogeneousMap N_Conditions = 100 # These tests are run automatically by pytest-benchmark. The benchmark # object is passed to the test function. def test_timing_small_farm_set(benchmark): """Timing test for setting up a small farm""" fmodel = FlorisModel(configuration="defaults") wind_directions = np.linspace(0, 360, N_Conditions) wind_speeds = np.ones(N_Conditions) * 8 turbulence_intensities = np.ones(N_Conditions) * 0.06 benchmark( fmodel.set, wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, layout_x=np.linspace(0, 1000, 3), layout_y=np.linspace(0, 1000, 3), ) def test_timing_small_farm_run(benchmark): """Timing test for running a small farm""" fmodel = FlorisModel(configuration="defaults") wind_directions = np.linspace(0, 360, N_Conditions) wind_speeds = np.ones(N_Conditions) * 8 turbulence_intensities = np.ones(N_Conditions) * 0.06 fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, layout_x=np.linspace(0, 1000, 3), layout_y=np.linspace(0, 1000, 3), ) benchmark(fmodel.run) def test_timing_large_farm_set(benchmark): """Timing test for setting up a large farm""" fmodel = FlorisModel(configuration="defaults") wind_directions = np.linspace(0, 360, N_Conditions) wind_speeds = np.ones(N_Conditions) * 8 turbulence_intensities = np.ones(N_Conditions) * 0.06 benchmark( fmodel.set, wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, layout_x=np.linspace(0, 10000, 100), layout_y=np.linspace(0, 10000, 100), ) def test_timing_large_farm_run(benchmark): """Timing test for running a large farm""" fmodel = FlorisModel(configuration="defaults") wind_directions = np.linspace(0, 360, N_Conditions) wind_speeds = np.ones(N_Conditions) * 8 turbulence_intensities = np.ones(N_Conditions) * 0.06 fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, layout_x=np.linspace(0, 10000, 100), layout_y=np.linspace(0, 10000, 100), ) benchmark(fmodel.run) def test_timing_het_set(benchmark): """Timing test for setting up a farm with a heterogeneous map""" # The side of the flow which is accelerated reverses for east versus west het_map = HeterogeneousMap( x=np.array([0.0, 0.0, 500.0, 500.0]), y=np.array([0.0, 500.0, 0.0, 500.0]), speed_multipliers=np.array( [ [1.0, 2.0, 1.0, 2.0], # Top accelerated [2.0, 1.0, 2.0, 1.0], # Bottom accelerated ] ), wind_directions=np.array([270.0, 90.0]), wind_speeds=np.array([8.0, 8.0]), ) # Get the FLORIS model fmodel = FlorisModel(configuration="defaults") time_series = TimeSeries( wind_directions=np.linspace(0, 360, N_Conditions), wind_speeds=8.0, turbulence_intensities=0.06, heterogeneous_map=het_map, ) # Set the model to a turbines perpendicular to # east/west flow with 0 turbine closer to bottom and # turbine 1 closer to top benchmark( fmodel.set, wind_data=time_series, layout_x=[250.0, 250.0], layout_y=[100.0, 400.0], ) def test_timing_het_run(benchmark): """Timing test for running a farm with a heterogeneous map""" # The side of the flow which is accelerated reverses for east versus west het_map = HeterogeneousMap( x=np.array([0.0, 0.0, 500.0, 500.0]), y=np.array([0.0, 500.0, 0.0, 500.0]), speed_multipliers=np.array( [ [1.0, 2.0, 1.0, 2.0], # Top accelerated [2.0, 1.0, 2.0, 1.0], # Bottom accelerated ] ), wind_directions=np.array([270.0, 90.0]), wind_speeds=np.array([8.0, 8.0]), ) # Get the FLORIS model fmodel = FlorisModel(configuration="defaults") time_series = TimeSeries( wind_directions=np.linspace(0, 360, N_Conditions), wind_speeds=8.0, turbulence_intensities=0.06, heterogeneous_map=het_map, ) # Set the model to a turbines perpendicular to # east/west flow with 0 turbine closer to bottom and # turbine 1 closer to top fmodel.set( wind_data=time_series, layout_x=[250.0, 250.0], layout_y=[100.0, 400.0], ) benchmark(fmodel.run) ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/_config.yml ================================================ # Book settings # Learn more at https://jupyterbook.org/customize/config.html title: FLORIS author: National Laboratory of the Rockies logo: docs_image.png copyright: '2025' only_build_toc_files: false # Force re-execution of notebooks on each build. # See https://jupyterbook.org/content/execute.html execute: execute_notebooks: auto timeout: 420 # Give each notebook cell 7 minutes to execute # Define the name of the latex output file for PDF builds latex: latex_documents: targetname: book.tex # Add a bibtex file so that we can create citations bibtex_bibfiles: - references.bib # Information about where the book exists on the web repository: url: https://github.com/NatLabRockies/floris path_to_book: docs branch: main # Add GitHub buttons to your book # See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository html: use_issues_button: true use_repository_button: true use_edit_page_button: true analytics: google_analytics_id: G-JV2SK7CNPR # Sphinx for API doc generation sphinx: extra_extensions: - 'sphinx.ext.autodoc' - 'sphinx.ext.autosummary' - 'sphinx.ext.viewcode' - 'sphinx_autodoc_typehints' - 'sphinxcontrib.autoyaml' - 'sphinxcontrib.mermaid' config: language: 'python' nb_execution_show_tb: true # Shows the stack trace in stdout; its suppressed otherwise. nb_execution_raise_on_error: true # Stops the Sphinx build if there is an error in a notebook. See https://github.com/executablebooks/jupyter-book/issues/2011 suppress_warnings: - etoc.toctree # autodoc output contains toctrees, so suppress this warning. See https://github.com/executablebooks/sphinx-external-toc/issues/36 autoyaml_level: 3 autosummary_generate: true # Autodoc config reference # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration autodoc_default_options: members: true member-order: bysource undoc-members: true private-members: false # special-members: true # inherited-members # show-inheritance # ignore-module-all # imported-members: true # exclude-members # class-doc-from # no-value autodoc_typehints: both mermaid_version: "10.8" ================================================ FILE: docs/_toc.yml ================================================ # Table of contents # Learn more at https://jupyterbook.org/customize/toc.html format: jb-book root: index parts: - caption: Getting Started chapters: - file: installation - file: v3_to_v4 - caption: User Reference chapters: - file: intro_concepts - file: wind_data_user - file: floris_models - file: input_reference_main - file: turbine_models sections: - file: input_reference_turbine - file: operation_models_user - file: floating_wind_turbine - file: multidimensional_wind_turbine - file: turbine_library - file: advanced_concepts - file: heterogeneous_map - file: layout_optimization - file: examples - caption: Theory and Background chapters: - file: wake_models sections: - file: empirical_gauss_model - file: bibliography - caption: Developer Reference chapters: - file: dev_guide - file: architecture - file: code_quality - file: api_docs ================================================ FILE: docs/advanced_concepts.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "(concepts_advanced)=\n", "\n", "# Advanced Concepts\n", "\n", "More information regarding the numerical and computational formulation in FLORIS\n", "are detailed here. See [Introductory Concepts](intro_concepts) for a guide on the basics." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Create a basic FLORIS model for use later\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from floris import FlorisModel\n", "fmodel = FlorisModel(\"gch.yaml\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data structures\n", "\n", "FLORIS adopts a structures of arrays data modeling paradigm (SoA, relative to array of structures {AoS})\n", "for nearly all of the data in the `floris.core` package.\n", "This data model enables vectorization (SIMD operations) through Numpy array broadcasting\n", "for many operations.\n", "In general, there are two types of array shapes:\n", "- Field quantities have points throughout the computational domain but in context-specific locations\n", " and have the shape `(n findex, n turbines, n grid, n grid)`.\n", "- Scalar quantities have a single value for each turbine and typically have the shape\n", " `(n findex, n turbines, 1, 1)`. For scalar quanities, the arrays\n", " may be created with the shape `(n findex, n turbines)` and\n", " then expanded to the 4-dimensional shape prior to running the wake calculation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Grids\n", "\n", "FLORIS includes a number of grid-types that create sampling points within the computational\n", "domain for different contexts. In the typical use case, AEP or some other metric of wind\n", "farm energy yield is the end result. Since the mathematical models in FLORIS are all\n", "analytical, we only need to create points on the turbines themselves in order to calculate\n", "the incoming wind speeds given all of the upstream conditions. In this case, we use\n", "the {py:meth}`floris.core.grid.TurbineGrid` or {py:meth}`floris.core.grid.TurbineCubatureGrid`.\n", "Each of these grid-types put points only on the turbine swept area, so all other\n", "field-quantities in FLORIS have the same shape." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot the grid point locations for TurbineGrid and TurbineCubatureGrid\n", "\n", "fmodel.set(layout_x=[0.0], layout_y=[0.0])\n", "rotor_radius = fmodel.core.farm.rotor_diameters[0] / 2.0\n", "hub_height = fmodel.core.farm.hub_heights[0]\n", "theta = np.linspace(0, 2*np.pi, 100)\n", "circlex = rotor_radius * np.cos(theta)\n", "circley = rotor_radius * np.sin(theta) + hub_height\n", "\n", "# TurbineGrid is the default\n", "fig, ax = plt.subplots()\n", "ax.scatter(0, hub_height, marker=\"+\", color=\"r\")\n", "ax.scatter(fmodel.core.grid.y_sorted[0,0], fmodel.core.grid.z_sorted[0,0], marker=\"+\", color=\"r\")\n", "ax.plot(circlex, circley)\n", "ax.set_aspect('equal', 'box')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## FLORIS as a library\n", "\n", "FLORIS is commonly used as a library in other software packages.\n", "In cases where the calling-code will create inputs for FLORIS rather than require them from the\n", "user, it can be helpful to initialize the FLORIS model with default inputs and then\n", "change them in code.\n", "In this case, the following workflow is recommended." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import floris\n", "\n", "# Initialize FLORIS with defaults\n", "fmodel = floris.FlorisModel(\"defaults\")\n", "\n", "# Within the calling-code's setup step, update FLORIS as needed\n", "fmodel.set(\n", " wind_directions=[i for i in range(10)],\n", " wind_speeds=[5 + i for i in range(10)],\n", " turbulence_intensities=[i for i in range(10)],\n", " # turbine_library_path=\"path/to/turbine_library\", # Shown here for reference\n", " # turbine_type=[\"my_turbine\"]\n", ")\n", "\n", "# Within the calling code's computation, run FLORIS\n", "fmodel.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, the calling-code can import the FLORIS default inputs as a Python dictionary\n", "and modify them directly before initializing the FLORIS model.\n", "This is especially helpful when the calling-code will modify a parameter that isn't\n", "supported by the `FlorisModel.set(...)` command.\n", "In particular, the wake model parameters are not directly accessible, so these can be updated\n", "externally, as shown below.\n", "Note that the `FlorisModel.get_defaults()` function returns a deep copy of the default inputs,\n", "so these can be modified directly without side effects." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "solver\n", " type\n", " turbine_grid\n", " turbine_grid_points\n", " 3\n", "wake\n", " model_strings\n", " combination_model\n", " sosfs\n", " deflection_model\n", " gauss\n", " turbulence_model\n", " crespo_hernandez\n", " velocity_model\n", " jensen\n", "farm\n", " layout_x\n", " [0.0]\n", " layout_y\n", " [0.0]\n", " turbine_type\n", " ['nrel_5MW']\n", " turbine_library_path\n", " /Users/rmudafor/Development/floris/floris/turbine_library\n", "flow_field\n", " wind_speeds\n", " [5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0]\n", " wind_directions\n", " [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]\n", " wind_veer\n", " 0.0\n", " wind_shear\n", " 0.12\n", " air_density\n", " 1.225\n", " turbulence_intensities\n", " [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]\n", " reference_wind_height\n", " 90.0\n", "name\n", " GCH\n", "description\n", " Default initialization: Gauss-Curl hybrid model (GCH)\n", "floris_version\n", " v4\n" ] } ], "source": [ "import floris\n", "\n", "# Retrieve the default parameters\n", "fdefaults = floris.FlorisModel.get_defaults()\n", "\n", "# Update wake model parameters\n", "fdefaults[\"wake\"][\"model_strings\"][\"velocity_model\"] = \"jensen\"\n", "fdefaults[\"wake\"][\"wake_velocity_parameters\"][\"jensen\"][\"we\"] = 0.05\n", "\n", "# Initialize FLORIS with modified parameters\n", "fmodel = floris.FlorisModel(configuration=fdefaults)\n", "\n", "# Within the calling-code's setup step, update FLORIS as needed\n", "fmodel.set(\n", " wind_directions=[i for i in range(10)],\n", " wind_speeds=[5 + i for i in range(10)],\n", " turbulence_intensities=[i for i in range(10)],\n", " # turbine_library_path=\"path/to/turbine_library\", # Shown here for reference\n", " # turbine_type=[\"my_turbine\"]\n", ")\n", "\n", "# Verify settings are correct\n", "fmodel.show_config() # Shows truncated set of inputs; show all with fmodel.show_config(full=True)\n", "\n", "# Within the calling code's computation, run FLORIS\n", "fmodel.run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.1" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: docs/api_docs.md ================================================ # API Documentation FLORIS is primarily divided into the {py:mod}`floris` package, which contains the user-level API, and {py:mod}`floris.core` is the core code that models the wind turbines and wind farms. Additionally, the {py:mod}`turbine_library` package contains turbine models that ship with FLORIS; and the {py:mod}`optimization` package contains high-level optimization routines that accept and work on instantiated `FlorisModel` objects. ```{eval-rst} .. autosummary:: :toctree: _autosummary :recursive: floris.flow_visualization floris.floris_model floris.wind_data floris.uncertain_floris_model floris.turbine_library floris.parallel_floris_model floris.optimization floris.layout_visualization floris.cut_plane floris.core floris.convert_turbine_v3_to_v4 floris.convert_floris_input_v3_to_v4 floris.utilities floris.type_dec floris.logging_manager ``` ================================================ FILE: docs/architecture.md ================================================ # Architecture and Design At the outset of the design of the FLORIS software, a few fundamental ideas were identified that should continue to guide future design decisions. These characteristics should never be violated, and ongoing work should strive to meet these ideas and expand on them as much as possible. - Modularity in wake model formulation: - New mathematical formulation should be straightforward to incorporate by non-expert software developers. - Solver and grid data structures for one wake model should not conflict with the data structures for other wake models. - Any new feature or work should not affect an existing feature: - Low level code should be reused as much as possible, but high level code should rarely be repurposed. - It is expected that a new feature will include a new user entry point at the highest level. - Avoid flags and if-statements that allow using one high-level routine for multiple unrelated tasks. - When in doubt, create a new pipeline from the user-level API to the low level implementation and refactor to consolidate, if necessary, afterwards. - Management of abstraction: - Low level code is opaque but well tested and exercised; it should be very computationally efficient with low algorithmic complexity. - High level code should be expressive and clear even if it results in verbose or less efficient code. The FLORIS software consists of two primary high-level packages and a few other low level packages. The internal structure and hierarchy is described below. ```{mermaid} classDiagram class core["floris.core"] { +Core } class floris["floris"] { +FlorisModel } class logging_manager class type_dec class utilities tools <-- logging_manager simulation <-- logging_manager simulation <-- type_dec simulation <-- utilities tools <-- simulation ``` ## floris This is the user interface. Most operations at the user level will happen through `floris`. This package contains a wide variety of functionality including but not limited to: - Initializing and driving a simulation with `floris_model` - Wake field visualization through `flow_visualization` - Yaw and layout optimization in `optimization` - Wind data handling in `wind_data` ## floris.core This is the core simulation package. This should primarily be used within `floris.core` and `floris`, and user scripts generally won't interact directly with this package. ```{mermaid} classDiagram class Core class Farm class FlowField { u: NDArrayFloat v: NDArrayFloat w: NDArrayFloat } class Grid { <> x: NDArrayFloat y: NDArrayFloat z: NDArrayFloat } class TurbineGrid class TurbineCubatureGrid class FlowFieldPlanarGrid class PointsGrid class WakeModelManager { <> } class WakeCombination { parameters: dict function() } class WakeDeflection { parameters: dict function() } class WakeTurbulence { parameters: dict function() } class WakeVelocity { parameters: dict function() } class Solver { <> parameters: dict } Core *-- Farm Core *-- FlowField Core *-- Grid Core *-- WakeModelManager Core --> Solver WakeModelManager *-- WakeCombination WakeModelManager *-- WakeDeflection WakeModelManager *-- WakeTurbulence WakeModelManager *-- WakeVelocity Grid <|-- TurbineGrid Grid <|-- TurbineCubatureGrid Grid <|-- FlowFieldPlanarGrid Grid <|-- PointsGrid Solver --> Farm Solver --> FlowField Solver --> Grid Solver --> WakeModelManager ``` ================================================ FILE: docs/bibliography.md ================================================ # Bibliography ```{bibliography} :style: unsrt ``` ================================================ FILE: docs/code_quality.ipynb ================================================ { "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Code Quality\n", "\n", "This page contains information on various code quality metrics\n", "that are collected during the development cycle. Some of these\n", "are autogenerated while others rely on manual scripts and updating\n", "the documentation, so these will be updating as needed and when\n", "appropriate. All plots show the corresponding commit hash\n", "when hovering over a point with the mouse pointer." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Live Code Quality Metrics\n", "\n", "Live code quality metrics are computed when develop branch is updated. [Live Chart](https://nrel.github.io/floris/dev/bench/). Note these charts are computed within github actions and are somewhat noisy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Performance History\n", "\n", "The plot below shows the runtime performance over a series of commits for a\n", "3-turbine test case with 1,440 atmospheric conditions.\n", "This data is collected on [NLR's Kestrel supercomputer](https://nrel.github.io/HPC/Documentation/Systems/Kestrel/)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/yp/vdv3q8mn4xj1_m277lk54s75tvdqcf/T/ipykernel_74430/1348870237.py:3: DeprecationWarning: \n", "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", "but was not found to be installed on your system.\n", "If this would cause problems for you,\n", "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", " \n", " import pandas as pd\n" ] }, { "data": { "text/html": [ " \n", "
\n", " \n", " Loading BokehJS ...\n", "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": "'use strict';\n(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\nconst JS_MIME_TYPE = 'application/javascript';\n const HTML_MIME_TYPE = 'text/html';\n const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n const CLASS_NAME = 'output_bokeh rendered_html';\n\n /**\n * Render data to the DOM node\n */\n function render(props, node) {\n const script = document.createElement(\"script\");\n node.appendChild(script);\n }\n\n /**\n * Handle when an output is cleared or removed\n */\n function handleClearOutput(event, handle) {\n function drop(id) {\n const view = Bokeh.index.get_by_id(id)\n if (view != null) {\n view.model.document.clear()\n Bokeh.index.delete(view)\n }\n }\n\n const cell = handle.cell;\n\n const id = cell.output_area._bokeh_element_id;\n const server_id = cell.output_area._bokeh_server_id;\n\n // Clean up Bokeh references\n if (id != null) {\n drop(id)\n }\n\n if (server_id !== undefined) {\n // Clean up Bokeh references\n const cmd_clean = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n cell.notebook.kernel.execute(cmd_clean, {\n iopub: {\n output: function(msg) {\n const id = msg.content.text.trim()\n drop(id)\n }\n }\n });\n // Destroy server and session\n const cmd_destroy = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n cell.notebook.kernel.execute(cmd_destroy);\n }\n }\n\n /**\n * Handle when a new output is added\n */\n function handleAddOutput(event, handle) {\n const output_area = handle.output_area;\n const output = handle.output;\n\n // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n if ((output.output_type != \"display_data\") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {\n return\n }\n\n const toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n\n if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n // store reference to embed id on output_area\n output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n }\n if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n const bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n const script_attrs = bk_div.children[0].attributes;\n for (let i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n }\n\n function register_renderer(events, OutputArea) {\n\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n const toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[toinsert.length - 1]);\n element.append(toinsert);\n return toinsert\n }\n\n /* Handle when an output is cleared or removed */\n events.on('clear_output.CodeCell', handleClearOutput);\n events.on('delete.Cell', handleClearOutput);\n\n /* Handle when a new output is added */\n events.on('output_added.OutputArea', handleAddOutput);\n\n /**\n * Register the mime type and append_mime function with output_area\n */\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n /* Is output safe? */\n safe: true,\n /* Index of renderer in `output_area.display_order` */\n index: 0\n });\n }\n\n // register the mime type if in Jupyter Notebook environment and previously unregistered\n if (root.Jupyter !== undefined) {\n const events = require('base/js/events');\n const OutputArea = require('notebook/js/outputarea').OutputArea;\n\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n }\n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n const NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"
    \\n\"+\n \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n \"
  • use INLINE resources instead, as so:
  • \\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded(error = null) {\n const el = document.getElementById(\"a67e122f-6aa9-4ff8-97f7-6378dc8e5a11\");\n if (el != null) {\n const html = (() => {\n if (typeof root.Bokeh === \"undefined\") {\n if (error == null) {\n return \"BokehJS is loading ...\";\n } else {\n return \"BokehJS failed to load.\";\n }\n } else {\n const prefix = `BokehJS ${root.Bokeh.version}`;\n if (error == null) {\n return `${prefix} successfully loaded.`;\n } else {\n return `${prefix} encountered errors while loading and may not function as expected.`;\n }\n }\n })();\n el.innerHTML = html;\n\n if (error != null) {\n const wrapper = document.createElement(\"div\");\n wrapper.style.overflow = \"auto\";\n wrapper.style.height = \"5em\";\n wrapper.style.resize = \"vertical\";\n const content = document.createElement(\"div\");\n content.style.fontFamily = \"monospace\";\n content.style.whiteSpace = \"pre-wrap\";\n content.style.backgroundColor = \"rgb(255, 221, 221)\";\n content.textContent = error.stack ?? error.toString();\n wrapper.append(content);\n el.append(wrapper);\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(() => display_loaded(error), 100);\n }\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error(url) {\n console.error(\"failed to load \" + url);\n }\n\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.6.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.6.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.6.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.6.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.6.3.min.js\"];\n const css_urls = [];\n\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {\n }\n ];\n\n function run_inline_js() {\n if (root.Bokeh !== undefined || force === true) {\n try {\n for (let i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n\n } catch (error) {display_loaded(error);throw error;\n }if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n const cell = $(document.getElementById(\"a67e122f-6aa9-4ff8-97f7-6378dc8e5a11\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));", "application/vnd.bokehjs_load.v0+json": "" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from bokeh.plotting import figure, show, output_notebook\n", "from bokeh.models import ColumnDataSource, HoverTool, Range1d\n", "import pandas as pd\n", "from datetime import datetime\n", "\n", "output_notebook()\n", "\n", "COLORS = ['blue', 'green', 'red', 'cyan', 'magenta', 'y', 'k']\n", "\n", "columns = [\"commit_hash\", \"commit_hash_8char\", \"date\", \"jensen\", \"gauss\", \"gch\", \"cc\", \"emgauss\", \"tooltip_label\"]\n", "data = [\n", " (\"df25a9cfacd3d652361d2bd37f568af00acb2631\", \"df25a9cf\", datetime(2021, 12, 29), 1.2691, 1.2584, 1.6432, None, None, \"df25a9cf\"),\n", " (\"b797390a43298a815f3ff57955cfdc71ecf3e866\", \"b797390a\", datetime(2022, 1, 3), 0.6867, 1.2354, 1.8026, None, None, \"b797390a\"),\n", " (\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\", \"01a02d5f\", datetime(2022, 1, 4), 0.2329, 0.5661, 0.9869, None, None, \"01a02d5f\"),\n", " (\"dd847210082035d43b0273ae63a76a53cb8d2e12\", \"dd847210\", datetime(2022, 1, 6), 0.2290, 0.5815, 0.9822, None, None, \"dd847210\"),\n", " (\"33779269e98cc882a5f066c462d8ec1eadf37a1a\", \"33779269\", datetime(2022, 1, 10), 0.2300, 0.5845, 1.0114, None, None, \"33779269\"),\n", " (\"12890e029a7155b074b9b325d320d1798338e287\", \"12890e02\", datetime(2022, 1, 11), 0.2296, 0.5660, 1.0114, None, None, \"12890e02\"),\n", " (\"66dafc08bd620d96deda7d526b0e4bfc3b086650\", \"66dafc08\", datetime(2022, 1, 12), 0.2342, 0.5937, 1.0284, None, None, \"66dafc08\"),\n", " (\"a325819b3b03b84bd76ad455e3f9b4600744ba14\", \"a325819b\", datetime(2022, 1, 13), 0.2349, 0.5711, 1.0072, None, None, \"a325819b\"),\n", " (\"8a2c1a610295c007f0222ce737723c341189811d\", \"8a2c1a61\", datetime(2022, 1, 14), 0.2338, 0.5837, 1.0242, None, None, \"8a2c1a61\"),\n", " (\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\", \"c6bc79b0\", datetime(2022, 1, 14), 0.2374, 0.5653, 0.9781, None, None, \"c6bc79b0\"),\n", " (\"03e1f461c152e4f221fe92c834f2787680cf5772\", \"03e1f461\", datetime(2022, 1, 18), 0.2423, 0.5820, 1.0411, 1.2251, None, \"PR #56\"),\n", " (\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\", \"9e96d6c4\", datetime(2022, 1, 19), 0.2507, 0.5674, 1.0053, 1.1828, None, \"v3.0rc1\"),\n", " (\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\", \"2a98428f\", datetime(2022, 2, 15), 0.1662, 0.5699, 1.0081, 1.1906, None, \"PR #317\"),\n", " (\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\", \"9b4e85cf\", datetime(2022, 3, 1), 0.1710, 0.6143, 1.0626, 1.2013, None, \"v3.0\"),\n", " (\"d18f4d263ecabf502242592f9d60815a07c7b89c\", \"d18f4d26\", datetime(2022, 3, 4), 0.1766, 0.6020, 1.0612, 1.2088, None, \"v3.0.1\"),\n", " (\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\", \"a23241bb\", datetime(2022, 4, 6), 0.1764, 0.6044, 1.0656, 1.2067, None, \"v3.1\"),\n", " (\"c2006b0011a5df036c306c15e75763ec492dafda\", \"c2006b00\", datetime(2022, 6, 22), 0.1801, 0.6082, 1.0603, 1.2025, None, \"v3.1.1\"),\n", " (\"0c2adf3e702b6427da946a6ba9dbedbea22738be\", \"0c2adf3e\", datetime(2022, 9, 16), 0.1753, 0.6202, 1.0486, 1.1962, None, \"v3.2\"),\n", " (\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\", \"39c46600\", datetime(2022, 11, 20), 0.1738, 0.6140, 1.0498, 1.1783, None, \"v3.2.1\"),\n", " (\"8436fd78b002e5792f5d0dd1409332d171036d49\", \"8436fd78\", datetime(2023, 2, 8), 0.1980, 0.6334, 1.0631, 1.2009, None, \"v3.2.2\"),\n", " (\"07a45b66c5facfea06c40bd82e34040c97560640\", \"07a45b66\", datetime(2023, 2, 8), 0.1831, 0.6070, 1.0233, 1.1991, None, \"07a45b66\"),\n", " (\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\", \"1d84538c\", datetime(2023, 2, 22), 0.1771, 0.6226, 1.0541, 1.2044, None, \"1d84538c\"),\n", " (\"4d528a3d6456621a382d409b5145a877b5414b88\", \"4d528a3d\", datetime(2023, 2, 23), 0.1768, 0.6026, 1.0366, 1.1958, None, \"4d528a3d\"),\n", " (\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\", \"8c637b36\", datetime(2023, 2, 27), 0.2052, 0.6264, 1.0655, 1.2137, None, \"8c637b36\"),\n", " (\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\", \"4d23fa6d\", datetime(2023, 2, 27), 0.1996, 0.6083, 1.0465, 1.2184, None, \"4d23fa6d\"),\n", " (\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\", \"015f6874\", datetime(2023, 3, 1), 0.2204, 0.6383, 1.0633, 1.2093, None, \"015f6874\"),\n", " (\"26f06d449da208ce64724b1463b07ad20746cbdc\", \"26f06d44\", datetime(2023, 3, 6), 0.1848, 0.6186, 1.0410, 1.2076, None, \"26f06d44\"),\n", " (\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\", \"6b9d6bb8\", datetime(2023, 3, 6), 0.1946, 0.6247, 1.0742, 1.2250, None, \"6b9d6bb8\"),\n", " (\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\", \"b796bd0f\", datetime(2023, 3, 6), 0.2027, 0.6333, 1.0612, 1.2038, None, \"b796bd0f\"),\n", " (\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\", \"780aef7c\", datetime(2023, 3, 7), 0.1922, 0.6292, 1.0494, 1.2125, None, \"780aef7c\"),\n", " (\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\", \"9f93ad9b\", datetime(2023, 3, 7), 0.2007, 0.6342, 1.0611, 1.2059, None, \"9f93ad9b\"),\n", " (\"16628a0ba45a675df762245694e0a7666a3478f8\", \"16628a0b\", datetime(2023, 3, 7), 0.2110, 0.6405, 1.0799, 1.2101, None, \"v3.3\"),\n", " (\"01684c8559604344bd09791268131819a09770a8\", \"01684c85\", datetime(2023, 3, 17), 0.2067, 0.6334, 1.0691, 1.1924, None, \"01684c85\"),\n", " (\"e9231fb893c765b723fa4c1e087a58761b6aa471\", \"e9231fb8\", datetime(2023, 3, 20), 0.2082, 0.6357, 1.0728, 1.2212, None, \"e9231fb8\"),\n", " (\"219889e243ffc69c71b6f7747f5af751d5694de1\", \"219889e2\", datetime(2023, 3, 23), 0.1912, 0.6182, 1.0651, 1.2033, None, \"219889e2\"),\n", " (\"6124d2a82a7a823722210bc2e8516d355ba19eb3\", \"6124d2a8\", datetime(2023, 4, 5), 0.2064, 0.6419, 1.0711, 1.1933, None, \"6124d2a8\"),\n", " (\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\", \"f6e4287f\", datetime(2023, 4, 25), 0.1944, 0.6103, 1.0460, 1.2236, None, \"f6e4287f\"),\n", " (\"f2797fef396f2f19b02abb1f9555b678dac614f1\", \"f2797fef\", datetime(2023, 4, 25), 0.1997, 0.6357, 1.0648, 1.2128, None, \"f2797fef\"),\n", " (\"b4e538f530048fec58eaca5170be82c67dbdcceb\", \"b4e538f5\", datetime(2023, 4, 25), 0.2103, 0.6415, 1.0679, 1.2248, None, \"b4e538f5\"),\n", " (\"68820b715ed6b2c981aa11d29c0102e879280d79\", \"68820b71\", datetime(2023, 4, 25), 0.2076, 0.6354, 1.0625, 1.2018, None, \"68820b71\"),\n", " (\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\", \"03deffed\", datetime(2023, 4, 25), 0.2138, 0.6453, 1.0738, 1.2148, None, \"03deffed\"),\n", " (\"0d2bfecc271d561f67050659684b4797af8ee740\", \"0d2bfecc\", datetime(2023, 4, 25), 0.2117, 0.6464, 1.0855, 1.2213, None, \"0d2bfecc\"),\n", " (\"1d03a465593f56c99a64a576d185d4ed17b659f2\", \"1d03a465\", datetime(2023, 4, 25), 0.2060, 0.6376, 1.0722, 1.2129, None, \"1d03a465\"),\n", " (\"78a953b7ef9a36b62e5b446c80ed68abfddbfb74\", \"78a953b7\", datetime(2023, 5, 4), 0.2170, 0.6323, 1.0673, 1.2116, None, \"78a953b7\"),\n", " (\"6c4f70ffbf3d4d2922d41d0032ae1b93d8a23c99\", \"6c4f70ff\", datetime(2023, 5, 4), 0.2123, 0.6407, 1.0688, 1.2183, None, \"6c4f70ff\"),\n", " (\"ab03282623d0262b20b8c132efcdcace2dace766\", \"ab032826\", datetime(2023, 5, 6), 0.1846, 0.6147, 1.0605, 1.2106, None, \"ab032826\"),\n", " (\"d2f7a45af27a6b40027d6f6a0f4f0be0c6dee5d9\", \"d2f7a45a\", datetime(2023, 5, 6), 0.1924, 0.6259, 1.0525, 1.1896, None, \"d2f7a45a\"),\n", " (\"98b23f3d517481b127f190f5f8b7ebfae7f8b6b2\", \"98b23f3d\", datetime(2023, 5, 6), 0.1940, 0.6177, 1.0524, 1.1916, None, \"98b23f3d\"),\n", " (\"452425de723cc1640d999022389672caf9bffbd0\", \"452425de\", datetime(2023, 5, 6), 0.2075, 0.6297, 1.0647, 1.2017, None, \"452425de\"),\n", " (\"85dadb1a566c9fa8dc84cb9837b98bd5d23b8d58\", \"85dadb1a\", datetime(2023, 5, 7), 0.1853, 0.6164, 1.0244, 1.1917, None, \"85dadb1a\"),\n", " (\"432ee7f96c1f6cccd05a0034c86c720cdb63a3e6\", \"432ee7f9\", datetime(2023, 5, 10), 0.1813, 0.6003, 1.0310, 1.1856, None, \"432ee7f9\"),\n", " (\"ebd70ecaef14c0e239337eb6e36506303378a31a\", \"ebd70eca\", datetime(2023, 5, 10), 0.1814, 0.6012, 1.0402, 1.2075, 0.3262, \"ebd70eca\"),\n", " (\"77fa7155d55bdf3fd43e29f58fe57feffcb107cf\", \"77fa7155\", datetime(2023, 5, 11), 0.1763, 0.6095, 1.0507, 1.2181, 0.3431, \"77fa7155\"),\n", " (\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\", \"d5d4b134\", datetime(2023, 5, 12), 0.1921, 0.6277, 1.0608, 1.2104, 0.3342, \"d5d4b134\"),\n", " (\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\", \"d5d4b134\", datetime(2023, 5, 16), 0.1783, 0.6172, 1.0474, 1.1958, 0.3263, \"d5d4b134\"),\n", " (\"7c879f1ce18b52d9b0a8eecf877d03e66afc975b\", \"7c879f1c\", datetime(2023, 5, 16), 0.1754, 0.5924, 1.0241, 1.1966, 0.3096, \"7c879f1c\"),\n", " (\"2aa9f2a55686f2ee5dc407e8e0223eb25176d906\", \"2aa9f2a5\", datetime(2023, 5, 16), 0.1843, 0.6066, 1.0405, 1.1988, 0.3263, \"2aa9f2a5\"),\n", " (\"5e5bb7f4e653621e7a81ff4bcaa27dbc1f759de7\", \"5e5bb7f4\", datetime(2023, 5, 16), 0.1911, 0.6197, 1.0425, 1.1792, 0.3426, \"v3.4\"),\n", " (\"d91953a499dfb88b457a1e7a07903debbda4058b\", \"d91953a4\", datetime(2023, 6, 1), 0.1839, 0.6171, 1.0521, 1.1919, 0.3269, \"d91953a4\"),\n", " (\"76742879c81c9baced49b9fc60abbf1d2eba65ff\", \"76742879\", datetime(2023, 7, 3), 0.1818, 0.6128, 1.0514, 1.1965, 0.3338, \"76742879\"),\n", " (\"9c73a41eaca95bb718ac79980a1799dfa1c48cf3\", \"9c73a41e\", datetime(2023, 7, 6), 0.1795, 0.6011, 1.0335, 1.2146, 0.3142, \"9c73a41e\"),\n", " (\"67104dd714de939be136646af68edd9643ddfcd3\", \"67104dd7\", datetime(2023, 7, 6), 0.1869, 0.5838, 0.7982, 0.9479, 0.3153, \"67104dd7\"),\n", " (\"e6906feebdee6bdd2103f0bd390679e6a1b0052d\", \"e6906fee\", datetime(2023, 7, 7), 0.1751, 0.5780, 0.7820, 0.9452, 0.3136, \"e6906fee\"),\n", " (\"8908ab47eaa8a3d7e7c9126484b524f751e41f55\", \"8908ab47\", datetime(2023, 7, 10), 0.1739, 0.5472, 0.7007, 0.8955, 0.3012, \"8908ab47\"),\n", " (\"063d8b58464f95520c9887ac4f575e6c1f6880d8\", \"063d8b58\", datetime(2023, 7, 11), 0.1758, 0.5891, 0.7337, 0.8818, 0.3062, \"063d8b58\"),\n", " (\"59e53a66aef134a3c9e912f9468ca667b599d4e5\", \"59e53a66\", datetime(2023, 7, 27), 0.1918, 0.6174, 1.0461, 1.1964, 0.3380, \"59e53a66\"),\n", " (\"cd14608474be8561c188d2aa7a772b8ac753fb70\", \"cd146084\", datetime(2023, 8, 3), 0.1799, 0.5783, 0.7979, 0.9539, 0.3214, \"cd146084\"),\n", " (\"db958c4b779ffc825689e052958020864cbcde63\", \"db958c4b\", datetime(2023, 8, 15), 0.1683, 0.5486, 0.7048, 0.8803, 0.2955, \"db958c4b\"),\n", " (\"8ece0f5f7d3bfd66f4f83198debf5627344af534\", \"8ece0f5f\", datetime(2023, 8, 15), 0.1697, 0.5529, 0.7032, 0.8925, 0.2899, \"8ece0f5f\"),\n", " (\"77ea50d9bd5d01f7110dbebf1ba689a25eee9d96\", \"77ea50d9\", datetime(2023, 9, 11), 0.1858, 0.5741, 0.7973, 0.9559, 0.3191, \"77ea50d9\"),\n", " (\"05b900c228d427bfa8e531527b546cdeb822cfc9\", \"05b900c2\", datetime(2023, 10, 4), 0.1842, 0.5622, 0.7196, 0.8759, 0.2821, \"05b900c2\"),\n", " (\"2dccbbd0ca67a274a2aeb9996f262014b3137fc0\", \"2dccbbd0\", datetime(2023, 10, 20), 0.1708, 0.5382, 0.6906, 0.8738, 0.2908, \"2dccbbd0\"),\n", " (\"e9c90aa521917e587dd9497d529822f359eec3e2\", \"e9c90aa5\", datetime(2023, 10, 26), 0.1778, 0.5911, 0.7423, 0.8710, 0.2858, \"e9c90aa5\"),\n", " (\"6c3ddb48b59d286899a8efd5989d741f86c4ade3\", \"6c3ddb48\", datetime(2023, 10, 26), 0.1700, 0.5585, 0.6991, 0.8823, 0.2887, \"6c3ddb48\"),\n", " (\"31fe1b69ff863f0a610aec5b22424382ec3cc933\", \"31fe1b69\", datetime(2023, 10, 26), 0.1661, 0.5548, 0.7630, 0.9317, 0.2969, \"v3.5\"),\n", " (\"a4768d08e172e009efb7ccafb8dc37a90753df7f\", \"a4768d08\", datetime(2023, 11, 8), 0.1725, 0.5768, 0.7635, 0.8827, 0.2990, \"a4768d08\"),\n", " (\"1a2e86847d7942d9ecac20d91d2f4cd73685b230\", \"1a2e8684\", datetime(2023, 11, 15), 0.1764, 0.5933, 0.7970, 0.8984, 0.2972, \"1a2e8684\"),\n", " (\"bc4fd2812636baba036d6f5648c6e77bcfb263e4\", \"bc4fd281\", datetime(2023, 11, 15), 0.1728, 0.5468, 0.7200, 0.8730, 0.2886, \"bc4fd281\"),\n", " (\"34a7e0cd84226520f5a39cf3345699f012aad505\", \"34a7e0cd\", datetime(2023, 11, 28), 0.1697, 0.5541, 0.7179, 0.8844, 0.2929, \"34a7e0cd\"),\n", " (\"e1dd6b73ae1d9552e0fe9679c0d35199c113d647\", \"e1dd6b73\", datetime(2023, 11, 30), 0.1715, 0.5709, 0.7846, 0.9334, 0.2871, \"e1dd6b73\"),\n", " (\"599f2266ad55f73f4afaf80d4d1e32523a256d3b\", \"599f2266\", datetime(2023, 12, 1), 0.1892, 0.5930, 0.7877, 0.9125, 0.2928, \"599f2266\"),\n", " (\"52b8a5f595f565b149a227b26bea7786d742f15c\", \"52b8a5f5\", datetime(2023, 12, 6), 0.1747, 0.5856, 0.7290, 0.8946, 0.2960, \"52b8a5f5\"),\n", " (\"6f2256dab34c9d9548bd6d4d81594605d75434bd\", \"6f2256da\", datetime(2023, 12, 6), 0.1799, 0.5777, 0.7798, 0.9518, 0.3016, \"6f2256da\"),\n", " (\"b6de318f12c93872daec8762edecb66d311504b5\", \"b6de318f\", datetime(2023, 12, 6), 0.1820, 0.5993, 0.8048, 0.9650, 0.3211, \"b6de318f\"),\n", " (\"94357caf7092f6bfb6d8b51b9daf1ae9b47f26dc\", \"94357caf\", datetime(2023, 12, 7), 0.1731, 0.5623, 0.7093, 0.8866, 0.2924, \"94357caf\"),\n", " (\"64ae6789f1860734ab4d6d34cd7c4313a85274fc\", \"64ae6789\", datetime(2023, 12, 8), 0.1701, 0.5625, 0.7113, 0.8901, 0.2897, \"64ae6789\"),\n", " (\"da61128340ac0e520b4583eb759bf771512332f6\", \"da611283\", datetime(2023, 12, 13), 0.1713, 0.5603, 0.6923, 0.8770, 0.2882, \"da611283\"),\n", " (\"bbcb8c33f4f35036c8156c57cea60fea5c5e17af\", \"bbcb8c33\", datetime(2023, 12, 13), 0.1748, 0.5769, 0.6966, 0.8766, 0.2840, \"bbcb8c33\"),\n", " (\"ebdf9a146359606dc5e13363beee6dd82ad1c247\", \"ebdf9a14\", datetime(2023, 12, 13), 0.1782, 0.5622, 0.7108, 0.8755, 0.2895, \"ebdf9a14\"),\n", " (\"b88dc7bb15370da91956ca24db3875245dc073d2\", \"b88dc7bb\", datetime(2024, 1, 4), 0.1836, 0.5699, 0.7204, 0.8891, 0.2957, \"b88dc7bb\"),\n", " (\"a88a15432ef01f7600e3ccd7b9d2b4ba89a01df0\", \"a88a1543\", datetime(2024, 2, 7), 0.1922, 0.5925, 0.7759, 0.8914, 0.2937, \"a88a1543\"),\n", " (\"c7f8f36178f35ccd8e08e0ce10a77fc487f78a14\", \"c7f8f361\", datetime(2024, 2, 20), 0.1801, 0.5775, 0.7097, 0.8755, 0.2907, \"c7f8f361\"),\n", " (\"4b331398bc4a08edcd32539157cff2866f481875\", \"4b331398\", datetime(2024, 3, 19), 0.1810, 0.5678, 0.7276, 0.8805, 0.2958, \"4b331398\"),\n", " (\"faba9890d5d07d9d8720464547602df1ff73124b\", \"faba9890\", datetime(2024, 3, 26), 0.1839, 0.5771, 0.7172, 0.8974, 0.2981, \"faba9890\"),\n", " (\"3fba47f290480d1795f2d9f813179bc61844778b\", \"3fba47f2\", datetime(2024, 4, 5), 0.1963, 0.5967, 0.8054, 0.9267, 0.2930, \"3fba47f2\"),\n", " (\"abc785646aff61557c7ed4c8850ea1117c65e8ea\", \"abc78564\", datetime(2024, 4, 5), 0.1878, 0.5878, 0.7231, 0.8906, 0.2978, \"abc78564\"),\n", "]\n", "\n", "df = pd.DataFrame(data=data, columns=columns)\n", "data_source = ColumnDataSource(df)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [ "hide-input" ] }, "outputs": [ { "data": { "text/html": [ "\n", "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": "(function(root) {\n function embed_document(root) {\n const docs_json = {\"67a0ecb7-543b-49ba-8f05-80e943eae498\":{\"version\":\"3.6.3\",\"title\":\"Bokeh Application\",\"roots\":[{\"type\":\"object\",\"name\":\"Figure\",\"id\":\"p1005\",\"attributes\":{\"height\":450,\"x_range\":{\"type\":\"object\",\"name\":\"DataRange1d\",\"id\":\"p1006\"},\"y_range\":{\"type\":\"object\",\"name\":\"DataRange1d\",\"id\":\"p1007\"},\"x_scale\":{\"type\":\"object\",\"name\":\"LinearScale\",\"id\":\"p1015\"},\"y_scale\":{\"type\":\"object\",\"name\":\"LinearScale\",\"id\":\"p1016\"},\"title\":{\"type\":\"object\",\"name\":\"Title\",\"id\":\"p1008\",\"attributes\":{\"text\":\"5x5 Wind Farm Timing Test\"}},\"renderers\":[{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1059\",\"attributes\":{\"data_source\":{\"type\":\"object\",\"name\":\"ColumnDataSource\",\"id\":\"p1001\",\"attributes\":{\"selected\":{\"type\":\"object\",\"name\":\"Selection\",\"id\":\"p1002\",\"attributes\":{\"indices\":[],\"line_indices\":[]}},\"selection_policy\":{\"type\":\"object\",\"name\":\"UnionRenderers\",\"id\":\"p1003\"},\"data\":{\"type\":\"map\",\"entries\":[[\"index\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"AAAAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAGwAAABwAAAAdAAAAHgAAAB8AAAAgAAAAIQAAACIAAAAjAAAAJAAAACUAAAAmAAAAJwAAACgAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAvAAAAMAAAADEAAAAyAAAAMwAAADQAAAA1AAAANgAAADcAAAA4AAAAOQAAADoAAAA7AAAAPAAAAD0AAAA+AAAAPwAAAEAAAABBAAAAQgAAAEMAAABEAAAARQAAAEYAAABHAAAASAAAAEkAAABKAAAASwAAAEwAAABNAAAATgAAAE8AAABQAAAAUQAAAFIAAABTAAAAVAAAAFUAAABWAAAAVwAAAFgAAABZAAAAWgAAAFsAAABcAAAAXQAAAF4AAABfAAAA\"},\"shape\":[96],\"dtype\":\"int32\",\"order\":\"little\"}],[\"commit_hash\",{\"type\":\"ndarray\",\"array\":[\"df25a9cfacd3d652361d2bd37f568af00acb2631\",\"b797390a43298a815f3ff57955cfdc71ecf3e866\",\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\",\"dd847210082035d43b0273ae63a76a53cb8d2e12\",\"33779269e98cc882a5f066c462d8ec1eadf37a1a\",\"12890e029a7155b074b9b325d320d1798338e287\",\"66dafc08bd620d96deda7d526b0e4bfc3b086650\",\"a325819b3b03b84bd76ad455e3f9b4600744ba14\",\"8a2c1a610295c007f0222ce737723c341189811d\",\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\",\"03e1f461c152e4f221fe92c834f2787680cf5772\",\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\",\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\",\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\",\"d18f4d263ecabf502242592f9d60815a07c7b89c\",\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\",\"c2006b0011a5df036c306c15e75763ec492dafda\",\"0c2adf3e702b6427da946a6ba9dbedbea22738be\",\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\",\"8436fd78b002e5792f5d0dd1409332d171036d49\",\"07a45b66c5facfea06c40bd82e34040c97560640\",\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\",\"4d528a3d6456621a382d409b5145a877b5414b88\",\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\",\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\",\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\",\"26f06d449da208ce64724b1463b07ad20746cbdc\",\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\",\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\",\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\",\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\",\"16628a0ba45a675df762245694e0a7666a3478f8\",\"01684c8559604344bd09791268131819a09770a8\",\"e9231fb893c765b723fa4c1e087a58761b6aa471\",\"219889e243ffc69c71b6f7747f5af751d5694de1\",\"6124d2a82a7a823722210bc2e8516d355ba19eb3\",\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\",\"f2797fef396f2f19b02abb1f9555b678dac614f1\",\"b4e538f530048fec58eaca5170be82c67dbdcceb\",\"68820b715ed6b2c981aa11d29c0102e879280d79\",\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\",\"0d2bfecc271d561f67050659684b4797af8ee740\",\"1d03a465593f56c99a64a576d185d4ed17b659f2\",\"78a953b7ef9a36b62e5b446c80ed68abfddbfb74\",\"6c4f70ffbf3d4d2922d41d0032ae1b93d8a23c99\",\"ab03282623d0262b20b8c132efcdcace2dace766\",\"d2f7a45af27a6b40027d6f6a0f4f0be0c6dee5d9\",\"98b23f3d517481b127f190f5f8b7ebfae7f8b6b2\",\"452425de723cc1640d999022389672caf9bffbd0\",\"85dadb1a566c9fa8dc84cb9837b98bd5d23b8d58\",\"432ee7f96c1f6cccd05a0034c86c720cdb63a3e6\",\"ebd70ecaef14c0e239337eb6e36506303378a31a\",\"77fa7155d55bdf3fd43e29f58fe57feffcb107cf\",\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\",\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\",\"7c879f1ce18b52d9b0a8eecf877d03e66afc975b\",\"2aa9f2a55686f2ee5dc407e8e0223eb25176d906\",\"5e5bb7f4e653621e7a81ff4bcaa27dbc1f759de7\",\"d91953a499dfb88b457a1e7a07903debbda4058b\",\"76742879c81c9baced49b9fc60abbf1d2eba65ff\",\"9c73a41eaca95bb718ac79980a1799dfa1c48cf3\",\"67104dd714de939be136646af68edd9643ddfcd3\",\"e6906feebdee6bdd2103f0bd390679e6a1b0052d\",\"8908ab47eaa8a3d7e7c9126484b524f751e41f55\",\"063d8b58464f95520c9887ac4f575e6c1f6880d8\",\"59e53a66aef134a3c9e912f9468ca667b599d4e5\",\"cd14608474be8561c188d2aa7a772b8ac753fb70\",\"db958c4b779ffc825689e052958020864cbcde63\",\"8ece0f5f7d3bfd66f4f83198debf5627344af534\",\"77ea50d9bd5d01f7110dbebf1ba689a25eee9d96\",\"05b900c228d427bfa8e531527b546cdeb822cfc9\",\"2dccbbd0ca67a274a2aeb9996f262014b3137fc0\",\"e9c90aa521917e587dd9497d529822f359eec3e2\",\"6c3ddb48b59d286899a8efd5989d741f86c4ade3\",\"31fe1b69ff863f0a610aec5b22424382ec3cc933\",\"a4768d08e172e009efb7ccafb8dc37a90753df7f\",\"1a2e86847d7942d9ecac20d91d2f4cd73685b230\",\"bc4fd2812636baba036d6f5648c6e77bcfb263e4\",\"34a7e0cd84226520f5a39cf3345699f012aad505\",\"e1dd6b73ae1d9552e0fe9679c0d35199c113d647\",\"599f2266ad55f73f4afaf80d4d1e32523a256d3b\",\"52b8a5f595f565b149a227b26bea7786d742f15c\",\"6f2256dab34c9d9548bd6d4d81594605d75434bd\",\"b6de318f12c93872daec8762edecb66d311504b5\",\"94357caf7092f6bfb6d8b51b9daf1ae9b47f26dc\",\"64ae6789f1860734ab4d6d34cd7c4313a85274fc\",\"da61128340ac0e520b4583eb759bf771512332f6\",\"bbcb8c33f4f35036c8156c57cea60fea5c5e17af\",\"ebdf9a146359606dc5e13363beee6dd82ad1c247\",\"b88dc7bb15370da91956ca24db3875245dc073d2\",\"a88a15432ef01f7600e3ccd7b9d2b4ba89a01df0\",\"c7f8f36178f35ccd8e08e0ce10a77fc487f78a14\",\"4b331398bc4a08edcd32539157cff2866f481875\",\"faba9890d5d07d9d8720464547602df1ff73124b\",\"3fba47f290480d1795f2d9f813179bc61844778b\",\"abc785646aff61557c7ed4c8850ea1117c65e8ea\"],\"shape\":[96],\"dtype\":\"object\",\"order\":\"little\"}],[\"commit_hash_8char\",{\"type\":\"ndarray\",\"array\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"03e1f461\",\"9e96d6c4\",\"2a98428f\",\"9b4e85cf\",\"d18f4d26\",\"a23241bb\",\"c2006b00\",\"0c2adf3e\",\"39c46600\",\"8436fd78\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"16628a0b\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\",\"78a953b7\",\"6c4f70ff\",\"ab032826\",\"d2f7a45a\",\"98b23f3d\",\"452425de\",\"85dadb1a\",\"432ee7f9\",\"ebd70eca\",\"77fa7155\",\"d5d4b134\",\"d5d4b134\",\"7c879f1c\",\"2aa9f2a5\",\"5e5bb7f4\",\"d91953a4\",\"76742879\",\"9c73a41e\",\"67104dd7\",\"e6906fee\",\"8908ab47\",\"063d8b58\",\"59e53a66\",\"cd146084\",\"db958c4b\",\"8ece0f5f\",\"77ea50d9\",\"05b900c2\",\"2dccbbd0\",\"e9c90aa5\",\"6c3ddb48\",\"31fe1b69\",\"a4768d08\",\"1a2e8684\",\"bc4fd281\",\"34a7e0cd\",\"e1dd6b73\",\"599f2266\",\"52b8a5f5\",\"6f2256da\",\"b6de318f\",\"94357caf\",\"64ae6789\",\"da611283\",\"bbcb8c33\",\"ebdf9a14\",\"b88dc7bb\",\"a88a1543\",\"c7f8f361\",\"4b331398\",\"faba9890\",\"3fba47f2\",\"abc78564\"],\"shape\":[96],\"dtype\":\"object\",\"order\":\"little\"}],[\"date\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"AACAyDfgd0IAAEDF0+F3QgAAACsm4ndCAACA9srid0IAAICNFOR3QgAAQPNm5HdCAAAAWbnkd0IAAMC+C+V3QgAAgCRe5XdCAACAJF7ld0IAAIC7p+Z3QgAAQCH65ndCAACA3Krvd0IAAABtLPR3QgAAQJ4j9XdCAAAAvML/d0IAAMBWixh4QgAAQIU5NHhCAAAAWyVJeEIAAAAn5WJ4QgAAACflYnhCAACAt2ZneEIAAEAduWd4QgAAQLQCaXhCAABAtAJpeEIAAMB/p2l4QgAAgHxDa3hCAACAfENreEIAAIB8Q2t4QgAAQOKVa3hCAABA4pVreEIAAEDilWt4QgAAwNvNbnhCAAAADcVveEIAAEA+vHB4QgAAAGnrdHhCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAMDvQH54QgAAwO9AfnhCAABAu+V+eEIAAEC75X54QgAAQLvlfnhCAABAu+V+eEIAAAAhOH94QgAAQFIvgHhCAABAUi+AeEIAAAC4gYB4QgAAwB3UgHhCAADAtB2CeEIAAMC0HYJ4QgAAwLQdgnhCAADAtB2CeEIAAMAQRId4QgAAwMiQkXhCAAAA+oeSeEIAAAD6h5J4QgAAwF/aknhCAAAAkdGTeEIAAMD2I5R4QgAAwFJKmXhCAAAAG4ubeEIAAADgZ594QgAAAOBnn3hCAABAmxioeEIAAIC/f694QgAAgBumtHhCAAAAfpS2eEIAAAB+lLZ4QgAAAH6UtnhCAADAqMO6eEIAAABxBL14QgAAAHEEvXhCAADAmzPBeEIAAEBn2MF4QgAAAM0qwnhCAADAycbDeEIAAMDJxsN4QgAAwMnGw3hCAACALxnEeEIAAECVa8R4QgAAAJIHxnhCAAAAkgfGeEIAAACSB8Z4QgAAgFAczXhCAAAA1A3YeEIAAMD+PNx4QgAAwB9A5XhCAAAA6IDneEIAAIDhuOp4QgAAgOG46nhC\"},\"shape\":[96],\"dtype\":\"float64\",\"order\":\"little\"}],[\"jensen\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"WKg1zTtO9D/vOEVHcvnlP+SDns2qz80/HVpkO99PzT9xPQrXo3DNP0+vlGWIY80/0ZFc/kP6zT+MSuoENBHOP7AD54wo7c0/3gIJih9jzj/5D+m3rwPPP13cRgN4C9A/gy9MpgpGxT8X2c73U+PFP+2ePCzUmsY/3NeBc0aUxj+TOgFNhA3HPwCRfvs6cMY/ArwFEhQ/xj/y0k1iEFjJP4/k8h/Sb8c/l5APejarxj/+ZffkYaHGP07RkVz+Q8o/eAskKH6MyT9L6gQ0ETbMP52AJsKGp8c/1JrmHafoyD/8GHPXEvLJPwtGJXUCmsg/VFInoImwyT81XrpJDALLP0ymCkYldco/SnuDL0ymyj+3Yn/ZPXnIP7N78rBQa8o/w9MrZRniyD8Ab4EExY/JP3qlLEMc68o/GCZTBaOSyj8gQfFjzF3LP/AWSFD8GMs/ke18PzVeyj8tsp3vp8bLPyJseHqlLMs/jLlrCfmgxz8bDeAtkKDIP6JFtvP91Mg/j8L1KFyPyj9HcvkP6bfHP/fkYaHWNMc/gEi/fR04xz9UdCSX/5DGP4Lix5i7lsg/+zpwzojSxj+I9NvXgXPGP/OOU3Qkl8c/Lv8h/fZ1yD/SAN4CCYrHP6HWNO84Rcc/YOXQItv5xj/Oqs/VVuzHP+/Jw0KtacY/ih9j7lpCxj+qglFJnYDGP+m3rwPnjMg/gnNGlPYGxz+0WfW52orFPynLEMe6uMU/8WPMXUvIxz9rK/aX3ZPHPwYSFD/G3MU/UkmdgCbCxj/D9Shcj8LFP/vL7snDQsU/FK5H4XoUxj/c14FzRpTGP67YX3ZPHsY/KcsQx7q4xT/ByqFFtvPFPw+cM6K0N8g/zTtO0ZFcxj+Cc0aU9gbHP7Kd76fGS8c/RwN4CyQoxj9LWYY41sXFP7AD54wo7cU/Vp+rrdhfxj9z1xLyQc/GPznWxW00gMc/C0YldQKayD+TOgFNhA3HP166SQwCK8c/0gDeAgmKxz/jNhrAWyDJP5kqGJXUCcg/\"},\"shape\":[96],\"dtype\":\"float64\",\"order\":\"little\"}],[\"gauss\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"GJXUCWgi9D+Hp1fKMsTzP8x/SL99HeI/z/dT46Wb4j9OYhBYObTiP+kmMQisHOI/j1N0JJf/4j/129eBc0biPz2bVZ+rreI/u7iNBvAW4j85tMh2vp/iP0cDeAskKOI/W7G/7J484j9/2T15WKjjP90kBoGVQ+M/EHo2qz5X4z+fq63YX3bjP5tVn6ut2OM/2c73U+Ol4z8wKqkT0ETkPwaBlUOLbOM/zqrP1Vbs4z8qOpLLf0jjP13cRgN4C+Q/gQTFjzF34z93LSEf9GzkP3rHKTqSy+M/WvW52or94z9O0ZFc/kPkPxiV1AloIuQ/QfFjzF1L5D/l0CLb+X7kPzAqqRPQROQ/gSbChqdX5D/xY8xdS8jjP0Otad5xiuQ/K/aX3ZOH4z+BJsKGp1fkP7pJDAIrh+Q/2ht8YTJV5D9Ke4MvTKbkPwFNhA1Pr+Q/SL99HThn5D95WKg1zTvkP6qCUUmdgOQ/CD2bVZ+r4z/zH9JvXwfkP4enV8oyxOM/g1FJnYAm5D8LJCh+jLnjP9k9eVioNeM/zF1LyAc94z8bL90kBoHjP9lfdk8eFuQ/HOviNhrA4z8U0ETY8PTiP34dOGdEaeM/MZkqGJXU4z86kst/SL/jP0Ck374OnOM/6gQ0ETY84z8f9GxWfa7iP+XQItv5fuI/3+ALk6mC4T/uWkI+6NniP+CcEaW9weM/jNtoAG+B4j88vVKWIY7hPzarPldbseE/dEaU9gZf4j9a9bnaiv3hP2Kh1jTvOOE/mEwVjErq4j+sHFpkO9/hP/5D+u3rwOE/TKYKRiV14j8H8BZIUPziP1Z9rrZif+E/0NVW7C+74T8wKqkT0ETiP2Dl0CLb+eI/BTQRNjy94j8/xty1hHziPwXFjzF3LeM/PE7RkVz+4T8AAAAAAADiP5Jc/kP67eE/Lv8h/fZ14j9a9bnaiv3hP1uxv+yePOI/9ihcj8L14j97FK5H4XriP89m1edqK+I/8rBQa5p34j8OvjCZKhjjP3PXEvJBz+I/\"},\"shape\":[96],\"dtype\":\"float64\",\"order\":\"little\"}],[\"gch\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"X5hMFYxK+j9IUPwYc9f8P02EDU+vlO8/yjLEsS5u7z9YyjLEsS7wP1jKMsSxLvA/ak3zjlN08D/Mf0i/fR3wP94CCYofY/A/lPYGX5hM7z9/2T15WKjwP2iz6nO1FfA/xY8xdy0h8D9xrIvbaADxP0I+6Nms+vA/seHplbIM8T9JLv8h/fbwP55eKcsQx/A/63O1FfvL8D+mCkYldQLxP+XyH9JvX/A/6Gor9pfd8D+gibDh6ZXwPz81XrpJDPE/WDm0yHa+8D+IY13cRgPxPw4tsp3vp/A/q8/VVuwv8T9CPujZrPrwPyfChqdXyvA/0ZFc/kP68D/XNO84RUfxPyV1ApoIG/E/fGEyVTAq8T97gy9MpgrxP/rt68A5I/E/I9v5fmq88D8ofoy5awnxP9lfdk8eFvE/AAAAAAAA8T/nHafoSC7xP5HtfD81XvE/1lbsL7sn8T8yVTAqqRPxP9JvXwfOGfE/K4cW2c738D/Xo3A9CtfwP2b35GGh1vA/t9EA3gIJ8T/AWyBB8WPwP+XQItv5fvA/hslUwaik8D/kg57Nqs/wP36MuWsJ+fA/UkmdgCbC8D9tVn2utmLwP9nO91PjpfA/rkfhehSu8D8T8kHPZtXwP/s6cM6I0vA/8KfGSzeJ8D+0WfW52orpP6AaL90kBuk/ldQJaCJs5j8KaCJseHrnP5SHhVrTvPA/Dk+vlGWI6T/LEMe6uI3mP6qCUUmdgOY/wTkjSnuD6T+Cc0aU9gbnP2HD0ytlGeY//kP67evA5z90RpT2Bl/mP9Ei2/l+aug/O99PjZdu6D8bL90kBoHpPwrXo3A9Cuc/foy5awn55j+WIY51cRvpP/fkYaHWNOk/hxbZzvdT5z8ydy0hH/ToP/5D+u3rwOk/irDh6ZWy5j80orQ3+MLmP2WqYFRSJ+Y/X5hMFYxK5j/J5T+k377mP5M6AU2EDec/wOyePCzU6D8SFD/G3LXmPyo6kst/SOc/Tx4Wak3z5j9LWYY41sXpP2uad5yiI+c/\"},\"shape\":[96],\"dtype\":\"float64\",\"order\":\"little\"}],[\"cc\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8LRiV1AprzPz9XW7G/7PI/seHplbIM8z/x9EpZhjjzPxB6Nqs+V/M/ylTBqKRO8z89CtejcD3zP2uad5yiI/M/XwfOGVHa8j8tQxzr4jbzPzojSnuDL/M/odY07zhF8z+n6Egu/yHzP7N78rBQa/M/dCSX/5B+8z9F2PD0SlnzP8NkqmBUUvM/mpmZmZmZ8z/7y+7Jw0LzP2ZmZmZmZvM/QfFjzF1L8z/NO07RkVzzP6MBvAUSFPM/0gDeAgmK8z/GbTSAt0DzP50Rpb3BF/M/ayv2l92T8z+6awn5oGfzP7hAguLHmPM/JlMFo5I68z+P5PIf0m/zP0Otad5xivM/KxiV1Alo8z9tVn2utmLzPwN4CyQofvM/ApoIG55e8z9GJXUCmgjzPxueXinLEPM/taZ5xyk68z+MSuoENBHzP5wzorQ3+PI/UrgehetR8z8hH/RsVn3zPyBB8WPMXfM/p+hILv8h8z8wTKYKRiXzP+cdp+hILvM/WRe30QDe8j9uowG8BRLzP76fGi/dJPM/rIvbaABv8z/aG3xhMlXuPwK8BRIUP+4/Di2yne+n7D8PnDOitDfsP03zjlN0JPM/2PD0SlmG7j/PZtXnaivsP4/C9Shcj+w/guLHmLuW7j/zH9JvXwfsP2fV52or9us/rBxaZDvf6z95WKg1zTvsP8bctYR80O0/ArwFEhQ/7D+rPldbsb/sP1YOLbKd7+s/BaOSOgFN7D/Kw0Ktad7tPzMzMzMzM+0/Gw3gLZCg7D9MpgpGJXXuP+F6FK5H4e4/dEaU9gZf7D9dbcX+snvsP6rx0k1iEOw/Io51cRsN7D9qvHSTGATsP4j029eBc+w/2PD0SlmG7D9qvHSTGATsP5MYBFYOLew/1sVtNIC37D+dgCbChqftP8cpOpLLf+w/\"},\"shape\":[96],\"dtype\":\"float64\",\"order\":\"little\"}],[\"emgauss\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh//yH99nXg1D+FfNCzWfXVP0+vlGWIY9U/w9MrZRni1D/G3LWEfNDTP8PTK2UZ4tQ/sAPnjCjt1T9d/kP67evUPz7o2az6XNU/B84ZUdob1D92cRsN4C3UP26jAbwFEtQ/ZohjXdxG0z+4QILix5jTP28Sg8DKodU/Ns07TtGR1D+28/3UeOnSP8sQx7q4jdI/ldQJaCJs1D8E54wo7Q3SP7FQa5p3nNI/X5hMFYxK0j+Zu5aQD3rSP3Gsi9toANM/iUFg5dAi0z++wRcmUwXTP9UJaCJseNI/yeU/pN++0j9Wn6ut2F/SPwU0ETY8vdI/i2zn+6nx0j92Tx4Wak3TP+m3rwPnjNQ/9GxWfa620j9DrWnecYrSP8RCrWnecdI/kxgEVg4t0j+6SQwCK4fSPz9XW7G/7NI/63O1FfvL0j/tnjws1JrSPwMJih9j7tI/owG8BRIU0z+Nl24Sg8DSP1fsL7snD9M/\"},\"shape\":[96],\"dtype\":\"float64\",\"order\":\"little\"}],[\"tooltip_label\",{\"type\":\"ndarray\",\"array\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"PR #56\",\"v3.0rc1\",\"PR #317\",\"v3.0\",\"v3.0.1\",\"v3.1\",\"v3.1.1\",\"v3.2\",\"v3.2.1\",\"v3.2.2\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"v3.3\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\",\"78a953b7\",\"6c4f70ff\",\"ab032826\",\"d2f7a45a\",\"98b23f3d\",\"452425de\",\"85dadb1a\",\"432ee7f9\",\"ebd70eca\",\"77fa7155\",\"d5d4b134\",\"d5d4b134\",\"7c879f1c\",\"2aa9f2a5\",\"v3.4\",\"d91953a4\",\"76742879\",\"9c73a41e\",\"67104dd7\",\"e6906fee\",\"8908ab47\",\"063d8b58\",\"59e53a66\",\"cd146084\",\"db958c4b\",\"8ece0f5f\",\"77ea50d9\",\"05b900c2\",\"2dccbbd0\",\"e9c90aa5\",\"6c3ddb48\",\"v3.5\",\"a4768d08\",\"1a2e8684\",\"bc4fd281\",\"34a7e0cd\",\"e1dd6b73\",\"599f2266\",\"52b8a5f5\",\"6f2256da\",\"b6de318f\",\"94357caf\",\"64ae6789\",\"da611283\",\"bbcb8c33\",\"ebdf9a14\",\"b88dc7bb\",\"a88a1543\",\"c7f8f361\",\"4b331398\",\"faba9890\",\"3fba47f2\",\"abc78564\"],\"shape\":[96],\"dtype\":\"object\",\"order\":\"little\"}]]}}},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1060\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1061\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1056\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"jensen\"},\"line_color\":\"blue\"}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1057\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"jensen\"},\"line_color\":\"blue\",\"line_alpha\":0.1}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1058\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"jensen\"},\"line_color\":\"blue\",\"line_alpha\":0.2}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1070\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1071\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1072\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1067\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"jensen\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"blue\"},\"fill_color\":{\"type\":\"value\",\"value\":\"blue\"}}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1068\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"jensen\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"blue\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.1},\"fill_color\":{\"type\":\"value\",\"value\":\"blue\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.1},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.1}}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1069\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"jensen\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"blue\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.2},\"fill_color\":{\"type\":\"value\",\"value\":\"blue\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.2},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.2}}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1079\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1080\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1081\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1076\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gauss\"},\"line_color\":\"green\"}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1077\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gauss\"},\"line_color\":\"green\",\"line_alpha\":0.1}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1078\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gauss\"},\"line_color\":\"green\",\"line_alpha\":0.2}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1089\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1090\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1091\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1086\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gauss\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"green\"},\"fill_color\":{\"type\":\"value\",\"value\":\"green\"}}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1087\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gauss\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"green\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.1},\"fill_color\":{\"type\":\"value\",\"value\":\"green\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.1},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.1}}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1088\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gauss\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"green\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.2},\"fill_color\":{\"type\":\"value\",\"value\":\"green\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.2},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.2}}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1098\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1099\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1100\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1095\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gch\"},\"line_color\":\"red\"}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1096\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gch\"},\"line_color\":\"red\",\"line_alpha\":0.1}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1097\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gch\"},\"line_color\":\"red\",\"line_alpha\":0.2}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1108\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1109\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1110\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1105\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gch\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"red\"},\"fill_color\":{\"type\":\"value\",\"value\":\"red\"}}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1106\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gch\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"red\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.1},\"fill_color\":{\"type\":\"value\",\"value\":\"red\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.1},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.1}}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1107\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"gch\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"red\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.2},\"fill_color\":{\"type\":\"value\",\"value\":\"red\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.2},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.2}}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1117\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1118\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1119\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1114\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"cc\"},\"line_color\":\"cyan\"}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1115\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"cc\"},\"line_color\":\"cyan\",\"line_alpha\":0.1}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1116\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"cc\"},\"line_color\":\"cyan\",\"line_alpha\":0.2}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1127\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1128\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1129\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1124\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"cc\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"cyan\"},\"fill_color\":{\"type\":\"value\",\"value\":\"cyan\"}}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1125\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"cc\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"cyan\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.1},\"fill_color\":{\"type\":\"value\",\"value\":\"cyan\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.1},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.1}}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1126\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"cc\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"cyan\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.2},\"fill_color\":{\"type\":\"value\",\"value\":\"cyan\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.2},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.2}}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1136\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1137\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1138\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1133\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"emgauss\"},\"line_color\":\"magenta\"}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1134\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"emgauss\"},\"line_color\":\"magenta\",\"line_alpha\":0.1}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1135\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"emgauss\"},\"line_color\":\"magenta\",\"line_alpha\":0.2}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1145\",\"attributes\":{\"data_source\":{\"id\":\"p1001\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1146\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1147\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1142\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"emgauss\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"magenta\"},\"fill_color\":{\"type\":\"value\",\"value\":\"magenta\"}}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1143\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"emgauss\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"magenta\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.1},\"fill_color\":{\"type\":\"value\",\"value\":\"magenta\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.1},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.1}}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Scatter\",\"id\":\"p1144\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"date\"},\"y\":{\"type\":\"field\",\"field\":\"emgauss\"},\"size\":{\"type\":\"value\",\"value\":6},\"line_color\":{\"type\":\"value\",\"value\":\"magenta\"},\"line_alpha\":{\"type\":\"value\",\"value\":0.2},\"fill_color\":{\"type\":\"value\",\"value\":\"magenta\"},\"fill_alpha\":{\"type\":\"value\",\"value\":0.2},\"hatch_alpha\":{\"type\":\"value\",\"value\":0.2}}}}}],\"toolbar\":{\"type\":\"object\",\"name\":\"Toolbar\",\"id\":\"p1014\",\"attributes\":{\"tools\":[{\"type\":\"object\",\"name\":\"PanTool\",\"id\":\"p1039\"},{\"type\":\"object\",\"name\":\"WheelZoomTool\",\"id\":\"p1040\",\"attributes\":{\"renderers\":\"auto\"}},{\"type\":\"object\",\"name\":\"BoxZoomTool\",\"id\":\"p1041\",\"attributes\":{\"overlay\":{\"type\":\"object\",\"name\":\"BoxAnnotation\",\"id\":\"p1042\",\"attributes\":{\"syncable\":false,\"line_color\":\"black\",\"line_alpha\":1.0,\"line_width\":2,\"line_dash\":[4,4],\"fill_color\":\"lightgrey\",\"fill_alpha\":0.5,\"level\":\"overlay\",\"visible\":false,\"left\":{\"type\":\"number\",\"value\":\"nan\"},\"right\":{\"type\":\"number\",\"value\":\"nan\"},\"top\":{\"type\":\"number\",\"value\":\"nan\"},\"bottom\":{\"type\":\"number\",\"value\":\"nan\"},\"left_units\":\"canvas\",\"right_units\":\"canvas\",\"top_units\":\"canvas\",\"bottom_units\":\"canvas\",\"handles\":{\"type\":\"object\",\"name\":\"BoxInteractionHandles\",\"id\":\"p1048\",\"attributes\":{\"all\":{\"type\":\"object\",\"name\":\"AreaVisuals\",\"id\":\"p1047\",\"attributes\":{\"fill_color\":\"white\",\"hover_fill_color\":\"lightgray\"}}}}}}}},{\"type\":\"object\",\"name\":\"SaveTool\",\"id\":\"p1049\"},{\"type\":\"object\",\"name\":\"ResetTool\",\"id\":\"p1050\"},{\"type\":\"object\",\"name\":\"HelpTool\",\"id\":\"p1051\"},{\"type\":\"object\",\"name\":\"HoverTool\",\"id\":\"p1052\",\"attributes\":{\"renderers\":\"auto\",\"tooltips\":[[\"git ref\",\"@tooltip_label\"]]}}]}},\"left\":[{\"type\":\"object\",\"name\":\"LinearAxis\",\"id\":\"p1034\",\"attributes\":{\"ticker\":{\"type\":\"object\",\"name\":\"BasicTicker\",\"id\":\"p1035\",\"attributes\":{\"mantissas\":[1,2,5]}},\"formatter\":{\"type\":\"object\",\"name\":\"BasicTickFormatter\",\"id\":\"p1036\"},\"axis_label\":\"Time to solution (s)\",\"major_label_policy\":{\"type\":\"object\",\"name\":\"AllLabels\",\"id\":\"p1037\"}}}],\"below\":[{\"type\":\"object\",\"name\":\"DatetimeAxis\",\"id\":\"p1017\",\"attributes\":{\"ticker\":{\"type\":\"object\",\"name\":\"DatetimeTicker\",\"id\":\"p1018\",\"attributes\":{\"num_minor_ticks\":5,\"tickers\":[{\"type\":\"object\",\"name\":\"AdaptiveTicker\",\"id\":\"p1019\",\"attributes\":{\"num_minor_ticks\":0,\"mantissas\":[1,2,5],\"max_interval\":500.0}},{\"type\":\"object\",\"name\":\"AdaptiveTicker\",\"id\":\"p1020\",\"attributes\":{\"num_minor_ticks\":0,\"base\":60,\"mantissas\":[1,2,5,10,15,20,30],\"min_interval\":1000.0,\"max_interval\":1800000.0}},{\"type\":\"object\",\"name\":\"AdaptiveTicker\",\"id\":\"p1021\",\"attributes\":{\"num_minor_ticks\":0,\"base\":24,\"mantissas\":[1,2,4,6,8,12],\"min_interval\":3600000.0,\"max_interval\":43200000.0}},{\"type\":\"object\",\"name\":\"DaysTicker\",\"id\":\"p1022\",\"attributes\":{\"days\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]}},{\"type\":\"object\",\"name\":\"DaysTicker\",\"id\":\"p1023\",\"attributes\":{\"days\":[1,4,7,10,13,16,19,22,25,28]}},{\"type\":\"object\",\"name\":\"DaysTicker\",\"id\":\"p1024\",\"attributes\":{\"days\":[1,8,15,22]}},{\"type\":\"object\",\"name\":\"DaysTicker\",\"id\":\"p1025\",\"attributes\":{\"days\":[1,15]}},{\"type\":\"object\",\"name\":\"MonthsTicker\",\"id\":\"p1026\",\"attributes\":{\"months\":[0,1,2,3,4,5,6,7,8,9,10,11]}},{\"type\":\"object\",\"name\":\"MonthsTicker\",\"id\":\"p1027\",\"attributes\":{\"months\":[0,2,4,6,8,10]}},{\"type\":\"object\",\"name\":\"MonthsTicker\",\"id\":\"p1028\",\"attributes\":{\"months\":[0,4,8]}},{\"type\":\"object\",\"name\":\"MonthsTicker\",\"id\":\"p1029\",\"attributes\":{\"months\":[0,6]}},{\"type\":\"object\",\"name\":\"YearsTicker\",\"id\":\"p1030\"}]}},\"formatter\":{\"type\":\"object\",\"name\":\"DatetimeTickFormatter\",\"id\":\"p1031\"},\"axis_label\":\"Commit date\",\"major_label_policy\":{\"type\":\"object\",\"name\":\"AllLabels\",\"id\":\"p1032\"}}}],\"center\":[{\"type\":\"object\",\"name\":\"Grid\",\"id\":\"p1033\",\"attributes\":{\"axis\":{\"id\":\"p1017\"}}},{\"type\":\"object\",\"name\":\"Grid\",\"id\":\"p1038\",\"attributes\":{\"dimension\":1,\"axis\":{\"id\":\"p1034\"}}},{\"type\":\"object\",\"name\":\"Legend\",\"id\":\"p1062\",\"attributes\":{\"location\":\"bottom_left\",\"border_line_color\":\"black\",\"click_policy\":\"mute\",\"items\":[{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1063\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"jensen\"},\"renderers\":[{\"id\":\"p1059\"},{\"id\":\"p1070\"}]}},{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1082\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"gauss\"},\"renderers\":[{\"id\":\"p1079\"},{\"id\":\"p1089\"}]}},{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1101\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"gch\"},\"renderers\":[{\"id\":\"p1098\"},{\"id\":\"p1108\"}]}},{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1120\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"cc\"},\"renderers\":[{\"id\":\"p1117\"},{\"id\":\"p1127\"},{\"id\":\"p1136\"}]}},{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1148\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"empirical gauss\"},\"renderers\":[{\"id\":\"p1145\"}]}}]}}]}}]}};\n const render_items = [{\"docid\":\"67a0ecb7-543b-49ba-8f05-80e943eae498\",\"roots\":{\"p1005\":\"c5f00d44-6823-4878-a9b4-0b348e9bde1f\"},\"root_ids\":[\"p1005\"]}];\n void root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n }\n if (root.Bokeh !== undefined) {\n embed_document(root);\n } else {\n let attempts = 0;\n const timer = setInterval(function(root) {\n if (root.Bokeh !== undefined) {\n clearInterval(timer);\n embed_document(root);\n } else {\n attempts++;\n if (attempts > 100) {\n clearInterval(timer);\n console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n }\n }\n }, 10, root)\n }\n})(window);", "application/vnd.bokehjs_exec.v0+json": "" }, "metadata": { "application/vnd.bokehjs_exec.v0+json": { "id": "p1005" } }, "output_type": "display_data" } ], "source": [ "## Run-time performance\n", "\n", "hover_tool = HoverTool(\n", " tooltips=[\n", " # (\"index\", \"$index\"),\n", " (\"git ref\", \"@tooltip_label\"),\n", " # (\"date\", \"@date\"),\n", " ],\n", " # formatters={\n", " # '@date': 'datetime',\n", " # },\n", ")\n", "p = figure(\n", " title=\"5x5 Wind Farm Timing Test\",\n", " x_axis_type=\"datetime\",\n", " tooltips=hover_tool.tooltips,\n", " width=600,\n", " height=450\n", ")\n", "\n", "p.line(\"date\", \"jensen\", source=data_source, color=COLORS[0], legend_label=\"jensen\")\n", "p.scatter(\"date\", \"jensen\", source=data_source, line_color=COLORS[0], fill_color=COLORS[0], size=6, legend_label=\"jensen\")\n", "p.line(\"date\", \"gauss\", source=data_source, color=COLORS[1], legend_label=\"gauss\")\n", "p.scatter(\"date\", \"gauss\", source=data_source, line_color=COLORS[1], fill_color=COLORS[1], size=6, legend_label=\"gauss\")\n", "p.line(\"date\", \"gch\", source=data_source, color=COLORS[2], legend_label=\"gch\")\n", "p.scatter(\"date\", \"gch\", source=data_source, line_color=COLORS[2], fill_color=COLORS[2], size=6, legend_label=\"gch\")\n", "p.line(\"date\", \"cc\", source=data_source, color=COLORS[3], legend_label=\"cc\")\n", "p.scatter(\"date\", \"cc\", source=data_source, line_color=COLORS[3], fill_color=COLORS[3], size=6, legend_label=\"cc\")\n", "p.line(\"date\", \"emgauss\", source=data_source, color=COLORS[4], legend_label=\"cc\")\n", "p.scatter(\"date\", \"emgauss\", source=data_source, line_color=COLORS[4], fill_color=COLORS[4], size=6, legend_label=\"empirical gauss\")\n", "\n", "p.xaxis.axis_label = \"Commit date\"\n", "p.yaxis.axis_label = \"Time to solution (s)\"\n", "\n", "p.legend.location = \"bottom_left\"\n", "p.legend.click_policy=\"mute\"\n", "p.legend.border_line_width = 1\n", "p.legend.border_line_color = \"black\"\n", "p.legend.border_line_alpha = 0.5\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: docs/dev_guide.md ================================================ # Developer's Guide FLORIS is maintained by NLR. We are excited about community contribution, and this page outlines processes and procedures to follow when contributing to the source code. For technical questions regarding FLORIS usage, please post your questions to [GitHub Discussions](https://github.com/NatLabRockies/floris/discussions). ## Getting Started There are a few steps that nearly all contributors will need to go through to get started with making contributions to FLORIS. Each of these steps will be addressed in a later section of the developer's guide, so please read on to learn more about each of these steps. 1. Create a fork of FLORIS on GitHub 2. Clone the repository ```bash git clone -b develop https://github.com//floris.git ``` 3. Move into the FLORIS source code directory ```bash cd floris/ ``` 4. Install FLORIS in editable mode with the appropriate developer tools - ``".[develop]"`` is for the linting and code checking tools - ``".[docs]"`` is for the documentation building tools. Ideally, developers should also be contributing to the documentation, and therefore checking that the documentation builds locally. ```bash pip install -e ".[develop, docs]" ``` 5. Turn on the linting and code checking tools ```bash pre-commit install ``` ## Git and GitHub Workflows The majority of the collaboration and development for FLORIS takes place in the [GitHub repository](http://github.com/NatLabRockies/floris). There, [issues](http://github.com/NatLabRockies/floris/issues) and [pull requests](http://github.com/NatLabRockies/floris/pulls) are managed, questions and ideas are [discussed](https://github.com/NatLabRockies/floris/discussions), and [new versions](http://github.com/NatLabRockies/floris/releases) are released. It is the best mechanism for engaging with the NLR team and other developers throughout the FLORIS community. FLORIS development should follow "Git Flow" when interacting with the GitHub repository. Git Flow is a git workflow outlining safe methods of pushing and pulling commits to a shared repository. Maintaining this workflow is critical to prevent remote changes from blocking your local development. The Git Flow process is detailed nicely [here](http://nvie.com/posts/a-successful-git-branching-model). ### Syncing a local repository with NREL/FLORIS The "main" FLORIS repository is continuously updated along with ongoing research at NLR. From time to time, developers of FLORIS using their own "local" repositories (versions of the software that exist on a local computer) may want to sync with NREL/FLORIS. To do this, use the following git commands: ```bash # Move into the FLORIS source code directory; # this may be named differently on your computer. cd floris/ # Find the remote name that corresponds to # NREL/FLORIS; usually "origin" or "upstream". git remote -v # Fetch the changes on all remotes. git fetch --all # Decide which branch to sync with # NREL/FLORIS. Generally, this will be "main". git checkout main git pull origin main # Update any local working branches with the # latest from NREL/FLORIS. git checkout feature/working_branch git merge main ``` Note that the example above is a general case and may need to be modified to fit a specific use case or purpose. If significant development has happened locally, then [merge conflicts](https://www.atlassian.com/git/tutorials/using-branches/merge-conflicts) are likely and should be resolved as early as possible. ## Code quality tools FLORIS is configured to use tools to automatically check and enforce aspects of code quality. In general, these should be adopted by all developers and incorporated into the development workflow. Most tools are configured in [pyproject.toml](https://github.com/NatLabRockies/floris/blob/main/pyproject.toml), but some may have a dedicated configuration file. ### isort Import lines can easily get out of hand and cause unnecessary distraction in source code files. [isort](https://pycqa.github.io/isort/index.html) is used to automatically manage imports in the source code. It can be run directly with the following command: ```bash isort isort dir/* ``` This tool was initially configured in [PR#535](https://github.com/NatLabRockies/floris/pull/535), and additional information on specific decisions can be found there. ### Ruff [Ruff](https://github.com/charliermarsh/ruff) is a general linter and limited auto-formatter. It is configured in `pyproject.toml` through various `[tool.ruff.*]` blocks. It is a command line tool and integrations into popular IDE's are available. A typical command to run Ruff for all of FLORIS is the following: ```bash ruff . --fix ``` This sets the configuration from `pyproject.toml`, applies the selected rules to Python files, and fixes errors in-place where possible. Ruff was initially configured in [PR#562](https://github.com/NatLabRockies/floris/pull/562), and discussed in more detail in [D#561](https://github.com/NatLabRockies/floris/discussions/561). See the Ruff documentation for a list of [supported rules](https://github.com/charliermarsh/ruff#supported-rules) and [available options for various rules](https://github.com/charliermarsh/ruff#reference). ### Pre-commit [Pre-commit](https://pre-commit.com) is a utility to execute a series of git-hooks to catch and fix formatting errors. It integrates easily with other tools, in our case isort and Ruff. Pre-commit is tightly integrated into git and is mostly not used directly by users. Once installed, the precommit hooks must be installed into each development environment with the following command from the `floris/` directory: ```bash pre-commit install ``` Then, each commit creation with `git` will run the installed hooks and display where checks have failed. Pre-commit will typically modify the files directly to fix the issues. However, each file fixed by Pre-commit must be added to the git-commit again to capture the changes. A typical workflow is given below. ```bash git add floris/simulation/turbine.py git commit -m "Update so and so" > [WARNING] Unstaged files detected. > [INFO] Stashing unstaged files to /Users/rafmudaf/.cache/pre-commit/patch1675722485-25489. > isort....................................................................Failed > - hook id: isort > - files were modified by this hook > > Fixing /Users/rafmudaf/Development/floris/floris/simulation/turbine.py # Check that the error is fixed or fixed it manually git status # Stage the new changes and commit git add floris/simulation/turbine.py git commit -m "Update so and so" ``` ## Testing In order to maintain a level of confidence in the software, FLORIS is expected to maintain a reasonable level of test coverage. To that end, unit tests for a the low-level code in the `floris.core` package are included. The full testing suite can by executed by running the command ``pytest`` from the highest directory in the repository. A testing-only class is included to provide consistent and convenient inputs to modules at `floris/tests/conftest.py`. Unit tests are integrated into FLORIS with [pytest](https://docs.pytest.org/en/latest/), and they can be executed with the following command: ```bash cd floris/ pytest tests/*_unit_test.py ``` Regression tests are also included through [pytest](https://docs.pytest.org/en/latest/). Functionally, the only difference is that the regression tests take more time to execute and exercise a large portion of the software. These can be executed with the following command: ```bash cd floris/ pytest tests/*_regression_test.py ``` ### Continuous Integration Continuous integration is configured with [GitHub Actions](https://github.com/NatLabRockies/floris/actions) and executes all of the existing tests for every push-event. The configuration file is located at `floris/.github/workflows/continuous-integration-workflow.yaml`. ## Documentation The online documentation is built with Jupyter Book which uses Sphinx as a framework. It is automatically built and hosted by GitHub, but it can also be compiled locally. Additional dependencies are required for the documentation, and they are listed in the `project.optional-dependencies` of `pyproject.toml`. The commands to build the docs are given below. After successfully compiling, a file should be located at ``docs/_build/html/index.html``. This file can be opened in any browser. ```bash pip install -e ".[docs]" jupyter-book build docs/ # Lots of output to the terminal here... open docs/_build/html/index.html ``` ## Release guide Follow the process outlined here to "release" FLORIS. After completing these steps, a few additional automated processes are launched to deploy FLORIS to PyPI and conda-forge. Be sure to complete each step in the sequence as described. 1. Merge the `develop` branch into `main` with a pull request. Create a pull request titled `FLORIS vN.M` with version number filled in as appropriate. The body of the pull request should a brief summary of the changes as well as a listing of the major changes and their associated pull requests. Since creating the pull request does not mean it is merged, it is reasonable to create the pull request, and edit the body of the pull request later. The pull request template has a checklist at the bottom that should be uncommented for this PR. 2. Update the version number and commit to the `develop`` branch with a commit message such as "Update version to vN.M". The version number must be updated in the following two files: - [floris/README.md](https://github.com/NatLabRockies/floris/blob/main/README.md) - [pyproject.toml](https://github.com/NatLabRockies/floris/blob/main/pyproject.toml) Note that a `.0` version number is left off meaning that valid versions are `v3`, `v3.1`, `v3.1.1`, etc. 3. Verify that the documentation is building correctly. The docs build for every commit to `develop`, so there should be no surprises in this regard prior to a release. However, it's a good opportunity to ensure that the documentation is up to date and there are no obvious issues. Check this by opening the documentation website at https://natlabrockies.github.io/floris and scrolling through the pages. Also, verify that the automated build process has successfully completed for the commits to `develop` in [GitHub Actions](https://github.com/NatLabRockies/floris/actions/workflows/deploy-pages.yaml). 4. The changes since the prior commit can be gotten from GitHub by going through the process to create a release, but stopping short of actually publishing it. In this form, GitHub provides the option to autogenerate release notes. Be sure to choose the correct starting tag, and then hit "Generate release notes". Then, copy the generated text into the pull request body, and format it as appropriate. A good reference is typically the previous release. 5. Merge the pull request into `main`. Select "Create a merge commit" from the merge dropdown, and hit "Merge pull request". 6. Create a [new release](https://github.com/NatLabRockies/floris/releases/new) on GitHub with the title "vN.M". Choose to create a new tag on publish with the same name. Also, autogenerate the release notes again. If you autogenerated the release notes in step 4, make sure to start this step from a new browser window. Be sure that the "Set as latest release" radio button is enabled. 7. Double check everything. 8. Hit "Publish release". 9. Go to GitHub Actions and watch the [Upload Python Package](https://github.com/NatLabRockies/floris/actions/workflows/python-publish.yml) job complete. Upon success, FLORIS will be uploaded to PyPI for installation with pip. If it fails, the latest release will not be distributed. 10. Merge the main branch into develop to align all branches on all remotes. 11. That's it, well done! ## Deploying to pip Generally, only NLR developers will have appropriate permissions to deploy FLORIS updates. When the time comes, here is a great reference on doing it is available [here](https://medium.freecodecamp.org/how-to-publish-a-pyton-package-on-pypi-a89e9522ce24). ## Extending the models The FLORIS architecture is designed to support adding new wake models relatively easily. Each of the following components have a general API that support plugging in to the rest of the FLORIS framework: - Velocity deficit - Wake deflection - Added turbulence due to the turbine wake - Wake combination - Solver algorithm - Grid-points Initially, it's recommended to copy an existing model as a starting point, and the [Jensen](https://github.com/NatLabRockies/floris/blob/main/floris/simulation/wake_velocity/jensen.py) and [Jimenez](https://github.com/NatLabRockies/floris/blob/main/floris/simulation/wake_deflection/jimenez.py) models are good choices due to their simplicity. New models must be registered in [Wake.model_map](https://github.com/NatLabRockies/floris/blob/main/floris/simulation/wake.py#L45) so that they can be enabled via the input dictionary. ```{mermaid} classDiagram class Floris class Farm class FlowField { u: NDArrayFloat v: NDArrayFloat w: NDArrayFloat } class Grid { <> x: NDArrayFloat y: NDArrayFloat z: NDArrayFloat } class WakeModelManager { <> combination_model: BaseModel deflection_model: BaseModel velocity_model: BaseModel turbulence_model: BaseModel } class Solver { <> parameters: dict } class BaseModel { prepare_function() dict function() None } Floris *-- Farm Floris *-- FlowField Floris *-- Grid Floris *-- WakeModelManager Floris --> Solver Solver --> Farm Solver --> FlowField Solver --> Grid Solver --> WakeModelManager WakeModelManager -- BaseModel style Grid stroke:#FF496B, stroke-width:2px style WakeModelManager stroke:#FF496B, stroke-width:2px style Solver stroke:#FF496B, stroke-width:2px ``` All of the models have a `prepare_function` and a `function` method. The `prepare_function` allows the model classes to extract any information from the `Grid` and `FlowField` data structures, and this is generally used for sizing the data arrays. The `prepare_function` should return a dictionary that will ultimately be passed to the `function`. The `function` method is where the actual calculation is performed. The API is dependent on the type of model, but generally it requires some indicationg of the location of the current turbine in the solve step and some other information about the atmospheric conditions and operation of the turbine. Note the `*` in the function signature, which is a Python feature that allows any number of arguments to be passed to the function after the `*` as keyword arguments. Typically, these arguments are the ones returned from the `prepare_function`. ```python def prepare_function(self, grid: Grid, flow_field: FlowField) -> Dict[str, Any] def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, axial_induction_i: np.ndarray, deflection_field_i: np.ndarray, yaw_angle_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, hub_height_i: np.ndarray, rotor_diameter_i: np.ndarray, *, variables_from_prepare_function: dict ) -> None: ``` Some models require a special grid and/or solver, and that mapping happens in [floris.core.core.Core](https://github.com/NatLabRockies/floris/blob/main/floris/core/core.py). Generally, a specific kind of solver requires one or a number of specific grid-types. For example, `full_flow_sequential_solver` requires either `FlowFieldGrid` or `FlowFieldPlanarGrid`. So, it is often the case that adding a new solver will require adding a new grid type, as well. ================================================ FILE: docs/empirical_gauss_model.md ================================================ (empirical_gauss_model)= # Empirical Gaussian model FLORIS's "empirical" model has the same Gaussian wake shape as other popular FLORIS models. However, the models that describe the wake width and deflection have been reorganized to provide simpler tuning and data fitting. ## Wake shape The velocity deficit at a point $(x, y, z)$ in the wake follows a Gaussian curve, i.e., $$ \frac{u}{U_\infty} = 1 - Ce^{-\frac{(y-\delta_y)^2}{2\sigma_y^2} -\frac{(z-z_h-\delta_z)^2}{2\sigma_z^2}} $$ where the $(x, y, z)$ origin is at the turbine location (at ground level). The terms $C$, $\sigma_y$, $\sigma_z$, $\delta_y$, and $\delta_z$ all depend on the downstream location $x$. $C$ is the scaling factor for the Gaussian curve, defined as $$C = \frac{1}{8\sigma_{0_D}^2}\left(1 - \sqrt{1 - \frac{\sigma_{y0} \sigma_{z0} C_T}{\sigma_y \sigma_z}}\right)$$ Here, $C_T$ is the turbine thrust coefficient, which includes any reduction in thrust due to yaw or tilt of the turbine rotor. $\sigma_{y0}$ and $\sigma_{z0}$ define the wake width at the turbine location $x=0$, which are based on the user-specified rotor-diameter normalized initial width $\sigma_{0_D}$. Note that this contrasts with FLORIS's other Gaussian models, where $\sigma_{y0}$ and $\sigma_{z0}$ are defined at the end of the near wake/beginning of the far wake, at some $x_0 > 0$. The normalization term $8\sigma_{0_D}^2$ provides consistency with actuator disc theory. ## Wake expansion The wake lateral and vertical widths, $\sigma_y$ and $\sigma_z$, respectively, are a function of downstream distance $x$. The expansion of the wake is described by a user-tunable, piecewise linear function. This is simplest to express as an integral of a piecewise constant wake expansion rate $k$, i.e., $$ \sigma_{y}(x) = \int_{0}^x \sum_{i=0}^n k_i \mathbf{1}_{[b_{i}, b_{i+1})} (x') dx' + \sigma_{y0} $$ Here, $\mathbf{1}_{[a, b)}(x)$ is the indicator function, which takes value 1 when $a \leq x < b$, and 0 otherwise. The above function ensures that expansion rate $k_i$ applies only between breakpoints $b_{i-1}$ and $b_i$, allowing $n+1$ varying rates of linear expansion at different downstream ranges, determined by the $b_i$. Note that $b_0 = 0$ and $b_{n+1} = \infty$ by design. A slight modification is made to the above so that the wake width varies smoothly. As stated above, the wake expansion rate contains jump discontinuities that create "sharp" changes in the wake width. To avoid this, the indicator function $\mathbf{1}_{[a, b)}(x)$ is replaced with a pair of "smoothstep" functions that vary smoothly with width parameter $d$. In the limit as $d\rightarrow 0$, the approximation becomes exact. While the form of this wake expansion model seems complex, it is very simple to tune to fit data: the user provides the $n+1$ expansion rates $k_i, i=0,\dots,n+1$ (defined as a list in the `wake_expansion_rates` field of the input yaml) and the $n$ 'break points' $b_i, i=1,\dots,n$ where those expansion rates should go into effect (specified in terms of rotor diameters downstream as a list in the `breakpoints_D` field of the input yaml. As well as these, the initial width $\sigma_{0_D}$ should be provided by setting `sigma_0_D` and the logistic function width $d$ as `smoothing_length_D` (both specified in terms of rotor diameters). We expect that the default values for $\sigma_{0_D}$ and $d$ should be satisfactory for most users. Further, we anticipate that most users will not need more than $n+1=3$ expansion rates (along with $n=2$ break points) to describe the wake expansion. ## Wake deflection The deflection of the wake centerline $\delta_y$ and $\delta_z$ due to yawing and tilting, respectively, follow a simple model $$ \delta = k_\text{def} C_T \alpha \operatorname{ln}\left(\frac{x/D - c}{x/D + c} + 2\right)$$ Here, $k_\text{def}$ is a user-tunable deflection gain and $\alpha$ is the misalignment. When computing the lateral wake deflection $\delta_y$ due to yaw misalignment, $\alpha$ should be the yaw misalignment _specified in radians, clockwise positive from the wind direction_. When computing the vertical wake deflection $\delta_z$ due to rotor tilt, $\alpha$ should be the tilt angle _specified in radians, clockwise positive when the rotor is tilted back_. Finally, $c$ in the above deflection model is a 'deflection rate'. This specifies how quickly the wake will reach it's maximum deflection $k_\text{def} C_T \alpha \operatorname{ln}(3)$ for a given yaw/tilt angle. User-tunable parameters of the model are as follows: - A separately tunable deflection gain $k_\text{def}$ for each of lateral deflections (due to yaw misalignments) and vertical deflections (due to nonzero tilt), specified using `horizontal_deflection_gain_D` and `vertical_deflection_gain_D` (specified in terms of rotor diameters) - The deflection rate $c$, specified using `deflection_rate`. We anticipate that most users will be able to use the default value for $c$, and set `vertical_deflection_gain_D` to the same value as `horizontal_deflection_gain_D` (which can also be achieved by providing `vertical_deflection_gain_D = -1`). ## Wake-induced mixing Finally, turbines contribute to mixing in the flow. In other models, this extra mixing is accounted for by adding to the turbulence intensity value. In the empirical model, explicit dependencies on turbulence intensity are removed completely to aid in tuning. Instead, a non-physical "wake-induced mixing factor" is specified for turbine $j$ as $$ \text{WIM}_j = \sqrt{\sum_{i \neq j} \left(\frac{A_{ij} a_i} {\bar{d}_{ij}^2}\right)^2} $$ where $A_{ij}$ is the area of overlap of the wake of turbine $i$ onto turbine $j$; $a_i$ is the axial induction factor of the turbine $i$; and $\bar{d}_{ij} = (x_j - x_i)/D_i$ is the downstream distance of turbine $j$ from the turbine $i$, normalized by turbine $i$'s rotor diameter. Here, $A_{ij} = 1$ if turbine $j$ is fully covered by turbine $i$'s wake; $A_{ij} = 0$ if turbine $j$ is not covered at all by turbine $i$'s wake; and $0 < A_{ij} < 1$ if turbine $j$ is partially covered by turbine $i$'s wake. By convention, $A_{ii} = 0$ and $A_{ij} = 0$ if $x_j \leq x_i$ (turbine $j$ is upstream of turbine $i$). Wake-induced mixing can affect both the velocity deficit and wake deflection. To account for wake-induced mixing, the wake width of turbine $j$ is adjusted to $$ \sigma_{y}(x) = \int_{0}^x \sum_{i=0}^n k_i \ell_{[b_{i}, b_{i+1})}(x') + w_v \text{WIM}_j dx' + \sigma_{y0} $$ where $w_v$ is the velocity deficit wake-induced mixing gain, which the user can vary by setting `wim_gain_velocity` to represent different levels of mixing caused by the turbines. The wake deflection model is similarly adjusted to $$ \delta = \frac{k_\text{def} C_T \alpha}{1 + w_d \text{WIM}_j}\operatorname{ln}\left(\frac{x/D - c}{x/D + c} + 2\right)$$ where $w_d$ is the wake-induced mixing gain for deflection, provided by the user by setting `wim_gain_deflection`. ```{note} The documentation previously had an erroneous form for the wake-induced mixing term, $$ \text{WIM}_j = \sum_{i \in T^{\text{up}}(j)} \frac{A_{ij} a_i} {(x_j - x_i)/D_i} \:.$$ This has been corrected to the form shown above, which is consistent with the implementation. The implementation has not changed; only the documentation has been corrected. ``` ## Yaw added mixing Yaw misalignment can also add turbulence to the wake. In the empirical Gaussian model, this effect, referred to as "yaw-added wake recovery" in other models, is activated by setting `enable_yaw_added_recovery` to `true`. Yaw-added mixing is represented by updating the wake-induced mixing term as follows: $$ \text{WIM}_j = \sqrt{\sum_{i \neq j} \left(\frac{A_{ij} a_i (1 + g_\text{YAM} (1-\cos(\gamma_i)))}{\bar{d}_{ij}^2}\right)^2 + \left(a_j g_\text{YAM} (1-\cos(\gamma_j))\right)^2}$$ Note that the second term means that, unlike when `enable_yaw_added_recovery` is `false`, a turbine may affect the recovery of its own wake by yawing. ## Mirror wakes Mirror wakes are also enabled by default in the empirical model to model the ground effect. Essentially, turbines are placed below the ground so that the vertical expansion of their (mirror) wakes appears in the above-ground flow some distance downstream, to model the reflection of the true turbine wakes as they bounce off of the ground/sea surface. ## Added mixing by active wake control As the name suggests, active wake control (AWC) aims to enhance mixing to the wake of the controlled turbine. This effect is activated by setting `enable_active_wake_mixing` to `true`, and `awc_modes` to `"helix"` (other AWC strategies are yet to be implemented). The wake can then be controlled by setting the amplitude of the AWC excitation using `awc_amplitudes` (see the [AWC operation model](operation_models_user.ipynb#awc-model)). The effect of AWC is represented by updating the wake-induced mixing term as follows: $$ \text{WIM}_j = \sqrt{\sum_{i \neq j} \left(\frac{A_{ij} a_i}{\bar{d}_{ij}^2}\right)^2 + \left(\frac{\beta_{j}^{p_\text{AWC}}}{d_\text{AWC}}\right)^2}$$ where $\beta_{j}$ is the AWC amplitude of turbine $j$, and the exponent $p_\text{AWC}$ and denominator $d_\text{AWC}$ are tuning parameters that can be set in the `emgauss.yaml` file with the fields `awc_wake_exp` and `awc_wake_denominator`, respectively. Note that, in contrast to the yaw added mixing case, a turbine currently affects _only_ its own wake by applying AWC. ================================================ FILE: docs/floating_wind_turbine.md ================================================ # Floating Wind Turbine Modeling The FLORIS wind turbine description includes a definition of the performance curves (`power` and `thrust_coefficient`) as a function of wind speed, and this lookup table is used directly in the calculation of power production for a steady-state atmospheric condition (wind speed and wind direction). The power curve definition typically assumes a fixed-bottom wind turbine with a fixed shaft tilt. However, floating wind turbines have an additional rotational degrees of freedom in the platform pitch, which adds a tilt angle to the rotor. As the turbine tilts, its performance is affected because the turbine is no longer operating on its defined performance curve. FLORIS allows the user to correct for the tilt angle of the turbine as a function of wind speed. This is accomplished by including an additional input, `floating_tilt_table`, in the turbine definition that sets the steady tilt angle of the turbine based on wind speed. An interpolation is created and the tilt angle is computed for each turbine based on its rotor effective velocity. Taking into account the turbine rotor's built-in tilt, the absolute tilt is used to compute the power and thrust coefficient. To enable the use of the `floating_tilt_table`, the `correct_cp_ct_for_tilt` input on the turbine definition should be set to `True`. The tilt angle is then used directly in the selected wake models to compute wake effects of tilted turbines. ================================================ FILE: docs/floris_models.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "(floris_models)=\n", "\n", "# FLORIS Models\n", "\n", "This notebook provides information on the provided FlorisModels. [Introductory Concepts](intro_concepts) introduced `FlorisModel` as the base class for all models in the FLORIS package. This notebook introduces the `ParFlorisModel`, `UncertainFlorisModel`, and `ApproxFlorisModel` classes, which are subclasses or compositions of `FlorisModel`.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parallelized FLORIS Model\n", "\n", "The `ParFlorisModel` class is a subclass of `FlorisModel` that parallelizes the FLORIS calculations. This class is designed to \n", "have an interface that is the same as `FlorisModel`, but the calculations are parallelized. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Instantiation\n", "\n", "The `ParFlorisModel` class can be instantiated in the same way as the `FlorisModel` class, or else it can be instantiated by passing a `FlorisModel` object to the constructor. \n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from floris import FlorisModel, ParFlorisModel, TimeSeries\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "fmodel = FlorisModel(\"gch.yaml\")\n", "\n", "# Instantiation using yaml input file\n", "pfmodel = ParFlorisModel(\"gch.yaml\")\n", "\n", "# Instantiation using fmodel\n", "pfmodel = ParFlorisModel(fmodel)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Parameters\n", "\n", "The `ParFlorisModel` class has additional parameters the define the parallelization. These parameters are:\n", "\n", "**interface**: The parallelization interface to use. Options are `\"multiprocessing\"`,\n", " `\"pathos\"`, and `\"concurrent\"`, with possible future support for `\"mpi4py\"`\n", "\n", "**max_workers**: The maximum number of workers to use. Defaults to -1, which then\n", " takes the number of CPUs available.\n", "\n", "**n_wind_condition_splits**: The number of wind conditions to split the simulation over.\n", " Defaults to the same as max_workers.\n", "\n", "**return_turbine_powers_only**: Whether to return only the turbine powers.\n", "\n", "**print_timings** (bool): Print the computation time to the console. Defaults to False." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Alternative parameters\n", "pfmodel = ParFlorisModel(fmodel, max_workers=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Usage\n", "\n", "The `ParFlorisModel` class can be used in the same way as the `FlorisModel` class. The only difference is that the calculations are parallelized. \n", "\n", "```python" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Set to a two turbine layout\n", "layout_x = [0, 500]\n", "layout_y = [0, 0]\n", "fmodel.set(layout_x=layout_x, layout_y=layout_y)\n", "pfmodel.set(layout_x=layout_x, layout_y=layout_y)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "wind_directions = np.arange(240, 300, 0.5)\n", "time_series = TimeSeries(\n", " wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06\n", ")\n", "fmodel.set(wind_data=time_series)\n", "pfmodel.set(wind_data=time_series)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "image/png": "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9qVkPrON9stcruAQms2b1Wn6OgCVjGVbUC8Fb9WqFTVr1rRTZEIULauSm19++YVnn30WDw+PPPfJa3ZhIYTtaF232hcVZCU34eHhpKen2yMsUcJoia55+4x0SYmyoFADivv3709MTEyO8h07djBw4MBcjhBC2FLHjh2pWLEid4AoY1kboDqQlJQks3YLFEXJd7yNrCclHFmhkpv77ruP/v37WyzUFxERweDBg/nggw9sFpwQInfOzs6mwfu5LcWgrQAtyq4jR45w/fp1ygNdjWXngIuol9Saz0wshKMpVHIzf/586tSpw9ChQ0lLS2Pr1q0MGTKEDz/8kFdeecXWMQohcqH98jZPYwYY77NPty/KHu0z0JusQefaZyUwMBBPT097hCVEsShUclOuXDmWL1+Oi4sLffr04YEHHmD69Om8/PLLto5PCJEHrQt4J6C1oQ4AdMD+/fu5ceOGnSITJYGW3LQzLzPey/AB4egKnNwcPnzY4qZNonflyhWefPJJevbsadomhCh6DRo0oFGjRuhRF9AEdb2pAONjuWqq7EpMTGT79u0AfAzUBsahDkAHSW6E4yvwPDft2rVDp9NZzH6qPf/hhx/4v//7PxRFQafTkZmZWSTBCiEsDRw4kG+++YYQYDhwBPAxbtuwYQNPPvmkvUITdrR161aLFZSvAouMj319fWnbtq1d4hKiuBQ4ublw4UJRxiGEKAQtufkFWIv6JaYJCQnBYDBYzPItyob8xlwNGDBAPhPC4RU4ualbt25RxiGEKISgoCBcXV2JS08nLtu2W7dusX//fjp27GiX2IT95JfcSJeUKAsKnL5bMzlfcnIyx44dK1RAQoiCq1ChAj169Mhze0hISDFGI0qCs2fPcv78eVxR50D6HNAu+tbpdDK/jSgTCpzcjBo1igEDBrBixQqSkpJy3ef48eO88847NGzYkH379tksSCFE3nL7JV7ReC+XhJc92r95N9RlF14BnjJu69ChA9WqVbNTZEIUnwInN8ePH2fIkCFMmTIFHx8fWrZsSb9+/Rg6dCjdu3enatWqtG/fngsXLrBx40ZGjx5dlHELIYzMk5vpwAngkPF5ZGQksbGxdohK2IuW3JinvFr7nXRJibKiwMmNi4sLEydO5NSpU0RGRvLMM8/QqlUratWqRVBQED/88APXrl3jl19+oXXr1kUZsxDCTMuW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Tz3F1q1bAXX8xogRI3B1dWX37t18//33vPXWWxbn0Ov1DBgwAC8vL7Zt28aOHTvw9PRk4MCBpKenW11PouDee+892rVrxw2gP3DTWN4TWI66RMOCBQvo1asX586dy/M8qampvPnmmzzwwAOmlp5EwGDc/gKwCvUHwuLFi3FzcyuaNySEKB73tP54KZTfkukpKSnK8ePHlZSUlJwHzp6tKLVq5Xsz1KqlpA8cqGRmZloeO3ToXY9VatVSX6OQxowZowwbNkxRFEUxGAxKaGio4ubmprz++us59l2xYoVSpUoV0/NFixYpgHLw4EGL/T744AOlXLlySoUKFUy3Tp065fqaiYmJiouLi7J06VLT9vT0dMXPz0+ZOXOmoiiKsnXrVgVQVq9erSiKomRmZip37txR3n//faVt27aKoijK7du3FUAJCwvL9X0uWrRIqVixoqIoitKuXTvlxx9/VAwGg9KwYUPlzz//VL744gulbt26pv27du2qPPPMMxbneOSRR5TBgwcriqIoISEhirOzs3L16lXT9vXr1yuAsmrVKkVRFOWnn35SmjZtqhgMBtM+aWlpioeHhxISEpKjLnKT72erANLT05XVq1cr6enphTq+NLt27ZpSv359BVDagxIPigKKHpRuoGC8lS9fXvnmm2+U+Ph4U32lpKQoERERSsuWLU37md9GgPK28XG5cuWUlStX2vvt2kVZ/nwVhtSXdWxVX/l9f2cnM1MVVHw8XL2a7y46QOfnl3PDv//e9VjTa9yDv/76C09PT/R6PQaDgSeeeIKpU6eyadMmpk+fzsmTJ4mPjycjI4PU1FSSk5MpX748AK6urrRp0ybHOZs2bWqxInNev2jPnTuHXq+nW7dupjIXFxc6d+7MiRMnLPbt2LFjnu+hcuXKjB07lgEDBtCvXz+Cg4N59NFHqVmzZo59x40bx6JFi6hTpw5JSUkMHjyYb775xmKfEydO8Oyzz1qUdevWjTlz5pi2+/v742f27xYYGGix/6FDhzh79myOS4JTU1PzbS0QtlGzZk1CQkLo1q0b+//9lweB34EngR1m+yUnJ/PSSy8xceJE2rRpg6IojBo1yjTpX3XgDuqVUJqVZo+///57HnzwwSJ+N0KI4iDJTUF5e0OtWvnuogBKbgNjq1W767Gm17gHvXv3Zu7cubi6uuLn54ezszMXL17k/vvv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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmodel.run()\n", "pfmodel.run()\n", "\n", "farm_power = fmodel.get_farm_power()\n", "pfarm_power = pfmodel.get_farm_power()\n", "\n", "# Show the results are the same\n", "fig, ax = plt.subplots()\n", "ax.plot(wind_directions, farm_power, label=\"FlorisModel\", color='k', lw=5)\n", "ax.plot(wind_directions, pfarm_power, label=\"ParFlorisModel\", color='r', ls='--', lw=2)\n", "ax.set_xlabel(\"Wind Direction (deg)\")\n", "ax.set_ylabel(\"Farm Power (kW)\")\n", "ax.legend()\n", "ax.grid()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## UncertainFlorisModel\n", "\n", "The `UncertainFlorisModel` class is a composition of `FlorisModel` that adds uncertainty to the input conditions. Its interface is meant to made similar to `FlorisModel`, but with the addition of uncertainty in wind direction." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Instantiation\n", "\n", "The `UncertainFlorisModel` class can be instantiated in the same way as the `FlorisModel` class, or else it can be instantiated by passing a `FlorisModel` object to the constructor. Alternatively a `ParFlorisModel` object can be passed to the constructor which ensures the underlying calculations are parallelized according to the `ParFlorisModel` parameters." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "from floris import UncertainFlorisModel\n", "\n", "# Instantiation options\n", "ufmodel = UncertainFlorisModel(\"gch.yaml\") # Using input yaml\n", "ufmodel = UncertainFlorisModel(fmodel) # Using a FlorisModel object\n", "ufmodel = UncertainFlorisModel(pfmodel) # Using a ParFlorisModel object" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Parameters\n", "\n", "To include uncertainty into the wind direction, the `UncertainFlorisModel` class, for each findex run, the result for a wind direction is provided by performing a Gaussian blend over results from multiple wind directions nearby wind directions. To reduce the total number of calculations required, a resolution of wind direction, wind speed, turbulence intensity and control inputs are specified and repeated calculations are only calculated once. See the class API for complete details but some key parameters are:\n", "\n", "**wd_resolution, ws_resolution, ti_resolution, yaw_resolution, and power_setpoint_resolution**: Define the granularity of calculations for wind direction, wind speed, turbulence intensity, yaw angle, and power setpoints, respectively.\n", "\n", "**wd_std**: The standard deviation of wind direction, used in the Gaussian blending.\n", "\n", "**wd_sample_points**: Specific wind direction points to sample for expanded conditions.\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Define the uncertainty to have a wd_std of 5 degrees and blend over 10 degrees\n", "ufmodel = UncertainFlorisModel(fmodel, wd_std=5, wd_sample_points=[-5, -4, -3, -2, -1, 0, 1, 2,3, 4, 5], wd_resolution=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Usage\n", "\n", "Usage of `UncertainFlorisModel` is similar to `FlorisModel` however the results will now include the effects of Gaussian blending\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "image/png": "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atfUhg2A2jW1N1Xxli1bmDx5sr2jEjnQmTNnCAsLMzpm2CWlLVQIp7Zt7R6XENZmUctN/fr1CQ8PZ+/evezbt48DBw6YnCtBCGFFQUFoUyZT603aX9rDhw/z8OFDu4clcp5ndUnJU1Iit7AouTl8+DAPHjzg888/x8PDg88//5ySJUtSp04dxowZw+rVq7l7966tYhUibypQwKhrqnmqYq1Wy/bt2+0elsh5TCU3Lxhsa194IU25EDmRxQOKvby86NKlC7NmzSIkJESf7Li6ujJixAhKlSpliziFyNM0vXvrt019/Pz111/2C0bkSNHR0ezfv9/omCtPu6SeeHigBATYOywhbMKiMTeGtFotR48eZffu3ezatYsDBw4QExND+fLlrRmfEAKge3e0zs44JSfzAvBOquLt27ejKAoajcYR0YkcYPfu3WlWkm8HFEzZvtu0KSVlLSmRS1jUcnPkyBE+//xzunbtio+PD82bN2f+/Pn4+vry9ddfc/XqVa5du2arWIXIuwoVIqlVKwAqAH6pim/evMmlS5fsHZXIQUx1XfY22A5rnrrDU4icy6KWm+eee44SJUrQtm1b5s6dS9u2balcubKtYhNCGHB+8UXYvRtQu6ZOpioPDg6mevXq9g5L5BDBwcFG+05Az5TtxHz5uOeXOmUWIueyqOXm/Pnz3L59m19//ZVXX33VZGKjKIrVghNCPKV9/nl088r2NlEug4qFObdv3+bcuXNGx1oCxVK2E9u3JzllskghcgOLkhvdX4VffPGFyfLk5GT69++f9aiEEGkVL85/FSoAUBOokap4165dJCUl2TsqkQOYSnwNB6bne/ll+wUjhB1kavmFL774gkWLFhkdS05O5uWXXyY0NNQacQkhTHho8DRL6qemoqKiOHr0qF3jETlD6i4pDU/fP0lOTihdu9o9JiFsKVPJzaZNm5gwYQJr1qwBICkpiRdffJG///6bXbt2WTVAIcRTd1q00G+beiQ89YeYEIqipGm5aQyUTdmOaNgQChZMc50QOVmmkpsmTZqwdu1ahg0bxsaNG+nduzcXL15k165dshyDEDYU5+tLeJkyADQCUk+8IMmNSO3vv/8mPDzc6JhhYuw9ZIhd4xHCHjK9Kni7du346aef6N27N9euXWPPnj0UL17cmrEJIUxQevbUb/dKVXb48GEeP35s13hE9mYq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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ufmodel.set(wind_data=time_series, layout_x=layout_x, layout_y=layout_y)\n", "ufmodel.run()\n", "\n", "# Get the power of the downstream turbine\n", "f_power = fmodel.get_turbine_powers()[:,1]\n", "uf_power = ufmodel.get_turbine_powers()[:,1]\n", "\n", "# Plot the two powers\n", "fig, ax = plt.subplots()\n", "ax.plot(wind_directions, f_power, label=\"FlorisModel\", color='k', lw=5)\n", "ax.plot(wind_directions, uf_power, label=\"UncertainFlorisModel\", color='r', lw=2)\n", "ax.set_xlabel(\"Wind Direction (deg)\")\n", "ax.set_ylabel(\"Turbine Power (kW)\")\n", "ax.legend()\n", "ax.grid()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ApproxFlorisModel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " The ApproxFlorisModel overloads the UncertainFlorisModel with the special case that the `wd_sample_points = [0]`. This is a special case where no uncertainty is added but the resolution of the values wind direction, wind speed etc are still reduced by the specified resolution. This allows for cases to be reused and a faster approximate result computed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Instantiation\n", "\n", "`ApproxFlorisModel` can be instantiated in the same way as the `UncertainFlorisModel` class" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "from floris import ApproxFlorisModel\n", "\n", "# Instantiation options\n", "amodel = ApproxFlorisModel(\"gch.yaml\") # Using input yaml\n", "amodel = ApproxFlorisModel(fmodel) # Using a FlorisModel object\n", "amodel = ApproxFlorisModel(pfmodel) # Using a ParFlorisModel object" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Usage\n", "\n", "`ApproxFlorisModel` is used in the same way as `UncertainFlorisModel` but with the special case that the `wd_sample_points = [0]`. This means that while the resolution of the values `wind_direction`, `wind_speeds` etc are still reduced by the specified resolution, no uncertainty is added. It is intended for quickly processing large sets of inflow conditions (e.g., when reproducing SCADA records), when approximate solutions are sufficient.\n", "\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# Instantiate with a wind speed resolution of 0.5 m/s and a wind direction resolution of 1 degree\n", "amodel = ApproxFlorisModel(fmodel, ws_resolution=0.5, wd_resolution=1.0) " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "amodel has an n_findex of 40, however, the number of unique cases to run at this resolution is 9.\n" ] } ], "source": [ "# Show approximation for a time series including smaller increments of wind speed\n", "wind_speeds = np.arange(6, 10, 0.1)\n", "time_series = TimeSeries(\n", " wind_directions = 270.0,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=0.06\n", ")\n", "amodel.set(wind_data=time_series)\n", "\n", "print(f\"amodel has an n_findex of {amodel.n_findex}, \",\n", " f\"however, the number of unique cases to run at this resolution is {amodel.n_unique}.\")\n", "\n", "amodel.run()\n", "farm_power = amodel.get_farm_power()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Show the farm power over wind speeds\n", "fig, ax = plt.subplots()\n", "ax.plot(wind_speeds, farm_power, label=\"ApproxFlorisModel\", color='k', lw=2)\n", "ax.legend()\n", "ax.set_xlabel(\"Wind Speed (m/s)\")\n", "ax.set_ylabel(\"Farm Power (kW)\")\n", "ax.grid()\n" ] } ], "metadata": { "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: docs/heterogeneous_map.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "(heterogeneous_map)=\n", "\n", "# Heterogeneous Map" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "FLORIS provides a HeterogeneousMap object to enable a heterogeneity in the background wind speed. This notebook demonstrates how to use the HeterogeneousMap object in FLORIS." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that more detailed examples are provided within the examples_heterogeneous folder" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from floris import (\n", " FlorisModel,\n", " HeterogeneousMap,\n", " TimeSeries,\n", ")\n", "from floris.flow_visualization import visualize_heterogeneous_cut_plane, visualize_cut_plane" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Initialization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The HeterogeneousMap defines the heterogeneity in the background wind speed. For a set of x,y coordinates, a speed up (or low down), relative to the inflow wind speed, can be defined. This can vary according to the inflow wind speed and wind direction. For wind directions and wind speeds not directly defined with the HeterogeneousMap, the nearest defined value is used." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define a map in which there is ab observed speed up for wind from the east (90 degrees)\n", "# but not from the west (270 degrees)\n", "heterogeneous_map = HeterogeneousMap(\n", " x=np.array([0.0, 0.0, 250.0, 500.0, 500.0]),\n", " y=np.array([0.0, 500.0, 250.0, 0.0, 500.0]),\n", " speed_multipliers=np.array(\n", " [\n", " [1.0, 1.0, 1.0, 1.0, 1.0],\n", " [1.0, 1.0, 1.0, 1.0, 1.0],\n", " [1.5, 1.0, 1.25, 1.5, 1.0],\n", " [1.0, 1.5, 1.25, 1.0, 1.5],\n", " ]\n", " ),\n", " wind_directions=np.array([270.0, 270.0, 90.0, 90.0]),\n", " wind_speeds=np.array([5.0, 10.0, 5.0, 10.0]),\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Can print basic information about the map\n", "print(heterogeneous_map)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Interpolation\n", "\n", "By default the HeterogeneousMap uses linear interpolation to determine the speed up for a given wind speed and wind direction using `scipy.interpolate.LinearNDInterpolator`. This can be changed to nearest neighbor interpolation by setting the `interp_method` argument to `'nearest'`, which uses `scipy.interpolate.NearestNDInterpolator`. The default behavior is recovered by setting `interp_method` to `'linear'`.\n", "\n", "When `interp_method` is `'linear'`, any location outside of the convex hull of points defined by `x`, `y` and `z` are given a speed multiplier of 1.0 (that is, the freestream wind speed is applied) and an \"out of bounds\" warning is raised. When `interp_method` is `'nearest'`, points \"outside\" of the specified region still get assigned the speed multiplier of the nearest specified point, and \"out of bounds\" warning is raised." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "heterogeneous_map_nearest = HeterogeneousMap(\n", " x=np.array([0.0, 0.0, 250.0, 500.0, 500.0]),\n", " y=np.array([0.0, 500.0, 250.0, 0.0, 500.0]),\n", " speed_multipliers=np.array(\n", " [\n", " [1.0, 1.0, 1.0, 1.0, 1.0],\n", " [1.0, 1.0, 1.0, 1.0, 1.0],\n", " [1.5, 1.0, 1.25, 1.5, 1.0],\n", " [1.0, 1.5, 1.25, 1.0, 1.5],\n", " ]\n", " ),\n", " wind_directions=np.array([270.0, 270.0, 90.0, 90.0]),\n", " wind_speeds=np.array([5.0, 10.0, 5.0, 10.0]),\n", " interp_method=\"nearest\"\n", ")\n", "print(heterogeneous_map_nearest)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "HeterogeneousMap includes methods to visualize the speed up for a given wind speed and wind direction. Note that in FLORIS, heterogeneity in the ambient flow only exists for points within the convex hull surrounding the defined points. This boundary is illustrated in the flow. All points outside the boundary are assume to be 1.0 * the inflow wind speed." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use the HeterogeneousMap object to plot the speedup map for 3 wd/ws combinations\n", "fig, axarr = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(15, 5))\n", "\n", "\n", "ax = axarr[0]\n", "heterogeneous_map.plot_single_speed_multiplier(\n", " wind_direction=60.0, wind_speed=8.5, ax=ax, vmin=1.0, vmax=1.2\n", ")\n", "ax.set_title(\"Wind Direction = 60.0\\nWind Speed = 8.5\")\n", "ax.legend()\n", "\n", "ax = axarr[1]\n", "heterogeneous_map.plot_single_speed_multiplier(\n", " wind_direction=130.0, wind_speed=4.0, ax=ax, vmin=1.0, vmax=1.2\n", ")\n", "ax.set_title(\"Wind Direction = 130.0\\nWind Speed = 4.0\")\n", "\n", "ax = axarr[2]\n", "heterogeneous_map.plot_single_speed_multiplier(\n", " wind_direction=280.0, wind_speed=16.0, ax=ax, vmin=1.0, vmax=1.2\n", ")\n", "ax.set_title(\"Wind Direction = 280.0\\nWind Speed = 16.0\")\n", "fig.suptitle(\"Heterogeneous speedup map for several directions and wind speeds\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Applying heterogeneity to a FlorisModel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Applying the HeterogeneousMap to a FlorisModel is done by passing the HeterogeneousMap to a WindData object which is used to set the FlorisModel. The WindData object constructs the appropriate speed up map for each wind direction and wind speed condition." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Initialize FlorisModel\n", "fmodel = FlorisModel(\"gch.yaml\")\n", "\n", "# Change the layout to a 2 turbine layout within the heterogeneous domain\n", "fmodel.set(layout_x=[200, 200.0], layout_y=[50, 450.0])\n", "\n", "# Define a TimeSeries object with 3 wind directions and wind speeds\n", "# and turbulence intensity and using the above HeterogeneousMap object\n", "time_series = TimeSeries(\n", " wind_directions=np.array([275.0, 95.0, 75.0]),\n", " wind_speeds=np.array([7.0, 6.2, 8.0]),\n", " turbulence_intensities=0.06,\n", " heterogeneous_map=heterogeneous_map,\n", ")\n", "\n", "# Apply the time series to the FlorisModel\n", "fmodel.set(wind_data=time_series)\n", "\n", "# Run the FLORIS simulation\n", "fmodel.run()\n", "\n", "# Visualize each of the findices\n", "fig, axarr = plt.subplots(3, 1, sharex=True, sharey=True, figsize=(10, 10))\n", "\n", "for findex in range(3):\n", " ax = axarr[findex]\n", "\n", " horizontal_plane = fmodel.calculate_horizontal_plane(\n", " x_resolution=200, y_resolution=100, height=90.0, findex_for_viz=findex\n", " )\n", "\n", " visualize_heterogeneous_cut_plane(\n", " cut_plane=horizontal_plane,\n", " fmodel=fmodel,\n", " ax=ax,\n", " title=(\n", " f\"Wind Direction = {time_series.wind_directions[findex]}\\n\"\n", " f\"Wind Speed = {time_series.wind_speeds[findex]}\"\n", " ),\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Definining a 3D HeterogeneousMap" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Including a z-dimension in the HetereogeneousMap allows for a 3D heterogeneity. This uses the underlying support in FlorisMode for 3D heterogeneous_inflow_config_by_wd.\n", "\n", "Note that when using the 3D version, wind_sheer must be set to 0.0 to avoid an error." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define a 3D heterogeneous map with two z-levels\n", "\n", "heterogeneous_map = HeterogeneousMap(\n", " x=np.array(\n", " [\n", " -1000.0,\n", " -1000.0,\n", " 1000.0,\n", " 1000.0,\n", " -1000.0,\n", " -1000.0,\n", " 1000.0,\n", " 1000.0,\n", " -1000.0,\n", " -1000.0,\n", " 1000.0,\n", " 1000.0,\n", " ]\n", " ),\n", " y=np.array(\n", " [-500.0, 500.0, -500.0, 500.0, -500.0, 500.0, -500.0, 500.0, -500.00, 500.0, -500.0, 500.0]\n", " ),\n", " z=np.array(\n", " [100.0, 100.0, 100.0, 100.0, 200.0, 200.0, 200.0, 200.0, 500.0, 500.0, 500.0, 500.0]\n", " ),\n", " speed_multipliers=np.array(\n", " [\n", " [1.0, 1.0, 1.0, 1.0, 1.1, 1.1, 1.1, 1.1, 1.5, 1.5, 1.5, 1.5],\n", " [1.0, 1.0, 1.0, 1.0, 1.1, 1.1, 1.1, 1.1, 1.5, 1.5, 1.5, 1.5],\n", " [1.0, 1.2, 1.2, 1.0, 1.3, 1.1, 1.1, 1.3, 1.5, 1.5, 1.5, 1.5],\n", " [1.0, 1.0, 1.0, 1.0, 1.1, 1.1, 1.1, 1.1, 1.5, 1.5, 1.5, 1.5],\n", " ]\n", " ),\n", " wind_directions=np.array([270.0, 270.0, 90.0, 90.0]),\n", " wind_speeds=np.array([5.0, 10.0, 5.0, 10.0]),\n", ")\n", "print(heterogeneous_map)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When visualizing the 3D heterogeneity, the z-height to plot must be specified (nearest defined is used)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "heterogeneous_map.plot_single_speed_multiplier(wind_direction=90.0, wind_speed=5.0, z=100.0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "heterogeneous_map.plot_single_speed_multiplier(wind_direction=90.0, wind_speed=5.0, z=200.0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## Apply the 3D heterogeneous map to the FlorisModel\n", "time_series = TimeSeries(\n", " wind_directions=np.array([275.0, 95.0, 75.0]),\n", " wind_speeds=np.array([7.0, 6.2, 8.0]),\n", " turbulence_intensities=0.06,\n", " heterogeneous_map=heterogeneous_map,\n", ")\n", "\n", "# Apply the time series to the FlorisModel, make sure to set wind_shear to 0.0\n", "fmodel.set(wind_data=time_series, wind_shear=0.0)\n", "\n", "# Run the FLORIS simulation\n", "fmodel.run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Show a horizontal and y plane slice (note that heterogeneity is only defined within the hull of points)\n", "\n", "# Visualize each of the findices\n", "fig, axarr = plt.subplots(2, 1, sharex=True, sharey=False, figsize=(5, 7))\n", "findex = 0\n", "\n", "ax = axarr[0]\n", "horizontal_plane = fmodel.calculate_horizontal_plane(\n", " x_resolution=200, y_resolution=100, height=90.0, findex_for_viz=findex\n", ")\n", "\n", "visualize_heterogeneous_cut_plane(\n", " cut_plane=horizontal_plane,\n", " fmodel=fmodel,\n", " ax=ax,\n", " title=(\n", " f\"Wind Direction = {time_series.wind_directions[findex]}\\n\"\n", " f\"Wind Speed = {time_series.wind_speeds[findex]}\"\n", " ),\n", ")\n", "\n", "ax = axarr[1]\n", "y_plane = fmodel.calculate_y_plane(\n", " x_resolution=200,\n", " z_resolution=100,\n", " findex_for_viz=findex,\n", " crossstream_dist=400.0, # x_bounds=[-200,500]\n", ")\n", "\n", "visualize_cut_plane(\n", " cut_plane=y_plane,\n", " ax=ax,\n", " title=(\n", " f\"Wind Direction = {time_series.wind_directions[findex]}\\n\"\n", " f\"Wind Speed = {time_series.wind_speeds[findex]}\"\n", " ),\n", ")" ] } ], "metadata": { "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: docs/index.md ================================================ # FLORIS Wake Modeling & Wind Farm Controls FLORIS is a controls-focused wind farm simulation software incorporating steady-state engineering wake models into a performance-focused Python framework. 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](https://github.com/NatLabRockies/floris/discussions/)! ```{note} FLORIS support for python version 3.8 and 3.9 was recently ended as they reached [end-of-life](https://devguide.python.org/versions/). FLORIS v4.3 also made the move to requiring `numpy` version 2. See the [numpy documentation for details](https://numpy.org/doc/stable/numpy_2_0_migration_guide.html). ``` ## WETO software FLORIS is primarily developed with the support from the U.S. Department of Energy and is part of the [WETO Software Stack](https://natlabrockies.github.io/WETOStack). For more information and other integrated modeling software, see: - [Portfolio Overview](https://natlabrockies.github.io/WETOStack/portfolio_analysis/overview.html) - [Entry Guide](https://natlabrockies.github.io/WETOStack/_static/entry_guide/index.html) - [Wind Farm Controls Workshop](https://www.youtube.com/watch?v=f-w6whxIBrA&list=PL6ksUtsZI1dwRXeWFCmJT6cEN1xijsHJz) ## 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 {ref}`installation`). The typical entry point is {py:class}`.FlorisModel` which accepts the path to the input file as an argument. From there, changes can be made to the initial configuration through the {py:meth}`.FlorisModel.set` routine, and the simulation is executed with {py:meth}`.FlorisModel.run`. ```python 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 {py:class}`.FlorisModel` such as {py:meth}`.FlorisModel.get_turbine_layout`, {py:meth}`.FlorisModel.get_turbine_powers` and {py:meth}`.FlorisModel.get_farm_AEP`, and a visualization package is available in {py:mod}`floris.flow_visualization`. A collection of examples are included in the [repository](https://github.com/NatLabRockies/floris/tree/main/examples) and described in detail in {ref}`examples`. ## Engaging on GitHub FLORIS leverages the following GitHub features to coordinate support and development efforts: - [Discussions](https://github.com/NatLabRockies/floris/discussions): Collaborate to develop ideas for new use cases, features, and software designs, and get support for usage questions - [Issues](https://github.com/NatLabRockies/floris/issues): Report potential bugs and well-developed feature requests - [Projects](https://github.com/orgs/NREL/projects/18/): 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](https://github.com/NatLabRockies/floris/discussions/categories/ideas) is a great spot to develop the details for a feature request. [Q&A](https://github.com/NatLabRockies/floris/discussions/categories/q-a) is where to get usage support. [Show and tell](https://github.com/NatLabRockies/floris/discussions/categories/show-and-tell) is a free-form space to show off the things you are doing with FLORIS. ================================================ FILE: docs/input_reference_main.md ================================================ # Main Input File Reference In addition to calling the `set()` method on {py:class}`FlorisModel`, users can configure FLORIS with an input file. The file must be YAML format with either "yaml" or "yml" extension. The below definitions guide a user to the top, mid, and lower level parameterizations. A few reference input files are available in the [floris/examples](https://github.com/NatLabRockies/floris/tree/main/examples/inputs) folder. ```{eval-rst} .. autoyaml:: docs/gch.yaml ``` ================================================ FILE: docs/input_reference_turbine.md ================================================ # Turbine Input File Reference The turbine input file is an optional input used to define a custom turbine type. The file must be YAML format with either "yaml" or "yml" extension. See for more information on inspecting and creating the turbine definition. ```{eval-rst} .. autoyaml:: docs/nrel_5MW.yaml ``` ================================================ FILE: docs/installation.md ================================================ (installation)= # Installation FLORIS can be installed by downloading the source code or via the PyPI package manager with `pip`. The following sections detail how download and install FLORIS for each use case. (requirements)= ## Requirements FLORIS is a python package. FLORIS is intended to work with all [active versions of python](https://devguide.python.org/versions/). Support will drop for python versions once they reach end-of-life. It is highly recommended that users work within a virtual environment for both working with and working on FLORIS, to maintain a clean and sandboxed environment. The simplest way to get started with virtual environments is through [conda](https://docs.conda.io/en/latest/miniconda.html). ```{note} FLORIS support for python version 3.8 and 3.9 was recently ended as they reached [end-of-life](https://devguide.python.org/versions/). FLORIS v4.3 also made the move to requiring `numpy` version 2. See the [numpy documentation for details](https://numpy.org/doc/stable/numpy_2_0_migration_guide.html). ``` ```{warning} Support for python version 3.9 will end in the latter half of 2025 as it reaches [end-of-life](https://devguide.python.org/versions/). ``` Installing into a Python environment that contains a previous version of FLORIS may cause conflicts. If you intend to use [pyOptSparse](https://mdolab-pyoptsparse.readthedocs-hosted.com/en/latest/) with FLORIS, it is recommended to install that package first before installing FLORIS. ```{note} If upgrading, it is highly recommended to install FLORIS v4 into a new virtual environment. ``` (pip)= ## Pip The simplest method is with `pip` by using this command: ```bash pip install floris ``` (source)= ## Source Code Installation Developers and anyone who intends to inspect the source code or wants to run examples can install FLORIS by downloading the git repository from GitHub with ``git`` and use ``pip`` to locally install it. The following commands in a terminal or shell will download and install FLORIS. ```bash # Download the source code from the `main` branch git clone -b main https://github.com/NatLabRockies/floris.git # If using conda, be sure to activate your environment prior to installing # conda activate # 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: ```python >>> import floris >>> help(floris) Help on package floris: NAME floris 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.6.4 FILE ~/floris/floris/__init__.py ``` (developers)= ## Developer Installation For users that will also be contributing to the FLORIS code repository, the process is similar to the source code installation, but with a few extra considerations. The steps are laid out in our [developer's guide](dev_guide.md). (updating)= ## Updating FLORIS 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, and will require manually updating the software. (pip-update)= ### Pip ```bash pip install --upgrade floris # Alternatively, users can specify a particular version, for example: # pip install --upgrade floris==3.2.1 ``` (source-update)= ### From Source ```bash # If you're not already on the main branch, save your changes and move there git checkout main # Pull down the changes from GitHub git pull main ``` ================================================ FILE: docs/intro_concepts.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "id": "86e53920", "metadata": {}, "source": [ "(intro_concepts)=\n", "# Introductory Concepts\n", "\n", "FLORIS is a Python-based software library for calculating wind farm performance considering\n", "the effect of turbine-turbine interactions through their wakes.\n", "There are two primary packages to understand when using FLORIS:\n", "- `floris.core`: This package contains the core functionality for calculating the wind farm wake\n", " and turbine-turbine interactions. This package is the computational engine of FLORIS.\n", " All of the mathematical models and algorithms are implemented here.\n", "- `floris`: This is the top-level package that provides most of the functionality that the\n", " majority of users will need. The main entry point is `FlorisModel` which is a high-level\n", " interface to the computational engine.\n", "\n", "\n", "\n", "Users of FLORIS will develop a Python script with the following sequence of steps:\n", "\n", "1. Load inputs and preprocess data\n", "2. Run the wind farm wake calculation\n", "3. Extract data and postprocess results\n", "\n", "Generally, users will only interact with `floris` and most often through the `FlorisModel` class.\n", "Additionally, `floris` contains functionality for comparing results, creating visualizations,\n", "and developing optimization cases. \n", "\n", "This notebook steps through the basic ideas and operations of FLORIS while showing\n", "realistic uses and expected behavior." ] }, { "cell_type": "markdown", "id": "699c51dd", "metadata": {}, "source": [ "## Initialize Floris\n", "\n", "The `FlorisModel` class provides functionality to build a wind farm representation and drive\n", "the simulation. This object is created (instantiated) by passing the path to a FLORIS input\n", "file as the only argument. After this object is created, it can immediately be used to\n", "inspect the data." ] }, { "cell_type": "code", "execution_count": 1, "id": "602f311c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " x y\n", " 0.0, 0.0\n", " 630.0, 0.0\n", "1260.0, 0.0\n" ] } ], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from floris import FlorisModel\n", "\n", "fmodel = FlorisModel(\"gch.yaml\")\n", "x, y = fmodel.get_turbine_layout()\n", "\n", "print(\" x y\")\n", "for _x, _y in zip(x, y):\n", " print(f\"{_x:6.1f}, {_y:6.1f}\")" ] }, { "cell_type": "markdown", "id": "e1eaeb53", "metadata": {}, "source": [ "## Build the model\n", "\n", "At this point, FLORIS has been initialized with the data defined in the input file.\n", "However, it is often simplest to define a basic configuration in the input file as\n", "a starting point and then make modifications in the Python script. This allows for\n", "generating data algorithmically or loading data from a data file. Modifications to\n", "the wind farm representation are handled through the `FlorisModel.set()`\n", "function with keyword arguments. Another way to think of this function is that it\n", "changes the value of inputs specified in the input file.\n", "\n", "Let's change the location of turbines in the wind farm. The code below changes the\n", "initial 3x1 layout to a 2x2 rectangular layout." ] }, { "cell_type": "code", "execution_count": 2, "id": "d040b810", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " x y\n", " 0.0, 0.0\n", " 0.0, 400.0\n", " 800.0, 0.0\n", " 800.0, 400.0\n" ] } ], "source": [ "x_2x2 = [0, 0, 800, 800]\n", "y_2x2 = [0, 400, 0, 400]\n", "fmodel.set(layout_x=x_2x2, layout_y=y_2x2)\n", "\n", "x, y = fmodel.get_turbine_layout()\n", "\n", "print(\" x y\")\n", "for _x, _y in zip(x, y):\n", " print(f\"{_x:6.1f}, {_y:6.1f}\")" ] }, { "cell_type": "markdown", "id": "63f45e11", "metadata": {}, "source": [ "Additionally, we can change the wind speeds, wind directions, and turbulence intensity.\n", "The set of wind conditions is given as arrays of wind speeds, wind directions, and turbulence\n", "intensity combinations that describe the atmospheric conditions to compute.\n", "This requires that all arrays be the same length.\n", "\n", "Notice that we can give `FlorisModel.set()` multiple keyword arguments at once.\n", "There is no expected output from the `FlorisModel.set()` function." ] }, { "cell_type": "code", "execution_count": 3, "id": "6f9d834a", "metadata": {}, "outputs": [], "source": [ "fmodel.set(wind_directions=[270.0], wind_speeds=[8.0], turbulence_intensities=[0.1])\n", "\n", "fmodel.set(\n", " wind_directions=[270.0, 280.0],\n", " wind_speeds=[8.0, 8.0],\n", " turbulence_intensities=[0.1, 0.1],\n", ")\n", "\n", "fmodel.set(\n", " wind_directions=[270.0, 280.0, 270.0, 280.0],\n", " wind_speeds=[8.0, 8.0, 9.0, 9.0],\n", " turbulence_intensities=[0.1, 0.1, 0.1, 0.1],\n", ")" ] }, { "cell_type": "markdown", "id": "da4f3309", "metadata": {}, "source": [ "`FlorisModel.set()` creates all of the basic data structures required\n", "for the simulation but it does not do any aerodynamic calculations. The low level\n", "data structures have a complex shape that enables faster computations. Specifically,\n", "most data is structured as a 4-dimensional Numpy array with the following dimensions:\n", "\n", "```python\n", "np.array(\n", " (\n", " findex,\n", " turbines,\n", " grid-1,\n", " grid-2\n", " )\n", ")\n", "```\n", "\n", "The `findex` dimension contains the index to a particular calculation in the overall data\n", "domain. This typically represents a unique combination of wind direction and wind speed\n", "making up a wind condition, but it can also be used to represent any other varying quantity.\n", "\n", "For example, we can see the shape of the data structure for the grid point x-coordinates\n", "for the all turbines and get the x-coordinates of grid points for the third turbine in\n", "the first wind condition. We can also plot all the grid points in\n", "space to get an idea of the overall form of our grid." ] }, { "cell_type": "code", "execution_count": 4, "id": "01ea3a98", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensions of grid x-components\n", "(4, 4, 3, 3)\n", "\n", "3rd turbine x-components for first wind condition (at findex=0)\n", "[[800. 800. 800.]\n", " [800. 800. 800.]\n", " [800. 800. 800.]]\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "print(\"Dimensions of grid x-components\")\n", "print(np.shape(fmodel.core.grid.x_sorted))\n", "\n", "print()\n", "print(\"3rd turbine x-components for first wind condition (at findex=0)\")\n", "print(fmodel.core.grid.x_sorted[0, 2, :, :])\n", "\n", "x = fmodel.core.grid.x_sorted[0, :, :, :]\n", "y = fmodel.core.grid.y_sorted[0, :, :, :]\n", "z = fmodel.core.grid.z_sorted[0, :, :, :]\n", "\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111, projection=\"3d\")\n", "ax.scatter(x, y, z, marker=\".\")\n", "ax.set_zlim([0, 150])\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "ebfdc746", "metadata": {}, "source": [ "## Run the Floris wake calculation\n", "\n", "Running the wake calculation is a one-liner. This will calculate the velocities\n", "at each turbine given the wake of other turbines for every wind speed and wind\n", "direction combination. Since we have not explicitly specified yaw control settings\n", "when creating the `FlorisModel` settings, all turbines are aligned with the inflow." ] }, { "cell_type": "code", "execution_count": 5, "id": "e3bf1698", "metadata": {}, "outputs": [], "source": [ "fmodel.run()" ] }, { "cell_type": "markdown", "id": "e11352e8", "metadata": {}, "source": [ "## Get turbine power\n", "\n", "At this point, the simulation has completed and we can use `FlorisModel` to\n", "extract useful information such as the power produced at each turbine. Remember that\n", "we have configured the simulation with four wind conditions and four turbines." ] }, { "cell_type": "code", "execution_count": 6, "id": "cc05bfe7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensions of `powers`\n", "(4, 4)\n", "\n", "Turbine powers for 8 m/s\n", "Wind condition 0\n", " Turbine 0 - 1,753.95 kW\n", " Turbine 1 - 1,753.95 kW\n", " Turbine 2 - 904.68 kW\n", " Turbine 3 - 904.85 kW\n", "\n", "Wind condition 1\n", " Turbine 0 - 1,753.95 kW\n", " Turbine 1 - 1,753.95 kW\n", " Turbine 2 - 1,644.86 kW\n", " Turbine 3 - 1,643.39 kW\n", "\n", "Turbine powers for all turbines at all wind conditions\n", "[[1753.95445918 1753.95445918 904.68478734 904.84672946]\n", " [1753.95445918 1753.95445918 1644.85720431 1643.39012544]\n", " [2496.42786184 2496.42786184 1276.4580679 1276.67310219]\n", " [2496.42786184 2496.42786184 2354.40522998 2352.47398836]]\n" ] } ], "source": [ "powers = fmodel.get_turbine_powers() / 1000.0 # calculated in Watts, so convert to kW\n", "\n", "print(\"Dimensions of `powers`\")\n", "print( np.shape(powers) )\n", "\n", "N_TURBINES = fmodel.core.farm.n_turbines\n", "\n", "print()\n", "print(\"Turbine powers for 8 m/s\")\n", "for i in range(2):\n", " print(f\"Wind condition {i}\")\n", " for j in range(N_TURBINES):\n", " print(f\" Turbine {j} - {powers[i, j]:7,.2f} kW\")\n", " print()\n", "\n", "print(\"Turbine powers for all turbines at all wind conditions\")\n", "print(powers)" ] }, { "cell_type": "markdown", "id": "8ab273db", "metadata": {}, "source": [ "## Applying yaw angles\n", "\n", "Yaw angles are another configuration option through `FlorisModel.set`.\n", "In order to fit into the vectorized framework, the yaw settings must be represented as\n", "a `Numpy.array` with dimensions equal to:\n", "- 0: findex\n", "- 1: number of turbines\n", "\n", "It is typically easiest to start with an array of 0's and modify individual\n", "turbine yaw settings, as shown below." ] }, { "cell_type": "code", "execution_count": 7, "id": "be78e20d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Yaw angle array initialized with 0's\n", "[[0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]]\n", "First turbine yawed 25 degrees for every atmospheric condition\n", "[[25. 0. 0. 0.]\n", " [25. 0. 0. 0.]\n", " [25. 0. 0. 0.]\n", " [25. 0. 0. 0.]]\n" ] } ], "source": [ "# Recall that the previous `fmodel.set()` command set up four atmospheric conditions\n", "# and there are 4 turbines in the farm. So, the yaw angles array must be 4x4.\n", "yaw_angles = np.zeros((4, 4))\n", "print(\"Yaw angle array initialized with 0's\")\n", "print(yaw_angles)\n", "\n", "print(\"First turbine yawed 25 degrees for every atmospheric condition\")\n", "yaw_angles[:, 0] = 25\n", "print(yaw_angles)\n", "\n", "fmodel.set(yaw_angles=yaw_angles)" ] }, { "cell_type": "markdown", "id": "1ef54dc5", "metadata": {}, "source": [ "## Start to finish\n", "\n", "Let's put it all together. The code below outlines these steps:\n", "1. Load an input file\n", "2. Modify the inputs with a more complex wind turbine layout and additional atmospheric conditions\n", "3. Calculate the velocities at each turbine for all atmospheric conditions\n", "4. Get the total farm power\n", "5. Develop the yaw control settings\n", "6. Calculate the velocities at each turbine for all atmospheric conditions with the new yaw settings\n", "7. Get the total farm power\n", "8. Compare farm power with and without wake steering" ] }, { "cell_type": "code", "execution_count": 8, "id": "205738aa", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Power % difference with yaw\n", " 270 degrees: 0.16%\n", " 280 degrees: 0.17%\n" ] } ], "source": [ "# 1. Load an input file\n", "fmodel = FlorisModel(\"gch.yaml\")\n", "\n", "# 2. Modify the inputs with a more complex wind turbine layout\n", "D = 126.0 # Design the layout based on turbine diameter\n", "x = [0, 0, 6 * D, 6 * D]\n", "y = [0, 3 * D, 0, 3 * D]\n", "wind_directions = [270.0, 280.0]\n", "wind_speeds = [8.0, 8.0]\n", "turbulence_intensities = [0.1, 0.1]\n", "\n", "# Pass the new data to FlorisInterface\n", "fmodel.set(\n", " layout_x=x,\n", " layout_y=y,\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", ")\n", "\n", "# 3. Calculate the velocities at each turbine for all atmospheric conditions\n", "# All turbines have 0 degrees yaw\n", "fmodel.run()\n", "\n", "# 4. Get the total farm power\n", "turbine_powers = fmodel.get_turbine_powers() / 1000.0 # Given in W, so convert to kW\n", "farm_power_baseline = np.sum(turbine_powers, 1) # Sum over the second dimension\n", "\n", "# 5. Develop the yaw control settings\n", "yaw_angles = np.zeros( (2, 4) ) # Construct the yaw array with dimensions for two wind directions, one wind speed, and four turbines\n", "yaw_angles[0, 0] = 25 # At 270 degrees, yaw the first turbine 25 degrees\n", "yaw_angles[0, 1] = 15 # At 270 degrees, yaw the second turbine 15 degrees\n", "yaw_angles[1, 0] = 10 # At 280 degrees, yaw the first turbine 10 degrees\n", "yaw_angles[1, 1] = 0 # At 280 degrees, yaw the second turbine 0 degrees\n", "fmodel.set(yaw_angles=yaw_angles)\n", "\n", "# 6. Calculate the velocities at each turbine for all atmospheric conditions with the new yaw settings\n", "fmodel.run()\n", "\n", "# 7. Get the total farm power\n", "turbine_powers = fmodel.get_turbine_powers() / 1000.0\n", "farm_power_yaw = np.sum(turbine_powers, 1)\n", "\n", "# 8. Compare farm power with and without wake steering\n", "difference = 100 * (farm_power_yaw - farm_power_baseline) / farm_power_baseline\n", "print(\"Power % difference with yaw\")\n", "print(f\" 270 degrees: {difference[0]:4.2f}%\")\n", "print(f\" 280 degrees: {difference[1]:4.2f}%\")" ] }, { "cell_type": "markdown", "id": "99b7465c", "metadata": {}, "source": [ "## Visualization\n", "\n", "While comparing turbine and farm powers is meaningful, a picture is worth at least\n", "1000 Watts, and `FlorisModel` provides powerful routines for visualization.\n", "\n", "The visualization functions require that the user select a single atmospheric condition\n", "to plot. The internal data structures still have the same shape but the wind speed and\n", "wind direction dimensions have a size of 1. This means that the yaw angle array used\n", "for plotting must have the same shape as above but a single atmospheric condition must\n", "be selected.\n", "\n", "Let's create a horizontal slice of each atmospheric condition from above with and without\n", "yaw settings included." ] }, { "cell_type": "code", "execution_count": 9, "id": "8bb179ff", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABNwAAAKqCAYAAADhZ0P2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd7wcV33//9e07Xdv173qzbLlinHBNsZUgxMbCL3E4WeTBALY+dJCgCSATTOQEJptCCHfwDcYHBJ6MWCMAeMqWa5qlm1Zxeq6ddvslPP748zM7t6ieq+kK3+ej8c+dndmtlxpd+fMez7nHEMppRBCCCGEEEIIIYQQQkwJ82i/ASGEEEIIIYQQQgghjicSuAkhhBBCCCGEEEIIMYUkcBNCCCGEEEIIIYQQYgpJ4CaEEEIIIYQQQgghxBSSwE0IIYQQQgghhBBCiCkkgZsQQgghhBBCCCGEEFNIAjchhBBCCCGEEEIIIaaQBG5CCCGEEEIIIYQQQkwhCdyEEEIIIYQQQgghhJhCErgJIcQkDMPgmmuuSe5/85vfxDAMnnrqqaP2ng7ElVdeyaJFi4722xBCCCGEEIdB2nRCzGwSuAkhDtiKFSu4+uqrOfXUU8nn8yxYsIA3vOENPPbYY+O2NQxj0stLX/rSlm3DMORzn/scixcvJpPJcMYZZ/Dd7353Wv+WG2+8EcMwOO+886b1dYQQQgghZqqZ3Pb70z/9Uzo7O9m5c+e4dcPDw8yePZvzzjuPMAyn9HWFECJmH+03IISYOT772c9y55138vrXv54zzjiDHTt2cP3113PWWWdxzz33cNpppyXb/td//de4x69cuZIvfelLvOxlL2tZ/o//+I985jOf4W1vexvnnnsuP/7xj/nzP/9zDMPgTW9607T8LTfddBOLFi3ivvvu4/HHH+eEE07Y72Pe8pa38KY3vYl0Oj0t70kIIYQQ4lgyk9t+N954I6eddhrvfe97+c53vtOy7h/+4R/Ys2cPv/zlLzFNqUERQkwTJYQQB+jOO+9Uruu2LHvsscdUOp1Wl19++X4f/1d/9VfKMAy1ZcuWZNnWrVuV4zjqqquuSpaFYaguuugiNW/ePOX7/tT9AZEnn3xSAeoHP/iB6u3tVddcc82E2wHqYx/72JS//nS74oor1MKFC4/22xBCCCHEDDfT236f/exnFaB+9atfJcvuu+8+ZZqm+vu///spe53pIm06IWY2ifOFEAfsuc99LqlUqmXZsmXLOPXUU1m7du0+H+u6Lt///vd5wQtewLx585LlP/7xj/E8j3e9613JMsMweOc738nWrVu5++67p/aPQFe3dXZ2ctlll/G6172Om2666YAeN9EYbmEYcs011zBnzhxyuRwvetGLWLNmDYsWLeLKK68c99g777yT973vffT29pLP53n1q1/N7t27x73WLbfcwkUXXUQ+n6etrY3LLruM1atXj9vuRz/6EaeddhqZTIbTTjuNH/7whwf97yGEEEIIMZGZ3vZ73/vexxlnnMG73vUuarUaQRDwjne8g4ULF/Kxj32Mhx9+mCuvvJIlS5aQyWTo7+/nL//yL9m7d2/yHA8//DCGYfCTn/wkWXb//fdjGAZnnXVWy+v96Z/+6bjhSqRNJ8QzlwRuQojDopRi586d9PT07HO7X/ziFwwNDXH55Ze3LH/ggQfI5/OcfPLJLcuf85znJOun2k033cRrXvMaUqkUb37zm9mwYQMrVqw4pOf68Ic/zLXXXss555zDP//zP7Ns2TIuueQSyuXyhNv/7d/+LQ899BAf+9jHeOc738lPf/pTrr766pZt/uu//ovLLruMQqHAZz/7WT7ykY+wZs0anve857WEfb/+9a957Wtfi2EYXHfddbzqVa/irW99KytXrjykv0UIIYQQYn9mUtvPtm2+/vWvs3HjRj7xiU9w/fXXs2rVKr761a+Sy+W49dZbefLJJ3nrW9/KV77yFd70pjdx8803c+mll6KUAuC0006jo6ODP/zhD8nz3nHHHZimyUMPPcTIyAigT8LeddddPP/5z0+2kzadEM9wR7nCTggxw/3Xf/2XAtR//Md/7HO71772tSqdTqvBwcGW5ZdddplasmTJuO3L5bIC1Ic+9KGpfLtq5cqVClC33nqrUkp3YZg3b55697vfPW5bxnQp/c///E8FqI0bNyqllNqxY4eybVu96lWvanncNddcowB1xRVXjHvsxRdfrMIwTJa/973vVZZlqaGhIaWUUqOjo6qjo0O97W1va3nOHTt2qPb29pblZ555ppo9e3byWKWU+vWvf60A6X4ghBBCiGkx09p+Sil19dVXK8dxVKFQUG9+85uT5ZVKZdy23/3udxWg/vCHP7S85+c85znJ/de85jXqNa95jbIsS91yyy1KKaVWrVqlAPXjH/9YKSVtOiGEdCkVQhyGdevWcdVVV3HBBRdwxRVXTLrdyMgIP//5z7n00kvp6OhoWVetViechCCTySTrp9JNN91EX18fL3rRiwDdheGNb3wjN998M0EQHNRz3Xbbbfi+39IlAnQV22Te/va3YxhGcv+iiy4iCAI2bdoEwK233srQ0BBvfvOb2bNnT3KxLIvzzjuP22+/HYDt27fz4IMPcsUVV9De3p4830tf+lJOOeWUg/o7hBBCCCEOxExs+wF86lOforu7G9M0+cIXvpAsz2azye1arcaePXs4//zzAVi1alWy7qKLLmLVqlVJD4Y//vGPXHrppZx55pnccccdgK56MwyD5z3veYC06YQQMkupEOIQ7dixg8suu4z29nb+93//F8uyJt32+9//PrVabVyXAtANHdd1xy2v1WrJ+slUq1WGh4dblvX390+6fRAE3HzzzbzoRS9i48aNyfLzzjuPz3/+89x2223jZtHalzgkGzvDaVdXF52dnRM+ZsGCBS334+0GBwcB2LBhAwAvfvGLJ3x8sVhsee1ly5aN2+akk05qaSQKIYQQQhyumdj2ixWLRU466ST27NlDX19fsnxgYIBrr72Wm2++mV27drU8pvl1LrroInzf5+6772b+/Pns2rWLiy66iNWrV7cEbqeccgpdXV2AtOmEEBK4CSEOwfDwMH/6p3/K0NAQd9xxB3PmzNnn9jfddBPt7e28/OUvH7du9uzZ3H777SilWiq/tm/fDrDP5/7v//5v3vrWt7YsU9F4GxP57W9/y/bt27n55pu5+eabJ3yfBxO4HYrJGqfx+w7DENBjfkzUgLRt+dkWQgghxJE1U9t++/OGN7yBu+66iw984AOceeaZFAoFwjDkT/7kT5I2GcA555xDJpPhD3/4AwsWLGDWrFmceOKJXHTRRdx44424rssdd9zBq1/96uQx0qYTQsi3XAhxUGq1Gq94xSt47LHH+M1vfrPfUvft27dz++23c+WVV07YfeDMM8/kG9/4BmvXrm15rnvvvTdZP5lLLrmEW2+99YDf+0033cSsWbO44YYbxq37wQ9+wA9/+EO+9rWv7fPMarOFCxcC8Pjjj7N48eJk+d69e5OKtYO1dOlSAGbNmsXFF1+839eOz542W79+/SG9thBCCCHEWDO57bcvg4OD3HbbbVx77bV8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b32Lb37zm3z0ox+dzrcuhBBCCCH2Qdp5QgghhBCTM5RS6ki92O7du5k1axa///3vef7zn8/w8DC9vb185zvf4XWvex0A69at4+STT+buu+/m/PPP55ZbbuHlL38527Zto6+vD4Cvfe1rfPCDH2T37t2kUqn9vu7IyAjt7e18z1pKzrCm9W8UQgghxPGhogLeEDzB8PAwxWLxaL+dY56084QQQggxUxyJdt4RHcNteHgYgK6uLgDuv/9+PM/j4osvTrZZvnw5CxYs4O677wbg7rvv5vTTT08aYQCXXHIJIyMjrF69+gi+eyGEEEIIMRlp5wkhhBBCNNhH6oXCMOQ973kPF154IaeddhoAO3bsIJVK0dHR0bJtX18fO3bsSLZpboTF6+N1E3FdF9d1k/sjIyNT9WcIIYQQQogxpJ0nhBBCCNHqiFW4XXXVVTz66KPcfPPN0/5a1113He3t7cll/vz50/6aQgghhBDPVNLOE0IIIYRodUQCt6uvvpqf/exn3H777cybNy9Z3t/fT71eZ2hoqGX7nTt30t/fn2wzdjar+H68zVgf/vCHGR4eTi5btmyZwr9GCCGEEELEpJ0nhBBCCDHetAZuSimuvvpqfvjDH/Lb3/6WxYsXt6w/++yzcRyH2267LVm2fv16Nm/ezAUXXADABRdcwCOPPMKuXbuSbW699VaKxSKnnHLKhK+bTqcpFostl/+fvfuOj+yq7///un2aelltb173buy16cWxIU6IScEBvmAnBBJikxCT0AN2GmBDAhjbwLdAyC/UACYhNGOwHWDd1rtu27z2eru2qk677fz+OPdOkbTV0q4kf56Phx4zc+dqNNLMaM687+d8jhBCCCGEmDwyzhNCCCGEOLQp7eF2/fXX87WvfY3vf//7tLS01HpxtLW1kc1maWtr4+1vfzs33ngjnZ2dtLa28u53v5vLLruMSy+9FIArrriCM888k7e+9a3ccsst9Pf385GPfITrr78ez/Om8u4LIYQQQohDkHGeEEIIIcShGUopNWU3bhgTbv/yl7/MddddB0ClUuG9730vX//616lWq1x55ZXccccdTdMItm7dyrve9S7uvfde8vk81157LZ/4xCew7aPLC2W5eCGEEEIcqxOxXPxMJuM8IYQQQsxUJ2KcN6WB23QhAzEhhBBCHCsJ3GYGGecJIYQQ4lidiHHeCVul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9/Anf/In/OQnPzmhv694YZBx3swi47yZTcZ6s4uM82aXGTfOU2JWuOSSS9T1119fuxxFkZo3b576+Mc/fhLvlZjIxz72MXXeeedNeN3g4KByHEd9+9vfrm1bv369AtSqVauUUkr98Ic/VKZpqv7+/to+d955p2ptbVXVanVK77toBqjvfe97tctxHKu+vj5166231rYNDg4qz/PU17/+daWUUuvWrVOAevjhh2v7/OhHP1KGYaidO3cqpZS64447VEdHR9Pj+f73v1+ddtppU/wbibGPqVJKXXvttep3fud3Dvk98phOb3v37lWAuu+++5RSk/d/9n3ve58666yzmn7WNddco6688sqp/pXEC5CM82YOGefNLjLWm11knDf7TPdxnlS4zQK+77N69Wouv/zy2jbTNLn88stZtWrVSbxn4lCefvpp5s2bx7Jly3jLW97Ctm3bAFi9ejVBEDQ9lqeffjqLFi2qPZarVq3inHPOYc6cObV9rrzySoaHh3nqqadO7C8immzZsoX+/v6mx6+trY2VK1c2PX7t7e286EUvqu1z+eWXY5omDz74YG2fl7/85biuW9vnyiuvZOPGjQwMDJyg30Y0uvfee+nt7eW0007jXe96FwcOHKhdJ4/p9DY0NARAZ2cnMHn/Z1etWtV0G+k+8r4rJpuM82YeGefNXjLWm51knDdzTfdxngRus8D+/fuJoqjpCQMwZ84c+vv7T9K9EoeycuVKvvKVr/DjH/+YO++8ky1btvCyl72MkZER+vv7cV2X9vb2pu9pfCz7+/snfKzT68TJk/79D/da7O/vp7e3t+l627bp7OyUx3iaeu1rX8tXv/pV7rnnHj75yU9y33338brXvY4oigB5TKezOI55z3vew0te8hLOPvtsgEn7P3uofYaHhymXy1Px64gXKBnnzSwyzpvdZKw3+8g4b+aaCeM8+5h+IyHE8/a6172udv7cc89l5cqVLF68mG9961tks9mTeM+EEBP5wz/8w9r5c845h3PPPZfly5dz77338prXvOYk3jNxJNdffz1PPvlkU/8kIYSYSjLOE2JmkXHezDUTxnlS4TYLdHd3Y1nWuJU39uzZQ19f30m6V+Jotbe3c+qpp7J582b6+vrwfZ/BwcGmfRofy76+vgkf6/Q6cfKkf//DvRb7+vrGNbkOw5CDBw/KYzxDLFu2jO7ubjZv3gzIYzpd3XDDDfzgBz/gF7/4BQsWLKhtn6z/s4fap7W1VT5Ui0kl47yZTcZ5s4uM9WY/GefNDDNlnCeB2yzgui4XXXQR99xzT21bHMfcc889XHbZZSfxnomjMTo6yjPPPMPcuXO56KKLcByn6bHcuHEj27Ztqz2Wl112GU888UTTP/67776b1tZWzjzzzBN+/0Xd0qVL6evra3r8hoeHefDBB5sev8HBQVavXl3b5+c//zlxHLNy5craPvfffz9BENT2ufvuuznttNPo6Og4Qb+NOJQdO3Zw4MAB5s6dC8hjOt0opbjhhhv43ve+x89//nOWLl3adP1k/Z+97LLLmm4j3Ufed8Vkk3HezCbjvNlFxnqzn4zzprcZN847joUgxDT0jW98Q3mep77yla+odevWqXe+852qvb29aeUNMT28973vVffee6/asmWL+tWvfqUuv/xy1d3drfbu3auUUurP/uzP1KJFi9TPf/5z9cgjj6jLLrtMXXbZZbXvD8NQnX322eqKK65Qa9euVT/+8Y9VT0+P+uAHP3iyfqUXlJGREbVmzRq1Zs0aBah//ud/VmvWrFFbt25VSin1iU98QrW3t6vvf//76vHHH1e/8zu/o5YuXarK5XLtNl772teqCy64QD344IPql7/8pVqxYoV605veVLt+cHBQzZkzR731rW9VTz75pPrGN76hcrmc+uIXv3jCf98XgsM9piMjI+qv//qv1apVq9SWLVvUz372M3XhhReqFStWqEqlUrsNeUynj3e9612qra1N3XvvvWr37t21r1KpVNtnMv7PPvvssyqXy6m/+Zu/UevXr1e33367sixL/fjHPz6hv694YZBx3swh47yZT8Z6s4uM82aXmTbOk8BtFrntttvUokWLlOu66pJLLlEPPPDAyb5LYgLXXHONmjt3rnJdV82fP19dc801avPmzbXry+Wy+vM//3PV0dGhcrmcesMb3qB2797ddBvPPfecet3rXqey2azq7u5W733ve1UQBCf6V3lB+sUvfqGAcV/XXnutUkovF/+3f/u3as6cOcrzPPWa17xGbdy4sek2Dhw4oN70pjepQqGgWltb1R/90R+pkZGRpn0ee+wx9dKXvlR5nqfmz5+vPvGJT5yoX/EF53CPaalUUldccYXq6elRjuOoxYsXq3e84x3jPuTKYzp9TPRYAurLX/5ybZ/J+j/7i1/8Qp1//vnKdV21bNmypp8hxGSTcd7MIOO8mU/GerOLjPNml5k2zjOSOy2EEEIIIYQQQgghhJgE0sNNCCGEEEIIIYQQQohJJIGbEEIIIYQQQgghhBCTSAI3IYQQQgghhBBCCCEmkQRuQgghhBBCCCGEEEJMIgnchBBCCCGEEEIIIYSYRBK4CSGEEEIIIYQQQggxiSRwE0IIIYQQQgghhBBiEkngJoQQQgghhBBCCCHEJJLATQghhBBCCCGEEEKISSSBmxBCCCGEEEIIIYQQk0gCNyGEEEIIIYQQQgghJpEEbkIIIYQQQgghhBBCTCIJ3IQQ4hAMw+Cmm26qXf7KV76CYRg899xzJ+0+HY3rrruOJUuWnOy7IYQQQgghngcZ0wkxs0ngJoQ4ag8//DA33HADZ511Fvl8nkWLFvHGN76RTZs2Tbj/t771LS699FLa29vp6uriFa94Bf/93/89br84jrnllltYunQpmUyGc889l69//etT+rvccccdGIbBypUrp/TnCCGEEELMVDN57Pe6172Ojo4O9uzZM+66oaEh5s6dy8qVK4njeFJ/rhBCpCRwE0IctU9+8pN85zvf4TWveQ2f/exneec738n999/PhRdeyJNPPtm072233cY111xDd3c3n/jEJ/jbv/1bhoaG+K3f+i2++93vNu374Q9/mPe///38xm/8BrfddhuLFi3izW9+M9/4xjem7Hf593//d5YsWcJDDz3E5s2bj+p73vrWt1Iul1m8ePGU3S8hhBBCiOliJo/97rjjDnzf56/+6q/GXfehD32I/fv386UvfQnTlI/EQogpooQQ4ij96le/UtVqtWnbpk2blOd56i1veUvT9hUrVqiLL75YxXFc2zY0NKQKhYJ6/etfX9u2Y8cO5TiOuv7662vb4jhWL3vZy9SCBQtUGIaT/ns8++yzClDf/e53VU9Pj7rpppsm3A9QH/vYxyb950+1a6+9Vi1evPhk3w0hhBBCzHAzfez3yU9+UgHqJz/5SW3bQw89pEzTVO973/sm7edMFRnTCTGzSZwvhDhqL37xi3Fdt2nbihUrOOuss1i/fn3T9uHhYXp7ezEMo7attbWVQqFANputbfv+979PEAT8+Z//eW2bYRi8613vYseOHaxatWrSf49///d/p6Ojg6uuuorf//3f59///d+P6vsm6uEWxzE33XQT8+bNI5fL8apXvYp169axZMkSrrvuunHf+6tf/Yobb7yRnp4e8vn8/8/efcfJWZf7/3/dc08vu7O9pAMhhZZA6MWGoKAexV5Rz9EjRxCB0/ye3znqQcGGoPjFcor6PaJ4VOyiIi2hI0hLIKGEkLZJNrs7fe65574/vz/uMjNbkk2yPdfz8dhHktnNzmz2hvu91/X5XB/e8pa3sGfPnhHPddttt3H22WeTSCRIpVJceOGFrF+/fsTH/eIXv+DYY48lGo1y7LHH8vOf//yA/z2EEEIIIUYz27PflVdeyfHHH8/f/d3fUS6XsSyLj33sYyxatIhPf/rTPPnkk3zwgx/kiCOOIBqN0t3dzYc//GH27t3rf44nn3wSTdP41a9+5T/26KOPomkaJ554YsPzvf71rx8xrkQynRCHLym4CSEOiVKKXbt20d7e3vD4K1/5Sn7/+99z44038tJLL/Hss8/y8Y9/nEwmw+WXX+5/3F/+8hcSiQQrVqxo+PunnHKK//6JdvPNN3PRRRcRDod597vfzXPPPccjjzxyUJ/rU5/6FJ/97GdZs2YNX/7yl1m6dCnnn38+hUJh1I+/7LLLeOKJJ/j0pz/NJZdcwq9//WsuvfTSho/5n//5Hy688EKSySRf/OIX+dd//Vc2bNjAWWed1VDs++Mf/8hb3/pWNE3j2muv5c1vfjMf+tCH+POf/3xQX4sQQgghxP7MpuwXDAb5zne+w+bNm7n66qv5xje+wWOPPcY3v/lN4vE4t99+Oy+++CIf+tCHuPHGG3nXu97FLbfcwgUXXIBSCoBjjz2WdDrN2rVr/c+7bt06AoEATzzxBNlsFnCasPfffz/nnHOO/3GS6YQ4zE3zCjshxCz3P//zPwpQ//Vf/9Xw+K5du9RrXvMaBfhv7e3t6v7772/4uAsvvFAdccQRIz5voVBQgPrnf/7nCX29f/7znxWgbr/9dqWUs4Vh/vz56vLLLx/xsQzbUvrd735XAWrz5s1KKaX6+vpUMBhUb37zmxv+3mc+8xkFqIsvvnjE3z333HMbtlpcccUVStd1NTQ0pJRSKpfLqXQ6rT7ykY80fM6+vj7V3Nzc8PiqVatUT0+P/3eVUuqPf/yjAmT7gRBCCCEmxWzLfkopdemll6pQKKSSyaR697vf7T9eLBZHfOyPfvQjBai1a9c2vOZTTjnF//NFF12kLrroIqXrurrtttuUUko99thjClC//OUvlVKS6YQQsqVUCHEIvM7l6aefzsUXX9zwvng8zrJly7j44ov5yU9+wn//93/T09PDRRdd1HBIQalUIhKJjPjc0WjUf/9Euvnmm+nq6uJVr3oV4GxheOc738ktt9yCZVkH9LnuuOMOqtVqw5YIcFaxjeWjH/1ow1aLs88+G8uy2LJlCwC33347Q0NDvPvd76a/v99/03WdU089lbvuuguAnTt38vjjj3PxxRfT3Nzsf77Xvva1rFy58oC+DiGEEEKI8ZiN2Q/g85//PG1tbQQCAa6//nr/8fqtruVymf7+fk477TQAHnvsMf99Z599No899pi/g+Hee+/lggsuYNWqVaxbtw5wVr1pmsZZZ50FSKYTQkBwul+AEGJ26uvr48ILL6S5uZmf/vSn6Lre8P63v/3tBINBfv3rX/uP/dVf/RVLly7lX/7lX/jxj38MOEHHMIwRn79cLvvvH0upVCKTyTQ81t3dPebHW5bFLbfcwqte9So2b97sP37qqady3XXXcccdd3Deeeft46tu5BXJjjrqqIbHW1tbaWlpGfXvLFy4sOHP3scNDg4C8NxzzwHw6le/etS/39TU1PDcS5cuHfExy5YtawiJQgghhBCHajZmP09TUxPLli2jv7+frq4u//GBgQE++9nPcsstt7B79+6Gv1P/PGeffTbVapUHHniABQsWsHv3bs4++2zWr1/fUHBbuXIlra2tgGQ6IYQU3IQQByGTyfD617+eoaEh1q1bR29vb8P7X3zxRX7/+9/zne98p+Hx1tZWzjrrLO677z7/sZ6eHu666y6UUg0rv3bu3Akw4nPX+/GPf8yHPvShhseUO29jNHfeeSc7d+7klltuGfXY+ZtvvvmACm4HY3g49Xiv27ZtwJn5MVqADAblf9tCCCGEmFqzNfvtzzve8Q7uv/9+/uEf/oFVq1aRTCaxbZvXve51fiYDWLNmDdFolLVr17Jw4UI6Ozs5+uijOfvss7npppswDIN169bxlre8xf87kumEEPJfuRDigJTLZd74xjeyadMm/vSnP4261H3Xrl0Ao27RNE2TarXq/3nVqlX853/+J88880zD53rooYf894/l/PPP5/bbbx/3a7/55pvp7Ozk//7f/zvifbfeeis///nP+da3vrXPzmq9RYsWAfD888+zZMkS//G9e/f6K9YO1JFHHglAZ2cn55577n6f2+ue1tu4ceNBPbcQQgghxHCzOfvty+DgIHfccQef/exn+bd/+zf/8dGyVTgc5pRTTmHdunUsXLiQs88+G3BWvhmGwc0338yuXbsaDkyQTCeEkBluQohxsyyLd77znTzwwAP85Cc/4fTTTx/144466igCgQA//vGPG7qO27ZtY926daxevdp/7K/+6q8IhULcdNNN/mNKKb71rW8xb948zjjjjDFfT09PD+eee27D21hKpRK33norb3jDG3jb29424u3SSy8ll8s1HPm+P695zWsIBoN885vfbHj8G9/4xrg/x3Dnn38+TU1NXHPNNZimOeL9e/bsAZyvfdWqVXz/+99v2PJw++23s2HDhoN+fiGEEEIIz2zOfvvj7ToYvkLuhhtuGPXjzz77bB566CHuuusuv+DW3t7OihUr+OIXv+h/jEcynRBCVrgJIcbtqquu4le/+hVvfOMbGRgY4Ac/+EHD+9/3vvcB0NHRwYc//GH+8z//k9e85jVcdNFF5HI5brrpJkqlEp/61Kf8vzN//nw++clP8uUvfxnTNDn55JP5xS9+wbp167j55pvH3IJ5oH71q1+Ry+V405veNOr7TzvtNDo6Orj55pt55zvfOa7P2dXVxeWXX851113Hm970Jl73utfxxBNPcNttt9He3t6wTWK8mpqa+OY3v8n73/9+TjzxRN71rnfR0dHByy+/zG9/+1vOPPNMv6B37bXXcuGFF3LWWWfx4Q9/mIGBAW688UaOOeYY8vn8AT+3EEIIIUS92Zz99qepqYlzzjmHL33pS5imybx58/jjH//YMOe33tlnn83nP/95tm7d2lBYO+ecc/j2t7/N4sWLmT9/fsPnl0wnxGFuuo5HFULMPq94xSsajnof/lbPNE114403qlWrVqlkMqmSyaR61atepe68884Rn9eyLHXNNdeoRYsWqXA4rI455hj1gx/8YEJf+xvf+EYVjUZVoVAY82M++MEPqlAopPr7+5VSSgHq05/+tP/+7373uwpQmzdv9h+rVqvqX//1X1V3d7eKxWLq1a9+tXrmmWdUW1ub+tjHPjbi7z7yyCMNz3nXXXcpQN11110jHj///PNVc3Ozikaj6sgjj1Qf/OAH1Z///OeGj/vZz36mVqxYoSKRiFq5cqW69dZb1cUX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0oLkc5/7HJqmTbisXbu2fXuz2eTDH/4w3d3dpNNpfu3Xfo29e/dOeI7t27dz7bXXkkwm6evr44/+6I9wXfdEv5V5yQMPPMCv/MqvMDg4iKZp3HXXXRNuV0rxmc98hiVLlpBIJLjqqqt48cUXJ9xndHSU97znPWSzWfL5PB/4wAeoVqsT7vPss89yxRVXEI/HWb58ObfeeutMv7V5yeE+j/e///0H/b286U1vmnAf+Tymj5tvvpmLLrqITCZDX18fb3vb29i8efOE+0zX/6j77ruP888/n1gsxurVq/nmN785029PEBY1ovNODKLzZhfReXMP0XpzB9F5x44YbsJx8e1vf5sbbriBz372szz11FOce+65XHPNNezbt2+2D21BcuaZZ7Jnz5725aGHHmrfdv311/Of//mffOc73+H+++9n9+7dvP3tb2/f7nke1157LbZt8/DDD/MP//APfPOb3+Qzn/nMbLyVeUetVuPcc8/lq1/96qS333rrrXz5y1/m61//Oo899hipVIprrrmGZrPZvs973vMeNmzYwN133833v/99HnjgAX7/93+/fXu5XObqq69m5cqVPPnkk9x222187nOf4xvf+MaMv7/5xuE+D4A3velNE/5evvWtb024XT6P6eP+++/nwx/+MI8++ih33303juNw9dVXU6vV2veZjv9Rr7zyCtdeey2ve93rePrpp7nuuuv4vd/7PX70ox+d0PcrCIsF0XknFtF5s4fovLmHaL25g+i840AJwnFw8cUXqw9/+MPt7z3PU4ODg+rmm2+exaNamHz2s59V55577qS3FYtFZZqm+s53vtP+2caNGxWgHnnkEaWUUj/4wQ+UrutqaGiofZ+vfe1rKpvNKsuyZvTYFxqA+vd///f2977vq4GBAXXbbbe1f1YsFlUsFlPf+ta3lFJKPf/88wpQTzzxRPs+P/zhD5WmaWrXrl1KKaX+5m/+RhUKhQmfx4033qjWrFkzw+9ofnPg56GUUu973/vUW9/61ikfI5/HzLJv3z4FqPvvv18pNX3/oz75yU+qM888c8JrvfOd71TXXHPNTL8lQViUiM47cYjOmzuIzpt7iNabW4jOO3Ikw004Zmzb5sknn+Sqq65q/0zXda666ioeeeSRWTyyhcuLL77I4OAgJ598Mu95z3vYvn07AE8++SSO40z4LNauXcuKFSva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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from floris.flow_visualization import visualize_cut_plane\n", "from floris.layout_visualization import plot_turbine_labels\n", "\n", "fig, axarr = plt.subplots(2, 2, figsize=(15,8))\n", "\n", "# Plot the first wind condition\n", "wd = wind_directions[0]\n", "ws = wind_speeds[0]\n", "ti = turbulence_intensities[0]\n", "\n", "fmodel.reset_operation()\n", "fmodel.set(wind_speeds=[ws], wind_directions=[wd], turbulence_intensities=[ti])\n", "horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0)\n", "visualize_cut_plane(horizontal_plane, ax=axarr[0,0], title=\"270 - Aligned\")\n", "plot_turbine_labels(fmodel, axarr[0,0])\n", "\n", "fmodel.set(yaw_angles=yaw_angles[0:1])\n", "horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0)\n", "visualize_cut_plane(horizontal_plane, ax=axarr[0,1], title=\"270 - Yawed\")\n", "plot_turbine_labels(fmodel, axarr[0,1])\n", "\n", "# Plot the second wind condition\n", "wd = wind_directions[1]\n", "ws = wind_speeds[1]\n", "ti = turbulence_intensities[1]\n", "\n", "fmodel.reset_operation()\n", "fmodel.set(wind_speeds=[ws], wind_directions=[wd], turbulence_intensities=[ti])\n", "horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0)\n", "visualize_cut_plane(horizontal_plane, ax=axarr[1,0], title=\"280 - Aligned\")\n", "plot_turbine_labels(fmodel, axarr[1,0])\n", "\n", "fmodel.set(yaw_angles=yaw_angles[1:2])\n", "horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0)\n", "visualize_cut_plane(horizontal_plane, ax=axarr[1,1], title=\"280 - Yawed\")\n", "plot_turbine_labels(fmodel, axarr[1,1])\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "d84cf194", "metadata": {}, "source": [ "We can also plot the streamwise inflow velocities on the turbine rotor\n", "grid points located on the rotor plane. The `plot_rotor_values` function\n", "simply plots any data given as the first argument, so in this case\n", "`fi.floris.flow_field.u` contains the yawed calculation from above." ] }, { "cell_type": "code", "execution_count": 10, "id": "4eaa8343", "metadata": {}, "outputs": [], "source": [ "# Set the FlorisModel as it was before visualizing the cut planes\n", "fmodel.reset_operation()\n", "fmodel.set(\n", " layout_x=x,\n", " layout_y=y,\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", ")\n", "fmodel.run()" ] }, { "cell_type": "code", "execution_count": 11, "id": "3e517614", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgoAAAFyCAYAAACUWPJkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAuPUlEQVR4nO3de1xVZaL/8e8G5ZqQJQooIl6mjLTStAE1cjJJyW4TVsdG7KKVlaOlTZ5+peWkWWPq6aJZhqZmx7SLjZZpI1MqlZ6pSS2lRgUysHolYMhF935+fzjsaQtLWO4t2w2f9+u1Xuew9nqe9ez9yPDtuaztMMYYAQAA1CHI3w0AAACnL4ICAACwRFAAAACWCAoAAMASQQEAAFgiKAAAAEsEBQAAYImgAAAALBEUAACAJYICmp2cnBw5HA7l5OScNnVOnTpVDofD41ynTp00atQo7xvnQ6djmwCcWgQFBJQVK1bI4XDorbfeqvXaBRdcIIfDoY0bN9Z6rWPHjkpNTW2MJga8LVu2aOrUqSopKfF3U9y2bt2qe++9V8nJyYqMjFTHjh01fPhw5eXl1brW4XBYHldccYXHtS6XS0899ZSSkpIUFhamnj17avny5Y31toCA0MLfDQDs6N+/vyRp06ZNuu6669zny8rKtGPHDrVo0UKbN2/WwIED3a8VFhaqsLBQN910kyTp0ksvVUVFhUJCQhq38Tbt3r1bQUGNn+W3bNmixx57TKNGjdKZZ555WrRp5syZ2rx5szIzM9WzZ08VFxfrueeeU69evfTJJ5/o/PPPd1+7ZMmSWuW3bdumuXPnavDgwR7nH374YT355JMaPXq0+vTpo3feeUf/9V//JYfD4f73AjR3BAUElPj4eCUlJWnTpk0e53Nzc2WMUWZmZq3Xan6uCRlBQUEKCwtrnAZ7ITQ0tN5rysvLFRkZ2QitOaYhbToV7r//fr322mse4e7GG29Ujx499OSTT2rp0qXu87fcckut8jVTQzfffLP73P79+zVr1izdc889eu655yRJd9xxh9LS0jRp0iRlZmYqODj4FL4rIDAw9YCA079/f33++eeqqKhwn9u8ebOSk5M1ZMgQffLJJ3K5XB6vORwO9evXT1Ld6wkuu+wynX/++frqq680cOBARUREqH379nrqqadq3f+7777Ttddeq8jISLVt21YTJkxQVVVVg9u/adMm9enTR2FhYerSpYtefPHFOq87fj3AokWL5HA49Pe//11jx45V27Zt1aFDB/fr7733ngYMGKDIyEi1atVKGRkZ2rlzZ616d+3apeHDhysmJkbh4eE655xz9PDDD0s6tlZi0qRJkqSkpCT3kP2+ffvqbJMk7dmzR5mZmTrrrLMUERGh3/72t1qzZo3HNTWf+YoVK/TEE0+oQ4cOCgsL0+WXX65vv/223s8sNTW11ghQt27dlJycrK+//vqEZauqqrRq1SqlpaV5fF7vvPOOjhw5orFjx7rPORwO3X333fruu++Um5tbb7uA5oARBQSc/v37a8mSJfr000912WWXSToWBlJTU5WamqrS0lLt2LFDPXv2dL927rnn6uyzzz5hvQcPHtSVV16p66+/XsOHD9fKlSv1pz/9ST169NCQIUMkSRUVFbr88stVUFCgcePGKT4+XkuWLNHf/va3BrV9+/btGjx4sGJiYjR16lQdPXpUU6ZMUbt27Rr8/seOHauYmBg9+uijKi8vl3RsuD0rK0vp6emaOXOmDh8+rHnz5rlDVadOnSRJX375pQYMGKCWLVtqzJgx6tSpk/71r3/p3Xff1RNPPKHrr79eeXl5Wr58uWbPnq02bdpIkmJiYupsy4EDB5SamqrDhw9r3LhxOvvss7V48WJdffXVWrlypcf0kCQ9+eSTCgoK0sSJE1VaWqqnnnpKI0aM0Kefftrg91/DGKMDBw4oOTn5hNetXbtWJSUlGjFihMf5zz//XJGRkerevbvH+b59+7pfrxmFApo1AwSYnTt3Gklm2rRpxhhjjhw5YiIjI83ixYuNMca0a9fOPP/888YYY8rKykxwcLAZPXq0u/zGjRuNJLNx40b3ubS0NCPJvPrqq+5zVVVVJjY21vz+9793n5szZ46RZFasWOE+V15ebrp27Vqrzrpce+21JiwszOTn57vPffXVVyY4ONgc/+uYmJhosrKy3D9nZ2cbSaZ///7m6NGj7vOHDh0yZ555psd7NMaY4uJiEx0d7XH+0ksvNa1atfK4vzHGuFwu9///9NNPG0lm7969tdp/fJvGjx9vJJmPP/7Yoz1JSUmmU6dOxul0GmP+85l3797dVFVVua+dO3eukWS2b99e18d1QkuWLDGSzMKFC0943e9//3sTGhpqDh486HE+IyPDdO7cudb15eXlRpJ56KGHbLcJaIqYekDA6d69u84++2z32oN//vOfKi8vd+9qSE1N1ebNmyUdW7vgdDob9F+GZ5xxhsf8dkhIiPr27as9e/a4z61du1ZxcXG64YYb3OciIiI0ZsyYeut3Op1at26drr32WnXs2NHj/aSnp9dbvsbo0aM95s7Xr1+vkpIS3Xzzzfrpp5/cR3BwsC655BL3LpAff/xRH330kW677TaP+0uqtTWzodauXau+fft6fL5nnHGGxowZo3379umrr77yuP7WW2/1mEIYMGCAJHl8xg2xa9cu3XPPPUpJSVFWVpbldWVlZVqzZo2GDh1aa2FmRUVFnWsuatav/HpqC2jOCAoIOA6HQ6mpqe61CJs3b1bbtm3VtWtXSZ5Boeb/NiQodOjQodYfzNatW+vgwYPun/Pz89W1a9da151zzjn11v/jjz+qoqJC3bp1q/VaQ8rXSEpK8vj5m2++kST97ne/U0xMjMfxwQcf6IcffpD0nz/Gv94h4K38/Pw6214znJ+fn+9x/viA0rp1a0ny+IzrU1xcrIyMDEVHR2vlypUnXHC4atUqVVZW1pp2kKTw8PA615ZUVla6XwfAGgUEqP79++vdd9/V9u3b3esTaqSmpmrSpEnav3+/Nm3apPj4eHXu3LneOq3+4BhjfNZuXzj+D1jNws0lS5YoNja21vUtWpw+v+befsalpaUaMmSISkpK9PHHHys+Pv6E1y9btkzR0dG66qqrar0WFxenjRs3yhjjEfyKiookqd66gebi9PlfEMCGXz9PYfPmzRo/frz7td69eys0NFQ5OTn69NNPNXToUJ/dNzExUTt27Kj1x2X37t31lq3ZZVAzAvBrDSlvpUuXLpKktm3batCgQZbX1YSlHTt2nLA+O9MQiYmJdbZ9165d7td9pbKyUsOGDVNeXp42bNig884774TXFxUVaePGjRo1alSdUwwXXnihXn75ZX399dceddUsrLzwwgt91nYgkDH1gIB08cUXKywsTMuWLdP+/fs9RhRCQ0PVq1cvPf/88yovL/fpyvWhQ4fq+++/18qVK93nDh8+rAULFtRbNjg4WOnp6Xr77bdVUFDgPv/1119r3bp1J92m9PR0RUVFafr06Tpy5Eit13/88UdJx4LKpZdeqldeecXj/pLnf9HXPJehIU9mHDp0qD777DOPrYTl5eVasGCBOnXqVO8f84ZyOp268cYblZubqzfeeEMpKSn1lnn99dflcrnqnHaQpGuuuUYtW7bUCy+84D5njNH8+fPVvn17nuQJ/BsjCghIISEh6tOnjz7++GOFhoaqd+/eHq+npqZq1qxZkhq2PqGhRo8ereeee04jR47U//3f/ykuLk5LlixRREREg8o/9thjev/99zVgwACNHTtWR48e1bPPPqvk5GR9+eWXJ9WmqKgozZs3T3/4wx/Uq1cv3XTTTYqJiVFBQYHWrFmjfv36uR8o9D//8z/q37+/evXqpTFjxigpKUn79u3TmjVr9MUXX0iS+7N8+OGHddNNN6lly5YaNmxYnQ92euihh7R8+XINGTJE48aN01lnnaXFixdr7969WrVqlc+e4vjAAw9o9erVGjZsmH7++WePByxJdT9kadmyZYqPj3dvoT1ehw4dNH78eD399NM6cuSI+vTpo7ffflsff/yxli1bxsOWgBr+3HIBeGPy5MlGkklNTa312ptvvmkkmVatWnlsJTTGentkcnJyrXqysrJMYmKix7n8/Hxz9dVXm4iICNOmTRvzxz/+0bz//vsN2h5pjDF///vfTe/evU1ISIjp3LmzmT9/vpkyZUqDt0du3bq1zno3btxo0tPTTXR0tAkLCzNdunQxo0aNMtu2bfO4bseOHea6664zZ555pgkLCzPnnHOOeeSRRzyumTZtmmnfvr0JCgry2Cp5fJuMMeZf//qXueGGG9z19e3b1/z1r3+t1TZJ5o033vA4v3fvXiPJZGdnn/Azq9m+anUcb9euXUaSuf/++09Yr9PpNNOnTzeJiYkmJCTEJCcnm6VLl56wDNDcOIw5zVZqAQCA0wZrFAAAgCWCAgAAsERQAAAAlggKAADAEkEBAABYIigAAABLBAUAAGCJoAAAACwRFAAAgCWCAgAAsERQAAAAlggKAADAEkEBAABYIigAAABLBAUAAGCJoAAAACwRFAAAgCWCAgAAsERQAAAAlggKAADAEkEBAABYIigAAABLBAUAAGCJoAAAACwRFAAAgCWCAgAAsERQAAAAlggKAADAEkEBAABYIigAAABLBAUAAGCJoAAAACwRFAAAgCWCAgAAsERQAAAAlggKAADAEkEBAABYIigAAABLBAUAAGCJoAAAACwRFAAAgCWCAgAAsERQAAAAlggKAADAEkEBAABYIigAAABLBAUAAGCJ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G379wCRv6ggUBAUAAGwwLiPj5V96b8s3JoICAAA2+OM5Cv5EUAAAwAZ/PJnRnwgKAADYwGJGAABgybi8397I9kgAAJoolzFyeTki4G35xkRQAADABqYeAACAJRYzAgAAS2yPBAAAlozxwQOXAigpEBQAALDB5XTJ6fRu24LLy/KNiaAAAIANPMIZAABY4kuhAACAJUYUAACAJZ6jAAAALLlc3j8HwRU4axkJCgAA2MGIAgAAsMQaBQAAYImgAAAALLnkg2+PFEEBAIAmiREFAABgicWMAADAkvHB10wzogAAQBPF1AMAALDE1AMAALDkPOpUULDT6zoCBUEBAAAbGFEAAACWWKMAAAAsERQAAIAll1xyGe++/tGlwPn6SIICAAA2GJf3IwJe5oxGRVAAAMAGph4AAIAldj0AAABLLpdLLpeXaxS8LN+YgvzdAAAAAknN1IO3h1379+/XLbfcorPPPlvh4eHq0aOHtm3bdsIyOTk56tWrl0JDQ9W1a1ctWrTI9n0JCgAA2GCMyyeHHQcPHlS/fv3UsmVLvffee/rqq680a9YstW7d2rLM3r17lZGRoYEDB+qLL77Q+PHjdccdd2jdunW27s3UAwAANvhjMePMmTOVkJCg7Oxs97mkpKQTlpk/f76SkpI0a9YsSVL37t21adMmzZ49W+np6Q2+NyMKAADY4Ytph38HhbKyMo+jqqqqzluuXr1aF198sTIzM9W2bVtddNFFeumll07YzNzcXA0aNMjjXHp6unJzc229XYICAAA2uIzLJ4ckJSQkKDo62n3MmDGjznvu2bNH8+bNU7du3bRu3TrdfffdGjdunBYvXmzZzuLiYrVr187jXLt27VRWVqaKiooGv1+mHgAAsMGXUw+FhYWKiopynw8NDa3zepfLpYsvvljTp0+XJF100UXasWOH5s+fr6ysLK/aUh+CAgAANricTrm8/Jpol/NY+aioKI+gYCUuLk7nnXeex7nu3btr1apVlmViY2N14MABj3MHDhxQVFSUwsPDG9xWggIAADaczK6Fuuqwo1+/ftq9e7fHuby8PCUmJlqWSUlJ0dq1az3OrV+/XikpKbbuzRoFAABscLkkl8t4edi754QJE/TJJ59o+vTp+vbbb/Xaa69pwYIFuueee9zXTJ48WSNHjnT/fNddd2nPnj168MEHtWvXLr3wwgtasWKFJkyYYOveBAUAAGwwLpdPDjv69Omjt956S8uXL9f555+vadOmac6cORoxYoT7mqKiIhUUFLh/TkpK0po1a7R+/XpdcMEFmjVrll5++WVbWyMlyWEC6YHTAAD4SVlZmaKjo5V61Qdq0TLSq7qOHinXlr8OVmlpaYPWKPgTaxQAALDBH2sU/ImgAACADXzNNAAAsHS0+pDtNQbHcx4t91FrTj2CAgAADRASEqLY2Fht+3C4T+qLjY1VSEiIT+o6lVjMCABAA1VWVqq6utondYWEhCgsLMwndZ1KBAUAAGCJ5ygAAABLBAUAAGCJoAAAACwRFAAAgCWCAgAAsERQAAAAlggKAADA0v8HFGft/M49IjUAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgoAAAF5CAYAAAD+nwKnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAwdElEQVR4nO3df3zO9eL/8ed7YxtjQzaMMUZodCoHx/Kjjl+HtX4d9EOZVnEO/VCH4khIflbio5NSLBGFfJQot/aJ42fopEK0CpuE6tvZxvwY1/X6/uHsOl22N3vvuuyy7XG/3d63btf7er9e79d1vZo993q93u+3ZYwxAgAAKEJQoBsAAAAuXwQFAABgi6AAAABsERQAAIAtggIAALBFUAAAALYICgAAwBZBAQAA2CIoAAAAWwQFVDjr1q2TZVlat27dZVPnuHHjZFmW1764uDgNHDjQ98b50eXYJgCXFkEBZcqSJUtkWZb+93//t9B7v/vd72RZltauXVvovYYNGyoxMbE0mljmbd68WePGjVN2dnagm+Kxfft2PfTQQ0pISFB4eLgaNmyofv36KSMjo8jjlyxZoj/84Q+qUaOGrrjiCnXp0kWrVq0qdJzb7da0adPUuHFjhYWF6eqrr9bixYsv9ccByhSCAsqUjh07SpI2btzotT83N1e7du1SpUqVtGnTJq/3Dh48qIMHD3rKdu7cWSdPnlTnzp1Lp9El9M033+i1114r9fNu3rxZ48ePLzIoBKpNU6dO1bvvvquuXbtq5syZGjRokNavX6/rrrtOu3bt8jp21qxZuuOOO1S7dm1NmTJFY8aMUU5Ojm666SYtX77c69jRo0frySefVPfu3TVr1iw1bNhQd999t95+++3S/HjA5c0AZUzjxo1Nu3btvPZ99NFHxrIsc9ddd5mePXt6vbdo0SIjybz33nuXrE1r1641kszatWtLVH7s2LGmJD+Ox48fL9H5LuS5554zksz+/fv9XndJbdq0yZw+fdprX0ZGhgkNDTX9+/f32t+sWTPTtm1b43a7PftycnJMtWrVzM033+zZ98MPP5jKlSuboUOHeva53W7TqVMn06BBA3P27NlL9GmAsoURBZQ5HTt21I4dO3Ty5EnPvk2bNikhIUG9evXSp59+Krfb7fWeZVm6/vrrJRW9nuCGG25Qq1at9PXXX+vGG29U1apVVb9+fU2bNq3Q+X/44QfdeuutCg8PV3R0tB577DGdPn262O3fuHGj2rZtq7CwMMXHx+vVV18t8rjz1wO88cYbsixL//znPzVkyBBFR0erQYMGnvc//PBDderUSeHh4apevbqSkpK0e/fuQvXu3btX/fr1U1RUlKpUqaLmzZtr9OjRks6tlRgxYoQkqXHjxrIsS5Zl6cCBA0W2SZL27dunvn37qlatWqpatar+8Ic/FBrmL/jOlyxZookTJ6pBgwYKCwtT165d9d133130O0tMTFRISIjXvmbNmikhIUF79uzx2p+bm6vo6GivNR8RERGqVq2aqlSp4tn33nvv6cyZMxoyZIhnn2VZ+utf/6offvhBW7ZsuWi7gIqgUqAbADjVsWNHLViwQFu3btUNN9wg6VwYSExMVGJionJycrRr1y5dffXVnvdatGihK6644oL1/vvf/9af/vQn3X777erXr5+WLVumJ598Uq1bt1avXr0kSSdPnlTXrl2VlZWlRx55RDExMVqwYIE++eSTYrV9586d6tGjh6KiojRu3DidPXtWY8eOVZ06dYr9+YcMGaKoqCg9/fTTysvLkyQtWLBAKSkp6tmzp6ZOnaoTJ05o9uzZnlAVFxcnSfrqq6/UqVMnVa5cWYMGDVJcXJy+//57rVy5UhMnTtTtt9+ujIwMLV68WC+++KJq164tSYqKiiqyLUePHlViYqJOnDihRx55RFdccYXmz5+vm2++WcuWLdNtt93mdfyUKVMUFBSk4cOHKycnR9OmTVP//v21devWYn/+AsYYHT16VAkJCV77b7jhBi1btkyzZs1ScnKyTp06pVmzZiknJ0ePPvqo57gdO3YoPDxcLVu29Crfrl07z/sF01VAhRboIQ3Aqd27dxtJZsKECcYYY86cOWPCw8PN/PnzjTHG1KlTx/zjH/8wxhiTm5trgoODzYMPPugpX9Q0QZcuXYwk8+abb3r2nT592tStW9f8+c9/9uybMWOGkWSWLFni2ZeXl2eaNm1arKmHW2+91YSFhZnMzEzPvq+//toEBwcXmnpo1KiRSUlJ8bxOS0szkkzHjh29hsWPHTtmatSo4fUZjTHmyJEjJjIy0mt/586dTfXq1b3Ob4zxGqa/0NTD+W0aNmyYkWQ2bNjg1Z7GjRubuLg443K5jDH//c5btmzpNYUwc+ZMI8ns3LmzqK/rghYsWGAkmblz53rtP3r0qOnatauR5Nlq165tNm/e7HVcUlKSadKkSaF68/LyjCQzcuRIx20CyiOmHlDmtGzZUldccYVnQeOXX36pvLw8z1UNiYmJngWNW7ZskcvlKtZfhtWqVdM999zjeR0SEqJ27dpp3759nn2rV69WvXr11KdPH8++qlWratCgQRet3+Vyac2aNbr11lvVsGFDr8/Ts2fPi5Yv8OCDDyo4ONjz+uOPP1Z2drbuuusu/fLLL54tODhY7du391wF8vPPP2v9+vVKTU31Or+kQpdmFtfq1avVrl07r++3WrVqGjRokA4cOKCvv/7a6/j77rvPawqhU6dOkuT1HRfH3r17NXToUHXo0EEpKSle71WtWlXNmzdXSkqKli5dqnnz5qlevXq6/fbbvaY5Tp48qdDQ0EJ1h4WFed4HwNQDyiDLspSYmKj169fL7XZr06ZNio6OVtOmTSWdCwovvfSSJHkCQ3GCQoMGDQr9wqxZs6a++uorz+vMzEw1bdq00HHNmze/aP0///yzTp48qWbNmhV6r3nz5lq9evVF65DOrR34rW+//VaS9Mc//rHI4yMiIiT995dxq1atinWe4sjMzFT79u0L7S8Yzs/MzPQ63/kBpWbNmpLOTfsU15EjR5SUlKTIyEgtW7bMKzRJUt++fVWpUiWtXLnSs++WW25Rs2bNNHr0aL3zzjuSpCpVqhS5tuTUqVOe9wEQFFBGdezYUStXrtTOnTs96xMKJCYmasSIETp06JA2btyomJgYNWnS5KJ1nv8Lp4Axxm/t9ofzf4EVLNxcsGCB6tatW+j4SpUunx9zX7/jnJwc9erVS9nZ2dqwYYNiYmK83t+3b58++ugjzZkzx2t/rVq11LFjR69LZ+vVq6e1a9fKGOMV/A4fPixJheoGKqrL518QwIHf3k9h06ZNGjZsmOe9Nm3aKDQ0VOvWrdPWrVvVu3dvv523UaNG2rVrV6FfLt98881FyxZcZVAwAvBbxSlvJz4+XpIUHR2tbt262R5XEJbOv+/A+ZxMQzRq1KjItu/du9fzvr+cOnVKycnJysjIUHp6uq666qpCxxw9elTSuWme8505c0Znz571vL7mmmv0+uuva8+ePV51FSysvOaaa/zWdqAsY40CyqTf//73CgsL01tvvaVDhw55jSiEhobquuuu0z/+8Q/l5eX5deV679699eOPP2rZsmWefSdOnCj0F2xRgoOD1bNnT61YsUJZWVme/Xv27NGaNWtK3KaePXsqIiJCkyZN0pkzZwq9//PPP0s6F1Q6d+6sefPmeZ1f8v6LPjw8XJKKdWfG3r17a9u2bV6XEubl5WnOnDmKi4sr8pd5SbhcLt1xxx3asmWLli5dqg4dOhR5XNOmTRUUFKR33nnH6zP98MMP2rBhg6699lrPvltuuUWVK1fWyy+/7NlnjNErr7yi+vXrcydP4D8YUUCZFBISorZt22rDhg0KDQ1VmzZtvN5PTEzUCy+8IKl46xOK68EHH9RLL72kAQMG6F//+pfq1aunBQsWqGrVqsUqP378eH300Ufq1KmThgwZorNnz2rWrFlKSEjwWgvhREREhGbPnq17771X1113ne68805FRUUpKytLq1at0vXXX+9Zs/E///M/6tixo6677joNGjRIjRs31oEDB7Rq1Sp98cUXkuT5LkePHq0777xTlStXVnJysidA/NbIkSO1ePFi9erVS4888ohq1aql+fPna//+/Xr33XcVFOSfv0X+9re/6f3331dycrJ+/fVXLVy40Ov9gkWoUVFRSk1N1euvv66uXbvq9ttv17Fjx/Tyyy/r5MmTGjVqlKdMgwYNNGzYMD333HM6c+aM2rZtqxUrVmjDhg166623bKdJgAongFdcAD4ZNWqUkWQSExMLvbd8+XIjyVSvXr3QHfbsLo9MSEgoVE9KSopp1KiR177MzExz8803m6pVq5ratWubRx991Hz00UfFvjPjP//5T9OmTRsTEhJimjRpYl555ZUi78xod3nk9u3bi6x37dq1pmfPniYyMtKEhYWZ+Ph4M3DgQPPZZ595Hbdr1y5z2223mRo1apiwsDDTvHlzM2bMGK9jJkyYYOrXr2+CgoK8LpU8v03GGPP999+bPn36eOpr166d+eCDDwq1TZJZunSp1/79+/cbSSYtLe2C31nB5at222+dOXPGzJo1y1xzzTWmWrVqplq1aubGG280n3zySaF6XS6XmTRpkmnUqJEJCQkxCQkJZuHChRdsC1DRWMZcZiu1AADAZYM1CgAAwBZBAQAA2CIoAAAAWwQFAABgi6AAAABsERQAAIAtggIAALBFUAAAALYICgAAwBZBAQAA2CIoAAAAWwQFAABgi6AAAABsERQAAIAtggIAALBFUAAAALYICgAA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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from floris.flow_visualization import plot_rotor_values\n", "\n", "fig, _, _ , _ = plot_rotor_values(fmodel.core.flow_field.u, findex=0, n_rows=1, n_cols=4, return_fig_objects=True)\n", "fig.suptitle(\"Wind direction 270\")\n", "\n", "fig, _, _ , _ = plot_rotor_values(fmodel.core.flow_field.u, findex=1, n_rows=1, n_cols=4, return_fig_objects=True)\n", "fig.suptitle(\"Wind direction 280\")\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "4dc966e1", "metadata": {}, "source": [ "## On grid points" ] }, { "cell_type": "markdown", "id": "e8241714", "metadata": {}, "source": [ "In FLORIS, grid points are the points in space where the wind conditions are calculated.\n", "In a typical simulation, these are all located on a regular grid on each turbine rotor.\n", "\n", "The parameter `turbine_grid_points` specifies the number of rows and columns which define the turbine grid.\n", "In the example inputs, this value is 3 meaning there are 3 x 3 = 9 total grid points for each turbine.\n", "Wake steering codes currently require greater values greater than 1 in order to compute gradients.\n", "However, a single grid point (1 x 1) may be suitable for non wind farm control applications,\n", "but retuning of some parameters might be required.\n", "\n", "We can visualize the locations of the grid points in the current example using `matplotlib.pyplot`." ] }, { "cell_type": "code", "execution_count": 12, "id": "774acfea", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "shape of xs: (2, 4, 3, 3)\n", " 2 wd x 2 ws x 4 turbines x 3 x 3 grid points\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Get the grid points\n", "xs = fmodel.core.grid.x_sorted\n", "ys = fmodel.core.grid.y_sorted\n", "zs = fmodel.core.grid.z_sorted\n", "\n", "# Consider the shape\n", "print(f\"shape of xs: {xs.shape}\")\n", "print(\" 2 wd x 2 ws x 4 turbines x 3 x 3 grid points\")\n", "\n", "# Lets plot just one wd/ws conditions\n", "xs = xs[1, :, :, :]\n", "ys = ys[1, :, :, :]\n", "zs = zs[1, :, :, :]\n", "\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111, projection=\"3d\")\n", "ax.scatter(xs, ys, zs, marker=\".\")\n", "ax.set_zlim([0, 150])\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "e91f7a84", "metadata": {}, "source": [ "## Basic use case: calculating AEP" ] }, { "cell_type": "markdown", "id": "34bc7865", "metadata": {}, "source": [ "FLORIS leverages vectorized operations on the CPU to reduce the computation\n", "time for bulk calculations, and this is especially meaningful for calculating\n", "annual energy production (AEP) on a wind rose.\n", "Here, we demonstrate a simple AEP calculation for a 25-turbine farm\n", "using several different modeling options. We make the assumption\n", "that every wind speed and direction is equally likely. We also\n", "report the time required for the computation using the Python\n", "`time.perf_counter()` function." ] }, { "cell_type": "code", "execution_count": 13, "id": "ee1918d6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Calculating AEP for 1440 wind direction and speed combinations...\n", "Number of turbines = 25\n", "Model AEP (GWh) Compute Time (s)\n", "Jensen 661.838 0.438 \n", "GCH 683.869 1.489 \n", "CC 661.315 3.243 \n" ] } ], "source": [ "import time\n", "from typing import Tuple\n", "\n", "# Using Numpy.meshgrid, we can combine 1D arrays of wind speeds and wind directions to produce\n", "# combinations of both. Though the input arrays are not the same size, the resulting arrays\n", "# will be the same size.\n", "wind_directions, wind_speeds = np.meshgrid(\n", " np.arange(0.0, 360.0, 5), # wind directions 0 to 360 degrees (exclusive) in 5 degree increments\n", " np.arange(8.0, 12.0, 0.2), # wind speeds from 8 to 12 m/s in 0.2 m/s increments\n", " indexing=\"ij\"\n", ")\n", "# meshgrid returns arrays with shape (len(wind_speeds), len(wind_directions)), so we \"flatten\" them\n", "wind_directions = wind_directions.flatten()\n", "wind_speeds = wind_speeds.flatten()\n", "turbulence_intensities = 0.1 * np.ones_like(wind_speeds)\n", "\n", "n_findex = len(wind_directions)\n", "print(f\"Calculating AEP for {n_findex} wind direction and speed combinations...\")\n", "\n", "# Set up a square 25 turbine layout\n", "N = 5 # Number of turbines per row and per column\n", "D = 126.0\n", "\n", "# Create the turbine locations using the same method as above\n", "x, y = np.meshgrid(\n", " 7.0 * D * np.arange(0, N, 1),\n", " 7.0 * D * np.arange(0, N, 1),\n", ")\n", "x = x.flatten()\n", "y = y.flatten()\n", "print(f\"Number of turbines = {len(x)}\")\n", "\n", "# Define several models\n", "fmodel_jensen = FlorisModel(\"jensen.yaml\")\n", "fmodel_gch = FlorisModel(\"gch.yaml\")\n", "fmodel_cc = FlorisModel(\"cc.yaml\")\n", "\n", "# Assign the layouts, wind speeds and directions\n", "fmodel_jensen.set(\n", " layout_x=x,\n", " layout_y=y,\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities\n", ")\n", "fmodel_gch.set(\n", " layout_x=x,\n", " layout_y=y,\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", ")\n", "fmodel_cc.set(\n", " layout_x=x,\n", " layout_y=y,\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", ")\n", "\n", "def time_model_calculation(model_fmodel: FlorisModel) -> Tuple[float, float]:\n", " \"\"\"\n", " This function performs the wake calculation for a given\n", " FlorisModel object and computes the AEP while\n", " tracking the amount of wall-time required for both steps.\n", "\n", " Args:\n", " model_fmodel (FlorisModel): _description_\n", " float (_type_): _description_\n", "\n", " Returns:\n", " tuple(float, float):\n", " 0: AEP\n", " 1: Wall-time for the computation\n", " \"\"\"\n", " start = time.perf_counter()\n", " model_fmodel.run()\n", " aep = model_fmodel.get_farm_power().sum() / n_findex / 1E9 * 365 * 24\n", " end = time.perf_counter()\n", " return aep, end - start\n", "\n", "jensen_aep, jensen_compute_time = time_model_calculation(fmodel_jensen)\n", "gch_aep, gch_compute_time = time_model_calculation(fmodel_gch)\n", "cc_aep, cc_compute_time = time_model_calculation(fmodel_cc)\n", "\n", "print('Model AEP (GWh) Compute Time (s)')\n", "print('{:8s} {:<10.3f} {:<6.3f}'.format(\"Jensen\", jensen_aep, jensen_compute_time))\n", "print('{:8s} {:<10.3f} {:<6.3f}'.format(\"GCH\", gch_aep, gch_compute_time))\n", "print('{:8s} {:<10.3f} {:<6.3f}'.format(\"CC\", cc_aep, cc_compute_time))" ] }, { "cell_type": "markdown", "id": "c006ae1e", "metadata": {}, "source": [ "## Basic use case: wake steering design" ] }, { "cell_type": "markdown", "id": "f5777dae", "metadata": {}, "source": [ "FLORIS includes a set of optimization routines for the design of wake steering controllers.\n", "`SerialRefine` is a new method for quickly identifying optimum yaw angles." ] }, { "cell_type": "code", "execution_count": 14, "id": "32a93c6d", "metadata": {}, "outputs": [], "source": [ "# Demonstrate on 7-turbine single row farm\n", "x = np.linspace(0, 6*7*D, 7)\n", "y = np.zeros_like(x)\n", "wind_directions = np.arange(0.0, 360.0, 2.0)\n", "wind_speeds = 8.0 * np.ones_like(wind_directions)\n", "turbulence_intensities = 0.1 * np.ones_like(wind_directions)\n", "fmodel_gch.set(\n", " layout_x=x,\n", " layout_y=y,\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", ")" ] }, { "cell_type": "code", "execution_count": 15, "id": "7d773cdc", "metadata": {}, "outputs": [], "source": [ "from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR\n", "\n", "# Define the SerialRefine optimization\n", "yaw_opt = YawOptimizationSR(\n", " fmodel=fmodel_gch,\n", " minimum_yaw_angle=0.0, # Allowable yaw angles lower bound\n", " maximum_yaw_angle=25.0, # Allowable yaw angles upper bound\n", " Ny_passes=[5, 4],\n", " exclude_downstream_turbines=True,\n", ")" ] }, { "cell_type": "code", "execution_count": 16, "id": "1ccb9ab7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[Serial Refine] Processing pass=0, turbine_depth=0 (0.0%)\n", "[Serial Refine] Processing pass=0, turbine_depth=1 (7.1%)\n", "[Serial Refine] Processing pass=0, turbine_depth=2 (14.3%)\n", "[Serial Refine] Processing pass=0, turbine_depth=3 (21.4%)\n", "[Serial Refine] Processing pass=0, turbine_depth=4 (28.6%)\n", "[Serial Refine] Processing pass=0, turbine_depth=5 (35.7%)\n", "[Serial Refine] Processing pass=0, turbine_depth=6 (42.9%)\n", "[Serial Refine] Processing pass=1, turbine_depth=0 (50.0%)\n", "[Serial Refine] Processing pass=1, turbine_depth=1 (57.1%)\n", "[Serial Refine] Processing pass=1, turbine_depth=2 (64.3%)\n", "[Serial Refine] Processing pass=1, turbine_depth=3 (71.4%)\n", "[Serial Refine] Processing pass=1, turbine_depth=4 (78.6%)\n", "[Serial Refine] Processing pass=1, turbine_depth=5 (85.7%)\n", "[Serial Refine] Processing pass=1, turbine_depth=6 (92.9%)\n", "Optimization wall time: 3.771 s\n" ] } ], "source": [ "start = time.perf_counter()\n", "\n", "## Calculate the optimum yaw angles for 25 turbines and 72 wind directions\n", "df_opt = yaw_opt.optimize()\n", "\n", "end = time.perf_counter()\n", "\n", "walltime = end - start\n", "print(f\"Optimization wall time: {walltime:.3f} s\")\n" ] }, { "cell_type": "markdown", "id": "fb2e01e8", "metadata": {}, "source": [ "In the results, T0 is the upstream turbine when wind direction is 270, while T6 is upstream at 90 deg" ] }, { "cell_type": "code", "execution_count": 17, "id": "686548be", "metadata": {}, "outputs": [ { "data": { "image/png": "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dAHZnj17snfvXjZt2uS3RZL4p8OHD7v+/7333mP69Ons27fPdSwyMtL1/7fffjubN29mx44ddRKbW0VScXExzz33HIsWLeKXX36hc+fOJCQkEBYWxg8//MCHH37IxIkTSU5OZvr06W53nZs0aRJLly7lo48+IioqyrXPKCYmhrCwMGJiYhg/fjwpKSk0btyY6Oho7rnnHnr37q3OdiIiItKgOJs2BAQEVFpC1LNnT/7xj3+wefNmX4UmUiMVV4bFxMRgsViqXC22YMECAI4dO2auIqlNmzb07t2bxYsXV7sm9MCBAyxdupQxY8bw2GOPMXHixAuOu2jRIgD69etX6fiSJUu47bbbAJg3bx4BAQGMGjWq0sVkRURERBqSiu2/Ky5T6tmzJwBbtmzBbreb/jo2UjecF1b1BrvdTmFhIYGBgW79foWHh9eLvUvn41aRlJaWRvv27c97n5YtWzJ16lQefPBBsrOz3Tq5s53g+YSGhrJw4UIWLlzo1pgiIiIi/si53K7iEiSAK664gtDQUE6dOsV3331Hu3btfBGemExRUdE5vyt1paCgoNKS0PrIrY8aLlQgVWS1Wrn00ktrHJCIiIiInKviTFJFVquVbt26AWjJnYiXeNzdrrp1gBaLhdDQUFq0aFFldzkRERERqTlnkVTVJ/Q9e/bkyy+/ZPPmzdx66611HZqYUHh4uOt3prbsdjt5eXlER0e7vdyuvvO4SOrcufN51xharVZGjx7N3//+d6/3aBcRERFpqKqbSYKz+5I0kyROFovFa0ve7HY75eXlRERENJg9bx7/K5cvX07r1q155ZVXyMzMJDMzk1deeYW2bduydOlSXnvtNT7//HOmTZtmRLwiIiIiDZI7RdKOHTtc11IS8Rc//PADmZmZ5ObmUlxc7KpBysrKDDunxzNJTz75JM8//zyDBg1yHbviiitITEzkL3/5C1u2bCEiIoIpU6bw7LPPejVYERERkYaqusYNAElJScTHx5Obm8vXX3/N7373u7oOT8QwEyZMYN26da7vu3TpAkBWVhaXXHKJIef0eCZp586dtGzZ8pzjLVu2ZOfOncCZJXkVLw4lIiIiIrVzvpkki8WiJXdSr912222cPHmyytvWrl2Lw+E458uoAglqUCS1a9eOOXPmVJrestlszJkzx9Vy8qeffiIuLs57UYqIiIg0cOdr3ADalyTiTR4vt1u4cCHDhg0jMTGRjh07Amdml8rLy1mxYgUA+/fv5+677/ZupCIiIiIN2PlmkkBFkog3eVwk9enTh6ysLN5++22+++47AG688UbGjh1LVFQUADfffLN3oxQRERFp4C5UJHXr1g2LxcKBAwfIzc0lPj6+LsMT8SseF0kAUVFR3Hnnnd6ORURERESqcb7GDQDR0dFcdtll7N69m82bNzN8+PC6DE/Er9So0fk///lPrrrqKhISEjhw4AAA8+bN46OPPvJqcCIiIiJyxoVmkkBL7kS8xeMiadGiRaSkpDB48GBOnDhBeXk5AI0aNWL+/Pnejk9EREREcK9I6tWrF6AiqSFzOBy+DsHn7HZ7rcfweLndCy+8wOLFixkxYgRz5sxxHe/WrRsPPvhgrQMSERERkXNdqLsdnJ1J2rp1K+Xl5QQGBtZJbOJ7VqsVi8XCsWPHaNq0KRaLxWtj2+12ysrKKCkpISCgRgvR6oTD4aCsrIxjx44REBBAcHBwjcfyuEjKyspyXcCpopCQENdaWRERERHxLndmkjp06EBERAT5+fns3buXDh061FV44mOBgYEkJiZy8OBBfvzxR6+O7XA4KC4uJiwszKvFl1HCw8Np0aJFrQo6j4ukVq1akZmZec4FZVetWkX79u1rHIiIiIiIVO9CjRvgzB/K3bp1Y926dWzevFlFUgMTGRlJ69atsdlsXh3XZrOxfv16+vbti9Vq9erY3hYYGEhQUFCtizmPi6SUlBQmTZpESUkJDoeDLVu28M477zB79mxeffXVWgUjIiIiIlVzZyYJziy5cxZJt99+e12EJiYSGBjo9WWWgYGBnD59mtDQUNMXSd7icZE0YcIEwsLCmDZtGkVFRYwdO5aEhASef/55xowZY0SMIiIiIg2eJ0USqHmDSG3U6DpJN910EzfddBNFRUUUFBTQrFkzb8clIiIiIv+nrKzMtYTqfI0b4GyRtHPnTgoLCy94fxE5V412Mx0/fpxt27bx7bffqmuKiIiIiMGcs0hw4SLp4osv5uKLL8Zut5ORkWF0aCJ+yaMiaffu3fTt25e4uDh69uxJjx49aNasGf3792ffvn1GxSgiIiLSoDmbNgQHB7vV1lhL7kRqx+0iKTc3l2uuuYZjx44xd+5cVq5cySeffMIzzzzD4cOHufrqqzl69KiRsYqIiIg0SO7uR3JSkSRSO24XSfPmzaNly5Zs376d++67j0GDBnHttdeSkpLC119/TVJSEvPmzTMyVhExmVdffZVbb72V/Px8X4ciIuLXalokbdq0ybCYRPyZ20VSeno6jzzyCKGhoefcFhYWxkMPPcRnn33m1eBExLyKi4u57777ePPNN/nzn//s63BERPyas0hytwlD165dCQgI4KeffuKnn34yMjQRv+R2kbR//36uvPLKam/v1q0b+/fv90pQImJ+n332GUVFRQC89NJLfPnllz6OSETEf3k6kxQZGckVV1wBaMmdSE24XSTl5+cTHR1d7e1RUVGVOq+IiH9btmwZcPYNe8KECZSUlPgyJBERv+VpkQTalyRSGx51t8vPzycvL6/aL4fDYVScImIiZWVl/Pvf/wbgnXfeoXnz5uzbt4+//vWvPo5MRMQ/ObvbqUgSqRtuF0kOh4M2bdrQqFGjKr/atm1rZJwiYiKff/45p06dIj4+niFDhrBw4UIAnn76ab755hsfRyci4n9qM5O0bds2ysvLDYlLxF8FuXvHNWvWGBmHiNQjzqV2I0aMICAggBtuuIFRo0axbNkyxo8fz6ZNmwgKcvvlRURELqAmRVK7du2IiooiPz+f3bt307FjR6PCE/E7bs8kXXPNNW59eWL9+vUMHTqUhIQELBYLH374YaXbHQ4H06dPp3nz5oSFhTFgwAC+//57j84hIt5VXl7ueq6OGjXKdfzFF18kNjaWjIwM5s+f75vgRET8lKfd7QACAwPp06cPAM8995whcYn4K7eKJOc6WHe5e//CwkI6derkWqrza08//TQLFizg5ZdfZvPmzURERDBo0CBtDhfxoS+++ILjx4/TuHHjSh+MxMfHu96Ep0+fzg8//OCrEEVE/E5NZpIAnnjiCSwWC2+++aYu1SLiAbeKpN/+9rfMmTOHw4cPV3sfh8NBeno6gwcPZsGCBW6dfPDgwcyaNYsbbrihyvHmz5/PtGnTGD58OB07duTNN9/k0KFD58w4iUjdcS61GzZsGFartdJt48aN43/+538oLi7mjjvuUDMXEREvqUnjBjizL+m+++4D4I477lAnYhE3ubVpYO3atTz66KM8/vjjdOrUiW7dupGQkEBoaCgnTpxgz549bNy4kaCgIKZOncqf/vSnWgeWlZVFbm4uAwYMcB2LiYmhZ8+ebNy4kTFjxlT5uNLS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s3jx4mqnOw8cOMDSpUsZM2YMjz32GBMnTrzguIsWLQKgX79+lY4vWbKE2267DYB58+YREBDAqFGjKl1MVkRERKShWbBgAY8//jjLly/n448/rhdLn0SqU3H7TExMDBaL5ZwtNbfffnul73/zm9+wceNGUlNTfV8kpaWl0b59+/Pep2XLlkydOpUHH3yQ7Oxst07u7JRxPqGhoSxcuJCFCxe6NaaIiIiIvzp48CAAK1as4L333mPMmDE+jkjMynlhVW+w2+0UFhYSGBjo1nK78PBwQwv4U6dOuS4ZZBS3iqQLFUgVWa1WLr300hoHJCIiIiJVq7jp/N5772XgwIFcdNFFPoxIzKqoqIjIyEifnLugoICIiAhDxt6wYQPvvfcen3zyiSHjO3nc3W7Hjh1VHrdYLISGhtKiRYsqGyeIiIiISO04i6SAgACOHTvGAw88wJtvvunjqETqxq5duxg+fDgzZswgOTnZ0HN5XCR17tz5vNNnVquV0aNH8/e//93r7QdFREREGjJnkfTggw/yzDPP8M9//pOxY8dy7bXX+jgyMZvw8HAKCgq8MpbdbicvL4/o6Gi3l9t52549e/if//kf7rjjDqZNm+b18X/N4yJp+fLlPPLIIzz00EP06NEDgC1btvDcc88xY8YMTp8+zZ///GemTZvGs88+6/WARURERBoqZ5E0cOBAysrKmD9/Pn/605/YtWuXut1JJRaLxWtL3ux2O+Xl5URERHi9vbg7du/eTf/+/bn11lt58skn6+ScHhdJTz75JM8//zyDBg1yHbviiitITEzkL3/5C1u2bCEiIoIpU6aoSBIRERHxovz8fACioqKYNWsWH374IT/++COPPfYYCxYs8HF0It63a9cu+vfvz6BBg0hJSXFdMigwMJCmTZsadl6PS8GdO3fSsmXLc463bNmSnTt3AmeW5FXsey4iIiIiteecSYqOjiYiIoLFixcD8OKLL7Jx40ZfhiZiiA8++IBjx47x1ltv0bx5c9dX9+7dDT2vx0VSu3btmDNnDmVlZa5jNpuNOXPm0K5dOwB++ukn4uLivBeliIiISAPncDgqFUkAAwYM4LbbbsPhcDB+/HhKS0t9GaJIjd12222cPHnynOOPP/44DofjnK8ff/zR0Hg8LpIWLlzIihUrSExMZMCAAQwYMIDExERWrFjhujjs/v37ufvuu70e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i/NupCLic6dOnQJgzJgxdOjQgcsuu4xly5bxySefUFJSon2IIiIGc74O//73v+e2226jtLSUF198kUOHDrFlyxZ69erl4whFaua2225zXSe1ot///vds2LChzuPxeCZp4cKFrFixgsTERAYMGMCAAQNITExkxYoVrovD7t+/n7vvvtvrwYqI7zgcDk6ePAmcueAbQPfu3UlMTKSwsJC0tDQfRici0jD8+nU4JCSE66+/HoBly5b5KiwRv+NxkdSnTx+ysrKYOXMmHTt2pGPHjsycOZOsrCzXpxc333wzDz30kNeDFRHfKSoqcjVscL45WywWRo4cCejNWUSkLjhnkpyvw0Cl12GHw+GTuET8jcfL7eBMF5U777zT27GIiIk535gDAgIqXcF71KhRLFiwgI8//hibzYbVavVViCIifq+qImnw4MGEhYWRlZXFN998Q+fOnX0UnYj/qFGj83/+859cddVVJCQkcODAAeDMBZ4++ugjrwYnIubhfGOOjo6udK203/3udzRr1oyTJ0+yZs0aX4UnItIgVFUkRUREcO211wKa1RfxFo+LpEWLFpGSksLgwYM5ceKEa/lNo0aNmD9/vrfjExGTqOqNGSAwMJARI0YA6nInImIkh8NR7Wuxlj6Lk5Zceudn4HGR9MILL7B48WIee+wxgoLOrtbr1q2bqwW4iPif6t6YAVcr8OXLl1e60KyIiHhPSUkJNpsNOPe1+Prrr8dqtfLtt9/y7bff+iI88bHAwEAAysrKfByJ7xUVFQHUaguAx3uSsrKy6NKlyznHQ0JCKCwsrHEgImJu5yuS+vXrR2xsLEePHuWrr76ib9++dR2eiIjfc74OWyyWSntDAWJjYxkwYACffvopqampPPbYY74IUXwoKCiI8PBwjh07htVqJSCgRrtqqmS32ykrK6OkpMSr43qbw+GgqKiIo0ePEhsb6yoca8LjIqlVq1ZkZmaec0HZVatW0b59+xoHIiLmdr4iKTg4mGHDhvHmm2+SmpqqIklExAAV94ZW9YfqyJEj+fTTT1m2bJmKpAbIYrHQvHlzsrKyXD0DvMXhcFBcXExYWFilfclmFRsbS3x8fK3G8LhISklJYdKkSZSUlOBwONiyZQvvvPMOs2fP5tVXX61VMCJiXucrkuDMkjtnkTRv3rx68SIqIlKfXOh1ePjw4fzpT39i+/btZGVl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ojpYJBEREREREdXBIomIiIiIiKgOFklERERERER1sEgiIiIiIiKqg0USERERERFRHSySiIiIiIiI6mCRREREREREVAeLJCIiIiIiojpszB2A1DQaDTIzM+Hi4gKFQmHucIiIiIiIyEwEQUBxcTF8fX1hZdVwf5HFF0mZmZnw8/MzdxhERERERCQTN27cQPv27Rvcb/FFkouLC4DaN8LV1dWssajVauzevRvh4eFQKpVmjaW1Yy7kgXmQB+ZBPpgLeWAe5IO5kAdLykNRURH8/Px0NUJDLL5I0g6xc3V1lUWR5OjoCFdX1xb/DdbSMRfywDzIA/MgH8yFPDAP8sFcyIMl5qGxaThcuIGIiIiIiKgOFklERERERER1sEgiIiIiIiKqw+LnJBERERERtRYajQZVVVWitqlWq2FjY4OKigrU1NSI2rbYlEolrK2tm92OWYukZcuWITY2FpcuXYKDgwOGDBmC9957D926ddMdU1FRgblz5+K7775DZWUlIiIi8Omnn8Lb29uMkRMRERERyUtVVRXS09Oh0WhEbVcQBKhUKty4caNF3HfU3d0dKpWqWbGatUg6cOAAZs2ahYEDB6K6uhoLFy5EeHg4Lly4ACcnJwDAa6+9hh07duCHH36Am5sbZs+ejQkTJuDIkSPmDJ2IiIiISDYEQUBWVhasra3h5+d3zxulGkuj0aCkpATOzs6itis2QRBQVlaG3NxcAICPj0+T2zJrkRQXF6f3eNOmTfDy8kJycjJCQ0NRWFiIL774Aps3b8bIkSMBAF999RV69OiBY8eOYdCgQeYIm6jV2rRpE/773//i888/h6+vr7nDISIiov9TXV2NsrIy+Pr6wtHRUdS2tUP47O3tZV0kAYCDgwMAIDc3F15eXk0eeierOUmFhYUAgDZt2gAAkpOToVarERYWpjume/fu6NChAxITE+stkiorK1FZWal7XFRUBKB2LKVarZYy/EZpz2/uOIi5aKpVq1bhzJkzeOGFF7B169Zmd7kzD/LAPMgHcyEPzIN8MBeGq6yshCAIsLGxkWS4nfZfsduWgr29PQRBQHl5Oezs7PT2Gfq9pBC0r9rMNBoNxo0bh4KCAhw+fBgAsHnzZkybNk2v6AGA4OBgjBgxAu+9995d7bz11lt4++2379q+efNm0atqotZm5syZyM7OBgC8/vrrePDBB80cERER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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Show the results\n", "yaw_angles_opt = np.vstack(df_opt[\"yaw_angles_opt\"])\n", "fig, axarr = plt.subplots(len(x), 1, sharex=True, sharey=True, figsize=(10, 10))\n", "for i in range(len(x)):\n", " axarr[i].plot(wind_directions, yaw_angles_opt[:, i], 'k-', label='T%d' % i)\n", " axarr[i].set_ylabel('Yaw (Deg)')\n", " axarr[i].legend()\n", " axarr[i].grid(True)\n", "axarr[-1].set_xlabel('Wind Direction (Deg)')\n", "\n", "plt.show()" ] } ], "metadata": { "jekyll": { "layout": "default", "nav_order": 1, "permalink": "/tutorials/index", "title": "Overview" }, "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.4" }, "vscode": { "interpreter": { "hash": "853a8652e3619d46ff0e51baac54f380b0862f9ec17aef8c5e0b66472a177ac0" } } }, "nbformat": 4, "nbformat_minor": 5 } ================================================ FILE: docs/layout_optimization.md ================================================ (layout_optimization)= # Layout Optimization The FLORIS package provides layout optimization tools to place turbines within a specified boundary area to optimize annual energy production (AEP) or wind plant value. Layout optimizers accept an instantiated `FlorisModel` and alter the turbine layouts in order to improve the objective function value (AEP or value). ## Background Layout optimization entails placing turbines in a wind farm in a configuration that maximizes an objective function, usually the AEP. Turbines are moved to minimize their wake interactions in the most dominant wind directions, while respecting the boundaries of the area for turbine placement as well as minimum distance requirements between neighboring turbines. Mathematically, we represent this as a (nonconvex) optimization problem. Let $x = \{x_i\}_{i=1,\dots,N}$, $x_i \in \mathbb{R}^2$ represent the set of coordinates of turbines within a farm (that is, $x_i$ represents the $(X, Y)$ location of turbine $i$). Further, let $R \subset \mathbb{R}^2$ be a closed region in which to place turbines. Finally, let $d$ represent the minimum allowable distance between two turbines. Then, the layout optimization problem is expressed as $$ \begin{aligned} \underset{x}{\text{maximize}} & \:\: f(x)\\ \text{subject to} & \:\: x \subset R \\ & \:\: ||x_i - x_j|| \geq d \:\: \forall j = 1,\dots,N, j\neq i \end{aligned} $$ Here, $||\cdot||$ denotes the Euclidean norm, and $f(x)$ is the cost function to be maximized. When maximizing the AEP, $f = \sum_w P(w, x)p_W(w)$, where $w$ is the wind condition bin (e.g., wind speed, wind direction pair); $P(w, x)$ is the power produced by the wind farm in condition $w$ with layout $x$; and $p_W(w)$ is the annual frequency of occurrence of condition $w$. Layout optimizers take iterative approaches to solving the layout optimization problem specified above. Optimization routines available in FLORIS are described below. ## Scipy layout optimization The `LayoutOptimizationScipy` class is built around `scipy.optimize`s `minimize` routine, using the `SLSQP` solver by default. Options for adjusting `minimize`'s behavior are exposed to the user with the `optOptions` argument. Other options include enabling fast wake steering at each layout optimizer iteration with the `enable_geometric_yaw` argument, and switch from AEP optimization to value optimization with the `use_value` argument. ## Genetic random search layout optimization The `LayoutOptimizationRandomSearch` class is a custom optimizer designed specifically for layout optimization via random perturbations of the turbine locations. It is designed to have the following features: - Robust to complex wind conditions and complex boundaries, including disjoint regions for turbine placement - Easy to parallelize and wrapped in a genetic algorithm for propagating candidate solutions - Simple to set up and tune for non-optimization experts - Set up to run cheap constraint checks prior to more expensive objective function evaluations to accelerate optimization The algorithm, described in full in {cite:t}`SinnerFleming2024grs`, moves a random turbine and random distance in a random direction; checks that constraints are satisfied; evaluates the objective function (AEP or value); and then commits to the move if there is an objective function improvement. The main tuning parameter is the probability mass function for the random movement distance, which is a dictionary to be passed to the `distance_pmf` argument. The `distance_pmf` dictionary should contain two keys, each containing a 1D array of equal length: `"d"`, which specifies the perturbation distance _in units of the rotor diameter_, and `"p"`, which specifies the probability that the corresponding perturbation distance is chosen at any iteration of the random search algorithm. The `distance_pmf` can therefore be used to encourage or discourage more aggressive or more conservative movements, and to enable or disable jumps between disjoint regions for turbine placement. The figure below shows an example of the optimized layout of a farm using the GRS algorithm, with the black dots indicating the initial layout; red dots indicating the final layout; and blue shading indicating wind speed heterogeneity (lighter shade is lower wind speed, darker shade is higher wind speed). The progress of each of the genetic individuals in the optimization process is shown in the right-hand plot. ![](plot_complex_docs.png) ## Gridded layout optimization The `LayoutOptimizationGridded` class allows users to quickly find a layout that fits the most turbines possible into the specified boundary area, given that the turbines are arranged in a gridded layout. To do so, a range of different rotations and translations of a generic gridded arrangement are tried, and the one that fits the most turbines into the boundary area is selected. No AEP evaluations are performed; rather, the cost function $f$ to be maximized is simply $N$, the number of turbines, and there is an additional constraint that the turbines are arranged in a gridded fashion. Note that in other layout optimizers, $N$ is fixed. We envisage that this will be useful for users that want to quickly generate a layout to "fill" a boundary region in a gridded manner. By default, the gridded arrangement is a square grid with spacing of `min_dist` (or `min_dist_D`); however, instantiating with the `hexagonal_packing` keyword argument set to `True` will provide a grid that offsets the rows to enable tighter packing of turbines while still satisfying the `min_dist`. As with the `LayoutOptimizationRandomSearch` class, the boundaries specified can be complex (and may contain separate areas). User settings include `rotation_step`, which specifies the step size for rotating the grid (in degrees); `rotation_range`, which specifies the range of rotation angles; `translation_step` or `translation_step_D`, which specifies the step size for translating the grid in meters or rotor diameters, respectively; and `translation_range`, which specifies the range of possible translations. All come with default values, which we expect to be suitable for many or most users. ================================================ FILE: docs/multidimensional_wind_turbine.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "id": "ab10767e", "metadata": {}, "source": [ "# Multidimensional Wind Turbine\n", "\n", "Many external factors can affect the power and thrust curves of wind turbines. FLORIS supports the\n", "ability to define \"multidimensional\" wind turbines, where the power and thrust curves are defined\n", "as a function of external parameters. To enable this functionality, rather than defining `power`\n", "and `thrust_coefficient` as a function of `wind_speed` on the `power_thrust_table`, users should\n", "instead provide a path to a data file (described below) as `power_thrust_data_file`. Additionally,\n", "users must set the `multi_dimensional_cp_ct` option on the turbine definition to `True`.\n", "\n", "The power thrust data file should be a CSV file with the following columns:\n", "(``, ``, ..., `ws`, `power`,\n", "`thrust_coefficient`). The external parameters can be any relevant factors that affect the turbine\n", "performance, and the values to be used will be specified at run time or in the FLORIS input file.\n", "For example, the external parameters could be air density, wave height, etc. The `ws` column should\n", "contain the wind speed values for specification of the power and thrust coefficient (stored in the\n", "`power` and `thrust_coefficient` columns, respectively). The wind speed, power, and thrust\n", "coefficient values should be defined for each combination of the external parameters.\n", "\n", "The user can then specify the values of the external parameters either on the FLORIS input file\n", "or using the `multidim_conditions` argument of `FlorisModel.set()`. The power and thrust coefficient\n", "are determined based on the specified conditions using a nearest-neighbor approach.\n", "\n", "The following code snippet shows an example of a multidimensional wind turbine definition and its\n", "corresponding power thrust data file." ] }, { "cell_type": "code", "execution_count": null, "id": "cc97a774", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from floris import FlorisModel, TimeSeries\n", "\n", "n_wind_speeds = 100\n", "wind_speeds = np.linspace(0.1, 30, n_wind_speeds)\n", "\n", "fmodel = FlorisModel(\"defaults\") # Defaults to NREL 5MW turbine\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_directions=np.zeros(n_wind_speeds),\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=0.06\n", " ),\n", " layout_x=[0],\n", " layout_y=[0],\n", " wind_shear=0.0,\n", " turbine_type=[\"iea_15MW_floating_multi_dim_cp_ct\"],\n", " reference_wind_height=-1,\n", ")\n", "\n", "# Now, we need to specify what external parameters to run the model for\n", "fmodel.set(multidim_conditions={\"Tp\": 2, \"Hs\": 1})\n", "fmodel.run()\n", "\n", "powers = fmodel.get_turbine_powers()\n", "thrust_coefficients = fmodel.get_turbine_thrust_coefficients()\n", "\n", "fig, ax = plt.subplots(2, 1, sharex=True)\n", "ax[0].plot(wind_speeds, powers, label=\"First condition\")\n", "ax[0].grid()\n", "ax[0].set_ylabel(\"Power [kW]\")\n", "ax[1].plot(wind_speeds, thrust_coefficients)\n", "ax[1].grid()\n", "ax[1].set_ylabel(\"Thrust coefficient [-]\")\n", "ax[1].set_xlabel(\"Wind speed [m/s]\")\n", "ax[1].set_xlim([0, 30])\n", "\n", "# Set a second multidimensional condition and rerun\n", "fmodel.set(multidim_conditions={\"Tp\": 4, \"Hs\": 5})\n", "fmodel.run()\n", "\n", "powers = fmodel.get_turbine_powers()\n", "thrust_coefficients = fmodel.get_turbine_thrust_coefficients()\n", "ax[0].plot(wind_speeds, powers, label=\"Second condition\")\n", "ax[0].legend(loc=\"upper left\")\n", "ax[1].plot(wind_speeds, thrust_coefficients)" ] }, { "cell_type": "markdown", "id": "98fd51f6", "metadata": {}, "source": [ "Note that this example is not meant to be represntative of a real turbine, but rather to illustrate\n", "the functionality. At this time, FLORIS only support a single external condition combination at a\n", "time, but multiple combinations can be run sequentially as is shown above.\n", "\n", "As of FLORIS v4.6, users can further specify array-based multidimensional conditions, where a\n", "different multidimensional condition can be specified for each findex. This allows users to run\n", "multiple multidimensional condition combinations in a single call to `fmodel.run()`. Further,\n", "multidimensional conditions can now be passed to `floris.set()` via the `WindData` objects\n", "(`TimeSeries`, in this case)." ] }, { "cell_type": "markdown", "id": "8c21f432", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": null, "id": "c0b18ef8", "metadata": {}, "outputs": [], "source": [ "multidim_conditions = {\n", " \"Tp\": np.array([2]*n_wind_speeds + [4]*n_wind_speeds),\n", " \"Hs\": np.array([1]*n_wind_speeds + [5]*n_wind_speeds),\n", "}\n", "\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_directions=np.zeros(n_wind_speeds*2),\n", " wind_speeds=np.tile(wind_speeds, 2),\n", " turbulence_intensities=0.06,\n", " multidim_conditions=multidim_conditions,\n", " ),\n", ")\n", "# Call fmodel.run() once and run both conditions in one go\n", "fmodel.run()\n", "\n", "powers = fmodel.get_turbine_powers()\n", "thrust_coefficients = fmodel.get_turbine_thrust_coefficients()\n", "\n", "fig, ax = plt.subplots(2, 1, sharex=True)\n", "ax[0].plot(wind_speeds, powers[:n_wind_speeds], label=\"First condition\")\n", "ax[0].grid()\n", "ax[0].set_ylabel(\"Power [kW]\")\n", "ax[1].plot(wind_speeds, thrust_coefficients[:n_wind_speeds])\n", "ax[1].grid()\n", "ax[1].set_ylabel(\"Thrust coefficient [-]\")\n", "ax[1].set_xlabel(\"Wind speed [m/s]\")\n", "ax[1].set_xlim([0, 30])\n", "ax[0].plot(wind_speeds, powers[n_wind_speeds:], label=\"Second condition\")\n", "ax[0].legend(loc=\"upper left\")\n", "ax[1].plot(wind_speeds, thrust_coefficients[n_wind_speeds:])" ] } ], "metadata": { "kernelspec": { "display_name": "eni", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.7" } }, "nbformat": 4, "nbformat_minor": 5 } ================================================ FILE: docs/operation_models_user.ipynb ================================================ { "cells": [ { "attachments": {}, "cell_type": "markdown", "id": "ac224ce9-bd4f-4f5c-88b7-f0e9e49ee498", "metadata": {}, "source": [ "# Turbine Operation Models\n", "\n", "Separate from the turbine models, which define the physical characterstics of the turbines, FLORIS\n", "allows users to specify how the turbine behaves in terms of producing power and thurst. We refer to \n", "different models for turbine behavior as \"operation models\". A key feature of operation models is\n", "the ability for users to specify control setpoints at which the operation model will be evaluated. \n", "For instance, some operation models allow users to specify `yaw_angles`, which alter the power \n", "being produced by the turbine along with it's thrust force on flow.\n", "\n", "Operation models are specified by the `operation_model` key on the turbine yaml file, or by using\n", "the `set_operation_model()` method on `FlorisModel`. Each operation model available in FLORIS is\n", "described and demonstrated below. The simplest operation model is the `\"simple\"` operation model,\n", "which takes no control setpoints and simply evaluates the power and thrust coefficient curves for \n", "the turbine at the current wind condition. The default operation model is the `\"cosine-loss\"`\n", "operation model, which models the loss in power of a turbine under yaw misalignment using a cosine\n", "term with an exponent.\n", "\n", "We first provide a quick demonstration of how to switch between different operation models. Then, \n", "each operation model available in FLORIS is described, along with its relevant control setpoints.\n", "We also describe the different parameters that must be specified in the turbine \n", "`\"power_thrust_table\"` dictionary in order to use that operation model." ] }, { "attachments": {}, "cell_type": "markdown", "id": "71788b47-6641-4080-bb3f-eb799d969e0b", "metadata": {}, "source": [ "## Selecting the operation model\n", "\n", "There are two options for selecting the operation model:\n", "1. Manually changing the `\"operation_model\"` field of the turbine input yaml \n", "(see [Turbine Input File Reference](input_reference_turbine))\n", "\n", "2. Using `set_operation_model()` on an instantiated `FlorisModel` object.\n", "\n", "The following code demonstrates the use of the second option." ] }, { "cell_type": "code", "execution_count": null, "id": "2275840e-48a3-41d2-ace9-fad05da0dc02", "metadata": { "tags": [] }, "outputs": [], "source": [ "from floris import FlorisModel\n", "from floris import layout_visualization as layoutviz\n", "\n", "fmodel = FlorisModel(\"../examples/inputs/gch.yaml\")\n", "\n", "# Look at layout\n", "ax = layoutviz.plot_turbine_rotors(fmodel)\n", "layoutviz.plot_turbine_labels(fmodel, ax=ax)\n", "ax.set_xlabel(\"x [m]\")\n", "ax.set_ylabel(\"y [m]\")\n", "\n", "# Set simple operation model\n", "fmodel.set_operation_model(\"simple\")\n", "\n", "# Evalaute the model and extract the power output\n", "fmodel.run()\n", "print(\"simple operation model powers [kW]: \", fmodel.get_turbine_powers() / 1000)\n", "\n", "# Set the yaw angles (which the \"simple\" operation model does not use\n", "# and change the operation model to \"cosine-loss\"\n", "fmodel.set(yaw_angles=[[20., 0., 0.]])\n", "fmodel.set_operation_model(\"cosine-loss\")\n", "ax = layoutviz.plot_turbine_rotors(fmodel)\n", "layoutviz.plot_turbine_labels(fmodel, ax=ax)\n", "ax.set_xlabel(\"x [m]\")\n", "ax.set_ylabel(\"y [m]\")\n", "\n", "# Evaluate again\n", "fmodel.run()\n", "powers_cosine_loss = fmodel.get_turbine_powers()\n", "print(\"cosine-loss operation model powers [kW]: \", fmodel.get_turbine_powers() / 1000)\n" ] }, { "cell_type": "markdown", "id": "5d22f376", "metadata": {}, "source": [ "## Operation model library" ] }, { "attachments": {}, "cell_type": "markdown", "id": "f2576e8a-47ee-48b5-8707-aca0dc76929c", "metadata": {}, "source": [ "### Simple model\n", "User-level name: `\"simple\"`\n", "\n", "Underlying class: `SimpleTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `ref_air_density` (scalar)\n", "- `ref_tilt` (scalar)\n", "- `wind_speed` (list)\n", "- `power` (list)\n", "- `thrust_coefficient` (list)\n", "\n", "The `\"simple\"` operation model describes the \"normal\" function of a wind turbine, as described by\n", "its power curve and thrust coefficient. It does not respond to any control setpoints, and is most \n", "often used as a baseline or for users wanting to evaluate wind farms in nominal operation." ] }, { "cell_type": "markdown", "id": "ced1e091", "metadata": {}, "source": [ "### Cosine loss model\n", "User-level name: `\"cosine-loss\"`\n", "\n", "Underlying class: `CosineLossTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `ref_air_density` (scalar)\n", "- `ref_tilt` (scalar)\n", "- `wind_speed` (list)\n", "- `power` (list)\n", "- `thrust_coefficient` (list)\n", "- `cosine_loss_exponent_yaw` (scalar)\n", "- `cosine_loss_exponent_tilt` (scalar)\n", "\n", "The `\"cosine-loss\"` operation model describes the decrease in power and thrust produced by a \n", "wind turbine as it yaws (or tilts) away from the incoming wind. The thrust is reduced by a factor of \n", "$\\cos \\gamma$, where $\\gamma$ is the yaw misalignment angle, while the power is reduced by a factor \n", "of $(\\cos\\gamma)^{p_P}$, where $p_P$ is the cosine loss exponent, specified by `cosine_loss_exponent_yaw`.\n", "Similarly, the thrust and power are reduced by factors of $(\\cos \\theta / \\cos \\theta_{ref})$ and\n", "$(\\cos \\theta / \\cos \\theta_{ref})^{p_T}$, respectively, where $\\theta$ is the tilt angle, \n", "$\\theta_{ref}$ is the reference tilt angle under which the power and thrust curves are specified, and\n", "$p_T$ is the cosine loss exponent for tilt, specified by `cosine_loss_exponent_tilt`.\n", "The power and thrust produced by the turbine\n", "thus vary as a function of the turbine's yaw angle, set using the `yaw_angles` argument to \n", "`FlorisModel.set()`, and tilt angle (if applicable, for example for floating wind turbines)." ] }, { "cell_type": "code", "execution_count": null, "id": "b9a5f00a-0ead-4759-b911-3a1161e55791", "metadata": { "tags": [] }, "outputs": [], "source": [ "from floris import TimeSeries\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "# Set up the FlorisModel\n", "fmodel.set_operation_model(\"cosine-loss\")\n", "fmodel.set(layout_x=[0.0], layout_y=[0.0])\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_speeds=np.ones(100) * 8.0,\n", " wind_directions=np.ones(100) * 270.0,\n", " turbulence_intensities=0.06\n", " )\n", ")\n", "fmodel.reset_operation()\n", "\n", "# Sweep the yaw angles\n", "yaw_angles = np.linspace(-25, 25, 100)\n", "fmodel.set(yaw_angles=yaw_angles.reshape(-1,1))\n", "fmodel.run()\n", "\n", "powers = fmodel.get_turbine_powers()/1000\n", "\n", "fig, ax = plt.subplots()\n", "ax.plot(yaw_angles, powers)\n", "ax.grid()\n", "ax.set_xlabel(\"Yaw angle [deg]\")\n", "ax.set_ylabel(\"Power [kW]\")" ] }, { "cell_type": "markdown", "id": "019abca6", "metadata": {}, "source": [ "### Simple derating model\n", "User-level name: `\"simple-derating\"`\n", "\n", "Underlying class: `SimpleDeratingTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `ref_air_density` (scalar)\n", "- `ref_tilt` (scalar)\n", "- `wind_speed` (list)\n", "- `power` (list)\n", "- `thrust_coefficient` (list)\n", "\n", "The `\"simple-derating\"` operation model enables users to derate turbines by setting a new power \n", "rating. It does not require any extra parameters on the `power_thrust_table`, but adescribes the \n", "decrease in power and thrust produced by providing the `power_setpoints` argument to\n", "`FlorisModel.set()`. The default power rating for the turbine can be acheived by setting the\n", "appropriate entries of `power_setpoints` to `None`." ] }, { "cell_type": "code", "execution_count": null, "id": "722be425-9231-451a-bd84-7824db6a5098", "metadata": { "tags": [] }, "outputs": [], "source": [ "# Set up the FlorisModel\n", "fmodel.set_operation_model(\"simple-derating\")\n", "fmodel.reset_operation()\n", "wind_speeds = np.linspace(0, 30, 100)\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_speeds=wind_speeds,\n", " wind_directions=np.ones(100) * 270.0,\n", " turbulence_intensities=0.06\n", " )\n", ")\n", "\n", "fig, ax = plt.subplots()\n", "for power_setpoint in [5.0, 4.0, 3.0, 2.0]:\n", " fmodel.set(power_setpoints=np.array([[power_setpoint*1e6]]*100))\n", " fmodel.run()\n", " powers = fmodel.get_turbine_powers()/1000\n", " ax.plot(wind_speeds, powers[:,0], label=f\"Power setpoint (MW): {power_setpoint}\")\n", "\n", "ax.grid()\n", "ax.legend()\n", "ax.set_xlabel(\"Wind speed [m/s]\")\n", "ax.set_ylabel(\"Power [kW]\")" ] }, { "cell_type": "markdown", "id": "4caca5fa", "metadata": {}, "source": [ "### Mixed operation model\n", "User-level name: `\"mixed\"`\n", "\n", "Underlying class: `MixedOperationTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `ref_air_density` (scalar)\n", "- `ref_tilt` (scalar)\n", "- `wind_speed` (list)\n", "- `power` (list)\n", "- `thrust_coefficient` (list)\n", "- `cosine_loss_exponent_yaw` (scalar)\n", "- `cosine_loss_exponent_tilt` (scalar)\n", "\n", "The `\"mixed\"` operation model allows users to specify _either_ `yaw_angles` (evaluated using the \n", "`\"cosine-loss\"` operation model) _or_ `power_setpoints` (evaluated using the `\"simple-derating\"`\n", "operation model). That is, for each turbine, and at each `findex`, a non-zero yaw angle or a \n", "non-`None` power setpoint may be specified. However, specifying both a non-zero yaw angle and a \n", "finite power setpoint for the same turbine and at the same `findex` will produce an error." ] }, { "cell_type": "code", "execution_count": null, "id": "5e3cda81", "metadata": {}, "outputs": [], "source": [ "fmodel.set_operation_model(\"mixed\")\n", "fmodel.set(layout_x=[0.0, 0.0], layout_y=[0.0, 500.0])\n", "fmodel.reset_operation()\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_speeds=np.array([10.0]),\n", " wind_directions=np.array([270.0]),\n", " turbulence_intensities=0.06\n", " )\n", ")\n", "fmodel.set(\n", " yaw_angles=np.array([[20.0, 0.0]]),\n", " power_setpoints=np.array([[None, 2e6]])\n", ")\n", "fmodel.run()\n", "print(\"Powers [kW]: \", fmodel.get_turbine_powers()/1000)" ] }, { "cell_type": "markdown", "id": "c036feda", "metadata": {}, "source": [ "### AWC model\n", "\n", "User-level name: `\"awc\"`\n", "\n", "Underlying class: `AWCTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `ref_air_density` (scalar)\n", "- `ref_tilt` (scalar)\n", "- `wind_speed` (list)\n", "- `power` (list)\n", "- `thrust_coefficient` (list)\n", "- `helix_a` (scalar)\n", "- `helix_power_b` (scalar)\n", "- `helix_power_c` (scalar)\n", "- `helix_thrust_b` (scalar)\n", "- `helix_thrust_c` (scalar)\n", "\n", "The `\"awc\"` operation model allows for users to define _active wake control_ strategies. These strategies \n", "use pitch control to actively enhance wake mixing and subsequently decrease wake velocity deficits. As a \n", "result, downstream turbines can increase their power production, with limited power loss for the controlled \n", "upstream turbine. The `AWCTurbine` class models this power loss at the turbine applying AWC. For each \n", "turbine, the user can define an AWC strategy to implement through the `awc_modes` array. Note that currently, \n", "only `\"baseline\"`, i.e., no AWC, and `\"helix\"`, i.e., the \n", "[counterclockwise helix method](https://doi.org/10.1002/we.2513) have been implemented. \n", "\n", "The user then defines the exact AWC implementation through setting the variable `awc_amplitudes` for \n", "each turbine. This variable defines the mean-to-peak amplitude of the sinusoidal AWC pitch excitation,\n", "i.e., for a turbine that under `awc_modes = \"baseline\"` has a constant pitch angle of 0 degrees, setting \n", "`awc_amplitude = 2` results in a pitch signal varying from -2 to 2 degrees over the desired Strouhal\n", "frequency. This Strouhal frequency is not used as an input here, since it has minimal influence on turbine \n", "power production. Note that setting `awc_amplitudes = 0` effectively disables AWC and is therefore the same \n", "as running a turbine at `awc_modes = \"baseline\"`.\n", "\n", "Each example turbine input file `floris/turbine_library/*.yaml` has its own `helix_*` parameter data. These \n", "parameters are determined by fitting data from `OpenFAST` simulations in region II to the following equation:\n", "\n", "$$\n", " P_\\text{AWC} = P_\\text{baseline} \\cdot (1 - (b + c \\cdot P_\\text{baseline} ) \\cdot A_\\text{AWC}^a)\n", "$$\n", "\n", "where $a$ is `\"helix_a\"`, $b$ is `\"helix_power_b\"`, $c$ is `\"helix_power_c\"`, and $A_\\text{AWC}$ is `awc_amplitudes`. \n", "The thrust coefficient follows the same equation, but with the respective thrust parameters. When AWC is \n", "turned on while $P_\\text{baseline} > P_\\text{rated}$, a warning is given as the model is not yet tuned for region III.\n", "\n", "The figure below shows the fit between the turbine power and thrust in OpenFAST helix AWC simulations (x) \n", "and FLORIS simulations (--) at different region II wind speeds for the NREL 5MW reference turbine.\n", "\n", "\n", "![](./powerthrust_helix.png)" ] }, { "cell_type": "code", "execution_count": null, "id": "40e9bcda", "metadata": {}, "outputs": [], "source": [ "fmodel = FlorisModel(\"../examples/inputs/emgauss_helix.yaml\")\n", "fmodel.set_operation_model(\"awc\")\n", "fmodel.set(layout_x=[0.0, 0.0], layout_y=[0.0, 500.0])\n", "fmodel.reset_operation()\n", "fmodel.set(\n", " wind_speeds=np.array([8.0]),\n", " wind_directions=np.array([270.0]),\n", " turbulence_intensities=np.array([0.06])\n", ")\n", "fmodel.set(\n", " awc_modes=np.array([[\"helix\", \"baseline\"]]),\n", " awc_amplitudes=np.array([[2.5, 0]])\n", ")\n", "fmodel.run()\n", "print(\"Powers [kW]: \", fmodel.get_turbine_powers()/1000)" ] }, { "cell_type": "markdown", "id": "25f9c86c", "metadata": {}, "source": [ "### Peak shaving model\n", "\n", "User-level name: `\"peak-shaving\"`\n", "\n", "Underlying class: `PeakShavingTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `ref_air_density` (scalar)\n", "- `ref_tilt` (scalar)\n", "- `wind_speed` (list)\n", "- `power` (list)\n", "- `thrust_coefficient` (list)\n", "- `peak_shaving_fraction` (scalar)\n", "- `peak_shaving_TI_threshold` (scalar)\n", "\n", "The `\"peak-shaving\"` operation model allows users to implement peak shaving, where the thrust\n", "of the wind turbine is reduced from the nominal curve near rated to reduce unwanted structural\n", "loading. Peak shaving here is implemented here by reducing the thrust by a fixed fraction from\n", "the peak thrust on the nominal thrust curve, as specified by `peak_shaving_fraction`.This only\n", "affects wind speeds near the peak in the thrust\n", "curve (usually near rated wind speed), as thrust values away from the peak will be below the\n", "fraction regardless. Further, peak shaving is only applied if the turbulence intensity experienced\n", "by the turbine meets the `peak_shaving_TI_threshold`. To apply peak shaving in all wind conditions,\n", "`peak_shaving_TI_threshold` may be set to zero.\n", "\n", "When the turbine is peak shaving to reduce thrust, the power output is updated accordingly. Letting\n", "$C_{T}$ represent the thrust coefficient when peak shaving (at given wind speed), and $C_{T}'$\n", "represent the thrust coefficient that the turbine would be operating at under nominal control, then\n", "the power $P$ due to peak shaving (compared to the power $P'$ available under nominal control) is \n", "computed (based on actuator disk theory) as\n", "\n", "$$ P = \\frac{C_T (1 - a)}{C_T' (1 - a')} P'$$\n", "\n", "where $a$ (respectively, $a'$) is the axial induction factor corresponding to $C_T$\n", "(respectively, $C_T'$), computed using the usual relationship from actuator disk theory,\n", "i.e. the lesser solution to $C_T=4a(1-a)$.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1eff05f3", "metadata": {}, "outputs": [], "source": [ "# Set up the FlorisModel\n", "fmodel = FlorisModel(\"../examples/inputs/gch.yaml\")\n", "fmodel.set(\n", " layout_x=[0.0], layout_y=[0.0],\n", " wind_data=TimeSeries(\n", " wind_speeds=wind_speeds,\n", " wind_directions=np.ones(100) * 270.0,\n", " turbulence_intensities=0.2 # Higher than threshold value of 0.1\n", " )\n", ")\n", "fmodel.reset_operation()\n", "fmodel.set_operation_model(\"simple\")\n", "fmodel.run()\n", "powers_base = fmodel.get_turbine_powers()/1000\n", "thrust_coefficients_base = fmodel.get_turbine_thrust_coefficients()\n", "fmodel.set_operation_model(\"peak-shaving\")\n", "fmodel.run()\n", "powers_peak_shaving = fmodel.get_turbine_powers()/1000\n", "thrust_coefficients_peak_shaving = fmodel.get_turbine_thrust_coefficients()\n", "\n", "fig, ax = plt.subplots(2,1,sharex=True)\n", "ax[0].plot(wind_speeds, thrust_coefficients_base, label=\"Without peak shaving\", color=\"black\")\n", "ax[0].plot(wind_speeds, thrust_coefficients_peak_shaving, label=\"With peak shaving\", color=\"C0\")\n", "ax[1].plot(wind_speeds, powers_base, label=\"Without peak shaving\", color=\"black\")\n", "ax[1].plot(wind_speeds, powers_peak_shaving, label=\"With peak shaving\", color=\"C0\")\n", "\n", "ax[1].grid()\n", "ax[0].grid()\n", "ax[0].legend()\n", "ax[0].set_ylabel(\"Thrust coefficient [-]\")\n", "ax[1].set_xlabel(\"Wind speed [m/s]\")\n", "ax[1].set_ylabel(\"Power [kW]\")" ] }, { "cell_type": "markdown", "id": "92912bf7", "metadata": {}, "source": [ "### Controller-dependent model\n", "\n", "User-level name: `\"controller-dependent\"`\n", "\n", "Underlying class: `ControllerDependentTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `rotor_solidity`: (scalar)\n", "- `rated_rpm`: (scalar)\n", "- `generator_efficiency`: (scalar)\n", "- `rated_power`: (scalar)\n", "- `rotor_diameter`: (scalar)\n", "- `beta`: (scalar)\n", "- `cd`: (scalar)\n", "- `cl_alfa`: (scalar)\n", "- `cp_ct_data_file`: (string)\n", "\n", "The `\"controller-dependent\"` operation model is an advanced operation model that uses the turbine's Cp/Ct\n", "surface to optimize performance under yaw misalignment.\n", "\n", "The `\"controller-dependent\"` operation model determines the wind turbine power output in yaw misalignment conditions, taking into account the effects of the control system. \n", "When the rotor operates in below-rated conditions, the models assumes that blade pitch is equal to the optimal value (corresponding to maximum power coefficient $C_P$),\n", "while the generator torque is scheduled as a function of rotational speed $\\omega$ according to the $K\\omega^2$ law.\n", "The $K$ coefficient is computed using the condition of maximum efficiency as shown in Chapter 7 of {cite:t}`hansen_book`.\n", "When the turbine operates above rated wind speed, the rotor speed is fixed and equal to the rated value, while the pitch angle is used to keep the power output equal to rated. \n", "The `\"controller-dependent\"` operation model solves a balance equation between electrical power and aerodynamic power, assuming the aforementioned control strategies.\n", "This yields the pitch and torque values that correspond to the current inflow and yaw misalignment. Based on these quantities, the power output of the turbine is computed.\n", "Because the balance equation must be solved, the `ControllerDependentTurbine` is slower to execute than some other operation models.\n", "\n", "The `\"controller-dependent\"` operation model considers the effect of vertical shear. Hence, the wind turbine can perform differently depending on the direction of misalignment.\n", "Vertical shear is computed locally at each rotor, so it can be affected by wake impingement. The model includes both the effects of yaw and tilt on rotor performance.\n", "To include the effects of shear, ensure that `turbine_grid_points` is greater than 1 in the main FLORIS configuration file.\n", "\n", "The `\"controller-dependent\"` operation model requires the definition of some parameters that depend on the turbine:\n", "1) Rated rotation speed (`rated_rpm`), generator efficiency (`generator_efficiency`), and rated power, specified in kW (`rated_power`).\n", "2) Three parameters that describe the aerodynamics of the blade, namely `beta` (twist), `cd` (drag) and `cl_alfa` (lift slope).\n", "These parameters are provided for the NREL 5MW, IEA 10MW, and IEA 15MW turbines in the \"floris/turbine_library/\".\n", "When using a different turbine model, these parameters can be estimated as shown in Section 3.5 of {cite:t}`tamaro2024beyondcosine`. \n", "3) Look-up tables of the power coefficient ($C_P$) and thrust coefficient $C_T$ as functions of tip-speed ratio and blade pitch angle.\n", "Approximate values for these are provided for the reference turbines in \"floris/turbine_library/\".\n", "4) Rotor solidity (`rotor_solidity`), i.e. the fraction of the rotor area occupied by the blades.\n", "\n", "Further details can be found in {cite:t}`tamaro2024beyondcosine`.\n", "Developed and implemented by Simone Tamaro, Filippo Campagnolo, and Carlo L. Bottasso at Technische Universität München (TUM) (email: simone.tamaro@tum.de)." ] }, { "cell_type": "code", "execution_count": null, "id": "6e4c5ba2", "metadata": {}, "outputs": [], "source": [ "N = 101 # How many steps to cover yaw range in\n", "yaw_max = 30 # Maximum yaw to test\n", "\n", "# Set up the yaw angle sweep\n", "yaw_angles = np.linspace(-yaw_max, yaw_max, N).reshape(-1,1)\n", "\n", "# We can use the same FlorisModel, but we'll set it up for this test\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_speeds=np.ones(N) * 8.0,\n", " wind_directions=np.ones(N) * 270.0,\n", " turbulence_intensities=0.06\n", " ),\n", " yaw_angles=yaw_angles,\n", ")\n", "\n", "# We'll compare the \"controller-dependent\" model to the standard \"cosine-loss\" model\n", "op_models = [\"cosine-loss\", \"controller-dependent\"]\n", "results = {}\n", "\n", "for op_model in op_models:\n", " print(f\"Evaluating model: {op_model}\")\n", " fmodel.set_operation_model(op_model)\n", "\n", " fmodel.run()\n", " results[op_model] = fmodel.get_turbine_powers().squeeze()\n", "\n", "fig, ax = plt.subplots()\n", "colors = [\"C0\", \"k\"]\n", "linestyles = [\"solid\", \"dashed\"]\n", "for key, c, ls in zip(results, colors, linestyles):\n", " central_power = results[key][yaw_angles.squeeze() == 0]\n", " ax.plot(yaw_angles.squeeze(), results[key]/central_power, label=key, color=c, linestyle=ls)\n", "\n", "ax.grid(True)\n", "ax.legend()\n", "ax.set_xlabel(\"Yaw angle [deg]\")\n", "ax.set_ylabel(\"Normalized turbine power [deg]\")" ] }, { "cell_type": "markdown", "id": "a269aa73", "metadata": {}, "source": [ "__WARNING__: The power and thrust curves generated by querying tabulated data as a function of blade pitch and tip-speed ratio for the reference wind turbines is _not_ an exact match for the reference power and thrust curves found at the IEA Wind Task 37's [Github page](https://github.com/IEAWindTask37) or the NREL 5MW [reference data](https://github.com/NREL/turbine-models/blob/master/Offshore/NREL_5MW_126_RWT_corrected.csv). As such, results using the Controller-dependent model, which rely on these $C_P$ and $C_T$ tables, should be considered demonstrative only and not necessarily consistent with other results using the reference wind turbines.\n", "\n", "For example, the nominal power and thrust curves for the IEA 15MW, IEA 10MW, and NREL 5MW are shown below, along with their derivations from the provided demonstrative $C_P$ / $C_T$ tables." ] }, { "cell_type": "code", "execution_count": null, "id": "90d5c155", "metadata": {}, "outputs": [], "source": [ "fmodel.reset_operation()\n", "for turbine in [\"nrel_5MW\", \"iea_10MW\", \"iea_15MW\"]:\n", " fmodel.set(\n", " layout_x=[0.0], layout_y=[0.0], turbine_type=[turbine],\n", " wind_data=TimeSeries(\n", " wind_speeds=wind_speeds,\n", " wind_directions=np.ones(100) * 270.0,\n", " turbulence_intensities=0.2 # Higher than threshold value of 0.1\n", " )\n", " )\n", " # Simple model (using reference power and thrust curves)\n", " fmodel.set_operation_model(\"simple\")\n", " fmodel.run()\n", " P_s = fmodel.get_turbine_powers()/1000\n", " CT_s = fmodel.get_turbine_thrust_coefficients()\n", "\n", " # Controller-dependent model (using demonstration Cp/Ct tables)\n", " fmodel.set_operation_model(\"controller-dependent\")\n", " fmodel.run()\n", " P_cd = fmodel.get_turbine_powers()/1000\n", " CT_cd = fmodel.get_turbine_thrust_coefficients()\n", "\n", " fig, ax = plt.subplots(2,1,sharex=True)\n", " ax[0].plot(wind_speeds, CT_s, label=\"Reference data\", color=\"black\")\n", " ax[0].plot(wind_speeds, CT_cd, label=\"Cp/Ct tables\", color=\"red\", linestyle=\"--\")\n", " ax[1].plot(wind_speeds, P_s, label=\"Reference data\", color=\"black\")\n", " ax[1].plot(wind_speeds, P_cd, label=\"Cp/Ct data\", color=\"red\", linestyle=\"--\")\n", "\n", " ax[1].grid()\n", " ax[0].grid()\n", " ax[0].legend()\n", " ax[0].set_ylabel(\"Thrust coefficient [-]\")\n", " ax[1].set_xlabel(\"Wind speed [m/s]\")\n", " ax[1].set_ylabel(\"Power [kW]\")\n", " ax[0].set_title(turbine)" ] }, { "cell_type": "markdown", "id": "a04db676", "metadata": {}, "source": [ "### Unified Momentum Model\n", "\n", "User-level name: `\"unified-momentum\"`\n", "\n", "Underlying class: `UnifiedMomentumModelTurbine`\n", "\n", "Required data on `power_thrust_table`:\n", "- `ref_air_density` (scalar)\n", "- `ref_tilt` (scalar)\n", "- `wind_speed` (list)\n", "- `power` (list)\n", "- `thrust_coefficient` (list)\n", "\n", "An extension of the classical one-dimensional momentum theory to model the induction of an\n", "actuator disk is presented in {cite:t}`HeckJohlasHowland2023_yawed_adm` to directly account\n", "for power and thrust loss due to yaw misalignment rather than using an empirical correction\n", "as in the cosine loss model. Analytical expressions for the induction, thrust, initial wake\n", "velocities and power are developed as a function of the yaw angle and thrust coefficient.\n", "\n", "Note that the low thrust limit of the Unified Momentum Model is presently implemented in FLORIS, which returns the equations derived and validated in Heck et al. (2023).\n", "This low thrust limit will be accurate for thrust coefficients approximately less than 0.9.\n", "\n", "This section recreates key validation figures discussed in the paper through FLORIS." ] }, { "cell_type": "code", "execution_count": null, "id": "b824e63b", "metadata": {}, "outputs": [], "source": [ "n_points = 20\n", "\n", "fmodel.set_operation_model(\"unified-momentum\")\n", "fmodel.set(layout_x=[0.0], layout_y=[0.0], turbine_type=[\"nrel_5MW\"])\n", "fmodel.reset_operation()\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_speeds=np.array(n_points * [11.0]),\n", " wind_directions=np.array(n_points * [270.0]),\n", " turbulence_intensities=0.06\n", " )\n", ")\n", "yaw_angles = np.linspace(0, 50, n_points)\n", "cos_reference = np.cos(np.radians(yaw_angles))\n", "cos3_reference = np.cos(np.radians(yaw_angles))**3\n", "\n", "fmodel.set(yaw_angles=np.reshape(yaw_angles, (-1,1)))\n", "fmodel.run()\n", "\n", "powers = fmodel.get_turbine_powers()\n", "power_ratio_umm = powers[:,0] / powers[0,0]\n", "\n", "fig, ax = plt.subplots(1,1)\n", "ax.plot(yaw_angles, power_ratio_umm, label=\"Unified momentum model\", color=\"black\")\n", "ax.plot(yaw_angles, cos_reference, label=r\"$\\cos(\\gamma)$\", linestyle=\":\", color=\"purple\")\n", "ax.plot(yaw_angles, cos3_reference, label=r\"$\\cos^3(\\gamma)$\", linestyle=\":\", color=\"orange\")\n", "ax.grid()\n", "ax.legend()\n", "ax.set_title(\"Figure 2 (a): Power ratio vs yaw angle\")\n", "ax.set_xlabel(r\"Yaw angle, $\\gamma$ (degrees)\")\n", "ax.set_ylabel(r\"Power ratio, $P(\\gamma)/P(0)$\")" ] }, { "cell_type": "code", "execution_count": null, "id": "44e95590", "metadata": {}, "outputs": [], "source": [ "from floris.core.turbine.unified_momentum_model import Heck, LimitedHeck\n", "\n", "n_points = 20\n", "yaw_angles = np.linspace(0, 50, n_points)\n", "\n", "ct_prime = 1.33\n", "\n", "heck = Heck()\n", "ai_umm = np.array([heck(ct_prime, np.radians(yaw)).an for yaw in yaw_angles])\n", "heck_no_spanwise = LimitedHeck()\n", "ai_no_spanwise = np.array([heck_no_spanwise(ct_prime, np.radians(yaw)).an for yaw in yaw_angles])\n", "\n", "fig, ax = plt.subplots(1,1)\n", "ax.plot(yaw_angles, ai_umm / ai_umm[0], label=\"Yaw-dependent UMM\", color=\"black\")\n", "ax.plot(yaw_angles, ai_no_spanwise/ ai_no_spanwise[0], label=\"Low outlet spanwise velocity limit\", linestyle=\"--\", color=\"blue\")\n", "ax.grid()\n", "ax.legend()\n", "ax.set_title(\"Figure 3: Normalized rotor-normal, rotor-averaged induction for the yawed UMM\")\n", "ax.set_xlabel(r\"Yaw angle, $\\gamma$ (degrees)\")\n", "ax.set_ylabel(r\"Axial induction ratio, $a_n(\\gamma) / a_n(0)$\")" ] }, { "cell_type": "code", "execution_count": null, "id": "d17daafe", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" } }, "nbformat": 4, "nbformat_minor": 5 } ================================================ FILE: docs/references.bib ================================================ --- --- @article{abkar2015influence, title={Influence of atmospheric stability on wind-turbine wakes: A large-eddy simulation study}, author={Abkar, Mahdi and Port{\'e}-Agel, Fernando}, journal={Physics of fluids}, volume={27}, number={3}, pages={035104}, year={2015}, publisher={AIP Publishing LLC} } @article{bastankhah2014new, title={A new analytical model for wind-turbine wakes}, author={Bastankhah, Majid and Port{\'e}-Agel, Fernando}, journal={Renewable Energy}, volume={70}, pages={116--123}, year={2014}, publisher={Elsevier} } @article{bastankhah2016experimental, title={Experimental and theoretical study of wind turbine wakes in yawed conditions}, author={Bastankhah, Majid and Port{\'e}-Agel, Fernando}, journal={Journal of Fluid Mechanics}, volume={806}, pages={506--541}, year={2016}, publisher={Cambridge University Press} } @article{niayifar2016analytical, title={Analytical modeling of wind farms: A new approach for power prediction}, author={Niayifar, Amin and Port{\'e}-Agel, Fernando}, journal={Energies}, volume={9}, number={9}, pages={741}, year={2016}, publisher={Multidisciplinary Digital Publishing Institute} } @article{dilip2017wind, title={Wind turbine wake mitigation through blade pitch offset}, author={Dilip, Deepu and Port{\'e}-Agel, Fernando}, journal={Energies}, volume={10}, number={6}, pages={757}, year={2017}, publisher={Multidisciplinary Digital Publishing Institute} } @Article{blondel2020alternative, AUTHOR = {Blondel, F. and Cathelain, M.}, TITLE = {An alternative form of the super-Gaussian wind turbine wake model}, JOURNAL = {Wind Energy Science Discussions}, VOLUME = {2020}, YEAR = {2020}, PAGES = {1--16}, URL = {https://www.wind-energ-sci-discuss.net/wes-2019-99/}, DOI = {10.5194/wes-2019-99} } @inproceedings{Cathelain2020calibration, author = {M. Cathelain and F. Blondel and P.A. Joulin and P. Bozonnet}, title = {Calibration of a super-Gaussian wake model with a focus on near-wake characteristics}, doi = {10.1088/1742-6596/1618/6/062008}, year = 2020, number={1}, month = {sep}, publisher = {{IOP} Publishing}, volume = {1618}, pages = {062008}, booktitle={Journal of Physics: Conference Series}, organization={IOP Publishing: Conference Series} } @article{qian2018new, title={A new analytical wake model for yawed wind turbines}, author={Qian, Guo-Wei and Ishihara, Takeshi}, journal={Energies}, volume={11}, number={3}, pages={665}, year={2018}, publisher={Multidisciplinary Digital Publishing Institute} } @article{martinez2019aerodynamics, title={The aerodynamics of the curled wake: A simplified model in view of flow control}, author={Mart{\'\i}nez-Tossas, Luis A and Annoni, Jennifer and Fleming, Paul A and Churchfield, Matthew J}, journal={Wind Energy Science (Online)}, volume={4}, number={NREL/JA-5000-73451}, year={2019}, publisher={National Renewable Energy Lab.(NREL), Golden, CO (United States)} } @article{fleming2018simulation, AUTHOR = {Fleming, P. and Annoni, J. and Churchfield, M. and Martinez-Tossas, L. A. and Gruchalla, K. and Lawson, M. and Moriarty, P.}, TITLE = {A simulation study demonstrating the importance of large-scale trailing vortices in wake steering}, JOURNAL = {Wind Energy Science}, VOLUME = {3}, YEAR = {2018}, NUMBER = {1}, PAGES = {243--255}, URL = {https://www.wind-energ-sci.net/3/243/2018/}, DOI = {10.5194/wes-3-243-2018} } @inproceedings{gebraad2014data, title={A data-driven model for wind plant power optimization by yaw control}, author={Gebraad, Pieter MO and Teeuwisse, FW and van Wingerden, Jan-Willem and Fleming, Paul A and Ruben, Shalom D and Marden, Jason R and Pao, Lucy Y}, booktitle={2014 American Control Conference}, pages={3128--3134}, year={2014}, organization={IEEE} } @article{gebraad2016wind, title={Wind plant power optimization through yaw control using a parametric model for wake effects—a CFD simulation study}, author={Gebraad, Pieter MO and Teeuwisse, FW and Van Wingerden, JW and Fleming, Paul A and Ruben, SD and Marden, JR and Pao, LY}, journal={Wind Energy}, volume={19}, number={1}, pages={95--114}, year={2016}, publisher={Wiley Online Library} } @Article{annoni2018analysis, AUTHOR = {Annoni, J. and Fleming, P. and Scholbrock, A. and Roadman, J. and Dana, S. and Adcock, C. and Porte-Agel, F. and Raach, S. and Haizmann, F. and Schlipf, D.}, TITLE = {Analysis of control-oriented wake modeling tools using lidar field results}, JOURNAL = {Wind Energy Science}, VOLUME = {3}, YEAR = {2018}, NUMBER = {2}, PAGES = {819--831}, URL = {https://www.wind-energ-sci.net/3/819/2018/}, DOI = {10.5194/wes-3-819-2018} } @article{crespo1996turbulence, title={Turbulence characteristics in wind-turbine wakes}, author={Crespo, A. and Hernández, J.}, journal={Journal of wind engineering and industrial aerodynamics}, volume={61}, number={1}, pages={71--85}, year={1996}, publisher={Elsevier} } @inproceedings{gunn2016limitations, title={Limitations to the validity of single wake superposition in wind farm yield assessment}, author={Gunn, Kester and Stock-Williams, Clym and Burke, M and Willden, Richard and Vogel, C and Hunter, W and Stallard, T and Robinson, N and Schmidt, SR}, booktitle={Journal of Physics: Conference Series}, number={1}, year={2016}, organization={IOP Publishing: Conference Series} } @Article{King2019Controls, author = {Jennifer King and Paul Fleming and Ryan King and Luis A. Martinez-Tossas}, title = {Controls-Oriented Model to Capture Secondary Effects of Wake Steering}, journal = {Submitted to Wind Energy Science}, year = {2019} } @article{jimenez2010application, title={Application of a LES technique to characterize the wake deflection of a wind turbine in yaw}, author={Jim{\'e}nez, {\'A}ngel and Crespo, Antonio and Migoya, Emilio}, journal={Wind energy}, volume={13}, number={6}, pages={559--572}, year={2010}, publisher={Wiley Online Library} } @book{jensen1983note, title={A note on wind generator interaction}, author={Jensen, Niels Otto}, year={1983}, publisher={Ris{\o} National Laboratory} } @inproceedings{bay2020towards, title={Towards flow control: an assessment of the curled wake model in the FLORIS framework}, author={Bay, Christopher J. and King, Jennifer and Mart{\`i}nez-Tossas, Luis A. and Mudafort, Rafael and Hulsman, Paul and K{\"u}hn, Martin and Fleming, Paul}, booktitle={Journal of Physics: Conference Series}, year={2020}, organization={IOP Publishing} } @techreport{wharton2010assessing, title={Assessing atmospheric stability and the impacts on wind characteristics at an onshore wind farm}, author={Wharton, Sonia and Lundquist, JK}, year={2010}, institution={Lawrence Livermore National Lab.(LLNL), Livermore, CA (United States)} } @Article{fleming2019initial, AUTHOR = {Fleming, P. and King, J. and Dykes, K. and Simley, E. and Roadman, J. and Scholbrock, A. and Murphy, P. and Lundquist, J. K. and Moriarty, P. and Fleming, K. and van Dam, J. and Bay, C. and Mudafort, R. and Lopez, H. and Skopek, J. and Scott, M. and Ryan, B. and Guernsey, C. and Brake, D.}, TITLE = {Initial results from a field campaign of wake steering applied at a commercial wind farm -- Part 1}, JOURNAL = {Wind Energy Science}, VOLUME = {4}, YEAR = {2019}, NUMBER = {2}, PAGES = {273--285}, URL = {https://www.wind-energ-sci.net/4/273/2019/}, DOI = {10.5194/wes-4-273-2019} } @Article{fleming2019continued, AUTHOR = {Fleming, P. and King, J. and Simley, E. and Roadman, J. and Scholbrock, A. and Murphy, P. and Lundquist, J. K. and Moriarty, P. and Fleming, K. and van Dam, J. and Bay, C. and Mudafort, R. and Jager, D. and Skopek, J. and Scott, M. and Ryan, B. and Guernsey, C. and Brake, D.}, TITLE = {Continued Results from a Field Campaign of Wake Steering Applied at a Commercial Wind Farm: Part 2}, JOURNAL = {Wind Energy Science Discussions}, VOLUME = {2020}, YEAR = {2020}, PAGES = {1--24}, URL = {https://www.wind-energ-sci-discuss.net/wes-2019-104/}, DOI = {10.5194/wes-2019-104} } @article{ainslie1988calculating, title={Calculating the flowfield in the wake of wind turbines}, author={Ainslie, John F}, journal={Journal of Wind Engineering and Industrial Aerodynamics}, volume={27}, number={1-3}, pages={213--224}, year={1988}, publisher={Elsevier} } @InProceedings{nygaard2020modelling, author = {Nygaard, Nicolai Gayle and Steen, S{\o}ren Trads and Poulsen, Lina and Pedersen, Jesper Gr{\o}nnegaard}, booktitle = {Journal of Physics: Conference Series}, title = {Modelling cluster wakes and wind farm blockage}, year = {2020}, number = {6}, organization = {IOP Publishing}, pages = {062072}, volume = {1618}, } @article{bastankhah_2021, title={Analytical solution for the cumulative wake of wind turbines in wind farms}, volume={911}, DOI={10.1017/jfm.2020.1037}, journal={Journal of Fluid Mechanics}, publisher={Cambridge University Press}, author={Bastankhah, Majid and Welch, Bridget L. and Martínez-Tossas, Luis A. and King, Jennifer and Fleming, Paul}, year={2021}, pages={A53} } @Article{bay_2022, AUTHOR = {Bay, C. J. and Fleming, P. and Doekemeijer, B. and King, J. and Churchfield, M. and Mudafort, R.}, TITLE = {Addressing deep array effects and impacts to wake steering with the cumulative-curl wake model}, JOURNAL = {Wind Energy Science Discussions}, VOLUME = {2022}, YEAR = {2022}, PAGES = {1--28}, URL = {https://wes.copernicus.org/preprints/wes-2022-17/}, DOI = {10.5194/wes-2022-17} } @article{Pedersen_2022_turbopark2, url = {https://dx.doi.org/10.1088/1742-6596/2265/2/022063}, year = {2022}, month = {may}, publisher = {IOP Publishing}, volume = {2265}, number = {2}, pages = {022063}, author = {J G Pedersen and E Svensson and L Poulsen and N G Nygaard}, title = {Turbulence Optimized Park model with Gaussian wake profile}, journal = {Journal of Physics: Conference Series}, } @article{SinnerFleming2024grs, doi = {10.1088/1742-6596/2767/3/032036}, url = {https://dx.doi.org/10.1088/1742-6596/2767/3/032036}, year = {2024}, month = {jun}, publisher = {IOP Publishing}, volume = {2767}, number = {3}, pages = {032036}, author = {Michael Sinner and Paul Fleming}, title = {Robust wind farm layout optimization}, journal = {Journal of Physics: Conference Series}, } @Article{tamaro2024beyondcosine, AUTHOR = {Tamaro, S. and Campagnolo, F. and Bottasso, C. L.}, TITLE = {On the power and control of a misaligned rotor -- beyond the cosine law}, JOURNAL = {Wind Energy Science}, VOLUME = {9}, YEAR = {2024}, NUMBER = {7}, PAGES = {1547--1575}, URL = {https://wes.copernicus.org/articles/9/1547/2024/}, DOI = {10.5194/wes-9-1547-2024} } @book{hansen_book, author = {Hansen, M.O.L.}, year = {2015}, month = {05}, publisher = {Routledge}, title = {Aerodynamics of wind turbines: Third edition}, isbn = {9781315769981}, doi = {10.4324/9781315769981}, collection={earthscan} } @article{HeckJohlasHowland2023_yawed_adm, doi = {10.1017/jfm.2023.129}, url = {https://doi.org/10.1017/jfm.2023.129}, year = {2023}, month = {mar}, publisher={Cambridge University Press}, volume = {959}, author = {K.S. Heck, H.M. Johlas and M.F. Howland}, title = {Modelling the induction, thrust and power of a yaw-misaligned actuator disk}, journal = {Journal of Fluid Mechanics}, } @techreport{jonkman_NREL5MW_2009, title = {Definition of a 5-{MW} Reference Wind Turbine for Offshore System Development}, institution = {National Renewable Energy Laboratory}, url = {http://www.osti.gov/servlets/purl/947422-nhrlni/}, number = {NREL/TP-500-38060}, author = {Jonkman, J. and Butterfield, S. and Musial, W. and Scott, G.}, year = {2009}, doi = {10.2172/947422}, } @techreport{kainz_IEA10MW_2024, title = {{IEA}-{Wind} 740-{10MW} Reference Offshore Wind Plants}, url = {https://research-hub.nrel.gov/en/publications/iea-wind-tcp-task-55-the-iea-wind-740-10-mw-reference-offshore-wi}, institution = {International Energy Agency}, author = {Kainz, Samuel and Quick, Julian and Souza de Alencar, Mauricio and Sanchez Perez-Moreno, Sebastian and Dykes, Katherine and Bay, Christopher and Zaaijer, Michiel B. and Bortolotti, Pietro}, year = {2024}, } @techreport{gaertner_IEA15MW_2020, title = {{IEA} {Wind} {TCP} {Task} 37: {Definition} of the {IEA} 15-{Megawatt} Offshore Reference Wind Turbine}, url = {https://research-hub.nrel.gov/en/publications/iea-wind-tcp-task-37-definition-of-the-iea-15-megawatt-offshore-r}, number = {NREL/TP-5000-75698}, institution = {International Energy Agency}, author = {Gaertner, Evan and Sethuraman, Latha and Anderson, Benjamin and Barter, Garrett and Abbas, Nikhar and Bortolotti, Pietro and Scott, George and Feil, Roland and Shields, Matthew and Rinker, Jennifer and Zahle, Frederik and Meng, Fanzhong and Skrzypinski, Witold and Bredmose, Henrik and Dykes, Katherine and Allen, Christopher and Viselli, Anthony}, year = {2020}, } @techreport{zahle_IEA22MW_2024, title = {Definition of the {IEA} {Wind} 22-{Megawatt} Offshore Reference Wind Turbine}, url = {https://orbit.dtu.dk/en/publications/definition-of-the-iea-wind-22-megawatt-offshore-reference-wind-tu}, institution = {International Energy Agency}, author = {Zahle, Frederik and Barlas, Athanasios and Loenbaek, Kenneth and Bortolotti, Pietro and Zalkind, Daniel and Wang, Lu and Labuschagne, Casper and Sethuraman, Latha and Barter, Garrett}, year = {2024}, } @article{fleming_sr_2022, title = {Serial-Refine Method for Fast Wake-Steering Yaw Optimization}, volume = {2265}, issn = {1742-6588, 1742-6596}, url = {https://iopscience.iop.org/article/10.1088/1742-6596/2265/3/032109}, doi = {10.1088/1742-6596/2265/3/032109}, number = {3}, journal = {Journal of Physics: Conference Series (TORQUE)}, author = {Fleming, Paul A. and Stanley, Andrew P. J. and Bay, Christopher J. and King, Jennifer and Simley, Eric and Doekemeijer, Bart M. and Mudafort, Rafael}, year = {2022}, pages = {032109}, } @inproceedings{katic_sos_1986, address = {Rome, Italy}, title = {A simple model for cluster efficiency}, volume = {1}, author = {Katic, I and Højstrup, J and Jensen, N O}, year = {1986}, pages = {407--410}, } @article{zehtabiyan_rezaie_CH_2023, title = {A short note on turbulence characteristics in wind-turbine wakes}, volume = {240}, issn = {0167-6105}, doi = {10.1016/j.jweia.2023.105504}, journal = {Journal of Wind Engineering and Industrial Aerodynamics}, author = {Zehtabiyan-Rezaie, Navid and Abkar, Mahdi}, year = {2023}, pages = {105504}, } ================================================ FILE: docs/turbine_library.md ================================================ (turbine_library)= # Turbine Library FLORIS includes a library of predefined wind turbine models that can be used to quickly set up simulations without needing to define the turbine characteristics manually. These include standard reference wind turbines as well as fictional wind turbine models for the purpose of demonstrating various features of FLORIS. These turbines are stored in the `floris.turbine_library` module. ## NREL 5MW reference wind turbine FLORIS representation of the NREL 5MW reference wind turbine {cite:t}`jonkman_NREL5MW_2009`. Data based on https://github.com/NREL/turbine-models/blob/master/Offshore/NREL_5MW_126_RWT_corrected.csv. Specified as `"nrel_5MW"` in the `turbine_type` field of the FLORIS input dictionary. The NREL 5MW turbine is the default turbine model used in most FLORIS examples and tutorials. It is also the model used if FLORIS is instantiated in the defaults configuration using `FlorisModel("defaults")`. ## IEA 15MW reference wind turbine FLORIS representation of the IEA 15MW reference wind turbine {cite:t}`gaertner_IEA15MW_2020`. Data based on https://github.com/IEAWindTask37/IEA-15-240-RWT/blob/master/Documentation/IEA-15-240-RWT_tabular.xlsx. Specified as `"iea_15MW"` in the `turbine_type` field of the FLORIS input dictionary. The IEA 15MW turbine is used in the following examples: - examples/examples_control_types/004_helix_active_wake_mixing.py ## IEA 10MW reference wind turbine FLORIS representation of the IEA 10MW reference wind turbine {cite:t}`kainz_IEA10MW_2024`. Data based on https://github.com/NREL/turbine-models/blob/master/Offshore/IEA_10MW_198_RWT.csv. Specified as `"iea_10MW"` in the `turbine_type` field of the FLORIS input dictionary. The IEA 10MW turbine is used in the following examples: - examples/examples_turbine/002_multiple_turbine_types.py ## IEA 22MW reference wind turbine FLORIS representation of the IEA 22MW reference wind turbine {cite:t}`zahle_IEA22MW_2024`. Data generated using OpenFAST v4.1.2 and ROSCO v2.10.2. See [pull request](https://github.com/NatLabRockies/floris/pull/1146) for full OpenFAST and ROSCO input files. Specified as `"iea_22MW"` in the `turbine_type` field of the FLORIS input dictionary. The IEA 22MW is demonstrated, alongside other reference wind turbines, in: - examples/examples_turbine/001_reference_turbines.py ## IEA 15MW multidimensional Fictional IEA 15MW turbine model used to demonstrate the use of multidimensional power and thrust coefficient data. Reads in fictional multidimensional data describing the power and thrust coefficient relationships on wave period `Tp` and wave height `Hs` from `iea_15MW_multi_dim_Tp_Hs.csv` in the `turbine_library` folder. Specified as `"iea_15MW_multi_dim"` in the `turbine_type` field of the FLORIS input dictionary. This data should be treated as fictional and for demonstrative purposes only. This fictional turbine model is not currently used in examples. ## IEA 15MW floating, multidimensional The same as the multidimensional IEA 15MW turbine model above, but with an additional floating platform tilt table. This model is used to demonstrate the floating wind turbine capabilities in FLORIS. Specified as `"iea_15MW_floating_multi_dim"` in the `turbine_type` field of the FLORIS input dictionary. The data for the floating tilt table was generated using OpenFAST with the UMaine VolturnUS-S Reference Platform by Sam Kaufman-Martin, as seen [here](https://github.com/FLOWMAS-EERC/IEA15_FOWT/blob/main/iea_15MW_floating_power-from-fixed.yaml). This fictional turbine model is used in the following examples: - examples/examples_multidim/001_multi_dimensional_cp_ct.py - examples/examples_multidim/002_multi_dimensional_cp_ct_2Hs.py ================================================ FILE: docs/turbine_models.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "id": "ab10767e", "metadata": {}, "source": [ "# Wind turbine models\n", "\n", "FLORIS generally represents wind turbines as actuator disks specified by a power curve and a\n", "thrust coefficient curve (both specified as a function of wind speed). We can easily investigate the\n", "power and thrust coefficients of a turbine by running a single-turbine `FlorisModel` using a range\n", "of wind speeds." ] }, { "cell_type": "code", "execution_count": null, "id": "cc97a774", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from floris import FlorisModel, TimeSeries\n", "\n", "n_wind_speeds = 100\n", "wind_speeds = np.linspace(0.1, 30, n_wind_speeds)\n", "\n", "fmodel = FlorisModel(\"defaults\") # Defaults to NREL 5MW turbine\n", "fmodel.set(\n", " wind_data=TimeSeries(\n", " wind_directions=np.zeros(n_wind_speeds),\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=0.06\n", " ),\n", " layout_x=[0],\n", " layout_y=[0],\n", " wind_shear=0.0\n", ")\n", "\n", "fmodel.run()\n", "\n", "powers = fmodel.get_turbine_powers()\n", "thrust_coefficients = fmodel.get_turbine_thrust_coefficients()\n", "\n", "fig, ax = plt.subplots(2, 1, sharex=True)\n", "ax[0].plot(wind_speeds, powers)\n", "ax[0].grid()\n", "ax[0].set_ylabel(\"Power [kW]\")\n", "ax[1].plot(wind_speeds, thrust_coefficients)\n", "ax[1].grid()\n", "ax[1].set_ylabel(\"Thrust coefficient [-]\")\n", "ax[1].set_xlabel(\"Wind speed [m/s]\")\n", "ax[1].set_xlim([0, 30])" ] }, { "cell_type": "markdown", "id": "88d2ebce", "metadata": {}, "source": [ "## Prepackaged turbine models\n", "\n", "FLORIS ships with three reference wind turbine models: the NREL 5MW turbine\n", "{cite:t}`jonkman_NREL5MW_2009`,\n", "the IEA 10 MW turbine {cite:t}`kainz_IEA10MW_2024`, and the IEA 15 MW turbine\n", "{cite:t}`gaertner_IEA15MW_2020`. The\n", "turbine models used for FLORIS simulations can be changed by specifying the `turbine_type` keyword\n", "argument to `FlorisModel.set()`. See {ref}`turbine_library` for more details." ] }, { "cell_type": "code", "execution_count": null, "id": "7c1ec9ba", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(2, 1, sharex=True)\n", "\n", "turbine_models = [\"nrel_5MW\", \"iea_10MW\", \"iea_15MW\"]\n", "\n", "for turbine_model in turbine_models:\n", " fmodel.set(turbine_type=[turbine_model], reference_wind_height=-1)\n", "\n", " fmodel.run()\n", "\n", " powers = fmodel.get_turbine_powers()\n", " thrust_coefficients = fmodel.get_turbine_thrust_coefficients()\n", "\n", " ax[0].plot(wind_speeds, powers, label=turbine_model)\n", " ax[1].plot(wind_speeds, thrust_coefficients)\n", "\n", "\n", "ax[0].grid()\n", "ax[0].set_ylabel(\"Power [kW]\")\n", "ax[0].legend()\n", "ax[1].grid()\n", "ax[1].set_ylabel(\"Thrust coefficient [-]\")\n", "ax[1].set_xlabel(\"Wind speed [m/s]\")\n", "ax[1].set_xlim([0, 30])" ] }, { "cell_type": "markdown", "id": "bd2c8544", "metadata": {}, "source": [ "## User-defined wind turbine models\n", "\n", "Users may also provide their own wind turbine models, provided that they contain the appropriate\n", "information. To include your own wind turbine model in your main FLORIS input YAML, use the\n", "`!include` specifier, e.g.\n", "```\n", " turbine_type:\n", " - !include path/to/your/turbine.yaml\n", "```\n", "\n", "You can also `set` the `turbine_type` to your own turbine using the path, e.g.\n", "```python\n", "fmodel.set(turbine_type=[\"path/to/your/turbine.yaml\"])\n", "```" ] }, { "cell_type": "markdown", "id": "cdd9ad19", "metadata": {}, "source": [ "The following pages describe in closer detail how wind turbines are implemented in FLORIS and\n", "provide information on advanced wind turbine operation and modeling.\n", "\n", "```{note}\n", "The `TurbineInterface` and `TurbineLibrary` classes are now deprecated and will be removed in a\n", "future release. Users should simply use an instantiated `FlorisModel` to investigate wind turbine\n", "models.\n", "```" ] }, { "cell_type": "markdown", "id": "98fd51f6", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" } }, "nbformat": 4, "nbformat_minor": 5 } ================================================ FILE: docs/v3_to_v4.md ================================================ # Switching from FLORIS v3 to v4 There are several major changes introduced in FLORIS v4. The largest underlying change is that, where FLORIS v3 had a "wind directions" and a "wind speeds" dimension to its internal data structures, FLORIS v4 collapses these into a single dimension, which we refer to as the `findex` dimension. This dimension contains each "configuration" or "condition" to be run, and is conceptually similar to running FLORIS v3 in `time_series` mode. At the user interface level, the largest implication of this change is that users must specify `wind_directions`, `wind_speeds`, and `turbulence_intensities` (new) as arrays of equal length; and these are "zipped" to create the conditions for FLORIS to run, rather than creating a grid of all combinations. This is discussed further in [Setting and Running](#setting-and-running). ## Setting and running In FLORIS v3, users interacted with FLORIS by instantiating a `FlorisInterface` object, nominally called `fi`. The notion here is that the users "interface" with the underlying FLORIS code using `fi`. For FLORIS v4, we acknowledge that to most users, this main "interface" object, for all intents and purposes, _is FLORIS_. We therefore have renamed the `FlorisInterface` the `FlorisModel`, nominally instantiated as `fmodel`. To instantiate a `FlorisModel`, the code is very similar to before, i.e. ```python from floris import FlorisModel fmodel = FlorisModel("input_file.yaml") ``` Previously, to set the atmospheric conditions on `fi`, users called the `reinitialize()` method; and to run the calculations, as well as provide any control setpoints such as yaw angles, users generally called `calculate_wake()`. Some of the other methods on `FlorisInterface` also called `calculate_wake()` internally, most notably `get_farm_AEP()`. For FLORIS v4, we have changed from the (`reinitialize()`, `calculate_wake()`) paradigm to a new pair of methods (`set()`, `run()`). `set()` is similar to the retired `reinitialize()` method, and `run()` is similar to the retired `calculate_wake()` method. However, there are some important differences: - `FlorisModel.set()` accepts both atmospheric conditions _and_ control setpoints. - `FlorisModel.run()` accept no arguments. Its sole function is to run the FLORIS calculation. - Control setpoints are now "remembered". Previously, if control setpoints (`yaw_angles`) were passed to `calculate_wake()`, they were discarded at the end of the calculation. In FLORIS v4, the control setpoints passed to `set()` are stored, and invoking `run()` multiple times will continue to use those control setpoints. - To "forget" previously provided control setpoints, use the new method `FlorisModel.reset_operation()`. - When providing arguments to `set()`, all arguments much have the same length, as they will be "paired" (rather than gridded) for the computation. For instance, if the user provides `n_findex` wind directions, they _must_ provide `n_findex` wind speeds and `n_findex` turbulence intensities; as well as `n_findex`x`n_turbines` yaw angles, if yaw angles are being used. - Providing varying `turbulence_intensities` is new for FLORIS v4. - To facilitate "easier" use of the `set()` method (for instance, to run all combinations of wind directions and wind speeds), we now provide `WindData` objects that can be passed directly to `set()`'s `wind_data` keyword argument. See [Wind data](#wind-data) as well as [Wind Data Objects](wind_data_user) for more information. - `calculate_no_wake()` has been replaced with `run_no_wake()` - `get_farm_AEP()` no longer calls `run()`; to compute the farm AEP, users should `run()` the `fmodel` themselves before calling `get_farm_AEP()`. An example workflow for using `set` and `run` is: ```python import numpy as np from floris import FlorisModel fmodel = FlorisModel("input_file.yaml") # Input file with 3 turbines # Set up a base case and run fmodel.set( wind_directions=np.array([270., 270.]), wind_speeds=np.array([8.0, 8.0]), turbulence_intensities=np.array([0.06, 0.06]) ) fmodel.run() turbine_powers_base = fmodel.get_turbine_powers() # Provide yaw angles fmodel.set( yaw_angles=np.array([[10.0, 0.0, 0.0], [20.0, 0.0, 0.0]]) # n_findex x n_turbines ) fmodel.run() turbine_powers_yawed = fmodel.get_turbine_powers() # If we run again, this time with no wake, the provided yaw angles will still be used fmodel.run_no_wake() turbine_powers_yawed_nowake = fmodel.get_turbine_powers() # To "forget" the yaw angles, we use the reset_operation method fmodel.reset_operation() fmodel.run_no_wake() turbine_powers_base_nowake = fmodel.get_turbine_powers() ``` For more advanced users, it is best to group many conditions into single calls of `set` and `run` than to step through various conditions individually, as this will make the best use of FLORIS's vectorization capabilities. ## Input files As in FLORIS v3, there are two main input files to FLORIS v4: 1. The "main" FLORIS input yaml, which contains wake model parameters and wind farm data 2. The "turbine" input yaml, which contains data about the wind turbines Examples for main FLORIS input yamls are in examples/inputs/. Default turbine yamls, which many users may use if they do not have their own turbine models to use, can be found in floris/turbine_library/. See also [Turbine Library Interface](input_reference_turbine) and [Main Input File Reference](input_reference_main). Conceptually, both the main FLORIS input yaml and the turbine input yaml is much the same in v4 as in v3. However, there are a few changes to the fields on each that mean that existing yamls for v3 will not run in v4 as is. #### Main FLORIS input yaml On the main FLORIS input file, the `turbulence_intensity` field (on`flow_field`), which was specified as a scalar in FLORIS v3, has been changed to `turbulence_intensities`, and should now contain a list of turbulence intensities that is of the same length as `wind_directions` and `wind_speeds`. Additionally, the length of the lists for `wind_directions` and `wind_speeds` _must_ now be of equal length. In addition, a new field `enable_active_wake_mixing` has been added to the `wake` field, which users may set to `false` unless they would like to use active wake mixing strategies such as [Helix](empirical_gauss_model.md#Added-mixing-by-active-wake-control). #### Turbine input yaml To reflect the transition to more flexible [operation models](#operation-model), there are a number of changes to the fields on the turbine yaml. The changes are mostly regrouping and renaming of the existing fields. - The `power_thrust_table` field now has `wind_speed` and `power` fields, as before; however, the `thrust` field has been renamed `thrust_coefficient` for clarity, and the `power` field now specifies the turbine _absolute_ power (in kW) rather than the _power coefficient_. - Additionally, any extra parameters and data required by operation models to evaluate the power and thrust curves have been moved onto the `power_thrust_table` field. This includes `ref_density_cp_ct` (renamed `ref_air_density` and moved onto the `power_thrust_table`); `ref_tilt_cp_ct` (renamed `ref_tilt` and moved onto the `power_thrust_table`); and `pP` and `pT` (renamed `cosine_loss_exponent_yaw` and `cosine_loss_exponent_tilt`, respectively, and moved onto the `power_thrust_table`). - The `generator_efficiency` field has been removed. The `power` field on `power_thrust_table` should reflect the electrical power produced by the turbine, including any losses. - A new field `operation_model` has been added, whose value should be a string that selects the operation model the user would like to evaluate. The default is `"cosine-loss"`, which recovers FLORIS v3-type turbine operation. See [Operation model](#operation-model) and [Turbine Operation Models](operation_models_user) for details. ### Converting v3 yamls to v4 To aid users in converting their existing v3 main FLORIS input yamls and turbine input, we provide two utilities: - floris/tools/convert_floris_input_v3_to_v4.py - floris/tools/convert_turbine_v3_to_v4.py These can be executed from the command line and expect to be passed the exiting v3 yaml as an input; the will then write a new v4-compatible yaml of the same name but appended _v4. ```bash python convert_floris_input_v3_to_v4.py your_v3_input_file.yaml python convert_floris_turbine_v3_to_v4.py your_v3_turbine_file.yaml ``` Additionally, a function for building a turbine dictionary that can be passed directly to the `turbine_type` argument of `FlorisModel.set()` is provided: ```python from floris.turbine_library.turbine_utilities import build_cosine_loss_turbine_dict ``` ### Reference turbine updates The power and thrust curves for the NREL 5MW, IEA 10MW, and IEA 15MW turbines have been updated slightly do reflect publicly available data. The x_20MW reference turbine has been removed, as data was not readily available. See [Turbine Library Interface](turbine_interaction). ## Wind data To aid users in setting the wind conditions they are interested in running, we provide "wind data" classes, which can be passed directly to `FlorisModel.set()`'s `wind_data` keyword argument in place of `wind_directions`, `wind_speeds`, and `turbulence_intensities`. The wind data objects enable, for example, gridding inputs (`WindRose` and `WindTIRose`) and broadcasting a scalar-valued turbulence intensity (`TimeSeries`). ```python import numpy as np from floris import FlorisModel from floris import TimeSeries fmodel = FlorisModel("input_file.yaml") # Input file with 3 turbines time_series = TimeSeries( wind_directions=np.array([270.0, 270.0]), wind_speeds=8.0, turbulence_intensities=0.06 ) fmodel.set(wind_data=time_series) fmodel.set(wind_data=time_series)turbine_powers_base = fmodel.get_turbine_powers() turbine_powers = fmodel.get_turbine_powers() ``` More information about the various wind data classes can be found at [Wind Data Objects](wind_data_user). ## Operation model FLORIS v4 allows for significantly more flexible turbine operation via [Turbine Operation Models](operation_models_user). These allow users to specify how a turbine loses power when yaw misaligned; how a turbine operates when derated; and how turbines produce power and thrust when operating with active wake mixing strategies. The default operation model is the `"cosine-loss"` model, which models a turbine's power loss when in yaw misalignment using the same cosine model as was hardcoded in FLORIS v3. ================================================ FILE: docs/wake_models.ipynb ================================================ { "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Wake Models\n", "\n", "A wake model in FLORIS is made up of four components that together constitute a wake.\n", "At minimum, the velocity deficit profile behind a wind turbine is required. For most models,\n", "an additional wake deflection model is included to model the effect of yaw misalignment.\n", "Turbulence models are also available to couple with the deficit and deflection components.\n", "Finally, methods for combining wakes with the rest of the flow field are available.\n", "\n", "Computationally, the solver algorithm and grid-type supported by each wake model can also\n", "be considered as part of the model itself. As shown in the diagram below, the mathematical\n", "formulations can be considered as the main components of the model. These are typically\n", "associated directly to each other and in some cases they are bundled together into\n", "a single mathematical formulation. The solver algorithm and grid type are associated\n", "to the math formulation, but they are typically more generic.\n", "\n", "```{mermaid}\n", "flowchart LR\n", " A[\"Deficit\"]\n", " B[\"Deflection\"]\n", " C[\"Turbulence\"]\n", " D[\"Velocity\"]\n", " E[\"Solver\"]\n", " F[\"Grid\"]\n", "\n", " subgraph H[FLORIS Wake Model]\n", " direction LR\n", " subgraph G[Math Model]\n", " direction LR\n", " A---B\n", " B---C\n", " C---D\n", " end\n", " G---E\n", " E---F\n", " end\n", "```\n", "\n", "The models in FLORIS are typically developed as a combination of velocity deficit and wake\n", "deflection models, and some also have custom turbulence and combination models. The descriptions\n", "below use the typical combinations except where indicated. The specific settings can be seen\n", "in the corresponding input files found in the source code dropdowns." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from floris import FlorisModel\n", "import floris.flow_visualization as flowviz\n", "import floris.layout_visualization as layoutviz\n", "\n", "NREL5MW_D = 126.0\n", "\n", "def model_plot(inputfile, include_wake_deflection=True):\n", " fig, axes = plt.subplots(1, 1, figsize=(10, 10))\n", " yaw_angles = np.zeros((1, 2))\n", " if include_wake_deflection:\n", " yaw_angles[:,0] = 20.0\n", " fmodel = FlorisModel(inputfile)\n", " fmodel.set(\n", " layout_x=np.array([0.0, 2*NREL5MW_D]),\n", " layout_y=np.array([0.0, 2*NREL5MW_D]),\n", " yaw_angles=yaw_angles,\n", " )\n", " horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0)\n", " flowviz.visualize_cut_plane(horizontal_plane, ax=axes, clevels=100)\n", " layoutviz.plot_turbine_rotors(fmodel, ax=axes, yaw_angles=yaw_angles)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Jensen and Jimenez\n", "\n", "The Jensen model computes the wake velocity deficit based on the classic Jensen/Park model\n", "{cite:t}`jensen1983note`. It is often refered to as a \"top-hat\" model because the spanwise\n", "velocity profile is constant across the wake and abruptly jumps to freestream outside of the\n", "wake boundary line. The slope of the wake boundary line, or wake expansion, is a user parameter.\n", "\n", "The Jiménez wake deflection model is derived from {cite:t}`jimenez2010application`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_plot(\"../examples/inputs/jensen.yaml\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Gauss and GCH\n", "\n", "The Gaussian velocity model is implemented based on {cite:t}`bastankhah2016experimental` and\n", "{cite:t}`niayifar2016analytical`. This model represents the velocity deficity as a gaussian\n", "distribution in the spanwise direction, and the gaussian profile is controlled by user parameters.\n", "There is a near wake zone and a far wake zone. Both maintain the gaussian profile in the spanwise\n", "direction, but they have different models for wake recovery.\n", "\n", "The Gauss deflection model is a blend of the models described in\n", "{cite:t}`bastankhah2016experimental` and {cite:t}`King2019Controls` for calculating\n", "the deflection field in turbine wakes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_plot(\"../examples/inputs/gch.yaml\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Empirical Gaussian\n", "\n", "FLORIS's \"empirical\" model has the same Gaussian wake shape as other popular FLORIS models.\n", "However, the models that describe the wake width and deflection have been reorganized to provide\n", "simpler tuning and data fitting.\n", "\n", "For more information, see {ref}`empirical_gauss_model`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_plot(\"../examples/inputs/emgauss.yaml\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Cumulative Curl\n", "The cumulative curl model is an implementation of the model described in {cite:t}`bay_2022`,\n", "which itself is based on the cumulative model of {cite:t}`bastankhah_2021`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "model_plot(\"../examples/inputs/cc.yaml\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## TurbOPark\n", "\n", "The TurbOPark model is designed to model long wakes from large wind farm clusters. It was originally presented as a “top-hat” model in {cite:t}`nygaard2020modelling` and was updated in {cite:t}`Pedersen_2022_turbopark2` to have a Gaussian profile. For the latter, Ørsted released the [Matlab code with documentation](https://github.com/OrstedRD/TurbOPark), which allows the verification of the implementation in FLORIS.\n", "\n", "The first implementation, the [`TurboparkVelocityDeficitModel`](https://github.com/NREL/floris/blob/main/floris/core/wake_velocity/turbopark.py), was released in [FLORIS v3.1](https://github.com/NREL/floris/releases/tag/v3.1). The second implementation, the [`TurboparkgaussVelocityDeficitModel`](https://github.com/NREL/floris/blob/main/floris/core/wake_velocity/turboparkgauss.py), was released in FLORIS v4.2 and shows a near-perfect match to the predictions of Ørsted’s Matlab implementation. As such, we will emphasize the use of the `TurboparkgaussVelocityDeficitModel` going forward, and suggest that new users use this model (by setting the `velocity_model` field of the FLORIS input file to `turboparkgauss` instead of the `TurboparkVelocityDeficitModel` (`velocity_model: turbopark`)) if they are interested in testing the TurbOPark model.\n", "\n", "The `TurboparkgaussVelocityDeficitModel` implementation was contributed by [Jasper Kreeft](https://github.com/JasperShell).\n", "\n", "Note that the original top-hat TurbOPark model ({cite:t}`nygaard2020modelling`) is _not_ available in FLORIS.\n", "\n", "The wakes as predicted by the `TurboparkgaussVelocityDeficit` model are demonstrated below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_plot(\"../examples/inputs/turboparkgauss_cubature.yaml\", include_wake_deflection=False)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Turbulence Models\n", "\n", "### Crespo-Hernandez\n", "\n", "CrespoHernandez is a wake-turbulence model that is used to compute additional variability introduced\n", "to the flow field by operation of a wind turbine. Implementation of the model follows the original\n", "formulation and limitations outlined in {cite:t}`crespo1996turbulence`.\n", "\n", "The default parameter values used in FLORIS for CrespoHernandez differ from those reported in {cite:t}`crespo1996turbulence` following subsequent calibration. However, {cite:t}`zehtabiyan_rezaie_CH_2023` argue that the sign of certain parameters are not physically consistent (and also misreported in subsequent literature). See the `CrespoHernandez` class docstring for more details." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Wake Combination Models\n", "\n", "The wakes throughout the flow field need to be combined in a careful manner in order to\n", "accurately capture their coupled effects. A simple model is to simple add them,\n", "but this can result in negative velocities a few turbines into the farm. More careful\n", "methods are available within FLORIS and shown here.\n", "\n", "Each model is described below and its effects are plotted with two turbines in a line.\n", "These descriptions use the Jensen and Jimenez models since they highlight the differences\n", "in the combination models themselves.\n", "The upper plots show the turbine wakes individually to give a reference for the uncombined wake.\n", "The lower plots show both turbines along with their wakes combined with the chosen model." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "def combination_plot(method: str):\n", " X_UPSTREAM = 0.0\n", " X_DOWNSTREAM = 5 * 126.0\n", " X0_BOUND = -200\n", " X1_BOUND = 1500\n", "\n", " # Set the combination method\n", " fmodel = FlorisModel(\"../examples/inputs/jensen.yaml\")\n", " settings = fmodel.core.as_dict()\n", " settings[\"wake\"][\"model_strings\"][\"combination_model\"] = method\n", " fmodel = FlorisModel(settings)\n", "\n", " # Plot two turbines individually\n", " fig, axes = plt.subplots(1, 2, figsize=(10, 10))\n", " fmodel.set(\n", " layout_x=np.array([X_UPSTREAM]),\n", " layout_y=np.zeros(1),\n", " yaw_angles=np.array([[20.0]]),\n", " )\n", " horizontal_plane = fmodel.calculate_horizontal_plane(\n", " height=90.0,\n", " x_bounds=(X0_BOUND, X1_BOUND),\n", " )\n", " layoutviz.plot_turbine_rotors(fmodel, ax=axes[0])\n", " flowviz.visualize_cut_plane(horizontal_plane, ax=axes[0], clevels=100)\n", " layoutviz.plot_turbine_rotors(fmodel, ax=axes[1])\n", "\n", " fmodel.set(\n", " layout_x=np.array([X_DOWNSTREAM]),\n", " layout_y=np.zeros(1),\n", " yaw_angles=np.array([[0.0]]),\n", " )\n", " horizontal_plane = fmodel.calculate_horizontal_plane(\n", " height=90.0,\n", " x_bounds=(X0_BOUND, X1_BOUND),\n", " )\n", " flowviz.visualize_cut_plane(horizontal_plane, ax=axes[1], clevels=100)\n", " layoutviz.plot_turbine_rotors(fmodel, ax=axes[0])\n", " layoutviz.plot_turbine_rotors(fmodel, ax=axes[1])\n", "\n", " # Plot the combination of turbines\n", " fig, axes = plt.subplots(1, 1, figsize=(10, 10))\n", " fmodel.set(\n", " layout_x=np.array([X_UPSTREAM, X_DOWNSTREAM]),\n", " layout_y=np.zeros(2),\n", " yaw_angles=np.array([[20.0, 0.0]]),\n", " )\n", " horizontal_plane = fmodel.calculate_horizontal_plane(\n", " height=90.0,\n", " x_bounds=(X0_BOUND, X1_BOUND),\n", " )\n", " flowviz.visualize_cut_plane(horizontal_plane, ax=axes, clevels=100)\n", " layoutviz.plot_turbine_rotors(fmodel, ax=axes)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Freestream Linear Superposition (FLS)\n", "\n", "FLS uses freestream linear superposition to apply the wake velocity deficits to the freestream\n", "flow field." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "combination_plot(\"fls\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Max\n", "\n", "The MAX model incorporates the velocity deficits into the base flow field by selecting the\n", "maximum of the two for each point. For more information, refer to {cite:t}`gunn2016limitations`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "combination_plot(\"max\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Sum of Squares Freestream Superposition (SOSFS)\n", "\n", "This model combines the wakes via a sum of squares of the new wake to add and the existing flow field. For more information, refer to :cite:`katic_sos_1986`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "combination_plot(\"sosfs\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: docs/wind_data_user.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Wind Data Objects" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "FLORIS v4 introduces WindData objects. These include TimeSeries, WindRose, and WindTIRose. These objects are used to hold inputs to FLORIS simulations, such as the ambient wind data, and to provide high-level methods for working with wind data. This notebook provides an overview of the WindData objects and demonstrates how to use them.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## WindDataBase" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "WindDataBase is the base class for all WindData objects. It provides a common interface for working with wind data. The WindDataBase class is not intended to be used directly, but rather to be subclassed by more specific wind data objects. It is only important to mention that many of the methods in FLORIS that accept wind data as input will accept any WindDataBase object as input. But is not typical to use it directly." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from floris.wind_data import WindDataBase\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import warnings\n", "\n", "warnings.simplefilter(\"ignore\")\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## TimeSeries" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "TimeSeries objects are used to represent data which are in a time-series form, or more generally and data which is represented as a list of conditions without frequency weighting (i.e. not a wind rose). In addition to representing time series input conditions, TimeSeries objects are useful for generating sweep inputs where most values are held constant while one input is swept through a range of values. Also useful can be an input of identical repeated inputs which can be useful if some control setting is going to be swept. TimeSeries represents data most similarly to how data structures within FLORIS are represented in that there are N wind_directions, wind_speeds etc., in the TimeSeries, the n_findex value in FLORIS will be N." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### TimeSeries Instantiation" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from floris import TimeSeries\n", "\n", "# Like FlorisModel, TimeSeries require wind directions, wind speeds, and turbulence intensities to be of the same length.\n", "N = 50\n", "wind_speeds = np.linspace(3, 15, N)\n", "wind_directions = 270.0 * np.ones(N)\n", "turbulence_intensities = 0.06 * np.ones(N)\n", "\n", "# Create a TimeSeries object\n", "time_series = TimeSeries(\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Broadcasting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unlike FlorisModel, TimeSeries objects do allow broadcasting. As long as one of the inputs is a numpy array, the other inputs can be specified as a float, which will be broadcasted to the length of the numpy array.\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Equivalent to the above\n", "time_series = TimeSeries(\n", " wind_directions=270.0, wind_speeds=wind_speeds, turbulence_intensities=0.06\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition to wind directions, wind speeds, and turbulence intensities, TimeSeries objects can also hold an array of values. These values can be used for example to represent electricity market prices (e.g., price/MWh). The values are intended to be multiplied by the corresponding wind plant power at each time step or wind condition to determine the total value produced over all conditions. \n", "\n", "If values are included in the TimeSeries object, they must be the same length as the wind directions, wind speeds, and turbulence intensities. If included, values enable calculation of Annual Value Production (AVP), in addition to AEP, and certain optimization routines, such as layout, can be configured to maximize value instead of energy production." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Including value for each indices\n", "time_series = TimeSeries(\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", " values=np.linspace(0, 1, N),\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generating Turbulence Intensity" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The TimeSeries object also includes functions for generating TI as a function of wind direction and wind speed. This can be accomplished by passing in a custom function, or by taking use of the IEC 61400-1 standard " ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Turbulence Intensity')" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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nFasWKFx48Zp3759CgsLqzH+888/15QpU5SSkqLrrrtO69ev16RJk5Sdna2YmBhJUmJiorZv365169YpKipKW7du1T333KMuXbro+uuvd/dHBOCl6qoTqrQYbKcHWjCTYRge7fseFxenoUOHauXKlZIki8WiyMhIzZkzRwsWLKgxfvLkySotLdX7779vvXbllVcqNjbWOssTExOjyZMna+HChdYxgwcP1oQJE/T444/XG1NJSYlCQ0NVXFyskJCQxn5EAM1U9aGqkv1lsnkJfercTl/trbuuZDs94AbO/H57dAmsoqJCe/bsUUJCgvWan5+fEhISlJmZafc1mZmZNuMlady4cTbjhw8frvfee09HjhyRYRhKT0/Xt99+q7Fjx9q9Z3l5uUpKSmz+AIDt9EDL5dElsOPHj6uyslLh4eE218PDw/XNN9/YfU1BQYHd8QUFBdbHL7zwgv77v/9b3bp1U6tWreTn56e1a9dq5MiRdu+ZkpKi5OTkRn4aAC2RJ7bTs1QGND2P1wA1hRdeeEE7d+7Ue++9p+7du+vjjz/Wvffeqy5dutSYPZKkpKQkJSYmWh+XlJQoMjLSnSED8GLu3E7PjjLAPTy6BNapUyf5+/ursLDQ5nphYaHMZvs7MMxmc53jf/75Zz388MN65plnNHHiRF1xxRWaPXu2Jk+erOXLl9u9Z2BgoEJCQmz+AKA+1dvppV/qgqpduJ3eEdXb6VkqA9zDowlQQECABg8erLS0NOs1i8WitLQ0xcfH231NfHy8zXhJ2rZtm3X8uXPndO7cOfn52X40f39/WSwWF38CAL7OVdvpO7UNVPKWvFqXyqSqpbJKS9WjSouhzP0ntDnniDL3n7BeB+AYjy+BJSYmavr06RoyZIiGDRumFStWqLS0VDNmzJAkTZs2TV27dlVKSookae7cuRo1apSefvppXXvttdqwYYN2796tl156SZIUEhKiUaNG6aGHHlKbNm3UvXt37dixQ2+++aaeeeYZj31OAC1XY7fTm0ODJJNqzPxciANaAdfyeAI0efJkHTt2TIsWLVJBQYFiY2OVmppqLXQ+dOiQzWzO8OHDtX79ej3yyCN6+OGH1adPH7377rvWHkCStGHDBiUlJem2227TyZMn1b17dz3xxBO6++673f75APiG2uqEHOk6vXhitI6fKXfofbblFei1z36okUxV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3HbbbR6KyDtJMt555x3rY4vFYpjNZmPZsmXWaz/99JMRGBhovPXWWx6I0Ltc/H3Zk5WVZUgyfvzxR/cE5aVq+67+/e9/G127djVyc3ON7t27G88++6zbY/MmzABB9957r6699lolJCR4OhSv9d5772nIkCG6+eabFRYWpoEDB2rt2rWeDssrDR8+XGlpafr2228lSXv37tWnn36qCRMmeDgy73bw4EEVFBTY/HcYGhqquLg4ZWZmejCy5qO4uFgmk0nt27f3dChex2Kx6Pbbb9dDDz2kfv36eTocr8BhqD5uw4YNys7O1q5duzwdilc7cOCAVq9ercTERD388MPatWuX7rvvPgUEBGj69OmeDs+rLFiwQCUlJerbt6/8/f1VWVmpJ554QrfddpunQ/NqBQUFkqTw8HCb6+Hh4dbnULuysjLNnz9fU6ZM8ZlDP52xdOlStWrVSvfdd5+nQ/EaJEA+7PDhw5o7d662bdumoKAgT4fj1SwWi4YMGaInn3xSkjRw4EDl5uZqzZo1JEAX+etf/6q//OUvWr9+vfr166ecnBzNmzdPXbp04btCkzh37pxuueUWGYah1atXezocr7Nnzx4999xzys7Olslk8nQ4XoMlMB+2Z88eFRUVadCgQWrVqpVatWqlHTt26Pnnn1erVq1UWVnp6RC9RkREhKKjo22u/frXv9ahQ4c8FJH3euihh7RgwQLdeuut6t+/v26//Xbdf//9SklJ8XRoXs1sNkuSCgsLba4XFhZan0NN1cnPjz/+qG3btjH7Y8cnn3yioqIiXXbZZdb/rf/xxx/1wAMPKCoqytPheQwzQD7sN7/5jf71r3/ZXJsxY4b69u2r+fPny9/f30OReZ+rrrpK+/bts7n27bffqnv37h6KyHudPXtWfn62/9/K399fFovFQxE1Dz169JDZbFZaWppiY2MlSSUlJfriiy80a9YszwbnpaqTn++++07p6enq2LGjp0PySrfffnuNGs9x48bp9ttv14wZMzwUleeRAPmw4OBgxcTE2Fxr27atOnbsWOO6r7v//vs1fPhwPfnkk7rllluUlZWll156SS+99JKnQ/M6EydO1BNPPKHLLrtM/fr105dffqlnnnlGd955p6dD87gzZ87o+++/tz4+ePCgcnJy1KFDB1122WWaN2+eHn/8cfXp00c9evTQwoUL1aVLF02aNMlzQXtQXd9XRESE/vM//1PZ2dl6//33VVlZaa2V6tChgwICAjwVtkfU9+/Wxclh69atZTabdfnll7s7VO/h6W1o8C5sg6/dli1bjJiYGCMwMNDo27ev8dJLL3k6JK9UUlJizJ0717jsssuMoKAgo2fPnsb//M//GOXl5Z4OzePS09MNSTX+pk+fbhhG1Vb4hQsXGuHh4UZgYKDxm9/8xti3b59ng/agur6vgwcP2n1OkpGenu7p0N2uvn+3LsY2eMMwGQbtWQEAgG+hCBoAAPgcEiAAAOBzSIAAAIDPIQECAAA+hwQIAAD4HBIgAADgc0iAAACAzyEBAgAAPocECIDTMjIyZDKZ9NNPPzXqPnfccUezPubh6quv1rx58+odN3LkSK1fv77pA7rArbfeqqefftqt7wk0JyRAgA9bs2aNgoODdf78eeu1M2fOqHXr1rr66qttxlYnPfv379fw4cOVn5+v0NDQJo9x7dq1GjBggNq1a6f27dtr4MCBzepk+ffee0+FhYW69dZbXXK/N954QyNGjKh33COPPKInnnhCxcXFLnlfoKUhAQJ82DXXXKMzZ85o9+7d1muffPKJzGazvvjiC5WVlVmvp6en67LLLlOvXr0UEBAgs9ksk8nUpPG9+uqrmjdvnu677z7l5OTos88+0x/+8AedOXOmSd/XlZ5//nnNmDFDfn6u+Z/bzZs36/rrr693XExMjHr16qV169a55H2BloYECPBhl19+uSIiIpSRkWG9lpGRoRtuuEE9evTQzp07ba5fc8011n++cAns9ddfV/v27fWPf/xDv/71r9WuXTuNHz9e+fn51tdXVlYqMTFR7du3V8eOHfWHP/xB9R1F+N577+mWW27RzJkz1bt3b/Xr109TpkzRE088YR1TvYyWnJyszp07KyQkRHfffbcqKiqsYywWi1JSUtSjRw+1adNGAwYM0N/+9jeb98rNzdWECRPUrl07hYeH6/bbb9fx48etz5eWlmratGlq166dIiIiHFpeOnbsmLZv366JEyfaXDeZTHrxxRd13XXX6ZJLLtGvf/1rZWZm6vvvv9fVV1+ttm3bavjw4dq/f7/N68rKyrR161ZrAvSnP/1Jffr0UVBQkMLDw/Wf//mfNuMnTpyoDRs21Bsn4ItIgAAfd8011yg9Pd36OD09XVdffbVGjRplvf7zzz/riy++sCZA9pw9e1bLly/Xn//8Z3388cc6dOiQHnzwQevzTz/9tF5//XW9+uqr+vTTT3Xy5Em98847dcZmNpu1c+dO/fjjj3WOS0tL09dff62MjAy99dZb2rRpk5KTk63Pp6Sk6M0339SaNWv01Vdf6f7779fUqVO1Y8cOSdJPP/2k0aNHa+DAgdq9e7dSU1NVWFioW265xXqPhx56SDt27NDmzZu1detWZWRkKDs7u864Pv30U2uCc7HHHntM06ZNU05Ojvr27avf//73+n//7/8pKSlJu3fvlmEYmj17do3P2bVrV/Xt21e7d+/Wfffdpz/+8Y/at2+fUlNTNXLkSJvxw4YNU1ZWlsrLy+uME/BJnj2MHoCnrV271mjbtq1x7tw5o6SkxGjVqpVRVFRkrF+/3hg5cqRhGIaRlpZmSDJ+/PFHwzAMIz093ZBknDp1yjAMw3jttdcMScb3339vve+qVauM8PBw6+OIiAjjqaeesj4+d+6c0a1bN+OGG26oNbajR48aV155pSHJ+NWvfmVMnz7d2Lhxo1FZWWkdM336dKNDhw5GaWmp9drq1auNdu3aGZWVlUZZWZlxySWXGJ9//rnNvWfOnGlMmTLFMAzDeOyxx4yxY8faPH/48GFDkrFv3z7j9OnTRkBAgPHXv/7V+vyJEyeMNm3aGHPnzq01/meffdbo2bNnjeuSjEceecT6ODMz05BkvPLKK9Zrb731lhEUFGTzurvuust48MEHDcMwjP/93/81QkJCjJKSklrff+/evYYk44cffqh1DOCrWnku9QLgDa6++mqVlpZq165dOnXqlH71q1+pc+fOGjVqlGbMmKGysjJlZGSoZ8+euuyyy2q9zyWXXKJevXpZH0dERKioqEiSVFxcrPz8fMXFxVmfb9WqlYYMGVLnMlhERIQyMzOVm5urjz/+WJ9//rmmT5+ul19+Wampqda6mgEDBuiSSy6xvi4+Pl5nzpzR4cOHdebMGZ09e1ZjxoyxuXdFRYUGDhwoSdq7d6/S09PVrl27GjHs379fP//8syoqKmzi79Chgy6//PJaY5eqZs6CgoLsPnfFFVdY/zk8PFyS1L9/f5trZWVlKikpUUhIiAzD0JYtW/TXv/5VkjRmzBh1795dPXv21Pjx4zV+/Hj97ne/s/ke2rRpI6lqdg6ALRIgwMf17t1b3bp1U3p6uk6dOqVRo0ZJkrp06aLIyEh9/vnnSk9P1+jRo+u8T+vWrW0em0ymemt8HBUTE6OYmBjdc889uvvuu/Uf//Ef2rFjR51LctWqC6Y/+OADde3a1ea5wMBA65iJEydq6dKlNV4fERGh77//vkFxd+rUSadOnbL73IXfV3Uxub1rFotFkpSVlaXz589r+PDhkqTg4GBlZ2crIyNDW7du1aJFi/Too49q165dat++vSTp5MmTkqTOnTs3KH6gJaMGCICuueYaZWRkKCMjw2b7+8iRI/X3v/9dWVlZDiUbtQkNDVVERIS++OIL67Xz589rz549Tt8rOjpaUlVRcrW9e/fq559/tj7euXOn2rVrp8jISEVHRyswMFCHDh1S7969bf4iIyMlSYMGDdJXX32lqKioGmPatm2rXr16qXXr1jbxnzp1St9++22dsQ4cOFAFBQW1JkHO2Lx5s6699lr5+/tbr7Vq1UoJCQl66qmn9M9//lM//PCDtm/fbn0+NzdX3bp1U6dOnRr9/kBLwwwQAF1zzTW69957de7cOesMkCSNGjVKs2fPVkVFRaMSIEmaO3eulixZoj59+qhv37565pln6m2kOGvWLHXp0kWjR49Wt27dlJ+fr8cff1ydO3dWfHy8dVxFRYVmzpypRx55RD/88IMWL16s2bNny8/PT8HBwXrwwQd1//33y2KxaMSIESouLtZnn32mkJAQTZ8+Xffee6/Wrl2rKVOm6A9/+IM6dOig77//Xhs2bNDLL7+sdu3aaebMmXrooYfUsWNHhYWF6X/+53/q3do+cOBAderUSZ999pmuu+66Rn1/7733nv74xz9aH7///vs6cOCARo4cqUsvvVQffvihLBaLzbLcJ598orFjxzbqfYGWigQIgK655hr9/PPP6tu3r7UeRapKgE6fPm3dLt8YDzzwgPLz8zV9+nT5+fnpzjvv1O9+97s6G/UlJCTo1Vdf1erVq3XixAl16tRJ8fHxSktLU8eOHa3jfvOb36hPnz4aOXKkysvLNWXKFD366KPW5x977DF17txZKSkpOnDggNq3b69Bgwbp4YcfllS13PfZZ59p/vz5Gjt2rMrLy9W9e3eNHz/emuQsW7bMulQWHBysBx54oN4mg/7+/poxY4b+8pe/NCoB2r9/v77//nuNGzfOeq19+/batGmTHn30UZWVlalPnz5666231K9fP0lVW+bfffddpaamNvh9gZbMZLhqkR4APOCOO+7QTz/9pHfffdfTodhVUFCgfv36KTs7W927d2/QPZ555hl99NFH+vDDDx1+zerVq/XOO+9o69atDXpPoKWjBggAmpDZbNYrr7yiQ4cONfge3bp1U1JSklOvad26tV544YUGvyfQ0jEDBKBZ8/YZIADeiQQIAAD4HJbAAACAzyEBAgAAPocECAAA+BwSIAAA4HNIgAAAgM8hAQIAAD6HBAgAAPgcEiAAAOBz/j/TR8Zlo0gdzQAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Assign TI as a function of wind speed using the IEC method and default parameters.\n", "time_series.assign_ti_using_IEC_method()\n", "\n", "fig, ax = plt.subplots()\n", "ax.scatter(time_series.wind_speeds, time_series.turbulence_intensities)\n", "ax.set_xlabel(\"Wind Speed (m/s)\")\n", "ax.set_ylabel(\"Turbulence Intensity\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generating Value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The TimeSeries object also includes functions for generating value as a function of wind direction and wind speed. This can be accomplished by passing in a custom function using the `TimeSeries.assign_value_using_wd_ws_function` method, or by using the `TimeSeries.assign_value_piecewise_linear` method, which approximates value using a two-segment piecewise linear function of wind speed. When using the default parameters, this produces a value vs. wind speed that approximates the normalized mean electricity price vs. wind speed curve for the SPP market in the U.S. for years 2018-2020 from figure 7 in \"The value of wake steering wind farm flow control in US energy markets,\" Wind Energy Science, 2024. https://doi.org/10.5194/wes-9-219-2024. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Value (normalized price/MWh)')" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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X7+OLl26aPzL398fANCvXz+cP38eHTt2rDOmZcuW6Ny5M+zt7TXqv3fvHq5cudJgrX379kV+fr7WBkeKvXv3YuzYsbC1tVUfs7Ozw6hRo7Bq1Sr873//w9WrV3H48GH1+1lZWWjXrh08PT2b/ftEloRXboisQEhICF5//XUoFAr1lRsAGD58OGJiYlBVVdWs5gYAZs2ahffffx9du3ZFt27dsGbNmkZDAGfOnAlfX1+MHDkS7dq1Q15eHt599120bdsWwcHB6nFVVVV45ZVXsGjRIly9ehXx8fGIiYmBjY0NXF1dMXfuXMyZMwdKpRJDhw5FUVERfvjhB7i5uSEqKgqvv/46Nm/ejMjISMybNw8eHh7Izs7Gjh078Omnn8LFxQWvvPIK3nrrLbRp0wZeXl5YuHBho9u7+/btC09PT/zwww945plnmjV/+/btw7Jly9Sv/+///g+//fYbhg0bhtatW+PAgQNQKpUat8qOHz+OsLCwZv0ukSVic0NkBUJCQvDgwQN069ZNvf4DqGluSkpK1FvGm+PNN99EXl4eoqKiYGNjg2nTpuG5555rMGRu1KhRSEpKwsaNG3H37l14enoiODgYqampaNOmjXrcU089ha5du2LYsGGorKxEZGQkli5dqn7/nXfeQdu2bbFixQr89ttvaNWqFfr164e3334bQM0tuB9++AHz589HWFgYKisr0aFDB4wePVrdwHzwwQfq21eurq548803Gw3Is7W1RXR0NL788stmNTc5OTnIzs5GeHi4+lirVq2wa9cuLF26FBUVFejatSu2b9+OHj16AKjZNr5nzx4kJyc3+XeJLJVM0NVNcyIiPXj55Zdx//597Nmzx9il1Cs/Px89evRAZmYmOnTo0KTvWLNmDQ4dOoQDBw6I/szGjRuxe/duHDx4sEm/SWTJuOaGiKgZ5HI5/v3vf+P69etN/o527dohLi5O0mfs7e3x8ccfN/k3iSwZr9wQkUkz9Ss3RGR62NwQERGRReFtKSIiIrIobG6IiIjIorC5ISIiIovC5oaIiIgsCpsbIiIisihsboiIiMiisLkhIiIii8LmhoiIiCzK/wfRRPOhs/f7fgAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Assign value as a function of wind speed using the piecewise linear method and default parameters.\n", "time_series.assign_value_piecewise_linear()\n", "\n", "fig, ax = plt.subplots()\n", "ax.scatter(time_series.wind_speeds, time_series.values)\n", "ax.grid()\n", "ax.set_xlabel(\"Wind Speed (m/s)\")\n", "ax.set_ylabel(\"Value (normalized price/MWh)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## WindRose" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A second wind data object is the WindRose, which represents the data as:\n", "\n", " - An array of wind directions\n", " - An array of wind speeds\n", " - A table of turbulence intensities of size (n_wind_directions, n_wind_speeds) which represents the TI at each wind direction and wind speed.\n", " - A table of frequencies of size (n_wind_directions, n_wind_speeds) which represents the frequency of occurance of each wind direction and wind speed.\n", " - An (optional) table of values of size (n_wind_directions, n_wind_speeds) which represents the value of the wind condition." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.16666667, 0.16666667, 0.16666667],\n", " [0.16666667, 0.16666667, 0.16666667]])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from floris import WindRose\n", "\n", "wind_directions = np.array([270, 280]) # 2 Wind Directions\n", "wind_speeds = np.array([6.0, 7.0, 8.0]) # 3 Wind Speeds\n", "\n", "# Create a WindRose object, not indicating a frequency table indicates uniform frequency\n", "wind_rose = WindRose(\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " ti_table=0.06, # As in Time Series, a float indicates a constant table\n", ")\n", "\n", "wind_rose.freq_table" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "wind_rose.ti_table\n", "[[0.09683333 0.0905 0.08575 ]\n", " [0.09683333 0.0905 0.08575 ]]\n", "\n", "wind_rose.value_table\n", "[[1.2225 1.0875 0.9525]\n", " [1.2225 1.0875 0.9525]]\n" ] } ], "source": [ "# Several of the functions implemented for TimeSeries are likewise implemented for WindRose\n", "\n", "wind_rose.assign_ti_using_IEC_method()\n", "\n", "print(\"wind_rose.ti_table\")\n", "print(wind_rose.ti_table)\n", "\n", "wind_rose.assign_value_piecewise_linear()\n", "\n", "print(\"\\nwind_rose.value_table\")\n", "print(wind_rose.value_table)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## WindTIRose" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The WindTIRose is similar to the WindRose except that rather than specififying wind directions and wind speeds as arrays, with TI, frequency, adn value as 2D tables, the WindTIRose specificies wind directions, wind speeds, and turbulence intensities as arrays with the frequency and value tables now 3 dimensional, representing the frequency and value of each wind direction, wind speed, and turbulence intensity occurence." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "from floris import WindTIRose\n", "\n", "wind_directions = np.array([270, 280]) # 2 Wind Directions\n", "wind_speeds = np.array([6.0, 7.0, 8.0]) # 3 Wind Speeds\n", "turbulence_intensities = np.array([0.06, 0.07, 0.08]) # 3 Turbulence Intensities\n", "\n", "# The frequency table therefore is 2 x 3 x 3 and the sum over all entries = 1\n", "freq_table = np.array(\n", " [\n", " [[2 / 18, 0, 1 / 18], [1 / 18, 1 / 18, 1 / 18], [1 / 18, 1 / 18, 1 / 18]],\n", " [[1 / 18, 1 / 18, 1 / 18], [1 / 18, 1 / 18, 1 / 18], [1 / 18, 1 / 18, 1 / 18]],\n", " ]\n", ")\n", "\n", "# The value table has the same dimensions as frequency\n", "value_table = np.ones_like(freq_table)\n", "\n", "wind_ti_rose = WindTIRose(\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", " freq_table=freq_table,\n", " value_table=value_table,\n", ")\n", "\n", "# Demonstrate setting value again\n", "wind_ti_rose.assign_value_piecewise_linear()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conversions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Several methods for converting between WindData objects and resampling to different bin sizes are provided" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# Converting from TimeSeries to WindRose/WindTiRose by binning\n", "wind_rose = time_series.to_WindRose(wd_step=2, ws_step=1)\n", "wind_ti_rose = time_series.to_WindTIRose(wd_step=2, ws_step=1, ti_step=0.01)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Aggregating and Resampling WindRose" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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hQuBE5/5Dz/8kFtvvfWUZdiVlZVs27aN1atX4+joyKFDh3jooYdISUkhPDx8Ct4tEZHZgShARLRGEAR+/vlnNmzYwPHjx3nmmWe4+eabp8zrMDg4SGNjI/X19fT09GgSSF1cXERPxyxGJpPh7u6Ou7s7w8PDNDU10dDQQFFRES4uLnh6eiKXy09bj+Xaa6+ltbWVJ554AoVCQUREBD///LMmMbWurm5MsbimpiYiIyM1vysUCuBE2GbFihVccMEFPP/889TX11NXV8cdd9xxypgmJibs2rVLI3a8vLy46qqreOyxx3T91oiIzCrEUuwiWvHbb7/xyCOPcPjwYf7+979zzz33YGZmpvNxBEGgtbWVmpoaFAoFjo6OeHp64u7uPi0qpBqKiZZin0309/drvGADAwN4eXnh6+s7ZaWrFQoFzz33HO+//z433HADTz31FF5eXlMylsjMQizFrhumV91okWlHWVkZV155JStWrGDRokVUVVWxbt06nYuPoaEhysvL2bVrF/n5+VhbW7NixQqSkpLw8fE5byddkf/DwsKCoKAgli5dSkJCAkqlkj179rBnzx7q6urGVCvVBXK5nDfffJOioiIGBgYIDg5m/fr1dHR06HQcEZHzFTEEI3Jaent7ee6553j99de57bbbOHr06JRUeezo6KC6uprGxkYcHBwIDQ1FLpdPu54qItMHiUSiqcwaGhpKfX09FRUVFBcX4+Pjg5+fn06X9AYEBLBt2zYKCgp4+OGHCQ4OZvPmzdx6663i51REZBKIAkRkDIIg8J///Id169bh6+tLVlYWCxYs0PkYTU1NVFZW0t3djbe3N0uWLBG7mIqMGxMTEwICAvD396etrY2qqip27dqFq6srgYGBY8rHT5bIyEh++eUXvv32Wx588EHeffdd3nrrrXEXMxMRETmBKEBENJSWlnL//fdTVFTECy+8wE033aTTFSZKpZL6+nqOHj2KSqUiICCAhIQEMbwiMmkkEgnOzs44OzvT399PdXU1mZmZ2NvbM2fOHJydnXXyWZZIJFxxxRWsWrWK559/nuTkZG6//Xaee+65MWXpRUREzo3oPxSht7eXhx9+mKioKObPn09ZWRk333yzzsTHyMgIR48eZdeuXVRVVREcHMyKFSsICAgQxYeIzrGwsCA0NJSVK1fi5OREXl4eGRkZNDY2oqucewsL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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Generate a wind rose with a few wind directions and speeds\n", "wind_directions = np.array([260, 265, 270, 275, 280, 285, 290])\n", "wind_speeds = np.array([6.0, 7.0, 8.0, 9.0])\n", "freq_table = np.random.rand(7, 4)\n", "freq_table /= freq_table.sum()\n", "\n", "wind_rose = WindRose(\n", " wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=0.06, freq_table=freq_table\n", ")\n", "\n", "wind_rose.plot()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAikAAAHVCAYAAAA0K2vhAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydd1gUB/7/X7tL7wjC0nsXAWkKCBbUNNPv0k1ySS6Xdt+cqaaZS3JR085075JLzKVdyqUXTUSxIh1Ueu9L753d+f3hjz03gALCLui8nmcedWZ25rPr7sx7PlUiCIKAiIiIiIiIiMgcQ6prA0RERERERERExkMUKSIiIiIiIiJzElGkiIiIiIiIiMxJRJEiIiIiIiIiMicRRYqIiIiIiIjInEQUKSIiIiIiIiJzElGkiIiIiIiIiMxJRJEiIiIiIiIiMicRRYqIiIiIiIjInEQUKSIiIiIiIiJzElGkiIjMUW655RYkEglbt27VWP/NN98gkUh0ZJWIiIiI9hBFiojIHMbIyIht27bR3t6ua1NEREREtI4oUkRE5jCJiYnI5XK2bNmia1NEREREtI4oUkRE5jAymYznn3+e119/ndraWl2bIyIiIqJVRJEiIjLHueKKKwgNDWXz5s26NkVEREREq4giRURkHrBt2zY++OADCgoKdG2KiIiIiNYQRYqIyDwgPj6edevWsWnTJl2bIiIiIqI19HRtgIiIyOTYunUroaGh+Pn56doUEREREa0gelJEROYJwcHB3HDDDbz22mu6NkVEREREK4giRURkHvHMM8+gUql0bYaIiIiIVpAIgiDo2ggRERERERERkd8ielJERERERERE5iSiSBERERERERGZk4giRURERERERGROIooUERERERERkTmJKFJERERERERE5iSiSBERERERERGZk4giRURERERERGROIooUERERERERkTmJKFJERERERERE5iSiSBERERERERGZk4giRUREREREZB5x4MAB1q9fj6OjIxKJhG+++UZjuyAIPPXUUzg4OGBsbExiYiIlJSVnPO6bb76Ju7s7RkZGREdHk5aWNkvvYPKIIkVERERERGQe0dvbS0hICG+++ea421944QVee+01duzYQWpqKqampqxbt46BgYEJj/nZZ5+xceNGNm/eTFZWFiEhIaxbt46mpqbZehuTQhwwKCIyh1Cp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AZgzvmMWC+yICJdFRi73WGPYMik4EonHWsOWRICa59xL5vPRaDSAFx8PXnw8Imu2udKdR8egO3IExuPN6Og2oafPhmdf1oDPo1C9SYhLa0TYeY0UH+824rW/DWDz5o2Qy6Pw/PPP49vf/jaAcwuVQAgUN6R7zgQrboOE5YbFYhENLj2fRQqbzfb5LoVCIczmpQXmz2bnzp3485//7DN+8cUX45NPPln28S8E5HI5xsbGfEowNDU1eSzGS2HBImXbtm0wGAw4duwYNmxwVQA9cOAAFAoF6uvrvQRGbW0tUlNTFyVQAFd67Fxi5Mx+DYHAffJzOGw4bf67qDCgXRvlPLApwElIo1AAWBQFmtBFjM9iwUwv7MNRbtGwiHRjxuHwEjXxBavQefo02VElQnQUC+MTTqgnnZiYtGNaw3yha2gaTp0OTp0OtuFhgMUGwLisN2fAEgpdIkYmc1lopFKIoyIRe9230NPVBbuA77LO8PmudOfkJMR+/SrEfv0qME4nTH39mPq/f8I6OYld+034fJ8rADchno3LdoigHLCjrWsaN954I2677Xv40Y/uwXe+8515hUogBQrgSgteaFNFf0BSpLBYLKKpsyRFAwBPcPv5Nhcw93fpchX75/u95JJL8Prrr3uNkWz8uhicTid++tOf4s0334RKpUJiYiJ27tyJhx9++Kx75d69e3HPPfegtbUVKSkpePjhh7Fz506/rOmaa67B/fffj3/961+gKAo0TePQoUP4yU9+4rn5WgoLFil5eXlISEjA3r17PSJl7969+OpXv4o9e/bgyJEj2Lp1q2d827Zti17M2UywgRYp7osWRbHmyupdMgwYUKz5rUAsUKBB5iLm/gZJ3dexgYDaiCgOBxyZDDhdCEwqlcFxWrHfeascl+8Qe72eYRiYzAwmppyYnHJictr1mDr958SUE6pxOwaGnVCNO6HTO2GxArTZDNpshl097hKcFIWotDQYr7gSXc8+/8V6uFyX0JLJwJnlamJLpeDIZJDTMzj2aQr2H3EVgevoseODz4yYvV+azRY88cQT+NWvnsK9994PANi4caPHx9vf34+urq6AVqslHYzI4/GIxVEAZIWDPzfRhUDyswX6mjzXfHOJFH8JJT6fT6QSsT946qmn8PLLL+PPf/4zCgsLcezYMdx0002QyWS4884753xPX18fLr/8ctx2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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# The downsample functions of WindRose/WindTiRose allows for\n", "# aggregating the data into larger bin sizes\n", "wind_rose_aggregated = wind_rose.downsample(wd_step=10, ws_step=2)\n", "wind_rose_aggregated.plot()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiAAAAHVCAYAAADIPkArAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydd3hUZdqH7zMzyaT33ntCCQkppBBSAEFAEF0Ve3d1bau4FuxrQ2yr4ioqn7oW1roiKoqUJJBCgBRKEhJI7wXS62Rmvj/YzBppCSRTwrmvay7IyXvO+5zJzDm/87xPEdRqtRoRERERERERES0i0bUBIiIiIiIiIhceogARERERERER0TqiABERERERERHROqIAEREREREREdE6ogARERERERER0TqiABERERERERHROqIAEREREREREdE6ogARERERERER0TqiABERERERERHROqIAEREREREREdE6ogAREdFTbr75ZgRB4OWXXx6xfePGjQiCoCOrRERERMYHUYCIiOgxJiYmrFmzhra2Nl2bIiIiIjKuiAJERESPmT9/Pi4uLqxevVrXpoiIiIiMK6IAERHRY6RSKS+99BJr166ltrZW1+aIiIiIjBuiABER0XMuu+wywsPDeeaZZ3RtioiIiMi4IQoQEREDYM2aNfzrX/+iuLhY16aIiIiIjAuiABERMQASExNZuHAhq1at0rUpIiIiIuOCTNcGiIiIjI6XX36Z8PBwgoODdW2KiIiIyHkjekBERAyE0NBQrrvuOt5++21dmyIiIiJy3ogCRETEgHjuuedQqVS6NkNERETkvBHUarVa10aIiIiIiIiIXFiIHhARERERERERrSMKEBERERERERGtIwoQEREREREREa0jChARERERERERrSMKEBERERERERGtIwoQEREREREREa0jChARERERERERrSMKEBERERERERGtIwoQEREREREREa0jChARERERERERrSMKEBEREREREQOjq6uLBx54AG9vb0xNTYmPj2fv3r2nHZ+WloYgCCe9GhsbtWj1SGQ6m1lERERERETknLj99ts5dOgQn332GW5ubnz++efMnz+foqIi3N3dT7tfSUkJVlZWmp+dnJy0Ye4pEZvRiYjoESqVip6eHgYGBlCpVJpXU1MTJiYmWFtbI5FIkEqlSCQSTE1NMTU1RRAEXZsuInJB0d/fz+Dg4LgdT61Wn/Q9lsvlyOXyk8b29fVhaWnJDz/8wJIlSzTbIyMjWbRoES+88MJJ+6SlpZGSkkJbWxs2NjbjZvf5IHpAREQmgP7+fhoaGqivr6ehoUHzamlpoauri66uLrq7u0f829XVRU9Pz5jnkkgkWFhYYGlpiaWlpeb/w/9aWVnh7OyMq6srbm5uuLq64urqirOzMzKZeAkQERkr/f39WJvaMkj/uB3TwsKC7u7uEdueeeYZnn322ZPGDg0NoVQqMTExGbHd1NSUjIyMM84THh7OwMAA06dP59lnn2X27Nnnbfu5InpARETOge7ubo4cOUJpaSmlpaUcOXKEuro6jdBob29HKpXi4uKiueG7urri5OSElZXVGQWDpaUlcrlc4+WQSCQMDQ2xefNmLr74YqRSKSqVCqVSSV9f30mC5o//7+jooKmpSWNbfX09ra2tCIKAo6OjRpR4eHgQFBSkefn5+WFsbKzrt1pERO/o7OzE2tqaBBYjw+i8jzeEggw2U1NTM2J55HQeEID4+HiMjY3ZsGEDzs7O/Pvf/+amm24iICCAkpKSk8aXlJSQlpZGVFQUAwMDrF+/ns8++4ycnBwiIiLO+xzOBVGAiIicBrVaTXl5OUVFRRqhMfyqr6/H2tqa4OBggoKCCAgIwNPTc4TYcHBwQCqVjostCoWCzZs3s3jxYoyMzv+CNzg4qBElw16a6upqjag6cuQIg4OD+Pr6jhAlQUFBhIaG4uzsPA5nJSJimAwLkGQuRSaMgwBRK0jjBzo6OkYIkDNRVlbGrbfeys6dO5FKpURERBAUFERubi7FxcWjOkZSUhJeXl589tln52P+OSP6X0VEOBF7UVZWRm5uruaVl5dHb28vQUFBGqFx4403am7EDg4OBht7YWxsjKenJ56enqf8vUqlora2doTo+umnnygpKaG8vBx3d3ciIyNHvFxcXLR8FiIiFy7+/v6kp6fT09NDZ2cnrq6urFixAj8/v1EfY9asWWddsplIRAEickFSW1tLRkYG+/btIzc3l/z8fPr6+pgxYwaRkZFcffXVvPrqq0yfPv20LtDJjEQiwcvLCy8vL+bPnz/idx0dHeTn52tE2oYNGygtLcXV1VUjRqKjo5k9ezbW1tY6OgMRkQsDc3NzzM3NaWtrY8uWLbzyyiuj3regoABXV9cJtO7MiAJE5IKgrq6OtLQ0zauiooLw8HCio6O57rrreOONN5g2bZoY8zAKrK2tSU5OJjk5WbOtq6tLI0pyc3P54osvKCsrIyIiQjM2ISFBFCQiIuPEli1bUKvVBAcHc/ToUR5++GFCQkK45ZZbAFi1ahV1dXV8+umnALz55pv4+voybdo0+vv7Wb9+PTt27OC3337T2TmIAkRkUvJHwVFeXk5kZCTJycm89dZbJCQkjHqtVeTsWFpakpiYSGJiomZbXV0d6enppKWl8eCDD1JeXn6SIBH/BiIi50ZHRwerVq2itrYWOzs7/vSnP/Hiiy9qYsSG47qGGRwc5KGHHqKurg4zMzNmzJjBtm3bSElJ0dUpiEGoIpMDpVJJdnY2P/74Iz/++CMlJSUawTEZbnbjHYSqC4YFSWpqqsYLFRcXx9KlS1m2bBnBwcEGG1MjcmGhD0GokwHRAyJisHR1dfHbb7+xadMmNm/ejFqt5pJLLuH5559n/vz5ortfz3B3d+faa6/l2muvBaCmpoZffvmFTZs28fTTT+Pp6cmyZctYtmwZs2fPFmuUiIhMcsRvuIhBUVNTw48//simTZtITU3Fz8+PZcuW8f333xMXFzduaa8iE4+npyd//vOf+fOf/0xPTw/btm1j06ZNXHXVVSgUChYvXszSpUu5+OKLRTEpIjIJEZvRieg9x48f5/333ycxMRE/Pz++/vprFixYwKFDhyguLmbNmjUkJCSI4sOAMTc359JLL+X//u//aGhoYPPmzXh5efH888/j7OzMlVdeyQ8//DCupa9FRER0iyhARPSS/v5+vv32Wy677DJcXV356KOPuPLKKzXBpStXriQwMFDXZopMABKJhNjYWF566SUOHTpEfn4+wcHBPPDAA7i4uHDXXXeRkZGBSqXStakiIiLngShARPQGlUpFamoqt99+Oy4uLqxatYqwsDAOHTpETk4O9913n047N4rohilTpvDCCy9QXl7Ojz/+iCAIXHrppfj5+fHEE09QVFSkaxNFRETOAVGAiOicmpoannnmGby9vVmxYgVmZmZs2bKF0tJSnn32WdHTIQKAIAjMnj2b9957j4aGBt5++22OHDmiKX72/vvv09XVpWszRURERokoQER0glKpZPPmzSxbtgx/f3/y8vJ49913qaur4+233yYmJkZMyRQ5LcbGxixbtoyvv/6axsZG7rzzTj744APc3Nz4y1/+QkFBga5NFBEROQuiABHRKseOHePVV18lICCAO+64g5kzZ3L06FF+/PFHli5darA1LkR0h7W1NX/+85/Jzc0lNTUVhULB7NmziY+PZ8OGDWLgqoiIniIKEBGtUFBQwO23346npyc///wzr732GpWVlfz973/Hy8tL1+aJTBKioqJYv3499fX1rFixgmeffRYvLy+eeeYZ6uvrdW2eiIjI7xAFiMiEoVar2b59O/Pnz2f27NlIpVJycnJIS0vjT3/6k+jtEJkwrK2t+etf/8rhw4f55JNPyM3Nxc/Pj9tuu43S0lJdmyciIoIoQEQmAJVKxcaNG4mNjeXKK68kISGB6upq3n//fUJDQ3VtnsgFhEQi4eKLL+ann36ioKAAlUrFjBkzuPLKK8nLy9O1eSIiFzSiABEZNxQKBZ9++imhoaHcc889rFixgurqap599lns7e11bZ7IBU5ISAgff/wxpaWluLm5MWfOHBYuXEh6ejpiSywREe0jChCR86avr4933nmHwMBAnn/+eR544AHKy8tZuXIlFhYWujZPRGQEXl5evPXWW1RWVjJr1iwuvfRSZs+ezY8//igWNxMR0SKiABE5ZwYHB1m7di0+Pj6sX7+eNWvWcPjwYe644w7kcrmuzRMROSOOjo48//zzVFdXs3z5cu644w4iIiI0jQ1FREQmFlGAiIwZlUrFF198QUhICOvWreODDz4gPz+fFStWiP1YRAwOKysrHnnkESorK7npppu48cYbSU5OZvfu3bo2TURkUiMKEJFRo1ar+eWXX4iIiGDVqlU8/fTTHDhwgEsvvVQsGiZi8JiYmPDggw9SVlZGYmIi8+fP57LLLqO4uFjXpomITEpEASIyKnbv3k1KSgrXX389N954I6Wlpdx8882ix0Nk0mFtbc3zzz/P0aNHcXd3JyIigttuu42amhpdmyYiMqkQBYjIGSkpKeHyyy9n/vz5zJkzRxNcamJiomvTREQmFBcXF9555x0OHTpEX18fwcHBPPzww7S1tenaNBGRSYEoQEROSXd3N4899hgzZ87ExcWFo0eP8vzzz2Ntba1r00REtIq/vz8bNmwgMzOT/fv3ExwczMcffyxmzIiInCeiABEZgVqt5ptvvmHKlClkZmaSnZ3Nu+++i4uLi65NExHRKTNnzmTLli28//77PPvss8yePVss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bt++nXvvvfeU+8TFxbF9+3YeeOABzbatW7cSFxcHnBDsCoXipM+HVCod98JixsbGvPbaayQnJ3PTTTexY8cO3n77bczMzMZ1HhERQ0AUIJOI//znP9x2221cc801vP7665iamurapAlHrVbT0tJCbW0tDQ0NmJiY4OHhwbRp07CwsNC1eSK/QyaTabwjarWa48ePU1dXx969e5FKpbi7u+Pp6XnWLICVK1dy0003ERUVxaxZs3jzzTfp6enhlltuAeDGG2/E3d2d1atXA/DXv/6VpKQkXn/9dZYsWcKXX37Jvn37+OCDD4AT2U1JSUk8/PDDmJqa4u3tTXp6Op9++ilvvPHGhLwXl1xyCQUFBVx77bVER0fz9ddfM23atAmZS0REXxEfCU/DzTffjCAIvPzyyyO2b9y4EUEQ6O7uxsjIiC+//HLE76+++moEQaCysnLEdh8fnxEpfuNJf38/9913H7feeisffvgh77777qQWH2q1mvb2dg4dOsSWLVvIy8vDyMiI2bNnM3fuXIKDg0XxoecIgoC9vT0zZsxg4cKFhIWF0dfXx86dO0lNTeXIkSP09fWdct8VK1bw2muv8fTTTxMeHk5BQQG//vqrJtC0urqahoYGzfj4+Hg2bNjABx98QFhYGN9++y0bN27U1AAB+PLLL4mOjua6665j6tSpvPzyy7z44oujKkR2rnh6epKamspll11GTEwMH330EdqoinC2axtAWloagiCc8vX74F0RkfNB9ICcARMTE9asWcOdd96Jra3tiN9ZWFgQFRVFWloaV199tWZ7Wloanp6epKWlcfPNNwNQUVFBVVUVc+fOHXcbjxw5wooVK5DJZOTl5eHn5zfuc+gLCoWC2tpaKisr6enpwc3NjYiICBwcHMTlFQNGIpHg7OyMs7MzCoWCxsZGampqKC4uxtHRER8fH5ydnUf8je+9997TLrmkpaWdtO3KK688YwaYi4sLH3/88Xmfy1iRyWS88MILJCUlcf3117N9+3bWrVs34cG6Z7q2/Z6SkpKTPFIXajC7yPgjXrXPwPz583FxcdG4cv9ISkrKiItdcXEx/f39/OUvfxmxPS0tDblcrllzHi82b95MdHQ0ycnJZGRkTFrx0dHRQUFBAVu2bKG6uho/Pz8uvvhiIiIicHJyEsXHJMLIyAhPT0/i4+O56KKLsLOz4+DBg2zd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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# For upsampling, the upsample method is available which can\n", "# interpolate the data via linear or nearest-neighbor interpolation\n", "wind_rose_resampled = wind_rose.upsample(wd_step=2.5, ws_step=0.5)\n", "wind_rose_resampled.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are several plotting methods available to help visualize wind data objects" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAGwCAYAAABVdURTAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA7pklEQVR4nO3dfXzP9f7H8ecXu8QMG9uYuQq5SCulIRflsjilzjqh0HGUq1wlWodsCtVJdMmPwq+LoQ5CRcS2aJuLio50FqNWGiK2ZjZjn98f/Xzr22y+n+17wafH/XZzOz7vz/vz/ry+r/Pt9Dyfz+f7/doMwzAEAABgEZW8XQAAAIArEW4AAIClEG4AAIClEG4AAIClEG4AAIClEG4AAIClEG4AAIClVPF2AZ5WXFysH3/8UdWrV5fNZvN2OQAAwAmGYeiXX35RRESEKlUq+9rMny7c/Pjjj4qMjPR2GQAAoBy+//571a9fv8w5f7pwU716dUm/NicoKMilaxcVFWnjxo3q2bOnfHx8XLo2fkOfPYM+ewZ99hx67Rnu6nNubq4iIyPt/x4vy58u3Fy4FRUUFOSWcBMYGKigoCD+wXEj+uwZ9Nkz6LPn0GvPcHefnXmkhAeKAQCApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApXg93Bw+fFj33XefateurYCAALVp00a7du0qdf62bdvUsWNH+/wWLVpo7ty5HqwYAABczrz621InT55Ux44d1a1bN61fv16hoaHav3+/atasWeoxVatW1ZgxY3TNNdeoatWq2rZtmx566CFVrVpVDz74oAerBwAAlyOvhptnnnlGkZGRWrJkiX2sUaNGZR4THR2t6Oho+3bDhg21atUqbd26lXADAAC8G27Wrl2rXr16KTY2VikpKapXr55GjRql4cOHO73GF198odTUVD311FMX3V9YWKjCwkL7dm5urqRff7W0qKioYi/gDy6s5+p14Yg+ewZ99gz67Dn02jPc1Wcz69kMwzBcenYT/P39JUkTJ05UbGysdu7cqXHjxmnBggUaMmRImcfWr19fP/30k86dO6f4+HhNmzbtovPi4+OVkJBQYjwxMVGBgYEVfxEAAMDt8vPzNXDgQOXk5CgoKKjMuV4NN76+vmrXrp1SU1PtY2PHjtXOnTuVlpZW5rGHDh1SXl6e0tPT9dhjj+nll1/WgAEDSsy72JWbyMhIHT9+/JLNMauoqEibNm1Sjx495OPj49K18Rv67Bn02TPos+fQa89wV59zc3MVEhLiVLjx6m2p8PBwtWzZ0mHs6quv1sqVKy957IVnc9q0aaOjR48qPj7+ouHGz89Pfn5+JcZ9fHzc9uZ259r4DX32DPrsGfTZc+i1Z7i6z2bW8upHwTt27KiMjAyHsW+++UZRUVGm1ikuLna4OgMAAP68vHrlZsKECerQoYNmzZqle+65Rzt27NDChQu1cOFC+5y4uDgdPnxYb7zxhiTplVdeUYMGDdSiRQtJ0ieffKLnnntOY8eO9cprAAAAlxevhpsbbrhBq1evVlxcnGbMmKFGjRpp3rx5GjRokH1Odna2srKy7NvFxcWKi4vToUOHVKVKFTVp0kTPPPOMHnroIW+8BAAAcJnxariRpL59+6pv376l7l+6dKnD9sMPP6yHH37YzVUBAIArldd/fgEAAMCVCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSCDcAAMBSvB5uDh8+rPvuu0+1a9dWQECA2rRpo127dpU6f9WqVerRo4dCQ0MVFBSkmJgYffTRRx6sGAAAXM68Gm5Onjypjh07ysfHR+vXr9e+ffs0Z84c1axZs9RjPvnkE/Xo0UMffvihPvvsM3Xr1k39+vXTF1984cHKAQDA5aqKN0/+zDPPKDIyUkuWLLGPNWrUqMxj5s2b57A9a9YsrVmzRuvWrVN0dLQ7ygQAAFcQr4abtWvXqlevXoqNjVVKSorq1aunUaNGafjw4U6vUVxcrF9++UW1atW66P7CwkIVFhbat3NzcyVJRUVFKioqqtgL+IML67l6XTiiz55Bnz2DPnsOvfYMd/XZzHo2wzAMl57dBH9/f0nSxIkTFRsbq507d2rcuHFasGCBhgwZ4tQazz77rJ5++mn997//VZ06dUrsj4+PV0JCQonxxMREBQYGVuwFAAAAj8jPz9fAgQOVk5OjoKCgMud6Ndz4+vqqXbt2Sk1NtY+NHTtWO3fuVFpa2iWPT0xM1PDhw7VmzRp17979onMuduUmMjJSx48fv2RzzCoqKtKmTZvUo0cP+fj4uHRt/IY+ewZ99gz67Dn02jPc1efc3FyFhIQ4FW68elsqPDxcLVu2dBi7+uqrtXLlykseu3z5cv3jH//Qu+++W2qwkSQ/Pz/5+fmVGPfx8XHbm9uda+M39Nkz6LNn0GfPodee4eo+m1nLq5+W6tixozIyMhzGvvnmG0VFRZV53LJly/TAAw9o2bJluv32291ZIgAAuMJ4NdxMmDBB6enpmjVrlg4cOKDExEQtXLhQo0ePts+Ji4vT4MGD7duJiYkaPHiw5syZo/bt2+vIkSM6cuSIcnJyvPESAADAZcar4eaGG27Q6tWrtWzZMrVu3VpPPvmk5s2bp0GDBtnnZGdnKysry769cOFCnTt3TqNHj1Z4eLj9z7hx47zxEgAAwGXGq8/cSFLfvn3Vt2/fUvcvXbrUYTs5Odm9BQEAgCua139+AQAAwJUINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFIINwAAwFKqmD3g0KFD2rp1q7777jvl5+crNDRU0dHRiomJkb+/vztqBAAAcJrT4ebtt9/WCy+8oF27dqlu3bqKiIhQQECAfv75Z2VmZsrf31+DBg3SlClTFBUV5c6aAQAASuVUuImOjpavr6+GDh2qlStXKjIy0mF/YWGh0tLStHz5crVr106vvvqqYmNj3VIwAABAWZwKN08//bR69epV6n4/Pz917dpVXbt21cyZM/Xtt9+6qj4AAABTnAo3ZQWbP6pdu7Zq165d7oIAAAAqwvQDxb/3wQcfKDk5WefPn1fHjh119913u6ouAACAcin3R8GnTZumyZMny2azyTAMTZgwQQ8//LArawMAADDN6Ss3u3btUrt27ezbK1as0J49exQQECBJGjp0qLp27aqXXnrJ9VUCAAA4yekrNyNGjND48eOVn58vSWrcuLHmzJmjjIwM/ec//9H8+fPVrFkztxUKAADgDKfDzfbt2xUeHq7rrrtO69at0+LFi/XFF1+oQ4cOuvnmm/XDDz8oMTHRnbUCAABcktO3pSpXrqwpU6YoNjZWI0eOVNWqVfXyyy8rIiLCnfUBAACYYvqB4saNG+ujjz5S//791blzZ73yyivuqAsAAKBcnA43p06d0uTJk9WvXz9NnTpV/fv31/bt27Vz507ddNNN+s9//uPOOgEAAJzidLgZMmSItm/frttvv10ZGRkaOXKkateuraVLl2rmzJn629/+pilTprizVgAAgEty+pmbLVu26IsvvlDTpk01fPhwNW3a1L7v1ltv1eeff64ZM2a4pUgAAABnOX3l5qqrrtLChQv1zTffaMGCBSV++dvf31+zZs1yeYEAAABmOB1uFi9erC1btig6OlqJiYmaP3++O+sCAAAoF6dvS1177bXatWuXO2sBAACoMKeu3BiG4e46AAAAXMKpcNOqVSstX75cZ8+eLXPe/v37NXLkSD399NMuKQ4AAMAsp25LvfTSS5oyZYpGjRqlHj16qF27doqIiJC/v79Onjypffv2adu2bfrqq680ZswYjRw50t11AwAAXJRT4ebWW2/Vrl27tG3bNq1YsUJvv/22vvvuO505c0YhISGKjo7W4MGDNWjQINWsWdPdNQMAAJTK1M8vdOrUSS+99JJ2796tkydPqqCgQD/88IPWrVunMWPGlCvYHD58WPfdd59q166tgIAAtWnTpswHl7OzszVw4EA1a9ZMlSpV0vjx402fEwAAWJfp35ZypZMnT6pjx47y8fHR+vXrtW/fPs2ZM6fMkFRYWKjQ0FBNnTpVbdu29WC1AADgSuD0R8Hd4ZlnnlFkZKSWLFliH2vUqFGZxzRs2FAvvPCCpF+/ewcAAOD3vBpu1q5dq169eik2NlYpKSmqV6+eRo0apeHDh7vsHIWFhSosLLRv5+bmSpKKiopUVFTksvNcWPP3/wn3oM+eQZ89gz57Dr32DHf12cx6NsOLX2Lj7+8vSZo4caJiY2O1c+dOjRs3TgsWLNCQIUMueXzXrl117bXXat68eaXOiY+PV0JCQonxxMREBQYG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AABwCZf9/AIAAMDlgHADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAshXADAAAsxavhJj4+XjabzeFPixYtyjxm3rx5at68uQICAhQZGakJEyaooKDAQxUDAIDLXRVvF9CqVSt9/PHH9u0qVUovKTExUY899pgWL16sDh066JtvvtHQoUNls9n0/PPPe6JcAABwmfN6uKlSpYrCwsKcmpuamqqOHTtq4MCBkqSGDRtqwIAB2r59uztLBAAAVxCvh5v9+/crIiJC/v7+iomJ0ezZs9WgQYOLzu3QoYPeeust7dixQzfeeKMOHjyoDz/8UPfff3+p6xcWFqqwsNC+nZubK0kqKipSUVGRS1/LhfVcvS4c0WfPoM+eQZ89h157hrv6bGY9m2EYhkvPbsL69euVl5en5s2bKzs7WwkJCTp8+LD27t2r6tWrX/SYF198UZMmTZJhGDp37pxGjBih+fPnl3qO+Ph4JSQklBhPTExUYGCgy14LAABwn/z8fA0cOFA5OTkKCgoqc65Xw80fnTp1SlFRUXr++ec1bNiwEvuTk5N177336qmnnlL79u114MABjRs3TsOHD9e0adMuuubFrtxERkbq+PHjl2yOWUVFRdq0aZN69OghHx8fl66N39Bnz6DPnkGfPYdee4a7+pybm6uQkBCnwo3Xb0v9XnBwsJo1a6YDBw5cdP+0adN0//336x//+IckqU2bNjp9+rQefPBB/fOf/1SlSiU//OXn5yc/P78S4z4+Pm57c7tzbfyGPnsGffYM+uw59NozXN1nM2tdVt9zk5eXp8zMTIWHh190f35+fokAU7lyZUnSZXQBCgAAeJFXw82kSZOUkpKib7/9Vqmpqerfv78qV66sAQMGSJIGDx6suLg4+/x+/fpp/vz5Wr58uQ4dOqRNmzZp2rRp6tevnz3kAACAPzev3pb64YcfNGDAAJ04cUKhoaHq1KmT0tPTFRoaKknKyspyuFIzdepU2Ww2TZ06VYcPH1ZoaKj69eunmTNneuslAACAy4xXw83y5cvL3J+cnOywXaVKFU2fPl3Tp093Y1UAAOBKdlk9cwMAAFBRhBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGApXg038fHxstlsDn9atGhR6vyuXbuWmG+z2XT77bd7sGoAAHA5q+LtAlq1aqWPP/7Yvl2lSuklrVq1SmfPnrVvnzhxQm3btlVsbKxbawQAAFcOr4ebKlWqKCwszKm5tWrVcthevny5AgMDCTcAAMDO6+Fm//79ioiIkL+/v2JiYjR79mw1aNDAqWNff/113XvvvapatWqpcwoLC1VYWGjfzs3NlSQVFRWpqKioYsX/wYX1XL0uHNFnz6DPnkGfPYdee4a7+mxmPZthGIZLz27C+vXrlZeXp+bNmys7O1sJCQk6fPiw9u7dq+rVq5d57I4dO9S+fXtt375dN954Y6nz4uPjlZCQUGI8MTFRgYGBFX4NAADA/fLz8zVw4EDl5OQoKCiozLleDTd/dOrUKUVFRen555/XsGHDypz70EMPKS0tTV9++WWZ8y525SYyMlLHjx+/ZHPMKioq0qZNm9SjRw/5+Pi4dG38hj57Bn32DPrsOfTaM9zV59zcXIWEhDgVbrx+W+r3goOD1axZMx04cKDMeadPn9by5cs1Y8aMS67p5+cnPz+/EuM+Pj5ue3O7c238hj57Bn32DPrsOfTaM1zdZzNrXVbfc5OXl6fMzEyFh4eXOe/dd99VYWGh7rvvPg9VBgAArhReDTeTJk1SSkqKvv32W6Wmpqp///6qXLmyBgwYIEkaPHiw4uLiShz3+uuv684771Tt2rU9XTIAALjMefW21A8//KABAwboxIkTCg0NVadOnZSenq7Q0FBJUlZWlipVcsxfGRkZ2rZtmzZu3OiNkgEAwGXOq+Fm+fLlZe5PTk4uMda8eXNdRs9AAwCAy8xl9cwNAABARRFuAACApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApRBuAACApVxWvwruCRe+3Tg3N9flaxcVFSk/P1+5ubn84qwb0WfPoM+eQZ89h157hrv6fOHf2878SsGfLtz88ssvkqTIyEgvVwIAAMz65ZdfVKNGjTLn2Iw/2Q81FRcX68cff1T16tVls9lcunZubq4iIyP1/fffKygoyKVr4zf02TPos2fQZ8+h157hrj4bhqFffvlFERERJX5U+4/+dFduKlWqpPr167v1HEFBQfyD4wH02TPos2fQZ8+h157hjj5f6orNBTxQDAAALIVwAwAALIVw40J+fn6aPn26/Pz8vF2KpdFnz6DPnkGfPYdee8bl0Oc/3QPFAADA2rhyAwAALIVwAwAALIVwAwAALIVwAwAALIVw4yKvvPKKGjZsKH9/f7Vv3147duzwdkmWEx8fL5vN5vCnRYsW3i7rivfJJ5+oX79+ioiIkM1m03vvveew3zAMPfHEEwoPD1dAQIC6d++u/fv3e6fYK9il+jx06NAS7+/evXt7p9gr2OzZs3XDDTeoevXqqlOnju68805lZGQ4zCkoKNDo0aNVu3ZtVatWTXfffbeOHj3qpYqvTM70uWvXriXe0yNGjPBIfYQbF1ixYoUmTpyo6dOn6/PPP1fbtm3Vq1cvHTt2zNulWU6rVq2UnZ1t/7Nt2zZvl3TFO336tNq2batXXnnlovufffZZvfjii1qwYIG2b9+uqlWrqlevXiooKPBwpVe2S/VZknr37u3w/l62bJkHK7SGlJQUjR49Wunp6dq0aZOKiorUs2dPnT592j5nwoQJWrdund59912lpKToxx9/1F133eXFqq88zvRZkoYPH+7wnn722Wc9U6CBCrvxxhuN0aNH27fPnz9vREREGLNnz/ZiVdYzffp0o23btt4uw9IkGatXr7ZvFxcXG2FhYca//vUv+9ipU6cMPz8/Y9myZV6o0Br+2GfDMIwhQ4YYd9xxh1fqsbJjx44ZkoyUlBTDMH59//r4+Bjvvvuufc7XX39tSDLS0tK8VeYV7499NgzD6NKlizFu3Div1MOVmwo6e/asPvvsM3Xv3t0+VqlSJXXv3l1paWlerMya9u/fr4iICDVu3FiDBg1SVlaWt0uytEOHDunIkSMO7+8aNWqoffv2vL/dIDk5WXXq1FHz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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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jAzOBAFe0uwgVFhYqCuvvFLSD+GkoqJCDodD8+fP15///GevF/hjqampevLJJ/XMM89o3759uu2221RRUeF+imz69OkeN1PPnTtXmzdv1qpVq7R//34tW7ZMu3bt0pw5c3xaJwAACAzNvjR2zjnn6NixY5KkLl26aO/evRo4cKCOHDnS5BuTTtf111+vr7/+WkuWLFFJSYkuvPBCbd682X1DdFFRkVq1+v9sN3LkSK1fv16LFy/W3Xffrb59+yojI4PPEAIAAJKaEYT27t2rAQMGaNSoUcrKytLAgQN17bXXau7cudqyZYuysrI0duxYX9YqSZozZ069Z3Ryc3NrjV177bW69tprfVwVAAAIRE0OQoMGDdLPfvYzTZ482R0sfvvb38rpdGr79u265pprtHjxYp8VCgAA4G1NDkJbt27V008/rbS0NN1333265pprdPPNN9f5ic4AAACBoMk3S1922WVat26diouL9dhjj+nzzz/X6NGjdd555+n+++9v8EMKAQAAzkbNfmqsffv2mjFjhrZu3aqPP/5Y1157rdasWaNu3brp5z//uS9qBAAA8IlmB6Ef69Onj+6++24tXrxYoaGheuONN7xVFwAAgM+d9neNbdu2TevWrdPf/vY3tWrVStddd51mzpzpzdoAAAB8qllB6ODBg0pPT1d6eroOHDigkSNH6tFHH9V1112n9u3b+6pGAAAAn2hyEJo4caLeeustRUREaPr06frVr36lfv36+bI2AAAAn2pyEHI6nXrllVd01VVXqXXr1r6sCQAAwBZNDkKbNm3yZR0AAAC2O6OnxgAAAAIZQQgAABiLIAQAAIxFEAIAAMYiCAEAAGMRhAAAgLEIQgAAwFgEIQAAYCyCEAAAMBZBCAAAGIsgBAAAjEUQAgAAxiIIAQAAYxGEAACAsQhCAADAWAQhAABgLIIQAAAwFkEIAAAYiyAEAACMRRACAADGIggBAABjEYQAAICxCEIAAMBYBCEAAGAsghAAADAWQQgAABiLIAQAAIxFEAIAAMYiCAEAAGMRhAAAgLEIQgAAwFgEIQAAYCyCEAAAMBZBCAAAGIsgBAAAjEUQAgAAxiIIAQAAYxGEAACAsQhCAADAWAQhAABgrIAJQt99951uvPFGhYWFKTw8XDNnzlR5eXmD+yQmJsrhcHj83HrrrTZVDAAAznZt/F1AU914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P/XNN9+411dUVGj69Onq0KGDYmJimnTZ6euvv9aWLVs0adIkj3GHw6EnnnhCV111lUJCQnT++ecrLy9PBw4cUGJiotq3b6+RI0eqsLDQY78TJ04oMzPTHYT+9Kc/qW/fvmrbtq2ioqL0H//xHx7bT5o0SS+88EKjdQKmIQgBcBszZoxycnLcyzk5OUpMTNTo0aPd48ePH9eOHTvcQagulZWVeuihh/Tcc89p27ZtKioq0u233+5ev2rVKqWnp2vdunV655139N1332njxo0N1hYdHa333ntPX3zxRYPbZWdna9++fcrNzdWGDRv06quvavny5e71aWlpevbZZ7V27Vp99NFHmj9/vn75y19q69atkqQjR47o8ssv15AhQ7Rr1y5t3rxZpaWluu6669xz3HHHHdq6datee+01ZWZmKjc3V3v27GmwrnfeeccddH5q5cqVmj59uvLz8xUfH69p06bpv/7rv7Ro0SLt2rVLlmVpzpw5td5nly5dFB8fr127duk3v/mNVqxYoYKCAm3evFmjRo3y2H748OHauXOnqqqqGqwTMI7Pv98eQMB48sknrfbt21sul8sqKyuz2rRpYx06dMhav369NWrUKMuyLCs7O9uSZH3xxReWZVlWTk6OJck6fPiwZVmW9fTTT1uSrAMHDrjnXbNmjRUVFeVejomJsR544AH3ssvlsrp27WpdffXV9dZ28OBB6+KLL7YkWeedd56VkpJivfjii9bJkyfd26SkpFidOnWyKioq3GOPP/641aFDB+vkyZPWiRMnrJCQEGv79u0ec8+cOdOaOnWqZVmWtXLlSispKclj/ZdffmlJsgoKCqxjx45ZQUFB1ksvveRe/+2331rt2rWz5s6dW2/9jzzyiNWrV69a45KsxYsXu5fz8vIsSdZf/vIX99iGDRustm3beuw3a9Ys6/bbb7csy7L+9re/WWFhYVZZWVm9r//BBx9YkqzPP/+83m0AE7XxXwQDcLZJTExURUWF3n//fR0+fFjnnXeeIiMjNXr0aM2YMUMnTpxQbm6uevXqpW7dutU7T0hIiHr37u1ejomJ0aFDhyRJR48eVXFxsUaMGOFe36ZNGw0bNqzBy2MxMTHKy8vT3r17tW3bNm3fvl0pKSl66qmntHnzZvd9N4MHD1ZISIh7v4SEBJWXl+vLL79UeXm5KisrNX78eI+5q6urNWTIEEnSBx98oJycHHXo0KFWDYWFhTp+/Liqq6s96u/UqZP69etXb+3SD2fS2rZtW+e6QYMGuX+PioqSJA0cONBj7MSJEyorK1NYWJgsy9Lrr7+ul156SZI0fvx4de/eXb169dKECRM0YcIETZkyxaMP7dq1k/TD2ToA/48gBMCtT58+6tq1q3JycnT48GGNHj1akhQbG6u4uDht375dOTk5uvzyyxucx+l0eiw7HI5G7wFqqgEDBmjAgAH69a9/rVtvvVWXXXaZtm7d2uClulNO3Vj9xhtvqEuXLh7rgoOD3dtMmjRJ999/f639Y2JidODAgdOqOyIiQocPH65z3Y/7deqm87rGampqJEk7d+7U999/r5EjR0qSQkNDtWfPHuXm5iozM1NLlizRsmXL9P777ys8PFyS9N1330mSIiMjT6t+oKXiHiEAHsaMGaPc3Fzl5uZ6PDY/atQo/c///I927tzZpNBRn44dOyomJkY7duxwj33//ffavXt3s+e64IILJP1w8/IpH3zwgY4fP+5efu+999ShQwfFxcXpggsuUHBwsIqKitSnTx+Pn7i4OEnSRRddpI8++kg9evSotU379u3Vu3dvOZ1Oj/oPHz6sjz/+uMFahwwZopKSknrDUHO89tpruvLKK9W6dWv3WJs2bTRu3Dg98MAD+vDDD/X5559ry5Yt7vV79+5V165dFRERccavD7QknBEC4GHMmDGaPXu2XC6X+4yQJI0ePVpz5sxRdXX1GQUhSZo7d65+//vfq2/fvoqPj9fDDz/c6Acy3nbbbYqNjdXll1+url27qri4WPfee68iIyOVkJDg3q66ulozZ87U4sWL9fnnn2vp0qWaM2eOWrVqpdDQUN1+++2aP3++ampqdOmll+ro0aN69913FRYWppSUFM2ePVtPPvmkpk6dqoULF6pTp046cOCAXnjhBT311FPq0KGDZs6cqTvuuEPnnnuuOnfurN/+9reNPhI/ZMgQRURE6N1339VVV111Rv3btGmTVqxY4V7++9//rk8//VSjRo3SOeecozfffFM1NTUel+vefvttJSUlndHrAi0RQQiAhzFjxuj48eOKj493368i/RCEjh075n7M/kwsWLBAxcXFSklJUatWrfSrX/1KU6ZMafAD/8aNG6d169bp8ccf17fffquIiAglJCQoOztb5557rnu7sWPHqm/fvho1apSqqqo0depULVu2zL1+5cqVioyMVFpamj799FOFh4froosu0t133y3ph8uA7777ru68804lJSWpqqpK3bt314QJE9xh58EHH3RfQgsNDdWCBQsa/bDC1q1ba8aMGXr++efPKAgVFhbqwIEDSk5Odo+Fh4fr1Vdf1bJly3TixAn17dtXGzZsUP/+/SX98Kh9RkaGNm/efNqvC7RUDstbF+4BwM9uuukmHTlyRBkZGf4upU4lJSXq37+/9uzZo+7du5/WHA8//LDeeustvfnmm03e5/HHH9fGjRuVmZl5Wq8JtGTcIwQANomOjtZf/vIXFRUVnfYcXbt21aJFi5q1j9Pp1GOPPXbarwm0ZJwRAtBinO1nhACcfQhCAADAWFwaAwAAxiIIAQAAYxGEAACAsQhCAADAWAQhAABgLIIQAAAwFkEIAAAYiyAEAACM9b9ce9fOz1shqAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "wind_directions = np.arange(0, 360, 10)\n", "wind_speeds = np.arange(0.0, 30.0, 5.0)\n", "freq_table = np.random.rand(36, 6)\n", "freq_table = freq_table / freq_table.sum()\n", "\n", "wind_rose = WindRose(\n", " wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=0.06, freq_table=freq_table\n", ")\n", "\n", "# Set value\n", "wind_rose.assign_value_piecewise_linear()\n", "\n", "wind_rose.plot()\n", "\n", "# Plot with aggregated wind directions\n", "wind_rose.plot(wd_step=30)\n", "\n", "wind_rose.plot_ti_over_ws()\n", "\n", "wind_rose.plot_value_over_ws()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setting FLORIS" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "WindData objects are used to set wind direction, speed, TI, frequency, and value in a FlorisModel (or UncertainFlorisModel)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### TimeSeries" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# TimeSeries\n", "\n", "from floris import FlorisModel\n", "\n", "# Create a FlorisModel object\n", "fmodel = FlorisModel(\"../examples/inputs/gch.yaml\")\n", "\n", "# Set a two-turbine layout\n", "fmodel.set(layout_x=[0, 500], layout_y=[0, 0])\n", "\n", "# Make a set of inputs with 5 wind directions, while wind speed and TI are constant\n", "wind_directions = np.array([270, 280, 290, 300, 310])\n", "wind_speeds = 8.0 * np.ones(5)\n", "turbulence_intensities = 0.06 * np.ones(5)\n", "\n", "fmodel.set(\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " turbulence_intensities=turbulence_intensities,\n", ")\n", "\n", "# Is equivalent to the following (but now we'll include value as well):\n", "time_series = TimeSeries(\n", " wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06\n", ")\n", "\n", "# Scale some of the default parameters to get reasonable values representing USD/MWh\n", "time_series.assign_value_piecewise_linear(value_zero_ws=25 * 1.425, slope_2=-25 * 0.135)\n", "\n", "fmodel.set(wind_data=time_series)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[34mfloris.floris_model.FlorisModel\u001b[0m \u001b[1;30mWARNING\u001b[0m \u001b[33mComputing AEP with uniform frequencies. Results results may not reflect annual operation.\u001b[0m\n", "\u001b[34mfloris.floris_model.FlorisModel\u001b[0m \u001b[1;30mWARNING\u001b[0m \u001b[33mComputing AVP with uniform frequencies. Results results may not reflect annual operation.\u001b[0m\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Turbine power have shape (5, 2) and are [[1753954.45917917 354990.76412771]\n", " [1753954.45917917 1320346.28513924]\n", " [1753954.45917917 1748551.48278202]\n", " [1753954.45917917 1753951.95262087]\n", " [1753954.45917917 1753954.45908051]]\n", "Farm power has shape (5,) and is [2108945.22330688 3074300.74431841 3502505.94196119 3507906.41180004\n", " 3507908.91825968]\n", "Expected farm power has shape () and is 3140313.447929242\n", "Farm AEP is 27.50914580386016 GWh\n", "Expected farm value has shape () and is 74778713.9788151\n", "Farm annual value production (AVP) is 655061.5344544202 USD\n" ] } ], "source": [ "# Run the model and get outputs\n", "fmodel.run()\n", "\n", "# Get the power outputs\n", "turbine_powers = fmodel.get_turbine_powers()\n", "farm_power = fmodel.get_farm_power()\n", "expected_farm_power = fmodel.get_expected_farm_power()\n", "aep = fmodel.get_farm_AEP()\n", "\n", "# Get value outputs\n", "expected_farm_value = fmodel.get_expected_farm_value()\n", "avp = fmodel.get_farm_AVP()\n", "\n", "# Display\n", "print(f\"Turbine power have shape {turbine_powers.shape} and are {turbine_powers}\")\n", "print(f\"Farm power has shape {farm_power.shape} and is {farm_power}\")\n", "print(f\"Expected farm power has shape {expected_farm_power.shape} and is {expected_farm_power}\")\n", "print(f\"Farm AEP is {aep/1e9} GWh\")\n", "print(f\"Expected farm value has shape {expected_farm_power.shape} and is {expected_farm_value}\")\n", "print(f\"Farm annual value production (AVP) is {avp/1e6} USD\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### WindRose" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "WindRose objects set FLORIS as TimeSeries, but there are some additional considerations.\n", "\n", " - By default, wind direction/speed combinations with 0 frequency are not run\n", " - The outputs of the functions get_turbine_powers and get_farm_power will be reshaped to have dimensions num_wind_directions x num_wind_speeds ( x num_turbines)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fmodel has n_findex 4 because two cases have 0 frequency\n" ] } ], "source": [ "wind_directions = np.array([270, 280]) # 2 Wind Directions\n", "wind_speeds = np.array([6.0, 7.0, 8.0]) # 3 Wind Speeds\n", "\n", "# Frequency matrix is 2 x 3, include some 0 frequency results\n", "freq_table = np.array([[0, 0, 1 / 2], [1 / 6, 1 / 6, 1 / 6]])\n", "\n", "# Create a WindRose object, not indicating a frequency table indicates uniform frequency\n", "wind_rose = WindRose(\n", " wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=0.06, freq_table=freq_table\n", ")\n", "\n", "# Set value and scale some of the default parameters to get reasonable values representing USD/MWh\n", "wind_rose.assign_value_piecewise_linear(value_zero_ws=25 * 1.425, slope_2=-25 * 0.135)\n", "\n", "fmodel.set(wind_data=wind_rose)\n", "\n", "print(f\"Fmodel has n_findex {fmodel.core.flow_field.n_findex} because two cases have 0 frequency\")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Turbine power have shape (2, 3, 2) and are [[[ nan nan]\n", " [ nan nan]\n", " [1753954.45917917 354990.76412771]]\n", "\n", " [[ 731003.41073165 523849.55426108]\n", " [1176825.66812027 876937.12082426]\n", " [1753954.45917917 1320346.28513924]]]\n", "Farm power has shape (2, 3) and is [[ nan nan 2108945.22330688]\n", " [1254852.96499273 2053762.78894454 3074300.74431841]]\n", "Expected farm power has shape () and is 2118292.0280293887\n", "Farm AEP is 18.556238165537444 GWh\n", "Expected farm value has shape () and is 53008780.071847945\n", "Farm annual value production (AVP) is 464356.913429388 USD\n" ] } ], "source": [ "# Run the model and collect the outputs\n", "fmodel.run()\n", "\n", "# Get the power outputs\n", "turbine_powers = fmodel.get_turbine_powers()\n", "farm_power = fmodel.get_farm_power()\n", "expected_farm_power = fmodel.get_expected_farm_power()\n", "aep = fmodel.get_farm_AEP()\n", "\n", "# Get value outputs\n", "expected_farm_value = fmodel.get_expected_farm_value()\n", "avp = fmodel.get_farm_AVP()\n", "\n", "# Note that the nan values in the non-computed cases are expected since these are not run\n", "\n", "# Display\n", "print(f\"Turbine power have shape {turbine_powers.shape} and are {turbine_powers}\")\n", "print(f\"Farm power has shape {farm_power.shape} and is {farm_power}\")\n", "print(f\"Expected farm power has shape {expected_farm_power.shape} and is {expected_farm_power}\")\n", "print(f\"Farm AEP is {aep/1e9} GWh\")\n", "print(f\"Expected farm value has shape {expected_farm_power.shape} and is {expected_farm_value}\")\n", "print(f\"Farm annual value production (AVP) is {avp/1e6} USD\")" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fmodel has n_findex 6\n", "Turbine powers and farm power are now computed for all cases\n", "Turbine power have shape (2, 3, 2) and are [[[ 731003.41073165 80999.08780495]\n", " [1176825.66812027 191637.98384374]\n", " [1753954.45917917 354990.76412771]]\n", "\n", " [[ 731003.41073165 523849.55426108]\n", " [1176825.66812027 876937.12082426]\n", " [1753954.45917917 1320346.28513924]]]\n", "Farm power has shape (2, 3) and is [[ 812002.4985366 1368463.65196401 2108945.22330688]\n", " [1254852.96499273 2053762.78894454 3074300.74431841]]\n", "Expected farm power and value, AEP, and AVP are the same as before since the new cases are weighted by 0\n", "Expected farm power has shape () and is 2118292.0280293887\n", "Farm AEP is 18.556238165537444 GWh\n", "Expected farm value has shape () and is 53008780.071847945\n", "Farm annual value production (AVP) is 464356.913429388 USD\n" ] } ], "source": [ "# It's possible however to force the running of 0 frequency cases\n", "wind_rose = WindRose(\n", " wind_directions=wind_directions,\n", " wind_speeds=wind_speeds,\n", " ti_table=0.06,\n", " freq_table=freq_table,\n", " compute_zero_freq_occurrence=True,\n", ")\n", "\n", "# Set value and scale some of the default parameters to get reasonable values representing USD/MWh\n", "wind_rose.assign_value_piecewise_linear(value_zero_ws=25 * 1.425, slope_2=-25 * 0.135)\n", "\n", "fmodel.set(wind_data=wind_rose)\n", "\n", "print(f\"Fmodel has n_findex {fmodel.core.flow_field.n_findex}\")\n", "\n", "# Run the model and collect the outputs\n", "fmodel.run()\n", "\n", "# Get the power outputs\n", "turbine_powers = fmodel.get_turbine_powers()\n", "farm_power = fmodel.get_farm_power()\n", "expected_farm_power = fmodel.get_expected_farm_power()\n", "aep = fmodel.get_farm_AEP()\n", "\n", "# Get value outputs\n", "expected_farm_value = fmodel.get_expected_farm_value()\n", "avp = fmodel.get_farm_AVP()\n", "\n", "# Display\n", "print(\"Turbine powers and farm power are now computed for all cases\")\n", "print(f\"Turbine power have shape {turbine_powers.shape} and are {turbine_powers}\")\n", "print(f\"Farm power has shape {farm_power.shape} and is {farm_power}\")\n", "\n", "print(\n", " \"Expected farm power and value, AEP, and AVP are the same as before since the new cases are weighted by 0\"\n", ")\n", "print(f\"Expected farm power has shape {expected_farm_power.shape} and is {expected_farm_power}\")\n", "print(f\"Farm AEP is {aep/1e9} GWh\")\n", "print(f\"Expected farm value has shape {expected_farm_power.shape} and is {expected_farm_value}\")\n", "print(f\"Farm annual value production (AVP) is {avp/1e6} USD\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# WindRoseWRG" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `WindRoseWRG` is a data object which is used to represent within FLORIS the information in a Wind Resource Grid (WRG) file. `WindRoseWRG` is a type of WindData object, like `WindRose` and `TimeSeries`, that\n", "is used to store wind data in a format that can be used by the FLORIS model. `WindRoseWRG` is different that `WindRose` however because the internal data holds the information of the WRG file and then a `WindRose` object is created \n", "for each turbine in a provided layout." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "from floris import WindRoseWRG" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "# Read a WRG file (from examples)\n", "wind_rose_wrg = WindRoseWRG(\"../examples/examples_wind_resource_grid/wrg_example.wrg\")" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "WindResourceGrid with 2 x 3 grid points, min x: 0.0, min y: 0.0, grid size: 1000.0, z: 0.0, h: 90.0, 12 sectors\n", "Wind directions in file: [ 0. 30. 60. 90. 120. 150. 180. 210. 240. 270. 300. 330.]\n", "Wind directions: [ 0. 30. 60. 90. 120. 150. 180. 210. 240. 270. 300. 330.]\n", "Wind speeds: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.\n", " 18. 19. 20. 21. 22. 23. 24. 25.]\n", "ti_table: 0.06\n" ] } ], "source": [ "# Print some basic information\n", "print(wind_rose_wrg)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# Aggregate the wind speeds and directions\n", "wind_rose_wrg.set_wd_step(5.0)\n", "wind_rose_wrg.set_wind_speeds(np.arange(0, 30, 5))" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "# Set a turbine layout within the grid points of the WRG file\n", "layout_x = np.array([0, 500, 1000])\n", "layout_y = np.array([0, 1000, 2000])" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "# Set up a FLORIS model with the above layout and wind_rose_wrg\n", "fmodel = FlorisModel(\"../examples/inputs/gch.yaml\")\n", "\n", "fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose_wrg)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Within FlorisModel, the is set to a separate wind rose per turbine\n", "fig, axarr = plt.subplots(1, 3, figsize=(15, 5), subplot_kw=dict(polar=True))\n", "fmodel.wind_data.plot_wind_roses(axarr=axarr)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2296892.47124259 2297743.68483228 2342752.51651458]\n" ] } ], "source": [ "# Can get the expected power for each turbine\n", "fmodel.run()\n", "print(fmodel.get_expected_turbine_powers())" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6937388.672589453\n", "60771524771.883606\n" ] } ], "source": [ "# Getting expected farm power and AEP weights each turbine by its own wind rose\n", "print(fmodel.get_expected_farm_power())\n", "print(fmodel.get_farm_AEP())" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using point 0 at (0.0, 0.0) as reference location\n" ] }, { "data": { "image/png": "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zQ0SiQS7du0qc5vjx4+jdevWUCgU8Pb2RlBQkMb7KpUKn376Kby8vGBubo4GDRrgyy+/hCAIVXMQRERUbYyt5uCMT0RERERERERElbBu3bpS3z9+/LjGcpcuXXDjxo1Stxk2bBiGDRtW5r7feustvPXWW2XGERGR/mDeICKiisrIyECLFi0wbtw4DBo0qMz48PBw9O3bF++88w42b96MI0eOYMKECXB1dUVAQAAAYNGiRVi9ejU2bNiAJk2a4NKlSxg7dixsbW3xv//9r6oPiYiIqpCx1Rwc+EREREREREREREREREREpKcCAwMRGBhY7vg1a9bAy8tLfMyQr68vTp8+jWXLlokDn86ePYsBAwagb9++AIB69eph69atuHDhgvYPgIiIqApx4BMRVas0lRmUqsp99GSplFrqDRER6TvmDSIiIiIiIiIiqqlSU1M1lhUKBRQKRaXbDQ4ORo8ePTTWBQQEYNq0aeJyhw4dsHbtWty5cweNGjXCP//8g9OnT2Pp0qWV3j8REVF14sAnIiIiIiIiIiIiIiIiIqJySFFZIE8lq1QbmSoVAMDDw0Nj/WeffYbPP/+8Um0DQExMDJydnTXWOTs7IzU1FVlZWTA3N8dHH32E1NRU+Pj4QCaTQaVSYcGCBRg5cmSl909ERFSdOPCJiIiIiIiIiIiIiIiIiKiaPXz4EDY2NuKyNmZ7Kq9t27Zh8+bN2LJlC5o0aYKQkBBMmzYNbm5uGD16dLX1g4iIqLI48ImIiIiIiIiIiIiIiIiIqJrZ2NhoDHzSFhcXF8TGxmqsi42NhY2NDczNzQEAM2fOxEcffYThw4cDAJo1a4YHDx5g4cKFHPhEREQGRarrDhARERERERERERERERERkXb4+/vjyJEjGusOHToEf39/cTkzMxNSqealYplMBrVaXS19JCIi0hbO+EREREREREREREREREREpKfS09Nx7949cTk8PBwhISGwt7dH3bp1MXv2bERFReGXX34BALzzzjv4/vvvMWvWLIwbNw5Hjx7Ftm3bsHfvXrGNfv36YcGCBahbty6aNGmCq1evYunSpRg3bly1Hx8REVFlcOATEREREREREREREREREZGeunTpErp16yYuz5gxAwAwevRoBAUFITo6GpGRkeL7Xl5e2Lt3L6ZPn47ly5fD3d0dP/30EwICAsSYFStW4NNPP8W7776LuLg4uLm54e2338bcuXOr78CIiIi0gAOfiIiIiIiIiIiIiIiIiIj0VNeuXSEIQonvBwUFFbvN1atXS9zG2toa3333Hb777jst9JCIiEh3pGWHEBERERERERERERERERERERER6RfO+ERE1SpdrYBSLa9UG9nqPC31hoiI9B3zBhEREREREREREREREZWEMz4REREREREREREREREREREREZHB4YxPRERERERERERERERERETlkC6YQaWu3CXWLEGppd4QERERZ3wiIiIiIiIiIiIiIiIiIiIiIiKDw4FPRERERERERERERERERERERERkcDjwiYiIiIiIiIiIiIiIiIiIiIiIDA4HPhERERERERERERERERERERERkcHhwCciIiIiIiIiIiIiIiIiIiIiIjI4HPhEREREREREREREREREREREREQGx0TXHSAi45KqtECOUl6pNnKUeVrqDRER6TvmDSIiIiIiIiIiIiIiIioJZ3wiIiIiIiIiIiIiIiIiIiIiIiKDo9OBTwsXLkTbtm1hbW0NJycnDBw4ELdv39aIyc7OxuTJk1G7dm1YWVlh8ODBiI2N1YiJjIxE3759YWFhAScnJ8ycORNKpVIj5vjx42jdujUUCgW8vb0RFBRU1YdHRERaxrxBREQVxdxBREQVwbxBREQVxdxBZHxSVBZIruQrRWWh68MgHWLuICLSLp0OfDpx4gQmT56Mc+fO4dChQ8jLy0OvXr2QkZEhxkyfPh1//fUXtm/fjhMnTuDx48cYNGiQ+L5KpULfvn2Rm5uLs2fPYsOGDQgKCsLcuXPFmPDwcPTt2xfdunVDSEgIpk2bhgkTJuDgwYPVcpypqakIDw9HampqteyP6Fn8GaSawljyBsDfW9It/vxRTcLcQVQ9+PNHNQXzBlH14c8g1RTMHUTVgz9/VJMYS+7g7y3pGn8GjYdEEARB150oEB8fDycnJ5w4cQKdO3dGSkoKHB0dsWXLFgwZMgQAcOvWLfj6+iI4OBjt27fH/v378corr+Dx48dwdnYGAKxZswYffvgh4uPjYWpqig8//BB79+7F9evXxX0NHz4cycnJOHDgQJn9Sk1Nha2tLVJSUmBjY1OhY7py5Qr++usvcblPnz5o27YtAEAQBOTl5ZW4rVQqhYmJSYVjASA3N1crsRKJBHK5/Lli8/LyUNKPV1XFAoCp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69X/+3//T71791ZqaqpycnK0ZMmSsDZfffWVJk+erJ49eyotLU3nn3++3nnnnbA2lZWVuuGGG5SZmalWrVrp0ksv1datW495vdLSUn3nO99R69at5fF49O1vf1s1NTXN88YBAI1GdgAAWsL3v/999ejRQ6mpqeratauuu+467d69+6TP2b59u37wgx+oc+fO8ng8uvLKK1VZWRnWZv369brkkkvUrl07dezYUbfccosOHjwYeryoqEiGYRz3tnfv3hZ5rwCA5kF2AABWrlypMWPGKDMzU4ZhaPHixfU+Z8WKFRoyZIhSUlLUp08fFRUVHdMmkvEvAED8cnrNwcQnAHHv2WefVUFBgWbMmKH169crJydH+fn5J/yynD59uv785z/rT3/6kz744ANNnDhRP/jBD7Rhw4ZQmwkTJqikpER//etf9f7772vkyJHKy8vTrl27JEmWZWns2LH6+OOP9dJLL2nDhg3q2bOn8vLyVF1dHdpPaWmpLr30Uo0cOVJr1qzRO++8o0mTJsk0+XoGADuRHQCAljJixAgtWrRIW7Zs0QsvvKDt27frRz/60QnbV1dXa+TIkTIMQ8uXL9ebb76puro6jRkzRoFAQJK0e/du5eXlqU+fPlq9erWWLFmiTZs26YYbbgjt56qrrtKePXvCbvn5+brooovUpUuXln7bAIAmIDsAANXV1crJydGCBQsa1L68vFyjR4/WiBEjVFZWpsmTJ2vChAlaunRpqE2k418AgPjl9JrDsCzLavS7TxBVVVVq27atDhw4II/HY3d3gKhrjt+B4D6G6zIlGe4m9cdnebVCLzW4P8OGDdM555yj+fPnS5ICgYCysrL005/+VNOmTTumfWZmpu666y7dfvvtoW2XX3650tLS9L//+7+qqalRmzZt9NJLL2n06NGhNkOHDtWoUaP029/+Vh999JH69u2rjRs3auDAgaHXzcjI0L333qsJEyZIks477zxdcskl+s1vftOkzwSxh+xAInN6bkhkB+xBdiCRxUN2NNY//vEPjR07VrW1tXK7j+33a6+9plGjRumLL74I9eXAgQNq3769XnvtNeXl5ekvf/mLfv3rX2vPnj2hibDvv/++Bg0apK1bt6pPnz7H7Hffvn3q1q2bHn/8cV133XUt+h7RMsgNJLqm/g44NTcksgONR3YgkTVnzTHC9cNmyY5/+f/e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zUc7OMrGpxb022+/RU1NTZ0XT0REUVFRVFZWttGo2o/S0tJYunRpvP3227Fw4cKoqKiIyy+/PKqrq6OysjLy8/OjsLCwzj3/XrvKysp61/boua7k6Hwb+12rrKyM008/vc757t27x6mnnmpNoR3RjobpRvPRDeg8dKNx2tF8tAM6D+1omG40L+2AzkE3GqcdzUc3oPPQjobpRvPSDo7q3tYDoOuaMmVK7v8XXnhhlJaWxtChQ2PZsmXRq1evNhwZAO2RbgCQlXYAkIVuAJCVdgCQhW5Ay/DFpxY0YMCAOOmkk2L37t11ju/evTuKi4vbaFTtV2FhYYwYMSK2b98excXFcejQoaiqqqpzzb/Xrri4uN61PXquKzk638Z+14qLi2PPnj11zh85ciT++OMPawrtiHYcP904cboBnYduZKMdJ047oPPQjuOnG02jHdA56EY22nHidAM6D+04frrRNNrBUTY+taD8/PwYO3ZsvPfee7ljtbW18d5778X48ePbcGTt019//RXff/99DBw4MMaOHRs9evSos3bbtm2LnTt35tZu/Pjx8fXXX9d5Ua1atSr69esXo0aNavXxt6Xhw4dHcXFxnfXat29frF+/vs56VVVVxcaNG3PXvP/++1FbWxulpaW5a1avXh2HDx/OXbNq1aoYOXJk9O/fv5VmA12bdhw/3ThxugGdh25kox0nTjug89CO46cbTaMd0DnoRjbaceJ0AzoP7Th+utE02kFOokWVl5ennj17pqVLl6atW7em2267LRUWFqbKysq2Hlqbu//++9OHH36YKioq0ieffJImTpyYBgwYkPbs2ZNSSumOO+5IQ4YMSe+//376/PPP0/jx49P48eNz9x85ciSdf/75adKkSemLL75Ib7/9djrttNPS3Llz22pKLaq6ujpt2rQpbdq0KUVEevLJJ9OmTZvSjh07UkoplZWVpcLCwvTaa6+lr776Kl177bVp+PDh6cCBA7lnXHXVVWnMmDFp/fr16eOPP07nnHNOmj59eu58VVVVKioqSjNnzkybN29O5eXlqaCgID333HOtPl/oyrSjfrqRjW5A16EbDdOObLQDug7tqJ9uZKcd0DXoRsO0IxvdgK5DO+qnG9lpB8fDxqdW8Mwzz6QhQ4ak/Pz8VFJSkj799NO2HlK7MG3atDRw4MCUn5+fBg8enKZNm5a2b9+e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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Use the get_heterogeneous_map method to generate a WindRose that represents\n", "# the information in the WindRoseWRG, rather than a set of WindRose objects\n", "# but as a single WindRose object (for one location) and a HeterogeneousMap\n", "# the describes the speed up information per direction across the domain\n", "# This will allow running the optimization for a single wind speed while still\n", "# accounting for the difference in wind speeds in location by direction\n", "wind_rose_het = wind_rose_wrg.get_heterogeneous_wind_rose(\n", " fmodel=fmodel,\n", " x_loc=0.0,\n", " y_loc=0.0,\n", " representative_wind_speed=10.0,\n", ")\n", "\n", "# Pull out the heterogeneous plot to show the underlying speedups\n", "het_map = wind_rose_het.heterogeneous_map\n", "wind_direction_to_plot = [0.0, 10.0, 45.0, 75.0, 90.0, 180.0]\n", "\n", "# Show the het_map for a few wind directions\n", "fig, axarr = plt.subplots(1, len(wind_direction_to_plot), figsize=(30, 5))\n", "axarr = axarr.flatten()\n", "for i, wd in enumerate(wind_direction_to_plot):\n", " het_map.plot_single_speed_multiplier(\n", " wind_direction=wd,\n", " wind_speed=8.0,\n", " ax=axarr[i],\n", " show_colorbar=True,\n", " )\n", "\n", " axarr[i].set_title(f\"Wind Direction: {wd}\")" ] } ], "metadata": { "kernelspec": { "display_name": "floris", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: examples/001_opening_floris_computing_power.py ================================================ """Example 1: Opening FLORIS and Computing Power This example illustrates several of the key concepts in FLORIS. It demonstrates: 1) Initializing a FLORIS model 2) Changing the wind farm layout 3) Changing the incoming wind speed, wind direction and turbulence intensity 4) Running the FLORIS simulation 5) Getting the power output of the turbines Main concept is introduce FLORIS and illustrate essential structure of most-used FLORIS calls """ import numpy as np from floris import FlorisModel # The FlorisModel class is the entry point for most usage. # Initialize using an input yaml file fmodel = FlorisModel("inputs/gch.yaml") # Changing the wind farm layout uses FLORIS' set method to a two-turbine layout fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) # Changing wind speed, wind direction, and turbulence intensity uses the set method # as well. Note that the wind_speeds, wind_directions, and turbulence_intensities # are all specified as arrays of the same length. fmodel.set( wind_directions=np.array([270.0]), wind_speeds=[8.0], turbulence_intensities=np.array([0.06]) ) # Note that typically all 3, wind_directions, wind_speeds and turbulence_intensities # must be supplied to set. However, the exception is if not changing the length # of the arrays, then only one or two may be supplied. fmodel.set(turbulence_intensities=np.array([0.07])) # The number of elements in the wind_speeds, wind_directions, and turbulence_intensities # corresponds to the number of conditions to be simulated. In FLORIS, each of these are # tracked by a simple index called a findex. There is no requirement that the values # be unique. Internally in FLORIS, most data structures will have the findex as their # 0th dimension. The value n_findex is the total number of conditions to be simulated. # This command would simulate 4 conditions (n_findex = 4). fmodel.set( wind_directions=np.array([270.0, 270.0, 270.0, 270.0]), wind_speeds=[8.0, 8.0, 10.0, 10.0], turbulence_intensities=np.array([0.06, 0.06, 0.06, 0.06]), ) # After the set method, the run method is called to perform the simulation fmodel.run() # There are functions to get either the power of each turbine, or the farm power turbine_powers = fmodel.get_turbine_powers() / 1000.0 farm_power = fmodel.get_farm_power() / 1000.0 print("The turbine power matrix should be of dimensions 4 (n_findex) X 2 (n_turbines)") print(turbine_powers) print("Shape: ", turbine_powers.shape) print("The farm power should be a 1D array of length 4 (n_findex)") print(farm_power) print("Shape: ", farm_power.shape) ================================================ FILE: examples/002_visualizations.py ================================================ """Example 2: Visualizations This example demonstrates the use of the flow and layout visualizations in FLORIS. First, an example wind farm layout is plotted, with the turbine names and the directions and distances between turbines shown in different configurations by subplot. Next, the horizontal flow field at hub height is plotted for a single wind condition. FLORIS includes two modules for visualization: 1) flow_visualization: for visualizing the flow field 2) layout_visualization: for visualizing the layout of the wind farm The two modules can be used together to visualize the flow field and the layout of the wind farm. """ import matplotlib.pyplot as plt import floris.layout_visualization as layoutviz from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane fmodel = FlorisModel("inputs/gch.yaml") # Set the farm layout to have 8 turbines irregularly placed layout_x = [0, 500, 0, 128, 1000, 900, 1500, 1250] layout_y = [0, 300, 750, 1400, 0, 567, 888, 1450] fmodel.set(layout_x=layout_x, layout_y=layout_y) # Layout visualization contains the functions for visualizing the layout: # plot_turbine_points # plot_turbine_labels # plot_turbine_rotors # plot_waking_directions # Each of which can be overlaid to provide further information about the layout # This series of 4 subplots shows the different ways to visualize the layout # Create the plotting objects using matplotlib fig, axarr = plt.subplots(2, 2, figsize=(15, 10), sharex=False) axarr = axarr.flatten() ax = axarr[0] layoutviz.plot_turbine_points(fmodel, ax=ax) ax.set_title("Turbine Points") ax = axarr[1] layoutviz.plot_turbine_points(fmodel, ax=ax) layoutviz.plot_turbine_labels(fmodel, ax=ax) ax.set_title("Turbine Points and Labels") ax = axarr[2] layoutviz.plot_turbine_points(fmodel, ax=ax) layoutviz.plot_turbine_labels(fmodel, ax=ax) layoutviz.plot_waking_directions(fmodel, ax=ax, limit_num=2) ax.set_title("Turbine Points, Labels, and Waking Directions") # In the final subplot, use provided turbine names in place of the t_index ax = axarr[3] turbine_names = ["T1", "T2", "T3", "T4", "T9", "T10", "T75", "T78"] layoutviz.plot_turbine_points(fmodel, ax=ax) layoutviz.plot_turbine_labels(fmodel, ax=ax, turbine_names=turbine_names) layoutviz.plot_waking_directions(fmodel, ax=ax, limit_num=2) ax.set_title("Use Provided Turbine Names") # Visualizations of the flow field are made by using calculate plane methods. In this example # we show the horizontal plane at hub height, further examples are provided within # the examples_visualizations folder # For flow visualizations, the FlorisModel may be set to run a single condition # (n_findex = 1). Otherwise, the user may set multiple conditions and then use # the findex_for_viz keyword argument to calculate_horizontal_plane to specify which # flow condition to visualize. fmodel.set(wind_speeds=[8.0], wind_directions=[290.0], turbulence_intensities=[0.06]) horizontal_plane = fmodel.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=90.0, ) # Plot the flow field with rotors fig, ax = plt.subplots() visualize_cut_plane( horizontal_plane, ax=ax, label_contours=False, title="Horizontal Flow with Turbine Rotors and labels", ) # Plot the turbine rotors layoutviz.plot_turbine_rotors(fmodel, ax=ax) layoutviz.plot_turbine_labels(fmodel, ax=ax, turbine_names=turbine_names) plt.show() ================================================ FILE: examples/003_wind_data_objects.py ================================================ """Example 3: Wind Data Objects This example demonstrates the use of wind data objects in FLORIS: TimeSeries, WindRose, and WindTIRose. For each of the WindData objects, examples are shown of: 1) Initializing the object 2) Broadcasting values 3) Converting between objects 4) Setting TI and value 5) Plotting 6) Setting the FLORIS model using the object """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, WindRose, WindTIRose, ) ################################################## # Initializing ################################################## # FLORIS provides a set of wind data objects to hold the ambient wind conditions in a # convenient classes that include capabilities and methods to manipulate and visualize # the data. # The TimeSeries class is used to hold time series data, such as wind speed, wind direction, # and turbulence intensity. # There is also a "value" wind data variable, which represents the value of the power # generated at each time step or wind condition (e.g., the price of electricity). This can # then be used in later optimization methods to optimize for quantities besides AEP. # Generate wind speeds, directions, turbulence intensities, and values via random signals N = 100 rng = np.random.default_rng(0) wind_speeds = 8 + 2 * rng.standard_normal(N) wind_directions = 270 + 30 * rng.standard_normal(N) turbulence_intensities = 0.06 + 0.02 * rng.standard_normal(N) values = 25 + 10 * rng.standard_normal(N) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, values=values, ) # The WindRose class is used to hold wind rose data, such as wind speed, wind direction, # and frequency. TI and value are represented as bin averages per wind direction and # speed bin. wind_directions = np.arange(0, 360, 3.0) wind_speeds = np.arange(4, 20, 2.0) # Make TI table 6% TI for all wind directions and speeds ti_table = 0.06 * np.ones((len(wind_directions), len(wind_speeds))) # Make value table 25 for all wind directions and speeds value_table = 25 * np.ones((len(wind_directions), len(wind_speeds))) # Uniform frequency freq_table = np.ones((len(wind_directions), len(wind_speeds))) freq_table = freq_table / np.sum(freq_table) wind_rose = WindRose( wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=ti_table, freq_table=freq_table, value_table=value_table, ) # The WindTIRose class is similar to the WindRose table except that TI is also binned # making the frequency table a 3D array. turbulence_intensities = np.arange(0.05, 0.15, 0.01) # Uniform frequency freq_table = np.ones((len(wind_directions), len(wind_speeds), len(turbulence_intensities))) # Uniform value value_table = 25 * np.ones((len(wind_directions), len(wind_speeds), len(turbulence_intensities))) wind_ti_rose = WindTIRose( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, freq_table=freq_table, value_table=value_table, ) ################################################## # Broadcasting ################################################## # A convenience method of the wind data objects is that, unlike the lower-level # FlorisModel.set() method, the wind data objects can broadcast upward data provided # as a scalar to the full array. This is useful for setting the same wind conditions # for all turbines in a wind farm. # For TimeSeries, as long as one condition is given as an array, the other 2 # conditions can be given as scalars. The TimeSeries object will broadcast the # scalars to the full array (uniform) wind_directions = 270 + 30 * rng.standard_normal(N) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06 ) # For WindRose, wind directions and wind speeds must be given as arrays, but the # ti_table can be supplied as a scalar which will apply uniformly to all wind # directions and speeds. Not supplying a freq table will similarly generate # a uniform frequency table. wind_directions = np.arange(0, 360, 3.0) wind_speeds = np.arange(4, 20, 2.0) wind_rose = WindRose(wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=0.06) ################################################## # Wind Rose from Time Series ################################################## # The TimeSeries class has a method to generate a wind rose from a time series based on binning wind_rose = time_series.to_WindRose(wd_edges=np.arange(0, 360, 3.0), ws_edges=np.arange(2, 20, 2.0)) ################################################## # Wind Rose from long CSV FILE ################################################## # The WindRose class can also be initialized from a long CSV file. By long what is meant is # that the file has a column for each wind direction, wind speed combination. The file can # also specify the mean TI per bin and the frequency of each bin as seperate columns. # If the TI is not provided, can specify a fixed TI for all bins using the ti_col_or_value # input wind_rose_from_csv = WindRose.read_csv_long( "inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06 ) ################################################## # Aggregating and Resampling the Wind Rose ################################################## # The downsample function allows for aggregation of the wind rose data into # fewer wind direction and wind speed bins. # Note it will throw an error if the step sizes passed in are smaller than the # step sizes of the original data. wind_rose_aggregate = wind_rose.downsample(wd_step=10, ws_step=2) # For upsampling, the upsample function can be used to interpolate # the wind rose data to a finer grid. It can use either linear or nearest neighbor wind_rose_resample = wind_rose.upsample(wd_step=0.5, ws_step=0.25) ################################################## # Setting turbulence intensity ################################################## # Each of the wind data objects also has the ability to set the turbulence intensity # according to a function of wind speed and direction. This can be done using a custom # function by using the assign_ti_using_wd_ws_function method. There is also a method # called assign_ti_using_IEC_method which assigns TI based on the IEC 61400-1 standard. wind_rose.assign_ti_using_IEC_method() # Assign using default settings for Iref and offset ################################################## # Setting value ################################################## # Similarly, each of the wind data objects also has the ability to set the value according to # a function of wind speed and direction. This can be done using a custom function by using # the assign_value_using_wd_ws_function method. There is also a method called # assign_value_piecewise_linear which assigns value based on a linear piecewise function of # wind speed. # Assign value using default settings. This produces a value vs. wind speed that approximates # the normalized mean electricity price vs. wind speed curve for the SPP market in the U.S. # for years 2018-2020 from figure 7 in "The value of wake steering wind farm flow control in # US energy markets," Wind Energy Science, 2024. https://doi.org/10.5194/wes-9-219-2024. wind_rose.assign_value_piecewise_linear() ################################################## # Plotting Wind Data Objects ################################################## # Certain plotting methods are included to enable visualization of the wind data objects # Plotting a wind rose wind_rose.plot() # Plot a wind rose with the wind directions aggregated into 10-deg bins wind_rose.plot(wd_step=10) # Showing TI over wind speed for a WindRose wind_rose.plot_ti_over_ws() # Showing value over wind speed for a WindRose wind_rose.plot_value_over_ws() ################################################## # Setting the FLORIS model via wind data ################################################## # Each of the wind data objects can be used to set the FLORIS model by passing # them in as is to the set method. The FLORIS model will then use the member functions # of the wind data to extract the wind conditions for the simulation. Frequency tables # are also extracted for expected power and AEP-like calculations. # Similarly the value data is extracted and maintained. fmodel = FlorisModel("inputs/gch.yaml") # Set the wind conditions using the TimeSeries object fmodel.set(wind_data=time_series) # Set the wind conditions using the WindRose object fmodel.set(wind_data=wind_rose) # Note that in the case of the wind_rose, under the default settings, wind direction and wind speed # bins for which frequency is zero are not simulated. This can be changed by setting the # compute_zero_freq_occurrence parameter to True. wind_directions = np.array([200.0, 300.0]) wind_speeds = np.array([5.0, 10.0]) freq_table = np.array([[0.5, 0], [0.5, 0]]) wind_rose = WindRose( wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=0.06, freq_table=freq_table ) fmodel.set(wind_data=wind_rose) print( f"Number of conditions to simulate with compute_zero_freq_occurrence = False: " f"{fmodel.n_findex}" ) wind_rose = WindRose( wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=0.06, freq_table=freq_table, compute_zero_freq_occurrence=True, ) fmodel.set(wind_data=wind_rose) print( f"Number of conditions to simulate with compute_zero_freq_occurrence = " f"True: {fmodel.n_findex}" ) # Set the wind conditions using the WindTIRose object fmodel.set(wind_data=wind_ti_rose) plt.show() ================================================ FILE: examples/004_set.py ================================================ """Example 4: Set This example illustrates the use of the set method. The set method is used to change the wind conditions, the wind farm layout, the turbine type, and the controls settings. This example demonstrates setting each of the following: 1) Wind conditions 2) Wind farm layout 3) Controls settings """ import numpy as np from floris import ( FlorisModel, TimeSeries, WindRose, ) fmodel = FlorisModel("inputs/gch.yaml") ###################################################### # Atmospheric Conditions ###################################################### # Change the wind directions, wind speeds, and turbulence intensities using numpy arrays fmodel.set( wind_directions=np.array([270.0, 270.0, 270.0]), wind_speeds=[8.0, 9.0, 10.0], turbulence_intensities=np.array([0.06, 0.06, 0.06]), ) # Set the wind conditions as above using the TimeSeries object fmodel.set( wind_data=TimeSeries( wind_directions=270.0, wind_speeds=np.array([8.0, 9.0, 10.0]), turbulence_intensities=0.06 ) ) # Set the wind conditions as above using the WindRose object fmodel.set( wind_data=WindRose( wind_directions=np.array([270.0]), wind_speeds=np.array([8.0, 9.0, 10.0]), ti_table=0.06, ) ) # Set the wind shear fmodel.set(wind_shear=0.2) # Set the air density fmodel.set(air_density=1.1) # Set the reference wind height (which is the height at which the wind speed is given) fmodel.set(reference_wind_height=92.0) ###################################################### # Array Settings ###################################################### # Changing the wind farm layout uses FLORIS' set method to a two-turbine layout fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) ###################################################### # Controls Settings ###################################################### # Changes to controls settings can be made using the set method # Note the dimension must match (n_findex, n_turbines) or (number of conditions, number of turbines) # Above we n_findex = 3 and n_turbines = 2 so the matrix of yaw angles must be 3x2 yaw_angles = np.array([[0.0, 0.0], [25.0, 0.0], [0.0, 0.0]]) fmodel.set(yaw_angles=yaw_angles) # By default for the turbines in the turbine_library, the power # thrust model is set to "cosine-loss" which adjusts # power and thrust according to cos^cosine_loss_exponent(yaw | tilt) # where the default exponent is 1.88. For other # control capabilities, the power thrust model can be set to "mixed" # which provides the same cosine loss model, and # additionally methods for specifying derating levels for power and disabling turbines. # Use the reset operation method to clear out control signals fmodel.reset_operation() # Change to the mixed model turbine fmodel.set_operation_model("mixed") # Shut down the front turbine for the first two findex disable_turbines = np.array([[True, False], [True, False], [False, False]]) fmodel.set(disable_turbines=disable_turbines) # Derate the front turbine for the first two findex RATED_POWER = 5e6 # 5MW (Anything above true rated power will still result in rated power) power_setpoints = np.array( [[RATED_POWER * 0.3, RATED_POWER], [RATED_POWER * 0.3, RATED_POWER], [RATED_POWER, RATED_POWER]] ) fmodel.set(power_setpoints=power_setpoints) ================================================ FILE: examples/005_getting_power.py ================================================ """Example 5: Getting Turbine and Farm Power After setting the FlorisModel and running, the next step is typically to get the power output of the turbines. FLORIS has several methods for getting power: 1. `get_turbine_powers()`: Returns the power output of each turbine in the farm for each findex (n_findex, n_turbines) 2. `get_farm_power()`: Returns the total power output of the farm for each findex (n_findex) 3. `get_expected_farm_power()`: Returns the combination of the farm power over each findex with the frequency of each findex to get the expected farm power 4. `get_farm_AEP()`: Multiplies the expected farm power by the number of hours in a year to get the expected annual energy production (AEP) of the farm """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, WindRose, ) fmodel = FlorisModel("inputs/gch.yaml") # Set to a 3-turbine layout fmodel.set(layout_x=[0, 126 * 5, 126 * 10], layout_y=[0, 0, 0]) ###################################################### # Using TimeSeries ###################################################### # Set up a time series in which the wind speed and TI are constant but the wind direction # sweeps the range from 250 to 290 degrees wind_directions = np.arange(250, 290, 1.0) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=9.9, turbulence_intensities=0.06 ) fmodel.set(wind_data=time_series) # Run the model fmodel.run() # Get the turbine powers turbine_powers = fmodel.get_turbine_powers() # Turbines powers will have shape (n_findex, n_turbines) where n_findex is the number of unique # wind conditions and n_turbines is the number of turbines in the farm print(f"Turbine power has shape {turbine_powers.shape}") # It is also possible to get the farm power directly farm_power = fmodel.get_farm_power() # Farm power has length n_findex, and is the sum of the turbine powers print(f"Farm power has shape {farm_power.shape}") # It's possible to get these powers with wake losses disabled, this can be useful # for computing total wake losses fmodel.run_no_wake() farm_power_no_wake = fmodel.get_farm_power() # Plot the results fig, axarr = plt.subplots(1, 3, figsize=(15, 5)) # Plot the turbine powers ax = axarr[0] for i in range(turbine_powers.shape[1]): ax.plot(wind_directions, turbine_powers[:, i] / 1e3, label=f"Turbine {i+1} ") ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") ax.grid(True) ax.legend() ax.set_title("Turbine Powers") # Plot the farm power ax = axarr[1] ax.plot(wind_directions, farm_power / 1e3, label="Farm Power With Wakes", color="k") ax.plot(wind_directions, farm_power_no_wake / 1e3, label="Farm Power No Wakes", color="r") ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") ax.grid(True) ax.legend() ax.set_title("Farm Power") # Plot the percent wake losses ax = axarr[2] percent_wake_losses = 100 * (farm_power_no_wake - farm_power) / farm_power_no_wake ax.plot(wind_directions, percent_wake_losses, label="Percent Wake Losses", color="k") ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Percent Wake Losses") ax.grid(True) ax.legend() ax.set_title("Percent Wake Losses") ###################################################### # Using WindRose ###################################################### # When running FLORIS using a wind rose, that is when a WindRose or WindTIRose object is # passed into the set function. The functions get_expected_farm_power and get_farm_AEP # will operate the same as above, however the functions get_turbine_powers and get_farm_power # will be reshaped from (n_findex, n_turbines) and # (n_findex) to (n_wind_dir, n_wind_speed, n_turbines) # and (n_wind_dir, n_wind_speed) respectively. This is make the powers align more easily with the # provided wind rose. # Declare a WindRose object of 2 wind directions and 3 wind speeds and constant turbulence intensity wind_rose = WindRose( wind_directions=np.array([270.0, 280.0]), wind_speeds=np.array([8.0, 9.0, 10.0]), ti_table=0.06 ) fmodel.set(wind_data=wind_rose) print("==========Wind Rose==========") print(f"Number of conditions to simulate (2 x 3): {fmodel.n_findex}") fmodel.run() turbine_powers = fmodel.get_turbine_powers() print(f"Shape of turbine powers: {turbine_powers.shape}") farm_power = fmodel.get_farm_power() print(f"Shape of farm power: {farm_power.shape}") # Plot the farm power fig, ax = plt.subplots() for w_idx, wd in enumerate(wind_rose.wind_directions): ax.plot(wind_rose.wind_speeds, farm_power[w_idx, :] / 1e3, label=f"WD: {wd}") ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Power (kW)") ax.grid(True) ax.legend() ax.set_title("Farm Power (from Wind Rose)") plt.show() ================================================ FILE: examples/006_get_farm_aep.py ================================================ """Example 6: Getting Expected Power and AEP The expected power of a farm is computed by multiplying the power output of the farm by the frequency of each findex. This is done by the `get_expected_farm_power` method. The expected AEP is annual energy production is computed by multiplying the expected power by the number of hours in a year. If a wind_data object is provided to the model, the expected power and AEP can be computed directly by the`get_farm_AEP_with_wind_data` using the frequency table of the wind data object. If not, a frequency table must be passed into these functions """ import numpy as np from floris import ( FlorisModel, TimeSeries, WindRose, ) fmodel = FlorisModel("inputs/gch.yaml") # Set to a 3-turbine layout D = 126.0 fmodel.set(layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0]) # Using TimeSeries # Randomly generated a time series with time steps = 365 * 24 N = 365 * 24 rng = np.random.default_rng(0) wind_directions = rng.uniform(0, 360, N) wind_speeds = rng.uniform(5, 25, N) # Set up a time series time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06 ) # Set the wind data fmodel.set(wind_data=time_series) # Run the model fmodel.run() expected_farm_power = fmodel.get_expected_farm_power() aep = fmodel.get_farm_AEP() # Note this is equivalent to the following aep_b = fmodel.get_farm_AEP(freq=time_series.unpack_freq()) print(f"AEP from time series: {aep}, and re-computed AEP: {aep_b}") # Using WindRose============================================== # Load the wind rose from csv as in example 003 wind_rose = WindRose.read_csv_long( "inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06 ) # Store some values n_wd = len(wind_rose.wind_directions) n_ws = len(wind_rose.wind_speeds) # Store the number of elements of the freq_table which are 0 n_zeros = np.sum(wind_rose.freq_table == 0) # Set the wind rose fmodel.set(wind_data=wind_rose) # Run the model fmodel.run() # Note that the frequency table contains 0 frequency for some wind directions and wind speeds # and we've not selected to compute 0 frequency bins, therefore the n_findex will be less than # the total number of wind directions and wind speed combinations print(f"Total number of wind direction and wind speed combination: {n_wd * n_ws}") print(f"Number of 0 frequency bins: {n_zeros}") print(f"n_findex: {fmodel.n_findex}") # Get the AEP aep = fmodel.get_farm_AEP() # Print the AEP print(f"AEP from wind rose: {aep/1E9:.3f} (GWh)") # Run the model again, without wakes, and use the result to compute the wake losses fmodel.run_no_wake() # Get the AEP without wake aep_no_wake = fmodel.get_farm_AEP() # Compute the wake losses wake_losses = 100 * (aep_no_wake - aep) / aep_no_wake # Print the wake losses print(f"Wake losses: {wake_losses:.2f}%") ================================================ FILE: examples/007_sweeping_variables.py ================================================ """Example 7: Sweeping Variables Demonstrate methods for sweeping across variables. Wind directions, wind speeds, turbulence intensities, as well as control inputs are passed to set() as arrays and so can be swept and run in one call to run(). The example includes demonstrations of sweeping: 1) Wind speeds 2) Wind directions 3) Turbulence intensities 4) Yaw angles 5) Power setpoints 6) Disabling turbines """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, ) fmodel = FlorisModel("inputs/gch.yaml") # Set to a 2 turbine layout fmodel.set(layout_x=[0.0, 126 * 5], layout_y=[0.0, 0.0]) # Start a figure for the results fig, axarr = plt.subplots(2, 3, figsize=(15, 10), sharey=True) axarr = axarr.flatten() ###################################################### # Sweep wind speeds ###################################################### # The TimeSeries object is the most convenient for sweeping # wind speeds while keeping the wind direction and turbulence # intensity constant wind_speeds = np.arange(5, 10, 0.1) fmodel.set( wind_data=TimeSeries( wind_speeds=wind_speeds, wind_directions=270.0, turbulence_intensities=0.06 ) ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1e3 # Plot the results ax = axarr[0] ax.plot(wind_speeds, turbine_powers[:, 0], label="Upstream Turbine", color="k") ax.plot(wind_speeds, turbine_powers[:, 1], label="Downstream Turbine", color="r") ax.set_ylabel("Power (kW)") ax.set_xlabel("Wind Speed (m/s)") ax.legend() ###################################################### # Sweep wind directions ###################################################### wind_directions = np.arange(250, 290, 1.0) fmodel.set( wind_data=TimeSeries( wind_speeds=8.0, wind_directions=wind_directions, turbulence_intensities=0.06 ) ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1e3 # Plot the results ax = axarr[1] ax.plot(wind_directions, turbine_powers[:, 0], label="Upstream Turbine", color="k") ax.plot(wind_directions, turbine_powers[:, 1], label="Downstream Turbine", color="r") ax.set_xlabel("Wind Direction (deg)") ###################################################### # Sweep turbulence intensities ###################################################### turbulence_intensities = np.arange(0.03, 0.2, 0.01) fmodel.set( wind_data=TimeSeries( wind_speeds=8.0, wind_directions=270.0, turbulence_intensities=turbulence_intensities ) ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1e3 # Plot the results ax = axarr[2] ax.plot(turbulence_intensities, turbine_powers[:, 0], label="Upstream Turbine", color="k") ax.plot(turbulence_intensities, turbine_powers[:, 1], label="Downstream Turbine", color="r") ax.set_xlabel("Turbulence Intensity") ###################################################### # Sweep the upstream yaw angle ###################################################### # First set the conditions to uniform for N yaw_angles n_yaw = 100 wind_directions = np.ones(n_yaw) * 270.0 fmodel.set( wind_data=TimeSeries( wind_speeds=8.0, wind_directions=wind_directions, turbulence_intensities=0.06 ) ) yaw_angles_upstream = np.linspace(-30, 30, n_yaw) yaw_angles = np.zeros((n_yaw, 2)) yaw_angles[:, 0] = yaw_angles_upstream fmodel.set(yaw_angles=yaw_angles) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1e3 # Plot the results ax = axarr[3] ax.plot(yaw_angles_upstream, turbine_powers[:, 0], label="Upstream Turbine", color="k") ax.plot(yaw_angles_upstream, turbine_powers[:, 1], label="Downstream Turbine", color="r") ax.set_xlabel("Upstream Yaw Angle (deg)") ax.set_ylabel("Power (kW)") ###################################################### # Sweep the upstream power rating ###################################################### # Since we're changing control modes, need to reset the operation fmodel.reset_operation() # To the de-rating need to change the power_thrust_mode to mixed or simple de-rating fmodel.set_operation_model("simple-derating") # Sweep the de-rating levels RATED_POWER = 5e6 # For NREL 5MW n_derating_levels = 150 upstream_power_setpoint = np.linspace(0.0, RATED_POWER * 0.5, n_derating_levels) power_setpoints = np.ones((n_derating_levels, 2)) * RATED_POWER power_setpoints[:, 0] = upstream_power_setpoint # Set the wind conditions to fixed wind_directions = np.ones(n_derating_levels) * 270.0 fmodel.set( wind_data=TimeSeries( wind_speeds=8.0, wind_directions=wind_directions, turbulence_intensities=0.06 ) ) # Set the de-rating levels fmodel.set(power_setpoints=power_setpoints) fmodel.run() # Get the turbine powers turbine_powers = fmodel.get_turbine_powers() / 1e3 # Plot the results ax = axarr[4] ax.plot(upstream_power_setpoint / 1e3, turbine_powers[:, 0], label="Upstream Turbine", color="k") ax.plot(upstream_power_setpoint / 1e3, turbine_powers[:, 1], label="Downstream Turbine", color="r") ax.plot( upstream_power_setpoint / 1e3, upstream_power_setpoint / 1e3, label="De-Rating Level", color="b", linestyle="--", ) ax.set_xlabel("Upstream Power Setpoint (kW)") ax.legend() ###################################################### # Sweep through disabling turbine combinations ###################################################### # Reset the control settings fmodel.reset_operation() # Make a list of possible turbine disable combinations disable_combinations = np.array([[False, False], [True, False], [False, True], [True, True]]) n_combinations = disable_combinations.shape[0] # Make a list of strings representing the combinations disable_combination_strings = ["None", "T0", "T1", "T0 & T1"] # Set the wind conditions to fixed wind_directions = np.ones(n_combinations) * 270.0 fmodel.set( wind_data=TimeSeries( wind_speeds=8.0, wind_directions=wind_directions, turbulence_intensities=0.06 ) ) # Assign the disable settings fmodel.set(disable_turbines=disable_combinations) # Run the model fmodel.run() # Get the turbine powers turbine_powers = fmodel.get_turbine_powers() / 1e3 # Plot the results ax = axarr[5] ax.plot(disable_combination_strings, turbine_powers[:, 0], "ks-", label="Upstream Turbine") ax.plot(disable_combination_strings, turbine_powers[:, 1], "ro-", label="Downstream Turbine") ax.set_xlabel("Turbine Disable Combination") for ax in axarr: ax.grid(True) plt.show() ================================================ FILE: examples/008_uncertain_models.py ================================================ """Example 8: Uncertain Models UncertainFlorisModel is a class that adds uncertainty to the inflow wind direction on the FlorisModel class. The UncertainFlorisModel class is interacted with in the same manner as the FlorisModel class is. This example demonstrates how the wind farm power production is calculated with and without uncertainty. Other use cases of UncertainFlorisModel are, e.g., comparing FLORIS to historical SCADA data and robust optimization. For more details on using uncertain models, see further examples within the examples_uncertain directory. """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, UncertainFlorisModel, ) # Instantiate FLORIS FLORIS and UncertainFLORIS models fmodel = FlorisModel("inputs/gch.yaml") # GCH model # The instantiation of the UncertainFlorisModel class is similar to the FlorisModel class # with the addition of the wind direction standard deviation (wd_std) parameter # and certain resolution parameters. Internally, the UncertainFlorisModel class # expands the wind direction time series to include the uncertainty but then # only runs the unique cases. The final result is computed via a gaussian weighting # of the cases according to wd_std. Here we use the default resolution parameters. # wd_resolution=1.0, # Degree # ws_resolution=1.0, # m/s # ti_resolution=0.01, ufmodel_3 = UncertainFlorisModel("inputs/gch.yaml", wd_std=3) ufmodel_5 = UncertainFlorisModel("inputs/gch.yaml", wd_std=5) # Define an inflow where wind direction is swept while # wind speed and turbulence intensity are held constant wind_directions = np.arange(240.0, 300.0, 1.0) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06, ) # Define a two turbine farm and apply the inflow D = 126.0 layout_x = np.array([0, D * 6]) layout_y = [0, 0] fmodel.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) ufmodel_3.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) ufmodel_5.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) # Run both models fmodel.run() ufmodel_3.run() ufmodel_5.run() # Collect the nominal and uncertain farm power turbine_powers_nom = fmodel.get_turbine_powers() / 1e3 turbine_powers_unc_3 = ufmodel_3.get_turbine_powers() / 1e3 turbine_powers_unc_5 = ufmodel_5.get_turbine_powers() / 1e3 farm_powers_nom = fmodel.get_farm_power() / 1e3 farm_powers_unc_3 = ufmodel_3.get_farm_power() / 1e3 farm_powers_unc_5 = ufmodel_5.get_farm_power() / 1e3 # Plot results fig, axarr = plt.subplots(1, 3, figsize=(15, 5)) ax = axarr[0] ax.plot(wind_directions, turbine_powers_nom[:, 0].flatten(), color="k", label="Nominal power") ax.plot( wind_directions, turbine_powers_unc_3[:, 0].flatten(), color="r", label="Power with uncertainty = 3 deg", ) ax.plot( wind_directions, turbine_powers_unc_5[:, 0].flatten(), color="m", label="Power with uncertainty = 5deg", ) ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") ax.set_title("Upstream Turbine") ax = axarr[1] ax.plot(wind_directions, turbine_powers_nom[:, 1].flatten(), color="k", label="Nominal power") ax.plot( wind_directions, turbine_powers_unc_3[:, 1].flatten(), color="r", label="Power with uncertainty = 3 deg", ) ax.plot( wind_directions, turbine_powers_unc_5[:, 1].flatten(), color="m", label="Power with uncertainty = 5 deg", ) ax.set_title("Downstream Turbine") ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") ax = axarr[2] ax.plot(wind_directions, farm_powers_nom.flatten(), color="k", label="Nominal farm power") ax.plot( wind_directions, farm_powers_unc_3.flatten(), color="r", label="Farm power with uncertainty = 3 deg", ) ax.plot( wind_directions, farm_powers_unc_5.flatten(), color="m", label="Farm power with uncertainty = 5 deg", ) ax.set_title("Farm Power") ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") # Compare the AEP calculation freq = np.ones_like(wind_directions) freq = freq / freq.sum() aep_nom = fmodel.get_farm_AEP(freq=freq) aep_unc_3 = ufmodel_3.get_farm_AEP(freq=freq) aep_unc_5 = ufmodel_5.get_farm_AEP(freq=freq) print(f"AEP without uncertainty {aep_nom}") print(f"AEP without uncertainty (3 deg) {aep_unc_3} ({100*aep_unc_3/aep_nom:.2f}%)") print(f"AEP without uncertainty (5 deg) {aep_unc_5} ({100*aep_unc_5/aep_nom:.2f}%)") plt.show() ================================================ FILE: examples/009_parallel_models.py ================================================ """Example 9: Parallel Models This example demonstrates how to use the ParFlorisModel class to parallelize the calculation of the FLORIS model. ParFlorisModel inherits from the FlorisModel and so can be used in the same way with a consistent interface. ParFlorisModel replaces the ParallelFlorisModel, which will be deprecated in a future release. """ import numpy as np from floris import ( FlorisModel, ParFlorisModel, TimeSeries, UncertainFlorisModel, ) # When using parallel optimization it is important the "root" script include this # if __name__ == "__main__": block to avoid problems if __name__ == "__main__": # Instantiate the FlorisModel fmodel = FlorisModel("inputs/gch.yaml") # The ParFlorisModel can be instantiated either from a FlorisModel or from # the input file. pfmodel_1 = ParFlorisModel("inputs/gch.yaml") # Via input file pfmodel_2 = ParFlorisModel(fmodel) # Via FlorisModel # The ParFlorisModel has additional inputs which define the parallelization # but don't affect the output. pfmodel_3 = ParFlorisModel( fmodel, interface="multiprocessing", # Default max_workers=2, # Defaults to num_cpu n_wind_condition_splits=2, # Defaults to max_workers ) # Define a simple inflow time_series = TimeSeries( wind_speeds=np.arange(1, 25, 0.5), wind_directions=270.0, turbulence_intensities=0.06 ) # Demonstrate that interface and results are the same fmodel.set(wind_data=time_series) pfmodel_1.set(wind_data=time_series) pfmodel_2.set(wind_data=time_series) pfmodel_3.set(wind_data=time_series) fmodel.run() pfmodel_1.run() pfmodel_2.run() pfmodel_3.run() # Compare the results powers_fmodel = fmodel.get_turbine_powers() powers_pfmodel_1 = pfmodel_1.get_turbine_powers() powers_pfmodel_2 = pfmodel_2.get_turbine_powers() powers_pfmodel_3 = pfmodel_3.get_turbine_powers() print( f"Testing if outputs of fmodel and pfmodel_1 are " f"close: {np.allclose(powers_fmodel, powers_pfmodel_1)}" ) print( f"Testing if outputs of fmodel and pfmodel_2 are " f"close: {np.allclose(powers_fmodel, powers_pfmodel_2)}" ) print( f"Testing if outputs of fmodel and pfmodel_3 are " f"close: {np.allclose(powers_fmodel, powers_pfmodel_3)}" ) # Because ParFlorisModel is a subclass of FlorisModel, it can also be used as # an input to the UncertainFlorisModel class. This allows for parallelization of # the uncertainty calculations. ufmodel = UncertainFlorisModel(fmodel) pufmodel = UncertainFlorisModel(pfmodel_1) # Demonstrate matched results ufmodel.set(wind_data=time_series) pufmodel.set(wind_data=time_series) ufmodel.run() pufmodel.run() powers_ufmodel = ufmodel.get_turbine_powers() powers_pufmodel = pufmodel.get_turbine_powers() print("--------------------") print( f"Testing if outputs of ufmodel and pufmodel are " f"close: {np.allclose(powers_ufmodel, powers_pufmodel)}" ) ================================================ FILE: examples/010_compare_farm_power_with_neighbor.py ================================================ """Example 10: Compare farm power with neighboring farm This example demonstrates how to use turbine_weights to define a set of turbines belonging to a neighboring farm which impacts the power production of the farm under consideration via wake losses, but whose own power production is not considered in farm power / aep production """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel # Instantiate FLORIS using either the GCH or CC model fmodel = FlorisModel("inputs/gch.yaml") # GCH model matched to the default "legacy_gauss" of V2 # Define a 4 turbine farm turbine farm D = 126.0 layout_x = np.array([0, D * 6, 0, D * 6]) layout_y = [0, 0, D * 3, D * 3] fmodel.set(layout_x=layout_x, layout_y=layout_y) # Define a simple inflow with just 1 wind speed wd_array = np.arange(0, 360, 4.0) ws_array = 8.0 * np.ones_like(wd_array) turbulence_intensities = 0.06 * np.ones_like(wd_array) fmodel.set( wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=turbulence_intensities ) # Calculate fmodel.run() # Collect the farm power farm_power_base = fmodel.get_farm_power() / 1e3 # In kW # Add a neighbor to the east layout_x = np.array([0, D * 6, 0, D * 6, D * 12, D * 15, D * 12, D * 15]) layout_y = np.array([0, 0, D * 3, D * 3, 0, 0, D * 3, D * 3]) fmodel.set(layout_x=layout_x, layout_y=layout_y) # Define the weights to exclude the neighboring farm from calculations of power turbine_weights = np.zeros(len(layout_x), dtype=int) turbine_weights[0:4] = 1.0 # Calculate fmodel.run() # Collect the farm power with the neighbor farm_power_neighbor = fmodel.get_farm_power(turbine_weights=turbine_weights) / 1e3 # In kW # Show the farms fig, ax = plt.subplots() ax.scatter( layout_x[turbine_weights == 1], layout_y[turbine_weights == 1], color="k", label="Base Farm" ) ax.scatter( layout_x[turbine_weights == 0], layout_y[turbine_weights == 0], color="r", label="Neighboring Farm", ) ax.legend() # Plot the power difference fig, ax = plt.subplots() ax.plot(wd_array, farm_power_base, color="k", label="Farm Power (no neighbor)") ax.plot(wd_array, farm_power_neighbor, color="r", label="Farm Power (neighboring farm due east)") ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") plt.show() ================================================ FILE: examples/_convert_examples_to_notebooks.py ================================================ """ Utility script to convert all Python scripts in the current directory to Jupyter notebooks. """ import os import nbformat as nbf def script_to_notebook(script_path, notebook_path): # Read Python script with open(script_path, "r") as f: python_code = f.read() # Clear out leading whitespace python_code = python_code.strip() # Append to the bottom of the code suppression of warnings python_code += """ import warnings warnings.filterwarnings('ignore') """ # Create a new Jupyter notebook nb = nbf.v4.new_notebook() # The first line of code it the title, copy it, remove and # leading quotes or comments and make it a markdown cell with one hash title = python_code.split("\n")[0].strip().strip("#").strip().strip('"').strip().strip("'") nb["cells"].append(nbf.v4.new_markdown_cell(f"# {title}")) # # Every code block starts with a comment block surrounded by """ and ends with """ # # Find that block and place it in markdown cell # code_comments = python_code.split('"""')[1] # # Remove the top line # code_comments = code_comments.split("\n")[1:] # # Add the code comments # nb["cells"].append(nbf.v4.new_markdown_cell(code_comments)) # # Add Python code to the notebook # # Remove the top commented block ("""...""") but keep everything after it # python_code = python_code.split('"""')[2] # Strip any leading white space python_code = python_code.strip() nb["cells"].append(nbf.v4.new_code_cell(python_code)) # Write the notebook to a file with open(notebook_path, "w") as f: nbf.write(nb, f) # Traverse the current directory and subdirectories to find # all python scripts that start with a number # and end with .py and make a list of all such scripts including relative path scripts = sorted( [ os.path.join(dp, f) for dp, dn, filenames in os.walk(".") for f in filenames if f.endswith(".py") and f[0].isdigit() ] ) # For each Python script, convert it to a Jupyter notebook notebook_directories = [] notebook_filenames = [] for script_path in scripts: print(f"Converting {script_path} to Notebook...") notebook_path = script_path.replace(".py", ".ipynb") notebook_directories.append(os.path.dirname(notebook_path)) notebook_filenames.append(os.path.basename(notebook_path)) script_to_notebook(script_path, notebook_path) # Make a dictionary of all the notebooks, whose keys are # unique entries in the notebook_directories list # and values are lists of notebook filenames in that directory notebooks = {k: [] for k in notebook_directories} for i, directory in enumerate(notebook_directories): notebooks[directory].append(notebook_filenames[i]) print(notebooks) # Now read in the _toc.yaml file one level up and add each of the note books to a new chapter # called examples and re-write the _toc.yaml file toc_path = "../_toc.yml" # Load the toc file as a file with open(toc_path, "r") as f: toc = f.read() # Append a blank line and then " - caption: Developer Reference" to the toc toc += "\n - caption: Examples\n chapters:\n" # For each entry in the '.' directory, add it to the toc as a file for nb in notebooks["."]: toc += f" - file: examples/{nb}\n" # For the remaining keys in the notebooks dictionary, first add a section for the directory # and then add the notebooks in that directory as a file for directory in notebooks: if directory == ".": continue dir_without_dot_slash = directory[2:] dir_without_examples_ = dir_without_dot_slash.replace("examples_", "") dir_without_examples_ = dir_without_examples_.replace("_", " ").capitalize() toc += f"\n - caption: Examples - {dir_without_examples_}\n chapters:\n" for nb in notebooks[directory]: toc += f" - file: examples/{dir_without_dot_slash}/{nb}\n" # Print the toc print("\n\nTOC: FILE:\n") print(toc) # Save the toc with open(toc_path, "w") as f: f.write(toc) ================================================ FILE: examples/examples_control_optimization/001_opt_yaw_single_ws.py ================================================ """Example: Optimize yaw for a single wind speed and multiple wind directions Use the serial-refine method to optimize the yaw angles for a 3-turbine wind farm """ import matplotlib.pyplot as plt import numpy as np import floris.flow_visualization as flowviz import floris.layout_visualization as layoutviz from floris import FlorisModel, TimeSeries from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # Load the default example floris object fmodel = FlorisModel("../inputs/gch.yaml") # Define an inflow that # keeps wind speed and TI constant while sweeping the wind directions wind_directions = np.arange(0.0, 360.0, 3.0) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06, ) # Reinitialize as a 3-turbine using the above inflow D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_data=time_series, ) # Initialize optimizer object and run optimization using the Serial-Refine method yaw_opt = YawOptimizationSR(fmodel) df_opt = yaw_opt.optimize() print("Optimization results:") print(df_opt) # Split out the turbine results for t in range(3): df_opt['t%d' % t] = df_opt.yaw_angles_opt.apply(lambda x: x[t]) # Show the results fig, axarr = plt.subplots(2,1,sharex=True,sharey=False,figsize=(8,8)) # Yaw results ax = axarr[0] for t in range(3): ax.plot(df_opt.wind_direction,df_opt['t%d' % t],label='t%d' % t) ax.set_ylabel('Yaw Offset (deg') ax.legend() ax.grid(True) # Power results ax = axarr[1] ax.plot(df_opt.wind_direction,df_opt.farm_power_baseline,color='k',label='Baseline Farm Power') ax.plot(df_opt.wind_direction,df_opt.farm_power_opt,color='r',label='Optimized Farm Power') ax.set_ylabel('Power (W)') ax.set_xlabel('Wind Direction (deg)') ax.legend() ax.grid(True) # Visualize results for a single wind direction (270 deg) and wind speed (8 m/s) fig, axarr = plt.subplots(2, 1, figsize=(10, 5), sharex=False) ax = axarr[0] # Baseline aligned operation fmodel.reset_operation() fmodel.set(wind_directions=[270.0], wind_speeds=[8.0], turbulence_intensities=[0.06]) fmodel.run() horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0) flowviz.visualize_cut_plane(horizontal_plane, ax=ax) layoutviz.plot_turbine_rotors(fmodel, ax=ax) ax.set_title("Turbines aligned") ax = axarr[1] # Optimized yaw angles optimal_yaw_angles = ( df_opt[(df_opt["wind_direction"] == 270.0) & (df_opt["wind_speed"] == 8.0)] .yaw_angles_opt.values[0] ).reshape(1,-1) fmodel.set(yaw_angles=optimal_yaw_angles) fmodel.run() horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0) flowviz.visualize_cut_plane(horizontal_plane, ax=ax) layoutviz.plot_turbine_rotors(fmodel, ax=ax, yaw_angles=optimal_yaw_angles) ax.set_title("Optimized yaw angles") plt.show() ================================================ FILE: examples/examples_control_optimization/002_opt_yaw_single_ws_uncertain.py ================================================ """Example: Optimize yaw for a single wind speed and multiple wind directions. Compare certain and uncertain results. Use the serial-refine method to optimize the yaw angles for a 3-turbine wind farm. In one case use the FlorisModel without uncertainty and in the other use the UncertainFlorisModel with a wind direction standard deviation of 3 degrees. Compare the results. """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, UncertainFlorisModel, ) from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # Load the floris model and uncertain floris model fmodel = FlorisModel("../inputs/gch.yaml") ufmodel = UncertainFlorisModel("../inputs/gch.yaml", wd_std=3) # Define an inflow that # keeps wind speed and TI constant while sweeping the wind directions wind_directions = np.arange(250, 290.0, 1.0) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06, ) # Reinitialize as a 3-turbine using the above inflow D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_data=time_series, ) ufmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_data=time_series, ) # Initialize optimizer object and run optimization using the Serial-Refine method print("++++++++++CERTAIN++++++++++++") yaw_opt = YawOptimizationSR(fmodel) df_opt = yaw_opt.optimize() # Repeat with uncertain model print("++++++++++UNCERTAIN++++++++++++") yaw_opt_u = YawOptimizationSR(ufmodel) df_opt_uncertain = yaw_opt_u.optimize() # Split out the turbine results for t in range(3): df_opt["t%d" % t] = df_opt.yaw_angles_opt.apply(lambda x: x[t]) df_opt_uncertain["t%d" % t] = df_opt_uncertain.yaw_angles_opt.apply(lambda x: x[t]) # Show the yaw and turbine results fig, axarr = plt.subplots(3, sharex=True, sharey=False, figsize=(15, 8)) # Yaw results for tindex in range(3): ax = axarr[tindex] ax.plot( df_opt.wind_direction, df_opt["t%d" % tindex], label="FlorisModel", color="k", marker="o" ) ax.plot( df_opt_uncertain.wind_direction, df_opt_uncertain["t%d" % tindex], label="UncertainFlorisModel", color="r", marker="x", ) ax.set_ylabel("Yaw Offset T{0:03d} (deg)".format(tindex)) ax.legend() ax.grid(True) # Power results fig, axarr = plt.subplots(1, 2, figsize=(15, 5), sharex=True, sharey=True) ax = axarr[0] ax.plot(df_opt.wind_direction, df_opt.farm_power_baseline, color="k", label="Baseline Farm Power") ax.plot(df_opt.wind_direction, df_opt.farm_power_opt, color="r", label="Optimized Farm Power") ax.set_ylabel("Power (W)") ax.set_xlabel("Wind Direction (deg)") ax.legend() ax.grid(True) ax.set_title("Certain") ax = axarr[1] ax.plot( df_opt_uncertain.wind_direction, df_opt_uncertain.farm_power_baseline, color="k", label="Baseline Farm Power", ) ax.plot( df_opt_uncertain.wind_direction, df_opt_uncertain.farm_power_opt, color="r", label="Optimized Farm Power", ) ax.set_xlabel("Wind Direction (deg)") ax.grid(True) ax.set_title("Uncertain") plt.show() ================================================ FILE: examples/examples_control_optimization/003_opt_yaw_multiple_ws.py ================================================ """Example: Optimize yaw for multiple wind directions and multiple wind speeds. This example demonstrates how to perform a yaw optimization for multiple wind directions and multiple wind speeds using the WindRose object First, we initialize our Floris Interface, and then generate a 3 turbine wind farm. Next, we create the yaw optimization object `yaw_opt` and perform the optimization using the SerialRefine method. Finally, we plot the results. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, WindRose from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # Load the default example floris object fmodel = FlorisModel("../inputs/gch.yaml") # GCH model matched to the default "legacy_gauss" of V2 # fmodel = FlorisModel("inputs/cc.yaml") # New CumulativeCurl model # Define a WindRose object with uniform TI and frequency table wind_rose = WindRose( wind_directions=np.arange(0.0, 360.0, 3.0), wind_speeds=np.arange(2.0, 18.0, 1.0), ti_table=0.06, ) # Reinitialize as a 3-turbine farm with range of WDs and WSs D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_data=wind_rose, ) # Initialize optimizer object and run optimization using the Serial-Refine method # Now, we enable the verify_convergence option. This function is useful to prevent # yaw misalignment that increases the wind farm power production by a negligible # amount. For example, at high wind speeds (e.g., 16 m/s), a turbine might yaw # by a substantial amount to increase the power production by less than 1 W. This # is typically the result of numerical imprecision of the power coefficient curve, # which slightly differs for different above-rated wind speeds. The option # verify_convergence therefore refines and validates the yaw angle choices # but has no effect on the predicted power uplift from wake steering. # Hence, it should mostly be used when actually synthesizing a practicable # wind farm controller. yaw_opt = YawOptimizationSR(fmodel) df_opt = yaw_opt.optimize() print("Optimization results:") print(df_opt) # Split out the turbine results for t in range(3): df_opt['t%d' % t] = df_opt.yaw_angles_opt.apply(lambda x: x[t]) # Show the results: optimal yaw angles fig, axarr = plt.subplots( nrows=4, ncols=4, sharex=True, sharey=True, figsize=(10, 8) ) jj = 0 for ii, ws in enumerate(np.unique(fmodel.wind_speeds)): xi = np.remainder(ii, 4) if ((ii > 0) & (xi == 0)): jj += 1 ax = axarr[np.remainder(ii, 4)][jj] ids = (df_opt.wind_speed == ws) wd = df_opt.loc[ids, "wind_direction"] for t in range(3): yaw_opt = df_opt.loc[ids, "t{:d}".format(t)] ax.plot(wd, yaw_opt, label='Turbine {:d}'.format(t)) ax.set_title("Wind speed = {:.1f} m/s".format(ws), size=10) if ((ii == 0) & (jj == 0)): ax.legend() ax.grid(True) if jj == 0: ax.set_ylabel('Yaw angle (deg)', size=10) if xi == 3: axarr[xi][jj].set_xlabel('Wind Direction (deg)', size=10) plt.tight_layout() # Show the results: baseline and optimized farm power fig, axarr = plt.subplots( nrows=4, ncols=4, sharex=True, sharey=True, figsize=(10, 8) ) jj = 0 for ii, ws in enumerate(np.unique(fmodel.wind_speeds)): xi = np.remainder(ii, 4) if ((ii > 0) & (xi == 0)): jj += 1 ax = axarr[np.remainder(ii, 4)][jj] ids = (df_opt.wind_speed == ws) wd = df_opt.loc[ids, "wind_direction"] power_baseline = df_opt.loc[ids, "farm_power_baseline"] power_opt = df_opt.loc[ids, "farm_power_opt"] ax.plot(wd, power_baseline / 1e6, color='k', label='Baseline') ax.plot(wd, power_opt / 1e6, color='r', label='Optimized') ax.set_title("Wind speed = {:.1f} m/s".format(ws), size=10) ax.set_ylim([0.0, 16.0]) if ((ii == 0) & (jj == 0)): ax.legend() ax.grid(True) if jj == 0: ax.set_ylabel('Farm Power (MW)', size=10) if xi == 3: axarr[xi][jj].set_xlabel('Wind Direction (deg)', size=10) plt.tight_layout() plt.show() ================================================ FILE: examples/examples_control_optimization/004_optimize_yaw_aep.py ================================================ """Example: Optimize yaw and compare AEP This example demonstrates how to perform a yaw optimization and evaluate the performance over a full wind rose. The script performs the following steps: 1. Load a wind rose from a csv file 2. Calculates the optimal yaw angles for a wind speed of 8 m/s across the directions 3. Applies the optimal yaw angles to the wind rose and calculates the AEP """ from time import perf_counter as timerpc import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, WindRose, ) from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # Load the wind rose from csv wind_rose = WindRose.read_csv_long( "../inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06 ) # Load FLORIS fmodel = FlorisModel("../inputs/gch.yaml") # Specify wind farm layout and update in the floris object N = 2 # number of turbines per row and per column X, Y = np.meshgrid( 5.0 * fmodel.core.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), 5.0 * fmodel.core.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), ) fmodel.set(layout_x=X.flatten(), layout_y=Y.flatten()) # Get the number of turbines n_turbines = len(fmodel.layout_x) # Optimize the yaw angles. This could be done for every wind direction and wind speed # but in practice it is much faster to optimize only for one speed and infer the rest # using a rule of thumb time_series = TimeSeries( wind_directions=wind_rose.wind_directions, wind_speeds=8.0, turbulence_intensities=0.06 ) fmodel.set(wind_data=time_series) # Get the optimal angles start_time = timerpc() yaw_opt = YawOptimizationSR( fmodel=fmodel, minimum_yaw_angle=0.0, # Allowable yaw angles lower bound maximum_yaw_angle=20.0, # Allowable yaw angles upper bound Ny_passes=[5, 4], exclude_downstream_turbines=True, ) df_opt = yaw_opt.optimize() end_time = timerpc() t_tot = end_time - start_time print(f"Optimization finished in {t_tot:.2f} seconds.") # Calculate the AEP in the baseline case fmodel.set(wind_data=wind_rose) fmodel.run() farm_power_baseline = fmodel.get_farm_power() aep_baseline = fmodel.get_farm_AEP() # Now need to apply the optimal yaw angles to the wind rose to get the optimized AEP # do this by applying a rule of thumb where the optimal yaw is applied between 6 and 12 m/s # and ramped down to 0 above and below this range # Grab wind speeds and wind directions from the fmodel. Note that we do this because the # yaw angles will need to be n_findex long, and accounting for the fact that some wind # directions and wind speeds may not be present in the wind rose (0 frequency) and aren't # included in the fmodel wind_directions = fmodel.wind_directions wind_speeds = fmodel.wind_speeds n_findex = fmodel.n_findex # Now define how the optimal yaw angles for 8 m/s are applied over the other wind speeds yaw_angles_opt = np.vstack(df_opt["yaw_angles_opt"]) yaw_angles_wind_rose = np.zeros((n_findex, n_turbines)) for i in range(n_findex): wind_speed = wind_speeds[i] wind_direction = wind_directions[i] # Interpolate the optimal yaw angles for this wind direction from df_opt id_opt = df_opt["wind_direction"] == wind_direction yaw_opt_full = np.array(df_opt.loc[id_opt, "yaw_angles_opt"])[0] # Now decide what to do for different wind speeds wind_speed_low_no_steer = 4.0 wind_speed_high_no_steer = 14.0 wind_speed_low_steer = 6.0 wind_speed_high_steer = 12.0 if (wind_speed < wind_speed_low_no_steer) | (wind_speed > wind_speed_high_no_steer): yaw_opt = np.zeros(n_turbines) # do nothing for very low/high speeds elif wind_speed < wind_speed_low_steer: yaw_opt = ( yaw_opt_full * (wind_speed_low_steer - wind_speed) / (wind_speed_low_steer - wind_speed_low_no_steer) ) # Linear ramp up elif wind_speed > wind_speed_high_steer: yaw_opt = ( yaw_opt_full * (wind_speed_high_no_steer - wind_speed) / (wind_speed_high_no_steer - wind_speed_high_steer) ) # Linear ramp down else: yaw_opt = yaw_opt_full # Apply full offsets between 6.0 and 12.0 m/s # Save to collective array yaw_angles_wind_rose[i, :] = yaw_opt # Now apply the optimal yaw angles and get the AEP fmodel.set(yaw_angles=yaw_angles_wind_rose) fmodel.run() aep_opt = fmodel.get_farm_AEP() aep_uplift = 100.0 * (aep_opt / aep_baseline - 1) farm_power_opt = fmodel.get_farm_power() print(f"Baseline AEP: {aep_baseline / 1e9:.2f} GWh.") print(f"Optimal AEP: {aep_opt / 1e9:.2f} GWh.") print(f"Relative AEP uplift by wake steering: {aep_uplift:.3f} %.") # Use farm_power_baseline, farm_power_opt and wind_data to make a heat map of uplift by # wind direction and wind speed wind_directions = wind_rose.wind_directions wind_speeds = wind_rose.wind_speeds relative_gain = farm_power_opt - farm_power_baseline # Plot the heatmap with wind speeds on x, wind directions on y and relative gain as the color fig, ax = plt.subplots(figsize=(10, 12)) cax = ax.imshow(relative_gain, cmap="viridis", aspect="auto") fig.colorbar(cax, ax=ax, label="Relative gain (%)") ax.set_yticks(np.arange(len(wind_directions))) ax.set_yticklabels(wind_directions) ax.set_xticks(np.arange(len(wind_speeds))) ax.set_xticklabels(wind_speeds) ax.set_ylabel("Wind direction (deg)") ax.set_xlabel("Wind speed (m/s)") # Reduce x and y tick font size for tick in ax.yaxis.get_major_ticks(): tick.label1.set_fontsize(8) for tick in ax.xaxis.get_major_ticks(): tick.label1.set_fontsize(8) # Set y ticks to be horizontal for tick in ax.get_yticklabels(): tick.set_rotation(0) ax.set_title("Uplift in farm power by wind direction and wind speed", fontsize=12) plt.tight_layout() plt.show() ================================================ FILE: examples/examples_control_optimization/005_optimize_yaw_aep_parallel.py ================================================ """Example: Optimize yaw and compare AEP in parallel This example demonstrates how to perform a yaw optimization and evaluate the performance over a full wind rose. The example repeats the steps in 04 except using parallel optimization and evaluation. Note that constraints on parallelized operations mean that some syntax is different and not all operations are possible. Also, rather passing the ParallelFlorisModel object to a YawOptimizationSR object, the optimization is performed directly by member functions """ from time import perf_counter as timerpc import numpy as np from floris import ( ParFlorisModel, TimeSeries, WindRose, ) from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # When using parallel optimization it is important the "root" script include this # if __name__ == "__main__": block to avoid problems if __name__ == "__main__": # Load the wind rose from csv wind_rose = WindRose.read_csv_long( "../inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06 ) # Load FLORIS as a parallel model max_workers = 16 pfmodel = ParFlorisModel( "../inputs/gch.yaml", max_workers=max_workers, n_wind_condition_splits=max_workers, interface="pathos", print_timings=True, ) # Specify wind farm layout and update in the floris object N = 2 # number of turbines per row and per column X, Y = np.meshgrid( 5.0 * pfmodel.core.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), 5.0 * pfmodel.core.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), ) pfmodel.set(layout_x=X.flatten(), layout_y=Y.flatten()) # Get the number of turbines n_turbines = len(pfmodel.layout_x) # Optimize the yaw angles. This could be done for every wind direction and wind speed # but in practice it is much faster to optimize only for one speed and infer the rest # using a rule of thumb time_series = TimeSeries( wind_directions=wind_rose.wind_directions, wind_speeds=8.0, turbulence_intensities=0.06 ) pfmodel.set(wind_data=time_series) start_time = timerpc() yaw_opt = YawOptimizationSR( fmodel=pfmodel, minimum_yaw_angle=0.0, # Allowable yaw angles lower bound maximum_yaw_angle=20.0, # Allowable yaw angles upper bound Ny_passes=[5, 4], exclude_downstream_turbines=True, ) df_opt = yaw_opt.optimize() end_time = timerpc() t_tot = end_time - start_time print("Optimization finished in {:.2f} seconds.".format(t_tot)) # Calculate the AEP in the baseline case, using the parallel interface pfmodel.set(wind_data=wind_rose) pfmodel.run() aep_baseline = pfmodel.get_farm_AEP() # Now need to apply the optimal yaw angles to the wind rose to get the optimized AEP # do this by applying a rule of thumb where the optimal yaw is applied between 6 and 12 m/s # and ramped down to 0 above and below this range # Grab wind speeds and wind directions from the fmodel. Note that we do this because the # yaw angles will need to be n_findex long, and accounting for the fact that some wind # directions and wind speeds may not be present in the wind rose (0 frequency) and aren't # included in the fmodel # TODO: add operation wind rose to example, once built wind_directions = pfmodel.wind_directions wind_speeds = pfmodel.wind_speeds n_findex = pfmodel.n_findex # Now define how the optimal yaw angles for 8 m/s are applied over the other wind speeds yaw_angles_opt = np.vstack(df_opt["yaw_angles_opt"]) yaw_angles_wind_rose = np.zeros((n_findex, n_turbines)) for i in range(n_findex): wind_speed = wind_speeds[i] wind_direction = wind_directions[i] # Interpolate the optimal yaw angles for this wind direction from df_opt id_opt = df_opt["wind_direction"] == wind_direction yaw_opt_full = np.array(df_opt.loc[id_opt, "yaw_angles_opt"])[0] # Now decide what to do for different wind speeds if (wind_speed < 4.0) | (wind_speed > 14.0): yaw_opt = np.zeros(n_turbines) # do nothing for very low/high speeds elif wind_speed < 6.0: yaw_opt = yaw_opt_full * (6.0 - wind_speed) / 2.0 # Linear ramp up elif wind_speed > 12.0: yaw_opt = yaw_opt_full * (14.0 - wind_speed) / 2.0 # Linear ramp down else: yaw_opt = yaw_opt_full # Apply full offsets between 6.0 and 12.0 m/s # Save to collective array yaw_angles_wind_rose[i, :] = yaw_opt # Now apply the optimal yaw angles and get the AEP pfmodel.set(yaw_angles=yaw_angles_wind_rose) pfmodel.run() aep_opt = pfmodel.get_farm_AEP() aep_uplift = 100.0 * (aep_opt / aep_baseline - 1) print("Baseline AEP: {:.2f} GWh.".format(aep_baseline/1E9)) print("Optimal AEP: {:.2f} GWh.".format(aep_opt/1E9)) print("Relative AEP uplift by wake steering: {:.3f} %.".format(aep_uplift)) ================================================ FILE: examples/examples_control_optimization/006_compare_yaw_optimizers.py ================================================ """Example: Compare yaw optimizers This example compares the SciPy-based yaw optimizer with the Serial-Refine optimizer and geometric optimizer. First, we initialize Floris, and then generate a 3 turbine wind farm. Next, we create two yaw optimization objects, `yaw_opt_sr` and `yaw_opt_scipy` for the Serial-Refine and SciPy methods, respectively. We then perform the optimization using both methods. Finally, we compare the time it took to find the optimal angles and plot the optimal yaw angles and resulting wind farm powers. The example now also compares the Geometric Yaw optimizer, which is fast a method to find approximately optimal yaw angles based on the wind farm geometry. Its main use case is for coupled layout and yaw optimization. see floris.optimization.yaw_optimization.yaw_optimizer_geometric.py and the paper online at https://wes.copernicus.org/preprints/wes-2023-1/. See also example 16c. """ from time import perf_counter as timerpc import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel from floris.optimization.yaw_optimization.yaw_optimizer_geometric import ( YawOptimizationGeometric, ) from floris.optimization.yaw_optimization.yaw_optimizer_scipy import YawOptimizationScipy from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # Load the default example floris object fmodel = FlorisModel("../inputs/gch.yaml") # Reinitialize as a 3-turbine farm with range of WDs and 1 WS D = 126.0 # Rotor diameter for the NREL 5 MW wd_array = np.arange(0.0, 360.0, 3.0) ws_array = 8.0 * np.ones_like(wd_array) turbulence_intensities = 0.06 * np.ones_like(wd_array) fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=turbulence_intensities, ) print("Performing optimizations with SciPy...") start_time = timerpc() yaw_opt_scipy = YawOptimizationScipy(fmodel) df_opt_scipy = yaw_opt_scipy.optimize() time_scipy = timerpc() - start_time print("Performing optimizations with Serial Refine...") start_time = timerpc() yaw_opt_sr = YawOptimizationSR(fmodel) df_opt_sr = yaw_opt_sr.optimize() time_sr = timerpc() - start_time print("Performing optimizations with Geometric Yaw...") start_time = timerpc() yaw_opt_geo = YawOptimizationGeometric(fmodel) df_opt_geo = yaw_opt_geo.optimize() time_geo = timerpc() - start_time # Print time spent print("\n Time spent, Geometric Yaw: {:.2f} s.".format(time_geo)) print(" Time spent, Serial Refine: {:.2f} s.".format(time_sr)) print(" Time spent, SciPy (SLSQP): {:.2f} s.\n".format(time_scipy)) # Split out the turbine results yaw_angles_opt_geo = np.vstack(df_opt_geo.yaw_angles_opt) yaw_angles_opt_sr = np.vstack(df_opt_sr.yaw_angles_opt) yaw_angles_opt_scipy = np.vstack(df_opt_scipy.yaw_angles_opt) # Yaw results for t in range(3): fig, ax = plt.subplots() ax.plot(df_opt_geo.wind_direction, yaw_angles_opt_geo[:, t],color='m',label='Geometric') ax.plot(df_opt_sr.wind_direction, yaw_angles_opt_sr[:, t],color='r',label='Serial Refine') ax.plot(df_opt_scipy.wind_direction, yaw_angles_opt_scipy[:, t],'--', color='g', label='SciPy') ax.grid(True) ax.set_ylabel('Yaw Offset (deg') ax.legend() ax.grid(True) ax.set_title("Turbine {:d}".format(t)) # Power results ============== # Before plotting results, need to compute values for GEOOPT since it doesn't compute # power within the optimization fmodel.set(yaw_angles=yaw_angles_opt_geo) fmodel.run() geo_farm_power = fmodel.get_farm_power().squeeze() fig, ax = plt.subplots() ax.plot( df_opt_sr.wind_direction, df_opt_sr.farm_power_baseline, color='k', label='Baseline' ) ax.plot( df_opt_geo.wind_direction, geo_farm_power, color='m', label='Optimized, Gemoetric' ) ax.plot( df_opt_sr.wind_direction, df_opt_sr.farm_power_opt, color='r', label='Optimized, Serial Refine' ) ax.plot( df_opt_scipy.wind_direction, df_opt_scipy.farm_power_opt, '--', color='g', label='Optimized, SciPy' ) ax.set_ylabel('Wind Farm Power (W)') ax.set_xlabel('Wind Direction (deg)') ax.legend() ax.grid(True) # Finally, compare the overall the power gains fig, ax = plt.subplots() ax.plot( df_opt_geo.wind_direction, geo_farm_power - df_opt_sr.farm_power_baseline, color='m', label='Optimized, Gemoetric' ) ax.plot( df_opt_sr.wind_direction, df_opt_sr.farm_power_opt - df_opt_sr.farm_power_baseline, color='r', label='Optimized, Serial Refine' ) ax.plot( df_opt_scipy.wind_direction, df_opt_scipy.farm_power_opt - df_opt_scipy.farm_power_baseline, '--', color='g', label='Optimized, SciPy' ) ax.set_ylabel('Increase in Wind Farm Power (W)') ax.set_xlabel('Wind Direction (deg)') ax.legend() ax.grid(True) # Finally, make a quick bar plot comparing nomimal power and nomimal uplift total_power_uplift_geo = np.sum(geo_farm_power - df_opt_sr.farm_power_baseline) total_power_uplift_sr = np.sum(df_opt_sr.farm_power_opt - df_opt_sr.farm_power_baseline) total_power_uplift_scipy = np.sum(df_opt_scipy.farm_power_opt - df_opt_scipy.farm_power_baseline) # Plot on the left subplot a barplot comparing the uplift normalized to scipy and on the right # subplot a barplot of total time normalzed to scipy fig, axarr = plt.subplots(1,2,figsize=(10,5)) ax = axarr[0] ax.bar( [0, 1, 2], [ total_power_uplift_geo / total_power_uplift_scipy, total_power_uplift_sr / total_power_uplift_scipy, 1.0, ], color=['m', 'r', 'g'], ) ax.set_xticks([0, 1, 2]) ax.set_xticklabels(['Geometric', 'Serial Refine', 'SciPy']) ax.set_ylabel('Normalized Power Gain') ax.grid(True) ax = axarr[1] ax.bar( [0, 1, 2], [ time_geo / time_scipy, time_sr / time_scipy, 1.0, ], color=['m', 'r', 'g'], ) ax.set_xticks([0, 1, 2]) ax.set_xticklabels(['Geometric', 'Serial Refine', 'SciPy']) ax.set_ylabel('Normalized Computation Time') ax.grid(True) # Change to semi-logy axarr[1].set_yscale('log') plt.show() ================================================ FILE: examples/examples_control_optimization/007_optimize_yaw_with_neighbor_farms.py ================================================ """Example: Optimize yaw with neighbor farm This example demonstrates how to optimize the yaw angles of a subset of turbines in order to maximize the annual energy production (AEP) of a wind farm. In this case, the wind farm is part of a larger collection of turbines, some of which are part of a neighboring farm. The optimization is performed in two ways: first by accounting for the wakes of the neighboring farm (while not including those turbines) in the optimization as a target of yaw angle changes or including their power in the objective function. In the second method the neighboring farms are removed from FLORIS for the optimization. The AEP is then calculated for the optimized yaw angles (accounting for and not accounting for the neighboring farm) and compared to the baseline AEP. """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, WindRose, ) from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # Load the wind rose from csv wind_rose = WindRose.read_csv_long( "../inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06 ) # Load FLORIS fmodel = FlorisModel("../inputs/gch.yaml") # Specify a layout of turbines in which only the first 10 turbines are part # of the farm to be optimized, while the others belong to a neighboring farm X = ( np.array( [ 0.0, 756.0, 1512.0, 2268.0, 3024.0, 0.0, 756.0, 1512.0, 2268.0, 3024.0, 0.0, 756.0, 1512.0, 2268.0, 3024.0, 0.0, 756.0, 1512.0, 2268.0, 3024.0, 4500.0, 5264.0, 6028.0, 4878.0, 0.0, 756.0, 1512.0, 2268.0, 3024.0, ] ) / 1.5 ) Y = ( np.array( [ 0.0, 0.0, 0.0, 0.0, 0.0, 504.0, 504.0, 504.0, 504.0, 504.0, 1008.0, 1008.0, 1008.0, 1008.0, 1008.0, 1512.0, 1512.0, 1512.0, 1512.0, 1512.0, 4500.0, 4059.0, 3618.0, 5155.0, -504.0, -504.0, -504.0, -504.0, -504.0, ] ) / 1.5 ) # Turbine weights: we want to only optimize for the first 10 turbines turbine_weights = np.zeros(len(X), dtype=int) turbine_weights[0:10] = 1.0 # Now reinitialize FLORIS layout fmodel.set(layout_x=X, layout_y=Y) # And visualize the floris layout fig, ax = plt.subplots() ax.plot(X[turbine_weights == 0], Y[turbine_weights == 0], "ro", label="Neighboring farms") ax.plot(X[turbine_weights == 1], Y[turbine_weights == 1], "go", label="Farm subset") ax.grid(True) ax.set_xlabel("x coordinate (m)") ax.set_ylabel("y coordinate (m)") ax.legend() # Indicate turbine 0 in the plot above with an annotation arrow ax.annotate( "Turbine 0", (X[0], Y[0]), xytext=(X[0] + 100, Y[0] + 100), arrowprops={"facecolor": "black", "shrink": 0.05}, ) # Optimize the yaw angles. This could be done for every wind direction and wind speed # but in practice it is much faster to optimize only for one speed and infer the rest # using a rule of thumb time_series = TimeSeries( wind_directions=wind_rose.wind_directions, wind_speeds=8.0, turbulence_intensities=0.06 ) fmodel.set(wind_data=time_series) # CASE 1: Optimize the yaw angles of the included farm while accounting for the # wake effects of the neighboring farm by using turbine weights # It's important here to do two things: # 1. Exclude the downstream turbines from the power optimization goal via # turbine_weights # 2. Prevent the optimizer from changing the yaw angles of the turbines in the # neighboring farm by limiting the yaw angles min max both to 0 # Set the yaw angles max min according to point(2) above minimum_yaw_angle = np.zeros( ( fmodel.n_findex, fmodel.n_turbines, ) ) maximum_yaw_angle = np.zeros( ( fmodel.n_findex, fmodel.n_turbines, ) ) maximum_yaw_angle[:, :10] = 30.0 yaw_opt = YawOptimizationSR( fmodel=fmodel, minimum_yaw_angle=minimum_yaw_angle, # Allowable yaw angles lower bound maximum_yaw_angle=maximum_yaw_angle, # Allowable yaw angles upper bound Ny_passes=[5, 4], exclude_downstream_turbines=True, turbine_weights=turbine_weights, ) df_opt_with_neighbor = yaw_opt.optimize() # CASE 2: Repeat the optimization, this time ignoring the wakes of the neighboring farm # by limiting the FLORIS model to only the turbines in the farm to be optimized f_model_subset = fmodel.copy() f_model_subset.set( layout_x=X[:10], layout_y=Y[:10], ) yaw_opt = YawOptimizationSR( fmodel=f_model_subset, minimum_yaw_angle=0, # Allowable yaw angles lower bound maximum_yaw_angle=30, # Allowable yaw angles upper bound Ny_passes=[5, 4], exclude_downstream_turbines=True, ) df_opt_without_neighbor = yaw_opt.optimize() # Calculate the AEP in the baseline case # Use turbine weights again to only consider the first 10 turbines power fmodel.set(wind_data=wind_rose) fmodel.run() farm_power_baseline = fmodel.get_farm_power(turbine_weights=turbine_weights) aep_baseline = fmodel.get_farm_AEP(turbine_weights=turbine_weights) # Now need to apply the optimal yaw angles to the wind rose to get the optimized AEP # do this by applying a rule of thumb where the optimal yaw is applied between 6 and 12 m/s # and ramped down to 0 above and below this range # Grab wind speeds and wind directions from the fmodel. Note that we do this because the # yaw angles will need to be n_findex long, and accounting for the fact that some wind # directions and wind speeds may not be present in the wind rose (0 frequency) and aren't # included in the fmodel wind_directions = fmodel.wind_directions wind_speeds = fmodel.wind_speeds n_findex = fmodel.n_findex yaw_angles_wind_rose_with_neighbor = np.zeros((n_findex, fmodel.n_turbines)) yaw_angles_wind_rose_without_neighbor = np.zeros((n_findex, fmodel.n_turbines)) for i in range(n_findex): wind_speed = wind_speeds[i] wind_direction = wind_directions[i] # Interpolate the optimal yaw angles for this wind direction from df_opt id_opt_with_neighbor = df_opt_with_neighbor["wind_direction"] == wind_direction id_opt_without_neighbor = df_opt_without_neighbor["wind_direction"] == wind_direction # Get the yaw angles for this wind direction yaw_opt_full_with_neighbor = np.array( df_opt_with_neighbor.loc[id_opt_with_neighbor, "yaw_angles_opt"] )[0] yaw_opt_full_without_neighbor = np.array( df_opt_without_neighbor.loc[id_opt_without_neighbor, "yaw_angles_opt"] )[0] # Extend the yaw angles from 10 turbine to n_turbine by filling with 0s # in the case of the removed neighboring farms yaw_opt_full_without_neighbor = np.concatenate( (yaw_opt_full_without_neighbor, np.zeros(fmodel.n_turbines - 10)) ) # Now decide what to do for different wind speeds wind_speed_low_no_steer = 4.0 wind_speed_high_no_steer = 14.0 wind_speed_low_steer = 6.0 wind_speed_high_steer = 12.0 if (wind_speed < wind_speed_low_no_steer) | (wind_speed > wind_speed_high_no_steer): yaw_opt_with_neighbor = np.zeros(fmodel.n_turbines) # do nothing for very low/high speeds yaw_opt_without_neighbor = np.zeros( fmodel.n_turbines ) # do nothing for very low/high speeds elif wind_speed < wind_speed_low_steer: yaw_opt_with_neighbor = ( yaw_opt_full_with_neighbor * (wind_speed_low_steer - wind_speed) / (wind_speed_low_steer - wind_speed_low_no_steer) ) # Linear ramp up yaw_opt_without_neighbor = ( yaw_opt_full_without_neighbor * (wind_speed_low_steer - wind_speed) / (wind_speed_low_steer - wind_speed_low_no_steer) ) # Linear ramp up elif wind_speed > wind_speed_high_steer: yaw_opt_with_neighbor = ( yaw_opt_full_with_neighbor * (wind_speed_high_no_steer - wind_speed) / (wind_speed_high_no_steer - wind_speed_high_steer) ) # Linear ramp down yaw_opt_without_neighbor = ( yaw_opt_full_without_neighbor * (wind_speed_high_no_steer - wind_speed) / (wind_speed_high_no_steer - wind_speed_high_steer) ) # Linear ramp down else: yaw_opt_with_neighbor = ( yaw_opt_full_with_neighbor # Apply full offsets between 6.0 and 12.0 m/s ) yaw_opt_without_neighbor = ( yaw_opt_full_without_neighbor # Apply full offsets between 6.0 and 12.0 m/s ) # Save to collective array yaw_angles_wind_rose_with_neighbor[i, :] = yaw_opt_with_neighbor yaw_angles_wind_rose_without_neighbor[i, :] = yaw_opt_without_neighbor # Now apply the optimal yaw angles and get the AEP, first accounting for the neighboring farm fmodel.set(yaw_angles=yaw_angles_wind_rose_with_neighbor) fmodel.run() aep_opt_with_neighbor = fmodel.get_farm_AEP(turbine_weights=turbine_weights) aep_uplift_with_neighbor = 100.0 * (aep_opt_with_neighbor / aep_baseline - 1) farm_power_opt_with_neighbor = fmodel.get_farm_power(turbine_weights=turbine_weights) # Repeat without accounting for neighboring farm fmodel.set(yaw_angles=yaw_angles_wind_rose_without_neighbor) fmodel.run() aep_opt_without_neighbor = fmodel.get_farm_AEP(turbine_weights=turbine_weights) aep_uplift_without_neighbor = 100.0 * (aep_opt_without_neighbor / aep_baseline - 1) farm_power_opt_without_neighbor = fmodel.get_farm_power(turbine_weights=turbine_weights) print(f"Baseline AEP: {aep_baseline / 1e9:.2f} GWh.") print( f"Optimal AEP (Not accounting for neighboring farm): {aep_opt_without_neighbor / 1e9:.2f} GWh." ) print(f"Optimal AEP (Accounting for neighboring farm): {aep_opt_with_neighbor / 1e9:.2f} GWh.") # Plot the optimal yaw angles for turbine 0 with and without accounting for the neighboring farm yaw_angles_0_with_neighbor = np.vstack(df_opt_with_neighbor["yaw_angles_opt"])[:, 0] yaw_angles_0_without_neighbor = np.vstack(df_opt_without_neighbor["yaw_angles_opt"])[:, 0] fig, ax = plt.subplots() ax.plot( df_opt_with_neighbor["wind_direction"], yaw_angles_0_with_neighbor, label="Accounting for neighboring farm", ) ax.plot( df_opt_without_neighbor["wind_direction"], yaw_angles_0_without_neighbor, label="Not accounting for neighboring farm", ) ax.set_xlabel("Wind direction (deg)") ax.set_ylabel("Yaw angle (deg)") ax.legend() ax.grid(True) ax.set_title("Optimal yaw angles for turbine 0") plt.show() ================================================ FILE: examples/examples_control_optimization/008_optimize_yaw_with_disabled_turbines.py ================================================ """Example: Optimizing yaw angles with disabled turbines This example demonstrates how to optimize yaw angles in FLORIS, when some turbines are disabled. The example optimization is run using both YawOptimizerSR and YawOptimizerGeometric, the two yaw optimizers that support disabling turbines. """ import numpy as np from floris import FlorisModel from floris.optimization.yaw_optimization.yaw_optimizer_geometric import YawOptimizationGeometric from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR # Load a 3-turbine model fmodel = FlorisModel("../inputs/gch.yaml") # Set wind conditions to be the same for two cases fmodel.set(wind_directions=[270.]*2, wind_speeds=[8.]*2, turbulence_intensities=[.06]*2) # First run the case where all turbines are active and print results yaw_opt = YawOptimizationSR(fmodel) df_opt = yaw_opt.optimize() print("Serial Refine optimized yaw angles (all turbines active) [deg]:\n", df_opt.yaw_angles_opt) yaw_opt = YawOptimizationGeometric(fmodel) df_opt = yaw_opt.optimize() print("\nGeometric optimized yaw angles (all turbines active) [deg]:\n", df_opt.yaw_angles_opt) # Disable turbines (different pattern for each of the two cases) # First case: disable the middle turbine # Second case: disable the front turbine fmodel.set_operation_model('mixed') fmodel.set(disable_turbines=np.array([[False, True, False], [True, False, False]])) # Rerun optimizations and print results yaw_opt = YawOptimizationSR(fmodel) df_opt = yaw_opt.optimize() print( "\nSerial Refine optimized yaw angles (some turbines disabled) [deg]:\n", df_opt.yaw_angles_opt ) # Note that disabled turbines are assigned a zero yaw angle, but their yaw angle is arbitrary as it # does not affect the total power output. yaw_opt = YawOptimizationGeometric(fmodel) df_opt = yaw_opt.optimize() print("\nGeometric optimized yaw angles (some turbines disabled) [deg]:\n", df_opt.yaw_angles_opt) ================================================ FILE: examples/examples_control_types/001_derating_control.py ================================================ """Example of using the simple-derating control model in FLORIS. This example demonstrates how to use the simple-derating control model in FLORIS. The simple-derating control model allows the user to specify a power setpoint for each turbine in the farm. The power setpoint is used to derate the turbine power output to be at most the power setpoint. In this example: 1. A simple two-turbine layout is created. 2. The wind conditions are set to be constant. 3. The power setpoint is varied, and set the same for each turbine 4. The power produced by each turbine is computed and plotted """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel fmodel = FlorisModel("../inputs/gch.yaml") # Change to the simple-derating model turbine # (Note this could also be done with the mixed model) fmodel.set_operation_model("simple-derating") # Convert to a simple two turbine layout with derating turbines fmodel.set(layout_x=[0, 1000.0], layout_y=[0.0, 0.0]) # For reference, load the turbine type turbine_type = fmodel.core.farm.turbine_definitions[0] # Set the wind directions and speeds to be constant over n_findex = N time steps N = 50 fmodel.set( wind_directions=270 * np.ones(N), wind_speeds=10.0 * np.ones(N), turbulence_intensities=0.06 * np.ones(N), ) fmodel.run() turbine_powers_orig = fmodel.get_turbine_powers() # Add derating level to both turbines power_setpoints = np.tile(np.linspace(1, 6e6, N), 2).reshape(2, N).T fmodel.set(power_setpoints=power_setpoints) fmodel.run() turbine_powers_derated = fmodel.get_turbine_powers() # Compute available power at downstream turbine power_setpoints_2 = np.array([np.linspace(1, 6e6, N), np.full(N, None)]).T fmodel.set(power_setpoints=power_setpoints_2) fmodel.run() turbine_powers_avail_ds = fmodel.get_turbine_powers()[:, 1] # Plot the results fig, ax = plt.subplots(1, 1) ax.plot( power_setpoints[:, 0] / 1000, turbine_powers_derated[:, 0] / 1000, color="C0", label="Upstream" ) ax.plot( power_setpoints[:, 1] / 1000, turbine_powers_derated[:, 1] / 1000, color="C1", label="Downstream", ) ax.plot( power_setpoints[:, 0] / 1000, turbine_powers_orig[:, 0] / 1000, color="C0", linestyle="dotted", label="Upstream available", ) ax.plot( power_setpoints[:, 1] / 1000, turbine_powers_avail_ds / 1000, color="C1", linestyle="dotted", label="Downstream available", ) ax.plot( power_setpoints[:, 1] / 1000, np.ones(N) * np.max(turbine_type["power_thrust_table"]["power"]), color="k", linestyle="dashed", label="Rated power", ) ax.grid() ax.legend() ax.set_xlim([0, 6e3]) ax.set_xlabel("Power setpoint (kW) [Applied to both turbines]") ax.set_ylabel("Power produced (kW)") plt.show() ================================================ FILE: examples/examples_control_types/002_disable_turbines.py ================================================ """Example: Disabling turbines This example is adapted from https://github.com/NatLabRockies/floris/pull/693 contributed by Elie Kadoche. This example demonstrates the ability of FLORIS to shut down some turbines during a simulation. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel # Initialize FLORIS fmodel = FlorisModel("../inputs/gch.yaml") # Change to the mixed model turbine # (Note this could also be done with the simple-derating model) fmodel.set_operation_model("mixed") # Consider a wind farm of 3 aligned wind turbines layout = np.array([[0.0, 0.0], [500.0, 0.0], [1000.0, 0.0]]) # Run the computations for 2 identical wind data # (n_findex = 2) wind_directions = np.array([270.0, 270.0]) wind_speeds = np.array([8.0, 8.0]) turbulence_intensities = np.array([0.06, 0.06]) # Shut down the first 2 turbines for the second findex # 2 findex x 3 turbines disable_turbines = np.array([[False, False, False], [True, True, False]]) # Simulation # ------------------------------------------ # Reinitialize flow field fmodel.set( layout_x=layout[:, 0], layout_y=layout[:, 1], wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, disable_turbines=disable_turbines, ) # # Compute wakes fmodel.run() # Results # ------------------------------------------ # Get powers and effective wind speeds turbine_powers = fmodel.get_turbine_powers() turbine_powers = np.round(turbine_powers * 1e-3, decimals=2) effective_wind_speeds = fmodel.turbine_average_velocities # Plot the results fig, axarr = plt.subplots(2, 1, sharex=True) # Plot the power ax = axarr[0] ax.plot(["T0", "T1", "T2"], turbine_powers[0, :], "ks-", label="All on") ax.plot(["T0", "T1", "T2"], turbine_powers[1, :], "ro-", label="T0 & T1 disabled") ax.set_ylabel("Power (kW)") ax.grid(True) ax.legend() ax = axarr[1] ax.plot(["T0", "T1", "T2"], effective_wind_speeds[0, :], "ks-", label="All on") ax.plot(["T0", "T1", "T2"], effective_wind_speeds[1, :], "ro-", label="T0 & T1 disabled") ax.set_ylabel("Effective wind speeds (m/s)") ax.grid(True) ax.legend() plt.show() ================================================ FILE: examples/examples_control_types/003_setting_yaw_and_disabling.py ================================================ """Example: Setting yaw angles and disabling turbine This example demonstrates how to set yaw angles and disable turbines in FLORIS. The yaw angles are set to sweep from -20 to 20 degrees for the upstream-most turbine and to 0 degrees for the downstream-most turbine(s). A two-turbine case is compared to a three-turbine case where the middle turbine is disabled making the two cases functionally equivalent. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, TimeSeries # Initialize 2 FLORIS models, a two-turbine layout # and three-turbine layout fmodel_2 = FlorisModel("../inputs/gch.yaml") fmodel_3 = FlorisModel("../inputs/gch.yaml") # Change to the mixed model turbine # This example sets both yaw angle and power setpoints fmodel_2.set_operation_model("mixed") fmodel_3.set_operation_model("mixed") # Set the layouts, f_model_3 has an extra turbine in-between the two # turbines of f_model_2 fmodel_2.set(layout_x=[0, 1000.0], layout_y=[0.0, 0.0]) fmodel_3.set(layout_x=[0, 500.0, 1000.0], layout_y=[0.0, 0.0, 0.0]) # Set bo # Set both to have constant wind conditions N = 50 time_series = TimeSeries( wind_directions=270.0 * np.ones(N), wind_speeds = 8., turbulence_intensities=0.06 ) fmodel_2.set(wind_data=time_series) fmodel_3.set(wind_data=time_series) # In both cases, set the yaw angles of the upstream-most turbine # to sweep from -20 to 20 degrees, while other turbines are set to 0 upstream_yaw_angles = np.linspace(-20, 20, N) yaw_angles_2 = np.array([upstream_yaw_angles, np.zeros(N)]).T yaw_angles_3 = np.array([upstream_yaw_angles, np.zeros(N), np.zeros(N)]).T # In the three turbine case, also disable the middle turbine # Declare a np array of booleans that is Nx3 and whose middle column is True disable_turbines = np.array([np.zeros(N), np.ones(N), np.zeros(N)]).T.astype(bool) # Set the yaw angles for both and disable the middle turbine for the # three turbine case fmodel_2.set(yaw_angles=yaw_angles_2) fmodel_3.set(yaw_angles=yaw_angles_3, disable_turbines=disable_turbines) # Run both models fmodel_2.run() fmodel_3.run() # Collect the turbine powers from both turbine_powers_2 = fmodel_2.get_turbine_powers() turbine_powers_3 = fmodel_3.get_turbine_powers() # Make a 2-panel plot of the turbine powers. For the three-turbine case, # only plot the first and last turbine fig, axarr = plt.subplots(2, 1, sharex=True) axarr[0].plot(upstream_yaw_angles, turbine_powers_2[:, 0] / 1000, label="Two-Turbine", marker='s') axarr[0].plot(upstream_yaw_angles, turbine_powers_3[:, 0] / 1000, label="Three-Turbine", marker='.') axarr[0].set_ylabel("Power (kW)") axarr[0].legend() axarr[0].grid(True) axarr[0].set_title("Upstream Turbine") axarr[1].plot(upstream_yaw_angles, turbine_powers_2[:, 1] / 1000, label="Two-Turbine", marker='s') axarr[1].plot(upstream_yaw_angles, turbine_powers_3[:, 2] / 1000, label="Three-Turbine", marker='.') axarr[1].set_ylabel("Power (kW)") axarr[1].legend() axarr[1].grid(True) axarr[1].set_title("Downstream-most Turbine") plt.show() ================================================ FILE: examples/examples_control_types/004_helix_active_wake_mixing.py ================================================ """Example: Helix active wake mixing Example to test out using helix wake mixing of upstream turbines. Helix wake mixing is turned on at turbine 1, off at turbines 2 to 4; Turbine 2 is in wake turbine 1, turbine 4 in wake of turbine 3. """ import matplotlib.pyplot as plt import numpy as np import yaml import floris.flow_visualization as flowviz from floris import FlorisModel # Grab model of FLORIS and update to awc-enabled turbines fmodel = FlorisModel("../inputs/emgauss_helix.yaml") fmodel.set_operation_model("awc") # Set the wind directions and speeds to be constant over N different helix amplitudes N = 1 awc_modes = np.array(["helix", "baseline", "baseline", "baseline"]).reshape(4, N).T awc_amplitudes = np.array([2.5, 0, 0, 0]).reshape(4, N).T # Create 4 WT WF layout with lateral offset of 3D and streamwise offset of 4D D = 240 fmodel.set( layout_x=[0.0, 4*D, 0.0, 4*D], layout_y=[0.0, 0.0, -3*D, -3*D], wind_directions=270 * np.ones(N), wind_speeds=8.0 * np.ones(N), turbulence_intensities=0.06*np.ones(N), awc_modes=awc_modes, awc_amplitudes=awc_amplitudes ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() # Plot the flow fields for T1 awc_amplitude = 2.5 horizontal_plane = fmodel.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=150.0, ) y_plane_baseline = fmodel.calculate_y_plane( x_resolution=200, z_resolution=100, crossstream_dist=0.0, ) y_plane_helix = fmodel.calculate_y_plane( x_resolution=200, z_resolution=100, crossstream_dist=-3*D, ) cross_plane = fmodel.calculate_cross_plane( y_resolution=100, z_resolution=100, downstream_dist=720.0, ) # Create the plots fig, ax_list = plt.subplots(2, 2, figsize=(10, 8), tight_layout=True) ax_list = ax_list.flatten() flowviz.visualize_cut_plane( horizontal_plane, ax=ax_list[0], label_contours=True, title="Horizontal" ) flowviz.visualize_cut_plane( cross_plane, ax=ax_list[2], label_contours=True, title="Spanwise profile at 3D" ) # fig2, ax_list2 = plt.subplots(2, 1, figsize=(10, 8), tight_layout=True) # ax_list2 = ax_list2.flatten() flowviz.visualize_cut_plane( y_plane_baseline, ax=ax_list[1], label_contours=True, title="Streamwise profile, helix" ) flowviz.visualize_cut_plane( y_plane_helix, ax=ax_list[3], label_contours=True, title="Streamwise profile, baseline" ) # Calculate the effect of changing awc_amplitudes N = 50 awc_amplitudes = np.array([ np.linspace(0, 5, N), np.zeros(N), np.zeros(N), np.zeros(N) ]).reshape(4, N).T awc_modes = np.tile(awc_modes, (N,1)) # Repeat over N findices # Reset FlorisModel for different helix amplitudes fmodel.set( wind_directions=270 * np.ones(N), wind_speeds=8 * np.ones(N), turbulence_intensities=0.06*np.ones(N), awc_modes=awc_modes, awc_amplitudes=awc_amplitudes ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() # Plot the power as a function of helix amplitude fig_power, ax_power = plt.subplots() ax_power.fill_between( awc_amplitudes[:, 0], 0, turbine_powers[:, 0]/1000, color='C0', label='Turbine 1' ) ax_power.fill_between( awc_amplitudes[:, 0], turbine_powers[:, 0]/1000, turbine_powers[:, :2].sum(axis=1)/1000, color='C1', label='Turbine 2' ) ax_power.plot( awc_amplitudes[:, 0], turbine_powers[:,:2].sum(axis=1)/1000, color='k', label='Farm' ) ax_power.set_xlabel("Upstream turbine helix amplitude [deg]") ax_power.set_ylabel("Power [kW]") ax_power.legend() flowviz.show() ================================================ FILE: examples/examples_control_types/005_peak_shaving.py ================================================ """Example of using the peak-shaving turbine operation model. This example demonstrates how to use the peak-shaving operation model in FLORIS. The peak-shaving operation model allows the user to a thrust reduction near rated wind speed to reduce loads on the turbine. The power is reduced accordingly, and wind turbine wakes are shallower due to the reduced thrust. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, TimeSeries fmodel = FlorisModel("../inputs/gch.yaml") fmodel.set(layout_x=[0, 1000.0], layout_y=[0.0, 0.0]) wind_speeds = np.linspace(0, 30, 100) fmodel.set( wind_data=TimeSeries( wind_directions=270 * np.ones_like(wind_speeds), wind_speeds=wind_speeds, turbulence_intensities=0.10, # High enough to engage peak shaving ) ) # Start with "normal" operation under the simple turbine operation model fmodel.set_operation_model("simple") fmodel.run() powers_base = fmodel.get_turbine_powers()/1000 thrust_coefficients_base = fmodel.get_turbine_thrust_coefficients() # Switch to the peak-shaving operation model fmodel.set_operation_model("peak-shaving") fmodel.run() powers_peak_shaving = fmodel.get_turbine_powers()/1000 thrust_coefficients_peak_shaving = fmodel.get_turbine_thrust_coefficients() # Compare the power and thrust coefficients of the upstream turbine fig, ax = plt.subplots(2,1,sharex=True) ax[0].plot( wind_speeds, thrust_coefficients_base[:,0], label="Without peak shaving", color="black" ) ax[0].plot( wind_speeds, thrust_coefficients_peak_shaving[:,0], label="With peak shaving", color="C0" ) ax[1].plot( wind_speeds, powers_base[:,0], label="Without peak shaving", color="black" ) ax[1].plot( wind_speeds, powers_peak_shaving[:,0], label="With peak shaving", color="C0" ) ax[1].grid() ax[0].grid() ax[0].legend() ax[0].set_ylabel("Thrust coefficient [-]") ax[1].set_xlabel("Wind speed [m/s]") ax[1].set_ylabel("Power [kW]") # Look at the total power across the two turbines for each case fig, ax = plt.subplots(2,1,sharex=True,sharey=True) ax[0].fill_between( wind_speeds, 0, powers_base[:, 0]/1e6, color='C0', label='Turbine 1' ) ax[0].fill_between( wind_speeds, powers_base[:, 0]/1e6, powers_base[:, :2].sum(axis=1)/1e6, color='C1', label='Turbine 2' ) ax[0].plot( wind_speeds, powers_base[:,:2].sum(axis=1)/1e6, color='k', label='Farm' ) ax[1].fill_between( wind_speeds, 0, powers_peak_shaving[:, 0]/1e6, color='C0', label='Turbine 1' ) ax[1].fill_between( wind_speeds, powers_peak_shaving[:, 0]/1e6, powers_peak_shaving[:, :2].sum(axis=1)/1e6, color='C1', label='Turbine 2' ) ax[1].plot( wind_speeds, powers_peak_shaving[:,:2].sum(axis=1)/1e6, color='k', label='Farm' ) ax[0].legend() ax[0].set_title("Without peak shaving") ax[1].set_title("With peak shaving") ax[0].set_ylabel("Power [MW]") ax[1].set_ylabel("Power [MW]") ax[0].grid() ax[1].grid() ax[1].set_xlabel("Free stream wind speed [m/s]") plt.show() ================================================ FILE: examples/examples_emgauss/001_empirical_gauss_velocity_deficit_parameters.py ================================================ """Example: Empirical Gaussian velocity deficit parameters This example illustrates the main parameters of the Empirical Gaussian velocity deficit model and their effects on the wind turbine wake. """ import copy import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane # Options show_flow_cuts = True num_in_row = 5 # Define function for visualizing wakes def generate_wake_visualization(fmodel: FlorisModel, title=None): # Using the FlorisModel functions, get 2D slices. x_bounds = [-500, 3000] y_bounds = [-250, 250] z_bounds = [0.001, 500] cross_plane_locations = [10, 1200, 2500] horizontal_plane_location = 90.0 streamwise_plane_location = 0.0 # Contour plot colors min_ws = 4 max_ws = 10 horizontal_plane = fmodel.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=horizontal_plane_location, x_bounds=x_bounds, y_bounds=y_bounds, ) y_plane = fmodel.calculate_y_plane( x_resolution=200, z_resolution=100, crossstream_dist=streamwise_plane_location, x_bounds=x_bounds, z_bounds=z_bounds, ) cross_planes = [ fmodel.calculate_cross_plane(y_resolution=100, z_resolution=100, downstream_dist=x) for x in cross_plane_locations ] # Create the plots # Cutplane settings cp_ls = "solid" # line style cp_lw = 0.5 # line width cp_clr = "black" # line color fig = plt.figure() fig.set_size_inches(12, 12) # Horizontal profile ax = fig.add_subplot(311) visualize_cut_plane( horizontal_plane, ax=ax, title="Top-down profile", min_speed=min_ws, max_speed=max_ws ) ax.plot( x_bounds, [streamwise_plane_location] * 2, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls ) for cpl in cross_plane_locations: ax.plot([cpl] * 2, y_bounds, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls) ax = fig.add_subplot(312) visualize_cut_plane( y_plane, ax=ax, title="Streamwise profile", min_speed=min_ws, max_speed=max_ws ) ax.plot( x_bounds, [horizontal_plane_location] * 2, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls ) for cpl in cross_plane_locations: ax.plot([cpl, cpl], z_bounds, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls) # Spanwise profiles for i, (cp, cpl) in enumerate(zip(cross_planes, cross_plane_locations)): visualize_cut_plane( cp, ax=fig.add_subplot(3, len(cross_planes), i + 7), title="Loc: {:.0f}m".format(cpl), min_speed=min_ws, max_speed=max_ws, ) # Add overall figure title if title is not None: fig.suptitle(title, fontsize=16) ## Main script # Load input yaml and define farm layout fmodel = FlorisModel("../inputs/emgauss.yaml") D = fmodel.core.farm.rotor_diameters[0] fmodel.set( layout_x=[x * 5.0 * D for x in range(num_in_row)], layout_y=[0.0] * num_in_row, wind_speeds=[8.0], wind_directions=[270.0], ) # Save dictionary to modify later fmodel_dict = fmodel.core.as_dict() # Run wake calculation fmodel.run() # Look at the powers of each turbine turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 fig0, ax0 = plt.subplots(1, 1) width = 0.1 nw = -2 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Original" ax0.bar(x, turbine_powers, width=width, label=title) ax0.legend() # Visualize wakes if show_flow_cuts: generate_wake_visualization(fmodel, title) # Increase the base recovery rate fmodel_dict_mod = copy.deepcopy(fmodel_dict) fmodel_dict_mod["wake"]["wake_velocity_parameters"]["empirical_gauss"]["wake_expansion_rates"] = [ 0.03, 0.015, ] fmodel = FlorisModel(fmodel_dict_mod) fmodel.set(wind_speeds=[8.0], wind_directions=[270.0]) fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Increase base recovery" ax0.bar(x, turbine_powers, width=width, label=title) if show_flow_cuts: generate_wake_visualization(fmodel, title) # Add new expansion rate fmodel_dict_mod = copy.deepcopy(fmodel_dict) fmodel_dict_mod["wake"]["wake_velocity_parameters"]["empirical_gauss"]["wake_expansion_rates"] = ( fmodel_dict["wake"]["wake_velocity_parameters"]["empirical_gauss"]["wake_expansion_rates"] + [0.0] ) fmodel_dict_mod["wake"]["wake_velocity_parameters"]["empirical_gauss"]["breakpoints_D"] = [5, 10] fmodel = FlorisModel(fmodel_dict_mod) fmodel.set(wind_speeds=[8.0], wind_directions=[270.0]) fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Add rate, change breakpoints" ax0.bar(x, turbine_powers, width=width, label=title) if show_flow_cuts: generate_wake_visualization(fmodel, title) # Increase the wake-induced mixing gain fmodel_dict_mod = copy.deepcopy(fmodel_dict) fmodel_dict_mod["wake"]["wake_velocity_parameters"]["empirical_gauss"]["mixing_gain_velocity"] = 3.0 fmodel = FlorisModel(fmodel_dict_mod) fmodel.set(wind_speeds=[8.0], wind_directions=[270.0]) fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Increase mixing gain" ax0.bar(x, turbine_powers, width=width, label=title) if show_flow_cuts: generate_wake_visualization(fmodel, title) # Power plot aesthetics ax0.set_xticks(range(num_in_row)) ax0.set_xticklabels(["T{0}".format(t) for t in range(num_in_row)]) ax0.legend() ax0.set_xlabel("Turbine") ax0.set_ylabel("Power [MW]") plt.show() ================================================ FILE: examples/examples_emgauss/002_empirical_gauss_deflection_parameters.py ================================================ """Example: Empirical Gaussian deflection parameters This example illustrates the main parameters of the Empirical Gaussian deflection model and their effects on the wind turbine wake. """ import copy import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane # Initialize FLORIS with the given input file. # For basic usage, FlorisModel provides a simplified and expressive # entry point to the simulation routines. # Options show_flow_cuts = True num_in_row = 5 # Should be at least 3 first_three_yaw_angles = [20.0, 20.0, 10.0] yaw_angles = np.array(first_three_yaw_angles + [0.0] * (num_in_row - 3))[None, :] print("Turbine yaw angles (degrees): ", yaw_angles[0]) # Define function for visualizing wakes def generate_wake_visualization(fmodel: FlorisModel, title=None): # Using the FlorisModel functions, get 2D slices. x_bounds = [-500, 3000] y_bounds = [-250, 250] z_bounds = [0.001, 500] cross_plane_locations = [10, 1200, 2500] horizontal_plane_location = 90.0 streamwise_plane_location = 0.0 # Contour plot colors min_ws = 4 max_ws = 10 horizontal_plane = fmodel.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=horizontal_plane_location, x_bounds=x_bounds, y_bounds=y_bounds, ) y_plane = fmodel.calculate_y_plane( x_resolution=200, z_resolution=100, crossstream_dist=streamwise_plane_location, x_bounds=x_bounds, z_bounds=z_bounds, ) cross_planes = [ fmodel.calculate_cross_plane(y_resolution=100, z_resolution=100, downstream_dist=x) for x in cross_plane_locations ] # Create the plots # Cutplane settings cp_ls = "solid" # line style cp_lw = 0.5 # line width cp_clr = "black" # line color fig = plt.figure() fig.set_size_inches(12, 12) # Horizontal profile ax = fig.add_subplot(311) visualize_cut_plane( horizontal_plane, ax=ax, title="Top-down profile", min_speed=min_ws, max_speed=max_ws ) ax.plot( x_bounds, [streamwise_plane_location] * 2, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls ) for cpl in cross_plane_locations: ax.plot([cpl] * 2, y_bounds, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls) ax = fig.add_subplot(312) visualize_cut_plane( y_plane, ax=ax, title="Streamwise profile", min_speed=min_ws, max_speed=max_ws ) ax.plot( x_bounds, [horizontal_plane_location] * 2, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls ) for cpl in cross_plane_locations: ax.plot([cpl, cpl], z_bounds, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls) # Spanwise profiles for i, (cp, cpl) in enumerate(zip(cross_planes, cross_plane_locations, strict=False)): visualize_cut_plane( cp, ax=fig.add_subplot(3, len(cross_planes), i + 7), title=f"Loc: {cpl:.0f}m", min_speed=min_ws, max_speed=max_ws, ) # Add overall figure title if title is not None: fig.suptitle(title, fontsize=16) ## Main script # Load input yaml and define farm layout fmodel = FlorisModel("../inputs/emgauss.yaml") D = fmodel.core.farm.rotor_diameters[0] fmodel.set( layout_x=[x * 5.0 * D for x in range(num_in_row)], layout_y=[0.0] * num_in_row, wind_speeds=[8.0], wind_directions=[270.0], yaw_angles=yaw_angles, ) # Save dictionary to modify later fmodel_dict = fmodel.core.as_dict() # Run wake calculation fmodel.run() # Look at the powers of each turbine turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 fig0, ax0 = plt.subplots(1, 1) width = 0.1 nw = -2 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Original" ax0.bar(x, turbine_powers, width=width, label=title) ax0.legend() # Visualize wakes if show_flow_cuts: generate_wake_visualization(fmodel, title) # Increase the maximum deflection attained fmodel_dict_mod = copy.deepcopy(fmodel_dict) fmodel_dict_mod["wake"]["wake_deflection_parameters"]["empirical_gauss"][ "horizontal_deflection_gain_D" ] = 5.0 fmodel = FlorisModel(fmodel_dict_mod) fmodel.set( wind_speeds=[8.0], wind_directions=[270.0], yaw_angles=yaw_angles, ) fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Increase max deflection" ax0.bar(x, turbine_powers, width=width, label=title) if show_flow_cuts: generate_wake_visualization(fmodel, title) # Add (increase) influence of wake added mixing fmodel_dict_mod = copy.deepcopy(fmodel_dict) fmodel_dict_mod["wake"]["wake_deflection_parameters"]["empirical_gauss"][ "mixing_gain_deflection" ] = 100.0 fmodel = FlorisModel(fmodel_dict_mod) fmodel.set( wind_speeds=[8.0], wind_directions=[270.0], yaw_angles=yaw_angles, ) fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Increase mixing gain" ax0.bar(x, turbine_powers, width=width, label=title) if show_flow_cuts: generate_wake_visualization(fmodel, title) # Add (increase) the yaw-added mixing contribution fmodel_dict_mod = copy.deepcopy(fmodel_dict) # Include a WIM gain so that YAM is reflected in deflection as well # as deficit fmodel_dict_mod["wake"]["wake_deflection_parameters"]["empirical_gauss"][ "mixing_gain_deflection" ] = 100.0 fmodel_dict_mod["wake"]["wake_deflection_parameters"]["empirical_gauss"][ "yaw_added_mixing_gain" ] = 1.0 fmodel = FlorisModel(fmodel_dict_mod) fmodel.set( wind_speeds=[8.0], wind_directions=[270.0], yaw_angles=yaw_angles, ) fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e6 x = np.array(range(num_in_row)) + width * nw nw += 1 title = "Increase yaw-added mixing" ax0.bar(x, turbine_powers, width=width, label=title) if show_flow_cuts: generate_wake_visualization(fmodel, title) # Power plot aesthetics ax0.set_xticks(range(num_in_row)) ax0.set_xticklabels([f"T{t}" for t in range(num_in_row)]) ax0.legend() ax0.set_xlabel("Turbine") ax0.set_ylabel("Power [MW]") plt.show() ================================================ FILE: examples/examples_floating/001_floating_turbine_models.py ================================================ """Example: Floating turbines This example demonstrates the impact of floating on turbine power and thrust (not wake behavior). A floating turbine in FLORIS is defined by including a `floating_tilt_table` in the turbine input yaml which sets the steady tilt angle of the turbine based on wind speed. This tilt angle is computed for each turbine based on effective velocity. This tilt angle is then passed on to the respective wake model. The value of the parameter ref_tilt is the value of tilt at which the ct/cp curves have been defined. If `correct_cp_ct_for_tilt` is True, then the difference between the current tilt as interpolated from the floating tilt table is used to scale the turbine power and thrust. If `correct_cp_ct_for_tilt` is False, then it is assumed that the power/thrust coefficient tables provided already account for the variation in tilt with wind speed (for example they were computed from a turbine simulator with tilt degree-of-freedom enabled and the floating platform simulated), and no correction is made. In the example below, three single-turbine simulations are run to show the different behaviors. fmodel_fixed: Fixed bottom turbine (no tilt variation with wind speed) fmodel_floating: Floating turbine (tilt varies with wind speed) fmodel_floating_defined_floating: Floating turbine (tilt varies with wind speed, but tilt does not scale power/thrust coefficient) """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, TimeSeries # Create the Floris instances fmodel_fixed = FlorisModel("../inputs_floating/gch_fixed.yaml") fmodel_floating = FlorisModel("../inputs_floating/gch_floating.yaml") fmodel_floating_defined_floating = FlorisModel( "../inputs_floating/gch_floating_defined_floating.yaml" ) # Calculate across wind speeds, while holding win directions constant ws_array = np.arange(3.0, 25.0, 1.0) time_series = TimeSeries(wind_directions=270.0, wind_speeds=ws_array, turbulence_intensities=0.06) fmodel_fixed.set(wind_data=time_series) fmodel_floating.set(wind_data=time_series) fmodel_floating_defined_floating.set(wind_data=time_series) fmodel_fixed.run() fmodel_floating.run() fmodel_floating_defined_floating.run() # Grab power power_fixed = fmodel_fixed.get_turbine_powers().flatten() / 1000.0 power_floating = fmodel_floating.get_turbine_powers().flatten() / 1000.0 power_floating_defined_floating = ( fmodel_floating_defined_floating.get_turbine_powers().flatten() / 1000.0 ) # Grab Ct ct_fixed = fmodel_fixed.get_turbine_thrust_coefficients().flatten() ct_floating = fmodel_floating.get_turbine_thrust_coefficients().flatten() ct_floating_defined_floating = ( fmodel_floating_defined_floating.get_turbine_thrust_coefficients().flatten() ) # Grab turbine tilt angles eff_vels = fmodel_fixed.turbine_average_velocities tilt_angles_fixed = np.squeeze(fmodel_fixed.core.farm.calculate_tilt_for_eff_velocities(eff_vels)) eff_vels = fmodel_floating.turbine_average_velocities tilt_angles_floating = np.squeeze( fmodel_floating.core.farm.calculate_tilt_for_eff_velocities(eff_vels) ) eff_vels = fmodel_floating_defined_floating.turbine_average_velocities tilt_angles_floating_defined_floating = np.squeeze( fmodel_floating_defined_floating.core.farm.calculate_tilt_for_eff_velocities(eff_vels) ) # Plot results fig, axarr = plt.subplots(4, 1, figsize=(8, 10), sharex=True) ax = axarr[0] ax.plot(ws_array, tilt_angles_fixed, color="k", lw=2, label="Fixed Bottom") ax.plot(ws_array, tilt_angles_floating, color="b", label="Floating") ax.plot( ws_array, tilt_angles_floating_defined_floating, color="m", ls="--", label="Floating (power/thrust coefficient not scaled by tilt)", ) ax.grid(True) ax.legend() ax.set_title("Tilt angle (deg)") ax.set_ylabel("Tlit (deg)") ax = axarr[1] ax.plot(ws_array, power_fixed, color="k", lw=2, label="Fixed Bottom") ax.plot(ws_array, power_floating, color="b", label="Floating") ax.plot( ws_array, power_floating_defined_floating, color="m", ls="--", label="Floating (power/thrust coefficient not scaled by tilt)", ) ax.grid(True) ax.legend() ax.set_title("Power") ax.set_ylabel("Power (kW)") ax = axarr[2] # ax.plot(ws_array, power_fixed, color='k',label='Fixed Bottom') ax.plot(ws_array, power_floating - power_fixed, color="b", label="Floating") ax.plot( ws_array, power_floating_defined_floating - power_fixed, color="m", ls="--", label="Floating (power/thrust coefficient not scaled by tilt)", ) ax.grid(True) ax.legend() ax.set_title("Difference from fixed bottom power") ax.set_ylabel("Power (kW)") ax = axarr[3] ax.plot(ws_array, ct_fixed, color="k", lw=2, label="Fixed Bottom") ax.plot(ws_array, ct_floating, color="b", label="Floating") ax.plot( ws_array, ct_floating_defined_floating, color="m", ls="--", label="Floating (power/thrust coefficient not scaled by tilt)", ) ax.grid(True) ax.legend() ax.set_title("Coefficient of thrust") ax.set_ylabel("Ct (-)") plt.show() ================================================ FILE: examples/examples_floating/002_floating_vs_fixedbottom_farm.py ================================================ """Example: Floating vs fixed-bottom farm This example demonstrates the impact of floating on turbine power and thrust and wake behavior. A floating turbine in FLORIS is defined by including a `floating_tilt_table` in the turbine input yaml which sets the steady tilt angle of the turbine based on wind speed. This tilt angle is computed for each turbine based on effective velocity. This tilt angle is then passed on to the respective wake model. The value of the parameter ref_tilt is the value of tilt at which the ct/cp curves have been defined. With `correct_cp_ct_for_tilt` True, the difference between the current tilt as interpolated from the floating tilt table is used to scale the turbine power and thrust. In the example below, a 20-turbine, gridded wind farm is simulated using the Empirical Gaussian wake model to show the effects of floating turbines on both turbine power and wake development. fmodel_fixed: Fixed bottom turbine (no tilt variation with wind speed) fmodel_floating: Floating turbine (tilt varies with wind speed) """ import matplotlib.pyplot as plt import numpy as np import pandas as pd from scipy.interpolate import NearestNDInterpolator import floris.flow_visualization as flowviz from floris import FlorisModel, WindRose # Declare the Floris Interface for fixed bottom, provide layout fmodel_fixed = FlorisModel("../inputs_floating/emgauss_fixed.yaml") fmodel_floating = FlorisModel("../inputs_floating/emgauss_floating.yaml") x, y = np.meshgrid(np.linspace(0, 4*630., 5), np.linspace(0, 3*630., 4)) x = x.flatten() y = y.flatten() for fmodel in [fmodel_fixed, fmodel_floating]: fmodel.set(layout_x=x, layout_y=y) # Compute a single wind speed and direction, power and wakes for fmodel in [fmodel_fixed, fmodel_floating]: fmodel.set( layout_x=x, layout_y=y, wind_speeds=[10], wind_directions=[270], turbulence_intensities=[0.06], ) fmodel.run() powers_fixed = fmodel_fixed.get_turbine_powers() powers_floating = fmodel_floating.get_turbine_powers() power_difference = powers_floating - powers_fixed # Show the power differences fig, ax = plt.subplots() ax.set_aspect('equal', adjustable='box') sc = ax.scatter( x, y, c=power_difference.flatten()/1000, cmap="PuOr", vmin=-30, vmax=30, s=200, ) ax.set_xlabel("x coordinate [m]") ax.set_ylabel("y coordinate [m]") ax.set_title("Power increase due to floating for each turbine.") plt.colorbar(sc, label="Increase (kW)") print("Power increase from floating over farm (10m/s, 270deg winds): {0:.2f} kW".\ format(power_difference.sum()/1000)) # Visualize flows (see also 02_visualizations.py) horizontal_planes = [] y_planes = [] for fmodel in [fmodel_fixed, fmodel_floating]: horizontal_planes.append( fmodel.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=90.0, ) ) y_planes.append( fmodel.calculate_y_plane( x_resolution=200, z_resolution=100, crossstream_dist=0.0, ) ) # Create the plots fig, ax_list = plt.subplots(2, 1, figsize=(10, 8)) ax_list = ax_list.flatten() flowviz.visualize_cut_plane(horizontal_planes[0], ax=ax_list[0], title="Horizontal") flowviz.visualize_cut_plane(y_planes[0], ax=ax_list[1], title="Streamwise profile") fig.suptitle("Fixed-bottom farm") fig, ax_list = plt.subplots(2, 1, figsize=(10, 8)) ax_list = ax_list.flatten() flowviz.visualize_cut_plane(horizontal_planes[1], ax=ax_list[0], title="Horizontal") flowviz.visualize_cut_plane(y_planes[1], ax=ax_list[1], title="Streamwise profile") fig.suptitle("Floating farm") # Compute AEP # Load the wind rose from csv as in example 003 wind_rose = WindRose.read_csv_long( "../inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06 ) for fmodel in [fmodel_fixed, fmodel_floating]: fmodel.set( wind_data=wind_rose, ) fmodel.run() # Compute the AEP aep_fixed = fmodel_fixed.get_farm_AEP() aep_floating = fmodel_floating.get_farm_AEP() print("Farm AEP (fixed bottom): {:.3f} GWh".format(aep_fixed / 1.0e9)) print("Farm AEP (floating): {:.3f} GWh".format(aep_floating / 1.0e9)) print( "Floating AEP increase: {0:.3f} GWh ({1:.2f}%)".\ format((aep_floating - aep_fixed) / 1.0e9, (aep_floating - aep_fixed)/aep_fixed*100) ) plt.show() ================================================ FILE: examples/examples_floating/003_tilt_driven_vertical_wake_deflection.py ================================================ """Example: Tilt-driven vertical wake deflection This example demonstrates vertical wake deflections due to the tilt angle when running with the Empirical Gauss model. Note that only the Empirical Gauss model implements vertical deflections at this time. Also be aware that this example uses a potentially unrealistic tilt angle, 15 degrees, to highlight the wake deflection. Moreover, the magnitude of vertical deflections due to tilt has not been validated. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane # Initialize two FLORIS objects: one with 5 degrees of tilt (fixed across all # wind speeds) and one with 15 degrees of tilt (fixed across all wind speeds). fmodel_5 = FlorisModel("../inputs_floating/emgauss_floating_fixedtilt5.yaml") fmodel_15 = FlorisModel("../inputs_floating/emgauss_floating_fixedtilt15.yaml") D = fmodel_5.core.farm.rotor_diameters[0] num_in_row = 5 # Figure settings x_bounds = [-500, 3000] y_bounds = [-250, 250] z_bounds = [0.001, 500] cross_plane_locations = [10, 1200, 2500] horizontal_plane_location = 90.0 streamwise_plane_location = 0.0 # Create the plots # Cutplane settings cp_ls = "solid" # line style cp_lw = 0.5 # line width cp_clr = "black" # line color min_ws = 4 max_ws = 10 fig = plt.figure() fig.set_size_inches(12, 6) powers = np.zeros((2, num_in_row)) # Calculate wakes, powers, plot for i, (fmodel, tilt) in enumerate(zip([fmodel_5, fmodel_15], [5, 15])): # Farm layout and wind conditions fmodel.set( layout_x=[x * 5.0 * D for x in range(num_in_row)], layout_y=[0.0] * num_in_row, wind_speeds=[8.0], wind_directions=[270.0], ) # Flow solve and power computation fmodel.run() powers[i, :] = fmodel.get_turbine_powers().flatten() # Compute flow slices y_plane = fmodel.calculate_y_plane( x_resolution=200, z_resolution=100, crossstream_dist=streamwise_plane_location, x_bounds=x_bounds, z_bounds=z_bounds, ) # Horizontal profile ax = fig.add_subplot(2, 1, i + 1) visualize_cut_plane(y_plane, ax=ax, min_speed=min_ws, max_speed=max_ws) ax.plot( x_bounds, [horizontal_plane_location] * 2, color=cp_clr, linewidth=cp_lw, linestyle=cp_ls ) ax.set_title("Tilt angle: {0} degrees".format(tilt)) fig = plt.figure() fig.set_size_inches(6, 4) ax = fig.add_subplot(1, 1, 1) x_locs = np.arange(num_in_row) width = 0.25 ax.bar(x_locs - width / 2, powers[0, :] / 1000, width=width, label="5 degree tilt") ax.bar(x_locs + width / 2, powers[1, :] / 1000, width=width, label="15 degree tilt") ax.set_xticks(x_locs) ax.set_xticklabels(["T{0}".format(i) for i in range(num_in_row)]) ax.set_xlabel("Turbine number in row") ax.set_ylabel("Power [kW]") ax.legend() plt.show() ================================================ FILE: examples/examples_get_flow/001_extract_wind_speed_at_turbines.py ================================================ """Example: Extract wind speed at turbines This example demonstrates how to extract the wind speed at the turbine points from the FLORIS model. Both the u velocities and the turbine average velocities are grabbed from the model, then the turbine average is recalculated from the u velocities to show that they are equivalent. """ import numpy as np from floris import FlorisModel # Initialize the FLORIS model fmodel = FlorisModel("../inputs/gch.yaml") # Create a 4-turbine layouts fmodel.set(layout_x=[0, 0.0, 500.0, 500.0], layout_y=[0.0, 300.0, 0.0, 300.0]) # Calculate wake fmodel.run() # Collect the wind speed at all the turbine points u_points = fmodel.core.flow_field.u print("U points is 1 findex x 4 turbines x 3 x 3 points (turbine_grid_points=3)") print(u_points.shape) print("turbine_average_velocities is 1 findex x 4 turbines") print(fmodel.turbine_average_velocities) # Show that one is equivalent to the other following averaging print( "turbine_average_velocities is determined by taking the cube root of mean " "of the cubed value across the points " ) print(f"turbine_average_velocities: {fmodel.turbine_average_velocities}") print(f"Recomputed: {np.cbrt(np.mean(u_points**3, axis=(2,3)))}") ================================================ FILE: examples/examples_get_flow/002_extract_wind_speed_at_points.py ================================================ """Example: Extract wind speed at points This example demonstrates the use of the sample_flow_at_points method of FlorisModel. sample_flow_at_points extracts the wind speed information at user-specified locations in the flow. Specifically, this example returns the wind speed at a single x, y location and four different heights over a sweep of wind directions. This mimics the wind speed measurements of a met mast across all wind directions (at a fixed free stream wind speed). Try different values for met_mast_option to vary the location of the met mast within the two-turbine farm. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel # User options # FLORIS model to use (legacy Turbopark not available) floris_model = "gch" # Try "gch", "cc", "jensen", "emgauss", "turboparkgauss" # Option to try different met mast locations met_mast_option = 0 # Try 0, 1, 2, 3 # Instantiate FLORIS model fmodel = FlorisModel("../inputs/" + floris_model + ".yaml") # Set up a two-turbine farm D = 126 fmodel.set(layout_x=[0, 3 * D], layout_y=[0, 3 * D]) fig, ax = plt.subplots(1, 2) fig.set_size_inches(10, 4) ax[0].scatter(fmodel.layout_x, fmodel.layout_y, color="black", label="Turbine") # Set the wind direction to run 360 degrees wd_array = np.arange(0, 360, 1) ws_array = 8.0 * np.ones_like(wd_array) ti_array = 0.06 * np.ones_like(wd_array) fmodel.set(wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array) # Simulate a met mast in between the turbines if met_mast_option == 0: points_x = 4 * [3 * D] points_y = 4 * [0] elif met_mast_option == 1: points_x = 4 * [200.0] points_y = 4 * [200.0] elif met_mast_option == 2: points_x = 4 * [20.0] points_y = 4 * [20.0] elif met_mast_option == 3: points_x = 4 * [305.0] points_y = 4 * [158.0] points_z = [30, 90, 150, 250] # Collect the points u_at_points = fmodel.sample_flow_at_points(points_x, points_y, points_z) ax[0].scatter(points_x, points_y, color="red", marker="x", label="Met mast") ax[0].grid() ax[0].set_xlabel("x [m]") ax[0].set_ylabel("y [m]") ax[0].legend() # Plot the velocities for z_idx, z in enumerate(points_z): ax[1].plot(wd_array, u_at_points[:, z_idx].flatten(), label=f"Speed at z={z} m") ax[1].grid() ax[1].legend() ax[1].set_xlabel("Wind Direction (deg)") ax[1].set_ylabel("Wind Speed (m/s)") plt.show() ================================================ FILE: examples/examples_get_flow/003_extract_turbulence_intensity_at_points.py ================================================ """Example: Extract turbulence intensity at points This example demonstrates the use of the sample_ti_at_points method of FlorisModel. sample_ti_at_points extracts the turbulence intensity (TI) information at user-specified locations in the flow. Specifically, this example returns the TI at a single x, y location and four different heights over a sweep of wind directions. This mimics the TI measurements of a met mast across all wind directions (at a fixed free stream wind speed). Try different values for met_mast_option to vary the location of the met mast within the two-turbine farm. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel # User options # FLORIS model to use. Note that legacy "turbopark" is not available, # "turboparkgauss" is configured with none turbulence model by default, # and "emgauss" does not contain an explicit turbulence model. floris_model = "gch" # Try "gch", "cc", "jensen" # Option to try different met mast locations met_mast_option = 0 # Try 0, 1, 2, 3 # Instantiate FLORIS model fmodel = FlorisModel("../inputs/" + floris_model + ".yaml") # Set up a two-turbine farm D = 126 fmodel.set(layout_x=[0, 3 * D], layout_y=[0, 3 * D]) fig, ax = plt.subplots(1, 2) fig.set_size_inches(10, 4) ax[0].scatter(fmodel.layout_x, fmodel.layout_y, color="black", label="Turbine") # Set the wind direction to run 360 degrees wd_array = np.arange(0, 360, 1) ws_array = 8.0 * np.ones_like(wd_array) ti_array = 0.06 * np.ones_like(wd_array) fmodel.set(wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array) # Simulate a met mast in between the turbines if met_mast_option == 0: points_x = 4 * [3 * D] points_y = 4 * [0] elif met_mast_option == 1: points_x = 4 * [200.0] points_y = 4 * [200.0] elif met_mast_option == 2: points_x = 4 * [20.0] points_y = 4 * [20.0] elif met_mast_option == 3: points_x = 4 * [305.0] points_y = 4 * [158.0] points_z = [30, 90, 150, 250] # Collect the points ti_at_points = fmodel.sample_ti_at_points(points_x, points_y, points_z) ax[0].scatter(points_x, points_y, color="red", marker="x", label="Met mast") ax[0].grid() ax[0].set_xlabel("x [m]") ax[0].set_ylabel("y [m]") ax[0].legend() # Plot the turbulence intensities for z_idx, z in enumerate(points_z): ax[1].plot(wd_array, ti_at_points[:, z_idx].flatten(), label=f"TI at z={z} m") ax[1].grid() ax[1].legend() ax[1].set_xlabel("Wind Direction (deg)") ax[1].set_ylabel("Turbulence Intensity (-)") plt.show() ================================================ FILE: examples/examples_get_flow/004_plot_velocity_deficit_profiles.py ================================================ """Example: Plot velocity deficit profiles This example illustrates how to plot velocity deficit profiles at several locations downstream of a turbine. Here we use the following definition: velocity_deficit = (homogeneous_wind_speed - u) / homogeneous_wind_speed , where u is the wake velocity obtained when the incoming wind speed is the same at all heights and equal to `homogeneous_wind_speed`. """ import matplotlib.pyplot as plt import numpy as np from matplotlib import ticker import floris.flow_visualization as flowviz from floris import FlorisModel from floris.flow_visualization import VelocityProfilesFigure from floris.utilities import reverse_rotate_coordinates_rel_west # The first two functions are just used to plot the coordinate system in which the # profiles are sampled. Please go to the main function to begin the example. def plot_coordinate_system(x_origin, y_origin, wind_direction): quiver_length = 1.4 * D plt.quiver( [x_origin, x_origin], [y_origin, y_origin], [quiver_length, quiver_length], [0, 0], angles=[270 - wind_direction, 360 - wind_direction], scale_units="x", scale=1, ) annotate_coordinate_system(x_origin, y_origin, quiver_length) def annotate_coordinate_system(x_origin, y_origin, quiver_length): x1 = np.array([quiver_length + 0.35 * D, 0.0]) x2 = np.array([0.0, quiver_length + 0.35 * D]) x3 = np.array([90.0, 90.0]) x, y, _ = reverse_rotate_coordinates_rel_west( fmodel.wind_directions, x1[None, :], x2[None, :], x3[None, :], x_center_of_rotation=0.0, y_center_of_rotation=0.0, ) x = np.squeeze(x, axis=0) + x_origin y = np.squeeze(y, axis=0) + y_origin plt.text(x[0], y[0], "$x_1$", bbox={"facecolor": "white"}) plt.text(x[1], y[1], "$x_2$", bbox={"facecolor": "white"}) if __name__ == "__main__": D = 125.88 # Turbine diameter hub_height = 90.0 homogeneous_wind_speed = 8.0 fmodel = FlorisModel("../inputs/gch.yaml") fmodel.set(layout_x=[0.0], layout_y=[0.0]) # ------------------------------ Single-turbine layout ------------------------------ # We first show how to sample and plot velocity deficit profiles on a single-turbine layout. # Lines are drawn on a horizontal plane to indicate were the velocity is sampled. downstream_dists = D * np.array([3, 5, 7]) # Sample three profiles along three corresponding lines that are all parallel to the y-axis # (cross-stream direction). The streamwise location of each line is given in `downstream_dists`. profiles = fmodel.sample_velocity_deficit_profiles( direction="cross-stream", downstream_dists=downstream_dists, homogeneous_wind_speed=homogeneous_wind_speed, ) horizontal_plane = fmodel.calculate_horizontal_plane(height=hub_height) fig, ax = plt.subplots(figsize=(6.4, 3)) flowviz.visualize_cut_plane(horizontal_plane, ax) colors = ["b", "g", "c"] for i, profile in enumerate(profiles): # Plot profile coordinates on the horizontal plane ax.plot(profile["x"], profile["y"], colors[i], label=f"x/D={downstream_dists[i] / D:.1f}") ax.set_xlabel("x [m]") ax.set_ylabel("y [m]") ax.set_title("Streamwise velocity in a horizontal plane: gauss velocity model") fig.tight_layout(rect=[0, 0, 0.82, 1]) ax.legend(bbox_to_anchor=[1.29, 1.04]) # Initialize a VelocityProfilesFigure. The workflow is similar to a matplotlib Figure: # Initialize it, plot data, and then customize it further if needed. profiles_fig = VelocityProfilesFigure( downstream_dists_D=downstream_dists / D, layout=["cross-stream"], coordinate_labels=["x/D", "y/D"], ) # Add profiles to the VelocityProfilesFigure. This method automatically matches the supplied # profiles to the initialized axes in the figure. profiles_fig.add_profiles(profiles, color="k") # Change velocity model to jensen, get the velocity deficit profiles, # and add them to the figure. floris_dict = fmodel.core.as_dict() floris_dict["wake"]["model_strings"]["velocity_model"] = "jensen" fmodel = FlorisModel(floris_dict) profiles = fmodel.sample_velocity_deficit_profiles( direction="cross-stream", downstream_dists=downstream_dists, homogeneous_wind_speed=homogeneous_wind_speed, resolution=400, ) profiles_fig.add_profiles(profiles, color="r") # The dashed reference lines show the extent of the rotor profiles_fig.add_ref_lines_x2([-0.5, 0.5]) for ax in profiles_fig.axs[0]: ax.xaxis.set_major_locator(ticker.MultipleLocator(0.2)) profiles_fig.axs[0, 0].legend(["gauss", "jensen"], fontsize=11) profiles_fig.fig.suptitle( "Velocity deficit profiles from different velocity models", fontsize=14, ) # -------------------------------- Two-turbine layout -------------------------------- # This is a two-turbine case where the wind direction is north-west. Velocity profiles # are sampled behind the second turbine. This illustrates the need for a # sampling-coordinate-system (x1, x2, x3) that is rotated such that x1 is always in the # streamwise direction. The user may define the origin of this coordinate system # (i.e. where to start sampling the profiles). wind_direction = 315.0 # Try to change this downstream_dists = D * np.array([3, 5]) floris_dict = fmodel.core.as_dict() floris_dict["wake"]["model_strings"]["velocity_model"] = "gauss" fmodel = FlorisModel(floris_dict) # Let (x_t1, y_t1) be the location of the second turbine x_t1 = 2 * D y_t1 = -2 * D fmodel.set(wind_directions=[wind_direction], layout_x=[0.0, x_t1], layout_y=[0.0, y_t1]) # Extract profiles at a set of downstream distances from the starting point (x_start, y_start) cross_profiles = fmodel.sample_velocity_deficit_profiles( direction="cross-stream", downstream_dists=downstream_dists, homogeneous_wind_speed=homogeneous_wind_speed, x_start=x_t1, y_start=y_t1, ) horizontal_plane = fmodel.calculate_horizontal_plane( height=hub_height, x_bounds=[-2 * D, 9 * D] ) ax = flowviz.visualize_cut_plane(horizontal_plane) colors = ["b", "g", "c"] for i, profile in enumerate(cross_profiles): ax.plot( profile["x"], profile["y"], colors[i], label=f"$x_1/D={downstream_dists[i] / D:.1f}$", ) ax.set_xlabel("x [m]") ax.set_ylabel("y [m]") ax.set_title("Streamwise velocity in a horizontal plane") ax.legend() plot_coordinate_system(x_origin=x_t1, y_origin=y_t1, wind_direction=wind_direction) # Sample velocity deficit profiles in the vertical direction at the same downstream # locations as before. We stay directly downstream of the turbine (i.e. x2 = 0). These # profiles are almost identical to the cross-stream profiles. However, we now explicitly # set the profile range. The default range is [-2 * D, 2 * D]. vertical_profiles = fmodel.sample_velocity_deficit_profiles( direction="vertical", profile_range=[-1.5 * D, 1.5 * D], downstream_dists=downstream_dists, homogeneous_wind_speed=homogeneous_wind_speed, x_start=x_t1, y_start=y_t1, ) profiles_fig = VelocityProfilesFigure( downstream_dists_D=downstream_dists / D, layout=["cross-stream", "vertical"], ) profiles_fig.add_profiles(cross_profiles + vertical_profiles, color="k") profiles_fig.set_xlim([-0.05, 0.85]) profiles_fig.axs[1, 0].set_ylim([-2.2, 2.2]) for ax in profiles_fig.axs[0]: ax.xaxis.set_major_locator(ticker.MultipleLocator(0.4)) profiles_fig.fig.suptitle( "Cross-stream profiles at hub-height, and\nvertical profiles at $x_2 = 0$", fontsize=14, ) plt.show() ================================================ FILE: examples/examples_heterogeneous/001_heterogeneous_inflow_single.py ================================================ """Example: Heterogeneous Inflow for single case This example illustrates how to set up a heterogeneous inflow condition in FLORIS. It: 1) Initializes FLORIS 2) Changes the wind farm layout 3) Changes the incoming wind speed, wind direction and turbulence intensity to a single condition 4) Sets up a heterogeneous inflow condition for that single condition 5) Runs the FLORIS simulation 6) Gets the power output of the turbines 7) Visualizes the horizontal plane at hub height """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, TimeSeries from floris.flow_visualization import visualize_heterogeneous_cut_plane from floris.layout_visualization import plot_turbine_labels # Initialize FlorisModel fmodel = FlorisModel("../inputs/gch.yaml") # Change the layout to a 4 turbine layout in a box fmodel.set(layout_x=[0, 0, 500.0, 500.0], layout_y=[0, 500.0, 0, 500.0]) # Set FLORIS to run for a single condition fmodel.set(wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06]) # Define the speed-ups of the heterogeneous inflow, and their locations. # Note that heterogeneity is only applied within the bounds of the points defined in the # heterogeneous_inflow_config dictionary. In this case, set the inflow to be 1.25x the ambient # wind speed for the upper turbines at y = 500m. speed_ups = [[1.0, 1.25, 1.0, 1.25]] # Note speed-ups has dimensions of n_findex X n_points x_locs = [-500.0, -500.0, 1000.0, 1000.0] y_locs = [-500.0, 1000.0, -500.0, 1000.0] # Create the configuration dictionary to be used for the heterogeneous inflow. heterogeneous_inflow_config = { "speed_multipliers": speed_ups, "x": x_locs, "y": y_locs, } # Set the heterogeneous inflow configuration fmodel.set(heterogeneous_inflow_config=heterogeneous_inflow_config) # Run the FLORIS simulation fmodel.run() # Get the power output of the turbines turbine_powers = fmodel.get_turbine_powers() / 1000.0 # Print the turbine powers print(f"Turbine 0 power = {turbine_powers[0, 0]:.1f} kW") print(f"Turbine 1 power = {turbine_powers[0, 1]:.1f} kW") print(f"Turbine 2 power = {turbine_powers[0, 2]:.1f} kW") print(f"Turbine 3 power = {turbine_powers[0, 3]:.1f} kW") # Extract the horizontal plane at hub height horizontal_plane = fmodel.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=90.0 ) # Plot the horizontal plane using the visualize_heterogeneous_cut_plane. # Note that this function is not very different than the standard # visualize_cut_plane except that it accepts the fmodel object in order to # visualize the boundary of the heterogeneous inflow region. fig, ax = plt.subplots() visualize_heterogeneous_cut_plane( horizontal_plane, fmodel=fmodel, ax=ax, title="Horizontal plane at hub height", color_bar=True, label_contours=True, ) plot_turbine_labels(fmodel, ax) ax.legend() plt.show() ================================================ FILE: examples/examples_heterogeneous/002_heterogeneous_using_wind_data.py ================================================ """Example: Heterogeneous Inflow using wind data When multiple cases are considered, the heterogeneous inflow conditions can be defined in two ways: 1. Passing heterogeneous_inflow_config to the set method, with P points, and speed_multipliers of size n_findex X P 2. More conveniently, building a HeterogeneousMap object that defines the speed_multipliers as a function of wind direction and/or wind speed and passing that to a WindData object. When the WindData object is passed to the set method, the heterogeneous_inflow_config is automatically generated for each findex by finding the nearest wind direction and/or wind speed in the HeterogeneousMap object. This example: 1) Implements heterogeneous inflow for a 4 turbine layout using both of the above methods 2) Compares the results of the two methods and shows that they are equivalent """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, HeterogeneousMap, TimeSeries, ) # Initialize FlorisModel fmodel = FlorisModel("../inputs/gch.yaml") # Change the layout to a 4 turbine layout in a box fmodel.set(layout_x=[0, 0, 500.0, 500.0], layout_y=[0, 500.0, 0, 500.0]) # Define a TimeSeries object with 4 wind directions and constant wind speed # and turbulence intensity time_series = TimeSeries( wind_directions=np.array([269.0, 270.0, 271.0, 282.0]), wind_speeds=8.0, turbulence_intensities=0.06, ) # Apply the time series to the FlorisModel fmodel.set(wind_data=time_series) # Define the x_locs to be used in the heterogeneous inflow configuration that form # a box around the turbines x_locs = [-500.0, -500.0, 1000.0, 1000.0] y_locs = [-500.0, 1000.0, -500.0, 1000.0] # Assume the speed-ups are defined such that they are the same 265-275 degrees and 275-285 degrees # If defining heterogeneous_inflow_config directly, then the speed_multipliers are of size # n_findex x P, where the first 3 rows are identical and the last row is different speed_multipliers = [ [1.0, 1.25, 1.0, 1.25], [1.0, 1.25, 1.0, 1.25], [1.0, 1.25, 1.0, 1.25], [1.0, 1.35, 1.0, 1.35], ] heterogeneous_inflow_config = { "speed_multipliers": speed_multipliers, "x": x_locs, "y": y_locs, } # Set the heterogeneous inflow configuration fmodel.set(heterogeneous_inflow_config=heterogeneous_inflow_config) # Run the FLORIS simulation fmodel.run() # Get the power output of the turbines turbine_powers = fmodel.get_turbine_powers() / 1000.0 # Now repeat using the wind_data object and HeterogeneousMap object # First, create the speed multipliers for the two wind directions speed_multipliers = [[1.0, 1.25, 1.0, 1.25], [1.0, 1.35, 1.0, 1.35]] # Now define the HeterogeneousMap object heterogeneous_map = HeterogeneousMap( x=x_locs, y=y_locs, speed_multipliers=speed_multipliers, wind_directions=[270.0, 280.0], ) # Print the HeterogeneousMap object print(heterogeneous_map) # Now create a new TimeSeries object including the heterogeneous_inflow_config_by_wd time_series = TimeSeries( wind_directions=np.array([269.0, 270.0, 271.0, 282.0]), wind_speeds=8.0, turbulence_intensities=0.06, heterogeneous_map=heterogeneous_map, ) # Note that previously, the a heterogeneous_inflow_config_by_wd, which only only # specification by wind direction was defined, and for backwards compatibility, # this is still accepted. However, the HeterogeneousMap object is more flexible. # The following code produces the same results as the previous code block. heterogeneous_inflow_config_by_wd = { "speed_multipliers": speed_multipliers, "x": x_locs, "y": y_locs, "wind_directions": [270.0, 280.0], } time_series = TimeSeries( wind_directions=np.array([269.0, 270.0, 271.0, 282.0]), wind_speeds=8.0, turbulence_intensities=0.06, heterogeneous_inflow_config_by_wd=heterogeneous_inflow_config_by_wd, ) # Apply the time series to the FlorisModel fmodel.set(wind_data=time_series) # Run the FLORIS simulation fmodel.run() # Get the power output of the turbines turbine_powers_by_wd = fmodel.get_turbine_powers() / 1000.0 # Plot the results wind_directions = fmodel.wind_directions fig, axarr = plt.subplots(2, 2, sharex=True, sharey=True, figsize=(10, 10)) axarr = axarr.flatten() for tindex in range(4): ax = axarr[tindex] ax.plot(wind_directions, turbine_powers[:, tindex], "ks-", label="Heterogeneous Inflow") ax.plot( wind_directions, turbine_powers_by_wd[:, tindex], ".--", label="Heterogeneous Inflow by WD" ) ax.set_title(f"Turbine {tindex}") ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") ax.legend() plt.show() ================================================ FILE: examples/examples_heterogeneous/003_heterogeneous_speedup_by_wd_and_ws.py ================================================ """Example: Heterogeneous Speedup by Wind Direction and Wind Speed The HeterogeneousMap object is a flexible way to define speedups as a function of wind direction and/or wind speed. It also contains methods to plot the speedup map for a given wind direction and wind speed. This example: 1) Instantiates a HeterogeneousMap object with speedups defined for two wind directions and two wind speeds 2) Visualizes the speedups for two particular combinations of wind direction and wind speed 3) Runs a FLORIS simulation using the HeterogeneousMap and visualizes the results """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, HeterogeneousMap, TimeSeries, ) from floris.flow_visualization import visualize_heterogeneous_cut_plane # Define a HeterogeneousMap object with speedups defined for two wind directions # and two wind speeds. The speedups imply no heterogeneity for the first wind direction # (0 degrees) with heterogeneity for the second wind direction (180 degrees) with the # specific speedups for this direction depending on the wind speed. heterogeneous_map = HeterogeneousMap( x=np.array([0.0, 0.0, 250.0, 500.0, 500.0]), y=np.array([0.0, 500.0, 250.0, 0.0, 500.0]), speed_multipliers=np.array( [ [1.0, 1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0, 1.0], [1.5, 1.0, 1.25, 1.5, 1.0], [1.0, 1.5, 1.25, 1.0, 1.5], ] ), wind_directions=np.array([270.0, 270.0, 90.0, 90.0]), wind_speeds=np.array([5.0, 10.0, 5.0, 10.0]), ) # Use the HeterogeneousMap object to plot the speedup map for 3 wd/ws combinations fig, axarr = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(15, 5)) ax = axarr[0] heterogeneous_map.plot_single_speed_multiplier( wind_direction=60.0, wind_speed=8.5, ax=ax, vmin=1.0, vmax=1.2 ) ax.set_title("Wind Direction = 60.0\nWind Speed = 8.5") ax = axarr[1] heterogeneous_map.plot_single_speed_multiplier( wind_direction=130.0, wind_speed=4.0, ax=ax, vmin=1.0, vmax=1.2 ) ax.set_title("Wind Direction = 130.0\nWind Speed = 4.0") ax = axarr[2] heterogeneous_map.plot_single_speed_multiplier( wind_direction=280.0, wind_speed=16.0, ax=ax, vmin=1.0, vmax=1.2 ) ax.set_title("Wind Direction = 280.0\nWind Speed = 16.0") fig.suptitle("Heterogeneous speedup map for several directions and wind speeds") # Initialize FlorisModel fmodel = FlorisModel("../inputs/gch.yaml") # Change the layout to a 2 turbine layout within the heterogeneous domain fmodel.set(layout_x=[200, 200.0], layout_y=[50, 450.0]) # Define a TimeSeries object with 3 wind directions and wind speeds # and turbulence intensity and using the above HeterogeneousMap object time_series = TimeSeries( wind_directions=np.array([275.0, 95.0, 75.0]), wind_speeds=np.array([7.0, 6.2, 8.0]), turbulence_intensities=0.06, heterogeneous_map=heterogeneous_map, ) # Apply the time series to the FlorisModel fmodel.set(wind_data=time_series) # Run the FLORIS simulation fmodel.run() # Visualize each of the findices fig, axarr = plt.subplots(3, 1, sharex=True, sharey=True, figsize=(10, 10)) for findex in range(3): ax = axarr[findex] horizontal_plane = fmodel.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=90.0, findex_for_viz=findex ) visualize_heterogeneous_cut_plane( cut_plane=horizontal_plane, fmodel=fmodel, ax=ax, title=( f"Wind Direction = {time_series.wind_directions[findex]}\n" f"Wind Speed = {time_series.wind_speeds[findex]}" ), ) plt.show() ================================================ FILE: examples/examples_heterogeneous/004_heterogeneous_2d_and_3d.py ================================================ """Example: Heterogeneous Inflow in 2D and 3D This example showcases the heterogeneous inflow capabilities of FLORIS. Heterogeneous flow can be defined in either 2- or 3-dimensions for a single condition. For the 2-dimensional case, it can be seen that the freestream velocity only varies in the x direction. For the 3-dimensional case, it can be seen that the freestream velocity only varies in the z direction. This is because of how the speed ups for each case were defined. More complex inflow conditions can be defined. For each case, we are plotting three slices of the resulting flow field: 1. Horizontal slice parallel to the ground and located at the hub height 2. Vertical slice parallel with the direction of the wind 3. Vertical slice parallel to to the turbine disc plane Since the intention is for plotting, only a single condition is run and in this case the heterogeneous_inflow_config is more convenient to use than heterogeneous_inflow_config_by_wd. However, the latter is more convenient when running multiple conditions. """ import matplotlib.pyplot as plt from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane # Initialize FLORIS with the given input file via FlorisModel. # Note that the heterogeneous flow is defined in the input file. The heterogeneous_inflow_config # dictionary is defined as below. The speed ups are multipliers of the ambient wind speed, # and the x and y are the locations of the speed ups. # # heterogeneous_inflow_config = { # 'speed_multipliers': [[2.0, 1.0, 2.0, 1.0]], # 'x': [-300.0, -300.0, 2600.0, 2600.0], # 'y': [ -300.0, 300.0, -300.0, 300.0], # } fmodel_2d = FlorisModel("../inputs/gch_heterogeneous_inflow.yaml") # Set shear to 0.0 to highlight the heterogeneous inflow fmodel_2d.set(wind_shear=0.0) # Using the FlorisModel functions for generating plots, run FLORIS # and extract 2D planes of data. horizontal_plane_2d = fmodel_2d.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=90.0 ) y_plane_2d = fmodel_2d.calculate_y_plane(x_resolution=200, z_resolution=100, crossstream_dist=0.0) cross_plane_2d = fmodel_2d.calculate_cross_plane( y_resolution=100, z_resolution=100, downstream_dist=500.0 ) # Create the plots fig, ax_list = plt.subplots(3, 1, figsize=(10, 8)) ax_list = ax_list.flatten() visualize_cut_plane( horizontal_plane_2d, ax=ax_list[0], title="Horizontal", color_bar=True, label_contours=True ) ax_list[0].set_xlabel("x") ax_list[0].set_ylabel("y") visualize_cut_plane( y_plane_2d, ax=ax_list[1], title="Streamwise profile", color_bar=True, label_contours=True ) ax_list[1].set_xlabel("x") ax_list[1].set_ylabel("z") visualize_cut_plane( cross_plane_2d, ax=ax_list[2], title="Spanwise profile at 500m downstream", color_bar=True, label_contours=True, ) ax_list[2].set_xlabel("y") ax_list[2].set_ylabel("z") # Define the speed ups of the heterogeneous inflow, and their locations. # For the 3-dimensional case, this requires x, y, and z locations. # The speed ups are multipliers of the ambient wind speed. speed_multipliers = [[1.0, 1.0, 2.0, 2.0, 1.0, 1.0, 2.0, 2.0]] x_locs = [-300.0, -300.0, -300.0, -300.0, 2600.0, 2600.0, 2600.0, 2600.0] y_locs = [-300.0, 300.0, -300.0, 300.0, -300.0, 300.0, -300.0, 300.0] z_locs = [540.0, 540.0, 0.0, 0.0, 540.0, 540.0, 0.0, 0.0] # Create the configuration dictionary to be used for the heterogeneous inflow. heterogeneous_inflow_config = { "speed_multipliers": speed_multipliers, "x": x_locs, "y": y_locs, "z": z_locs, } # Initialize FLORIS with the given input file. # Note that we initialize FLORIS with a homogenous flow input file, but # then configure the heterogeneous inflow via the reinitialize method. fmodel_3d = FlorisModel("../inputs/gch.yaml") # Set shear to 0.0 to highlight the heterogeneous inflow fmodel_3d.set(wind_shear=0.0) # Apply the heterogeneous inflow configuration fmodel_3d.set(heterogeneous_inflow_config=heterogeneous_inflow_config) # Using the FlorisModel functions for generating plots, run FLORIS # and extract 2D planes of data. horizontal_plane_3d = fmodel_3d.calculate_horizontal_plane( x_resolution=200, y_resolution=100, height=90.0 ) y_plane_3d = fmodel_3d.calculate_y_plane(x_resolution=200, z_resolution=100, crossstream_dist=0.0) cross_plane_3d = fmodel_3d.calculate_cross_plane( y_resolution=100, z_resolution=100, downstream_dist=500.0 ) # Create the plots fig, ax_list = plt.subplots(3, 1, figsize=(10, 8)) ax_list = ax_list.flatten() visualize_cut_plane( horizontal_plane_3d, ax=ax_list[0], title="Horizontal", color_bar=True, label_contours=True ) ax_list[0].set_xlabel("x") ax_list[0].set_ylabel("y") visualize_cut_plane( y_plane_3d, ax=ax_list[1], title="Streamwise profile", color_bar=True, label_contours=True ) ax_list[1].set_xlabel("x") ax_list[1].set_ylabel("z") visualize_cut_plane( cross_plane_3d, ax=ax_list[2], title="Spanwise profile at 500m downstream", color_bar=True, label_contours=True, ) ax_list[2].set_xlabel("y") ax_list[2].set_ylabel("z") plt.show() ================================================ FILE: examples/examples_layout_optimization/001_optimize_layout.py ================================================ """Example: Optimize Layout This example shows a simple layout optimization using the python module Scipy, optimizing for both annual energy production (AEP) and annual value production (AVP). First, a 4 turbine array is optimized such that the layout of the turbine produces the highest AEP based on the given wind resource. The turbines are constrained to a square boundary and a random wind resource is supplied. The results of the optimization show that the turbines are pushed to near the outer corners of the boundary, which, given the generally uniform wind rose, makes sense in order to maximize the energy production by minimizing wake interactions. Next, with the same boundary, the same 4 turbine array is optimized to maximize AVP instead of AEP, using the value table defined in the WindRose object, where value represents the value of the energy produced for a given wind condition (e.g., the price of electricity). In this example, value is defined to be significantly higher for northerly and southerly wind directions, and zero when the wind is from the east or west. Because the value is much higher when the wind is from the north or south, the turbines are spaced apart roughly evenly in the x direction while being relatively close in the y direction to avoid wake interactions for northerly and southerly winds. Although the layout results in large wake losses when the wind is from the east or west, these losses do not significantly impact the objective function because of the low value for those wind directions. """ import os import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, WindRose from floris.optimization.layout_optimization.layout_optimization_scipy import ( LayoutOptimizationScipy, ) # Define scipy optimization parameters opt_options = { "maxiter": 20, "disp": True, "iprint": 2, "ftol": 1e-12, "eps": 0.05, } # Initialize the FLORIS interface fi fmodel = FlorisModel('../inputs/gch.yaml') # Setup 72 wind directions with a 1 wind speed and frequency distribution wind_directions = np.arange(0, 360.0, 5.0) wind_speeds = np.array([8.0]) # Shape random frequency distribution to match number of wind directions and wind speeds freq_table = np.zeros((len(wind_directions), len(wind_speeds))) np.random.seed(1) freq_table[:,0] = (np.abs(np.sort(np.random.randn(len(wind_directions))))) freq_table = freq_table / freq_table.sum() # Define the value table such that the value of the energy produced is # significantly higher when the wind direction is close to the north or # south, and zero when the wind is from the east or west. Here, value is # given a mean value of 25 USD/MWh. value_table = (0.5 + 0.5*np.cos(2*np.radians(wind_directions)))**10 value_table = 25*value_table/np.mean(value_table) value_table = value_table.reshape((len(wind_directions),1)) # Establish a WindRose object wind_rose = WindRose( wind_directions=wind_directions, wind_speeds=wind_speeds, freq_table=freq_table, ti_table=0.06, value_table=value_table ) fmodel.set(wind_data=wind_rose) # The boundaries for the turbines, specified as vertices boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)] # Set turbine locations to 4 turbines in a rectangle D = 126.0 # rotor diameter for the NREL 5MW layout_x = [0, 0, 6 * D, 6 * D] layout_y = [0, 4 * D, 0, 4 * D] fmodel.set(layout_x=layout_x, layout_y=layout_y) # Setup the optimization problem to maximize AEP instead of value layout_opt = LayoutOptimizationScipy(fmodel, boundaries, optOptions=opt_options) # Run the optimization sol = layout_opt.optimize() # Get the resulting improvement in AEP print('... calculating improvement in AEP') fmodel.run() base_aep = fmodel.get_farm_AEP() / 1e6 fmodel.set(layout_x=sol[0], layout_y=sol[1]) fmodel.run() opt_aep = fmodel.get_farm_AEP() / 1e6 percent_gain = 100 * (opt_aep - base_aep) / base_aep # Print and plot the results print(f'Optimal layout: {sol}') print( f'Optimal layout improves AEP by {percent_gain:.1f}% ' f'from {base_aep:.1f} MWh to {opt_aep:.1f} MWh' ) layout_opt.plot_layout_opt_results() # reset to the original layout fmodel.set(layout_x=layout_x, layout_y=layout_y) # Now set up the optimization problem to maximize annual value production (AVP) # using the value table provided in the WindRose object. layout_opt = LayoutOptimizationScipy(fmodel, boundaries, optOptions=opt_options, use_value=True) # Run the optimization sol = layout_opt.optimize() # Get the resulting improvement in AVP print('... calculating improvement in annual value production (AVP)') fmodel.run() base_avp = fmodel.get_farm_AVP() / 1e6 fmodel.set(layout_x=sol[0], layout_y=sol[1]) fmodel.run() opt_avp = fmodel.get_farm_AVP() / 1e6 percent_gain = 100 * (opt_avp - base_avp) / base_avp # Print and plot the results print(f'Optimal layout: {sol}') print( f'Optimal layout improves AVP by {percent_gain:.1f}% ' f'from {base_avp:.1f} dollars to {opt_avp:.1f} dollars' ) layout_opt.plot_layout_opt_results() plt.show() ================================================ FILE: examples/examples_layout_optimization/002_optimize_layout_with_heterogeneity.py ================================================ """Example: Layout optimization with heterogeneous inflow This example shows a layout optimization using the geometric yaw option. It combines elements of layout optimization and heterogeneous inflow for demonstrative purposes. Heterogeneity in the inflow provides the necessary driver for coupled yaw and layout optimization to be worthwhile. First, a layout optimization is run without coupled yaw optimization; then a coupled optimization is run to show the benefits of coupled optimization when flows are heterogeneous. """ import os import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, WindRose from floris.optimization.layout_optimization.layout_optimization_scipy import ( LayoutOptimizationScipy, ) # Initialize FLORIS fmodel = FlorisModel("../inputs/gch.yaml") # Setup 2 wind directions (due east and due west) # and 1 wind speed with uniform probability wind_directions = np.array([90.0, 270.0]) n_wds = len(wind_directions) wind_speeds = np.array([8.0]) # Shape frequency distribution to match number of wind directions and wind speeds freq_table = np.ones((len(wind_directions), len(wind_speeds))) freq_table = freq_table / freq_table.sum() # The boundaries for the turbines, specified as vertices D = 126.0 # rotor diameter for the NREL 5MW size_D = 12 boundaries = [(0.0, 0.0), (size_D * D, 0.0), (size_D * D, 0.1), (0.0, 0.1), (0.0, 0.0)] # Set turbine locations to 4 turbines at corners of the rectangle # (optimal without flow heterogeneity) layout_x = [0.1, 0.3 * size_D * D, 0.6 * size_D * D] layout_y = [0, 0, 0] # Generate exaggerated heterogeneous inflow (same for all wind directions) speed_multipliers = np.repeat(np.array([0.5, 1.0, 0.5, 1.0])[None, :], n_wds, axis=0) x_locs = [0, size_D * D, 0, size_D * D] y_locs = [-D, -D, D, D] # Create the configuration dictionary to be used for the heterogeneous inflow. heterogeneous_inflow_config_by_wd = { "speed_multipliers": speed_multipliers, "wind_directions": wind_directions, "x": x_locs, "y": y_locs, } # Establish a WindRose object wind_rose = WindRose( wind_directions=wind_directions, wind_speeds=wind_speeds, freq_table=freq_table, ti_table=0.06, heterogeneous_inflow_config_by_wd=heterogeneous_inflow_config_by_wd, ) fmodel.set( layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose, ) # Setup and solve the layout optimization problem without heterogeneity maxiter = 100 layout_opt = LayoutOptimizationScipy( fmodel, boundaries, min_dist=2 * D, optOptions={"maxiter": maxiter} ) # Run the optimization np.random.seed(0) sol = layout_opt.optimize() # Get the resulting improvement in AEP print("... calcuating improvement in AEP") fmodel.run() base_aep = fmodel.get_farm_AEP() / 1e6 fmodel.set(layout_x=sol[0], layout_y=sol[1]) fmodel.run() opt_aep = fmodel.get_farm_AEP() / 1e6 percent_gain = 100 * (opt_aep - base_aep) / base_aep # Print and plot the results print(f"Optimal layout: {sol}") print( f"Optimal layout improves AEP by {percent_gain:.1f}% " f"from {base_aep:.1f} MWh to {opt_aep:.1f} MWh" ) layout_opt.plot_layout_opt_results() ax = plt.gca() fig = plt.gcf() sm = ax.tricontourf(x_locs, y_locs, speed_multipliers[0], cmap="coolwarm") fig.colorbar(sm, ax=ax, label="Speed multiplier") ax.legend(["_Optimization boundary", "Initial layout", "Optimized layout" ]) ax.set_title("Geometric yaw disabled") # Rerun the layout optimization with geometric yaw enabled print("\nReoptimizing with geometric yaw enabled.") fmodel.set(layout_x=layout_x, layout_y=layout_y) layout_opt = LayoutOptimizationScipy( fmodel, boundaries, min_dist=2 * D, enable_geometric_yaw=True, optOptions={"maxiter": maxiter} ) # Run the optimization np.random.seed(0) sol = layout_opt.optimize() # Get the resulting improvement in AEP print("... calcuating improvement in AEP") fmodel.set(yaw_angles=np.zeros_like(layout_opt.yaw_angles)) fmodel.run() base_aep = fmodel.get_farm_AEP() / 1e6 fmodel.set(layout_x=sol[0], layout_y=sol[1], yaw_angles=layout_opt.yaw_angles) fmodel.run() opt_aep = fmodel.get_farm_AEP() / 1e6 percent_gain = 100 * (opt_aep - base_aep) / base_aep # Print and plot the results print(f"Optimal layout: {sol}") print( f"Optimal layout improves AEP by {percent_gain:.1f}% " f"from {base_aep:.1f} MWh to {opt_aep:.1f} MWh" ) layout_opt.plot_layout_opt_results() ax = plt.gca() fig = plt.gcf() sm = ax.tricontourf(x_locs, y_locs, speed_multipliers[0], cmap="coolwarm") fig.colorbar(sm, ax=ax, label="Speed multiplier") ax.legend(["_Optimization boundary", "Initial layout", "Optimized layout"]) ax.set_title("Geometric yaw enabled") print( "Turbine geometric yaw angles for wind direction {0:.2f}".format(wind_directions[1]) + " and wind speed {0:.2f} m/s:".format(wind_speeds[0]), f"{layout_opt.yaw_angles[1, :]}", ) plt.show() ================================================ FILE: examples/examples_layout_optimization/003_genetic_random_search.py ================================================ """Example: Layout optimization with genetic random search This example shows a layout optimization using the genetic random search algorithm. It provides options for the users to try different distance probability mass functions for the random search perturbations. """ import matplotlib.pyplot as plt import numpy as np from scipy.stats import gamma from floris import FlorisModel, WindRose from floris.optimization.layout_optimization.layout_optimization_random_search import ( LayoutOptimizationRandomSearch, ) if __name__ == '__main__': # Set up FLORIS fmodel = FlorisModel('../inputs/gch.yaml') # Setup 72 wind directions with a random wind speed and frequency distribution wind_directions = np.arange(0, 360.0, 5.0) np.random.seed(1) wind_speeds = 8.0 + np.random.randn(1) * 0.0 # Shape frequency distribution to match number of wind directions and wind speeds freq = ( np.abs( np.sort( np.random.randn(len(wind_directions)) ) ) .reshape( ( len(wind_directions), len(wind_speeds) ) ) ) freq = freq / freq.sum() fmodel.set( wind_data=WindRose( wind_directions=wind_directions, wind_speeds=wind_speeds, freq_table=freq, ti_table=0.06 ) ) # Set the boundaries # The boundaries for the turbines, specified as vertices boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)] # Set turbine locations to 4 turbines in a rectangle D = 126.0 # rotor diameter for the NREL 5MW layout_x = [0, 0, 6 * D, 6 * D] layout_y = [0, 4 * D, 0, 4 * D] fmodel.set(layout_x=layout_x, layout_y=layout_y) # Perform the optimization distance_pmf = None # Other options that users can try # 1. # distance_pmf = {"d": [100, 1000], "p": [0.8, 0.2]} # 2. # p = gamma.pdf(np.linspace(0, 900, 91), 15, scale=20); p = p/p.sum() # distance_pmf = {"d": np.linspace(100, 1000, 91), "p": p} layout_opt = LayoutOptimizationRandomSearch( fmodel, boundaries, min_dist_D=5., seconds_per_iteration=10, total_optimization_seconds=60., distance_pmf=distance_pmf ) layout_opt.describe() layout_opt.plot_distance_pmf() layout_opt.optimize() layout_opt.plot_layout_opt_results() layout_opt.plot_progress() plt.show() ================================================ FILE: examples/examples_layout_optimization/004_generate_gridded_layout.py ================================================ """Example: Gridded layout design This example shows a layout optimization that places as many turbines as possible into a given boundary using a gridded layout pattern. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, WindRose from floris.optimization.layout_optimization.layout_optimization_gridded import ( LayoutOptimizationGridded, ) if __name__ == '__main__': # Load the Floris model fmodel = FlorisModel('../inputs/gch.yaml') # Set the boundaries # The boundaries for the turbines, specified as vertices boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)] # Set up the optimization object with 5D spacing layout_opt = LayoutOptimizationGridded( fmodel, boundaries, min_dist_D=5., # results in spacing of 5*125.88 = 629.4 m min_dist=None, # Alternatively, can specify spacing directly in meters ) layout_opt.optimize() # Note that the "initial" layout that is provided with the fmodel is # not used by the layout optimization. layout_opt.plot_layout_opt_results() plt.show() ================================================ FILE: examples/examples_layout_optimization/005_layout_optimization_complex_boundary.py ================================================ """Example: Separated boundaries layout optimization Demonstrates the capabilities of LayoutOptimizationGridded and LayoutOptimizationRandomSearch to optimize turbine layouts with complex boundaries. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, WindRose from floris.optimization.layout_optimization.layout_optimization_gridded import ( LayoutOptimizationGridded, ) from floris.optimization.layout_optimization.layout_optimization_random_search import ( LayoutOptimizationRandomSearch, ) if __name__ == '__main__': # Load the Floris model fmodel = FlorisModel('../inputs/gch.yaml') # Set the boundaries # The boundaries for the turbines, specified as vertices boundaries = [ [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)], [(1500.0, 0.0), (1500.0, 1000.0), (2500.0, 0.0), (1500.0, 0.0)], ] # Set up the wind data information wind_directions = np.arange(0, 360.0, 5.0) np.random.seed(1) wind_speeds = 8.0 + np.random.randn(1) * 0.0 # Shape frequency distribution to match number of wind directions and wind speeds freq = ( np.abs( np.sort( np.random.randn(len(wind_directions)) ) ) .reshape( ( len(wind_directions), len(wind_speeds) ) ) ) freq = freq / freq.sum() # Set wind data in the FlorisModel fmodel.set( wind_data=WindRose( wind_directions=wind_directions, wind_speeds=wind_speeds, freq_table=freq, ti_table=0.06 ) ) # Begin by placing as many turbines as possible using a gridded layout at 6D spacing layout_opt_gridded = LayoutOptimizationGridded( fmodel, boundaries, min_dist_D=6., min_dist=None, ) layout_opt_gridded.optimize() print("Gridded layout complete.") # Set the layout on the fmodel fmodel.set(layout_x=layout_opt_gridded.x_opt, layout_y=layout_opt_gridded.y_opt) # Update the layout using a random search optimization with 5D minimum spacing layout_opt_rs = LayoutOptimizationRandomSearch( fmodel, boundaries, min_dist_D=5., seconds_per_iteration=10, total_optimization_seconds=60., use_dist_based_init=False, ) layout_opt_rs.optimize() layout_opt_rs.plot_layout_opt_results( initial_locs_plotting_dict={"label": "Gridded initial layout"}, final_locs_plotting_dict={"label": "Random search optimized layout"}, ) plt.show() ================================================ FILE: examples/examples_load_optimization/001_lti_and_voc.py ================================================ """Example: LTI and VOC Behavior with Changing Wind Direction and Power Setpoints Demonstrate the behavior of the load turbulence intensity model and variable operating cost (VOC) model with respect to changing wind direction (which changes wake interactions) and changing power setpoints (which changes wake strength). """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, TimeSeries from floris.core.turbine.operation_models import ( POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED, ) from floris.optimization.load_optimization.load_optimization import ( compute_lti, compute_turbine_voc, ) # Declare a floris model with default configuration fmodel = FlorisModel(configuration="defaults") fmodel.set_operation_model("simple-derating") ## Wind direction sweep wind_directions = np.arange(0, 360, 1.0) # Assume uniform load ambient turbulence intensities ambient_lti = 0.1 * np.ones_like(wind_directions) # Declare a time series representing the wind direction sweep time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06 ) # Set the turbine layout to be a simple two turbine layout using the # time series object D = fmodel.core.farm.rotor_diameters[0] fmodel.set(layout_x=[0, D * 7], layout_y=[0.0, 0.0], wind_data=time_series) fmodel.run() # Compute the load turbulence intensity and VOC load_ti = compute_lti(fmodel, ambient_lti) voc = compute_turbine_voc(fmodel, A=2e-5, ambient_lti=ambient_lti) # Plot the TI and VOC for each turbine fig, ax = plt.subplots(2, 1, sharex=True) for t in range(fmodel.n_turbines): ax[0].plot(wind_directions, load_ti[:, t], label=f"Turbine {t}") ax[1].plot(wind_directions, voc[:, t], label=f"Turbine {t}") ax[0].set_ylabel("LTI [-]") ax[1].set_ylabel("VOC [$]") ax[1].set_xlabel("Wind Direction [deg]") ax[1].legend(loc="lower right") ax[0].grid() ax[1].grid() ax[0].set_title( "LTI and VOC vs Wind Direction\n" "X, Y Turbine Coordinates: T0: (0, 0), T1: (7D, 0)" ) ## Power setpoint sweep N = 50 time_series = TimeSeries( wind_directions=np.ones(N) * 270.0, # Single wind direction wind_speeds=8.0, turbulence_intensities=0.06, ) power_setpoints = np.column_stack([ np.linspace(5e6, POWER_SETPOINT_DISABLED, N), np.ones(N)*POWER_SETPOINT_DEFAULT ]) ambient_lti = np.ones(N) * 0.1 fmodel.set( layout_x=[0, D * 7], layout_y=[0.0, 0.0], wind_data=time_series, power_setpoints=power_setpoints, ) fmodel.run() # Compute the load turbulence intensity and VOC load_ti = compute_lti(fmodel, ambient_lti) voc = compute_turbine_voc(fmodel, A=2e-5, ambient_lti=ambient_lti) # Plot the TI and VOC for each turbine fig, ax = plt.subplots(2, 1, sharex=True) for t in range(fmodel.n_turbines): ax[0].plot(power_setpoints[:,0], load_ti[:, t], label=f"Turbine {t}") ax[1].plot(power_setpoints[:,0], voc[:, t], label=f"Turbine {t}") ax[0].set_ylabel("LTI [-]") ax[1].set_ylabel("VOC [$]") ax[1].set_xlabel("Power Setpoint (T0) [W]") ax[1].legend(loc="lower right") ax[0].grid() ax[1].grid() ax[0].set_title( "LTI and VOC vs T0 Power Setpoint\n" "Wind Direction = 270\u00B0, Wind Speed = 8 m/s" ) ## Load ambient TI sweep ambient_lti = np.linspace(0.05, 0.25, N) power_setpoints = POWER_SETPOINT_DEFAULT * np.ones((N, 2)) fmodel.set(power_setpoints=power_setpoints) fmodel.run() # Compute the load turbulence intensity and VOC load_ti = compute_lti(fmodel, ambient_lti) voc = compute_turbine_voc(fmodel, A=2e-5, ambient_lti=ambient_lti) # Plot the TI and VOC for each turbine fig, ax = plt.subplots(2, 1, sharex=True) for t in range(fmodel.n_turbines): ax[0].plot(ambient_lti, load_ti[:, t], label=f"Turbine {t}") ax[1].plot(ambient_lti, voc[:, t], label=f"Turbine {t}") ax[0].set_ylabel("LTI [-]") ax[1].set_ylabel("VOC [$]") ax[1].set_xlabel("Load Ambient TI [-]") ax[1].legend(loc="lower right") ax[0].grid() ax[1].grid() ax[0].set_title( "LTI and VOC vs Load Ambient TI\n" "Wind Direction = 270\u00B0, Wind Speed = 8 m/s" ) plt.show() ================================================ FILE: examples/examples_load_optimization/002_row_opt_example.py ================================================ """Example: Optimize a row of turbines This example optimizes the derating of a row of three turbines to maximize net revenue for a variety of combinations of wind direction, ambient "load TI" (LTI), and electricity values. The row is aligned when the wind direction is 270 degrees. """ import matplotlib.pyplot as plt import numpy as np import pandas as pd from floris import FlorisModel, TimeSeries from floris.core.turbine.operation_models import ( POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED, ) from floris.optimization.load_optimization.load_optimization import ( compute_farm_revenue, compute_farm_voc, compute_net_revenue, optimize_power_setpoints, ) # Parameters D = 126.0 d_spacing = 7.0 power_setpoint_levels = np.linspace(POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED, 10) n_turbines = 3 A = 4e-6 # Selected to demonstrate variation in derating selection # Declare a floris model with default configuration fmodel = FlorisModel(configuration="defaults") # Set up a row of turbines fmodel.set( layout_x=[i * D * d_spacing for i in range(n_turbines)], layout_y=[0.0 for i in range(n_turbines)], ) # Set operation to simple derating fmodel.set_operation_model("simple-derating") # Set up input conditions wind_directions = [] values = [] ambient_lti = [] for w_i in [240.0, 270.0]: for t_i in [0.1, 0.25]: for v_i in [1e-5, 1e-6]: wind_directions.append(w_i) values.append(v_i) ambient_lti.append(t_i) wind_directions = np.array(wind_directions) values = np.array(values) ambient_lti = np.array(ambient_lti) N = len(wind_directions) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06, values=values ) # Run the FLORIS model fmodel.set(wind_data=time_series) fmodel.run() # Set the initial power setpoints as no derating initial_power_setpoint = np.ones((N, n_turbines)) * 5e6 # Set these initial power setpoints fmodel.set(power_setpoints=initial_power_setpoint) fmodel.run() net_revenue_initial = compute_net_revenue(fmodel, A, ambient_lti) farm_voc_initial = compute_farm_voc(fmodel, A, ambient_lti) farm_revenue_initial = compute_farm_revenue(fmodel) # Compute the optimal derating levels given A_initial opt_power_setpoints, opt_net_revenue = optimize_power_setpoints( fmodel, A, ambient_lti, power_setpoint_initial=initial_power_setpoint, power_setpoint_levels=power_setpoint_levels, ) # Compute final values fmodel.set(power_setpoints=opt_power_setpoints) fmodel.run() net_revenue_opt = compute_net_revenue(fmodel, A, ambient_lti) farm_voc_opt = compute_farm_voc(fmodel, A, ambient_lti) farm_revenue_opt = compute_farm_revenue(fmodel) # Show the results fig, axarr = plt.subplots(7, 1, sharex=True, figsize=(10, 9)) # Plot the wind direction ax = axarr[0] ax.plot(wind_directions, color="k") ax.set_ylabel("Wind\n Direction (deg)", fontsize=7) ax.set_title("X, Y Turbine Coordinates: T0: (0, 0), T1: (7D, 0), T2: (14D, 0); Wind Speed = 8 m/s") # Plot the load TI ax = axarr[1] ax.plot(ambient_lti, color="k") ax.set_ylabel("LTI (-)", fontsize=7) # Plot the values ax = axarr[2] ax.plot(1e6 * values, color="k") ax.set_ylabel("Value of\n Electricity ($/MWh)", fontsize=7) # Plot the initial and final farm revenue ax = axarr[3] ax.plot(farm_revenue_initial, label="Initial", color="k") ax.plot(farm_revenue_opt, label="Optimized", color="r") ax.set_ylabel("Farm\n Revenue ($)", fontsize=7) ax.legend() # Plot the initial and final farm VOC ax = axarr[4] ax.plot(farm_voc_initial, label="Initial", color="k") ax.plot(farm_voc_opt, label="Optimized", color="r") ax.set_ylabel("Farm VOC ($)", fontsize=7) ax.legend() # Plot the initial and final farm net revenue ax = axarr[5] ax.plot(net_revenue_initial, label="Initial", color="k") ax.plot(net_revenue_opt, label="Optimized", color="r") ax.set_ylabel("Farm Net\nRevenue ($)", fontsize=7) ax.legend() # Plot the turbine deratings ax = axarr[6] for i in range(n_turbines): ax.plot(opt_power_setpoints[:, i] / 1000.0, label=f"Turbine {i}", lw=3 * n_turbines / (i + 1)) ax.set_ylabel("Power\n Setpoint (kW)", fontsize=7) ax.set_xlabel("Wind Condition and Electricity Value Combination") ax.legend() for ax in axarr: ax.grid(True) # Produce a heat-map to illustrate the chosen derating configurations by condition # Make a list of strings whose value is "waked" when wind_directions is at its maximum wake_status = ["waked" if wd == wind_directions.max() else "unwaked" for wd in wind_directions] # Make a list of strings whose value is "high_ambient_ti" # when ambient_lti is at its maximum, otherwise "low_ambient_ti" ambient_status = [ "high_ambient_ti" if ti == ambient_lti.max() else "low_ambient_ti" for ti in ambient_lti ] # Make a list of strings whose value is "high_value" when values is at its maximum, # otherwise "low_value" value_status = ["high_value" if v == values.max() else "low_value" for v in values] # Cat the elements of each of these strings together status = [f"{w} {a} {v}" for w, a, v in zip(wake_status, ambient_status, value_status)] # Combine into a dataframe df = pd.DataFrame( { "status": status, "Turbine 0": opt_power_setpoints[:, 0], "Turbine 1": opt_power_setpoints[:, 1], "Turbine 2": opt_power_setpoints[:, 2], } ) df = df.set_index("status") # Assuming your dataframe df is already set up as in your example fig, ax = plt.subplots(figsize=(10, 5)) # Get the data values as a numpy array data = df.values # Create the heatmap im = ax.imshow(data, cmap="coolwarm_r", aspect="auto") # Add the annotations to the cells for i in range(len(df.index)): for j in range(len(df.columns)): text = ax.text(j, i, f"{data[i, j]:.3f}", ha="center", va="center", color="w") # Set title ax.set_title("Optimized Power Setpoints (W)") # Set x and y tick labels ax.set_xticks(np.arange(len(df.columns))) ax.set_yticks(np.arange(len(df.index))) ax.set_xticklabels(df.columns) ax.set_yticklabels(df.index) # Rotate the x tick labels plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor") plt.tight_layout() plt.show() ================================================ FILE: examples/examples_multidim/001_multi_dimensional_cp_ct.py ================================================ """Example: Multi-dimensional power/thrust coefficient data This example creates a FLORIS instance and: 1) Makes a two-turbine layout 2) Demonstrates single ws/wd simulations 3) Demonstrates multiple ws/wd simulations with the modification of using a turbine definition that has a multi-dimensional power/thrust coefficient table. In the input file `gch_multi_dim_cp_ct.yaml`, the turbine_type points to a turbine definition, iea_15MW_floating_multi_dim_cp_ct.yaml located in the turbine_library, that supplies a multi-dimensional power/thrust coefficient data file in the form of a .csv file. This .csv file contains two additional conditions to define power and thrust coefficient values for: Tp for wave period, and Hs for wave height. For every combination of Tp and Hs defined, a power/thrust coefficient/Wind speed table of values is also defined. It is required for this .csv file to have the last 3 columns be ws, power, and thrust coefficient. In order for this table to be used, the flag 'multi_dimensional_cp_ct' must be present and set to true in the turbine definition. With this flag enabled, the solver will down-select to use the interpolant defined at the closest conditions. The user must supply these conditions in the main input file under the 'flow_field' section, e.g.: NOTE: The multi-dimensional power/thrust coefficient data used in this example is fictional for the purposes of facilitating this example. The power/thrust coefficient values for the different wave conditions are scaled values of the original power/thrust coefficient data for the IEA 15MW turbine. flow_field: multidim_conditions: Tp: 2.5 Hs: 3.01 The solver will then use the nearest-neighbor interpolant. These conditions are currently global and used to select the interpolant at each turbine. Also note in the example below that there is a specific method for computing powers when using turbines with multi-dimensional power/thrust coefficient data under FlorisModel, called 'get_turbine_powers_multidim'. The normal 'get_turbine_powers' method will not work. """ import numpy as np from floris import FlorisModel # Initialize FLORIS with the given input file. fmodel = FlorisModel("../inputs/gch_multi_dim_cp_ct.yaml") # Convert to a simple two turbine layout fmodel.set(layout_x=[0.0, 500.0], layout_y=[0.0, 0.0]) # Single wind speed and wind direction print("\n========================= Single Wind Direction and Wind Speed =========================") # Get the turbine powers assuming 1 wind speed and 1 wind direction fmodel.set(wind_directions=[270.0], wind_speeds=[8.0], turbulence_intensities=[0.06]) # Set the yaw angles to 0 yaw_angles = np.zeros([1, 2]) # 1 wind direction and wind speed, 2 turbines fmodel.set(yaw_angles=yaw_angles) # Calculate fmodel.run() # Get the turbine powers turbine_powers = fmodel.get_turbine_powers() / 1000.0 print("The turbine power matrix should be of dimensions 1 findex X 2 Turbines") print(turbine_powers) print("Shape: ", turbine_powers.shape) # Single wind speed and multiple wind directions print("\n========================= Single Wind Direction and Multiple Wind Speeds ===============") wind_speeds = np.array([8.0, 9.0, 10.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) yaw_angles = np.zeros([3, 2]) # 3 wind directions/ speeds, 2 turbines fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, yaw_angles=yaw_angles, ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1000.0 print("The turbine power matrix should be of dimensions 3 findex X 2 Turbines") print(turbine_powers) print("Shape: ", turbine_powers.shape) # Multiple wind speeds and multiple wind directions print("\n========================= Multiple Wind Directions and Multiple Wind Speeds ============") wind_speeds = np.tile([8.0, 9.0, 10.0], 3) wind_directions = np.repeat([260.0, 270.0, 280.0], 3) turbulence_intensities = 0.06 * np.ones_like(wind_speeds) yaw_angles = np.zeros([9, 2]) # 9 wind directions/ speeds, 2 turbines fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, yaw_angles=yaw_angles, ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1000.0 print("The turbine power matrix should be of dimensions 9 WD/WS X 2 Turbines") print(turbine_powers) print("Shape: ", turbine_powers.shape) ================================================ FILE: examples/examples_multidim/002_multi_dimensional_cp_ct_2Hs.py ================================================ """Example: Multi-dimensional power/thrust coefficient with 2 Hs values This example follows the previous example but shows the effect of changing the Hs setting. Updated in FLORIS v4.6 to use new array-based multidimensional functionality, where a different multidimensional condition can be specified for each findex. Prior to v4.6, when only scalar multidimensional conditions were supported, this example used two separate FLORIS runs to compute the two Hs cases. NOTE: The multi-dimensional power/thrust coefficient data used in this example is fictional for the purposes of facilitating this example. The power/thrust coefficient values for the different wave conditions are scaled values of the original power/thrust coefficient data for the IEA 15MW turbine. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, TimeSeries # Initialize FLORIS with the given input file. fmodel = FlorisModel("../inputs/gch_multi_dim_cp_ct.yaml") fmodel.set(layout_x=[0.0, 500.0, 1000.0], layout_y=[0.0, 0.0, 0.0]) # Conditions to evaluate n_wind_speeds = 16 wind_speeds = np.tile(np.linspace(5, 20, n_wind_speeds), 2) # Sweep wind speeds multidim_conditions = { "Tp": np.array([2.5]*n_wind_speeds*2), "Hs": np.array([3.1]*n_wind_speeds + [1.0]*n_wind_speeds), } time_series = TimeSeries( wind_directions=270.0, wind_speeds=wind_speeds, turbulence_intensities=0.06, multidim_conditions=multidim_conditions ) # Set wind, multidim conditios onto the FlorisModel fmodel.set(wind_data=time_series) # Calculate wakes fmodel.run() # Collect the turbine powers in kW turbine_powers = fmodel.get_turbine_powers() / 1000.0 # Plot the power in each case and the difference in power fig, axarr = plt.subplots(1, 3, sharex=True, figsize=(12, 4)) for t_idx in range(3): ax = axarr[t_idx] ax.plot( wind_speeds[:n_wind_speeds], turbine_powers[:n_wind_speeds, t_idx], color="k", label="Hs=3.1 (5)" ) ax.plot( wind_speeds[n_wind_speeds:], turbine_powers[n_wind_speeds:, t_idx], color="r", label="Hs=1.0" ) ax.grid(True) ax.set_xlabel("Wind Speed (m/s)") ax.set_title(f"Turbine {t_idx}") axarr[0].set_ylabel("Power (kW)") axarr[0].legend() fig.suptitle("Power of each turbine") plt.show() ================================================ FILE: examples/examples_multidim/003_multi_dimensional_cp_ct_TI.py ================================================ """Example: Multi-dimensional power/thrust coefficients with turbulence intensity This example follows the previous example, but demonstrating how a multidimensional turbine can be used to model the effect of turbulence intensity on power and thrust coefficient. Updated in FLORIS v4.6 to demonstrate new array-based multidimensional functionality. In the updated version, setting loop_over_ti to True will run a single scalar multidimensional condition at a time and call fmodel.run() multiple times, similar to the behavior prior to v4.6. Alternatively, users may set loop_over_ti to False to use the new (and improved!) functionality and run all TI values in a single call to fmodel.run(). NOTE: The multi-dimensional power/thrust coefficient data used in this example is fictional for the purposes of facilitating this example and the power values shown should not be taken as representative of the actual effect of turbulence intensity on power/thrust coefficient. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel, TimeSeries # Initialize FLORIS with the given input file. fmodel = FlorisModel("../inputs/gch_multi_dim_cp_ct_TI.yaml") # Set both cases to 3 turbine layout fmodel.set(layout_x=[0.0, 500.0, 1000.0], layout_y=[0.0, 0.0, 0.0]) loop_over_ti = True # Otherwise, will set all TIs at once. # Use a sweep of wind speeds wind_speeds = np.arange(5, 20, 0.1) if loop_over_ti: # In this case, we will run() the fmodel multiple times, one for each turbulence intensity. # We set a scalar multidim_conditions, which is broadcast over the wind speeds. time_series = TimeSeries( wind_directions=270.0, wind_speeds=wind_speeds, turbulence_intensities=0.06 ) fmodel.set(wind_data=time_series) # Loop over different turbulence intensities using set() # When running with TI=0.10, the multidimensional data handler will find the nearest defined # value of 0.08 and use that data. fig, axarr = plt.subplots(1, 3, sharex=True, figsize=(12, 4)) for ti, col in zip([0.06, 0.10], ["k", "r"]): fmodel.set(multidim_conditions={"TI": ti}) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1000.0 for t_idx in range(3): ax = axarr[t_idx] ax.plot(wind_speeds, turbine_powers[:, t_idx], color=col, label="TI={0:.2f}".format(ti)) else: # Set all conditions to evaluate at once, and call fmodel.run() only once. # We set multidim_conditions to be arrays matching the number of findices of the fmodel. # Note that turbulence_intensities on the TimeSeries object is _not_ linked to the # multidim_conditions time_series = TimeSeries( wind_directions=270.0, wind_speeds=np.tile(wind_speeds, 2), turbulence_intensities=0.06, # This value will be used for wake calculations only multidim_conditions={"TI": np.array([0.06]*len(wind_speeds) + [0.10]*len(wind_speeds))}, ) fmodel.set(wind_data=time_series) fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1000.0 fig, axarr = plt.subplots(1, 3, sharex=True, figsize=(12, 4)) for t_idx in range(3): ax = axarr[t_idx] ax.plot(wind_speeds, turbine_powers[:len(wind_speeds), t_idx], color="k", label="TI=0.06") ax.plot(wind_speeds, turbine_powers[len(wind_speeds):, t_idx], color="r", label="TI=0.10") # Plot aesthetics for t_idx in range(3): axarr[t_idx].grid(True) axarr[t_idx].set_xlabel("Wind Speed (m/s)") axarr[t_idx].set_title(f"Turbine {t_idx}") axarr[0].legend() plt.show() ================================================ FILE: examples/examples_operation_models/001_compare_yaw_loss.py ================================================ """Example: Compare yaw loss under different operation models This example shows demonstrates how the Controller-dependent operation model (developed at TUM) and Unified Momentum Model (developed at MIT) alter how a turbine loses power to yaw compared to the standard cosine loss model. """ import itertools import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel # Parameters N = 101 # How many steps to cover yaw range in yaw_max = 30 # Maximum yaw to test # Set up the yaw angle sweep yaw_angles = np.zeros((N,1)) yaw_angles[:,0] = np.linspace(-yaw_max, yaw_max, N) # Create the FLORIS model fmodel = FlorisModel("../inputs/gch.yaml") fmodel.set( layout_x=[0.0], layout_y=[0.0], wind_directions=np.ones(N) * 270.0, wind_speeds=1.0*np.ones(N), # Will be replaced turbulence_intensities=0.06 * np.ones(N), yaw_angles=yaw_angles, ) # Define a function to evaluate the power under various yaw angles def evaluate_yawed_power(wsp: float, op_model: str) -> float: print(f"Evaluating model: {op_model} wind speed: {wsp} m/s") fmodel.set(wind_speeds=wsp * np.ones(N)) fmodel.set_operation_model(op_model) fmodel.run() return fmodel.get_turbine_powers()[:, 0] # Loop over the operational models and wind speeds to compare op_models = ["simple", "cosine-loss", "controller-dependent", "unified-momentum"] wind_speeds = [8.0, 11.5, 15.0] results = {} for op_model, wsp in itertools.product(op_models, wind_speeds): results[(op_model, wsp)] = evaluate_yawed_power(wsp, op_model) # Plot the results fig, axes = plt.subplots(1, len(wind_speeds), sharey=True, figsize=(10, 5)) colors = ["k", "k", "C0", "C1"] linestyles = ["dashed", "solid", "dashed", "dotted"] for wsp, ax in zip(wind_speeds, axes): ax.set_title(f"Wind speed: {wsp} m/s") ax.set_xlabel("Yaw angle [deg]") ax.grid(True) for op_model, c, ls in zip(op_models, colors, linestyles): upstream_yaw_angle = yaw_angles[:, 0] central_power = results[(op_model, wsp)][upstream_yaw_angle == 0] ax.plot( upstream_yaw_angle, results[(op_model, wsp)] / central_power, label=op_model, color=c, linestyle=ls, ) ax.grid(True) ax.legend() axes[0].set_xlabel("Yaw angle [deg]") axes[0].set_ylabel("Normalized turbine power [-]") plt.show() ================================================ FILE: examples/examples_turbine/001_reference_turbines.py ================================================ """Example: Reference turbines For each reference wind turbine in the turbine library, make a small figure showing its power and thrust coefficient curves and demonstrate its power loss to yaw. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel ws_array = np.arange(0.1, 30, 0.2) wd_array = 270.0 * np.ones_like(ws_array) turbulence_intensities = 0.06 * np.ones_like(ws_array) yaw_angles = np.linspace(-30, 30, 60) wind_speed_to_test_yaw = 11 # Grab the gch model fmodel = FlorisModel("../inputs/gch.yaml") # Make one turbine simulation fmodel.set(layout_x=[0], layout_y=[0]) # Apply wind directions and wind speeds fmodel.set( wind_speeds=ws_array, wind_directions=wd_array, turbulence_intensities=turbulence_intensities ) # Get a list of available turbine models provided through FLORIS, and remove # multi-dimensional power/thrust coefficient turbine definitions as they require different handling turbines = [ t.stem for t in fmodel.core.farm.internal_turbine_library.iterdir() if t.suffix == ".yaml" and ("multi_dim" not in t.stem) ] # Declare a set of figures for comparing cp and ct across models fig_pow_ct, axarr_pow_ct = plt.subplots(2, 1, sharex=True, figsize=(10, 10)) # For each turbine model available plot the basic info for t in turbines: # Set t as the turbine fmodel.set(turbine_type=[t]) fmodel.reset_operation() # Remove any previously applied yaw angles # Since we are changing the turbine type, make a matching change to the reference wind height fmodel.assign_hub_height_to_ref_height() # Plot power and ct onto the fig_pow_ct plot axarr_pow_ct[0].plot( fmodel.core.farm.turbine_map[0].power_thrust_table["wind_speed"], fmodel.core.farm.turbine_map[0].power_thrust_table["power"], label=t, ) axarr_pow_ct[0].grid(True) axarr_pow_ct[0].legend() axarr_pow_ct[0].set_ylabel("Power (kW)") axarr_pow_ct[1].plot( fmodel.core.farm.turbine_map[0].power_thrust_table["wind_speed"], fmodel.core.farm.turbine_map[0].power_thrust_table["thrust_coefficient"], label=t, ) axarr_pow_ct[1].grid(True) axarr_pow_ct[1].legend() axarr_pow_ct[1].set_ylabel("Ct (-)") axarr_pow_ct[1].set_xlabel("Wind Speed (m/s)") # Create a figure fig, axarr = plt.subplots(1, 2, figsize=(10, 5)) # Try a few density for density in [1.15, 1.225, 1.3]: fmodel.set(air_density=density) # POWER CURVE ax = axarr[0] fmodel.set( wind_speeds=ws_array, wind_directions=wd_array, turbulence_intensities=turbulence_intensities, ) fmodel.reset_operation() # Remove any previously applied yaw angles fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e3 if density == 1.225: ax.plot(ws_array, turbine_powers, label="Air Density = %.3f" % density, lw=2, color="k") else: ax.plot(ws_array, turbine_powers, label="Air Density = %.3f" % density, lw=1) ax.grid(True) ax.legend() ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Power (kW)") # Power loss to yaw, try a range of yaw angles ax = axarr[1] fmodel.set( wind_speeds=[wind_speed_to_test_yaw], wind_directions=[270.0], turbulence_intensities=[0.06], ) yaw_result = [] for yaw in yaw_angles: fmodel.set(yaw_angles=np.array([[yaw]])) fmodel.run() turbine_powers = fmodel.get_turbine_powers().flatten() / 1e3 yaw_result.append(turbine_powers[0]) if density == 1.225: ax.plot(yaw_angles, yaw_result, label="Air Density = %.3f" % density, lw=2, color="k") else: ax.plot(yaw_angles, yaw_result, label="Air Density = %.3f" % density, lw=1) # ax.plot(yaw_angles,yaw_result,label='Air Density = %.3f' % density) ax.grid(True) ax.legend() ax.set_xlabel("Yaw Error (deg)") ax.set_ylabel("Power (kW)") ax.set_title("Wind Speed = %.1f" % wind_speed_to_test_yaw) # Give a suptitle fig.suptitle(t) plt.show() ================================================ FILE: examples/examples_turbine/002_multiple_turbine_types.py ================================================ """Example: Multiple turbine types This example uses an input file where multiple turbine types are defined. The first two turbines are the NREL 5MW, and the third turbine is the IEA 10MW. """ import matplotlib.pyplot as plt import floris.flow_visualization as flowviz from floris import FlorisModel # Initialize FLORIS with the given input file. # For basic usage, FlorisModel provides a simplified and expressive # entry point to the simulation routines. fmodel = FlorisModel("../inputs/gch_multiple_turbine_types.yaml") # Using the FlorisModel functions for generating plots, run FLORIS # and extract 2D planes of data. horizontal_plane = fmodel.calculate_horizontal_plane(x_resolution=200, y_resolution=100, height=90) y_plane = fmodel.calculate_y_plane(x_resolution=200, z_resolution=100, crossstream_dist=0.0) cross_plane = fmodel.calculate_cross_plane( y_resolution=100, z_resolution=100, downstream_dist=500.0 ) # Create the plots fig, ax_list = plt.subplots(3, 1, figsize=(10, 8)) ax_list = ax_list.flatten() flowviz.visualize_cut_plane(horizontal_plane, ax=ax_list[0], title="Horizontal") flowviz.visualize_cut_plane(y_plane, ax=ax_list[1], title="Streamwise profile") flowviz.visualize_cut_plane(cross_plane, ax=ax_list[2], title="Spanwise profile") plt.show() ================================================ FILE: examples/examples_turbine/003_specify_turbine_power_curve.py ================================================ """Example: Specify turbine power curve This example demonstrates how to specify a turbine model based on a power and thrust curve for the wind turbine, as well as possible physical parameters (which default to the parameters of the NREL 5MW reference turbine). Note that it is also possible to have a .yaml created, if the file_path argument to build_turbine_dict is set. """ import matplotlib.pyplot as plt import numpy as np from floris import FlorisModel from floris.turbine_library import build_cosine_loss_turbine_dict # Generate an example turbine power and thrust curve for use in the FLORIS model powers_orig = np.array([0, 30, 200, 500, 1000, 2000, 4000, 4000, 4000, 4000, 4000]) wind_speeds = np.array([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]) power_coeffs = powers_orig[1:] / (0.5 * 126.0**2 * np.pi / 4 * 1.225 * wind_speeds[1:] ** 3) turbine_data_dict = { "wind_speed": list(wind_speeds), "power_coefficient": [0] + list(power_coeffs), "thrust_coefficient": [0, 0.9, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.25, 0.2], } turbine_dict = build_cosine_loss_turbine_dict( turbine_data_dict, "example_turbine", file_name=None, generator_efficiency=1, hub_height=90, cosine_loss_exponent_yaw=1.88, cosine_loss_exponent_tilt=1.88, rotor_diameter=126, TSR=8, ref_air_density=1.225, ref_tilt=5, ) fmodel = FlorisModel("../inputs/gch.yaml") wind_speeds = np.linspace(1, 15, 100) wind_directions = 270 * np.ones_like(wind_speeds) turbulence_intensities = 0.06 * np.ones_like(wind_speeds) # Replace the turbine(s) in the FLORIS model with the created one fmodel.set( layout_x=[0], layout_y=[0], wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, turbine_type=[turbine_dict], reference_wind_height=fmodel.reference_wind_height ) fmodel.run() powers = fmodel.get_farm_power() specified_powers = ( np.array(turbine_data_dict["power_coefficient"]) * 0.5 * turbine_dict["power_thrust_table"]["ref_air_density"] * turbine_dict["rotor_diameter"] ** 2 * np.pi / 4 * np.array(turbine_data_dict["wind_speed"]) ** 3 ) / 1000 fig, ax = plt.subplots(1, 1, sharex=True) ax.scatter(wind_speeds, powers / 1000, color="C0", s=5, label="Test points") ax.scatter( turbine_data_dict["wind_speed"], specified_powers, color="red", s=20, label="Specified points" ) ax.grid() ax.set_xlabel("Wind speed [m/s]") ax.set_ylabel("Power [kW]") ax.legend() plt.show() ================================================ FILE: examples/examples_turbopark/001_compare_turbopark_implementations.py ================================================ """Example: Compare TurbOPark model implementations This example demonstrates a new implementation of the TurbOPark model that is more faithful to the original description provided by Pedersen et al and uses the sequential_solver, and compares it to the existing implementation in Floris. """ import matplotlib.pyplot as plt import numpy as np import pandas as pd import floris.flow_visualization as flowviz from floris import FlorisModel, TimeSeries from floris.turbine_library import build_cosine_loss_turbine_dict # Note: "new" is used to refer to the new implementation of TurbOPark, which is # more faithful to the description provided by Pedersen et al. (2022). "orig" # is used to refer to the existing TurbOPark implementation in Floris (which # was based on Ørsted's Matlab code, originally from Nygaard et al. (2020). ### Build a constant CT turbine model for use in comparisons (not realistic) const_CT_turb = build_cosine_loss_turbine_dict( turbine_data_dict={ "wind_speed":[0.0, 30.0], "power":[0.0, 1.0], # Not realistic but won't be used here "thrust_coefficient":[0.75, 0.75] }, turbine_name="ConstantCT", rotor_diameter=120.0, hub_height=100.0, ref_tilt=0.0, ) ### Start by visualizing a single turbine in and its wake with the new model # Load the new TurboPark implementation and switch to constant CT turbine fmodel_new = FlorisModel("../inputs/turboparkgauss_cubature.yaml") fmodel_new.set( turbine_type=[const_CT_turb], reference_wind_height=fmodel_new.reference_wind_height ) fmodel_new.run() u0 = fmodel_new.wind_speeds[0] col_orig = "C0" col_new = "C1" # Get plane of points for visualization rotor_diameter = 120.0 x_resolution=1501 y_resolution=201 z_resolution=100 x_bounds = [-5*rotor_diameter, 25*rotor_diameter] horizontal_plane = fmodel_new.calculate_horizontal_plane( x_resolution=x_resolution, y_resolution=y_resolution, height=100.0, x_bounds=x_bounds ) # Visualize the flows with a horizontal slice fig, ax = plt.subplots(3,1) fig.set_size_inches(7, 10) flowviz.visualize_cut_plane( horizontal_plane, ax=ax[0], label_contours=True, title="Horizontal plane" ) ax[0].set_xlabel("x [m]") ax[0].set_ylabel("y [m]") # Get points and velocities, normalized by rotor diameter and freestream velocity x_locs_norm = horizontal_plane.df.x1[:x_resolution]/rotor_diameter y_locs_norm = horizontal_plane.df.x2[::x_resolution]/rotor_diameter u_norm = horizontal_plane.df.u[150100:151601]/u0 # Plot downstream velocities ax[1].plot(x_locs_norm, u_norm, color=col_new) ax[1].set_xlabel("Downstream distance [D]") ax[1].set_ylabel("Normalized velocity [-]") ax[1].grid() ax[1].set_xlim([x/rotor_diameter for x in x_bounds]) # Plot axial velocities at various downstream distances for loc in np.append(251, np.linspace(350,750,5)): #range(200,1200,200): u_norm = horizontal_plane.df.u[int(loc)::x_resolution]/u0 alpha = 1.0 - (loc-250)/1000 ax[2].plot(y_locs_norm, u_norm, label=str((loc-250)/50)+"D downstream", alpha=alpha, c=col_new) ax[2].legend() ax[2].set_xlabel("Radial distance [D]") ax[2].set_ylabel("Normalized velocity [-]") ax[2].grid() ax[2].set_xlim([-2, 2]) ### Look at the wake profile at a single downstream distance for a range of wind directions # Load the original TurboPark implementation and switch to constant CT turbine fmodel_orig = FlorisModel("../inputs/turbopark_cubature.yaml") fmodel_orig.set( turbine_type=[const_CT_turb], reference_wind_height=fmodel_orig.reference_wind_height ) # Set up and solve flows wd_array = np.arange(225,315,0.1) wind_data_wd_sweep = TimeSeries( wind_speeds=8.0, wind_directions=wd_array, turbulence_intensities=0.06 ) fmodel_orig.set( layout_x = [0.0, 600.0], layout_y = [0.0, 0.0], wind_data=wind_data_wd_sweep ) fmodel_orig.run() # Extract output velocities at downstream turbine orig_vels_ds = fmodel_orig.turbine_average_velocities[:,1] u0 = fmodel_orig.wind_speeds[0] # Get freestream wind speed for normalization # Set up and solve flows; extract velocities at downstream turbine fmodel_new.set( layout_x = [0.0, 600.0], layout_y = [0.0, 0.0], wind_data=wind_data_wd_sweep ) fmodel_new.run() new_vels_ds = fmodel_new.turbine_average_velocities[:,1] # Load comparison data (generated by running Ørsted's Matlab code # https://github.com/OrstedRD/TurbOPark) df_twinpark = pd.read_csv("comparison_data/WindDirection_Sweep_Orsted.csv") # Plot the data and compare fig, ax = plt.subplots(2, 1) fig.set_size_inches(7, 10) ax[0].plot(wd_array, orig_vels_ds/u0, label="Floris - TurbOPark", c=col_orig) ax[0].plot(wd_array, new_vels_ds/u0, label="Floris - TurbOPark-Gauss", c=col_new) df_twinpark.plot("wd", "wws", ax=ax[0], linestyle="--", color="k", label="Orsted - TurbOPark") ax[0].set_xlabel("Wind direction [deg]") ax[0].set_ylabel("Normalized rotor averaged waked wind speed [-]") ax[0].set_xlim(240,300) ax[0].set_ylim(0.65,1.05) ax[0].legend() ax[0].grid() ### Now, look at velocities along a row of ten turbines aligned with the flow layout_x = np.linspace(0.0, 5400.0, 10) layout_y = np.zeros_like(layout_x) turbines = range(len(layout_x)) wind_data_row = TimeSeries( wind_speeds=np.array([8.0]), wind_directions=270.0, turbulence_intensities=0.06 ) fmodel_orig.set( layout_x=layout_x, layout_y=layout_y, wind_data=wind_data_row ) fmodel_new.set( layout_x=layout_x, layout_y=layout_y, wind_data=wind_data_row ) # Run and extract flow velocities at the turbines fmodel_orig.run() orig_vels_row = fmodel_orig.turbine_average_velocities fmodel_new.run() new_vels_row = fmodel_new.turbine_average_velocities u0 = fmodel_orig.wind_speeds[0] # Get freestream wind speed for normalization # Load comparison data df_rowpark = pd.read_csv("comparison_data/Rowpark_Orsted.csv") # Plot the data and compare ax[1].scatter( turbines, df_rowpark["wws"], s=80, marker="o", c="k", label="Orsted - TurbOPark" ) ax[1].scatter( turbines, orig_vels_row/u0, s=20, marker="o", c=col_orig, label="Floris - TurbOPark" ) ax[1].scatter( turbines, new_vels_row/u0, s=20, marker="o", c=col_new, label="Floris - TurbOPark_Gauss" ) ax[1].set_xlabel("Turbine number") ax[1].set_ylabel("Normalized rotor averaged wind speed [-]") ax[1].set_ylim(0.25, 1.05) ax[1].legend() ax[1].grid() plt.show() ================================================ FILE: examples/examples_turbopark/comparison_data/Rowpark_Orsted.csv ================================================ wtg_nr,wws 1,1 2,0.709920677983239 3,0.615355749367675 4,0.551410465937128 5,0.502600655337247 6,0.46316755609319 7,0.430238792036599 8,0.402137593655074 9,0.377783142608699 10,0.356429516711137 ================================================ FILE: examples/examples_turbopark/comparison_data/WindDirection_Sweep_Orsted.csv ================================================ wd,wws 240,1 240.1,1 240.2,1 240.3,1 240.4,1 240.5,1 240.6,1 240.7,1 240.8,1 240.9,1 241,1 241.1,1 241.2,1 241.3,1 241.4,1 241.5,1 241.6,1 241.7,1 241.8,1 241.9,1 242,1 242.1,1 242.2,1 242.3,1 242.4,1 242.5,1 242.6,1 242.7,1 242.8,1 242.9,1 243,1 243.1,1 243.2,1 243.3,1 243.4,1 243.5,1 243.6,1 243.7,1 243.8,1 243.9,1 244,1 244.1,1 244.2,1 244.3,1 244.4,1 244.5,1 244.6,1 244.7,1 244.8,1 244.9,1 245,1 245.1,1 245.2,1 245.3,1 245.4,1 245.5,1 245.6,1 245.7,1 245.8,1 245.9,1 246,1 246.1,1 246.2,1 246.3,1 246.4,1 246.5,1 246.6,1 246.7,1 246.8,1 246.9,1 247,1 247.1,1 247.2,1 247.3,1 247.4,1 247.5,1 247.6,1 247.7,1 247.8,1 247.9,1 248,1 248.1,1 248.2,1 248.3,1 248.4,1 248.5,1 248.6,1 248.7,1 248.8,1 248.9,1 249,1 249.1,1 249.2,1 249.3,1 249.4,1 249.5,1 249.6,1 249.7,1 249.8,1 249.9,1 250,1 250.1,1 250.2,1 250.3,1 250.4,1 250.5,1 250.6,1 250.7,1 250.8,1 250.9,1 251,1 251.1,1 251.2,1 251.3,1 251.4,1 251.5,1 251.6,1 251.7,1 251.8,1 251.9,1 252,1 252.1,1 252.2,1 252.3,1 252.4,1 252.5,1 252.6,1 252.7,1 252.8,1 252.9,1 253,1 253.1,1 253.2,1 253.3,1 253.4,1 253.5,1 253.6,1 253.7,1 253.8,1 253.9,1 254,1 254.1,1 254.2,1 254.3,1 254.4,1 254.5,1 254.6,1 254.7,1 254.8,1 254.9,1 255,1 255.1,1 255.2,1 255.3,1 255.4,1 255.5,1 255.6,1 255.7,1 255.8,0.993538664077203 255.9,0.993163657321804 256,0.992770495327025 256.1,0.992358313847786 256.2,0.991925610053336 256.3,0.991472642429319 256.4,0.990998649684299 256.5,0.990501674053008 256.6,0.98998221270739 256.7,0.989439530053926 256.8,0.988871463143701 256.9,0.98827850586591 257,0.987659983170125 257.1,0.987013702153042 257.2,0.986339994876325 257.3,0.985638312213906 257.4,0.984906427366052 257.5,0.984144540999792 257.6,0.983352231745704 257.7,0.982527231179693 257.8,0.981669653228834 257.9,0.980779200463236 258,0.979853551280177 258.1,0.978892787644138 258.2,0.977896729926102 258.3,0.976862989104334 258.4,0.975791697415342 258.5,0.97468276936836 258.6,0.973533728520613 258.7,0.972344839286829 258.8,0.971116091762022 258.9,0.96984490254454 259,0.968531765562319 259.1,0.96717671860786 259.2,0.965777049935718 259.3,0.96433358834902 259.4,0.962846386758057 259.5,0.961312584298864 259.6,0.959733454176337 259.7,0.958109028463984 259.8,0.956436278914363 259.9,0.954717032094708 260,0.952951261644229 260.1,0.951135758385933 260.2,0.949273005115791 260.3,0.9473627123794 260.4,0.945401986275212 260.5,0.943393553818119 260.6,0.941336847389892 260.7,0.939229736935285 260.8,0.937074820137102 260.9,0.934871338387179 261,0.932618039295836 261.1,0.930317338994172 261.2,0.927968281561089 261.3,0.925570580132477 261.4,0.923126425330613 261.5,0.92063467246435 261.6,0.918096054594981 261.7,0.915512504495281 261.8,0.912882714836796 261.9,0.910208452415198 262,0.90749137471691 262.1,0.904730057903725 262.2,0.901927269278807 262.3,0.899084382259158 262.4,0.896199952561399 262.5,0.893277634297122 262.6,0.890318154571413 262.7,0.887321318579717 262.8,0.884290346375519 262.9,0.881225831351371 263,0.878128716993821 263.1,0.875001814308954 263.2,0.871845680663186 263.3,0.86866222402995 263.4,0.865453897868534 263.5,0.862221346668234 263.6,0.858967205627504 263.7,0.855693649591388 263.8,0.852401558002709 263.9,0.84909399374919 264,0.845772832335429 264.1,0.842439740485255 264.2,0.839097468583526 264.3,0.835747987758945 264.4,0.832393539537496 264.5,0.829036552890671 264.6,0.825679322258521 264.7,0.822324260240938 264.8,0.818973626984464 264.9,0.815630241610804 265,0.812296234810439 265.1,0.808973964717588 265.2,0.805666720853472 265.3,0.802376128951174 265.4,0.799105063046563 265.5,0.795856576665548 265.6,0.792632199808558 265.7,0.789435477805356 265.8,0.786268700366033 265.9,0.783133519200963 266,0.780034380951238 266.1,0.776972265521718 266.2,0.773949449942233 266.3,0.770970689004856 266.4,0.76803592392432 266.5,0.7651486501479 266.6,0.762312258910748 266.7,0.759527003855557 266.8,0.756797459385703 266.9,0.754125094991387 267,0.751510723346101 267.1,0.748960214180543 267.2,0.746472579043332 267.3,0.744050181507681 267.4,0.741698131597992 267.5,0.739414734981526 267.6,0.737204041555762 267.7,0.735068602520699 267.8,0.733007537734214 267.9,0.731026342177643 268,0.729124534970449 268.1,0.727302277422094 268.2,0.725566656688728 268.3,0.723913754893217 268.4,0.722346595328026 268.5,0.720869110121062 268.6,0.719478470489897 268.7,0.718179259631063 268.8,0.716971888690611 268.9,0.715854804612834 269,0.714834246915094 269.1,0.713906819981133 269.2,0.713073268024026 269.3,0.712338934517223 269.4,0.711699026367391 269.5,0.71115682475977 269.6,0.710713804599393 269.7,0.710366653964001 269.8,0.710120302126759 269.9,0.709972224114491 270,0.709920677983239 270.1,0.709972224114491 270.2,0.710120302126759 270.3,0.710366653964001 270.4,0.710713804599393 270.5,0.71115682475977 270.6,0.711699026367391 270.7,0.712338934517223 270.8,0.713073268024026 270.9,0.713906819981133 271,0.714834246915094 271.1,0.715854804612834 271.2,0.716971888690611 271.3,0.718179259631063 271.4,0.719478470489897 271.5,0.720869110121062 271.6,0.722346595328026 271.7,0.723913754893217 271.8,0.725566656688728 271.9,0.727302277422094 272,0.729124534970449 272.1,0.731026342177643 272.2,0.733007537734214 272.3,0.735068602520699 272.4,0.737204041555762 272.5,0.739414734981526 272.6,0.741698131597992 272.7,0.744050181507681 272.8,0.746472579043332 272.9,0.748960214180543 273,0.751510723346101 273.1,0.754125094991387 273.2,0.756797459385703 273.3,0.759527003855557 273.4,0.762312258910748 273.5,0.7651486501479 273.6,0.76803592392432 273.7,0.770970689004856 273.8,0.773949449942233 273.9,0.776972265521718 274,0.780034380951238 274.1,0.783133519200963 274.2,0.786268700366033 274.3,0.789435477805356 274.4,0.792632199808558 274.5,0.795856576665548 274.6,0.799105063046563 274.7,0.802376128951174 274.8,0.805666720853472 274.9,0.808973964717588 275,0.812296234810439 275.1,0.815630241610804 275.2,0.818973626984464 275.3,0.822324260240938 275.4,0.825679322258521 275.5,0.829036552890671 275.6,0.832393539537496 275.7,0.835747987758945 275.8,0.839097468583526 275.9,0.842439740485255 276,0.845772832335429 276.1,0.84909399374919 276.2,0.852401558002709 276.3,0.855693649591388 276.4,0.858967205627504 276.5,0.862221346668234 276.6,0.865453897868534 276.7,0.86866222402995 276.8,0.871845680663186 276.9,0.875001814308954 277,0.878128716993821 277.1,0.881225831351371 277.2,0.884290346375519 277.3,0.887321318579717 277.4,0.890318154571413 277.5,0.893277634297122 277.6,0.896199952561399 277.7,0.899084382259158 277.8,0.901927269278807 277.9,0.904730057903725 278,0.90749137471691 278.1,0.910208452415198 278.2,0.912882714836796 278.3,0.915512504495281 278.4,0.918096054594981 278.5,0.92063467246435 278.6,0.923126425330613 278.7,0.925570580132477 278.8,0.927968281561089 278.9,0.930317338994172 279,0.932618039295836 279.1,0.934871338387179 279.2,0.937074820137102 279.3,0.939229736935285 279.4,0.941336847389892 279.5,0.943393553818119 279.6,0.945401986275212 279.7,0.9473627123794 279.8,0.949273005115791 279.9,0.951135758385933 280,0.952951261644229 280.1,0.954717032094708 280.2,0.956436278914363 280.3,0.958109028463984 280.4,0.959733454176337 280.5,0.961312584298864 280.6,0.962846386758057 280.7,0.96433358834902 280.8,0.965777049935718 280.9,0.96717671860786 281,0.968531765562319 281.1,0.96984490254454 281.2,0.971116091762022 281.3,0.972344839286829 281.4,0.973533728520613 281.5,0.97468276936836 281.6,0.975791697415342 281.7,0.976862989104334 281.8,0.977896729926102 281.9,0.978892787644138 282,0.979853551280177 282.1,0.980779200463236 282.2,0.981669653228834 282.3,0.982527231179693 282.4,0.983352231745704 282.5,0.984144540999792 282.6,0.984906427366052 282.7,0.985638312213906 282.8,0.986339994876325 282.9,0.987013702153042 283,0.987659983170125 283.1,0.98827850586591 283.2,0.988871463143701 283.3,0.989439530053926 283.4,0.98998221270739 283.5,0.990501674053008 283.6,0.990998649684299 283.7,0.991472642429319 283.8,0.991925610053336 283.9,0.992358313847786 284,0.992770495327025 284.1,0.993163657321804 284.2,0.993538664077203 284.3,1 284.4,1 284.5,1 284.6,1 284.7,1 284.8,1 284.9,1 285,1 285.1,1 285.2,1 285.3,1 285.4,1 285.5,1 285.6,1 285.7,1 285.8,1 285.9,1 286,1 286.1,1 286.2,1 286.3,1 286.4,1 286.5,1 286.6,1 286.7,1 286.8,1 286.9,1 287,1 287.1,1 287.2,1 287.3,1 287.4,1 287.5,1 287.6,1 287.7,1 287.8,1 287.9,1 288,1 288.1,1 288.2,1 288.3,1 288.4,1 288.5,1 288.6,1 288.7,1 288.8,1 288.9,1 289,1 289.1,1 289.2,1 289.3,1 289.4,1 289.5,1 289.6,1 289.7,1 289.8,1 289.9,1 290,1 290.1,1 290.2,1 290.3,1 290.4,1 290.5,1 290.6,1 290.7,1 290.8,1 290.9,1 291,1 291.1,1 291.2,1 291.3,1 291.4,1 291.5,1 291.6,1 291.7,1 291.8,1 291.9,1 292,1 292.1,1 292.2,1 292.3,1 292.4,1 292.5,1 292.6,1 292.7,1 292.8,1 292.9,1 293,1 293.1,1 293.2,1 293.3,1 293.4,1 293.5,1 293.6,1 293.7,1 293.8,1 293.9,1 294,1 294.1,1 294.2,1 294.3,1 294.4,1 294.5,1 294.6,1 294.7,1 294.8,1 294.9,1 295,1 295.1,1 295.2,1 295.3,1 295.4,1 295.5,1 295.6,1 295.7,1 295.8,1 295.9,1 296,1 296.1,1 296.2,1 296.3,1 296.4,1 296.5,1 296.6,1 296.7,1 296.8,1 296.9,1 297,1 297.1,1 297.2,1 297.3,1 297.4,1 297.5,1 297.6,1 297.7,1 297.8,1 297.9,1 298,1 298.1,1 298.2,1 298.3,1 298.4,1 298.5,1 298.6,1 298.7,1 298.8,1 298.9,1 299,1 299.1,1 299.2,1 299.3,1 299.4,1 299.5,1 299.6,1 299.7,1 299.8,1 299.9,1 300,1 300.1,1 300.2,1 300.3,1 300.4,1 300.5,1 300.6,1 300.7,1 300.8,1 300.9,1 301,1 301.1,1 301.2,1 301.3,1 301.4,1 301.5,1 301.6,1 301.7,1 301.8,1 301.9,1 302,1 302.1,1 302.2,1 302.3,1 302.4,1 302.5,1 302.6,1 302.7,1 302.8,1 302.9,1 303,1 303.1,1 303.2,1 303.3,1 303.4,1 303.5,1 303.6,1 303.7,1 303.8,1 303.9,1 304,1 304.1,1 304.2,1 304.3,1 304.4,1 304.5,1 304.6,1 304.7,1 304.8,1 304.9,1 305,1 305.1,1 305.2,1 305.3,1 305.4,1 305.5,1 305.6,1 305.7,1 305.8,1 305.9,1 306,1 306.1,1 306.2,1 306.3,1 306.4,1 306.5,1 306.6,1 306.7,1 306.8,1 306.9,1 307,1 307.1,1 307.2,1 307.3,1 307.4,1 307.5,1 307.6,1 307.7,1 307.8,1 307.9,1 308,1 308.1,1 308.2,1 308.3,1 308.4,1 308.5,1 308.6,1 308.7,1 308.8,1 308.9,1 309,1 309.1,1 309.2,1 309.3,1 309.4,1 309.5,1 309.6,1 309.7,1 309.8,1 309.9,1 310,1 ================================================ FILE: examples/examples_uncertain/001_uncertain_model_params.py ================================================ """Example: Uncertain Model Parameters This example demonstrates how to use the UncertainFlorisModel class to analyze the impact of uncertain wind direction on power results. """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, UncertainFlorisModel, ) # Instantiate FlorisModel for comparison fmodel = FlorisModel("../inputs/gch.yaml") # GCH model ################################################ # Resolution parameters ################################################ # The resolution parameters are used to define the precision of the wind direction, # wind speed, and turbulence intensity and control parameters. All the inputs # passed into the UncertainFlorisModel class are rounded to this resolution. Then # following expansion, non-unique cases are removed. Here we apply the default # resolution parameters. wd_resolution = 1.0 # Degree ws_resolution = 1.0 # m/s ti_resolution = 0.01 # Decimal fraction yaw_resolution = 1.0 # Degree power_setpoint_resolution = 100.0 # kW ################################################ # wd_sample_points ################################################ # The wind direction sample points (wd_sample_points) parameter is used to define # the number of points to sample the wind direction uncertainty. For example, # if the the single condition to analyze is 270 degrees, and the wd_sample_points # is [-2, -1, 0, 1 ,2], then the cases to be run and weighted # will be 268, 269, 270, 271, 272. If not supplied default is # [-2 * wd_std, -1 * wd_std, 0, wd_std, 2 * wd_std] wd_sample_points = [-6, -3, 0, 3, 6] ################################################ # WT_STD ################################################ # The wind direction standard deviation (wd_std) parameter is the primary input # to the UncertainFlorisModel class. This parameter is used to weight the points # following expansion by the wd_sample_points. The smaller the value, the closer # the weighting will be to the nominal case. wd_std = 3 # Default is 3 degrees ################################################ # Verbosity ################################################ # Setting verbose = True will print out the sizes of teh cases run verbose = True ################################################ # Define the UncertainFlorisModel ################################################ print('*** Instantiating UncertainFlorisModel ***') ufmodel = UncertainFlorisModel( "../inputs/gch.yaml", wd_resolution=wd_resolution, ws_resolution=ws_resolution, ti_resolution=ti_resolution, yaw_resolution=yaw_resolution, power_setpoint_resolution=power_setpoint_resolution, wd_std=wd_std, wd_sample_points=wd_sample_points, verbose=verbose, ) ################################################ # Run the models ################################################ # Define an inflow where wind direction is swept while # wind speed and turbulence intensity are held constant wind_directions = np.arange(240.0, 300.0, 1.0) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06, ) # Define a two turbine farm and apply the inflow D = 126.0 layout_x = np.array([0, D * 6]) layout_y = [0, 0] fmodel.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) print('*** Setting UncertainFlorisModel to 60 Wind Direction Inflow ***') ufmodel.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) # Run both models fmodel.run() ufmodel.run() # Collect the nominal and uncertain farm power turbine_powers_nom = fmodel.get_turbine_powers() / 1e3 turbine_powers_unc = ufmodel.get_turbine_powers() / 1e3 farm_powers_nom = fmodel.get_farm_power() / 1e3 farm_powers_unc_3 = ufmodel.get_farm_power() / 1e3 # Plot results fig, axarr = plt.subplots(1, 3, figsize=(15, 5)) ax = axarr[0] ax.plot(wind_directions, turbine_powers_nom[:, 0].flatten(), color="k", label="Nominal power") ax.plot( wind_directions, turbine_powers_unc[:, 0].flatten(), color="r", label="Power with uncertainty", ) ax.set_ylim(bottom=0) ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") ax.set_title("Upstream Turbine") ax = axarr[1] ax.plot(wind_directions, turbine_powers_nom[:, 1].flatten(), color="k", label="Nominal power") ax.plot( wind_directions, turbine_powers_unc[:, 1].flatten(), color="r", label="Power with uncertainty", ) ax.set_ylim(bottom=0) ax.set_title("Downstream Turbine") ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") ax = axarr[2] ax.plot(wind_directions, farm_powers_nom.flatten(), color="k", label="Nominal farm power") ax.plot( wind_directions, farm_powers_unc_3.flatten(), color="r", label="Farm power with uncertainty", ) ax.set_ylim(bottom=0) ax.set_title("Farm Power") ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") plt.show() ================================================ FILE: examples/examples_uncertain/002_approx_floris_model.py ================================================ """Example: Approximate Model Parameters This example demonstrates how to use the UncertainFlorisModel class to analyze the impact of uncertain wind direction on power results. """ from time import perf_counter as timerpc import matplotlib.pyplot as plt import numpy as np from floris import ( ApproxFlorisModel, FlorisModel, TimeSeries, ) # Generate time series data using a random walk on wind speeds with constant wind direction N = 5000 n_turbines = 25 # Random walk on wind speed with values between 5 and 20 m/s ws = np.ones(N) * 10 rng = np.random.default_rng(0) ws_ll = 5 ws_ul = 20 for i in range(1, N): ws[i] = ws[i - 1] + rng.normal(0, 0.25) if ws[i] < ws_ll: ws[i] = ws_ll if ws[i] > ws_ul: ws[i] = ws_ul time_series = TimeSeries(wind_directions=270.0, wind_speeds=ws, turbulence_intensities=0.06) # Instantiate a FlorisModel and an ApproxFlorisModel fmodel = FlorisModel("../inputs/gch.yaml") afmodel = ApproxFlorisModel("../inputs/gch.yaml", ws_resolution=0.5) # Set both models to an n_turbine layout and use the above time series layout_x = np.array([i*500 for i in range(n_turbines)]) layout_y = np.zeros(n_turbines) fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=time_series) afmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=time_series) # Now time both runs to show the speedup from approximating the wind speed start = timerpc() fmodel.run() end = timerpc() print(f"FlorisModel run time: {end - start} s") start = timerpc() afmodel.run() end = timerpc() print(f"ApproxFlorisModel run time: {end - start} s") # Plot the power output from both models fig, ax = plt.subplots() ax.plot(fmodel.get_farm_power(), label="FlorisModel") ax.plot(afmodel.get_farm_power(), label="ApproxFlorisModel") ax.set_xlabel("Time Step") ax.set_ylabel("Farm Power [W]") ax.legend() ax.grid(True) # Compare the expected power results print(f"Expected power from FlorisModel: {fmodel.get_expected_farm_power()/1E6:0.2f} MW") print(f"Expected power from ApproxFlorisModel: {afmodel.get_expected_farm_power()/1E6:0.2f} MW") plt.show() ================================================ FILE: examples/examples_uncertain/003_uncertain_model_with_parallelization.py ================================================ """Example: Uncertain Model With Parallelization This example demonstrates how to combined the parallelized model with the uncertain model """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, UncertainFlorisModel, ) from floris.par_floris_model import ParFlorisModel if __name__ == "__main__": # Following the refactoring of ParFlorisModel, the UncertainFlorisModel can be # parallelized by passing the ParFlorisModel as the model to be run. This example # demonstrates the usage and shows that the result obtained from the UncertainFlorisModel # with and without parallelization is the same. The results are compared to the nominal # results. # Instantiate a FlorisModel and ParallelFlorisModel using the GCH model fmodel = FlorisModel("../inputs/gch.yaml") pfmodel = ParFlorisModel("../inputs/gch.yaml") # Use the above model to declare a serial and parallel UncertainFlorisModel ufmodel = UncertainFlorisModel(fmodel) pufmodel = UncertainFlorisModel(pfmodel) # Define an inflow where wind direction is swept while # wind speed and turbulence intensity are held constant wind_directions = np.arange(240.0, 300.0, 1.0) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06, ) # Define a two turbine farm and apply the inflow D = 126.0 layout_x = np.array([0, D * 6]) layout_y = [0, 0] # Apply to fmodel, ufmodel, and pufmodel fmodel.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) ufmodel.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) pufmodel.set( layout_x=layout_x, layout_y=layout_y, wind_data=time_series, ) # Run the models fmodel.run() ufmodel.run() pufmodel.run() # Collect the farm power results from each model farm_powers_nom = fmodel.get_farm_power() / 1e3 farm_powers_unc = ufmodel.get_farm_power() / 1e3 farm_powers_punc = pufmodel.get_farm_power() / 1e3 # Compare the results fig, ax = plt.subplots() ax.plot(wind_directions, farm_powers_nom.flatten(), 'k-', label="Nominal power") ax.plot(wind_directions, farm_powers_unc.flatten(), 'bs-', label="Uncertain power") ax.plot(wind_directions, farm_powers_punc.flatten(), 'r.--', label="Parallel uncertain power") ax.grid(True) ax.legend() ax.set_xlabel("Wind Direction (deg)") ax.set_ylabel("Power (kW)") plt.show() ================================================ FILE: examples/examples_visualizations/001_layout_visualizations.py ================================================ """Example: Layout Visualizations Demonstrate the use of all the functions within the layout_visualization module """ import matplotlib.pyplot as plt import numpy as np import floris.layout_visualization as layoutviz from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane # Create the plotting objects using matplotlib fig, axarr = plt.subplots(3, 3, figsize=(16, 10), sharex=False) axarr = axarr.flatten() MIN_WS = 1.0 MAX_WS = 8.0 # Initialize FLORIS with the given input file. fmodel = FlorisModel("../inputs/gch.yaml") # Change to 5-turbine layout with a wind direction from northwest fmodel.set( layout_x=[0, 0, 1000, 1000, 1000], layout_y=[0, 500, 0, 500, 1000], wind_directions=[300] ) # Plot 1: Visualize the flow ax = axarr[0] # Plot a horizatonal slice of the initial configuration horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0) visualize_cut_plane( horizontal_plane, ax=ax, min_speed=MIN_WS, max_speed=MAX_WS, ) # Plot the turbine points, setting the color to white layoutviz.plot_turbine_points(fmodel, ax=ax, plotting_dict={"color": "w"}) ax.set_title("Flow visualization and turbine points") # Plot 2: Show a particular flow case ax = axarr[1] turbine_names = [f"T{i}" for i in [10, 11, 12, 13, 22]] layoutviz.plot_turbine_points(fmodel, ax=ax) layoutviz.plot_turbine_labels( fmodel, ax=ax, turbine_names=turbine_names, show_bbox=True, bbox_dict={"facecolor": "r"} ) ax.set_title("Show turbine names with a red bounding box") # Plot 2: Show turbine rotors on flow ax = axarr[2] fmodel.set(yaw_angles=np.array([[0., 30., 0., 0., 0.]])) horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0) visualize_cut_plane(horizontal_plane, ax=ax, min_speed=MIN_WS, max_speed=MAX_WS) layoutviz.plot_turbine_rotors(fmodel, ax=ax, yaw_angles=np.array([[0.0, 30.0, 0.0, 0.0, 0.0]])) ax.set_title("Flow visualization with yawed turbine") # Plot 3: Show the layout, including wake directions ax = axarr[3] layoutviz.plot_turbine_points(fmodel, ax=ax) layoutviz.plot_turbine_labels(fmodel, ax=ax, turbine_names=turbine_names) layoutviz.plot_waking_directions(fmodel, ax=ax) ax.set_title("Show turbine names and wake direction") # Plot 4: Plot a subset of the layout, and limit directions less than 7D ax = axarr[4] layoutviz.plot_turbine_points(fmodel, ax=ax, turbine_indices=[0, 1, 2, 3]) layoutviz.plot_turbine_labels( fmodel, ax=ax, turbine_names=turbine_names, turbine_indices=[0, 1, 2, 3] ) layoutviz.plot_waking_directions(fmodel, ax=ax, turbine_indices=[0, 1, 2, 3], limit_dist_D=7) ax.set_title("Plot a subset and limit wake line distance") # Plot with a shaded region ax = axarr[5] layoutviz.plot_turbine_points(fmodel, ax=ax) layoutviz.shade_region(np.array([[0, 0], [300, 0], [300, 1000], [0, 700]]), ax=ax) ax.set_title("Plot with a shaded region") # Change hub heights and plot as a proxy for terrain ax = axarr[6] fmodel.core.farm.hub_heights = np.array([110, 90, 100, 100, 95]) layoutviz.plot_farm_terrain(fmodel, ax=ax) plt.tight_layout() plt.show() ================================================ FILE: examples/examples_visualizations/002_visualize_y_cut_plane.py ================================================ """Example: Visualize y cut plane Demonstrate visualizing a plane cut vertically through the flow field along the wind direction. """ import matplotlib.pyplot as plt from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane fmodel = FlorisModel("../inputs/gch.yaml") # Set a 3 turbine layout with wind direction along the row fmodel.set( layout_x=[0, 500, 1000], layout_y=[0, 0, 0], wind_directions=[270], wind_speeds=[8], turbulence_intensities=[0.06], ) # Collect the yplane y_plane = fmodel.calculate_y_plane(x_resolution=200, z_resolution=100, crossstream_dist=0.0) # Plot the flow field fig, ax = plt.subplots(figsize=(10, 4)) visualize_cut_plane( y_plane, ax=ax, min_speed=3, max_speed=9, label_contours=True, title="Y Cut Plane" ) plt.show() ================================================ FILE: examples/examples_visualizations/003_visualize_cross_plane.py ================================================ """Example: Visualize cross plane Demonstrate visualizing a plane cut vertically through the flow field across the wind direction. """ import matplotlib.pyplot as plt from floris import FlorisModel from floris.flow_visualization import visualize_cut_plane fmodel = FlorisModel("../inputs/gch.yaml") # Set a 1 turbine layout fmodel.set( layout_x=[0], layout_y=[0], wind_directions=[270], wind_speeds=[8], turbulence_intensities=[0.06], ) # Collect the cross plane downstream of the turbine cross_plane = fmodel.calculate_cross_plane( y_resolution=100, z_resolution=100, downstream_dist=500.0, ) # Plot the flow field fig, ax = plt.subplots(figsize=(4, 6)) visualize_cut_plane( cross_plane, ax=ax, min_speed=3, max_speed=9, label_contours=True, title="Cross Plane" ) plt.show() ================================================ FILE: examples/examples_visualizations/004_visualize_rotor_values.py ================================================ """Example: Visualize rotor velocities Demonstrate visualizing the flow velocities at the rotor using plot_rotor_values """ import matplotlib.pyplot as plt import floris.flow_visualization as flowviz from floris import FlorisModel fmodel = FlorisModel("../inputs/gch.yaml") # Set a 2 turbine layout fmodel.set( layout_x=[0, 500], layout_y=[0, 0], wind_directions=[270], wind_speeds=[8], turbulence_intensities=[0.06], ) # Run the model fmodel.run() # Plot the values at each rotor fig, axes, _, _ = flowviz.plot_rotor_values( fmodel.core.flow_field.u, findex=0, n_rows=1, n_cols=2, return_fig_objects=True ) fig.suptitle("Rotor Plane Visualization, Original Resolution") plt.show() ================================================ FILE: examples/examples_visualizations/005_visualize_flow_by_sweeping_turbines.py ================================================ """Example: Visualize flow by sweeping turbines Demonstrate the use calculate_horizontal_plane_with_turbines """ import matplotlib.pyplot as plt import floris.flow_visualization as flowviz from floris import FlorisModel fmodel = FlorisModel("../inputs/gch.yaml") # # Some wake models may not yet have a visualization method included, for these cases can use # # a slower version which scans a turbine model to produce the horizontal flow # Set a 2 turbine layout fmodel.set( layout_x=[0, 500], layout_y=[0, 0], wind_directions=[270], wind_speeds=[8], turbulence_intensities=[0.06], ) horizontal_plane_scan_turbine = flowviz.calculate_horizontal_plane_with_turbines( fmodel, x_resolution=20, y_resolution=10, ) fig, ax = plt.subplots(figsize=(10, 4)) flowviz.visualize_cut_plane( horizontal_plane_scan_turbine, ax=ax, label_contours=True, title="Horizontal (coarse turbine scan method)", ) plt.show() ================================================ FILE: examples/examples_wind_data/001_wind_data_comparisons.py ================================================ """Example: Wind Data Comparisons In this example, a random time series of wind speeds, wind directions, turbulence intensities, and values is generated. Value represents the value of the power generated at each time step or wind condition (e.g., the price of electricity). This can then be used in later optimization methods to optimize for total value instead of energy. This time series is then used to instantiate a TimeSeries object. The TimeSeries object is then used to instantiate a WindRose object and WindTIRose object based on the same data. The three objects are then each used to drive a FLORIS model of a simple two-turbine wind farm. The annual energy production (AEP) and annual value production (AVP) outputs are then compared and printed to the console. """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, TimeSeries, ) from floris.utilities import wrap_360 # Generate a random time series of wind speeds, wind directions, turbulence # intensities, and values. In this case let's treat value as the dollars per MWh. N = 500 rng = np.random.default_rng(0) wd_array = wrap_360(270 * np.ones(N) + rng.standard_normal(N) * 20) ws_array = np.clip(8 * np.ones(N) + rng.standard_normal(N) * 8, 3, 50) ti_array = np.clip(0.1 * np.ones(N) + rng.standard_normal(N) * 0.05, 0, 0.25) value_array = np.clip(25 * np.ones(N) + rng.standard_normal(N) * 10, 0, 100) fig, axarr = plt.subplots(4, 1, sharex=True, figsize=(7, 6)) ax = axarr[0] ax.plot(wd_array, marker=".", ls="None") ax.set_ylabel("Wind Direction") ax = axarr[1] ax.plot(ws_array, marker=".", ls="None") ax.set_ylabel("Wind Speed") ax = axarr[2] ax.plot(ti_array, marker=".", ls="None") ax.set_ylabel("Turbulence Intensity") ax = axarr[3] ax.plot(value_array, marker=".", ls="None") ax.set_ylabel("Value") # Build the time series time_series = TimeSeries(wd_array, ws_array, turbulence_intensities=ti_array, values=value_array) # Now build the wind rose wind_rose = time_series.to_WindRose() # Plot the wind rose fig, ax = plt.subplots(subplot_kw={"polar": True}) wind_rose.plot(ax=ax, legend_kwargs={"label": "WS"}) fig.suptitle("WindRose Plot") # Now build a wind rose with turbulence intensity wind_ti_rose = time_series.to_WindTIRose() # Plot the wind rose with TI fig, axs = plt.subplots(2, 1, figsize=(6, 8), subplot_kw={"polar": True}) wind_ti_rose.plot(ax=axs[0], wind_rose_var="ws", legend_kwargs={"label": "WS"}) axs[0].set_title("Wind Direction and Wind Speed Frequencies") wind_ti_rose.plot(ax=axs[1], wind_rose_var="ti", legend_kwargs={"label": "TI"}) axs[1].set_title("Wind Direction and Turbulence Intensity Frequencies") fig.suptitle("WindTIRose Plots") plt.tight_layout() # Now set up a FLORIS model and initialize it using the time series and wind rose fmodel = FlorisModel("../inputs/gch.yaml") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) fmodel_time_series = fmodel.copy() fmodel_wind_rose = fmodel.copy() fmodel_wind_ti_rose = fmodel.copy() fmodel_time_series.set(wind_data=time_series) fmodel_wind_rose.set(wind_data=wind_rose) fmodel_wind_ti_rose.set(wind_data=wind_ti_rose) fmodel_time_series.run() fmodel_wind_rose.run() fmodel_wind_ti_rose.run() # Now, compute AEP using the FLORIS models initialized with the three types of # WindData objects. The AEP values are very similar but not exactly the same # because of the effects of binning in the wind roses. time_series_aep = fmodel_time_series.get_farm_AEP() wind_rose_aep = fmodel_wind_rose.get_farm_AEP() wind_ti_rose_aep = fmodel_wind_ti_rose.get_farm_AEP() print(f"AEP from TimeSeries {time_series_aep / 1e9:.2f} GWh") print(f"AEP from WindRose {wind_rose_aep / 1e9:.2f} GWh") print(f"AEP from WindTIRose {wind_ti_rose_aep / 1e9:.2f} GWh") # Now, compute annual value production (AVP) using the FLORIS models initialized # with the three types of WindData objects. The AVP values are very similar but # not exactly the same because of the effects of binning in the wind roses. time_series_avp = fmodel_time_series.get_farm_AVP() wind_rose_avp = fmodel_wind_rose.get_farm_AVP() wind_ti_rose_avp = fmodel_wind_ti_rose.get_farm_AVP() print(f"Annual Value Production (AVP) from TimeSeries {time_series_avp / 1e6:.2f} dollars") print(f"AVP from WindRose {wind_rose_avp / 1e6:.2f} dollars") print(f"AVP from WindTIRose {wind_ti_rose_avp / 1e6:.2f} dollars") plt.show() ================================================ FILE: examples/examples_wind_data/002_generate_ti.py ================================================ """Example: Generate TI Demonstrate usage of TI generating and plotting functionality in the WindRose and TimeSeries classes """ import matplotlib.pyplot as plt import numpy as np from floris import ( TimeSeries, WindRose, ) # Generate a random time series of wind speeds, wind directions and turbulence intensities wind_directions = np.array([250, 260, 270]) wind_speeds = np.array([5, 6, 7, 8, 9, 10]) ti_table = 0.06 # Declare a WindRose object wind_rose = WindRose(wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=ti_table) # Define a custom function where TI = 1 / wind_speed def custom_ti_func(wind_directions, wind_speeds): return 1 / wind_speeds wind_rose.assign_ti_using_wd_ws_function(custom_ti_func) fig, ax = plt.subplots() wind_rose.plot_ti_over_ws(ax) ax.set_title("Turbulence Intensity defined by custom function") # Now use the normal turbulence model approach from the IEC 61400-1 standard, # wherein TI is defined as a function of wind speed: # Iref is defined as the TI value at 15 m/s. Note that Iref = 0.07 is lower # than the values of Iref used in the IEC standard, but produces TI values more # in line with those typically used in FLORIS (TI=8.6% at 8 m/s). Iref = 0.07 wind_rose.assign_ti_using_IEC_method(Iref) fig, ax = plt.subplots() wind_rose.plot_ti_over_ws(ax) ax.set_title(f"Turbulence Intensity defined by Iref = {Iref:0.2}") # Demonstrate equivalent usage in time series N = 100 wind_directions = 270 * np.ones(N) wind_speeds = np.linspace(5, 15, N) turbulence_intensities = 0.06 * np.ones(N) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities ) time_series.assign_ti_using_IEC_method(Iref=Iref) fig, axarr = plt.subplots(2, 1, sharex=True, figsize=(7, 8)) ax = axarr[0] ax.plot(wind_speeds) ax.set_ylabel("Wind Speeds (m/s)") ax.grid(True) ax = axarr[1] ax.plot(time_series.turbulence_intensities) ax.set_ylabel("Turbulence Intensity (-)") ax.grid(True) fig.suptitle("Generating TI in TimeSeries") plt.show() ================================================ FILE: examples/examples_wind_data/003_generate_value.py ================================================ """Example: Generate value Demonstrate usage of value generating and plotting functionality in the WindRose and TimeSeries classes. Value represents the value of the power or energy generated at each time step or wind condition (e.g., the price of electricity in dollars/MWh). This can then be used to compute the annual value production (AVP) instead of AEP, or in later optimization methods to optimize for total value instead of energy. """ import matplotlib.pyplot as plt import numpy as np from floris import ( TimeSeries, WindRose, ) # Generate a random time series of wind speeds, wind directions and turbulence intensities wind_directions = np.array([250, 260, 270]) wind_speeds = np.arange(3.0, 11.0, 1.0) ti_table = 0.06 # Declare a WindRose object wind_rose = WindRose(wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=ti_table) # Define a custom function where value = 100 / wind_speed def custom_value_func(wind_directions, wind_speeds): return 100 / wind_speeds wind_rose.assign_value_using_wd_ws_function(custom_value_func) fig, ax = plt.subplots() wind_rose.plot_value_over_ws(ax) ax.set_title("Value defined by custom function") # Now assign value using the provided assign_value_piecewise_linear method with the default # settings. This method assigns value based on a linear piecewise function of wind speed # (with two line segments). The default arguments produce a value vs. wind speed that # approximates the normalized mean electricity price vs. wind speed curve for the SPP market # in the U.S. for years 2018-2020 from figure 7 in "The value of wake steering wind farm flow # control in US energy markets," Wind Energy Science, 2024. https://doi.org/10.5194/wes-9-219-2024. wind_rose.assign_value_piecewise_linear( value_zero_ws=1.425, ws_knee=4.5, slope_1=0.0, slope_2=-0.135 ) fig, ax = plt.subplots() wind_rose.plot_value_over_ws(ax) ax.set_title("Value defined by default piecewise linear function") # Demonstrate equivalent usage in time series N = 100 wind_directions = 270 * np.ones(N) wind_speeds = np.linspace(3, 15, N) turbulence_intensities = 0.06 * np.ones(N) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities ) time_series.assign_value_piecewise_linear() fig, axarr = plt.subplots(2, 1, sharex=True, figsize=(7, 8)) ax = axarr[0] ax.plot(wind_speeds) ax.set_ylabel("Wind Speeds (m/s)") ax.grid(True) ax = axarr[1] ax.plot(time_series.values) ax.set_ylabel("Value (normalized price/MWh)") ax.grid(True) fig.suptitle("Generating value in TimeSeries") plt.show() ================================================ FILE: examples/examples_wind_resource_grid/000_generate_example_wrg.py ================================================ """Example: Generate Example WRG File This first example demonstrates the content and structure of a Wind Resource Grid (WRG) file. WRG files are Wind Resource Grid files, and their structure is defined here: https://backend.orbit.dtu.dk/ws/portalfiles/portal/116352660/WAsP_10_Help_Facility.pdf In the script, a synthetic WRG file is derived using the WindRose class. """ import matplotlib.pyplot as plt import numpy as np from scipy.optimize import curve_fit from floris import WindRose # Define a function given the distribution of wind speeds in one sector, # compute the A and k parameters of the Weibull distribution def weibull_func(U, A, k): return (k / A) * (U / A) ** (k - 1) * np.exp(-((U / A) ** k)) def estimate_weibull(U, freq): # Normalize the frequency freq = freq / freq.sum() # Fit the Weibull distribution popt, _ = curve_fit(weibull_func, U, freq, p0=(6.0, 2.0)) A_fit, k_fit = popt return A_fit, k_fit ################################################## # Parameters ################################################## # Top line parameters Nx = 2 # Number of grid points in x Ny = 3 # Number of grid points in y Xmin = 0.0 # Minimum value of x (m) Ymin = 0.0 # Minimum value of y (m) cell_size = 1000.0 # Grid spacing (m) # Other fixed parameters z_coord = 0.0 # z-coordinate of the grid height_above_ground_level = 90.0 # Height above ground level num_sectors = 12 # Number of direction sectors ################################################## # Generating data ################################################## # The above parameters define a 3x3 grid of points. Let's start # by assuming the point at (0,0) has the wind rose as # defined in inputs/wind_rose.csv wind_rose_base = WindRose.read_csv_long( "../inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06 ) # Resample to number of sectors wind_rose_base = wind_rose_base.aggregate(wd_step=360 / num_sectors) ## Generate the other wind roses # Assume that the wind roses at other points are generated by increasing # the north winds with increasing y and east winds with increasing x x_list = [] y_list = [] wind_rose_list = [] for xi in range(Nx): for yi in range(Ny): # Get the x and y locations for this point x = Xmin + xi * cell_size y = Ymin + yi * cell_size x_list.append(x) y_list.append(y) # Instantiate the wind rose object wind_rose = WindRose.read_csv_long( "../inputs/wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06, ) # Resample to number of sectors wind_rose = wind_rose.aggregate(wd_step=360 / num_sectors) # Copy the frequency table freq_table = wind_rose.freq_table.copy() # How much to shift the wind rose for this location percent_x = xi / (Nx - 1) percent_y = yi / (Ny - 1) # East frequency scaling east_row = freq_table[3, :] shift_amount = percent_x * east_row[:5] * .9 east_row[:5] = east_row[:5] - shift_amount east_row[5:10] = east_row[5:10] + shift_amount freq_table[3, :] = east_row # North frequency scaling north_row = freq_table[0, :] shift_amount = percent_y * north_row[:6] * .9 north_row[:6] = north_row[:6] - shift_amount north_row[6:12] = north_row[6:12] + shift_amount freq_table[0, :] = north_row # Add to list wind_rose_list.append( WindRose( wind_directions=wind_rose.wind_directions, wind_speeds=wind_rose.wind_speeds, ti_table=wind_rose.ti_table, freq_table=freq_table, ) ) ################################################## # Show the wind roses in a grid ################################################## fig, axarr = plt.subplots(Nx, Ny, figsize=(12, 12), subplot_kw={"polar": True}) axarr = axarr.flatten() for i, wind_rose in enumerate(wind_rose_list): wind_rose.plot(ax=axarr[i], ws_step=5) axarr[i].set_title(f"({x_list[i]}, {y_list[i]})") fig.suptitle("Wind Roses at Grid Points") ################################################## # Demonstrate fitting the Weibull distribution ################################################## freq_test = wind_rose_list[0].freq_table[0, :] / wind_rose_list[0].freq_table[0, :].sum() a_test, k_test = estimate_weibull(wind_rose_list[0].wind_speeds, freq_test) print(f"A: {a_test}, k: {k_test}") fig, ax = plt.subplots(1, 1, figsize=(6, 3)) ax.plot(wind_rose_list[0].wind_speeds, freq_test, label="Original") ax.plot( wind_rose_list[0].wind_speeds, weibull_func(wind_rose_list[0].wind_speeds, a_test, k_test), label="Fitted", ) ax.legend() ax.set_xlabel("Wind speed (m/s)") ax.set_ylabel("Frequency") ax.grid(True) ################################################## # Write out the WRG file ################################################## # Open the file with open("wrg_example.wrg", "w") as f: # Write the top line of the file f.write(f"{Nx} {Ny} {Xmin} {Ymin} {cell_size}\n") # Now loop over the points for i in range(Nx * Ny): # Initiate the line to write as 10 blank spaces line = " " # Add the x-coodinate as a 10 character fixed width integer line = line + f"{int(x_list[i]):10d}" # Add the y-coodinate as a 10 character fixed width integer line = line + f"{int(y_list[i]):10d}" # Add the z-coodinate as a 10 character fixed width integer line = line + f"{int(z_coord):8d}" # Add the height above ground level as a 10 character fixed width integer line = line + f"{int(height_above_ground_level):5d}" # Get the wind rose for this point wind_rose = wind_rose_list[i] # Get the frequency matrix and wind speed freq_table = wind_rose.freq_table wind_speeds = wind_rose.wind_speeds wind_directions = wind_rose.wind_directions # Get the A and k parameters across all sectors freq_table_ws = freq_table.sum(axis=0) A, k = estimate_weibull(wind_speeds, freq_table_ws) # Write the A and k parameters line = line + f"{A:5.1f}{k:6.2f}" # Add place holder 0 for the power density line = line + f"{0:15d}" # Write the number of sectors line = line + f"{num_sectors:3d}" # Get the frequency table across wind directions freq_table_wd = freq_table.sum(axis=1) # Step through the sectors for wd_idx in range(num_sectors): # Write the probability for this sector line = line + f"{int(1000*freq_table_wd[wd_idx]):4d}" # Get the A and k parameters for this sector A, k = estimate_weibull(wind_speeds, freq_table[wd_idx, :]) # Write the A and k parameters line = line + f"{int(A*10):4d}{int(k*100):5d}" # Write the line to the file f.write(line + "\n") # Show the plots plt.show() ================================================ FILE: examples/examples_wind_resource_grid/001_wind_rose_wrg.py ================================================ """Example: WindRoseWRG `WindRoseWRG` is a type of WindData object, like `WindRose` and `TimeSeries`, that is used to store wind data in a format that can be used by the FLORIS model. `WindRoseWRG` is different that `WindRose` however because the internal data holds the information of the WRG file and then a `WindRose` object is created for each turbine in a provided layout. In this example the WRG file generated in the previous example is read in using the `WindRoseWRG` object, and wind roses as points on the WRG grid, as will as in-between interpolated points have wind roses calculated using the `get_wind_rose_at_point` method. Finally, the wind roses are upsampled to 5 degree wind direction bins and plotted. """ import matplotlib.pyplot as plt import numpy as np from floris import WindRoseWRG # Read the WRG file wind_rose_wrg = WindRoseWRG("wrg_example.wrg") # Print some basic information print(wind_rose_wrg) # The wind roses were set to have a higher concentration of faster north winds for # increasing y, show that this is contained within the wind roses, even those interpolated # between grid points y_points_to_test = np.array([0, 500, 1000, 1500, 2000]) fig, axarr = plt.subplots(1, 5, figsize=(16, 5), subplot_kw={"polar": True}) for i in range(5): wind_rose = wind_rose_wrg.get_wind_rose_at_point(0, y_points_to_test[i]) wind_rose.plot(ax=axarr[i], ws_step=5) if i %2 == 0: axarr[i].set_title(f"y = {y_points_to_test[i]}") else: axarr[i].set_title(f"y = {y_points_to_test[i]}\n(Interpolated)") # Go through the axarr and delete the legends except for the middle for ax in [axarr[0], axarr[1], axarr[3], axarr[4]]: ax.legend().set_visible(False) # Draw a horizontal line on each axis indicating the level of the lower wind speed # bucket for the north wind from the first wind rose for i in range(5): axarr[i].axhline(y=0.036, color="red", alpha=0.5) fig.suptitle("Wind Roses at locations with increasing y. Note the location where the 5 m/s bin \ transitions to 10 m/s for north wind at y = 0 is \nindicated by the red line to show \ the increase in wind speed to the north as y increases.") # Since wind directions was not specified, the wind directions implied by the number of sectors # in the WRG was used, however the wind directions can be set using the set_wind_directions method # or passed in at initialization. Here we upsample from 12, 30-deg sectors, to 72 5-deg sectors wind_rose_wrg.set_wd_step(5.0) fig, axarr = plt.subplots(1, 5, figsize=(16, 5), subplot_kw={"polar": True}) for i in range(5): wind_rose = wind_rose_wrg.get_wind_rose_at_point(0, y_points_to_test[i]) wind_rose.plot(ax=axarr[i], ws_step=5) if i %2 == 0: axarr[i].set_title(f"y = {y_points_to_test[i]}") else: axarr[i].set_title(f"y = {y_points_to_test[i]}\n(Interpolated)") # Go through the axarr and delete all the legends except for the middle for ax in axarr: ax.legend().set_visible(False) fig.suptitle('Wind roses with upsampling to 5-deg bins') plt.show() ================================================ FILE: examples/examples_wind_resource_grid/002_set_floris_with_wrg.py ================================================ """Example: Setting FLORIS with WindRoseWRG This example shows how to set a FLORIS model with a WindRoseWRG object. When a WindRoseWRG object is set as the wind data in a FLORIS model, the wind roses for each turbine in the layout are generated and stored in the WindRoseWRG object. The wind roses are then used to calculate the expected turbine powers and farm power. """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, WindRoseWRG, ) # Read the WRG file into a WindRoseWRG object wind_rose_wrg = WindRoseWRG("wrg_example.wrg") # Print out some information about the print(wind_rose_wrg) print("=====================================") # Since we didn't specify the wind speeds or fixed ti, the default values are used # and displayed in the above printout. Note the wrg file itself, does not specify # turbulence intensity, or the wind speed bins FLORIS should use. These can be chosen # at initialization of the WindRoseWRG object, or set later. We can update these values # using the following methods wind_rose_wrg.set_wind_speeds(np.arange(0, 26, 2.0)) # Use 2m/s steps wind_rose_wrg.set_ti_table(0.07) # Use a fixed ti of 7% for all wind directions and wind speeds # Select a turbine layout # The original grid in example 000 was defined by having the wind rose rotate with increasing y # and move to higher speeds with increasing x. Here we will define a turbine layout that # has a line of turbine along the diagonals of the grid. layout_x = np.array([0, 500, 1000]) layout_y = np.array([0, 1000, 2000]) # Set the turbine layout in the WindRoseWRG object, note that until this is done, the # wind_rose_wrg object contains no WindRose objects. # Note that if the wind_rose_wrg is applied to a FlorisModel, the layout_x and layout_y # will be set in the FlorisModel and the wind roses will be generated for each turbine wind_rose_wrg.set_layout(layout_x, layout_y) # Plot the wind roses in the first figure fig, axarr = plt.subplots(1, 3, figsize=(16, 5), subplot_kw={"polar": True}) wind_rose_wrg.plot_wind_roses(axarr=axarr, ws_step=5) # Apply the wind_rose_wrg to a FlorisModel # Load the FLORIS model and set that layout fmodel = FlorisModel("../inputs/gch.yaml") fmodel.set(layout_x=layout_x, layout_y=layout_y) # Set the wind data as the wind_rose_wrg fmodel.set(wind_data=wind_rose_wrg) # Run the model and get the expected turbine powers and farm power fmodel.run() expected_turbine_powers = fmodel.get_expected_turbine_powers() expected_farm_power = fmodel.get_expected_farm_power() # Print the expected turbine powers, farm power, and the sum of the expected turbine powers print("=====================================") print("Expected turbine powers:", expected_turbine_powers) print("Expected farm power:", expected_farm_power) print("Sum of expected turbine powers:", expected_turbine_powers.sum()) print("=====================================") # Now re-run using one of the turbine wind roses alone # Compare with the result if just using the first wind rose or the last wind rose fmodel.set(wind_data=wind_rose_wrg.wind_roses[0]) fmodel.run() expected_turbine_powers_first = fmodel.get_expected_turbine_powers() fmodel.set(wind_data=wind_rose_wrg.wind_roses[-1]) fmodel.run() expected_turbine_powers_last = fmodel.get_expected_turbine_powers() # Print the results to show match of first and last turbine when using their respective wind roses print("Expected turbine powers:") print("All wind roses:", expected_turbine_powers) print( "First wind rose (1st power matches): (" + str(expected_turbine_powers_first[0]) + "), " + str(expected_turbine_powers_first[1]) + ", " + str(expected_turbine_powers_first[2]) + ")" ) print( "Last wind rose (Last power matches): " + str(expected_turbine_powers_last[0]) + ", " + str(expected_turbine_powers_last[1]) + ", (" + str(expected_turbine_powers_last[2]) + ")" ) plt.show() ================================================ FILE: examples/examples_wind_resource_grid/003_wrg_compar_layout_optimization.py ================================================ """Example: Layout optimization with WindRoseWRG comparison This example compares a layout optimization using a WindRoseWRG. In the example, two turbine positions are optimized within a square grid. The optimization is run 3 times: 1. Using a WindRoseWRG object generated using the example wrg file 2. Using a WindRose object created from the WindRoseWRG object 3. Using a WindRose object created from the WindRoseWRG object, but with the HeterogeneousMap also generated by the WindRoseWRG object Because the WRG file includes a speed-up for northern winds, optimizaitions (1) and (3) place both turbines near the northern boundary of the grid, while optimization (2) places the turbines in the furthest corners of the grid. The optimization illustrates that using the full WindRoseWRG object produces a similar result to the WindRose object with the HeterogeneousMap, since they both can represent the difference in resource for different locations. The HeterogeneousMap may have advantage for larger cases since it is running with only 1 wind speed. For this example the results and performance are similar. """ import matplotlib.pyplot as plt import numpy as np from floris import ( FlorisModel, WindRoseWRG, ) from floris.optimization.layout_optimization.layout_optimization_random_search import ( LayoutOptimizationRandomSearch, ) if __name__ == "__main__": # Parameters of layout optimization seconds_per_iteration = 30.0 total_optimization_seconds = 120.0 min_dist_D = 3.0 use_dist_based_init = False # Initialize the WindRoseWRG object with wind speeds every 2 m/s and fixed ti of 6%. Specify # a wd_step of 4 degrees, which implies upsampling from wrg's 90 degree sectors to 12 # degree sectors wind_rose_wrg = WindRoseWRG( "wrg_example.wrg", wd_step=2.0, wind_speeds=np.arange(0, 21, 1.0), # Use a sparser range of speeds ti_table=0.06, ) # Define an optimization boundary within the grid that is a square with a # buffer to avoid the outer limits of the heterogeneous area buffer = 100.0 boundaries = [ (buffer, buffer), (1000 - buffer, buffer), (1000 - buffer, 2000 - buffer), (buffer, 2000 - buffer), (buffer, buffer), ] # Select and initial layout in the corners of the boundary layout_x = np.array([500, 1000 - buffer]) layout_y = np.array([900, 2000 - buffer]) ########################## # Set up the FlorisModel fmodel = FlorisModel("../inputs/gch.yaml") ########################## # Use the get_heterogeneous_map method to generate a WindRose that represents # the information in the WindRoseWRG, rather than a set of WindRose objects # but as a single WindRose object (for one location) and a HeterogeneousMap # the describes the speed up information per direction across the domain # This will allow running the optimization for a single wind speed while still # accounting for the difference in wind speeds in location by direction wind_rose_het = wind_rose_wrg.get_heterogeneous_wind_rose( fmodel=fmodel, x_loc=0.0, y_loc=0.0, representative_wind_speed=9.0, ) # Pull out the heterogeneous plot to show the underlying speedups het_map = wind_rose_het.heterogeneous_map wind_direction_to_plot = [0.0, 10.0, 45.0, 75.0, 90.0, 180.0] # Show the het_map for a few wind directions fig, axarr = plt.subplots(1, len(wind_direction_to_plot), figsize=(16, 5)) axarr = axarr.flatten() for i, wd in enumerate(wind_direction_to_plot): het_map.plot_single_speed_multiplier( wind_direction=wd, wind_speed=8.0, ax=axarr[i], show_colorbar=True, ) axarr[i].set_title(f"Wind Direction: {wd}") # ########################## # Run the optimization with the full WindRoseWRG first fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose_wrg) # Set the layout optimization layout_opt = LayoutOptimizationRandomSearch( fmodel, boundaries, min_dist_D=min_dist_D, seconds_per_iteration=seconds_per_iteration, total_optimization_seconds=total_optimization_seconds, use_dist_based_init=use_dist_based_init, ) layout_opt.optimize() x_initial, y_initial, x_opt_wrg, y_opt_wrg = layout_opt._get_initial_and_final_locs() # Grab the log array objective_log_array_wrg = np.array(layout_opt.objective_candidate_log) # Normalize objective_log_array_wrg = objective_log_array_wrg / np.max(objective_log_array_wrg) print("=====================================") print("Objective log array (WRG):") print(objective_log_array_wrg.shape) print(objective_log_array_wrg) # ########################## # Repeat using wind_rose_het fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose_het) # Set the layout optimization layout_opt = LayoutOptimizationRandomSearch( fmodel, boundaries, min_dist_D=min_dist_D, seconds_per_iteration=seconds_per_iteration, total_optimization_seconds=total_optimization_seconds, use_dist_based_init=use_dist_based_init, ) layout_opt.optimize() _, _, x_opt_het, y_opt_het = layout_opt._get_initial_and_final_locs() # Grab the log array objective_log_array_het = np.array(layout_opt.objective_candidate_log) # Normalize objective_log_array_het = objective_log_array_het / np.max(objective_log_array_het) # ########################## # Repeat using single wind rose (without het) wind_rose = wind_rose_wrg.get_wind_rose_at_point(0, 0) fmodel = FlorisModel("../inputs/gch.yaml") fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose) # Set the layout optimization layout_opt = LayoutOptimizationRandomSearch( fmodel, boundaries, min_dist_D=min_dist_D, seconds_per_iteration=seconds_per_iteration, total_optimization_seconds=total_optimization_seconds, use_dist_based_init=use_dist_based_init, ) layout_opt.optimize() _, _, x_opt_wr, y_opt_wr = layout_opt._get_initial_and_final_locs() # Grab the log array objective_log_array_wr = np.array(layout_opt.objective_candidate_log) # Normalize objective_log_array_wr = objective_log_array_wr / np.max(objective_log_array_wr) fig, ax = plt.subplots(1, 1, figsize=(8, 8)) layout_opt.plot_layout_opt_boundary(ax=ax) ax.scatter(x_initial, y_initial, label="Initial Layout", s=80, color="k", marker="s") ax.scatter( x_opt_wr, y_opt_wr, label="Optimized Layout (Single Wind Rose)", s=60, color="b", marker="^" ) ax.scatter(x_opt_wrg, y_opt_wrg, label="Optimized Layout (WRG)", s=40, color="r", marker="o") ax.scatter( x_opt_het, y_opt_het, label="Optimized Layout (Single Wind Rose + Het)", s=20, color="g", marker="h", ) ax.set_aspect('equal') ax.legend() print("=====================================") print("Objective log array (HET):") print(objective_log_array_het.shape) print(objective_log_array_het) fig, ax = plt.subplots(1, 1, figsize=(8, 8)) for objective_log_array, label, color in zip( [objective_log_array_wr, objective_log_array_wrg, objective_log_array_het], ["WR", "WRG", "Het"], ["b", "r", "g"], ): ax.plot( np.arange(len(objective_log_array)), np.log10(objective_log_array * 100.0), label=label, color=color, ) ax.set_xlabel("Iteration") ax.set_ylabel("Objective") ax.legend() plt.show() ================================================ FILE: examples/examples_wind_resource_grid/wrg_example.wrg ================================================ 2 3 0.0 0.0 1000.0 0 0 0 90 9.5 2.25 0 12 116 106 273 86 93 228 61 76 220 54 74 220 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 0 1000 0 90 9.6 2.31 0 12 116 107 341 86 93 228 61 76 220 54 74 220 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 0 2000 0 90 9.7 2.36 0 12 116 106 409 86 93 228 61 76 220 54 74 220 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 1000 0 0 90 9.6 2.34 0 12 116 106 273 86 93 228 61 76 220 54 82 407 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 1000 1000 0 90 9.6 2.40 0 12 116 107 341 86 93 228 61 76 220 54 82 407 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 1000 2000 0 90 9.7 2.46 0 12 116 106 409 86 93 228 61 76 220 54 82 407 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 ================================================ FILE: examples/inputs/cc.yaml ================================================ name: CC description: Three turbines using Cumulative Gauss Curl model floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: cc enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 wake_turbulence_parameters: crespo_hernandez: initial: 0.01 constant: 0.9 ai: 0.83 downstream: -0.25 ================================================ FILE: examples/inputs/emgauss.yaml ================================================ name: Emperical Gaussian description: Three turbines using emperical Gaussian model floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: empirical_gauss turbulence_model: wake_induced_mixing velocity_model: empirical_gauss enable_secondary_steering: false enable_yaw_added_recovery: true enable_active_wake_mixing: false enable_transverse_velocities: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs/emgauss_helix.yaml ================================================ name: Emperical Gaussian description: Three turbines using empirical Gaussian model floris_version: v4.0 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - iea_15MW flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: empirical_gauss turbulence_model: wake_induced_mixing velocity_model: empirical_gauss enable_secondary_steering: false enable_yaw_added_recovery: false enable_active_wake_mixing: true enable_transverse_velocities: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 30 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 awc_wake_exp: 1.2 awc_wake_denominator: 400 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs/gch.yaml ================================================ ### # Name for the input file. # This is not used by FLORIS and is simply for the user's reference. # String type. name: GCH ### # Description of the contents of this input file. # This is not used by FLORIS and is simply for the user's reference. # String type. description: Three turbines using Gauss Curl Hybrid model ### # The FLORIS version that the file is defined for. # This is not used by FLORIS and is simply for the user's reference. # String type. floris_version: v4 ### # Upper-level group of options for configuring logging. logging: ### # Group of settings for logging to the console (i.e. terminal). console: ### # Flag to enable console logging. Boolean type. enable: true ### # Severity to show output in console. Messages at this level or higher will be shown. # String type. Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING ### # Group of settings for logging to a file. file: ### # Flag to enable file logging. Boolean type. enable: false ### # Severity to show output in file. Messages at this level or higher will be shown. # String type. Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING ### # Upper-level group of options for configuring solution grid. solver: ### # Grid type for solving flow values at the turbines. # String type. Can be one of: "turbine_grid", "turbine_cubature_grid". type: turbine_grid ### # Number of grid points per turbine for solve. For turbine_grid type solve, represents the number # of points along each of the two axes. For turbine_cubature_grid type solve, represents the # total number of points used in the cubature solve. Integer type. turbine_grid_points: 3 ### # Group for setting the wind farm configuration. farm: ### # x-coordinates for the turbine locations, with the x axis corresponding to the # "west to east" direction. The order of the coordinates corresponds to the index of the # turbine in the primary data structures. # List of float type. layout_x: - 0.0 - 630.0 - 1260.0 ### # y-coordinates for the turbine locations, with the y axis corresponding to the # "south to north" direction. The order of the coordinates corresponds to the index of the # turbine in the primary data structures. # List of float type. layout_y: - 0.0 - 0.0 - 0.0 ### # Listing of turbine types for placement at the x and y coordinates. # The list length must be 1 or the same as ``layout_x`` and ``layout_y``. If it is a # single value, all turbines are of the same type. Otherwise, the turbine type # is mapped to the location at the same index in ``layout_x`` and ``layout_y``. # The types can be either a string for a turbine included in the turbine_library, # a string beginning with !include for a path to the user's turbine; or a full # definition of a wind turbine (as a nested dictionary). turbine_type: - nrel_5MW ### # Group for defining the atmospheric conditions. flow_field: ### # Air density. Float type. air_density: 1.225 ### # The height in meters to consider the "center" of the vertical wind speed profile # due to shear. With a shear exponent not 1, the wind speed at this height # will be the value given in ``wind_speeds``. Above and below this height, # the wind speed will change according to the shear profile; see # :py:meth:`.FlowField.initialize_velocity_field`. # For farms consisting of one wind turbine type, use ``reference_wind_height: -1`` # to use the hub height of the wind turbine definition. For multiple wind turbine # types, the reference wind height must be given explicitly. Float type (or -1). reference_wind_height: -1 ### # Turbulence intensities for the simulation, specified as a decimal value. Type list of floats. turbulence_intensities: - 0.06 ### # Wind directions for the simulation, specified in degrees according to compass directions # (0 is northerly, 90 is easterly, etc). Type list of floats. wind_directions: - 270.0 ### # The exponent used to model the wind shear profile; see # :py:meth:`.FlowField.initialize_velocity_field`. Float type. wind_shear: 0.12 ### # The wind speeds for the simulation, specified in m/s at the ``reference_wind_height``. # Type list of floats. wind_speeds: - 8.0 ### # The wind veer (in degrees) as a constant value for all points in the grid. Only used in # certain models. Float type. wind_veer: 0.0 ### # Conditions that are specified for use with the multi-dimensional power/thrust capability. # These conditions are external to FLORIS and specified by the user. They are used internally # through a nearest-neighbor selection process to choose the correct Cp/Ct interpolants # to use. Type dictionary of string:float pairs. multidim_conditions: Tp: 2.5 Hs: 3.01 ### # Group for defining the wake model parameters. wake: ### # Group for selecting the model elements for the simulation. # See :py:mod:`~.wake` for a list of available models and their descriptions. model_strings: ### # Wake combination model. String type.. combination_model: sosfs ### # Wake deflection model. String type. deflection_model: gauss ### # Wake turbulence model. String type.. turbulence_model: crespo_hernandez ### # Wake velocity deficit model. String type. velocity_model: gauss ### # Flag to include secondary steering effects. Only used in some models. Boolean type. enable_secondary_steering: true ### # Flag to include yaw added recovery effects. Only used in some models. Boolean type. enable_yaw_added_recovery: true ### # Flag to include active wake mixing effects. Only used in Empirical Guassian model. Boolean type. enable_active_wake_mixing: false ### # Flag to compute transverse velocities across turbine rotors. Only used in some models. # Boolean type. enable_transverse_velocities: true ### # Parameters for the wake deflection model. See model descriptions and implementations for # details of each parameter and its use. wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 ### # Parameters for the wake velocity deficit model. See model descriptions and implementations for # details of each parameter and its use. wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 ### # Parameters for the wake turbulence model. See model descriptions and implementations for # details of each parameter and its use. wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs/gch_heterogeneous_inflow.yaml ================================================ name: GCH description: Three turbines using Gauss Curl Hybrid model floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 1 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 heterogeneous_inflow_config: speed_multipliers: - - 2.0 - 1.0 - 2.0 - 1.0 x: - -300. - -300. - 2600. - 2600. y: - -300. - 300. - -300. - 300. reference_wind_height: -1 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs/gch_multi_dim_cp_ct.yaml ================================================ name: GCH multi dimensional power/thrust coefficient description: Three turbines using GCH model floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - iea_15MW_floating_multi_dim_cp_ct flow_field: multidim_conditions: Tp: 2.5 Hs: 3.01 air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 turbopark: A: 0.04 sigma_max_rel: 4.0 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.01 constant: 0.9 ai: 0.83 downstream: -0.25 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs/gch_multi_dim_cp_ct_TI.yaml ================================================ name: GCH multi dimensional power/thrust coefficient description: Three turbines using GCH model floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - iea_15MW_multi_dim_TI.yaml turbine_library_path: ../inputs/turbine_files/ flow_field: multidim_conditions: TI: 0.06 air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 turbopark: A: 0.04 sigma_max_rel: 4.0 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.01 constant: 0.9 ai: 0.83 downstream: -0.25 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs/gch_multiple_turbine_types.yaml ================================================ name: GCH description: Three turbines using Gauss Curl Hybrid model floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 layout_y: - 0.0 - 0.0 turbine_type: - nrel_5MW - iea_10MW flow_field: air_density: 1.225 reference_wind_height: 90.0 # Since multiple defined turbines, must specify explicitly the reference wind height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: false enable_yaw_added_recovery: false enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs/jensen.yaml ================================================ name: Jensen-Jimenez description: Three turbines using Jensen / Jimenez models floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: jimenez turbulence_model: crespo_hernandez velocity_model: jensen enable_secondary_steering: false enable_yaw_added_recovery: false enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs/turbine_files/iea_15MW_multi_dim_TI.csv ================================================ TI,ws,power,thrust_coefficient 0.06,0.0,0.0,0.0 0.06,2.9,0.0,0.0 0.06,3.0,42.733312,0.80742173 0.06,3.54953237,292.585981,0.784655297 0.06,4.067900771,607.966543,0.781771245 0.06,4.553906848,981.097693,0.785377072 0.06,5.006427063,1401.98084,0.788045584 0.06,5.424415288,1858.67086,0.789922119 0.06,5.806905228,2337.575997,0.790464625 0.06,6.153012649,2824.097302,0.789868339 0.06,6.461937428,3303.06456,0.788727582 0.06,6.732965398,3759.432328,0.787359348 0.06,6.965470002,4178.637714,0.785895402 0.06,7.158913742,4547.19121,0.778275899 0.06,7.312849418,4855.342682,0.778275899 0.06,7.426921164,5091.537139,0.778275899 0.06,7.500865272,5248.453137,0.778275899 0.06,7.534510799,5320.793207,0.778275899 0.06,7.541241633,5335.345498,0.778275899 0.06,7.58833327,5437.90563,0.778275899 0.06,7.675676842,5631.253025,0.778275899 0.06,7.803070431,5920.980626,0.778275899 0.06,7.970219531,6315.115602,0.778275899 0.06,8.176737731,6824.470067,0.778275899 0.06,8.422147605,7462.846389,0.778275899 0.06,8.70588182,8238.359448,0.778275899 0.06,9.027284445,9167.96703,0.778275899 0.06,9.385612468,10285.211,0.778275899 0.06,9.780037514,11617.23699,0.778275899 0.06,10.20964776,13194.41511,0.778275899 0.06,10.67345004,15000.0,0.77176172 0.06,10.86770694,15000.0,0.747149663 0.06,11.17037214,15000.0,0.562338457 0.06,11.6992653,15000.0,0.463477777 0.06,12.25890683,15000.0,0.389083718 0.06,12.84800295,15000.0,0.329822385 0.06,13.46519181,15000.0,0.281465071 0.06,14.10904661,15000.0,0.241494345 0.06,14.77807889,15000.0,0.208180574 0.06,15.470742,15000.0,0.180257568 0.06,16.18543466,15000.0,0.156747535 0.06,16.92050464,15000.0,0.136877529 0.06,17.67425264,15000.0,0.120026379 0.06,18.44493615,15000.0,0.105689427 0.06,19.23077353,15000.0,0.093453742 0.06,20.02994808,15000.0,0.082979637 0.06,20.8406123,15000.0,0.073986457 0.06,21.66089211,15000.0,0.066241166 0.06,22.4888912,15000.0,0.059552107 0.06,23.32269542,15000.0,0.053756866 0.06,24.1603772,15000.0,0.048721662 0.06,25.0,15000.0,0.044334197 0.06,25.02,0.0,0.0 0.06,50.0,0.0,0.0 0.08,0.0,0.0,0.0 0.08,2.9,0.0,0.0 0.08,3.0,47.0066432,0.80742173 0.08,3.54953237,321.84457910000003,0.784655297 0.08,4.067900771,668.7631973,0.781771245 0.08,4.553906848,1079.2074623,0.785377072 0.08,5.006427063,1542.178924,0.788045584 0.08,5.424415288,2044.5379460000001,0.789922119 0.08,5.806905228,2571.3335967000003,0.790464625 0.08,6.153012649,3106.5070322000006,0.789868339 0.08,6.461937428,3633.371016,0.788727582 0.08,6.732965398,4135.3755608,0.787359348 0.08,6.965470002,4596.501485400001,0.785895402 0.08,7.158913742,5001.910331,0.778275899 0.08,7.312849418,5340.8769502000005,0.778275899 0.08,7.426921164,5600.6908529,0.778275899 0.08,7.500865272,5773.298450700001,0.778275899 0.08,7.534510799,5852.8725277,0.778275899 0.08,7.541241633,5868.8800478,0.778275899 0.08,7.58833327,5981.696193000001,0.778275899 0.08,7.675676842,6194.3783275000005,0.778275899 0.08,7.803070431,6513.0786886,0.778275899 0.08,7.970219531,6946.6271622,0.778275899 0.08,8.176737731,7506.9170737,0.778275899 0.08,8.422147605,8209.1310279,0.778275899 0.08,8.70588182,9062.1953928,0.778275899 0.08,9.027284445,10084.763733,0.778275899 0.08,9.385612468,11313.732100000001,0.778275899 0.08,9.780037514,12778.960689,0.778275899 0.08,10.20964776,14513.856621,0.778275899 0.08,10.67345004,15000.0,0.77176172 0.08,10.86770694,15000.0,0.747149663 0.08,11.17037214,15000.0,0.562338457 0.08,11.6992653,15000.0,0.463477777 0.08,12.25890683,15000.0,0.389083718 0.08,12.84800295,15000.0,0.329822385 0.08,13.46519181,15000.0,0.281465071 0.08,14.10904661,15000.0,0.241494345 0.08,14.77807889,15000.0,0.208180574 0.08,15.470742,15000.0,0.180257568 0.08,16.18543466,15000.0,0.156747535 0.08,16.92050464,15000.0,0.136877529 0.08,17.67425264,15000.0,0.120026379 0.08,18.44493615,15000.0,0.105689427 0.08,19.23077353,15000.0,0.093453742 0.08,20.02994808,15000.0,0.082979637 0.08,20.8406123,15000.0,0.073986457 0.08,21.66089211,15000.0,0.066241166 0.08,22.4888912,15000.0,0.059552107 0.08,23.32269542,15000.0,0.053756866 0.08,24.1603772,15000.0,0.048721662 0.08,25.0,15000.0,0.044334197 0.08,25.02,0.0,0.0 0.08,50.0,0.0,0.0 ================================================ FILE: examples/inputs/turbine_files/iea_15MW_multi_dim_TI.yaml ================================================ turbine_type: 'iea_15MW_floating' hub_height: 150.0 rotor_diameter: 242.24 TSR: 8.0 multi_dimensional_cp_ct: True power_thrust_table: ref_air_density: 1.225 ref_tilt: 6.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 # Note that the multidimensional data provided here is fictional and does not represent a real # wind turbine. power_thrust_data_file: './iea_15MW_multi_dim_TI.csv' ================================================ FILE: examples/inputs/turbopark.yaml ================================================ name: TurbOPark description: Three turbines using TurbOPark model floris_version: v4 logging: console: enable: false level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 1 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 reference_wind_height: 90.0 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: fls deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: turbopark enable_secondary_steering: false enable_yaw_added_recovery: false enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 turbopark: A: 0.04 sigma_max_rel: 4.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs/turbopark_cubature.yaml ================================================ name: Case TwinPark FLORIS v4.0 description: Two aligned wind turbines with 5D spacing as currently implemented in v4.0 floris_version: v4.0 logging: console: enable: true # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING file: enable: false # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING solver: type: turbine_cubature_grid # turbine_grid turbine_grid_points: 6 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: # Constant CT turbine (TODO: implement in script?) - nrel_5MW # Will be replaced in script # Configure the atmospheric conditions. flow_field: air_density: 1.225 reference_wind_height: -1 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_speeds: - 8.0 wind_shear: 0.0 # Needed to reproduce Pedersen et al's results with new model wind_veer: 0.0 # Configure the wake model. wake: model_strings: combination_model: sosfs deflection_model: none turbulence_model: none velocity_model: turbopark # current TurboPark model in FLORIS enable_secondary_steering: false enable_yaw_added_recovery: false enable_active_wake_mixing: false enable_transverse_velocities: false wake_velocity_parameters: # Parameters for the wake velocity deficit model turbopark: A: 0.04 sigma_max_rel: 4.0 wake_deflection_parameters: none: wake_turbulence_parameters: none: ================================================ FILE: examples/inputs/turboparkgauss.yaml ================================================ name: TurbOParkGauss description: Three turbines using TurbOParkGauss model floris_version: v4 logging: console: enable: false level: WARNING file: enable: false level: WARNING solver: type: turbine_cubature_grid # turboparkgauss does not work with type: turbine_grid turbine_grid_points: 4 # 4 is sufficient in nearly all cases farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 reference_wind_height: -1 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: none turbulence_model: none velocity_model: turboparkgauss enable_secondary_steering: false enable_yaw_added_recovery: false enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: none: wake_velocity_parameters: turboparkgauss: A: 0.04 include_mirror_wake: true wake_turbulence_parameters: none: ================================================ FILE: examples/inputs/turboparkgauss_cubature.yaml ================================================ name: Case TwinPark new implementation description: Two aligned wind turbines with 5D spacing under the new implementation floris_version: v4.0 logging: console: enable: true # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING file: enable: false # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING solver: type: turbine_cubature_grid # turboparkgauss does not work with type: turbine_grid turbine_grid_points: 6 # 4 is sufficient in nearly all cases farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: # Constant CT turbine (TODO: implement in script?) - nrel_5MW # Will be replaced in script # Configure the atmospheric conditions. flow_field: air_density: 1.225 reference_wind_height: -1 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_speeds: - 8.0 wind_shear: 0.0 # Needed to reproduce Pedersen et al's results wind_veer: 0.0 # Configure the wake model. wake: model_strings: combination_model: sosfs deflection_model: none turbulence_model: none velocity_model: turboparkgauss # New model enable_secondary_steering: false enable_yaw_added_recovery: false enable_active_wake_mixing: false enable_transverse_velocities: false wake_velocity_parameters: # Parameters for the wake velocity deficit model turboparkgauss: A: 0.04 include_mirror_wake: true wake_deflection_parameters: none: wake_turbulence_parameters: none: ================================================ FILE: examples/inputs/wind_rose.csv ================================================ ws,wd,freq_val 0.0,0.0,1.629513753096076e-05 0.0,5.0,1.629513753096076e-05 0.0,10.0,4.888541259288228e-05 0.0,15.0,3.259027506192152e-05 0.0,20.0,6.518055012384304e-05 0.0,25.0,6.518055012384304e-05 0.0,30.0,1.629513753096076e-05 0.0,35.0,0.0 0.0,40.0,3.259027506192152e-05 0.0,45.0,4.888541259288228e-05 0.0,50.0,0.0 0.0,55.0,0.0 0.0,60.0,4.888541259288228e-05 0.0,65.0,1.629513753096076e-05 0.0,70.0,3.259027506192152e-05 0.0,75.0,1.629513753096076e-05 0.0,80.0,3.259027506192152e-05 0.0,85.0,4.888541259288228e-05 0.0,90.0,4.888541259288228e-05 0.0,95.0,4.888541259288228e-05 0.0,100.0,4.888541259288228e-05 0.0,105.0,6.518055012384304e-05 0.0,110.0,4.888541259288228e-05 0.0,115.0,4.888541259288228e-05 0.0,120.0,0.0 0.0,125.0,1.629513753096076e-05 0.0,130.0,1.629513753096076e-05 0.0,135.0,1.629513753096076e-05 0.0,140.0,3.259027506192152e-05 0.0,145.0,4.888541259288228e-05 0.0,150.0,1.629513753096076e-05 0.0,155.0,1.629513753096076e-05 0.0,160.0,0.0 0.0,165.0,1.629513753096076e-05 0.0,170.0,1.629513753096076e-05 0.0,175.0,3.259027506192152e-05 0.0,180.0,0.0 0.0,185.0,1.629513753096076e-05 0.0,190.0,1.629513753096076e-05 0.0,195.0,1.629513753096076e-05 0.0,200.0,1.629513753096076e-05 0.0,205.0,3.259027506192152e-05 0.0,210.0,1.629513753096076e-05 0.0,215.0,3.259027506192152e-05 0.0,220.0,4.888541259288228e-05 0.0,225.0,4.888541259288228e-05 0.0,230.0,4.888541259288228e-05 0.0,235.0,4.888541259288228e-05 0.0,240.0,4.888541259288228e-05 0.0,245.0,1.629513753096076e-05 0.0,250.0,8.14756876548038e-05 0.0,255.0,1.629513753096076e-05 0.0,260.0,1.629513753096076e-05 0.0,265.0,1.629513753096076e-05 0.0,270.0,1.629513753096076e-05 0.0,275.0,1.629513753096076e-05 0.0,280.0,3.259027506192152e-05 0.0,285.0,1.629513753096076e-05 0.0,290.0,4.888541259288228e-05 0.0,295.0,0.0 0.0,300.0,0.0 0.0,305.0,1.629513753096076e-05 0.0,310.0,1.629513753096076e-05 0.0,315.0,0.0 0.0,320.0,9.777082518576456e-05 0.0,325.0,0.0 0.0,330.0,0.0 0.0,335.0,0.0 0.0,340.0,1.629513753096076e-05 0.0,345.0,3.259027506192152e-05 0.0,350.0,4.888541259288228e-05 0.0,355.0,6.518055012384304e-05 1.0,0.0,0.00019554165037152912 1.0,5.0,0.00022813192543345064 1.0,10.0,0.00026072220049537216 1.0,15.0,0.00014665623777864684 1.0,20.0,0.00017924651284056836 1.0,25.0,0.00026072220049537216 1.0,30.0,0.00024442706296441143 1.0,35.0,0.00024442706296441143 1.0,40.0,0.00019554165037152912 1.0,45.0,0.0001629513753096076 1.0,50.0,0.0002118367879024899 1.0,55.0,0.00026072220049537216 1.0,60.0,0.00014665623777864684 1.0,65.0,0.00021183678790248988 1.0,70.0,0.00030960761308825447 1.0,75.0,0.00030960761308825447 1.0,80.0,0.0003259027506192152 1.0,85.0,0.00029331247555729374 1.0,90.0,0.0001629513753096076 1.0,95.0,0.00017924651284056836 1.0,100.0,0.0001629513753096076 1.0,105.0,0.00021183678790248988 1.0,110.0,0.00024442706296441143 1.0,115.0,0.00024442706296441143 1.0,120.0,0.0002933124755572937 1.0,125.0,0.00024442706296441143 1.0,130.0,0.0002770173380263329 1.0,135.0,0.00024442706296441143 1.0,140.0,0.00026072220049537216 1.0,145.0,0.0003259027506192152 1.0,150.0,0.00022813192543345064 1.0,155.0,0.0002933124755572937 1.0,160.0,0.00024442706296441143 1.0,165.0,0.0001629513753096076 1.0,170.0,0.00021183678790248988 1.0,175.0,0.00017924651284056836 1.0,180.0,0.00019554165037152912 1.0,185.0,0.0001629513753096076 1.0,190.0,0.0002933124755572937 1.0,195.0,0.00022813192543345064 1.0,200.0,0.0002933124755572937 1.0,205.0,0.00021183678790248988 1.0,210.0,0.0002933124755572937 1.0,215.0,0.00021183678790248988 1.0,220.0,0.00026072220049537216 1.0,225.0,0.0003259027506192152 1.0,230.0,0.00021183678790248988 1.0,235.0,0.00030960761308825447 1.0,240.0,0.000342197888150176 1.0,245.0,0.00026072220049537216 1.0,250.0,0.00021183678790248988 1.0,255.0,0.00017924651284056836 1.0,260.0,0.0003259027506192152 1.0,265.0,0.00021183678790248988 1.0,270.0,0.0003259027506192152 1.0,275.0,0.00019554165037152912 1.0,280.0,9.777082518576456e-05 1.0,285.0,0.0002770173380263329 1.0,290.0,0.00019554165037152912 1.0,295.0,0.00026072220049537216 1.0,300.0,0.00022813192543345064 1.0,305.0,0.00019554165037152912 1.0,310.0,0.00021183678790248988 1.0,315.0,0.00026072220049537216 1.0,320.0,0.00021183678790248988 1.0,325.0,0.00019554165037152912 1.0,330.0,0.0002770173380263329 1.0,335.0,0.00022813192543345064 1.0,340.0,0.00013036110024768608 1.0,345.0,0.00022813192543345064 1.0,350.0,0.0001629513753096076 1.0,355.0,0.00026072220049537216 2.0,0.0,0.0006029200886455481 2.0,5.0,0.000407378438274019 2.0,10.0,0.0005214444009907443 2.0,15.0,0.000407378438274019 2.0,20.0,0.0005540346760526659 2.0,25.0,0.00040737843827401903 2.0,30.0,0.0004888541259288228 2.0,35.0,0.0005051492634597836 2.0,40.0,0.0005866249511145874 2.0,45.0,0.0005051492634597836 2.0,50.0,0.0005540346760526658 2.0,55.0,0.0005866249511145874 2.0,60.0,0.0006029200886455481 2.0,65.0,0.0007006909138313127 2.0,70.0,0.0005540346760526659 2.0,75.0,0.0004888541259288229 2.0,80.0,0.00043996871333594055 2.0,85.0,0.0005377395385217051 2.0,90.0,0.0002770173380263329 2.0,95.0,0.0004562638508669013 2.0,100.0,0.0005540346760526658 2.0,105.0,0.0004888541259288229 2.0,110.0,0.0005051492634597836 2.0,115.0,0.0005051492634597836 2.0,120.0,0.000342197888150176 2.0,125.0,0.0004562638508669013 2.0,130.0,0.0005214444009907443 2.0,135.0,0.00042367357580497977 2.0,140.0,0.0007332811888932343 2.0,145.0,0.000472558988397862 2.0,150.0,0.0005214444009907442 2.0,155.0,0.000749576326424195 2.0,160.0,0.0008473471516099595 2.0,165.0,0.0005377395385217051 2.0,170.0,0.0005214444009907443 2.0,175.0,0.000749576326424195 2.0,180.0,0.0006192152261765088 2.0,185.0,0.0005214444009907443 2.0,190.0,0.0006029200886455481 2.0,195.0,0.0005703298135836265 2.0,200.0,0.0005866249511145874 2.0,205.0,0.0006192152261765089 2.0,210.0,0.0006843957763003519 2.0,215.0,0.0006843957763003519 2.0,220.0,0.0007821666014861165 2.0,225.0,0.0007006909138313126 2.0,230.0,0.0006029200886455482 2.0,235.0,0.0004399687133359405 2.0,240.0,0.0005866249511145874 2.0,245.0,0.0006192152261765088 2.0,250.0,0.0003910833007430583 2.0,255.0,0.0004399687133359405 2.0,260.0,0.0005051492634597836 2.0,265.0,0.00034219788815017594 2.0,270.0,0.00034219788815017594 2.0,275.0,0.0005214444009907443 2.0,280.0,0.0003584930256811367 2.0,285.0,0.0004236735758049798 2.0,290.0,0.00039108330074305825 2.0,295.0,0.00026072220049537216 2.0,300.0,0.00022813192543345064 2.0,305.0,0.000342197888150176 2.0,310.0,0.00030960761308825447 2.0,315.0,0.0004888541259288229 2.0,320.0,0.0002933124755572937 2.0,325.0,0.0004888541259288229 2.0,330.0,0.0005051492634597836 2.0,335.0,0.00022813192543345064 2.0,340.0,0.00030960761308825447 2.0,345.0,0.0004888541259288229 2.0,350.0,0.00043996871333594055 2.0,355.0,0.00043996871333594055 3.0,0.0,0.0006681006387693913 3.0,5.0,0.0007332811888932342 3.0,10.0,0.0007332811888932342 3.0,15.0,0.0007332811888932343 3.0,20.0,0.0007332811888932343 3.0,25.0,0.0007169860513622735 3.0,30.0,0.0008310520140789988 3.0,35.0,0.000749576326424195 3.0,40.0,0.0007984617390170772 3.0,45.0,0.0009125277017338027 3.0,50.0,0.0008636422891409204 3.0,55.0,0.0005540346760526658 3.0,60.0,0.0008636422891409204 3.0,65.0,0.0009940033893886065 3.0,70.0,0.0007984617390170773 3.0,75.0,0.0008962325642028417 3.0,80.0,0.0008799374266718811 3.0,85.0,0.0008636422891409204 3.0,90.0,0.0008799374266718811 3.0,95.0,0.0007006909138313127 3.0,100.0,0.0008310520140789987 3.0,105.0,0.0008310520140789988 3.0,110.0,0.0008799374266718811 3.0,115.0,0.0007169860513622735 3.0,120.0,0.000879937426671881 3.0,125.0,0.0008310520140789988 3.0,130.0,0.0008799374266718811 3.0,135.0,0.0008310520140789987 3.0,140.0,0.0010428888019814887 3.0,145.0,0.0011243644896362925 3.0,150.0,0.000945117976795724 3.0,155.0,0.001026593664450528 3.0,160.0,0.0011406596271672533 3.0,165.0,0.0009940033893886062 3.0,170.0,0.001433972102724547 3.0,175.0,0.0012710207274149394 3.0,180.0,0.0012384304523530179 3.0,185.0,0.001026593664450528 3.0,190.0,0.0011243644896362925 3.0,195.0,0.0009614131143266848 3.0,200.0,0.0009940033893886065 3.0,205.0,0.0011243644896362925 3.0,210.0,0.0009940033893886065 3.0,215.0,0.0007984617390170772 3.0,220.0,0.0007006909138313126 3.0,225.0,0.0007821666014861165 3.0,230.0,0.0008473471516099595 3.0,235.0,0.0006843957763003519 3.0,240.0,0.0009940033893886065 3.0,245.0,0.0006355103637074697 3.0,250.0,0.0007006909138313127 3.0,255.0,0.0006681006387693911 3.0,260.0,0.000684395776300352 3.0,265.0,0.0005866249511145873 3.0,270.0,0.0008473471516099595 3.0,275.0,0.00043996871333594055 3.0,280.0,0.0005540346760526658 3.0,285.0,0.0006681006387693911 3.0,290.0,0.0006192152261765089 3.0,295.0,0.0006029200886455481 3.0,300.0,0.0006029200886455481 3.0,305.0,0.0004725589883978621 3.0,310.0,0.0006355103637074697 3.0,315.0,0.0006681006387693911 3.0,320.0,0.0006192152261765089 3.0,325.0,0.0005051492634597836 3.0,330.0,0.0007006909138313126 3.0,335.0,0.0006681006387693911 3.0,340.0,0.0009125277017338026 3.0,345.0,0.0007658714639551558 3.0,350.0,0.0006518055012384304 3.0,355.0,0.0007169860513622733 4.0,0.0,0.0008310520140789988 4.0,5.0,0.0009125277017338026 4.0,10.0,0.000749576326424195 4.0,15.0,0.00149915265284839 4.0,20.0,0.0013199061400078218 4.0,25.0,0.0008962325642028418 4.0,30.0,0.001352496415069743 4.0,35.0,0.0012547255898839786 4.0,40.0,0.0011080693521053318 4.0,45.0,0.0010102985269195672 4.0,50.0,0.0011243644896362925 4.0,55.0,0.0010754790770434101 4.0,60.0,0.0011243644896362925 4.0,65.0,0.0013036110024768608 4.0,70.0,0.0010265936644505277 4.0,75.0,0.001482857515317429 4.0,80.0,0.0008636422891409203 4.0,85.0,0.000896232564202842 4.0,90.0,0.0010102985269195672 4.0,95.0,0.0008962325642028418 4.0,100.0,0.0008636422891409203 4.0,105.0,0.0008799374266718811 4.0,110.0,0.0009451179767957243 4.0,115.0,0.0014502672402555079 4.0,120.0,0.0012710207274149394 4.0,125.0,0.001222135314822057 4.0,130.0,0.001222135314822057 4.0,135.0,0.0010102985269195672 4.0,140.0,0.0014013818276626254 4.0,145.0,0.0012384304523530179 4.0,150.0,0.0012384304523530179 4.0,155.0,0.0013362012775387823 4.0,160.0,0.001482857515317429 4.0,165.0,0.002167253291617781 4.0,170.0,0.0015480380654412723 4.0,175.0,0.0014176769651935864 4.0,180.0,0.0014176769651935862 4.0,185.0,0.0014665623777864686 4.0,190.0,0.0012058401772910964 4.0,195.0,0.001156954764698214 4.0,200.0,0.0011569547646982138 4.0,205.0,0.0013362012775387825 4.0,210.0,0.0010102985269195672 4.0,215.0,0.001156954764698214 4.0,220.0,0.0007984617390170772 4.0,225.0,0.0009940033893886065 4.0,230.0,0.000749576326424195 4.0,235.0,0.0009777082518576457 4.0,240.0,0.0006681006387693911 4.0,245.0,0.0007658714639551558 4.0,250.0,0.0007006909138313126 4.0,255.0,0.0008799374266718811 4.0,260.0,0.0007169860513622733 4.0,265.0,0.0007332811888932342 4.0,270.0,0.000945117976795724 4.0,275.0,0.000684395776300352 4.0,280.0,0.0004888541259288229 4.0,285.0,0.0005866249511145874 4.0,290.0,0.0004562638508669013 4.0,295.0,0.0005051492634597835 4.0,300.0,0.0008473471516099595 4.0,305.0,0.0005866249511145875 4.0,310.0,0.0006518055012384304 4.0,315.0,0.0007658714639551558 4.0,320.0,0.0006029200886455481 4.0,325.0,0.0009125277017338026 4.0,330.0,0.0010754790770434101 4.0,335.0,0.0008310520140789988 4.0,340.0,0.0010428888019814887 4.0,345.0,0.0007658714639551558 4.0,350.0,0.0011243644896362925 4.0,355.0,0.0008799374266718811 5.0,0.0,0.0011080693521053318 5.0,5.0,0.0012547255898839784 5.0,10.0,0.001433972102724547 5.0,15.0,0.0012710207274149394 5.0,20.0,0.0013036110024768608 5.0,25.0,0.0014502672402555076 5.0,30.0,0.0012058401772910962 5.0,35.0,0.0013362012775387823 5.0,40.0,0.001368791552600704 5.0,45.0,0.0011080693521053318 5.0,50.0,0.0013524964150697432 5.0,55.0,0.001091774214574371 5.0,60.0,0.0013199061400078216 5.0,65.0,0.0014502672402555076 5.0,70.0,0.0010102985269195672 5.0,75.0,0.001368791552600704 5.0,80.0,0.0012873158649459 5.0,85.0,0.001222135314822057 5.0,90.0,0.000945117976795724 5.0,95.0,0.0011406596271672533 5.0,100.0,0.0011732499022291747 5.0,105.0,0.001368791552600704 5.0,110.0,0.0011895450397601355 5.0,115.0,0.0013524964150697432 5.0,120.0,0.0011732499022291747 5.0,125.0,0.0014339721027245471 5.0,130.0,0.0014013818276626254 5.0,135.0,0.001482857515317429 5.0,140.0,0.0014502672402555079 5.0,145.0,0.0014502672402555079 5.0,150.0,0.0019554165037152915 5.0,155.0,0.0017272845782818405 5.0,160.0,0.001776169990874723 5.0,165.0,0.0019554165037152915 5.0,170.0,0.0022976143918654675 5.0,175.0,0.0018413505409985659 5.0,180.0,0.0017435797158128017 5.0,185.0,0.0018739408160604876 5.0,190.0,0.0015643332029722332 5.0,195.0,0.0017924651284056837 5.0,200.0,0.0014502672402555076 5.0,205.0,0.0014176769651935862 5.0,210.0,0.0012547255898839786 5.0,215.0,0.0009940033893886062 5.0,220.0,0.0010591839395124494 5.0,225.0,0.0009777082518576457 5.0,230.0,0.000945117976795724 5.0,235.0,0.000961413114326685 5.0,240.0,0.000814756876548038 5.0,245.0,0.000814756876548038 5.0,250.0,0.0006192152261765089 5.0,255.0,0.0006518055012384304 5.0,260.0,0.0005866249511145875 5.0,265.0,0.00043996871333594055 5.0,270.0,0.0007332811888932343 5.0,275.0,0.0006192152261765089 5.0,280.0,0.000749576326424195 5.0,285.0,0.000749576326424195 5.0,290.0,0.0006192152261765089 5.0,295.0,0.0007658714639551557 5.0,300.0,0.0007169860513622735 5.0,305.0,0.0007984617390170772 5.0,310.0,0.0006681006387693911 5.0,315.0,0.0007658714639551558 5.0,320.0,0.0006843957763003519 5.0,325.0,0.0010102985269195672 5.0,330.0,0.0009125277017338026 5.0,335.0,0.0009451179767957242 5.0,340.0,0.0009451179767957243 5.0,345.0,0.0012547255898839786 5.0,350.0,0.0010754790770434101 5.0,355.0,0.0012384304523530177 6.0,0.0,0.001352496415069743 6.0,5.0,0.00149915265284839 6.0,10.0,0.0015480380654412723 6.0,15.0,0.0014828575153174295 6.0,20.0,0.0012873158649458999 6.0,25.0,0.0011243644896362925 6.0,30.0,0.0012710207274149394 6.0,35.0,0.0014665623777864684 6.0,40.0,0.0009777082518576457 6.0,45.0,0.0012384304523530177 6.0,50.0,0.0010754790770434101 6.0,55.0,0.0011732499022291747 6.0,60.0,0.0013036110024768608 6.0,65.0,0.0010591839395124494 6.0,70.0,0.0011243644896362925 6.0,75.0,0.0010428888019814887 6.0,80.0,0.0013036110024768608 6.0,85.0,0.0008636422891409203 6.0,90.0,0.0009777082518576457 6.0,95.0,0.0007821666014861164 6.0,100.0,0.0010917742145743709 6.0,105.0,0.0010102985269195672 6.0,110.0,0.0013362012775387823 6.0,115.0,0.0009777082518576457 6.0,120.0,0.0012873158649459 6.0,125.0,0.0012547255898839786 6.0,130.0,0.0012058401772910964 6.0,135.0,0.0016295137530960761 6.0,140.0,0.0016783991656889583 6.0,145.0,0.0014176769651935862 6.0,150.0,0.001825055403467605 6.0,155.0,0.0018087602659366446 6.0,160.0,0.0024931560422369967 6.0,165.0,0.002721287967670447 6.0,170.0,0.002656107417546604 6.0,175.0,0.0021998435666797027 6.0,180.0,0.0021020727414939383 6.0,185.0,0.0020368921913700953 6.0,190.0,0.0018413505409985659 6.0,195.0,0.0015154477903793506 6.0,200.0,0.0017598748533437622 6.0,205.0,0.0013199061400078218 6.0,210.0,0.0015480380654412723 6.0,215.0,0.0011243644896362925 6.0,220.0,0.0009614131143266849 6.0,225.0,0.001368791552600704 6.0,230.0,0.0011243644896362925 6.0,235.0,0.0009451179767957242 6.0,240.0,0.0007006909138313127 6.0,245.0,0.0007006909138313127 6.0,250.0,0.000749576326424195 6.0,255.0,0.0005051492634597836 6.0,260.0,0.000602920088645548 6.0,265.0,0.0005051492634597836 6.0,270.0,0.0005866249511145874 6.0,275.0,0.0007332811888932342 6.0,280.0,0.0006681006387693911 6.0,285.0,0.0005377395385217051 6.0,290.0,0.0007495763264241949 6.0,295.0,0.000749576326424195 6.0,300.0,0.0006355103637074697 6.0,305.0,0.0006681006387693913 6.0,310.0,0.001026593664450528 6.0,315.0,0.000749576326424195 6.0,320.0,0.0011243644896362925 6.0,325.0,0.0010754790770434103 6.0,330.0,0.0011080693521053318 6.0,335.0,0.0014828575153174293 6.0,340.0,0.0011406596271672533 6.0,345.0,0.0014502672402555079 6.0,350.0,0.0012547255898839786 6.0,355.0,0.0013524964150697432 7.0,0.0,0.0014665623777864686 7.0,5.0,0.0015806283405031937 7.0,10.0,0.0020205970538391344 7.0,15.0,0.00149915265284839 7.0,20.0,0.0014339721027245471 7.0,25.0,0.0012873158649459 7.0,30.0,0.0012547255898839786 7.0,35.0,0.0011080693521053318 7.0,40.0,0.001026593664450528 7.0,45.0,0.0010754790770434103 7.0,50.0,0.0010102985269195672 7.0,55.0,0.0010102985269195672 7.0,60.0,0.0013362012775387823 7.0,65.0,0.0013850866901316647 7.0,70.0,0.0012710207274149394 7.0,75.0,0.001156954764698214 7.0,80.0,0.0012058401772910964 7.0,85.0,0.0010428888019814887 7.0,90.0,0.0008636422891409203 7.0,95.0,0.0007984617390170772 7.0,100.0,0.0011895450397601355 7.0,105.0,0.0008473471516099595 7.0,110.0,0.0012221353148220572 7.0,115.0,0.0011895450397601355 7.0,120.0,0.0014665623777864686 7.0,125.0,0.0013850866901316647 7.0,130.0,0.0013362012775387823 7.0,135.0,0.0016621040281579976 7.0,140.0,0.001906531091122409 7.0,145.0,0.0020205970538391344 7.0,150.0,0.0016295137530960761 7.0,155.0,0.0019554165037152915 7.0,160.0,0.0024279754921131534 7.0,165.0,0.002607222004953722 7.0,170.0,0.0025583365923608397 7.0,175.0,0.0024442706296441143 7.0,180.0,0.0019391213661843305 7.0,185.0,0.0019554165037152915 7.0,190.0,0.002265024116803546 7.0,195.0,0.0019554165037152915 7.0,200.0,0.0011895450397601355 7.0,205.0,0.0012221353148220572 7.0,210.0,0.0011406596271672533 7.0,215.0,0.0012058401772910962 7.0,220.0,0.0012547255898839786 7.0,225.0,0.0009451179767957242 7.0,230.0,0.0006192152261765089 7.0,235.0,0.0005703298135836266 7.0,240.0,0.0005866249511145875 7.0,245.0,0.0007332811888932342 7.0,250.0,0.0005214444009907443 7.0,255.0,0.0005540346760526659 7.0,260.0,0.0006192152261765088 7.0,265.0,0.0006192152261765089 7.0,270.0,0.0004725589883978621 7.0,275.0,0.0004725589883978621 7.0,280.0,0.0006355103637074697 7.0,285.0,0.0006843957763003519 7.0,290.0,0.0005051492634597836 7.0,295.0,0.0005051492634597836 7.0,300.0,0.0007984617390170773 7.0,305.0,0.000814756876548038 7.0,310.0,0.0006192152261765088 7.0,315.0,0.0006681006387693911 7.0,320.0,0.0008636422891409204 7.0,325.0,0.0012384304523530179 7.0,330.0,0.0016783991656889583 7.0,335.0,0.0011895450397601355 7.0,340.0,0.0011569547646982142 7.0,345.0,0.001156954764698214 7.0,350.0,0.0016132186155651154 7.0,355.0,0.0015480380654412723 8.0,0.0,0.0020857776039629773 8.0,5.0,0.0018250554034676054 8.0,10.0,0.0019391213661843305 8.0,15.0,0.001433972102724547 8.0,20.0,0.0012058401772910964 8.0,25.0,0.0012873158649459003 8.0,30.0,0.001482857515317429 8.0,35.0,0.0012710207274149396 8.0,40.0,0.0011243644896362925 8.0,45.0,0.0010917742145743709 8.0,50.0,0.0012384304523530179 8.0,55.0,0.0009451179767957242 8.0,60.0,0.001156954764698214 8.0,65.0,0.0010917742145743709 8.0,70.0,0.0010591839395124496 8.0,75.0,0.0008799374266718811 8.0,80.0,0.0007658714639551558 8.0,85.0,0.0008310520140789988 8.0,90.0,0.0007332811888932343 8.0,95.0,0.0008636422891409204 8.0,100.0,0.0007658714639551558 8.0,105.0,0.000945117976795724 8.0,110.0,0.0009125277017338026 8.0,115.0,0.0009125277017338026 8.0,120.0,0.0010591839395124494 8.0,125.0,0.0009940033893886065 8.0,130.0,0.0014502672402555076 8.0,135.0,0.00149915265284839 8.0,140.0,0.0017435797158128013 8.0,145.0,0.0018087602659366446 8.0,150.0,0.002053187328901056 8.0,155.0,0.0022813192543345065 8.0,160.0,0.0025583365923608397 8.0,165.0,0.0024279754921131534 8.0,170.0,0.002672402555077565 8.0,175.0,0.0025746317298918 8.0,180.0,0.0027212879676704474 8.0,185.0,0.0023464998044583495 8.0,190.0,0.0029168296180419762 8.0,195.0,0.0017109894407508798 8.0,200.0,0.0014502672402555079 8.0,205.0,0.0016132186155651152 8.0,210.0,0.0013362012775387823 8.0,215.0,0.0010591839395124494 8.0,220.0,0.0011406596271672533 8.0,225.0,0.0006029200886455482 8.0,230.0,0.0006192152261765089 8.0,235.0,0.0004236735758049798 8.0,240.0,0.0006192152261765089 8.0,245.0,0.0006518055012384304 8.0,250.0,0.0004888541259288229 8.0,255.0,0.0007169860513622736 8.0,260.0,0.0005377395385217052 8.0,265.0,0.0005866249511145874 8.0,270.0,0.0003096076130882544 8.0,275.0,0.0003747881632120975 8.0,280.0,0.0005051492634597836 8.0,285.0,0.00047255898839786213 8.0,290.0,0.000684395776300352 8.0,295.0,0.0007495763264241949 8.0,300.0,0.0005703298135836266 8.0,305.0,0.0006029200886455482 8.0,310.0,0.0008473471516099594 8.0,315.0,0.000749576326424195 8.0,320.0,0.0009288228392647633 8.0,325.0,0.0012384304523530177 8.0,330.0,0.0011895450397601355 8.0,335.0,0.0012873158649459 8.0,340.0,0.0016132186155651152 8.0,345.0,0.0014991526528483898 8.0,350.0,0.0018739408160604876 8.0,355.0,0.0017761699908747227 9.0,0.0,0.0019228262286533698 9.0,5.0,0.001857645678529527 9.0,10.0,0.0018250554034676052 9.0,15.0,0.001694694303219919 9.0,20.0,0.0014339721027245471 9.0,25.0,0.0015154477903793506 9.0,30.0,0.0011406596271672533 9.0,35.0,0.0012710207274149394 9.0,40.0,0.0010102985269195672 9.0,45.0,0.0009777082518576457 9.0,50.0,0.0010428888019814887 9.0,55.0,0.0006029200886455481 9.0,60.0,0.0008636422891409203 9.0,65.0,0.0006843957763003519 9.0,70.0,0.0006355103637074697 9.0,75.0,0.0008962325642028418 9.0,80.0,0.0010917742145743709 9.0,85.0,0.0008310520140789989 9.0,90.0,0.0007006909138313127 9.0,95.0,0.0006518055012384304 9.0,100.0,0.0006681006387693911 9.0,105.0,0.0007332811888932342 9.0,110.0,0.0008310520140789988 9.0,115.0,0.0009288228392647633 9.0,120.0,0.0008636422891409203 9.0,125.0,0.0012221353148220572 9.0,130.0,0.001091774214574371 9.0,135.0,0.0014013818276626257 9.0,140.0,0.0014991526528483898 9.0,145.0,0.0017761699908747232 9.0,150.0,0.0017272845782818408 9.0,155.0,0.0022976143918654675 9.0,160.0,0.002395385217051232 9.0,165.0,0.002753878242732369 9.0,170.0,0.0025909268674227616 9.0,175.0,0.0030308955807587016 9.0,180.0,0.002395385217051232 9.0,185.0,0.002167253291617781 9.0,190.0,0.0025257463172989178 9.0,195.0,0.0022324338417416246 9.0,200.0,0.001564333202972233 9.0,205.0,0.0012547255898839786 9.0,210.0,0.001156954764698214 9.0,215.0,0.0010428888019814887 9.0,220.0,0.0008799374266718811 9.0,225.0,0.0008310520140789988 9.0,230.0,0.0004888541259288229 9.0,235.0,0.0005214444009907443 9.0,240.0,0.00026072220049537216 9.0,245.0,0.0006029200886455481 9.0,250.0,0.0005377395385217051 9.0,255.0,0.0004725589883978621 9.0,260.0,0.0004236735758049798 9.0,265.0,0.00043996871333594055 9.0,270.0,0.00030960761308825447 9.0,275.0,0.0003747881632120975 9.0,280.0,0.0004725589883978621 9.0,285.0,0.0005866249511145874 9.0,290.0,0.00030960761308825447 9.0,295.0,0.0007821666014861165 9.0,300.0,0.0007006909138313126 9.0,305.0,0.0007332811888932343 9.0,310.0,0.0006843957763003519 9.0,315.0,0.001026593664450528 9.0,320.0,0.001515447790379351 9.0,325.0,0.0017598748533437622 9.0,330.0,0.0016458088906270369 9.0,335.0,0.0016458088906270369 9.0,340.0,0.0015154477903793508 9.0,345.0,0.001971711641246252 9.0,350.0,0.0021509581540868207 9.0,355.0,0.002183548429148742 10.0,0.0,0.0021020727414939383 10.0,5.0,0.002053187328901056 10.0,10.0,0.0016783991656889586 10.0,15.0,0.0019065310911224093 10.0,20.0,0.001580628340503194 10.0,25.0,0.0012384304523530179 10.0,30.0,0.0015480380654412723 10.0,35.0,0.0011080693521053318 10.0,40.0,0.0008310520140789988 10.0,45.0,0.0008473471516099595 10.0,50.0,0.0005540346760526658 10.0,55.0,0.0007332811888932343 10.0,60.0,0.0009940033893886062 10.0,65.0,0.0006518055012384304 10.0,70.0,0.0005540346760526659 10.0,75.0,0.0006843957763003519 10.0,80.0,0.0006192152261765089 10.0,85.0,0.0008147568765480381 10.0,90.0,0.0004236735758049797 10.0,95.0,0.0007821666014861165 10.0,100.0,0.0005377395385217052 10.0,105.0,0.0005703298135836266 10.0,110.0,0.0009940033893886062 10.0,115.0,0.000684395776300352 10.0,120.0,0.0007332811888932342 10.0,125.0,0.000945117976795724 10.0,130.0,0.0010428888019814887 10.0,135.0,0.00149915265284839 10.0,140.0,0.0014339721027245467 10.0,145.0,0.0017435797158128015 10.0,150.0,0.001482857515317429 10.0,155.0,0.0017598748533437622 10.0,160.0,0.002460565767175075 10.0,165.0,0.0027701733802633294 10.0,170.0,0.003275322643723113 10.0,175.0,0.003047190718289662 10.0,180.0,0.003128666405944466 10.0,185.0,0.002933124755572937 10.0,190.0,0.0024768609047060358 10.0,195.0,0.0017109894407508798 10.0,200.0,0.0014339721027245471 10.0,205.0,0.0012058401772910962 10.0,210.0,0.0010591839395124494 10.0,215.0,0.0012058401772910964 10.0,220.0,0.0006518055012384304 10.0,225.0,0.00043996871333594055 10.0,230.0,0.00047255898839786213 10.0,235.0,0.0004725589883978621 10.0,240.0,0.0003584930256811367 10.0,245.0,0.0003584930256811368 10.0,250.0,0.0004073784382740191 10.0,255.0,0.0005540346760526658 10.0,260.0,0.0004236735758049798 10.0,265.0,0.00034219788815017594 10.0,270.0,0.00029331247555729374 10.0,275.0,0.0005866249511145874 10.0,280.0,0.0007821666014861165 10.0,285.0,0.00043996871333594045 10.0,290.0,0.0006355103637074697 10.0,295.0,0.0006192152261765089 10.0,300.0,0.0005703298135836266 10.0,305.0,0.0006681006387693913 10.0,310.0,0.0008310520140789988 10.0,315.0,0.0010102985269195672 10.0,320.0,0.0012873158649459 10.0,325.0,0.001368791552600704 10.0,330.0,0.0018250554034676052 10.0,335.0,0.002167253291617781 10.0,340.0,0.0020205970538391344 10.0,345.0,0.0019228262286533698 10.0,350.0,0.0021346630165558597 10.0,355.0,0.0020857776039629778 11.0,0.0,0.0017598748533437622 11.0,5.0,0.002053187328901056 11.0,10.0,0.0016783991656889583 11.0,15.0,0.0014013818276626252 11.0,20.0,0.001303611002476861 11.0,25.0,0.0011080693521053318 11.0,30.0,0.0012058401772910962 11.0,35.0,0.0009777082518576457 11.0,40.0,0.0009777082518576457 11.0,45.0,0.0007658714639551558 11.0,50.0,0.0006518055012384304 11.0,55.0,0.0005540346760526659 11.0,60.0,0.00047255898839786213 11.0,65.0,0.0004888541259288228 11.0,70.0,0.00030960761308825447 11.0,75.0,0.00045626385086690123 11.0,80.0,0.00030960761308825447 11.0,85.0,0.00043996871333594055 11.0,90.0,0.00043996871333594055 11.0,95.0,0.0003584930256811367 11.0,100.0,0.00030960761308825447 11.0,105.0,0.0006518055012384304 11.0,110.0,0.0005866249511145874 11.0,115.0,0.0003747881632120975 11.0,120.0,0.0007006909138313127 11.0,125.0,0.0007006909138313127 11.0,130.0,0.0009288228392647634 11.0,135.0,0.0011732499022291747 11.0,140.0,0.0014013818276626254 11.0,145.0,0.0014339721027245471 11.0,150.0,0.0015154477903793508 11.0,155.0,0.0020043019163081734 11.0,160.0,0.0028027636553252513 11.0,165.0,0.00299830530569678 11.0,170.0,0.003731586494590014 11.0,175.0,0.0034382740190327206 11.0,180.0,0.0029494198931038977 11.0,185.0,0.0026398122800156435 11.0,190.0,0.002248728979272585 11.0,195.0,0.0018087602659366444 11.0,200.0,0.0019717116412462524 11.0,205.0,0.001776169990874723 11.0,210.0,0.000945117976795724 11.0,215.0,0.0009451179767957242 11.0,220.0,0.0005540346760526659 11.0,225.0,0.00043996871333594055 11.0,230.0,0.00026072220049537216 11.0,235.0,0.00034219788815017594 11.0,240.0,0.0003584930256811368 11.0,245.0,0.00021183678790248988 11.0,250.0,0.0002770173380263329 11.0,255.0,0.00034219788815017594 11.0,260.0,0.00021183678790248988 11.0,265.0,0.0002770173380263329 11.0,270.0,0.00022813192543345064 11.0,275.0,0.00042367357580497977 11.0,280.0,0.0005051492634597836 11.0,285.0,0.0005866249511145874 11.0,290.0,0.0007006909138313127 11.0,295.0,0.0007169860513622735 11.0,300.0,0.0005540346760526658 11.0,305.0,0.0009288228392647633 11.0,310.0,0.0007821666014861166 11.0,315.0,0.0012547255898839786 11.0,320.0,0.0012873158649459 11.0,325.0,0.0014502672402555076 11.0,330.0,0.0019554165037152915 11.0,335.0,0.0020368921913700953 11.0,340.0,0.001988006778777213 11.0,345.0,0.0019391213661843307 11.0,350.0,0.0017761699908747227 11.0,355.0,0.0020043019163081734 12.0,0.0,0.0016458088906270369 12.0,5.0,0.0015969234780341547 12.0,10.0,0.0013036110024768608 12.0,15.0,0.00149915265284839 12.0,20.0,0.0013524964150697432 12.0,25.0,0.0009125277017338027 12.0,30.0,0.0007821666014861165 12.0,35.0,0.0005540346760526659 12.0,40.0,0.0006355103637074697 12.0,45.0,0.0006355103637074697 12.0,50.0,0.00037478816321209746 12.0,55.0,0.0004725589883978621 12.0,60.0,0.0003096076130882544 12.0,65.0,0.0003584930256811367 12.0,70.0,0.0002933124755572937 12.0,75.0,0.00017924651284056836 12.0,80.0,0.00030960761308825447 12.0,85.0,0.00029331247555729374 12.0,90.0,0.00026072220049537216 12.0,95.0,0.00024442706296441143 12.0,100.0,0.00039108330074305825 12.0,105.0,0.0004562638508669013 12.0,110.0,0.0003584930256811368 12.0,115.0,0.00043996871333594055 12.0,120.0,0.00039108330074305825 12.0,125.0,0.0005866249511145874 12.0,130.0,0.0007169860513622736 12.0,135.0,0.0007984617390170773 12.0,140.0,0.001156954764698214 12.0,145.0,0.001580628340503194 12.0,150.0,0.0018250554034676054 12.0,155.0,0.0023627949419893104 12.0,160.0,0.0025420414548298787 12.0,165.0,0.003340503193846956 12.0,170.0,0.003275322643723113 12.0,175.0,0.00321014209359927 12.0,180.0,0.0027701733802633294 12.0,185.0,0.0026398122800156435 12.0,190.0,0.0022976143918654675 12.0,195.0,0.0020205970538391344 12.0,200.0,0.0014828575153174293 12.0,205.0,0.0010754790770434101 12.0,210.0,0.0009125277017338027 12.0,215.0,0.0003910833007430583 12.0,220.0,0.0003096076130882544 12.0,225.0,0.00030960761308825447 12.0,230.0,0.00022813192543345064 12.0,235.0,0.00030960761308825447 12.0,240.0,0.00014665623777864684 12.0,245.0,0.00019554165037152912 12.0,250.0,0.00022813192543345064 12.0,255.0,0.00022813192543345064 12.0,260.0,0.00014665623777864684 12.0,265.0,0.00024442706296441143 12.0,270.0,0.00019554165037152912 12.0,275.0,0.0002770173380263329 12.0,280.0,0.0003584930256811368 12.0,285.0,0.0004562638508669013 12.0,290.0,0.0005703298135836266 12.0,295.0,0.0005214444009907443 12.0,300.0,0.0005540346760526658 12.0,305.0,0.0009288228392647633 12.0,310.0,0.0010102985269195672 12.0,315.0,0.001091774214574371 12.0,320.0,0.0010102985269195672 12.0,325.0,0.0015969234780341545 12.0,330.0,0.001433972102724547 12.0,335.0,0.0018087602659366444 12.0,340.0,0.0016132186155651154 12.0,345.0,0.0018739408160604876 12.0,350.0,0.0018250554034676052 12.0,355.0,0.0016783991656889583 13.0,0.0,0.0012873158649459003 13.0,5.0,0.0013036110024768608 13.0,10.0,0.0012710207274149394 13.0,15.0,0.0012873158649459003 13.0,20.0,0.0009940033893886065 13.0,25.0,0.0009125277017338026 13.0,30.0,0.0004236735758049798 13.0,35.0,0.000472558988397862 13.0,40.0,0.0003747881632120975 13.0,45.0,0.00030960761308825447 13.0,50.0,0.00026072220049537216 13.0,55.0,0.00026072220049537216 13.0,60.0,0.0003584930256811368 13.0,65.0,0.00013036110024768608 13.0,70.0,0.00014665623777864684 13.0,75.0,0.00022813192543345064 13.0,80.0,0.00013036110024768608 13.0,85.0,9.777082518576456e-05 13.0,90.0,0.00013036110024768608 13.0,95.0,0.00011406596271672532 13.0,100.0,0.00026072220049537216 13.0,105.0,0.00017924651284056836 13.0,110.0,0.00030960761308825447 13.0,115.0,0.00039108330074305825 13.0,120.0,0.0002770173380263329 13.0,125.0,0.000342197888150176 13.0,130.0,0.00039108330074305825 13.0,135.0,0.0007169860513622736 13.0,140.0,0.0009288228392647633 13.0,145.0,0.0010754790770434103 13.0,150.0,0.0016132186155651154 13.0,155.0,0.0020205970538391344 13.0,160.0,0.0021346630165558593 13.0,165.0,0.0030471907182896625 13.0,170.0,0.003079780993351584 13.0,175.0,0.0031123712684135055 13.0,180.0,0.00236279494198931 13.0,185.0,0.002167253291617781 13.0,190.0,0.0017761699908747232 13.0,195.0,0.0012221353148220572 13.0,200.0,0.0005214444009907443 13.0,205.0,0.000407378438274019 13.0,210.0,0.0003259027506192152 13.0,215.0,0.00030960761308825447 13.0,220.0,0.00011406596271672532 13.0,225.0,0.00014665623777864684 13.0,230.0,0.00021183678790248988 13.0,235.0,0.00013036110024768608 13.0,240.0,0.00017924651284056836 13.0,245.0,0.00019554165037152912 13.0,250.0,6.518055012384304e-05 13.0,255.0,9.777082518576456e-05 13.0,260.0,0.00014665623777864684 13.0,265.0,8.14756876548038e-05 13.0,270.0,0.00013036110024768608 13.0,275.0,0.00014665623777864684 13.0,280.0,0.00030960761308825447 13.0,285.0,0.0003259027506192152 13.0,290.0,0.0005214444009907443 13.0,295.0,0.0006843957763003519 13.0,300.0,0.0006518055012384305 13.0,305.0,0.0007821666014861166 13.0,310.0,0.0009125277017338026 13.0,315.0,0.0008473471516099595 13.0,320.0,0.0009288228392647634 13.0,325.0,0.0013850866901316647 13.0,330.0,0.0014828575153174293 13.0,335.0,0.0016783991656889586 13.0,340.0,0.0013850866901316647 13.0,345.0,0.001433972102724547 13.0,350.0,0.0013850866901316647 13.0,355.0,0.0014013818276626257 14.0,0.0,0.0006192152261765089 14.0,5.0,0.0007658714639551558 14.0,10.0,0.0007984617390170772 14.0,15.0,0.0006843957763003519 14.0,20.0,0.0007169860513622736 14.0,25.0,0.0004562638508669013 14.0,30.0,0.0003259027506192152 14.0,35.0,0.0003259027506192152 14.0,40.0,0.00027701733802633295 14.0,45.0,0.00017924651284056836 14.0,50.0,9.777082518576456e-05 14.0,55.0,6.518055012384304e-05 14.0,60.0,0.00013036110024768608 14.0,65.0,3.259027506192152e-05 14.0,70.0,4.888541259288228e-05 14.0,75.0,8.14756876548038e-05 14.0,80.0,4.888541259288228e-05 14.0,85.0,3.259027506192152e-05 14.0,90.0,3.259027506192152e-05 14.0,95.0,8.14756876548038e-05 14.0,100.0,8.14756876548038e-05 14.0,105.0,0.00014665623777864684 14.0,110.0,8.14756876548038e-05 14.0,115.0,0.00013036110024768608 14.0,120.0,8.14756876548038e-05 14.0,125.0,0.00017924651284056836 14.0,130.0,0.00039108330074305825 14.0,135.0,0.00034219788815017594 14.0,140.0,0.0004888541259288229 14.0,145.0,0.0005540346760526659 14.0,150.0,0.0005540346760526659 14.0,155.0,0.0009614131143266848 14.0,160.0,0.0017435797158128013 14.0,165.0,0.001792465128405684 14.0,170.0,0.0023627949419893104 14.0,175.0,0.0018739408160604876 14.0,180.0,0.0017109894407508798 14.0,185.0,0.0010428888019814887 14.0,190.0,0.0006681006387693913 14.0,195.0,0.0002770173380263329 14.0,200.0,0.00021183678790248988 14.0,205.0,0.00017924651284056836 14.0,210.0,0.00017924651284056836 14.0,215.0,9.777082518576456e-05 14.0,220.0,4.888541259288228e-05 14.0,225.0,1.629513753096076e-05 14.0,230.0,0.0 14.0,235.0,0.0 14.0,240.0,3.259027506192152e-05 14.0,245.0,0.00013036110024768608 14.0,250.0,3.259027506192152e-05 14.0,255.0,4.888541259288228e-05 14.0,260.0,4.888541259288228e-05 14.0,265.0,4.888541259288228e-05 14.0,270.0,1.629513753096076e-05 14.0,275.0,0.00013036110024768608 14.0,280.0,9.777082518576456e-05 14.0,285.0,0.00024442706296441143 14.0,290.0,0.00014665623777864684 14.0,295.0,0.0003910833007430583 14.0,300.0,0.000472558988397862 14.0,305.0,0.0006192152261765089 14.0,310.0,0.0005377395385217051 14.0,315.0,0.0005866249511145874 14.0,320.0,0.0007658714639551557 14.0,325.0,0.0010591839395124494 14.0,330.0,0.0012384304523530179 14.0,335.0,0.0010754790770434103 14.0,340.0,0.0008147568765480381 14.0,345.0,0.0008473471516099596 14.0,350.0,0.0008310520140789988 14.0,355.0,0.0009777082518576457 15.0,0.0,0.000472558988397862 15.0,5.0,0.000342197888150176 15.0,10.0,0.0004888541259288229 15.0,15.0,0.0002770173380263329 15.0,20.0,0.00027701733802633295 15.0,25.0,0.00030960761308825447 15.0,30.0,0.00019554165037152912 15.0,35.0,9.777082518576456e-05 15.0,40.0,6.518055012384304e-05 15.0,45.0,9.777082518576456e-05 15.0,50.0,6.518055012384304e-05 15.0,55.0,4.888541259288228e-05 15.0,60.0,1.629513753096076e-05 15.0,65.0,3.259027506192152e-05 15.0,70.0,4.888541259288228e-05 15.0,75.0,1.629513753096076e-05 15.0,80.0,8.14756876548038e-05 15.0,85.0,6.518055012384304e-05 15.0,90.0,1.629513753096076e-05 15.0,95.0,3.259027506192152e-05 15.0,100.0,0.0 15.0,105.0,8.14756876548038e-05 15.0,110.0,1.629513753096076e-05 15.0,115.0,8.14756876548038e-05 15.0,120.0,4.888541259288228e-05 15.0,125.0,1.629513753096076e-05 15.0,130.0,0.00027701733802633295 15.0,135.0,0.00014665623777864684 15.0,140.0,0.00024442706296441143 15.0,145.0,0.0001629513753096076 15.0,150.0,0.00027701733802633295 15.0,155.0,0.00034219788815017594 15.0,160.0,0.0007169860513622733 15.0,165.0,0.0009288228392647633 15.0,170.0,0.0012873158649459003 15.0,175.0,0.0008636422891409203 15.0,180.0,0.0007006909138313127 15.0,185.0,0.00040737843827401903 15.0,190.0,0.00024442706296441143 15.0,195.0,8.14756876548038e-05 15.0,200.0,4.888541259288228e-05 15.0,205.0,1.629513753096076e-05 15.0,210.0,3.259027506192152e-05 15.0,215.0,1.629513753096076e-05 15.0,220.0,0.0 15.0,225.0,4.888541259288228e-05 15.0,230.0,3.259027506192152e-05 15.0,235.0,1.629513753096076e-05 15.0,240.0,0.0 15.0,245.0,1.629513753096076e-05 15.0,250.0,0.0 15.0,255.0,0.0 15.0,260.0,1.629513753096076e-05 15.0,265.0,3.259027506192152e-05 15.0,270.0,0.0 15.0,275.0,1.629513753096076e-05 15.0,280.0,4.888541259288228e-05 15.0,285.0,6.518055012384304e-05 15.0,290.0,0.00013036110024768608 15.0,295.0,0.00022813192543345064 15.0,300.0,0.00035849302568113667 15.0,305.0,0.0005377395385217051 15.0,310.0,0.00042367357580497977 15.0,315.0,0.0005377395385217051 15.0,320.0,0.0005703298135836265 15.0,325.0,0.0007495763264241949 15.0,330.0,0.0006843957763003519 15.0,335.0,0.0008147568765480381 15.0,340.0,0.0006192152261765089 15.0,345.0,0.0006681006387693913 15.0,350.0,0.0007495763264241949 15.0,355.0,0.0007006909138313128 16.0,0.0,0.0004236735758049798 16.0,5.0,0.00027701733802633295 16.0,10.0,0.000342197888150176 16.0,15.0,0.00021183678790248988 16.0,20.0,0.000342197888150176 16.0,25.0,0.0001629513753096076 16.0,30.0,4.888541259288228e-05 16.0,35.0,8.14756876548038e-05 16.0,40.0,1.629513753096076e-05 16.0,45.0,3.259027506192152e-05 16.0,50.0,0.0 16.0,55.0,1.629513753096076e-05 16.0,60.0,1.629513753096076e-05 16.0,65.0,0.0 16.0,70.0,1.629513753096076e-05 16.0,75.0,0.0 16.0,80.0,0.0 16.0,85.0,0.0 16.0,90.0,0.0 16.0,95.0,1.629513753096076e-05 16.0,100.0,3.259027506192152e-05 16.0,105.0,1.629513753096076e-05 16.0,110.0,1.629513753096076e-05 16.0,115.0,3.259027506192152e-05 16.0,120.0,4.888541259288228e-05 16.0,125.0,1.629513753096076e-05 16.0,130.0,3.259027506192152e-05 16.0,135.0,6.518055012384304e-05 16.0,140.0,8.14756876548038e-05 16.0,145.0,9.777082518576456e-05 16.0,150.0,0.00013036110024768608 16.0,155.0,0.00017924651284056836 16.0,160.0,0.0003259027506192152 16.0,165.0,0.0005214444009907443 16.0,170.0,0.0006029200886455482 16.0,175.0,0.00039108330074305825 16.0,180.0,0.00021183678790248988 16.0,185.0,0.00014665623777864684 16.0,190.0,0.00014665623777864684 16.0,195.0,6.518055012384304e-05 16.0,200.0,1.629513753096076e-05 16.0,205.0,0.0 16.0,210.0,0.0 16.0,215.0,0.0 16.0,220.0,0.0 16.0,225.0,0.0 16.0,230.0,0.0 16.0,235.0,1.629513753096076e-05 16.0,240.0,3.259027506192152e-05 16.0,245.0,0.0 16.0,250.0,0.0 16.0,255.0,3.259027506192152e-05 16.0,260.0,1.629513753096076e-05 16.0,265.0,1.629513753096076e-05 16.0,270.0,3.259027506192152e-05 16.0,275.0,0.0 16.0,280.0,1.629513753096076e-05 16.0,285.0,3.259027506192152e-05 16.0,290.0,0.00011406596271672532 16.0,295.0,0.00014665623777864684 16.0,300.0,0.0001629513753096076 16.0,305.0,0.0003259027506192152 16.0,310.0,0.00019554165037152912 16.0,315.0,0.00026072220049537216 16.0,320.0,0.0002933124755572937 16.0,325.0,0.00039108330074305825 16.0,330.0,0.0005540346760526659 16.0,335.0,0.0005051492634597836 16.0,340.0,0.0005377395385217051 16.0,345.0,0.00039108330074305825 16.0,350.0,0.00042367357580497977 16.0,355.0,0.0005540346760526659 17.0,0.0,0.0002770173380263329 17.0,5.0,0.00019554165037152912 17.0,10.0,8.14756876548038e-05 17.0,15.0,9.777082518576456e-05 17.0,20.0,8.14756876548038e-05 17.0,25.0,3.259027506192152e-05 17.0,30.0,4.888541259288228e-05 17.0,35.0,3.259027506192152e-05 17.0,40.0,1.629513753096076e-05 17.0,45.0,0.0 17.0,50.0,0.0 17.0,55.0,0.0 17.0,60.0,0.0 17.0,65.0,0.0 17.0,70.0,0.0 17.0,75.0,3.259027506192152e-05 17.0,80.0,0.0 17.0,85.0,0.0 17.0,90.0,0.0 17.0,95.0,1.629513753096076e-05 17.0,100.0,1.629513753096076e-05 17.0,105.0,0.0 17.0,110.0,0.0 17.0,115.0,4.888541259288228e-05 17.0,120.0,0.0 17.0,125.0,0.0 17.0,130.0,1.629513753096076e-05 17.0,135.0,0.0 17.0,140.0,0.0 17.0,145.0,0.0 17.0,150.0,0.0 17.0,155.0,1.629513753096076e-05 17.0,160.0,0.00014665623777864684 17.0,165.0,0.00022813192543345064 17.0,170.0,0.00030960761308825447 17.0,175.0,0.0001629513753096076 17.0,180.0,0.00017924651284056836 17.0,185.0,8.14756876548038e-05 17.0,190.0,8.14756876548038e-05 17.0,195.0,0.0 17.0,200.0,3.259027506192152e-05 17.0,205.0,0.0 17.0,210.0,0.0 17.0,215.0,0.0 17.0,220.0,1.629513753096076e-05 17.0,225.0,0.0 17.0,230.0,1.629513753096076e-05 17.0,235.0,0.0 17.0,240.0,0.0 17.0,245.0,0.0 17.0,250.0,0.0 17.0,255.0,0.0 17.0,260.0,0.0 17.0,265.0,0.0 17.0,270.0,0.0 17.0,275.0,1.629513753096076e-05 17.0,280.0,0.0 17.0,285.0,0.0 17.0,290.0,3.259027506192152e-05 17.0,295.0,0.00011406596271672532 17.0,300.0,4.888541259288228e-05 17.0,305.0,8.14756876548038e-05 17.0,310.0,0.00013036110024768608 17.0,315.0,0.00022813192543345064 17.0,320.0,4.888541259288228e-05 17.0,325.0,0.00017924651284056836 17.0,330.0,0.0002770173380263329 17.0,335.0,0.00019554165037152912 17.0,340.0,0.00034219788815017594 17.0,345.0,0.000472558988397862 17.0,350.0,0.00030960761308825447 17.0,355.0,0.00019554165037152912 18.0,0.0,6.518055012384304e-05 18.0,5.0,9.777082518576456e-05 18.0,10.0,1.629513753096076e-05 18.0,15.0,1.629513753096076e-05 18.0,20.0,1.629513753096076e-05 18.0,25.0,4.888541259288228e-05 18.0,30.0,3.259027506192152e-05 18.0,35.0,3.259027506192152e-05 18.0,40.0,0.0 18.0,45.0,0.0 18.0,50.0,0.0 18.0,55.0,0.0 18.0,60.0,1.629513753096076e-05 18.0,65.0,1.629513753096076e-05 18.0,70.0,0.0 18.0,75.0,0.0 18.0,80.0,1.629513753096076e-05 18.0,85.0,0.0 18.0,90.0,0.0 18.0,95.0,0.0 18.0,100.0,0.0 18.0,105.0,1.629513753096076e-05 18.0,110.0,0.0 18.0,115.0,0.0 18.0,120.0,0.0 18.0,125.0,0.0 18.0,130.0,0.0 18.0,135.0,0.0 18.0,140.0,1.629513753096076e-05 18.0,145.0,0.0 18.0,150.0,0.0 18.0,155.0,6.518055012384304e-05 18.0,160.0,4.888541259288228e-05 18.0,165.0,6.518055012384304e-05 18.0,170.0,8.14756876548038e-05 18.0,175.0,0.00013036110024768608 18.0,180.0,0.00014665623777864684 18.0,185.0,4.888541259288228e-05 18.0,190.0,1.629513753096076e-05 18.0,195.0,0.0 18.0,200.0,0.0 18.0,205.0,0.0 18.0,210.0,1.629513753096076e-05 18.0,215.0,0.0 18.0,220.0,0.0 18.0,225.0,0.0 18.0,230.0,0.0 18.0,235.0,0.0 18.0,240.0,0.0 18.0,245.0,0.0 18.0,250.0,0.0 18.0,255.0,0.0 18.0,260.0,0.0 18.0,265.0,0.0 18.0,270.0,0.0 18.0,275.0,0.0 18.0,280.0,0.0 18.0,285.0,0.0 18.0,290.0,3.259027506192152e-05 18.0,295.0,9.777082518576456e-05 18.0,300.0,6.518055012384304e-05 18.0,305.0,4.888541259288228e-05 18.0,310.0,3.259027506192152e-05 18.0,315.0,9.777082518576456e-05 18.0,320.0,8.14756876548038e-05 18.0,325.0,6.518055012384304e-05 18.0,330.0,0.00013036110024768608 18.0,335.0,9.777082518576456e-05 18.0,340.0,0.00013036110024768608 18.0,345.0,0.00017924651284056836 18.0,350.0,0.00017924651284056836 18.0,355.0,8.14756876548038e-05 19.0,0.0,4.888541259288228e-05 19.0,5.0,6.518055012384304e-05 19.0,10.0,1.629513753096076e-05 19.0,15.0,0.0 19.0,20.0,0.0 19.0,25.0,1.629513753096076e-05 19.0,30.0,3.259027506192152e-05 19.0,35.0,0.0 19.0,40.0,0.0 19.0,45.0,0.0 19.0,50.0,0.0 19.0,55.0,0.0 19.0,60.0,0.0 19.0,65.0,0.0 19.0,70.0,0.0 19.0,75.0,0.0 19.0,80.0,0.0 19.0,85.0,0.0 19.0,90.0,0.0 19.0,95.0,0.0 19.0,100.0,0.0 19.0,105.0,0.0 19.0,110.0,0.0 19.0,115.0,0.0 19.0,120.0,0.0 19.0,125.0,0.0 19.0,130.0,0.0 19.0,135.0,0.0 19.0,140.0,1.629513753096076e-05 19.0,145.0,0.0 19.0,150.0,0.0 19.0,155.0,0.0 19.0,160.0,0.0 19.0,165.0,0.0 19.0,170.0,3.259027506192152e-05 19.0,175.0,6.518055012384304e-05 19.0,180.0,4.888541259288228e-05 19.0,185.0,1.629513753096076e-05 19.0,190.0,0.0 19.0,195.0,1.629513753096076e-05 19.0,200.0,0.0 19.0,205.0,0.0 19.0,210.0,0.0 19.0,215.0,1.629513753096076e-05 19.0,220.0,0.0 19.0,225.0,0.0 19.0,230.0,0.0 19.0,235.0,0.0 19.0,240.0,0.0 19.0,245.0,0.0 19.0,250.0,0.0 19.0,255.0,1.629513753096076e-05 19.0,260.0,0.0 19.0,265.0,0.0 19.0,270.0,0.0 19.0,275.0,0.0 19.0,280.0,0.0 19.0,285.0,3.259027506192152e-05 19.0,290.0,0.0 19.0,295.0,1.629513753096076e-05 19.0,300.0,4.888541259288228e-05 19.0,305.0,0.0 19.0,310.0,1.629513753096076e-05 19.0,315.0,0.0 19.0,320.0,0.0 19.0,325.0,9.777082518576456e-05 19.0,330.0,4.888541259288228e-05 19.0,335.0,4.888541259288228e-05 19.0,340.0,0.00014665623777864684 19.0,345.0,8.14756876548038e-05 19.0,350.0,0.00011406596271672532 19.0,355.0,4.888541259288228e-05 20.0,0.0,4.888541259288228e-05 20.0,5.0,0.0 20.0,10.0,0.0 20.0,15.0,1.629513753096076e-05 20.0,20.0,0.0 20.0,25.0,0.0 20.0,30.0,0.0 20.0,35.0,1.629513753096076e-05 20.0,40.0,0.0 20.0,45.0,0.0 20.0,50.0,0.0 20.0,55.0,0.0 20.0,60.0,0.0 20.0,65.0,0.0 20.0,70.0,0.0 20.0,75.0,0.0 20.0,80.0,0.0 20.0,85.0,0.0 20.0,90.0,0.0 20.0,95.0,0.0 20.0,100.0,0.0 20.0,105.0,0.0 20.0,110.0,1.629513753096076e-05 20.0,115.0,0.0 20.0,120.0,0.0 20.0,125.0,0.0 20.0,130.0,0.0 20.0,135.0,0.0 20.0,140.0,0.0 20.0,145.0,0.0 20.0,150.0,0.0 20.0,155.0,0.0 20.0,160.0,0.0 20.0,165.0,1.629513753096076e-05 20.0,170.0,1.629513753096076e-05 20.0,175.0,3.259027506192152e-05 20.0,180.0,1.629513753096076e-05 20.0,185.0,1.629513753096076e-05 20.0,190.0,0.0 20.0,195.0,0.0 20.0,200.0,0.0 20.0,205.0,0.0 20.0,210.0,0.0 20.0,215.0,0.0 20.0,220.0,0.0 20.0,225.0,0.0 20.0,230.0,0.0 20.0,235.0,0.0 20.0,240.0,1.629513753096076e-05 20.0,245.0,0.0 20.0,250.0,0.0 20.0,255.0,0.0 20.0,260.0,0.0 20.0,265.0,0.0 20.0,270.0,0.0 20.0,275.0,0.0 20.0,280.0,0.0 20.0,285.0,1.629513753096076e-05 20.0,290.0,0.0 20.0,295.0,0.0 20.0,300.0,0.0 20.0,305.0,3.259027506192152e-05 20.0,310.0,6.518055012384304e-05 20.0,315.0,3.259027506192152e-05 20.0,320.0,0.0 20.0,325.0,0.0 20.0,330.0,3.259027506192152e-05 20.0,335.0,0.0 20.0,340.0,1.629513753096076e-05 20.0,345.0,9.777082518576456e-05 20.0,350.0,4.888541259288228e-05 20.0,355.0,1.629513753096076e-05 21.0,0.0,0.0 21.0,5.0,1.629513753096076e-05 21.0,10.0,0.0 21.0,15.0,0.0 21.0,20.0,0.0 21.0,25.0,0.0 21.0,30.0,0.0 21.0,35.0,0.0 21.0,40.0,0.0 21.0,45.0,0.0 21.0,50.0,0.0 21.0,55.0,0.0 21.0,60.0,0.0 21.0,65.0,0.0 21.0,70.0,0.0 21.0,75.0,0.0 21.0,80.0,0.0 21.0,85.0,0.0 21.0,90.0,0.0 21.0,95.0,0.0 21.0,100.0,0.0 21.0,105.0,0.0 21.0,110.0,0.0 21.0,115.0,0.0 21.0,120.0,0.0 21.0,125.0,0.0 21.0,130.0,0.0 21.0,135.0,0.0 21.0,140.0,0.0 21.0,145.0,0.0 21.0,150.0,0.0 21.0,155.0,0.0 21.0,160.0,0.0 21.0,165.0,0.0 21.0,170.0,0.0 21.0,175.0,1.629513753096076e-05 21.0,180.0,0.0 21.0,185.0,0.0 21.0,190.0,0.0 21.0,195.0,0.0 21.0,200.0,0.0 21.0,205.0,0.0 21.0,210.0,1.629513753096076e-05 21.0,215.0,0.0 21.0,220.0,0.0 21.0,225.0,0.0 21.0,230.0,0.0 21.0,235.0,0.0 21.0,240.0,0.0 21.0,245.0,0.0 21.0,250.0,0.0 21.0,255.0,0.0 21.0,260.0,0.0 21.0,265.0,1.629513753096076e-05 21.0,270.0,0.0 21.0,275.0,0.0 21.0,280.0,0.0 21.0,285.0,0.0 21.0,290.0,0.0 21.0,295.0,0.0 21.0,300.0,0.0 21.0,305.0,0.0 21.0,310.0,0.0 21.0,315.0,0.0 21.0,320.0,0.0 21.0,325.0,0.0 21.0,330.0,1.629513753096076e-05 21.0,335.0,6.518055012384304e-05 21.0,340.0,3.259027506192152e-05 21.0,345.0,1.629513753096076e-05 21.0,350.0,0.0 21.0,355.0,0.0 22.0,0.0,0.0 22.0,5.0,0.0 22.0,10.0,0.0 22.0,15.0,0.0 22.0,20.0,0.0 22.0,25.0,0.0 22.0,30.0,0.0 22.0,35.0,0.0 22.0,40.0,0.0 22.0,45.0,0.0 22.0,50.0,0.0 22.0,55.0,0.0 22.0,60.0,0.0 22.0,65.0,0.0 22.0,70.0,0.0 22.0,75.0,0.0 22.0,80.0,0.0 22.0,85.0,0.0 22.0,90.0,0.0 22.0,95.0,0.0 22.0,100.0,0.0 22.0,105.0,0.0 22.0,110.0,0.0 22.0,115.0,0.0 22.0,120.0,0.0 22.0,125.0,0.0 22.0,130.0,0.0 22.0,135.0,0.0 22.0,140.0,0.0 22.0,145.0,0.0 22.0,150.0,0.0 22.0,155.0,0.0 22.0,160.0,0.0 22.0,165.0,1.629513753096076e-05 22.0,170.0,0.0 22.0,175.0,0.0 22.0,180.0,0.0 22.0,185.0,0.0 22.0,190.0,0.0 22.0,195.0,0.0 22.0,200.0,0.0 22.0,205.0,1.629513753096076e-05 22.0,210.0,0.0 22.0,215.0,0.0 22.0,220.0,0.0 22.0,225.0,0.0 22.0,230.0,0.0 22.0,235.0,0.0 22.0,240.0,0.0 22.0,245.0,0.0 22.0,250.0,0.0 22.0,255.0,0.0 22.0,260.0,0.0 22.0,265.0,0.0 22.0,270.0,0.0 22.0,275.0,0.0 22.0,280.0,0.0 22.0,285.0,0.0 22.0,290.0,1.629513753096076e-05 22.0,295.0,0.0 22.0,300.0,0.0 22.0,305.0,0.0 22.0,310.0,0.0 22.0,315.0,0.0 22.0,320.0,1.629513753096076e-05 22.0,325.0,0.0 22.0,330.0,0.0 22.0,335.0,0.0 22.0,340.0,0.0 22.0,345.0,0.0 22.0,350.0,0.0 22.0,355.0,0.0 23.0,0.0,0.0 23.0,5.0,0.0 23.0,10.0,0.0 23.0,15.0,0.0 23.0,20.0,0.0 23.0,25.0,0.0 23.0,30.0,0.0 23.0,35.0,0.0 23.0,40.0,0.0 23.0,45.0,0.0 23.0,50.0,0.0 23.0,55.0,0.0 23.0,60.0,0.0 23.0,65.0,0.0 23.0,70.0,0.0 23.0,75.0,0.0 23.0,80.0,0.0 23.0,85.0,0.0 23.0,90.0,0.0 23.0,95.0,0.0 23.0,100.0,0.0 23.0,105.0,0.0 23.0,110.0,0.0 23.0,115.0,0.0 23.0,120.0,0.0 23.0,125.0,0.0 23.0,130.0,0.0 23.0,135.0,0.0 23.0,140.0,0.0 23.0,145.0,0.0 23.0,150.0,0.0 23.0,155.0,0.0 23.0,160.0,0.0 23.0,165.0,0.0 23.0,170.0,0.0 23.0,175.0,0.0 23.0,180.0,0.0 23.0,185.0,0.0 23.0,190.0,0.0 23.0,195.0,0.0 23.0,200.0,0.0 23.0,205.0,0.0 23.0,210.0,0.0 23.0,215.0,0.0 23.0,220.0,0.0 23.0,225.0,0.0 23.0,230.0,0.0 23.0,235.0,0.0 23.0,240.0,0.0 23.0,245.0,0.0 23.0,250.0,0.0 23.0,255.0,0.0 23.0,260.0,0.0 23.0,265.0,0.0 23.0,270.0,0.0 23.0,275.0,0.0 23.0,280.0,0.0 23.0,285.0,0.0 23.0,290.0,0.0 23.0,295.0,0.0 23.0,300.0,0.0 23.0,305.0,1.629513753096076e-05 23.0,310.0,0.0 23.0,315.0,0.0 23.0,320.0,0.0 23.0,325.0,1.629513753096076e-05 23.0,330.0,0.0 23.0,335.0,3.259027506192152e-05 23.0,340.0,0.0 23.0,345.0,0.0 23.0,350.0,0.0 23.0,355.0,0.0 24.0,0.0,1.629513753096076e-05 24.0,5.0,0.0 24.0,10.0,0.0 24.0,15.0,0.0 24.0,20.0,0.0 24.0,25.0,0.0 24.0,30.0,0.0 24.0,35.0,0.0 24.0,40.0,0.0 24.0,45.0,0.0 24.0,50.0,0.0 24.0,55.0,0.0 24.0,60.0,0.0 24.0,65.0,0.0 24.0,70.0,0.0 24.0,75.0,0.0 24.0,80.0,0.0 24.0,85.0,0.0 24.0,90.0,0.0 24.0,95.0,0.0 24.0,100.0,0.0 24.0,105.0,0.0 24.0,110.0,0.0 24.0,115.0,0.0 24.0,120.0,0.0 24.0,125.0,0.0 24.0,130.0,0.0 24.0,135.0,0.0 24.0,140.0,0.0 24.0,145.0,0.0 24.0,150.0,0.0 24.0,155.0,0.0 24.0,160.0,0.0 24.0,165.0,0.0 24.0,170.0,0.0 24.0,175.0,0.0 24.0,180.0,0.0 24.0,185.0,0.0 24.0,190.0,0.0 24.0,195.0,0.0 24.0,200.0,0.0 24.0,205.0,0.0 24.0,210.0,0.0 24.0,215.0,0.0 24.0,220.0,0.0 24.0,225.0,0.0 24.0,230.0,0.0 24.0,235.0,0.0 24.0,240.0,0.0 24.0,245.0,0.0 24.0,250.0,0.0 24.0,255.0,0.0 24.0,260.0,0.0 24.0,265.0,0.0 24.0,270.0,0.0 24.0,275.0,0.0 24.0,280.0,0.0 24.0,285.0,0.0 24.0,290.0,0.0 24.0,295.0,0.0 24.0,300.0,0.0 24.0,305.0,0.0 24.0,310.0,0.0 24.0,315.0,0.0 24.0,320.0,0.0 24.0,325.0,0.0 24.0,330.0,0.0 24.0,335.0,0.0 24.0,340.0,0.0 24.0,345.0,0.0 24.0,350.0,1.629513753096076e-05 24.0,355.0,0.0 25.0,0.0,0.0 25.0,5.0,0.0 25.0,10.0,0.0 25.0,15.0,0.0 25.0,20.0,0.0 25.0,25.0,0.0 25.0,30.0,0.0 25.0,35.0,0.0 25.0,40.0,0.0 25.0,45.0,0.0 25.0,50.0,0.0 25.0,55.0,0.0 25.0,60.0,0.0 25.0,65.0,0.0 25.0,70.0,0.0 25.0,75.0,0.0 25.0,80.0,0.0 25.0,85.0,0.0 25.0,90.0,0.0 25.0,95.0,0.0 25.0,100.0,0.0 25.0,105.0,0.0 25.0,110.0,0.0 25.0,115.0,0.0 25.0,120.0,0.0 25.0,125.0,0.0 25.0,130.0,0.0 25.0,135.0,0.0 25.0,140.0,0.0 25.0,145.0,0.0 25.0,150.0,0.0 25.0,155.0,0.0 25.0,160.0,0.0 25.0,165.0,0.0 25.0,170.0,0.0 25.0,175.0,0.0 25.0,180.0,0.0 25.0,185.0,0.0 25.0,190.0,0.0 25.0,195.0,0.0 25.0,200.0,0.0 25.0,205.0,0.0 25.0,210.0,0.0 25.0,215.0,0.0 25.0,220.0,0.0 25.0,225.0,1.629513753096076e-05 25.0,230.0,0.0 25.0,235.0,0.0 25.0,240.0,0.0 25.0,245.0,0.0 25.0,250.0,0.0 25.0,255.0,0.0 25.0,260.0,0.0 25.0,265.0,0.0 25.0,270.0,1.629513753096076e-05 25.0,275.0,0.0 25.0,280.0,0.0 25.0,285.0,0.0 25.0,290.0,0.0 25.0,295.0,0.0 25.0,300.0,0.0 25.0,305.0,0.0 25.0,310.0,0.0 25.0,315.0,0.0 25.0,320.0,0.0 25.0,325.0,0.0 25.0,330.0,0.0 25.0,335.0,0.0 25.0,340.0,0.0 25.0,345.0,0.0 25.0,350.0,0.0 25.0,355.0,0.0 ================================================ FILE: examples/inputs_floating/emgauss_fixed.yaml ================================================ name: Emperical Gaussian description: Example of single fixed-bottom turbine floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - !include turbine_files/nrel_5MW_fixed.yaml flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: empirical_gauss turbulence_model: wake_induced_mixing velocity_model: empirical_gauss enable_secondary_steering: false enable_yaw_added_recovery: true enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs_floating/emgauss_floating.yaml ================================================ name: Emperical Gaussian description: Example of single floating turbine floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 - 630.0 - 1260.0 layout_y: - 0.0 - 0.0 - 0.0 turbine_type: - !include turbine_files/nrel_5MW_floating.yaml flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: empirical_gauss turbulence_model: wake_induced_mixing velocity_model: empirical_gauss enable_secondary_steering: false enable_yaw_added_recovery: true enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs_floating/emgauss_floating_fixedtilt15.yaml ================================================ name: Emperical Gaussian floating description: Single turbine using emperical Gaussian model for floating floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: - !include turbine_files/nrel_5MW_floating_fixedtilt15.yaml flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: empirical_gauss turbulence_model: wake_induced_mixing velocity_model: empirical_gauss enable_secondary_steering: false enable_yaw_added_recovery: true enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs_floating/emgauss_floating_fixedtilt5.yaml ================================================ name: Emperical Gaussian floating description: Single turbine using emperical Gaussian model for floating floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: - !include turbine_files/nrel_5MW_floating_fixedtilt5.yaml flow_field: air_density: 1.225 reference_wind_height: -1 # -1 is code for use the hub height turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: empirical_gauss turbulence_model: wake_induced_mixing velocity_model: empirical_gauss enable_secondary_steering: false enable_yaw_added_recovery: true enable_transverse_velocities: false enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 empirical_gauss: wake_expansion_rates: - 0.023 - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: examples/inputs_floating/gch_fixed.yaml ================================================ name: GCH description: Example of single fixed-bottom turbine floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: - !include turbine_files/nrel_5MW_fixed.yaml flow_field: air_density: 1.225 reference_wind_height: -1 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs_floating/gch_floating.yaml ================================================ name: GCH description: Example of single floating turbine floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: - !include turbine_files/nrel_5MW_floating.yaml flow_field: air_density: 1.225 reference_wind_height: -1 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs_floating/gch_floating_defined_floating.yaml ================================================ name: GCH description: Example of single floating turbine where the cp/ct is calculated with floating tilt included floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: - !include turbine_files/nrel_5MW_floating_defined_floating.yaml flow_field: air_density: 1.225 reference_wind_height: -1 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: examples/inputs_floating/turbine_files/nrel_5MW_fixed.yaml ================================================ turbine_type: 'nrel_5MW_floating' hub_height: 90.0 rotor_diameter: 125.88 TSR: 8.0 correct_cp_ct_for_tilt: True # Apply tilt correction to cp/ct power_thrust_table: ref_air_density: 1.225 ref_tilt: 5.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 power: - 0.0 - 0.0 - 40.518011517569214 - 177.67162506419703 - 403.900880943964 - 737.5889584824021 - 1187.1774030611875 - 1239.245945375778 - 1292.5184293723503 - 1347.3213147477102 - 1403.2573725578948 - 1460.7011898730707 - 1519.6419125979983 - 1580.174365096404 - 1642.1103166918167 - 1705.758292831 - 1771.1659528893977 - 2518.553107505315 - 3448.381605840943 - 3552.140809000129 - 3657.9545431794127 - 3765.121299313842 - 3873.928844315059 - 3984.4800226955504 - 4096.582833096852 - 4210.721306623712 - 4326.154305853405 - 4443.395565353604 - 4562.497934188341 - 4683.419890251577 - 4806.164748311019 - 4929.931918769215 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 0.0 - 0.0 thrust_coefficient: - 0.0 - 0.0 - 1.132034888 - 0.999470963 - 0.917697381 - 0.860849503 - 0.815371198 - 0.811614904 - 0.807939328 - 0.80443352 - 0.800993851 - 0.79768116 - 0.794529244 - 0.791495834 - 0.788560434 - 0.787217182 - 0.787127977 - 0.785839257 - 0.783812219 - 0.783568108 - 0.783328285 - 0.781194418 - 0.777292539 - 0.773464375 - 0.769690236 - 0.766001924 - 0.762348072 - 0.758760824 - 0.755242872 - 0.751792927 - 0.748434131 - 0.745113997 - 0.717806682 - 0.672204789 - 0.63831272 - 0.610176496 - 0.585456847 - 0.563222111 - 0.542912273 - 0.399312061 - 0.310517829 - 0.248633226 - 0.203543725 - 0.169616419 - 0.143478955 - 0.122938861 - 0.106515296 - 0.093026095 - 0.081648606 - 0.072197368 - 0.064388275 - 0.057782745 - 0.0 - 0.0 wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 7.5 - 7.6 - 7.7 - 7.8 - 7.9 - 8.0 - 9.0 - 10.0 - 10.1 - 10.2 - 10.3 - 10.4 - 10.5 - 10.6 - 10.7 - 10.8 - 10.9 - 11.0 - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 - 11.9 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 20.0 - 21.0 - 22.0 - 23.0 - 24.0 - 25.0 - 25.1 - 50.0 floating_tilt_table: tilt: - 5.0 - 5.0 - 5.0 - 5.0 - 5.0 wind_speed: - 0.0 - 4.0 - 11.0 - 25.0 - 50.0 ================================================ FILE: examples/inputs_floating/turbine_files/nrel_5MW_floating.yaml ================================================ turbine_type: 'nrel_5MW_floating' hub_height: 90.0 rotor_diameter: 125.88 TSR: 8.0 correct_cp_ct_for_tilt: True # Apply tilt correction to cp/ct power_thrust_table: ref_air_density: 1.225 ref_tilt: 5.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 power: - 0.0 - 0.0 - 40.518011517569214 - 177.67162506419703 - 403.900880943964 - 737.5889584824021 - 1187.1774030611875 - 1239.245945375778 - 1292.5184293723503 - 1347.3213147477102 - 1403.2573725578948 - 1460.7011898730707 - 1519.6419125979983 - 1580.174365096404 - 1642.1103166918167 - 1705.758292831 - 1771.1659528893977 - 2518.553107505315 - 3448.381605840943 - 3552.140809000129 - 3657.9545431794127 - 3765.121299313842 - 3873.928844315059 - 3984.4800226955504 - 4096.582833096852 - 4210.721306623712 - 4326.154305853405 - 4443.395565353604 - 4562.497934188341 - 4683.419890251577 - 4806.164748311019 - 4929.931918769215 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 0.0 - 0.0 thrust_coefficient: - 0.0 - 0.0 - 1.132034888 - 0.999470963 - 0.917697381 - 0.860849503 - 0.815371198 - 0.811614904 - 0.807939328 - 0.80443352 - 0.800993851 - 0.79768116 - 0.794529244 - 0.791495834 - 0.788560434 - 0.787217182 - 0.787127977 - 0.785839257 - 0.783812219 - 0.783568108 - 0.783328285 - 0.781194418 - 0.777292539 - 0.773464375 - 0.769690236 - 0.766001924 - 0.762348072 - 0.758760824 - 0.755242872 - 0.751792927 - 0.748434131 - 0.745113997 - 0.717806682 - 0.672204789 - 0.63831272 - 0.610176496 - 0.585456847 - 0.563222111 - 0.542912273 - 0.399312061 - 0.310517829 - 0.248633226 - 0.203543725 - 0.169616419 - 0.143478955 - 0.122938861 - 0.106515296 - 0.093026095 - 0.081648606 - 0.072197368 - 0.064388275 - 0.057782745 - 0.0 - 0.0 wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 7.5 - 7.6 - 7.7 - 7.8 - 7.9 - 8.0 - 9.0 - 10.0 - 10.1 - 10.2 - 10.3 - 10.4 - 10.5 - 10.6 - 10.7 - 10.8 - 10.9 - 11.0 - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 - 11.9 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 20.0 - 21.0 - 22.0 - 23.0 - 24.0 - 25.0 - 25.1 - 50.0 floating_tilt_table: tilt: - 5.0 - 5.0 - 9.0 - 5.0 - 5.0 wind_speed: - 0.0 - 4.0 - 11.0 - 25.0 - 50.0 ================================================ FILE: examples/inputs_floating/turbine_files/nrel_5MW_floating_defined_floating.yaml ================================================ turbine_type: 'nrel_5MW_floating' hub_height: 90.0 rotor_diameter: 125.88 TSR: 8.0 correct_cp_ct_for_tilt: False # Do not apply tilt correction to cp/ct power_thrust_table: ref_air_density: 1.225 ref_tilt: 5.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 power: - 0.0 - 0.0 - 40.518011517569214 - 177.67162506419703 - 403.900880943964 - 737.5889584824021 - 1187.1774030611875 - 1239.245945375778 - 1292.5184293723503 - 1347.3213147477102 - 1403.2573725578948 - 1460.7011898730707 - 1519.6419125979983 - 1580.174365096404 - 1642.1103166918167 - 1705.758292831 - 1771.1659528893977 - 2518.553107505315 - 3448.381605840943 - 3552.140809000129 - 3657.9545431794127 - 3765.121299313842 - 3873.928844315059 - 3984.4800226955504 - 4096.582833096852 - 4210.721306623712 - 4326.154305853405 - 4443.395565353604 - 4562.497934188341 - 4683.419890251577 - 4806.164748311019 - 4929.931918769215 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 0.0 - 0.0 thrust_coefficient: - 0.0 - 0.0 - 1.132034888 - 0.999470963 - 0.917697381 - 0.860849503 - 0.815371198 - 0.811614904 - 0.807939328 - 0.80443352 - 0.800993851 - 0.79768116 - 0.794529244 - 0.791495834 - 0.788560434 - 0.787217182 - 0.787127977 - 0.785839257 - 0.783812219 - 0.783568108 - 0.783328285 - 0.781194418 - 0.777292539 - 0.773464375 - 0.769690236 - 0.766001924 - 0.762348072 - 0.758760824 - 0.755242872 - 0.751792927 - 0.748434131 - 0.745113997 - 0.717806682 - 0.672204789 - 0.63831272 - 0.610176496 - 0.585456847 - 0.563222111 - 0.542912273 - 0.399312061 - 0.310517829 - 0.248633226 - 0.203543725 - 0.169616419 - 0.143478955 - 0.122938861 - 0.106515296 - 0.093026095 - 0.081648606 - 0.072197368 - 0.064388275 - 0.057782745 - 0.0 - 0.0 wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 7.5 - 7.6 - 7.7 - 7.8 - 7.9 - 8.0 - 9.0 - 10.0 - 10.1 - 10.2 - 10.3 - 10.4 - 10.5 - 10.6 - 10.7 - 10.8 - 10.9 - 11.0 - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 - 11.9 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 20.0 - 21.0 - 22.0 - 23.0 - 24.0 - 25.0 - 25.1 - 50.0 floating_tilt_table: tilt: - 5.0 - 5.0 - 9.0 - 5.0 - 5.0 wind_speed: - 0.0 - 4.0 - 11.0 - 25.0 - 50.0 ================================================ FILE: examples/inputs_floating/turbine_files/nrel_5MW_floating_fixedtilt15.yaml ================================================ turbine_type: 'nrel_5MW_floating' hub_height: 90.0 rotor_diameter: 125.88 TSR: 8.0 correct_cp_ct_for_tilt: True # Apply tilt correction to cp/ct power_thrust_table: ref_air_density: 1.225 ref_tilt: 5.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 power: - 0.0 - 0.0 - 40.518011517569214 - 177.67162506419703 - 403.900880943964 - 737.5889584824021 - 1187.1774030611875 - 1239.245945375778 - 1292.5184293723503 - 1347.3213147477102 - 1403.2573725578948 - 1460.7011898730707 - 1519.6419125979983 - 1580.174365096404 - 1642.1103166918167 - 1705.758292831 - 1771.1659528893977 - 2518.553107505315 - 3448.381605840943 - 3552.140809000129 - 3657.9545431794127 - 3765.121299313842 - 3873.928844315059 - 3984.4800226955504 - 4096.582833096852 - 4210.721306623712 - 4326.154305853405 - 4443.395565353604 - 4562.497934188341 - 4683.419890251577 - 4806.164748311019 - 4929.931918769215 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 0.0 - 0.0 thrust_coefficient: - 0.0 - 0.0 - 1.132034888 - 0.999470963 - 0.917697381 - 0.860849503 - 0.815371198 - 0.811614904 - 0.807939328 - 0.80443352 - 0.800993851 - 0.79768116 - 0.794529244 - 0.791495834 - 0.788560434 - 0.787217182 - 0.787127977 - 0.785839257 - 0.783812219 - 0.783568108 - 0.783328285 - 0.781194418 - 0.777292539 - 0.773464375 - 0.769690236 - 0.766001924 - 0.762348072 - 0.758760824 - 0.755242872 - 0.751792927 - 0.748434131 - 0.745113997 - 0.717806682 - 0.672204789 - 0.63831272 - 0.610176496 - 0.585456847 - 0.563222111 - 0.542912273 - 0.399312061 - 0.310517829 - 0.248633226 - 0.203543725 - 0.169616419 - 0.143478955 - 0.122938861 - 0.106515296 - 0.093026095 - 0.081648606 - 0.072197368 - 0.064388275 - 0.057782745 - 0.0 - 0.0 wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 7.5 - 7.6 - 7.7 - 7.8 - 7.9 - 8.0 - 9.0 - 10.0 - 10.1 - 10.2 - 10.3 - 10.4 - 10.5 - 10.6 - 10.7 - 10.8 - 10.9 - 11.0 - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 - 11.9 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 20.0 - 21.0 - 22.0 - 23.0 - 24.0 - 25.0 - 25.1 - 50.0 floating_tilt_table: tilt: - 15.0 - 15.0 - 15.0 - 15.0 - 15.0 wind_speed: - 0.0 - 4.0 - 11.0 - 25.0 - 50.0 ================================================ FILE: examples/inputs_floating/turbine_files/nrel_5MW_floating_fixedtilt5.yaml ================================================ turbine_type: 'nrel_5MW_floating' hub_height: 90.0 rotor_diameter: 125.88 TSR: 8.0 correct_cp_ct_for_tilt: True # Apply tilt correction to cp/ct power_thrust_table: ref_air_density: 1.225 ref_tilt: 5.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 power: - 0.0 - 0.0 - 40.518011517569214 - 177.67162506419703 - 403.900880943964 - 737.5889584824021 - 1187.1774030611875 - 1239.245945375778 - 1292.5184293723503 - 1347.3213147477102 - 1403.2573725578948 - 1460.7011898730707 - 1519.6419125979983 - 1580.174365096404 - 1642.1103166918167 - 1705.758292831 - 1771.1659528893977 - 2518.553107505315 - 3448.381605840943 - 3552.140809000129 - 3657.9545431794127 - 3765.121299313842 - 3873.928844315059 - 3984.4800226955504 - 4096.582833096852 - 4210.721306623712 - 4326.154305853405 - 4443.395565353604 - 4562.497934188341 - 4683.419890251577 - 4806.164748311019 - 4929.931918769215 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 0.0 - 0.0 thrust_coefficient: - 0.0 - 0.0 - 1.132034888 - 0.999470963 - 0.917697381 - 0.860849503 - 0.815371198 - 0.811614904 - 0.807939328 - 0.80443352 - 0.800993851 - 0.79768116 - 0.794529244 - 0.791495834 - 0.788560434 - 0.787217182 - 0.787127977 - 0.785839257 - 0.783812219 - 0.783568108 - 0.783328285 - 0.781194418 - 0.777292539 - 0.773464375 - 0.769690236 - 0.766001924 - 0.762348072 - 0.758760824 - 0.755242872 - 0.751792927 - 0.748434131 - 0.745113997 - 0.717806682 - 0.672204789 - 0.63831272 - 0.610176496 - 0.585456847 - 0.563222111 - 0.542912273 - 0.399312061 - 0.310517829 - 0.248633226 - 0.203543725 - 0.169616419 - 0.143478955 - 0.122938861 - 0.106515296 - 0.093026095 - 0.081648606 - 0.072197368 - 0.064388275 - 0.057782745 - 0.0 - 0.0 wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 7.5 - 7.6 - 7.7 - 7.8 - 7.9 - 8.0 - 9.0 - 10.0 - 10.1 - 10.2 - 10.3 - 10.4 - 10.5 - 10.6 - 10.7 - 10.8 - 10.9 - 11.0 - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 - 11.9 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 20.0 - 21.0 - 22.0 - 23.0 - 24.0 - 25.0 - 25.1 - 50.0 floating_tilt_table: tilt: - 5.0 - 5.0 - 5.0 - 5.0 - 5.0 wind_speed: - 0.0 - 4.0 - 11.0 - 25.0 - 50.0 ================================================ FILE: floris/__init__.py ================================================ from importlib.metadata import version from pathlib import Path __version__ = version("floris") from .floris_model import FlorisModel from .flow_visualization import ( plot_rotor_values, visualize_cut_plane, visualize_quiver, ) from .heterogeneous_map import HeterogeneousMap from .par_floris_model import ParFlorisModel from .parallel_floris_model import ParallelFlorisModel from .uncertain_floris_model import ApproxFlorisModel, UncertainFlorisModel from .wind_data import ( TimeSeries, WindRose, WindRoseWRG, WindTIRose, ) ================================================ FILE: floris/convert_floris_input_v3_to_v4.py ================================================ import sys from pathlib import Path import yaml """ This script is intended to be called with an argument and converts a floris input yaml file specified for FLORIS v3 to one specified for FLORIS v4. Usage: python convert_floris_input_v3_to_v4.py .yaml The resulting floris input file is placed in the same directory as the original yaml, and is appended _v4. """ def ignore_include(loader, node): # Parrot back the !include tag return node.tag + " " + node.value if __name__ == "__main__": if len(sys.argv) != 2: raise Exception( "Usage: python convert_floris_input_v3_to_v4.py .yaml" ) # Set the yaml loader to ignore the !include tag yaml.SafeLoader.add_constructor("!include", ignore_include) input_yaml = sys.argv[1] # Handling the path and new filename input_path = Path(input_yaml) split_input = input_path.parts [filename_v3, extension] = split_input[-1].split(".") filename_v4 = filename_v3 + "_v4" split_output = list(split_input[:-1]) + [filename_v4 + "." + extension] output_path = Path(*split_output) # Load existing v3 model with open(input_yaml, "r") as file: v3_floris_input_dict = yaml.safe_load(file) v4_floris_input_dict = v3_floris_input_dict.copy() # Change turbulence_intensity field to turbulence_intensities as list if "turbulence_intensities" in v3_floris_input_dict["flow_field"]: if "turbulence_intensity" in v3_floris_input_dict["flow_field"]: del v4_floris_input_dict["flow_field"]["turbulence_intensity"] elif "turbulence_intensity" in v3_floris_input_dict["flow_field"]: v4_floris_input_dict["flow_field"]["turbulence_intensities"] = [ v3_floris_input_dict["flow_field"]["turbulence_intensity"] ] del v4_floris_input_dict["flow_field"]["turbulence_intensity"] # Change multidim_cp_ct velocity model to gauss if v3_floris_input_dict["wake"]["model_strings"]["velocity_model"] == "multidim_cp_ct": print( "multidim_cp_ct velocity model specified. Changing to gauss, " + "but note that other velocity models are also compatible with multidimensional " + "turbines in FLORIS v4. " + "You will also need to convert your multidimensional turbine yaml files and their " + "corresponding power/thrust csv files to be compatible with FLORIS v4 and to reflect " + " the absolute power curve, rather than the power coefficient curve." ) v4_floris_input_dict["wake"]["model_strings"]["velocity_model"] = "gauss" # Add enable_active_wake_mixing field v4_floris_input_dict["wake"]["enable_active_wake_mixing"] = False # Write the new v4 model to a new file, note that the in order to ignore the !include tag # it is wrapped in single quotes by the ignore include/load/dump sequence and these will # need to be removed in the next block of code yaml.dump(v4_floris_input_dict, open(output_path, "w"), sort_keys=False) # Open the output file and loop through line by line # if a line contains the substring !include, then strip all # occurrences of ' from the line to remove the extra single quotes # added by the ignore include/load/dump sequence temp_output_path = output_path.with_name("temp.yaml") with open(temp_output_path, "w") as file: with open(output_path, "r") as f: for line in f: if "!include" in line: line = line.replace("'", "") file.write(line) # Move the temp file to the output file temp_output_path.replace(output_path) print(output_path, "created.") ================================================ FILE: floris/convert_turbine_v3_to_v4.py ================================================ import sys from pathlib import Path from floris.turbine_library import build_cosine_loss_turbine_dict, check_smooth_power_curve from floris.utilities import load_yaml """ This script is intended to be called with an argument and converts a turbine yaml file specified for FLORIS v3 to one specified for FLORIS v4. Usage: python convert_turbine_v3_to_v4.py .yaml The resulting turbine is placed in the same directory as the original yaml, and is appended _v4. """ if __name__ == "__main__": if len(sys.argv) != 2: raise Exception("Usage: python convert_turbine_v3_to_v4.py .yaml") input_yaml = sys.argv[1] # Handling the path and new filename input_path = Path(input_yaml) split_input = input_path.parts [filename_v3, extension] = split_input[-1].split(".") filename_v4 = filename_v3 + "_v4" split_output = list(split_input[:-1]) + [filename_v4+"."+extension] output_path = Path(*split_output) # Load existing v3 model v3_turbine_dict = load_yaml(input_yaml) # Split into components expected by build_turbine_dict power_thrust_table = v3_turbine_dict["power_thrust_table"] if "power_thrust_data_file" in power_thrust_table: raise ValueError( "Cannot convert multidimensional turbine model. Please manually update your " + "turbine yaml. Note that the power_thrust_data_file csv needs to be updated to " + "reflect the absolute power curve, rather than the power coefficient curve," + "and that `thrust` has been replaced by `thrust_coefficient`." ) power_thrust_table["power_coefficient"] = power_thrust_table["power"] power_thrust_table["thrust_coefficient"] = power_thrust_table["thrust"] power_thrust_table.pop("power") power_thrust_table.pop("thrust") valid_properties = [ "generator_efficiency", "hub_height", "cosine_loss_exponent_yaw", "cosine_loss_exponent_tilt", "rotor_diameter", "TSR", "ref_air_density", "ref_tilt" ] turbine_properties = {k:v for k,v in v3_turbine_dict.items() if k in valid_properties} turbine_properties["ref_air_density"] = v3_turbine_dict["ref_density_cp_ct"] turbine_properties["cosine_loss_exponent_yaw"] = v3_turbine_dict["pP"] if "ref_tilt_cp_ct" in v3_turbine_dict: turbine_properties["ref_tilt"] = v3_turbine_dict["ref_tilt_cp_ct"] if "pT" in v3_turbine_dict: turbine_properties["cosine_loss_exponent_tilt"] = v3_turbine_dict["pT"] # Convert to v4 and print new yaml v4_turbine_dict = build_cosine_loss_turbine_dict( power_thrust_table, v3_turbine_dict["turbine_type"], output_path, **turbine_properties ) if not check_smooth_power_curve( v4_turbine_dict["power_thrust_table"]["power"], tolerance=0.001 ): print( "Non-smoothness detected in output power curve. ", "Check above-rated power in generated v4 yaml file." ) ================================================ FILE: floris/core/__init__.py ================================================ """ The :py:obj:`floris` package contains :py:obj:`floris.utilities` module and the modules that make up the FLORIS software. The floris simulation modules are used to complete a wake simulation for a given wind farm and turbine configuration. All modules and package can be imported with >>> import floris The ``__init__.py`` file enables the import of all modules in this package so any additional modules should be included there. isort:skip_file """ # Provide full-path imports here for all modules # that should be included in the simulation package. # Since some of these depend on each other, the order # that they are listed here does matter. import floris.logging_manager from .base import BaseClass, BaseModel, State from .turbine.turbine import ( axial_induction, power, thrust_coefficient, Turbine ) from .rotor_velocity import ( average_velocity, rotor_effective_velocity, compute_tilt_angles_for_floating_turbines, ) from .farm import Farm from .grid import ( FlowFieldGrid, FlowFieldPlanarGrid, Grid, PointsGrid, TurbineGrid, TurbineCubatureGrid ) from .flow_field import FlowField from .wake import WakeModelManager from .solver import ( cc_solver, empirical_gauss_solver, full_flow_cc_solver, full_flow_empirical_gauss_solver, full_flow_sequential_solver, full_flow_turbopark_solver, sequential_solver, turbopark_solver, ) from .core import Core # initialize the logger floris.logging_manager._setup_logger() ================================================ FILE: floris/core/base.py ================================================ from abc import abstractmethod from enum import Enum from typing import ( Any, Dict, Final, ) from attrs import ( Attribute, define, field, fields, setters, ) from floris.logging_manager import LoggingManager from floris.type_dec import FromDictMixin """ Defines the BaseClass parent class for all models to be based upon. """ class State(Enum): UNINITIALIZED = 0 INITIALIZED = 1 USED = 2 @define class BaseClass(FromDictMixin): """ BaseClass object class. This class does the logging and MixIn class inheritance. """ # Initialize `state` and ensure it is treated as an attribute rather than a constant parameter. # See https://www.attrs.org/en/stable/api-attr.html#attr.ib state = field(init=False, default=State.UNINITIALIZED) _logging_manager: LoggingManager = field(init=False, default=LoggingManager()) @property def logger(self): """Returns the logger manager object.""" return self._logging_manager.logger @define class BaseModel(BaseClass): """ BaseModel is the generic class for any wake models. It defines the API required to create a valid model. """ # This is a numerical epsilon to prevent divide by zeros NUM_EPS: Final[float] = field(init=False, default=0.001, on_setattr=setters.frozen) @abstractmethod def prepare_function() -> dict: raise NotImplementedError("BaseModel.prepare_function") @abstractmethod def function() -> None: raise NotImplementedError("BaseModel.function") ================================================ FILE: floris/core/core.py ================================================ from __future__ import annotations from pathlib import Path import numpy as np import pandas as pd import yaml from attrs import define, field from floris import logging_manager from floris.core import ( BaseClass, cc_solver, empirical_gauss_solver, Farm, FlowField, FlowFieldGrid, FlowFieldPlanarGrid, full_flow_cc_solver, full_flow_empirical_gauss_solver, full_flow_sequential_solver, full_flow_turbopark_solver, Grid, PointsGrid, sequential_solver, State, TurbineCubatureGrid, TurbineGrid, turbopark_solver, WakeModelManager, ) from floris.type_dec import NDArrayFloat from floris.utilities import ( load_yaml, reverse_rotate_coordinates_rel_west, ) @define class Core(BaseClass): """ Top-level class that describes a Floris model and initializes the simulation. Use the :py:class:`~.simulation.farm.Farm` attribute to access other objects within the model. """ logging: dict = field(converter=dict) solver: dict = field(converter=dict) wake: WakeModelManager = field(converter=WakeModelManager.from_dict) farm: Farm = field(converter=Farm.from_dict) flow_field: FlowField = field(converter=FlowField.from_dict) # These fields are included to appease the requirement that all inputs must # be mapped to a field in the class. They are not used in FLORIS. name: str = field(converter=str) description: str = field(converter=str) floris_version: str = field(converter=str) grid: Grid = field(init=False) def __attrs_post_init__(self) -> None: # Configure logging logging_manager.configure_console_log( self.logging["console"]["enable"], self.logging["console"]["level"], ) logging_manager.configure_file_log( self.logging["file"]["enable"], self.logging["file"]["level"], ) # Initialize farm quantities that depend on other objects self.farm.construct_turbine_map() self.farm.construct_turbine_thrust_coefficient_functions() self.farm.construct_turbine_axial_induction_functions() self.farm.construct_turbine_power_functions() self.farm.construct_turbine_power_thrust_tables() self.farm.construct_hub_heights() self.farm.construct_rotor_diameters() self.farm.construct_turbine_TSRs() self.farm.construct_turbine_ref_tilts() self.farm.construct_turbine_tilt_interps() self.farm.construct_turbine_correct_cp_ct_for_tilt() self.farm.set_yaw_angles_to_ref_yaw(self.flow_field.n_findex) self.farm.set_tilt_to_ref_tilt(self.flow_field.n_findex) self.farm.set_power_setpoints_to_ref_power(self.flow_field.n_findex) self.farm.set_awc_modes_to_ref_mode(self.flow_field.n_findex) self.farm.set_awc_amplitudes_to_ref_amp(self.flow_field.n_findex) self.farm.set_awc_frequencies_to_ref_freq(self.flow_field.n_findex) if self.solver["type"] == "turbine_grid": self.grid = TurbineGrid( turbine_coordinates=self.farm.coordinates, turbine_diameters=self.farm.rotor_diameters, wind_directions=self.flow_field.wind_directions, grid_resolution=self.solver["turbine_grid_points"], ) elif self.solver["type"] == "turbine_cubature_grid": self.grid = TurbineCubatureGrid( turbine_coordinates=self.farm.coordinates, turbine_diameters=self.farm.rotor_diameters, wind_directions=self.flow_field.wind_directions, grid_resolution=self.solver["turbine_grid_points"], ) elif self.solver["type"] == "flow_field_grid": self.grid = FlowFieldGrid( turbine_coordinates=self.farm.coordinates, turbine_diameters=self.farm.rotor_diameters, wind_directions=self.flow_field.wind_directions, grid_resolution=self.solver["flow_field_grid_points"], ) elif self.solver["type"] == "flow_field_planar_grid": self.grid = FlowFieldPlanarGrid( turbine_coordinates=self.farm.coordinates, turbine_diameters=self.farm.rotor_diameters, wind_directions=self.flow_field.wind_directions, normal_vector=self.solver["normal_vector"], planar_coordinate=self.solver["planar_coordinate"], grid_resolution=self.solver["flow_field_grid_points"], x1_bounds=self.solver["flow_field_bounds"][0], x2_bounds=self.solver["flow_field_bounds"][1], ) else: raise ValueError( "Supported solver types are " "[turbine_grid, turbine_cubature_grid, flow_field_grid, flow_field_planar_grid], " f"but type given was {self.solver['type']}" ) if isinstance(self.grid, (TurbineGrid, TurbineCubatureGrid)): self.farm.expand_farm_properties( self.flow_field.n_findex, self.grid.sorted_coord_indices ) def initialize_domain(self): """Initialize solution space prior to wake calculations""" # Initialize field quantities; doing this immediately prior to doing # the calculation step allows for manipulating inputs in a script # without changing the data structures self.flow_field.initialize_velocity_field(self.grid) # Initialize farm quantities self.farm.initialize(self.grid.sorted_indices) self.state.INITIALIZED def steady_state_atmospheric_condition(self): """Perform the steady-state wind farm wake calculations. Note that initialize_domain() is required to be called before this function.""" vel_model = self.wake.model_strings["velocity_model"] if vel_model not in ["empirical_gauss"] and \ self.farm.correct_cp_ct_for_tilt.any(): self.logger.warning( "The current model does not account for vertical wake deflection due to " + "tilt. Corrections to power and thrust coefficient can be included, but no " + "vertical wake deflection will occur." ) operation_model_awc = False for td in self.farm.turbine_definitions: if "operation_model" in td and td["operation_model"] == "awc": operation_model_awc = True if vel_model != "empirical_gauss" and operation_model_awc: self.logger.warning( f"The current model `{vel_model}` does not account for additional wake mixing " + "due to active wake control. Corrections to power and thrust coefficient can " + "be included, but no enhanced wake recovery will occur." ) if vel_model=="cc": cc_solver( self.farm, self.flow_field, self.grid, self.wake ) elif vel_model=="turbopark": self.logger.warning( "The turbopark model has been superseded by the turboparkgauss model. We " + "recommend using `velocity_model: turboparkgauss` instead." ) turbopark_solver( self.farm, self.flow_field, self.grid, self.wake ) elif vel_model=="empirical_gauss": empirical_gauss_solver( self.farm, self.flow_field, self.grid, self.wake ) else: sequential_solver( self.farm, self.flow_field, self.grid, self.wake ) self.finalize() def solve_for_viz(self): # Do the calculation with the TurbineGrid for a single wind speed # and wind direction and 1 point on the grid. Then, use the result # to construct the full flow field grid. # This function call should be for a single wind direction and wind speed # since the memory consumption is very large. self.flow_field.initialize_velocity_field(self.grid) vel_model = self.wake.model_strings["velocity_model"] if vel_model=="cc": full_flow_cc_solver(self.farm, self.flow_field, self.grid, self.wake) elif vel_model=="turbopark": full_flow_turbopark_solver(self.farm, self.flow_field, self.grid, self.wake) elif vel_model=="empirical_gauss": full_flow_empirical_gauss_solver(self.farm, self.flow_field, self.grid, self.wake) else: full_flow_sequential_solver(self.farm, self.flow_field, self.grid, self.wake) def solve_for_points(self, x, y, z): # Do the calculation with the TurbineGrid for a single wind speed # and wind direction and a 3x3 rotor grid. Then, use the result # to construct the full flow field grid. # This function call should be for a single wind direction and wind speed # since the memory consumption is very large. # Instantiate the flow_grid field_grid = PointsGrid( points_x=x, points_y=y, points_z=z, turbine_coordinates=self.farm.coordinates, turbine_diameters=self.farm.rotor_diameters, wind_directions=self.flow_field.wind_directions, grid_resolution=1, x_center_of_rotation=self.grid.x_center_of_rotation, y_center_of_rotation=self.grid.y_center_of_rotation ) self.flow_field.initialize_velocity_field(field_grid) vel_model = self.wake.model_strings["velocity_model"] if vel_model == "turbopark": raise NotImplementedError( "solve_for_points is not available for the legacy \'turbopark\' model. " "However, it is available for \'turboparkgauss\'." ) elif vel_model == "empirical_gauss": full_flow_empirical_gauss_solver(self.farm, self.flow_field, field_grid, self.wake) elif vel_model == "cc": full_flow_cc_solver(self.farm, self.flow_field, field_grid, self.wake) else: full_flow_sequential_solver(self.farm, self.flow_field, field_grid, self.wake) return self.flow_field.u_sorted[:,:,0,0] # Remove turbine grid dimensions def solve_for_velocity_deficit_profiles( self, direction: str, downstream_dists: NDArrayFloat | list, profile_range: NDArrayFloat | list, resolution: int, homogeneous_wind_speed: float, ref_rotor_diameter: float, x_start: float, y_start: float, reference_height: float, ) -> list[pd.DataFrame]: """ Extract velocity deficit profiles. See :py:meth:`~floris.floris_model.FlorisModel.sample_velocity_deficit_profiles` for more details. """ # Create a grid that contains coordinates for all the sample points in all profiles. # Effectively, this is a grid of parallel lines. n_lines = len(downstream_dists) # Coordinate system (x1, x2, x3) is used to define the sample points. The origin is at # (x_start, y_start, reference_height) and x1 is in the streamwise direction. # The x1-coordinate is fixed for every line (every row in `x1`). x1 = np.atleast_2d(downstream_dists).T * np.ones((n_lines, resolution)) if resolution == 1: single_line = [0.0] else: single_line = np.linspace(profile_range[0], profile_range[1], resolution) if direction == 'cross-stream': x2 = single_line * np.ones((n_lines, resolution)) x3 = np.zeros((n_lines, resolution)) elif direction == 'vertical': x3 = single_line * np.ones((n_lines, resolution)) x2 = np.zeros((n_lines, resolution)) # Find the coordinates of the sample points in the inertial frame (x, y, z). This is done # through one rotation and one translation. x, y, z = reverse_rotate_coordinates_rel_west( self.flow_field.wind_directions, x1[None, :, :], x2[None, :, :], x3[None, :, :], x_center_of_rotation=0.0, y_center_of_rotation=0.0, ) x = np.squeeze(x, axis=0) + x_start y = np.squeeze(y, axis=0) + y_start z = np.squeeze(z, axis=0) + reference_height u = self.solve_for_points(x.flatten(), y.flatten(), z.flatten()) u = np.reshape(u[0, :], (n_lines, resolution)) velocity_deficit = (homogeneous_wind_speed - u) / homogeneous_wind_speed velocity_deficit_profiles = [] for i in range(n_lines): df = pd.DataFrame( { 'x': x[i], 'y': y[i], 'z': z[i], 'x1/D': x1[i]/ref_rotor_diameter, 'x2/D': x2[i]/ref_rotor_diameter, 'x3/D': x3[i]/ref_rotor_diameter, 'velocity_deficit': velocity_deficit[i], } ) velocity_deficit_profiles.append(df) return velocity_deficit_profiles def finalize(self): # Once the wake calculation is finished, unsort the values to match # the user-supplied order of things. self.flow_field.finalize(self.grid.unsorted_indices) self.farm.finalize(self.grid.unsorted_indices) self.state = State.USED ## I/O @classmethod def from_file(cls, input_file_path: str | Path) -> Core: """Creates a `Floris` instance from an input file. Must be filetype YAML. Args: input_file_path (str): The relative or absolute file path and name to the input file. Returns: Floris: The class object instance. """ input_dict = load_yaml(Path(input_file_path).resolve()) check_input_file_for_v3_keys(input_dict) return Core.from_dict(input_dict) def to_file(self, output_file_path: str) -> None: """Converts the `Floris` object to an input-ready YAML file at `output_file_path`. Args: output_file_path (str): The full path and filename for where to save the file. """ with open(output_file_path, "w+") as f: yaml.dump( self.as_dict(), f, sort_keys=False, default_flow_style=False ) def check_input_file_for_v3_keys(input_dict) -> None: """ Checks if any FLORIS v3 keys are present in the input file and raises special errors if the extra keys belong to a v3 definition of the input_dct. and raises special errors if the extra arguments belong to a v3 definition of the class. Args: input_dict (dict): The input dictionary to be checked for v3 keys. """ v3_deprecation_msg = ( "Consider using the convert_floris_input_v3_to_v4.py utility in floris/tools " "to convert from a FLORIS v3 input file to FLORIS v4. " "See https://natlabrockies.github.io/floris/upgrade_guides/v3_to_v4.html " "for more information." ) if "turbulence_intensity" in input_dict["flow_field"]: raise AttributeError( "turbulence_intensity has been updated to turbulence_intensities in FLORIS v4. " + v3_deprecation_msg ) elif not hasattr(input_dict["flow_field"]["turbulence_intensities"], "__len__"): raise AttributeError( "turbulence_intensities must be a list of floats in FLORIS v4. " + v3_deprecation_msg ) if input_dict["wake"]["model_strings"]["velocity_model"] == "multidim_cp_ct": raise AttributeError( "Dedicated 'multidim_cp_ct' velocity model has been removed in FLORIS v4 in favor of " + "supporting all available wake models. To recover previous operation, set " + "velocity_model to gauss. " + v3_deprecation_msg ) ================================================ FILE: floris/core/farm.py ================================================ import copy from collections.abc import Callable from pathlib import Path from typing import ( Any, Dict, List, ) import attrs import numpy as np from attrs import define, field from scipy.interpolate import interp1d from floris.core import ( BaseClass, State, Turbine, ) from floris.core.rotor_velocity import compute_tilt_angles_for_floating_turbines_map from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.type_dec import ( convert_to_path, floris_array_converter, iter_validator, NDArrayFloat, NDArrayObject, NDArrayStr, ) from floris.utilities import load_yaml default_turbine_library_path = Path(__file__).parents[1] / "turbine_library" @define class Farm(BaseClass): """Farm is where wind power plants should be instantiated from a YAML configuration file. The Farm will create a heterogeneous set of turbines that compose a wind farm, validate the inputs, and then create a vectorized representation of the the turbine data. Farm is the container class of the FLORIS package. It brings together all of the component objects after input (i.e., Turbine, Wake, FlowField) and packages everything into the appropriate data type. Farm should also be used as an entry point to probe objects for generating output. Args: layout_x (NDArrayFloat): A sequence of x-axis locations for the turbines that can be converted to a 1-D :py:obj:`numpy.ndarray`. layout_y (NDArrayFloat): A sequence of y-axis locations for the turbines that can be converted to a 1-D :py:obj:`numpy.ndarray`. turbine_type (list[dict | str]): A list of turbine definition dictionaries, or string references to the filename of the turbine type in either the FLORIS-provided turbine library (.../floris/turbine_library/), or a user-provided :py:attr:`turbine_library_path`. turbine_library_path (:obj:`str`): Either an absolute file path to the turbine library, or a path relative to the file that is running the analysis. """ layout_x: NDArrayFloat = field(converter=floris_array_converter) layout_y: NDArrayFloat = field(converter=floris_array_converter) # TODO: turbine_type should be immutable turbine_type: List = field(validator=iter_validator(list, (dict, str))) turbine_library_path: Path = field( default=default_turbine_library_path, converter=convert_to_path ) turbine_definitions: list = field(init=False, validator=iter_validator(list, dict)) turbine_thrust_coefficient_functions: Dict[str, Callable] = field(init=False, factory=list) turbine_axial_induction_functions: Dict[str, Callable] = field(init=False, factory=list) turbine_tilt_interps: dict[str, interp1d] = field(init=False, factory=dict) yaw_angles: NDArrayFloat = field(init=False) yaw_angles_sorted: NDArrayFloat = field(init=False) tilt_angles: NDArrayFloat = field(init=False) tilt_angles_sorted: NDArrayFloat = field(init=False) power_setpoints: NDArrayFloat = field(init=False) power_setpoints_sorted: NDArrayFloat = field(init=False) awc_modes: NDArrayStr = field(init=False) awc_modes_sorted: NDArrayStr = field(init=False) awc_amplitudes: NDArrayFloat = field(init=False) awc_amplitudes_sorted: NDArrayFloat = field(init=False) awc_frequencies: NDArrayFloat = field(init=False) awc_frequencies_sorted: NDArrayFloat = field(init=False) hub_heights: NDArrayFloat = field(init=False) hub_heights_sorted: NDArrayFloat = field(init=False, factory=list) turbine_map: List[Turbine] = field(init=False, factory=list) turbine_type_map: NDArrayObject = field(init=False, factory=list) turbine_type_map_sorted: NDArrayObject = field(init=False, factory=list) turbine_power_functions: Dict[str, Callable] = field(init=False, factory=list) turbine_power_thrust_tables: Dict[str, dict] = field(init=False, factory=list) rotor_diameters: NDArrayFloat = field(init=False, factory=list) rotor_diameters_sorted: NDArrayFloat = field(init=False, factory=list) TSRs: NDArrayFloat = field(init=False, factory=list) TSRs_sorted: NDArrayFloat = field(init=False, factory=list) ref_tilts: NDArrayFloat = field(init=False, factory=list) ref_tilts_sorted: NDArrayFloat = field(init=False, factory=list) correct_cp_ct_for_tilt: NDArrayFloat = field(init=False, factory=list) correct_cp_ct_for_tilt_sorted: NDArrayFloat = field(init=False, factory=list) internal_turbine_library: Path = field(init=False, default=default_turbine_library_path) # Private attributes _turbine_types: List = field(init=False, validator=iter_validator(list, str), factory=list) _turbine_definition_cache: dict = field(init=False, factory=dict) def __attrs_post_init__(self) -> None: # Turbine definitions can be supplied in three ways: # - A string selecting a turbine in the floris turbine library # - A Python dict representation of a turbine definition # - There's an option to use the yaml keyword "!include" which results in the yaml # library preprocessing the inputs and loading the specified file directly into # the main input file. The result is that floris sees the turbine definition as a dict. # - A string selecting an turbine that exists in an external turbine library # specified in `turbine_library_path` # Load all the turbine types into a cache to be mapped to specific turbine indices later. # This allows to read the yaml input files once rather than every time they're given. # In other words, if the turbine type is already in the cache, skip that iteration of # the for-loop. for t in self.turbine_type: # If a turbine type is a dict, then it was either preprocessed by the yaml # library to resolve the "!include" or it was set in a script as a dict. In either case, # add an entry to the cache if isinstance(t, dict): if t["turbine_type"] in self._turbine_definition_cache: if self._turbine_definition_cache[t["turbine_type"]] == t: continue # Skip t if already loaded else: raise ValueError( "Two different turbine definitions have the same name: "\ f"'{t['turbine_type']}'. "\ "Please specify a unique 'turbine_type' for each turbine definition." ) self._turbine_definition_cache[t["turbine_type"]] = t self._turbine_definition_cache[t["turbine_type"]]["turbine_library_path"] = ( self.turbine_library_path ) # If a turbine type is a string, then it is expected in the internal or external # turbine library if isinstance(t, str): if t in self._turbine_definition_cache: continue # Skip t if already loaded # Check if the file exists in the internal and/or external library internal_fn = (self.internal_turbine_library / t).with_suffix(".yaml") external_fn = (self.turbine_library_path / t).with_suffix(".yaml") in_internal = internal_fn.exists() in_external = external_fn.exists() # If an external library is used and there's a duplicate of an internal # definition, then raise an error is_unique_path = self.turbine_library_path != default_turbine_library_path if is_unique_path and in_external and in_internal: raise ValueError( f"The turbine type: {t} exists in both the internal and external" " turbine library." ) if in_internal: full_path = internal_fn elif in_external: full_path = external_fn else: raise FileNotFoundError( f"The turbine type: {t} does not exist in either the internal or" " external turbine library." ) self._turbine_definition_cache[t] = load_yaml(full_path) self._turbine_definition_cache[t]["turbine_library_path"] = ( self.turbine_library_path ) # Convert any dict entries in the turbine_type list to the type string. Since the # definition is saved above, we can make the whole list consistent now to use it # for mapping turbines later. # We use a private variable here instead of self.turbine_type because self.turbine_type # should always retain the input data. When this class is exported as_dict, the input # types must be used. If we modify that directly and change its shape, recreating this # class with a different layout but not a new self.turbine_type could cause the data # to be out of sync. self._turbine_types = [ copy.deepcopy(t["turbine_type"]) if isinstance(t, dict) else t for t in self.turbine_type ] # If 1 turbine definition is given, expand to N turbines; this covers a 1-turbine # farm and 1 definition for multiple turbines if len(self._turbine_types) == 1: self._turbine_types *= self.n_turbines # Check that turbine definitions contain any v3 keys for _, v in self._turbine_definition_cache.items(): check_turbine_definition_for_v3_keys(v) # Map each turbine definition to its index in this list self.turbine_definitions = [ copy.deepcopy(self._turbine_definition_cache[t]) for t in self._turbine_types ] @layout_x.validator def check_x(self, attribute: attrs.Attribute, value: Any) -> None: if len(value) != len(self.layout_y): raise ValueError("layout_x and layout_y must have the same number of entries.") @layout_y.validator def check_y(self, attribute: attrs.Attribute, value: Any) -> None: if len(value) != len(self.layout_x): raise ValueError("layout_x and layout_y must have the same number of entries.") @turbine_type.validator def check_turbine_type(self, attribute: attrs.Attribute, value: Any) -> None: # Check that the list of turbines is either of length 1 or N turbines if len(value) != 1 and len(value) != self.n_turbines: raise ValueError( "turbine_type must have the same number of entries as layout_x/layout_y or have " "a single turbine_type value. This error can arise if you set the turbine_type or " "alter the operation model before setting the layout." ) @turbine_library_path.validator def check_library_path(self, attribute: attrs.Attribute, value: Path) -> None: """Ensures that the input to `library_path` exists and is a directory.""" if not value.is_dir(): raise FileExistsError(f"The input file path: {str(value)} is not a valid directory.") def initialize(self, sorted_indices): # Sort yaw angles from most upstream to most downstream wind turbine self.yaw_angles_sorted = np.take_along_axis( self.yaw_angles, sorted_indices[:, :, 0, 0], axis=1, ) self.tilt_angles_sorted = np.take_along_axis( self.tilt_angles, sorted_indices[:, :, 0, 0], axis=1, ) self.power_setpoints_sorted = np.take_along_axis( self.power_setpoints, sorted_indices[:, :, 0, 0], axis=1, ) self.awc_modes_sorted = np.take_along_axis( self.awc_modes, sorted_indices[:, :, 0, 0], axis=1, ) self.awc_amplitudes_sorted = np.take_along_axis( self.awc_amplitudes, sorted_indices[:, :, 0, 0], axis=1, ) self.awc_frequencies_sorted = np.take_along_axis( self.awc_frequencies, sorted_indices[:, :, 0, 0], axis=1, ) self.state = State.INITIALIZED def construct_hub_heights(self): self.hub_heights = np.array([turb['hub_height'] for turb in self.turbine_definitions]) def construct_rotor_diameters(self): self.rotor_diameters = np.array([ turb['rotor_diameter'] for turb in self.turbine_definitions ]) def construct_turbine_TSRs(self): self.TSRs = np.array([turb['TSR'] for turb in self.turbine_definitions]) def construct_turbine_ref_tilts(self): self.ref_tilts = np.array( [turb['power_thrust_table']['ref_tilt'] for turb in self.turbine_definitions] ) def construct_turbine_correct_cp_ct_for_tilt(self): self.correct_cp_ct_for_tilt = np.array( [turb.correct_cp_ct_for_tilt for turb in self.turbine_map] ) def construct_turbine_map(self): turbine_map_unique = { k: Turbine.from_dict(v) for k, v in self._turbine_definition_cache.items() } self.turbine_map = [turbine_map_unique[k] for k in self._turbine_types] def construct_turbine_thrust_coefficient_functions(self): self.turbine_thrust_coefficient_functions = { turb.turbine_type: turb.thrust_coefficient_function for turb in self.turbine_map } def construct_turbine_axial_induction_functions(self): self.turbine_axial_induction_functions = { turb.turbine_type: turb.axial_induction_function for turb in self.turbine_map } def construct_turbine_tilt_interps(self): self.turbine_tilt_interps = { turb.turbine_type: turb.tilt_interp for turb in self.turbine_map } def construct_turbine_power_functions(self): self.turbine_power_functions = { turb.turbine_type: turb.power_function for turb in self.turbine_map } def construct_turbine_power_thrust_tables(self): self.turbine_power_thrust_tables = { turb.turbine_type: turb.power_thrust_table for turb in self.turbine_map } def expand_farm_properties(self, n_findex: int, sorted_coord_indices): template_shape = np.ones_like(sorted_coord_indices) self.hub_heights_sorted = np.take_along_axis( self.hub_heights * template_shape, sorted_coord_indices, axis=1 ) self.rotor_diameters_sorted = np.take_along_axis( self.rotor_diameters * template_shape, sorted_coord_indices, axis=1 ) self.TSRs_sorted = np.take_along_axis( self.TSRs * template_shape, sorted_coord_indices, axis=1 ) self.ref_tilts_sorted = np.take_along_axis( self.ref_tilts * template_shape, sorted_coord_indices, axis=1 ) self.correct_cp_ct_for_tilt_sorted = np.take_along_axis( self.correct_cp_ct_for_tilt * template_shape, sorted_coord_indices, axis=1 ) # NOTE: Tilt angles are sorted twice - here and in initialize() self.tilt_angles_sorted = np.take_along_axis( self.tilt_angles * template_shape, sorted_coord_indices, axis=1 ) self.turbine_type_map_sorted = np.take_along_axis( np.reshape( [turb["turbine_type"] for turb in self.turbine_definitions] * n_findex, np.shape(sorted_coord_indices) ), sorted_coord_indices, axis=1 ) def set_yaw_angles(self, yaw_angles: NDArrayFloat | list[float]): self.yaw_angles = np.array(yaw_angles) def set_yaw_angles_to_ref_yaw(self, n_findex: int): yaw_angles = np.zeros((n_findex, self.n_turbines)) self.set_yaw_angles(yaw_angles) self.yaw_angles_sorted = np.zeros((n_findex, self.n_turbines)) def set_tilt_to_ref_tilt(self, n_findex: int): self.tilt_angles = ( np.ones((n_findex, self.n_turbines)) * self.ref_tilts ) self.tilt_angles_sorted = ( np.ones((n_findex, self.n_turbines)) * self.ref_tilts ) def set_power_setpoints(self, power_setpoints: NDArrayFloat): self.power_setpoints = np.array(power_setpoints) def set_power_setpoints_to_ref_power(self, n_findex: int): power_setpoints = POWER_SETPOINT_DEFAULT * np.ones((n_findex, self.n_turbines)) self.set_power_setpoints(power_setpoints) self.power_setpoints_sorted = POWER_SETPOINT_DEFAULT * np.ones((n_findex, self.n_turbines)) def set_awc_modes(self, awc_modes: NDArrayStr): self.awc_modes = np.array(awc_modes) def set_awc_modes_to_ref_mode(self, n_findex: int): # awc_modes = np.empty((n_findex, self.n_turbines))\ awc_modes = np.array([["baseline"]*self.n_turbines]*n_findex) self.set_awc_modes(awc_modes) # self.awc_modes_sorted = np.empty((n_findex, self.n_turbines)) self.awc_modes_sorted = np.array([["baseline"]*self.n_turbines]*n_findex) def set_awc_amplitudes(self, awc_amplitudes: NDArrayFloat): self.awc_amplitudes = np.array(awc_amplitudes) def set_awc_amplitudes_to_ref_amp(self, n_findex: int): awc_amplitudes = np.zeros((n_findex, self.n_turbines)) self.set_awc_amplitudes(awc_amplitudes) self.awc_amplitudes_sorted = np.zeros((n_findex, self.n_turbines)) def set_awc_frequencies(self, awc_frequencies: NDArrayFloat): self.awc_frequencies = np.array(awc_frequencies) def set_awc_frequencies_to_ref_freq(self, n_findex: int): awc_frequencies = np.zeros((n_findex, self.n_turbines)) self.set_awc_frequencies(awc_frequencies) self.awc_frequencies_sorted = np.zeros((n_findex, self.n_turbines)) def calculate_tilt_for_eff_velocities(self, rotor_effective_velocities): tilt_angles = compute_tilt_angles_for_floating_turbines_map( self.turbine_type_map_sorted, self.tilt_angles_sorted, self.turbine_tilt_interps, rotor_effective_velocities, ) return tilt_angles def finalize(self, unsorted_indices): self.yaw_angles = np.take_along_axis( self.yaw_angles_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.tilt_angles = np.take_along_axis( self.tilt_angles_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.hub_heights = np.take_along_axis( self.hub_heights_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.rotor_diameters = np.take_along_axis( self.rotor_diameters_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.TSRs = np.take_along_axis( self.TSRs_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.ref_tilts = np.take_along_axis( self.ref_tilts_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.correct_cp_ct_for_tilt = np.take_along_axis( self.correct_cp_ct_for_tilt_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.turbine_type_map = np.take_along_axis( self.turbine_type_map_sorted, unsorted_indices[:,:,0,0], axis=1 ) self.state.USED @property def coordinates(self): return np.array([ np.array([x, y, z]) for x, y, z in zip( self.layout_x, self.layout_y, self.hub_heights if len(self.hub_heights.shape) == 1 else self.hub_heights[0] ) ]) @property def n_turbines(self): return len(self.layout_x) def check_turbine_definition_for_v3_keys(turbine_definition: dict): """Check that the turbine definition does not contain any v3 keys.""" v3_deprecation_msg = ( "Consider using the convert_turbine_v3_to_v4.py utility in floris/tools " + "to convert from a FLORIS v3 turbine definition to FLORIS v4. " + "See https://natlabrockies.github.io/floris/v3_to_v4.html for more information." ) if "generator_efficiency" in turbine_definition: raise ValueError( "generator_efficiency is no longer supported as power is specified in absolute terms " + "in FLORIS v4. " + v3_deprecation_msg ) v3_renamed_keys = ["pP", "pT", "ref_density_cp_ct", "ref_tilt_cp_ct"] if any(k in turbine_definition for k in v3_renamed_keys): v3_list_keys = ", ".join(map(str,v3_renamed_keys[:-1]))+", and "+v3_renamed_keys[-1] v4_versions = ( "cosine_loss_exponent_yaw, cosine_loss_exponent_tilt, ref_air_density, and ref_tilt" ) raise ValueError( v3_list_keys + " have been renamed to " + v4_versions + ", respectively, and placed under the power_thrust_table field in FLORIS v4. " + v3_deprecation_msg ) if "thrust" in turbine_definition["power_thrust_table"]: raise ValueError( "thrust has been renamed thrust_coefficient in FLORIS v4 (and power is now specified " "in absolute terms with units kW, rather than as a coefficient). " + v3_deprecation_msg ) ================================================ FILE: floris/core/flow_field.py ================================================ import copy import attrs import matplotlib.path as mpltPath import numpy as np from attrs import define, field from scipy.interpolate import LinearNDInterpolator, NearestNDInterpolator from scipy.spatial import ConvexHull from shapely.geometry import Polygon from floris.core import ( BaseClass, Grid, PointsGrid, ) from floris.type_dec import ( floris_array_converter, NDArrayFloat, NDArrayObject, ) @define class FlowField(BaseClass): wind_speeds: NDArrayFloat = field(converter=floris_array_converter) wind_directions: NDArrayFloat = field(converter=floris_array_converter) wind_veer: float = field(converter=float) wind_shear: float = field(converter=float) air_density: float = field(converter=float) turbulence_intensities: NDArrayFloat = field(converter=floris_array_converter) reference_wind_height: float = field(converter=float) heterogeneous_inflow_config: dict = field(default=None) multidim_conditions: dict = field(default=None) n_findex: int = field(init=False) u_initial_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([])) v_initial_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([])) w_initial_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([])) u_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([])) v_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([])) w_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([])) u: NDArrayFloat = field(init=False, factory=lambda: np.array([])) v: NDArrayFloat = field(init=False, factory=lambda: np.array([])) w: NDArrayFloat = field(init=False, factory=lambda: np.array([])) het_map: NDArrayObject = field(init=False, default=None) dudz_initial_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([])) turbulence_intensity_field: NDArrayFloat = field(init=False, factory=lambda: np.array([])) turbulence_intensity_field_sorted: NDArrayFloat = field( init=False, factory=lambda: np.array([]) ) turbulence_intensity_field_sorted_avg: NDArrayFloat = field( init=False, factory=lambda: np.array([]) ) @turbulence_intensities.validator def turbulence_intensities_validator( self, instance: attrs.Attribute, value: NDArrayFloat ) -> None: # Check that the array is 1-dimensional if value.ndim != 1: raise ValueError( "turbulence_intensities must have 1-dimension" ) # Check the turbulence intensity is length n_findex if len(value) != self.n_findex: raise ValueError("turbulence_intensities must be length n_findex") @wind_directions.validator def wind_directions_validator(self, instance: attrs.Attribute, value: NDArrayFloat) -> None: # Check that the array is 1-dimensional if self.wind_directions.ndim != 1: raise ValueError( "wind_directions must have 1-dimension" ) """Using the validator method to keep the `n_findex` attribute up to date.""" self.n_findex = value.size @wind_speeds.validator def wind_speeds_validator(self, instance: attrs.Attribute, value: NDArrayFloat) -> None: # Check that the array is 1-dimensional if self.wind_speeds.ndim != 1: raise ValueError( "wind_speeds must have 1-dimension" ) """Confirm wind speeds and wind directions have the same length""" if len(self.wind_directions) != len(self.wind_speeds): raise ValueError( f"wind_directions (length = {len(self.wind_directions)}) and " f"wind_speeds (length = {len(self.wind_speeds)}) must have the same length" ) @heterogeneous_inflow_config.validator def heterogeneous_config_validator(self, instance: attrs.Attribute, value: dict | None) -> None: """Using the validator method to check that the heterogeneous_inflow_config dictionary has the correct key-value pairs. """ if value is None: return # Check that the correct keys are supplied for the heterogeneous_inflow_config dict for k in ["speed_multipliers", "x", "y"]: if k not in value.keys(): raise ValueError( "heterogeneous_inflow_config must contain entries for 'speed_multipliers'," f"'x', and 'y', with 'z' optional. Missing '{k}'." ) if "z" not in value: # If only a 2D case, add "None" for the z locations value["z"] = None if "interp_method" not in value: # If no interpolation method is specified, default to linear value["interp_method"] = "linear" @het_map.validator def het_map_validator(self, instance: attrs.Attribute, value: list | None) -> None: """Using this validator to make sure that the het_map has an interpolant defined for each findex. """ if value is None: return if self.n_findex != np.array(value).shape[0]: raise ValueError( "The het_map's first dimension not equal to the FLORIS first dimension." ) def __attrs_post_init__(self) -> None: if self.heterogeneous_inflow_config is not None: self.generate_heterogeneous_wind_map() def initialize_velocity_field(self, grid: Grid) -> None: # Create an initial wind profile as a function of height. The values here will # be multiplied with the wind speeds to give the initial wind field. # Since we use grid.z, this is a vertical plane for each turbine # Here, the profile is of shape (# turbines, N grid points, M grid points) # This velocity profile is 1.0 at the reference wind height and then follows wind # shear as an exponent. # NOTE: the convention of which dimension on the TurbineGrid is vertical and horizontal is # determined by this line. Since the right-most dimension on grid.z is storing the values # for height, using it here to apply the shear law makes that dimension store the vertical # wind profile. # Extract relevant x,y,z locations from the grid object if isinstance(grid, PointsGrid): x = np.tile(grid.points_x[None, :, None, None], (self.n_findex, 1, 1, 1)) y = np.tile(grid.points_y[None, :, None, None], (self.n_findex, 1, 1, 1)) z = grid.z_sorted else: x = grid.x_sorted_inertial_frame y = grid.y_sorted_inertial_frame z = grid.z_sorted wind_profile_plane = (z / self.reference_wind_height) ** self.wind_shear dwind_profile_plane = ( self.wind_shear * (1 / self.reference_wind_height) ** self.wind_shear * np.power( z, (self.wind_shear - 1), where=z != 0.0 ) ) # If no heterogeneous inflow defined, then set all speeds ups to 1.0 if self.het_map is None: speed_ups = 1.0 # If heterogeneous flow data is given, the speed ups at the defined # grid locations are determined in either 2 or 3 dimensions. else: if isinstance(self.het_map[0], NearestNDInterpolator): # Do not check for being inside of bounds for nearest neighbor interpolation. pass else: # Create a convex hull around the user-defined heterogeneous inflow bounds # to check if the calculated flow field is within the bounds. bounds = np.array(list(zip( self.heterogeneous_inflow_config['x'], self.heterogeneous_inflow_config['y'] ))) hull = ConvexHull(bounds) polygon = Polygon(bounds[hull.vertices]) path = mpltPath.Path(polygon.boundary.coords) inside = path.contains_points(np.column_stack((x.flatten(), y.flatten()))) if not np.all(inside): self.logger.warning( "The calculated flow field contains points outside of the the user-defined " "heterogeneous inflow bounds. For these points, the interpolated value has " "been filled with the freestream wind speed. If this is not the desired " "behavior, the user will need to expand the heterogeneous inflow bounds to " "fully cover the calculated flow field area." ) if len(self.het_map[0].points[0]) == 2: speed_ups = self.calculate_speed_ups(self.het_map, x, y) elif len(self.het_map[0].points[0]) == 3: speed_ups = self.calculate_speed_ups(self.het_map, x, y, z) # Create the sheer-law wind profile # This array is of shape (# wind directions, # wind speeds, grid.template_array) # Since generally grid.template_array may be many different shapes, we use transposes # here to do broadcasting from left to right (transposed), and then transpose back. # The result is an array the wind speed and wind direction dimensions on the left side # of the shape and the grid.template array on the right self.u_initial_sorted = (self.wind_speeds.T * wind_profile_plane.T).T * speed_ups self.dudz_initial_sorted = (self.wind_speeds.T * dwind_profile_plane.T).T * speed_ups self.v_initial_sorted = np.zeros( np.shape(self.u_initial_sorted), dtype=self.u_initial_sorted.dtype ) self.w_initial_sorted = np.zeros( np.shape(self.u_initial_sorted), dtype=self.u_initial_sorted.dtype ) self.u_sorted = self.u_initial_sorted.copy() self.v_sorted = self.v_initial_sorted.copy() self.w_sorted = self.w_initial_sorted.copy() self.turbulence_intensity_field = self.turbulence_intensities[:, None, None, None] self.turbulence_intensity_field = np.repeat( self.turbulence_intensity_field, grid.n_turbines, axis=1 ) self.turbulence_intensity_field_sorted = self.turbulence_intensity_field.copy() def finalize(self, unsorted_indices): self.u = np.take_along_axis(self.u_sorted, unsorted_indices, axis=1) self.v = np.take_along_axis(self.v_sorted, unsorted_indices, axis=1) self.w = np.take_along_axis(self.w_sorted, unsorted_indices, axis=1) self.turbulence_intensity_field = np.mean( np.take_along_axis( self.turbulence_intensity_field_sorted, unsorted_indices, axis=1 ), axis=(2,3) ) def calculate_speed_ups(self, het_map, x, y, z=None): if z is not None: # Calculate the 3-dimensional speed ups; squeeze is needed as the generator # adds an extra dimension speed_ups = np.squeeze( [het_map[i](x[i:i+1], y[i:i+1], z[i:i+1]) for i in range( len(het_map))], axis=1, ) else: # Calculate the 2-dimensional speed ups; squeeze is needed as the generator # adds an extra dimension speed_ups = np.squeeze( [het_map[i](x[i:i+1], y[i:i+1]) for i in range(len(het_map))], axis=1, ) return speed_ups def generate_heterogeneous_wind_map(self): """This function creates the heterogeneous interpolant used to calculate heterogeneous inflows. The interpolant is for computing wind speed based on an x and y location in the flow field. This is computed using SciPy's LinearNDInterpolator and uses a fill value equal to the freestream for interpolated values outside of the user-defined heterogeneous map bounds. Args: heterogeneous_inflow_config (dict): The heterogeneous inflow configuration dictionary. The configuration should have the following inputs specified. - **speed_multipliers** (list): A list of speed up factors that will multiply the specified freestream wind speed. This 2-dimensional array should have an array of multiplicative factors defined for each wind direction. - **x** (list): A list of x locations at which the speed up factors are defined. - **y**: A list of y locations at which the speed up factors are defined. - **z** (optional): A list of z locations at which the speed up factors are defined. """ speed_multipliers = np.array(self.heterogeneous_inflow_config["speed_multipliers"]) x = self.heterogeneous_inflow_config["x"] y = self.heterogeneous_inflow_config["y"] z = self.heterogeneous_inflow_config["z"] if "interp_method" in self.heterogeneous_inflow_config.keys(): interp_method = self.heterogeneous_inflow_config["interp_method"] else: interp_method = "linear" # Declare an empty list to store interpolants by findex interps_f = np.empty(self.n_findex, dtype=object) if z is not None: # Compute the 3-dimensional interpolants for each wind direction # Linear interpolation is used for points within the user-defined area of values, # while the freestream wind speed is used for points outside that region. # Because the (x,y,z) points are the same for each findex, we create the triangulation # once and then overwrite the values for each findex. # Create triangulation using zeroth findex interp_3d = self.interpolate_multiplier_xyz( x, y, z, speed_multipliers[0], fill_value=1.0, interp_method=interp_method, ) interp_shape = interp_3d.values.shape # Copy the interpolant for each findex and overwrite the values for findex in range(self.n_findex): interp_3d.values = speed_multipliers[findex, :].reshape(interp_shape) interps_f[findex] = copy.deepcopy(interp_3d) else: # Compute the 2-dimensional interpolants for each wind direction # Linear interpolation is used for points within the user-defined area of values, # while the freestream wind speed is used for points outside that region # Because the (x,y) points are the same for each findex, we create the triangulation # once and then overwrite the values for each findex. # Create triangulation using zeroth findex interp_2d = self.interpolate_multiplier_xy( x, y, speed_multipliers[0], fill_value=1.0, interp_method=interp_method ) # Copy the interpolant for each findex and overwrite the values for findex in range(self.n_findex): if interp_method == "linear": interp_2d.values = speed_multipliers[findex, :].reshape(-1, 1) else: interp_2d.values = speed_multipliers[findex, :] interps_f[findex] = copy.deepcopy(interp_2d) self.het_map = interps_f @staticmethod def interpolate_multiplier_xy( x: NDArrayFloat, y: NDArrayFloat, multiplier: NDArrayFloat, fill_value: float = 1.0, interp_method: str = "linear", ): """Return an interpolant for a 2D multiplier field. Args: x (NDArrayFloat): x locations y (NDArrayFloat): y locations multiplier (NDArrayFloat): multipliers fill_value (float): fill value for points outside the region interp_method (str): interpolation method, either "linear" or "nearest". Default is "linear". Returns: LinearNDInterpolator: interpolant """ if interp_method == "linear": return LinearNDInterpolator(list(zip(x, y)), multiplier, fill_value=fill_value) elif interp_method == "nearest": return NearestNDInterpolator(list(zip(x, y)), multiplier) else: raise UserWarning("Incompatible interpolation method specified.") @staticmethod def interpolate_multiplier_xyz( x: NDArrayFloat, y: NDArrayFloat, z: NDArrayFloat, multiplier: NDArrayFloat, fill_value: float = 1.0, interp_method: str = "linear", ): """Return an interpolant for a 3D multiplier field. Args: x (NDArrayFloat): x locations y (NDArrayFloat): y locations z (NDArrayFloat): z locations multiplier (NDArrayFloat): multipliers fill_value (float): fill value for points outside the region interp_method (str): interpolation method, either "linear" or "nearest". Default is "linear". Returns: LinearNDInterpolator: interpolant """ if interp_method == "linear": return LinearNDInterpolator(list(zip(x, y, z)), multiplier, fill_value=fill_value) elif interp_method == "nearest": return NearestNDInterpolator(list(zip(x, y, z)), multiplier) else: raise UserWarning("Incompatible interpolation method specified.") ================================================ FILE: floris/core/grid.py ================================================ from abc import ABC, abstractmethod from typing import Iterable import attrs import numpy as np from attrs import define, field from floris.core import BaseClass from floris.type_dec import ( floris_array_converter, floris_float_type, NDArrayFloat, NDArrayInt, ) from floris.utilities import ( reverse_rotate_coordinates_rel_west, rotate_coordinates_rel_west, ) @define class Grid(ABC, BaseClass): """ Grid should establish domain bounds based on given criteria, and develop three arrays to contain components of the grid locations in space. This could be generalized to any number of dimensions to be used by perhaps a turbulence field. The grid will have to be reestablished for each wind direction since the planform area of the farm will be different. x are the locations in space in the primary direction (typically the direction of the wind) y are the locations in space in the lateral direction z are the locations in space in the vertical direction u are the velocity components at each point in space v are the velocity components at each point in space w are the velocity components at each point in space all of these arrays are the same size Args: turbine_coordinates (:py:obj:`NDArrayFloat`): The arrays of turbine coordinates as Numpy arrays with shape (N coordinates, 3). turbine_diameters (:py:obj:`NDArrayFloat`): The rotor diameters of each turbine. wind_directions (:py:obj:`NDArrayFloat`): Wind directions supplied by the user. grid_resolution (:py:obj:`int` | :py:obj:`Iterable(int,)`): Grid resolution with values specific to each grid type. """ turbine_coordinates: NDArrayFloat = field(converter=floris_array_converter) turbine_diameters: NDArrayFloat = field(converter=floris_array_converter) wind_directions: NDArrayFloat = field(converter=floris_array_converter) grid_resolution: int | Iterable = field() n_turbines: int = field(init=False) n_findex: int = field(init=False) x_sorted: NDArrayFloat = field(init=False) y_sorted: NDArrayFloat = field(init=False) z_sorted: NDArrayFloat = field(init=False) x_sorted_inertial_frame: NDArrayFloat = field(init=False) y_sorted_inertial_frame: NDArrayFloat = field(init=False) z_sorted_inertial_frame: NDArrayFloat = field(init=False) cubature_weights: NDArrayFloat = field(init=False, default=None) @turbine_coordinates.validator def check_coordinates(self, instance: attrs.Attribute, value: np.ndarray) -> None: """ Ensures all elements are Numpy arrays and keeps the `n_turbines` attribute up to date. """ types = np.unique([isinstance(c, np.ndarray) for c in value]) if not all(types): raise TypeError( "'turbine_coordinates' must be `np.array` objects " "with three components of type `float`." ) self.n_turbines = len(value) @wind_directions.validator def wind_directions_validator(self, instance: attrs.Attribute, value: NDArrayFloat) -> None: """Using the validator method to keep the `n_findex` attribute up to date.""" self.n_findex = value.size @grid_resolution.validator def grid_resolution_validator(self, instance: attrs.Attribute, value: int | Iterable) -> None: # TODO move this to the grid types and off of the base class """Check that grid resolution is given as appropriate for the chosen Grid-type.""" if isinstance(value, int) and \ isinstance(self, (TurbineGrid, TurbineCubatureGrid, PointsGrid)): return elif isinstance(value, Iterable) and isinstance(self, FlowFieldPlanarGrid): assert type(value[0]) is int assert type(value[1]) is int elif isinstance(value, Iterable) and isinstance(self, FlowFieldGrid): assert type(value[0]) is int assert type(value[1]) is int assert type(value[2]) is int else: raise TypeError("`grid_resolution` must be of type int or Iterable(int,)") @abstractmethod def set_grid(self) -> None: raise NotImplementedError("Grid.set_grid") @define class TurbineGrid(Grid): """See `Grid` for more details. Args: turbine_coordinates (:py:obj:`NDArrayFloat`): The arrays of turbine coordinates as Numpy arrays with shape (N coordinates, 3). turbine_diameters (:py:obj:`NDArrayFloat`): The rotor diameters of each turbine. wind_directions (:py:obj:`NDArrayFloat`): Wind directions supplied by the user. grid_resolution (:py:obj:`int`): The number of points in each direction of the square grid on the rotor plane. For example, grid_resolution=3 creates a 3x3 grid within the rotor swept area. """ # TODO: describe these and the differences between `sorted_indices` and `sorted_coord_indices` sorted_indices: NDArrayInt = field(init=False) sorted_coord_indices: NDArrayInt = field(init=False) unsorted_indices: NDArrayInt = field(init=False) x_center_of_rotation: NDArrayFloat = field(init=False) y_center_of_rotation: NDArrayFloat = field(init=False) average_method = "cubic-mean" def __attrs_post_init__(self) -> None: self.set_grid() def set_grid(self) -> None: """ Create grid points at each turbine for each wind direction and wind speed in the simulation. This creates the underlying data structure for the calculation. arrays have shape (n wind directions, n wind speeds, n turbines, m grid spanwise, m grid vertically) - dimension 1: each wind direction - dimension 2: each wind speed - dimension 3: each turbine - dimension 4: number of points in the spanwise direction (ngrid) - dimension 5: number of points in the vertical dimension (ngrid) For example - x is [ n wind direction, n wind speeds, n turbines, x-component of the points in the spanwise direction, x-component of the points in the vertical direction ] - y is [ n wind direction, n wind speeds, n turbines, y-component of the points in the spanwise direction, y-component of the points in the vertical direction ] The x,y,z arrays contain the actual locations in that direction. # - **self.grid_resolution** (*int*, optional): The square root of the number # of points to use on the turbine grid. This number will be # squared so that the points can be evenly distributed. # Defaults to 5. If the grid conforms to the sequential solver interface, it must be sorted from upstream to downstream In a y-z plane on the rotor swept area, the -2 dimension is a column of points and the -1 dimension is the row number. So the following line prints the 0'th column of the the 0'th turbine's grid: print(grid.y_sorted[0,0,0,0,:]) print(grid.z_sorted[0,0,0,0,:]) And this line prints a single point print(grid.y_sorted[0,0,0,0,0]) print(grid.z_sorted[0,0,0,0,0]) Note that the x coordinates are all the same for the rotor plane. """ # TODO: Where should we locate the coordinate system? Currently, its at # the foot of the turbine where the tower meets the ground. # These are the rotated coordinates of the wind turbines based on the wind direction x, y, z, self.x_center_of_rotation, self.y_center_of_rotation = rotate_coordinates_rel_west( self.wind_directions, self.turbine_coordinates, ) # - **rloc** (*float, optional): A value, from 0 to 1, that determines # the width/height of the grid of points on the rotor as a ratio of # the rotor radius. # Defaults to 0.5. # Create the data for the turbine grids radius_ratio = 0.5 disc_area_radius = radius_ratio * self.turbine_diameters / 2 template_grid = np.ones( ( self.n_findex, self.n_turbines, self.grid_resolution, self.grid_resolution, ), dtype=floris_float_type ) # Calculate the radial distance from the center of the turbine rotor. # If a grid resolution of 1 is selected, create a disc_grid of zeros, as # np.linspace would just return the starting value of -1 * disc_area_radius # which would place the point below the center of the rotor. if self.grid_resolution == 1: disc_grid = np.zeros((np.shape(disc_area_radius)[0], 1 ),dtype=floris_float_type) else: disc_grid = np.linspace( -1 * disc_area_radius, disc_area_radius, self.grid_resolution, dtype=floris_float_type, axis=1 ) # Construct the turbine grids # Here, they are already rotated to the correct orientation for each wind direction _x = x[:, :, None, None] * template_grid ones_grid = np.ones( (self.n_turbines, self.grid_resolution, self.grid_resolution), dtype=floris_float_type ) _y = y[:, :, None, None] + template_grid * ( disc_grid[None, :, :, None]) _z = z[:, :, None, None] + template_grid * ( disc_grid[:, None, :] * ones_grid ) # Sort the turbines at each wind direction # Get the sorted indices for the x coordinates. These are the indices # to sort the turbines from upstream to downstream for all wind directions. # Also, store the indices to sort them back for when the calculation finishes. self.sorted_indices = _x.argsort(axis=1) self.sorted_coord_indices = x.argsort(axis=1) self.unsorted_indices = self.sorted_indices.argsort(axis=1) # Put the turbine coordinates into the final arrays in their sorted order # These are the coordinates that should be used within the internal calculations # such as the wake models and the solvers. self.x_sorted = np.take_along_axis(_x, self.sorted_indices, axis=1) self.y_sorted = np.take_along_axis(_y, self.sorted_indices, axis=1) self.z_sorted = np.take_along_axis(_z, self.sorted_indices, axis=1) # Now calculate grid coordinates in original frame (from 270 deg perspective) self.x_sorted_inertial_frame, self.y_sorted_inertial_frame, self.z_sorted_inertial_frame = \ reverse_rotate_coordinates_rel_west( wind_directions=self.wind_directions, grid_x=self.x_sorted, grid_y=self.y_sorted, grid_z=self.z_sorted, x_center_of_rotation=self.x_center_of_rotation, y_center_of_rotation=self.y_center_of_rotation, ) @define class TurbineCubatureGrid(Grid): """ This grid type arranges points throughout the swept area of the rotor based on the cubature of a unit circle. The number of points is set by the user, and then the location of the points and their weighting in integration is automatically set. This type of grid enables a better approximation of the total incoming velocities on the rotor and therefore a more accurate average velocity, thrust coefficient, and axial induction. Args: turbine_coordinates (:py:obj:`NDArrayFloat`): The arrays of turbine coordinates as Numpy arrays with shape (N coordinates, 3). turbine_diameters (:py:obj:`NDArrayFloat`): The rotor diameters of each turbine. wind_directions (:py:obj:`NDArrayFloat`): Wind directions supplied by the user. grid_resolution (:py:obj:`int`): The number of points to include in the cubature method. This value must be in the range [1, 10], and the corresponding cubature weights are set automatically. """ sorted_indices: NDArrayInt = field(init=False) sorted_coord_indices: NDArrayInt = field(init=False) unsorted_indices: NDArrayInt = field(init=False) x_center_of_rotation: NDArrayFloat = field(init=False) y_center_of_rotation: NDArrayFloat = field(init=False) average_method = "simple-cubature" def __attrs_post_init__(self) -> None: self.set_grid() def set_grid(self) -> None: """ """ # These are the rotated coordinates of the wind turbines based on the wind direction x, y, z, self.x_center_of_rotation, self.y_center_of_rotation = rotate_coordinates_rel_west( self.wind_directions, self.turbine_coordinates ) # Coefficients cubature_coefficients = TurbineCubatureGrid.get_cubature_coefficients(self.grid_resolution) # Generate grid points yv = np.kron(cubature_coefficients["r"], cubature_coefficients["q"]) zv = np.kron(cubature_coefficients["r"], cubature_coefficients["t"]) # Calculate weighting terms for the grid points self.cubature_weights = ( np.kron(cubature_coefficients["A"], np.ones((1, self.grid_resolution))) * cubature_coefficients["B"] / np.pi ) # Here, the coordinates are already rotated to the correct orientation for each # wind direction template_grid = np.ones( ( self.n_findex, self.n_turbines, len(yv), # Number of coordinates 1, ), dtype=floris_float_type ) _x = x[:, :, None, None] * template_grid _y = y[:, :, None, None] * template_grid _z = z[:, :, None, None] * template_grid n_coordinates = len(yv) yv = np.broadcast_to(yv, (self.n_findex, self.n_turbines, n_coordinates)) yv = np.expand_dims(yv, axis=-1) zv = np.broadcast_to(zv, (self.n_findex, self.n_turbines, n_coordinates)) zv = np.expand_dims(zv, axis=-1) for ti in range(self.n_turbines): _y[:, ti, :, :] += yv[:, ti] * self.turbine_diameters[ti] / 2.0 _z[:, ti, :, :] += zv[:, ti] * self.turbine_diameters[ti] / 2.0 # Sort the turbines at each wind direction # Get the sorted indices for the x coordinates. These are the indices # to sort the turbines from upstream to downstream for all wind directions. # Also, store the indices to sort them back for when the calculation finishes. self.sorted_indices = _x.argsort(axis=1) self.sorted_coord_indices = x.argsort(axis=1) self.unsorted_indices = self.sorted_indices.argsort(axis=1) # Put the turbine coordinates into the final arrays in their sorted order # These are the coordinates that should be used within the internal calculations # such as the wake models and the solvers. self.x_sorted = np.take_along_axis(_x, self.sorted_indices, axis=1) self.y_sorted = np.take_along_axis(_y, self.sorted_indices, axis=1) self.z_sorted = np.take_along_axis(_z, self.sorted_indices, axis=1) # Now calculate grid coordinates in original frame (from 270 deg perspective) self.x_sorted_inertial_frame, self.y_sorted_inertial_frame, self.z_sorted_inertial_frame = \ reverse_rotate_coordinates_rel_west( wind_directions=self.wind_directions, grid_x=self.x_sorted, grid_y=self.y_sorted, grid_z=self.z_sorted, x_center_of_rotation=self.x_center_of_rotation, y_center_of_rotation=self.y_center_of_rotation, ) @classmethod def get_cubature_coefficients(cls, N: int): """ Retrieve cubature integration coefficients. This is a class-method, and therefore the coefficients can be accessed without creating an instance of the class. Args: N (int): Order of the cubature integration. The total number of rotor points will be N^2. Must be an integer in the range [1, 10]. Returns: cubature_coefficients (dict): A dictionary containing the cubature integration coefficients, "r", "t", "q", "A" and "B". """ if N < 1 or N > 10: raise ValueError( f"Order of cubature integration must be between '1' and '10', given {N}." ) elif N == 1: r = [0.0000000000000000000000000] t = [0.0000000000000000000000000] q = [1.0000000000000000000000000] A = [1.0000000000000000000000000] elif N == 2: r = [-0.7071067811865475244008444, 0.7071067811865475244008444] t = [-0.7071067811865475244008444, 0.7071067811865475244008444] q = [ 0.7071067811865475244008444, 0.7071067811865475244008444] A = [ 0.5000000000000000000000000, 0.5000000000000000000000000] elif N == 3: r = [-0.8164965809277260327324280, 0.0000000000000000000000000, 0.8164965809277260327324280] # noqa: E501 t = [-0.8660254037844386467637232, 0.0000000000000000000000000, 0.8660254037844386467637232] # noqa: E501 q = [ 0.5000000000000000000000000, 1.0000000000000000000000000, 0.5000000000000000000000000] # noqa: E501 A = [ 0.3750000000000000000000000, 0.2500000000000000000000000, 0.3750000000000000000000000] # noqa: E501 elif N == 4: r = [-0.8880738339771152621607646,-0.4597008433809830609776340, 0.4597008433809830609776340, 0.8880738339771152621607646] # noqa: E501 t = [-0.9238795325112867561281832,-0.3826834323650897717284600, 0.3826834323650897717284600, 0.9238795325112867561281832] # noqa: E501 q = [ 0.3826834323650897717284600, 0.9238795325112867561281832, 0.9238795325112867561281832, 0.3826834323650897717284600] # noqa: E501 A = [ 0.2500000000000000000000000, 0.2500000000000000000000000, 0.2500000000000000000000000, 0.2500000000000000000000000] # noqa: E501 elif N == 5: r = [-0.9192110607898045793726291,-0.5958615826865180525340234, 0.0000000000000000000000000, 0.5958615826865180525340234, 0.9192110607898045793726291] # noqa: E501 t = [-0.9510565162951535721164393,-0.5877852522924731291687060, 0.0000000000000000000000000, 0.5877852522924731291687060, 0.9510565162951535721164393] # noqa: E501 q = [ 0.3090169943749474241022934, 0.8090169943749474241022934, 1.0000000000000000000000000, 0.8090169943749474241022934, 0.3090169943749474241022934] # noqa: E501 A = [ 0.1882015313502336375250377, 0.2562429130942108069194067, 0.1111111111111111111111111, 0.2562429130942108069194067, 0.1882015313502336375250377] # noqa: E501 elif N == 6: r = [-0.9419651451198933233901941,-0.7071067811865475244008444,-0.3357106870197288066698994, 0.3357106870197288066698994, 0.7071067811865475244008444, 0.9419651451198933233901941] # noqa: E501 t = [-0.9659258262890682867497432,-0.7071067811865475244008444,-0.2588190451025207623488988, 0.2588190451025207623488988, 0.7071067811865475244008444, 0.9659258262890682867497432] # noqa: E501 q = [ 0.2588190451025207623488988, 0.7071067811865475244008444, 0.9659258262890682867497432, 0.9659258262890682867497432, 0.7071067811865475244008444, 0.2588190451025207623488988] # noqa: E501 A = [ 0.1388888888888888888888889, 0.2222222222222222222222222, 0.1388888888888888888888889, 0.1388888888888888888888889, 0.2222222222222222222222222, 0.1388888888888888888888889] # noqa: E501 elif N == 7: r = [-0.9546790248493448767148503,-0.7684615381131740734708478,-0.4608042298407784190147371, 0.0000000000000000000000000, 0.4608042298407784190147371, 0.7684615381131740734708478, 0.9546790248493448767148503] # noqa: E501 t = [-0.9749279121818236070181317,-0.7818314824680298087084445,-0.4338837391175581204757683, 0.0000000000000000000000000, 0.4338837391175581204757683, 0.7818314824680298087084445, 0.9749279121818236070181317] # noqa: E501 q = [ 0.2225209339563144042889026, 0.6234898018587335305250049, 0.9009688679024191262361023, 1.0000000000000000000000000, 0.9009688679024191262361023, 0.6234898018587335305250049, 0.2225209339563144042889026] # noqa: E501 A = [ 0.1102311055883841876377392, 0.1940967344215859403901162, 0.1644221599900298719721446, 0.0625000000000000000000000, 0.1644221599900298719721446, 0.1940967344215859403901162, 0.1102311055883841876377392] # noqa: E501 elif N == 8: r = [-0.9646596061808674528345806,-0.8185294874300058668603761,-0.5744645143153507855310459,-0.2634992299855422962484895, 0.2634992299855422962484895, 0.5744645143153507855310459, 0.8185294874300058668603761, 0.9646596061808674528345806] # noqa: E501 t = [-0.9807852804032304491261822,-0.8314696123025452370787884,-0.5555702330196022247428308,-0.1950903220161282678482849, 0.1950903220161282678482849, 0.5555702330196022247428308, 0.8314696123025452370787884, 0.9807852804032304491261822] # noqa: E501 q = [ 0.1950903220161282678482849, 0.5555702330196022247428308, 0.8314696123025452370787884, 0.9807852804032304491261822, 0.9807852804032304491261822, 0.8314696123025452370787884, 0.5555702330196022247428308, 0.1950903220161282678482849] # noqa: E501 A = [ 0.0869637112843634643432660, 0.1630362887156365356567340, 0.1630362887156365356567340, 0.0869637112843634643432660, 0.0869637112843634643432660, 0.1630362887156365356567340, 0.1630362887156365356567340, 0.0869637112843634643432660] # noqa: E501 elif N == 9: r = [-0.9710282199223060261836893,-0.8503863747508400503582112,-0.6452980455813291706201889,-0.3738447061866471744516959, 0.0000000000000000000000000, 0.3738447061866471744516959, 0.6452980455813291706201889, 0.8503863747508400503582112, 0.9710282199223060261836893] # noqa: E501 t = [-0.9848077530122080593667430,-0.8660254037844386467637232,-0.6427876096865393263226434,-0.3420201433256687330440996, 0.0000000000000000000000000, 0.3420201433256687330440996, 0.6427876096865393263226434, 0.8660254037844386467637232, 0.9848077530122080593667430] # noqa: E501 q = [ 0.1736481776669303488517166, 0.5000000000000000000000000, 0.7660444431189780352023927, 0.9396926207859083840541093, 1.0000000000000000000000000, 0.9396926207859083840541093, 0.7660444431189780352023927, 0.5000000000000000000000000, 0.1736481776669303488517166] # noqa: E501 A = [ 0.0718567803956129706617061, 0.1406780075747310300960863, 0.1559132614878706270409275, 0.1115519505417853722012801, 0.0400000000000000000000000, 0.1115519505417853722012801, 0.1559132614878706270409275, 0.1406780075747310300960863, 0.0718567803956129706617061] # noqa: E501 elif N == 10: r = [-0.9762632447087885713212574,-0.8770602345636481685478274,-0.7071067811865475244008444,-0.4803804169063914437972190,-0.2165873427295972057980989, 0.2165873427295972057980989, 0.4803804169063914437972190, 0.7071067811865475244008444, 0.8770602345636481685478274, 0.9762632447087885713212574] # noqa: E501 t = [-0.9876883405951377261900402,-0.8910065241883678623597096,-0.7071067811865475244008444,-0.4539904997395467915604084,-0.1564344650402308690101053, 0.1564344650402308690101053, 0.4539904997395467915604084, 0.7071067811865475244008444, 0.8910065241883678623597096, 0.9876883405951377261900402] # noqa: E501 q = [ 0.1564344650402308690101053, 0.4539904997395467915604084, 0.7071067811865475244008444, 0.8910065241883678623597096, 0.9876883405951377261900402, 0.9876883405951377261900402, 0.8910065241883678623597096, 0.7071067811865475244008444, 0.4539904997395467915604084, 0.1564344650402308690101053] # noqa: E501 A = [ 0.0592317212640472718785660, 0.1196571676248416170103229, 0.1422222222222222222222222, 0.1196571676248416170103229, 0.0592317212640472718785660, 0.0592317212640472718785660, 0.1196571676248416170103229, 0.1422222222222222222222222, 0.1196571676248416170103229, 0.0592317212640472718785660] # noqa: E501 return { "r": np.array(r, dtype=float), "t": np.array(t, dtype=float), "q": np.array(q, dtype=float), "A": np.array(A, dtype=float), "B": np.pi/N, } @define class FlowFieldGrid(Grid): """ Args: turbine_coordinates (:py:obj:`NDArrayFloat`): The arrays of turbine coordinates as Numpy arrays with shape (N coordinates, 3). turbine_diameters (:py:obj:`NDArrayFloat`): The rotor diameters of each turbine. wind_directions (:py:obj:`NDArrayFloat`): Wind directions supplied by the user. grid_resolution (:py:obj:`Iterable(int,)`): The number of grid points to create in each planar direction. Must be 3 components for resolution in the x, y, and z directions. """ x_center_of_rotation: NDArrayFloat = field(init=False) y_center_of_rotation: NDArrayFloat = field(init=False) def __attrs_post_init__(self) -> None: self.set_grid() def set_grid(self) -> None: """ Create a structured grid for the entire flow field domain. Calculates the domain bounds for the current wake model. The bounds are calculated based on preset extents from the given layout. The bounds consist of the minimum and maximum values in the x-, y-, and z-directions. If the Curl model is used, the predefined bounds are always set. First, sort the turbines so that we know the bounds in the correct orientation. Then, create the grid based on this wind-from-left orientation """ # These are the rotated coordinates of the wind turbines based on the wind direction x, y, z, self.x_center_of_rotation, self.y_center_of_rotation = rotate_coordinates_rel_west( self.wind_directions, self.turbine_coordinates ) # Construct the arrays storing the grid points eps = 0.01 xmin = min(x[0,0]) - 2 * self.turbine_diameters xmax = max(x[0,0]) + 10 * self.turbine_diameters ymin = min(y[0,0]) - 2 * self.turbine_diameters ymax = max(y[0,0]) + 2 * self.turbine_diameters zmin = 0 + eps zmax = 6 * max(z[0,0]) x_points, y_points, z_points = np.meshgrid( np.linspace(xmin, xmax, int(self.grid_resolution[0])), np.linspace(ymin, ymax, int(self.grid_resolution[1])), np.linspace(zmin, zmax, int(self.grid_resolution[2])), indexing="ij" ) self.x_sorted = x_points[None, None, :, :, :] self.y_sorted = y_points[None, None, :, :, :] self.z_sorted = z_points[None, None, :, :, :] # Now calculate grid coordinates in original frame (from 270 deg perspective) self.x_sorted_inertial_frame, self.y_sorted_inertial_frame, self.z_sorted_inertial_frame = \ reverse_rotate_coordinates_rel_west( wind_directions=self.wind_directions, grid_x=self.x_sorted, grid_y=self.y_sorted, grid_z=self.z_sorted, x_center_of_rotation=self.x_center_of_rotation, y_center_of_rotation=self.y_center_of_rotation, ) @define class FlowFieldPlanarGrid(Grid): """ Args: turbine_coordinates (:py:obj:`NDArrayFloat`): The arrays of turbine coordinates as Numpy arrays with shape (N coordinates, 3). turbine_diameters (:py:obj:`NDArrayFloat`): The rotor diameters of each turbine. wind_directions (:py:obj:`NDArrayFloat`): Wind directions supplied by the user. grid_resolution (:py:obj:`Iterable(int,)`): The number of grid points to create in each planar direction. Must be 2 components for resolution in the x and y directions. The z direction is set to 3 planes at -10.0, 0.0, and +10.0 relative to the `planar_coordinate`. """ normal_vector: str = field() planar_coordinate: float = field() x1_bounds: tuple = field(default=None) x2_bounds: tuple = field(default=None) x_center_of_rotation: NDArrayFloat = field(init=False) y_center_of_rotation: NDArrayFloat = field(init=False) sorted_indices: NDArrayInt = field(init=False) unsorted_indices: NDArrayInt = field(init=False) def __attrs_post_init__(self) -> None: self.set_grid() def set_grid(self) -> None: """ Create a structured grid for the entire flow field domain. Calculates the domain bounds for the current wake model. The bounds are calculated based on preset extents from the given layout. The bounds consist of the minimum and maximum values in the x-, y-, and z-directions. First, sort the turbines so that we know the bounds in the correct orientation. Then, create the grid based on this wind-from-left orientation """ # These are the rotated coordinates of the wind turbines based on the wind direction x, y, z, self.x_center_of_rotation, self.y_center_of_rotation = rotate_coordinates_rel_west( self.wind_directions, self.turbine_coordinates ) max_diameter = np.max(self.turbine_diameters) if self.normal_vector == "z": # Rules of thumb for horizontal plane if self.x1_bounds is None: self.x1_bounds = (np.min(x) - 2 * max_diameter, np.max(x) + 10 * max_diameter) if self.x2_bounds is None: self.x2_bounds = (np.min(y) - 2 * max_diameter, np.max(y) + 2 * max_diameter) # TODO figure out proper z spacing for GCH, currently set to +/- 10.0 x_points, y_points, z_points = np.meshgrid( np.linspace(self.x1_bounds[0], self.x1_bounds[1], int(self.grid_resolution[0])), np.linspace(self.x2_bounds[0], self.x2_bounds[1], int(self.grid_resolution[1])), np.array([ float(self.planar_coordinate) - 10.0, float(self.planar_coordinate), float(self.planar_coordinate) + 10.0 ]), indexing="ij" ) self.x_sorted = x_points[None, :, :, :] self.y_sorted = y_points[None, :, :, :] self.z_sorted = z_points[None, :, :, :] elif self.normal_vector == "x": # Rules of thumb for cross plane if self.x1_bounds is None: self.x1_bounds = (np.min(y) - 2 * max_diameter, np.max(y) + 2 * max_diameter) if self.x2_bounds is None: self.x2_bounds = (0.001, 6 * np.max(z)) x_points, y_points, z_points = np.meshgrid( np.array([float(self.planar_coordinate)]), np.linspace(self.x1_bounds[0], self.x1_bounds[1], int(self.grid_resolution[0])), np.linspace(self.x2_bounds[0], self.x2_bounds[1], int(self.grid_resolution[1])), indexing="ij" ) self.x_sorted = x_points[None, :, :, :] self.y_sorted = y_points[None, :, :, :] self.z_sorted = z_points[None, :, :, :] elif self.normal_vector == "y": # Rules of thumb for y plane if self.x1_bounds is None: self.x1_bounds = (np.min(x) - 2 * max_diameter, np.max(x) + 10 * max_diameter) if self.x2_bounds is None: self.x2_bounds = (0.001, 6 * np.max(z)) x_points, y_points, z_points = np.meshgrid( np.linspace(self.x1_bounds[0], self.x1_bounds[1], int(self.grid_resolution[0])), np.array([float(self.planar_coordinate)]), np.linspace(self.x2_bounds[0], self.x2_bounds[1], int(self.grid_resolution[1])), indexing="ij" ) self.x_sorted = x_points[None, :, :, :] self.y_sorted = y_points[None, :, :, :] self.z_sorted = z_points[None, :, :, :] # Now calculate grid coordinates in original frame (from 270 deg perspective) self.x_sorted_inertial_frame, self.y_sorted_inertial_frame, self.z_sorted_inertial_frame = \ reverse_rotate_coordinates_rel_west( wind_directions=self.wind_directions, grid_x=self.x_sorted, grid_y=self.y_sorted, grid_z=self.z_sorted, x_center_of_rotation=self.x_center_of_rotation, y_center_of_rotation=self.y_center_of_rotation, ) @define class PointsGrid(Grid): """ Args: turbine_coordinates (:py:obj:`NDArrayFloat`): Not used for PointsGrid, but required for the `Grid` super-class. turbine_diameters (:py:obj:`NDArrayFloat`): Not used for PointsGrid, but required for the `Grid` super-class. wind_directions (:py:obj:`NDArrayFloat`): Wind directions supplied by the user. grid_resolution (:py:obj:`int` | :py:obj:`Iterable(int,)`): Not used for PointsGrid, but required for the `Grid` super-class. points_x (:py:obj:`NDArrayFloat`): Array of x-components for the points in the grid. points_y (:py:obj:`NDArrayFloat`): Array of y-components for the points in the grid. points_z (:py:obj:`NDArrayFloat`): Array of z-components for the points in the grid. x_center_of_rotation (:py:obj:`float`, optional): Component of the centroid of the farm or area of interest. The PointsGrid will be rotated around this center of rotation to account for wind direction changes. If not supplied, the center of rotation will be the centroid of the points in the PointsGrid. y_center_of_rotation (:py:obj:`float`, optional): Component of the centroid of the farm or area of interest. The PointsGrid will be rotated around this center of rotation to account for wind direction changes. If not supplied, the center of rotation will be the centroid of the points in the PointsGrid. """ points_x: NDArrayFloat = field(converter=floris_array_converter) points_y: NDArrayFloat = field(converter=floris_array_converter) points_z: NDArrayFloat = field(converter=floris_array_converter) x_center_of_rotation: float | None = field(default=None) y_center_of_rotation: float | None = field(default=None) def __attrs_post_init__(self) -> None: self.set_grid() def set_grid(self) -> None: """ Set points for calculation based on a series of user-supplied coordinates. """ point_coordinates = np.array(list(zip(self.points_x, self.points_y, self.points_z))) # These are the rotated coordinates of the wind turbines based on the wind direction x, y, z, _, _ = rotate_coordinates_rel_west( self.wind_directions, point_coordinates, x_center_of_rotation=self.x_center_of_rotation, y_center_of_rotation=self.y_center_of_rotation ) self.x_sorted = x[:,:,None,None] self.y_sorted = y[:,:,None,None] self.z_sorted = z[:,:,None,None] ================================================ FILE: floris/core/rotor_velocity.py ================================================ import copy from collections.abc import Iterable import numpy as np from scipy.interpolate import interp1d from floris.type_dec import ( NDArrayBool, NDArrayFilter, NDArrayFloat, NDArrayInt, NDArrayObject, ) from floris.utilities import cosd def rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw: float, yaw_angles: NDArrayFloat, rotor_effective_velocities: NDArrayFloat, ) -> NDArrayFloat: # Compute the rotor effective velocity adjusting for yaw settings pW = cosine_loss_exponent_yaw / 3.0 # Convert from cosine_loss_exponent_yaw to w rotor_effective_velocities = rotor_effective_velocities * cosd(yaw_angles) ** pW return rotor_effective_velocities def rotor_velocity_tilt_cosine_correction( tilt_angles: NDArrayFloat, ref_tilt: NDArrayFloat, cosine_loss_exponent_tilt: float, tilt_interp: NDArrayObject, correct_cp_ct_for_tilt: NDArrayBool, rotor_effective_velocities: NDArrayFloat, ) -> NDArrayFloat: # Compute the tilt, if using floating turbines old_tilt_angle = copy.deepcopy(tilt_angles) tilt_angles = compute_tilt_angles_for_floating_turbines( tilt_angles, tilt_interp, rotor_effective_velocities, ) # Only update tilt angle if requested (if the tilt isn't accounted for in the Cp curve) tilt_angles = np.where(correct_cp_ct_for_tilt, tilt_angles, old_tilt_angle) # Compute the rotor effective velocity adjusting for tilt rotor_effective_velocities = ( rotor_effective_velocities * (cosd(tilt_angles) / cosd(ref_tilt)) ** (cosine_loss_exponent_tilt / 3.0) ) return rotor_effective_velocities def simple_mean(array, axis=0): return np.mean(array, axis=axis) def cubic_mean(array, axis=0): return np.cbrt(np.mean(array ** 3.0, axis=axis)) def simple_cubature(array, cubature_weights, axis=0): weights = cubature_weights.flatten() weights = weights * len(weights) / np.sum(weights) product = (array * weights[None, None, :, None]) return simple_mean(product, axis) def cubic_cubature(array, cubature_weights, axis=0): weights = cubature_weights.flatten() weights = weights * len(weights) / np.sum(weights) return np.cbrt(np.mean((array**3.0 * weights[None, None, :, None]), axis=axis)) def average_velocity( velocities: NDArrayFloat, ix_filter: NDArrayFilter | Iterable[int] | None = None, method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None ) -> NDArrayFloat: """This property calculates and returns the average of the velocity field in turbine's rotor swept area. The average is calculated using the user-specified method. This is a vectorized function, so it can be used to calculate the average velocity for multiple turbines at once or a single turbine. **Note:** The velocity is scaled to an effective velocity by the yaw. Args: velocities (NDArrayFloat): The velocity field at each turbine; should be shape: (number of turbines, ngrid, ngrid), or (ngrid, ngrid) for a single turbine. ix_filter (NDArrayFilter | Iterable[int] | None], optional): The boolean array, or integer indices (as an iterable or array) to filter out before calculation. Defaults to None. method (str, optional): The method to use for averaging. Options are: - "simple-mean": The simple mean of the velocities - "cubic-mean": The cubic mean of the velocities - "simple-cubature": A cubature integration of the velocities - "cubic-cubature": A cubature integration of the cube of the velocities Defaults to "cubic-mean". cubature_weights (NDArrayFloat, optional): The cubature weights to use for the cubature integration methods. Defaults to None. Returns: NDArrayFloat: The average velocity across the rotor(s). """ # The input velocities are expected to be a 4 dimensional array with shape: # (# findex, # turbines, grid resolution, grid resolution) if ix_filter is not None: velocities = velocities[:, ix_filter] axis = tuple([2 + i for i in range(velocities.ndim - 2)]) if method == "simple-mean": return simple_mean(velocities, axis) elif method == "cubic-mean": return cubic_mean(velocities, axis) elif method == "simple-cubature": if cubature_weights is None: raise ValueError("cubature_weights is required for 'simple-cubature' method.") return simple_cubature(velocities, cubature_weights, axis) elif method == "cubic-cubature": if cubature_weights is None: raise ValueError("cubature_weights is required for 'cubic-cubature' method.") return cubic_cubature(velocities, cubature_weights, axis) else: raise ValueError("Incorrect method given.") def compute_tilt_angles_for_floating_turbines_map( turbine_type_map: NDArrayObject, tilt_angles: NDArrayFloat, tilt_interps: dict[str, interp1d], rotor_effective_velocities: NDArrayFloat, ) -> NDArrayFloat: # Loop over each turbine type given to get tilt angles for all turbines old_tilt_angles = copy.deepcopy(tilt_angles) tilt_angles = np.zeros(np.shape(rotor_effective_velocities)) turb_types = np.unique(turbine_type_map) for turb_type in turb_types: # If no tilt interpolation is specified, assume no modification to tilt if tilt_interps[turb_type] is None: # Use passed tilt angles tilt_angles += old_tilt_angles * (turbine_type_map == turb_type) else: # Apply interpolated tilt angle tilt_angles += compute_tilt_angles_for_floating_turbines( tilt_angles, tilt_interps[turb_type], rotor_effective_velocities ) * (turbine_type_map == turb_type) return tilt_angles def compute_tilt_angles_for_floating_turbines( tilt_angles: NDArrayFloat, tilt_interp: dict[str, interp1d], rotor_effective_velocities: NDArrayFloat, ) -> NDArrayFloat: # Loop over each turbine type given to get tilt angles for all turbines # If no tilt interpolation is specified, assume no modification to tilt if tilt_interp is None: # TODO should this be break? Should it be continue? Do we want to support mixed # fixed-bottom and floating? Or non-tilting floating? pass # Using a masked array, apply the tilt angle for all turbines of the current # type to the main tilt angle array else: tilt_angles = tilt_interp(rotor_effective_velocities) return tilt_angles def rotor_effective_velocity( air_density: float, ref_air_density: float, velocities: NDArrayFloat, yaw_angle: NDArrayFloat, tilt_angle: NDArrayFloat, ref_tilt: NDArrayFloat, cosine_loss_exponent_yaw: float, cosine_loss_exponent_tilt: float, tilt_interp: NDArrayObject, correct_cp_ct_for_tilt: NDArrayBool, turbine_type_map: NDArrayObject, ix_filter: NDArrayInt | Iterable[int] | None = None, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None ) -> NDArrayFloat: if isinstance(yaw_angle, list): yaw_angle = np.array(yaw_angle) if isinstance(tilt_angle, list): tilt_angle = np.array(tilt_angle) # Down-select inputs if ix_filter is given if ix_filter is not None: velocities = velocities[:, ix_filter] yaw_angle = yaw_angle[:, ix_filter] tilt_angle = tilt_angle[:, ix_filter] ref_tilt = ref_tilt[:, ix_filter] cosine_loss_exponent_yaw = cosine_loss_exponent_yaw[:, ix_filter] cosine_loss_exponent_tilt = cosine_loss_exponent_tilt[:, ix_filter] turbine_type_map = turbine_type_map[:, ix_filter] # Compute the rotor effective velocity adjusting for air density average_velocities = average_velocity( velocities, method=average_method, cubature_weights=cubature_weights ) rotor_effective_velocities = (air_density/ref_air_density)**(1/3) * average_velocities # Compute the rotor effective velocity adjusting for yaw settings rotor_effective_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw, yaw_angle, rotor_effective_velocities ) # Compute the tilt, if using floating turbines rotor_effective_velocities = rotor_velocity_tilt_cosine_correction( turbine_type_map, tilt_angle, ref_tilt, cosine_loss_exponent_tilt, tilt_interp, correct_cp_ct_for_tilt, rotor_effective_velocities, ) return rotor_effective_velocities def rotor_velocity_air_density_correction( velocities: NDArrayFloat, air_density: float, ref_air_density: float, ) -> NDArrayFloat: # Produce equivalent velocities at the reference air density return (air_density/ref_air_density)**(1/3) * velocities ================================================ FILE: floris/core/solver.py ================================================ import copy import numpy as np from floris.core import ( axial_induction, Farm, FlowField, FlowFieldGrid, FlowFieldPlanarGrid, PointsGrid, thrust_coefficient, TurbineGrid, ) from floris.core.rotor_velocity import average_velocity from floris.core.wake import WakeModelManager from floris.core.wake_deflection.empirical_gauss import yaw_added_wake_mixing from floris.core.wake_deflection.gauss import ( calculate_transverse_velocity, wake_added_yaw, yaw_added_turbulence_mixing, ) from floris.core.wake_velocity.empirical_gauss import awc_added_wake_mixing from floris.type_dec import NDArrayFloat from floris.utilities import cosd def calculate_area_overlap(wake_velocities, freestream_velocities, y_ngrid, z_ngrid): """ compute wake overlap based on the number of points that are not freestream velocity, i.e. affected by the wake """ # Count all of the rotor points with a negligible difference from freestream # count = np.sum(freestream_velocities - wake_velocities <= 0.05, axis=(3, 4)) # return (y_ngrid * z_ngrid - count) / (y_ngrid * z_ngrid) # return 1 - count / (y_ngrid * z_ngrid) # Find the points on the rotor grids with a difference from freestream of greater # than some tolerance. These are all the points in the wake. The ratio of # these points to the total points is the portion of wake overlap. return np.sum(freestream_velocities - wake_velocities > 0.05, axis=(3, 4)) / (y_ngrid * z_ngrid) # @profile def sequential_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> None: # Algorithm # For each turbine, calculate its effect on every downstream turbine. # For the current turbine, we are calculating the deficit that it adds to downstream turbines. # Integrate this into the main data structure. # Move on to the next turbine. # <> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) # Expand input turbulence intensity to 4d for (n_turbines, grid, grid) turbine_turbulence_intensity = flow_field.turbulence_intensities[:, None, None, None] turbine_turbulence_intensity = np.repeat(turbine_turbulence_intensity, farm.n_turbines, axis=1) # Ambient turbulent intensity should be a copy of n_findex-long turbulence_intensity # with dimensions expanded for (n_turbines, grid, grid) ambient_turbulence_intensities = flow_field.turbulence_intensities.copy() ambient_turbulence_intensities = ambient_turbulence_intensities[:, None, None, None] # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3), keepdims=True) y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3), keepdims=True) z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3), keepdims=True) u_i = flow_field.u_sorted[:, i:i+1] v_i = flow_field.v_sorted[:, i:i+1] ct_i = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] turbulence_intensity_i = turbine_turbulence_intensity[:, i:i+1] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] TSR_i = farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, flow_field.u_initial_sorted, grid.y_sorted[:, i:i+1] - y_i, grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, ct_i, rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, grid.x_sorted - x_i, grid.y_sorted - y_i, grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) if model_manager.enable_yaw_added_recovery: I_mixing = yaw_added_turbulence_mixing( u_i, turbulence_intensity_i, v_i, flow_field.w_sorted[:, i:i+1], v_wake[:, i:i+1], w_wake[:, i:i+1], ) gch_gain = 2 turbine_turbulence_intensity[:, i:i+1] = turbulence_intensity_i + gch_gain * I_mixing # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field, yaw_angle_i, turbulence_intensity_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args, ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( ambient_turbulence_intensities, grid.x_sorted, x_i, rotor_diameter_i, axial_induction_i, ) # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = ( np.sum(velocity_deficit * flow_field.u_initial_sorted > 0.05, axis=(2, 3)) / (grid.grid_resolution * grid.grid_resolution) ) area_overlap = area_overlap[:, :, None, None] # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) * (grid.x_sorted > x_i) * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbine_turbulence_intensity = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbine_turbulence_intensity ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.turbulence_intensity_field_sorted = turbine_turbulence_intensity flow_field.turbulence_intensity_field_sorted_avg = np.mean( turbine_turbulence_intensity, axis=(2,3), keepdims=True ) def full_flow_sequential_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid | FlowFieldPlanarGrid | PointsGrid, model_manager: WakeModelManager ) -> None: # Get the flow quantities and turbine performance turbine_grid_farm = copy.deepcopy(farm) turbine_grid_flow_field = copy.deepcopy(flow_field) turbine_grid_farm.construct_turbine_map() turbine_grid_farm.construct_turbine_thrust_coefficient_functions() turbine_grid_farm.construct_turbine_axial_induction_functions() turbine_grid_farm.construct_turbine_power_functions() turbine_grid_farm.construct_hub_heights() turbine_grid_farm.construct_rotor_diameters() turbine_grid_farm.construct_turbine_TSRs() turbine_grid_farm.construct_turbine_ref_tilts() turbine_grid_farm.construct_turbine_tilt_interps() turbine_grid_farm.construct_turbine_correct_cp_ct_for_tilt() turbine_grid_farm.set_tilt_to_ref_tilt(flow_field.n_findex) turbine_grid = TurbineGrid( turbine_coordinates=turbine_grid_farm.coordinates, turbine_diameters=turbine_grid_farm.rotor_diameters, wind_directions=turbine_grid_flow_field.wind_directions, grid_resolution=3, ) turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_findex, turbine_grid.sorted_coord_indices, ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) sequential_solver(turbine_grid_farm, turbine_grid_flow_field, turbine_grid, model_manager) ### Referring to the quantities from above, calculate the wake in the full grid # Use full flow_field here to use the full grid in the wake models deflection_model_args = model_manager.deflection_model.prepare_function( flow_field_grid, flow_field ) deficit_model_args = model_manager.velocity_model.prepare_function( flow_field_grid, flow_field ) wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) # Initialize the turbulence intensity field over the entire flow field grid n_points = flow_field_grid.x_sorted.shape[1] ambient_turbulence_intensities = flow_field.turbulence_intensities[:, None, None, None] ambient_turbulence_intensities = np.repeat(ambient_turbulence_intensities, n_points, axis=1) turbulence_intensity_field = ambient_turbulence_intensities.copy() # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(flow_field_grid.n_turbines): # Get the current turbine quantities x_i = np.mean(turbine_grid.x_sorted[:, i:i+1], axis=(2, 3), keepdims=True) y_i = np.mean(turbine_grid.y_sorted[:, i:i+1], axis=(2, 3), keepdims=True) z_i = np.mean(turbine_grid.z_sorted[:, i:i+1], axis=(2, 3), keepdims=True) u_i = turbine_grid_flow_field.u_sorted[:, i:i+1] v_i = turbine_grid_flow_field.v_sorted[:, i:i+1] ct_i = thrust_coefficient( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_sorted, air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, power_setpoints=turbine_grid_farm.power_setpoints_sorted, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_sorted, thrust_coefficient_functions=turbine_grid_farm.turbine_thrust_coefficient_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust_coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_sorted, air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, power_setpoints=turbine_grid_farm.power_setpoints_sorted, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_sorted, axial_induction_functions=turbine_grid_farm.turbine_axial_induction_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] turbulence_intensity_i = \ turbine_grid_flow_field.turbulence_intensity_field_sorted_avg[:, i:i+1] yaw_angle_i = turbine_grid_farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = turbine_grid_farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[:, i:i+1, None, None] TSR_i = turbine_grid_farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, turbine_grid_flow_field.u_initial_sorted, turbine_grid.y_sorted[:, i:i+1] - y_i, turbine_grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, ct_i, rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, flow_field_grid.x_sorted - x_i, flow_field_grid.y_sorted - y_i, flow_field_grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field, yaw_angle_i, turbulence_intensity_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args, ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) wake_added_ti = model_manager.turbulence_model.function( ambient_turbulence_intensities, flow_field_grid.x_sorted, x_i, rotor_diameter_i, axial_induction_i, ) # Calculate locations where wake-added turbulence (WAT) applies area_overlap = np.where(velocity_deficit * flow_field.u_initial_sorted > 0.05, 1, 0) # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_ti, posinf=0.0) * (flow_field_grid.x_sorted > x_i) * (np.abs(y_i - flow_field_grid.y_sorted) < 2 * rotor_diameter_i) * (flow_field_grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbulence_intensity_field = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbulence_intensity_field ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.turbulence_intensity_field_sorted = turbulence_intensity_field def cc_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> None: # <> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) turb_u_wake = np.zeros_like(flow_field.u_initial_sorted) turb_inflow_field = copy.deepcopy(flow_field.u_initial_sorted) # Set up turbulence arrays turbine_turbulence_intensity = flow_field.turbulence_intensities[:, None, None, None] turbine_turbulence_intensity = np.repeat(turbine_turbulence_intensity, farm.n_turbines, axis=1) # Ambient turbulent intensity should be a copy of n_findex-long turbulence_intensities # with extra dimension to reach 4d ambient_turbulence_intensities = flow_field.turbulence_intensities.copy() ambient_turbulence_intensities = ambient_turbulence_intensities[:, None, None, None] shape = (farm.n_turbines,) + np.shape(flow_field.u_initial_sorted) Ctmp = np.zeros((shape)) # Ctmp = np.zeros((len(x_coord), len(wd), len(ws), len(x_coord), y_ngrid, z_ngrid)) # sigma_i = np.zeros((shape)) # sigma_i = np.zeros((len(x_coord), len(wd), len(ws), len(x_coord), y_ngrid, z_ngrid)) # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3), keepdims=True) y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3), keepdims=True) z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3), keepdims=True) rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] mask2 = ( (grid.x_sorted < x_i + 0.01) * (grid.x_sorted > x_i - 0.01) * (grid.y_sorted < y_i + 0.51 * rotor_diameter_i) * (grid.y_sorted > y_i - 0.51 * rotor_diameter_i) ) turb_inflow_field = ( turb_inflow_field * ~mask2 + (flow_field.u_initial_sorted - turb_u_wake) * mask2 ) turb_avg_vels = average_velocity(turb_inflow_field)[:, :, None, None] turb_Cts = thrust_coefficient( turb_avg_vels, flow_field.turbulence_intensity_field_sorted, flow_field.air_density, farm.yaw_angles_sorted, farm.tilt_angles_sorted, farm.power_setpoints_sorted, farm.awc_modes_sorted, farm.awc_amplitudes_sorted, farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) turb_Cts = turb_Cts[:, :, None, None] turb_aIs = axial_induction( turb_avg_vels, flow_field.turbulence_intensity_field_sorted, flow_field.air_density, farm.yaw_angles_sorted, farm.tilt_angles_sorted, farm.power_setpoints_sorted, farm.awc_modes_sorted, farm.awc_amplitudes_sorted, farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) turb_aIs = turb_aIs[:, :, None, None] u_i = turb_inflow_field[:, i:i+1] v_i = flow_field.v_sorted[:, i:i+1] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) axial_induction_i = axial_induction_i[:, :, None, None] turbulence_intensity_i = turbine_turbulence_intensity[:, i:i+1] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = farm.hub_heights_sorted[:, i:i+1, None, None] TSR_i = farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, flow_field.u_initial_sorted, grid.y_sorted[:, i:i+1] - y_i, grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, turb_Cts[:, i:i+1], rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, grid.x_sorted - x_i, grid.y_sorted - y_i, grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) if model_manager.enable_yaw_added_recovery: I_mixing = yaw_added_turbulence_mixing( u_i, turbulence_intensity_i, v_i, flow_field.w_sorted[:, i:i+1], v_wake[:, i:i+1], w_wake[:, i:i+1], ) gch_gain = 1.0 turbine_turbulence_intensity[:, i:i+1] = turbulence_intensity_i + gch_gain * I_mixing turb_u_wake, Ctmp = model_manager.velocity_model.function( i, x_i, y_i, z_i, u_i, deflection_field, yaw_angle_i, turbine_turbulence_intensity, turb_Cts, farm.rotor_diameters_sorted[:, :, None, None], turb_u_wake, Ctmp, **deficit_model_args, ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( ambient_turbulence_intensities, grid.x_sorted, x_i, rotor_diameter_i, turb_aIs ) # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = 1 - ( np.sum(turb_u_wake <= 0.05, axis=(2, 3), keepdims=True) / (grid.grid_resolution * grid.grid_resolution) ) # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) * (grid.x_sorted > x_i) * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbine_turbulence_intensity = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbine_turbulence_intensity ) flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.u_sorted = turb_inflow_field flow_field.turbulence_intensity_field_sorted = turbine_turbulence_intensity flow_field.turbulence_intensity_field_sorted_avg = np.mean( turbine_turbulence_intensity, axis=(2,3), keepdims=True ) def full_flow_cc_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid | FlowFieldPlanarGrid | PointsGrid, model_manager: WakeModelManager, ) -> None: # Get the flow quantities and turbine performance turbine_grid_farm = copy.deepcopy(farm) turbine_grid_flow_field = copy.deepcopy(flow_field) turbine_grid_farm.construct_turbine_map() turbine_grid_farm.construct_turbine_thrust_coefficient_functions() turbine_grid_farm.construct_turbine_axial_induction_functions() turbine_grid_farm.construct_turbine_power_functions() turbine_grid_farm.construct_hub_heights() turbine_grid_farm.construct_rotor_diameters() turbine_grid_farm.construct_turbine_TSRs() turbine_grid_farm.construct_turbine_ref_tilts() turbine_grid_farm.construct_turbine_tilt_interps() turbine_grid_farm.construct_turbine_correct_cp_ct_for_tilt() turbine_grid_farm.set_tilt_to_ref_tilt(flow_field.n_findex) turbine_grid = TurbineGrid( turbine_coordinates=turbine_grid_farm.coordinates, turbine_diameters=turbine_grid_farm.rotor_diameters, wind_directions=turbine_grid_flow_field.wind_directions, grid_resolution=3, ) turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_findex, turbine_grid.sorted_coord_indices, ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) cc_solver(turbine_grid_farm, turbine_grid_flow_field, turbine_grid, model_manager) ### Referring to the quantities from above, calculate the wake in the full grid # Use full flow_field here to use the full grid in the wake models deflection_model_args = model_manager.deflection_model.prepare_function( flow_field_grid, flow_field ) deficit_model_args = model_manager.velocity_model.prepare_function( flow_field_grid, flow_field ) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) turb_u_wake = np.zeros_like(flow_field.u_initial_sorted) # Initialize the turbulence intensity field over the entire flow field grid n_points = flow_field_grid.x_sorted.shape[1] ambient_turbulence_intensities = flow_field.turbulence_intensities[:, None, None, None] ambient_turbulence_intensities = np.repeat(ambient_turbulence_intensities, n_points, axis=1) turbulence_intensity_field = ambient_turbulence_intensities.copy() shape = (farm.n_turbines,) + np.shape(flow_field.u_initial_sorted) Ctmp = np.zeros((shape)) # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(flow_field_grid.n_turbines): # Get the current turbine quantities x_i = np.mean(turbine_grid.x_sorted[:, i:i+1], axis=(2, 3), keepdims=True) y_i = np.mean(turbine_grid.y_sorted[:, i:i+1], axis=(2, 3), keepdims=True) z_i = np.mean(turbine_grid.z_sorted[:, i:i+1], axis=(2, 3), keepdims=True) u_i = turbine_grid_flow_field.u_sorted[:, i:i+1] v_i = turbine_grid_flow_field.v_sorted[:, i:i+1] turb_avg_vels = average_velocity(turbine_grid_flow_field.u_sorted) turb_Cts = thrust_coefficient( velocities=turb_avg_vels, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_sorted, air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, power_setpoints=turbine_grid_farm.power_setpoints_sorted, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_sorted, thrust_coefficient_functions=turbine_grid_farm.turbine_thrust_coefficient_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) turb_Cts = turb_Cts[:, :, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_sorted, air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, power_setpoints=turbine_grid_farm.power_setpoints_sorted, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_sorted, axial_induction_functions=turbine_grid_farm.turbine_axial_induction_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) axial_induction_i = axial_induction_i[:, :, None, None] turbulence_intensity_i = \ turbine_grid_flow_field.turbulence_intensity_field_sorted_avg[:, i:i+1] yaw_angle_i = turbine_grid_farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = turbine_grid_farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[:, i:i+1, None, None] TSR_i = turbine_grid_farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, turbine_grid_flow_field.u_initial_sorted, turbine_grid.y_sorted[:, i:i+1] - y_i, turbine_grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, turb_Cts[:, i:i+1], rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, flow_field_grid.x_sorted - x_i, flow_field_grid.y_sorted - y_i, flow_field_grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) # NOTE: exponential turb_u_wake, Ctmp = model_manager.velocity_model.function( i, x_i, y_i, z_i, u_i, deflection_field, yaw_angle_i, turbine_grid_flow_field.turbulence_intensity_field_sorted_avg, turb_Cts, turbine_grid_farm.rotor_diameters_sorted[:, :, None, None], turb_u_wake, Ctmp, **deficit_model_args, ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( ambient_turbulence_intensities, flow_field_grid.x_sorted, x_i, rotor_diameter_i, axial_induction_i ) # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = np.where(turb_u_wake > 0.05, 1, 0) # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) * (flow_field_grid.x_sorted > x_i) * (np.abs(y_i - flow_field_grid.y_sorted) < 2 * rotor_diameter_i) * (flow_field_grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbulence_intensity_field = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbulence_intensity_field ) flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.u_sorted = flow_field.u_initial_sorted - turb_u_wake flow_field.turbulence_intensity_field_sorted = turbulence_intensity_field def turbopark_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> None: # Algorithm # For each turbine, calculate its effect on every downstream turbine. # For the current turbine, we are calculating the deficit that it adds to downstream turbines. # Integrate this into the main data structure. # Move on to the next turbine. # <> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) shape = (farm.n_turbines,) + np.shape(flow_field.u_initial_sorted) velocity_deficit = np.zeros(shape) deflection_field = np.zeros_like(flow_field.u_initial_sorted) # Set up turbulence arrays turbine_turbulence_intensity = flow_field.turbulence_intensities[:, None, None, None] turbine_turbulence_intensity = np.repeat(turbine_turbulence_intensity, farm.n_turbines, axis=1) # Ambient turbulent intensity should be a copy of n_findex-long turbulence_intensities # with extra dimension to reach 4d ambient_turbulence_intensities = flow_field.turbulence_intensities.copy() ambient_turbulence_intensities = ambient_turbulence_intensities[:, None, None, None] # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3), keepdims=True) y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3), keepdims=True) z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3), keepdims=True) Cts = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) ct_i = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: raise NotImplementedError( "Secondary steering not available for this model.") # Model calculations # NOTE: exponential if np.any(farm.yaw_angles_sorted): model_manager.deflection_model.logger.warning( "WARNING: Deflection with the TurbOPark model has not been fully validated. " "This is an initial implementation, and we advise you use at your own risk " "and perform a thorough examination of the results." ) for ii in range(i): x_ii = np.mean(grid.x_sorted[:, ii:ii+1], axis=(2, 3), keepdims=True) y_ii = np.mean(grid.y_sorted[:, ii:ii+1], axis=(2, 3), keepdims=True) yaw_ii = farm.yaw_angles_sorted[:, ii:ii+1, None, None] turbulence_intensity_ii = turbine_turbulence_intensity[:, ii:ii+1] ct_ii = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[ii], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) ct_ii = ct_ii[:, 0:1, None, None] rotor_diameter_ii = farm.rotor_diameters_sorted[:, ii:ii+1, None, None] deflection_field_ii = model_manager.deflection_model.function( x_ii, y_ii, yaw_ii, turbulence_intensity_ii, ct_ii, rotor_diameter_ii, **deflection_model_args, ) deflection_field[:, ii:ii+1, :, :] = deflection_field_ii[:, i:i+1, :, :] if model_manager.enable_transverse_velocities: raise NotImplementedError( "Transverse velocities not used in this model.") if model_manager.enable_yaw_added_recovery: raise NotImplementedError( "Yaw added recovery not used in this model.") # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, turbine_turbulence_intensity, Cts[:, :, None, None], rotor_diameter_i, farm.rotor_diameters_sorted[:, :, None, None], i, deflection_field, **deficit_model_args, ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( ambient_turbulence_intensities, grid.x_sorted, x_i, rotor_diameter_i, axial_induction_i ) # TODO: leaving this in for GCH quantities; will need to find another way to # compute area_overlap as the current wake deficit is solved for only upstream # turbines; could use WAT_upstream # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = ( np.sum( velocity_deficit * flow_field.u_initial_sorted > 0.05, axis=(2, 3), keepdims=True ) / (grid.grid_resolution * grid.grid_resolution) ) # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) * (grid.x_sorted > x_i) * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbine_turbulence_intensity = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbine_turbulence_intensity ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.turbulence_intensity_field_sorted = turbine_turbulence_intensity flow_field.turbulence_intensity_field_sorted_avg = np.mean( turbine_turbulence_intensity, axis=(2, 3), keepdims=True ) def full_flow_turbopark_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid, model_manager: WakeModelManager ) -> None: raise NotImplementedError("Plotting for the TurbOPark model is not currently implemented.") def empirical_gauss_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> NDArrayFloat: """ Algorithm: For each turbine, calculate its effect on every downstream turbine. For the current turbine, we are calculating the deficit that it adds to downstream turbines. Integrate this into the main data structure. Move on to the next turbine. Args: farm (Farm) flow_field (FlowField) grid (TurbineGrid) model_manager (WakeModelManager) Raises: NotImplementedError: Raised if secondary steering is enabled with the EmGauss model. NotImplementedError: Raised if transverse velocities is enabled with the EmGauss model. Returns: NDArrayFloat: wake induced mixing field primarily for use in the full-flow EmGauss solver """ # <> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) x_locs = np.mean(grid.x_sorted, axis=(2, 3))[:,:,None] downstream_distance_D = x_locs - np.transpose(x_locs, axes=(0,2,1)) downstream_distance_D = downstream_distance_D / \ np.repeat(farm.rotor_diameters_sorted[:,:,None], grid.n_turbines, axis=-1) downstream_distance_D = np.maximum(downstream_distance_D, 0.1) # For ease # Initialize the mixing factor model using TI if specified initial_mixing_factor = model_manager.turbulence_model.atmospheric_ti_gain * np.eye( grid.n_turbines ) mixing_factor = np.repeat( initial_mixing_factor[None, :, :], flow_field.n_findex, axis=0 ) mixing_factor = mixing_factor * flow_field.turbulence_intensities[:, None, None] # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3), keepdims=True) y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3), keepdims=True) z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3), keepdims=True) ct_i = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_sorted, air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, power_setpoints=farm.power_setpoints_sorted, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] awc_mode_i = farm.awc_modes_sorted[:, i:i+1, None, None] awc_amplitude_i = farm.awc_amplitudes_sorted[:, i:i+1, None, None] awc_frequency_i = farm.awc_frequencies_sorted[:, i:i+1, None, None] hub_height_i = farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] # Secondary steering not currently implemented in EmGauss model # effective_yaw_i = np.zeros_like(yaw_angle_i) # effective_yaw_i += yaw_angle_i average_velocities = average_velocity( flow_field.u_sorted, method=grid.average_method, cubature_weights=grid.cubature_weights ) tilt_angle_i = farm.calculate_tilt_for_eff_velocities(average_velocities) tilt_angle_i = tilt_angle_i[:, i:i+1, None, None] if model_manager.enable_secondary_steering: raise NotImplementedError( "Secondary steering not available for this model.") if model_manager.enable_transverse_velocities: raise NotImplementedError( "Transverse velocities not used in this model.") if model_manager.enable_yaw_added_recovery: # Influence of yawing on turbine's own wake mixing_factor[:, i:i+1, i] += \ yaw_added_wake_mixing( axial_induction_i, yaw_angle_i, 1, model_manager.deflection_model.yaw_added_mixing_gain ) if model_manager.enable_active_wake_mixing: # Influence of awc on turbine's own wake mixing_factor[:, i:i+1, i] += \ awc_added_wake_mixing( awc_mode_i, awc_amplitude_i, awc_frequency_i, model_manager.velocity_model.awc_wake_exp, model_manager.velocity_model.awc_wake_denominator ) # Extract total wake induced mixing for turbine i mixing_i = np.linalg.norm( mixing_factor[:, i:i+1, :, None], ord=2, axis=2, keepdims=True ) # Model calculations # NOTE: exponential deflection_field_y, deflection_field_z = model_manager.deflection_model.function( x_i, y_i, yaw_angle_i, tilt_angle_i, mixing_i, ct_i, rotor_diameter_i, **deflection_model_args ) # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field_y, deflection_field_z, yaw_angle_i, tilt_angle_i, mixing_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = np.sum(velocity_deficit * flow_field.u_initial_sorted > 0.05, axis=(2, 3))\ / (grid.grid_resolution * grid.grid_resolution) # Compute wake induced mixing factor mixing_factor[:,:,i] += \ area_overlap * model_manager.turbulence_model.function( axial_induction_i, downstream_distance_D[:,:,i] ) if model_manager.enable_yaw_added_recovery: mixing_factor[:,:,i] += \ area_overlap * yaw_added_wake_mixing( axial_induction_i, yaw_angle_i, downstream_distance_D[:,:,i], model_manager.deflection_model.yaw_added_mixing_gain ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake return mixing_factor def full_flow_empirical_gauss_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid, model_manager: WakeModelManager ) -> None: # Get the flow quantities and turbine performance turbine_grid_farm = copy.deepcopy(farm) turbine_grid_flow_field = copy.deepcopy(flow_field) turbine_grid_farm.construct_turbine_map() turbine_grid_farm.construct_turbine_thrust_coefficient_functions() turbine_grid_farm.construct_turbine_axial_induction_functions() turbine_grid_farm.construct_turbine_power_functions() turbine_grid_farm.construct_hub_heights() turbine_grid_farm.construct_rotor_diameters() turbine_grid_farm.construct_turbine_TSRs() turbine_grid_farm.construct_turbine_ref_tilts() turbine_grid_farm.construct_turbine_tilt_interps() turbine_grid_farm.construct_turbine_correct_cp_ct_for_tilt() turbine_grid_farm.set_tilt_to_ref_tilt(flow_field.n_findex) turbine_grid = TurbineGrid( turbine_coordinates=turbine_grid_farm.coordinates, turbine_diameters=turbine_grid_farm.rotor_diameters, wind_directions=turbine_grid_flow_field.wind_directions, grid_resolution=3, ) turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_findex, turbine_grid.sorted_coord_indices ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) wim_field = empirical_gauss_solver( turbine_grid_farm, turbine_grid_flow_field, turbine_grid, model_manager ) # Create placeholder for TI, which is not currently used in the EmG model n_points = flow_field_grid.x_sorted.shape[1] ambient_turbulence_intensities = flow_field.turbulence_intensities[:, None, None, None] ambient_turbulence_intensities = np.repeat(ambient_turbulence_intensities, n_points, axis=1) turbulence_intensity_field = ambient_turbulence_intensities.copy() ### Referring to the quantities from above, calculate the wake in the full grid # Use full flow_field here to use the full grid in the wake models deflection_model_args = model_manager.deflection_model.prepare_function( flow_field_grid, flow_field ) deficit_model_args = model_manager.velocity_model.prepare_function(flow_field_grid, flow_field) wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(flow_field_grid.n_turbines): # Get the current turbine quantities x_i = np.mean(turbine_grid.x_sorted[:, i:i+1], axis=(2,3), keepdims=True) y_i = np.mean(turbine_grid.y_sorted[:, i:i+1], axis=(2,3), keepdims=True) z_i = np.mean(turbine_grid.z_sorted[:, i:i+1], axis=(2,3), keepdims=True) ct_i = thrust_coefficient( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_sorted, air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, power_setpoints=turbine_grid_farm.power_setpoints_sorted, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_sorted, thrust_coefficient_functions=turbine_grid_farm.turbine_thrust_coefficient_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_sorted, air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, power_setpoints=turbine_grid_farm.power_setpoints_sorted, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_sorted, axial_induction_functions=turbine_grid_farm.turbine_axial_induction_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] yaw_angle_i = turbine_grid_farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = turbine_grid_farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[:, i:i+1, None, None] wake_induced_mixing_i = wim_field[:, i:i+1, :, None].sum(axis=2, keepdims=1) effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i average_velocities = average_velocity( turbine_grid_flow_field.u_sorted, method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights ) tilt_angle_i = turbine_grid_farm.calculate_tilt_for_eff_velocities(average_velocities) tilt_angle_i = tilt_angle_i[:, i:i+1, None, None] if model_manager.enable_secondary_steering: raise NotImplementedError( "Secondary steering not available for this model.") if model_manager.enable_transverse_velocities: raise NotImplementedError( "Transverse velocities not used in this model.") # Model calculations # NOTE: exponential deflection_field_y, deflection_field_z = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, tilt_angle_i, wake_induced_mixing_i, ct_i, rotor_diameter_i, **deflection_model_args ) # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field_y, deflection_field_z, yaw_angle_i, tilt_angle_i, wake_induced_mixing_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.turbulence_intensity_field_sorted = turbulence_intensity_field ================================================ FILE: floris/core/turbine/__init__.py ================================================ from floris.core.turbine.controller_dependent_operation_model import ControllerDependentTurbine from floris.core.turbine.operation_models import ( AWCTurbine, CosineLossTurbine, MixedOperationTurbine, PeakShavingTurbine, SimpleDeratingTurbine, SimpleTurbine, ) from floris.core.turbine.unified_momentum_model import UnifiedMomentumModelTurbine ================================================ FILE: floris/core/turbine/controller_dependent_operation_model.py ================================================ import copy import numpy as np from attrs import define from scipy.interpolate import RegularGridInterpolator from scipy.optimize import fsolve from floris.core.rotor_velocity import ( average_velocity, compute_tilt_angles_for_floating_turbines, rotor_velocity_air_density_correction, ) from floris.core.turbine.operation_models import BaseOperationModel from floris.type_dec import ( NDArrayFloat, NDArrayObject, ) from floris.utilities import cosd, sind @define class ControllerDependentTurbine(BaseOperationModel): """ Static class defining a wind turbine model that may be misaligned with the flow. Nonzero tilt and yaw angles are handled via the model presented in https://doi.org/10.5194/wes-2023-133 . The method requires C_P, C_T look-up tables as functions of tip speed ratio and blade pitch angle, available here: "floris/turbine_library/iea_15MW_demo_cp_ct_surface.npz" for the IEA 15MW reference turbine. As with all turbine submodules, implements only static power() and thrust_coefficient() methods, which are called by power() and thrust_coefficient() on turbine.py, respectively. There are also two new functions, i.e. compute_local_vertical_shear() and control_trajectory(). These are called by thrust_coefficient() and power() to compute the vertical shear and predict the turbine status in terms of tip speed ratio and pitch angle. This class is not intended to be instantiated; it simply defines a library of static methods. Developed and implemented by Simone Tamaro, Filippo Campagnolo, and Carlo L. Bottasso at Technische Universität München (TUM). email: simone.tamaro@tum.de """ @staticmethod def power( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, power_setpoints: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_, # <- Allows other models to accept other keyword arguments ): # Sign convention: in the TUM model, negative tilt creates tower clearance tilt_angles = -tilt_angles # Compute the power-effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) rotor_effective_velocities = rotor_velocity_air_density_correction( velocities=rotor_average_velocities, air_density=air_density, ref_air_density=power_thrust_table["ref_air_density"], ) # Compute power n_findex, n_turbines = tilt_angles.shape shear = ControllerDependentTurbine.compute_local_vertical_shear(velocities) beta = power_thrust_table["controller_dependent_turbine_parameters"]["beta"] cd = power_thrust_table["controller_dependent_turbine_parameters"]["cd"] cl_alfa = power_thrust_table["controller_dependent_turbine_parameters"]["cl_alfa"] sigma = power_thrust_table["controller_dependent_turbine_parameters"]["rotor_solidity"] R = power_thrust_table["controller_dependent_turbine_parameters"]["rotor_diameter"] / 2 air_density = power_thrust_table["ref_air_density"] pitch_out, tsr_out = ControllerDependentTurbine.control_trajectory( rotor_effective_velocities, yaw_angles, tilt_angles, air_density, R, shear, power_setpoints, power_thrust_table, ) tsr_array = tsr_out theta_array = np.deg2rad(pitch_out + beta) x0 = 0.2 ### Solve for the power in yawed conditions # Compute overall misalignment (eq. (1) in Tamaro et al.) MU = np.arccos(cosd(yaw_angles) * cosd(tilt_angles)) cosMu = np.cos(MU) sinMu = np.sin(MU) p = np.zeros_like(average_velocity(velocities)) # Need to loop over findices to use fsolve for i in np.arange(n_findex): for j in np.arange(n_turbines): # Create data tuple for fsolve data = ( sigma, cd, cl_alfa, yaw_angles[i, j], tilt_angles[i, j], shear[i, j], cosMu[i, j], sinMu[i, j], (tsr_array[i, j]), (theta_array[i, j]), MU[i, j], ) ct, info, ier, msg = fsolve( ControllerDependentTurbine.get_ct, x0, args=data, full_output=True ) if ier == 1: p[i, j] = np.squeeze( ControllerDependentTurbine.find_cp( sigma, cd, cl_alfa, yaw_angles[i, j], tilt_angles[i, j], shear[i, j], cosMu[i, j], sinMu[i, j], (tsr_array[i, j]), (theta_array[i, j]), MU[i, j], ct, ) ) else: p[i, j] = -1e3 ### Solve for the power in non-yawed conditions yaw_angles = np.zeros_like(yaw_angles) # Compute overall misalignment (eq. (1) in Tamaro et al.) MU = np.arccos(cosd(yaw_angles) * cosd(tilt_angles)) cosMu = np.cos(MU) sinMu = np.sin(MU) p0 = np.zeros_like((average_velocity(velocities))) for i in np.arange(n_findex): for j in np.arange(n_turbines): data = ( sigma, cd, cl_alfa, yaw_angles[i, j], tilt_angles[i, j], shear[i, j], cosMu[i, j], sinMu[i, j], (tsr_array[i, j]), (theta_array[i, j]), MU[i, j], ) ct, info, ier, msg = fsolve( ControllerDependentTurbine.get_ct, x0, args=data, full_output=True ) if ier == 1: p0[i, j] = np.squeeze( ControllerDependentTurbine.find_cp( sigma, cd, cl_alfa, yaw_angles[i, j], tilt_angles[i, j], shear[i, j], cosMu[i, j], sinMu[i, j], (tsr_array[i, j]), (theta_array[i, j]), MU[i, j], ct, ) ) else: p0[i, j] = -1e3 # ratio of yawed to unyawed thrust coefficients ratio = p / p0 # Extract data from lookup table and construct interpolator cp_ct_data = power_thrust_table["controller_dependent_turbine_parameters"]["cp_ct_data"] cp_i = np.array(cp_ct_data["cp_lut"]) pitch_i = np.array(cp_ct_data["pitch_lut"]) tsr_i = np.array(cp_ct_data["tsr_lut"]) interp_lut = RegularGridInterpolator( (tsr_i, pitch_i), cp_i, bounds_error=False, fill_value=None ) power_coefficient = np.zeros_like(average_velocity(velocities)) cp_interp = interp_lut( np.concatenate((tsr_array[:,:,None], pitch_out[:,:,None]), axis=2), method="cubic" ) power_coefficient = cp_interp * ratio power = ( 0.5 * air_density * (rotor_effective_velocities)**3 * np.pi * R**2 * power_coefficient * power_thrust_table["controller_dependent_turbine_parameters"]["generator_efficiency"] ) if power.max() > (power_thrust_table["controller_dependent_turbine_parameters"] ["rated_power"] * 1e3 * 1.01): print("Powers more than 1% above rated detected. Consider checking Cp-Ct data.") power = np.clip( power, 0, power_thrust_table["controller_dependent_turbine_parameters"]["rated_power"] * 1e3 ) return power @staticmethod def thrust_coefficient( power_thrust_table: dict, velocities: NDArrayFloat, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, power_setpoints: NDArrayFloat, tilt_interp: NDArrayObject, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, correct_cp_ct_for_tilt: bool = False, **_, # <- Allows other models to accept other keyword arguments ): # sign convention. in the TUM model, negative tilt creates tower clearance tilt_angles = -tilt_angles # Compute the effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) # Apply tilt and yaw corrections # Compute the tilt, if using floating turbines old_tilt_angles = copy.deepcopy(tilt_angles) tilt_angles = compute_tilt_angles_for_floating_turbines( tilt_angles=tilt_angles, tilt_interp=tilt_interp, rotor_effective_velocities=rotor_average_velocities, ) # Only update tilt angle if requested (if the tilt isn't accounted for in the Ct curve) tilt_angles = np.where(correct_cp_ct_for_tilt, tilt_angles, old_tilt_angles) beta = power_thrust_table["controller_dependent_turbine_parameters"]["beta"] cd = power_thrust_table["controller_dependent_turbine_parameters"]["cd"] cl_alfa = power_thrust_table["controller_dependent_turbine_parameters"]["cl_alfa"] sigma = power_thrust_table["controller_dependent_turbine_parameters"]["rotor_solidity"] R = power_thrust_table["controller_dependent_turbine_parameters"]["rotor_diameter"] / 2 shear = ControllerDependentTurbine.compute_local_vertical_shear(velocities) air_density = power_thrust_table["ref_air_density"] # CHANGE rotor_effective_velocities = rotor_velocity_air_density_correction( velocities=rotor_average_velocities, air_density=air_density, ref_air_density=power_thrust_table["ref_air_density"], ) # Apply standard control trajectory to determine pitch and TSR pitch_out, tsr_out = ControllerDependentTurbine.control_trajectory( rotor_effective_velocities, yaw_angles, tilt_angles, air_density, R, shear, power_setpoints, power_thrust_table, ) n_findex, n_turbines = tilt_angles.shape # u = np.squeeze(u) theta_array = np.deg2rad(pitch_out + beta) tsr_array = tsr_out x0 = 0.2 # Initial guess for the thrust coefficient solve ### Solve for the thrust coefficient in yawed conditions # Compute overall misalignment (eq. (1) in Tamaro et al.) MU = np.arccos(cosd(yaw_angles) * cosd(tilt_angles)) cosMu = np.cos(MU) sinMu = np.sin(MU) thrust_coefficient1 = np.zeros_like(average_velocity(velocities)) # Need to loop over n_findex and n_turbines here to use fsolve for i in np.arange(n_findex): for j in np.arange(n_turbines): data = ( sigma, cd, cl_alfa, yaw_angles[i, j], tilt_angles[i, j], shear[i, j], cosMu[i, j], sinMu[i, j], (tsr_array[i, j]), (theta_array[i, j]), MU[i, j], ) ct = fsolve(ControllerDependentTurbine.get_ct, x0, args=data) # Solves eq. (25) thrust_coefficient1[i, j] = np.squeeze(np.clip(ct, 0.0001, 0.9999)) ### Resolve thrust coefficient in non-yawed conditions yaw_angles = np.zeros_like(yaw_angles) MU = np.arccos(cosd(yaw_angles) * cosd(tilt_angles)) cosMu = np.cos(MU) sinMu = np.sin(MU) thrust_coefficient0 = np.zeros_like(average_velocity(velocities)) # Need to loop over n_findex and n_turbines here to use fsolve for i in np.arange(n_findex): for j in np.arange(n_turbines): data = ( sigma, cd, cl_alfa, yaw_angles[i, j], tilt_angles[i, j], shear[i, j], cosMu[i, j], sinMu[i, j], (tsr_array[i, j]), (theta_array[i, j]), MU[i, j], ) ct = fsolve(ControllerDependentTurbine.get_ct, x0, args=data) # Solves eq. (25) thrust_coefficient0[i, j] = np.squeeze(ct) # np.clip(ct, 0.0001, 0.9999) # Compute ratio of yawed to unyawed thrust coefficients ratio = thrust_coefficient1 / thrust_coefficient0 # See above eq. (29) # Extract data from lookup table and construct interpolator cp_ct_data = power_thrust_table["controller_dependent_turbine_parameters"]["cp_ct_data"] ct_i = np.array(cp_ct_data["ct_lut"]) pitch_i = np.array(cp_ct_data["pitch_lut"]) tsr_i = np.array(cp_ct_data["tsr_lut"]) interp_lut = RegularGridInterpolator( (tsr_i, pitch_i), ct_i, bounds_error=False, fill_value=None ) # *0.9722085500886761) # Interpolate and apply ratio to determine thrust coefficient ct_interp = interp_lut( np.concatenate((tsr_array[:,:,None], pitch_out[:,:,None]), axis=2), method="cubic" ) thrust_coefficient = ct_interp * ratio return thrust_coefficient @staticmethod def axial_induction( power_thrust_table: dict, velocities: NDArrayFloat, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, power_setpoints: NDArrayFloat, tilt_interp: NDArrayObject, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, correct_cp_ct_for_tilt: bool = False, **_, # <- Allows other models to accept other keyword arguments ): thrust_coefficients = ControllerDependentTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, yaw_angles=yaw_angles, tilt_angles=tilt_angles, power_setpoints=power_setpoints, tilt_interp=tilt_interp, average_method=average_method, cubature_weights=cubature_weights, correct_cp_ct_for_tilt=correct_cp_ct_for_tilt, ) # TODO: should the axial induction calculation be based on MU for zero yaw (as it is # currently) or should this be the actual yaw angle? yaw_angles = np.zeros_like(yaw_angles) MU = np.arccos(cosd(yaw_angles) * cosd(tilt_angles)) sinMu = np.sin(MU) # all the same in this case anyway (since yaw zero) # Eq. (25a) from Tamaro et al. a = 1 - ( (1 + np.sqrt(1 - thrust_coefficients - 1 / 16 * thrust_coefficients**2 * sinMu ** 2)) / (2 * (1 + 1 / 16 * thrust_coefficients * sinMu ** 2)) ) axial_induction = np.clip(a, 0.0001, 0.9999) return axial_induction @staticmethod def compute_local_vertical_shear(velocities): """ Called to evaluate the vertical (linear) shear that each rotor experience, based on the inflow velocity. This allows to make the power curve asymmetric w.r.t. yaw misalignment. """ # Check that there is a vertical profile to compute a shear profile for. If not, # raise an error. if velocities.shape[3] == 1: raise ValueError(( "The ControllerDependentTurbine computes a local shear based on inflow wind speeds " "across the rotor. The provided velocities does not contain a vertical profile. " "This can occur if n_grid is set to 1 in the FLORIS input yaml." )) n_findex, n_turbines = velocities.shape[:2] shear = np.zeros((n_findex, n_turbines)) for i in np.arange(n_findex): for j in np.arange(n_turbines): mean_speed = np.mean(velocities[i, j, :, :], axis=0) if len(mean_speed) % 2 != 0: # odd number u_u_hh = mean_speed / mean_speed[int(np.floor(len(mean_speed) / 2))] else: u_u_hh = ( mean_speed / ( mean_speed[int((len(mean_speed) / 2))] + mean_speed[int((len(mean_speed) / 2)) - 1] ) / 2 ) zg_R = np.linspace(-1, 1, len(mean_speed) + 2) polifit_k = np.polyfit(zg_R[1:-1], 1 - u_u_hh, 1) shear[i, j] = -polifit_k[0] return shear @staticmethod def control_trajectory( rotor_average_velocities, yaw_angles, tilt_angles, air_density, R, shear, power_setpoints, power_thrust_table, ): """ Determines the tip-speed-ratio and pitch angles that occur in operation. This routine assumes a standard region 2 control approach (i.e. k*rpm^2) and a region 3. Also region 2-1/2 is considered. In the future, different control strategies could be included, even user-defined. """ # Unpack parameters from power_thrust_table beta = power_thrust_table["controller_dependent_turbine_parameters"]["beta"] cd = power_thrust_table["controller_dependent_turbine_parameters"]["cd"] cl_alfa = power_thrust_table["controller_dependent_turbine_parameters"]["cl_alfa"] sigma = power_thrust_table["controller_dependent_turbine_parameters"]["rotor_solidity"] # Compute power demanded if power_setpoints is None: power_demanded = ( np.ones_like(tilt_angles) * power_thrust_table["controller_dependent_turbine_parameters"]["rated_power"] * 1000 / power_thrust_table["controller_dependent_turbine_parameters"] ["generator_efficiency"] ) else: power_demanded = ( power_setpoints / power_thrust_table["controller_dependent_turbine_parameters"] ["generator_efficiency"] ) ## Define function to get tip speed ratio def get_tsr(x, *data): ( air_density, R, sigma, shear, cd, cl_alfa, beta, gamma, tilt, u, pitch_in, omega_lut_pow, torque_lut_omega, cp_i, pitch_i, tsr_i, ) = data omega_lut_torque = omega_lut_pow * np.pi / 30 omega = x * u / R omega_rpm = omega * 30 / np.pi torque_nm = np.interp(omega, omega_lut_torque, torque_lut_omega) # Yawed case mu = np.arccos(cosd(gamma) * cosd(tilt)) data = ( sigma, cd, cl_alfa, gamma, tilt, shear, np.cos(mu), np.sin(mu), x, np.deg2rad(pitch_in) + np.deg2rad(beta), mu, ) x0 = 0.1 [ct, infodict, ier, mesg] = fsolve( ControllerDependentTurbine.get_ct, x0, args=data, full_output=True, factor=0.1 ) cp = ControllerDependentTurbine.find_cp( sigma, cd, cl_alfa, gamma, tilt, shear, np.cos(mu), np.sin(mu), x, np.deg2rad(pitch_in) + np.deg2rad(beta), mu, ct, ) # Unyawed case mu = np.arccos(cosd(0) * cosd(tilt)) data = ( sigma, cd, cl_alfa, 0, tilt, shear, np.cos(mu), np.sin(mu), x, np.deg2rad(pitch_in) + np.deg2rad(beta), mu, ) x0 = 0.1 [ct, infodict, ier, mesg] = fsolve( ControllerDependentTurbine.get_ct, x0, args=data, full_output=True, factor=0.1 ) cp0 = ControllerDependentTurbine.find_cp( sigma, cd, cl_alfa, 0, tilt, shear, np.cos(mu), np.sin(mu), x, np.deg2rad(pitch_in) + np.deg2rad(beta), mu, ct, ) # Ratio eta_p = cp / cp0 interp = RegularGridInterpolator( (np.squeeze((tsr_i)), np.squeeze((pitch_i))), cp_i, bounds_error=False, fill_value=None, ) Cp_now = interp((x, pitch_in), method="cubic") cp_g1 = Cp_now * eta_p aero_pow = 0.5 * air_density * (np.pi * R**2) * (u)**3 * cp_g1 electric_pow = torque_nm * (omega_rpm * np.pi / 30) y = aero_pow - electric_pow return y ## Define function to get pitch angle def get_pitch(x, *data): ( air_density, R, sigma, shear, cd, cl_alfa, beta, gamma, tilt, u, omega_rated, omega_lut_torque, torque_lut_omega, cp_i, pitch_i, tsr_i, ) = data omega_rpm = omega_rated * 30 / np.pi tsr = omega_rated * R / (u) pitch_in = np.deg2rad(x) torque_nm = np.interp( omega_rpm, omega_lut_torque * 30 / np.pi, torque_lut_omega ) # Yawed case mu = np.arccos(cosd(gamma) * cosd(tilt)) data = ( sigma, cd, cl_alfa, gamma, tilt, shear, np.cos(mu), np.sin(mu), tsr, (pitch_in) + np.deg2rad(beta), mu, ) x0 = 0.1 [ct, infodict, ier, mesg] = fsolve( ControllerDependentTurbine.get_ct, x0, args=data, full_output=True, factor=0.1 ) cp = ControllerDependentTurbine.find_cp( sigma, cd, cl_alfa, gamma, tilt, shear, np.cos(mu), np.sin(mu), tsr, (pitch_in) + np.deg2rad(beta), mu, ct, ) # Unyawed case mu = np.arccos(cosd(0) * cosd(tilt)) data = ( sigma, cd, cl_alfa, 0, tilt, shear, np.cos(mu), np.sin(mu), tsr, (pitch_in) + np.deg2rad(beta), mu, ) x0 = 0.1 [ct, infodict, ier, mesg] = fsolve( ControllerDependentTurbine.get_ct, x0, args=data, full_output=True, factor=0.1 ) cp0 = ControllerDependentTurbine.find_cp( sigma, cd, cl_alfa, 0, tilt, shear, np.cos(mu), np.sin(mu), tsr, (pitch_in) + np.deg2rad(beta), mu, ct, ) # Ratio yawed / unyawed eta_p = cp / cp0 interp = RegularGridInterpolator( (np.squeeze((tsr_i)), np.squeeze((pitch_i))), cp_i, bounds_error=False, fill_value=None, ) Cp_now = interp((tsr, x), method="cubic") cp_g1 = Cp_now * eta_p aero_pow = 0.5 * air_density * (np.pi * R**2) * (u)**3 * cp_g1 electric_pow = torque_nm * (omega_rpm * np.pi / 30) y = aero_pow - electric_pow return y # Extract data from lookup table cp_ct_data = power_thrust_table["controller_dependent_turbine_parameters"]["cp_ct_data"] cp_i = np.array(cp_ct_data["cp_lut"]) pitch_i = np.array(cp_ct_data["pitch_lut"]) tsr_i = np.array(cp_ct_data["tsr_lut"]) idx = np.squeeze(np.where(cp_i == np.max(cp_i))) tsr_opt = tsr_i[idx[0]] pitch_opt = pitch_i[idx[1]] max_cp = cp_i[idx[0], idx[1]] omega_cut_in = 0 # RPM omega_max = power_thrust_table["controller_dependent_turbine_parameters"]["rated_rpm"] rated_power_aero = ( power_thrust_table["controller_dependent_turbine_parameters"]["rated_power"] / power_thrust_table["controller_dependent_turbine_parameters"]["generator_efficiency"] ) * 1000 # Compute torque-rpm relation and check for region 2-and-a-half Region2andAhalf = False omega_array = np.linspace(omega_cut_in, omega_max, 161) * np.pi / 30 # rad/s Q = (0.5 * air_density * omega_array**2 * R**5 * np.pi * max_cp) / tsr_opt**3 Paero_array = Q * omega_array if Paero_array[-1] < rated_power_aero: # then we have region 2-and-a-half Region2andAhalf = True Q_extra = rated_power_aero / (omega_max * np.pi / 30) Q = np.append(Q, Q_extra) # TODO: Expression below is not assigned to anything. Should this be removed? (Paero_array[-1] / (0.5 * air_density * np.pi * R**2 * max_cp)) ** (1 / 3) omega_array = np.append(omega_array, omega_array[-1]) Paero_array = np.append(Paero_array, rated_power_aero) else: # limit aero_power to the last Q*omega_max rated_power_aero = Paero_array[-1] u_rated = (rated_power_aero / (0.5 * air_density * np.pi * R**2 * max_cp)) ** ( 1 / 3 ) u_array = np.linspace(3, 25, 45) idx = np.argmin(np.abs(u_array - u_rated)) if u_rated > u_array[idx]: u_array = np.insert(u_array, idx + 1, u_rated) else: u_array = np.insert(u_array, idx, u_rated) pow_lut_omega = Paero_array omega_lut_pow = omega_array * 30 / np.pi torque_lut_omega = Q omega_lut_torque = omega_lut_pow n_findex, n_turbines = tilt_angles.shape omega_rated = np.interp(power_demanded, pow_lut_omega, omega_lut_pow) * np.pi / 30 # rad/s u_rated = (power_demanded / (0.5 * air_density * np.pi * R**2 * max_cp)) ** (1 / 3) pitch_out = np.zeros_like(rotor_average_velocities) tsr_out = np.zeros_like(rotor_average_velocities) # Must loop to use fsolve for i in np.arange(n_findex): for j in np.arange(n_turbines): u_v = rotor_average_velocities[i, j] if u_v > u_rated[i, j]: tsr_v = ( omega_rated[i, j] * R / u_v * cosd(yaw_angles[i, j]) ** 0.5 ) else: tsr_v = tsr_opt * cosd(yaw_angles[i, j]) if Region2andAhalf: # fix for interpolation omega_lut_torque[-1] = omega_lut_torque[-1] + 1e-2 omega_lut_pow[-1] = omega_lut_pow[-1] + 1e-2 data = ( air_density, R, sigma, shear[i, j], cd, cl_alfa, beta, yaw_angles[i, j], tilt_angles[i, j], u_v, pitch_opt, omega_lut_pow, torque_lut_omega, cp_i, pitch_i, tsr_i, ) [tsr_out_soluzione, infodict, ier, mesg] = fsolve( get_tsr, tsr_v, args=data, full_output=True ) # check if solution was possible. If not, we are in region 3 if np.abs(infodict["fvec"]) > 10 or tsr_out_soluzione < 4: tsr_out_soluzione = 1000 # save solution tsr_outO = tsr_out_soluzione omega = tsr_outO * u_v / R # check if we are in region 2 or 3 if omega < omega_rated[i, j]: # region 2 # Define optimum pitch pitch_out0 = pitch_opt else: # region 3 tsr_outO = omega_rated[i, j] * R / u_v data = ( air_density, R, sigma, shear[i, j], cd, cl_alfa, beta, yaw_angles[i, j], tilt_angles[i, j], u_v, omega_rated[i, j], omega_array, Q, cp_i, pitch_i, tsr_i, ) # solve aero-electrical power balance with TSR from rated omega [pitch_out_soluzione, infodict, ier, mesg] = fsolve( get_pitch, u_v, args=data, factor=0.1, full_output=True, xtol=1e-10, maxfev=2000, ) if pitch_out_soluzione < pitch_opt: pitch_out_soluzione = pitch_opt pitch_out0 = pitch_out_soluzione # pitch and tsr will be used to compute Cp and Ct pitch_out[i, j] = np.squeeze(pitch_out0) tsr_out[i, j] = np.squeeze(tsr_outO) return pitch_out, tsr_out @staticmethod def find_cp(sigma, cd, cl_alfa, gamma, delta, k, cosMu, sinMu, tsr, theta, MU, ct): # add a small misalignment in case MU = 0 to avoid division by 0 if MU == 0: MU = 1e-6 sinMu = np.sin(MU) cosMu = np.cos(MU) a = 1 - ( (1 + np.sqrt(1 - ct - 1 / 16 * sinMu**2 * ct**2)) / (2 * (1 + 1 / 16 * ct * sinMu**2)) ) SG = sind(gamma) CG = cosd(gamma) SD = sind(delta) CD = cosd(delta) k_1s = -1 * (15 * np.pi / 32 * np.tan((MU + sinMu * (ct / 2)) / 2)) p = sigma * ( ( np.pi * cosMu**2 * tsr * cl_alfa * (a - 1) ** 2 - ( tsr * cd * np.pi * ( CD**2 * CG**2 * SD**2 * k**2 + 3 * CD**2 * SG**2 * k**2 - 8 * CD * tsr * SG * k + 8 * tsr**2 ) ) / 16 - (np.pi * tsr * sinMu**2 * cd) / 2 - (2 * np.pi * cosMu * tsr**2 * cl_alfa * theta) / 3 + (np.pi * cosMu**2 * k_1s**2 * tsr * a**2 * cl_alfa) / 4 + (2 * np.pi * cosMu * tsr**2 * a * cl_alfa * theta) / 3 + (2 * np.pi * CD * cosMu * tsr * SG * cl_alfa * k * theta) / 3 + ( ( CD**2 * cosMu**2 * tsr * cl_alfa * k**2 * np.pi * (a - 1)**2 * (CG**2 * SD**2 + SG**2) ) / (4 * sinMu**2) ) - (2 * np.pi * CD * cosMu * tsr * SG * a * cl_alfa * k * theta) / 3 + ( ( CD**2 * cosMu**2 * k_1s**2 * tsr * a**2 * cl_alfa * k**2 * np.pi * (3 * CG**2 * SD**2 + SG**2) ) / (24 * sinMu**2) ) - (np.pi * CD * CG * cosMu**2 * k_1s * tsr * SD * a * cl_alfa * k) / sinMu + (np.pi * CD * CG * cosMu**2 * k_1s * tsr * SD * a**2 * cl_alfa * k) / sinMu + (np.pi * CD * CG * cosMu * k_1s * tsr**2 * SD * a * cl_alfa * k * theta) / (5 * sinMu) - (np.pi * CD**2 * CG * cosMu * k_1s * tsr * SD * SG * a * cl_alfa * k**2 * theta) / (10 * sinMu) ) / (2 * np.pi) ) return p @staticmethod def get_ct(x, *data): """ System of equations for Ct, as represented in Eq. (25) of Tamaro et al. x is a stand-in variable for Ct, which a numerical solver will solve for. data is a tuple of input parameters to the system of equations to solve. """ sigma, cd, cl_alfa, gamma, delta, k, cosMu, sinMu, tsr, theta, MU = data # Add a small misalignment in case MU = 0 to avoid division by 0 if MU == 0: MU = 1e-6 sinMu = np.sin(MU) cosMu = np.cos(MU) CD = cosd(delta) CG = cosd(gamma) SD = sind(delta) SG = sind(gamma) # Axial induction a = 1 - ( (1 + np.sqrt(1 - x - 1 / 16 * x**2 * sinMu**2)) / (2 * (1 + 1 / 16 * x * sinMu**2)) ) k_1s = -1 * (15 * np.pi / 32 * np.tan((MU + sinMu * (x / 2)) / 2)) I1 = -( np.pi * cosMu * (tsr - CD * SG * k) * (a - 1) + (CD * CG * cosMu * k_1s * SD * a * k * np.pi * (2 * tsr - CD * SG * k)) / (8 * sinMu) ) / (2 * np.pi) I2 = ( np.pi * sinMu**2 + ( np.pi * ( CD**2 * CG**2 * SD**2 * k**2 + 3 * CD**2 * SG**2 * k**2 - 8 * CD * tsr * SG * k + 8 * tsr**2 ) ) / 12 ) / (2 * np.pi) return (sigma * (cd + cl_alfa) * (I1) - sigma * cl_alfa * theta * (I2)) - x ================================================ FILE: floris/core/turbine/operation_models.py ================================================ import copy from abc import abstractmethod from typing import ( Any, Dict, Final, ) import numpy as np from attrs import define, field from scipy.interpolate import interp1d from floris.core import BaseClass from floris.core.rotor_velocity import ( average_velocity, compute_tilt_angles_for_floating_turbines, rotor_velocity_air_density_correction, rotor_velocity_tilt_cosine_correction, rotor_velocity_yaw_cosine_correction, ) from floris.type_dec import ( NDArrayFloat, NDArrayObject, ) from floris.utilities import cosd POWER_SETPOINT_DEFAULT = 1e12 POWER_SETPOINT_DISABLED = 0.001 @define class BaseOperationModel(BaseClass): """ Base class for turbine operation models. All turbine operation models must implement static power(), thrust_coefficient(), and axial_induction() methods, which are called by power() and thrust_coefficient() through the interface in the turbine.py module. Args: BaseClass (_type_): _description_ Raises: NotImplementedError: _description_ NotImplementedError: _description_ """ @staticmethod @abstractmethod def power() -> None: raise NotImplementedError("BaseOperationModel.power") @staticmethod @abstractmethod def thrust_coefficient() -> None: raise NotImplementedError("BaseOperationModel.thrust_coefficient") @staticmethod @abstractmethod def axial_induction() -> None: # TODO: Consider whether we can make a generic axial_induction method # based purely on thrust_coefficient so that we don't need to implement # axial_induciton() in individual operation models. raise NotImplementedError("BaseOperationModel.axial_induction") @define class SimpleTurbine(BaseOperationModel): """ Static class defining an actuator disk turbine model that is fully aligned with the flow. No handling for yaw or tilt angles. As with all turbine submodules, implements only static power() and thrust_coefficient() methods, which are called by power() and thrust_coefficient() on turbine.py, respectively. This class is not intended to be instantiated; it simply defines a library of static methods. """ def power( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): # Construct power interpolant power_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["power"], fill_value=0.0, bounds_error=False, ) # Compute the power-effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) rotor_effective_velocities = rotor_velocity_air_density_correction( velocities=rotor_average_velocities, air_density=air_density, ref_air_density=power_thrust_table["ref_air_density"] ) # Compute power power = power_interpolator(rotor_effective_velocities) * 1e3 # Convert to W return power def thrust_coefficient( power_thrust_table: dict, velocities: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): # Construct thrust coefficient interpolant thrust_coefficient_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["thrust_coefficient"], fill_value=0.0001, bounds_error=False, ) # Compute the effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) # TODO: Do we need an air density correction here? thrust_coefficient = thrust_coefficient_interpolator(rotor_average_velocities) thrust_coefficient = np.clip(thrust_coefficient, 0.0001, 0.9999) return thrust_coefficient def axial_induction( power_thrust_table: dict, velocities: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): thrust_coefficient = SimpleTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, average_method=average_method, cubature_weights=cubature_weights, ) return (1 - np.sqrt(1 - thrust_coefficient))/2 @define class CosineLossTurbine(BaseOperationModel): """ Static class defining an actuator disk turbine model that may be misaligned with the flow. Nonzero tilt and yaw angles are handled via cosine relationships, with the power lost to yawing defined by the cosine of the yaw misalignment raised to the power of cosine_loss_exponent_yaw. This turbine submodel is the default, and matches the turbine model in FLORIS v3. As with all turbine submodules, implements only static power() and thrust_coefficient() methods, which are called by power() and thrust_coefficient() on turbine.py, respectively. This class is not intended to be instantiated; it simply defines a library of static methods. """ def power( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, tilt_interp: NDArrayObject, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, correct_cp_ct_for_tilt: bool = False, **_ # <- Allows other models to accept other keyword arguments ): # Construct power interpolant power_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["power"], fill_value=0.0, bounds_error=False, ) # Compute the power-effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) rotor_effective_velocities = rotor_velocity_air_density_correction( velocities=rotor_average_velocities, air_density=air_density, ref_air_density=power_thrust_table["ref_air_density"] ) rotor_effective_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=power_thrust_table["cosine_loss_exponent_yaw"], yaw_angles=yaw_angles, rotor_effective_velocities=rotor_effective_velocities, ) rotor_effective_velocities = rotor_velocity_tilt_cosine_correction( tilt_angles=tilt_angles, ref_tilt=power_thrust_table["ref_tilt"], cosine_loss_exponent_tilt=power_thrust_table["cosine_loss_exponent_tilt"], tilt_interp=tilt_interp, correct_cp_ct_for_tilt=correct_cp_ct_for_tilt, rotor_effective_velocities=rotor_effective_velocities, ) # Compute power power = power_interpolator(rotor_effective_velocities) * 1e3 # Convert to W return power def thrust_coefficient( power_thrust_table: dict, velocities: NDArrayFloat, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, tilt_interp: NDArrayObject, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, correct_cp_ct_for_tilt: bool = False, **_ # <- Allows other models to accept other keyword arguments ): # Construct thrust coefficient interpolant thrust_coefficient_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["thrust_coefficient"], fill_value=0.0001, bounds_error=False, ) # Compute the effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) # TODO: Do we need an air density correction here? thrust_coefficient = thrust_coefficient_interpolator(rotor_average_velocities) thrust_coefficient = np.clip(thrust_coefficient, 0.0001, 0.9999) # Apply tilt and yaw corrections # Compute the tilt, if using floating turbines old_tilt_angles = copy.deepcopy(tilt_angles) tilt_angles = compute_tilt_angles_for_floating_turbines( tilt_angles=tilt_angles, tilt_interp=tilt_interp, rotor_effective_velocities=rotor_average_velocities, ) # Only update tilt angle if requested (if the tilt isn't accounted for in the Ct curve) tilt_angles = np.where(correct_cp_ct_for_tilt, tilt_angles, old_tilt_angles) thrust_coefficient = ( thrust_coefficient * cosd(yaw_angles) * cosd(tilt_angles) / cosd(power_thrust_table["ref_tilt"]) ) return thrust_coefficient def axial_induction( power_thrust_table: dict, velocities: NDArrayFloat, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, tilt_interp: NDArrayObject, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, correct_cp_ct_for_tilt: bool = False, **_ # <- Allows other models to accept other keyword arguments ): thrust_coefficient = CosineLossTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, yaw_angles=yaw_angles, tilt_angles=tilt_angles, tilt_interp=tilt_interp, average_method=average_method, cubature_weights=cubature_weights, correct_cp_ct_for_tilt=correct_cp_ct_for_tilt ) misalignment_loss = ( cosd(yaw_angles) * cosd(tilt_angles) / cosd(power_thrust_table["ref_tilt"]) ) return 0.5 / misalignment_loss * (1 - np.sqrt(1 - thrust_coefficient * misalignment_loss)) @define class SimpleDeratingTurbine(BaseOperationModel): """ power_thrust_table is a dictionary (normally defined on the turbine input yaml) that contains the parameters necessary to evaluate power(), thrust(), and axial_induction(). Any specific parameters for derating can be placed here. (they can be added to the turbine yaml). For this operation model to receive those arguements, they'll need to be added to the kwargs dictionaries in the respective functions on turbine.py. They won't affect the other operation models. """ def power( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, power_setpoints: NDArrayFloat | None, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): base_powers = SimpleTurbine.power( power_thrust_table=power_thrust_table, velocities=velocities, air_density=air_density, average_method=average_method, cubature_weights=cubature_weights ) if power_setpoints is None: return base_powers else: return np.minimum(base_powers, power_setpoints) # TODO: would we like special handling of zero power setpoints # (mixed with non-zero values) to speed up computation in that case? def thrust_coefficient( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, power_setpoints: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): base_thrust_coefficients = SimpleTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, average_method=average_method, cubature_weights=cubature_weights ) if power_setpoints is None: return base_thrust_coefficients else: # Assume thrust coefficient scales directly with power base_powers = SimpleTurbine.power( power_thrust_table=power_thrust_table, velocities=velocities, air_density=air_density ) power_fractions = power_setpoints / base_powers thrust_coefficients = power_fractions * base_thrust_coefficients return np.minimum(base_thrust_coefficients, thrust_coefficients) def axial_induction( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, power_setpoints: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): thrust_coefficient = SimpleDeratingTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, air_density=air_density, power_setpoints=power_setpoints, average_method=average_method, cubature_weights=cubature_weights, ) return (1 - np.sqrt(1 - thrust_coefficient))/2 @define class MixedOperationTurbine(BaseOperationModel): @staticmethod def power( yaw_angles: NDArrayFloat, power_setpoints: NDArrayFloat, **kwargs ): ( yaw_angles, power_setpoints, yaw_angles_mask, power_setpoints_mask, neither_mask ) = MixedOperationTurbine._handle_mixed_operation_setpoints( yaw_angles=yaw_angles, power_setpoints=power_setpoints ) powers = np.zeros_like(power_setpoints) powers[yaw_angles_mask] += CosineLossTurbine.power( yaw_angles=yaw_angles, **kwargs )[yaw_angles_mask] powers[power_setpoints_mask] += SimpleDeratingTurbine.power( power_setpoints=power_setpoints, **kwargs )[power_setpoints_mask] powers[neither_mask] += SimpleTurbine.power( **kwargs )[neither_mask] return powers @staticmethod def thrust_coefficient( yaw_angles: NDArrayFloat, power_setpoints: NDArrayFloat, **kwargs ): ( yaw_angles, power_setpoints, yaw_angles_mask, power_setpoints_mask, neither_mask ) = MixedOperationTurbine._handle_mixed_operation_setpoints( yaw_angles=yaw_angles, power_setpoints=power_setpoints ) thrust_coefficients = np.zeros_like(power_setpoints) thrust_coefficients[yaw_angles_mask] += CosineLossTurbine.thrust_coefficient( yaw_angles=yaw_angles, **kwargs )[yaw_angles_mask] thrust_coefficients[power_setpoints_mask] += SimpleDeratingTurbine.thrust_coefficient( power_setpoints=power_setpoints, **kwargs )[power_setpoints_mask] thrust_coefficients[neither_mask] += SimpleTurbine.thrust_coefficient( **kwargs )[neither_mask] return thrust_coefficients @staticmethod def axial_induction( yaw_angles: NDArrayFloat, power_setpoints: NDArrayFloat, **kwargs ): ( yaw_angles, power_setpoints, yaw_angles_mask, power_setpoints_mask, neither_mask ) = MixedOperationTurbine._handle_mixed_operation_setpoints( yaw_angles=yaw_angles, power_setpoints=power_setpoints ) axial_inductions = np.zeros_like(power_setpoints) axial_inductions[yaw_angles_mask] += CosineLossTurbine.axial_induction( yaw_angles=yaw_angles, **kwargs )[yaw_angles_mask] axial_inductions[power_setpoints_mask] += SimpleDeratingTurbine.axial_induction( power_setpoints=power_setpoints, **kwargs )[power_setpoints_mask] axial_inductions[neither_mask] += SimpleTurbine.axial_induction( **kwargs )[neither_mask] return axial_inductions @staticmethod def _handle_mixed_operation_setpoints( yaw_angles: NDArrayFloat, power_setpoints: NDArrayFloat, ): """ Check for incompatible yaw angles and power setpoints and raise an error if found. Return masks and updated setpoints. """ # If any turbines are disabled, set their yaw angles to zero yaw_angles[power_setpoints <= POWER_SETPOINT_DISABLED] = 0.0 # Create masks for whether yaw angles and power setpoints are set yaw_angles_mask = yaw_angles != 0.0 power_setpoints_mask = power_setpoints < POWER_SETPOINT_DEFAULT neither_mask = np.logical_not(yaw_angles_mask) & np.logical_not(power_setpoints_mask) # Check for incompatibility and raise error if found. if (power_setpoints_mask & yaw_angles_mask).any(): raise ValueError(( "Power setpoints and yaw angles are incompatible." "If yaw_angles entry is nonzero, power_setpoints must be greater than" " or equal to {0}.".format(POWER_SETPOINT_DEFAULT) )) # Return updated setpoints as well as masks return yaw_angles, power_setpoints, yaw_angles_mask, power_setpoints_mask, neither_mask @define class AWCTurbine(BaseOperationModel): """ power_thrust_table is a dictionary (normally defined on the turbine input yaml) that contains the parameters necessary to evaluate power(), thrust(), and axial_induction(). Feel free to put any Helix tuning parameters into here (they can be added to the turbine yaml). Also, feel free to add any commanded inputs to power(), thrust_coefficient(), or axial_induction(). For this operation model to receive those arguments, they'll need to be added to the kwargs dictionaries in the respective functions on turbine.py. They won't affect the other operation models. """ def AWC_model(a, b, c, base_values, awc_amplitudes): return base_values * (1 - (b + c*base_values)*awc_amplitudes**a) def power( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, awc_modes: str, awc_amplitudes: NDArrayFloat | None, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): base_powers = SimpleTurbine.power( power_thrust_table=power_thrust_table, velocities=velocities, air_density=air_density, average_method=average_method, cubature_weights=cubature_weights ) valid_entries = ['helix', 'baseline'] if not np.all(np.isin(awc_modes, valid_entries)): raise UserWarning( 'Active wake mixing strategies other than the `helix` strategy ' 'have not yet been implemented in FLORIS. Returning baseline power.' ) # Create a copy of the base power to modify according to different AWC modes powers = base_powers.copy() helix_mask = (awc_modes == 'helix') if np.any(np.isclose(base_powers[helix_mask]/1000,np.max(power_thrust_table['power']))): raise UserWarning( 'The selected wind speed is above or near rated wind speed. ' '`AWCTurbine` operation model is not designed ' 'or verified for above-rated conditions.' ) awc_powers = AWCTurbine.AWC_model( power_thrust_table['helix_a'], power_thrust_table['helix_power_b'], power_thrust_table['helix_power_c'], base_powers[helix_mask], awc_amplitudes[helix_mask] ) powers[helix_mask] = awc_powers return powers def thrust_coefficient( power_thrust_table: dict, velocities: NDArrayFloat, awc_modes: str, awc_amplitudes: NDArrayFloat | None, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): base_thrust_coefficients = SimpleTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, average_method=average_method, cubature_weights=cubature_weights ) # Create a copy of the base thrust coefficients to modify according to different AWC modes thrust_coefficients = base_thrust_coefficients.copy() helix_mask = (awc_modes == 'helix') awc_thrust_coefficients = AWCTurbine.AWC_model( power_thrust_table['helix_a'], power_thrust_table['helix_thrust_b'], power_thrust_table['helix_thrust_c'], base_thrust_coefficients[helix_mask], awc_amplitudes[helix_mask] ) thrust_coefficients[helix_mask] = awc_thrust_coefficients return thrust_coefficients def axial_induction( power_thrust_table: dict, velocities: NDArrayFloat, awc_modes: str, awc_amplitudes: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): thrust_coefficient = AWCTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, awc_modes=awc_modes, awc_amplitudes=awc_amplitudes, average_method=average_method, cubature_weights=cubature_weights, ) return (1 - np.sqrt(1 - thrust_coefficient))/2 @define class PeakShavingTurbine(): def power( power_thrust_table: dict, velocities: NDArrayFloat, turbulence_intensities: NDArrayFloat, air_density: float, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): base_powers = SimpleTurbine.power( power_thrust_table=power_thrust_table, velocities=velocities, air_density=air_density, average_method=average_method, cubature_weights=cubature_weights ) # Get fraction by thrust base_thrust_coefficients = SimpleTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, average_method=average_method, cubature_weights=cubature_weights ) peak_shaving_thrust_coefficients = PeakShavingTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, turbulence_intensities=turbulence_intensities, average_method=average_method, cubature_weights=cubature_weights ) # Compute equivalent axial inductions base_ais = (1 - np.sqrt(1 - base_thrust_coefficients))/2 peak_shaving_ais = (1 - np.sqrt(1 - peak_shaving_thrust_coefficients))/2 # Power proportion power_fractions = ( (peak_shaving_thrust_coefficients * (1-peak_shaving_ais)) / (base_thrust_coefficients * (1-base_ais)) ) # Apply fraction to power and return powers = power_fractions * base_powers return powers def thrust_coefficient( power_thrust_table: dict, velocities: NDArrayFloat, turbulence_intensities: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): base_thrust_coefficients = SimpleTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, average_method=average_method, cubature_weights=cubature_weights ) peak_normal_thrust_prime = np.max( np.array(power_thrust_table["wind_speed"])**2 * np.array(power_thrust_table["thrust_coefficient"]) ) rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) # Replace zeros with small values to avoid division by zero rotor_average_velocities = np.maximum(rotor_average_velocities, 0.01) max_allowable_thrust_coefficient = ( (1-power_thrust_table["peak_shaving_fraction"]) * peak_normal_thrust_prime / rotor_average_velocities**2 ) # Apply TI mask max_allowable_thrust_coefficient = np.where( ( turbulence_intensities.mean( axis=tuple([2 + i for i in range(turbulence_intensities.ndim - 2)]) ) >= power_thrust_table["peak_shaving_TI_threshold"] ), max_allowable_thrust_coefficient, base_thrust_coefficients ) thrust_coefficient = np.minimum(base_thrust_coefficients, max_allowable_thrust_coefficient) return thrust_coefficient def axial_induction( power_thrust_table: dict, velocities: NDArrayFloat, turbulence_intensities: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **_ # <- Allows other models to accept other keyword arguments ): thrust_coefficient = PeakShavingTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=velocities, turbulence_intensities=turbulence_intensities, average_method=average_method, cubature_weights=cubature_weights, ) return (1 - np.sqrt(1 - thrust_coefficient))/2 ================================================ FILE: floris/core/turbine/turbine.py ================================================ import copy import logging import os from collections.abc import Callable, Iterable from pathlib import Path import attrs import numpy as np import pandas as pd from attrs import define, field from scipy.interpolate import interp1d from floris.core import BaseClass from floris.core.turbine import ( AWCTurbine, ControllerDependentTurbine, CosineLossTurbine, MixedOperationTurbine, PeakShavingTurbine, SimpleDeratingTurbine, SimpleTurbine, UnifiedMomentumModelTurbine, ) from floris.type_dec import ( convert_to_path, floris_numeric_dict_converter, NDArrayBool, NDArrayFilter, NDArrayFloat, NDArrayInt, NDArrayObject, NDArrayStr, ) from floris.utilities import cosd TURBINE_MODEL_MAP = { "operation_model": { "simple": SimpleTurbine, "cosine-loss": CosineLossTurbine, "controller-dependent": ControllerDependentTurbine, "simple-derating": SimpleDeratingTurbine, "mixed": MixedOperationTurbine, "awc": AWCTurbine, "peak-shaving": PeakShavingTurbine, "unified-momentum": UnifiedMomentumModelTurbine, }, } def _select_multidim_condition( condition: dict, specified_conditions: Iterable[tuple], condition_keys: list[str], n_findex: int, ) -> tuple: """ Convert condition to the type expected by power_thrust_table and select nearest specified condition """ if type(condition) is dict: # Check valid keys if set(condition.keys()) != set(condition_keys): raise ValueError( f"The provided condition keys {list(condition.keys())} do not match the " f"expected keys {condition_keys}. A single value should be provided for " "each dimension of the multidimensional power/thrust_coefficient table." ) # Create a tuple of the condition values in the correct order if isinstance(condition[condition_keys[0]], list) or isinstance( condition[condition_keys[0]], np.ndarray ): # Assume multiple specified conditions n_conds = len(condition[condition_keys[0]]) if n_conds != n_findex: raise ValueError( "When providing multiple specified conditions, the number of conditions " "must match the number of findices." ) for k in condition_keys: if len(condition[k]) != n_conds: raise ValueError( "All condition values must have the same length when providing " "multiple specified conditions." ) condition = [tuple(condition[k][i] for k in condition_keys) for i in range(n_conds)] else: n_conds = 1 condition = [tuple(condition[k] for k in condition_keys)] elif condition is None: raise ValueError( "multidim_condition must be provided if using multidimensional " "power/thrust_coefficient." ) else: raise TypeError("condition should be of type dict.") # Find the nearest key to the specified conditions. specified_conditions = np.array(specified_conditions) if specified_conditions.ndim == 1: # Single specified condition specified_conditions = specified_conditions.reshape(-1, 1) # Find the nearest key to the specified conditions. nearest_conditions = np.zeros((n_conds, specified_conditions.shape[1])) for f, cond in enumerate(condition): # Loop over findices for i, c in enumerate(cond): nearest_conditions[f, i] = ( specified_conditions[:, i][np.absolute(specified_conditions[:, i] - c).argmin()] ) nearest_conditions, md_map = np.unique(nearest_conditions, axis=0, return_inverse=True) # Update map if only a single condition was provided if n_conds == 1: md_map = np.repeat(md_map, n_findex, axis=0) return nearest_conditions, md_map def power( velocities: NDArrayFloat, turbulence_intensities: NDArrayFloat, air_density: float, power_functions: dict[str, Callable], yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, power_setpoints: NDArrayFloat, awc_modes: NDArrayStr, awc_amplitudes: NDArrayFloat, tilt_interps: dict[str, interp1d], turbine_type_map: NDArrayObject, turbine_power_thrust_tables: dict, ix_filter: NDArrayInt | Iterable[int] | None = None, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, correct_cp_ct_for_tilt: bool = False, multidim_condition: dict | None = None, ) -> NDArrayFloat: """Power produced by a turbine adjusted for yaw and tilt. Value given in Watts. Args: velocities (NDArrayFloat[n_findex, n_turbines, n_grid, n_grid]): The velocities at a turbine. turbulence_intensities (NDArrayFloat[findex, turbines]): The turbulence intensity at each turbine. air_density (float): air density for simulation [kg/m^3] power_functions (dict[str, Callable]): A dictionary of power functions for each turbine type. Keys are the turbine type and values are the callable functions. yaw_angles (NDArrayFloat[findex, turbines]): The yaw angle for each turbine. tilt_angles (NDArrayFloat[findex, turbines]): The tilt angle for each turbine. power_setpoints: (NDArrayFloat[findex, turbines]): Maximum power setpoint for each turbine [W]. awc_modes: (NDArrayStr[findex, turbines]): awc excitation mode (currently, only "baseline" and "helix" are implemented). awc_modes: (NDArrayStr[findex, turbines]): awc excitation mode (currently, only "baseline" and "helix" are implemented). awc_amplitudes: (NDArrayFloat[findex, turbines]): awc excitation amplitude for each turbine [deg]. tilt_interps (Iterable[tuple]): The tilt interpolation functions for each turbine. turbine_type_map: (NDArrayObject[wd, ws, turbines]): The Turbine type definition for each turbine. turbine_power_thrust_tables: Reference data for the power and thrust representation ix_filter (NDArrayInt, optional): The boolean array, or integer indices to filter out before calculation. Defaults to None. average_method (str, optional): The method for averaging over turbine rotor points to determine a rotor-average wind speed. Defaults to "cubic-mean". cubature_weights (NDArrayFloat | None): Weights for cubature averaging methods. Defaults to None. multidim_condition (dict | None): The condition dictionary used to select the appropriate thrust coefficient relationship for multidimensional power/thrust tables. Defaults to None. Returns: NDArrayFloat: The power, in Watts, for each turbine after adjusting for yaw and tilt. """ # Down-select inputs if ix_filter is given if ix_filter is not None: velocities = velocities[:, ix_filter] turbulence_intensities = turbulence_intensities[:, ix_filter] yaw_angles = yaw_angles[:, ix_filter] tilt_angles = tilt_angles[:, ix_filter] power_setpoints = power_setpoints[:, ix_filter] awc_modes = awc_modes[:, ix_filter] awc_amplitudes = awc_amplitudes[:, ix_filter] turbine_type_map = turbine_type_map[:, ix_filter] if type(correct_cp_ct_for_tilt) is bool: pass else: correct_cp_ct_for_tilt = correct_cp_ct_for_tilt[:, ix_filter] # Establish the main set of keyword arguments for power() power_model_kwargs = { "power_thrust_table": None, # Will be filled below "velocities": velocities, "turbulence_intensities": turbulence_intensities, "air_density": air_density, "yaw_angles": yaw_angles, "tilt_angles": tilt_angles, "power_setpoints": power_setpoints, "awc_modes": awc_modes, "awc_amplitudes": awc_amplitudes, "tilt_interp": None, # Will be filled below "average_method": average_method, "cubature_weights": cubature_weights, "correct_cp_ct_for_tilt": correct_cp_ct_for_tilt, } # Loop over each turbine type given to get power for all turbines p = np.zeros(np.shape(velocities)[0:2]) turb_types = np.unique(turbine_type_map) for turb_type in turb_types: if "power" in turbine_power_thrust_tables[turb_type]: # Not multidimensional power_thrust_table = turbine_power_thrust_tables[turb_type] power_model_kwargs["power_thrust_table"] = power_thrust_table power_model_kwargs["tilt_interp"] = tilt_interps[turb_type] p += ( power_functions[turb_type](**power_model_kwargs) * (turbine_type_map == turb_type) ) else: # Multidimensional md_conditions, md_conditions_map = _select_multidim_condition( multidim_condition, [k for k in turbine_power_thrust_tables[turb_type].keys() if k != "condition_keys"], turbine_power_thrust_tables[turb_type]["condition_keys"], velocities.shape[0], ) # Loop over conditions and mask onto power for i, md_cond in enumerate(md_conditions): power_thrust_table = turbine_power_thrust_tables[turb_type][tuple(md_cond)] power_model_kwargs["power_thrust_table"] = power_thrust_table power_model_kwargs["tilt_interp"] = tilt_interps[turb_type] p += ( power_functions[turb_type](**power_model_kwargs) * (turbine_type_map == turb_type) * (md_conditions_map[:, None] == i) ) return p def thrust_coefficient( velocities: NDArrayFloat, turbulence_intensities: NDArrayFloat, air_density: float, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, power_setpoints: NDArrayFloat, awc_modes: NDArrayStr, awc_amplitudes: NDArrayFloat, thrust_coefficient_functions: dict[str, Callable], tilt_interps: dict[str, interp1d], correct_cp_ct_for_tilt: NDArrayBool, turbine_type_map: NDArrayObject, turbine_power_thrust_tables: dict, ix_filter: NDArrayFilter | Iterable[int] | None = None, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, multidim_condition: dict | None = None, ) -> NDArrayFloat: """Thrust coefficient of a turbine. The value is obtained from the coefficient of thrust specified by the callables specified in the thrust_coefficient_functions. Args: velocities (NDArrayFloat[findex, turbines, grid1, grid2]): The velocity field at a turbine. turbulence_intensities (NDArrayFloat[findex, turbines]): The turbulence intensity at each turbine. air_density (float): air density for simulation [kg/m^3] yaw_angles (NDArrayFloat[findex, turbines]): The yaw angle for each turbine. tilt_angles (NDArrayFloat[findex, turbines]): The tilt angle for each turbine. power_setpoints: (NDArrayFloat[findex, turbines]): Maximum power setpoint for each turbine [W]. awc_modes: (NDArrayStr[findex, turbines]): awc excitation mode (currently, only "baseline" and "helix" are implemented). awc_amplitudes: (NDArrayFloat[findex, turbines]): awc excitation amplitude for each turbine [deg]. thrust_coefficient_functions (dict): The thrust coefficient functions for each turbine. Keys are the turbine type string and values are the callable functions. tilt_interps (Iterable[tuple]): The tilt interpolation functions for each turbine. correct_cp_ct_for_tilt (NDArrayBool[findex, turbines]): Boolean for determining if the turbines Cp and Ct should be corrected for tilt. turbine_type_map: (NDArrayObject[findex, turbines]): The Turbine type definition for each turbine. ix_filter (NDArrayFilter | Iterable[int] | None, optional): The boolean array, or integer indices as an iterable of array to filter out before calculation. Defaults to None. average_method (str, optional): The method for averaging over turbine rotor points to determine a rotor-average wind speed. Defaults to "cubic-mean". cubature_weights (NDArrayFloat | None): Weights for cubature averaging methods. Defaults to None. multidim_condition (dict | None): The condition dictionary used to select the appropriate thrust coefficient relationship for multidimensional power/thrust tables. Defaults to None. Returns: NDArrayFloat: Coefficient of thrust for each requested turbine. """ # Down-select inputs if ix_filter is given if ix_filter is not None: velocities = velocities[:, ix_filter] turbulence_intensities = turbulence_intensities[:, ix_filter] yaw_angles = yaw_angles[:, ix_filter] tilt_angles = tilt_angles[:, ix_filter] power_setpoints = power_setpoints[:, ix_filter] awc_modes = awc_modes[:, ix_filter] awc_amplitudes = awc_amplitudes[:, ix_filter] turbine_type_map = turbine_type_map[:, ix_filter] if type(correct_cp_ct_for_tilt) is bool: pass else: correct_cp_ct_for_tilt = correct_cp_ct_for_tilt[:, ix_filter] # Establish the main set of keyword arguments for thrust_coefficient() thrust_model_kwargs = { "power_thrust_table": None, # Will be filled below "velocities": velocities, "turbulence_intensities": turbulence_intensities, "air_density": air_density, "yaw_angles": yaw_angles, "tilt_angles": tilt_angles, "power_setpoints": power_setpoints, "awc_modes": awc_modes, "awc_amplitudes": awc_amplitudes, "tilt_interp": None, # Will be filled below "average_method": average_method, "cubature_weights": cubature_weights, "correct_cp_ct_for_tilt": correct_cp_ct_for_tilt, } # Loop over each turbine type given to get thrust coefficient for all turbines thrust_coefficient = np.zeros(np.shape(velocities)[0:2]) turb_types = np.unique(turbine_type_map) for turb_type in turb_types: if "thrust_coefficient" in turbine_power_thrust_tables[turb_type]: # Not multidimensional power_thrust_table = turbine_power_thrust_tables[turb_type] thrust_model_kwargs["power_thrust_table"] = power_thrust_table thrust_model_kwargs["tilt_interp"] = tilt_interps[turb_type] thrust_coefficient += ( thrust_coefficient_functions[turb_type](**thrust_model_kwargs) * (turbine_type_map == turb_type) ) else: # Multidimensional md_conditions, md_conditions_map = _select_multidim_condition( multidim_condition, [k for k in turbine_power_thrust_tables[turb_type].keys() if k != "condition_keys"], turbine_power_thrust_tables[turb_type]["condition_keys"], velocities.shape[0], ) # Loop over conditions and mask onto thrust_coefficient for i, md_cond in enumerate(md_conditions): power_thrust_table = turbine_power_thrust_tables[turb_type][tuple(md_cond)] thrust_model_kwargs["power_thrust_table"] = power_thrust_table thrust_model_kwargs["tilt_interp"] = tilt_interps[turb_type] thrust_coefficient += ( thrust_coefficient_functions[turb_type](**thrust_model_kwargs) * (turbine_type_map == turb_type) * (md_conditions_map[:, None] == i) ) return thrust_coefficient def axial_induction( velocities: NDArrayFloat, turbulence_intensities: NDArrayFloat, air_density: float, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, power_setpoints: NDArrayFloat, awc_modes: NDArrayStr, awc_amplitudes: NDArrayFloat, axial_induction_functions: dict, tilt_interps: NDArrayObject, correct_cp_ct_for_tilt: NDArrayBool, turbine_type_map: NDArrayObject, turbine_power_thrust_tables: dict, ix_filter: NDArrayFilter | Iterable[int] | None = None, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, multidim_condition: dict | None = None, ) -> NDArrayFloat: """Axial induction factor of the turbine incorporating the thrust coefficient and yaw angle. Args: velocities (NDArrayFloat): The velocity field at each turbine; should be shape: (number of turbines, ngrid, ngrid), or (ngrid, ngrid) for a single turbine. turbulence_intensities (NDArrayFloat[findex, turbines]): The turbulence intensity at each turbine. air_density (float): air density for simulation [kg/m^3] yaw_angles (NDArrayFloat[findex, turbines]): The yaw angle for each turbine. tilt_angles (NDArrayFloat[findex, turbines]): The tilt angle for each turbine. power_setpoints: (NDArrayFloat[findex, turbines]): Maximum power setpoint for each turbine [W]. awc_amplitudes: (NDArrayFloat[findex, turbines]): awc excitation amplitude for each turbine [deg]. axial_induction_functions (dict): The axial induction functions for each turbine. Keys are the turbine type string and values are the callable functions. tilt_interps (Iterable[tuple]): The tilt interpolation functions for each turbine. correct_cp_ct_for_tilt (NDArrayBool[findex, turbines]): Boolean for determining if the turbines Cp and Ct should be corrected for tilt. turbine_type_map: (NDArrayObject[findex, turbines]): The Turbine type definition for each turbine. ix_filter (NDArrayFilter | Iterable[int] | None, optional): The boolean array, or integer indices (as an array or iterable) to filter out before calculation. Defaults to None. average_method (str, optional): The method for averaging over turbine rotor points to determine a rotor-average wind speed. Defaults to "cubic-mean". cubature_weights (NDArrayFloat | None): Weights for cubature averaging methods. Defaults to None. multidim_condition (dict | None): The condition dictionary used to select the appropriate thrust coefficient relationship for multidimensional power/thrust tables. Defaults to None. Returns: Union[float, NDArrayFloat]: [description] """ # Down-select inputs if ix_filter is given if ix_filter is not None: velocities = velocities[:, ix_filter] turbulence_intensities = turbulence_intensities[:, ix_filter] yaw_angles = yaw_angles[:, ix_filter] tilt_angles = tilt_angles[:, ix_filter] power_setpoints = power_setpoints[:, ix_filter] awc_modes = awc_modes[:, ix_filter] awc_amplitudes = awc_amplitudes[:, ix_filter] turbine_type_map = turbine_type_map[:, ix_filter] if type(correct_cp_ct_for_tilt) is bool: pass else: correct_cp_ct_for_tilt = correct_cp_ct_for_tilt[:, ix_filter] # Establish the main set of keyword arguments for axial_induction() axial_induction_model_kwargs = { "power_thrust_table": None, # Will be filled below "velocities": velocities, "turbulence_intensities": turbulence_intensities, "air_density": air_density, "yaw_angles": yaw_angles, "tilt_angles": tilt_angles, "power_setpoints": power_setpoints, "awc_modes": awc_modes, "awc_amplitudes": awc_amplitudes, "tilt_interp": None, # Will be filled below "average_method": average_method, "cubature_weights": cubature_weights, "correct_cp_ct_for_tilt": correct_cp_ct_for_tilt, } # Loop over each turbine type given to get axial induction for all turbines axial_induction = np.zeros(np.shape(velocities)[0:2]) turb_types = np.unique(turbine_type_map) for turb_type in turb_types: if "thrust_coefficient" in turbine_power_thrust_tables[turb_type]: # Not multidimensional power_thrust_table = turbine_power_thrust_tables[turb_type] axial_induction_model_kwargs["power_thrust_table"] = power_thrust_table axial_induction_model_kwargs["tilt_interp"] = tilt_interps[turb_type] axial_induction += ( axial_induction_functions[turb_type](**axial_induction_model_kwargs) * (turbine_type_map == turb_type) ) else: # Multidimensional md_conditions, md_conditions_map = _select_multidim_condition( multidim_condition, [k for k in turbine_power_thrust_tables[turb_type].keys() if k != "condition_keys"], turbine_power_thrust_tables[turb_type]["condition_keys"], velocities.shape[0], ) # Loop over conditions and mask onto axial_induction for i, md_cond in enumerate(md_conditions): power_thrust_table = turbine_power_thrust_tables[turb_type][tuple(md_cond)] axial_induction_model_kwargs["power_thrust_table"] = power_thrust_table axial_induction_model_kwargs["tilt_interp"] = tilt_interps[turb_type] axial_induction += ( axial_induction_functions[turb_type](**axial_induction_model_kwargs) * (turbine_type_map == turb_type) * (md_conditions_map[:, None] == i) ) return axial_induction @define class Turbine(BaseClass): """ A class containing the parameters and infrastructure to model a wind turbine's performance for a particular atmospheric condition. Args: turbine_type (str): An identifier for this type of turbine such as "NREL_5MW". rotor_diameter (float): The rotor diameter in meters. hub_height (float): The hub height in meters. TSR (float): The Tip Speed Ratio of the turbine. power_thrust_table (dict[str, float]): Contains power coefficient and thrust coefficient values at a series of wind speeds to define the turbine performance. The dictionary must have the following three keys with equal length values: { "wind_speeds": List[float], "power": List[float], "thrust": List[float], } or, contain a key "power_thrust_data_file" pointing to the power/thrust data. Optionally, power_thrust_table may include parameters for use in the turbine submodel, for example: cosine_loss_exponent_yaw (float): The cosine exponent relating the yaw misalignment angle to turbine power. cosine_loss_exponent_tilt (float): The cosine exponent relating the rotor tilt angle to turbine power. ref_air_density (float): The density at which the provided Cp and Ct curves are defined. ref_tilt (float): The implicit tilt of the turbine for which the Cp and Ct curves are defined. This is typically the nacelle tilt. correct_cp_ct_for_tilt (bool): A flag to indicate whether to correct Cp and Ct for tilt usually for a floating turbine. Optional, defaults to False. floating_tilt_table (dict[str, float]): Look up table of tilt angles at a series of wind speeds. The dictionary must have the following keys with equal length values: { "wind_speeds": List[float], "tilt": List[float], } Required if `correct_cp_ct_for_tilt = True`. Defaults to None. multi_dimensional_cp_ct (bool): Use a multidimensional power_thrust_table. Defaults to False. """ turbine_type: str = field() rotor_diameter: float = field() hub_height: float = field() TSR: float = field() power_thrust_table: dict = field(default={}) # conversion to numpy in __post_init__ operation_model: str = field(default="cosine-loss") correct_cp_ct_for_tilt: bool = field(default=False) floating_tilt_table: dict[str, NDArrayFloat] | None = field(default=None) multi_dimensional_cp_ct: bool = field(default=False) # Initialized in the post_init function rotor_radius: float = field(init=False) rotor_area: float = field(init=False) thrust_coefficient_function: Callable = field(init=False) axial_induction_function: Callable = field(init=False) power_function: Callable = field(init=False) tilt_interp: interp1d = field(init=False, default=None) power_thrust_data_file: str = field(default=None) # Only used by mutlidimensional turbines turbine_library_path: Path = field( default=Path(__file__).parents[2] / "turbine_library", converter=convert_to_path, validator=attrs.validators.instance_of(Path) ) # Not to be provided by the user condition_keys: list[str] = field(init=False, factory=list) def __attrs_post_init__(self) -> None: self._initialize_power_thrust_functions() self.__post_init__() def __post_init__(self) -> None: self._initialize_tilt_interpolation() bypass_numeric_converter = False if self.multi_dimensional_cp_ct: self._initialize_multidim_power_thrust_table() bypass_numeric_converter = True # Check for whether a cp_ct_data_file is specified, and load it if so. if "controller_dependent_turbine_parameters" in self.power_thrust_table: floris_root = Path(__file__).resolve().parents[2] file_path = ( floris_root / "turbine_library" / self.power_thrust_table["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ) npz_data = dict(np.load(file_path)) self.power_thrust_table["controller_dependent_turbine_parameters"]["cp_ct_data"] = { k: v.tolist() for k, v in npz_data.items() } bypass_numeric_converter = True # Raise warning if "demo" in the cp_ct data file name if ( self.operation_model in ["controller-dependent"] and "demo" in self.power_thrust_table["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ): self.logger.warning( "Cp/Ct data provided with FLORIS is for demonstration purposes only," " and may not accurately reflect the actual Cp/Ct surfaces of reference wind" " turbines." ) if not bypass_numeric_converter: self.power_thrust_table = floris_numeric_dict_converter(self.power_thrust_table) def _initialize_power_thrust_functions(self) -> None: turbine_function_model = TURBINE_MODEL_MAP["operation_model"][self.operation_model] self.thrust_coefficient_function = turbine_function_model.thrust_coefficient self.axial_induction_function = turbine_function_model.axial_induction self.power_function = turbine_function_model.power def _initialize_tilt_interpolation(self) -> None: # TODO: # Remove any duplicate wind speed entries # _, duplicate_filter = np.unique(self.wind_speeds, return_index=True) # self.tilt = self.tilt[duplicate_filter] # self.wind_speeds = self.wind_speeds[duplicate_filter] if self.floating_tilt_table is not None: self.floating_tilt_table = floris_numeric_dict_converter(self.floating_tilt_table) # If defined, create a tilt interpolation function for floating turbines. # fill_value currently set to apply the min or max tilt angles if outside # of the interpolation range. if self.correct_cp_ct_for_tilt: self.tilt_interp = interp1d( self.floating_tilt_table["wind_speed"], self.floating_tilt_table["tilt"], fill_value=(0.0, self.floating_tilt_table["tilt"][-1]), bounds_error=False, ) def _initialize_multidim_power_thrust_table(self): # Collect reference information power_thrust_table_ref = copy.deepcopy(self.power_thrust_table) self.power_thrust_data_file = power_thrust_table_ref.pop("power_thrust_data_file") # Solidify the data file path and name self.power_thrust_data_file = self.turbine_library_path / self.power_thrust_data_file # Read in the multi-dimensional data supplied by the user. df = pd.read_csv(self.power_thrust_data_file) # Down-select the DataFrame to have just the ws, Cp, and Ct values index_col = df.columns.values[:-3] self.condition_keys = index_col.tolist() df2 = df.set_index(index_col.tolist()) # Loop over the multi-dimensional keys to get the correct ws/Cp/Ct data to make # the thrust_coefficient and power interpolants. power_thrust_table_ = {} # Reset for key in df2.index.unique(): # Select the correct ws/Cp/Ct data data = df2.loc[key] if type(key) is not tuple: key = (key,) # Build the interpolants power_thrust_table_.update( { key: { "wind_speed": data['ws'].values, "power": data['power'].values, "thrust_coefficient": data['thrust_coefficient'].values, **power_thrust_table_ref }, } ) # Add reference information at the lower level # Save names of dimensions and set on-object version power_thrust_table_.update({"condition_keys": self.condition_keys}) self.power_thrust_table = power_thrust_table_ @power_thrust_table.validator def check_power_thrust_table(self, instance: attrs.Attribute, value: dict) -> None: """ Verify that the power and thrust tables are given with arrays of equal length to the wind speed array. """ if self.multi_dimensional_cp_ct: if "power_thrust_data_file" in value.keys(): return None else: key_types = [type(k) for k in value.keys()] if key_types[0] in (tuple, float, int): value = list(value.values())[0] # Check the first entry of multidim else: raise ValueError( "power_thrust_data_file must be defined if multi_dimensional_cp_ct is True." ) if not {"wind_speed", "power", "thrust_coefficient"} <= set(value.keys()): raise ValueError( """ power_thrust_table dictionary must contain: { "wind_speed": List[float], "power": List[float], "thrust_coefficient": List[float], } """ ) @rotor_diameter.validator def reset_rotor_diameter_dependencies(self, instance: attrs.Attribute, value: float) -> None: """Resets the `rotor_radius` and `rotor_area` attributes.""" # Temporarily turn off validators to avoid infinite recursion with attrs.validators.disabled(): # Reset the values self.rotor_radius = value / 2.0 self.rotor_area = np.pi * self.rotor_radius ** 2.0 @rotor_radius.validator def reset_rotor_radius(self, instance: attrs.Attribute, value: float) -> None: """ Resets the `rotor_diameter` value to trigger the recalculation of `rotor_diameter`, `rotor_radius` and `rotor_area`. """ self.rotor_diameter = value * 2.0 @rotor_area.validator def reset_rotor_area(self, instance: attrs.Attribute, value: float) -> None: """ Resets the `rotor_radius` value to trigger the recalculation of `rotor_diameter`, `rotor_radius` and `rotor_area`. """ self.rotor_radius = (value / np.pi) ** 0.5 @floating_tilt_table.validator def check_floating_tilt_table(self, instance: attrs.Attribute, value: dict | None) -> None: """ If the tilt / wind_speed table is defined, verify that the tilt and wind_speed arrays are the same length. """ if value is None: return if len(value.keys()) != 2 or set(value.keys()) != {"wind_speed", "tilt"}: raise ValueError( """ floating_tilt_table dictionary must have the form: { "wind_speed": List[float], "tilt": List[float], } """ ) if any(len(np.shape(e)) > 1 for e in (value["tilt"], value["wind_speed"])): raise ValueError("tilt and wind_speed inputs must be 1-D.") if len( {len(value["tilt"]), len(value["wind_speed"])} ) > 1: raise ValueError("tilt and wind_speed inputs must be the same size.") @correct_cp_ct_for_tilt.validator def check_for_cp_ct_correct_flag_if_floating( self, instance: attrs.Attribute, value: bool ) -> None: """ Check that the boolean flag exists for correcting Cp/Ct for tilt if a tile/wind_speed table is also defined. """ if self.correct_cp_ct_for_tilt and self.floating_tilt_table is None: raise ValueError( "To enable the Cp and Ct tilt correction, a tilt table must be given." ) ================================================ FILE: floris/core/turbine/unified_momentum_model.py ================================================ from dataclasses import dataclass from typing import ( Any, Callable, List, Optional, Protocol, Tuple, Union, ) import numpy as np from numpy.typing import ArrayLike from scipy.interpolate import interp1d from floris.core.rotor_velocity import ( average_velocity, rotor_velocity_air_density_correction, ) from floris.core.turbine.operation_models import BaseOperationModel from floris.type_dec import NDArrayFloat ## Turbine operation model functions # These are called by FLORIS through the UnifiedMomentumModelTurbine class to ultimately compute # the power, thrust coefficient, and axial induction of the turbine. def UMM_rotor_axial_induction(Cts: NDArrayFloat, yaw_angles: NDArrayFloat)-> NDArrayFloat: """ Computes the axial induction of a yawed rotor given the yaw-aligned thrust coefficient and yaw angles using the yawed actuator disk model developed at MIT as described in Heck et al. 2023. Assumes the modified thrust coefficient, C_T', is invariant to yaw misalignment angle. Uses form of C_T' from Eq. (19) of Calaf et al., 2010, https://doi.org/10.1063/1.3291077 Args Cts (NDArrayFloat): Yaw-aligned thrust coefficient(s). yaw_angles (NDArrayFloat): Rotor yaw angle(s) in degrees. Returns: NDArrayFloat: Axial induction factor(s) of the yawed rotor. """ ai_yawaligned = 0.5*(1 - np.sqrt(1 - Cts)) # Actuator disc theory Ctprime = Cts / (1-ai_yawaligned)**2 # Eq. (19) of Calaf et al., 2010 sol = Heck()(Ctprime, np.deg2rad(yaw_angles)) return sol.an def UMM_rotor_velocity_yaw_correction( Cts: NDArrayFloat, yaw_angles: NDArrayFloat, axial_inductions: NDArrayFloat, rotor_effective_velocities: NDArrayFloat, ) -> NDArrayFloat: """ Computes adjusted rotor wind speeds given the yaw-aligned thrust coefficient, yaw angles, and axial induction values using the yawed actuator disk model developed at MIT as described in Heck et al. 2023. Assumes the modified thrust coefficient, C_T', is invariant to yaw misalignment angle. Args Cts (NDArrayFloat): Yaw-aligned thrust coefficient(s). yaw_angles (NDArrayFloat): Rotor yaw angle(s) in degrees. axial_induction (NDArrayFloat): Rotor axial induction(s); this should follow the MIT model yaw dependent derivation and probably gotten from `UMM_rotor_axial_induction`. rotor_effective_velocities (NDArrayFloat) rotor effective wind speed(s) at the rotor. Returns: NDArrayFloat: corrected rotor effective wind speed(s) of the yawed rotor. """ ai_yawaligned = 0.5*(1 - np.sqrt(1 - Cts)) # Actuator disc theory Ctprime = Cts / (1-ai_yawaligned)**2 # Eq. (19) of Calaf et al., 2010 u_d_yawed = (1 - axial_inductions) * np.cos(np.deg2rad(yaw_angles)) # Eq. 2.3 of Heck et al. u_d_aligned = (1 - (Ctprime)/(Ctprime + 4)) # From eq. D1 of Heck et al. # Ratio of yaw-adjusted rotor wind speeds to yaw-aligned rotor wind speeds ratio = u_d_yawed / u_d_aligned return ratio * rotor_effective_velocities ## Iterative solver functions class FixedPointIterationCompatible(Protocol): def residual(self, *args, **kwargs) -> Tuple[ArrayLike]: ... def initial_guess(self, *args, **kwargs) -> Tuple[ArrayLike]: ... @dataclass class FixedPointIterationResult: converged: bool niter: int relax: float max_resid: float x: ArrayLike def _fixedpointiteration( f: Callable[[ArrayLike, Any], np.ndarray], x0: np.ndarray, args=(), kwargs={}, eps=0.00001, maxiter=100, relax=0, callback=None, ) -> FixedPointIterationResult: """ Performs fixed-point iteration on function f until residuals converge or max iterations is reached. Args: f (Callable): residual function of form f(x, *args, **kwargs) -> np.ndarray x0 (np.ndarray): Initial guess args (tuple): arguments to pass to residual function. Defaults to (). kwargs (dict): keyword arguments to pass to residual function. Defaults to {}. eps (float): Convergence tolerance. Defaults to 0.000001. maxiter (int): Maximum number of iterations. Defaults to 100. relax (float): Relaxation factor between 0 and 1. Defaults to 0. callback (Callable): optional callback function at each iteration of the form f(x0) -> None Returns: FixedPointIterationResult: Solution to residual function. """ for c in range(maxiter): residuals = f(x0, *args, **kwargs) x0 = [_x0 + (1 - relax) * _r for _x0, _r in zip(x0, residuals)] max_resid = [np.nanmax(np.abs(_r)) for _r in residuals] if callback: callback(x0) if all(_r < eps for _r in max_resid): converged = True break else: converged = False if maxiter == 0: return FixedPointIterationResult(False, 0, np.nan, np.nan, x0) return FixedPointIterationResult(converged, c, relax, max_resid, x0) def fixedpointiteration( max_iter: int = 100, tolerance: float = 1e-6, relaxation: float = 0.0 ) -> FixedPointIterationCompatible: """ Class decorator which adds a __call__ method to the class which performs fixed-point iteration. Args: max_iter (int): Maximum number of iterations (default: 100) tolerance (float): Convergence criteria (default: 1e-6) relaxation (float): Relaxation factor between 0 and 1 (default: 0.0) The class must contain 2 mandatory methods and 3 optional method: mandatory: initial_guess(self, *args, **kwargs) residual(self, x, *args, **kwargs) optional: pre_process(self, *args, **kwargs) # Optional post_process(self, result:FixedPointIterationResult) # Optional callback(self, x) # Optional """ def decorator(cls: FixedPointIterationCompatible) -> Callable: def call(self, *args, **kwargs): if hasattr(self, "pre_process"): self.pre_process(*args, **kwargs) callback = self.callback if hasattr(self, "callback") else None x0 = self.initial_guess(*args, **kwargs) result = _fixedpointiteration( self.residual, x0, args=args, kwargs=kwargs, eps=tolerance, maxiter=max_iter, relax=relaxation, callback=callback, ) if hasattr(self, "post_process"): return self.post_process(result, *args, **kwargs) else: return result setattr(cls, "__call__", call) return cls return decorator def adaptivefixedpointiteration( max_iter: int = 100, tolerance: float = 1e-6, relaxations: List[float] = [0.0] ) -> Callable: """ Class decorator which adds a __call__ method to the class which performs fixed-point iteration. Same as `fixedpointiteration`, but takes a list of relaxation factors, and iterates over all of them in order until convergence is reached. """ def decorator(cls: FixedPointIterationCompatible) -> Callable: def call(self, *args, **kwargs): if hasattr(self, "pre_process"): self.pre_process(*args, **kwargs) callback = self.callback if hasattr(self, "callback") else None for relaxation in relaxations: x0 = self.initial_guess(*args, **kwargs) result = _fixedpointiteration( self.residual, x0, args=args, kwargs=kwargs, eps=tolerance, maxiter=max_iter, relax=relaxation, callback=callback, ) if result.converged: break if hasattr(self, "post_process"): return self.post_process(result, *args, **kwargs) else: return result setattr(cls, "__call__", call) return cls return decorator ## The operation model class to interface with FLORIS. # This uses the iterative solve functions above. class UnifiedMomentumModelTurbine(BaseOperationModel): """ Turbine operation model as described by Heck et al. (2023). """ def power( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, yaw_angles: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **kwargs, ) -> None: # Construct thrust coefficient interpolant thrust_coefficient_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["thrust_coefficient"], fill_value=0.0001, bounds_error=False, ) # Compute the power-effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) rotor_effective_velocities = rotor_velocity_air_density_correction( velocities=rotor_average_velocities, air_density=air_density, ref_air_density=power_thrust_table["ref_air_density"] ) thrust_coefficients = thrust_coefficient_interpolator(rotor_effective_velocities) axial_inductions = UMM_rotor_axial_induction(thrust_coefficients, yaw_angles) corrected_rotor_effective_velocities = UMM_rotor_velocity_yaw_correction( thrust_coefficients, yaw_angles, axial_inductions, rotor_effective_velocities ) # TODO: Tilt correction? # Construct power interpolant power_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["power"], fill_value=0.0, bounds_error=False, ) # Compute power power = power_interpolator(corrected_rotor_effective_velocities) * 1e3 # Convert to W return power def thrust_coefficient( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, yaw_angles: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **kwargs, ) -> None: # Construct thrust coefficient interpolant thrust_coefficient_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["thrust_coefficient"], fill_value=0.0001, bounds_error=False, ) # Compute the power-effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) rotor_effective_velocities = rotor_velocity_air_density_correction( velocities=rotor_average_velocities, air_density=air_density, ref_air_density=power_thrust_table["ref_air_density"] ) thrust_coefficients = thrust_coefficient_interpolator(rotor_effective_velocities) axial_inductions = UMM_rotor_axial_induction(thrust_coefficients, yaw_angles) corrected_rotor_effective_velocities = UMM_rotor_velocity_yaw_correction( thrust_coefficients, yaw_angles, axial_inductions, rotor_effective_velocities ) # TODO: Tilt correction? # Compute thrust coefficient yawed_thrust_coefficients = thrust_coefficient_interpolator( corrected_rotor_effective_velocities ) return yawed_thrust_coefficients def axial_induction( power_thrust_table: dict, velocities: NDArrayFloat, air_density: float, yaw_angles: NDArrayFloat, average_method: str = "cubic-mean", cubature_weights: NDArrayFloat | None = None, **kwargs, ): # Construct thrust coefficient interpolant thrust_coefficient_interpolator = interp1d( power_thrust_table["wind_speed"], power_thrust_table["thrust_coefficient"], fill_value=0.0001, bounds_error=False, ) # Compute the power-effective wind speed across the rotor rotor_average_velocities = average_velocity( velocities=velocities, method=average_method, cubature_weights=cubature_weights, ) rotor_effective_velocities = rotor_velocity_air_density_correction( velocities=rotor_average_velocities, air_density=air_density, ref_air_density=power_thrust_table["ref_air_density"] ) thrust_coefficients = thrust_coefficient_interpolator(rotor_effective_velocities) axial_inductions = UMM_rotor_axial_induction(thrust_coefficients, yaw_angles) return axial_inductions ## Below is the implementation of the model as described in the paper. @dataclass class MomentumSolution: """Stores the results of the Unified Momentum model solution.""" Ctprime: float yaw: float an: Union[float, NDArrayFloat] u4: Union[float, NDArrayFloat] v4: Union[float, NDArrayFloat] x0: Union[float, NDArrayFloat] dp: Union[float, NDArrayFloat] dp_NL: Optional[Union[float, NDArrayFloat]] = 0.0 niter: Optional[int] = 1 converged: Optional[bool] = True beta: Optional[float] = 0.0 @property def Ct(self): """Returns the thrust coefficient Ct.""" return self.Ctprime * (1 - self.an) ** 2 * np.cos(self.yaw) ** 2 @property def Cp(self): """Returns the power coefficient Cp.""" return self.Ctprime * ((1 - self.an) * np.cos(self.yaw)) ** 3 class LimitedHeck(): """ Solves the limiting case when v_4 << u_4. (Eq. 2.19, 2.20). Also takes Numpy array arguments. """ def __call__(self, Ctprime: float, yaw: float, **kwargs) -> MomentumSolution: """ Args: Ctprime (float): Rotor thrust coefficient. yaw (float): Rotor yaw angle (radians). Returns: Tuple[float, float, float]: induction and outlet velocities. """ a = Ctprime * np.cos(yaw) ** 2 / (4 + Ctprime * np.cos(yaw) ** 2) u4 = (4 - Ctprime * np.cos(yaw) ** 2) / (4 + Ctprime * np.cos(yaw) ** 2) v4 = ( -(4 * Ctprime * np.sin(yaw) * np.cos(yaw) ** 2) / (4 + Ctprime * np.cos(yaw) ** 2) ** 2 ) dp = np.zeros_like(a) x0 = np.inf * np.ones_like(a) return MomentumSolution(Ctprime, yaw, a, u4, v4, x0, dp) @fixedpointiteration(max_iter=500, tolerance=0.00001, relaxation=0.1) class Heck(): """ Solves the iterative momentum equation for an actuator disk model. """ def __init__(self, v4_correction: float = 1.0): """ Initialize the HeckModel instance. Args: v4_correction (float, optional): The premultiplier of v4 in the Heck model. A correction factor applied to v4, with a default value of 1.0, indicating no correction. Lu (2023) suggests an empirical correction of 1.5. Example: >>> model = HeckModel(v4_correction=1.5) """ self.v4_correction = v4_correction def initial_guess(self, Ctprime, yaw): sol = LimitedHeck()(Ctprime, yaw) return sol.an, sol.u4, sol.v4 def residual(self, x: np.ndarray, Ctprime: float, yaw: float) -> np.ndarray: """ Residual function of yawed-actuator disk model in Eq. 2.15. Args: x (np.ndarray): (a, u4, v4) Ctprime (float): Rotor thrust coefficient. yaw (float): Rotor yaw angle (radians). Returns: np.ndarray: residuals of induction and outlet velocities. """ a, u4, v4 = x e_a = 1 - np.sqrt(1 - u4**2 - v4**2) / (np.sqrt(Ctprime) * np.cos(yaw)) - a e_u4 = (1 - 0.5 * Ctprime * (1 - a) * np.cos(yaw) ** 2) - u4 e_v4 = ( -self.v4_correction * 0.25 * Ctprime * (1 - a) ** 2 * np.sin(yaw) * np.cos(yaw) ** 2 - v4 ) return np.array([e_a, e_u4, e_v4]) def post_process(self, result, Ctprime: float, yaw: float): if result.converged: a, u4, v4 = result.x else: a, u4, v4 = np.nan * np.zeros_like([Ctprime, Ctprime, Ctprime]) dp = np.zeros_like(a) x0 = np.inf * np.ones_like(a) return MomentumSolution( Ctprime, yaw, a, u4, v4, x0, dp, niter=result.niter, converged=result.converged, ) ================================================ FILE: floris/core/wake.py ================================================ import attrs from attrs import define, field from floris.core import BaseClass, BaseModel from floris.core.wake_combination import ( FLS, MAX, SOSFS, ) from floris.core.wake_deflection import ( EmpiricalGaussVelocityDeflection, GaussVelocityDeflection, JimenezVelocityDeflection, NoneVelocityDeflection, ) from floris.core.wake_turbulence import ( CrespoHernandez, NoneWakeTurbulence, WakeInducedMixing, ) from floris.core.wake_velocity import ( CumulativeGaussCurlVelocityDeficit, EmpiricalGaussVelocityDeficit, GaussVelocityDeficit, JensenVelocityDeficit, NoneVelocityDeficit, TurboparkgaussVelocityDeficit, TurbOParkVelocityDeficit, ) MODEL_MAP = { "combination_model": { "fls": FLS, "max": MAX, "sosfs": SOSFS }, "deflection_model": { "jimenez": JimenezVelocityDeflection, "gauss": GaussVelocityDeflection, "none": NoneVelocityDeflection, "empirical_gauss": EmpiricalGaussVelocityDeflection }, "turbulence_model": { "none": NoneWakeTurbulence, "crespo_hernandez": CrespoHernandez, "wake_induced_mixing": WakeInducedMixing }, "velocity_model": { "none": NoneVelocityDeficit, "cc": CumulativeGaussCurlVelocityDeficit, "gauss": GaussVelocityDeficit, "jensen": JensenVelocityDeficit, "turbopark": TurbOParkVelocityDeficit, "empirical_gauss": EmpiricalGaussVelocityDeficit, "turboparkgauss": TurboparkgaussVelocityDeficit, }, } @define class WakeModelManager(BaseClass): """ WakeModelManager is a container class for the wake velocity, deflection, turbulence, and combination models. Args: wake (:obj:`dict`): The wake's properties input dictionary - velocity_model (str): The name of the velocity model to be instantiated. - turbulence_model (str): The name of the turbulence model to be instantiated. - deflection_model (str): The name of the deflection model to be instantiated. - combination_model (str): The name of the combination model to be instantiated. """ model_strings: dict = field(converter=dict) enable_secondary_steering: bool = field(converter=bool) enable_yaw_added_recovery: bool = field(converter=bool) enable_active_wake_mixing: bool = field(converter=bool) enable_transverse_velocities: bool = field(converter=bool) wake_deflection_parameters: dict = field(converter=dict) wake_turbulence_parameters: dict = field(converter=dict) wake_velocity_parameters: dict = field(converter=dict, factory=dict) combination_model: BaseModel = field(init=False) deflection_model: BaseModel = field(init=False) turbulence_model: BaseModel = field(init=False) velocity_model: BaseModel = field(init=False) def __attrs_post_init__(self) -> None: velocity_model_string = self.model_strings["velocity_model"].lower() model: BaseModel = MODEL_MAP["velocity_model"][velocity_model_string] if velocity_model_string == "none": model_parameters = None else: model_parameters = self.wake_velocity_parameters[velocity_model_string] if model_parameters is None: # Use model defaults self.velocity_model = model() else: self.velocity_model = model.from_dict(model_parameters) deflection_model_string = self.model_strings["deflection_model"].lower() model: BaseModel = MODEL_MAP["deflection_model"][deflection_model_string] if deflection_model_string == "none": model_parameters = None else: model_parameters = self.wake_deflection_parameters[deflection_model_string] if model_parameters is None: self.deflection_model = model() else: self.deflection_model = model.from_dict(model_parameters) turbulence_model_string = self.model_strings["turbulence_model"].lower() model: BaseModel = MODEL_MAP["turbulence_model"][turbulence_model_string] if turbulence_model_string == "none": model_parameters = None else: model_parameters = self.wake_turbulence_parameters[turbulence_model_string] if model_parameters is None: self.turbulence_model = model() else: self.turbulence_model = model.from_dict(model_parameters) combination_model_string = self.model_strings["combination_model"].lower() model: BaseModel = MODEL_MAP["combination_model"][combination_model_string] self.combination_model = model() @model_strings.validator def validate_model_strings(self, instance: attrs.Attribute, value: dict) -> None: required_strings = [ "velocity_model", "deflection_model", "combination_model", "turbulence_model" ] # Check that all required strings are given for s in required_strings: if s not in value.keys(): raise KeyError(f"Wake: '{s}' not provided in the input but it is required.") # Check that no other strings are given for k in value.keys(): if k not in required_strings: raise KeyError(( f"Wake: '{k}' was given as input but it is not a valid option." f"Required inputs are: {', '.join(required_strings)}" )) @property def deflection_function(self): return self.deflection_model.function @property def velocity_function(self): return self.velocity_model.function @property def turbulence_function(self): return self.turbulence_model.function @property def combination_function(self): return self.combination_model.function ================================================ FILE: floris/core/wake_combination/__init__.py ================================================ from floris.core.wake_combination.fls import FLS from floris.core.wake_combination.max import MAX from floris.core.wake_combination.sosfs import SOSFS ================================================ FILE: floris/core/wake_combination/fls.py ================================================ import numpy as np from attrs import define from floris.core import BaseModel @define class FLS(BaseModel): """ FLS uses freestream linear superposition to apply the wake velocity deficits to the freestream flow field. """ def prepare_function(self) -> dict: pass def function(self, wake_field: np.ndarray, velocity_field: np.ndarray): """ Combines the base flow field with the velocity deficits using freestream linear superposition. In other words, the wake field and base fields are simply added together. Args: u_field (np.array): The base flow field. u_wake (np.array): The wake to apply to the base flow field. Returns: np.array: The resulting flow field after applying the wake to the base. """ return wake_field + velocity_field ================================================ FILE: floris/core/wake_combination/max.py ================================================ import numpy as np from attrs import define from floris.core import BaseModel @define class MAX(BaseModel): """ MAX uses the maximum wake velocity deficit to add to the base flow field. For more information, refer to :cite:`max-gunn2016limitations`. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames :keyprefix: max- """ def prepare_function(self) -> dict: pass def function(self, wake_field: np.ndarray, velocity_field: np.ndarray): """ Incorporates the velocity deficits into the base flow field by selecting the maximum of the two for each point. Args: u_field (np.array): The base flow field. u_wake (np.array): The wake to apply to the base flow field. Returns: np.array: The resulting flow field after applying the wake to the base. """ return np.maximum(wake_field, velocity_field) ================================================ FILE: floris/core/wake_combination/sosfs.py ================================================ import numpy as np from attrs import define from floris.core import BaseModel @define class SOSFS(BaseModel): """ SOSFS uses sum of squares freestream superposition to combine the wake velocity deficits to the base flow field. For more information, refer to :cite:`katic_sos_1986`. """ def prepare_function(self) -> dict: pass def function(self, wake_field: np.ndarray, velocity_field: np.ndarray): """ Combines the base flow field with the velocity deficits using sum of squares. Args: u_field (np.array): The base flow field. u_wake (np.array): The wake to apply to the base flow field. Returns: np.array: The resulting flow field after applying the wake to the base. """ return np.hypot(wake_field, velocity_field) ================================================ FILE: floris/core/wake_deflection/__init__.py ================================================ from floris.core.wake_deflection.empirical_gauss import EmpiricalGaussVelocityDeflection from floris.core.wake_deflection.gauss import GaussVelocityDeflection from floris.core.wake_deflection.jimenez import JimenezVelocityDeflection from floris.core.wake_deflection.none import NoneVelocityDeflection ================================================ FILE: floris/core/wake_deflection/empirical_gauss.py ================================================ from typing import Any, Dict import numpy as np from attrs import define, field from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import cosd, sind @define class EmpiricalGaussVelocityDeflection(BaseModel): """ The Empirical Gauss deflection model is based on the form of previous the Gauss deflection model (see :cite:`bastankhah2016experimental` and :cite:`King2019Controls`) but simplifies the formulation for simpler tuning and more independence from the velocity deficit model. parameter_dictionary (dict): Model-specific parameters. Default values are used when a parameter is not included in `parameter_dictionary`. Possible key-value pairs include: - **horizontal_deflection_gain_D** (*float*): Gain for the maximum (y-direction) deflection achieved far downstream of a yawed turbine. - **vertical_deflection_gain_D** (*float*): Gain for the maximum vertical (z-direction) deflection achieved at a far downstream location due to rotor tilt. Specifying as -1 will mean that vertical deflections due to tilt match horizontal deflections due to yaw. - **deflection_rate** (*float*): Rate at which the deflected wake center approaches its maximum deflection. - **mixing_gain_deflection** (*float*): Gain to set the reduction in deflection due to wake-induced mixing. - **yaw_added_mixing_gain** (*float*): Sets the contribution of turbine yaw misalignment to the mixing in that turbine's wake (similar to yaw-added recovery). References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames """ horizontal_deflection_gain_D: float = field(default=3.0) vertical_deflection_gain_D: float = field(default=-1) deflection_rate: float = field(default=22) mixing_gain_deflection: float = field(default=0.0) yaw_added_mixing_gain: float = field(default=0.0) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "x": grid.x_sorted, } return kwargs # @profile def function( self, x_i: np.ndarray, y_i: np.ndarray, yaw_i: np.ndarray, tilt_i: np.ndarray, mixing_i: np.ndarray, ct_i: np.ndarray, rotor_diameter_i: float, *, x: np.ndarray, ): """ Calculates the deflection field of the wake. Args: x_i (np.array): Streamwise direction grid coordinates of the ith turbine (m). y_i (np.array): Cross stream direction grid coordinates of the ith turbine (m) [not used]. yaw_i (np.array): Yaw angle of the ith turbine (deg). tilt_i (np.array): Tilt angle of the ith turbine (deg). mixing_i (np.array): The wake-induced mixing term for the ith turbine. ct_i (np.array): Thrust coefficient for the ith turbine (-). rotor_diameter_i (np.array): Rotor diameter for the ith turbine (m). x (np.array): Streamwise direction grid coordinates of the flow field domain (m). Returns: np.array: Deflection field for the wake. """ # ============================================================== deflection_gain_y = self.horizontal_deflection_gain_D * rotor_diameter_i if self.vertical_deflection_gain_D == -1: deflection_gain_z = deflection_gain_y else: deflection_gain_z = self.vertical_deflection_gain_D * rotor_diameter_i # Convert to radians, CW yaw for consistency with other models yaw_r = np.pi/180 * -yaw_i tilt_r = np.pi/180 * tilt_i A_y = (deflection_gain_y * ct_i * yaw_r) / (1 + self.mixing_gain_deflection * mixing_i) A_z = (deflection_gain_z * ct_i * tilt_r) / (1 + self.mixing_gain_deflection * mixing_i) # Apply downstream mask in the process x_normalized = (x - x_i) * (x > x_i + 0.1) / rotor_diameter_i log_term = np.log( (x_normalized - self.deflection_rate) / (x_normalized + self.deflection_rate) + 2 ) deflection_y = A_y * log_term deflection_z = A_z * log_term return deflection_y, deflection_z def yaw_added_wake_mixing( axial_induction_i, yaw_angle_i, downstream_distance_D_i, yaw_added_mixing_gain ): return ( axial_induction_i[:,:,0,0] * yaw_added_mixing_gain * (1 - cosd(yaw_angle_i[:,:,0,0])) / downstream_distance_D_i**2 ) ================================================ FILE: floris/core/wake_deflection/gauss.py ================================================ from typing import Any import numexpr as ne import numpy as np from attrs import ( define, field, fields, ) from numpy import pi from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import cosd, sind NUM_EPS = fields(BaseModel).NUM_EPS.default @define class GaussVelocityDeflection(BaseModel): """ The Gauss deflection model is a blend of the models described in :cite:`gdm-bastankhah2016experimental` and :cite:`gdm-King2019Controls` for calculating the deflection field in turbine wakes. parameter_dictionary (dict): Model-specific parameters. Default values are used when a parameter is not included in `parameter_dictionary`. Possible key-value pairs include: - **ka** (*float*): Parameter used to determine the linear relationship between the turbulence intensity and the width of the Gaussian wake shape. - **kb** (*float*): Parameter used to determine the linear relationship between the turbulence intensity and the width of the Gaussian wake shape. - **alpha** (*float*): Parameter that determines the dependence of the downstream boundary between the near wake and far wake region on the turbulence intensity. - **beta** (*float*): Parameter that determines the dependence of the downstream boundary between the near wake and far wake region on the turbine's induction factor. - **ad** (*float*): Additional tuning parameter to modify the wake deflection with a lateral offset. Defaults to 0. - **bd** (*float*): Additional tuning parameter to modify the wake deflection with a lateral offset. Defaults to 0. - **dm** (*float*): Additional tuning parameter to scale the amount of wake deflection. Defaults to 1.0 - **use_secondary_steering** (*bool*): Flag to use secondary steering on the wake velocity using methods developed in [2]. - **eps_gain** (*float*): Tuning value for calculating the V- and W-component velocities using methods developed in [7]. TODO: Believe this should be removed, need to verify. See property on super-class for more details. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames :keyprefix: gdm- """ ad: float = field(converter=float, default=0.0) bd: float = field(converter=float, default=0.0) alpha: float = field(converter=float, default=0.58) beta: float = field(converter=float, default=0.077) ka: float = field(converter=float, default=0.38) kb: float = field(converter=float, default=0.004) dm: float = field(converter=float, default=1.0) eps_gain: float = field(converter=float, default=0.2) use_secondary_steering: bool = field(converter=bool, default=True) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> dict[str, Any]: kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, "freestream_velocity": flow_field.u_initial_sorted, "wind_veer": flow_field.wind_veer, } return kwargs # @profile def function( self, x_i: np.ndarray, y_i: np.ndarray, yaw_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, rotor_diameter_i: float, *, x: np.ndarray, y: np.ndarray, z: np.ndarray, freestream_velocity: np.ndarray, wind_veer: float, ): """ Calculates the deflection field of the wake. See :cite:`gdm-bastankhah2016experimental` and :cite:`gdm-King2019Controls` for details on the methods used. Args: x_i (np.array): x-coordinates of turbine i. y_i (np.array): y-coordinates of turbine i. yaw_i (np.array): Yaw angle of turbine i. turbulence_intensity_i (np.array): Turbulence intensity at turbine i. ct_i (np.array): Thrust coefficient of turbine i. rotor_diameter_i (float): Rotor diameter of turbine i. Returns: np.array: Deflection field for the wake. """ # ============================================================== # Opposite sign convention in this model yaw_i *= -1 # TODO: connect support for tilt tilt = 0.0 # turbine.tilt_angle # initial velocity deficits uR = ( freestream_velocity * ct_i * cosd(tilt) * cosd(yaw_i) / (2.0 * (1 - np.sqrt(1 - (ct_i * cosd(tilt) * cosd(yaw_i))))) ) u0 = freestream_velocity * np.sqrt(1 - ct_i) # length of near wake x0 = ( rotor_diameter_i * (cosd(yaw_i) * (1 + np.sqrt(1 - ct_i * cosd(yaw_i)))) / (np.sqrt(2) * ( 4 * self.alpha * turbulence_intensity_i + 2 * self.beta * (1 - np.sqrt(1 - ct_i)) )) + x_i ) # wake expansion parameters ky = self.ka * turbulence_intensity_i + self.kb kz = self.ka * turbulence_intensity_i + self.kb C0 = 1 - u0 / freestream_velocity M0 = C0 * (2 - C0) E0 = ne.evaluate("C0 ** 2 - 3 * exp(1.0 / 12.0) * C0 + 3 * exp(1.0 / 3.0)") # initial Gaussian wake expansion sigma_z0 = ne.evaluate("rotor_diameter_i * 0.5 * sqrt(uR / (freestream_velocity + u0))") sigma_y0 = sigma_z0 * cosd(yaw_i) * cosd(wind_veer) # yR = y - y_i xR = x_i # yR * tand(yaw) + x_i # yaw parameters (skew angle and distance from centerline) # skew angle in radians theta_c0 = self.dm * (0.3 * np.radians(yaw_i) / cosd(yaw_i)) theta_c0 *= (1 - np.sqrt(1 - ct_i * cosd(yaw_i))) delta0 = np.tan(theta_c0) * (x0 - x_i) # initial wake deflection; # NOTE: use np.tan here since theta_c0 is radians # deflection in the near wake delta_near_wake = ((x - xR) / (x0 - xR)) * delta0 + (self.ad + self.bd * (x - x_i)) delta_near_wake *= (x >= xR) & (x <= x0) # deflection in the far wake sigma_y = ky * (x - x0) + sigma_y0 sigma_z = kz * (x - x0) + sigma_z0 sigma_y = sigma_y * (x >= x0) + sigma_y0 * (x < x0) sigma_z = sigma_z * (x >= x0) + sigma_z0 * (x < x0) M0_sqrt = np.sqrt(M0) middle_term = np.sqrt(sigma_y * sigma_z / (sigma_y0 * sigma_z0)) ln_deltaNum = (1.6 + M0_sqrt) * (1.6 * middle_term - M0_sqrt) ln_deltaDen = (1.6 - M0_sqrt) * (1.6 * middle_term + M0_sqrt) middle_term = ne.evaluate( "theta_c0" " * E0" " / 5.2" " * sqrt(sigma_y0 * sigma_z0 / (ky * kz * M0))" " * log(ln_deltaNum / ln_deltaDen)" ) delta_far_wake = delta0 + middle_term + (self.ad + self.bd * (x - x_i)) delta_far_wake = delta_far_wake * (x > x0) deflection = delta_near_wake + delta_far_wake return deflection ## GCH components def gamma( D, velocity, Uinf, Ct, scale=1.0, ): """ Vortex circulation strength. Units of XXX TODO Args: D (float): Rotor diameter of the current turbine velocity (np.array(float)): Velocities at the current turbine Uinf (float): Free-stream velocity Ct (float): Thrust coefficient at the current turbine Returns: [type]: [description] """ # NOTE the cos commented below is included in Ct return scale * (pi / 8) * D * velocity * Uinf * Ct # * cosd(yaw) def wake_added_yaw( u_i, v_i, u_initial, delta_y, z_i, rotor_diameter, hub_height, ct_i, tip_speed_ratio, axial_induction_i, wind_shear, scale=1.0, ): """ what yaw angle would have produced that same average spanwise velocity These calculations focus around the current turbine. The formulation could remove the dimension for n-turbines, but for consistency with other similar equations it is left. However, the turbine dimension should always have length 1. """ # turbine parameters D = rotor_diameter # scalar HH = hub_height # scalar Ct = ct_i # (findex, 1, 1, 1) for the current turbine TSR = tip_speed_ratio # scalar aI = axial_induction_i # (findex, 1, 1, 1) for the current turbine avg_v = np.mean(v_i, axis=(2,3)) # (findex, 1, grid, grid) # flow parameters Uinf = np.mean(u_initial, axis=(1, 2, 3)) Uinf = Uinf[:, None, None, None] # TODO: Allow user input for eps gain eps_gain = 0.2 eps = eps_gain * D # Use set value vel_top = ((HH + D / 2) / HH) ** wind_shear * np.ones((1, 1, 1, 1)) Gamma_top = gamma( D, vel_top, Uinf, Ct, scale, ) vel_bottom = ((HH - D / 2) / HH) ** wind_shear * np.ones((1, 1, 1, 1)) Gamma_bottom = -1 * gamma( D, vel_bottom, Uinf, Ct, scale, ) turbine_average_velocity = np.cbrt(np.mean(u_i ** 3, axis=(2, 3), keepdims=True)) Gamma_wake_rotation = 0.25 * 2 * pi * D * (aI - aI ** 2) * turbine_average_velocity / TSR ### compute the spanwise and vertical velocities induced by yaw # decay = eps ** 2 / (4 * nu * delta_x / Uinf + eps ** 2) # This is the decay downstream yLocs = delta_y + NUM_EPS # top vortex # NOTE: this is the top of the grid, not the top of the rotor zT = z_i - (HH + D / 2) + NUM_EPS # distance from the top of the grid # NOTE: This is (-) in the paper, but (+) is consistent with the # Martínez-Tossas et al. (2019) source. rT_squared = ne.evaluate("yLocs ** 2 + zT ** 2") # This looks like spanwise decay; # it defines the vortex profile in the spanwise directions core_shape = ne.evaluate("1 - exp(-rT_squared / (eps ** 2))") v_top = ne.evaluate("(Gamma_top * zT) / (2 * pi * rT_squared) * core_shape") v_top = np.mean( v_top, axis=(2,3) ) # w_top = (-1 * Gamma_top * yLocs) / (2 * pi * rT) * core_shape * decay # bottom vortex zB = z_i - (HH - D / 2) + NUM_EPS rB_squared = ne.evaluate("yLocs ** 2 + zB ** 2") core_shape = ne.evaluate("1 - exp(-rB_squared / (eps ** 2))") v_bottom = ne.evaluate("(Gamma_bottom * zB) / (2 * pi * rB_squared) * core_shape") v_bottom = np.mean( v_bottom, axis=(2,3) ) # w_bottom = (-1 * Gamma_bottom * yLocs) / (2 * pi * rB) * core_shape * decay # wake rotation vortex zC = z_i - HH + NUM_EPS rC_squared = ne.evaluate("yLocs ** 2 + zC ** 2") core_shape = ne.evaluate("1 - exp(-rC_squared / (eps ** 2))") v_core = ne.evaluate("(Gamma_wake_rotation * zC) / (2 * pi * rC_squared) * core_shape") v_core = np.mean( v_core, axis=(2,3) ) # w_core = (-1 * Gamma_wake_rotation * yLocs) / (2 * pi * rC_squared) * core_shape * decay # Cap the effective yaw values between -45 and 45 degrees val = 2 * (avg_v - v_core) / (v_top + v_bottom) val = np.where(val < -1.0, -1.0, val) val = np.where(val > 1.0, 1.0, val) y = np.degrees(0.5 * np.arcsin(val)) return y[:, :, None, None] def calculate_transverse_velocity( u_i, u_initial, dudz_initial, delta_x, delta_y, z, rotor_diameter, hub_height, yaw, ct_i, tsr_i, axial_induction_i, wind_shear, scale=1.0, ): """ Calculate transverse velocity components for all downstream turbines given the vortices at the current turbine. """ # turbine parameters D = rotor_diameter HH = hub_height Ct = ct_i TSR = tsr_i aI = axial_induction_i # flow parameters Uinf = np.mean(u_initial, axis=(1, 2, 3)) Uinf = Uinf[:, None, None, None] eps_gain = 0.2 eps = eps_gain * D # Use set value vel_top = ((HH + D / 2) / HH) ** wind_shear * np.ones((1, 1, 1, 1)) Gamma_top = sind(yaw) * cosd(yaw) * gamma( D, vel_top, Uinf, Ct, scale, ) vel_bottom = ((HH - D / 2) / HH) ** wind_shear * np.ones((1, 1, 1, 1)) Gamma_bottom = -1 * sind(yaw) * cosd(yaw) * gamma( D, vel_bottom, Uinf, Ct, scale, ) turbine_average_velocity = np.cbrt(np.mean(u_i ** 3, axis=(2,3), keepdims=True)) Gamma_wake_rotation = 0.25 * 2 * pi * D * (aI - aI ** 2) * turbine_average_velocity / TSR ### compute the spanwise and vertical velocities induced by yaw # decay the vortices as they move downstream - using mixing length lmda = D / 8 kappa = 0.41 lm = kappa * z / (1 + kappa * z / lmda) nu = lm ** 2 * np.abs(dudz_initial) # This is the decay downstream decay = ne.evaluate("eps ** 2 / (4 * nu * delta_x / Uinf + eps ** 2)") yLocs = delta_y + NUM_EPS # top vortex zT = z - (HH + D / 2) + NUM_EPS # NOTE: This is (-) in the paper, but (+) is consistent with the # Martínez-Tossas et al. (2019) source. rT_squared = ne.evaluate("yLocs ** 2 + zT ** 2") # This looks like spanwise decay; # it defines the vortex profile in the spanwise directions core_shape = ne.evaluate("1 - exp(-rT_squared / (eps ** 2))") V1 = ne.evaluate("(Gamma_top * zT) / (2 * pi * rT_squared) * core_shape * decay") W1 = ne.evaluate("(-1 * Gamma_top * yLocs) / (2 * pi * rT_squared) * core_shape * decay") # bottom vortex zB = z - (HH - D / 2) + NUM_EPS rB_squared = ne.evaluate("yLocs ** 2 + zB ** 2") core_shape = ne.evaluate("1 - exp(-rB_squared / (eps ** 2))") V2 = ne.evaluate("(Gamma_bottom * zB) / (2 * pi * rB_squared) * core_shape * decay") W2 = ne.evaluate("(-1 * Gamma_bottom * yLocs) / (2 * pi * rB_squared) * core_shape * decay") # wake rotation vortex zC = z - HH + NUM_EPS rC_squared = ne.evaluate("yLocs ** 2 + zC ** 2") core_shape = ne.evaluate("1 - exp(-rC_squared / (eps ** 2))") V5 = ne.evaluate("(Gamma_wake_rotation * zC) / (2 * pi * rC_squared) * core_shape * decay") W5 = ne.evaluate( "(-1 * Gamma_wake_rotation * yLocs) / (2 * pi * rC_squared) * core_shape * decay" ) ### Boundary condition - ground mirror vortex # top vortex - ground zTb = z + (HH + D / 2) + NUM_EPS rTb_squared = ne.evaluate("yLocs ** 2 + zTb ** 2") # This looks like spanwise decay; # it defines the vortex profile in the spanwise directions core_shape = ne.evaluate("1 - exp(-rTb_squared / (eps ** 2))") V3 = ne.evaluate("(-1 * Gamma_top * zTb) / (2 * pi * rTb_squared) * core_shape * decay") W3 = ne.evaluate("(Gamma_top * yLocs) / (2 * pi * rTb_squared) * core_shape * decay") # bottom vortex - ground zBb = z + (HH - D / 2) + NUM_EPS rBb_squared = ne.evaluate("yLocs ** 2 + zBb ** 2") core_shape = ne.evaluate("1 - exp(-rBb_squared / (eps ** 2))") V4 = ne.evaluate("(-1 * Gamma_bottom * zBb) / (2 * pi * rBb_squared) * core_shape * decay") W4 = ne.evaluate("(Gamma_bottom * yLocs) / (2 * pi * rBb_squared) * core_shape * decay") # wake rotation vortex - ground effect zCb = z + HH + NUM_EPS rCb_squared = ne.evaluate("yLocs ** 2 + zCb ** 2") core_shape = ne.evaluate("1 - exp(-rCb_squared / (eps ** 2))") V6 = ne.evaluate( "(-1 * Gamma_wake_rotation * zCb) / (2 * pi * rCb_squared) * core_shape * decay" ) W6 = ne.evaluate( "(Gamma_wake_rotation * yLocs) / (2 * pi * rCb_squared) * core_shape * decay" ) # total spanwise velocity V = V1 + V2 + V3 + V4 + V5 + V6 W = W1 + W2 + W3 + W4 + W5 + W6 # No spanwise and vertical velocity upstream of the turbine ### Original v3 implementation # V[delta_x < -1] = 0.0 # Subtract by 1 to avoid numerical issues on rotation # W[delta_x < -1] = 0.0 # Subtract by 1 to avoid numerical issues on rotation # TODO Should this be <= ? Shouldn't be adding V and W on the current turbine? ### Then we changed it to this # V[delta_x < 0.0] = 0.0 # Subtract by 1 to avoid numerical issues on rotation # W[delta_x < 0.0] = 0.0 # Subtract by 1 to avoid numerical issues on rotation ### Currently, here V = np.where(delta_x >= 0.0, V, 0.0) W = np.where(delta_x >= 0.0, W, 0.0) # TODO: Why would the say W cannot be negative? W = np.where(W >= 0, W, 0.0) return V, W def yaw_added_turbulence_mixing( u_i, I_i, v_i, w_i, turb_v_i, turb_w_i ): # Since turbulence mixing is constant for the turbine, # use the left two dimensions only here and expand # before returning. Dimensions are (wd, ws). I_i = I_i[:, 0, 0, 0] average_u_i = np.cbrt(np.mean(u_i ** 3, axis=(1, 2, 3))) # Convert ambient turbulence intensity to TKE (eq 24) k = (average_u_i * I_i) ** 2 / (2 / 3) u_term = np.sqrt(2 * k) v_term = np.mean(v_i + turb_v_i, axis=(1, 2, 3)) w_term = np.mean(w_i + turb_w_i, axis=(1, 2, 3)) # Compute the new TKE (eq 23) k_total = 0.5 * (u_term ** 2 + v_term ** 2 + w_term ** 2) # Convert TKE back to TI I_total = np.sqrt((2 / 3) * k_total) / average_u_i # Remove ambient from total TI leaving only the TI due to mixing I_mixing = I_total - I_i return I_mixing[:, None, None, None] ================================================ FILE: floris/core/wake_deflection/jimenez.py ================================================ from typing import Any, Dict import numexpr as ne import numpy as np from attrs import define, field from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import cosd, sind @define class JimenezVelocityDeflection(BaseModel): """ Jiménez wake deflection model, derived from :cite:`jdm-jimenez2010application`. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames :keyprefix: jdm- """ kd: float = field(default=0.05) ad: float = field(default=0.0) bd: float = field(default=0.0) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "x": grid.x_sorted, } return kwargs # @profile def function( self, x_i: np.ndarray, y_i: np.ndarray, yaw_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, rotor_diameter_i: np.ndarray, *, x: np.ndarray, ): """ Calculates the deflection field of the wake in relation to the yaw of the turbine. This is coded as defined in [1]. Args: x_locations (np.array): streamwise locations in wake y_locations (np.array): spanwise locations in wake z_locations (np.array): vertical locations in wake (not used in Jiménez) turbine (:py:class:`floris.core.turbine.Turbine`): Turbine object coord (:py:meth:`floris.core.turbine_map.TurbineMap.coords`): Spatial coordinates of wind turbine. flow_field (:py:class:`floris.core.flow_field.FlowField`): Flow field object. Returns: deflection (np.array): Deflected wake centerline. This function calculates the deflection of the entire flow field given the yaw angle and Ct of the current turbine """ # NOTE: Its important to remember the rules of broadcasting here. # An operation between two np.arrays of different sizes involves # broadcasting. First, the rank and then the dimensions are compared. # If the ranks are different, new dimensions of size 1 are added to # the missing dimensions. Then, arrays can be combined (arithmetic) # if corresponding dimensions are either the same size or 1. # https://numpy.org/doc/stable/user/basics.broadcasting.html # Here, many dimensions are 1, but these are essentially treated # as a scalar value for that dimension. # angle of deflection xi_init = cosd(yaw_i) * sind(yaw_i) * ct_i / 2.0 """ delta_x = x - x_i # yaw displacement A = 15 * (2 * self.kd * delta_x / rotor_diameter_i + 1) ** 4.0 + xi_init ** 2.0 B = (30 * self.kd / rotor_diameter_i) B *= ( 2 * self.kd * delta_x / rotor_diameter_i + 1 ) ** 5.0 C = xi_init * rotor_diameter_i * (15 + xi_init ** 2.0) D = 30 * self.kd yYaw_init = (xi_init * A / B) - (C / D) # corrected yaw displacement with lateral offset # This has the same shape as the grid deflection = yYaw_init + self.ad + self.bd * delta_x """ # Numexpr - do not change below without corresponding changes above. kd = self.kd ad = self.ad bd = self.bd delta_x = ne.evaluate("x - x_i") A = ne.evaluate("15 * (2 * kd * delta_x / rotor_diameter_i + 1) ** 4.0 + xi_init ** 2.0") B = ne.evaluate("(30 * kd / rotor_diameter_i)") B = ne.evaluate("B * ( 2 * kd * delta_x / rotor_diameter_i + 1 ) ** 5.0") C = ne.evaluate("xi_init * rotor_diameter_i * (15 + xi_init ** 2.0)") D = ne.evaluate("30 * kd") yYaw_init = ne.evaluate("(xi_init * A / B) - (C / D)") deflection = ne.evaluate("yYaw_init + ad + bd * delta_x") return deflection ================================================ FILE: floris/core/wake_deflection/none.py ================================================ from typing import Any, Dict import numpy as np from attrs import define from floris.core import ( BaseModel, FlowField, Grid, ) @define class NoneVelocityDeflection(BaseModel): """ The None deflection model is a placeholder code that simple ignores any deflection and returns an array of zeroes. """ def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "freestream_velocity": flow_field.u_initial_sorted, } return kwargs def function( self, x_i: np.ndarray, y_i: np.ndarray, yaw_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, rotor_diameter_i: float, *, freestream_velocity: np.ndarray, ): """Skip all deflection calculations and returns zeros array.""" self.logger.info( "The wake deflection model is set to 'none'. Deflection modeling disabled." ) if np.any(np.abs(yaw_i) > 0.001): raise ValueError( "The deflection model is disabled yet not all effective yaw angles are zero. " + "To resolve this error, please ensure secondary steering is disabled in your " + "input file and ensure no nonzero yaw angles are passed to the floris object." ) return np.zeros_like(freestream_velocity) ================================================ FILE: floris/core/wake_turbulence/__init__.py ================================================ from floris.core.wake_turbulence.crespo_hernandez import CrespoHernandez from floris.core.wake_turbulence.none import NoneWakeTurbulence from floris.core.wake_turbulence.wake_induced_mixing import WakeInducedMixing ================================================ FILE: floris/core/wake_turbulence/crespo_hernandez.py ================================================ from typing import Any, Dict import numexpr as ne import numpy as np from attrs import define, field from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import cosd, sind @define class CrespoHernandez(BaseModel): """ CrespoHernandez is a wake-turbulence model that is used to compute additional variability introduced to the flow field by operation of a wind turbine. Implementation of the model follows the original formulation and limitations outlined in :cite:`cht-crespo1996turbulence`. Note: The values for default parameters provided here differ from those in :cite:`cht-crespo1996turbulence. Following their recommendations, the default parameters would instead be: - initial: -0.0325* - constant: 0.73 - ai: 0.8325 - downstream: -0.32 * The "initial" parameter is given as -0.0325 in :cite:`cht-crespo1996turbulence`, but the negative exponent is not clear in the scans of the paper found on the internet, and several subsequent paper cite the exponent as positive (0.0325). This discrepancy is noted in :cite:`zehtabiyan_rezaie_CH_2023`. Moreover, :cite:`zehtabiyan_rezaie_CH_2023` argues that positive values for this exponent are not representative of the physical phenomena occurring. For more details, see https://github.com/NREL/floris/issues/773. Nonetheless, the default value here is set to 0.1 for consistency with previous FLORIS versions. The default value may be updated in a future release. Args: parameter_dictionary (dict): Model-specific parameters. Default values are used when a parameter is not included in `parameter_dictionary`. Possible key-value pairs include: - **initial** (*float*): The exponent on the initial ambient turbulence intensity. - **constant** (*float*): The constant used to scale the wake-added turbulence intensity. - **ai** (*float*): The axial induction factor exponent used in in the calculation of wake-added turbulence. - **downstream** (*float*): The exponent applied to the distance downstream of an upstream turbine normalized by the rotor diameter used in the calculation of wake-added turbulence. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames :keyprefix: cht- """ initial: float = field(converter=float, default=0.1) constant: float = field(converter=float, default=0.9) ai: float = field(converter=float, default=0.8) downstream: float = field(converter=float, default=-0.32) def prepare_function(self) -> dict: pass def function( self, ambient_TI: float, x: np.ndarray, x_i: np.ndarray, rotor_diameter: float, axial_induction: np.ndarray, ) -> None: # Replace zeros and negatives with 1 to prevent nans/infs delta_x = x - x_i # TODO: ensure that these fudge factors are needed for different rotations upstream_mask = delta_x <= 0.1 downstream_mask = delta_x > -0.1 # Keep downstream components Set upstream to 1.0 delta_x = delta_x * downstream_mask + np.ones_like(delta_x) * upstream_mask # turbulence intensity calculation based on Crespo et. al. constant = self.constant ai = self.ai initial = self.initial downstream = self.downstream ti = ne.evaluate( "constant" " * axial_induction ** ai" " * ambient_TI ** initial" " * (delta_x / rotor_diameter) ** downstream" ) # Mask the 1 values from above with zeros return ti * downstream_mask ================================================ FILE: floris/core/wake_turbulence/none.py ================================================ from typing import Any, Dict import numpy as np from attrs import define, field from floris.core import BaseModel @define class NoneWakeTurbulence(BaseModel): """ The None wake turbulence model is a placeholder code that simple ignores any wake turbulence and just returns an array of the ambient TIs. """ def prepare_function(self) -> dict: pass def function( self, ambient_TI: float, x: np.ndarray, x_i: np.ndarray, rotor_diameter: float, axial_induction: np.ndarray, ) -> None: """Return unchanged field of turbulence intensities""" self.logger.info( "The wake-turbulence model is set to 'none'. Turbulence model disabled." ) return np.zeros_like(x) ================================================ FILE: floris/core/wake_turbulence/wake_induced_mixing.py ================================================ from typing import Any, Dict import numpy as np from attrs import define, field from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import cosd, sind @define class WakeInducedMixing(BaseModel): """ WakeInducedMixing is a model used to generalize wake-added turbulence in the Empirical Gaussian wake model. It computes the contribution of each turbine to a "wake-induced mixing" term that in turn is used in the velocity deficit and deflection models. Args: parameter_dictionary (dict): Model-specific parameters. Default values are used when a parameter is not included in `parameter_dictionary`. Possible key-value pairs include: - **atmospheric_ti_gain** (*float*): The contribution of ambient turbulent intensity to the wake-induced mixing term. Currently throws a warning if nonzero. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames """ atmospheric_ti_gain: float = field(converter=float, default=0.0) def __attrs_post_init__(self) -> None: if self.atmospheric_ti_gain != 0.0: nonzero_err_msg = \ "Running wake_induced_mixing model with mixing contributions"+\ " from the atmospheric turbulence intensity has not been"+\ " vetted. To avoid this warning, set atmospheric_ti_gain=0."+\ " in the FLORIS input yaml." self.logger.warning(nonzero_err_msg, stack_info=True) def prepare_function(self) -> dict: pass def function( self, axial_induction_i: np.ndarray, downstream_distance_D_i: np.ndarray, ) -> None: """ Calculates the contribution of turbine i to all other turbines' mixing terms. Args: axial_induction_i (np.array): Axial induction factor of the ith turbine (-). downstream_distance_D_i (np.array): The distance downstream from turbine i to all other turbines (specified in terms of multiples of turbine i's rotor diameter) (D). Returns: np.array: Components of the wake-induced mixing term due to the ith turbine. """ wake_induced_mixing = axial_induction_i[:,:,0,0] / downstream_distance_D_i**2 return wake_induced_mixing ================================================ FILE: floris/core/wake_velocity/__init__.py ================================================ from floris.core.wake_velocity.cumulative_gauss_curl import CumulativeGaussCurlVelocityDeficit from floris.core.wake_velocity.empirical_gauss import EmpiricalGaussVelocityDeficit from floris.core.wake_velocity.gauss import GaussVelocityDeficit from floris.core.wake_velocity.jensen import JensenVelocityDeficit from floris.core.wake_velocity.none import NoneVelocityDeficit from floris.core.wake_velocity.turbopark import TurbOParkVelocityDeficit from floris.core.wake_velocity.turboparkgauss import TurboparkgaussVelocityDeficit ================================================ FILE: floris/core/wake_velocity/cumulative_gauss_curl.py ================================================ from typing import Any, Dict import numpy as np from attrs import define, field from scipy.special import gamma from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import ( cosd, sind, tand, ) @define class CumulativeGaussCurlVelocityDeficit(BaseModel): """ The cumulative curl model is an implementation of the model described in :cite:`cc-bay_2022`, which itself is based on the cumulative model of :cite:`cc-bastankhah_2021`. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames :keyprefix: cc- """ a_s: float = field(default=0.179367259) b_s: float = field(default=0.0118889215) c_s1: float = field(default=0.0563691592) c_s2: float = field(default=0.13290157) a_f: float = field(default=3.11) b_f: float = field(default=-0.68) c_f: float = field(default=2.41) alpha_mod: float = field(default=1.0) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, "u_initial": flow_field.u_initial_sorted, } return kwargs def function( self, ii: int, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, u_i: np.ndarray, deflection_field: np.ndarray, yaw_i: np.ndarray, turbulence_intensity: np.ndarray, ct: np.ndarray, turbine_diameter: np.ndarray, turb_u_wake: np.ndarray, Ctmp: np.ndarray, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, x: np.ndarray, y: np.ndarray, z: np.ndarray, u_initial: np.ndarray, ) -> None: turbine_Ct = ct turbine_ti = turbulence_intensity turbine_yaw = yaw_i # TODO Should this be cbrt? This is done to match v2 turb_avg_vels = np.cbrt(np.mean(u_i ** 3, axis=(2, 3), keepdims=True)) delta_x = x - x_i sigma_n = wake_expansion( delta_x, turbine_Ct[:, ii:ii+1], turbine_ti[:, ii:ii+1], turbine_diameter[:, ii:ii+1], self.a_s, self.b_s, self.c_s1, self.c_s2, ) y_i_loc = np.mean(y_i, axis=(2, 3), keepdims=True) z_i_loc = np.mean(z_i, axis=(2, 3), keepdims=True) x_coord = np.mean(x, axis=(2, 3), keepdims=True) y_loc = y y_coord = np.mean(y, axis=(2, 3), keepdims=True) z_loc = z # np.mean(z, axis=(3,4)) z_coord = np.mean(z, axis=(2, 3), keepdims=True) sum_lbda = np.zeros_like(u_initial) for m in range(0, ii - 1): x_coord_m = x_coord[:, m:m+1] y_coord_m = y_coord[:, m:m+1] z_coord_m = z_coord[:, m:m+1] # For computing cross planes, we don't need to compute downstream # turbines from out cross plane position. if x_coord[:, m:m+1].size == 0: break delta_x_m = x - x_coord_m sigma_i = wake_expansion( delta_x_m, turbine_Ct[:, m:m+1], turbine_ti[:, m:m+1], turbine_diameter[:, m:m+1], self.a_s, self.b_s, self.c_s1, self.c_s2, ) S_i = sigma_n ** 2 + sigma_i ** 2 Y_i = (y_i_loc - y_coord_m - deflection_field) ** 2 / (2 * S_i) Z_i = (z_i_loc - z_coord_m) ** 2 / (2 * S_i) lbda = 1.0 * sigma_i ** 2 / S_i * np.exp(-Y_i) * np.exp(-Z_i) sum_lbda = sum_lbda + lbda * (Ctmp[m] / u_initial) # Vectorized version of sum_lbda calc; has issues with y_coord (needs to be # down-selected appropriately. Prelim. timings show vectorized form takes # longer than for loop.) # if ii >= 2: # S = sigma_n ** 2 + sigma_i[0:ii-1, :, :, :, :, :] ** 2 # Y = (y_i_loc - y_coord - deflection_field) ** 2 / (2 * S) # Z = (z_i_loc - z_coord) ** 2 / (2 * S) # lbda = self.alpha_mod * sigma_i[0:ii-1, :, :, :, :, :] ** 2 # lbda /= S * np.exp(-Y) * np.exp(-Z) # sum_lbda = np.sum(lbda * (Ctmp[0:ii-1, :, :, :, :, :] / u_initial), axis=0) # else: # sum_lbda = 0.0 # sigma_i[ii] = sigma_n # blondel # super gaussian # b_f = self.b_f1 * np.exp(self.b_f2 * TI) + self.b_f3 x_tilde = np.abs(delta_x) / turbine_diameter[:,ii:ii+1] r_tilde = np.sqrt( (y_loc - y_i_loc - deflection_field) ** 2 + (z_loc - z_i_loc) ** 2 ) r_tilde /= turbine_diameter[:,ii:ii+1] n = self.a_f * np.exp(self.b_f * x_tilde) + self.c_f a1 = 2 ** (2 / n - 1) a2 = 2 ** (4 / n - 2) # based on Blondel model, modified to include cumulative effects tmp = a2 - ( (n * turbine_Ct[:, ii:ii+1]) * cosd(turbine_yaw) / ( 16.0 * gamma(2 / n) * np.sign(sigma_n) * (np.abs(sigma_n) ** (4 / n)) * (1 - sum_lbda) ** 2 ) ) # for some low wind speeds, tmp can become slightly negative, which causes NANs, # so replace the slightly negative values with zeros tmp = tmp * (tmp >= 0) C = a1 - np.sqrt(tmp) C = C * (1 - sum_lbda) Ctmp[ii] = C yR = y_loc - y_i_loc xR = yR * tand(turbine_yaw) + x_i # add turbines together velDef = C * np.exp((-1 * r_tilde ** n) / (2 * sigma_n ** 2)) velDef = velDef * (x - xR >= 0.1) turb_u_wake = turb_u_wake + turb_avg_vels * velDef return (turb_u_wake, Ctmp) def wake_expansion( delta_x, ct_i, turbulence_intensity_i, rotor_diameter, a_s, b_s, c_s1, c_s2, ): # Calculate Beta (Eq 10, pp 5 of ref. [1] and table 4 of ref. [2] in docstring) beta = 0.5 * (1.0 + np.sqrt(1.0 - ct_i)) / np.sqrt(1.0 - ct_i) k = a_s * turbulence_intensity_i + b_s eps = (c_s1 * ct_i + c_s2) * np.sqrt(beta) # Calculate sigma_tilde (Eq 9, pp 5 of ref. [1] and table 4 of ref. [2] in docstring) x_tilde = np.abs(delta_x) / rotor_diameter sigma_y = k * x_tilde + eps # [added dimension to get upstream values, empty, wd, ws, x, y, z ] # return sigma_y[na, :, :, :, :, :, :] # Do this ^^ in the main function return sigma_y ================================================ FILE: floris/core/wake_velocity/empirical_gauss.py ================================================ from typing import Any, Dict import numexpr as ne import numpy as np from attrs import define, field from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.core.wake_velocity.gauss import gaussian_function from floris.type_dec import floris_float_type from floris.utilities import ( cosd, sind, tand, ) @define class EmpiricalGaussVelocityDeficit(BaseModel): """ The Empirical Gauss velocity model has a Gaussian profile (see :cite:`bastankhah2016experimental` and :cite:`King2019Controls`) throughout and expands in a (smoothed) piecewise linear fashion. parameter_dictionary (dict): Model-specific parameters. Default values are used when a parameter is not included in `parameter_dictionary`. Possible key-value pairs include: - **wake_expansion_rates** (*list*): List of expansion rates for the Gaussian wake width. Must be of length 1 or greater. - **breakpoints_D** (*list*): List of downstream locations, specified in terms of rotor diameters, where the expansion rates go into effect. Must be one element shorter than wake_expansion_rates. May be empty. - **sigma_0_D** (*float*): Initial width of the Gaussian wake at the turbine location, specified as a multiplier of the rotor diameter. - **smoothing_length_D** (*float*): Distance over which the corners in the piece-wise linear wake expansion rate are smoothed (specified as a multiplier of the rotor diameter). - **mixing_gain_deflection** (*float*): Gain to set the increase in wake expansion due to wake-induced mixing. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames """ wake_expansion_rates: list = field(factory=lambda: [0.023, 0.008]) breakpoints_D: list = field(factory=lambda: [10]) sigma_0_D: float = field(default=0.28) smoothing_length_D: float = field(default=2.0) mixing_gain_velocity: float = field(default=2.0) awc_mode: str = field(default="baseline") awc_wake_exp: float = field(default=1.2) awc_wake_denominator: float = field(default=400) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, "wind_veer": flow_field.wind_veer } return kwargs def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, axial_induction_i: np.ndarray, deflection_field_y_i: np.ndarray, deflection_field_z_i: np.ndarray, yaw_angle_i: np.ndarray, tilt_angle_i: np.ndarray, mixing_i: np.ndarray, ct_i: np.ndarray, hub_height_i: float, rotor_diameter_i: np.ndarray, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, x: np.ndarray, y: np.ndarray, z: np.ndarray, wind_veer: float ) -> None: """ Calculates the velocity deficits in the wake. Args: x_i (np.array): Streamwise direction grid coordinates of the ith turbine (m). y_i (np.array): Cross stream direction grid coordinates of the ith turbine (m). z_i (np.array): Vertical direction grid coordinates of the ith turbine (m) [not used]. axial_induction_i (np.array): Axial induction factor of the ith turbine (-) [not used]. deflection_field_y_i (np.array): Horizontal wake deflections due to the ith turbine's yaw misalignment (m). deflection_field_z_i (np.array): Vertical wake deflections due to the ith turbine's tilt angle (m). yaw_angle_i (np.array): Yaw angle of the ith turbine (deg). tilt_angle_i (np.array): Tilt angle of the ith turbine (deg). mixing_i (np.array): The wake-induced mixing term for the ith turbine. ct_i (np.array): Thrust coefficient for the ith turbine (-). hub_height_i (float): Hub height for the ith turbine (m). rotor_diameter_i (np.array): Rotor diameter for the ith turbine (m). x (np.array): Streamwise direction grid coordinates of the flow field domain (m). y (np.array): Cross stream direction grid coordinates of the flow field domain (m). z (np.array): Vertical direction grid coordinates of the flow field domain (m). wind_veer (np.array): Wind veer (deg). Returns: np.array: Velocity deficits (-). """ include_mirror_wake = True # Could add this as a user preference. # Only symmetric terms using yaw, but keep for consistency yaw_angle = -1 * yaw_angle_i # Initial wake widths sigma_y0 = self.sigma_0_D * rotor_diameter_i * cosd(yaw_angle) sigma_z0 = self.sigma_0_D * rotor_diameter_i * cosd(tilt_angle_i) # No specific near, far wakes in this model downstream_mask = (x > x_i + 0.1) upstream_mask = (x < x_i - 0.1) # Wake expansion in the lateral (y) and the vertical (z) # TODO: could compute shared components in sigma_z, sigma_y # with one function call. sigma_y = empirical_gauss_model_wake_width( x - x_i, self.wake_expansion_rates, [b * rotor_diameter_i for b in self.breakpoints_D], # .flatten()[0] sigma_y0, self.smoothing_length_D * rotor_diameter_i, self.mixing_gain_velocity * mixing_i, ) sigma_y[upstream_mask] = \ np.tile(sigma_y0, np.shape(sigma_y)[1:])[upstream_mask] sigma_z = empirical_gauss_model_wake_width( x - x_i, self.wake_expansion_rates, [b * rotor_diameter_i for b in self.breakpoints_D], # .flatten()[0] sigma_z0, self.smoothing_length_D * rotor_diameter_i, self.mixing_gain_velocity * mixing_i, ) sigma_z[upstream_mask] = \ np.tile(sigma_z0, np.shape(sigma_z)[1:])[upstream_mask] # 'Standard' wake component r, C = rCalt( wind_veer, sigma_y, sigma_z, y, y_i, deflection_field_y_i, deflection_field_z_i, z, hub_height_i, ct_i, yaw_angle, tilt_angle_i, rotor_diameter_i, sigma_y0, sigma_z0 ) # Normalize to match end of actuator disk model tube C = C / (8 * self.sigma_0_D**2 ) wake_deficit = gaussian_function(C, r, 1, np.sqrt(0.5)) if include_mirror_wake: # TODO: speed up this option by calculating various elements in # rCalt only once. # Mirror component r_mirr, C_mirr = rCalt( wind_veer, # TODO: Is veer OK with mirror wakes? sigma_y, sigma_z, y, y_i, deflection_field_y_i, deflection_field_z_i, z, -hub_height_i, # Turbine at negative hub height location ct_i, yaw_angle, tilt_angle_i, rotor_diameter_i, sigma_y0, sigma_z0 ) # Normalize to match end of actuator disk model tube C_mirr = C_mirr / (8 * self.sigma_0_D**2) # ASSUME sum-of-squares superposition for the real and mirror wakes wake_deficit = np.sqrt( wake_deficit**2 + gaussian_function(C_mirr, r_mirr, 1, np.sqrt(0.5))**2 ) velocity_deficit = wake_deficit * downstream_mask return velocity_deficit def rCalt(wind_veer, sigma_y, sigma_z, y, y_i, delta_y, delta_z, z, HH, Ct, yaw, tilt, D, sigma_y0, sigma_z0): ## Numexpr wind_veer = np.deg2rad(wind_veer) a = ne.evaluate( "cos(wind_veer) ** 2 / (2 * sigma_y ** 2) + sin(wind_veer) ** 2 / (2 * sigma_z ** 2)" ) b = ne.evaluate( "-sin(2 * wind_veer) / (4 * sigma_y ** 2) + sin(2 * wind_veer) / (4 * sigma_z ** 2)" ) c = ne.evaluate( "sin(wind_veer) ** 2 / (2 * sigma_y ** 2) + cos(wind_veer) ** 2 / (2 * sigma_z ** 2)" ) r = ne.evaluate( "a * ( (y - y_i - delta_y) ** 2) - "+\ "2 * b * (y - y_i - delta_y) * (z - HH - delta_z) + "+\ "c * ((z - HH - delta_z) ** 2)" ) d = 1 - Ct * (sigma_y0 * sigma_z0)/(sigma_y * sigma_z) * cosd(yaw) * cosd(tilt) C = ne.evaluate("1 - sqrt(d)") return r, C def sigmoid_integral(x, center=0, width=1): y = np.zeros_like(x) # TODO: Can this be made faster? above_smoothing_zone = (x-center) > width/2 y[above_smoothing_zone] = (x-center)[above_smoothing_zone] in_smoothing_zone = ((x-center) >= -width/2) & ((x-center) <= width/2) z = ((x-center)/width + 0.5)[in_smoothing_zone] if width.shape[0] > 1: # multiple turbine sizes width = np.broadcast_to(width, x.shape)[in_smoothing_zone] y[in_smoothing_zone] = (width*(z**6 - 3*z**5 + 5/2*z**4)).flatten() return y def empirical_gauss_model_wake_width( x, wake_expansion_rates, breakpoints, sigma_0, smoothing_length, mixing_final, ): assert len(wake_expansion_rates) == len(breakpoints) + 1, \ "Invalid combination of wake_expansion_rates and breakpoints." sigma = (wake_expansion_rates[0] + mixing_final) * x + sigma_0 for ib, b in enumerate(breakpoints): sigma += (wake_expansion_rates[ib+1] - wake_expansion_rates[ib]) * \ sigmoid_integral(x, center=b, width=smoothing_length) return sigma def awc_added_wake_mixing( awc_mode_i, awc_amplitude_i, awc_frequency_i, awc_wake_exp, awc_wake_denominator ): # Drop surplus (grid) dimensions awc_amplitude_i = awc_amplitude_i[:,:,0,0] awc_mode_i = awc_mode_i[:,:,0,0] # TODO: Add TI in the mix, finetune amplitude/freq effect awc_mixing_factor = np.zeros_like(awc_amplitude_i, dtype=floris_float_type) helix_mask = awc_mode_i == 'helix' awc_mixing_factor[helix_mask] = ( awc_amplitude_i[helix_mask]**awc_wake_exp/awc_wake_denominator ) return awc_mixing_factor ================================================ FILE: floris/core/wake_velocity/gauss.py ================================================ from typing import Any, Dict import numexpr as ne import numpy as np from attrs import define, field from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import ( cosd, sind, tand, ) @define class GaussVelocityDeficit(BaseModel): alpha: float = field(default=0.58) beta: float = field(default=0.077) ka: float = field(default=0.38) kb: float = field(default=0.004) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, "u_initial": flow_field.u_initial_sorted, "wind_veer": flow_field.wind_veer } return kwargs # @profile def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, axial_induction_i: np.ndarray, deflection_field_i: np.ndarray, yaw_angle_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, hub_height_i: float, rotor_diameter_i: np.ndarray, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, x: np.ndarray, y: np.ndarray, z: np.ndarray, u_initial: np.ndarray, wind_veer: float, ) -> None: # yaw_angle is all turbine yaw angles for each wind speed # Extract and broadcast only the current turbine yaw setting # for all wind speeds # Opposite sign convention in this model yaw_angle = -1 * yaw_angle_i # Initialize the velocity deficit uR = u_initial * ct_i / (2.0 * (1 - np.sqrt(1 - ct_i))) u0 = u_initial * np.sqrt(1 - ct_i) # Initial lateral bounds sigma_z0 = rotor_diameter_i * 0.5 * np.sqrt(uR / (u_initial + u0)) sigma_y0 = sigma_z0 * cosd(yaw_angle) * cosd(wind_veer) # Compute the bounds of the near and far wake regions and a mask # Start of the near wake xR = x_i # Start of the far wake x0 = np.ones_like(u_initial) x0 *= rotor_diameter_i * cosd(yaw_angle) * (1 + np.sqrt(1 - ct_i) ) x0 /= np.sqrt(2) * ( 4 * self.alpha * turbulence_intensity_i + 2 * self.beta * (1 - np.sqrt(1 - ct_i) ) ) x0 += x_i # Initialize the velocity deficit array velocity_deficit = np.zeros_like(u_initial) # Masks # When we have only an inequality, the current turbine may be applied its own # wake in cases where numerical precision cause in incorrect comparison. We've # applied a small bump to avoid this. "0.1" is arbitrary but it is a small, non # zero value. # This mask defines the near wake; keeps the areas downstream of xR and upstream of x0 near_wake_mask = (x > xR + 0.1) * (x < x0) far_wake_mask = (x >= x0) # Compute the velocity deficit in the NEAR WAKE region # ONLY If there are points within the near wake boundary # TODO: for the TurbineGrid, do we need to do this near wake calculation at all? # same question for any grid with a resolution larger than the near wake region if np.sum(near_wake_mask): # Calculate the wake expansion # This is a linear ramp from 0 to 1 from the start of the near wake to the start # of the far wake. near_wake_ramp_up = (x - xR) / (x0 - xR) # Another linear ramp, but positive upstream of the far wake and negative in the # far wake; 0 at the start of the far wake near_wake_ramp_down = (x0 - x) / (x0 - xR) # near_wake_ramp_down = -1 * (near_wake_ramp_up - 1) # : this is equivalent, right? sigma_y = near_wake_ramp_down * 0.501 * rotor_diameter_i * np.sqrt(ct_i / 2.0) sigma_y += near_wake_ramp_up * sigma_y0 sigma_y *= (x >= xR) sigma_y += np.ones_like(sigma_y) * (x < xR) * 0.5 * rotor_diameter_i sigma_z = near_wake_ramp_down * 0.501 * rotor_diameter_i * np.sqrt(ct_i / 2.0) sigma_z += near_wake_ramp_up * sigma_z0 sigma_z *= (x >= xR) sigma_z += np.ones_like(sigma_z) * (x < xR) * 0.5 * rotor_diameter_i r_squared, C = rC( wind_veer, sigma_y, sigma_z, y, y_i, deflection_field_i, z, hub_height_i, ct_i, yaw_angle, rotor_diameter_i, ) near_wake_deficit = gaussian_function(C, r_squared, 1, np.sqrt(0.5)) near_wake_deficit *= near_wake_mask velocity_deficit += near_wake_deficit # Compute the velocity deficit in the FAR WAKE region if np.sum(far_wake_mask): # Wake expansion in the lateral (y) and the vertical (z) ky = self.ka * turbulence_intensity_i + self.kb # wake expansion parameters kz = self.ka * turbulence_intensity_i + self.kb # wake expansion parameters sigma_y = (ky * (x - x0) + sigma_y0) * far_wake_mask + sigma_y0 * (x < x0) sigma_z = (kz * (x - x0) + sigma_z0) * far_wake_mask + sigma_z0 * (x < x0) r_squared, C = rC( wind_veer, sigma_y, sigma_z, y, y_i, deflection_field_i, z, hub_height_i, ct_i, yaw_angle, rotor_diameter_i, ) far_wake_deficit = gaussian_function(C, r_squared, 1, np.sqrt(0.5)) far_wake_deficit *= far_wake_mask velocity_deficit += far_wake_deficit return velocity_deficit # @profile def rC(wind_veer, sigma_y, sigma_z, y, y_i, delta, z, HH, Ct, yaw, D): ## original # a = cosd(wind_veer) ** 2 / (2 * sigma_y ** 2) + sind(wind_veer) ** 2 / (2 * sigma_z ** 2) # b = -sind(2 * wind_veer) / (4 * sigma_y ** 2) + sind(2 * wind_veer) / (4 * sigma_z ** 2) # c = sind(wind_veer) ** 2 / (2 * sigma_y ** 2) + cosd(wind_veer) ** 2 / (2 * sigma_z ** 2) # r_squared = ( # a * (y - y_i - delta) ** 2 # - 2 * b * (y - y_i - delta) * (z - HH) # + c * (z - HH) ** 2 # ) # C = 1 - np.sqrt(np.clip(1 - (Ct * cosd(yaw) / (8.0 * sigma_y * sigma_z / D ** 2)), 0.0, 1.0)) ## Precalculate some parts # twox_sigmay_2 = 2 * sigma_y ** 2 # twox_sigmaz_2 = 2 * sigma_z ** 2 # a = cosd(wind_veer) ** 2 / (twox_sigmay_2) + sind(wind_veer) ** 2 / (twox_sigmaz_2) # b = -sind(2 * wind_veer) / (2 * twox_sigmay_2) + sind(2 * wind_veer) / (2 * twox_sigmaz_2) # c = sind(wind_veer) ** 2 / (twox_sigmay_2) + cosd(wind_veer) ** 2 / (twox_sigmaz_2) # delta_y = y - y_i - delta # delta_z = z - HH # r_squared = (a * (delta_y ** 2) - 2 * b * (delta_y) * (delta_z) + c * (delta_z ** 2)) # C = 1 - np.sqrt(np.clip(1 - (Ct * cosd(yaw) / (8.0 * sigma_y * sigma_z / (D * D))), 0.0, 1.0)) ## Numexpr wind_veer = np.deg2rad(wind_veer) a = ne.evaluate( "cos(wind_veer) ** 2 / (2 * sigma_y ** 2) + sin(wind_veer) ** 2 / (2 * sigma_z ** 2)" ) b = ne.evaluate( "-sin(2 * wind_veer) / (4 * sigma_y ** 2) + sin(2 * wind_veer) / (4 * sigma_z ** 2)" ) c = ne.evaluate( "sin(wind_veer) ** 2 / (2 * sigma_y ** 2) + cos(wind_veer) ** 2 / (2 * sigma_z ** 2)" ) r_squared = ne.evaluate( "a * ((y - y_i - delta) ** 2) - 2 * b * (y - y_i - delta) * (z - HH) + c * ((z - HH) ** 2)" ) d = np.clip(1 - (Ct * cosd(yaw) / ( 8.0 * sigma_y * sigma_z / (D * D) )), 0.0, 1.0) C = ne.evaluate("1 - sqrt(d)") return r_squared, C def mask_upstream_wake(mesh_y_rotated, x_coord_rotated, y_coord_rotated, turbine_yaw): yR = mesh_y_rotated - y_coord_rotated xR = yR * tand(turbine_yaw) + x_coord_rotated return xR, yR def gaussian_function(C, r_squared, n, sigma): result = ne.evaluate("C * exp(-1 * r_squared ** n / (2 * sigma ** 2))") return result ================================================ FILE: floris/core/wake_velocity/jensen.py ================================================ from typing import Any, Dict import numexpr as ne import numpy as np from attrs import ( define, field, fields, ) from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) NUM_EPS = fields(BaseModel).NUM_EPS.default @define class JensenVelocityDeficit(BaseModel): """ The Jensen model computes the wake velocity deficit based on the classic Jensen/Park model :cite:`jvm-jensen1983note`. - **we** (*float*): The linear wake decay constant that defines the cone boundary for the wake as well as the velocity deficit. D/2 +/- we*x is the cone boundary for the wake. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames :keyprefix: jvm- """ we: float = field(converter=float, default=0.05) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: """ This function prepares the inputs from the various FLORIS data structures for use in the Jensen model. This should only be used to 'initialize' the inputs. For any data that should be updated successively, do not use this function and instead pass that data directly to the model function. """ kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, } return kwargs # @profile def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, axial_induction_i: np.ndarray, deflection_field_i: np.ndarray, yaw_angle_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, hub_height_i, rotor_diameter_i, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, x: np.ndarray, y: np.ndarray, z: np.ndarray, ) -> None: # u is 4-dimensional (n wind speeds, n turbines, grid res 1, grid res 2) # velocities is 3-dimensional (n turbines, grid res 1, grid res 2) # TODO: check the rotations with multiple directions or non-0/270 # grid.rotate_fields(flow_field.wind_directions) # Calculate and apply wake mask # x = grid.x_sorted # mesh_x_rotated - x_coord_rotated # This is the velocity deficit seen by the i'th turbine due to wake effects # from upstream turbines. # Indeces of velocity_deficit corresponding to unwaked turbines will have 0's # velocity_deficit = np.zeros(np.shape(flow_field.u_initial)) rotor_radius = rotor_diameter_i / 2.0 # Numexpr - do not change below without corresponding changes above. dx = ne.evaluate("x - x_i") dy = ne.evaluate("y - y_i - deflection_field_i") dz = ne.evaluate("z - z_i") we = self.we # Construct a boolean mask to include all points downstream of the turbine downstream_mask = ne.evaluate("dx > 0 + NUM_EPS") # Construct a boolean mask to include all points within the wake boundary # as defined by the Jensen model. This is a linear wake expansion that makes # a shape like a cone and starts at the turbine disc. # The left side of the inequality below evaluates the distance from the wake centerline # for all points including positive and negative values. The inequality compares distance # from the centerline and it must be below the line defined by the wake # expansion parameter, "we". boundary_mask = ne.evaluate("sqrt(dy ** 2 + dz ** 2) < we * dx + rotor_radius") # Calculate C for points within the mask and fill points outside with 0 c = np.where( np.logical_and(downstream_mask, boundary_mask), ne.evaluate("(rotor_radius / (rotor_radius + we * dx + NUM_EPS)) ** 2"), # This is "C" 0.0, ) velocity_deficit = ne.evaluate("2 * axial_induction_i * c") return velocity_deficit ================================================ FILE: floris/core/wake_velocity/none.py ================================================ from typing import Any, Dict import numpy as np from attrs import define, field from floris.core import ( BaseModel, FlowField, Grid, ) @define class NoneVelocityDeficit(BaseModel): """ The None deficit model is a placeholder code that simple ignores any wake wind speed deficits and returns an array of zeroes. """ def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "u_initial": flow_field.u_initial_sorted, } return kwargs def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, axial_induction_i: np.ndarray, deflection_field_i: np.ndarray, yaw_angle_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, hub_height_i: float, rotor_diameter_i: np.ndarray, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, u_initial: np.ndarray, ) -> None: self.logger.warning("The wake deficit model is set to 'none'. Wake modeling disabled.") return np.zeros_like(u_initial) ================================================ FILE: floris/core/wake_velocity/turbopark.py ================================================ from pathlib import Path from typing import Any, Dict import numpy as np import scipy.io from attrs import define, field from scipy import integrate from scipy.interpolate import RegularGridInterpolator from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.utilities import ( cosd, sind, tand, ) @define class TurbOParkVelocityDeficit(BaseModel): """ Model based on the TurbOPark model. For model details see https://github.com/OrstedRD/TurbOPark, https://github.com/OrstedRD/TurbOPark/blob/main/TurbOPark%20description.pdf, and Nygaard, Nicolai Gayle, et al. "Modelling cluster wakes and wind farm blockage." Journal of Physics: Conference Series. Vol. 1618. No. 6. IOP Publishing, 2020. """ A: float = field(default=0.04) sigma_max_rel: float = field(default=4.0) overlap_gauss_interp: RegularGridInterpolator = field(init=False) def __attrs_post_init__(self) -> None: lookup_table_matlab_file = Path(__file__).parent / "turbopark_lookup_table.mat" lookup_table_file = scipy.io.loadmat(lookup_table_matlab_file) dist = lookup_table_file['overlap_lookup_table'][0][0][0][0] radius_down = lookup_table_file['overlap_lookup_table'][0][0][1][0] overlap_gauss = lookup_table_file['overlap_lookup_table'][0][0][2] self.overlap_gauss_interp = RegularGridInterpolator( (dist, radius_down), overlap_gauss, method='linear', bounds_error=False ) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, "u_initial": flow_field.u_initial_sorted, } return kwargs # @profile def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, ambient_turbulence_intensities: np.ndarray, Cts: np.ndarray, rotor_diameter_i: np.ndarray, rotor_diameters: np.ndarray, i: int, deflection_field: np.ndarray, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, x: np.ndarray, y: np.ndarray, z: np.ndarray, u_initial: np.ndarray, ) -> None: delta_total = np.zeros_like(u_initial) # Normalized distances along x between the turbine i and all other turbines # The downstream_mask is used to avoid negative numbers in the sqrt and the # subsequent runtime warnings. # Here self.NUM_EPS is to avoid precision issues with masking, and is slightly # larger than 0.0 downstream_mask = (x_i - x >= self.NUM_EPS) x_dist = (x_i - x) * downstream_mask / rotor_diameters # Radial distance between turbine i and the center lines of wakes from all # real/image turbines r_dist = np.sqrt((y_i - (y + deflection_field)) ** 2 + (z_i - z) ** 2) r_dist_image = np.sqrt((y_i - (y + deflection_field)) ** 2 + (z_i - (-z)) ** 2) Cts[:, i:, :, :] = 0.00001 # Characteristic wake widths from all turbines relative to turbine i dw = characteristic_wake_width(x_dist, ambient_turbulence_intensities, Cts, self.A) epsilon = 0.25 * np.sqrt( np.min( 0.5 * (1 + np.sqrt(1 - Cts)) / np.sqrt(1 - Cts), 3, keepdims=True ) ) sigma = rotor_diameters * (epsilon + dw) # Peak wake deficits val = 1 - Cts / (8 * (sigma / rotor_diameters) ** 2) C = 1 - np.sqrt(val) # Compute deficit for all turbines and mask to keep upstream and overlapping turbines # NOTE self.sigma_max_rel * sigma is an effective wake width is_overlapping = (self.sigma_max_rel * sigma) / 2 + rotor_diameter_i / 2 > r_dist wtg_overlapping = (x_dist > 0) * is_overlapping delta_real = np.empty(np.shape(u_initial)) * np.nan delta_image = np.empty(np.shape(u_initial)) * np.nan # Compute deficits for real turbines and for mirrored (image) turbines delta_real = C * wtg_overlapping * self.overlap_gauss_interp( (r_dist / sigma, rotor_diameter_i / 2 / sigma) ) delta_image = C * wtg_overlapping * self.overlap_gauss_interp( (r_dist_image / sigma, rotor_diameter_i / 2 / sigma) ) delta = np.concatenate((delta_real, delta_image), axis=1) delta_total[:, i, :, :] = np.sqrt(np.sum(np.nan_to_num(delta) ** 2, axis=1)) return delta_total def precalculate_overlap(): # TODO: first implementation to generate wake overlap lookup table # (currently supplied by turbopark_lookup_table.mat.) # However, the result of this function doesn't generate the same # interpolant as the .mat file, so if used, needs to be corrected. dist = np.arange(0, 10, 1.0) radius_down = np.arange(0, 20, 1.0) overlap_gauss = np.zeros((len(dist), len(radius_down))) for i in range(len(dist)): for j in range(len(radius_down)): if radius_down[j] > 0: def fun(r, theta): return r * np.exp( -1 * (r ** 2 + dist[i] ** 2 - 2 * dist[i] * r * np.cos(theta)) / 2 ) out = integrate.dblquad(fun, 0, radius_down[j], lambda x: 0, lambda x: 2 * np.pi)[0] out = out / (np.pi * radius_down[j] ** 2) else: out = np.exp(-(dist[i] ** 2) / 2) overlap_gauss[i, j] = out return dist, radius_down, overlap_gauss def characteristic_wake_width(x_dist, TI, Cts, A): # Parameter values taken from S. T. Frandsen, “Risø-R-1188(EN) Turbulence # and turbulence generated structural loading in wind turbine clusters” # Risø, Roskilde, Denmark, 2007. c1 = 1.5 c2 = 0.8 alpha = TI * c1 beta = c2 * TI / np.sqrt(Cts) dw = A * TI / beta * ( np.sqrt((alpha + beta * x_dist) ** 2 + 1) - np.sqrt(1 + alpha ** 2) - np.log( ((np.sqrt((alpha + beta * x_dist) ** 2 + 1) + 1) * alpha) / ((np.sqrt(1 + alpha ** 2) + 1) * (alpha + beta * x_dist)) ) ) return dw ================================================ FILE: floris/core/wake_velocity/turboparkgauss.py ================================================ from typing import Any, Dict import numexpr as ne import numpy as np from attrs import define, field from floris.core import ( BaseModel, Farm, FlowField, Grid, Turbine, ) from floris.core.wake_velocity.gauss import gaussian_function from floris.utilities import ( cosd, sind, tand, ) @define class TurboparkgaussVelocityDeficit(BaseModel): """ Model based on the TurbOPark model with Gaussian wake profile. For model details see: Pedersen J G, Svensen E, Poulsen L, and Nygaard N G. "Turbulence Optimized Park model with Gaussian wake profile." Journal of Physics: Conference Series. Vol. 2265. No. 022063. IOP Publishing, 2020. doi:10.1088/1742-6596/2265/2/022063 """ A: float = field(default=0.04) include_mirror_wake: bool = field(default=True) def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, "u_initial": flow_field.u_initial_sorted, "wind_veer": flow_field.wind_veer } return kwargs # @profile def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, axial_induction_i: np.ndarray, deflection_field_i: np.ndarray, yaw_angle_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, hub_height_i: float, rotor_diameter_i: np.ndarray, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, x: np.ndarray, y: np.ndarray, z: np.ndarray, u_initial: np.ndarray, wind_veer: float, ) -> None: # Initialize the velocity deficit array velocity_deficit = np.zeros_like(u_initial) downstream_mask = (x - x_i >= self.NUM_EPS) x_dist = (x - x_i) * downstream_mask / rotor_diameter_i # Characteristic wake widths from all turbines relative to turbine i sigma = characteristic_wake_width( x_dist, turbulence_intensity_i, ct_i, self.A ) * rotor_diameter_i # Peak wake deficits C = 1 - np.sqrt(np.clip(1 - ct_i / (8 * (sigma / rotor_diameter_i) ** 2), 0.0, 1.0)) r_dist = np.sqrt((y - y_i) ** 2 + (z - z_i) ** 2) # Compute deficits for real turbines and for mirrored (image) turbines delta_real = (x_dist > 0) * gaussian_function(C, r_dist, 2, sigma) if self.include_mirror_wake: r_dist_image = np.sqrt((y - y_i) ** 2 + (z - 3*z_i) ** 2) delta_image = (x_dist > 0) * gaussian_function(C, r_dist_image, 2, sigma) delta = np.hypot(delta_real, delta_image) else: # No mirror wakes delta = delta_real velocity_deficit = np.nan_to_num(delta) return velocity_deficit def characteristic_wake_width(x_D, ambient_TI, Cts, A): # Parameter values taken from S. T. Frandsen, “Risø-R-1188(EN) Turbulence # and turbulence generated structural loading in wind turbine clusters” # Risø, Roskilde, Denmark, 2007. c1 = 1.5 c2 = 0.8 alpha = ambient_TI * c1 beta = c2 * ambient_TI / np.sqrt(Cts) # Term for the initial width at the turbine location (denoted epsilon in Pedersen et al.) # Saturate term in initial width to 3.0, as is done in Orsted Matlab code. initial_width = 0.25 * np.sqrt(np.minimum(0.5 * (1 + np.sqrt(1 - Cts)) / np.sqrt(1 - Cts), 3.0)) # Term for the added width downstream of the turbine added_width = A * ambient_TI / beta * ( np.sqrt((alpha + beta * x_D) ** 2 + 1) - np.sqrt(1 + alpha ** 2) - np.log( ((np.sqrt((alpha + beta * x_D) ** 2 + 1) + 1) * alpha) / ((np.sqrt(1 + alpha ** 2) + 1) * (alpha + beta * x_D)) ) ) sigma_w_D = initial_width + added_width return sigma_w_D ================================================ FILE: floris/cut_plane.py ================================================ import copy import matplotlib.pyplot as plt import numpy as np import pandas as pd from scipy.interpolate import griddata def nudge_outward(x): """ Avoid numerical issue in grid data by slightly expanding input x. TODO: expand this description - What numerical issues? - Whats the scenario when I would need this? Args: x (np.array): Vector of values. Returns: np.array: Expanded vector. """ nudge_val = 0.001 min_x = np.min(x) max_x = np.max(x) x = np.where(x == min_x, min_x - nudge_val, x) x = np.where(x == max_x, max_x + nudge_val, x) return x def get_plane_from_flow_data(flow_data, normal_vector="z", x3_value=100): """ Get a plane of data, in form of DataFrame, from a :py:class:`~.FlowData` object. This is used to get planes from SOWFA results and FLORIS simulations with fixed grids, i.e. curl. Args: flow_data (np.array): 3D vector field of velocity data. #TODO: is this supposed to be a :py:class:`~.FlowData` object? normal_vector (string, optional): Vector normal to plane. Defaults to z. x3_value (float, optional): Value of normal vector to slice through. Defaults to 100. Returns: pandas.DataFrame: Extracted data. """ order = "f" if normal_vector == "z": x1_array = flow_data.x.flatten(order=order) x2_array = flow_data.y.flatten(order=order) x3_array = flow_data.z.flatten(order=order) if normal_vector == "x": x3_array = flow_data.x.flatten(order=order) x1_array = flow_data.y.flatten(order=order) x2_array = flow_data.z.flatten(order=order) if normal_vector == "y": x3_array = flow_data.y.flatten(order=order) x1_array = flow_data.x.flatten(order=order) x2_array = flow_data.z.flatten(order=order) u = flow_data.u.flatten(order=order) v = flow_data.v.flatten(order=order) w = flow_data.w.flatten(order=order) search_values = np.array(sorted(np.unique(x3_array))) nearest_idx = (np.abs(search_values - x3_value)).argmin() nearest_value = search_values[nearest_idx] print("Nearest value to %.2f is %.2f" % (x3_value, nearest_value)) # Select down the data x3_select_mask = x3_array == nearest_value # Store the un-interpolated input arrays at this slice x1 = x1_array[x3_select_mask] x2 = x2_array[x3_select_mask] x3 = np.ones_like(x1) * x3_value u = u[x3_select_mask] v = v[x3_select_mask] w = w[x3_select_mask] df = pd.DataFrame({"x1": x1, "x2": x2, "x3": x3, "u": u, "v": v, "w": w}) return df class CutPlane: """ A CutPlane object represents a 2D slice through the flow of a FLORIS simulation, or other such as SOWFA result. """ def __init__(self, df, x1_resolution, x2_resolution, normal_vector): """ Initialize CutPlane object, storing the DataFrame and resolution. Args: df (pandas.DataFrame): Pandas DataFrame of data with columns x1, x2, u, v, w. """ self.df: pd.DataFrame = df self.normal_vector: str = normal_vector self.resolution = (x1_resolution, x2_resolution) self.df.set_index(["x1", "x2"]) def __sub__(self, other): if self.normal_vector != other.normal_vector: raise ValueError("Operands must have consistent normal vectors.") # if self.normal_vector.df. # DF must be of the same size # resolution must be of the same size df: pd.DataFrame = self.df.copy() other_df: pd.DataFrame = other.df.copy() df['u'] = self.df['u'] - other_df['u'] df['v'] = self.df['v'] - other_df['v'] df['w'] = self.df['w'] - other_df['w'] return CutPlane( df, self.resolution[0], self.resolution[1], self.normal_vector ) # Modification functions def set_origin(cut_plane, center_x1=0.0, center_x2=0.0): """ Establish the origin of a CutPlane object. Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Plane of data. center_x1 (float, optional): x1-coordinate of origin. Defaults to 0.0. center_x2 (float, optional): x2-coordinate of origin. Defaults to 0.0. Returns: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Updated plane of data. """ # Store the un-interpolated input arrays at this slice cut_plane.df.x1 = cut_plane.df.x1 - center_x1 cut_plane.df.x2 = cut_plane.df.x2 - center_x2 return cut_plane def change_resolution(cut_plane, resolution=(100, 100)): """ Modify default resolution of a CutPlane object. Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Plane of data. resolution (tuple, optional): Desired resolution in x1 and x2. Defaults to (100, 100). Returns: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Updated plane of data. """ # Linearize the data x1_lin = np.linspace(min(cut_plane.df.x1), max(cut_plane.df.x1), resolution[0]) x2_lin = np.linspace(min(cut_plane.df.x2), max(cut_plane.df.x2), resolution[1]) # x3 = np.ones_like(x1) * cut_plane.df.x3[0] # Mesh the data x1_mesh, x2_mesh = np.meshgrid(x1_lin, x2_lin) x3_mesh = np.ones_like(x1_mesh) * cut_plane.df.x3[0] # Interpolate u,v,w u_mesh = griddata( np.column_stack( [nudge_outward(cut_plane.df.x1), nudge_outward(cut_plane.df.x2)] ), cut_plane.df.u.values, (x1_mesh.flatten(), x2_mesh.flatten()), method="cubic", ) v_mesh = griddata( np.column_stack( [nudge_outward(cut_plane.df.x1), nudge_outward(cut_plane.df.x2)] ), cut_plane.df.v.values, (x1_mesh.flatten(), x2_mesh.flatten()), method="cubic", ) w_mesh = griddata( np.column_stack( [nudge_outward(cut_plane.df.x1), nudge_outward(cut_plane.df.x2)] ), cut_plane.df.w.values, (x1_mesh.flatten(), x2_mesh.flatten()), method="cubic", ) # Assign back to df cut_plane.df = pd.DataFrame( { "x1": x1_mesh.flatten(), "x2": x2_mesh.flatten(), "x3": x3_mesh.flatten(), "u": u_mesh.flatten(), "v": v_mesh.flatten(), "w": w_mesh.flatten(), } ) # Save the resolution cut_plane.resolution = resolution # Return the cutplane return cut_plane def interpolate_onto_array(cut_plane_in, x1_array, x2_array): """ Interpolate a CutPlane object onto specified coordinate arrays. Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Plane of data. x1_array (np.array): Specified x1-coordinate. x2_array (np.array): Specified x2-coordinate. Returns: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Updated plane of data. """ cut_plane = copy.deepcopy(cut_plane_in) # Linearize the data x1_lin = x1_array x2_lin = x2_array # Save the new resolution cut_plane.resolution = (len(np.unique(x1_lin)), len(np.unique(x2_lin))) # Mesh the data x1_mesh, x2_mesh = np.meshgrid(x1_lin, x2_lin) x3_mesh = np.ones_like(x1_mesh) * cut_plane.df.x3.iloc[0] # Interpolate u,v,w u_mesh = griddata( np.column_stack( [nudge_outward(cut_plane.df.x1), nudge_outward(cut_plane.df.x2)] ), cut_plane.df.u.values, (x1_mesh.flatten(), x2_mesh.flatten()), method="cubic", ) v_mesh = griddata( np.column_stack( [nudge_outward(cut_plane.df.x1), nudge_outward(cut_plane.df.x2)] ), cut_plane.df.v.values, (x1_mesh.flatten(), x2_mesh.flatten()), method="cubic", ) w_mesh = griddata( np.column_stack( [nudge_outward(cut_plane.df.x1), nudge_outward(cut_plane.df.x2)] ), cut_plane.df.w.values, (x1_mesh.flatten(), x2_mesh.flatten()), method="cubic", ) # Assign back to df cut_plane.df = pd.DataFrame( { "x1": x1_mesh.flatten(), "x2": x2_mesh.flatten(), "x3": x3_mesh.flatten(), "u": u_mesh.flatten(), "v": v_mesh.flatten(), "w": w_mesh.flatten(), } ) # Return the cutplane return cut_plane def rescale_axis(cut_plane, x1_factor=1.0, x2_factor=1.0): """ Stretch or compress CutPlane coordinates. Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Plane of data. x1_factor (float): Scaling factor for x1-coordinate. x2_factor (float): Scaling factor for x2-coordinate. Returns: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Updated plane of data. """ # Store the un-interpolated input arrays at this slice cut_plane.df.x1 = cut_plane.df.x1 / x1_factor cut_plane.df.x2 = cut_plane.df.x2 / x2_factor return cut_plane def project_onto(cut_plane_a, cut_plane_b): """ Project cut_plane_a onto the x1, x2 of cut_plane_b Args: cut_plane_a (:py:class:`~.tools.cut_plane.CutPlane`): Plane of data to project from. cut_plane_b (:py:class:`~.tools.cut_plane.CutPlane`): Plane of data to project onto. Returns: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): Cut_plane_a projected onto cut_plane_b's axis. """ return interpolate_onto_array( cut_plane_a, cut_plane_b.df.x1.unique(), cut_plane_b.df.x2.unique() ) def calculate_wind_speed(cross_plane, x1_loc, x2_loc, R): """ Calculate effective wind speed within specified range of a point. Args: cross_plane (:py:class:`floris.cut_plane.CrossPlane`): plane of data. x1_loc (float): x1-coordinate of point of interest. x2_loc (float): x2-coordinate of point of interest. R (float): radius from point of interest to consider Returns: (float): effective wind speed """ # Temp local copy df = cross_plane.df.copy() # Make a distance column df["distance"] = np.sqrt((df.x1 - x1_loc) ** 2 + (df.x2 - x2_loc) ** 2) # Return the cube-mean wind speed return np.cbrt(np.mean(df[df.distance < R].u ** 3)) def wind_speed_profile(cross_plane, R, x2_loc, resolution=100, x1_locs=None): if x1_locs is None: x1_locs = np.linspace( min(cross_plane.df.x1), max(cross_plane.df.x1), resolution ) v_array = np.array( [calculate_wind_speed(cross_plane, x1_loc, x2_loc, R) for x1_loc in x1_locs] ) return x1_locs, v_array def calculate_power( cross_plane, x1_loc, x2_loc, R, ws_array, cp_array, air_density=1.225 ): """ Calculate maximum power available in a given cross plane. Args: cross_plane (:py:class:`floris.cut_plane.CrossPlane`): plane of data. x1_loc (float): x1-coordinate of point of interest. x2_loc (float): x2-coordinate of point of interest. R (float): Radius of wind turbine rotor. ws_array (np.array): reference wind speed for cp curve. cp_array (np.array): cp curve at reference wind speeds. air_density (float, optional): air density. Defaults to 1.225. Returns: float: Power! """ # Compute the ws ws = calculate_wind_speed(cross_plane, x1_loc, x2_loc, R) # Compute the cp cp_value = np.interp(ws, ws_array, cp_array) # Return the power return 0.5 * air_density * (np.pi * R ** 2) * cp_value * ws ** 3 def get_power_profile( cross_plane, x2_loc, ws_array, cp_array, R, air_density=1.225, resolution=100, x1_locs=None, ): if x1_locs is None: x1_locs = np.linspace( min(cross_plane.df.x1), max(cross_plane.df.x1), resolution ) p_array = np.array( [ calculate_power( cross_plane, x1_loc, x2_loc, R, ws_array, cp_array, air_density=air_density, ) for x1_loc in x1_locs ] ) return x1_locs, p_array # # Define horizontal subclass # class HorPlane(_CutPlane): # """ # Subclass of _CutPlane. Shortcut to extracting a horizontal plane. # """ # def __init__(self, df): # """ # Initialize horizontal CutPlane # Args: # flow_data (np.array): 3D vector field of velocity data # z_value (float): vertical position through which to slice # """ # # Set up call super # super().__init__(df) # # Define cross plane subclass # class CrossPlane(_CutPlane): # """ # Subclass of _CutPlane. Shortcut to extracting a cross-stream plane. # """ # def __init__(self, df): # """ # Initialize cross-stream CutPlane # Args: # flow_data (np.array): 3D vector field of velocity data # x_value (float): streamwise position through which to slice # """ # # Set up call super # super().__init__(df) # # Define cross plane subclass # class VertPlane(_CutPlane): # """ # Subclass of _CutPlane. Shortcut to extracting a streamwise-vertical plane. # """ # def __init__(self, df): # """ # Initialize streamwise-vertical CutPlane # Args: # flow_data (np.array): 3D vector field of velocity data # y_value (float): spanwise position through which to slice # """ # # Set up call super # super().__init__(df) ================================================ FILE: floris/default_inputs.yaml ================================================ name: GCH description: "Default initialization: Gauss-Curl hybrid model (GCH)" floris_version: v4 logging: console: enable: true level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 reference_wind_height: -1 turbulence_intensities: [] wind_directions: [] wind_shear: 0.12 wind_speeds: [] wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_transverse_velocities: true enable_active_wake_mixing: false wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 deflection_rate: 22 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 wake_velocity_parameters: gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 turbopark: A: 0.04 sigma_max_rel: 4.0 turboparkgauss: A: 0.04 include_mirror_wake: True empirical_gauss: wake_expansion_rates: [0.023, 0.008] breakpoints_D: [10] sigma_0_D: 0.28 smoothing_length_D: 2.0 mixing_gain_velocity: 2.0 awc_wake_exp: 1.2 awc_wake_denominator: 400 wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 wake_induced_mixing: atmospheric_ti_gain: 0.0 ================================================ FILE: floris/floris_model.py ================================================ import copy import inspect from pathlib import Path from typing import ( Any, List, Optional, ) import numpy as np import pandas as pd from floris.core import Core, State from floris.core.rotor_velocity import average_velocity from floris.core.turbine.operation_models import ( POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED, ) from floris.core.turbine.turbine import ( axial_induction, power, thrust_coefficient, ) from floris.cut_plane import CutPlane from floris.logging_manager import LoggingManager from floris.type_dec import ( floris_array_converter, NDArrayBool, NDArrayFloat, NDArrayStr, ) from floris.utilities import ( load_yaml, nested_get, nested_set, print_nested_dict, ) from floris.wind_data import ( TimeSeries, WindDataBase, WindRose, WindRoseWRG, WindTIRose, ) class FlorisModel(LoggingManager): """ FlorisModel provides a high-level user interface to many of the underlying methods within the FLORIS framework. It is meant to act as a single entry-point for the majority of users, simplifying the calls to methods on objects within FLORIS. """ @staticmethod def get_defaults() -> dict: """ Load the default FLORIS configuration dictionary. Returns: dict: The default FLORIS configuration dictionary. """ return copy.deepcopy(load_yaml(Path(__file__).parent / "default_inputs.yaml")) def __init__(self, configuration: dict | str | Path): """ Initialize the FlorisModel object. Args: configuration: The Floris configuration dictionary or path to the input YAML file. See floris.default_inputs.yaml for an example of the configuration dictionary or visit https://nrel.github.io/floris/input_reference_main.html. """ if configuration == "defaults": configuration = FlorisModel.get_defaults() self.configuration = configuration if isinstance(self.configuration, (str, Path)): try: self.core = Core.from_file(self.configuration) except FileNotFoundError: # If the file cannot be found, then attempt the configuration path relative to the # file location from which FlorisModel was attempted to be run. If successful, # update self.configuration to an absolute, working file path and name. base_fn = Path(inspect.stack()[-1].filename).resolve().parent config = (base_fn / self.configuration).resolve() self.core = Core.from_file(config) self.configuration = config elif isinstance(self.configuration, dict): self.core = Core.from_dict(self.configuration) else: raise TypeError("The Floris `configuration` must be of type 'dict', 'str', or 'Path'.") # If ref height is -1, assign the hub height if np.abs(self.core.flow_field.reference_wind_height + 1.0) < 1.0e-6: self.assign_hub_height_to_ref_height() # Make a check on reference height and provide a helpful warning unique_heights = np.unique(np.round(self.core.farm.hub_heights, decimals=6)) if (( len(unique_heights) == 1) and (np.abs(self.core.flow_field.reference_wind_height - unique_heights[0]) > 1.0e-6 )): err_msg = ( "The only unique hub-height is not equal to the specified reference " "wind height. If this was unintended use -1 as the reference hub height to " "indicate use of hub-height as reference wind height." ) self.logger.warning(err_msg, stack_info=True) # Check the turbine_grid_points is reasonable if self.core.solver["type"] == "turbine_grid": if self.core.solver["turbine_grid_points"] > 3: self.logger.error( f"turbine_grid_points value is {self.core.solver['turbine_grid_points']} " "which is larger than the recommended value of less than or equal to 3. " "High amounts of turbine grid points reduce the computational performance " "but have a small change on accuracy." ) raise ValueError("turbine_grid_points must be less than or equal to 3.") # Initialize stored wind_data object to None self._wind_data = None ### Methods for setting and running the FlorisModel def _reinitialize( self, wind_speeds: list[float] | NDArrayFloat | None = None, wind_directions: list[float] | NDArrayFloat | None = None, wind_shear: float | None = None, wind_veer: float | None = None, reference_wind_height: float | None = None, turbulence_intensities: list[float] | NDArrayFloat | None = None, air_density: float | None = None, layout_x: list[float] | NDArrayFloat | None = None, layout_y: list[float] | NDArrayFloat | None = None, turbine_type: list | None = None, turbine_library_path: str | Path | None = None, solver_settings: dict | None = None, heterogeneous_inflow_config: dict | None = None, wind_data: type[WindDataBase] | None = None, multidim_conditions: dict | None = None, ): """ Instantiate a new Floris object with updated conditions set by arguments. Any parameters in Floris that aren't changed by arguments to this function retain their values. Note that, although it's name is similar to the reinitialize() method from Floris v3, this function is not meant to be called directly by the user---users should instead call the set() method. Args: wind_speeds (NDArrayFloat | list[float] | None, optional): Wind speeds at each findex. Defaults to None. wind_directions (NDArrayFloat | list[float] | None, optional): Wind directions at each findex. Defaults to None. wind_shear (float | None, optional): Wind shear exponent. Defaults to None. wind_veer (float | None, optional): Wind veer. Defaults to None. reference_wind_height (float | None, optional): Reference wind height. Defaults to None. turbulence_intensities (NDArrayFloat | list[float] | None, optional): Turbulence intensities at each findex. Defaults to None. air_density (float | None, optional): Air density. Defaults to None. layout_x (NDArrayFloat | list[float] | None, optional): X-coordinates of the turbines. Defaults to None. layout_y (NDArrayFloat | list[float] | None, optional): Y-coordinates of the turbines. Defaults to None. turbine_type (list | None, optional): Turbine type. Defaults to None. turbine_library_path (str | Path | None, optional): Path to the turbine library. Defaults to None. solver_settings (dict | None, optional): Solver settings. Defaults to None. heterogeneous_inflow_config (dict | None, optional): heterogeneous inflow configuration. Defaults to None. wind_data (type[WindDataBase] | None, optional): Wind data. Defaults to None. """ # Export the floris object recursively as a dictionary floris_dict = self.core.as_dict() flow_field_dict = floris_dict["flow_field"] farm_dict = floris_dict["farm"] ## Farm if layout_x is not None: farm_dict["layout_x"] = layout_x if layout_y is not None: farm_dict["layout_y"] = layout_y if turbine_type is not None: if reference_wind_height is None: self.logger.warning( "turbine_type has been changed without specifying a new " +"reference_wind_height. reference_wind_height remains {0:.2f} m.".format( flow_field_dict["reference_wind_height"] ) +f" Consider calling `{self.__class__.__name__}." +"assign_hub_height_to_ref_height` to update the reference wind height to the " +"turbine hub height." ) farm_dict["turbine_type"] = turbine_type if turbine_library_path is not None: farm_dict["turbine_library_path"] = turbine_library_path ## If layout is changed and self._wind_data is not None, update the layout in wind_data if (layout_x is not None) or (layout_y is not None): if self._wind_data is not None: self._wind_data.set_layout(farm_dict["layout_x"], farm_dict["layout_y"]) # Wind data if ( (wind_directions is not None) or (wind_speeds is not None) or (turbulence_intensities is not None) or (heterogeneous_inflow_config is not None) ): if wind_data is not None: raise ValueError( "If wind_data is passed to reinitialize, then do not pass wind_directions, " "wind_speeds, turbulence_intensities or " "heterogeneous_inflow_config as this is redundant" ) elif self.wind_data is not None: self.logger.warning("Deleting stored wind_data information.") self._wind_data = None if wind_data is not None: # Set the wind data to the current layout wind_data.set_layout(farm_dict["layout_x"], farm_dict["layout_y"]) # Unpack wind data for reinitialization and save wind_data for use in output ( wind_directions, wind_speeds, turbulence_intensities, heterogeneous_inflow_config, ) = wind_data.unpack_for_reinitialize() # For backwards compatibility, multidim_conditions are unpacked separately. # multidim_conditions may be included in unpack_for_reinitialize in a future release. if (multidim_conditions is not None and wind_data.unpack_multidim_conditions() is not None): self.logger.warning( "multidim_conditions passed to reinitialize() and also found in " "wind_data. Using multidim_conditions from wind_data." ) multidim_conditions = wind_data.unpack_multidim_conditions() elif wind_data.unpack_multidim_conditions() is not None: multidim_conditions = wind_data.unpack_multidim_conditions() self._wind_data = wind_data ## FlowField if wind_speeds is not None: flow_field_dict["wind_speeds"] = wind_speeds if wind_directions is not None: flow_field_dict["wind_directions"] = wind_directions if wind_shear is not None: flow_field_dict["wind_shear"] = wind_shear if wind_veer is not None: flow_field_dict["wind_veer"] = wind_veer if reference_wind_height is not None: flow_field_dict["reference_wind_height"] = reference_wind_height if turbulence_intensities is not None: flow_field_dict["turbulence_intensities"] = turbulence_intensities if air_density is not None: flow_field_dict["air_density"] = air_density if heterogeneous_inflow_config is not None: if ( "z" in heterogeneous_inflow_config and flow_field_dict["wind_shear"] != 0.0 and heterogeneous_inflow_config['z'] is not None ): raise ValueError( "Heterogeneous inflow configuration contains a z term, and " "flow_field_dict['wind_shear'] is not 0.0. Combining both options " "is not currently allowed in FLORIS. If using a z term in the " " heterogeneous inflow configuration, set flow_field_dict['wind_shear'] " "to 0.0." ) flow_field_dict["heterogeneous_inflow_config"] = heterogeneous_inflow_config if multidim_conditions is not None: flow_field_dict["multidim_conditions"] = multidim_conditions if solver_settings is not None: floris_dict["solver"] = solver_settings floris_dict["flow_field"] = flow_field_dict floris_dict["farm"] = farm_dict # Create a new instance of floris and attach to self self.core = Core.from_dict(floris_dict) def set_operation( self, yaw_angles: NDArrayFloat | list[float] | None = None, power_setpoints: NDArrayFloat | list[float] | list[float, None] | None = None, awc_modes: NDArrayStr | list[str] | list[str, None] | None = None, awc_amplitudes: NDArrayFloat | list[float] | list[float, None] | None = None, awc_frequencies: NDArrayFloat | list[float] | list[float, None] | None = None, disable_turbines: NDArrayBool | list[bool] | None = None, ): """ Apply operating setpoints to the floris object. This function is not meant to be called directly by most users---users should instead call the set() method. Args: yaw_angles (NDArrayFloat | list[float] | None, optional): Turbine yaw angles. Defaults to None. power_setpoints (NDArrayFloat | list[float] | list[float, None] | None, optional): Turbine power setpoints. Defaults to None. disable_turbines (NDArrayBool | list[bool] | None, optional): Boolean array on whether to disable turbines. Defaults to None. """ # Add operating conditions to the floris object if yaw_angles is not None: if np.array(yaw_angles).shape[1] != self.core.farm.n_turbines: raise ValueError( f"yaw_angles has a size of {np.array(yaw_angles).shape[1]} in the 1st " f"dimension, must be equal to n_turbines={self.core.farm.n_turbines}" ) self.core.farm.set_yaw_angles(yaw_angles) if power_setpoints is not None: if np.array(power_setpoints).shape[1] != self.core.farm.n_turbines: raise ValueError( f"power_setpoints has a size of {np.array(power_setpoints).shape[1]} in the 1st" f" dimension, must be equal to n_turbines={self.core.farm.n_turbines}" ) power_setpoints = np.array(power_setpoints) # Convert any None values to the default power setpoint power_setpoints[ power_setpoints == np.full(power_setpoints.shape, None) ] = POWER_SETPOINT_DEFAULT power_setpoints = floris_array_converter(power_setpoints) self.core.farm.set_power_setpoints(power_setpoints) if awc_modes is None: awc_modes = np.array( [["baseline"] *self.core.farm.n_turbines] *self.core.flow_field.n_findex ) self.core.farm.awc_modes = awc_modes if awc_amplitudes is None: awc_amplitudes = np.zeros( ( self.core.flow_field.n_findex, self.core.farm.n_turbines, ) ) self.core.farm.awc_amplitudes = awc_amplitudes if awc_frequencies is None: awc_frequencies = np.zeros( ( self.core.flow_field.n_findex, self.core.farm.n_turbines, ) ) self.core.farm.awc_frequencies = awc_frequencies # Check for turbines to disable if disable_turbines is not None: # Force to numpy array disable_turbines = np.array(disable_turbines) # Must have first dimension = n_findex if disable_turbines.shape[0] != self.core.flow_field.n_findex: raise ValueError( f"disable_turbines has a size of {disable_turbines.shape[0]} " f"in the 0th dimension, must be equal to " f"n_findex={self.core.flow_field.n_findex}" ) # Must have first dimension = n_turbines if disable_turbines.shape[1] != self.core.farm.n_turbines: raise ValueError( f"disable_turbines has a size of {disable_turbines.shape[1]} " f"in the 1th dimension, must be equal to " f"n_turbines={self.core.farm.n_turbines}" ) # Set power setpoints to small value (non zero to avoid numerical issues) and # yaw_angles to 0 in all locations where disable_turbines is True self.core.farm.yaw_angles[disable_turbines] = 0.0 self.core.farm.power_setpoints[disable_turbines] = POWER_SETPOINT_DISABLED if any([yaw_angles is not None, power_setpoints is not None, disable_turbines is not None]): self.core.state = State.UNINITIALIZED def set( self, wind_speeds: list[float] | NDArrayFloat | None = None, wind_directions: list[float] | NDArrayFloat | None = None, wind_shear: float | None = None, wind_veer: float | None = None, reference_wind_height: float | None = None, turbulence_intensities: list[float] | NDArrayFloat | None = None, air_density: float | None = None, layout_x: list[float] | NDArrayFloat | None = None, layout_y: list[float] | NDArrayFloat | None = None, turbine_type: list | None = None, turbine_library_path: str | Path | None = None, solver_settings: dict | None = None, heterogeneous_inflow_config: dict | None = None, wind_data: type[WindDataBase] | None = None, yaw_angles: NDArrayFloat | list[float] | None = None, power_setpoints: NDArrayFloat | list[float] | list[float, None] | None = None, awc_modes: NDArrayStr | list[str] | list[str, None] | None = None, awc_amplitudes: NDArrayFloat | list[float] | list[float, None] | None = None, awc_frequencies: NDArrayFloat | list[float] | list[float, None] | None = None, disable_turbines: NDArrayBool | list[bool] | None = None, multidim_conditions: dict | None = None, ): """ Set the wind conditions and operation setpoints for the wind farm. Args: wind_speeds (NDArrayFloat | list[float] | None, optional): Wind speeds at each findex. Defaults to None. wind_directions (NDArrayFloat | list[float] | None, optional): Wind directions at each findex. Defaults to None. wind_shear (float | None, optional): Wind shear exponent. Defaults to None. wind_veer (float | None, optional): Wind veer. Defaults to None. reference_wind_height (float | None, optional): Reference wind height. Defaults to None. turbulence_intensities (NDArrayFloat | list[float] | None, optional): Turbulence intensities at each findex. Defaults to None. air_density (float | None, optional): Air density. Defaults to None. layout_x (NDArrayFloat | list[float] | None, optional): X-coordinates of the turbines. Defaults to None. layout_y (NDArrayFloat | list[float] | None, optional): Y-coordinates of the turbines. Defaults to None. turbine_type (list | None, optional): Turbine type. Defaults to None. turbine_library_path (str | Path | None, optional): Path to the turbine library. Defaults to None. solver_settings (dict | None, optional): Solver settings. Defaults to None. heterogeneous_inflow_config (dict | None, optional): heterogeneous inflow configuration. Defaults to None. wind_data (type[WindDataBase] | None, optional): Wind data. Defaults to None. yaw_angles (NDArrayFloat | list[float] | None, optional): Turbine yaw angles. Defaults to None. power_setpoints (NDArrayFloat | list[float] | list[float, None] | None, optional): Turbine power setpoints. disable_turbines (NDArrayBool | list[bool] | None, optional): NDArray with dimensions n_findex x n_turbines. True values indicate the turbine is disabled at that findex and the power setpoint at that position is set to 0. Defaults to None. """ # Initialize a new Floris object after saving the setpoints _yaw_angles = self.core.farm.yaw_angles _power_setpoints = self.core.farm.power_setpoints _awc_modes = self.core.farm.awc_modes _awc_amplitudes = self.core.farm.awc_amplitudes _awc_frequencies = self.core.farm.awc_frequencies self._reinitialize( wind_speeds=wind_speeds, wind_directions=wind_directions, wind_shear=wind_shear, wind_veer=wind_veer, reference_wind_height=reference_wind_height, turbulence_intensities=turbulence_intensities, air_density=air_density, layout_x=layout_x, layout_y=layout_y, turbine_type=turbine_type, turbine_library_path=turbine_library_path, solver_settings=solver_settings, heterogeneous_inflow_config=heterogeneous_inflow_config, wind_data=wind_data, multidim_conditions=multidim_conditions, ) # If the yaw angles or power setpoints are not the default, set them back to the # previous setting if not (_yaw_angles == 0).all(): self.core.farm.set_yaw_angles(_yaw_angles) if not (_power_setpoints == POWER_SETPOINT_DEFAULT).all(): self.core.farm.set_power_setpoints(_power_setpoints) if _awc_modes is not None: self.core.farm.set_awc_modes(_awc_modes) if not (_awc_amplitudes == 0).all(): self.core.farm.set_awc_amplitudes(_awc_amplitudes) if not (_awc_frequencies == 0).all(): self.core.farm.set_awc_frequencies(_awc_frequencies) # Set the operation self.set_operation( yaw_angles=yaw_angles, power_setpoints=power_setpoints, awc_modes=awc_modes, awc_amplitudes=awc_amplitudes, awc_frequencies=awc_frequencies, disable_turbines=disable_turbines, ) def reset_operation(self): """ Instantiate a new Floris object to set all operation setpoints to their default values. """ self._reinitialize() def run(self) -> None: """ Run the FLORIS solve to compute the velocity field and wake effects. """ # Initialize solution space self.core.initialize_domain() # Perform the wake calculations self.core.steady_state_atmospheric_condition() def run_no_wake(self) -> None: """ This function is similar to `run()` except that it does not apply a wake model. That is, the wind farm is modeled as if there is no wake in the flow. Operation settings may reduce the power and thrust of the turbine to where they're applied. """ # Initialize solution space self.core.initialize_domain() # Finalize values to user-supplied order self.core.finalize() ### Methods for extracting turbine performance after running def _get_turbine_powers(self) -> NDArrayFloat: """Calculates the power at each turbine in the wind farm. Returns: NDArrayFloat: Powers at each turbine. """ # Confirm calculate wake has been run if self.core.state is not State.USED: raise RuntimeError( "Can't compute turbine powers without first running `FlorisModel.run()`." ) # Check for negative velocities, which could indicate bad model # parameters or turbines very closely spaced. if (self.core.flow_field.u < 0.0).any(): self.logger.warning("Some velocities at the rotor are negative.") turbine_powers = power( velocities=self.core.flow_field.u, turbulence_intensities=self.core.flow_field.turbulence_intensity_field[:,:,None,None], air_density=self.core.flow_field.air_density, power_functions=self.core.farm.turbine_power_functions, yaw_angles=self.core.farm.yaw_angles, tilt_angles=self.core.farm.tilt_angles, power_setpoints=self.core.farm.power_setpoints, awc_modes = self.core.farm.awc_modes, awc_amplitudes=self.core.farm.awc_amplitudes, tilt_interps=self.core.farm.turbine_tilt_interps, turbine_type_map=self.core.farm.turbine_type_map, turbine_power_thrust_tables=self.core.farm.turbine_power_thrust_tables, correct_cp_ct_for_tilt=self.core.farm.correct_cp_ct_for_tilt, multidim_condition=self.core.flow_field.multidim_conditions, ) return turbine_powers def get_turbine_powers(self): """ Calculates the power at each turbine in the wind farm. Returns: NDArrayFloat: Powers at each turbine. """ turbine_powers = self._get_turbine_powers() if self.wind_data is not None: if isinstance(self.wind_data, (WindRose, WindRoseWRG)): turbine_powers_rose = np.full( (len(self.wind_data.wd_flat), self.core.farm.n_turbines), np.nan ) turbine_powers_rose[self.wind_data.non_zero_freq_mask, :] = turbine_powers turbine_powers = turbine_powers_rose.reshape( len(self.wind_data.wind_directions), len(self.wind_data.wind_speeds), self.core.farm.n_turbines ) elif type(self.wind_data) is WindTIRose: turbine_powers_rose = np.full( (len(self.wind_data.wd_flat), self.core.farm.n_turbines), np.nan ) turbine_powers_rose[self.wind_data.non_zero_freq_mask, :] = turbine_powers turbine_powers = turbine_powers_rose.reshape( len(self.wind_data.wind_directions), len(self.wind_data.wind_speeds), len(self.wind_data.turbulence_intensities), self.core.farm.n_turbines ) return turbine_powers def get_expected_turbine_powers(self, freq=None): """ Compute the expected (mean) power of each turbine. Args: freq (NDArrayFloat): NumPy array with shape with the frequencies of each wind direction and wind speed combination. freq is either a 1D array, in which case the same frequencies are used for all turbines, or a 2D array with shape equal to (n_findex, n_turbines), in which case each turbine has a unique set of frequencies (this is the case for example using WindRoseByTurbine). These frequencies should typically sum across rows up to 1.0 and are used to weigh the wind farm power for every condition in calculating the wind farm's AEP. Defaults to None. If None and a WindData object was supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). """ turbine_powers = self._get_turbine_powers() if freq is None: if self.wind_data is None: freq = np.array([1.0/self.core.flow_field.n_findex]) else: freq = self.wind_data.unpack_freq() # If freq is 2d, then use the per turbine frequencies if len(np.shape(freq)) == 2: return np.nansum(np.multiply(freq, turbine_powers), axis=0) else: return np.nansum(np.multiply(freq.reshape(-1, 1), turbine_powers), axis=0) def _get_weighted_turbine_powers( self, turbine_weights=None, use_turbulence_correction=False, ): if use_turbulence_correction: raise NotImplementedError( "Turbulence correction is not yet implemented in the power calculation." ) # Confirm run() has been run if self.core.state is not State.USED: raise RuntimeError( f"Can't run function `{self.__class__.__name__}.get_farm_power` without " f"first running `{self.__class__.__name__}.run`." ) if turbine_weights is None: # Default to equal weighing of all turbines when turbine_weights is None turbine_weights = np.ones( ( self.core.flow_field.n_findex, self.core.farm.n_turbines, ) ) elif len(np.shape(turbine_weights)) == 1: # Deal with situation when 1D array is provided turbine_weights = np.tile( turbine_weights, (self.core.flow_field.n_findex, 1), ) # Calculate all turbine powers and apply weights turbine_powers = self._get_turbine_powers() turbine_powers = np.multiply(turbine_weights, turbine_powers) return turbine_powers def _get_farm_power( self, turbine_weights=None, use_turbulence_correction=False, ): """ Report wind plant power from instance of floris. Optionally includes uncertainty in wind direction and yaw position when determining power. Uncertainty is included by computing the mean wind farm power for a distribution of wind direction and yaw position deviations from the original wind direction and yaw angles. Args: turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. use_turbulence_correction: (bool, optional): When True uses a turbulence parameter to adjust power output calculations. Defaults to False. Not currently implemented. Returns: float: Sum of wind turbine powers in W. """ turbine_powers = self._get_weighted_turbine_powers( turbine_weights=turbine_weights, use_turbulence_correction=use_turbulence_correction ) return np.sum(turbine_powers, axis=1) def get_farm_power( self, turbine_weights=None, use_turbulence_correction=False, ): """ Report wind plant power from instance of floris. Optionally includes uncertainty in wind direction and yaw position when determining power. Uncertainty is included by computing the mean wind farm power for a distribution of wind direction and yaw position deviations from the original wind direction and yaw angles. Args: turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. use_turbulence_correction: (bool, optional): When True uses a turbulence parameter to adjust power output calculations. Defaults to False. Not currently implemented. Returns: float: Sum of wind turbine powers in W. """ farm_power = self._get_farm_power(turbine_weights, use_turbulence_correction) if self.wind_data is not None: if isinstance(self.wind_data, (WindRose, WindRoseWRG)): farm_power_rose = np.full(len(self.wind_data.wd_flat), np.nan) farm_power_rose[self.wind_data.non_zero_freq_mask] = farm_power farm_power = farm_power_rose.reshape( len(self.wind_data.wind_directions), len(self.wind_data.wind_speeds) ) elif type(self.wind_data) is WindTIRose: farm_power_rose = np.full(len(self.wind_data.wd_flat), np.nan) farm_power_rose[self.wind_data.non_zero_freq_mask] = farm_power farm_power = farm_power_rose.reshape( len(self.wind_data.wind_directions), len(self.wind_data.wind_speeds), len(self.wind_data.turbulence_intensities) ) return farm_power def get_expected_farm_power( self, freq=None, turbine_weights=None, ) -> float: """ Compute the expected (mean) power of the wind farm. Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind direction and wind speed combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm power for every condition in calculating the wind farm's AEP. Defaults to None. If None and a WindData object was supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. """ if freq is None: if self.wind_data is None: freq = np.array([1.0/self.core.flow_field.n_findex]) else: freq = self.wind_data.unpack_freq() # If freq is 1d if len(np.shape(freq)) == 1: farm_power = self._get_farm_power(turbine_weights=turbine_weights) return np.nansum(np.multiply(freq, farm_power)) else: weighted_turbine_powers = self._get_weighted_turbine_powers( turbine_weights=turbine_weights, ) return np.nansum(np.multiply(freq, weighted_turbine_powers)) def get_farm_AEP( self, freq=None, turbine_weights=None, hours_per_year=8760, ) -> float: """ Estimate annual energy production (AEP) for distributions of wind speed, wind direction, frequency of occurrence, and yaw offset. Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind direction and wind speed combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm power for every condition in calculating the wind farm's AEP. Defaults to None. If None and a WindData object was supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed. turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. hours_per_year (float, optional): Number of hours in a year. Defaults to 365 * 24. Returns: float: The Annual Energy Production (AEP) for the wind farm in watt-hours. """ if freq is None and not isinstance(self.wind_data, (WindRose, WindRoseWRG, WindTIRose)): self.logger.warning( "Computing AEP with uniform frequencies. Results results may not reflect annual " "operation." ) return self.get_expected_farm_power( freq=freq, turbine_weights=turbine_weights ) * hours_per_year def get_expected_farm_value( self, freq=None, values=None, turbine_weights=None, ) -> float: """ Compute the expected (mean) value produced by the wind farm. This is computed by multiplying the wind farm power for each wind condition by the corresponding value of the power generated (e.g., electricity market price per unit of energy), then weighting by frequency and summing over all conditions. Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind condition combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm value for every condition in calculating the wind farm's expected value. Defaults to None. If None and a WindData object is supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). values (NDArrayFloat): NumPy array with shape (n_findex) with the values corresponding to the power generated for each wind condition combination. The wind farm power is multiplied by the value for every condition in calculating the wind farm's expected value. Defaults to None. If None and a WindData object is supplied, the WindData object's values will be used. Otherwise, a value of 1 for all conditions is assumed (i.e., the expected farm value will be equivalent to the expected farm power). turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the value production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the expected value. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. Returns: float: The expected value produced by the wind farm in units of value. """ if freq is None: if self.wind_data is None: freq = np.array([1.0/self.core.flow_field.n_findex]) else: freq = self.wind_data.unpack_freq() # If freq is 1d if len(np.shape(freq)) == 1: farm_power = self._get_farm_power(turbine_weights=turbine_weights) farm_power = np.multiply(freq, farm_power) else: weighted_turbine_powers = self._get_weighted_turbine_powers( turbine_weights=turbine_weights ) farm_power = np.nansum(np.multiply(freq, weighted_turbine_powers), axis=1) if values is None: if self.wind_data is None: values = np.array([1.0]) else: values = self.wind_data.unpack_value() return np.nansum(np.multiply(values, farm_power)) def get_farm_AVP( self, freq=None, values=None, turbine_weights=None, hours_per_year=8760, ) -> float: """ Estimate annual value production (AVP) for distribution of wind conditions, frequencies of occurrence, and corresponding values of power generated (e.g., electricity price per unit of energy). Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind condition combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm value for every condition in calculating the wind farm's AVP. Defaults to None. If None and a WindData object is supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). values (NDArrayFloat): NumPy array with shape (n_findex) with the values corresponding to the power generated for each wind condition combination. The wind farm power is multiplied by the value for every condition in calculating the wind farm's AVP. Defaults to None. If None and a WindData object is supplied, the WindData object's values will be used. Otherwise, a value of 1 for all conditions is assumed (i.e., the AVP will be equivalent to the AEP). turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the value production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the AVP. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. hours_per_year (float, optional): Number of hours in a year. Defaults to 365 * 24. Returns: float: The Annual Value Production (AVP) for the wind farm in units of value. """ if ( freq is None and not isinstance(self.wind_data, WindRose) and not isinstance(self.wind_data, WindRoseWRG) and not isinstance(self.wind_data, WindTIRose) ): self.logger.warning( "Computing AVP with uniform frequencies. Results results may not reflect annual " "operation." ) if values is None and self.wind_data is None: self.logger.warning( "Computing AVP with uniform value equal to 1. Results will be equivalent to " "annual energy production." ) return self.get_expected_farm_value( freq=freq, values=values, turbine_weights=turbine_weights ) * hours_per_year def get_turbine_ais(self) -> NDArrayFloat: turbine_ais = axial_induction( velocities=self.core.flow_field.u, turbulence_intensities=self.core.flow_field.turbulence_intensity_field[:,:,None,None], air_density=self.core.flow_field.air_density, yaw_angles=self.core.farm.yaw_angles, tilt_angles=self.core.farm.tilt_angles, power_setpoints=self.core.farm.power_setpoints, awc_modes = self.core.farm.awc_modes, awc_amplitudes=self.core.farm.awc_amplitudes, axial_induction_functions=self.core.farm.turbine_axial_induction_functions, tilt_interps=self.core.farm.turbine_tilt_interps, correct_cp_ct_for_tilt=self.core.farm.correct_cp_ct_for_tilt, turbine_type_map=self.core.farm.turbine_type_map, turbine_power_thrust_tables=self.core.farm.turbine_power_thrust_tables, average_method=self.core.grid.average_method, cubature_weights=self.core.grid.cubature_weights, multidim_condition=self.core.flow_field.multidim_conditions, ) return turbine_ais def get_turbine_thrust_coefficients(self) -> NDArrayFloat: turbine_thrust_coefficients = thrust_coefficient( velocities=self.core.flow_field.u, turbulence_intensities=self.core.flow_field.turbulence_intensity_field[:,:,None,None], air_density=self.core.flow_field.air_density, yaw_angles=self.core.farm.yaw_angles, tilt_angles=self.core.farm.tilt_angles, power_setpoints=self.core.farm.power_setpoints, awc_modes = self.core.farm.awc_modes, awc_amplitudes=self.core.farm.awc_amplitudes, thrust_coefficient_functions=self.core.farm.turbine_thrust_coefficient_functions, tilt_interps=self.core.farm.turbine_tilt_interps, correct_cp_ct_for_tilt=self.core.farm.correct_cp_ct_for_tilt, turbine_type_map=self.core.farm.turbine_type_map, turbine_power_thrust_tables=self.core.farm.turbine_power_thrust_tables, average_method=self.core.grid.average_method, cubature_weights=self.core.grid.cubature_weights, multidim_condition=self.core.flow_field.multidim_conditions, ) return turbine_thrust_coefficients def get_turbine_TIs(self) -> NDArrayFloat: return self.core.flow_field.turbulence_intensity_field ### Methods for sampling and visualization def set_for_viz(self, findex: int, solver_settings: dict) -> None: """ Set the floris object to a single findex for visualization. Args: findex (int): The findex to set the floris object to. solver_settings (dict): The solver settings to use for visualization. """ # If not None, set the heterogeneous inflow configuration if self.core.flow_field.heterogeneous_inflow_config is not None: heterogeneous_inflow_config = { 'x': self.core.flow_field.heterogeneous_inflow_config['x'], 'y': self.core.flow_field.heterogeneous_inflow_config['y'], 'speed_multipliers': self.core.flow_field.heterogeneous_inflow_config['speed_multipliers'][findex:findex+1], 'interp_method': self.core.flow_field.heterogeneous_inflow_config['interp_method'], } if 'z' in self.core.flow_field.heterogeneous_inflow_config: heterogeneous_inflow_config['z'] = ( self.core.flow_field.heterogeneous_inflow_config['z'] ) else: heterogeneous_inflow_config = None self.set( wind_speeds=self.wind_speeds[findex:findex+1], wind_directions=self.wind_directions[findex:findex+1], turbulence_intensities=self.turbulence_intensities[findex:findex+1], yaw_angles=self.core.farm.yaw_angles[findex:findex+1,:], power_setpoints=self.core.farm.power_setpoints[findex:findex+1,:], awc_modes=self.core.farm.awc_modes[findex:findex+1,:], awc_amplitudes=self.core.farm.awc_amplitudes[findex:findex+1,:], heterogeneous_inflow_config = heterogeneous_inflow_config, solver_settings=solver_settings, ) def calculate_cross_plane( self, downstream_dist, y_resolution=200, z_resolution=200, y_bounds=None, z_bounds=None, findex_for_viz=None, ): """ Shortcut method to instantiate a :py:class:`~.tools.cut_plane.CutPlane` object containing the velocity field in a vertical plane cut through the simulation domain at a specific downstream (x) distance. Args: downstream_dist (float): Distance downstream to compute. y_resolution (float, optional): Output array resolution. Defaults to 200 points. z_resolution (float, optional): Output array resolution. Defaults to 200 points. y_bounds (tuple, optional): Limits of output array (in m). Defaults to None. z_bounds (tuple, optional): Limits of output array (in m). Defaults to None. finder_for_viz (int, optional): Index of the condition to visualize. Returns: :py:class:`~.tools.cut_plane.CutPlane`: containing values of y, z, u, v, w """ if self.n_findex > 1 and findex_for_viz is None: self.logger.warning( "Multiple findices detected. Using first findex for visualization." ) if findex_for_viz is None: findex_for_viz = 0 # Store the current state for reinitialization fmodel_viz = copy.deepcopy(self) # Set the solver to a flow field planar grid solver_settings = { "type": "flow_field_planar_grid", "normal_vector": "x", "planar_coordinate": downstream_dist, "flow_field_grid_points": [y_resolution, z_resolution], "flow_field_bounds": [y_bounds, z_bounds], } fmodel_viz.set_for_viz(findex_for_viz, solver_settings) # Calculate wake fmodel_viz.core.solve_for_viz() # Get the points of data in a dataframe # TODO this just seems to be flattening and storing the data in a df; is this necessary? # It seems the biggest dependency is on CutPlane and the subsequent visualization tools. df = fmodel_viz.get_plane_of_points( normal_vector="x", planar_coordinate=downstream_dist, ) # Compute the cutplane cross_plane = CutPlane(df, y_resolution, z_resolution, "x") return cross_plane def calculate_horizontal_plane( self, height, x_resolution=200, y_resolution=200, x_bounds=None, y_bounds=None, findex_for_viz=None, ): """ Shortcut method to instantiate a :py:class:`~.tools.cut_plane.CutPlane` object containing the velocity field in a horizontal plane cut through the simulation domain at a specific height. Args: height (float): Height of cut plane. Defaults to Hub-height. x_resolution (float, optional): Output array resolution. Defaults to 200 points. y_resolution (float, optional): Output array resolution. Defaults to 200 points. x_bounds (tuple, optional): Limits of output array (in m). Defaults to None. y_bounds (tuple, optional): Limits of output array (in m). Defaults to None. findex_for_viz (int, optional): Index of the condition to visualize. Returns: :py:class:`~.tools.cut_plane.CutPlane`: containing values of x, y, u, v, w """ if self.n_findex > 1 and findex_for_viz is None: self.logger.warning( "Multiple findices detected. Using first findex for visualization." ) if findex_for_viz is None: findex_for_viz = 0 # Store the current state for reinitialization fmodel_viz = copy.deepcopy(self) # Set the solver to a flow field planar grid solver_settings = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": height, "flow_field_grid_points": [x_resolution, y_resolution], "flow_field_bounds": [x_bounds, y_bounds], } fmodel_viz.set_for_viz(findex_for_viz, solver_settings) # Calculate wake fmodel_viz.core.solve_for_viz() # Get the points of data in a dataframe # TODO this just seems to be flattening and storing the data in a df; is this necessary? # It seems the biggest depenedcy is on CutPlane and the subsequent visualization tools. df = fmodel_viz.get_plane_of_points( normal_vector="z", planar_coordinate=height, ) # Compute the cutplane horizontal_plane = CutPlane( df, fmodel_viz.core.grid.grid_resolution[0], fmodel_viz.core.grid.grid_resolution[1], "z", ) return horizontal_plane def calculate_y_plane( self, crossstream_dist, x_resolution=200, z_resolution=200, x_bounds=None, z_bounds=None, findex_for_viz=None, ): """ Shortcut method to instantiate a :py:class:`~.tools.cut_plane.CutPlane` object containing the velocity field in a vertical plane cut through the simulation domain at a specific cross-stream (y) distance. Args: crossstream_dist (float): Cross-stream distance to compute. x_resolution (float, optional): Output array resolution. Defaults to 200 points. z_resolution (float, optional): Output array resolution. Defaults to 200 points. x_bounds (tuple, optional): Limits of output array (in m). Defaults to None. z_bounds (tuple, optional): Limits of output array (in m). Defaults to None. findex_for_viz (int, optional): Index of the condition to visualize. Defaults to 0. Returns: :py:class:`~.tools.cut_plane.CutPlane`: containing values of x, z, u, v, w """ if self.n_findex > 1 and findex_for_viz is None: self.logger.warning( "Multiple findices detected. Using first findex for visualization." ) if findex_for_viz is None: findex_for_viz = 0 # Store the current state for reinitialization fmodel_viz = copy.deepcopy(self) # Set the solver to a flow field planar grid solver_settings = { "type": "flow_field_planar_grid", "normal_vector": "y", "planar_coordinate": crossstream_dist, "flow_field_grid_points": [x_resolution, z_resolution], "flow_field_bounds": [x_bounds, z_bounds], } fmodel_viz.set_for_viz(findex_for_viz, solver_settings) # Calculate wake fmodel_viz.core.solve_for_viz() # Get the points of data in a dataframe # TODO this just seems to be flattening and storing the data in a df; is this necessary? # It seems the biggest dependency is on CutPlane and the subsequent visualization tools. df = fmodel_viz.get_plane_of_points( normal_vector="y", planar_coordinate=crossstream_dist, ) # Compute the cutplane y_plane = CutPlane(df, x_resolution, z_resolution, "y") return y_plane def get_plane_of_points( self, normal_vector="z", planar_coordinate=None, ): """ Calculates velocity values through the :py:meth:`FlorisModel.calculate_wake` method at points in plane specified by inputs. Args: normal_vector (string, optional): Vector normal to plane. Defaults to z. planar_coordinate (float, optional): Value of normal vector to slice through. Defaults to None. Returns: :py:class:`pandas.DataFrame`: containing values of x1, x2, x3, u, v, w """ # Get results vectors if normal_vector == "z": x_flat = self.core.grid.x_sorted_inertial_frame[0].flatten() y_flat = self.core.grid.y_sorted_inertial_frame[0].flatten() z_flat = self.core.grid.z_sorted_inertial_frame[0].flatten() else: x_flat = self.core.grid.x_sorted[0].flatten() y_flat = self.core.grid.y_sorted[0].flatten() z_flat = self.core.grid.z_sorted[0].flatten() u_flat = self.core.flow_field.u_sorted[0].flatten() v_flat = self.core.flow_field.v_sorted[0].flatten() w_flat = self.core.flow_field.w_sorted[0].flatten() # Create a df of these if normal_vector == "z": df = pd.DataFrame( { "x1": x_flat, "x2": y_flat, "x3": z_flat, "u": u_flat, "v": v_flat, "w": w_flat, } ) if normal_vector == "x": df = pd.DataFrame( { "x1": y_flat, "x2": z_flat, "x3": x_flat, "u": u_flat, "v": v_flat, "w": w_flat, } ) if normal_vector == "y": df = pd.DataFrame( { "x1": x_flat, "x2": z_flat, "x3": y_flat, "u": u_flat, "v": v_flat, "w": w_flat, } ) # Subset to plane # TODO: Seems sloppy as need more than one plane in the z-direction for GCH if planar_coordinate is not None: df = df[np.isclose(df.x3, planar_coordinate)] # , atol=0.1, rtol=0.0)] # Drop duplicates # TODO is this still needed now that we setup a grid for just this plane? df = df.drop_duplicates() # Sort values of df to make sure plotting is acceptable df = df.sort_values(["x2", "x1"]).reset_index(drop=True) return df def sample_flow_at_points(self, x: NDArrayFloat, y: NDArrayFloat, z: NDArrayFloat): """ Extract the wind speed at points in the flow. Args: x (1DArrayFloat | list): x-locations of points where flow is desired. y (1DArrayFloat | list): y-locations of points where flow is desired. z (1DArrayFloat | list): z-locations of points where flow is desired. Returns: 3DArrayFloat containing wind speed with dimensions (# of findex, # of sample points) """ # Check that x, y, z are all the same length if not len(x) == len(y) == len(z): raise ValueError("x, y, and z must be the same size") return self.core.solve_for_points(x, y, z) def sample_ti_at_points(self, x: NDArrayFloat, y: NDArrayFloat, z: NDArrayFloat): """ Extract the turbulence intensity at points in the flow. Args: x (1DArrayFloat | list): x-locations of points where TI is desired. y (1DArrayFloat | list): y-locations of points where TI is desired. z (1DArrayFloat | list): z-locations of points where TI is desired. Returns: 3DArrayFloat containing turbulence intensity with dimensions (# of findex, # of sample points) """ self.sample_flow_at_points(x, y, z) # Solve, but ignore returned velocities # Remove grid dimensions and return sorted TI field return self.core.flow_field.turbulence_intensity_field_sorted[:, :, 0, 0] def sample_velocity_deficit_profiles( self, direction: str = "cross-stream", downstream_dists: NDArrayFloat | list = None, profile_range: NDArrayFloat | list = None, resolution: int = 100, wind_direction: float = None, homogeneous_wind_speed: float = None, ref_rotor_diameter: float = None, x_start: float = 0.0, y_start: float = 0.0, reference_height: float = None, ) -> list[pd.DataFrame]: """ Extract velocity deficit profiles at a set of downstream distances from a starting point (usually a turbine location). For each downstream distance, a profile is sampled along a line in either the cross-stream direction (x2) or the vertical direction (x3). Velocity deficit is here defined as (homogeneous_wind_speed - u)/homogeneous_wind_speed, where u is the wake velocity obtained when wind_shear = 0.0. Args: direction: At each downstream location, this is the direction in which to sample the profile. Either `cross-stream` or `vertical`. downstream_dists: A list/array of streamwise locations for where to sample the profiles. Default starting point is (0.0, 0.0, reference_height). profile_range: Determines the extent of the line along which the profiles are sampled. The range is defined about a point which lies some distance directly downstream of the starting point. resolution: Number of sample points in each profile. wind_direction: A single wind direction. homogeneous_wind_speed: A single wind speed. It is called homogeneous since 'wind_shear' is temporarily set to 0.0 in this method. ref_rotor_diameter: A reference rotor diameter which is used to normalize the coordinates. x_start: x-coordinate of starting point. y_start: y-coordinate of starting point. reference_height: If `direction` is cross-stream, then `reference_height` defines the height of the horizontal plane in which the velocity profiles are sampled. If `direction` is vertical, then the velocity is sampled along the vertical direction with the `profile_range` being relative to the `reference_height`. Returns: A list of pandas DataFrame objects where each DataFrame represents one velocity deficit profile. """ if direction not in ["cross-stream", "vertical"]: raise ValueError("`direction` must be either `cross-stream` or `vertical`.") if ref_rotor_diameter is None: unique_rotor_diameters = np.unique(self.core.farm.rotor_diameters) if len(unique_rotor_diameters) == 1: ref_rotor_diameter = unique_rotor_diameters[0] else: raise ValueError( "Please provide a `ref_rotor_diameter`. This is needed to normalize the " "coordinates. Could not select a value automatically since the number of " "unique rotor diameters in the turbine layout is not 1. " f"Found the following rotor diameters: {unique_rotor_diameters}." ) if downstream_dists is None: downstream_dists = ref_rotor_diameter * np.array([3, 5, 7, 9]) if profile_range is None: profile_range = ref_rotor_diameter * np.array([-2, 2]) wind_directions_copy = np.array(self.core.flow_field.wind_directions, copy=True) wind_speeds_copy = np.array(self.core.flow_field.wind_speeds, copy=True) wind_shear_copy = self.core.flow_field.wind_shear if wind_direction is None: if len(wind_directions_copy) == 1: wind_direction = wind_directions_copy[0] else: raise ValueError( "Could not determine a wind direction for which to sample the velocity " "profiles. Either provide a single `wind_direction` as an argument to this " "method, or initialize the Floris object with a single wind direction." ) if homogeneous_wind_speed is None: if len(wind_speeds_copy) == 1: homogeneous_wind_speed = wind_speeds_copy[0] self.logger.warning( "`homogeneous_wind_speed` not provided. Setting it to the following wind speed " f"found in the current flow field: {wind_speeds_copy[0]} m/s. Note that the " "inflow is always homogeneous when calculating the velocity deficit profiles. " "This is done by temporarily setting `wind_shear` to 0.0" ) else: raise ValueError( "Could not determine a wind speed for which to sample the velocity " "profiles. Provide a single `homogeneous_wind_speed` to this method." ) if reference_height is None: reference_height = self.core.flow_field.reference_wind_height self.set( wind_directions=[wind_direction], wind_speeds=[homogeneous_wind_speed], wind_shear=0.0, ) velocity_deficit_profiles = self.core.solve_for_velocity_deficit_profiles( direction, downstream_dists, profile_range, resolution, homogeneous_wind_speed, ref_rotor_diameter, x_start, y_start, reference_height, ) self.set( wind_directions=wind_directions_copy, wind_speeds=wind_speeds_copy, wind_shear=wind_shear_copy, ) return velocity_deficit_profiles ### Utility methods def assign_hub_height_to_ref_height(self): # Confirm can do this operation unique_heights = np.unique(self.core.farm.hub_heights) if len(unique_heights) > 1: raise ValueError( "To assign hub heights to reference height, can not have more than one " "specified height. " f"Current length is {unique_heights}." ) self.core.flow_field.reference_wind_height = unique_heights[0] def get_operation_model(self) -> str: """Get the operation model of a FlorisModel. Returns: str: The operation_model. """ operation_models = [ self.core.farm.turbine_definitions[tindex]["operation_model"] for tindex in range(self.core.farm.n_turbines) ] if len(set(operation_models)) == 1: return operation_models[0] else: return operation_models def set_operation_model(self, operation_model: str | List[str]): """Set the turbine operation model(s). Args: operation_model (str): The operation model to set. """ if isinstance(operation_model, str): if len(self.core.farm.turbine_type) == 1: # Set a single one here, then, and return turbine_type = self.core.farm.turbine_definitions[0] turbine_type["operation_model"] = operation_model self.set( turbine_type=[turbine_type], reference_wind_height=self.reference_wind_height ) return else: operation_model = [operation_model]*self.core.farm.n_turbines if len(operation_model) != self.core.farm.n_turbines: raise ValueError( "The length of the operation_model list must be " "equal to the number of turbines." ) turbine_type_list = self.core.farm.turbine_definitions for tindex in range(self.core.farm.n_turbines): turbine_type_list[tindex]["turbine_type"] = ( turbine_type_list[tindex]["turbine_type"]+"_"+operation_model[tindex] ) turbine_type_list[tindex]["operation_model"] = operation_model[tindex] self.set( turbine_type=turbine_type_list, reference_wind_height=self.reference_wind_height ) def copy(self): """Create an independent copy of the current FlorisModel object When creating the copy, this method uses self.__class__(), rather than FlorisModel() directly, so that subclasses of FlorisModel can inherit this method and return instantiations of their own class, rather than the FlorisModel class. """ return self.__class__(self.core.as_dict(), **self.secondary_init_kwargs) def get_param( self, param: List[str], param_idx: Optional[int] = None ) -> Any: """Get a parameter from a FlorisModel object. Args: param (List[str]): A list of keys to traverse the FlorisModel dictionary. param_idx (Optional[int], optional): The index to get the value at. Defaults to None. If None, the entire parameter is returned. Returns: Any: The value of the parameter. """ fm_dict = self.core.as_dict() if param_idx is None: return nested_get(fm_dict, param) else: return nested_get(fm_dict, param)[param_idx] def set_param( self, param: List[str], value: Any, param_idx: Optional[int] = None ): """Set a parameter in a FlorisModel object. Args: param (List[str]): A list of keys to traverse the FlorisModel dictionary. value (Any): The value to set. param_idx (Optional[int], optional): The index to set the value at. Defaults to None. """ fm_dict_mod = self.core.as_dict() nested_set(fm_dict_mod, param, value, param_idx) self.__init__(fm_dict_mod, **self.secondary_init_kwargs) def get_turbine_layout(self, z=False): """ Get turbine layout Args: z (bool): When *True*, return lists of x, y, and z coords, otherwise, return x and y only. Defaults to *False*. Returns: np.array: lists of x, y, and (optionally) z coordinates of each turbine """ xcoords, ycoords, zcoords = self.core.farm.coordinates.T if z: return xcoords, ycoords, zcoords else: return xcoords, ycoords def show_config(self, full=False) -> None: """Print the FlorisModel dictionary. """ config_dict = self.core.as_dict() if not full: del config_dict["logging"] del config_dict["wake"]["enable_secondary_steering"] del config_dict["wake"]["enable_yaw_added_recovery"] del config_dict["wake"]["enable_transverse_velocities"] del config_dict["wake"]["enable_active_wake_mixing"] del config_dict["wake"]["wake_deflection_parameters"] del config_dict["wake"]["wake_velocity_parameters"] del config_dict["wake"]["wake_turbulence_parameters"] print_nested_dict(config_dict) def print_dict(self) -> None: """Print the FlorisModel dictionary. """ self.logger.warning( "The print_dict() method has been deprecated." " Please use the show_config() method instead." ) self.show_config(full=True) ### Properties @property def secondary_init_kwargs(self): """ FlorisModel takes only the configuration argument. This method is a placeholder for subclasses of FlorisModel. """ return {} @property def layout_x(self): """ Wind turbine coordinate information. Returns: np.array: Wind turbine x-coordinate. """ return self.core.farm.layout_x @property def layout_y(self): """ Wind turbine coordinate information. Returns: np.array: Wind turbine y-coordinate. """ return self.core.farm.layout_y @property def wind_directions(self): """ Wind direction information. Returns: np.array: Wind direction. """ return self.core.flow_field.wind_directions @property def wind_speeds(self): """ Wind speed information. Returns: np.array: Wind speed. """ return self.core.flow_field.wind_speeds @property def turbulence_intensities(self): """ Turbulence intensity information. Returns: np.array: Turbulence intensity. """ return self.core.flow_field.turbulence_intensities @property def n_findex(self): """ Number of floris indices (findex). Returns: int: Number of flow indices. """ return self.core.flow_field.n_findex @property def n_turbines(self): """ Number of turbines. Returns: int: Number of turbines. """ return self.core.farm.n_turbines @property def reference_wind_height(self): """ Reference wind height. Returns: float: Reference wind height. """ return self.core.flow_field.reference_wind_height @property def turbine_average_velocities(self) -> NDArrayFloat: return average_velocity( velocities=self.core.flow_field.u, method=self.core.grid.average_method, cubature_weights=self.core.grid.cubature_weights, ) @property def wind_data(self): return self._wind_data ### v3 functions that are removed - raise an error if used def calculate_wake(self, **_): raise NotImplementedError( "The calculate_wake method has been removed. Please use the run method. " "See https://natlabrockies.github.io/floris/v3_to_v4.html for more information." ) def reinitialize(self, **_): raise NotImplementedError( "The reinitialize method has been removed. Please use the set method. " "See https://natlabrockies.github.io/floris/v3_to_v4.html for more information." ) @staticmethod def merge_floris_models(fmodel_list, reference_wind_height=None): """Merge a list of FlorisModel objects into a single FlorisModel object. Note that it uses the first object specified in fmodel_list to build upon, so it uses those wake model parameters, air density, and so on. Currently, this function supports merging the following components of the FLORIS inputs: - farm - layout_x - layout_y - turbine_type - flow_field - reference_wind_height Args: fmodel_list (list): Array-like of FlorisModel objects. reference_wind_height (float, optional): Height in meters at which the reference wind speed is assigned. If None, will assume this value is equal to the reference wind height specified in the FlorisModel objects. This only works if all objects have the same value for their reference_wind_height. Returns: fmodel_merged (FlorisModel): The merged FlorisModel object, merged in the same order as fmodel_list. The objects are merged on the turbine locations and turbine types, but not on the wake parameters or general solver settings. """ if not all( type(fm) is FlorisModel for fm in fmodel_list ): raise TypeError( "Incompatible input specified. fmodel_list must be a list of FlorisModel objects." ) # Get the turbine locations and specifications for each subset and save as a list x_list = [] y_list = [] turbine_type_list = [] reference_wind_heights = [] for fmodel in fmodel_list: # Remove any control setpoints that might be specified for the turbines on one fmodel fmodel.reset_operation() x_list.extend(fmodel.layout_x) y_list.extend(fmodel.layout_y) fmodel_turbine_type = fmodel.core.farm.turbine_type if len(fmodel_turbine_type) == 1: fmodel_turbine_type = fmodel_turbine_type * len(fmodel.layout_x) elif not len(fmodel_turbine_type) == len(fmodel.layout_x): raise ValueError("Incompatible format of turbine_type in fmodel.") turbine_type_list.extend(fmodel_turbine_type) reference_wind_heights.append(fmodel.core.flow_field.reference_wind_height) # Derive reference wind height, if unspecified by the user if reference_wind_height is None: reference_wind_height = np.mean(reference_wind_heights) if np.any(np.abs(np.array(reference_wind_heights) - reference_wind_height) > 1.0e-3): raise ValueError( "Cannot automatically derive a fitting reference_wind_height since they " "substantially differ between FlorisModel objects. " "Please specify 'reference_wind_height' manually." ) # Construct the merged FLORIS model based on the first entry in fmodel_list fmodel_merged = fmodel_list[0].copy() fmodel_merged.set( layout_x=x_list, layout_y=y_list, turbine_type=turbine_type_list, reference_wind_height=reference_wind_height, ) return fmodel_merged ================================================ FILE: floris/flow_visualization.py ================================================ import copy import warnings from typing import Union import attrs import matplotlib as mpl import matplotlib.colors as mplcolors import matplotlib.pyplot as plt import numpy as np import pandas as pd from attrs import define, field from matplotlib import rcParams from scipy.spatial import ConvexHull from floris import FlorisModel from floris.core import Core from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.cut_plane import CutPlane from floris.heterogeneous_map import HeterogeneousMap from floris.type_dec import ( floris_array_converter, NDArrayFloat, ) from floris.utilities import rotate_coordinates_rel_west, wind_delta def show(): """ Display all open figures. This is a wrapper for `plt.show()`. This function is useful if the user doesn't wish to import `matplotlib.pyplot` """ plt.show( ) def line_contour_cut_plane( cut_plane, ax=None, levels=None, colors=None, label_contours=False, **kwargs): """ Visualize a cut_plane as a line contour plot. Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): CutPlane Object. ax (:py:class:`matplotlib.pyplot.axes`): Figure axes. Defaults to None. levels (np.array, optional): Contour levels for plot. Defaults to None. colors (list, optional): Strings of color specification info. Defaults to None. label_contours (Boolean, optional): Flag to include a numerical contour labels on the plot. Defaults to False. **kwargs: Additional parameters to pass to `ax.contour`. """ if not ax: fig, ax = plt.subplots() rcParams["contour.negative_linestyle"] = "solid" # Plot the cut-through contours = ax.tricontour( cut_plane.df.x1, cut_plane.df.x2, cut_plane.df.u, levels=levels, colors=colors, extend="both", **kwargs, ) if label_contours: ax.clabel(contours, contours.levels, inline=True, fontsize=10, colors="black") # Make equal axis ax.set_aspect("equal") def visualize_cut_plane( cut_plane, ax=None, vel_component='u', min_speed=None, max_speed=None, cmap="coolwarm", levels=None, clevels=None, color_bar=False, label_contours=False, title="", **kwargs ): """ Generate pseudocolor mesh plot of the cut_plane. Horizontal cut planes are plotted with "North" pointing up the page (assuming the layout is defined with x pointing East and y pointing North). y planes are plotted with flows coming from the left (that is, the layout is rotated such that the wind is coming from the left). Cross planes are plotted "looking downstream". Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): 2D plane through wind plant. ax (:py:class:`matplotlib.pyplot.axes`, optional): Figure axes. Defaults to None. vel_component (str, optional): The velocity component that the cut plane is perpendicular to. min_speed (float, optional): Minimum value of wind speed for contours. Defaults to None. max_speed (float, optional): Maximum value of wind speed for contours. Defaults to None. cmap (str, optional): Colormap specifier. Defaults to 'coolwarm'. levels (np.array, optional): Contour levels for line contour plot. Defaults to None. clevels (np.array, optional): Contour levels for tricontourf plot. Defaults to None. color_bar (Boolean, optional): Flag to include a color bar on the plot. Defaults to False. label_contours (Boolean, optional): Flag to include a numerical contour labels on the plot. Defaults to False. title (str, optional): User-supplied title for the plot. Defaults to "". **kwargs: Additional parameters to pass to line contour plot. Returns: ax (:py:class:`matplotlib.pyplot.axes`): Figure axes. """ if not ax: fig, ax = plt.subplots() if vel_component=='u': # vel_mesh = cut_plane.df.u.values.reshape(cut_plane.resolution[1], cut_plane.resolution[0]) if min_speed is None: min_speed = cut_plane.df.u.min() if max_speed is None: max_speed = cut_plane.df.u.max() elif vel_component=='v': # vel_mesh = cut_plane.df.v.values.reshape(cut_plane.resolution[1], cut_plane.resolution[0]) if min_speed is None: min_speed = cut_plane.df.v.min() if max_speed is None: max_speed = cut_plane.df.v.max() elif vel_component=='w': # vel_mesh = cut_plane.df.w.values.reshape(cut_plane.resolution[1], cut_plane.resolution[0]) if min_speed is None: min_speed = cut_plane.df.w.min() if max_speed is None: max_speed = cut_plane.df.w.max() # Allow separate number of levels for tricontourf and for line_contour if clevels is None: clevels = levels # Plot the cut-through im = ax.tricontourf( cut_plane.df.x1, cut_plane.df.x2, cut_plane.df.u, vmin=min_speed, vmax=max_speed, levels=clevels, cmap=cmap, extend="both", ) # Add line contour line_contour_cut_plane( cut_plane, ax=ax, levels=levels, colors="b", label_contours=label_contours, linewidths=0.8, alpha=0.3, **kwargs ) if cut_plane.normal_vector == "x": ax.invert_xaxis() if color_bar: cbar = plt.colorbar(im, ax=ax) cbar.set_label('m/s') # Set the title ax.set_title(title) # Make equal axis ax.set_aspect("equal") return ax def visualize_heterogeneous_cut_plane( cut_plane, fmodel, ax=None, vel_component='u', min_speed=None, max_speed=None, cmap="coolwarm", levels=None, clevels=None, color_bar=False, label_contours=False, title="", plot_het_bounds=True, **kwargs ): """ Generate pseudocolor mesh plot of the heterogeneous cut_plane. Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): 2D plane through wind plant. fmodel (:py:class:`~.floris_model.FlorisModel`): FlorisModel object. ax (:py:class:`matplotlib.pyplot.axes`): Figure axes. Defaults to None. vel_component (str, optional): The velocity component that the cut plane is perpendicular to. min_speed (float, optional): Minimum value of wind speed for contours. Defaults to None. max_speed (float, optional): Maximum value of wind speed for contours. Defaults to None. cmap (str, optional): Colormap specifier. Defaults to 'coolwarm'. levels (np.array, optional): Contour levels for line contour plot. Defaults to None. clevels (np.array, optional): Contour levels for tricontourf plot. Defaults to None. color_bar (Boolean, optional): Flag to include a color bar on the plot. Defaults to False. label_contours (Boolean, optional): Flag to include a numerical contour labels on the plot. Defaults to False. title (str, optional): User-supplied title for the plot. Defaults to "". plot_het_bonds (boolean, optional): Flag to include the user-defined bounds of the heterogeneous wind speed area. Defaults to True. **kwargs: Additional parameters to pass to line contour plot. Returns: ax (:py:class:`matplotlib.pyplot.axes`): Figure axes. """ ax = visualize_cut_plane( cut_plane=cut_plane, ax=ax, vel_component=vel_component, min_speed=min_speed, max_speed=max_speed, cmap=cmap, levels=levels, clevels=clevels, color_bar=color_bar, label_contours=label_contours, title=title, **kwargs ) if plot_het_bounds: HeterogeneousMap.plot_heterogeneous_boundary( fmodel.core.flow_field.heterogeneous_inflow_config['x'], fmodel.core.flow_field.heterogeneous_inflow_config['y'], ax=ax ) return ax def visualize_quiver(cut_plane, ax=None, min_speed=None, max_speed=None, downSamp=1, **kwargs): """ Visualize the in-plane flows in a cut_plane using quiver. Args: cut_plane (:py:class:`~.tools.cut_plane.CutPlane`): 2D plane through wind plant. ax (:py:class:`matplotlib.pyplot.axes`): Figure axes. Defaults to None. min_speed (float, optional): Minimum value of wind speed for contours. Defaults to None. max_speed (float, optional): Maximum value of wind speed for contours. Defaults to None. downSamp (int, optional): Down sample the number of quiver arrows from underlying grid. **kwargs: Additional parameters to pass to `ax.streamplot`. Returns: im (:py:class:`matplotlib.plt.pcolormesh`): Image handle. """ if not ax: fig, ax = plt.subplots() # Reshape UMesh internally x1_mesh = cut_plane.df.x1.values.reshape(cut_plane.resolution[1], cut_plane.resolution[0]) x2_mesh = cut_plane.df.x2.values.reshape(cut_plane.resolution[1], cut_plane.resolution[0]) v_mesh = cut_plane.df.v.values.reshape(cut_plane.resolution[1], cut_plane.resolution[0]) w_mesh = cut_plane.df.w.values.reshape(cut_plane.resolution[1], cut_plane.resolution[0]) # plot the stream plot ax.streamplot( (x1_mesh[::downSamp, ::downSamp]), (x2_mesh[::downSamp, ::downSamp]), v_mesh[::downSamp, ::downSamp], w_mesh[::downSamp, ::downSamp], # scale=80.0, # alpha=0.75, # **kwargs ) # ax.quiverkey(QV1, -.75, -0.4, 1, '1 m/s', coordinates='data') # Make equal axis # ax.set_aspect('equal') def reverse_cut_plane_x_axis_in_plot(ax): """ Shortcut method to reverse direction of x-axis. Args: ax (:py:class:`matplotlib.pyplot.axes`): Figure axes. """ ax.invert_xaxis() def plot_rotor_values( values: np.ndarray, findex: int, n_rows: int, n_cols: int, t_range: range | None = None, cmap: str = "coolwarm", return_fig_objects: bool = False, save_path: Union[str, None] = None, show: bool = False ) -> Union[None, tuple[plt.figure, plt.axes, plt.axis, plt.colorbar]]: """ Plots the gridded turbine rotor values. This is intended to be used for understanding the differences between two sets of values, so each subplot can be used for inspection of what values are differing, and under what conditions. Parameters: values (np.ndarray): The 4-dimensional array of values to plot. Should be: (N findex, N turbines, N rotor points, N rotor points). findex (int): The index for the sample point to plot. n_rows (int): The number of rows to include for subplots. With ncols, this should generally add up to the number of turbines in the farm. n_cols (int): The number of columns to include for subplots. With ncols, this should generally add up to the number of turbines in the farm. t_range (range | None): Optional. The turbine count used to create the title for each subplot. If not provided, the size of the 2-th dimension of `values` is used. cmap (str): Optional. The matplotlib colormap to be used, default "coolwarm". return_fig_objects (bool): Optional. Flag to return the primary figure objects for further editing, default False. save_path (str | None): Optional. Where to save the figure, if a value is provided. show (bool): Optional. Flag to run `plt.show()` to present all the plots currently created with matplotlib. Returns: None | tuple[plt.figure, plt.axes, plt.axis, plt.colorbar]: If `return_fig_objects` is `False, then `None` is returned`, otherwise the primary figure objects are returned for custom editing. Example: from floris.visualization import plot_rotor_values plot_rotor_values(floris.flow_field.u, findex=0, n_rows=1, ncols=4) plot_rotor_values(floris.flow_field.v, findex=0, n_rows=1, ncols=4) plot_rotor_values(floris.flow_field.w, findex=0, n_rows=1, ncols=4, show=True) """ cmap = plt.get_cmap(name=cmap) if t_range is None: t_range = range(values.shape[1]) fig = plt.figure() axes = fig.subplots(n_rows, n_cols) # For 1x1, fig.subplots returns an Axes object, but for more than 1x1 it returns a np.array. # In this case, convert to an array so that the rest of this function can be simplified. if n_rows == 1 and n_cols == 1: axes = np.array([axes]) titles = np.array([f"tindex: {i}" for i in t_range]) for ax, t, i in zip(axes.flatten(), titles, t_range): vmin = np.min(values[findex]) vmax = np.max(values[findex]) norm = mplcolors.Normalize(vmin, vmax) ax.imshow(values[findex, i].T, cmap=cmap, norm=norm, origin="lower") ax.invert_xaxis() ax.set_xticks([]) ax.set_yticks([]) ax.set_title(t) fig.subplots_adjust(right=0.8) cbar_ax = fig.add_axes([0.83, 0.25, 0.03, 0.5]) cb = fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap), cax=cbar_ax) if save_path: plt.savefig(save_path, bbox_inches="tight") if return_fig_objects: return fig, axes, cbar_ax, cb if show: plt.show() def calculate_horizontal_plane_with_turbines( fmodel, x_resolution=200, y_resolution=200, x_bounds=None, y_bounds=None, findex_for_viz=None, ) -> CutPlane: """ This function creates a :py:class:`~.tools.cut_plane.CutPlane` by adding an additional turbine to the farm and moving it through every a regular grid throughout the flow field. This method allows for visualizing wake models that do not support the FullFlowGrid and its associated solver. As the new turbine is moved around the flow field, the velocities at its rotor are stored in local variables, and the flow field is reset to its initial state for every new location. Then, the local velocities are put into a DataFrame and then into a CutPlane. This method is much slower than `FlorisModel.calculate_horizontal_plane`, but it is helpful for models where the visualization capability is not yet available. Args: fmodel (:py:class:`floris.floris_model.FlorisModel`): Preinitialized FlorisModel object. x_resolution (float, optional): Output array resolution. Defaults to 200 points. y_resolution (float, optional): Output array resolution. Defaults to 200 points. x_bounds (tuple, optional): Limits of output array (in m). Defaults to None. y_bounds (tuple, optional): Limits of output array (in m). Defaults to None. findex_for_viz (int, optional): Index of the condition to visualize. Returns: :py:class:`~.tools.cut_plane.CutPlane`: containing values of x, y, u, v, w """ if fmodel.core.flow_field.n_findex > 1 and findex_for_viz is None: print( "Multiple findices detected. Using first findex for visualization." ) if findex_for_viz is None: findex_for_viz = 0 # Make a local copy of fmodel to avoid editing passed in fmodel fmodel_viz = copy.deepcopy(fmodel) # Set the ws and wd fmodel_viz.set_for_viz(findex_for_viz, None) yaw_angles = fmodel_viz.core.farm.yaw_angles power_setpoints = fmodel_viz.core.farm.power_setpoints awc_modes = fmodel_viz.core.farm.awc_modes awc_amplitudes = fmodel_viz.core.farm.awc_amplitudes awc_frequencies = fmodel_viz.core.farm.awc_frequencies # Grab the turbine layout layout_x = copy.deepcopy(fmodel_viz.layout_x) layout_y = copy.deepcopy(fmodel_viz.layout_y) turbine_types = copy.deepcopy(fmodel_viz.core.farm.turbine_type) D = fmodel_viz.core.farm.rotor_diameters_sorted[0, 0] # Declare a new layout array with an extra turbine layout_x_test = np.append(layout_x,[0]) layout_y_test = np.append(layout_y,[0]) # Declare turbine types with an extra turbine in case of special one-type usage if len(layout_x) > 1 and len(turbine_types) == 1: # Convert to list length len(layout_x) + 1 turbine_types_test = [turbine_types[0] for i in range(len(layout_x))] + ['nrel_5MW'] else: turbine_types_test = np.append(turbine_types, 'nrel_5MW').tolist() yaw_angles = np.append( yaw_angles, np.zeros([fmodel_viz.core.flow_field.n_findex, 1]), axis=1 ) power_setpoints = np.append( power_setpoints, POWER_SETPOINT_DEFAULT * np.ones([fmodel_viz.core.flow_field.n_findex, 1]), axis=1 ) awc_modes = np.append( awc_modes, np.full((fmodel_viz.core.flow_field.n_findex, 1), "baseline"), axis=1 ) awc_amplitudes = np.append( awc_amplitudes, np.zeros([fmodel_viz.core.flow_field.n_findex, 1]), axis=1 ) awc_frequencies = np.append( awc_frequencies, np.zeros([fmodel_viz.core.flow_field.n_findex, 1]), axis=1 ) # Get a grid of points test test if x_bounds is None: x_bounds = (np.min(layout_x) - 2 * D, np.max(layout_x) + 10 * D) if y_bounds is None: y_bounds = (np.min(layout_y) - 2 * D, np.max(layout_y) + 2 * D) # Now generate a list of points x_points = np.linspace(x_bounds[0], x_bounds[1], x_resolution) y_points = np.linspace(y_bounds[0], y_bounds[1], y_resolution) num_points = len(x_points) * len(y_points) # Now loop over the points x_results = np.zeros(num_points) y_results = np.zeros(num_points) z_results = np.zeros(num_points) u_results = np.zeros(num_points) v_results = np.zeros(num_points) w_results = np.zeros(num_points) idx = 0 for y in y_points: for x in x_points: # Save the x and y results x_results[idx] = x y_results[idx] = y # Place the test turbine at this location and calculate wake layout_x_test[-1] = x layout_y_test[-1] = y fmodel_viz.set( layout_x=layout_x_test, layout_y=layout_y_test, yaw_angles=yaw_angles, power_setpoints=power_setpoints, awc_modes=awc_modes, awc_amplitudes=awc_amplitudes, awc_frequencies=awc_frequencies, turbine_type=turbine_types_test, reference_wind_height=fmodel_viz.reference_wind_height ) fmodel_viz.run() # Get the velocity of that test turbines central point center_point = int(np.floor(fmodel_viz.core.flow_field.u[0,-1].shape[0] / 2.0)) u_results[idx] = fmodel_viz.core.flow_field.u[0,-1,center_point,center_point] # Increment index idx = idx + 1 # Make a dataframe df = pd.DataFrame({ 'x1':x_results, 'x2':y_results, 'x3':z_results, 'u':u_results, 'v':v_results, 'w':w_results, }) # Convert to a cut_plane horizontal_plane = CutPlane(df, x_resolution, y_resolution, "z") return horizontal_plane @define class VelocityProfilesFigure(): """ Create a figure which displays velocity deficit profiles at several downstream locations of a turbine. Args: downstream_dists_D: A list/array of streamwise locations at which the velocity deficit profiles have been sampled. The locations should be normalized by the turbine diameter D. layout: A one- or two-element list defining the direction of the profiles and in which order the directions are plotted. For example, ['cross-stream', 'vertical'] initializes a figure where cross-stream profiles are expected on the top row of Axes in the figure, and vertical profiles are expected on the bottom row. ax_width: Roughly the width of each Axes. ax_height: Roughly the height of each Axes. coordinate_labels: A list of labels for the normalized coordinates. """ downstream_dists_D: NDArrayFloat = field(converter=floris_array_converter) layout: list[str] = field(default=['cross-stream']) ax_width: float = field(default=2.07) ax_height: float = field(default=3.0) coordinate_labels: list[str] = field(default=['x_1/D', 'x_2/D', 'x_3/D']) n_rows: int = field(init=False) n_cols: int = field(init=False) fig: plt.Figure = field(init=False) axs: np.ndarray = field(init=False) deficit_max: float = field(init=False, default=0.0) def __attrs_post_init__(self) -> None: self.n_rows = len(self.layout) self.n_cols = len(self.downstream_dists_D) figsize = [0.7 + self.ax_width * self.n_cols, 1.0 + self.ax_height * self.n_rows] self.fig, self.axs = plt.subplots( self.n_rows, self.n_cols, figsize=figsize, layout='tight', sharex='col', sharey='row', squeeze=False, ) for ax in self.axs[-1]: ax.set_xlabel(r'$\Delta U / U_\infty$', fontsize=14) ax.tick_params('x', labelsize=14) for ax, x1_D in zip(self.axs[0], self.downstream_dists_D): ax.set_title(f'${self.coordinate_labels[0]} = {x1_D:.1f}$', fontsize=14) for ax, profile_direction in zip(self.axs[:,0], self.layout): if profile_direction == 'cross-stream': ylabel = f'${self.coordinate_labels[1]}$' elif profile_direction == 'vertical': ylabel = f'${self.coordinate_labels[2]}$' ax.set_ylabel(ylabel, fontsize=14) ax.tick_params('y', labelsize=14) @layout.validator def layout_validator(self, instance : attrs.Attribute, value : list[str]) -> None: allowed_layouts = [ ['cross-stream'], ['vertical'], ['cross-stream', 'vertical'], ['vertical', 'cross-stream'], ] if value not in allowed_layouts: raise ValueError(f"'layout' must be one of the following: {allowed_layouts}.") def add_profiles( self, velocity_deficit_profiles: list[pd.DataFrame], **kwargs ) -> None: """ Add a list of velocity deficit profiles to the figure. Each profile is represented as a pandas DataFrame. `kwargs` are passed to `ax.plot`. """ for df in velocity_deficit_profiles: ax, profile_direction = self.match_profile_to_axes(df) profile_direction_D = f'{profile_direction}/D' ax.plot(df['velocity_deficit'], df[profile_direction_D], **kwargs) self.deficit_max = max(self.deficit_max, df['velocity_deficit'].max()) margin = 0.05 self.set_xlim([0.0 - margin, self.deficit_max + margin]) def match_profile_to_axes( self, df: pd.DataFrame, ) -> tuple[plt.Axes, str]: x1_D = np.unique(df['x1/D']) if len(x1_D) == 1: x1_D = x1_D[0] else: raise ValueError( "The streamwise location x1/D must be constant for each velocity profile." ) unique_x2 = np.unique(df['x2/D']) unique_x3 = np.unique(df['x3/D']) if len(unique_x2) == 1: profile_direction = 'x3' profile_direction_name = 'vertical' elif len(unique_x3) == 1: profile_direction = 'x2' profile_direction_name = 'cross-stream' else: raise ValueError( f"Velocity deficit profile at x1/D = {x1_D} is neither in the cross-stream (x2) " "nor the vertical (x3) direction." ) row = self.layout.index(profile_direction_name) col = None for i in range(self.n_cols): if np.abs(x1_D - self.downstream_dists_D[i]) < 0.001: col = i break if col is None: raise ValueError( "Could not add a velocity deficit profile at downstream distance " f"x1/D = {x1_D}. The downstream distance must be one of the following " "values with which this VelocityProfilesFigure object was initialized: " f"{self.downstream_dists_D}." ) return self.axs[row,col], profile_direction def set_xlim( self, xlim: list[float] | NDArrayFloat, ) -> None: for ax in self.axs[-1]: ax.set_xlim(xlim) def add_ref_lines_x2( self, ref_lines_x2_D: list[float] | NDArrayFloat, **kwargs ) -> None: """ Add reference lines to the VelocityProfilesFigure which go along the XAxis. Commonly used to show the extent of the turbine. Args: ref_lines_x2_D: A list of x2-coordinates normalized by the turbine diameter D. One coordinate per reference line. **kwargs: Additional parameters to pass to `ax.plot`. """ if 'cross-stream' not in self.layout: raise Exception( "Could not add reference lines to cross-stream (x2) velocity profiles. No " "such profiles exist in the figure." ) row_x2 = self.layout.index('cross-stream') self.add_ref_lines(ref_lines_x2_D, row_x2, **kwargs) def add_ref_lines_x3( self, ref_lines_x3_D: list[float] | NDArrayFloat, **kwargs ) -> None: """ Add reference lines to the VelocityProfilesFigure which go along the XAxis. Commonly used to show the extent of the turbine. Args: ref_lines_x3_D: A list of x3-coordinates normalized by the turbine diameter D. One coordinate per reference line. **kwargs: Additional parameters to pass to `ax.plot`. """ if 'vertical' not in self.layout: raise Exception( "Could not add reference lines to vertical (x3) velocity profiles. No " "such profiles exist in the figure." ) row_x3 = self.layout.index('vertical') self.add_ref_lines(ref_lines_x3_D, row_x3, **kwargs) def add_ref_lines( self, ref_lines_D: list[float] | NDArrayFloat, row: int, **kwargs ) -> None: default_params = { 'linestyle': (0, (4, 2)), 'color': 'k', 'linewidth': 1.1 } params = copy.deepcopy(default_params) params.update(kwargs) for ax in self.axs[row]: for coordinate in ref_lines_D: ax.plot([0.0, 1.0], [coordinate, coordinate], **params) ================================================ FILE: floris/heterogeneous_map.py ================================================ import matplotlib.cm as cm import matplotlib.pyplot as plt import numpy as np import pandas as pd import scipy.spatial._qhull from scipy.interpolate import LinearNDInterpolator, NearestNDInterpolator from scipy.spatial import ConvexHull from floris.core.flow_field import FlowField from floris.logging_manager import LoggingManager from floris.type_dec import NDArrayFloat class HeterogeneousMap(LoggingManager): """ Class for handling heterogeneous inflow configurations when defined by wind direction and wind speed. Args: x (NDArrayFloat): A 1D NumPy array (size num_points) of x-coordinates (meters). y (NDArrayFloat): A 1D NumPy array (size num_points) of y-coordinates (meters). speed_multipliers (NDArrayFloat): A 2D NumPy array (size num_wd (or num_ws) x num_points) of speed multipliers. If neither wind_directions nor wind_speeds are defined, then this should be a single row array z (NDArrayFloat, optional): A 1D NumPy array (size num_points) of z-coordinates (meters). Optional. wind_directions (NDArrayFloat, optional): A 1D NumPy array (size num_wd) of wind directions (degrees). Optional. wind_speeds (NDArrayFloat, optional): A 1D NumPy array (size num_ws) of wind speeds (m/s). Optional. interp_method (str, optional): Interpolation method used to calculate the heterogeneous wind speeds at various locations in the wind farm. Options are 'linear' and 'nearest', representing linear interpolation and nearest-neighbor interpolation respectively. Linear interpolation is accurate, nearest-neighbor interpolation is very fast but inaccurate. Note also that in the default 'linear' setting, speed-ups at locations outside the convex hull of points defined by `x`, `y` and `z` are 1.0 while in the nearest case will be the value of the nearest point. Defaults to 'linear'. Optional. Notes: * If wind_directions and wind_speeds are both defined, then they must be the same length and equal the length of the 0th dimension of 'speed_multipliers'. """ def __init__( self, x: NDArrayFloat, y: NDArrayFloat, speed_multipliers: NDArrayFloat, z: NDArrayFloat = None, wind_directions: NDArrayFloat = None, wind_speeds: NDArrayFloat = None, interp_method: str = "linear", ): # Check that x, y and speed_multipliers are lists or numpy arrays if not isinstance(x, (list, np.ndarray)): raise TypeError("x must be a numpy array or list") if not isinstance(y, (list, np.ndarray)): raise TypeError("y must be a numpy array or list") if not isinstance(speed_multipliers, (list, np.ndarray)): raise TypeError("speed_multipliers must be a numpy array or list") # If z is provided, check that it is a list or numpy array if (z is not None) and (not isinstance(z, (list, np.ndarray))): raise TypeError("z must be a numpy array or list") # Save the values self.x = np.array(x) self.y = np.array(y) self.speed_multipliers = np.array(speed_multipliers) self.interp_method = str(interp_method) # If z is provided, save it as an np array if z is not None: self.z = np.array(z) else: self.z = None # Check that the length of the 1st dimension of speed_multipliers is the # same as the length of both x and y if ( len(self.x) != self.speed_multipliers.shape[1] or len(self.y) != self.speed_multipliers.shape[1] ): raise ValueError( "The lengths of x and y must equal the 1th dimension of speed_multipliers " ) # If z is provided, check that it is the same length as the 1st # dimension of speed_multipliers if self.z is not None: if len(self.z) != self.speed_multipliers.shape[1]: raise ValueError( "The length of z must equal the 1th dimension of speed_multipliers " ) # If wind_directions is note None, check that it is valid then save it if wind_directions is not None: if not isinstance(wind_directions, (list, np.ndarray)): raise TypeError("wind_directions must be a numpy array or list") # Check that length of wind_directions is the same as the length of the 0th # dimension of speed_multipliers if len(wind_directions) != self.speed_multipliers.shape[0]: raise ValueError( "The length of wind_directions must equal " "the 0th dimension of speed_multipliers" "Within the heterogeneous_inflow_config_by_wd dictionary" ) self.wind_directions = np.array(wind_directions) else: self.wind_directions = None # If wind_speeds is not None, check that it is valid then save it if wind_speeds is not None: if not isinstance(wind_speeds, (list, np.ndarray)): raise TypeError("wind_speeds must be a numpy array or list") # Check that length of wind_speeds is the same as the length of the 0th # dimension of speed_multipliers if len(wind_speeds) != self.speed_multipliers.shape[0]: raise ValueError( "The length of wind_speeds must equal " "the 0th dimension of speed_multipliers" "Within the heterogeneous_inflow_config_by_wd dictionary" ) self.wind_speeds = np.array(wind_speeds) else: self.wind_speeds = None # If both wind_directions and wind_speeds are None, then speed_multipliers should be # length 1 in 0th dimension if self.wind_speeds is None and self.wind_directions is None: if self.speed_multipliers.shape[0] != 1: raise ValueError( "If both wind_speeds and wind_directions are None, then speed_multipliers " "should be length 1 in 0th dimension." ) # If both wind_directions and wind_speeds are not None, then make sure each row # of a matrix where wind directions and wind speeds are the columns is unique if self.wind_speeds is not None and self.wind_directions is not None: if len( np.unique(np.column_stack((self.wind_directions, self.wind_speeds)), axis=0) ) != len(self.wind_directions): raise ValueError( "Each row of a matrix where wind directions and wind speeds are the columns " "should be unique." ) def __str__(self) -> str: """ Return a string representation of the HeterogeneousMap. Returns: str: A string representation of the HeterogeneousMap. """ if self.z is None: num_dim = 2 else: num_dim = 3 # Make a pandas dataframe of the data df = pd.DataFrame( data=self.speed_multipliers, index=self.wind_directions, columns=list(range(len(self.x))) ) return ( f"HeterogeneousMap with {num_dim} dimensions " f"using interpolation method \"{self.interp_method}\".\n" f"Speed multipliers are defined for {len(self.x)} points and " f"{self.speed_multipliers.shape[0]} wind conditions." f"\n\n{df}" ) def get_heterogeneous_inflow_config( self, wind_directions: NDArrayFloat | list[float], wind_speeds: NDArrayFloat | list[float], ): """ Get the heterogeneous inflow configuration for the given wind directions and wind speeds. Args: wind_directions (NDArrayFloat | list[float]): A 1D NumPy array or list of wind directions (degrees). wind_speeds (NDArrayFloat | list[float]): A 1D NumPy array or list of wind speeds (m/s). Returns: dict: A dictionary (heterogeneous_inflow_config) containing the x, y, and speed_multipliers for the given wind directions and wind speeds. """ # Check the wind_directions and wind_speeds are either lists or numpy arrays, # and are the same length if not isinstance(wind_directions, (list, np.ndarray)): raise TypeError("wind_directions must be a list or numpy array") if not isinstance(wind_speeds, (list, np.ndarray)): raise TypeError("wind_speeds must be a list or numpy array") if len(wind_directions) != len(wind_speeds): raise ValueError("wind_directions and wind_speeds must be the same length") # Select for wind direction first if self.wind_directions is not None: angle_diffs = np.abs(wind_directions[:, None] - self.wind_directions) min_angle_diffs = np.minimum(angle_diffs, 360 - angle_diffs) # If wind_speeds is none, can return the value in each case if self.wind_speeds is None: closest_wd_indices = np.argmin(min_angle_diffs, axis=1) # Construct the output array using the calculated indices speed_multipliers_by_findex = self.speed_multipliers[closest_wd_indices] # Need to loop over cases and match by wind speed else: speed_diffs = np.abs(wind_speeds[:, None] - self.wind_speeds) # Initialize the output array speed_multipliers_by_findex = np.zeros((len(wind_directions), len(self.x))) # Loop over each wind direction for i in range(len(wind_directions)): # Find all the indices in the ith row of min_angle_diffs # that are equal to the minimum value closest_wd_indices = np.where(min_angle_diffs[i] == min_angle_diffs[i].min())[0] # Find the index of the minimum value in the ith row of speed_diffs # conditions on that index being in closest_wd_indices closest_ws_index = np.argmin(speed_diffs[i, closest_wd_indices]) # Construct the output array using the calculated indices speed_multipliers_by_findex[i] = self.speed_multipliers[ closest_wd_indices[closest_ws_index] ] # If wind speeds are defined without wind direction elif self.wind_speeds is not None: speed_diffs = np.abs(wind_speeds[:, None] - self.wind_speeds) closest_ws_indices = np.argmin(speed_diffs, axis=1) # Construct the output array using the calculated indices speed_multipliers_by_findex = self.speed_multipliers[closest_ws_indices] # Else if both are None, then speed_multipliers should be length 1 in 0th # dimension and so just 1 row # repeat this row until length of wind_directions else: speed_multipliers_by_findex = np.repeat( self.speed_multipliers, len(wind_directions), axis=0 ) # Return heterogeneous_inflow_config with only x and y is z is not defined if self.z is None: return { "x": self.x, "y": self.y, "speed_multipliers": speed_multipliers_by_findex, "interp_method": self.interp_method, } else: return { "x": self.x, "y": self.y, "z": self.z, "speed_multipliers": speed_multipliers_by_findex, "interp_method": self.interp_method, } def get_heterogeneous_map_2d(self, z: float): """ Return a HeterogeneousMap with only x and y coordinates and a constant z value. Do this by selecting from x, y and speed_multipliers where z is nearest to the given value. """ if self.z is None: raise ValueError("No z values defined in the HeterogeneousMap") # Find the value in self.z that is closest to the given z value closest_z_index = np.argmin(np.abs(self.z - z)) # Get the indices of all the values in self.z that are equal to the closest value closest_z_indices = np.where(self.z == self.z[closest_z_index])[0] # Get versions of x, y and speed_multipliers that include only the closest z values # by selecting the indices in closest_z_indices x = self.x[closest_z_indices] y = self.y[closest_z_indices] speed_multipliers = self.speed_multipliers[:, closest_z_indices] # Return a new HeterogeneousMap with the new x, y and speed_multipliers return HeterogeneousMap( x=x, y=y, speed_multipliers=speed_multipliers, wind_directions=self.wind_directions, wind_speeds=self.wind_speeds, interp_method=self.interp_method, ) @staticmethod def plot_heterogeneous_boundary(x, y, ax=None): """ Plot the boundary of the heterogeneous inflow configuration. Args: x (NDArrayFloat): A 1D NumPy array of x-coordinates (meters). y (NDArrayFloat): A 1D NumPy array of y-coordinates (meters). ax (matplotlib.axes.Axes, optional): The axes on which to plot the boundary. If None, a new figure and axes will be created. """ # If not provided create the axis if ax is None: _, ax = plt.subplots() # Get the x and y coordinates of the het map points = np.array( list( zip( x, y, ) ) ) # Derive and plot the convex hull surrounding the points hull = ConvexHull(points) ax.plot( points[np.append(hull.vertices, hull.vertices[0]), 0], points[np.append(hull.vertices, hull.vertices[0]), 1], "--", color="gray", label="Heterogeneity Boundary", ) def plot_wind_direction(self, ax: plt.Axes, wind_direction: float): """ Plot the wind direction as an arrow on the plot. Args: ax (matplotlib.axes.Axes): The axes on which to plot the wind direction. wind_direction (float): The wind direction to plot. """ # Get the x and y limits of the axis xlim = ax.get_xlim() ylim = ax.get_ylim() # Find a point in the top-left corner of the plot xm = xlim[0] + 0.2 * (xlim[1] - xlim[0]) ym = ylim[1] - 0.2 * (ylim[1] - ylim[0]) # Select a radius for the circle 5% the plot width radius = 0.075 * (xlim[1] - xlim[0]) theta = np.linspace(0.0, 2 * np.pi, 100) xcirc = np.cos(theta) * radius + xm ycirc = np.sin(theta) * radius + ym ax.scatter(xm, ym, color="k", marker="o") ax.plot(xcirc, ycirc, color="w", linewidth=2) ax.arrow( x=xm - np.cos(-(wind_direction - 270.0) * np.pi / 180.0) * radius, y=ym - np.sin(-(wind_direction - 270.0) * np.pi / 180.0) * radius, dx=1 * np.cos(-(wind_direction - 270.0) * np.pi / 180.0) * radius, dy=1 * np.sin(-(wind_direction - 270.0) * np.pi / 180.0) * radius, width=0.125 * radius, head_width=0.3 * radius, head_length=0.3 * radius, length_includes_head=True, color="w", ) def plot_single_speed_multiplier( self, wind_direction: float, wind_speed: float, z: float = None, ax: plt.Axes = None, vmin: float = None, vmax: float = None, cmap: cm = cm.viridis, show_boundary: bool = True, show_wind_direction: bool = True, show_colorbar: bool = True, show_points: bool = True, ): """ Plot the speed multipliers as a heatmap. Args: wind_direction (float): The wind direction for which to plot the speed multipliers. wind_speed (float): The wind speed for which to plot the speed multipliers. z (float, optional): The z-coordinate for which to plot the speed multipliers. If None, the z-coordinate is not used. Only for when z is defined in the HeterogeneousMap. ax (matplotlib.axes.Axes, optional): The axes on which to plot the speed multipliers. If None, a new figure and axes will be created. vmin (float, optional): The minimum value for the colorbar. Default is the minimum value of the speed multipliers. vmax (float, optional): The maximum value for the colorbar. Default is the maximum value of the speed multipliers. cmap (matplotlib.colors.Colormap, optional): The colormap to use for the heatmap. Default is matplotlib.cm.viridis. show_boundary (bool, optional): Whether to show the boundary of the heterogeneous inflow configuration. Default is True. show_wind_direction (bool, optional): Whether to show the wind direction as an arrow. Default is True. show_colorbar (bool, optional): Whether to show the colorbar. Default is True. show_points (bool, optional): Whether to show the points of the heterogeneous inflow configuration. Default is True. Returns: matplotlib.axes.Axes: The axes on which the speed multipliers are plotted. """ # Confirm wind_direction and wind_speed are floats if not isinstance(wind_direction, float): raise TypeError("wind_direction must be a float") if not isinstance(wind_speed, float): raise TypeError("wind_speed must be a float") # If self.z is None, then z should be None if self.z is None and z is not None: raise ValueError("No z values defined in the HeterogeneousMap") # If self.z is defined and has more than one unique value, then z should be defined if self.z is not None and len(np.unique(self.z)) > 1 and z is None: raise ValueError( "Multiple z values defined in the HeterogeneousMap. z must be provided" ) # Get the 2d version at height z if z is defined and get the speed multiplier from there # as well as x and y if z is not None: hm_2d = self.get_heterogeneous_map_2d(z) x = hm_2d.x y = hm_2d.y speed_multiplier_row = hm_2d.get_heterogeneous_inflow_config( np.array([wind_direction]), np.array([wind_speed]) )["speed_multipliers"][0] else: x = self.x y = self.y # Get the speed multipliers for the given wind direction and wind speed speed_multiplier_row = self.get_heterogeneous_inflow_config( np.array([wind_direction]), np.array([wind_speed]) )["speed_multipliers"][0] # If not provided create the axis if ax is None: fig, ax = plt.subplots() else: fig = ax.get_figure() # Get some boundary info min_x = np.min(x) max_x = np.max(x) min_y = np.min(y) max_y = np.max(y) delta_x = max_x - min_x delta_y = max_y - min_y plot_min_x = min_x - 0.1 * delta_x plot_max_x = max_x + 0.1 * delta_x plot_min_y = min_y - 0.1 * delta_y plot_max_y = max_y + 0.1 * delta_y # Fill in the plot area x_plot, y_plot = np.meshgrid( np.linspace(plot_min_x, plot_max_x, 100), np.linspace(plot_min_y, plot_max_y, 100), indexing="ij", ) try: interpolant = FlowField.interpolate_multiplier_xy( x, y, speed_multiplier_row, fill_value=1.0, interp_method=self.interp_method ) het_map_mesh = interpolant(x_plot.flatten(), y_plot.flatten()).reshape(x_plot.shape) except scipy.spatial._qhull.QhullError: self.logger.warning( "QhullError occurred in computing visualize. Creating null visualization." ) het_map_mesh = np.nan * np.ones_like(x_plot) # If vmin is not provided, use a value rounded to the nearest 0.01 below the minimum if vmin is None: vmin = np.floor(het_map_mesh.min() * 100) / 100 # If vmax is not provided, use a value rounded to the nearest 0.01 above the maximum if vmax is None: vmax = np.ceil(het_map_mesh.max() * 100) / 100 # Produce color plot of the speed multipliers im = ax.contourf( x_plot, y_plot, het_map_mesh, cmap=cmap, vmin=vmin, vmax=vmax, levels=50, zorder=-1, ) # Plot the grid coordinates as a scatter plot if show_points: ax.scatter(x, y, color="gray", marker=".", label="Heterogeneity Coordinates") # Show the boundary if show_boundary: self.plot_heterogeneous_boundary(self.x, self.y, ax) # Add a colorbar if show_colorbar: fig.colorbar(im, ax=ax) # Set the x and y limits ax.set_xlim(plot_min_x, plot_max_x) ax.set_ylim(plot_min_y, plot_max_y) # Make equal axis ax.set_aspect("equal") # Set the x and y labels ax.set_xlabel("X (m)") ax.set_ylabel("Y (m)") # Add the wind direction arrow if show_wind_direction: self.plot_wind_direction(ax, wind_direction) return ax ================================================ FILE: floris/layout_visualization.py ================================================ import math from typing import ( Any, Dict, List, Tuple, ) import matplotlib.lines import matplotlib.pyplot as plt import numpy as np import pandas as pd from scipy.spatial.distance import pdist, squareform from floris import FlorisModel from floris.utilities import rotate_coordinates_rel_west, wind_delta def plot_turbine_points( fmodel: FlorisModel, ax: plt.Axes = None, turbine_indices: List[int] = None, plotting_dict: Dict[str, Any] = {}, ) -> plt.Axes: """ Plots turbine layout from a FlorisModel object. Args: fmodel (FlorisModel): The FlorisModel object containing layout data. ax (plt.Axes, optional): An existing axes object to plot on. If None, a new figure and axes will be created. Defaults to None. turbine_indices (List[int], optional): A list of turbine indices to plot. If None, all turbines will be plotted. Defaults to None. plotting_dict (Dict[str, Any], optional): A dictionary to customize plot appearance. Valid keys include: * 'color' (str): Turbine marker color. Defaults to 'black'. * 'marker' (str): Turbine marker style. Defaults to '.'. * 'markersize' (int): Turbine marker size. Defaults to 10. * 'label' (str): Label for the legend. Defaults to None. Returns: plt.Axes: The axes object used for the plot. Raises: IndexError: If any value in `turbine_indices` is an invalid turbine index. """ # Generate axis, if needed if ax is None: _, ax = plt.subplots() # If turbine_indices is not none, make sure all elements correspond to real indices if turbine_indices is not None: try: fmodel.layout_x[turbine_indices] except IndexError: raise IndexError("turbine_indices does not correspond to turbine indices in fi") else: turbine_indices = list(range(len(fmodel.layout_x))) # Generate plotting dictionary default_plotting_dict = { "color": "black", "marker": ".", "markersize": 10, "label": None, } plotting_dict = {**default_plotting_dict, **plotting_dict} # Plot ax.plot( fmodel.layout_x[turbine_indices], fmodel.layout_y[turbine_indices], linestyle="None", **plotting_dict, ) # Make sure axis set to equal ax.axis("equal") return ax def plot_turbine_labels( fmodel: FlorisModel, ax: plt.Axes = None, turbine_names: List[str] = None, turbine_indices: List[int] = None, label_offset: float = None, show_bbox: bool = False, bbox_dict: Dict[str, Any] = {}, plotting_dict: Dict[str, Any] = {}, ) -> plt.Axes: """ Adds turbine labels to a turbine layout plot. Args: fmodel (FlorisModel): The FlorisModel object containing layout data. ax (plt.Axes, optional): An existing axes object to plot on. If None, a new figure and axes will be created. Defaults to None. turbine_names (List[str], optional): Custom turbine labels. If None, defaults to turbine indices (e.g., '000', '001'). Defaults to None. turbine_indices (List[int], optional): Indices of turbines to label. If None, all turbines will be labeled. Defaults to None. label_offset (float, optional): Distance to offset labels from turbine points (in meters). If None, defaults to rotor_diameter/8. Defaults to None. show_bbox (bool, optional): If True, adds a bounding box around each label. Defaults to False. bbox_dict (Dict[str, Any], optional): Dictionary to customize the appearance of bounding boxes (if show_bbox is True). Valid keys include: * 'facecolor' (str): Box background color. Defaults to 'gray'. * 'alpha' (float): Opacity of box. Defaults to 0.5. * 'pad' (float): Padding around text. Defaults to 0.1. * 'boxstyle' (str): Box style (e.g., 'round'). Defaults to 'round'. plotting_dict (Dict[str, Any], optional): Dictionary to control text appearance. Valid keys include: * 'color' (str): Text color. Defaults to 'black'. Returns: plt.Axes: The axes object used for the plot. Raises: IndexError: If any value in `turbine_indices` is an invalid turbine index. ValueError: If the length of `turbine_names` does not match the number of turbines. """ # Generate axis, if needed if ax is None: _, ax = plt.subplots() # If turbine names not none, confirm has correct number of turbines if turbine_names is not None: if len(turbine_names) != len(fmodel.layout_x): raise ValueError( "Length of turbine_names not equal to number turbines in fmodel object" ) else: # Assign simple default numbering turbine_names = [f"{i:03d}" for i in range(len(fmodel.layout_x))] # If label_offset is None, use default value of r/8 if label_offset is None: rotor_diameters = fmodel.core.farm.rotor_diameters.flatten() r = rotor_diameters[0] / 2.0 label_offset = r / 8.0 # If turbine_indices is not none, make sure all elements correspond to real indices if turbine_indices is not None: try: fmodel.layout_x[turbine_indices] except IndexError: raise IndexError("turbine_indices does not correspond to turbine indices in fi") else: turbine_indices = list(range(len(fmodel.layout_x))) # Generate plotting dictionary default_plotting_dict = { "color": "black", "label": None, } plotting_dict = {**default_plotting_dict, **plotting_dict} # If showing bbox is true, if bbox_dict is None, use a default default_bbox_dict = {"facecolor": "gray", "alpha": 0.5, "pad": 0.1, "boxstyle": "round"} bbox_dict = {**default_bbox_dict, **bbox_dict} for ti in turbine_indices: if not show_bbox: ax.text( fmodel.layout_x[ti] + label_offset, fmodel.layout_y[ti] + label_offset, turbine_names[ti], **plotting_dict, ) else: ax.text( fmodel.layout_x[ti] + label_offset, fmodel.layout_y[ti] + label_offset, turbine_names[ti], bbox=bbox_dict, **plotting_dict, ) # Plot labels and aesthetics ax.axis("equal") return ax def plot_turbine_rotors( fmodel: FlorisModel, ax: plt.Axes = None, color: str = "k", wd: float = None, yaw_angles: np.ndarray = None, ) -> plt.Axes: """ Plots wind turbine rotors on an existing axes, visually representing their yaw angles. Args: fmodel (FlorisModel): The FlorisModel object containing layout and turbine data. ax (plt.Axes, optional): An existing axes object to plot on. If None, a new figure and axes will be created. Defaults to None. color (str, optional): Color of the turbine rotor lines. Defaults to 'k' (black). wd (float, optional): Wind direction (in degrees) relative to global reference. If None, the first wind direction in `fmodel.core.flow_field.wind_directions` is used. Defaults to None. yaw_angles (np.ndarray, optional): Array of turbine yaw angles (in degrees). If None, the values from `fmodel.core.farm.yaw_angles` are used. Defaults to None. Returns: plt.Axes: The axes object used for the plot. """ if not ax: _, ax = plt.subplots() if yaw_angles is None: yaw_angles = fmodel.core.farm.yaw_angles if wd is None: wd = fmodel.core.flow_field.wind_directions[0] # Rotate yaw angles to inertial frame for plotting turbines relative to wind direction yaw_angles = yaw_angles - wind_delta(np.array(wd)) if color is None: color = "k" # If yaw angles is not 1D, assume we want first findex yaw_angles = np.array(yaw_angles) if yaw_angles.ndim == 2: yaw_angles = yaw_angles[0, :] rotor_diameters = fmodel.core.farm.rotor_diameters.flatten() for x, y, yaw, d in zip(fmodel.layout_x, fmodel.layout_y, yaw_angles, rotor_diameters): R = d / 2.0 x_0 = x + np.sin(np.deg2rad(yaw)) * R x_1 = x - np.sin(np.deg2rad(yaw)) * R y_0 = y - np.cos(np.deg2rad(yaw)) * R y_1 = y + np.cos(np.deg2rad(yaw)) * R ax.plot([x_0, x_1], [y_0, y_1], color=color) return ax def get_wake_direction(x_i: float, y_i: float, x_j: float, y_j: float) -> float: """ Calculates the wind direction at which the wake of turbine i would impact turbine j. Args: x_i (float): X-coordinate of turbine i (the upstream turbine). y_i (float): Y-coordinate of turbine i. x_j (float): X-coordinate of turbine j (the downstream turbine). y_j (float): Y-coordinate of turbine j. Returns: float: Wind direction in degrees (0-360) where 0 degrees represents wind blowing from the north, and the angle increases clockwise. """ dx = x_j - x_i dy = y_j - y_i angle_rad = np.arctan2(dy, dx) # Adjust for "from" direction (add 180 degrees) and wrap within 0-360 angle_deg = 270 - np.rad2deg(angle_rad) wind_direction = angle_deg % 360 return wind_direction def label_line( line: matplotlib.lines.Line2D, label_text: str, ax: plt.Axes, near_i: int = None, near_x: float = None, near_y: float = None, rotation_offset: float = 0.0, offset: Tuple[float, float] = (0, 0), size: int = 7, ) -> None: """ Adds a text label to a matplotlib line, with options to specify label placement. Args: line (matplotlib.lines.Line2D): The line object to label. label_text (str): The text of the label. ax (plt.Axes): The axes object where the line is plotted. near_i (int, optional): Index near which to place the label. Defaults to None. near_x (float, optional): X-coordinate near which to place the label. Defaults to None. near_y (float, optional): Y-coordinate near which to place the label. Defaults to None. rotation_offset (float, optional): Additional rotation for the label (in degrees). Defaults to 0.0. offset (Tuple[float, float], optional): X and Y offset from the label position. Defaults to (0, 0). size (int, optional): Font size of the label. Defaults to 7. Raises: ValueError: If none of `near_i`, `near_x`, or `near_y` are provided to determine label placement. """ def put_label(i: int) -> None: """ Adds a label to a line segment within a plot (used internally by the 'label_line' function). Args: i (int): The index of the line segment where the label should be placed. The label will be positioned between points i and i+1. """ i = min(i, len(x) - 2) dx = sx[i + 1] - sx[i] dy = sy[i + 1] - sy[i] rotation = np.rad2deg(np.arctan2(dy, dx)) + rotation_offset pos = [(x[i] + x[i + 1]) / 2.0 + offset[0], (y[i] + y[i + 1]) / 2 + offset[1]] ax.text( pos[0], pos[1], label_text, size=size, rotation=rotation, color=line.get_color(), ha="center", va="center", bbox={"ec": "1", "fc": "1", "alpha": 0.8}, ) # extract line data x = line.get_xdata() y = line.get_ydata() # define screen spacing if ax.get_xscale() == "log": sx = np.log10(x) else: sx = x if ax.get_yscale() == "log": sy = np.log10(y) else: sy = y # find index if near_i is not None: i = near_i if i < 0: # sanitize negative i i = len(x) + i put_label(i) elif near_x is not None: for i in range(len(x) - 2): if (x[i] < near_x and x[i + 1] >= near_x) or (x[i + 1] < near_x and x[i] >= near_x): put_label(i) elif near_y is not None: for i in range(len(y) - 2): if (y[i] < near_y and y[i + 1] >= near_y) or (y[i + 1] < near_y and y[i] >= near_y): put_label(i) else: raise ValueError("Need one of near_i, near_x, near_y") def plot_waking_directions( fmodel: FlorisModel, ax: plt.Axes = None, turbine_indices: List[int] = None, wake_plotting_dict: Dict[str, Any] = {}, D: float = None, limit_dist_D: float = None, limit_dist_m: float = None, limit_num: int = None, wake_label_size: int = 7, ) -> plt.Axes: """ Plots lines representing potential waking directions between wind turbines in a layout. Args: fmodel (FlorisModel): Instantiated FlorisModel object containing layout data. ax (plt.Axes, optional): An existing axes object to plot on. If None, a new figure and axes will be created. Defaults to None. turbine_indices (List[int], optional): Indices of turbines to include in the plot. If None, all turbines are plotted. Defaults to None. wake_plotting_dict (Dict[str, Any], optional): Dictionary to customize the appearance of waking direction lines. Valid keys include: * 'color' (str): Line color. Defaults to 'black'. * 'linestyle' (str): Line style (e.g., 'solid', 'dashed'). Defaults to 'solid'. * 'linewidth' (float): Line width. Defaults to 0.5. D (float, optional): Rotor diameter. Used for distance calculations if `limit_dist_D` is provided. If None, defaults to the first turbine's rotor diameter. limit_dist_D (float, optional): Maximum distance between turbines (in rotor diameters) to plot waking lines. Defaults to None (no limit). limit_dist_m (float, optional): Maximum distance (in meters) between turbines to plot waking lines. Overrides `limit_dist_D` if provided. Defaults to None (no limit). limit_num (int, optional): Limits the number of waking lines plotted from each turbine to the `limit_num` closest neighbors. Defaults to None (no limit). wake_label_size (int, optional): Font size for labels showing wake distance and direction. Defaults to 7. Returns: plt.Axes: The axes object used for the plot. Raises: IndexError: If any value in `turbine_indices` is an invalid turbine index. """ if not ax: _, ax = plt.subplots() # If turbine_indices is not none, make sure all elements correspond to real indices if turbine_indices is not None: try: fmodel.layout_x[turbine_indices] except IndexError: raise IndexError("turbine_indices does not correspond to turbine indices in fi") else: turbine_indices = list(range(len(fmodel.layout_x))) layout_x = fmodel.layout_x[turbine_indices] layout_y = fmodel.layout_y[turbine_indices] N_turbs = len(layout_x) # Combine default plotting options def_wake_plotting_dict = { "color": "black", "linestyle": "solid", "linewidth": 0.5, } wake_plotting_dict = {**def_wake_plotting_dict, **wake_plotting_dict} # N_turbs = len(fmodel.core.farm.turbine_definitions) if D is None: D = fmodel.core.farm.turbine_definitions[0]["rotor_diameter"] # TODO: build out capability to use multiple diameters, if of interest. # D = np.array([turb['rotor_diameter'] for turb in # fmodel.core.farm.turbine_definitions]) # else: # D = D*np.ones(N_turbs) dists_m = np.zeros((N_turbs, N_turbs)) angles_d = np.zeros((N_turbs, N_turbs)) for i in range(N_turbs): for j in range(N_turbs): dists_m[i, j] = np.linalg.norm([layout_x[i] - layout_x[j], layout_y[i] - layout_y[j]]) angles_d[i, j] = get_wake_direction(layout_x[i], layout_y[i], layout_x[j], layout_y[j]) # Mask based on the limit distance (assumed to be in measurement D) if limit_dist_D is not None and limit_dist_m is None: limit_dist_m = limit_dist_D * D if limit_dist_m is not None: mask = dists_m > limit_dist_m dists_m[mask] = np.nan angles_d[mask] = np.nan # Handle default limit number case if limit_num is None: limit_num = -1 # Loop over pairs, plot label_exists = np.full((N_turbs, N_turbs), False) for i in range(N_turbs): for j in range(N_turbs): # import ipdb; ipdb.set_trace() if ( ~np.isnan(dists_m[i, j]) and dists_m[i, j] != 0.0 and ~(dists_m[i, j] > np.sort(dists_m[i, :])[limit_num]) # and i in layout_plotting_dict["turbine_indices"] # and j in layout_plotting_dict["turbine_indices"] ): (h,) = ax.plot( layout_x[[i, j]], layout_y[[i, j]], **wake_plotting_dict ) # Only label in one direction if ~label_exists[i, j]: linetext = "{0:.1f} D --- {1:.0f}/{2:.0f}".format( dists_m[i, j] / D, angles_d[i, j], angles_d[j, i], ) label_line( h, linetext, ax, near_i=1, near_x=None, near_y=None, rotation_offset=0, size=wake_label_size, ) label_exists[i, j] = True label_exists[j, i] = True return ax def plot_farm_terrain(fmodel: FlorisModel, ax: plt.Axes = None) -> None: """ Creates a filled contour plot visualizing terrain-corrected wind turbine hub heights. Args: fmodel (FlorisModel): The FlorisModel object containing layout data. ax (plt.Axes, optional): An existing axes object to plot on. If None, a new figure and axes will be created. Defaults to None. """ if not ax: _, ax = plt.subplots() hub_heights = fmodel.core.farm.hub_heights.flatten() cntr = ax.tricontourf(fmodel.layout_x, fmodel.layout_y, hub_heights, levels=14, cmap="RdBu_r") ax.get_figure().colorbar( cntr, ax=ax, label="Terrain-corrected hub height (m)", ticks=np.linspace( np.min(hub_heights) - 10.0, np.max(hub_heights) + 10.0, 15, ), ) return ax def shade_region( points: np.ndarray, show_points: bool = False, plotting_dict_region: Dict[str, Any] = {}, plotting_dict_points: Dict[str, Any] = {}, ax: plt.Axes = None, ) -> plt.Axes: """ Shades a region defined by a set of vertices and optionally plots the vertices. Args: points (np.ndarray): A 2D array where each row represents (x, y) coordinates of a vertex. show_points (bool, optional): If True, plots markers at the specified vertices. Defaults to False. plotting_dict_region (Dict[str, Any], optional): Customization options for shaded region. Valid keys include: * 'color' (str): Fill color. Defaults to 'black'. * 'edgecolor' (str): Edge color. Defaults to None (no edge). * 'alpha' (float): Opacity (transparency) of the fill. Defaults to 0.3. * 'label' (str): Optional label for legend. plotting_dict_points (Dict[str, Any], optional): Customization options for vertex markers. Valid keys include: * 'color' (str): Marker color. Defaults to 'black'. * 'marker' (str): Marker style (e.g., '.', 'o', 'x'). Defaults to None (no marker). * 's' (float): Marker size. Defaults to 10. * 'label' (str): Optional label for legend. ax (plt.Axes, optional): An existing axes object for plotting. If None, creates a new figure and axes. Defaults to None. Returns: plt.Axes: The axes object used for the plot. """ # Generate axis, if needed if ax is None: fig = plt.figure(figsize=(8, 8)) ax = fig.add_subplot(111) # Generate plotting dictionary default_plotting_dict_region = { "color": "black", "edgecolor": None, "alpha": 0.3, "label": None, } plotting_dict_region = {**default_plotting_dict_region, **plotting_dict_region} ax.fill(points[:, 0], points[:, 1], **plotting_dict_region) if show_points: default_plotting_dict_points = {"color": "black", "marker": ".", "s": 10, "label": None} plotting_dict_points = {**default_plotting_dict_points, **plotting_dict_points} ax.scatter(points[:, 0], points[:, 1], **plotting_dict_points) # Plot labels and aesthetics ax.axis("equal") return ax ================================================ FILE: floris/logging_manager.py ================================================ import logging from datetime import datetime import coloredlogs # Global variables for logging LOG_TO_CONSOLE = True CONSOLE_LEVEL = "INFO" LOG_TO_FILE = False FILE_LEVEL = "INFO" def configure_console_log(enabled=True, level="INFO"): """ Sets whether the log statements are displayed in the console logging, and, if enabled, the log level to use. If not explicitly configured, console logging is ON at the INFO level. Args: enabled (bool, optional): Whether to enable console logging. Defaults to True. level (str, optional): If `enabled` is True, sets the level that the logging module displays. This level is the minimum and all messages at a higher level are included. Valid values are - CRITICAL - ERROR - WARNING - INFO - DEBUG Defaults to "INFO". """ global LOG_TO_CONSOLE global CONSOLE_LEVEL LOG_TO_CONSOLE = enabled CONSOLE_LEVEL = level _setup_logger() def configure_file_log(enabled=True, level="INFO"): """ Sets whether the log statements are exported to a log file, and, if enabled, the log level to use. If not explicitly configured, file logging is OFF. Args: enabled (bool, optional): Whether to enable file logging. This argument defaults to True. level (str, optional): If `enabled` is True, sets the level that the logging module displays. This level is the minimum and all messages at a higher level are included. Valid values are - CRITICAL - ERROR - WARNING - INFO - DEBUG Defaults to "INFO". """ global LOG_TO_FILE global FILE_LEVEL LOG_TO_FILE = enabled FILE_LEVEL = level _setup_logger() def _setup_logger(): """ Configures the root logger based on the default or user-specified settings. As needed, a StreamHandler is created for console logging or FileHandler is created for file logging. Either or both are attached to the root logger for use throughout FLORIS. Returns: logging.Logger: The root logger from the `logging` module. """ # Create a logger object for floris logger = logging.getLogger(name="floris") logger.setLevel(logging.DEBUG) # level_styles = {'warning': {'color': 'red', 'bold': False}} fmt_console = "%(name)s %(levelname)s %(message)s" fmt_file = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" file_name = "floris_{:%Y-%m-%d-%H_%M_%S}.log".format(datetime.now()) # Remove all existing handlers before adding new ones for h in logger.handlers.copy(): logger.removeHandler(h) # Configure and add the console handler if LOG_TO_CONSOLE: console_handler = logging.StreamHandler() console_handler.setLevel(CONSOLE_LEVEL) console_format = coloredlogs.ColoredFormatter( # level_styles=level_styles, fmt=fmt_console ) console_handler.setFormatter(console_format) console_handler.addFilter(TracebackInfoFilter(clear=True)) logger.addHandler(console_handler) # Configure and add the file handler if LOG_TO_FILE: file_handler = logging.FileHandler(file_name) file_handler.setLevel(FILE_LEVEL) file_format = logging.Formatter(fmt_file) file_handler.setFormatter(file_format) file_handler.addFilter(TracebackInfoFilter(clear=False)) logger.addHandler(file_handler) return logger class TracebackInfoFilter(logging.Filter): """Clear or restore the exception on log records""" def __init__(self, clear=True): self.clear = clear def filter(self, record): if self.clear: record._stack_info_hidden, record.stack_info = record.stack_info, None elif hasattr(record, "_stack_info_hidden"): record.stack_info = record._stack_info_hidden del record._stack_info_hidden return True class LoggingManager: """ This class provide an easy access to the global logger. The virtual property here allows a simple and dynamic method for obtaining the correct logger for the calling class. """ @property def logger(self): return logging.getLogger( "{}.{}".format(type(self).__module__, type(self).__name__) ) ================================================ FILE: floris/optimization/__init__.py ================================================ from . import ( layout_optimization, other, yaw_optimization, ) ================================================ FILE: floris/optimization/layout_optimization/__init__.py ================================================ ================================================ FILE: floris/optimization/layout_optimization/layout_optimization_base.py ================================================ import matplotlib.pyplot as plt import numpy as np from shapely.geometry import MultiPolygon, Polygon from floris import TimeSeries from floris.optimization.yaw_optimization.yaw_optimizer_geometric import ( YawOptimizationGeometric, ) from floris.wind_data import WindDataBase from ...logging_manager import LoggingManager class LayoutOptimization(LoggingManager): """ Base class for layout optimization. This class should not be used directly but should be subclassed by a specific optimization method. Args: fmodel (FlorisModel): A FlorisModel object. boundaries (iterable(float, float)): Pairs of x- and y-coordinates that represent the boundary's vertices (m). min_dist (float, optional): The minimum distance to be maintained between turbines during the optimization (m). If not specified, initializes to 2 rotor diameters. Defaults to None. enable_geometric_yaw (bool, optional): If True, enables geometric yaw optimization. Defaults to False. use_value (bool, optional): If True, the layout optimization objective is to maximize annual value production using the value array in the FLORIS model's WindData object. If False, the optimization objective is to maximize AEP. Defaults to False. """ def __init__( self, fmodel, boundaries, min_dist=None, enable_geometric_yaw=False, use_value=False, ): self.fmodel = fmodel.copy() # Does not copy over the wind_data object self.fmodel.set(wind_data=fmodel.wind_data) self.boundaries = boundaries self.enable_geometric_yaw = enable_geometric_yaw self.use_value = use_value # Allow boundaries to be set either as a list of corners or as a # nested list of corners (for seperable regions) self.boundaries = boundaries b_depth = list_depth(boundaries) boundary_specification_error_msg = ( "boundaries should be a list of coordinates (specified as (x,y) "+\ "tuples) or as a list of list of tuples (for separable regions)." ) if b_depth == 1: self._boundary_polygon = MultiPolygon([Polygon(self.boundaries)]) self._boundary_line = self._boundary_polygon.boundary elif b_depth == 2: if not isinstance(self.boundaries[0][0], tuple): raise TypeError(boundary_specification_error_msg) self._boundary_polygon = MultiPolygon([Polygon(p) for p in self.boundaries]) self._boundary_line = self._boundary_polygon.boundary else: raise TypeError(boundary_specification_error_msg) self.xmin, self.ymin, self.xmax, self.ymax = self._boundary_polygon.bounds # If no minimum distance is provided, assume a value of 2 rotor diameters if min_dist is None: self.min_dist = 2 * self.rotor_diameter else: self.min_dist = min_dist # Check that wind_data is a WindDataBase object if (not isinstance(self.fmodel.wind_data, WindDataBase)): # NOTE: it is no longer strictly necessary that fmodel use # a WindData object, but it is still recommended. self.logger.warning( "Running layout optimization without a WindData object (e.g. TimeSeries, WindRose, " "WindTIRose). We suggest that the user set the wind conditions (and if applicable, " "frequencies and values) on the FlorisModel using the wind_data keyword argument " "for layout optimizations to capture frequencies and the value of the energy " "production accurately. If a WindData object is not defined, uniform frequencies " "will be assumed. If use_value is True and a WindData object is not defined, a " "value of 1 will be used for each wind condition and layout optimization will " "simply be performed to maximize AEP." ) # Establish geometric yaw class if self.enable_geometric_yaw: self.yaw_opt = YawOptimizationGeometric( fmodel, minimum_yaw_angle=-30.0, maximum_yaw_angle=30.0, ) fmodel.run() if self.use_value: self.initial_AEP_or_AVP = fmodel.get_farm_AVP() else: self.initial_AEP_or_AVP = fmodel.get_farm_AEP() def __str__(self): return "layout" def _norm(self, val, x1, x2): return (val - x1) / (x2 - x1) def _unnorm(self, val, x1, x2): return np.array(val) * (x2 - x1) + x1 def _get_geoyaw_angles(self): # NOTE: requires that child class saves x and y locations # as self.x and self.y and updates them during optimization. if self.enable_geometric_yaw: self.yaw_opt.fmodel_subset.set(layout_x=self.x, layout_y=self.y) df_opt = self.yaw_opt.optimize() self.yaw_angles = np.vstack(df_opt['yaw_angles_opt'])[:, :] else: self.yaw_angles = None return self.yaw_angles # Public methods def optimize(self): sol = self._optimize() return sol def plot_layout_opt_results( self, plot_boundary_dict={}, initial_locs_plotting_dict={}, final_locs_plotting_dict={}, ax=None, fontsize=16 ): x_initial, y_initial, x_opt, y_opt = self._get_initial_and_final_locs() # Generate axis, if needed if ax is None: fig = plt.figure(figsize=(9,6)) ax = fig.add_subplot(111) ax.set_aspect("equal") # Handle default boundary plotting default_plot_boundary_dict = { "color":"None", "alpha":1, "edgecolor":"b", "linewidth":2 } plot_boundary_dict = {**default_plot_boundary_dict, **plot_boundary_dict} # Handle default initial location plotting default_initial_locs_plotting_dict = { "marker":"o", "color":"b", "linestyle":"None", "label":"Initial locations", } initial_locs_plotting_dict = { **default_initial_locs_plotting_dict, **initial_locs_plotting_dict } # Handle default final location plotting default_final_locs_plotting_dict = { "marker":"o", "color":"r", "linestyle":"None", "label":"New locations", } final_locs_plotting_dict = {**default_final_locs_plotting_dict, **final_locs_plotting_dict} self.plot_layout_opt_boundary(plot_boundary_dict, ax=ax) ax.plot(x_initial, y_initial, **initial_locs_plotting_dict) ax.plot(x_opt, y_opt, **final_locs_plotting_dict) ax.set_xlabel("x (m)", fontsize=fontsize) ax.set_ylabel("y (m)", fontsize=fontsize) ax.grid(True) ax.tick_params(which="both", labelsize=fontsize) ax.legend( loc="lower center", bbox_to_anchor=(0.5, 1.01), ncol=2, fontsize=fontsize, ) return ax def plot_layout_opt_boundary(self, plot_boundary_dict={}, ax=None): # Generate axis, if needed if ax is None: fig = plt.figure(figsize=(9,6)) ax = fig.add_subplot(111) ax.set_aspect("equal") default_plot_boundary_dict = { "color":"k", "alpha":0.1, "edgecolor":None } plot_boundary_dict = {**default_plot_boundary_dict, **plot_boundary_dict} for line in self._boundary_line.geoms: xy = np.array(line.coords) ax.fill(xy[:,0], xy[:,1], **plot_boundary_dict) ax.grid(True) return ax def plot_progress(self, ax=None): if not hasattr(self, "objective_candidate_log"): raise NotImplementedError( "plot_progress not yet configured for "+self.__class__.__name__ ) if ax is None: _, ax = plt.subplots(1,1) objective_log_array = np.array(self.objective_candidate_log) if len(objective_log_array.shape) == 1: # Just one AEP candidate per step ax.plot(np.arange(len(objective_log_array)), objective_log_array, color="k") elif len(objective_log_array.shape) == 2: # Multiple AEP candidates per step for i in range(objective_log_array.shape[1]): ax.plot( np.arange(len(objective_log_array)), objective_log_array[:,i], color="lightgray" ) ax.scatter( np.zeros(objective_log_array.shape[1]), objective_log_array[0,:], color="b", label="Initial" ) ax.scatter( objective_log_array.shape[0]-1, objective_log_array[-1,:].max(), color="r", label="Final" ) # Plot aesthetics ax.grid(True) ax.set_xlabel("Optimization step [-]") ax.set_ylabel("Objective function") ax.legend() return ax ########################################################################### # Properties ########################################################################### @property def nturbs(self): """ This property returns the number of turbines in the FLORIS object. Returns: nturbs (int): The number of turbines in the FLORIS object. """ self._nturbs = self.fmodel.core.farm.n_turbines return self._nturbs @property def rotor_diameter(self): return self.fmodel.core.farm.rotor_diameters_sorted[0][0] # Helper functions def list_depth(x): if isinstance(x, list) and len(x) > 0: return 1 + max(list_depth(item) for item in x) else: return 0 ================================================ FILE: floris/optimization/layout_optimization/layout_optimization_boundary_grid.py ================================================ import matplotlib.pyplot as plt import numpy as np from scipy.spatial.distance import cdist from shapely.geometry import ( LineString, Point, Polygon, ) from .layout_optimization_base import LayoutOptimization class LayoutOptimizationBoundaryGrid(LayoutOptimization): def __init__( self, fmodel, boundaries, start, x_spacing, y_spacing, shear, rotation, center_x, center_y, boundary_setback, n_boundary_turbines=None, boundary_spacing=None, ): self.fmodel = fmodel self.boundary_x = np.array([val[0] for val in boundaries]) self.boundary_y = np.array([val[1] for val in boundaries]) boundary = np.zeros((len(self.boundary_x), 2)) boundary[:, 0] = self.boundary_x[:] boundary[:, 1] = self.boundary_y[:] self._boundary_polygon = Polygon(boundary) self.start = start self.x_spacing = x_spacing self.y_spacing = y_spacing self.shear = shear self.rotation = rotation self.center_x = center_x self.center_y = center_y self.boundary_setback = boundary_setback self.n_boundary_turbines = n_boundary_turbines self.boundary_spacing = boundary_spacing def _discontinuous_grid( self, nrows, ncols, farm_width, farm_height, shear, rotation, center_x, center_y, shrink_boundary, boundary_x, boundary_y, eps=1e-3, ): """ Map from grid design variables to turbine x and y locations. Includes integer design variables and the formulation results in a discontinous design space. TODO: shrink_boundary doesn't work well with concave boundaries, or with boundary angles less than 90 deg Args: nrows (Int): number of rows in the grid. ncols (Int): number of columns in the grid. farm_width (Float): total grid width (before shear). farm_height (Float): total grid height. shear (Float): grid shear (rad). rotation (Float): rotation about grid center (rad). center_x (Float): location of grid x center. center_y (Float): location of grid y center. shrink_boundary (Float): how much to shrink the boundary that the grid can occupy. boundary_x (Array(Float)): x boundary points. boundary_y (Array(Float)): y boundary points. Returns: grid_x (Array(Float)): turbine x locations. grid_y (Array(Float)): turbine y locations. """ # create grid nrows = int(nrows) ncols = int(ncols) xlocs = np.linspace(0.0, farm_width, ncols) ylocs = np.linspace(0.0, farm_height, nrows) y_spacing = ylocs[1] - ylocs[0] nturbs = nrows * ncols grid_x = np.zeros(nturbs) grid_y = np.zeros(nturbs) turb = 0 for i in range(nrows): for j in range(ncols): grid_x[turb] = xlocs[j] + float(i) * y_spacing * np.tan(shear) grid_y[turb] = ylocs[i] turb += 1 # rotate grid_x, grid_y = ( np.cos(rotation) * grid_x - np.sin(rotation) * grid_y, np.sin(rotation) * grid_x + np.cos(rotation) * grid_y, ) # move center of grid grid_x = (grid_x - np.mean(grid_x)) + center_x grid_y = (grid_y - np.mean(grid_y)) + center_y # arrange the boundary # boundary = np.zeros((len(boundary_x),2)) # boundary[:,0] = boundary_x[:] # boundary[:,1] = boundary_y[:] # poly = Polygon(boundary) # centroid = poly.centroid # boundary[:,0] = (boundary_x[:]-centroid.x)*boundary_mult + centroid.x # boundary[:,1] = (boundary_y[:]-centroid.y)*boundary_mult + centroid.y # poly = Polygon(boundary) boundary = np.zeros((len(boundary_x), 2)) boundary[:, 0] = boundary_x[:] boundary[:, 1] = boundary_y[:] poly = Polygon(boundary) if shrink_boundary != 0.0: nBounds = len(boundary_x) for i in range(nBounds): point = Point(boundary_x[i] + eps, boundary_y[i]) if poly.contains(point) is True or poly.touches(point) is True: boundary[i, 0] = boundary_x[i] + shrink_boundary else: boundary[i, 0] = boundary_x[i] - shrink_boundary point = Point(boundary_x[i], boundary_y[i] + eps) if poly.contains(point) is True or poly.touches(point) is True: boundary[i, 1] = boundary_y[i] + shrink_boundary else: boundary[i, 1] = boundary_y[i] - shrink_boundary poly = Polygon(boundary) # get rid of points outside of boundary index = 0 for i in range(len(grid_x)): point = Point(grid_x[index], grid_y[index]) if poly.contains(point) is False and poly.touches(point) is False: grid_x = np.delete(grid_x, index) grid_y = np.delete(grid_y, index) else: index += 1 return grid_x, grid_y def _discrete_grid( self, x_spacing, y_spacing, shear, rotation, center_x, center_y, boundary_setback, boundary_poly ): """ returns grid turbine layout. Assumes the turbines fill the entire plant area Args: x_spacing (Float): grid spacing in the unrotated x direction (m) y_spacing (Float): grid spacing in the unrotated y direction (m) shear (Float): grid shear (rad) rotation (Float): grid rotation (rad) center_x (Float): the x coordinate of the grid center (m) center_y (Float): the y coordinate of the grid center (m) boundary_poly (Polygon): a shapely Polygon of the wind plant boundary Returns return_x (Array(Float)): turbine x locations return_y (Array(Float)): turbine y locations """ shrunk_poly = boundary_poly.buffer(-boundary_setback) if shrunk_poly.area <= 0: return np.array([]), np.array([]) # create grid minx, miny, maxx, maxy = shrunk_poly.bounds width = maxx-minx height = maxy-miny center_point = Point((center_x,center_y)) poly_to_center = center_point.distance(shrunk_poly.centroid) width = np.max([width,poly_to_center]) height = np.max([height,poly_to_center]) nrows = int(np.max([width,height])/np.min([x_spacing,y_spacing]))*2 + 1 ncols = nrows xlocs = np.arange(0,ncols)*x_spacing ylocs = np.arange(0,nrows)*y_spacing row_number = np.arange(0,nrows) d = np.array([i for x in xlocs for i in row_number]) layout_x = np.array([x for x in xlocs for y in ylocs]) + d*y_spacing*np.tan(shear) layout_y = np.array([y for x in xlocs for y in ylocs]) # rotate rotate_x = np.cos(rotation)*layout_x - np.sin(rotation)*layout_y rotate_y = np.sin(rotation)*layout_x + np.cos(rotation)*layout_y # move center of grid rotate_x = (rotate_x - np.mean(rotate_x)) + center_x rotate_y = (rotate_y - np.mean(rotate_y)) + center_y # get rid of points outside of boundary polygon meets_constraints = np.zeros(len(rotate_x),dtype=bool) for i in range(len(rotate_x)): pt = Point(rotate_x[i],rotate_y[i]) if shrunk_poly.contains(pt) or shrunk_poly.touches(pt): meets_constraints[i] = True # arrange final x,y points return_x = rotate_x[meets_constraints] return_y = rotate_y[meets_constraints] return return_x, return_y def find_lengths(self, x, y, npoints): length = np.zeros(len(x) - 1) for i in range(npoints): length[i] = np.sqrt((x[i + 1] - x[i]) ** 2 + (y[i + 1] - y[i]) ** 2) return length # def _place_boundary_turbines(self, n_boundary_turbs, start, boundary_x, boundary_y): # """ # Place turbines equally spaced traversing the perimiter if the wind farm along the boundary # Args: # n_boundary_turbs (Int): number of turbines to be placed on the boundary # start (Float): where the first turbine should be placed # boundary_x (Array(Float)): x boundary points # boundary_y (Array(Float)): y boundary points # Returns # layout_x (Array(Float)): turbine x locations # layout_y (Array(Float)): turbine y locations # """ # # check if the boundary is closed, correct if not # if boundary_x[-1] != boundary_x[0] or boundary_y[-1] != boundary_y[0]: # boundary_x = np.append(boundary_x, boundary_x[0]) # boundary_y = np.append(boundary_y, boundary_y[0]) # # make the boundary # boundary = np.zeros((len(boundary_x), 2)) # boundary[:, 0] = boundary_x[:] # boundary[:, 1] = boundary_y[:] # poly = Polygon(boundary) # perimeter = poly.length # # get the flattened turbine locations # spacing = perimeter / float(n_boundary_turbs) # flattened_locs = np.linspace(start, perimeter + start - spacing, n_boundary_turbs) # # set all of the flattened values between 0 and the perimeter # for i in range(n_boundary_turbs): # while flattened_locs[i] < 0.0: # flattened_locs[i] += perimeter # if flattened_locs[i] > perimeter: # flattened_locs[i] = flattened_locs[i] % perimeter # # place the turbines around the perimeter # nBounds = len(boundary_x) # layout_x = np.zeros(n_boundary_turbs) # layout_y = np.zeros(n_boundary_turbs) # lenBound = np.zeros(nBounds - 1) # for i in range(nBounds - 1): # lenBound[i] = Point(boundary[i]).distance(Point(boundary[i + 1])) # for i in range(n_boundary_turbs): # for j in range(nBounds - 1): # if flattened_locs[i] < sum(lenBound[0 : j + 1]): # layout_x[i] = ( # boundary_x[j] # + (boundary_x[j + 1] - boundary_x[j]) # * (flattened_locs[i] - sum(lenBound[0:j])) # / lenBound[j] # ) # layout_y[i] = ( # boundary_y[j] # + (boundary_y[j + 1] - boundary_y[j]) # * (flattened_locs[i] - sum(lenBound[0:j])) # / lenBound[j] # ) # break # return layout_x, layout_y def _place_boundary_turbines(self, start, boundary_poly, nturbs=None, spacing=None): xBounds, yBounds = boundary_poly.boundary.coords.xy if xBounds[-1] != xBounds[0]: xBounds = np.append(xBounds, xBounds[0]) yBounds = np.append(yBounds, yBounds[0]) nBounds = len(xBounds) lenBound = self.find_lengths(xBounds, yBounds, len(xBounds) - 1) circumference = sum(lenBound) if nturbs is not None and spacing is None: # When the number of boundary turbines is specified nturbs = int(nturbs) bound_loc = np.linspace( start, start + circumference - circumference / float(nturbs), nturbs ) elif spacing is not None and nturbs is None: # When the spacing of boundary turbines is specified nturbs = int(np.floor(circumference / spacing)) bound_loc = np.linspace( start, start + circumference - circumference / float(nturbs), nturbs ) else: raise ValueError("Please specify either nturbs or spacing.") x = np.zeros(nturbs) y = np.zeros(nturbs) if spacing is None: # When the number of boundary turbines is specified for i in range(nturbs): if bound_loc[i] > circumference: bound_loc[i] = bound_loc[i] % circumference while bound_loc[i] < 0.0: bound_loc[i] += circumference for i in range(nturbs): done = False for j in range(nBounds): if done is False: if bound_loc[i] < sum(lenBound[0:j+1]): point_x = ( xBounds[j] + (xBounds[j+1] - xBounds[j]) * (bound_loc[i] - sum(lenBound[0:j])) / lenBound[j] ) point_y = ( yBounds[j] + (yBounds[j+1] - yBounds[j]) * (bound_loc[i] - sum(lenBound[0:j])) / lenBound[j] ) done = True x[i] = point_x y[i] = point_y else: # When the spacing of boundary turbines is specified additional_space = 0.0 end_loop = False for i in range(nturbs): done = False for j in range(nBounds): while done is False: dist = start + i*spacing + additional_space if dist < sum(lenBound[0:j+1]): point_x = ( xBounds[j] + (xBounds[j+1]-xBounds[j]) * (dist -sum(lenBound[0:j])) / lenBound[j] ) point_y = ( yBounds[j] + (yBounds[j+1]-yBounds[j]) * (dist -sum(lenBound[0:j])) / lenBound[j] ) # Check if turbine is too close to previous turbine if i > 0: # Check if turbine just placed is to close to first turbine min_dist = cdist([(point_x, point_y)], [(x[0], y[0])]) if min_dist < spacing: # TODO: make this more robust; # pass is needed if 2nd turbine is too close to the first if i == 1: pass else: end_loop = True ii = i break min_dist = cdist([(point_x, point_y)], [(x[i-1], y[i-1])]) if min_dist < spacing: additional_space += 1.0 else: done = True x[i] = point_x y[i] = point_y elif i == 0: # If first turbine, just add initial turbine point done = True x[i] = point_x y[i] = point_y else: pass else: break if end_loop is True: break if end_loop is True: x = x[:ii] y = y[:ii] break return x, y def _place_boundary_turbines_with_specified_spacing( self, spacing, start, boundary_x, boundary_y ): """ Place turbines equally spaced traversing the perimiter if the wind farm along the boundary Args: n_boundary_turbs (Int): number of turbines to be placed on the boundary start (Float): where the first turbine should be placed boundary_x (Array(Float)): x boundary points boundary_y (Array(Float)): y boundary points Returns layout_x (Array(Float)): turbine x locations layout_y (Array(Float)): turbine y locations """ # check if the boundary is closed, correct if not if boundary_x[-1] != boundary_x[0] or boundary_y[-1] != boundary_y[0]: boundary_x = np.append(boundary_x, boundary_x[0]) boundary_y = np.append(boundary_y, boundary_y[0]) # make the boundary boundary = np.zeros((len(boundary_x), 2)) boundary[:, 0] = boundary_x[:] boundary[:, 1] = boundary_y[:] poly = Polygon(boundary) perimeter = poly.length # get the flattened turbine locations n_boundary_turbs = int(perimeter / float(spacing)) flattened_locs = np.linspace(start, perimeter + start - spacing, n_boundary_turbs) # set all of the flattened values between 0 and the perimeter for i in range(n_boundary_turbs): while flattened_locs[i] < 0.0: flattened_locs[i] += perimeter if flattened_locs[i] > perimeter: flattened_locs[i] = flattened_locs[i] % perimeter # place the turbines around the perimeter nBounds = len(boundary_x) layout_x = np.zeros(n_boundary_turbs) layout_y = np.zeros(n_boundary_turbs) lenBound = np.zeros(nBounds - 1) for i in range(nBounds - 1): lenBound[i] = Point(boundary[i]).distance(Point(boundary[i + 1])) for i in range(n_boundary_turbs): for j in range(nBounds - 1): if flattened_locs[i] < sum(lenBound[0 : j + 1]): layout_x[i] = ( boundary_x[j] + (boundary_x[j + 1] - boundary_x[j]) * (flattened_locs[i] - sum(lenBound[0:j])) / lenBound[j] ) layout_y[i] = ( boundary_y[j] + (boundary_y[j + 1] - boundary_y[j]) * (flattened_locs[i] - sum(lenBound[0:j])) / lenBound[j] ) break return layout_x, layout_y def boundary_grid( self, start, x_spacing, y_spacing, shear, rotation, center_x, center_y, boundary_setback, n_boundary_turbines=None, boundary_spacing=None, ): """ Place turbines equally spaced traversing the perimiter if the wind farm along the boundary Args: n_boundary_turbs,start: boundary variables nrows,ncols,farm_width,farm_height,shear, rotation,center_x,center_y,shrink_boundary,eps: grid variables boundary_x,boundary_y: boundary points Returns layout_x (Array(Float)): turbine x locations layout_y (Array(Float)): turbine y locations """ boundary_turbines_x, boundary_turbines_y = self._place_boundary_turbines( start, self._boundary_polygon, nturbs=n_boundary_turbines, spacing=boundary_spacing ) # ( boundary_turbines_x, # boundary_turbines_y ) = self._place_boundary_turbines_with_specified_spacing( # spacing, start, boundary_x, boundary_y # ) # grid_turbines_x, grid_turbines_y = self._discontinuous_grid( # nrows, # ncols, # farm_width, # farm_height, # shear, # rotation, # center_x, # center_y, # shrink_boundary, # boundary_x, # boundary_y, # eps=eps, # ) grid_turbines_x, grid_turbines_y = self._discrete_grid( x_spacing, y_spacing, shear, rotation, center_x, center_y, boundary_setback, self._boundary_polygon, ) layout_x = np.append(boundary_turbines_x, grid_turbines_x) layout_y = np.append(boundary_turbines_y, grid_turbines_y) return layout_x, layout_y def reinitialize_bg( self, n_boundary_turbines=None, start=None, x_spacing=None, y_spacing=None, shear=None, rotation=None, center_x=None, center_y=None, boundary_setback=None, boundary_x=None, boundary_y=None, boundary_spacing=None, ): if n_boundary_turbines is not None: self.n_boundary_turbines = n_boundary_turbines if start is not None: self.start = start if x_spacing is not None: self.x_spacing = x_spacing if y_spacing is not None: self.y_spacing = y_spacing if shear is not None: self.shear = shear if rotation is not None: self.rotation = rotation if center_x is not None: self.center_x = center_x if center_y is not None: self.center_y = center_y if boundary_setback is not None: self.boundary_setback = boundary_setback if boundary_x is not None: self.boundary_x = boundary_x if boundary_y is not None: self.boundary_y = boundary_y if boundary_spacing is not None: self.boundary_spacing = boundary_spacing def reinitialize_xy(self): layout_x, layout_y = self.boundary_grid( self.start, self.x_spacing, self.y_spacing, self.shear, self.rotation, self.center_x, self.center_y, self.boundary_setback, self.n_boundary_turbines, self.boundary_spacing, ) self.fmodel.set(layout=(layout_x, layout_y)) def plot_layout(self): plt.figure(figsize=(9, 6)) fontsize = 16 plt.plot(self.fmodel.layout_x, self.fmodel.layout_y, "ob") # plt.plot(locsx, locsy, "or") plt.xlabel("x (m)", fontsize=fontsize) plt.ylabel("y (m)", fontsize=fontsize) plt.axis("equal") plt.grid() plt.tick_params(which="both", labelsize=fontsize) def space_constraint(self, x, y, min_dist, rho=500): # Calculate distances between turbines locs = np.vstack((x, y)).T distances = cdist(locs, locs) arange = np.arange(distances.shape[0]) distances[arange, arange] = 1e10 dist = np.min(distances, axis=0) g = 1 - np.array(dist) / min_dist # Following code copied from OpenMDAO KSComp(). # Constraint is satisfied when KS_constraint <= 0 g_max = np.max(np.atleast_2d(g), axis=-1)[:, np.newaxis] g_diff = g - g_max exponents = np.exp(rho * g_diff) summation = np.sum(exponents, axis=-1)[:, np.newaxis] KS_constraint = g_max + 1.0 / rho * np.log(summation) return KS_constraint[0][0], dist ================================================ FILE: floris/optimization/layout_optimization/layout_optimization_gridded.py ================================================ import numpy as np from floris import FlorisModel from .layout_optimization_base import LayoutOptimization from .layout_optimization_random_search import test_point_in_bounds class LayoutOptimizationGridded(LayoutOptimization): """ Generates layouts that fit the most turbines arranged in a gridded pattern into the given boundaries. The grid can be square (default) or hexagonal. The layout is optimized by rotating and translating the grid to maximize the number of turbines that fit within the boundaries. Note that no wake or AEP calculations are performed in determining the maximum number of turbines that fit within the boundary. """ def __init__( self, fmodel: FlorisModel, boundaries: list[tuple[float, float] | list[tuple[float, float]]], min_dist: float | None = None, min_dist_D: float | None = -1, rotation_step: float = 5.0, rotation_range: tuple[float, float] = (0.0, 360.0), translation_step: float | None = None, translation_step_D: float | None = -1, translation_range: tuple[float, float] | None = None, hexagonal_packing: bool = False, ): """ Initialize the LayoutOptimizationGridded object. Args: fmodel: FlorisModel, mostly used to obtain rotor diameter for spacing boundaries: List of boundary vertices. Specified as a list of two-tuples (x,y), or a list of lists of two-tuples if there are multiple separate boundary areas. min_dist: Minimum distance between turbines in meters. Defaults to None, which results in 5D spacing if min_dist_D is not defined. min_dist_D: Minimum distance between turbines in terms of rotor diameters. If specified as a negative number, will result in 5D spacing using the first turbine diameter found on the fmodel. Defaults to -1, which results in 5D spacing if min_dist is not defined. rotation_step: Step size for grid rotations in degrees. Defaults to 5.0. rotation_range: Range of possible rotation in degrees. Defaults to (0.0, 360.0). translation_step: Step size for translation in meters. Defaults to None, which results in 1D translations if translation_step_D is not defined. translation_step_D: Step size for translation in terms of rotor diameters. If specified as a negative number, will result in 1D translation steps using the first turbine diameter found on the fmodel. Defaults to -1, which results in 1D steps if translation_step is not defined. translation_range: Range of translation in meters. Defaults to None, which results in a range of (0, min_dist). hexagonal_packing: Use hexagonal packing instead of square grid. Defaults to False. """ # Handle spacing information if min_dist is not None and min_dist_D is not None and min_dist_D >= 0: raise ValueError("Only one of min_dist and min_dist_D can be defined.") if min_dist is None and min_dist_D is None: raise ValueError("Either min_dist or min_dist_D must be defined.") if min_dist_D is not None and min_dist is None: if min_dist_D < 0: # Default to 5D min_dist_D = 5.0 min_dist = min_dist_D * fmodel.core.farm.rotor_diameters.flat[0] if len(np.unique(fmodel.core.farm.rotor_diameters)) > 1: self.logger.warning(( "Found multiple turbine diameters. Using diameter of first turbine to set" f" min_dist to {min_dist}m ({min_dist_D} diameters)." )) # Similar for translation step if (translation_step is not None and translation_step_D is not None and translation_step_D >= 0 ): raise ValueError("Only one of translation_step and translation_step_D can be defined.") if translation_step is None and translation_step_D is None: raise ValueError("Either translation_step or translation_step_D must be defined.") if translation_step_D is not None and translation_step is None: if translation_step_D < 0: # Default to 1D translation_step_D = 1.0 translation_step = translation_step_D * fmodel.core.farm.rotor_diameters.flat[0] if len(np.unique(fmodel.core.farm.rotor_diameters)) > 1: self.logger.warning(( "Found multiple turbine diameters. Using diameter of first turbine to set" f" translation step to {translation_step}m ({translation_step_D} diameters)." )) # Initialize the base class super().__init__( fmodel, boundaries, min_dist=min_dist, enable_geometric_yaw=False, use_value=False, ) # Initial locations not used for optimization, but may be useful # for comparison self.x0 = fmodel.layout_x self.y0 = fmodel.layout_y # Create the default grid # use min_dist, hexagonal packing, and boundaries to create a grid. d = 1.1 * np.sqrt((self.xmax - self.xmin)**2 + (self.ymax - self.ymin)**2) grid_1D = np.arange(0, d+min_dist, min_dist) if hexagonal_packing: x_locs = np.tile(grid_1D.reshape(1,-1), (len(grid_1D), 1)) x_locs[np.arange(1, len(grid_1D), 2), :] += 0.5 * min_dist y_locs = np.tile(np.sqrt(3) / 2 * grid_1D.reshape(-1,1), (1, len(grid_1D))) else: x_locs, y_locs = np.meshgrid(grid_1D, grid_1D) x_locs = x_locs.flatten() - np.mean(x_locs) + 0.5*(self.xmax + self.xmin) y_locs = y_locs.flatten() - np.mean(y_locs) + 0.5*(self.ymax + self.ymin) # Trim to a circle to avoid wasted computation x_locs_grid, y_locs_grid = self.trim_to_circle( x_locs, y_locs, (grid_1D.max()-grid_1D.min()+min_dist)/2 ) self.xy_grid = np.concatenate( [x_locs_grid.reshape(-1,1), y_locs_grid.reshape(-1,1)], axis=1 ) # Limit the rotation range if grid has symmetry if hexagonal_packing: # Hexagonal packing has 60 degree symmetry rotation_range = ( rotation_range[0], np.minimum(rotation_range[1], rotation_range[0]+60) ) else: # Square grid has 90 degree symmetry rotation_range = ( rotation_range[0], np.minimum(rotation_range[1], rotation_range[0]+90) ) # Deal with None translation_range if translation_range is None: translation_range = (0.0, min_dist) # Create test rotations and translations self.rotations = np.arange(rotation_range[0], rotation_range[1], rotation_step) self.translations = np.arange(translation_range[0], translation_range[1], translation_step) self.translations = np.concatenate([-np.flip(self.translations), self.translations]) def optimize(self): # Sweep over rotations and translations to find the best layout n_rots = len(self.rotations) n_trans = len(self.translations) n_tot = n_rots * n_trans**2 # There are a total of n_rots x n_trans x n_trans layouts to test rots_rad = np.radians(self.rotations) rotation_matrices = np.array( [ [np.cos(rots_rad), -np.sin(rots_rad)], [np.sin(rots_rad), np.cos(rots_rad)] ] ).transpose(2,0,1) translations_x, translations_y = np.meshgrid(self.translations, self.translations) translation_matrices = np.concatenate( [translations_x.reshape(-1,1), translations_y.reshape(-1,1)], axis=1 ) rotations_all = np.tile(rotation_matrices, (n_trans**2, 1, 1)) translations_all = np.repeat(translation_matrices, n_rots, axis=0)[:,None,:] # Create candidate layouts [(n_rots x n_trans x n_trans) x n_turbines x 2] candidate_layouts = np.einsum('ijk,lk->ilj', rotations_all, self.xy_grid) + translations_all # For each candidate layout, check how many turbines are in bounds turbines_in_bounds = np.zeros(n_tot) for i in range(n_tot): turbines_in_bounds[i] = np.sum( [test_point_in_bounds(xy[0], xy[1], self._boundary_polygon) for xy in candidate_layouts[i, :, :]] ) idx_max = np.argmax(turbines_in_bounds) # First maximizing index returned # Get the best layout x_opt_all = candidate_layouts[idx_max, :, 0] y_opt_all = candidate_layouts[idx_max, :, 1] mask_in_bounds = [test_point_in_bounds(x, y, self._boundary_polygon) for x, y in zip(x_opt_all, y_opt_all)] # Save best layout, along with the number of turbines in bounds, and return self.n_turbines_max = round(turbines_in_bounds[idx_max]) self.x_opt = x_opt_all[mask_in_bounds] self.y_opt = y_opt_all[mask_in_bounds] return self.n_turbines_max, self.x_opt, self.y_opt def _get_initial_and_final_locs(self): return self.x0, self.y0, self.x_opt, self.y_opt @staticmethod def trim_to_circle(x_locs, y_locs, radius): center = np.array([0.5*(x_locs.max() + x_locs.min()), 0.5*(y_locs.max() + y_locs.min())]) xy = np.concatenate([x_locs.reshape(-1,1), y_locs.reshape(-1,1)], axis=1) mask = np.linalg.norm(xy - center, axis=1) <= radius return x_locs[mask], y_locs[mask] ================================================ FILE: floris/optimization/layout_optimization/layout_optimization_pyoptsparse.py ================================================ import matplotlib.pyplot as plt import numpy as np from scipy.spatial.distance import cdist from shapely.geometry import Point from .layout_optimization_base import LayoutOptimization, list_depth class LayoutOptimizationPyOptSparse(LayoutOptimization): """ This class provides an interface for optimizing the layout of wind turbines using the pyOptSparse optimization library. The optimization objective is to maximize annual energy production (AEP) or annual value production (AVP). Args: fmodel (FlorisModel): A FlorisModel object. boundaries (iterable(float, float)): Pairs of x- and y-coordinates that represent the boundary's vertices (m). min_dist (float, optional): The minimum distance to be maintained between turbines during the optimization (m). If not specified, initializes to 2 rotor diameters. Defaults to None. solver (str, optional): Sets the solver used by pyOptSparse. Defaults to 'SLSQP'. optOptions (dict, optional): Dictionary for setting the optimization options. Defaults to None. timeLimit (float, optional): Variable passed to pyOptSparse optimizer. The maximum amount of time for optimizer to run (seconds). If None, no time limit is imposed. Defaults to None. storeHistory (str, optional): Variable passed to pyOptSparse optimizer. File name of the history file into which the history of the pyOptSparse optimization will be stored. Defaults to "hist.hist". hotStart (str, optional): Variable passed to pyOptSparse optimizer. File name of the history file to “replay” for the optimization. If None, pyOptSparse initializes the optimization from scratch. Defaults to None. enable_geometric_yaw (bool, optional): If True, enables geometric yaw optimization. Defaults to False. use_value (bool, optional): If True, the layout optimization objective is to maximize annual value production using the value array in the FLORIS model's WindData object. If False, the optimization objective is to maximize AEP. Defaults to False. """ def __init__( self, fmodel, boundaries, min_dist=None, solver=None, optOptions=None, timeLimit=None, storeHistory='hist.hist', hotStart=None, enable_geometric_yaw=False, use_value=False, ): if list_depth(boundaries) > 1 and hasattr(boundaries[0][0], "__len__"): raise NotImplementedError( "LayoutOptimizationPyOptSparse is not configured for multiple regions." ) super().__init__( fmodel, boundaries, min_dist=min_dist, enable_geometric_yaw=enable_geometric_yaw, use_value=use_value ) self.x0 = self._norm(self.fmodel.layout_x, self.xmin, self.xmax) self.y0 = self._norm(self.fmodel.layout_y, self.ymin, self.ymax) self.storeHistory = storeHistory self.timeLimit = timeLimit self.hotStart = hotStart self.enable_geometric_yaw = enable_geometric_yaw try: import pyoptsparse except ImportError: err_msg = ( "It appears you do not have pyOptSparse installed. " + "Please refer to https://pyoptsparse.readthedocs.io/ for " + "guidance on how to properly install the module." ) self.logger.error(err_msg, stack_info=True) raise ImportError(err_msg) # Instantiate pyOptSparse optimization object with name and objective function self.optProb = pyoptsparse.Optimization('layout', self._obj_func) self.optProb = self.add_var_group(self.optProb) self.optProb = self.add_con_group(self.optProb) self.optProb.addObj("obj") if solver is not None: self.solver = solver print("Setting up optimization with user's choice of solver: ", self.solver) else: self.solver = "SLSQP" print("Setting up optimization with default solver: SLSQP.") if optOptions is not None: self.optOptions = optOptions else: if self.solver == "SNOPT": self.optOptions = {"Major optimality tolerance": 1e-7} else: self.optOptions = {} exec("self.opt = pyoptsparse." + self.solver + "(options=self.optOptions)") def _optimize(self): if hasattr(self, "_sens"): self.sol = self.opt(self.optProb, sens=self._sens) else: if self.timeLimit is not None: self.sol = self.opt( self.optProb, sens="CDR", storeHistory=self.storeHistory, timeLimit=self.timeLimit, hotStart=self.hotStart ) else: self.sol = self.opt( self.optProb, sens="CDR", storeHistory=self.storeHistory, hotStart=self.hotStart ) return self.sol def _obj_func(self, varDict): # Parse the variable dictionary self.parse_opt_vars(varDict) # Compute turbine yaw angles using PJ's geometric code (if enabled) yaw_angles = self._get_geoyaw_angles() # Update turbine map with turbine locations and yaw angles self.fmodel.set(layout_x=self.x, layout_y=self.y, yaw_angles=yaw_angles) self.fmodel.run() # Compute the objective function funcs = {} if self.use_value: funcs["obj"] = -1 * self.fmodel.get_farm_AVP() / self.initial_AEP_or_AVP else: funcs["obj"] = -1 * self.fmodel.get_farm_AEP() / self.initial_AEP_or_AVP # Compute constraints, if any are defined for the optimization funcs = self.compute_cons(funcs, self.x, self.y) fail = False return funcs, fail # Optionally, the user can supply the optimization with gradients # def _sens(self, varDict, funcs): # funcsSens = {} # fail = False # return funcsSens, fail def parse_opt_vars(self, varDict): self.x = self._unnorm(varDict["x"], self.xmin, self.xmax) self.y = self._unnorm(varDict["y"], self.ymin, self.ymax) def parse_sol_vars(self, sol): self.x = list(self._unnorm(sol.getDVs()["x"], self.xmin, self.xmax))[0] self.y = list(self._unnorm(sol.getDVs()["y"], self.ymin, self.ymax))[1] def add_var_group(self, optProb): optProb.addVarGroup( "x", self.nturbs, varType="c", lower=0.0, upper=1.0, value=self.x0 ) optProb.addVarGroup( "y", self.nturbs, varType="c", lower=0.0, upper=1.0, value=self.y0 ) return optProb def add_con_group(self, optProb): optProb.addConGroup("boundary_con", self.nturbs, upper=0.0) optProb.addConGroup("spacing_con", 1, upper=0.0) return optProb def compute_cons(self, funcs, x, y): funcs["boundary_con"] = self.distance_from_boundaries(x, y) funcs["spacing_con"] = self.space_constraint(x, y) return funcs def space_constraint(self, x, y, rho=500): # Calculate distances between turbines locs = np.vstack((x, y)).T distances = cdist(locs, locs) arange = np.arange(distances.shape[0]) distances[arange, arange] = 1e10 dist = np.min(distances, axis=0) g = 1 - np.array(dist) / self.min_dist # Following code copied from OpenMDAO KSComp(). # Constraint is satisfied when KS_constraint <= 0 g_max = np.max(np.atleast_2d(g), axis=-1)[:, np.newaxis] g_diff = g - g_max exponents = np.exp(rho * g_diff) summation = np.sum(exponents, axis=-1)[:, np.newaxis] KS_constraint = g_max + 1.0 / rho * np.log(summation) return KS_constraint[0][0] def distance_from_boundaries(self, x, y): boundary_con = np.zeros(self.nturbs) for i in range(self.nturbs): loc = Point(x[i], y[i]) boundary_con[i] = loc.distance(self._boundary_line) if self._boundary_polygon.contains(loc) is True: boundary_con[i] *= -1.0 return boundary_con def _get_initial_and_final_locs(self): x_initial = self._unnorm(self.x0, self.xmin, self.xmax) y_initial = self._unnorm(self.y0, self.ymin, self.ymax) x_opt, y_opt = self.get_optimized_locs() return x_initial, y_initial, x_opt, y_opt def get_optimized_locs(self): x_opt = self._unnorm(self.sol.getDVs()["x"], self.xmin, self.xmax) y_opt = self._unnorm(self.sol.getDVs()["y"], self.ymin, self.ymax) return x_opt, y_opt ================================================ FILE: floris/optimization/layout_optimization/layout_optimization_pyoptsparse_spread.py ================================================ import matplotlib.pyplot as plt import numpy as np from scipy.spatial.distance import cdist from shapely.geometry import Point from .layout_optimization_base import LayoutOptimization class LayoutOptimizationPyOptSparse(LayoutOptimization): def __init__( self, fmodel, boundaries, min_dist=None, solver=None, optOptions=None, timeLimit=None, storeHistory='hist.hist', hotStart=None ): super().__init__(fmodel, boundaries, min_dist=min_dist) self._reinitialize(solver=solver, optOptions=optOptions) self.storeHistory = storeHistory self.timeLimit = timeLimit self.hotStart = hotStart def _reinitialize(self, solver=None, optOptions=None): try: import pyoptsparse except ImportError: err_msg = ( "It appears you do not have pyOptSparse installed. " + "Please refer to https://pyoptsparse.readthedocs.io/ for " + "guidance on how to properly install the module." ) self.logger.error(err_msg, stack_info=True) raise ImportError(err_msg) # Insantiate ptOptSparse optimization object with name and objective function self.optProb = pyoptsparse.Optimization('layout', self._obj_func) self.optProb = self.add_var_group(self.optProb) self.optProb = self.add_con_group(self.optProb) self.optProb.addObj("obj") if solver is not None: self.solver = solver print("Setting up optimization with user's choice of solver: ", self.solver) else: self.solver = "SLSQP" print("Setting up optimization with default solver: SLSQP.") if optOptions is not None: self.optOptions = optOptions else: if self.solver == "SNOPT": self.optOptions = {"Major optimality tolerance": 1e-7} else: self.optOptions = {} exec("self.opt = pyoptsparse." + self.solver + "(options=self.optOptions)") def _optimize(self): if hasattr(self, "_sens"): self.sol = self.opt(self.optProb, sens=self._sens) else: if self.timeLimit is not None: self.sol = self.opt( self.optProb, sens="CDR", storeHistory=self.storeHistory, timeLimit=self.timeLimit, hotStart=self.hotStart ) else: self.sol = self.opt( self.optProb, sens="CDR", storeHistory=self.storeHistory, hotStart=self.hotStart ) return self.sol def _obj_func(self, varDict): # Parse the variable dictionary self.parse_opt_vars(varDict) # Update turbine map with turbince locations # self.fmodel.reinitialize(layout=[self.x, self.y]) # self.fmodel.calculate_wake() # Compute the objective function funcs = {} funcs["obj"] = ( -1 * self.mean_distance(self.x, self.y) ) # Compute constraints, if any are defined for the optimization funcs = self.compute_cons(funcs, self.x, self.y) fail = False return funcs, fail # Optionally, the user can supply the optimization with gradients # def _sens(self, varDict, funcs): # funcsSens = {} # fail = False # return funcsSens, fail def parse_opt_vars(self, varDict): self.x = self._unnorm(varDict["x"], self.xmin, self.xmax) self.y = self._unnorm(varDict["y"], self.ymin, self.ymax) def parse_sol_vars(self, sol): self.x = list(self._unnorm(sol.getDVs()["x"], self.xmin, self.xmax))[0] self.y = list(self._unnorm(sol.getDVs()["y"], self.ymin, self.ymax))[1] def add_var_group(self, optProb): optProb.addVarGroup( "x", self.nturbs, type="c", lower=0.0, upper=1.0, value=self.x0 ) optProb.addVarGroup( "y", self.nturbs, type="c", lower=0.0, upper=1.0, value=self.y0 ) return optProb def add_con_group(self, optProb): optProb.addConGroup("boundary_con", self.nturbs, upper=0.0) optProb.addConGroup("spacing_con", 1, upper=0.0) return optProb def compute_cons(self, funcs, x, y): funcs["boundary_con"] = self.distance_from_boundaries(x, y) funcs["spacing_con"] = self.space_constraint(x, y) return funcs def mean_distance(self, x, y): locs = np.vstack((x, y)).T distances = cdist(locs, locs) return np.mean(distances) def space_constraint(self, x, y, rho=500): # Calculate distances between turbines locs = np.vstack((x, y)).T distances = cdist(locs, locs) arange = np.arange(distances.shape[0]) distances[arange, arange] = 1e10 dist = np.min(distances, axis=0) g = 1 - np.array(dist) / self.min_dist # Following code copied from OpenMDAO KSComp(). # Constraint is satisfied when KS_constraint <= 0 g_max = np.max(np.atleast_2d(g), axis=-1)[:, np.newaxis] g_diff = g - g_max exponents = np.exp(rho * g_diff) summation = np.sum(exponents, axis=-1)[:, np.newaxis] KS_constraint = g_max + 1.0 / rho * np.log(summation) return KS_constraint[0][0] def distance_from_boundaries(self, x, y): boundary_con = np.zeros(self.nturbs) for i in range(self.nturbs): loc = Point(x[i], y[i]) boundary_con[i] = loc.distance(self.boundary_line) if self.boundary_polygon.contains(loc) is True: boundary_con[i] *= -1.0 return boundary_con def plot_layout_opt_results(self): """ Method to plot the old and new locations of the layout opitimization. """ locsx = self._unnorm(self.sol.getDVs()["x"], self.xmin, self.xmax) locsy = self._unnorm(self.sol.getDVs()["y"], self.ymin, self.ymax) x0 = self._unnorm(self.x0, self.xmin, self.xmax) y0 = self._unnorm(self.y0, self.ymin, self.ymax) plt.figure(figsize=(9, 6)) fontsize = 16 plt.plot(x0, y0, "ob") plt.plot(locsx, locsy, "or") # plt.title('Layout Optimization Results', fontsize=fontsize) plt.xlabel("x (m)", fontsize=fontsize) plt.ylabel("y (m)", fontsize=fontsize) plt.axis("equal") plt.grid() plt.tick_params(which="both", labelsize=fontsize) plt.legend( ["Old locations", "New locations"], loc="lower center", bbox_to_anchor=(0.5, 1.01), ncol=2, fontsize=fontsize, ) verts = self.boundaries for i in range(len(verts)): if i == len(verts) - 1: plt.plot([verts[i][0], verts[0][0]], [verts[i][1], verts[0][1]], "b") else: plt.plot( [verts[i][0], verts[i + 1][0]], [verts[i][1], verts[i + 1][1]], "b" ) ================================================ FILE: floris/optimization/layout_optimization/layout_optimization_random_search.py ================================================ from multiprocessing import Pool from time import perf_counter as timerpc import matplotlib.pyplot as plt import numpy as np from scipy.spatial.distance import cdist, pdist from shapely.geometry import Point, Polygon from floris import FlorisModel from floris.optimization.yaw_optimization.yaw_optimizer_geometric import ( YawOptimizationGeometric, ) from .layout_optimization_base import LayoutOptimization def _load_local_floris_object( fmodel_dict, wind_data=None, ): # Load local FLORIS object fmodel = FlorisModel(fmodel_dict) fmodel.set(wind_data=wind_data) return fmodel def test_min_dist(layout_x, layout_y, min_dist): coords = np.array([layout_x,layout_y]).T dist = pdist(coords) return dist.min() >= min_dist def test_point_in_bounds(test_x, test_y, poly_outer): return poly_outer.contains(Point(test_x, test_y)) # Return in MW def _get_objective( layout_x, layout_y, fmodel, yaw_angles=None, use_value=False ): fmodel.set( layout_x=layout_x, layout_y=layout_y, yaw_angles=yaw_angles ) fmodel.run() return fmodel.get_farm_AVP() if use_value else fmodel.get_farm_AEP() def _gen_dist_based_init( N, # Number of turbins to place step_size, #m, courseness of search grid poly_outer, # Polygon of outer boundary min_x, max_x, min_y, max_y, s ): """ Generates an initial layout by randomly placing the first turbine than placing the remaining turbines as far as possible from the existing turbines. """ # Set random seed np.random.seed(s) # Choose the initial point randomly init_x = float(np.random.randint(int(min_x),int(max_x))) init_y = float(np.random.randint(int(min_y),int(max_y))) while not (poly_outer.contains(Point([init_x,init_y]))): init_x = float(np.random.randint(int(min_x),int(max_x))) init_y = float(np.random.randint(int(min_y),int(max_y))) # Intialize the layout arrays layout_x = np.array([init_x]) layout_y = np.array([init_y]) layout = np.array([layout_x, layout_y]).T # Now add the remaining points for i in range(1,N): print("Placing turbine {0} of {1}.".format(i, N)) # Add a new turbine being as far as possible from current max_dist = 0. for x in np.arange(min_x, max_x,step_size): for y in np.arange(min_y, max_y,step_size): if poly_outer.contains(Point([x,y])): test_dist = cdist([[x,y]],layout) min_dist = np.min(test_dist) if min_dist > max_dist: max_dist = min_dist save_x = x save_y = y # Add point to the layout layout_x = np.append(layout_x,[save_x]) layout_y = np.append(layout_y,[save_y]) layout = np.array([layout_x, layout_y]).T # Return the layout return layout_x, layout_y class LayoutOptimizationRandomSearch(LayoutOptimization): def __init__( self, fmodel, boundaries, min_dist=None, min_dist_D=None, distance_pmf=None, n_individuals=4, seconds_per_iteration=60., total_optimization_seconds=600., interface="multiprocessing", # Options are 'multiprocessing', 'mpi4py', None max_workers=None, grid_step_size=100., relegation_number=1, enable_geometric_yaw=False, use_dist_based_init=True, random_seed=None, use_value=False, ): """ Optimize layout using genetic random search algorithm. Details of the algorithm can be found in Sinner and Fleming, 2024: https://dx.doi.org/10.1088/1742-6596/2767/3/032036 Args: fmodel (_type_): _description_ boundaries (iterable(float, float)): Pairs of x- and y-coordinates that represent the boundary's vertices (m). min_dist (float, optional): The minimum distance to be maintained between turbines during the optimization (m). If not specified, initializes to 2 rotor diameters. Defaults to None. min_dist_D (float, optional): The minimum distance to be maintained between turbines during the optimization, specified as a multiple of the rotor diameter. distance_pmf (dict, optional): Probability mass function describing the length of steps in the random search. Specified as a dictionary with keys "d" (array of step distances, specified in meters) and "p" (array of probability of occurrence, should sum to 1). Defaults to uniform probability between 0.5D and 2D, with some extra mass to encourage large changes. n_individuals (int, optional): The number of individuals to use in the optimization. Defaults to 4. seconds_per_iteration (float, optional): The number of seconds to run each step of the optimization for. Defaults to 60. total_optimization_seconds (float, optional): The total number of seconds to run the optimization for. Defaults to 600. interface (str): Parallel computing interface to leverage. Recommended is 'concurrent' or 'multiprocessing' for local (single-system) use, and 'mpi4py' for high performance computing on multiple nodes. Defaults to 'multiprocessing'. max_workers (int): Number of parallel workers, typically equal to the number of cores you have on your system or HPC. Defaults to None, which will use all available cores. grid_step_size (float): The coarseness of the grid used to generate the initial layout. Defaults to 100. relegation_number (int): The number of the lowest performing individuals to be replaced with new individuals generated from the best performing individual. Must be less than n_individuals / 2. Defaults to 1. enable_geometric_yaw (bool): Use geometric yaw code to determine approximate wake steering yaw angles during layout optimization routine. Defaults to False. use_dist_based_init (bool): Generate initial layouts automatically by placing turbines as far apart as possible. random_seed (int or None): Random seed for reproducibility. Defaults to None. use_value (bool, optional): If True, the layout optimization objective is to maximize annual value production using the value array in the FLORIS model's WindData object. If False, the optimization objective is to maximize AEP. Defaults to False. """ # The parallel computing interface to use if interface == "mpi4py": import mpi4py.futures as mp self._PoolExecutor = mp.MPIPoolExecutor elif interface == "multiprocessing": import multiprocessing as mp self._PoolExecutor = mp.Pool if max_workers is None: max_workers = mp.cpu_count() elif interface is None: if n_individuals > 1 or (max_workers is not None and max_workers > 1): print( "Parallelization not possible with interface=None. " +"Reducing n_individuals to 1 and ignoring max_workers." ) self._PoolExecutor = None max_workers = None n_individuals = 1 # elif interface == "concurrent": # from concurrent.futures import ProcessPoolExecutor # self._PoolExecutor = ProcessPoolExecutor else: raise ValueError( f"Interface '{interface}' not recognized. " "Please use ' 'multiprocessing' or 'mpi4py'." ) # Store the max_workers self.max_workers = max_workers # Store the interface self.interface = interface # Set and store the random seed self.random_seed = random_seed # Confirm the relegation_number is valid if relegation_number > n_individuals / 2: raise ValueError("relegation_number must be less than n_individuals / 2.") self.relegation_number = relegation_number # Store the rotor diameter and number of turbines self.D = fmodel.core.farm.rotor_diameters.max() if not all(fmodel.core.farm.rotor_diameters == self.D): self.logger.warning("Using largest rotor diameter for min_dist_D and distance_pmf.") self.N_turbines = fmodel.n_turbines # Make sure not both min_dist and min_dist_D are defined if min_dist is not None and min_dist_D is not None: raise ValueError("Only one of min_dist and min_dist_D can be defined.") # If min_dist_D is defined, convert to min_dist if min_dist_D is not None: min_dist = min_dist_D * self.D super().__init__( fmodel, boundaries, min_dist=min_dist, enable_geometric_yaw=enable_geometric_yaw, use_value=use_value, ) if use_value: self._obj_name = "value" self._obj_unit = "" else: self._obj_name = "AEP" self._obj_unit = "[GWh]" # Save min_dist_D self.min_dist_D = self.min_dist / self.D # Process and save the step distribution self._process_dist_pmf(distance_pmf) # Store the Core dictionary self.fmodel_dict = self.fmodel.core.as_dict() # Save the grid step size self.grid_step_size = grid_step_size # Save number of individuals self.n_individuals = n_individuals # Store the initial locations self.x_initial = self.fmodel.layout_x self.y_initial = self.fmodel.layout_y # Store the total optimization seconds self.total_optimization_seconds = total_optimization_seconds # Store the seconds per iteration self.seconds_per_iteration = seconds_per_iteration # Get the initial objective value self.x = self.x_initial # Required by _get_geoyaw_angles self.y = self.y_initial # Required by _get_geoyaw_angles self.objective_initial = _get_objective( self.x_initial, self.y_initial, self.fmodel, self._get_geoyaw_angles(), self.use_value, ) # Initialize the objective statistics self.objective_mean = self.objective_initial self.objective_median = self.objective_initial self.objective_max = self.objective_initial self.objective_min = self.objective_initial # Initialize the numpy arrays which will hold the candidate layouts # these will have dimensions n_individuals x N_turbines self.x_candidate = np.zeros((self.n_individuals, self.N_turbines)) self.y_candidate = np.zeros((self.n_individuals, self.N_turbines)) # Initialize the array which will hold the objective function values for each candidate self.objective_candidate = np.zeros(self.n_individuals) # Initialize the iteration step self.iteration_step = -1 # Initialize the optimization time self.opt_time_start = timerpc() self.opt_time = 0 # Generate the initial layouts if use_dist_based_init: self._generate_initial_layouts() else: print(f'Using supplied initial layout for {self.n_individuals} individuals.') for i in range(self.n_individuals): self.x_candidate[i, :] = self.x_initial self.y_candidate[i, :] = self.y_initial self.objective_candidate[i] = self.objective_initial # Evaluate the initial optimization step self._evaluate_opt_step() # Delete stored x and y to avoid confusion del self.x, self.y # Set up to run in normal mode self.debug = False def describe(self): print("Random Layout Optimization") print(f"Number of turbines to optimize = {self.N_turbines}") print(f"Minimum distance between turbines = {self.min_dist_D} [D], {self.min_dist} [m]") print(f"Number of individuals = {self.n_individuals}") print(f"Seconds per iteration = {self.seconds_per_iteration}") print(f"Initial {self._obj_name} = {self.objective_initial/1e9:.1f} {self._obj_unit}") def _process_dist_pmf(self, dist_pmf): """ Check validity of pmf and assign default if none provided. """ if dist_pmf is None: jump_dist = np.min([self.xmax-self.xmin, self.ymax-self.ymin])/2 jump_prob = 0.05 d = np.append(np.linspace(0.0, 2.0*self.D, 99), jump_dist) p = np.append((1-jump_prob)/len(d)*np.ones(len(d)-1), jump_prob) p = p / p.sum() dist_pmf = {"d":d, "p":p} # Check correct keys are provided if not all(k in dist_pmf for k in ("d", "p")): raise KeyError("distance_pmf must contains keys \"d\" (step distance)"+\ " and \"p\" (probability of occurrence).") # Check entries are in the correct form if not hasattr(dist_pmf["d"], "__len__") or not hasattr(dist_pmf["d"], "__len__")\ or len(dist_pmf["d"]) != len(dist_pmf["p"]): raise TypeError("distance_pmf entries should be numpy arrays or lists"+\ " of equal length.") if not np.isclose(dist_pmf["p"].sum(), 1): print("Probability mass function does not sum to 1. Normalizing.") dist_pmf["p"] = np.array(dist_pmf["p"]) / np.array(dist_pmf["p"]).sum() self.distance_pmf = dist_pmf def _evaluate_opt_step(self): # Sort the candidate layouts by objective function value sorted_indices = np.argsort(self.objective_candidate)[::-1] # Decreasing order self.objective_candidate = self.objective_candidate[sorted_indices] self.x_candidate = self.x_candidate[sorted_indices] self.y_candidate = self.y_candidate[sorted_indices] # Update the optimization time self.opt_time = timerpc() - self.opt_time_start # Update the optimizations step self.iteration_step += 1 # Update the objective statistics self.objective_mean = np.mean(self.objective_candidate) self.objective_median = np.median(self.objective_candidate) self.objective_max = np.max(self.objective_candidate) self.objective_min = np.min(self.objective_candidate) # Report the results increase_mean = ( 100 * (self.objective_mean - self.objective_initial) / self.objective_initial ) increase_median = ( 100 * (self.objective_median - self.objective_initial) / self.objective_initial ) increase_max = 100 * (self.objective_max - self.objective_initial) / self.objective_initial increase_min = 100 * (self.objective_min - self.objective_initial) / self.objective_initial print("=======================================") print(f"Optimization step {self.iteration_step:+.1f}") print(f"Optimization time = {self.opt_time:+.1f} [s]") print( f"Mean {self._obj_name} = {self.objective_mean/1e9:.1f}" f" {self._obj_unit} ({increase_mean:+.2f}%)" ) print( f"Median {self._obj_name} = {self.objective_median/1e9:.1f}" f" {self._obj_unit} ({increase_median:+.2f}%)" ) print( f"Max {self._obj_name} = {self.objective_max/1e9:.1f}" f" {self._obj_unit} ({increase_max:+.2f}%)" ) print( f"Min {self._obj_name} = {self.objective_min/1e9:.1f}" f" {self._obj_unit} ({increase_min:+.2f}%)" ) print("=======================================") # Replace the relegation_number worst performing layouts with relegation_number # best layouts if self.relegation_number > 0: self.objective_candidate[-self.relegation_number:] = ( self.objective_candidate[:self.relegation_number] ) self.x_candidate[-self.relegation_number:] = self.x_candidate[:self.relegation_number] self.y_candidate[-self.relegation_number:] = self.y_candidate[:self.relegation_number] # Private methods def _generate_initial_layouts(self): """ This method generates n_individuals initial layout of turbines. It does this by calling the _generate_random_layout method within a multiprocessing pool. """ # Set random seed for initial layout if self.random_seed is None: multi_random_seeds = [None]*self.n_individuals else: multi_random_seeds = [23 + i for i in range(self.n_individuals)] # 23 is just an arbitrary choice to ensure different random seeds # to the evaluation code print(f'Generating {self.n_individuals} initial layouts...') t1 = timerpc() # Generate the multiargs for parallel execution multiargs = [ (self.N_turbines, self.grid_step_size, self._boundary_polygon, self.xmin, self.xmax, self.ymin, self.ymax, multi_random_seeds[i]) for i in range(self.n_individuals) ] if self._PoolExecutor: # Parallelized with self._PoolExecutor(self.max_workers) as p: # This code is not currently necessary, but leaving in case implement # concurrent later, based on parallel_computing_interface.py if (self.interface == "mpi4py") or (self.interface == "multiprocessing"): out = p.starmap(_gen_dist_based_init, multiargs) else: # Parallelization not activated out = [_gen_dist_based_init(*multiargs[0])] # Unpack out into the candidate layouts for i in range(self.n_individuals): self.x_candidate[i, :] = out[i][0] self.y_candidate[i, :] = out[i][1] # Get the objective function values for each candidate layout for i in range(self.n_individuals): self.objective_candidate[i] = _get_objective( self.x_candidate[i, :], self.y_candidate[i, :], self.fmodel, self._get_geoyaw_angles(), self.use_value, ) t2 = timerpc() print(f" Time to generate initial layouts: {t2-t1:.3f} s") def _get_initial_and_final_locs(self): x_initial = self.x_initial y_initial = self.y_initial x_opt = self.x_opt y_opt = self.y_opt return x_initial, y_initial, x_opt, y_opt def _initialize_optimization(self): """ Set up logs etc """ print(f'Optimizing using {self.n_individuals} individuals.') self._opt_start_time = timerpc() self._opt_stop_time = self._opt_start_time + self.total_optimization_seconds self.objective_candidate_log = [self.objective_candidate.copy()] self.num_objective_calls_log = [] self._num_objective_calls = [0]*self.n_individuals def _run_optimization_generation(self): """ Run a generation of the outer genetic algorithm """ # Set random seed for the main loop if self.random_seed is None: multi_random_seeds = [None]*self.n_individuals else: multi_random_seeds = [55 + self.iteration_step + i for i in range(self.n_individuals)] # 55 is just an arbitrary choice to ensure different random seeds # to the initialization code # Update the optimization time sim_time = timerpc() - self._opt_start_time print(f'Optimization time: {sim_time:.1f} s / {self.total_optimization_seconds:.1f} s') # Generate the multiargs for parallel execution of single individual optimization multiargs = [ (self.seconds_per_iteration, self.objective_candidate[i], self.x_candidate[i, :], self.y_candidate[i, :], self.fmodel_dict, self.fmodel.wind_data, self.min_dist, self._boundary_polygon, self.distance_pmf, self.enable_geometric_yaw, multi_random_seeds[i], self.use_value, self.debug ) for i in range(self.n_individuals) ] # Run the single individual optimization in parallel if self._PoolExecutor: # Parallelized with self._PoolExecutor(self.max_workers) as p: out = p.starmap(_single_individual_opt, multiargs) else: # Parallelization not activated out = [_single_individual_opt(*multiargs[0])] # Unpack the results for i in range(self.n_individuals): self.objective_candidate[i] = out[i][0] self.x_candidate[i, :] = out[i][1] self.y_candidate[i, :] = out[i][2] self._num_objective_calls[i] = out[i][3] self.objective_candidate_log.append(self.objective_candidate) self.num_objective_calls_log.append(self._num_objective_calls) # Evaluate the individuals for this step self._evaluate_opt_step() def _finalize_optimization(self): """ Package and print final results. """ # Finalize the result self.objective_final = self.objective_candidate[0] self.x_opt = self.x_candidate[0, :] self.y_opt = self.y_candidate[0, :] # Print the final result increase = 100 * (self.objective_final - self.objective_initial) / self.objective_initial print( f"Final {self._obj_name} = {self.objective_final/1e9:.1f}" f" {self._obj_unit} ({increase:+.2f}%)" ) def _test_optimize(self): """ Perform a fixed number of iterations with a single worker for debugging and testing purposes. """ # Set up a minimal problem to run on a single worker print("Running test optimization on a single worker.") self._PoolExecutor = None self.max_workers = None self.n_individuals = 1 self.debug = True self._initialize_optimization() # Run 2 generations for _ in range(2): self._run_optimization_generation() self._finalize_optimization() return self.objective_final, self.x_opt, self.y_opt # Public methods def optimize(self): """ Perform the optimization """ self._initialize_optimization() # Run generations until the overall stop time while timerpc() < self._opt_stop_time: self._run_optimization_generation() self._finalize_optimization() return self.objective_final, self.x_opt, self.y_opt # Helpful visualizations def plot_distance_pmf(self, ax=None): """ Tool to check the used distance pmf. """ if ax is None: _, ax = plt.subplots(1,1) ax.stem(self.distance_pmf["d"], self.distance_pmf["p"], linefmt="k-") ax.grid(True) ax.set_xlabel("Step distance [m]") ax.set_ylabel("Probability") return ax def _single_individual_opt( seconds_per_iteration, initial_objective, layout_x, layout_y, fmodel_dict, wind_data, min_dist, poly_outer, dist_pmf, enable_geometric_yaw, s, use_value, debug ): # Set random seed np.random.seed(s) # Initialize the optimization time single_opt_start_time = timerpc() stop_time = single_opt_start_time + seconds_per_iteration num_objective_calls = 0 # Get the fmodel fmodel_ = _load_local_floris_object(fmodel_dict, wind_data) # Initialize local variables num_turbines = len(layout_x) get_new_point = True # Will always be true, due to hardcoded use_momentum current_objective = initial_objective # Establish geometric yaw optimizer, if desired if enable_geometric_yaw: yaw_opt = YawOptimizationGeometric( fmodel_, minimum_yaw_angle=-30.0, maximum_yaw_angle=30.0, ) else: # yaw_angles will always be none yaw_angles = None # We have a beta feature to maintain momentum, i.e., if a move improves # the objective, we try to keep moving in that direction. This is currently # disabled. use_momentum = False # Special handling for debug mode if debug: debug_iterations = 100 stop_time = np.inf dd = 0 # Loop as long as we've not hit the stop time while timerpc() < stop_time: if debug and dd >= debug_iterations: break elif debug: dd += 1 if not use_momentum: get_new_point = True if get_new_point: #If the last test wasn't successful # Randomly select a turbine to nudge tr = np.random.randint(0,num_turbines) # Randomly select a direction to nudge in (uniform direction) rand_dir = np.random.uniform(low=0.0, high=2*np.pi) # Randomly select a distance to travel according to pmf rand_dist = np.random.choice(dist_pmf["d"], p=dist_pmf["p"]) # Get a new test point test_x = layout_x[tr] + np.cos(rand_dir) * rand_dist test_y = layout_y[tr] + np.sin(rand_dir) * rand_dist # In bounds? if not test_point_in_bounds(test_x, test_y, poly_outer): get_new_point = True continue # Make a new layout original_x = layout_x[tr] original_y = layout_y[tr] layout_x[tr] = test_x layout_y[tr] = test_y # Acceptable distances? if not test_min_dist(layout_x, layout_y,min_dist): # Revert and continue layout_x[tr] = original_x layout_y[tr] = original_y get_new_point = True continue # Does it improve the objective? if enable_geometric_yaw: # Select appropriate yaw angles yaw_opt.fmodel_subset.set(layout_x=layout_x, layout_y=layout_y) df_opt = yaw_opt.optimize() yaw_angles = np.vstack(df_opt['yaw_angles_opt']) num_objective_calls += 1 test_objective = _get_objective(layout_x, layout_y, fmodel_, yaw_angles, use_value) if test_objective > current_objective: # Accept the change current_objective = test_objective # If not a random point this cycle and it did improve things # try not getting a new point # Feature is currently disabled by use_momentum flag get_new_point = False else: # Revert the change layout_x[tr] = original_x layout_y[tr] = original_y get_new_point = True # Return the best result from this individual return current_objective, layout_x, layout_y, num_objective_calls ================================================ FILE: floris/optimization/layout_optimization/layout_optimization_scipy.py ================================================ import matplotlib.pyplot as plt import numpy as np from scipy.optimize import minimize from scipy.spatial.distance import cdist from shapely.geometry import Point from .layout_optimization_base import LayoutOptimization, list_depth class LayoutOptimizationScipy(LayoutOptimization): """ This class provides an interface for optimizing the layout of wind turbines using the Scipy optimization library. The optimization objective is to maximize annual energy production (AEP) or annual value production (AVP). """ def __init__( self, fmodel, boundaries, bnds=None, min_dist=None, solver='SLSQP', optOptions=None, enable_geometric_yaw=False, use_value=False, ): """ Args: fmodel (FlorisModel): A FlorisModel object. boundaries (iterable(float, float)): Pairs of x- and y-coordinates that represent the boundary's vertices (m). bnds (iterable, optional): Bounds for the optimization variables (pairs of min/max values for each variable (m)). If none are specified, they are set to 0 and 1. Defaults to None. min_dist (float, optional): The minimum distance to be maintained between turbines during the optimization (m). If not specified, initializes to 2 rotor diameters. Defaults to None. solver (str, optional): Sets the solver used by Scipy. Defaults to 'SLSQP'. optOptions (dict, optional): Dictionary for setting the optimization options. Defaults to None. enable_geometric_yaw (bool, optional): If True, enables geometric yaw optimization. Defaults to False. use_value (bool, optional): If True, the layout optimization objective is to maximize annual value production using the value array in the FLORIS model's WindData object. If False, the optimization objective is to maximize AEP. Defaults to False. """ if list_depth(boundaries) > 1 and hasattr(boundaries[0][0], "__len__"): raise NotImplementedError( "LayoutOptimizationScipy is not configured for multiple regions." ) super().__init__( fmodel, boundaries, min_dist=min_dist, enable_geometric_yaw=enable_geometric_yaw, use_value=use_value ) self.boundaries_norm = [ [ self._norm(val[0], self.xmin, self.xmax), self._norm(val[1], self.ymin, self.ymax), ] for val in self.boundaries ] self.x0 = [ self._norm(x, self.xmin, self.xmax) for x in self.fmodel.layout_x ] + [ self._norm(y, self.ymin, self.ymax) for y in self.fmodel.layout_y ] if bnds is not None: self.bnds = bnds else: self._set_opt_bounds() if solver is not None: self.solver = solver default_optOptions = {"maxiter": 100, "disp": True, "iprint": 2, "ftol": 1e-9, "eps":0.01} if optOptions is not None: self.optOptions = {**default_optOptions, **optOptions} else: self.optOptions = default_optOptions self._generate_constraints() # Private methods def _optimize(self): self._num_aep_calls = 0 self._aep_record = [] self.residual_plant = minimize( self._obj_func, self.x0, method=self.solver, bounds=self.bnds, constraints=self.cons, options=self.optOptions, ) return self.residual_plant.x def _obj_func(self, locs): locs_unnorm = [ self._unnorm(valx, self.xmin, self.xmax) for valx in locs[0 : self.nturbs] ] + [ self._unnorm(valy, self.ymin, self.ymax) for valy in locs[self.nturbs : 2 * self.nturbs] ] self._change_coordinates(locs_unnorm) # Compute turbine yaw angles using PJ's geometric code (if enabled) yaw_angles = self._get_geoyaw_angles() self.fmodel.set_operation(yaw_angles=yaw_angles) self.fmodel.run() self._num_aep_calls += 1 if self.use_value: val = -1 * self.fmodel.get_farm_AVP() / self.initial_AEP_or_AVP self._aep_record.append(val) return val else: aep = -1 * self.fmodel.get_farm_AEP() / self.initial_AEP_or_AVP self._aep_record.append(aep) return aep def _change_coordinates(self, locs): # Parse the layout coordinates layout_x = locs[0 : self.nturbs] layout_y = locs[self.nturbs : 2 * self.nturbs] # Store on object for use in geoyaw code self.x = layout_x self.y = layout_y # Update the turbine map in floris self.fmodel.set(layout_x=layout_x, layout_y=layout_y) def _generate_constraints(self): tmp1 = { "type": "ineq", "fun": lambda x, *args: self._space_constraint(x), } tmp2 = { "type": "ineq", "fun": lambda x: self._distance_from_boundaries(x), } self.cons = [tmp1, tmp2] def _set_opt_bounds(self): self.bnds = [(0.0, 1.0) for _ in range(2 * self.nturbs)] def _space_constraint(self, x_in, rho=500): x = [ self._unnorm(valx, self.xmin, self.xmax) for valx in x_in[0 : self.nturbs] ] y = [ self._unnorm(valy, self.ymin, self.ymax) for valy in x_in[self.nturbs : 2 * self.nturbs] ] # Calculate distances between turbines locs = np.vstack((x, y)).T distances = cdist(locs, locs) arange = np.arange(distances.shape[0]) distances[arange, arange] = 1e10 dist = np.min(distances, axis=0) g = 1 - np.array(dist) / self.min_dist # Following code copied from OpenMDAO KSComp(). # Constraint is satisfied when KS_constraint <= 0 g_max = np.max(np.atleast_2d(g), axis=-1)[:, np.newaxis] g_diff = g - g_max exponents = np.exp(rho * g_diff) summation = np.sum(exponents, axis=-1)[:, np.newaxis] KS_constraint = g_max + 1.0 / rho * np.log(summation) return -1*KS_constraint[0][0] def _distance_from_boundaries(self, x_in): x = [ self._unnorm(valx, self.xmin, self.xmax) for valx in x_in[0 : self.nturbs] ] y = [ self._unnorm(valy, self.ymin, self.ymax) for valy in x_in[self.nturbs : 2 * self.nturbs] ] boundary_con = np.zeros(self.nturbs) for i in range(self.nturbs): loc = Point(x[i], y[i]) boundary_con[i] = loc.distance(self._boundary_line) if self._boundary_polygon.contains(loc) is True: boundary_con[i] *= 1.0 else: boundary_con[i] *= -1.0 return boundary_con def _get_initial_and_final_locs(self): x_initial = [ self._unnorm(valx, self.xmin, self.xmax) for valx in self.x0[0 : self.nturbs] ] y_initial = [ self._unnorm(valy, self.ymin, self.ymax) for valy in self.x0[self.nturbs : 2 * self.nturbs] ] x_opt = [ self._unnorm(valx, self.xmin, self.xmax) for valx in self.residual_plant.x[0 : self.nturbs] ] y_opt = [ self._unnorm(valy, self.ymin, self.ymax) for valy in self.residual_plant.x[self.nturbs : 2 * self.nturbs] ] return x_initial, y_initial, x_opt, y_opt # Public methods def optimize(self): """ This method finds the optimized layout of wind turbines for power production given the provided frequencies of occurrence of wind conditions (wind speed, direction). Returns: opt_locs (iterable): A list of the optimized locations of each turbine (m). """ print("=====================================================") print("Optimizing turbine layout...") print("Number of parameters to optimize = ", len(self.x0)) print("=====================================================") opt_locs_norm = self._optimize() print("Optimization complete.") opt_locs = [ [ self._unnorm(valx, self.xmin, self.xmax) for valx in opt_locs_norm[0 : self.nturbs] ], [ self._unnorm(valy, self.ymin, self.ymax) for valy in opt_locs_norm[self.nturbs : 2 * self.nturbs] ], ] return opt_locs ================================================ FILE: floris/optimization/load_optimization/__init__.py ================================================ ================================================ FILE: floris/optimization/load_optimization/load_optimization.py ================================================ """Module for the load optimization class and functions.""" import numpy as np from floris import FlorisModel from floris.core import State from floris.core.turbine.operation_models import ( POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED, ) def compute_lti( fmodel: FlorisModel, ambient_lti: np.array, wake_slope: float = 0.3, max_dist_D: float = 10.0, ): """Compute the turbine 'load turbulence intensity' (lti) for the current layout. LTI represents the turbulence intensity used in load calculations and follows the method of computing wake added turbulence described in Annex E of the IEC 61400-1 Ed. 4 standard. In principle this can be the same as the turbulence models used in the wake velocity and deflection models within FLORIS, but for consistency with the IEC standard is computed separately here. Args: fmodel (FlorisModel): FlorisModel object ambient_lti (list or np.array): Ambient 'load' turbulence intensity (lti) for each findex wake_slope (float, optional): Wake slope, defined as the lateral expansion of the wake on each side per unit downstream distance along the axial direction. Defaults to 0.3. max_dist_D (flat, optional): Maximum distance downstream of a turbine beyond which wake added turbulence is assumed to be zero, in rotor diameters. Defaults to 10.0 (see IEC 61400-1 Ed. 4 Annex E). Returns: np.array: Array of load turbulence intensity for each findex and turbine """ if fmodel.core.state is not State.USED: raise ValueError("FlorisModel must be run before computing load turbulence intensity") D = fmodel.core.farm.rotor_diameters.flatten()[0] # Get the indices for sorting and unsorting sorted_indices = fmodel.core.grid.sorted_indices[:, :, 0, 0] unsorted_indices = fmodel.core.grid.unsorted_indices[:, :, 0, 0] # Ensure ambient_lti is a list or np.array if not isinstance(ambient_lti, (list, np.ndarray)): raise ValueError("ambient_lti must be a list or np.array") # Ensure ambient_lti is of length n_findex if len(ambient_lti) != fmodel.n_findex: raise ValueError( ( "ambient_lti must be a list or np.array of length n_findex", f"FMODEL findex = {fmodel.n_findex}, ambient_lti = {len(ambient_lti)}", ) ) # Initialize the lti to the ambient_lti # This should be n_findex x n_turbines # Tile the ambient ti across the turbines lti = np.tile(np.array(ambient_lti).reshape(-1, 1), (1, fmodel.n_turbines)) # Get the turbine thrust coefficients # n_findex x n_turbines cts = fmodel.get_turbine_thrust_coefficients() # Get the ambient wind speeds # n_findex ambient_wind_speeds = fmodel.wind_speeds.reshape(-1, 1) # Reshape the ambient ti for multiplication ambient_lti_reshape = np.array(ambient_lti).reshape(-1, 1) # Get the x-sorted locations x_sorted = np.mean(fmodel.core.grid.x_sorted, axis=(2, 3)) y_sorted = np.mean(fmodel.core.grid.y_sorted, axis=(2, 3)) # Put ct into sorted frame ct_sorted = np.take_along_axis(cts, sorted_indices, axis=1) # 2. Iterate over turbines from front to back across findices for t in range(fmodel.n_turbines): # Get current turbine locations x_t = x_sorted[:, t].reshape(-1, 1) y_t = y_sorted[:, t].reshape(-1, 1) # Get the current ct value ct_t = ct_sorted[:, t].reshape(-1, 1) # Get the differences dx = x_sorted - x_t dy = y_sorted - y_t # Note no deflection # Set the boundary mask wake_cone_mask = np.abs(dy) < D + wake_slope * dx # Set the downstream mask downstream_mask = dx > 0 # Calculate total distance distance = np.sqrt(dx**2 + dy**2) # Set the minimum distance mask max_dist_mask = distance < D * max_dist_D # Compute the standard deviation of the wind speed owed to this wake following Annex E # of the IEC 61400-1 Ed. 4 standard ws_std_wake_add = np.where( wake_cone_mask & downstream_mask & max_dist_mask, ambient_wind_speeds / (1.5 + 0.8 * (distance / D) / np.sqrt(ct_t)), 0.0, ) # Combine with ambient TI to get the total TI from this wake following Annex E # of the IEC 61400-1 Ed. 4 standard lti_update = ( np.sqrt(ws_std_wake_add**2 + (ambient_lti_reshape * ambient_wind_speeds) ** 2) / ambient_wind_speeds ) # Update the lti using maximum wake TI lti = np.maximum(lti, lti_update) # Re-sort lti to non-sorted frame lti = np.take_along_axis(lti, unsorted_indices, axis=1) return lti def compute_turbine_voc( fmodel: FlorisModel, A: float, ambient_lti: np.array, wake_slope: float = 0.3, max_dist_D: float = 10.0, exp_ws_std: float = 1.0, exp_thrust: float = 1.0, ): """Compute the turbine Variable Operating Cost (VOC) for each findex and turbine. Variable Operating Cost (VOC) is meant to represent the cost of operating a turbine at a particular rating in particular conditions. We envision in the future there can be several possible functions to determine VOC for a turbine, but for now we use a simple model that is proportional to the wind speed standard deviation and the absolute thrust of the turbine. Args: fmodel (FlorisModel): FlorisModel object A (float): Coefficient for the VOC calculation ambient_lti (list or np.array): Ambient 'load' turbulence intensity for each findex. wake_slope (float, optional): Wake slope, defined as the lateral expansion of the wake on each side per unit downstream distance along the axial direction. Defaults to 0.3. max_dist_D (flat, optional): Maximum distance downstream of a turbine beyond which wake added turbulence is assumed to be zero, in rotor diameters. Defaults to 10.0 (see IEC 61400-1 Ed. 4 Annex E). exp_ws_std (float, optional): Exponent for the wind speed standard deviation. Defaults to 1.0. exp_thrust (float, optional): Exponent for the thrust. Defaults to 1.0. Returns: np.array: Array of VOC for each findex and turbine """ # A should be a float and not a list or array if not isinstance(A, (int, float)): raise ValueError("A (coefficient of VOC) must be a float") # Get the ambient wind speed and apply to each turbine per findex ambient_wind_speeds = fmodel.wind_speeds ambient_wind_speeds = np.tile(ambient_wind_speeds[:, np.newaxis], (1, fmodel.n_turbines)) # Compute the rotor area D = fmodel.core.farm.rotor_diameters.flatten()[0] area = np.pi * (D / 2) ** 2 # Compute the thrust cts = fmodel.get_turbine_thrust_coefficients() thrust = 0.5 * fmodel.core.flow_field.air_density * area * cts * ambient_wind_speeds**2 # Compute the load_ti load_ti = compute_lti( fmodel=fmodel, ambient_lti=ambient_lti, wake_slope=wake_slope, max_dist_D=max_dist_D, ) # Compute wind speed standard deviation ws_std = ambient_wind_speeds * load_ti # Compute voc return A * (ws_std**exp_ws_std) * (thrust**exp_thrust) def compute_farm_voc( fmodel: FlorisModel, A: float, ambient_lti: np.array, wake_slope: float = 0.3, max_dist_D: float = 10.0, exp_ws_std: float = 1.0, exp_thrust: float = 1.0, ): """Compute the farm-total Variable Operating Cost (VOC) for each findex. Variable Operating Cost (VOC) is meant to represent the cost of operating a turbine at a particular rating in particular conditions. We envision in the future there can be several possible functions to determine VOC for a turbine, but for now we use a simple model that is proportional to the wind speed standard deviation and the absolute thrust of the turbine. The farm-total VOC is the sum of the VOC for each turbine in the farm. Args: fmodel (FlorisModel): FlorisModel object A (float): Coefficient for the VOC calculation ambient_lti (list or np.array): Ambient 'load' turbulence intensity for each findex, expressed as fractions of mean wind speed wake_slope (float, optional): Wake slope, defined as the lateral expansion of the wake on each side per unit downstream distance along the axial direction. Defaults to 0.3. max_dist_D (flat, optional): Maximum distance downstream of a turbine beyond which wake added turbulence is assumed to be zero, in rotor diameters. Defaults to 10.0 (see IEC 61400-1 Ed. 4 Annex E). exp_ws_std (float, optional): Exponent for the wind speed standard deviation. Defaults to 1.0. exp_thrust (float, optional): Exponent for the thrust. Defaults to 1.0. Returns: np.array: Array of farm VOC for each findex """ turbine_voc = compute_turbine_voc( fmodel=fmodel, A=A, ambient_lti=ambient_lti, wake_slope=wake_slope, max_dist_D=max_dist_D, exp_ws_std=exp_ws_std, exp_thrust=exp_thrust, ) return np.sum(turbine_voc, axis=1) def compute_farm_revenue( fmodel: FlorisModel, ): """Compute the farm revenue of the FlorisModel object using the values from fmodel.wind_data. Args: fmodel (FlorisModel): FlorisModel object Returns: np.array: Array of farm revenue for each findex """ if fmodel.core.state is not State.USED: raise ValueError("FlorisModel must be run before computing net revenue") # Make sure fmodel.wind_data is not None if fmodel.wind_data is None: raise ValueError("FlorisModel must have wind_data to compute net revenue") # Ensure that fmodel.wind_data.values is not None if fmodel.wind_data.values is None: raise ValueError("FlorisModel wind_data.values must be set to compute revenue") farm_power = fmodel.get_farm_power() values = fmodel.wind_data.values return farm_power * values def compute_net_revenue( fmodel: FlorisModel, A: float, ambient_lti: np.array, wake_slope: float = 0.3, max_dist_D: float = 10.0, exp_ws_std: float = 1.0, exp_thrust: float = 1.0, ): """Compute the net revenue for the current layout as the difference between the farm revenue the farm VOC for each index. Args: fmodel (FlorisModel): FlorisModel object A (float): Coefficient for the VOC calculation ambient_lti (list or np.array): Ambient 'load' turbulence intensity for each findex, expressed as fractions of mean wind speed wake_slope (float, optional): Wake slope, defined as the lateral expansion of the wake on each side per unit downstream distance along the axial direction. Defaults to 0.3. max_dist_D (flat, optional): Maximum distance downstream of a turbine beyond which wake added turbulence is assumed to be zero, in rotor diameters. Defaults to 10.0 (see IEC 61400-1 Ed. 4 Annex E). exp_ws_std (float, optional): Exponent for the wind speed standard deviation. Defaults to 1.0. exp_thrust (float, optional): Exponent for the thrust. Defaults to 1.0. Returns: np.array: Array of net revenue for each findex """ revenue = compute_farm_revenue( fmodel=fmodel, ) farm_voc = compute_farm_voc( fmodel=fmodel, A=A, ambient_lti=ambient_lti, wake_slope=wake_slope, max_dist_D=max_dist_D, exp_ws_std=exp_ws_std, exp_thrust=exp_thrust, ) return revenue - farm_voc def find_A_to_satisfy_rev_voc_ratio( fmodel: FlorisModel, target_rev_voc_ratio: float, ambient_lti: np.array, wake_slope: float = 0.3, max_dist_D: float = 10.0, exp_ws_std: float = 1.0, exp_thrust: float = 1.0, ): """Find the value of A that satisfies the target ratio of total farm revenue over all findices to total farm VOC over all findices. Args: fmodel (FlorisModel): FlorisModel object target_rev_voc_ratio (float): Target revenue to VOC ratio ambient_lti (list or np.array): Ambient 'load' turbulence intensity for each findex, expressed as fractions of mean wind speed wake_slope (float, optional): Wake slope, defined as the lateral expansion of the wake on each side per unit downstream distance along the axial direction. Defaults to 0.3. max_dist_D (flat, optional): Maximum distance downstream of a turbine beyond which wake added turbulence is assumed to be zero, in rotor diameters. Defaults to 10.0 (see IEC 61400-1 Ed. 4 Annex E). exp_ws_std (float, optional): Exponent for the wind speed standard deviation. Defaults to 1.0. exp_thrust (float, optional): Exponent for the thrust. Defaults to 1. Returns: float: Value of A that satisfies the target revenue to VOC ratio """ # Compute farm revenue farm_revenue = compute_farm_revenue( fmodel=fmodel, ) # Compute farm VOC farm_voc = compute_farm_voc( fmodel=fmodel, A=1.0, ambient_lti=ambient_lti, wake_slope=wake_slope, max_dist_D=max_dist_D, exp_ws_std=exp_ws_std, exp_thrust=exp_thrust, ) return (farm_revenue.sum() / farm_voc.sum()) / target_rev_voc_ratio def find_A_to_satisfy_target_VOC_per_MW( fmodel: FlorisModel, target_VOC_per_MW_findex: float, ambient_lti: np.array, wake_slope: float = 0.3, max_dist_D: float = 10.0, exp_ws_std: float = 1.0, exp_thrust: float = 1.0, ): """Find the value of A that satisfies the target average cost per total farm power per findex over all findices. Note that if each findex represents 1 hour of operation, this is equivalent to the target average cost/MWh. Args: fmodel (FlorisModel): FlorisModel object target_VOC_per_MW_findex (float): Target average cost per MW per findex ambient_lti (list or np.array): Ambient 'load' turbulence intensity for each findex, expressed as fractions of mean wind speed wake_slope (float, optional): Wake slope, defined as the lateral expansion of the wake on each side per unit downstream distance along the axial direction. Defaults to 0.3. max_dist_D (flat, optional): Maximum distance downstream of a turbine beyond which wake added turbulence is assumed to be zero, in rotor diameters. Defaults to 10.0 (see IEC 61400-1 Ed. 4 Annex E). exp_ws_std (float, optional): Exponent for the wind speed standard deviation. Defaults to 1.0. exp_thrust (float, optional): Exponent for the thrust. Defaults to 1. Returns: float: Value of A that satisfies the target cost/MW/findex """ if fmodel.core.state is not State.USED: raise ValueError("FlorisModel must be run before finding A for target cost/MW/findex") # Compute farm power farm_power = fmodel.get_farm_power() # Compute farm VOC farm_voc = compute_farm_voc( fmodel=fmodel, A=1.0, ambient_lti=ambient_lti, wake_slope=wake_slope, max_dist_D=max_dist_D, exp_ws_std=exp_ws_std, exp_thrust=exp_thrust, ) return 1e-6 * target_VOC_per_MW_findex / (farm_voc.sum() / (farm_power.sum())) def optimize_power_setpoints( fmodel: FlorisModel, A: float, ambient_lti: np.array, wake_slope: float = 0.3, max_dist_D: float = 10.0, exp_ws_std: float = 1.0, exp_thrust: float = 1.0, power_setpoint_initial: np.array = None, power_setpoint_levels: np.array = np.linspace( POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED, 5 ), ): """Optimize the derating of each turbine to maximize net revenue sequentially from upstream to downstream. Args: fmodel (FlorisModel): FlorisModel object A (float): Coefficient for the VOC calculation ambient_lti (list or np.array): Ambient 'load' turbulence intensity for each findex, expressed as fractions of mean wind speed wake_slope (float, optional): Wake slope, defined as the lateral expansion of the wake on each side per unit downstream distance along the axial direction. Defaults to 0.3. max_dist_D (flat, optional): Maximum distance downstream of a turbine beyond which wake added turbulence is assumed to be zero, in rotor diameters. Defaults to 10.0 (see IEC 61400-1 Ed. 4 Annex E). exp_ws_std (float, optional): Exponent for the wind speed standard deviation. Defaults to 1.0. exp_thrust (float, optional): Exponent for the thrust. Defaults to 1. power_setpoint_initial (np.array, optional): Initial power setpoint for each turbine. If None, each turbine's rated power will be used. Defaults to None. power_setpoint_levels (np.array, optional): Array of power setpoint levels to consider in optimization in W. Defaults to np.linspace(POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED, 5). """ # Ensure we're in an operation model which includes derating # presently this can be "mixed" or "simple-derating" if fmodel.get_operation_model() not in ["mixed", "simple-derating"]: raise ValueError( "Operation model must include derating (e.g., 'mixed' or 'simple-derating')" ) # Raise an error if there is more than one turbine type specified if not np.array( [ fmodel.core.farm.turbine_definitions[0] == td for td in fmodel.core.farm.turbine_definitions ] ).all(): raise NotImplementedError("Only one turbine type is currently supported for optimization") # If initial set point not provided, set to rated (assumed max) power if power_setpoint_initial is None: max_power = fmodel.core.farm.turbine_map[0].power_thrust_table["power"].max() * 1000.0 power_setpoint_initial = np.tile(max_power, (fmodel.n_findex, 1)) # Initialize the test power setpoints power_setpoint_test = power_setpoint_initial.copy() power_setpoint_opt = power_setpoint_initial.copy() # Get the sorted coords sorted_indices = fmodel.core.grid.sorted_indices[:, :, 0, 0] # Initialize the net revenue using the initial setpoints fmodel.set(power_setpoints=power_setpoint_initial) fmodel.run() net_revenue_opt = compute_net_revenue( fmodel=fmodel, A=A, ambient_lti=ambient_lti, wake_slope=wake_slope, max_dist_D=max_dist_D, exp_ws_std=exp_ws_std, exp_thrust=exp_thrust, ) # Now loop over turbines for t in sorted_indices.T: # Loop over derating levels for d in power_setpoint_levels: # Apply the proposed derating level to the test_power_setpoint matrix power_setpoint_test[range(fmodel.n_findex), t] = d # Apply the setpoint to fmodel fmodel.set(power_setpoints=power_setpoint_test) # Run fmodel.run() # Get the net revenue test_net_revenue = compute_net_revenue( fmodel=fmodel, A=A, ambient_lti=ambient_lti, wake_slope=wake_slope, max_dist_D=max_dist_D, ) # Get a map of where test_net_revenue is greater than net_revenue update_mask = test_net_revenue > net_revenue_opt # Where update_mask is false, revert the test_power_setpoint to previous value power_setpoint_test[~update_mask, :] = power_setpoint_opt[~update_mask, :] # Update the final_power_setpoint power_setpoint_opt[:, :] = power_setpoint_test[:, :] # Update the net_revenue net_revenue_opt[update_mask] = test_net_revenue[update_mask] # Return the final power setpoint and optimized revenue return power_setpoint_opt, net_revenue_opt ================================================ FILE: floris/optimization/other/__init__.py ================================================ from . import boundary_grid ================================================ FILE: floris/optimization/other/boundary_grid.py ================================================ import numpy as np from shapely.geometry import Point, Polygon def discontinuous_grid( nrows, ncols, farm_width, farm_height, shear, rotation, center_x, center_y, shrink_boundary, boundary_x, boundary_y, eps=1e-3, ): """ Map from grid design variables to turbine x and y locations. Includes integer design variables and the formulation results in a discontinous design space. TODO: shrink_boundary doesn't work well with concave boundaries, or with boundary angles less than 90 deg Args: nrows (Int): number of rows in the grid. ncols (Int): number of columns in the grid. farm_width (Float): total grid width (before shear). farm_height (Float): total grid height. shear (Float): grid shear (rad). rotation (Float): rotation about grid center (rad). center_x (Float): location of grid x center. center_y (Float): location of grid y center. shrink_boundary (Float): how much to shrink the boundary that the grid can occupy. boundary_x (Array(Float)): x boundary points. boundary_y (Array(Float)): y boundary points. Returns: grid_x (Array(Float)): turbine x locations. grid_y (Array(Float)): turbine y locations. """ # create grid nrows = int(nrows) ncols = int(ncols) xlocs = np.linspace(0.0, farm_width, ncols) ylocs = np.linspace(0.0, farm_height, nrows) y_spacing = ylocs[1] - ylocs[0] nturbs = nrows * ncols grid_x = np.zeros(nturbs) grid_y = np.zeros(nturbs) turb = 0 for i in range(nrows): for j in range(ncols): grid_x[turb] = xlocs[j] + float(i) * y_spacing * np.tan(shear) grid_y[turb] = ylocs[i] turb += 1 # rotate grid_x, grid_y = ( np.cos(rotation) * grid_x - np.sin(rotation) * grid_y, np.sin(rotation) * grid_x + np.cos(rotation) * grid_y, ) # move center of grid grid_x = (grid_x - np.mean(grid_x)) + center_x grid_y = (grid_y - np.mean(grid_y)) + center_y # arrange the boundary # boundary = np.zeros((len(boundary_x),2)) # boundary[:,0] = boundary_x[:] # boundary[:,1] = boundary_y[:] # poly = Polygon(boundary) # centroid = poly.centroid # boundary[:,0] = (boundary_x[:]-centroid.x)*boundary_mult + centroid.x # boundary[:,1] = (boundary_y[:]-centroid.y)*boundary_mult + centroid.y # poly = Polygon(boundary) boundary = np.zeros((len(boundary_x), 2)) boundary[:, 0] = boundary_x[:] boundary[:, 1] = boundary_y[:] poly = Polygon(boundary) if shrink_boundary != 0.0: nBounds = len(boundary_x) for i in range(nBounds): point = Point(boundary_x[i] + eps, boundary_y[i]) if poly.contains(point) is True or poly.touches(point) is True: boundary[i, 0] = boundary_x[i] + shrink_boundary else: boundary[i, 0] = boundary_x[i] - shrink_boundary point = Point(boundary_x[i], boundary_y[i] + eps) if poly.contains(point) is True or poly.touches(point) is True: boundary[i, 1] = boundary_y[i] + shrink_boundary else: boundary[i, 1] = boundary_y[i] - shrink_boundary poly = Polygon(boundary) # get rid of points outside of boundary index = 0 for i in range(len(grid_x)): point = Point(grid_x[index], grid_y[index]) if poly.contains(point) is False and poly.touches(point) is False: grid_x = np.delete(grid_x, index) grid_y = np.delete(grid_y, index) else: index += 1 return grid_x, grid_y def place_boundary_turbines(n_boundary_turbs, start, boundary_x, boundary_y): """ Place turbines equally spaced traversing the perimiter if the wind farm along the boundary Args: n_boundary_turbs (Int): number of turbines to be placed on the boundary start (Float): where the first turbine should be placed boundary_x (Array(Float)): x boundary points boundary_y (Array(Float)): y boundary points Returns layout_x (Array(Float)): turbine x locations layout_y (Array(Float)): turbine y locations """ # check if the boundary is closed, correct if not if boundary_x[-1] != boundary_x[0] or boundary_y[-1] != boundary_y[0]: boundary_x = np.append(boundary_x, boundary_x[0]) boundary_y = np.append(boundary_y, boundary_y[0]) # make the boundary boundary = np.zeros((len(boundary_x), 2)) boundary[:, 0] = boundary_x[:] boundary[:, 1] = boundary_y[:] poly = Polygon(boundary) perimeter = poly.length # get the flattened turbine locations spacing = perimeter / float(n_boundary_turbs) flattened_locs = np.linspace(start, perimeter + start - spacing, n_boundary_turbs) # set all of the flattened values between 0 and the perimeter for i in range(n_boundary_turbs): while flattened_locs[i] < 0.0: flattened_locs[i] += perimeter if flattened_locs[i] > perimeter: flattened_locs[i] = flattened_locs[i] % perimeter # place the turbines around the perimeter nBounds = len(boundary_x) layout_x = np.zeros(n_boundary_turbs) layout_y = np.zeros(n_boundary_turbs) lenBound = np.zeros(nBounds - 1) for i in range(nBounds - 1): lenBound[i] = Point(boundary[i]).distance(Point(boundary[i + 1])) for i in range(n_boundary_turbs): for j in range(nBounds - 1): if flattened_locs[i] < sum(lenBound[0 : j + 1]): layout_x[i] = ( boundary_x[j] + (boundary_x[j + 1] - boundary_x[j]) * (flattened_locs[i] - sum(lenBound[0:j])) / lenBound[j] ) layout_y[i] = ( boundary_y[j] + (boundary_y[j + 1] - boundary_y[j]) * (flattened_locs[i] - sum(lenBound[0:j])) / lenBound[j] ) break return layout_x, layout_y def boundary_grid( n_boundary_turbs, start, nrows, ncols, farm_width, farm_height, shear, rotation, center_x, center_y, shrink_boundary, boundary_x, boundary_y, eps=1e-3, ): """ Place turbines equally spaced traversing the perimiter if the wind farm along the boundary Args: n_boundary_turbs,start: boundary variables nrows,ncols,farm_width,farm_height,shear, rotation,center_x,center_y,shrink_boundary,eps: grid variables boundary_x,boundary_y: boundary points Returns layout_x (Array(Float)): turbine x locations layout_y (Array(Float)): turbine y locations """ boundary_turbines_x, boundary_turbines_y = place_boundary_turbines( n_boundary_turbs, start, boundary_x, boundary_y ) grid_turbines_x, grid_turbines_y = discontinuous_grid( nrows, ncols, farm_width, farm_height, shear, rotation, center_x, center_y, shrink_boundary, boundary_x, boundary_y, eps=eps, ) layout_x = np.append(boundary_turbines_x, grid_turbines_x) layout_y = np.append(boundary_turbines_y, grid_turbines_y) return layout_x, layout_y class BoundaryGrid: """ Parameterize the wind farm layout with a grid or the boundary grid method """ def __init__(self, fi): """ Initializes a BoundaryGrid object by assigning a FlorisModel object. Args: fmodel (FlorisModel): A FlorisModel object. """ self.fmodel = fi self.n_boundary_turbs = 0 self.start = 0.0 self.nrows = 0 self.ncols = 0 self.farm_width = 0.0 self.farm_height = 0.0 self.shear = 0.0 self.rotation = 0.0 self.center_x = 0.0 self.center_y = 0.0 self.shrink_boundary = 0.0 self.boundary_x = np.array([]) self.boundary_y = np.array([]) self.eps = 1e-3 def reinitialize_bg( self, n_boundary_turbs=None, start=None, nrows=None, ncols=None, farm_width=None, farm_height=None, shear=None, rotation=None, center_x=None, center_y=None, shrink_boundary=None, boundary_x=None, boundary_y=None, eps=None, ): if n_boundary_turbs is not None: self.n_boundary_turbs = n_boundary_turbs if start is not None: self.start = start if nrows is not None: self.nrows = nrows if ncols is not None: self.ncols = ncols if farm_width is not None: self.farm_width = farm_width if farm_height is not None: self.farm_height = farm_height if shear is not None: self.shear = shear if rotation is not None: self.rotation = rotation if center_x is not None: self.center_x = center_x if center_y is not None: self.center_y = center_y if shrink_boundary is not None: self.shrink_boundary = shrink_boundary if boundary_x is not None: self.boundary_x = boundary_x if boundary_y is not None: self.boundary_y = boundary_y if eps is not None: self.eps = eps def reinitialize_xy(self): layout_x, layout_y = boundary_grid( self.n_boundary_turbs, self.start, self.nrows, self.ncols, self.farm_width, self.farm_height, self.shear, self.rotation, self.center_x, self.center_y, self.shrink_boundary, self.boundary_x, self.boundary_y, eps=self.eps, ) self.fmodel.reinitialize_flow_field(layout_array=(layout_x, layout_y)) if __name__ == "__main__": nrows = 10 ncols = 10 farm_width = 600 farm_height = 600 shear = np.deg2rad(10) rotation = np.deg2rad(30) center_x = 250 center_y = 300 boundary_mult = 0.6 shrink_boundary = 10.0 # boundary_x = np.array([-300.0,-100.0,100.0,100.0,0.0]) + 2200.0 # boundary_y = np.array([-100.0,100.0,140.0,-100.0,0.0]) + 300.0 boundary_x = np.array( [0.0, 100.0, 100.0, 200.0, 200.0, 300.0, 300.0, 400.0, 400.0, 500.0, 500.0, 0.0] ) boundary_y = np.array( [ 500.0, 500.0, 400.0, 400.0, 300.0, 300.0, 200.0, 200.0, 100.0, 100.0, 600.0, 600.0, ] ) x, y = discontinuous_grid( nrows, ncols, farm_width, farm_height, shear, rotation, center_x, center_y, shrink_boundary, boundary_x, boundary_y, ) n_boundary_turbs = 25 start = 1000.0 layout_x, layout_y = place_boundary_turbines( n_boundary_turbs, start, boundary_x, boundary_y ) bx = np.append(boundary_x, boundary_x[0]) by = np.append(boundary_y, boundary_y[0]) boundary = np.zeros((len(bx), 2)) boundary[:, 0] = bx[:] boundary[:, 1] = by[:] poly = Polygon(boundary) # centroid = poly.centroid # new_bx = (bx[:]-centroid.x)*boundary_mult + centroid.x # new_by = (by[:]-centroid.y)*boundary_mult + centroid.y nBounds = len(bx) new_bx = np.zeros(len(bx)) new_by = np.zeros(len(by)) eps = 1e-3 for i in range(nBounds): point = Point(bx[i] + eps, by[i]) if poly.contains(point) is True or poly.touches(point) is True: new_bx[i] = bx[i] + shrink_boundary else: new_bx[i] = bx[i] - shrink_boundary point = Point(bx[i], by[i] + eps) if poly.contains(point) is True or poly.touches(point) is True: new_by[i] = by[i] + shrink_boundary else: new_by[i] = by[i] - shrink_boundary import matplotlib.pyplot as plt nx, ny = boundary_grid( n_boundary_turbs, start, nrows, ncols, farm_width, farm_height, shear, rotation, center_x, center_y, shrink_boundary, boundary_x, boundary_y, ) plt.plot(bx, by) plt.plot(new_bx, new_by) plt.plot(x, y, "o") plt.plot(layout_x, layout_y, "o") plt.axis("equal") plt.figure(2) plt.plot(bx, by) plt.plot(nx, ny, "o") plt.axis("equal") plt.show() ================================================ FILE: floris/optimization/yaw_optimization/__init__.py ================================================ ================================================ FILE: floris/optimization/yaw_optimization/yaw_optimization_base.py ================================================ import copy from time import perf_counter as timerpc import numpy as np import pandas as pd from floris.core.turbine.operation_models import POWER_SETPOINT_DISABLED from floris.logging_manager import LoggingManager from .yaw_optimization_tools import derive_downstream_turbines class YawOptimization(LoggingManager): """ YawOptimization is a subclass of :py:class:`floris.optimization.scipy. Optimization` that is used to optimize the yaw angles of all turbines in a Floris Farm for a single set of inflow conditions using the SciPy optimize package. """ def __init__( self, fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=25.0, yaw_angles_baseline=None, x0=None, turbine_weights=None, normalize_control_variables=False, calc_baseline_power=True, exclude_downstream_turbines=True, verify_convergence=False, ): """ Instantiate YawOptimization object with a FlorisModel object and assign parameter values. Args: fmodel (:py:class:`~.floris_model.FlorisModel`): A FlorisModel object. minimum_yaw_angle (float or ndarray): Minimum constraint on yaw angle (deg). If a single value specified, assumes this value for all turbines. If a 1D array is specified, assumes these limits for each turbine specifically, but uniformly across all atmospheric conditions. If a 2D array, limits are specific both to the turbine and to the atmospheric condition. Defaults to 0.0. maximum_yaw_angle (float or ndarray): Maximum constraint on yaw angle (deg). If a single value specified, assumes this value for all turbines. If a 1D array is specified, assumes these limits for each turbine specifically, but uniformly across all atmospheric conditions. If a 2D array, limits are specific both to the turbine and to the atmospheric condition. Defaults to 25.0. yaw_angles_baseline (iterable, optional): The baseline yaw angles used to calculate the initial and baseline power production in the wind farm and used to normalize the cost function. If none are specified, this variable is set equal to the current yaw angles in floris. Note that this variable need not meet the yaw constraints specified in self.bnds, yet a warning is raised if it does to inform the user. Defaults to None. x0 (iterable, optional): The initial guess for the optimization problem. These values must meet the constraints specified in self.bnds. Note that, if exclude_downstream_turbines=True, the initial guess for any downstream turbines are ignored since they are not part of the optimization. Instead, the yaw angles for those turbines are 0.0 if that meets the lower and upper bound, or otherwise as close to 0.0 as feasible. If no values for x0 are specified, x0 is set to be equal to zeros wherever feasible (w.r.t. the bounds), and equal to the average of its lower and upper bound for all non-downstream turbines otherwise. Defaults to None. turbine_weights (iterable, optional): weighing terms that allow the user to emphasize power gains at particular turbines or completely ignore power gains from other turbines. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and length equal to the number of turbines. Defaults to None. calc_init_power (bool, optional): If True, calculates initial wind farm power for each set of wind conditions. Defaults to True. exclude_downstream_turbines (bool, optional): If True, automatically finds and excludes turbines that are most downstream from the optimization problem. This significantly reduces computation time at no loss in performance. The yaw angles of these downstream turbines are fixed to 0.0 deg if the yaw bounds specified in self.bnds allow that, or otherwise are fixed to the lower or upper yaw bound, whichever is closer to 0.0. Defaults to False. verify_convergence (bool, optional): specifies whether the found optimal yaw angles will be checked for accurately convergence. With large farms, especially when using SciPy or other global optimization methods, solutions do not always converge and turbines that should have a 0.0 deg actually have a 1.0 deg angle, for example. By enabling this function, the final yaw angles are compared to their baseline values one-by-one for the turbines to make sure no such convergence issues arise. Defaults to False. """ # Save turbine object to self self.fmodel = copy.deepcopy(fmodel) self.nturbs = len(self.fmodel.layout_x) # # Check floris options # if self.fmodel.core.flow_field.n_wind_speeds > 1: # raise NotImplementedError( # "Optimizer currently does not support more than one wind" + # " speed. Please assign FLORIS a single wind speed." # ) # Initialize optimizer self.verify_convergence = verify_convergence if yaw_angles_baseline is not None: yaw_angles_baseline = self._unpack_variable(yaw_angles_baseline) self.yaw_angles_baseline = yaw_angles_baseline else: b = self.fmodel.core.farm.yaw_angles self.yaw_angles_baseline = self._unpack_variable(b) if np.any(np.abs(b) > 0.0): print( "INFO: Baseline yaw angles were not specified and " "were derived from the floris object." ) print( "INFO: The inherent yaw angles in the floris object " "are not all 0.0 degrees." ) # Set optimization bounds self.minimum_yaw_angle = self._unpack_variable(minimum_yaw_angle) self.maximum_yaw_angle = self._unpack_variable(maximum_yaw_angle) # Limit yaw angles to zero for disabled turbines active_turbines = fmodel.core.farm.power_setpoints > POWER_SETPOINT_DISABLED self.minimum_yaw_angle[~active_turbines] = 0.0 self.maximum_yaw_angle[~active_turbines] = 0.0 # Set initial condition for optimization if x0 is not None: self.x0 = self._unpack_variable(x0) else: self.x0 = self._unpack_variable(0.0) for ti in range(self.nturbs): yaw_lb = self.minimum_yaw_angle[:, ti] yaw_ub = self.maximum_yaw_angle[:, ti] idx = (yaw_lb > 0.0) | (yaw_ub < 0.0) self.x0[idx, ti] = (yaw_lb[idx] + yaw_ub[idx]) / 2.0 # Check inputs for consistency if np.any(self.yaw_angles_baseline < self.minimum_yaw_angle): print("INFO: yaw_angles_baseline exceed lower bound constraints.") if np.any(self.yaw_angles_baseline > self.maximum_yaw_angle): print("INFO: yaw_angles_baseline exceed upper bound constraints.") if np.any(self.x0 < self.minimum_yaw_angle): raise ValueError("Initial guess x0 exceeds lower bound constraints.") if np.any(self.x0 > self.maximum_yaw_angle): raise ValueError("Initial guess x0 exceeds upper bound constraints.") # Define turbine weighing terms if turbine_weights is None: self.turbine_weights = self._unpack_variable(1.0) else: self.turbine_weights = self._unpack_variable(turbine_weights) # Save remaining user options to self self.normalize_variables = normalize_control_variables self.calc_baseline_power = calc_baseline_power self.exclude_downstream_turbines = exclude_downstream_turbines # Prepare for optimization and calculate baseline powers (if applic.) self._initialize() self._calculate_baseline_farm_power() # Initialize optimal yaw angles and cost function as baseline values self._yaw_angles_opt_subset = copy.deepcopy(self._yaw_angles_baseline_subset) self._farm_power_opt_subset = copy.deepcopy(self._farm_power_baseline_subset) self._yaw_lbs = copy.deepcopy(self._minimum_yaw_angle_subset) self._yaw_ubs = copy.deepcopy(self._maximum_yaw_angle_subset) # Private methods def _initialize(self): # Reduce optimization problem as much as possible self._reduce_control_problem() # Normalize optimization variables if self.normalize_variables: self._normalize_control_problem() def _unpack_variable(self, variable, subset=False): """Take a variable, can be either a float, a list equal in length to the number of turbines, or an ndarray. It then upsamples this value so that it always matches the dimensions (self.nconds, self.nturbs). """ # Deal with full vs. subset dimensions nturbs = self.nturbs if subset: nturbs = np.shape(self._x0_subset.shape[1]) # Then process maximum yaw angle if isinstance(variable, (int, float)): # If single value, copy over to all turbines variable = np.tile(variable, (nturbs)) variable = np.array(variable, dtype=float) if len(np.shape(variable)) == 1: # If one-dimensional array, copy over to all atmos. conditions variable = np.tile( variable, (self.fmodel.core.flow_field.n_findex, 1) ) return variable def _reduce_control_problem(self): """ This function reduces the control problem by eliminating turbines of which the yaw angles need not be optimized, either because of a user-specified set of bounds (where bounds[i][0] == bounds[i][1]), or alternatively turbines that are far downstream in the wind farm and of which the wake does not impinge other turbines, if exclude_downstream_turbines == True. """ # Initialize which turbines to optimize for self.turbs_to_opt = (self.maximum_yaw_angle - self.minimum_yaw_angle >= 0.001) # Initialize subset variables as full set self.fmodel_subset = copy.deepcopy(self.fmodel) if hasattr(self.fmodel_subset, "_wind_data"): # Normal FlorisModel, ParFlorisModel self.fmodel_subset._wind_data = None # Accessing private attribute! elif hasattr(self.fmodel_subset, "fmodel_expanded"): # UncertainFlorisModel self.fmodel_subset.fmodel_unexpanded._wind_data = None # Accessing private attribute! n_findex_subset = copy.deepcopy(self.fmodel.core.flow_field.n_findex) minimum_yaw_angle_subset = copy.deepcopy(self.minimum_yaw_angle) maximum_yaw_angle_subset = copy.deepcopy(self.maximum_yaw_angle) x0_subset = copy.deepcopy(self.x0) turbs_to_opt_subset = copy.deepcopy(self.turbs_to_opt) turbine_weights_subset = copy.deepcopy(self.turbine_weights) yaw_angles_template_subset = self._unpack_variable(0.0) yaw_angles_baseline_subset = copy.deepcopy(self.yaw_angles_baseline) # Define which turbines to optimize for if self.exclude_downstream_turbines: for iw, wd in enumerate(self.fmodel.core.flow_field.wind_directions): # Remove turbines from turbs_to_opt that are downstream downstream_turbines = derive_downstream_turbines(self.fmodel, wd) downstream_turbines = np.array(downstream_turbines, dtype=int) self.turbs_to_opt[iw, downstream_turbines] = False turbs_to_opt_subset = copy.deepcopy(self.turbs_to_opt) # Update # Set up a template yaw angles array with default solutions. The default # solutions are either 0.0 or the allowable yaw angle closest to 0.0 deg. # This solution addresses both downstream turbines, minimizing their abs. # yaw offset, and additionally fixing equality-constrained turbines to # their appropriate yaw angle. idx = (minimum_yaw_angle_subset > 0.0) | (maximum_yaw_angle_subset < 0.0) if np.any(idx): # Find bounds closest to 0.0 deg combined_bounds = np.concatenate( ( np.expand_dims(minimum_yaw_angle_subset, axis=2), np.expand_dims(maximum_yaw_angle_subset, axis=2) ), axis=2 ) # Overwrite all values that are not allowed to be 0.0 with bound value closest to zero ids_closest = np.expand_dims(np.argmin(np.abs(combined_bounds), axis=2), axis=2) yaw_mb = np.squeeze(np.take_along_axis(combined_bounds, ids_closest, axis=2), axis=2) yaw_angles_template_subset[idx] = yaw_mb[idx] # Save all subset variables to self self._n_findex_subset = n_findex_subset self._minimum_yaw_angle_subset = minimum_yaw_angle_subset self._maximum_yaw_angle_subset = maximum_yaw_angle_subset self._x0_subset = x0_subset self._turbs_to_opt_subset = turbs_to_opt_subset self._turbine_weights_subset = turbine_weights_subset self._yaw_angles_template_subset = yaw_angles_template_subset self._yaw_angles_baseline_subset = yaw_angles_baseline_subset def _normalize_control_problem(self): """ This private function normalizes variables for the optimization problem, specifically the initial condition x0 and the bounds. Normalization can improve optimization performance when using common optimization methods such as the SciPy Optimization Toolbox. """ lb = np.min(self._minimum_yaw_angle_subset) ub = np.max(self._maximum_yaw_angle_subset) self._normalization_length = (ub - lb) self._x0_subset_norm = self._x0_subset / self._normalization_length self._minimum_yaw_angle_subset_norm = ( self._minimum_yaw_angle_subset / self._normalization_length ) self._maximum_yaw_angle_subset_norm = ( self._maximum_yaw_angle_subset / self._normalization_length ) def _calculate_farm_power( self, yaw_angles=None, wd_array=None, ws_array=None, ti_array=None, turbine_weights=None, heterogeneous_speed_multipliers=None, power_setpoints=None, ): """ Calculate the wind farm power production assuming the predefined probability distribution (self.unc_options/unc_pmf), with the appropriate weighing terms, and for a specific set of yaw angles. Args: yaw_angles (iterable, optional): Array or list of yaw angles in degrees. Defaults to None. wd_array (iterable, optional): Array or list of wind directions in degrees. Defaults to None. ws_array (iterable, optional): Array or list of wind speeds in m/s. Defaults to None. ti_array (iterable, optional): Array or list of turbulence intensities. Defaults to None. turbine_weights (iterable, optional): Array or list of weights to apply to the turbine powers. Defaults to None. heterogeneous_speed_multipliers (iterable, optional): Array or list of speed up factors for heterogeneous inflow. Defaults to None. Returns: farm_power (float): Weighted wind farm power. """ # Unpack all variables, whichever are defined. fmodel_subset = copy.deepcopy(self.fmodel_subset) if wd_array is None: wd_array = fmodel_subset.core.flow_field.wind_directions if ws_array is None: ws_array = fmodel_subset.core.flow_field.wind_speeds if ti_array is None: ti_array = fmodel_subset.core.flow_field.turbulence_intensities if yaw_angles is None: yaw_angles = self._yaw_angles_baseline_subset if turbine_weights is None: turbine_weights = self._turbine_weights_subset if heterogeneous_speed_multipliers is not None: fmodel_subset.core.flow_field.\ heterogeneous_inflow_config['speed_multipliers'] = heterogeneous_speed_multipliers # Ensure format [incompatible with _subset notation] yaw_angles = self._unpack_variable(yaw_angles, subset=True) # # Correct wind direction definition: 270 deg is from left, cw positive # wd_array = wrap_360(wd_array) # Calculate solutions turbine_power = np.zeros_like(self._minimum_yaw_angle_subset[:, :]) fmodel_subset.set( wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array, yaw_angles=yaw_angles, power_setpoints=power_setpoints, ) fmodel_subset.run() turbine_power = fmodel_subset.get_turbine_powers() # Multiply with turbine weighing terms turbine_power_weighted = np.multiply(turbine_weights, turbine_power) farm_power_weighted = np.sum(turbine_power_weighted, axis=1) return farm_power_weighted def _calculate_baseline_farm_power(self): """ Calculate the weighted wind farm power under the baseline turbine yaw angles. """ if self.calc_baseline_power: P = self._calculate_farm_power(self._yaw_angles_baseline_subset) self._farm_power_baseline_subset = P self.farm_power_baseline = P else: self._farm_power_baseline_subset = None self.farm_power_baseline = None def _finalize(self, farm_power_opt_subset=None, yaw_angles_opt_subset=None): # Process final solutions if farm_power_opt_subset is None: farm_power_opt_subset = self._farm_power_opt_subset if yaw_angles_opt_subset is None: yaw_angles_opt_subset = self._yaw_angles_opt_subset # Now verify solutions for convergence, if necessary if self.verify_convergence: yaw_angles_opt_subset, farm_power_opt_subset = ( self._verify_solutions_for_convergence( farm_power_opt_subset, yaw_angles_opt_subset ) ) # Finalization step for optimization: undo reduction step self.farm_power_opt = farm_power_opt_subset self.yaw_angles_opt = yaw_angles_opt_subset # Produce output table df_list = [] df_list.append( pd.DataFrame( { "wind_direction": self.fmodel.core.flow_field.wind_directions, "wind_speed": self.fmodel.core.flow_field.wind_speeds, "turbulence_intensity": self.fmodel.core.flow_field.turbulence_intensities, "yaw_angles_opt": list(self.yaw_angles_opt[:, :]), "farm_power_opt": None if self.farm_power_opt is None else self.farm_power_opt[:], "farm_power_baseline": None if self.farm_power_baseline is None else self.farm_power_baseline[:], } ) ) df_opt = pd.concat(df_list, axis=0) return df_opt def _verify_solutions_for_convergence( self, farm_power_opt_subset, yaw_angles_opt_subset, min_yaw_offset=0.01, min_power_gain_for_yaw=0.02, verbose=True, ): """ This function verifies whether the found solutions (yaw_angles_opt) have any nonzero yaw angles that are actually a result of incorrect convergence. By evaluating the power production by setting each turbine's yaw angle to 0.0 deg, one by one, we verify that the found optimal values do in fact lead to a nonzero power production gain. Args: farm_power_opt_subset (iterable): Array with the optimal wind farm power values (i.e., farm powers with yaw_angles_opt_subset). yaw_angles_opt_subset (iterable): Array with the optimal yaw angles for all turbines in the farm (or for all the to-be-optimized turbines in the farm). The yaw angles in this array will be verified. min_yaw_offset (float, optional): Values that differ by less than this amount compared to the baseline value will be assumed to be too small to make any notable difference. Therefore, for practical reasons, the value is overwritten by its baseline value (which typically is 0.0 deg). Defaults to 0.01. min_power_gain_for_yaw (float, optional): The minimum percentage uplift a turbine must create in the farm power production for its yaw offset to be considered non negligible. Set to 0.0 to ignore this criteria. Defaults to 0.02 (implying 0.02%). verbose (bool, optional): Print to console. Defaults to True. Returns: x_opt (iterable): Array with the optimal yaw angles, possibly with certain values being set to 0.0 deg as they were found to be a result of incorrect convergence. If the optimization has perfectly converged, x_opt will be identical to the user- provided input yaw_angles_opt. """ print("Verifying convergence of the found optimal yaw angles.") # Start timer start_time = timerpc() # Define variables locally yaw_angles_opt_subset = np.array(yaw_angles_opt_subset, copy=True) yaw_angles_baseline_subset = self._yaw_angles_baseline_subset farm_power_baseline_subset = self._farm_power_baseline_subset turbs_to_opt_subset = self._turbs_to_opt_subset # Round small nonzero yaw angles to zero ydiff = np.abs(yaw_angles_opt_subset - yaw_angles_baseline_subset) ids = np.where((ydiff < min_yaw_offset) & (ydiff > 0.0)) if len(ids[0]) > 0: if verbose: print(f"Rounding {len(ids)} insignificant yaw angles to their baseline value.") yaw_angles_opt_subset[ids] = yaw_angles_baseline_subset[ids] ydiff[ids] = 0.0 # Turbines to test whether their angles sufficiently improve farm power ids = np.where((turbs_to_opt_subset) & (ydiff > min_yaw_offset)) # Define situations that need to be calculated and find farm power. # Each situation basically contains the exact same conditions as the # baseline conditions and optimal yaw angles, besides for a single # turbine for which its yaw angle was set to its baseline value ( # typically 0.0 deg). This way, we investigate whether the yaw offset # of that turbine really adds significant uplift to the farm power # production. # For each turbine in the farm, reset its values to baseline. Thus, # we copy the atmospheric conditions n_turbs times and for each # copy of atmospheric conditions, we reset that turbine's yaw angle # to its baseline value for all conditions. n_turbs = len(self.fmodel.layout_x) sp = (n_turbs, 1) # Tile shape for matrix expansion wd_array_nominal = self.fmodel_subset.core.flow_field.wind_directions ws_array_nominal = self.fmodel_subset.core.flow_field.wind_speeds ti_array_nominal = self.fmodel_subset.core.flow_field.turbulence_intensities n_wind_directions = len(wd_array_nominal) yaw_angles_verify = np.tile(yaw_angles_opt_subset, sp) yaw_angles_bl_verify = np.tile(yaw_angles_baseline_subset, sp) turbine_id_array = np.zeros(np.shape(yaw_angles_verify)[0], dtype=int) for ti in range(n_turbs): ids = ti * n_wind_directions + np.arange(n_wind_directions) yaw_angles_verify[ids, ti] = yaw_angles_bl_verify[ids, ti] turbine_id_array[ids] = ti # Now evaluate all situations farm_power_baseline_verify = np.tile(farm_power_baseline_subset, (n_turbs)) farm_power = self._calculate_farm_power( yaw_angles=yaw_angles_verify, wd_array=np.tile(wd_array_nominal, n_turbs), ws_array=np.tile(ws_array_nominal, n_turbs), ti_array=np.tile(ti_array_nominal, n_turbs), turbine_weights=np.tile(self._turbs_to_opt_subset, sp) ) # Calculate power uplift for optimal solutions uplift_o = 100 * ( np.tile(farm_power_opt_subset, (n_turbs)) / farm_power_baseline_verify - 1.0 ) # Calculate power uplift for all cases we evaluated uplift_n = 100.0 * (farm_power / farm_power_baseline_verify - 1.0) # Check difference in uplift, where each row represents a different # situation (i.e., where one turbine was set to its baseline yaw angle # instead of its optimal yaw angle). dp = uplift_o - uplift_n ids_to_simplify = np.where(dp < min_power_gain_for_yaw) ids_to_simplify = ( np.remainder(ids_to_simplify[0], n_wind_directions), # Wind direction identifier turbine_id_array[ids_to_simplify[0]], # Turbine identifier ) # Overwrite yaw angles that insufficiently increased farm power with baseline values yaw_angles_opt_subset[ids_to_simplify] = ( yaw_angles_baseline_subset[ids_to_simplify] ) n = len(ids_to_simplify[0]) if n > 0: # Yaw angles notably changed: recalculate farm powers farm_power_opt_subset_new = ( self._calculate_farm_power(yaw_angles_opt_subset) ) if verbose: # Calculate old uplift for all conditions dP_old = 100.0 * ( farm_power_opt_subset / farm_power_baseline_subset ) - 100.0 # Calculate new uplift for all conditions dP_new = 100.0 * ( farm_power_opt_subset_new / farm_power_baseline_subset ) - 100.0 # Calculate differences in power uplift diff_uplift = dP_old - dP_new ids_max_loss = np.where(np.nanmax(diff_uplift) == diff_uplift) jj = (ids_max_loss[0][0], ids_max_loss[1][0]) ws_array_nominal = self.fmodel_subset.core.flow_field.wind_speeds print( "Nullified the optimal yaw offset for {:d}".format(n) + " conditions and turbines." ) print( "Simplifying the yaw angles for these conditions lead " + "to a maximum change in wake-steering power uplift from " + "{:.5f}% to {:.5f}% at ".format(dP_old[jj], dP_new[jj]) + " WD = {:.1f} deg and WS = {:.1f} m/s.".format( wd_array_nominal[jj[0]], ws_array_nominal[jj[1]], ) ) t = timerpc() - start_time print( "Time spent to verify the convergence of the optimal " + "yaw angles: {:.3f} s.".format(t) ) # Return optimal solutions to the user farm_power_opt_subset = farm_power_opt_subset_new return yaw_angles_opt_subset, farm_power_opt_subset # Supporting functions def _norm(self, val, x1, x2): """ Normalize a variable to a value range. Args: val ([float]): Value to normalize. x1 ([float]): Normalization lower bound. x2 ([float]): Normalization upper bound. Returns: val_norm: Normalized variable. """ return (val - x1) / (x2 - x1) def _unnorm(self, val_norm, x1, x2): """ Unnormalize a variable to a value range. Args: val_norm ([float]): Normalized value. x1 ([float]): Normalization lower bound. x2 ([float]): Normalization upper bound. Returns: val: Unnormalized variable. """ return np.array(val_norm) * (x2 - x1) + x1 ================================================ FILE: floris/optimization/yaw_optimization/yaw_optimization_tools.py ================================================ import matplotlib.pyplot as plt import numpy as np import pandas as pd def derive_downstream_turbines(fmodel, wind_direction, wake_slope=0.30, plot_lines=False): """Determine which turbines have no effect on other turbines in the farm, i.e., which turbines have wakes that do not impact the other turbines in the farm. This allows the user to exclude these turbines from a control setpoint optimization, for example. This function assumes a very simplified wake function where the wakes are assumed to have a linearly diverging profile. In comparisons with the FLORIS GCH model, the wake_slope matches well with the FLORIS' wake profiles for a value of wake_slope = 0.5 * turbulence_intensity, where turbulence_intensity is an input to the FLORIS model at the default GCH parameterization. Note that does not include wind direction variability. To be conservative, the user is recommended to use the rule of thumb: `wake_slope = turbulence_intensity`. Hence, the default value for `wake_slope=0.30` should be conservative for turbulence intensities up to 0.30 and is likely to provide valid estimates of which turbines are downstream until a turbulence intensity of 0.50. This simple model saves time compared to FLORIS. Args: fmodel (FlorisModel): A FlorisModel object. wind_direction (float): The wind direction in the FLORIS frame of reference for which the downstream turbines are to be determined. wake_slope (float, optional): linear slope of the wake (dy/dx) plot_lines (bool, optional): Enable plotting wakes/turbines. Defaults to False. Returns: turbs_downstream (iterable): A list containing the turbine numbers that have a wake that does not affect any other turbine inside the farm. """ # Get farm layout x = fmodel.layout_x y = fmodel.layout_y D = np.ones_like(x) * fmodel.core.farm.rotor_diameters_sorted[0][0] n_turbs = len(x) # Rotate farm and determine freestream/waked turbines is_downstream = [False for _ in range(n_turbs)] x_rot = ( np.cos((wind_direction - 270.0) * np.pi / 180.0) * x - np.sin((wind_direction - 270.0) * np.pi / 180.0) * y ) y_rot = ( np.sin((wind_direction - 270.0) * np.pi / 180.0) * x + np.cos((wind_direction - 270.0) * np.pi / 180.0) * y ) if plot_lines: fig, ax = plt.subplots() for ii in range(n_turbs): ax.plot( x_rot[ii] * np.ones(2), [y_rot[ii] - D[ii] / 2, y_rot[ii] + D[ii] / 2], "k", ) for ii in range(n_turbs): ax.text(x_rot[ii], y_rot[ii], "T%03d" % ii) ax.axis("equal") srt = np.argsort(x_rot) x_rot_srt = x_rot[srt] y_rot_srt = y_rot[srt] for ii in range(n_turbs): x0 = x_rot_srt[ii] y0 = y_rot_srt[ii] def wake_profile_ub_turbii(x): y = (y0 + D[ii]) + (x - x0) * wake_slope if isinstance(y, (float, np.float64, np.float32)): if x < (x0 + 0.01): y = -np.inf else: y[x < x0 + 0.01] = -np.inf return y def wake_profile_lb_turbii(x): y = (y0 - D[ii]) - (x - x0) * wake_slope if isinstance(y, (float, np.float64, np.float32)): if x < (x0 + 0.01): y = -np.inf else: y[x < x0 + 0.01] = -np.inf return y def determine_if_in_wake(xt, yt): return (yt < wake_profile_ub_turbii(xt)) & (yt > wake_profile_lb_turbii(xt)) is_downstream[ii] = not any( determine_if_in_wake(x_rot_srt[iii], y_rot_srt[iii]) for iii in range(n_turbs) ) if plot_lines: x1 = np.max(x_rot_srt) + 500.0 ax.fill_between( [x0, x1, x1, x0], [ wake_profile_ub_turbii(x0 + 0.02), wake_profile_ub_turbii(x1), wake_profile_lb_turbii(x1), wake_profile_lb_turbii(x0 + 0.02), ], alpha=0.1, color="k", edgecolor=None, ) usrt = np.argsort(srt) is_downstream = [is_downstream[i] for i in usrt] turbs_downstream = list(np.where(is_downstream)[0]) if plot_lines: ax.set_title("wind_direction = %03d" % wind_direction) ax.set_xlim([np.min(x_rot) - 500.0, x1]) ax.set_ylim([np.min(y_rot) - 500.0, np.max(y_rot) + 500.0]) ax.plot( x_rot[turbs_downstream], y_rot[turbs_downstream], "o", color="green", ) return turbs_downstream ================================================ FILE: floris/optimization/yaw_optimization/yaw_optimizer_geometric.py ================================================ import numpy as np from floris.core.turbine.operation_models import POWER_SETPOINT_DISABLED from floris.utilities import rotate_coordinates_rel_west from .yaw_optimization_base import YawOptimization class YawOptimizationGeometric(YawOptimization): """ YawOptimizationGeometric is a subclass of :py:class:`floris.optimization.general_library.YawOptimization` that is used to provide a rough estimate of optimal yaw angles based purely on the wind farm geometry. Main use case is for coupled layout and yaw optimization. See Stanley et al. (2023) for details: https://wes.copernicus.org/articles/8/1341/2023/ """ def __init__( self, fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=25.0, ): """ Instantiate YawOptimizationGeometric object with a FlorisModel object assign parameter values. """ super().__init__( fmodel=fmodel, minimum_yaw_angle=minimum_yaw_angle, maximum_yaw_angle=maximum_yaw_angle, calc_baseline_power=False ) def optimize(self): """ Find rough yaw angles based on wind farm geometry. Assumes all wind turbines have the same rotor diameter. Returns: opt_yaw_angles (np.array): Optimal yaw angles in degrees. This array is equal in length to the number of turbines in the farm. """ # Loop through every WD individually. WS ignored! wd_array = self.fmodel_subset.core.flow_field.wind_directions active_turbines = self.fmodel_subset.core.farm.power_setpoints > POWER_SETPOINT_DISABLED for nwdi, wd in enumerate(wd_array): self._yaw_angles_opt_subset[nwdi, active_turbines[nwdi]] = geometric_yaw( self.fmodel_subset.layout_x[active_turbines[nwdi]], self.fmodel_subset.layout_y[active_turbines[nwdi]], wd, self.fmodel.core.farm.turbine_definitions[0]["rotor_diameter"], top_left_yaw_upper=self.maximum_yaw_angle[0, 0], bottom_left_yaw_upper=self.maximum_yaw_angle[0, 0], top_left_yaw_lower=self.minimum_yaw_angle[0, 0], bottom_left_yaw_lower=self.minimum_yaw_angle[0, 0], ) # Finalize optimization, i.e., retrieve full solutions df_opt = self._finalize() # Otherwise, df_opt will just copy the farm_power_baseline in return df_opt def geometric_yaw( turbine_x, turbine_y, wind_direction, rotor_diameter, left_x=0.0, top_left_y=1.0, right_x=25.0, top_right_y=1.0, top_left_yaw_upper=30.0, top_right_yaw_upper=0.0, bottom_left_yaw_upper=30.0, bottom_right_yaw_upper=0.0, top_left_yaw_lower=-30.0, top_right_yaw_lower=0.0, bottom_left_yaw_lower=-30.0, bottom_right_yaw_lower=0.0, ): """ Main function to compute geometric yaw angles based on wind farm layout for a single wind direction. Arguments: turbine_x: unrotated x turbine coords turbine_y: unrotated y turbine coords wind_direction: float, degrees rotor_diameter: float left_x: where we start the trapezoid. Should be left as 0. top_left_y: trapezoid top left coord right_x: where to stop the trapezoid downstream. Max coord after which the upstream turbine won't yaw. top_right_y: trapezoid top right coord top_left_yaw_upper: yaw angle associated with top left point (upper trapezoid) top_right_yaw_upper: yaw angle associated with top right point bottom_left_yaw_upper: yaw angle associated with bottom left point bottom_right_yaw_upper: yaw angle associated with bottom right point top_left_yaw_lower: yaw angle associated with top left point (lower trapezoid) top_right_yaw_lower: yaw angle associated with top right point bottom_left_yaw_lower: yaw angle associated with bottom left point bottom_right_yaw_lower: yaw angle associated with bottom right point Returns: yaw_array: yaw angles for all turbines for the present wind direction. """ nturbs = len(turbine_x) turbine_coordinates_array = np.zeros((nturbs,3)) turbine_coordinates_array[:,0] = turbine_x[:] turbine_coordinates_array[:,1] = turbine_y[:] rotated_x, rotated_y, _, _, _ = rotate_coordinates_rel_west( np.array([wind_direction]), turbine_coordinates_array ) processed_x, processed_y = _process_layout(rotated_x[0], rotated_y[0], rotor_diameter) yaw_array = np.zeros(nturbs) for i in range(nturbs): # TODO: fix shape of top left yaw etc? yaw_array[i] = _get_yaw_angles( processed_x[i], processed_y[i], left_x, top_left_y, right_x, top_right_y, top_left_yaw_upper, top_right_yaw_upper, bottom_left_yaw_upper, bottom_right_yaw_upper, top_left_yaw_lower, top_right_yaw_lower, bottom_left_yaw_lower, bottom_right_yaw_lower, ) return yaw_array def _process_layout( turbine_x, turbine_y, rotor_diameter, spread=0.1 ): """ Returns the distance from each turbine to the nearest downstream waked turbine normalized by the rotor diameter. Right now "waked" is determined by a Jensen-like wake spread, but this could/should be modified to be the same as the trapezoid rule used to determine the yaw angles. Arguments: turbine_x: turbine x coords (rotated) turbine_y: turbine y coords (rotated) rotor_diameter: turbine rotor diameter (float) spread=0.1: Jensen alpha wake spread value Returns: dx: distance to nearest downstream turbine in rotor diameters for all turbines dy: lateral distance to nearest downstream turbine in rotor diameters for all turbines """ # Compute distances x_dists = turbine_x.reshape(-1,1).T - turbine_x.reshape(-1,1) y_dists = turbine_y.reshape(-1,1).T - turbine_y.reshape(-1,1) # Any turbines upstream or at the turbine location are ineligible x_dists[x_dists <= 0.] = np.inf # Check within Jensen model spread in_Jensen_wake = (abs(y_dists) < spread * x_dists + rotor_diameter/2.0) x_dists[~in_Jensen_wake] = np.inf # Get minimums (and arguments to select the correct y values also) dx = x_dists.min(axis=1) dy = y_dists[range(len(turbine_x)), x_dists.argmin(axis=1)] return dx/rotor_diameter, dy/rotor_diameter def _get_yaw_angles( x, y, left_x, top_left_y, right_x, top_right_y, top_left_yaw_upper, top_right_yaw_upper, bottom_left_yaw_upper, bottom_right_yaw_upper, top_left_yaw_lower, top_right_yaw_lower, bottom_left_yaw_lower, bottom_right_yaw_lower ): """ For a given turbine, return the geometric yaw angle based on its position relative to the nearest downstream turbine. Arguments: x: downstream distance to the nearest downstream turbine in rotor diameters with turbines rotated so wind is coming left to right y: lateral distance to the nearest downstream turbine in rotor diameters with turbines rotated so wind is coming left to right left_x: where we start the trapezoid. Should be left as 0. top_left_y: trapezoid top left coord right_x: where to stop the trapezoid downstream. Max coord after which the upstream turbine won't yaw. top_right_y: trapezoid top right coord top_left_yaw_upper: yaw angle associated with top left point (upper trapezoid) top_right_yaw_upper: yaw angle associated with top right point bottom_left_yaw_upper: yaw angle associated with bottom left point bottom_right_yaw_upper: yaw angle associated with bottom right point top_left_yaw_lower: yaw angle associated with top left point (lower trapezoid) top_right_yaw_lower: yaw angle associated with top right point bottom_left_yaw_lower: yaw angle associated with bottom left point bottom_right_yaw_lower: yaw angle associated with bottom right point Returns: (yaw angle): float, geometric yaw angle for the given turbine """ dx = (x-left_x)/(right_x-left_x) if dx >= 1.0: return 0.0 edge_y = top_left_y + (top_right_y-top_left_y)*dx if abs(y) > edge_y: return 0.0 elif y >= -0.01: # Tolerance to handle numerical issues top_yaw = top_left_yaw_upper + (top_right_yaw_upper-top_left_yaw_upper)*dx bottom_yaw = bottom_left_yaw_upper + (bottom_right_yaw_upper-bottom_left_yaw_upper)*dx return bottom_yaw + (top_yaw-bottom_yaw)*abs(y)/edge_y elif y < -0.01: top_yaw = top_left_yaw_lower + (top_right_yaw_lower-top_left_yaw_lower)*dx bottom_yaw = bottom_left_yaw_lower + (bottom_right_yaw_lower-bottom_left_yaw_lower)*dx return bottom_yaw + (top_yaw-bottom_yaw)*abs(y)/edge_y ================================================ FILE: floris/optimization/yaw_optimization/yaw_optimizer_scipy.py ================================================ import numpy as np from scipy.optimize import minimize from .yaw_optimization_base import YawOptimization class YawOptimizationScipy(YawOptimization): """ YawOptimizationScipy is a subclass of :py:class:`floris.optimization.general_library.YawOptimization` that is used to optimize the yaw angles of all turbines in a Floris Farm for a single set of inflow conditions using the SciPy optimize package. """ def __init__( self, fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=25.0, yaw_angles_baseline=None, x0=None, opt_method="SLSQP", opt_options=None, turbine_weights=None, exclude_downstream_turbines=True, verify_convergence=False, ): """ Instantiate YawOptimizationScipy object with a FlorisModel object and assign parameter values. """ valid_op_models = ["cosine-loss"] if fmodel.get_operation_model() not in valid_op_models: raise ValueError( "YawOptimizationScipy is currently limited to the following operation models: " + ", ".join(valid_op_models) ) if opt_options is None: # Default SciPy parameters opt_options = { "maxiter": 100, "disp": True, "iprint": 2, "ftol": 1e-12, "eps": 0.1, } super().__init__( fmodel=fmodel, minimum_yaw_angle=minimum_yaw_angle, maximum_yaw_angle=maximum_yaw_angle, yaw_angles_baseline=yaw_angles_baseline, x0=x0, turbine_weights=turbine_weights, normalize_control_variables=True, calc_baseline_power=True, exclude_downstream_turbines=exclude_downstream_turbines, verify_convergence=verify_convergence, ) self.opt_method = opt_method self.opt_options = opt_options def optimize(self): """ Find optimum setting of turbine yaw angles for a single turbine cluster that maximizes the weighted wind farm power production given fixed atmospheric conditions (wind speed, direction, etc.) using the scipy.optimize.minimize function. Returns: opt_yaw_angles (np.array): Optimal yaw angles in degrees. This array is equal in length to the number of turbines in the farm. """ # Loop through every wind condition individually wd_array = self.fmodel_subset.core.flow_field.wind_directions ws_array = self.fmodel_subset.core.flow_field.wind_speeds ti_array = self.fmodel_subset.core.flow_field.turbulence_intensities for i, (wd, ws, ti) in enumerate(zip(wd_array, ws_array, ti_array)): self.fmodel_subset.set( wind_directions=[wd], wind_speeds=[ws], turbulence_intensities=[ti] ) # Find turbines to optimize turbs_to_opt = self._turbs_to_opt_subset[i, :] if not any(turbs_to_opt): continue # Nothing to do here: no turbines to optimize # Extract current optimization problem variables (normalized) yaw_lb = self._minimum_yaw_angle_subset_norm[i, turbs_to_opt] yaw_ub = self._maximum_yaw_angle_subset_norm[i, turbs_to_opt] bnds = [(a, b) for a, b in zip(yaw_lb, yaw_ub)] x0 = self._x0_subset_norm[i, turbs_to_opt] J0 = self._farm_power_baseline_subset[i] yaw_template = self._yaw_angles_template_subset[i, :] turbine_weights = self._turbine_weights_subset[i, :] yaw_template = np.tile(yaw_template, (1, 1)) turbine_weights = np.tile(turbine_weights, (1, 1)) # Handle heterogeneous inflow, if there is one if (hasattr(self.fmodel.core.flow_field, 'heterogeneous_inflow_config') and self.fmodel.core.flow_field.heterogeneous_inflow_config is not None): het_sm_orig = np.array( self.fmodel.core.flow_field.heterogeneous_inflow_config['speed_multipliers'] ) het_sm = het_sm_orig[i, :].reshape(1, -1) else: het_sm = None # Define cost function def cost(x): x_full = np.array(yaw_template, copy=True) x_full[0, turbs_to_opt] = x * self._normalization_length return ( - 1.0 * self._calculate_farm_power( yaw_angles=x_full, wd_array=[wd], ws_array=[ws], ti_array=[ti], turbine_weights=turbine_weights, heterogeneous_speed_multipliers=het_sm )[0] / J0 ) # Perform optimization residual_plant = minimize( fun=cost, x0=x0, bounds=bnds, method=self.opt_method, options=self.opt_options, ) # Undo normalization/masks and save results to self self._farm_power_opt_subset[i] = -residual_plant.fun * J0 self._yaw_angles_opt_subset[i, turbs_to_opt] = ( residual_plant.x * self._normalization_length ) # Finalize optimization, i.e., retrieve full solutions df_opt = self._finalize() return df_opt ================================================ FILE: floris/optimization/yaw_optimization/yaw_optimizer_sr.py ================================================ import copy import warnings from time import perf_counter as timerpc import numpy as np import pandas as pd from floris.logging_manager import LoggingManager # from .yaw_optimizer_scipy import YawOptimizationScipy from .yaw_optimization_base import YawOptimization class YawOptimizationSR(YawOptimization, LoggingManager): """ Optimize yaw angles using the Serial Refine (SR) optimization method. See :cite:`fleming_sr_2022` for full details on the SR method. """ def __init__( self, fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=25.0, yaw_angles_baseline=None, x0=None, Ny_passes=[5, 4], # Optimization options turbine_weights=None, exclude_downstream_turbines=True, verify_convergence=False, ): """ Instantiate YawOptimizationSR object with a FlorisModel object and assign parameter values. Args: fmodel: An instantiated FlorisModel object. minimum_yaw_angle: Minimum yaw angle for all turbines [degrees]. Default is 0.0. maximum_yaw_angle: Maximum yaw angle for all turbines [degrees]. Default is 25.0. yaw_angles_baseline: Yaw angles to use as a baseline for comparison to optimized yaw angles [degrees]. If None, defaults to 0.0 for all turbines. x0: Not used in this optimizer. Included for compatibility with base class. Defaults to None. Ny_passes: List of integers defining the number of yaw angles to evaluate per turbine in each pass of the SR algorithm. The length of the list defines the number of passes. The first entry can be even or odd, but all further entries must be even. Default is [5, 4]. turbine_weights: Weights for each turbine when calculating the weighted power output during optimization. If None, all turbines are weighted equally. Default is None. exclude_downstream_turbines: Not used in this optimizer. Included for compatibility with base class. Default is True. verify_convergence: If True, the optimizer will perform additional checks to verify that the optimal yaw angles have been found. See YawOptimization._verify_solutions_for_convergence() for more details. """ # Warn if non-default values are provided for unused inputs if x0 is not None: warnings.warn( "The 'x0' argument is not used in the Serial Refine optimization method " "and will be ignored.", UserWarning ) # Initialize base class super().__init__( fmodel=fmodel, minimum_yaw_angle=minimum_yaw_angle, maximum_yaw_angle=maximum_yaw_angle, yaw_angles_baseline=yaw_angles_baseline, x0=x0, turbine_weights=turbine_weights, calc_baseline_power=True, exclude_downstream_turbines=exclude_downstream_turbines, verify_convergence=verify_convergence, ) # Start a timer for FLORIS computations self.time_spent_in_floris = 0 # Confirm that Ny_passes are integers and odd/even for Nii, Ny in enumerate(Ny_passes): if not isinstance(Ny, int): raise ValueError("Ny_passes must contain exclusively integers") if Ny < 2: raise ValueError("Each entry in Ny_passes must have a value of at least 2.") if (Nii > 0) & ((Ny + 1) % 2 == 0): raise ValueError( "The second and further entries of Ny_passes must be even numbers. " "This is to ensure the same yaw angles are not evaluated twice between passes." ) # Save optimization choices to self self.Ny_passes = Ny_passes # For each wind direction, determine the order of turbines self._get_turbine_orders() def _get_turbine_orders(self): layout_x = self.fmodel.layout_x layout_y = self.fmodel.layout_y turbines_ordered_array = [] for wd in self.fmodel_subset.core.flow_field.wind_directions: layout_x_rot = ( np.cos((wd - 270.0) * np.pi / 180.0) * layout_x - np.sin((wd - 270.0) * np.pi / 180.0) * layout_y ) turbines_ordered = np.argsort(layout_x_rot) turbines_ordered_array.append(turbines_ordered) self.turbines_ordered_array_subset = np.vstack(turbines_ordered_array) def _calc_powers_with_memory(self, yaw_angles_subset, use_memory=True): # Define current optimal solutions and floris wind directions locally yaw_angles_opt_subset = self._yaw_angles_opt_subset farm_power_opt_subset = self._farm_power_opt_subset wd_array_subset = self.fmodel_subset.core.flow_field.wind_directions ws_array_subset = self.fmodel_subset.core.flow_field.wind_speeds ti_array_subset = self.fmodel_subset.core.flow_field.turbulence_intensities power_setpoints_subset = self.fmodel_subset.core.farm.power_setpoints turbine_weights_subset = self._turbine_weights_subset # Reformat yaw_angles_subset, if necessary eval_multiple_passes = (len(np.shape(yaw_angles_subset)) == 3) if eval_multiple_passes: # Four-dimensional; format everything into three-dimensional Ny = yaw_angles_subset.shape[0] # Number of passes yaw_angles_subset = np.vstack( [yaw_angles_subset[iii, :, :] for iii in range(Ny)] ) yaw_angles_opt_subset = np.tile(yaw_angles_opt_subset, (Ny, 1)) farm_power_opt_subset = np.tile(farm_power_opt_subset, (Ny)) wd_array_subset = np.tile(wd_array_subset, Ny) ws_array_subset = np.tile(ws_array_subset, Ny) ti_array_subset = np.tile(ti_array_subset, Ny) power_setpoints_subset = np.tile(power_setpoints_subset, (Ny, 1)) turbine_weights_subset = np.tile(turbine_weights_subset, (Ny, 1)) # Initialize empty matrix for floris farm power outputs farm_powers = np.zeros((yaw_angles_subset.shape[0])) # Find indices of yaw angles that we previously already evaluated, and # prevent redoing the same calculations if use_memory: idx = (np.abs(yaw_angles_opt_subset - yaw_angles_subset) < 0.01).all(axis=1) farm_powers[idx] = farm_power_opt_subset[idx] if self.print_progress: self.logger.info( "Skipping {:d}/{:d} calculations: already in memory.".format( np.sum(idx), len(idx)) ) else: idx = np.zeros(yaw_angles_subset.shape[0], dtype=bool) if not np.all(idx): # Now calculate farm powers for conditions we haven't yet evaluated previously start_time = timerpc() if (hasattr(self.fmodel.core.flow_field, 'heterogeneous_inflow_config') and self.fmodel.core.flow_field.heterogeneous_inflow_config is not None): het_sm_orig = np.array( self.fmodel.core.flow_field.heterogeneous_inflow_config['speed_multipliers'] ) het_sm = np.tile(het_sm_orig, (Ny, 1))[~idx, :] else: het_sm = None farm_powers[~idx] = self._calculate_farm_power( wd_array=wd_array_subset[~idx], ws_array=ws_array_subset[~idx], ti_array=ti_array_subset[~idx], turbine_weights=turbine_weights_subset[~idx, :], yaw_angles=yaw_angles_subset[~idx, :], heterogeneous_speed_multipliers=het_sm, power_setpoints=power_setpoints_subset[~idx, :], ) self.time_spent_in_floris += (timerpc() - start_time) # Finally format solutions back to original format, if necessary if eval_multiple_passes: farm_powers = np.reshape( farm_powers, ( Ny, self.fmodel_subset.core.flow_field.n_findex ) ) return farm_powers def _generate_evaluation_grid(self, pass_depth, turbine_depth): """ Calculate the yaw angles for every iteration in the SR algorithm, for turbine, for every wind direction, for every wind speed, for every TI. Basically, this should yield a grid of yaw angle sets to evaluate the wind farm AEP with 'Ny' times. Then, for each ambient condition set, """ # Initialize yaw angles to evaluate, 'Ny' times the wind rose Ny = self.Ny_passes[pass_depth] evaluation_grid = np.tile(self._yaw_angles_opt_subset, (Ny, 1, 1)) # Get a list of the turbines in order of x and sort front to back for iw in range(self._n_findex_subset): turbid = self.turbines_ordered_array_subset[iw, turbine_depth] # Turbine to manipulate # Grab yaw bounds from self yaw_lb = self._yaw_lbs[iw, turbid] yaw_ub = self._yaw_ubs[iw, turbid] # Saturate to allowable yaw limits yaw_lb = np.clip( yaw_lb, self.minimum_yaw_angle[iw, turbid], self.maximum_yaw_angle[iw, turbid] ) yaw_ub = np.clip( yaw_ub, self.minimum_yaw_angle[iw, turbid], self.maximum_yaw_angle[iw, turbid] ) if pass_depth == 0: # Evaluate all possible coordinates yaw_angles_subset = np.linspace(yaw_lb, yaw_ub, Ny) else: # Remove middle point: was evaluated in previous iteration c = int(Ny / 2) # Central point (to remove) ids = [*list(range(0, c)), *list(range(c + 1, Ny + 1))] yaw_angles_subset = np.linspace(yaw_lb, yaw_ub, Ny + 1)[ids] evaluation_grid[:, iw, turbid] = yaw_angles_subset self._yaw_evaluation_grid = evaluation_grid return evaluation_grid def _process_evaluation_grid(self): # Evaluate the farm AEPs for the grid of possible yaw angles evaluation_grid = self._yaw_evaluation_grid farm_powers = self._calc_powers_with_memory(evaluation_grid) return farm_powers def optimize(self, print_progress=True): """ Find the yaw angles that maximize the power production for every wind direction, wind speed and turbulence intensity using the SR optimization algorithm. """ self.print_progress = print_progress # For each pass, from front to back ii = 0 for Nii in range(len(self.Ny_passes)): # Disturb yaw angles for one turbine at a time, from front to back for turbine_depth in range(self.nturbs): p = 100.0 * ii / (len(self.Ny_passes) * self.nturbs) ii += 1 if self.print_progress: print( f"[Serial Refine] Processing pass={Nii}, " f"turbine_depth={turbine_depth} ({p:.1f}%)" ) # Create grid to evaluate yaw angles for one turbine == turbine_depth evaluation_grid = self._generate_evaluation_grid( pass_depth=Nii, turbine_depth=turbine_depth ) # Evaluate grid of yaw angles, get farm powers and find optimal solutions farm_powers = self._process_evaluation_grid() # If farm powers contains any nans, then issue a warning if np.any(np.isnan(farm_powers)): err_msg = ( "NaNs found in farm powers during SerialRefine " "optimization routine. Proceeding to maximize over yaw " "settings that produce valid powers." ) self.logger.warning(err_msg, stack_info=True) # Find optimal solutions in new evaluation grid args_opt = np.expand_dims(np.nanargmax(farm_powers, axis=0), axis=0) farm_powers_opt_new = np.squeeze( np.take_along_axis(farm_powers, args_opt, axis=0), axis=0, ) yaw_angles_opt_new = np.squeeze( np.take_along_axis( evaluation_grid, np.expand_dims(args_opt, axis=2), axis=0 ), axis=0 ) farm_powers_opt_prev = self._farm_power_opt_subset yaw_angles_opt_prev = self._yaw_angles_opt_subset # Now update optimal farm powers if better than previous ids_better = (farm_powers_opt_new > farm_powers_opt_prev) farm_power_opt = farm_powers_opt_prev farm_power_opt[ids_better] = farm_powers_opt_new[ids_better] # Now update optimal yaw angles if better than previous turbs_sorted = self.turbines_ordered_array_subset turbids = turbs_sorted[np.where(ids_better)[0], turbine_depth] ids = (*np.where(ids_better), turbids) yaw_angles_opt = yaw_angles_opt_prev yaw_angles_opt[ids] = yaw_angles_opt_new[ids] # Update bounds for next iteration to close proximity of optimal solution dx = ( evaluation_grid[1, :, :] - evaluation_grid[0, :, :] )[ids] self._yaw_lbs[ids] = np.clip( yaw_angles_opt[ids] - 0.50 * dx, self._minimum_yaw_angle_subset[ids], self._maximum_yaw_angle_subset[ids] ) self._yaw_ubs[ids] = np.clip( yaw_angles_opt[ids] + 0.50 * dx, self._minimum_yaw_angle_subset[ids], self._maximum_yaw_angle_subset[ids] ) # Save results to self self._farm_power_opt_subset = farm_power_opt self._yaw_angles_opt_subset = yaw_angles_opt # Finalize optimization, i.e., retrieve full solutions df_opt = self._finalize() return df_opt ================================================ FILE: floris/par_floris_model.py ================================================ import copy from pathlib import Path from time import perf_counter as timerpc import numpy as np from floris.core import State from floris.floris_model import FlorisModel from floris.type_dec import ( NDArrayFloat, ) from floris.utilities import is_all_scalar_dict class ParFlorisModel(FlorisModel): """ This class mimics the FlorisModel, but enables parallelization of the main computational effort. """ def __init__( self, configuration: dict | str | Path | FlorisModel, interface: str | None = "multiprocessing", max_workers: int = -1, n_wind_condition_splits: int = -1, return_turbine_powers_only: bool = False, print_timings: bool = False ): """ Initialize the ParFlorisModel object. Args: configuration (:py:obj:`dict`): The Floris configuration dictionary or YAML file. See floris.default_inputs.yaml for an example of the configuration dictionary or visit https://nrel.github.io/floris/input_reference_main.html. interface: The parallelization interface to use. Options are "multiprocessing", "pathos", and "concurrent", with possible future support for "mpi4py" max_workers: The maximum number of workers to use. Defaults to -1, which then takes the number of CPUs available. n_wind_condition_splits: The number of wind conditions to split the simulation over. Defaults to the same as max_workers. return_turbine_powers_only: Whether to return only the turbine powers. print_timings (bool): Print the computation time to the console. Defaults to False. """ # Instantiate the underlying FlorisModel if isinstance(configuration, FlorisModel): configuration_dict = configuration.core.as_dict() super().__init__(configuration_dict) # Copy over any control setpoints, wind data, if not already done. self.set( yaw_angles=configuration.core.farm.yaw_angles, power_setpoints=configuration.core.farm.power_setpoints, awc_modes=configuration.core.farm.awc_modes, awc_amplitudes=configuration.core.farm.awc_amplitudes, awc_frequencies=configuration.core.farm.awc_frequencies, wind_data=configuration.wind_data, ) else: super().__init__(configuration) # Save parallelization parameters if interface == "multiprocessing": import multiprocessing as mp self._PoolExecutor = mp.Pool if max_workers == -1: max_workers = mp.cpu_count() # TODO: test spinning up the worker pool at this point elif interface == "pathos": import pathos if max_workers == -1: max_workers = pathos.helpers.cpu_count() self.pathos_pool = pathos.pools.ProcessPool(nodes=max_workers) elif interface == "concurrent": from concurrent.futures import ProcessPoolExecutor if max_workers == -1: from multiprocessing import cpu_count max_workers = cpu_count() self._PoolExecutor = ProcessPoolExecutor elif interface in ["mpi4py"]: raise NotImplementedError( f"Parallelization interface {interface} not yet supported." ) elif interface is None: self.logger.warning( "No parallelization interface specified. Running in serial mode." ) if return_turbine_powers_only: self.logger.warn( "return_turbine_powers_only is not supported in serial mode." ) else: raise ValueError( f"Invalid parallelization interface {interface}. " "Options are 'multiprocessing', 'pathos', or 'concurrent'." ) self._interface = interface self.max_workers = max_workers if n_wind_condition_splits == -1: self.n_wind_condition_splits = max_workers else: self.n_wind_condition_splits = n_wind_condition_splits self.return_turbine_powers_only = return_turbine_powers_only self.print_timings = print_timings def run(self) -> None: """ Run the FLORIS model in parallel. """ if self.return_turbine_powers_only: # TODO: code here that does not return flow fields # Somehow, overload methods on FlorisModel that need flow field # data. # This version will call super().get_turbine_powers() on each of # the splits, and return them somehow. self._stored_turbine_powers = None # Temporary if self.interface is None: t0 = timerpc() super().run() t1 = timerpc() self._print_timings(t0, t1, None, None) else: t0 = timerpc() self.core.initialize_domain() parallel_run_inputs = self._preprocessing() t1 = timerpc() if self.interface == "multiprocessing": if self.return_turbine_powers_only: with self._PoolExecutor(self.max_workers) as p: self._turbine_powers_split = p.starmap( _parallel_run_powers_only, parallel_run_inputs ) else: with self._PoolExecutor(self.max_workers) as p: self._fmodels_split = p.starmap(_parallel_run, parallel_run_inputs) elif self.interface == "pathos": if self.return_turbine_powers_only: self._turbine_powers_split = self.pathos_pool.map( _parallel_run_powers_only_map, parallel_run_inputs ) else: self._fmodels_split = self.pathos_pool.map( _parallel_run_map, parallel_run_inputs ) elif self.interface == "concurrent": if self.return_turbine_powers_only: with self._PoolExecutor(self.max_workers) as p: self._turbine_powers_split = p.map( _parallel_run_powers_only_map, parallel_run_inputs ) self._turbine_powers_split = list(self._turbine_powers_split) else: with self._PoolExecutor(self.max_workers) as p: self._fmodels_split = p.map( _parallel_run_map, parallel_run_inputs ) self._fmodels_split = list(self._fmodels_split) t2 = timerpc() self._postprocessing() self.core.farm.finalize(self.core.grid.unsorted_indices) self.core.state = State.USED t3 = timerpc() self._print_timings(t0, t1, t2, t3) def sample_flow_at_points(self, x: NDArrayFloat, y: NDArrayFloat, z: NDArrayFloat): """ Sample the flow field velocity at specified points. Args: x: The x-coordinates of the points. y: The y-coordinates of the points. z: The z-coordinates of the points. Returns: NDArrayFloat: The wind speeds at the specified points. """ return self._sample_value_at_points(x, y, z, value="velocity") def sample_ti_at_points(self, x: NDArrayFloat, y: NDArrayFloat, z: NDArrayFloat): """ Sample the turbulence intensity at specified points. Args: x: The x-coordinates of the points. y: The y-coordinates of the points. z: The z-coordinates of the points. Returns: NDArrayFloat: The turbulence intensity at the specified points. """ return self._sample_value_at_points(x, y, z, value="turbulence_intensity") def _sample_value_at_points( self, x: NDArrayFloat, y: NDArrayFloat, z: NDArrayFloat, value: str ): """ Underlying method for sample values at (x,y,z) points for ParFlorisModel. Args: x: The x-coordinates of the points. y: The y-coordinates of the points. z: The z-coordinates of the points. value: The type of value to sample ("velocity" or "turbulence_intensity"). Returns: NDArrayFloat: The values at the specified points. """ if self.return_turbine_powers_only: raise NotImplementedError( "Sampling at points is not supported when " "return_turbine_powers_only is set to True on ParFlorisModel." ) valid_values = ["velocity", "turbulence_intensity"] if value not in valid_values: raise ValueError( f"Invalid value '{value}' for sampling. Must be one of {valid_values}." ) if self.interface is None: t0 = timerpc() if value == "velocity": output = super().sample_flow_at_points(x, y, z) elif value == "turbulence_intensity": output = super().sample_ti_at_points(x, y, z) t1 = timerpc() self._print_timings(t0, t1, None, None) else: t0 = timerpc() self.core.initialize_domain() parallel_run_inputs = self._preprocessing() parallel_sample_flow_at_points_inputs = [ (fmodel_dict, set_args, x, y, z) for fmodel_dict, set_args in parallel_run_inputs ] t1 = timerpc() if value == "velocity": _parallel_func = _parallel_sample_flow_at_points _parallel_fmap = _parallel_sample_flow_at_points_map elif value == "turbulence_intensity": _parallel_func = _parallel_sample_ti_at_points _parallel_fmap = _parallel_sample_ti_at_points_map if self.interface == "multiprocessing": with self._PoolExecutor(self.max_workers) as p: sampled_wind_speeds_p = p.starmap( _parallel_func, parallel_sample_flow_at_points_inputs ) elif self.interface == "pathos": sampled_wind_speeds_p = self.pathos_pool.map( _parallel_fmap, parallel_sample_flow_at_points_inputs ) elif self.interface == "concurrent": with self._PoolExecutor(self.max_workers) as p: sampled_wind_speeds_p = p.map( _parallel_fmap, parallel_sample_flow_at_points_inputs ) sampled_wind_speeds_p = list(sampled_wind_speeds_p) t2 = timerpc() output = np.concatenate(sampled_wind_speeds_p, axis=0) t3 = timerpc() self._print_timings(t0, t1, t2, t3) return output def _preprocessing(self): """ Prepare the input arguments for parallel execution. """ # Split over the wind conditions n_wind_condition_splits = self.n_wind_condition_splits n_wind_condition_splits = np.min( [n_wind_condition_splits, self.core.flow_field.n_findex] ) # Prepare the input arguments for parallel execution fmodel_dict = self.core.as_dict() wind_condition_id_splits = np.array_split( np.arange(self.core.flow_field.n_findex), n_wind_condition_splits, ) multiargs = [] for wc_id_split in wind_condition_id_splits: # for ws_id_split in wind_speed_id_splits: fmodel_dict_split = copy.deepcopy(fmodel_dict) # Extract and format all arguments that are per-findex # (scalar arguments are already broadcast on fmodel_dict_split) set_args_subset = { "wind_directions": self.core.flow_field.wind_directions[wc_id_split], "wind_speeds": self.core.flow_field.wind_speeds[wc_id_split], "turbulence_intensities": self.core.flow_field.turbulence_intensities[wc_id_split], "yaw_angles": self.core.farm.yaw_angles[wc_id_split, :], "power_setpoints": self.core.farm.power_setpoints[wc_id_split, :], "awc_modes": self.core.farm.awc_modes[wc_id_split, :], "awc_amplitudes": self.core.farm.awc_amplitudes[wc_id_split, :], "awc_frequencies": self.core.farm.awc_frequencies[wc_id_split, :], # disable_turbines is not saved on core, but passed through power_setpoints } # Handle heterogeneous_inflow_config if self.core.flow_field.heterogeneous_inflow_config is not None: heterogeneous_inflow_config_subset = { "x": self.core.flow_field.heterogeneous_inflow_config["x"], "y": self.core.flow_field.heterogeneous_inflow_config["y"], "speed_multipliers": self.core.flow_field.heterogeneous_inflow_config\ ["speed_multipliers"][wc_id_split, :] } if "z" in self.core.flow_field.heterogeneous_inflow_config.keys(): heterogeneous_inflow_config_subset["z"] = \ self.core.flow_field.heterogeneous_inflow_config["z"] set_args_subset["heterogeneous_inflow_config"] = heterogeneous_inflow_config_subset # Handle multidim_conditions if self.core.flow_field.multidim_conditions is not None: if is_all_scalar_dict(self.core.flow_field.multidim_conditions): multidim_conditions_subset = self.core.flow_field.multidim_conditions else: multidim_conditions_subset = { k: v[wc_id_split] for k, v in self.core.flow_field.multidim_conditions.items() } set_args_subset["multidim_conditions"] = multidim_conditions_subset # Prepare lightweight data to pass along multiargs.append((fmodel_dict_split, set_args_subset)) return multiargs def _postprocessing(self): # Append the remaining flow_fields # Could consider adding a merge method to the FlowField class # to make this easier if self.return_turbine_powers_only: self._stored_turbine_powers = np.vstack(self._turbine_powers_split) else: # Ensure fields to set have correct dimensions self.core.flow_field.u = self._fmodels_split[0].core.flow_field.u self.core.flow_field.v = self._fmodels_split[0].core.flow_field.v self.core.flow_field.w = self._fmodels_split[0].core.flow_field.w self.core.flow_field.turbulence_intensity_field = \ self._fmodels_split[0].core.flow_field.turbulence_intensity_field for fm in self._fmodels_split[1:]: self.core.flow_field.u = np.append( self.core.flow_field.u, fm.core.flow_field.u, axis=0 ) self.core.flow_field.v = np.append( self.core.flow_field.v, fm.core.flow_field.v, axis=0 ) self.core.flow_field.w = np.append( self.core.flow_field.w, fm.core.flow_field.w, axis=0 ) self.core.flow_field.turbulence_intensity_field = np.append( self.core.flow_field.turbulence_intensity_field, fm.core.flow_field.turbulence_intensity_field, axis=0 ) def _print_timings(self, t0, t1, t2, t3): """ Print the timings for the parallel execution. """ if self.print_timings: print("===============================================================================") if self.interface is None: print(f"Total time spent for serial calculation (interface=None): {t1 - t0:.3f} s") else: print( "Total time spent for parallel calculation " f"({self.max_workers} workers): {t3-t0:.3f} s" ) print(f" Time spent in parallel preprocessing: {t1-t0:.3f} s") print(f" Time spent in parallel loop execution: {t2-t1:.3f} s.") print(f" Time spent in parallel postprocessing: {t3-t2:.3f} s") def _get_turbine_powers(self): """ Calculates the power at each turbine in the wind farm. This override will only be necessary if we want to be able to use the return_turbine_powers_only option. Need to check if that makes a significant speed difference. Returns: NDArrayFloat: Powers at each turbine. """ if self.core.state is not State.USED: self.logger.warning( f"Please call `{self.__class__.__name__}.run` before computing" " turbine powers. In future versions, an explicit run() call will" "be required." ) self.run() if self.return_turbine_powers_only: return self._stored_turbine_powers else: return super()._get_turbine_powers() @property def secondary_init_kwargs(self): """ ParFlorisModel secondary keyword arguments (after configuration). """ return { "interface": self.interface, "max_workers": self.max_workers, "n_wind_condition_splits": self.n_wind_condition_splits, "return_turbine_powers_only": self.return_turbine_powers_only, "print_timings": self.print_timings } @property def fmodel(self): """ Raise deprecation warning. """ self.logger.warning( "ParFlorisModel no longer contains `fmodel` as an attribute " "and now directly inherits from FlorisModel. Please use the " "attributes and methods of FlorisModel directly." ) @property def interface(self): """ The parallelization interface used. """ return self._interface @interface.setter def interface(self, value): """ Raise error regarding setting the interface. """ raise AttributeError( "The parallelization interface cannot be changed after instantiation." ) def _parallel_run(fmodel_dict, set_kwargs) -> FlorisModel: """ Run the FLORIS model in parallel. Args: fmodel: The FLORIS model to run. set_kwargs: Additional keyword arguments to pass to fmodel.set(). """ fmodel = FlorisModel(fmodel_dict) fmodel.set(**set_kwargs) fmodel.run() return fmodel def _parallel_run_powers_only(fmodel_dict, set_kwargs) -> np.ndarray: """ Run the FLORIS model in parallel, returning only the turbine powers. Args: fmodel: The FLORIS model to run. set_kwargs: Additional keyword arguments to pass to fmodel.set(). """ fmodel = FlorisModel(fmodel_dict) fmodel.set(**set_kwargs) fmodel.run() return fmodel.get_turbine_powers() def _parallel_run_map(x): """ Wrapper for unpacking inputs to _parallel_run() for use with map(). """ return _parallel_run(*x) def _parallel_run_powers_only_map(x): """ Wrapper for unpacking inputs to _parallel_run_powers_only() for use with map(). """ return _parallel_run_powers_only(*x) def _parallel_sample_flow_at_points(fmodel_dict, set_kwargs, x, y, z): fmodel = FlorisModel(fmodel_dict) fmodel.set(**set_kwargs) return fmodel.sample_flow_at_points(x, y, z) def _parallel_sample_flow_at_points_map(x): """ Wrapper for unpacking inputs to _parallel_sample_flow_at_points() for use with map(). """ return _parallel_sample_flow_at_points(*x) def _parallel_sample_ti_at_points(fmodel_dict, set_kwargs, x, y, z): fmodel = FlorisModel(fmodel_dict) fmodel.set(**set_kwargs) return fmodel.sample_ti_at_points(x, y, z) def _parallel_sample_ti_at_points_map(x): """ Wrapper for unpacking inputs to _parallel_sample_ti_at_points() for use with map(). """ return _parallel_sample_ti_at_points(*x) ================================================ FILE: floris/parallel_floris_model.py ================================================ # Copyright 2022 Shell import copy import warnings from time import perf_counter as timerpc import numpy as np import pandas as pd from floris.floris_model import FlorisModel from floris.logging_manager import LoggingManager from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR from floris.uncertain_floris_model import map_turbine_values_uncertain, UncertainFlorisModel def _get_turbine_powers_serial(fmodel_information, yaw_angles=None): fmodel = FlorisModel(fmodel_information) fmodel.set(yaw_angles=yaw_angles) fmodel.run() return (fmodel.get_turbine_powers(), fmodel.core.flow_field) def _get_turbine_powers_serial_no_wake(fmodel_information, yaw_angles=None): fmodel = FlorisModel(fmodel_information) fmodel.set(yaw_angles=yaw_angles) fmodel.run_no_wake() return (fmodel.get_turbine_powers(), fmodel.core.flow_field) def _optimize_yaw_angles_serial( fmodel_information, minimum_yaw_angle, maximum_yaw_angle, yaw_angles_baseline, x0, Ny_passes, turbine_weights, exclude_downstream_turbines, verify_convergence, print_progress, ): fmodel_opt = FlorisModel(fmodel_information) yaw_opt = YawOptimizationSR( fmodel=fmodel_opt, minimum_yaw_angle=minimum_yaw_angle, maximum_yaw_angle=maximum_yaw_angle, yaw_angles_baseline=yaw_angles_baseline, x0=x0, Ny_passes=Ny_passes, turbine_weights=turbine_weights, exclude_downstream_turbines=exclude_downstream_turbines, verify_convergence=verify_convergence, ) # Perform optimization but silence print statements to avoid cluttering df_opt = yaw_opt.optimize(print_progress=print_progress) return df_opt class ParallelFlorisModel(LoggingManager): def __init__( self, fmodel, max_workers, n_wind_condition_splits, interface="multiprocessing", # Options are 'multiprocessing', 'mpi4py' or 'concurrent' use_mpi4py=None, propagate_flowfield_from_workers=False, print_timings=False ): """A wrapper around the nominal floris_interface class that adds parallel computing to common FlorisModel properties. Args: fmodel (FlorisModel object): Interactive FLORIS object used to perform the wake and turbine calculations. max_workers (int): Number of parallel workers, typically equal to the number of cores you have on your system or HPC. n_wind_condition_splits (int): Number of sectors to split the wind findex array over. This is typically equal to max_workers, or a multiple of it. interface (str): Parallel computing interface to leverage. Recommended is 'concurrent' or 'multiprocessing' for local (single-system) use, and 'mpi4py' for high performance computing on multiple nodes. Defaults to 'multiprocessing'. use_mpi4py (bool): Deprecated option to enable/disable the usage of 'mpi4py'. This option has been superseded by 'interface'. propagate_flowfield_from_workers (bool): By enabling this, the flow field from every floris object (one for each worker) is exported, combined and sent back to the main module. This is slow so unless it's needed, it's recommended to be disabled. Defaults to False. print_timings (bool): Print the computation time to the console. Defaults to False. """ self.logger.warning(( "ParallelFlorisModel is deprecated and will be removed in a future version. " "Please switch to ParFlorisModel instead." )) # Set defaults for backward compatibility if use_mpi4py is not None: warnings.warn( "The option 'mpi4py' will be removed in a future version. " "Please use the option 'interface'." ) if use_mpi4py: interface = "mpi4py" else: interface = "multiprocessing" if interface == "mpi4py": import mpi4py.futures as mp self._PoolExecutor = mp.MPIPoolExecutor elif interface == "multiprocessing": import multiprocessing as mp self._PoolExecutor = mp.Pool if max_workers is None: max_workers = mp.cpu_count() elif interface == "concurrent": from concurrent.futures import ProcessPoolExecutor self._PoolExecutor = ProcessPoolExecutor else: raise ValueError( f"Interface '{interface}' not recognized. " "Please use 'concurrent', 'multiprocessing' or 'mpi4py'." ) # Raise error if uncertain model is passed in and refer to new parallel_floris_model_2 if isinstance(fmodel, UncertainFlorisModel): raise ValueError( "UncertainFlorisModel is not supported in this version of ParallelFlorisModel. " "Please use the new version ParFlorisModel (par_floris_model) " "for UncertainFlorisModel compatibility." ) # Initialize floris object and copy common properties if isinstance(fmodel, FlorisModel): self.fmodel = fmodel.copy() self._is_uncertain = False elif isinstance(fmodel, UncertainFlorisModel): self.fmodel = fmodel.fmodel_expanded.copy() self._is_uncertain = True self._weights = fmodel.weights self._n_unexpanded = fmodel.n_unexpanded self._n_sample_points = fmodel.n_sample_points self._map_to_expanded_inputs = fmodel.map_to_expanded_inputs self.core = self.fmodel.core # Static copy as a placeholder # Save to self self._n_wind_condition_splits = n_wind_condition_splits # Save initial user input self._max_workers = max_workers # Save initial user input self.n_wind_condition_splits = int( np.min([n_wind_condition_splits, self.fmodel.core.flow_field.n_findex]) ) self.max_workers = int( np.min([max_workers, self.n_wind_condition_splits]) ) self.propagate_flowfield_from_workers = propagate_flowfield_from_workers self.interface = interface self.print_timings = print_timings def copy(self): # Make an independent copy self_copy = copy.deepcopy(self) self_copy.fmodel = self.fmodel.copy() return self_copy def set( self, wind_speeds=None, wind_directions=None, wind_shear=None, wind_veer=None, reference_wind_height=None, turbulence_intensities=None, air_density=None, layout=None, layout_x=None, layout_y=None, turbine_type=None, turbine_library_path=None, solver_settings=None, heterogeneous_inflow_config=None, wind_data=None, yaw_angles=None, power_setpoints=None, awc_modes=None, awc_amplitudes=None, awc_frequencies=None, disable_turbines=None, ): """Pass to the FlorisModel set function. To allow users to directly replace a FlorisModel object with this UncertainFlorisModel object, this function is required Args: wind_speeds (NDArrayFloat | list[float] | None, optional): Wind speeds at each findex. Defaults to None. wind_directions (NDArrayFloat | list[float] | None, optional): Wind directions at each findex. Defaults to None. wind_shear (float | None, optional): Wind shear exponent. Defaults to None. wind_veer (float | None, optional): Wind veer. Defaults to None. reference_wind_height (float | None, optional): Reference wind height. Defaults to None. turbulence_intensities (NDArrayFloat | list[float] | None, optional): Turbulence intensities at each findex. Defaults to None. air_density (float | None, optional): Air density. Defaults to None. layout_x (NDArrayFloat | list[float] | None, optional): X-coordinates of the turbines. Defaults to None. layout_y (NDArrayFloat | list[float] | None, optional): Y-coordinates of the turbines. Defaults to None. turbine_type (list | None, optional): Turbine type. Defaults to None. turbine_library_path (str | Path | None, optional): Path to the turbine library. Defaults to None. solver_settings (dict | None, optional): Solver settings. Defaults to None. heterogeneous_inflow_config (None, optional): heterogeneous inflow configuration. Defaults to None. wind_data (type[WindDataBase] | None, optional): Wind data. Defaults to None. yaw_angles (NDArrayFloat | list[float] | None, optional): Turbine yaw angles. Defaults to None. power_setpoints (NDArrayFloat | list[float] | list[float, None] | None, optional): Turbine power setpoints. disable_turbines (NDArrayBool | list[bool] | None, optional): NDArray with dimensions n_findex x n_turbines. True values indicate the turbine is disabled at that findex and the power setpoint at that position is set to 0. Defaults to None. """ if layout is not None: msg = "Use the `layout_x` and `layout_y` parameters in place of `layout` " msg += "because the `layout` parameter will be deprecated in 3.3." self.logger.warning(msg) layout_x = layout[0] layout_y = layout[1] # Just passes arguments to the floris object fmodel = self.fmodel.copy() fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, wind_shear=wind_shear, wind_veer=wind_veer, reference_wind_height=reference_wind_height, turbulence_intensities=turbulence_intensities, air_density=air_density, layout_x=layout_x, layout_y=layout_y, turbine_type=turbine_type, turbine_library_path=turbine_library_path, solver_settings=solver_settings, heterogeneous_inflow_config=heterogeneous_inflow_config, wind_data=wind_data, yaw_angles=yaw_angles, power_setpoints=power_setpoints, awc_modes=awc_modes, awc_amplitudes=awc_amplitudes, awc_frequencies=awc_frequencies, disable_turbines=disable_turbines, ) # Reinitialize settings self.__init__( fmodel=fmodel, max_workers=self._max_workers, n_wind_condition_splits=self._n_wind_condition_splits, interface=self.interface, propagate_flowfield_from_workers=self.propagate_flowfield_from_workers, print_timings=self.print_timings, ) def _preprocessing(self, yaw_angles=None): # Format yaw angles if yaw_angles is None: yaw_angles = np.zeros(( self.fmodel.core.flow_field.n_findex, self.fmodel.core.farm.n_turbines )) # Prepare settings n_wind_condition_splits = self.n_wind_condition_splits n_wind_condition_splits = np.min( [n_wind_condition_splits, self.fmodel.core.flow_field.n_findex] ) # Prepare the input arguments for parallel execution fmodel_dict = self.fmodel.core.as_dict() wind_condition_id_splits = np.array_split( np.arange(self.fmodel.core.flow_field.n_findex), n_wind_condition_splits, ) multiargs = [] for wc_id_split in wind_condition_id_splits: # for ws_id_split in wind_speed_id_splits: fmodel_dict_split = copy.deepcopy(fmodel_dict) wind_directions = self.fmodel.core.flow_field.wind_directions[wc_id_split] wind_speeds = self.fmodel.core.flow_field.wind_speeds[wc_id_split] turbulence_intensities = self.fmodel.core.flow_field.turbulence_intensities[wc_id_split] yaw_angles_subset = yaw_angles[wc_id_split[0]:wc_id_split[-1]+1, :] fmodel_dict_split["flow_field"]["wind_directions"] = wind_directions fmodel_dict_split["flow_field"]["wind_speeds"] = wind_speeds fmodel_dict_split["flow_field"]["turbulence_intensities"] = turbulence_intensities # Prepare lightweight data to pass along multiargs.append((fmodel_dict_split, yaw_angles_subset)) return multiargs # Function to merge subsets in dictionaries def _merge_subsets(self, field, subset): i, j, k = np.shape(subset) subset_reshape = np.reshape(subset, (i*j, k)) return [eval("f.{:s}".format(field) for f in subset_reshape)] def _postprocessing(self, output): # Split results power_subsets = [p[0] for p in output] flowfield_subsets = [p[1] for p in output] # Retrieve and merge turbine power productions turbine_powers = np.concatenate(power_subsets, axis=0) # Optionally, also merge flow field dictionaries from individual floris solutions if self.propagate_flowfield_from_workers: self.core = self.fmodel.core # Refresh static copy of underlying floris class # self.core.flow_field.u_initial = self._merge_subsets("u_initial", flowfield_subsets) # self.core.flow_field.v_initial = self._merge_subsets("v_initial", flowfield_subsets) # self.core.flow_field.w_initial = self._merge_subsets("w_initial", flowfield_subsets) self.core.flow_field.u = self._merge_subsets("u", flowfield_subsets) self.core.flow_field.v = self._merge_subsets("v", flowfield_subsets) self.core.flow_field.w = self._merge_subsets("w", flowfield_subsets) self.core.flow_field.turbulence_intensity_field = self._merge_subsets( "turbulence_intensity_field", flowfield_subsets ) return turbine_powers def run(self): raise UserWarning( "'run' not supported on ParallelFlorisModel. Please use " "'get_turbine_powers' or 'get_farm_power' directly." ) def get_turbine_powers(self, yaw_angles=None, no_wake=False): # Retrieve multiargs: preprocessing t0 = timerpc() multiargs = self._preprocessing(yaw_angles) t_preparation = timerpc() - t0 # Set the function based on whether wake is disabled if not no_wake: turbine_power_function = _get_turbine_powers_serial else: turbine_power_function = _get_turbine_powers_serial_no_wake # Perform parallel calculation t1 = timerpc() with self._PoolExecutor(self.max_workers) as p: if (self.interface == "mpi4py") or (self.interface == "multiprocessing"): out = p.starmap(turbine_power_function, multiargs) else: out = p.map( turbine_power_function, [j[0] for j in multiargs], [j[1] for j in multiargs] ) # out = list(out) t_execution = timerpc() - t1 # Postprocessing: merge power production (and opt. flow field) from individual runs t2 = timerpc() turbine_powers = self._postprocessing(out) if self._is_uncertain: turbine_powers = map_turbine_values_uncertain( unique_turbine_values=turbine_powers, map_to_expanded_inputs=self._map_to_expanded_inputs, weights=self._weights, n_unexpanded=self._n_unexpanded, n_sample_points=self._n_sample_points, n_turbines=self.fmodel.core.farm.n_turbines, ) t_postprocessing = timerpc() - t2 t_total = timerpc() - t0 if self.print_timings: print("===============================================================================") print( "Total time spent for parallel calculation " f"({self.max_workers} workers): {t_total:.3f} s" ) print(f" Time spent in parallel preprocessing: {t_preparation:.3f} s") print(f" Time spent in parallel loop execution: {t_execution:.3f} s.") print(f" Time spent in parallel postprocessing: {t_postprocessing:.3f} s") return turbine_powers def get_farm_power(self, yaw_angles=None, turbine_weights=None, no_wake=False): if turbine_weights is None: # Default to equal weighing of all turbines when turbine_weights is None turbine_weights = np.ones( ( (self._n_unexpanded if self._is_uncertain else self.fmodel.core.flow_field.n_findex), self.fmodel.core.farm.n_turbines ) ) elif len(np.shape(turbine_weights)) == 1: # Deal with situation when 1D array is provided turbine_weights = np.tile( turbine_weights, ( (self._n_unexpanded if self._is_uncertain else self.fmodel.core.flow_field.n_findex), 1 ) ) # Calculate all turbine powers and apply weights turbine_powers = self.get_turbine_powers(yaw_angles=yaw_angles, no_wake=no_wake) turbine_powers = np.multiply(turbine_weights, turbine_powers) return np.sum(turbine_powers, axis=1) def get_farm_AEP( self, freq, cut_in_wind_speed=None, cut_out_wind_speed=None, yaw_angles=None, turbine_weights=None, no_wake=False, ) -> float: """ Estimate annual energy production (AEP) for distributions of wind speed, wind direction, frequency of occurrence, and yaw offset. Args: freq (NDArrayFloat): NumPy array with shape (n_wind_directions, n_wind_speeds) with the frequencies of each wind direction and wind speed combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm power for every condition in calculating the wind farm's AEP. cut_in_wind_speed (float, optional): No longer supported. cut_out_wind_speed (float, optional): No longer supported. yaw_angles (NDArrayFloat | list[float] | None, optional): The relative turbine yaw angles in degrees. If None is specified, will assume that the turbine yaw angles are all zero degrees for all conditions. Defaults to None. turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_wind_directions, n_wind_speeds, n_turbines). Defaults to None. no_wake: (bool, optional): When *True* updates the turbine quantities without calculating the wake or adding the wake to the flow field. This can be useful when quantifying the loss in AEP due to wakes. Defaults to *False*. Returns: float: The Annual Energy Production (AEP) for the wind farm in watt-hours. """ # This code is out of date, let's just thread it through # # If no_wake==True, ignore parallelization because it's fast enough # if no_wake: # return self.fmodel.get_farm_AEP( # freq=freq, # cut_in_wind_speed=cut_in_wind_speed, # cut_out_wind_speed=cut_out_wind_speed, # yaw_angles=yaw_angles, # turbine_weights=turbine_weights, # no_wake=no_wake # ) # Verify dimensions of the variable "freq" if ((self._is_uncertain and np.shape(freq)[0] != self._n_unexpanded) or (not self._is_uncertain and np.shape(freq)[0] != self.fmodel.core.flow_field.n_findex)): raise UserWarning( "'freq' should be a one-dimensional array with dimensions (n_findex). " f"Given shape is {np.shape(freq)}" ) # Check if frequency vector sums to 1.0. If not, raise a warning if np.abs(np.sum(freq) - 1.0) > 0.001: self.logger.warning( "WARNING: The frequency array provided to get_farm_AEP() does not sum to 1.0." ) # Copy the full wind speed array from the floris object and initialize # the the farm_power variable as an empty array. wind_speeds = np.array(self.fmodel.core.flow_field.wind_speeds, copy=True) wind_directions = np.array(self.fmodel.core.flow_field.wind_directions, copy=True) turbulence_intensities = np.array( self.fmodel.core.flow_field.turbulence_intensities, copy=True, ) farm_power = np.zeros( self._n_unexpanded if self._is_uncertain else self.core.flow_field.n_findex ) # Determine which wind speeds we must evaluate in floris if cut_in_wind_speed is not None or cut_out_wind_speed is not None: raise NotImplementedError( "WARNING: The 'cut_in_wind_speed' and 'cut_out_wind_speed' " "parameters are no longer supported in the 'ParallelFlorisModel.get_farm_AEP' " "method." ) farm_power = ( self.get_farm_power(yaw_angles=yaw_angles, turbine_weights=turbine_weights, no_wake=no_wake) ) # Finally, calculate AEP in GWh aep = np.nansum(np.multiply(freq, farm_power) * 365 * 24) # Reset the FLORIS object to the full wind speed array self.fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, ) return aep def optimize_yaw_angles( self, minimum_yaw_angle=-25.0, maximum_yaw_angle=25.0, yaw_angles_baseline=None, x0=None, Ny_passes=[5,4], turbine_weights=None, exclude_downstream_turbines=True, verify_convergence=False, print_worker_progress=False, # Recommended disabled to avoid clutter. Useful for debugging ): # Prepare the inputs to each core for multiprocessing module t0 = timerpc() multiargs = self._preprocessing() for ii in range(len(multiargs)): multiargs[ii] = ( multiargs[ii][0], minimum_yaw_angle, maximum_yaw_angle, yaw_angles_baseline, x0, Ny_passes, turbine_weights, exclude_downstream_turbines, verify_convergence, print_worker_progress, ) t1 = timerpc() # Optimize yaw angles using parallel processing print("Optimizing yaw angles with {:d} workers.".format(self.max_workers)) with self._PoolExecutor(self.max_workers) as p: if (self.interface == "mpi4py") or (self.interface == "multiprocessing"): df_opt_splits = p.starmap(_optimize_yaw_angles_serial, multiargs) else: df_opt_splits = p.map( _optimize_yaw_angles_serial, [j[0] for j in multiargs], [j[1] for j in multiargs], [j[2] for j in multiargs], [j[3] for j in multiargs], [j[4] for j in multiargs], [j[5] for j in multiargs], [j[6] for j in multiargs], [j[7] for j in multiargs], [j[8] for j in multiargs], [j[9] for j in multiargs], ) t2 = timerpc() # Combine all solutions from multiprocessing into single dataframe df_opt = pd.concat(df_opt_splits, axis=0).reset_index(drop=True).sort_values( by=["wind_direction", "wind_speed", "turbulence_intensity"] ) t3 = timerpc() if self.print_timings: print("===============================================================================") print( "Total time spent for parallel calculation " f"({self.max_workers} workers): {t3 - t0:.3f} s" ) print(" Time spent in parallel preprocessing: {:.3f} s".format(t1 - t0)) print(" Time spent in parallel loop execution: {:.3f} s.".format(t2 - t1)) print(" Time spent in parallel postprocessing: {:.3f} s".format(t3 - t2)) return df_opt def get_operation_model(self): """Get the operation model of underlying fmodel. Returns: str: The operation_model. """ return self.fmodel.get_operation_model() def set_operation_model(self, operation_model): """Set the operation model of underlying fmodel. Args: operation_model (str): The operation model to set. """ self.fmodel.set_operation_model(operation_model) # Reinitialize settings self.__init__( fmodel=self.fmodel, max_workers=self._max_workers, n_wind_condition_splits=self._n_wind_condition_splits, interface=self.interface, propagate_flowfield_from_workers=self.propagate_flowfield_from_workers, print_timings=self.print_timings, ) def get_param(self, param, param_idx=None): """Get the parameter of underlying fmodel. Args: param (List[str]): A list of keys to traverse the FlorisModel dictionary. param_idx (Optional[int], optional): The index to get the value at. Defaults to None. If None, the entire parameter is returned. Returns: Any: The value of the parameter. """ return self.fmodel.get_param(param, param_idx) def set_param(self, param, value, param_idx=None): """Set the parameter of underlying fmodel. Args: param (List[str]): A list of keys to traverse the FlorisModel dictionary. value (Any): The value to set. param_idx (Optional[int], optional): The index to set the value at. Defaults to None. """ self.fmodel.set_param(param, value, param_idx) # Reinitialize settings self.__init__( fmodel=self.fmodel, max_workers=self._max_workers, n_wind_condition_splits=self._n_wind_condition_splits, interface=self.interface, propagate_flowfield_from_workers=self.propagate_flowfield_from_workers, print_timings=self.print_timings, ) @property def layout_x(self): return self.fmodel.layout_x @property def layout_y(self): return self.fmodel.layout_y @property def wind_speeds(self): return self.fmodel.wind_speeds @property def wind_directions(self): return self.fmodel.wind_directions @property def turbulence_intensities(self): return self.fmodel.turbulence_intensities @property def n_findex(self): return self.fmodel.n_findex @property def n_turbines(self): return self.fmodel.n_turbines ================================================ FILE: floris/turbine_library/__init__.py ================================================ from floris.turbine_library.turbine_previewer import TurbineInterface, TurbineLibrary from floris.turbine_library.turbine_utilities import ( build_cosine_loss_turbine_dict, check_smooth_power_curve, ) ================================================ FILE: floris/turbine_library/iea_10MW.yaml ================================================ # Data based on: # https://github.com/NREL/turbine-models/blob/master/Offshore/IEA_10MW_198_RWT.csv # Note: Generator efficiency of 94% used. Small power variations above rated removed. turbine_type: 'iea_10MW' hub_height: 119.0 rotor_diameter: 198.0 TSR: 8.0 operation_model: cosine-loss power_thrust_table: ref_air_density: 1.225 ref_tilt: 6.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 helix_a: 1.719 helix_power_b: 4.823e-03 helix_power_c: 2.314e-10 helix_thrust_b: 1.157e-03 helix_thrust_c: 1.167e-04 controller_dependent_turbine_parameters: rotor_solidity: 0.03500415472147307 rated_rpm: 8.6 generator_efficiency: 0.944 rated_power: 10000.0 rotor_diameter: 198 beta: -3.8233819218614817 cd: 0.004612981322772105 cl_alfa: 4.602140680380394 cp_ct_data_file: "demo_cp_ct_surfaces/iea_10MW_demo_cp_ct_surface.npz" power: - 0.0 - 0.0 - 35.60156 - 414.0606 - 1009.90686 - 1855.02326 - 2963.01442 - 4440.26484 - 6330.82856 - 7392.13274 - 8514.32824 - 9691.10578 - 10000.00 - 10000.00 - 10000.00 - 10000.00 - 10000.00 - 10000.00 - 10000.00 - 10000.00 - 10000.00 - 10000.00 - 0.0 - 0.0 thrust_coefficient: - 0.0 - 0.0 - 0.915 - 0.926 - 0.921 - 0.895 - 0.885 - 0.873 - 0.827 - 0.789 - 0.754 - 0.721 - 0.591 - 0.49 - 0.418 - 0.318 - 0.251 - 0.203 - 0.167 - 0.119 - 0.088 - 0.049 - 0.0 - 0.0 wind_speed: - 0.0000 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 8.0 - 9.0 - 9.5 - 10.0 - 10.5 - 11.0 - 11.5 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 18.0 - 20.0 - 25.0 - 25.01 - 50.0 ================================================ FILE: floris/turbine_library/iea_15MW.yaml ================================================ # Data based on: # https://github.com/IEAWindTask37/IEA-15-240-RWT/blob/master/Documentation/ # IEA-15-240-RWT_tabular.xlsx # Note: Small power variations above rated removed. # Generator efficiency of 100% used. turbine_type: 'iea_15MW' hub_height: 150.0 rotor_diameter: 242.24 TSR: 8.0 operation_model: cosine-loss power_thrust_table: ref_air_density: 1.225 ref_tilt: 6.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 helix_a: 1.809 helix_power_b: 4.828e-03 helix_power_c: 4.017e-11 helix_thrust_b: 1.390e-03 helix_thrust_c: 5.084e-04 controller_dependent_turbine_parameters: rotor_solidity: 0.031018237027995298 rated_rpm: 7.55 generator_efficiency: 0.95756 rated_power: 15000.00 rotor_diameter: 242.24 beta: -3.098605491003358 cd: 0.004426686198054057 cl_alfa: 4.546410770937916 cp_ct_data_file: "demo_cp_ct_surfaces/iea_15MW_demo_cp_ct_surface.npz" power: - 0.000000 - 0.000000 - 42.733312 - 292.585981 - 607.966543 - 981.097693 - 1401.98084 - 1858.67086 - 2337.575997 - 2824.097302 - 3303.06456 - 3759.432328 - 4178.637714 - 4547.19121 - 4855.342682 - 5091.537139 - 5248.453137 - 5320.793207 - 5335.345498 - 5437.90563 - 5631.253025 - 5920.980626 - 6315.115602 - 6824.470067 - 7462.846389 - 8238.359448 - 9167.96703 - 10285.211 - 11617.23699 - 13194.41511 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 15000.00 - 0.0 - 0.0 thrust_coefficient: - 0.000000 - 0.000000 - 0.80742173 - 0.784655297 - 0.781771245 - 0.785377072 - 0.788045584 - 0.789922119 - 0.790464625 - 0.789868339 - 0.788727582 - 0.787359348 - 0.785895402 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.778275899 - 0.77176172 - 0.747149663 - 0.562338457 - 0.463477777 - 0.389083718 - 0.329822385 - 0.281465071 - 0.241494345 - 0.208180574 - 0.180257568 - 0.156747535 - 0.136877529 - 0.120026379 - 0.105689427 - 0.093453742 - 0.082979637 - 0.073986457 - 0.066241166 - 0.059552107 - 0.053756866 - 0.048721662 - 0.044334197 - 0.0 - 0.0 wind_speed: - 0.000 - 2.9 - 3.0 - 3.54953237 - 4.067900771 - 4.553906848 - 5.006427063 - 5.424415288 - 5.806905228 - 6.153012649 - 6.461937428 - 6.732965398 - 6.965470002 - 7.158913742 - 7.312849418 - 7.426921164 - 7.500865272 - 7.534510799 - 7.541241633 - 7.58833327 - 7.675676842 - 7.803070431 - 7.970219531 - 8.176737731 - 8.422147605 - 8.70588182 - 9.027284445 - 9.385612468 - 9.780037514 - 10.20964776 - 10.67345004 - 10.86770694 - 11.17037214 - 11.6992653 - 12.25890683 - 12.84800295 - 13.46519181 - 14.10904661 - 14.77807889 - 15.470742 - 16.18543466 - 16.92050464 - 17.67425264 - 18.44493615 - 19.23077353 - 20.02994808 - 20.8406123 - 21.66089211 - 22.4888912 - 23.32269542 - 24.1603772 - 25 - 25.020 - 50.0 ================================================ FILE: floris/turbine_library/iea_15MW_floating_multi_dim_cp_ct.yaml ================================================ turbine_type: 'iea_15MW_floating' hub_height: 150.0 rotor_diameter: 242.24 TSR: 8.0 multi_dimensional_cp_ct: True power_thrust_table: ref_air_density: 1.225 ref_tilt: 6.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 # Note that the multidimensional data provided here is fictional and does not represent a real # wind turbine. power_thrust_data_file: 'iea_15MW_multi_dim_Tp_Hs.csv' # The floating_tilt_table describes the steady state tilt of the floating platform as a function of # wind speed. This is used to adjust the power and thrust coefficients when correct_cp_ct_for_tilt # is True. Updated values as of FLORIS v4.6, taken from # https://github.com/FLOWMAS-EERC/IEA15_FOWT/blob/main/iea_15MW_floating_power-from-fixed.yaml # generated using OpenFAST with the U-Maine floater by Sam Kaufman-Martin. # The floater model used to generate these values is the UMaine VolturnUS-S Reference Platform. floating_tilt_table: tilt: - 5.406938261 - 5.889508311 - 6.566875672 - 7.339842075 - 8.189297064 - 9.175639814 - 10.20156799 - 10.42520318 - 9.954627435 - 9.266065643 - 8.833742574 - 8.514576732 - 8.268973572 - 8.076813462 - 7.924809783 - 7.803116564 - 7.70524892 - 7.626825229 - 7.564642136 - 7.516322963 - 7.48004717 - 7.45510389 wind_speed: - 3.5 - 4.5 - 5.5 - 6.5 - 7.5 - 8.5 - 9.5 - 10.5 - 11.5 - 12.5 - 13.5 - 14.5 - 15.5 - 16.5 - 17.5 - 18.5 - 19.5 - 20.5 - 21.5 - 22.5 - 23.5 - 24.5 correct_cp_ct_for_tilt: True ================================================ FILE: floris/turbine_library/iea_15MW_multi_dim_TI_u.csv ================================================ TI,ws,power,thrust_coefficient 0.06,0.0,0.0,0.0 0.06,2.9,0.0,0.0 0.06,3.0,42.733312,0.80742173 0.06,3.54953237,292.585981,0.784655297 0.06,4.067900771,607.966543,0.781771245 0.06,4.553906848,981.097693,0.785377072 0.06,5.006427063,1401.98084,0.788045584 0.06,5.424415288,1858.67086,0.789922119 0.06,5.806905228,2337.575997,0.790464625 0.06,6.153012649,2824.097302,0.789868339 0.06,6.461937428,3303.06456,0.788727582 0.06,6.732965398,3759.432328,0.787359348 0.06,6.965470002,4178.637714,0.785895402 0.06,7.158913742,4547.19121,0.778275899 0.06,7.312849418,4855.342682,0.778275899 0.06,7.426921164,5091.537139,0.778275899 0.06,7.500865272,5248.453137,0.778275899 0.06,7.534510799,5320.793207,0.778275899 0.06,7.541241633,5335.345498,0.778275899 0.06,7.58833327,5437.90563,0.778275899 0.06,7.675676842,5631.253025,0.778275899 0.06,7.803070431,5920.980626,0.778275899 0.06,7.970219531,6315.115602,0.778275899 0.06,8.176737731,6824.470067,0.778275899 0.06,8.422147605,7462.846389,0.778275899 0.06,8.70588182,8238.359448,0.778275899 0.06,9.027284445,9167.96703,0.778275899 0.06,9.385612468,10285.211,0.778275899 0.06,9.780037514,11617.23699,0.778275899 0.06,10.20964776,13194.41511,0.778275899 0.06,10.67345004,15000.0,0.77176172 0.06,10.86770694,15000.0,0.747149663 0.06,11.17037214,15000.0,0.562338457 0.06,11.6992653,15000.0,0.463477777 0.06,12.25890683,15000.0,0.389083718 0.06,12.84800295,15000.0,0.329822385 0.06,13.46519181,15000.0,0.281465071 0.06,14.10904661,15000.0,0.241494345 0.06,14.77807889,15000.0,0.208180574 0.06,15.470742,15000.0,0.180257568 0.06,16.18543466,15000.0,0.156747535 0.06,16.92050464,15000.0,0.136877529 0.06,17.67425264,15000.0,0.120026379 0.06,18.44493615,15000.0,0.105689427 0.06,19.23077353,15000.0,0.093453742 0.06,20.02994808,15000.0,0.082979637 0.06,20.8406123,15000.0,0.073986457 0.06,21.66089211,15000.0,0.066241166 0.06,22.4888912,15000.0,0.059552107 0.06,23.32269542,15000.0,0.053756866 0.06,24.1603772,15000.0,0.048721662 0.06,25.0,15000.0,0.044334197 0.06,25.02,0.0,0.0 0.06,50.0,0.0,0.0 0.08,0.0,0.0,0.0 0.08,2.9,0.0,0.0 0.08,3.0,42.733312,0.80742173 0.08,3.54953237,292.585981,0.784655297 0.08,4.067900771,607.966543,0.781771245 0.08,4.553906848,981.097693,0.785377072 0.08,5.006427063,1401.98084,0.788045584 0.08,5.424415288,1858.67086,0.789922119 0.08,5.806905228,2337.575997,0.790464625 0.08,6.153012649,2824.097302,0.789868339 0.08,6.461937428,3303.06456,0.788727582 0.08,6.732965398,3759.432328,0.787359348 0.08,6.965470002,4178.637714,0.785895402 0.08,7.158913742,4547.19121,0.778275899 0.08,7.312849418,4855.342682,0.778275899 0.08,7.426921164,5091.537139,0.778275899 0.08,7.500865272,5248.453137,0.778275899 0.08,7.534510799,5320.793207,0.778275899 0.08,7.541241633,5335.345498,0.778275899 0.08,7.58833327,5437.90563,0.778275899 0.08,7.675676842,5631.253025,0.778275899 0.08,7.803070431,5920.980626,0.778275899 0.08,7.970219531,6315.115602,0.778275899 0.08,8.176737731,6824.470067,0.778275899 0.08,8.422147605,7462.846389,0.778275899 0.08,8.70588182,8238.359448,0.778275899 0.08,9.027284445,9167.96703,0.778275899 0.08,9.385612468,10285.211,0.778275899 0.08,9.780037514,11617.23699,0.778275899 0.08,10.20964776,13194.41511,0.778275899 0.08,10.67345004,15000.0,0.77176172 0.08,10.86770694,15000.0,0.747149663 0.08,11.17037214,15000.0,0.562338457 0.08,11.6992653,15000.0,0.463477777 0.08,12.25890683,15000.0,0.389083718 0.08,12.84800295,15000.0,0.329822385 0.08,13.46519181,15000.0,0.281465071 0.08,14.10904661,15000.0,0.241494345 0.08,14.77807889,15000.0,0.208180574 0.08,15.470742,15000.0,0.180257568 0.08,16.18543466,15000.0,0.156747535 0.08,16.92050464,15000.0,0.136877529 0.08,17.67425264,15000.0,0.120026379 0.08,18.44493615,15000.0,0.105689427 0.08,19.23077353,15000.0,0.093453742 0.08,20.02994808,15000.0,0.082979637 0.08,20.8406123,15000.0,0.073986457 0.08,21.66089211,15000.0,0.066241166 0.08,22.4888912,15000.0,0.059552107 0.08,23.32269542,15000.0,0.053756866 0.08,24.1603772,15000.0,0.048721662 0.08,25.0,15000.0,0.044334197 0.08,25.02,0.0,0.0 0.08,50.0,0.0,0.0 ================================================ FILE: floris/turbine_library/iea_15MW_multi_dim_Tp_Hs.csv ================================================ Tp,Hs,ws,power,thrust_coefficient 2,1,0,0,0 2,1,2.9,0,0 2,1,3,42.733312,0.80742173 2,1,3.54953237,292.585981,0.784655297 2,1,4.067900771,607.966543,0.781771245 2,1,4.553906848,981.097693,0.785377072 2,1,5.006427063,1401.98084,0.788045584 2,1,5.424415288,1858.67086,0.789922119 2,1,5.806905228,2337.575997,0.790464625 2,1,6.153012649,2824.097302,0.789868339 2,1,6.461937428,3303.06456,0.788727582 2,1,6.732965398,3759.432328,0.787359348 2,1,6.965470002,4178.637714,0.785895402 2,1,7.158913742,4547.19121,0.778275899 2,1,7.312849418,4855.342682,0.778275899 2,1,7.426921164,5091.537139,0.778275899 2,1,7.500865272,5248.453137,0.778275899 2,1,7.534510799,5320.793207,0.778275899 2,1,7.541241633,5335.345498,0.778275899 2,1,7.58833327,5437.90563,0.778275899 2,1,7.675676842,5631.253025,0.778275899 2,1,7.803070431,5920.980626,0.778275899 2,1,7.970219531,6315.115602,0.778275899 2,1,8.176737731,6824.470067,0.778275899 2,1,8.422147605,7462.846389,0.778275899 2,1,8.70588182,8238.359448,0.778275899 2,1,9.027284445,9167.96703,0.778275899 2,1,9.385612468,10285.211,0.778275899 2,1,9.780037514,11617.23699,0.778275899 2,1,10.20964776,13194.41511,0.778275899 2,1,10.67345004,15000,0.77176172 2,1,10.86770694,15000,0.747149663 2,1,11.17037214,15000,0.562338457 2,1,11.6992653,15000,0.463477777 2,1,12.25890683,15000,0.389083718 2,1,12.84800295,15000,0.329822385 2,1,13.46519181,15000,0.281465071 2,1,14.10904661,15000,0.241494345 2,1,14.77807889,15000,0.208180574 2,1,15.470742,15000,0.180257568 2,1,16.18543466,15000,0.156747535 2,1,16.92050464,15000,0.136877529 2,1,17.67425264,15000,0.120026379 2,1,18.44493615,15000,0.105689427 2,1,19.23077353,15000,0.093453742 2,1,20.02994808,15000,0.082979637 2,1,20.8406123,15000,0.073986457 2,1,21.66089211,15000,0.066241166 2,1,22.4888912,15000,0.059552107 2,1,23.32269542,15000,0.053756866 2,1,24.1603772,15000,0.048721662 2,1,25,15000,0.044334197 2,1,25.02,0,0 2,1,50,0,0 2,5,0,0,0 2,5,2.9,0,0 2,5,3,21.366656,0.403710865 2,5,3.54953237,146.2929905,0.392327649 2,5,4.067900771,303.9832715,0.390885623 2,5,4.553906848,490.5488465,0.392688536 2,5,5.006427063,700.99042,0.394022792 2,5,5.424415288,929.33543,0.39496106 2,5,5.806905228,1168.787999,0.395232313 2,5,6.153012649,1412.048651,0.39493417 2,5,6.461937428,1651.53228,0.394363791 2,5,6.732965398,1879.716164,0.393679674 2,5,6.965470002,2089.318857,0.392947701 2,5,7.158913742,2273.595605,0.38913795 2,5,7.312849418,2427.671341,0.38913795 2,5,7.426921164,2545.76857,0.38913795 2,5,7.500865272,2624.226569,0.38913795 2,5,7.534510799,2660.396604,0.38913795 2,5,7.541241633,2667.672749,0.38913795 2,5,7.58833327,2718.952815,0.38913795 2,5,7.675676842,2815.626513,0.38913795 2,5,7.803070431,2960.490313,0.38913795 2,5,7.970219531,3157.557801,0.38913795 2,5,8.176737731,3412.235034,0.38913795 2,5,8.422147605,3731.423195,0.38913795 2,5,8.70588182,4119.179724,0.38913795 2,5,9.027284445,4583.983515,0.38913795 2,5,9.385612468,5142.6055,0.38913795 2,5,9.780037514,5808.618495,0.38913795 2,5,10.20964776,6597.207555,0.38913795 2,5,10.67345004,7500,0.38588086 2,5,10.86770694,7500,0.373574832 2,5,11.17037214,7500,0.281169229 2,5,11.6992653,7500,0.231738889 2,5,12.25890683,7500,0.194541859 2,5,12.84800295,7500,0.164911193 2,5,13.46519181,7500,0.140732536 2,5,14.10904661,7500,0.120747173 2,5,14.77807889,7500,0.104090287 2,5,15.470742,7500,0.090128784 2,5,16.18543466,7500,0.078373768 2,5,16.92050464,7500,0.068438765 2,5,17.67425264,7500,0.06001319 2,5,18.44493615,7500,0.052844714 2,5,19.23077353,7500,0.046726871 2,5,20.02994808,7500,0.041489819 2,5,20.8406123,7500,0.036993229 2,5,21.66089211,7500,0.033120583 2,5,22.4888912,7500,0.029776054 2,5,23.32269542,7500,0.026878433 2,5,24.1603772,7500,0.024360831 2,5,25,7500,0.022167099 2,5,25.02,0,0 2,5,50,0,0 4,1,0,0,0 4,1,2.9,0,0 4,1,3,10.683328,0.201855433 4,1,3.54953237,73.14649525,0.196163824 4,1,4.067900771,151.9916358,0.195442811 4,1,4.553906848,245.2744233,0.196344268 4,1,5.006427063,350.49521,0.197011396 4,1,5.424415288,464.667715,0.19748053 4,1,5.806905228,584.3939993,0.197616156 4,1,6.153012649,706.0243255,0.197467085 4,1,6.461937428,825.76614,0.197181896 4,1,6.732965398,939.858082,0.196839837 4,1,6.965470002,1044.659429,0.196473851 4,1,7.158913742,1136.797803,0.194568975 4,1,7.312849418,1213.835671,0.194568975 4,1,7.426921164,1272.884285,0.194568975 4,1,7.500865272,1312.113284,0.194568975 4,1,7.534510799,1330.198302,0.194568975 4,1,7.541241633,1333.836375,0.194568975 4,1,7.58833327,1359.476408,0.194568975 4,1,7.675676842,1407.813256,0.194568975 4,1,7.803070431,1480.245157,0.194568975 4,1,7.970219531,1578.778901,0.194568975 4,1,8.176737731,1706.117517,0.194568975 4,1,8.422147605,1865.711597,0.194568975 4,1,8.70588182,2059.589862,0.194568975 4,1,9.027284445,2291.991758,0.194568975 4,1,9.385612468,2571.30275,0.194568975 4,1,9.780037514,2904.309248,0.194568975 4,1,10.20964776,3298.603778,0.194568975 4,1,10.67345004,3750,0.19294043 4,1,10.86770694,3750,0.186787416 4,1,11.17037214,3750,0.140584614 4,1,11.6992653,3750,0.115869444 4,1,12.25890683,3750,0.09727093 4,1,12.84800295,3750,0.082455596 4,1,13.46519181,3750,0.070366268 4,1,14.10904661,3750,0.060373586 4,1,14.77807889,3750,0.052045144 4,1,15.470742,3750,0.045064392 4,1,16.18543466,3750,0.039186884 4,1,16.92050464,3750,0.034219382 4,1,17.67425264,3750,0.030006595 4,1,18.44493615,3750,0.026422357 4,1,19.23077353,3750,0.023363436 4,1,20.02994808,3750,0.020744909 4,1,20.8406123,3750,0.018496614 4,1,21.66089211,3750,0.016560292 4,1,22.4888912,3750,0.014888027 4,1,23.32269542,3750,0.013439217 4,1,24.1603772,3750,0.012180416 4,1,25,3750,0.011083549 4,1,25.02,0,0 4,1,50,0,0 4,5,0,0,0 4,5,2.9,0,0 4,5,3,5.341664,0.100927716 4,5,3.54953237,36.57324763,0.098081912 4,5,4.067900771,75.99581788,0.097721406 4,5,4.553906848,122.6372116,0.098172134 4,5,5.006427063,175.247605,0.098505698 4,5,5.424415288,232.3338575,0.098740265 4,5,5.806905228,292.1969996,0.098808078 4,5,6.153012649,353.0121628,0.098733542 4,5,6.461937428,412.88307,0.098590948 4,5,6.732965398,469.929041,0.098419919 4,5,6.965470002,522.3297143,0.098236925 4,5,7.158913742,568.3989013,0.097284487 4,5,7.312849418,606.9178353,0.097284487 4,5,7.426921164,636.4421424,0.097284487 4,5,7.500865272,656.0566421,0.097284487 4,5,7.534510799,665.0991509,0.097284487 4,5,7.541241633,666.9181873,0.097284487 4,5,7.58833327,679.7382038,0.097284487 4,5,7.675676842,703.9066281,0.097284487 4,5,7.803070431,740.1225783,0.097284487 4,5,7.970219531,789.3894503,0.097284487 4,5,8.176737731,853.0587584,0.097284487 4,5,8.422147605,932.8557986,0.097284487 4,5,8.70588182,1029.794931,0.097284487 4,5,9.027284445,1145.995879,0.097284487 4,5,9.385612468,1285.651375,0.097284487 4,5,9.780037514,1452.154624,0.097284487 4,5,10.20964776,1649.301889,0.097284487 4,5,10.67345004,1875,0.096470215 4,5,10.86770694,1875,0.093393708 4,5,11.17037214,1875,0.070292307 4,5,11.6992653,1875,0.057934722 4,5,12.25890683,1875,0.048635465 4,5,12.84800295,1875,0.041227798 4,5,13.46519181,1875,0.035183134 4,5,14.10904661,1875,0.030186793 4,5,14.77807889,1875,0.026022572 4,5,15.470742,1875,0.022532196 4,5,16.18543466,1875,0.019593442 4,5,16.92050464,1875,0.017109691 4,5,17.67425264,1875,0.015003297 4,5,18.44493615,1875,0.013211178 4,5,19.23077353,1875,0.011681718 4,5,20.02994808,1875,0.010372455 4,5,20.8406123,1875,0.009248307 4,5,21.66089211,1875,0.008280146 4,5,22.4888912,1875,0.007444013 4,5,23.32269542,1875,0.006719608 4,5,24.1603772,1875,0.006090208 4,5,25,1875,0.005541775 4,5,25.02,0,0 4,5,50,0,0 ================================================ FILE: floris/turbine_library/iea_15MW_multi_dim_cp_ct.yaml ================================================ turbine_type: 'iea_15MW_multi_dim_cp_ct' hub_height: 150.0 rotor_diameter: 242.24 TSR: 8.0 multi_dimensional_cp_ct: True power_thrust_table: ref_air_density: 1.225 ref_tilt: 6.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 # Note that the multidimensional data provided here is fictional and does not represent a real # wind turbine. power_thrust_data_file: 'iea_15MW_multi_dim_Tp_Hs.csv' ================================================ FILE: floris/turbine_library/iea_22MW.yaml ================================================ # Data generated using OpenFAST v4.1.2 and ROSCO v2.10.2 turbine_type: 'iea_22MW' hub_height: 170.0 rotor_diameter: 284.0 TSR: 9.15 operation_model: cosine-loss power_thrust_table: ref_air_density: 1.225 ref_tilt: 6.0 cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 power: - 0.000000 - 0.000000 - 470.42006956610055 - 1118.232780617167 - 2188.311980648143 - 3782.7306259417846 - 5997.190541701143 - 8910.135612999937 - 10648.827448063703 - 12596.925211817956 - 14685.013190976455 - 15726.690294207267 - 16756.8567719253 - 17769.676433679262 - 18801.041099221588 - 19827.767728789564 - 20848.709510875513 - 21864.885529798317 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 22000.0 - 0.0 - 0.0 thrust_coefficient: - 0.000000 - 0.000000 - 0.8151028985050527 - 0.8186962341625531 - 0.8256090342400992 - 0.8302179769897181 - 0.828968333088208 - 0.8248223398544402 - 0.8212931067329768 - 0.8040275589649526 - 0.75721959807142 - 0.7326878124377916 - 0.7084846280602614 - 0.6843036442042609 - 0.663147304888859 - 0.6424161102200797 - 0.6221917507431868 - 0.6025783643621286 - 0.5539840778236028 - 0.5054754180818498 - 0.4648590217686185 - 0.42984915555590786 - 0.3991369663817839 - 0.3474807030632657 - 0.3055334704356 - 0.27077311601674536 - 0.21679726239514083 - 0.17717614488236633 - 0.14722553071482708 - 0.12404315188884445 - 0.10574107410244936 - 0.0791170545715597 - 0.06114076570325394 - 0.04852760403701628 - 0.0 - 0.0 wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 8.0 - 8.5 - 9.0 - 9.5 - 9.75 - 10.0 - 10.25 - 10.5 - 10.75 - 11.0 - 11.25 - 11.5 - 11.75 - 12.0 - 12.25 - 12.5 - 13.0 - 13.5 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 21.0 - 23.0 - 25.0 - 25.1 - 50.0 ================================================ FILE: floris/turbine_library/nrel_5MW.yaml ================================================ # NREL 5MW reference wind turbine. # Data based on: # https://github.com/NREL/turbine-models/blob/master/Offshore/NREL_5MW_126_RWT_corrected.csv # Note: Small power variations above rated removed. Rotor diameter includes coning angle. # Note: generator efficiency of 94.4% is assumed for the NREL 5MW turbine. ### # An ID for this type of turbine definition. # This is used to uniquely identify different turbines in the simulation, so should be different # for each different turbine definition being used in the same simulation. # String type. turbine_type: 'nrel_5MW' ### # Turbine hub height in meters. Float type. hub_height: 90.0 ### # Turbine rotor diameter in meters. Float type. rotor_diameter: 125.88 ### # Nominal wind turbine tip-speed ratio for below-rated operation. Only used in some wake models. # Float type. TSR: 8.0 ### # Model for power and thrust curve interpretation. See floris.core.turbine.operation_models for # details. String type. operation_model: 'cosine-loss' ### # Group of parameters needed to evaluate the power and thrust produced by the turbine. power_thrust_table: ### # Air density at which the power and thrust_coefficient curves are defined (kg / m^3). Float type. ref_air_density: 1.225 ### # Tilt angle at which the power and thrust_coefficient curves are defined (degrees). # Used to capture the effects of a floating platform on a turbine's power and wake. # Float type. ref_tilt: 5.0 ### # Cosine exponent for power loss due to tilt. Float type. cosine_loss_exponent_tilt: 1.88 ### # Cosine exponent for power loss due to yaw misalignment. Float type. cosine_loss_exponent_yaw: 1.88 ### # Helix parameter a. See documentation for details. Float type. helix_a: 1.802 ### # Helix parameter b for power calculation. See documentation for details. Float type. helix_power_b: 4.568e-03 ### # Helix parameter c for power calculation. See documentation for details. Float type. helix_power_c: 1.629e-10 ### # Helix parameter b for thrust calculation. See documentation for details. Float type. helix_thrust_b: 1.027e-03 ### # Helix parameter c for thrust calculation. See documentation for details. Float type. helix_thrust_c: 1.378e-06 ### # Fraction of peak thrust by which to reduce (specified as a decimal). Float type. peak_shaving_fraction: 0.2 ### # Threshold turbulence intensity above which to apply peak shaving (specified as a decimal). # Float tpe. peak_shaving_TI_threshold: 0.1 ### # Parameters for the 'controller-dependenter-dependent' operation model. See # floris.core.turbine.controller_dependent_operation_model and documentation for details. controller_dependent_turbine_parameters: rated_rpm: 12.1 rotor_solidity: 0.05132 generator_efficiency: 0.944 rated_power: 5000.0 rotor_diameter: 126 beta: -0.45891 cd: 0.0040638 cl_alfa: 4.275049 cp_ct_data_file: "demo_cp_ct_surfaces/nrel_5MW_demo_cp_ct_surface.npz" ### # Wind speeds for look-up tables of power and thrust_coefficient. List of float type. wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 7.5 - 7.6 - 7.7 - 7.8 - 7.9 - 8.0 - 9.0 - 10.0 - 10.1 - 10.2 - 10.3 - 10.4 - 10.5 - 10.6 - 10.7 - 10.8 - 10.9 - 11.0 - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 - 11.9 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 20.0 - 21.0 - 22.0 - 23.0 - 24.0 - 25.0 - 25.1 - 50.0 ### # Power values (specified in kW) for lookup by wind speed. List of float type. power: - 0.0 - 0.0 - 40.518011517569214 - 177.67162506419703 - 403.900880943964 - 737.5889584824021 - 1187.1774030611875 - 1239.245945375778 - 1292.5184293723503 - 1347.3213147477102 - 1403.2573725578948 - 1460.7011898730707 - 1519.6419125979983 - 1580.174365096404 - 1642.1103166918167 - 1705.758292831 - 1771.1659528893977 - 2518.553107505315 - 3448.381605840943 - 3552.140809000129 - 3657.9545431794127 - 3765.121299313842 - 3873.928844315059 - 3984.4800226955504 - 4096.582833096852 - 4210.721306623712 - 4326.154305853405 - 4443.395565353604 - 4562.497934188341 - 4683.419890251577 - 4806.164748311019 - 4929.931918769215 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 0.0 - 0.0 ### # Thrust coefficient values (unitless) for lookup by wind speed. List of float type. thrust_coefficient: - 0.0 - 0.0 - 1.132034888 - 0.999470963 - 0.917697381 - 0.860849503 - 0.815371198 - 0.811614904 - 0.807939328 - 0.80443352 - 0.800993851 - 0.79768116 - 0.794529244 - 0.791495834 - 0.788560434 - 0.787217182 - 0.787127977 - 0.785839257 - 0.783812219 - 0.783568108 - 0.783328285 - 0.781194418 - 0.777292539 - 0.773464375 - 0.769690236 - 0.766001924 - 0.762348072 - 0.758760824 - 0.755242872 - 0.751792927 - 0.748434131 - 0.745113997 - 0.717806682 - 0.672204789 - 0.63831272 - 0.610176496 - 0.585456847 - 0.563222111 - 0.542912273 - 0.399312061 - 0.310517829 - 0.248633226 - 0.203543725 - 0.169616419 - 0.143478955 - 0.122938861 - 0.106515296 - 0.093026095 - 0.081648606 - 0.072197368 - 0.064388275 - 0.057782745 - 0.0 - 0.0 ### # Boolean flag used when the user wants FLORIS to use the user-supplied multi-dimensional # power/thrust coefficient information information. Boolean type. multi_dimensional_cp_ct: False ### # Path to the .csv file that contains the multi-dimensional power/thrust coefficient data. # The format of this file is such that any external conditions, such as wave height or wave period, # that the power/thrust data is dependent on come first, in column format. The last three columns # of the .csv file must be ``ws``, ``power``, and ``thrust_coefficient``, in that order. An example # of fictional data is given in ``floris/turbine_library/iea_15MW_multi_dim_Tp_Hs.csv``. # String type. power_thrust_data_file: '../floris/turbine_library/iea_15MW_multi_dim_Tp_Hs.csv' ### # Group of parameters needed to evaluate the tilt angle of a floating turbine across wind speeds. floating_tilt_table: ### # Wind speeds at which steady tilt angles are defined (m/s). List of float type. wind_speed: - 4.0 - 6.0 - 8.0 - 10.0 - 12.0 - 14.0 - 16.0 ### # Tilt angle for each wind speed (degrees, positive "tilted back"). List of float type. tilt: - 5.0 - 5.0 - 5.0 - 5.0 - 5.0 - 5.0 - 5.0 ### # Flag for whether to apply the floating tilt table to correct turbine power and thrust curves. # Boolean type. correct_cp_ct_for_tilt: false ================================================ FILE: floris/turbine_library/turbine_previewer.py ================================================ from pathlib import Path import attrs import matplotlib.pyplot as plt import numpy as np from attrs import define, field from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.core.turbine.turbine import ( power, thrust_coefficient, Turbine, ) from floris.type_dec import convert_to_path, NDArrayFloat from floris.utilities import ( load_yaml, round_nearest, round_nearest_2_or_5, ) INTERNAL_LIBRARY = Path(__file__).parent DEFAULT_WIND_SPEEDS = np.linspace(0, 40, 81) DEPRECATION_MESSAGE = ( "The TurbineInterface and TurbineLibrary classes are now deprecated as will be removed in a", " future FLORIS release." ) @define(auto_attribs=True) class TurbineInterface: turbine: Turbine = field(validator=attrs.validators.instance_of(Turbine)) @classmethod def from_library(cls, library_path: str | Path, file_name: str): """Loads the turbine definition from a YAML configuration file located in either the internal turbine library ``floris/floris/turbine_library/``, or a user-specified location. Args: library_path (:obj:`str` | :obj:`pathlib.Path`): The location of the turbine library; use "internal" to use the FLORIS-provided library. file_name (:obj:`str` | :obj:`pathlib.Path`): The name of the configuration file. Returns: (TurbineInterface): Creates a new ``TurbineInterface`` object. """ print(DEPRECATION_MESSAGE) # Use the pre-mapped internal turbine library or validate the user's library if library_path == "internal": library_path = INTERNAL_LIBRARY else: library_path = convert_to_path(library_path) # Add in the library specification if needed, and load from dict turb_dict = load_yaml(library_path / file_name) return cls(turbine=Turbine.from_dict(turb_dict)) @classmethod def from_yaml(cls, file_path: str | Path): """Loads the turbine definition from a YAML configuration file. Args: file_path : str | Path The full path and file name of the turbine configuration file. Returns: (TurbineInterface): Creates a new ``TurbineInterface`` object. """ print(DEPRECATION_MESSAGE) file_path = Path(file_path).resolve() # Add in the library specification if needed, and load from dict turb_dict = load_yaml(file_path) return cls(turbine=Turbine.from_dict(turb_dict)) @classmethod def from_turbine_dict(cls, config_dict: dict): """Loads the turbine definition from a dictionary. Args: config_dict : dict The ``Turbine`` configuration dictionary. Returns: (`TurbineInterface`): Returns a ``TurbineInterface`` object. """ print(DEPRECATION_MESSAGE) return cls(turbine=Turbine.from_dict(config_dict)) def power_curve( self, wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, ) -> tuple[NDArrayFloat, NDArrayFloat] | tuple[NDArrayFloat, dict[tuple, NDArrayFloat]]: """Produces a plot-ready power curve for the turbine for wind speed vs power (MW), assuming no tilt or yaw effects. Args: wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. Returns: (tuple[NDArrayFloat, NDArrayFloat] | tuple[NDArrayFloat, dict[tuple, NDArrayFloat]]): Returns the wind speed array and the power array, or the wind speed array and a dictionary of the multidimensional parameters and their associated power arrays. """ shape = (wind_speeds.size, 1) if self.turbine.multi_dimensional_cp_ct: power_mw = { k: power( velocities=wind_speeds.reshape(shape), turbulence_intensities=np.zeros(shape), air_density=np.full(shape, v["ref_air_density"]), power_functions={self.turbine.turbine_type: self.turbine.power_function}, yaw_angles=np.zeros(shape), tilt_angles=np.full(shape, v["ref_tilt"]), power_setpoints=np.full(shape, POWER_SETPOINT_DEFAULT), awc_modes=np.full(shape, ["baseline"]), awc_amplitudes=np.zeros(shape), tilt_interps={self.turbine.turbine_type: self.turbine.tilt_interp}, turbine_type_map=np.full(shape, self.turbine.turbine_type), turbine_power_thrust_tables={self.turbine.turbine_type: v}, ).flatten() / 1e6 for k,v in self.turbine.power_thrust_table.items() } else: power_mw = power( velocities=wind_speeds.reshape(shape), turbulence_intensities=np.zeros(shape), air_density=np.full(shape, self.turbine.power_thrust_table["ref_air_density"]), power_functions={self.turbine.turbine_type: self.turbine.power_function}, yaw_angles=np.zeros(shape), tilt_angles=np.full(shape, self.turbine.power_thrust_table["ref_tilt"]), power_setpoints=np.full(shape, POWER_SETPOINT_DEFAULT), awc_modes=np.full(shape, ["baseline"]), awc_amplitudes=np.zeros(shape), tilt_interps={self.turbine.turbine_type: self.turbine.tilt_interp}, turbine_type_map=np.full(shape, self.turbine.turbine_type), turbine_power_thrust_tables={ self.turbine.turbine_type: self.turbine.power_thrust_table }, ).flatten() / 1e6 return wind_speeds, power_mw def thrust_coefficient_curve( self, wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, ) -> tuple[NDArrayFloat, NDArrayFloat]: """Produces a plot-ready thrust curve for the turbine for wind speed vs thrust coefficient assuming no tilt or yaw effects. Args: wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. Returns: tuple[NDArrayFloat, NDArrayFloat] Returns the wind speed array and the thrust coefficient array. """ shape = (wind_speeds.size, 1) if self.turbine.multi_dimensional_cp_ct: ct_curve = { k: thrust_coefficient( velocities=wind_speeds.reshape(shape), turbulence_intensities=np.zeros(shape), air_density=np.full(shape, v["ref_air_density"]), yaw_angles=np.zeros(shape), tilt_angles=np.full(shape, v["ref_tilt"]), power_setpoints=np.full(shape, POWER_SETPOINT_DEFAULT), awc_modes=np.full(shape, ["baseline"]), awc_amplitudes=np.zeros(shape), thrust_coefficient_functions={ self.turbine.turbine_type: self.turbine.thrust_coefficient_function }, tilt_interps={self.turbine.turbine_type: self.turbine.tilt_interp}, correct_cp_ct_for_tilt=np.zeros(shape, dtype=bool), turbine_type_map=np.full(shape, self.turbine.turbine_type), turbine_power_thrust_tables={self.turbine.turbine_type: v}, ).flatten() for k,v in self.turbine.power_thrust_table.items() } else: ct_curve = thrust_coefficient( velocities=wind_speeds.reshape(shape), turbulence_intensities=np.zeros(shape), air_density=np.full(shape, self.turbine.power_thrust_table["ref_air_density"]), yaw_angles=np.zeros(shape), tilt_angles=np.full(shape, self.turbine.power_thrust_table["ref_tilt"]), power_setpoints=np.full(shape, POWER_SETPOINT_DEFAULT), awc_modes=np.full(shape, ["baseline"]), awc_amplitudes=np.zeros(shape), thrust_coefficient_functions={ self.turbine.turbine_type: self.turbine.thrust_coefficient_function }, tilt_interps={self.turbine.turbine_type: self.turbine.tilt_interp}, correct_cp_ct_for_tilt=np.zeros(shape, dtype=bool), turbine_type_map=np.full(shape, self.turbine.turbine_type), turbine_power_thrust_tables={ self.turbine.turbine_type: self.turbine.power_thrust_table }, ).flatten() return wind_speeds, ct_curve def plot_power_curve( self, wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, fig_kwargs: dict | None = None, plot_kwargs: dict | None = None, legend_kwargs: dict | None = None, return_fig: bool = False ) -> None | tuple[plt.Figure, plt.Axes]: """Plots the power curve for a given set of wind speeds. Args: wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. fig_kwargs (dict, optional): Any keywords arguments to be passed to ``plt.Figure()``. Defaults to None. plot_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.plot()``. Defaults to None. legend_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.legend()``. Defaults to None. return_fig (bool, optional): Indicator if the ``Figure`` and ``Axes`` objects should be returned. Defaults to False. Returns: None | tuple[plt.Figure, plt.Axes]: None, if :py:attr:`return_fig` is False, otherwise a tuple of the Figure and Axes objects are returned. """ wind_speeds, power_mw = self.power_curve(wind_speeds=wind_speeds) # Initialize kwargs if None fig_kwargs = {} if fig_kwargs is None else fig_kwargs plot_kwargs = {} if plot_kwargs is None else plot_kwargs legend_kwargs = {} if legend_kwargs is None else legend_kwargs # Set the figure defaults if none are provided fig_kwargs.setdefault("dpi", 200) fig_kwargs.setdefault("figsize", (4, 3)) fig = plt.figure(**fig_kwargs) ax = fig.add_subplot(111) min_windspeed = 0 max_windspeed = max(wind_speeds) min_power = 0 max_power = 0 if isinstance(power_mw, dict): for key, _power_mw in power_mw.items(): max_power = max(max_power, *_power_mw) _cond = "; ".join((f"{c}: {k}" for c, k in zip(self.turbine.condition_keys, key))) label = f"{self.turbine.turbine_type} - {_cond}" ax.plot(wind_speeds, _power_mw, label=label, **plot_kwargs) else: max_power = max(power_mw) ax.plot(wind_speeds, power_mw, label=self.turbine.turbine_type, **plot_kwargs) ax.grid() ax.set_axisbelow(True) ax.legend(**legend_kwargs) max_power = round_nearest_2_or_5(max_power) ax.set_xlim(min_windspeed, max_windspeed) ax.set_ylim(min_power, max_power) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Power (MW)") if return_fig: return fig, ax fig.tight_layout() def plot_thrust_coefficient_curve( self, wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, fig_kwargs: dict | None = None, plot_kwargs: dict | None = None, legend_kwargs: dict | None = None, return_fig: bool = False ) -> None | tuple[plt.Figure, plt.Axes]: """Plots the thrust coefficient curve for a given set of wind speeds. Args: wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. fig_kwargs (dict, optional): Any keywords arguments to be passed to ``plt.Figure()``. Defaults to None. plot_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.plot()``. Defaults to None. legend_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.legend()``. Defaults to None. return_fig (bool, optional): Indicator if the ``Figure`` and ``Axes`` objects should be returned. Defaults to False. Returns: None | tuple[plt.Figure, plt.Axes]: None, if :py:attr:`return_fig` is False, otherwise a tuple of the Figure and Axes objects are returned. """ wind_speeds, thrust = self.thrust_coefficient_curve(wind_speeds=wind_speeds) # Initialize kwargs if None fig_kwargs = {} if fig_kwargs is None else fig_kwargs plot_kwargs = {} if plot_kwargs is None else plot_kwargs legend_kwargs = {} if legend_kwargs is None else legend_kwargs # Set the figure defaults if none are provided fig_kwargs.setdefault("dpi", 200) fig_kwargs.setdefault("figsize", (4, 3)) fig = plt.figure(**fig_kwargs) ax = fig.add_subplot(111) min_windspeed = 0 max_thrust = 0 max_windspeed = max(wind_speeds) if isinstance(thrust, dict): for key, _thrust in thrust.items(): max_thrust = max(max_thrust, *_thrust) _cond = "; ".join((f"{c}: {k}" for c, k in zip(self.turbine.condition_keys, key))) label = f"{self.turbine.turbine_type} - {_cond}" ax.plot(wind_speeds, _thrust, label=label, **plot_kwargs) else: max_thrust = max(thrust) ax.plot(wind_speeds, thrust, label=self.turbine.turbine_type, **plot_kwargs) ax.grid() ax.set_axisbelow(True) ax.legend(**legend_kwargs) ax.set_xlim(min_windspeed, max_windspeed) ax.set_ylim(0, round_nearest(max_thrust * 100, base=10) / 100) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Thrust Coefficient") if return_fig: return fig, ax fig.tight_layout() @define(auto_attribs=True) class TurbineLibrary: turbine_map: dict[str: TurbineInterface] = field(factory=dict) power_curves: dict[str, tuple[NDArrayFloat, NDArrayFloat]] = field(factory=dict) thrust_coefficient_curves: dict[str, tuple[NDArrayFloat, NDArrayFloat]] = field(factory=dict) def load_internal_library(self, which: list[str] = [], exclude: list[str] = []) -> None: """Loads all of the turbine configurations from ``floris/floris/turbine_libary``, except any turbines defined in :py:attr:`exclude`. Args: which (list[str], optional): A list of which file names to include from loading. Defaults to []. exclude (list[str], optional): A list of file names to exclude from loading. Defaults to []. """ print(DEPRECATION_MESSAGE) include = [el for el in INTERNAL_LIBRARY.iterdir() if el.suffix in (".yaml", ".yml")] which = [INTERNAL_LIBRARY / el for el in which] if which != [] else include exclude = [INTERNAL_LIBRARY / el for el in exclude] include = set(which).intersection(include).difference(exclude) for fn in include: turbine_dict = load_yaml(fn) self.turbine_map.update({ turbine_dict["turbine_type"]: TurbineInterface.from_turbine_dict(turbine_dict) }) def load_external_library( self, library_path: str | Path, which: list[str] = [], exclude: list[str] = [], ) -> None: """Loads all the turbine configurations from :py:attr:`library_path`, except the file names defined in :py:attr:`exclude`, and adds each to ``turbine_map`` via a dictionary update. Args: library_path : str | Path The external turbine library that should be used for loading the turbines. which (list[str], optional): A list of which file names to include from loading. Defaults to []. exclude (list[str], optional): A list of file names to exclude from loading. Defaults to []. """ print(DEPRECATION_MESSAGE) library_path = Path(library_path).resolve() include = [el for el in library_path.iterdir() if el.suffix in (".yaml", ".yml")] which = [library_path / el for el in which] if which != [] else include exclude = [library_path / el for el in exclude] include = set(which).intersection(include).difference(exclude) for fn in include: turbine_dict = load_yaml(fn) self.turbine_map.update({ turbine_dict["turbine_type"]: TurbineInterface.from_turbine_dict(turbine_dict) }) def compute_power_curves( self, wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, ) -> None: """Computes the power curves for each turbine in ``turbine_map`` and sets the ``power_curves`` attribute. Args: wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. """ self.power_curves = { name: t.power_curve(wind_speeds) for name, t in self.turbine_map.items() } def compute_thrust_coefficient_curves( self, wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, ) -> None: """Computes the thrust curves for each turbine in ``turbine_map`` and sets the ``thrust_coefficient_curves`` attribute. Args: wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. """ self.thrust_coefficient_curves = { name: t.thrust_coefficient_curve(wind_speeds) for name, t in self.turbine_map.items() } def plot_power_curves( self, fig: plt.Figure | None = None, ax: plt.Axes | None = None, which: list[str] = [], exclude: list[str] = [], wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, fig_kwargs: dict | None = None, plot_kwargs: dict | None = None, legend_kwargs: dict | None = None, return_fig: bool = False, show: bool = False, ) -> None | tuple[plt.Figure, plt.Axes]: """Plots each power curve in ``turbine_map`` in a single plot. Args: fig (plt.figure, optional): A pre-made figure where the plot should exist. ax (plt.Axes, optional): A pre-initialized axes object that should be used for the plot. which (list[str], optional): A list of which turbine types/names to include. Defaults to []. exclude (list[str], optional): A list of turbine types/names names to exclude. Defaults to []. wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. fig_kwargs (dict, optional): Any keywords arguments to be passed to ``plt.Figure()``. Defaults to None. plot_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.plot()``. Defaults to None. legend_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.legend()``. Defaults to None. return_fig (bool, optional): Indicator if the ``Figure`` and ``Axes`` objects should be returned. Defaults to False. show (bool, optional): Indicator if the figure should be automatically displayed. Defaults to False. Returns: None | tuple[plt.Figure, plt.Axes]: None, if :py:attr:`return_fig` is False, otherwise a tuple of the Figure and Axes objects are returned. """ if self.power_curves == {} or wind_speeds is not None: self.compute_power_curves(wind_speeds=wind_speeds) which = [*self.turbine_map] if which == [] else which # Initialize kwargs if None fig_kwargs = {} if fig_kwargs is None else fig_kwargs plot_kwargs = {} if plot_kwargs is None else plot_kwargs legend_kwargs = {} if legend_kwargs is None else legend_kwargs # Set the figure defaults if none are provided if fig is None: fig_kwargs.setdefault("dpi", 200) fig_kwargs.setdefault("figsize", (4, 3)) fig = plt.figure(**fig_kwargs) if ax is None: ax = fig.add_subplot(111) min_windspeed = 0 max_windspeed = 0 min_power = 0 max_power = 0 for name, (ws, p) in self.power_curves.items(): if name in exclude or name not in which: continue if isinstance(p, dict): max_windspeed = max(ws.max(), max_windspeed) for k, _p in p.items(): max_power = max(_p.max(), max_power) label = f"{name} - {k}" ax.plot(ws, _p, label=label, linestyle="--", **plot_kwargs) else: max_power = max(p.max(), max_power) max_windspeed = max(ws.max(), max_windspeed) ax.plot(ws, p, label=name, **plot_kwargs) ax.grid() ax.set_axisbelow(True) ax.legend(**legend_kwargs) max_power = round_nearest(max_power, base=5) ax.set_xlim(min_windspeed, max_windspeed) ax.set_ylim(min_power, max_power) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Power (MW)") if return_fig: return fig, ax if show: fig.tight_layout() def plot_thrust_coefficient_curves( self, fig: plt.Figure | None = None, ax: plt.Axes | None = None, which: list[str] = [], exclude: list[str] = [], wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, fig_kwargs: dict | None = None, plot_kwargs: dict | None = None, legend_kwargs: dict | None = None, return_fig: bool = False, show: bool = False, ) -> None | tuple[plt.Figure, plt.Axes]: """Plots each thrust coefficient curve in ``turbine_map`` in a single plot. Args: fig (plt.figure, optional): A pre-made figure where the plot should exist. ax (plt.Axes, optional): A pre-initialized axes object that should be used for the plot. which (list[str], optional): A list of which turbine types/names to include. Defaults to []. exclude (list[str], optional): A list of turbine types/names names to exclude. Defaults to []. wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. fig_kwargs (dict, optional): Any keywords arguments to be passed to ``plt.Figure()``. Defaults to None. plot_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.plot()``. Defaults to None. plot_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.legend()``. Defaults to None. return_fig (bool, optional): Indicator if the ``Figure`` and ``Axes`` objects should be returned. Defaults to False. show (bool, optional): Indicator if the figure should be automatically displayed. Defaults to False. Returns: None | tuple[plt.Figure, plt.Axes]: None, if :py:attr:`return_fig` is False, otherwise a tuple of the Figure and Axes objects are returned. """ if self.thrust_coefficient_curves == {} or wind_speeds is None: self.compute_thrust_coefficient_curves(wind_speeds=wind_speeds) which = [*self.turbine_map] if which == [] else which # Initialize kwargs if None fig_kwargs = {} if fig_kwargs is None else fig_kwargs plot_kwargs = {} if plot_kwargs is None else plot_kwargs legend_kwargs = {} if legend_kwargs is None else legend_kwargs # Set the figure defaults if none are provided if fig is None: fig_kwargs.setdefault("dpi", 200) fig_kwargs.setdefault("figsize", (4, 3)) fig = plt.figure(**fig_kwargs) if ax is None: ax = fig.add_subplot(111) min_windspeed = 0 max_windspeed = 0 max_thrust = 0 for name, (ws, t) in self.thrust_coefficient_curves.items(): if name in exclude or name not in which: continue if isinstance(t, dict): max_windspeed = max(ws.max(), max_windspeed) for k, _t in t.items(): max_thrust = max(_t.max(), max_thrust) label = f"{name} - {k}" ax.plot(ws, _t, label=label, linestyle="--", **plot_kwargs) else: max_windspeed = max(ws.max(), max_windspeed) max_thrust = max(t.max(), max_thrust) ax.plot(ws, t, label=name, **plot_kwargs) ax.grid() ax.set_axisbelow(True) ax.legend(**legend_kwargs) ax.set_xlim(min_windspeed, max_windspeed) ax.set_ylim(0, round_nearest(max_thrust * 100, base=10) / 100) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Thrust Coefficient") if return_fig: return fig, ax if show: fig.tight_layout() def plot_rotor_diameters( self, fig: plt.Figure | None = None, ax: plt.Axes | None = None, which: list[str] = [], exclude: list[str] = [], fig_kwargs: dict | None = None, bar_kwargs: dict | None = None, return_fig: bool = False, show: bool = False, ) -> None | tuple[plt.Figure, plt.Axes]: """Plots a bar chart of rotor diameters for each turbine in ``turbine_map``. Args: fig (plt.figure, optional): A pre-made figure where the plot should exist. ax (plt.Axes, optional): A pre-initialized axes object that should be used for the plot. which (list[str], optional): A list of which turbine types/names to include. Defaults to []. exclude (list[str], optional): A list of turbine types/names names to exclude. Defaults to []. fig_kwargs (dict, optional): Any keywords arguments to be passed to ``plt.Figure()``. Defaults to None. bar_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.bar()``. Defaults to None. return_fig (bool, optional): Indicator if the ``Figure`` and ``Axes`` objects should be returned. Defaults to False. show (bool, optional): Indicator if the figure should be automatically displayed. Defaults to False. Returns: None | tuple[plt.Figure, plt.Axes]: None, if :py:attr:`return_fig` is False, otherwise a tuple of the Figure and Axes objects are returned. """ which = [*self.turbine_map] if which == [] else which # Initialize kwargs if None fig_kwargs = {} if fig_kwargs is None else fig_kwargs bar_kwargs = {} if bar_kwargs is None else bar_kwargs # Set the figure defaults if none are provided if fig is None: fig_kwargs.setdefault("dpi", 200) fig_kwargs.setdefault("figsize", (4, 3)) fig = plt.figure(**fig_kwargs) if ax is None: ax = fig.add_subplot(111) subset_map = { name: t for name, t in self.turbine_map.items() if name not in exclude or name in which } x = np.arange(len(subset_map)) y = [ti.turbine.rotor_diameter for ti in subset_map.values()] ix_sort = np.argsort(y) y_sorted = np.array(y)[ix_sort] ax.bar(x, y_sorted, **bar_kwargs) ax.grid(axis="y") ax.set_axisbelow(True) ax.set_xlim(-0.5, len(x) - 0.5) ax.set_ylim(0, round_nearest(max(y) / 10, base=5) * 10) ax.set_xticks(x) ax.set_xticklabels(np.array([*subset_map])[ix_sort], rotation=30, ha="right") ax.set_ylabel("Rotor Diameter (m)") if return_fig: return fig, ax if show: fig.tight_layout() def plot_hub_heights( self, fig: plt.Figure | None = None, ax: plt.Axes | None = None, which: list[str] = [], exclude: list[str] = [], fig_kwargs: dict | None = None, bar_kwargs: dict | None = None, return_fig: bool = False, show: bool = False, ) -> None | tuple[plt.Figure, plt.Axes]: """Plots a bar chart of hub heights for each turbine in ``turbine_map``. Args: fig (plt.figure, optional): A pre-made figure where the plot should exist. ax (plt.Axes, optional): A pre-initialized axes object that should be used for the plot. which (list[str], optional): A list of which turbine types/names to include. Defaults to []. exclude (list[str], optional): A list of turbine types/names names to exclude. Defaults to []. fig_kwargs (dict, optional): Any keywords arguments to be passed to ``plt.Figure()``. Defaults to None. bar_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.bar()``. Defaults to None. return_fig (bool, optional): Indicator if the ``Figure`` and ``Axes`` objects should be returned. Defaults to False. show (bool, optional): Indicator if the figure should be automatically displayed. Defaults to False. Returns: None | tuple[plt.Figure, plt.Axes]: None, if :py:attr:`return_fig` is False, otherwise a tuple of the Figure and Axes objects are returned. """ which = [*self.turbine_map] if which == [] else which # Initialize kwargs if None fig_kwargs = {} if fig_kwargs is None else fig_kwargs bar_kwargs = {} if bar_kwargs is None else bar_kwargs # Set the figure defaults if none are provided if fig is None: fig_kwargs.setdefault("dpi", 200) fig_kwargs.setdefault("figsize", (4, 3)) fig = plt.figure(**fig_kwargs) if ax is None: ax = fig.add_subplot(111) subset_map = { name: t for name, t in self.turbine_map.items() if name not in exclude or name in which } x = np.arange(len(subset_map)) y = [ti.turbine.hub_height for ti in subset_map.values()] ix_sort = np.argsort(y) y_sorted = np.array(y)[ix_sort] ax.bar(x, y_sorted, **bar_kwargs) ax.grid(axis="y") ax.set_axisbelow(True) ax.set_xlim(-0.5, len(x) - 0.5) ax.set_ylim(0, round_nearest(max(y) / 10, base=5) * 10) ax.set_xticks(x) ax.set_xticklabels(np.array([*subset_map])[ix_sort], rotation=30, ha="right") ax.set_ylabel("Hub Height (m)") if return_fig: return fig, ax if show: fig.tight_layout() def plot_comparison( self, which: list[str] = [], exclude: list[str] = [], wind_speeds: NDArrayFloat = DEFAULT_WIND_SPEEDS, fig_kwargs: dict | None = None, plot_kwargs: dict | None = None, bar_kwargs: dict | None = None, legend_kwargs: dict | None = None, return_fig: bool = False ) -> None | tuple[plt.Figure, list[plt.Axes]]: """Plots each thrust curve in ``turbine_map`` in a single plot. Args: which (list[str], optional): A list of which turbine types/names to include. Defaults to []. exclude (list[str], optional): A list of turbine types/names names to exclude. Defaults to []. wind_speeds (NDArrayFloat, optional): A 1-D array of wind speeds, in m/s. Defaults to 0 m/s -> 40 m/s, every 0.5 m/s. fig_kwargs (dict, optional): Any keywords arguments to be passed to ``plt.Figure()``. Defaults to None. plot_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.plot()``. Defaults to None. bar_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.bar()``. Defaults to None. legend_kwargs (dict, optional): Any keyword arguments to be passed to ``plt.legend()``. Defaults to None. return_fig (bool, optional): Indicator if the ``Figure`` and ``Axes`` objects should be returned. Defaults to False. Returns: None | tuple[plt.Figure, list[plt.Axes]]: None, if :py:attr:`return_fig` is False, otherwise a tuple of the Figure and Axes objects are returned. """ # Initialize kwargs if None fig_kwargs = {} if fig_kwargs is None else fig_kwargs plot_kwargs = {} if plot_kwargs is None else plot_kwargs bar_kwargs = {} if bar_kwargs is None else bar_kwargs legend_kwargs = {} if legend_kwargs is None else legend_kwargs # Set the figure defaults if none are provided fig_kwargs.setdefault("dpi", 200) fig_kwargs.setdefault("figsize", (6, 5)) legend_kwargs.setdefault("fontsize", 6) fig = plt.figure(**fig_kwargs) ax1 = fig.add_subplot(321) ax2 = fig.add_subplot(322) ax3 = fig.add_subplot(323) ax4 = fig.add_subplot(324) ax_list = [ax1, ax2, ax3, ax4] self.plot_power_curves( fig, ax1, which=which, exclude=exclude, wind_speeds=wind_speeds, plot_kwargs=plot_kwargs, ) self.plot_thrust_coefficient_curves( fig, ax3, which=which, exclude=exclude, wind_speeds=wind_speeds, plot_kwargs=plot_kwargs, ) self.plot_rotor_diameters(fig, ax2, which=which, exclude=exclude, bar_kwargs=bar_kwargs) self.plot_hub_heights(fig, ax4, which=which, bar_kwargs=bar_kwargs) for ax in ax_list: ax.tick_params(axis='both', which='major', labelsize=7) ax.xaxis.label.set_size(7) ax.yaxis.label.set_size(8) for ax in (ax1, ax3): ax.legend(**legend_kwargs) if return_fig: return fig, ax_list fig.tight_layout() ================================================ FILE: floris/turbine_library/turbine_utilities.py ================================================ from collections.abc import Iterable import numpy as np import yaml def build_cosine_loss_turbine_dict( turbine_data_dict, turbine_name, file_name=None, generator_efficiency=None, hub_height=90.0, cosine_loss_exponent_yaw=1.88, cosine_loss_exponent_tilt=1.88, rotor_diameter=125.88, TSR=8.0, ref_air_density=1.225, ref_tilt=5.0 ): """ Tool for formatting a full turbine dict from data formatted as a dictionary. Default value for turbine physical parameters are from the NREL 5MW reference wind turbine. Returns a turbine dictionary object as expected by FLORIS. Optionally, prints the dictionary to a yaml to be included in a FLORIS wake model yaml. turbine_data is a dictionary that contains keys specifying the turbine power and thrust as a function of wind speed. The following keys are possible: - wind_speed [m/s] - power [kW] - power_coefficient [-] - thrust [kN] - thrust_coefficient [-] Of these, wind_speed is required. One of power and power_coefficient must be specified; and one of thrust and thrust_coefficient must be specified. If both (absolute) and _coefficient versions are specified, the (absolute) power will be used along with the thrust_coefficient, with the other entries ignored. Args: turbine_data_dict (dict): Dictionary containing performance of the wind turbine as a function of wind speed. Described in more detail above. turbine_name (string): Name of the turbine, which will be used for the turbine_type field as well as the filename. file_name (): Name for the produced yaml, including possibly path. Defaults to None, in which case no yaml is written. generator_efficiency (float): Generator efficiency [-]. Unused if power is specified in absolute terms in the turbine_data_dict. Must be specified if power not specified and power_coefficient specified instead. Defaults to None. hub_height (float): Hub height [m]. Defaults to 90.0. cosine_loss_exponent_yaw (float): Cosine exponent for power loss to yaw [-]. Defaults to 1.88. cosine_loss_exponent_tilt (float): Cosine exponent for thrust loss to yaw [-]. Defaults to 1.88. rotor_diameter (float). Rotor diameter [m]. Defaults to 126.0. TSR (float). Turbine optimal tip-speed ratio [-]. Defaults to 8.0. ref_air_density (float). Air density used to specify power and thrust curves [kg/m^3]. Defaults to 1.225. ref_tilt (float). Rotor tilt (due to shaft tilt and/or platform tilt) used when defining the power and thrust curves [deg]. Defaults to 5.0. Returns: turbine_dict (dict): Formatted turbine dictionary as expected by FLORIS. """ # Check that necessary columns are specified if "wind_speed" not in turbine_data_dict: raise KeyError("wind_speed column must be specified.") u = np.array(turbine_data_dict["wind_speed"]) A = np.pi * rotor_diameter**2/4 # Construct the Cp curve if "power" in turbine_data_dict: if "power_coefficient" in turbine_data_dict: print( "Found both power and power_coefficient. " "Ignoring power_coefficient." ) p = np.array(turbine_data_dict["power"]) elif "power_coefficient" in turbine_data_dict: if generator_efficiency is None: raise KeyError( "generator_efficiency must be specified to convert power_coefficient to power." ) Cp = np.array(turbine_data_dict["power_coefficient"]) if _find_nearest_value_for_wind_speed(Cp, u, 10) > 16.0/27.0 or \ _find_nearest_value_for_wind_speed(Cp, u, 10) < 0.0: print( "Unusual power coefficient detected. Check that power coefficients" "are physical." ) validity_mask = (Cp != 0) | (u != 0) p = np.zeros_like(Cp, dtype=float) p[validity_mask] = ( Cp[validity_mask] * 0.5 * ref_air_density * A * generator_efficiency * u[validity_mask]**3 / 1000 ) else: raise KeyError( "Either power or power_coefficient must be specified." ) # Construct Ct curve if "thrust_coefficient" in turbine_data_dict: if "thrust" in turbine_data_dict: print( "Found both thrust and thrust_coefficient. " "Ignoring thrust." ) Ct = np.array(turbine_data_dict["thrust_coefficient"]) elif "thrust" in turbine_data_dict: T = np.array(turbine_data_dict["thrust"]) if _find_nearest_value_for_wind_speed(T, u, 10) > 3000 or \ _find_nearest_value_for_wind_speed(T, u, 10) < 100: print( "Unusual thrust value detected. Please check that thrust", "is specified in kN." ) validity_mask = (T != 0) | (u != 0) Ct = np.zeros_like(T) Ct[validity_mask] = (T[validity_mask]*1000)/\ (0.5*ref_air_density*A*u[validity_mask]**2) else: raise KeyError( "Either thrust or thrust_coefficient must be specified." ) # Build the turbine dict power_thrust_dict = { "ref_air_density": ref_air_density, "ref_tilt": ref_tilt, "cosine_loss_exponent_yaw": cosine_loss_exponent_yaw, "cosine_loss_exponent_tilt": cosine_loss_exponent_tilt, "wind_speed": u.tolist(), "power": p.tolist(), "thrust_coefficient": Ct.tolist() } turbine_dict = { "turbine_type": turbine_name, "hub_height": hub_height, "rotor_diameter": rotor_diameter, "TSR": TSR, "operation_model": "cosine-loss", "power_thrust_table": power_thrust_dict } # Create yaml file if file_name is not None: yaml.dump( turbine_dict, open(file_name, "w"), sort_keys=False ) print(file_name, "created.") return turbine_dict def _find_nearest_value_for_wind_speed(test_vals, ws_vals, ws): errs = np.absolute(ws_vals-ws) idx = errs.argmin() return test_vals[idx] def check_smooth_power_curve(power, tolerance=0.001): """ Check whether there are "wiggles" in the power signal. """ if power[-1] < 0.95*max(power): # Cut-out or shutdown included expected_changes = 2 else: # Shutdown appears not to be included expected_changes = 1 dirs = np.where( np.abs(np.diff(power)) > tolerance, np.sign(np.diff(power)), np.zeros(len(power)-1) ) dir_changes = np.sum(np.abs(np.diff(dirs))) is_smooth = dir_changes <= expected_changes return is_smooth ================================================ FILE: floris/type_dec.py ================================================ import copy import inspect from pathlib import Path from typing import ( Any, Callable, Iterable, Tuple, Union, ) import attrs import numpy as np import numpy.typing as npt from attrs import Attribute, define ### Define general data types used throughout floris_float_type = np.float64 NDArrayFloat = npt.NDArray[floris_float_type] NDArrayInt = npt.NDArray[np.int_] NDArrayFilter = Union[npt.NDArray[np.int_], npt.NDArray[np.bool_]] NDArrayObject = npt.NDArray[np.object_] NDArrayBool = npt.NDArray[np.bool_] NDArrayStr = npt.NDArray[np.str_] ### Custom callables for attrs objects and functions def floris_array_converter(data: Iterable) -> np.ndarray: """ For a given iterable, convert the data to a numpy array and cast to `floris_float_type`. If the input is a scalar, np.array() creates a 0-dimensional array, and this is not supported in FLORIS so this function raises an error. Args: data (Iterable): The input data to be converted to a Numpy array. Raises: TypeError: Raises if the input data is not iterable. TypeError: Raises if the input data cannot be converted to a Numpy array. Returns: np.ndarray: data converted to a Numpy array and cast to `floris_float_type`. """ try: iter(data) except TypeError as e: raise TypeError(e.args[0] + f". Data given: {data}") try: a = np.array(data, dtype=floris_float_type) except (TypeError, ValueError) as e: raise TypeError(e.args[0] + f". Data given: {data}") return a def floris_numeric_dict_converter(data: dict) -> dict: """ For the given dictionary, convert all the values to a numeric type. If a value is a scalar, it will be converted to a float. If a value is an iterable, it will be converted to a Numpy array and cast to `floris_float_type`. If a value is not a numeric type, a TypeError will be raised. Args: data (dict): Dictionary of data to be converted to a numeric type. Returns: dict: Dictionary with the same keys and all values converted to a numeric type. """ converted_dict = copy.deepcopy(data) # deepcopy -> data is a container and passed by reference for k, v in data.items(): try: iter(v) except TypeError: # Not iterable so try to cast to float converted_dict[k] = float(v) else: # Iterable so convert to Numpy array converted_dict[k] = floris_array_converter(v) return converted_dict # def array_field(**kwargs) -> Callable: # """ # A wrapper for the :py:func:`attr.field` function that converts the input to a Numpy array, # adds a comparison function specific to Numpy arrays, and passes through all additional # keyword arguments. # """ # return field( # converter=floris_array_converter, # eq=cmp_using(eq=np.array_equal), # **kwargs # ) def _attr_serializer(inst: type, field: Attribute, value: Any): if isinstance(value, np.ndarray): return value.tolist() return value def _attr_floris_filter(inst: Attribute, value: Any) -> bool: if inst.init is False: return False if value is None: return False # This is removed to support initializing FLORIS with default values: # - defaults added in https://github.com/NatLabRockies/floris/pull/1040 # - bug fix in https://github.com/NatLabRockies/floris/pull/1061 # When Core is exported to a dict in _reinitialize, this filter removes empty arrays. # For init with defaults, this results in FlowField losing the wind speed, wind direction and TI # arrays if they weren't provided in the .set function. # if isinstance(value, np.ndarray): # if value.size == 0: # return False return True def iter_validator(iter_type, item_types: Union[Any, Tuple[Any]]) -> Callable: """ Helper function to generate iterable validators that will reduce the amount of boilerplate code. Args: iter_type (iterable): The type of iterable object that should be validated. item_types (Union[Any, Tuple[Any]]): The type or types of acceptable item types. Returns: Callable: The attr.validators.deep_iterable iterable and instance validator. """ validator = attrs.validators.deep_iterable( member_validator=attrs.validators.instance_of(item_types), iterable_validator=attrs.validators.instance_of(iter_type), ) return validator def convert_to_path(fn: str | Path) -> Path: """ Converts an input string or ``pathlib.Path`` object to a fully resolved ``pathlib.Path`` object. If the input is a string, it is converted to a pathlib.Path object. The function then checks if the path exists as an absolute path, a relative path from the script, or a relative path from the system location. If the path does not exist in any of these locations, a FileExistsError is raised. Args: fn (str | Path): The user input file path or file name. Raises: FileExistsError: Raised if :py:attr:`fn` is not able to be found as an absolute path, nor as a relative path. TypeError: Raised if :py:attr:`fn` is neither a :py:obj:`str`, nor a :py:obj:`pathlib.Path`. Returns: Path: A resolved pathlib.Path object. """ if isinstance(fn, str): fn = Path(fn) # Get the base path from where the analysis script was run to determine the relative # path from which `fn` might be based. [1] is where a direct call to this function will be # located (e.g., testing via pytest), and [-1] is where a direct call to the function via an # analysis script will be located (e.g., running an example). base_fn_script = Path(inspect.stack()[-1].filename).resolve().parent base_fn_sys = Path(inspect.stack()[1].filename).resolve().parent if isinstance(fn, Path): absolute_fn = fn.resolve() relative_fn_script = (base_fn_script / fn).resolve() relative_fn_sys = (base_fn_sys / fn).resolve() if absolute_fn.exists(): return absolute_fn if relative_fn_script.exists(): return relative_fn_script if relative_fn_sys.exists(): return relative_fn_sys raise FileExistsError( f"{fn} could not be found as either a\n" f" - relative file path from a script: {relative_fn_script}\n" f" - relative file path from a system location: {relative_fn_sys}\n" f" - or absolute file path: {absolute_fn}" ) raise TypeError(f"The passed input: {fn} could not be converted to a pathlib.Path object") @define class FromDictMixin: """ A Mixin class to allow for kwargs overloading when a data class doesn't have a specific parameter defined. This allows passing of larger dictionaries to a data class without throwing an error. """ @classmethod def from_dict(cls, data: dict): """Maps a data dictionary to an `attr`-defined class. TODO: Add an error to ensure that either none or all the parameters are passed in Args: data : dict The data dictionary to be mapped. Returns: cls The `attr`-defined class. """ # Make a copy of the input dict to prevent any side effects data = copy.deepcopy(data) # Check for any inputs that aren't part of the class definition class_attr_names = [a.name for a in cls.__attrs_attrs__] extra_args = [d for d in data if d not in class_attr_names] if len(extra_args): raise AttributeError( f"The initialization for {cls.__name__} was given extraneous inputs: {extra_args}" ) kwargs = {a.name: data[a.name] for a in cls.__attrs_attrs__ if a.name in data and a.init} # Map the inputs must be provided: 1) must be initialized, 2) no default value defined required_inputs = [ a.name for a in cls.__attrs_attrs__ if a.init and a.default is attrs.NOTHING ] undefined = sorted(set(required_inputs) - set(kwargs)) if undefined: raise AttributeError( f"The class definition for {cls.__name__} " f"is missing the following inputs: {undefined}" ) return cls(**kwargs) def as_dict(self) -> dict: """Creates a YAML friendly dictionary that can be saved for future reloading. This dictionary will contain only `Python` types that can later be converted to their proper formats. See `_attr_floris_filter` for detail on which attributes are removed from the export. Returns: dict: All key, value pairs required for class recreation. """ return attrs.asdict(self, filter=_attr_floris_filter, value_serializer=_attr_serializer) # Avoids constant redefinition of the same attr.ib properties for model attributes # from functools import partial, update_wrapper # def is_default(instance, attribute, value): # if attribute.default != value: # raise ValueError(f"{attribute.name} should never be set manually.") # model_attrib = partial(field, on_setattr=attrs.setters.frozen, validator=is_default) # update_wrapper(model_attrib, field) # float_attrib = partial( # attr.ib, # converter=float, # on_setattr=(attr.setters.convert, attr.setters.validate), # type: ignore # kw_only=True, # ) # update_wrapper(float_attrib, attr.ib) # bool_attrib = partial( # attr.ib, # converter=bool, # on_setattr=(attr.setters.convert, attr.setters.validate), # type: ignore # kw_only=True, # ) # update_wrapper(bool_attrib, attr.ib) # int_attrib = partial( # attr.ib, # converter=int, # on_setattr=(attr.setters.convert, attr.setters.validate), # type: ignore # kw_only=True, # ) # update_wrapper(int_attrib, attr.ib) ================================================ FILE: floris/uncertain_floris_model.py ================================================ from pathlib import Path from typing import ( Any, List, Optional, ) import numpy as np from floris import FlorisModel from floris.core import average_velocity, State from floris.logging_manager import LoggingManager from floris.par_floris_model import ParFlorisModel from floris.type_dec import ( floris_array_converter, NDArrayBool, NDArrayFloat, ) from floris.utilities import ( is_all_scalar_dict, nested_get, nested_set, wrap_180, ) from floris.wind_data import ( TimeSeries, WindDataBase, WindRose, WindRoseWRG, WindTIRose, ) class UncertainFlorisModel(LoggingManager): """ An interface for handling uncertainty in wind farm simulations. This class contains a FlorisModel object and adds functionality to handle uncertainty in wind direction. It is designed to be used similarly to FlorisModel. In the model, the turbine powers are computed for a set of expanded wind conditions, given by wd_sample_points, and then the powers are computed as a gaussian blend of these expanded conditions. To reduce computational costs, the wind directions, wind speeds, turbulence intensities, yaw angles, and power setpoints are rounded to specified resolutions. Only unique conditions from within the expanded set of conditions are run. Args: configuration (dict | str | Path | FlorisModel | ParFlorisModel): The configuration for the wind farm. This can be a dictionary, a path to a yaml file, or a FlorisModel or ParFlorisModel object. If dict, str or Path, a new FlorisModel object is created. If a FlorisModel or ParFlorisModel object a copy of the object is made. wd_resolution (float, optional): The resolution of wind direction for generating gaussian blends, in degrees. Defaults to 1.0. ws_resolution (float, optional): The resolution of wind speed, in m/s. Defaults to 1.0. ti_resolution (float, optional): The resolution of turbulence intensity. Defaults to 0.01. yaw_resolution (float, optional): The resolution of yaw angle, in degrees. Defaults to 1.0. power_setpoint_resolution (int, optional): The resolution of power setpoints, in kW. Defaults to 100. wd_std (float, optional): The standard deviation of wind direction. Defaults to 3.0. wd_sample_points (list[float], optional): The sample points for wind direction. If not provided, defaults to [-2 * wd_std, -1 * wd_std, 0, wd_std, 2 * wd_std]. fix_yaw_to_nominal_direction (bool, optional): Fix the yaw angle to the nominal direction? When False, the yaw misalignment is the same across the sampled wind directions. When True, the turbine orientation is fixed to the nominal wind direction such that the yaw misalignment changes depending on the sampled wind direction. Defaults to False. verbose (bool, optional): Verbosity flag for printing messages. Defaults to False. """ def __init__( self, configuration: dict | str | Path | FlorisModel | ParFlorisModel, wd_resolution=1.0, # Degree ws_resolution=1.0, # m/s ti_resolution=0.01, yaw_resolution=1.0, # Degree power_setpoint_resolution=100, # kW awc_amplitude_resolution=0.1, # Deg wd_std=3.0, wd_sample_points=None, fix_yaw_to_nominal_direction=False, verbose=False, ): # Check validity of inputs if wd_std <= 0: raise ValueError("wd_std must be strictly greater than 0.") # Save these inputs self.wd_resolution = wd_resolution self.ws_resolution = ws_resolution self.ti_resolution = ti_resolution self.yaw_resolution = yaw_resolution self.power_setpoint_resolution = power_setpoint_resolution self.awc_amplitude_resolution = awc_amplitude_resolution self.wd_std = wd_std self.fix_yaw_to_nominal_direction = fix_yaw_to_nominal_direction self.verbose = verbose # If wd_sample_points, default to 1 and 2 std if wd_sample_points is None: wd_sample_points = [-2 * wd_std, -1 * wd_std, 0, wd_std, 2 * wd_std] self.wd_sample_points = wd_sample_points self.n_sample_points = len(self.wd_sample_points) # Get the weights self.weights = self._get_weights(self.wd_std, self.wd_sample_points) # Instantiate the un-expanded FlorisModel if isinstance(configuration, (FlorisModel, ParFlorisModel)): self.fmodel_unexpanded = configuration.copy() # Copy over any control setpoints, wind data, if not already done. self.fmodel_unexpanded.set( yaw_angles=configuration.core.farm.yaw_angles, power_setpoints=configuration.core.farm.power_setpoints, awc_modes=configuration.core.farm.awc_modes, awc_amplitudes=configuration.core.farm.awc_amplitudes, awc_frequencies=configuration.core.farm.awc_frequencies, wind_data=configuration.wind_data, ) elif isinstance(configuration, (dict, str, Path)): self.fmodel_unexpanded = FlorisModel(configuration) else: raise ValueError( "configuration must be a FlorisModel, ParFlorisModel, dict, str, or Path" ) # Call set at this point with no arguments so ready to run self.set() def set( self, **kwargs, ): """ Set the wind farm conditions in the UncertainFlorisModel. See FlorisModel.set() for details of the contents of kwargs. Args: **kwargs: The wind farm conditions to set. """ # Call the nominal set function self.fmodel_unexpanded.set(**kwargs) self._set_uncertain() def _set_uncertain( self, ): """ Sets the underlying wind direction (wd), wind speed (ws), turbulence intensity (ti), yaw angle, and power setpoint for unique conditions, accounting for uncertainties. """ # Grab the unexpanded values of all arrays # These original dimensions are what is returned self.wind_directions_unexpanded = self.fmodel_unexpanded.core.flow_field.wind_directions self.wind_speeds_unexpanded = self.fmodel_unexpanded.core.flow_field.wind_speeds self.turbulence_intensities_unexpanded = ( self.fmodel_unexpanded.core.flow_field.turbulence_intensities ) self.yaw_angles_unexpanded = self.fmodel_unexpanded.core.farm.yaw_angles self.power_setpoints_unexpanded = self.fmodel_unexpanded.core.farm.power_setpoints self.awc_amplitudes_unexpanded = self.fmodel_unexpanded.core.farm.awc_amplitudes self.n_unexpanded = len(self.wind_directions_unexpanded) # Combine into the complete unexpanded_inputs self.unexpanded_inputs = np.hstack( ( self.wind_directions_unexpanded[:, np.newaxis], self.wind_speeds_unexpanded[:, np.newaxis], self.turbulence_intensities_unexpanded[:, np.newaxis], self.yaw_angles_unexpanded, self.power_setpoints_unexpanded, self.awc_amplitudes_unexpanded, ) ) # Get the rounded inputs self.rounded_inputs = self._get_rounded_inputs( self.unexpanded_inputs, self.wd_resolution, self.ws_resolution, self.ti_resolution, self.yaw_resolution, self.power_setpoint_resolution, self.awc_amplitude_resolution, ) # Get the expanded inputs self._expanded_wind_directions = self._expand_wind_directions( self.rounded_inputs, self.wd_sample_points, self.fix_yaw_to_nominal_direction, self.fmodel_unexpanded.core.farm.n_turbines, ) self.n_expanded = self._expanded_wind_directions.shape[0] # Get the unique inputs self.unique_inputs, self.map_to_expanded_inputs = self._get_unique_inputs( self._expanded_wind_directions ) self.n_unique = self.unique_inputs.shape[0] # Display info on sizes if self.verbose: print(f"Original num rows: {self.n_unexpanded}") print(f"Expanded num rows: {self.n_expanded}") print(f"Unique num rows: {self.n_unique}") # Initiate the expanded FlorisModel self.fmodel_expanded = self.fmodel_unexpanded.copy() # Now set the underlying wd/ws/ti/yaw/setpoint to check only the unique conditions self.fmodel_expanded.set( wind_directions=self.unique_inputs[:, 0], wind_speeds=self.unique_inputs[:, 1], turbulence_intensities=self.unique_inputs[:, 2], yaw_angles=self.unique_inputs[:, 3 : 3 + self.fmodel_unexpanded.core.farm.n_turbines], power_setpoints=self.unique_inputs[ :, 3 + self.fmodel_unexpanded.core.farm.n_turbines : 3 + 2 * self.fmodel_unexpanded.core.farm.n_turbines, ], awc_amplitudes=self.unique_inputs[ :, 3 + 2 * self.fmodel_unexpanded.core.farm.n_turbines : 3 + 3 * self.fmodel_unexpanded.core.farm.n_turbines, ], ) def reset_operation(self): """ Reset the operation of the underlying FlorisModel object. """ self.fmodel_unexpanded.set( wind_directions=self.wind_directions_unexpanded, wind_speeds=self.wind_speeds_unexpanded, turbulence_intensities=self.turbulence_intensities_unexpanded, ) self.fmodel_unexpanded.reset_operation() # Calling set_uncertain again to reset the expanded FlorisModel self._set_uncertain() def run(self): """ Run the simulation in the underlying FlorisModel object. """ self.fmodel_expanded.run() def run_no_wake(self): """ Run the simulation in the underlying FlorisModel object without wakes. """ self.fmodel_expanded.run_no_wake() def _get_turbine_powers(self): """Calculates the power at each turbine in the wind farm. This method calculates the power at each turbine in the wind farm, considering the underlying turbine powers and applying a weighted sum to handle uncertainty. Returns: NDArrayFloat: An array containing the powers at each turbine for each findex. """ # Pass to off-class function result = map_turbine_values_uncertain( unique_turbine_values=self.fmodel_expanded._get_turbine_powers(), map_to_expanded_inputs=self.map_to_expanded_inputs, weights=self.weights, n_unexpanded=self.n_unexpanded, n_sample_points=self.n_sample_points, n_turbines=self.fmodel_unexpanded.core.farm.n_turbines, ) return result def get_turbine_powers(self): """ Calculate the power at each turbine in the wind farm. If WindRose or WindTIRose is passed in, result is reshaped to match Returns: NDArrayFloat: An array containing the powers at each turbine for each findex. """ turbine_powers = self._get_turbine_powers() if self.fmodel_unexpanded.wind_data is not None: if isinstance(self.fmodel_unexpanded.wind_data, (WindRose, WindRoseWRG)): turbine_powers_rose = np.full( ( len(self.fmodel_unexpanded.wind_data.wd_flat), self.fmodel_unexpanded.core.farm.n_turbines, ), np.nan, ) turbine_powers_rose[self.fmodel_unexpanded.wind_data.non_zero_freq_mask, :] = ( turbine_powers ) turbine_powers = turbine_powers_rose.reshape( len(self.fmodel_unexpanded.wind_data.wind_directions), len(self.fmodel_unexpanded.wind_data.wind_speeds), self.fmodel_unexpanded.core.farm.n_turbines, ) elif type(self.fmodel_unexpanded.wind_data) is WindTIRose: turbine_powers_rose = np.full( ( len(self.fmodel_unexpanded.wind_data.wd_flat), self.fmodel_unexpanded.core.farm.n_turbines, ), np.nan, ) turbine_powers_rose[self.fmodel_unexpanded.wind_data.non_zero_freq_mask, :] = ( turbine_powers ) turbine_powers = turbine_powers_rose.reshape( len(self.fmodel_unexpanded.wind_data.wind_directions), len(self.fmodel_unexpanded.wind_data.wind_speeds), len(self.fmodel_unexpanded.wind_data.turbulence_intensities), self.fmodel_unexpanded.core.farm.n_turbines, ) return turbine_powers def get_expected_turbine_powers(self, freq=None): """ Compute the expected (mean) power of each turbine. Args: freq (NDArrayFloat): NumPy array with shape with the frequencies of each wind direction and wind speed combination. freq is either a 1D array, in which case the same frequencies are used for all turbines, or a 2D array with shape equal to (n_findex, n_turbines), in which case each turbine has a unique set of frequencies (this is the case for example using WindRoseByTurbine). These frequencies should typically sum across rows up to 1.0 and are used to weigh the wind farm power for every condition in calculating the wind farm's AEP. Defaults to None. If None and a WindData object was supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). """ turbine_powers = self._get_turbine_powers() if freq is None: if self.fmodel_unexpanded.wind_data is None: freq = np.array([1.0 / self.fmodel_unexpanded.core.flow_field.n_findex]) else: freq = self.fmodel_unexpanded.wind_data.unpack_freq() # If freq is 2d, then use the per turbine frequencies if len(np.shape(freq)) == 2: return np.nansum(np.multiply(freq, turbine_powers), axis=0) else: return np.nansum(np.multiply(freq.reshape(-1, 1), turbine_powers), axis=0) def _get_weighted_turbine_powers( self, turbine_weights=None, use_turbulence_correction=False, ): if use_turbulence_correction: raise NotImplementedError( "Turbulence correction is not yet implemented in the power calculation." ) # Confirm run() has been run on the expanded fmodel if self.fmodel_expanded.core.state is not State.USED: raise RuntimeError( "Can't run function `FlorisModel.get_farm_power` without " "first running `FlorisModel.run`." ) if turbine_weights is None: # Default to equal weighing of all turbines when turbine_weights is None turbine_weights = np.ones( ( self.fmodel_unexpanded.core.flow_field.n_findex, self.fmodel_unexpanded.core.farm.n_turbines, ) ) elif len(np.shape(turbine_weights)) == 1: # Deal with situation when 1D array is provided turbine_weights = np.tile( turbine_weights, (self.fmodel_unexpanded.core.flow_field.n_findex, 1), ) # Calculate all turbine powers and apply weights turbine_powers = self._get_turbine_powers() turbine_powers = np.multiply(turbine_weights, turbine_powers) return turbine_powers def _get_farm_power( self, turbine_weights=None, use_turbulence_correction=False, ): """ Report wind plant power from instance of floris with uncertainty. Args: turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. use_turbulence_correction: (bool, optional): When True uses a turbulence parameter to adjust power output calculations. Defaults to False. Not currently implemented. Returns: float: Sum of wind turbine powers in W. """ turbine_powers = self._get_weighted_turbine_powers( turbine_weights=turbine_weights, use_turbulence_correction=use_turbulence_correction ) return np.sum(turbine_powers, axis=1) def get_farm_power( self, turbine_weights=None, use_turbulence_correction=False, ): """ Report wind plant power from instance of floris. Optionally includes uncertainty in wind direction and yaw position when determining power. Uncertainty is included by computing the mean wind farm power for a distribution of wind direction and yaw position deviations from the original wind direction and yaw angles. Args: turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. use_turbulence_correction: (bool, optional): When True uses a turbulence parameter to adjust power output calculations. Defaults to False. Not currently implemented. Returns: float: Sum of wind turbine powers in W. """ farm_power = self._get_farm_power(turbine_weights, use_turbulence_correction) if self.fmodel_unexpanded.wind_data is not None: if isinstance(self.fmodel_unexpanded.wind_data, (WindRose, WindRoseWRG)): farm_power_rose = np.full(len(self.fmodel_unexpanded.wind_data.wd_flat), np.nan) farm_power_rose[self.fmodel_unexpanded.wind_data.non_zero_freq_mask] = farm_power farm_power = farm_power_rose.reshape( len(self.fmodel_unexpanded.wind_data.wind_directions), len(self.fmodel_unexpanded.wind_data.wind_speeds), ) elif type(self.fmodel_unexpanded.wind_data) is WindTIRose: farm_power_rose = np.full(len(self.fmodel_unexpanded.wind_data.wd_flat), np.nan) farm_power_rose[self.fmodel_unexpanded.wind_data.non_zero_freq_mask] = farm_power farm_power = farm_power_rose.reshape( len(self.fmodel_unexpanded.wind_data.wind_directions), len(self.fmodel_unexpanded.wind_data.wind_speeds), len(self.fmodel_unexpanded.wind_data.turbulence_intensities), ) return farm_power def get_expected_farm_power( self, freq=None, turbine_weights=None, ) -> float: """ Compute the expected (mean) power of the wind farm. Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind direction and wind speed combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm power for every condition in calculating the wind farm's AEP. Defaults to None. If None and a WindData object was supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. """ if freq is None: if self.fmodel_unexpanded.wind_data is None: freq = np.array([1.0 / self.fmodel_unexpanded.core.flow_field.n_findex]) else: freq = self.fmodel_unexpanded.wind_data.unpack_freq() farm_power = self._get_farm_power(turbine_weights=turbine_weights) # If freq is 1d if len(np.shape(freq)) == 1: farm_power = self._get_farm_power(turbine_weights=turbine_weights) return np.nansum(np.multiply(freq, farm_power)) else: weighted_turbine_powers = self._get_weighted_turbine_powers( turbine_weights=turbine_weights, ) return np.nansum(np.multiply(freq, weighted_turbine_powers)) def get_farm_AEP( self, freq=None, turbine_weights=None, hours_per_year=8760, ) -> float: """ Estimate annual energy production (AEP) for distributions of wind speed, wind direction, frequency of occurrence, and yaw offset. Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind direction and wind speed combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm power for every condition in calculating the wind farm's AEP. Defaults to None. If None and a WindData object was supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed. turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the power production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the objective function. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. hours_per_year (float, optional): Number of hours in a year. Defaults to 365 * 24. Returns: float: The Annual Energy Production (AEP) for the wind farm in watt-hours. """ if freq is None and not isinstance( self.fmodel_unexpanded.wind_data, (WindRose, WindRoseWRG, WindTIRose) ): self.logger.warning( "Computing AEP with uniform frequencies. Results results may not reflect annual " "operation." ) return ( self.get_expected_farm_power(freq=freq, turbine_weights=turbine_weights) * hours_per_year ) def get_expected_farm_value( self, freq=None, values=None, turbine_weights=None, ) -> float: """ Compute the expected (mean) value produced by the wind farm. This is computed by multiplying the wind farm power for each wind condition by the corresponding value of the power generated (e.g., electricity market price per unit of energy), then weighting by frequency and summing over all conditions. Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind condition combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm value for every condition in calculating the wind farm's expected value. Defaults to None. If None and a WindData object is supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). values (NDArrayFloat): NumPy array with shape (n_findex) with the values corresponding to the power generated for each wind condition combination. The wind farm power is multiplied by the value for every condition in calculating the wind farm's expected value. Defaults to None. If None and a WindData object is supplied, the WindData object's values will be used. Otherwise, a value of 1 for all conditions is assumed (i.e., the expected farm value will be equivalent to the expected farm power). turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the value production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the expected value. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. Returns: float: The expected value produced by the wind farm in units of value. """ if freq is None: if self.fmodel_unexpanded.wind_data is None: freq = np.array([1.0 / self.fmodel_unexpanded.core.flow_field.n_findex]) else: freq = self.fmodel_unexpanded.wind_data.unpack_freq() # If freq is 1d if len(np.shape(freq)) == 1: farm_power = self._get_farm_power(turbine_weights=turbine_weights) farm_power = np.multiply(freq, farm_power) else: weighted_turbine_powers = self._get_weighted_turbine_powers( turbine_weights=turbine_weights ) farm_power = np.nansum(np.multiply(freq, weighted_turbine_powers), axis=1) if values is None: if self.fmodel_unexpanded.wind_data is None: values = np.array([1.0]) else: values = self.fmodel_unexpanded.wind_data.unpack_value() return np.nansum(np.multiply(values, farm_power)) def get_farm_AVP( self, freq=None, values=None, turbine_weights=None, hours_per_year=8760, ) -> float: """ Estimate annual value production (AVP) for distribution of wind conditions, frequencies of occurrence, and corresponding values of power generated (e.g., electricity price per unit of energy). Args: freq (NDArrayFloat): NumPy array with shape (n_findex) with the frequencies of each wind condition combination. These frequencies should typically sum up to 1.0 and are used to weigh the wind farm value for every condition in calculating the wind farm's AVP. Defaults to None. If None and a WindData object is supplied, the WindData object's frequencies will be used. Otherwise, uniform frequencies are assumed (i.e., a simple mean over the findices is computed). values (NDArrayFloat): NumPy array with shape (n_findex) with the values corresponding to the power generated for each wind condition combination. The wind farm power is multiplied by the value for every condition in calculating the wind farm's AVP. Defaults to None. If None and a WindData object is supplied, the WindData object's values will be used. Otherwise, a value of 1 for all conditions is assumed (i.e., the AVP will be equivalent to the AEP). turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power from other turbines. This is useful when, for example, you are modeling multiple wind farms in a single floris object. If you only want to calculate the value production for one of those farms and include the wake effects of the neighboring farms, you can set the turbine_weights for the neighboring farms' turbines to 0.0. The array of turbine powers from floris is multiplied with this array in the calculation of the AVP. If None, this is an array with all values 1.0 and with shape equal to (n_findex, n_turbines). Defaults to None. hours_per_year (float, optional): Number of hours in a year. Defaults to 365 * 24. Returns: float: The Annual Value Production (AVP) for the wind farm in units of value. """ if ( freq is None and not isinstance( self.fmodel_unexpanded.wind_data, (WindRose, WindRoseWRG, WindTIRose) ) ): self.logger.warning( "Computing AVP with uniform frequencies. Results results may not reflect annual " "operation." ) if values is None and self.fmodel_unexpanded.wind_data is None: self.logger.warning( "Computing AVP with uniform value equal to 1. Results will be equivalent to " "annual energy production." ) return ( self.get_expected_farm_value(freq=freq, values=values, turbine_weights=turbine_weights) * hours_per_year ) def _get_rounded_inputs( self, input_array, wd_resolution=1.0, # Degree ws_resolution=1.0, # m/s ti_resolution=0.025, yaw_resolution=1.0, # Degree power_setpoint_resolution=100, # kW awc_amplitude_resolution=0.1, # Deg ): """ Round the input array specified resolutions. Parameters: input_array (numpy.ndarray): An array of shape (n, 5) with columns for wind direction (wd), wind speed (ws), turbulence intensity (tu), yaw angle (yaw), and power setpoint. wd_resolution (float): Resolution for rounding wind direction in degrees. Default is 1.0 degree. ws_resolution (float): Resolution for rounding wind speed in m/s. Default is 1.0 m/s. ti_resolution (float): Resolution for rounding turbulence intensity. Default is 0.1. yaw_resolution (float): Resolution for rounding yaw angle in degrees. Default is 1.0 degree. power_setpoint_resolution (int): Resolution for rounding power setpoint in kW. Default is 100 kW. awc_amplitude_resolution (float): Resolution for rounding amplitude of awc_amplitude Returns: numpy.ndarray: A rounded array of wind turbine parameters with the same shape as input_array, where each parameter is rounded to the specified resolution. """ # input_array is a nx5 numpy array whose columns are wd, ws, tu, yaw, power_setpoint # round each column by the respective resolution rounded_input_array = np.copy(input_array) rounded_input_array[:, 0] = ( np.round(rounded_input_array[:, 0] / wd_resolution) * wd_resolution ) rounded_input_array[:, 1] = ( np.round(rounded_input_array[:, 1] / ws_resolution) * ws_resolution ) rounded_input_array[:, 2] = ( np.round(rounded_input_array[:, 2] / ti_resolution) * ti_resolution ) rounded_input_array[:, 3 : 3 + self.fmodel_unexpanded.core.farm.n_turbines] = ( np.round( rounded_input_array[:, 3 : 3 + self.fmodel_unexpanded.core.farm.n_turbines] / yaw_resolution ) * yaw_resolution ) rounded_input_array[ :, 3 + self.fmodel_unexpanded.core.farm.n_turbines : 3 + 2 * self.fmodel_unexpanded.core.farm.n_turbines, ] = ( np.round( rounded_input_array[ :, 3 + self.fmodel_unexpanded.core.farm.n_turbines : 3 + 2 * self.fmodel_unexpanded.core.farm.n_turbines, ] / power_setpoint_resolution ) * power_setpoint_resolution ) rounded_input_array[ :, 3 + 2 * self.fmodel_unexpanded.core.farm.n_turbines : 3 + 3 * self.fmodel_unexpanded.core.farm.n_turbines, ] = ( np.round( rounded_input_array[ :, 3 + 2 * self.fmodel_unexpanded.core.farm.n_turbines : 3 + 3 * self.fmodel_unexpanded.core.farm.n_turbines, ] / awc_amplitude_resolution ) * awc_amplitude_resolution ) return rounded_input_array def _expand_wind_directions( self, input_array, wd_sample_points, fix_yaw_to_nominal_direction=False, n_turbines=None ): """ Expand wind direction data. Args: input_array (numpy.ndarray): 2D numpy array of shape (m, n) representing wind direction data, where m is the number of data points and n is the number of features. The first column represents wind direction. wd_sample_points (list): List of integers representing wind direction sample points. fix_yaw_to_nominal_direction (bool): Fix the yaw angle to the nominal direction? Defaults to False n_turbines (int): The number of turbines in the wind farm. Must be supplied if fix_yaw_to_nominal_direction is True. Returns: numpy.ndarray: Expanded wind direction data as a 2D numpy array of shape (m * p, n), where p is the number of sample points. Raises: ValueError: If wd_sample_points does not have an odd length or if the middle element is not 0. This function takes wind direction data and expands it by perturbing the wind direction column based on a list of sample points. It vertically stacks copies of the input array with the wind direction column perturbed by each sample point, ensuring the resultant values are within the range of 0 to 360. """ # Check if wd_sample_points is odd-length and the middle element is 0 if len(wd_sample_points) % 2 != 1: raise ValueError("wd_sample_points must have an odd length.") if wd_sample_points[len(wd_sample_points) // 2] != 0: raise ValueError("The middle element of wd_sample_points must be 0.") # If fix_yaw_to_nominal_direction is True, n_turbines must be supplied if fix_yaw_to_nominal_direction and n_turbines is None: raise ValueError("The number of turbines in the wind farm must be supplied") num_samples = len(wd_sample_points) num_rows = input_array.shape[0] # Create an array to hold the expanded data output_array = np.zeros((num_rows * num_samples, input_array.shape[1])) # Repeat each row of input_array for each sample point for i in range(num_samples): start_idx = i * num_rows end_idx = start_idx + num_rows output_array[start_idx:end_idx, :] = input_array.copy() # Perturb the wd column by the current sample point output_array[start_idx:end_idx, 0] = ( output_array[start_idx:end_idx, 0] + wd_sample_points[i] ) % 360 # If fix_yaw_to_nominal_direction is True, set the yaw angle to relative # to the nominal wind direction if fix_yaw_to_nominal_direction: # Wrap between -180 and 180 output_array[start_idx:end_idx, 3 : 3 + n_turbines] = wrap_180( output_array[start_idx:end_idx, 3 : 3 + n_turbines] + wd_sample_points[i] ) return output_array def _get_unique_inputs(self, input_array): """ Finds unique rows in the input numpy array and constructs a mapping array to reconstruct the input array from the unique rows. Include an exception that if the underlying FlorisModel object (fmodel_unexpanded) includes a multidim_conditions that includes non-scalar values, then force unique_inputs and map_to_expanded_inputs to represent that all rows are unique. Args: input_array (numpy.ndarray): Input array of shape (m, n). Returns: tuple: A tuple containing: numpy.ndarray: An array of unique rows found in the input_array, of shape (r, n), where r <= m. numpy.ndarray: A 1D array of indices mapping each row of the input_array to the corresponding row in the unique_inputs array. It represents how to reconstruct the input_array from the unique rows. """ if (self.fmodel_unexpanded.core.flow_field.multidim_conditions is not None and not is_all_scalar_dict(self.fmodel_unexpanded.core.flow_field.multidim_conditions) ): self.logger.warning("Given non-scalar multidim conditions. Forcing all unique values " "for the uncertainty model, which may be slower to run.") unique_inputs = input_array map_to_expanded_inputs = np.arange(len(input_array)) return unique_inputs, map_to_expanded_inputs unique_inputs, indices, map_to_expanded_inputs = np.unique( input_array, axis=0, return_index=True, return_inverse=True ) return unique_inputs, map_to_expanded_inputs def _get_weights(self, wd_std, wd_sample_points): """Generates weights based on a Gaussian distribution sampled at specific x-locations. Args: wd_std (float): The standard deviation of the Gaussian distribution. wd_sample_points (array-like): The x-locations where the Gaussian function is sampled. Returns: numpy.ndarray: An array of weights, generated using a Gaussian distribution with mean 0 and standard deviation wd_std, sampled at the specified x-locations. The weights are normalized so that they sum to 1. """ # Calculate the Gaussian function values at sample points gaussian_values = np.exp(-(np.array(wd_sample_points) ** 2) / (2 * wd_std**2)) # Normalize the Gaussian values to get the weights weights = gaussian_values / np.sum(gaussian_values) return weights def get_operation_model(self) -> str: """Get the operation model of a FlorisModel. Returns: str: The operation_model. """ operation_models = [ self.fmodel_unexpanded.core.farm.turbine_definitions[tindex]["operation_model"] for tindex in range(self.fmodel_unexpanded.core.farm.n_turbines) ] if len(set(operation_models)) == 1: return operation_models[0] else: return operation_models def set_operation_model(self, operation_model: str | List[str]): """Set the turbine operation model(s). Args: operation_model (str): The operation model to set. """ if isinstance(operation_model, str): if len(self.fmodel_unexpanded.core.farm.turbine_type) == 1: # Set a single one here, then, and return turbine_type = self.fmodel_unexpanded.core.farm.turbine_definitions[0] turbine_type["operation_model"] = operation_model self.set( turbine_type=[turbine_type], reference_wind_height=self.reference_wind_height ) return else: operation_model = [operation_model] * self.fmodel_unexpanded.core.farm.n_turbines if len(operation_model) != self.fmodel_unexpanded.core.farm.n_turbines: raise ValueError( "The length of the operation_model list must be " "equal to the number of turbines." ) turbine_type_list = self.fmodel_unexpanded.core.farm.turbine_definitions for tindex in range(self.fmodel_unexpanded.core.farm.n_turbines): turbine_type_list[tindex]["turbine_type"] = ( turbine_type_list[tindex]["turbine_type"] + "_" + operation_model[tindex] ) turbine_type_list[tindex]["operation_model"] = operation_model[tindex] self.set( turbine_type=turbine_type_list, reference_wind_height=self.reference_wind_height ) def copy(self): """Create an independent copy of the current UncertainFlorisModel object When creating the copy, this method uses self.__class__(), rather than UncertainFlorisModel() directly, so that subclasses of UncertainFlorisModel can inherit this method and return instantiations of their own class, rather than the UncertainFlorisModel class. """ return self.__class__(self.fmodel_unexpanded.copy(), **self.secondary_init_kwargs) def get_param(self, param: List[str], param_idx: Optional[int] = None) -> Any: """Get a parameter from a FlorisModel object. Args: param (List[str]): A list of keys to traverse the FlorisModel dictionary. param_idx (Optional[int], optional): The index to get the value at. Defaults to None. If None, the entire parameter is returned. Returns: Any: The value of the parameter. """ fm_dict = self.fmodel_unexpanded.core.as_dict() if param_idx is None: return nested_get(fm_dict, param) else: return nested_get(fm_dict, param)[param_idx] def set_param(self, param: List[str], value: Any, param_idx: Optional[int] = None): """Set a parameter in a FlorisModel object. Args: param (List[str]): A list of keys to traverse the FlorisModel dictionary. value (Any): The value to set. param_idx (Optional[int], optional): The index to set the value at. Defaults to None. """ fm_dict_mod = self.fmodel_unexpanded.core.as_dict() nested_set(fm_dict_mod, param, value, param_idx) self.fmodel_unexpanded.__init__(fm_dict_mod, **self.fmodel_unexpanded.secondary_init_kwargs) self.set() @property def secondary_init_kwargs(self): """ UncertainFlorisModel secondary keyword arguments (after configuration). """ return { "wd_resolution": self.wd_resolution, "ws_resolution": self.ws_resolution, "ti_resolution": self.ti_resolution, "yaw_resolution": self.yaw_resolution, "power_setpoint_resolution": self.power_setpoint_resolution, "awc_amplitude_resolution": self.awc_amplitude_resolution, "wd_std": self.wd_std, "wd_sample_points": self.wd_sample_points, "fix_yaw_to_nominal_direction": self.fix_yaw_to_nominal_direction, "verbose": self.verbose, } @property def layout_x(self): """ Wind turbine coordinate information. Returns: np.array: Wind turbine x-coordinate. """ return self.fmodel_unexpanded.core.farm.layout_x @property def layout_y(self): """ Wind turbine coordinate information. Returns: np.array: Wind turbine y-coordinate. """ return self.fmodel_unexpanded.core.farm.layout_y @property def wind_directions(self): """ Wind direction information. Returns: np.array: Wind direction. """ return self.fmodel_unexpanded.core.flow_field.wind_directions @property def wind_speeds(self): """ Wind speed information. Returns: np.array: Wind speed. """ return self.fmodel_unexpanded.core.flow_field.wind_speeds @property def turbulence_intensities(self): """ Turbulence intensity information. Returns: np.array: Turbulence intensity. """ return self.fmodel_unexpanded.core.flow_field.turbulence_intensities @property def n_findex(self): """ Number of unique wind conditions. Returns: int: Number of unique wind conditions. """ return self.fmodel_unexpanded.core.flow_field.n_findex @property def n_turbines(self): """ Number of turbines in the wind farm. Returns: int: Number of turbines in the wind farm. """ return self.fmodel_unexpanded.core.farm.n_turbines @property def reference_wind_height(self): """ Reference wind height. Returns: float: Reference wind height. """ return self.fmodel_unexpanded.core.flow_field.reference_wind_height @property def core(self): """ Returns the core of the unexpanded model. Returns: Floris: The core of the unexpanded model. """ return self.fmodel_unexpanded.core @property def turbine_average_velocities(self) -> NDArrayFloat: # Get the expanded velocities expanded_velocities = average_velocity( velocities=self.fmodel_expanded.core.flow_field.u, method=self.fmodel_expanded.core.grid.average_method, cubature_weights=self.fmodel_expanded.core.grid.cubature_weights, ) # Pass to off-class function result = map_turbine_values_uncertain( unique_turbine_values=expanded_velocities, map_to_expanded_inputs=self.map_to_expanded_inputs, weights=self.weights, n_unexpanded=self.n_unexpanded, n_sample_points=self.n_sample_points, n_turbines=self.fmodel_unexpanded.core.farm.n_turbines, ) return result def map_turbine_powers_uncertain( unique_turbine_powers, map_to_expanded_inputs, weights, n_unexpanded, n_sample_points, n_turbines, ): """ Alias for map_turbine_values_uncertain. """ # Deprecation warning print("map_turbine_powers_uncertain is deprecated, use map_turbine_values_uncertain instead.") return map_turbine_values_uncertain( unique_turbine_values=unique_turbine_powers, map_to_expanded_inputs=map_to_expanded_inputs, weights=weights, n_unexpanded=n_unexpanded, n_sample_points=n_sample_points, n_turbines=n_turbines, ) def map_turbine_values_uncertain( unique_turbine_values, map_to_expanded_inputs, weights, n_unexpanded, n_sample_points, n_turbines, ): """Calculates values at each turbine in the wind farm based on uncertainty weights. This function calculates the values (e.g. power, velocity) at each turbine in the wind farm, considering the underlying turbine values and applying a weighted sum to handle uncertainty. Args: unique_turbine_values (NDArrayFloat): An array of unique turbine powers, velocities, etc from the underlying FlorisModel map_to_expanded_inputs (NDArrayFloat): An array of indices mapping the unique values to the expanded values weights (NDArrayFloat): An array of weights for each wind direction sample point n_unexpanded (int): The number of unexpanded conditions n_sample_points (int): The number of wind direction sample points n_turbines (int): The number of turbines in the wind farm Returns: NDArrayFloat: An array containing the values at each turbine for each findex. """ # Expand back to the expanded value expanded_turbine_values = unique_turbine_values[map_to_expanded_inputs] # Reshape the weights array to make it compatible with broadcasting weights_reshaped = weights[:, np.newaxis] # Reshape expanded_turbine_values into blocks blocks = np.reshape( expanded_turbine_values, (n_unexpanded, n_sample_points, n_turbines), order="F", ) # Multiply each block by the corresponding weight weighted_blocks = blocks * weights_reshaped # Sum the blocks along the second axis result = np.sum(weighted_blocks, axis=1) return result class ApproxFlorisModel(UncertainFlorisModel): """ The ApproxFlorisModel overloads the UncertainFlorisModel with the special case that the wd_sample_points = [0]. This is a special case where no uncertainty is added but the resolution of the values wind direction, wind speed etc are still reduced by the specified resolution. This allows for cases to be reused and a faster approximate result computed """ def __init__( self, configuration: dict | str | Path, wd_resolution=1.0, # Degree ws_resolution=1.0, # m/s ti_resolution=0.01, yaw_resolution=1.0, # Degree power_setpoint_resolution=100, # kW awc_amplitude_resolution=0.1, # Deg verbose=False, ): super().__init__( configuration, wd_resolution, ws_resolution, ti_resolution, yaw_resolution, power_setpoint_resolution, awc_amplitude_resolution, wd_std=1.0, # Arbitrary nonzero value, not used since only one sample point wd_sample_points=[0], fix_yaw_to_nominal_direction=False, verbose=verbose, ) self.wd_resolution = wd_resolution self.ws_resolution = ws_resolution self.ti_resolution = ti_resolution self.yaw_resolution = yaw_resolution self.power_setpoint_resolution = power_setpoint_resolution self.awc_amplitude_resolution = awc_amplitude_resolution @property def secondary_init_kwargs(self): """ ApproxFlorisModel secondary keyword arguments (after configuration). """ return { "wd_resolution": self.wd_resolution, "ws_resolution": self.ws_resolution, "ti_resolution": self.ti_resolution, "yaw_resolution": self.yaw_resolution, "power_setpoint_resolution": self.power_setpoint_resolution, "awc_amplitude_resolution": self.awc_amplitude_resolution, "verbose": self.verbose, } ================================================ FILE: floris/utilities.py ================================================ import os from math import ceil from typing import ( Any, Dict, List, Optional, Tuple, ) import numpy as np import yaml from attrs import define, field from floris.type_dec import floris_float_type, NDArrayFloat def pshape(array: np.ndarray, label: str = ""): print(label, np.shape(array)) def cosd(angle): """ Cosine of an angle with the angle given in degrees. Args: angle (float): Angle in degrees. Returns: float """ return np.cos(np.radians(angle)) def sind(angle): """ Sine of an angle with the angle given in degrees. Args: angle (float): Angle in degrees. Returns: float """ return np.sin(np.radians(angle)) def tand(angle): """ Tangent of an angle with the angle given in degrees. Args: angle (float): Angle in degrees. Returns: float """ return np.tan(np.radians(angle)) def wrap_180(x): """ Shift the given values to within the range (-180, 180]. Args: x (numeric or np.array): Scalar value or np.array of values to shift. Returns: np.ndarray | float | int: Shifted values. """ return ((x + 180.0) % 360.0) - 180.0 def wrap_360(x): """ Shift the given values to within the range (0, 360]. Args: x (numeric or np.array): Scalar value or np.array of values to shift. Returns: np.ndarray | float | int: Shifted values. """ return x % 360.0 def check_and_identify_step_size(wind_directions): """ This function identifies the step size in a series of wind directions. The function will return the step size if the wind directions are evenly spaced, otherwise it will raise an error. Args: wind_directions (np.ndarray): Array of wind directions. Returns: float: The step size of the wind directions. """ if len(wind_directions) < 2: raise ValueError("Array must contain at least 2 elements") # First compute the steps between each wind direction steps = np.diff(wind_directions) # Confirm that the steps are all positive if not np.all(steps > 0): raise ValueError("wind_directions must be monotonically increasing") # Check the step from the last to the first element last_step = wind_directions[0] - wind_directions[-1] + 360 # If len(window_directions) == 2, then return whichever step is smaller if len(wind_directions) == 2: return min(steps[0], last_step) # If len(window_directions) == 3 make some checks elif len(wind_directions) == 3: if np.all(steps == steps[0]): return steps[0] elif steps[0] == last_step: return steps[0] elif steps[1] == last_step: return steps[1] else: raise ValueError("wind_directions must be evenly spaced") else: if np.allclose(steps, steps[0]): return steps[0] # If all but one of the steps are the same values, counts = np.unique(steps, return_counts=True) # Check for the case where there are more than two different step sizes if len(values) > 2: raise ValueError("wind_directions must be evenly spaced") # In the case there are only two step sizes, ensure that one only happens once if np.min(counts) > 1: raise ValueError("wind_directions must be evenly spaced") # If the last step equals the most common step, return the most common step if last_step == values[np.argmax(counts)]: return values[np.argmax(counts)] raise ValueError("wind_directions must be evenly spaced") def make_wind_directions_adjacent(wind_directions: NDArrayFloat) -> NDArrayFloat: """ This function reorders the wind directions so that they are adjacent. The function will return the reordered wind directions if the wind directions are not adjacent, otherwise it will return the input wind directions Args: wind_directions (NDArrayFloat): Array of wind directions. Returns: NDArrayFloat: The reordered wind directions to be adjacent. """ # Check the step size of the wind directions step_size = check_and_identify_step_size(wind_directions) # Get a list of steps steps = np.diff(wind_directions) # There will be at most one step with a size larger than the step size # If there is one, find it if np.any(steps > step_size): idx = np.argmax(steps) # Now change wind_directions such that for each direction after that index # subtract 360 and move that block to the front wind_directions = np.concatenate( (wind_directions[idx+1:] - 360, wind_directions[:idx+1]) ) # Return the wind directions and indices to go from the original to the new sort_indices = np.array(list(range(idx+1,len(wind_directions))) + list(range(idx+1))) return wind_directions, sort_indices else: return wind_directions, np.arange(len(wind_directions)) def wind_delta(wind_directions: NDArrayFloat | float): """ This function calculates the deviation from West (270) for a given wind direction or series of wind directions. First, 270 is subtracted from the input wind direction, and then the remainder after dividing by 360 is retained (modulo). The table below lists examples of results. | Input | Result | | ----- | ------ | | 270.0 | 0.0 | | 280.0 | 10.0 | | 360.0 | 90.0 | | 180.0 | 270.0 | | -10.0 | 80.0 | |-100.0 | 350.0 | Args: wind_directions (NDArrayFloat | float): A single or series of wind directions. They can be any number, negative or positive, but it is typically between 0 and 360. Returns: NDArrayFloat | float: The delta between the given wind direction and 270 in positive quantities between 0 and 360. The returned type is the same as wind_directions. """ return (wind_directions - 270) % 360 def rotate_coordinates_rel_west( wind_directions, coordinates, x_center_of_rotation=None, y_center_of_rotation=None ): """ This function rotates the given coordinates so that they are aligned with West (270) rather than North (0). The rotation happens about the centroid of the coordinates. Args: wind_directions (NDArrayFloat): Series of wind directions to base the rotation. coordinates (NDArrayFloat): Series of coordinates to rotate with shape (N coordinates, 3) so that each element of the array coordinates[i] yields a three-component coordinate. x_center_of_rotation (float, optional): The x-coordinate for the rotation center of the input coordinates. Defaults to None. y_center_of_rotation (float, optional): The y-coordinate for the rotational center of the input coordinates. Defaults to None. """ # Calculate the difference in given wind direction from 270 / West wind_deviation_from_west = wind_delta(wind_directions) wind_deviation_from_west = np.reshape(wind_deviation_from_west, (len(wind_directions), 1)) # Construct the arrays storing the turbine locations x_coordinates, y_coordinates, z_coordinates = coordinates.T # Find center of rotation - this is the center of box bounding all of the turbines if x_center_of_rotation is None: x_center_of_rotation = (np.min(x_coordinates) + np.max(x_coordinates)) / 2 if y_center_of_rotation is None: y_center_of_rotation = (np.min(y_coordinates) + np.max(y_coordinates)) / 2 # Rotate turbine coordinates about the center x_coord_offset = x_coordinates - x_center_of_rotation y_coord_offset = y_coordinates - y_center_of_rotation x_coord_rotated = ( x_coord_offset * cosd(wind_deviation_from_west) - y_coord_offset * sind(wind_deviation_from_west) + x_center_of_rotation ) y_coord_rotated = ( x_coord_offset * sind(wind_deviation_from_west) + y_coord_offset * cosd(wind_deviation_from_west) + y_center_of_rotation ) z_coord_rotated = np.ones_like( wind_deviation_from_west, dtype=floris_float_type ) * z_coordinates return x_coord_rotated, y_coord_rotated, z_coord_rotated, x_center_of_rotation, \ y_center_of_rotation def reverse_rotate_coordinates_rel_west( wind_directions: NDArrayFloat, grid_x: NDArrayFloat, grid_y: NDArrayFloat, grid_z: NDArrayFloat, x_center_of_rotation: float, y_center_of_rotation: float ): """ This function reverses the rotation of the given grid so that the coordinates are aligned with the original wind direction. The rotation happens about the centroid of the coordinates. Args: wind_directions (NDArrayFloat): Series of wind directions to base the rotation. grid_x (NDArrayFloat): X-coordinates to be rotated. grid_y (NDArrayFloat): Y-coordinates to be rotated. grid_z (NDArrayFloat): Z-coordinates to be rotated. x_center_of_rotation (float): The x-coordinate for the rotation center of the input coordinates. y_center_of_rotation (float): The y-coordinate for the rotational center of the input coordinates. """ # Calculate the difference in given wind direction from 270 / West # We are rotating in the other direction wind_deviation_from_west = -1.0 * wind_delta(wind_directions) # Construct the arrays storing the turbine locations grid_x_reversed = np.zeros_like(grid_x) grid_y_reversed = np.zeros_like(grid_x) grid_z_reversed = np.zeros_like(grid_x) for wii, angle_rotation in enumerate(wind_deviation_from_west): x_rot = grid_x[wii] y_rot = grid_y[wii] z_rot = grid_z[wii] # Rotate turbine coordinates about the center x_rot_offset = x_rot - x_center_of_rotation y_rot_offset = y_rot - y_center_of_rotation x = ( x_rot_offset * cosd(angle_rotation) - y_rot_offset * sind(angle_rotation) + x_center_of_rotation ) y = ( x_rot_offset * sind(angle_rotation) + y_rot_offset * cosd(angle_rotation) + y_center_of_rotation ) z = z_rot # Nothing changed in this rotation grid_x_reversed[wii] = x grid_y_reversed[wii] = y grid_z_reversed[wii] = z return grid_x_reversed, grid_y_reversed, grid_z_reversed class Loader(yaml.SafeLoader): def __init__(self, stream): self._root = os.path.split(stream.name)[0] super().__init__(stream) def include(self, node): filename = os.path.join(self._root, self.construct_scalar(node)) with open(filename, 'r') as f: return yaml.load(f, self.__class__) Loader.add_constructor('!include', Loader.include) def load_yaml(filename, loader=Loader): with open(filename) as fid: return yaml.load(fid, loader) def round_nearest_2_or_5(x: int | float) -> int: """Rounds a number (with a 0.5 buffer) up to the nearest integer divisible by 2 or 5. Args: x (int | float): The number to be rounded. Returns: int: The rounded number. """ base_2 = 2 base_5 = 5 return min(base_2 * ceil((x + 0.5) / base_2), base_5 * ceil((x + 0.5) / base_5)) def round_nearest(x: int | float, base: int = 5) -> int: """Rounds a number (with a 0.5 buffer) up to the nearest integer divisible by 5. Args: x (int | float): The number to be rounded. Returns: int: The rounded number. """ return base * ceil((x + 0.5) / base) def nested_get( d: Dict[str, Any], keys: List[str] ) -> Any: """Get a value from a nested dictionary using a list of keys. Based on: https://stackoverflow.com/questions/14692690/access-nested-dictionary-items-via-a-list-of-keys Args: d (Dict[str, Any]): The dictionary to get the value from. keys (List[str]): A list of keys to traverse the dictionary. Returns: Any: The value at the end of the key traversal. """ for key in keys: d = d[key] return d def nested_set( d: Dict[str, Any], keys: List[str], value: Any, idx: Optional[int] = None ) -> None: """Set a value in a nested dictionary using a list of keys. Based on: https://stackoverflow.com/questions/14692690/access-nested-dictionary-items-via-a-list-of-keys Args: dic (Dict[str, Any]): The dictionary to set the value in. keys (List[str]): A list of keys to traverse the dictionary. value (Any): The value to set. idx (Optional[int], optional): If the value is an list, the index to change. Defaults to None. """ d_in = d.copy() for key in keys[:-1]: d = d.setdefault(key, {}) if idx is None: # Parameter is a scalar, set directly d[keys[-1]] = value else: # Parameter is a list, need to first get the list, change the values at idx # # Get the underlying list par_list = nested_get(d_in, keys) par_list[idx] = value d[keys[-1]] = par_list def print_nested_dict(dictionary: Dict[str, Any], indent: int = 0) -> None: """Print a nested dictionary with indentation. Args: dictionary (Dict[str, Any]): The dictionary to print. indent (int, optional): The number of spaces to indent. Defaults to 0. """ for key, value in dictionary.items(): print(" " * indent + str(key)) if isinstance(value, dict): print_nested_dict(value, indent + 4) else: print(" " * (indent + 4) + str(value)) def is_all_scalar_dict(dictionary: Dict[str, Any]) -> bool: """Check if all values in a dictionary are scalar. A scalar is defined as a value that is not iterable (like int, float, str, bool) or a numpy scalar (0-dimensional array). Args: dictionary (Dict[str, Any]): The dictionary to check. Returns: bool: True if all values are scalar, False otherwise. """ for value in dictionary.values(): if not np.isscalar(value): return False return True ================================================ FILE: floris/wind_data.py ================================================ from __future__ import annotations import copy import inspect from abc import abstractmethod from pathlib import Path from typing import List import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np import pandas as pd from pandas.api.types import CategoricalDtype from scipy.interpolate import ( LinearNDInterpolator, NearestNDInterpolator, RegularGridInterpolator, ) from floris.heterogeneous_map import HeterogeneousMap from floris.type_dec import NDArrayFloat from floris.utilities import ( check_and_identify_step_size, is_all_scalar_dict, make_wind_directions_adjacent, ) class WindDataBase: """ Super class that WindRose and TimeSeries inherit from, enforcing the implementation of unpack() on the child classes and providing the general functions unpack_for_reinitialize() and unpack_freq(). """ @abstractmethod def unpack(self): """ Placeholder for child classes of WindDataBase, which each need to implement the unpack() method. """ raise NotImplementedError("unpack() not implemented on {0}".format(self.__class__.__name__)) def unpack_for_reinitialize(self): """ Return only the variables need for FlorisModel.reinitialize """ ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, _, _, heterogeneous_inflow_config, ) = self.unpack() return ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, heterogeneous_inflow_config, ) def unpack_freq(self): """Unpack frequency weighting""" return self.unpack()[3] def unpack_value(self): """Unpack values of power generated""" return self.unpack()[4] def unpack_multidim_conditions(self): """Unpack multidimensional conditions NOTE: This is a temporary method for backwards compatibility and will be removed in a future release, when multidim_conditions are included in the unpack() method of child classes. """ return getattr(self, "multidim_conditions", None) def check_heterogeneous_inflow_config(self, heterogeneous_inflow_config): """ Check that the heterogeneous_inflow_config dictionary is properly formatted Args: heterogeneous_inflow_config (dict): A dictionary containing the following keys: * 'speed_multipliers': A 2D NumPy array (size n_findex x num_points) of speed multipliers. * 'x': A 1D NumPy array (size num_points) of x-coordinates (meters). * 'y': A 1D NumPy array (size num_points) of y-coordinates (meters). """ if heterogeneous_inflow_config is not None: if not isinstance(heterogeneous_inflow_config, dict): raise TypeError("heterogeneous_inflow_config_by_wd must be a dictionary") if "speed_multipliers" not in heterogeneous_inflow_config: raise ValueError( "heterogeneous_inflow_config must contain a key 'speed_multipliers'" ) if "x" not in heterogeneous_inflow_config: raise ValueError("heterogeneous_inflow_config must contain a key 'x'") if "y" not in heterogeneous_inflow_config: raise ValueError("heterogeneous_inflow_config must contain a key 'y'") def set_layout(self, layout_x=None, layout_y=None): """ Default implementation the explicitly does nothing. Only WindData objects that depend on layout need to implement this method. Included so that FlorisModel can call this method on the WindData object when the layout is updated. Args: layout_x (list, optional): List of x-coordinates of the turbines. Defaults to None. layout_y (list, optional): List of y-coordinates of the turbines. Defaults to None. """ # No operation performed return None class WindRose(WindDataBase): """ The WindRose class is used to drive FLORIS and optimization operations in which the inflow is characterized by the frequency of binned wind speed and wind direction values. Turbulence intensities are defined as a function of wind direction and wind speed. Args: wind_directions: NumPy array of wind directions (NDArrayFloat). Must be evenly spaced and monotonically increasing. wind_speeds: NumPy array of wind speeds (NDArrayFloat). Must be evenly spaced and monotonically increasing. ti_table: Turbulence intensity table for binned wind direction, wind speed values (float, NDArrayFloat). Can be an array with dimensions (n_wind_directions, n_wind_speeds) or a single float value. If a single float value is provided, the turbulence intensity is assumed to be constant across all wind directions and wind speeds. freq_table: Frequency table for binned wind direction, wind speed values (NDArrayFloat, optional). Must have dimension (n_wind_directions, n_wind_speeds). Defaults to None in which case uniform frequency of all bins is assumed. value_table: Value table for binned wind direction, wind speed values (NDArrayFloat, optional). Must have dimension (n_wind_directions, n_wind_speeds). Defaults to None in which case uniform values are assumed. Value can be used to weight power in each bin to compute the total value of the energy produced compute_zero_freq_occurrence: Flag indicating whether to compute zero frequency occurrences (bool, optional). Defaults to False. heterogeneous_map (HeterogeneousMap, optional): A HeterogeneousMap object to define background heterogeneous inflow condition as a function of wind direction and wind speed. Alternatively, a dictionary can be passed in to define a HeterogeneousMap object. Defaults to None. heterogeneous_inflow_config_by_wd (dict, optional): A dictionary containing the following which can be used to define a heterogeneous_map object (note this parameter is kept for backwards compatibility and is not recommended for use): * 'x': A 1D NumPy array (size num_points) of x-coordinates (meters). * 'y': A 1D NumPy array (size num_points) of y-coordinates (meters). * 'speed_multipliers': A 2D NumPy array (size num_wd (or num_ws) x num_points) of speed multipliers. If neither wind_directions nor wind_speeds are defined, then this should be a single row array * 'wind_directions': A 1D NumPy array (size num_wd) of wind directions (degrees). Optional. * 'wind_speeds': A 1D NumPy array (size num_ws) of wind speeds (m/s). Optional. Defaults to None. multidim_conditions (dict, optional): A dictionary containing multidimensional inflow conditions. Each key is the name of the condition, and each value is either a 2D NumPy array of size n_wind_directions x n_wind_speeds containing the condition values, or a scalar value that will be broadcast to size n_findex. Defaults to None. """ def __init__( self, wind_directions: NDArrayFloat, wind_speeds: NDArrayFloat, ti_table: float | NDArrayFloat, freq_table: NDArrayFloat | None = None, value_table: NDArrayFloat | None = None, compute_zero_freq_occurrence: bool = False, heterogeneous_map: HeterogeneousMap | dict | None = None, heterogeneous_inflow_config_by_wd: dict | None = None, multidim_conditions: dict | None = None, ): if not isinstance(wind_directions, np.ndarray) or wind_directions.ndim != 1: raise TypeError("wind_directions must be a 1D NumPy array") if not isinstance(wind_speeds, np.ndarray) or wind_speeds.ndim != 1: raise TypeError("wind_speeds must be a 1D NumPy array") # Confirm that both wind_directions and wind_speeds are monitonically # increasing and evenly spaced _n_wd = len(wind_directions) _n_ws = len(wind_speeds) if _n_wd > 1: # Check monotonically increasing if not np.all(np.diff(wind_directions) > 0): raise ValueError("wind_directions must be monotonically increasing") # Check evenly spaced (Function will raise error if not) check_and_identify_step_size(wind_directions=wind_directions) if _n_ws > 1: # Check monotonically increasing if not np.all(np.diff(wind_speeds) > 0): raise ValueError("wind_speeds must be monotonically increasing") # Check evenly spaced if not np.allclose(np.diff(wind_speeds), wind_speeds[1] - wind_speeds[0]): raise ValueError("wind_speeds must be evenly spaced") # Save the wind speeds and directions self.wind_directions = wind_directions self.wind_speeds = wind_speeds # Check ti_table is a float or a NumPy array if not isinstance(ti_table, (float, np.ndarray)): raise TypeError("ti_table must be a float or a NumPy array") # Check if ti_table is a single float value if isinstance(ti_table, float): self.ti_table = np.full((_n_wd, _n_ws), ti_table) # Otherwise confirm the dimensions and then save it else: if not ti_table.shape[0] == _n_wd: raise ValueError("ti_table first dimension must equal len(wind_directions)") if not ti_table.shape[1] == _n_ws: raise ValueError("ti_table second dimension must equal len(wind_speeds)") self.ti_table = ti_table # If freq_table is not None, confirm it has correct dimension, # otherwise initialize to uniform probability if freq_table is not None: if not freq_table.shape[0] == _n_wd: raise ValueError("freq_table first dimension must equal len(wind_directions)") if not freq_table.shape[1] == _n_ws: raise ValueError("freq_table second dimension must equal len(wind_speeds)") self.freq_table = freq_table else: self.freq_table = np.ones((_n_wd, _n_ws)) # Normalize freq table self.freq_table = self.freq_table / np.sum(self.freq_table) # If value_table is not None, confirm it has correct dimension, # otherwise initialize to all ones if value_table is not None: if not value_table.shape[0] == _n_wd: raise ValueError("value_table first dimension must equal len(wind_directions)") if not value_table.shape[1] == _n_ws: raise ValueError("value_table second dimension must equal len(wind_speeds)") self.value_table = value_table # Save whether zero occurrence cases should be computed # First check if the ti_table contains any nan values (which would occur for example # if generated by the TimeSeries to WindRose conversion for wind speeds and directions # that were not present in the original time series) In this case, raise an error if compute_zero_freq_occurrence: if np.isnan(self.ti_table).any(): raise ValueError( "ti_table contains nan values. (This is likely the result of " " unsed wind speeds and directions in the original time series.)" " Cannot compute zero frequency occurrences." ) self.compute_zero_freq_occurrence = compute_zero_freq_occurrence # Check that heterogeneous_map and heterogeneous_inflow_config_by_wd are not both defined if heterogeneous_map is not None and heterogeneous_inflow_config_by_wd is not None: raise ValueError( "Only one of heterogeneous_map and heterogeneous_inflow_config_by_wd can be" + " defined." ) # If heterogeneous_inflow_config_by_wd is not None, then create a HeterogeneousMap object # using the dictionary if heterogeneous_inflow_config_by_wd is not None: # TODO: In future, add deprecation warning for this parameter here self.heterogeneous_map = HeterogeneousMap(**heterogeneous_inflow_config_by_wd) # Else if heterogeneous_map is not None elif heterogeneous_map is not None: # If heterogeneous_map is a dictionary, then create a HeterogeneousMap object if isinstance(heterogeneous_map, dict): self.heterogeneous_map = HeterogeneousMap(**heterogeneous_map) # Else if heterogeneous_map is a HeterogeneousMap object, then save it elif isinstance(heterogeneous_map, HeterogeneousMap): self.heterogeneous_map = heterogeneous_map # Else raise an error else: raise ValueError( "heterogeneous_map must be a HeterogeneousMap object or a dictionary." ) # Else if neither heterogeneous_map nor heterogeneous_inflow_config_by_wd are defined, # then set heterogeneous_map to None else: self.heterogeneous_map = None # Handle the multidim_conditions if multidim_conditions is not None: # Check that each value in the dictionary is either scalar or a correctly-sized array if is_all_scalar_dict(multidim_conditions): # Leave all scalar for performance purposes pass else: for key, value in multidim_conditions.items(): if isinstance(value, np.ndarray): if value.shape != (_n_wd, _n_ws): raise ValueError( f"multidim_conditions[{key}] must be of size len(wind_directions) " f"x len(wind_speeds) [({_n_wd}, {_n_ws})] but currently has shape " f"{value.shape}." ) else: raise ValueError( f"multidim_conditions[{key}] must be a 2D NumPy array of size " "len(wind_directions) x len(wind_speeds) or a scalar value." ) self.multidim_conditions = multidim_conditions # Build the gridded and flatten versions self._build_gridded_and_flattened_version() def _build_gridded_and_flattened_version(self): """ Given the wind direction and speed array, build the gridded versions covering all combinations, and then flatten versions which put all combinations into 1D array """ # Gridded wind speed and direction self.wd_grid, self.ws_grid = np.meshgrid( self.wind_directions, self.wind_speeds, indexing="ij" ) # Flat wind speed and direction self.wd_flat = self.wd_grid.flatten() self.ws_flat = self.ws_grid.flatten() # Flat frequency table self.freq_table_flat = self.freq_table.flatten() # Flat TI table self.ti_table_flat = self.ti_table.flatten() # value table if self.value_table is not None: self.value_table_flat = self.value_table.flatten() else: self.value_table_flat = None # Flatten multidim_conditions if defined if self.multidim_conditions is not None: if is_all_scalar_dict(self.multidim_conditions): # Leave all scalar for performance purposes self.multidim_conditions_flat = self.multidim_conditions else: self.multidim_conditions_flat = { k: v.flatten() for k, v in self.multidim_conditions.items() } else: self.multidim_conditions_flat = None # Set mask to non-zero frequency cases depending on compute_zero_freq_occurrence if self.compute_zero_freq_occurrence: # If computing zero freq occurrences, then this is all True self.non_zero_freq_mask = [True for i in range(len(self.freq_table_flat))] else: self.non_zero_freq_mask = self.freq_table_flat > 0.0 # N_findex should only be the calculated cases self.n_findex = np.sum(self.non_zero_freq_mask) def unpack(self): """ Unpack the flattened versions of the matrices and return the values accounting for the non_zero_freq_mask """ # The unpacked versions start as the flat version of each wind_directions_unpack = self.wd_flat.copy() wind_speeds_unpack = self.ws_flat.copy() freq_table_unpack = self.freq_table_flat.copy() ti_table_unpack = self.ti_table_flat.copy() # Now mask thes values according to self.non_zero_freq_mask wind_directions_unpack = wind_directions_unpack[self.non_zero_freq_mask] wind_speeds_unpack = wind_speeds_unpack[self.non_zero_freq_mask] freq_table_unpack = freq_table_unpack[self.non_zero_freq_mask] ti_table_unpack = ti_table_unpack[self.non_zero_freq_mask] # Now get unpacked value table if self.value_table_flat is not None: value_table_unpack = self.value_table_flat[self.non_zero_freq_mask].copy() else: value_table_unpack = None # If heterogeneous_map is not None, then get the heterogeneous_inflow_config if self.heterogeneous_map is not None: heterogeneous_inflow_config = self.heterogeneous_map.get_heterogeneous_inflow_config( wind_directions=wind_directions_unpack, wind_speeds=wind_speeds_unpack ) else: heterogeneous_inflow_config = None return ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, freq_table_unpack, value_table_unpack, heterogeneous_inflow_config, ) def unpack_multidim_conditions(self): if self.multidim_conditions_flat is None: return None elif is_all_scalar_dict(self.multidim_conditions_flat): return self.multidim_conditions_flat else: return { k: v[self.non_zero_freq_mask] for k, v in self.multidim_conditions_flat.items() } def aggregate(self, wd_step=None, ws_step=None, inplace=False): """ Wrapper for downsample method for backwards compatibility """ return self.downsample(wd_step, ws_step, inplace) def downsample(self, wd_step=None, ws_step=None, inplace=False): """ Aggregates the wind rose into fewer wind direction and wind speed bins. It is necessary the wd_step and ws_step passed in are at least as large as the current wind direction and wind speed steps. If they are not, the function will raise an error. The function will return a new WindRose object with the aggregated wind direction and wind speed bins. If inplace is set to True, the current WindRose object will be updated with the aggregated bins. Args: wd_step: Step size for wind direction resampling (float, optional). If None, the current step size will be used. Defaults to None. ws_step: Step size for wind speed resampling (float, optional). If None, the current step size will be used. Defaults to None. inplace: Flag indicating whether to update the current WindRose object when True or return a new WindRose object when False (bool, optional). Defaults to False. Returns: WindRose: Aggregated wind rose based on the provided or default step sizes. Only returned if inplace = False. Notes: - Returns a aggregated version of the wind rose using new `ws_step` and `wd_step`. - Uses the bin weights feature in TimeSeries to aggregated the wind rose. - If `ws_step` or `wd_step` is not specified, it uses the current values. """ # If ws_step is passed in, confirm is it at least as large as the current step if ws_step is not None: if len(self.wind_speeds) >= 2: current_ws_step = self.wind_speeds[1] - self.wind_speeds[0] if ws_step < current_ws_step: raise ValueError( "ws_step provided must be at least as large as the current ws_step " f"({current_ws_step} m/s)" ) # If wd_step is passed in, confirm is it at least as large as the current step if wd_step is not None: if len(self.wind_directions) >= 2: current_wd_step = check_and_identify_step_size(wind_directions=self.wind_directions) if wd_step < current_wd_step: raise ValueError( "wd_step provided must be at least as large as the current wd_step " f"({current_wd_step} degrees)" ) # If either ws_step or wd_step is None, set it to the current step if ws_step is None: if len(self.wind_speeds) >= 2: ws_step = self.wind_speeds[1] - self.wind_speeds[0] else: # wind rose will have only a single wind speed, and we assume a ws_step of 1 ws_step = 1.0 if wd_step is None: if len(self.wind_directions) >= 2: wd_step = check_and_identify_step_size(wind_directions=self.wind_directions) else: # wind rose will have only a single wind direction, and we assume a wd_step of 1 wd_step = 1.0 # Pass the flat versions of each quantity to build a TimeSeries model time_series = TimeSeries( self.wd_flat, self.ws_flat, self.ti_table_flat, self.value_table_flat, self.heterogeneous_map, ) # Now build a new wind rose using the new steps aggregated_wind_rose = time_series.to_WindRose( wd_step=wd_step, ws_step=ws_step, bin_weights=self.freq_table_flat ) if inplace: self.__init__( aggregated_wind_rose.wind_directions, aggregated_wind_rose.wind_speeds, aggregated_wind_rose.ti_table, aggregated_wind_rose.freq_table, aggregated_wind_rose.value_table, aggregated_wind_rose.compute_zero_freq_occurrence, aggregated_wind_rose.heterogeneous_map, ) else: return aggregated_wind_rose def resample_by_interpolation(self, wd_step=None, ws_step=None, method="linear", inplace=False): """ Wrapper to upsample method for backwards compatibility """ return self.upsample(wd_step, ws_step, method, inplace) def upsample(self, wd_step=None, ws_step=None, method="linear", inplace=False): """ Resample the wind rose using interpolation for upsampling. The method can be either 'linear' or 'nearest'. If inplace is set to True, the current WindRose object will be updated with the resampled bins. Args: wd_step: Step size for wind direction resampling (float, optional). If None, the current step size will be used. Defaults to None. ws_step: Step size for wind speed resampling (float, optional). If None, the current step size will be used. Defaults to None. method: Interpolation method to use (str, optional). Can be either 'linear' or 'nearest'. Defaults to "linear". inplace: Flag indicating whether to update the current WindRose object when True or return a new WindRose object when False (bool, optional). Defaults to False. Returns: WindRose: Resampled wind rose based on the provided or default step sizes. Only returned if inplace = False. """ if method == "linear": interpolator = LinearNDInterpolator elif method == "nearest": interpolator = NearestNDInterpolator else: raise ValueError( f"Unknown interpolation method: '{method}'. " "Available methods are 'linear' and 'nearest'" ) # First establish the current ws_step and wd_step if len(self.wind_speeds) >= 2: ws_step_current = self.wind_speeds[1] - self.wind_speeds[0] else: # wind rose will have only a single wind speed, and we assume a ws_step of 1 ws_step_current = 1.0 if len(self.wind_directions) >= 2: # Identify the current step size wd_step_current = check_and_identify_step_size(wind_directions=self.wind_directions) else: # wind rose will have only a single wind direction, and we assume a wd_step of 1 wd_step_current = 1.0 # If either ws_step or wd_step is None, set it to the current step if ws_step is None: ws_step = ws_step_current if wd_step is None: wd_step = wd_step_current # Make sure upsampling is appropriate if wd_step > wd_step_current: raise ValueError( f"Provided wd_step ({wd_step}) is larger than the current " f" wind direction step size. ({wd_step_current} degrees)" " Use the downsample method." ) if ws_step > ws_step_current: raise ValueError( f"Provided ws_step ({ws_step}) is larger than " f"the current wind speed step size. ({ws_step_current} m/s)" " Use the downsample method." ) # Get the current wind directions in adjacent from (ie 0, 2 358 -> -2, 0 ,2) if len(self.wind_directions) >= 2: current_wind_directions, adjacent_sort_index = make_wind_directions_adjacent( self.wind_directions ) else: current_wind_directions = self.wind_directions adjacent_sort_index = np.arange(len(current_wind_directions)) # Identify the covered range of wind directions wd_range_min_current = np.min(current_wind_directions) - wd_step_current / 2.0 wd_range_max_current = np.max(current_wind_directions) + wd_step_current / 2.0 # Look for unlikely case where for example wind directions are 8, 28, ... 358 if wd_range_max_current > 360: # TODO: Handle this case without an error raise ValueError( "Cannot upsample wind rose for case when wind directions are defined" " such that 0 degrees is included by bins to the left of 0 degrees. " ) # Identify the new minimum wind direction wd_min_new = wd_range_min_current + wd_step / 2.0 wd_max_new = wd_range_max_current - wd_step / 2.0 new_wind_directions = np.arange(wd_min_new, wd_max_new + wd_step / 2.0, wd_step) # Set up the new wind speeds ws_range_min_current = np.min(self.wind_speeds) - ws_step_current / 2.0 ws_range_max_current = np.max(self.wind_speeds) + ws_step_current / 2.0 ws_min_new = ws_range_min_current + ws_step / 2.0 ws_max_new = ws_range_max_current - ws_step / 2.0 # Force the new ws_min to 0 if negative if ws_min_new < 0: ws_min_new = 0.0 new_wind_speeds = np.arange(ws_min_new, ws_max_new + ws_step / 2.0, ws_step) # Set up for interpolation by copying the current values # and making sure they are sorted according to the adjacent wind directions wind_direction_column = current_wind_directions.copy() wind_speed_column = self.wind_speeds.copy() ti_matrix = self.ti_table.copy()[adjacent_sort_index, :] freq_matrix = self.freq_table.copy()[adjacent_sort_index, :] if self.value_table is not None: value_matrix = self.value_table.copy()[adjacent_sort_index, :] else: value_matrix = None # For padding wind directions, there are two cases to consider. In the first, # say that the wind directions are 30, 40, 50. In this case it's important append # 30 and 50 to 35 and 55 to ensure the interpolation covers the full range of data # This is the case when wind directions doesn't cover the full range of possible # degrees (0-360) if np.abs((wd_range_min_current % 360.0) - (wd_range_max_current % 360.0)) > 1e-6: wind_direction_column = np.concatenate(( np.array([wd_range_min_current]), wind_direction_column, np.array([wd_range_max_current]) )) ti_matrix = ti_matrix = np.vstack((ti_matrix[0, :], ti_matrix, ti_matrix[-1,:])) freq_matrix = np.vstack((freq_matrix[0, :], freq_matrix, freq_matrix[-1,:])) if self.value_table is not None: value_matrix = np.vstack((value_matrix[0, :], value_matrix, value_matrix[-1,:])) # In the alternative case, where the wind directions cover the full range # ie, 0, 10, 20 30, ...350, then need to place 0 at 360 and 350 at -10 # to cover all interpolations else: # Pad wind direction column with min_wd + 360 wind_direction_column = np.concatenate( ( [np.max(self.wind_directions) - 360.0], wind_direction_column, [np.min(self.wind_directions) + 360.0], ) ) # Pad the remaining with the appropriate value ti_matrix = ti_matrix = np.vstack((ti_matrix[-1, :], ti_matrix, ti_matrix[0, :])) freq_matrix = np.vstack((freq_matrix[-1, :], freq_matrix, freq_matrix[0, :])) if self.value_table is not None: value_matrix = np.vstack((value_matrix[-1, :], value_matrix, value_matrix[0, :])) # Pad out the wind speeds wind_speed_column = np.concatenate( ( np.array([ws_range_min_current]), wind_speed_column, np.array([ws_range_max_current]) ) ) ti_matrix = np.hstack( (ti_matrix[:, 0].reshape((-1, 1)), ti_matrix, ti_matrix[:, -1].reshape((-1, 1))) ) freq_matrix = np.hstack( (freq_matrix[:, 0].reshape((-1, 1)), freq_matrix, freq_matrix[:, -1].reshape((-1, 1))) ) if self.value_table is not None: value_matrix = np.hstack( ( value_matrix[:, 0].reshape((-1, 1)), value_matrix, value_matrix[:, -1].reshape((-1, 1)) ) ) # Grid wind directions and wind speeds to match the ti_matrix and freq_matrix when flattened wd_grid, ws_grid = np.meshgrid(wind_direction_column, wind_speed_column, indexing="ij") # Form wd_grid and ws_grid to a 2-column matrix wd_ws_mat = np.array([wd_grid.flatten(), ws_grid.flatten()]).T # Build the interpolator from wd_grid, ws_grid, to ti_matrix, freq_matrix and value_matrix ti_interpolator = interpolator(wd_ws_mat, ti_matrix.flatten()) freq_interpolator = interpolator(wd_ws_mat, freq_matrix.flatten()) if self.value_table is not None: value_interpolator = interpolator(wd_ws_mat, value_matrix.flatten()) # Grid the new wind directions and wind speeds new_wd_grid, new_ws_grid = np.meshgrid(new_wind_directions, new_wind_speeds, indexing="ij") new_wd_ws_mat = np.array([new_wd_grid.flatten(), new_ws_grid.flatten()]).T # Create the new ti_matrix and freq_matrix new_ti_matrix = ti_interpolator(new_wd_ws_mat).reshape( (len(new_wind_directions), len(new_wind_speeds)) ) new_freq_matrix = freq_interpolator(new_wd_ws_mat).reshape( (len(new_wind_directions), len(new_wind_speeds)) ) if self.value_table is not None: new_value_matrix = value_interpolator(new_wd_ws_mat).reshape( (len(new_wind_directions), len(new_wind_speeds)) ) else: new_value_matrix = None # Wrap new_wind_directions to 0-360 new_wind_directions = new_wind_directions % 360 # Finally sort new_wind_directions, and re-order new_ti_matrix, new_freq_matrix # and new_value_matrix accordingly sort_indices = np.argsort(new_wind_directions) new_wind_directions = new_wind_directions[sort_indices] new_ti_matrix = new_ti_matrix[sort_indices, :] new_freq_matrix = new_freq_matrix[sort_indices, :] if self.value_table is not None: new_value_matrix = new_value_matrix[sort_indices, :] # Create the resampled wind rose resampled_wind_rose = WindRose( new_wind_directions, new_wind_speeds, new_ti_matrix, new_freq_matrix, new_value_matrix, self.compute_zero_freq_occurrence, self.heterogeneous_map, ) if inplace: self.__init__( resampled_wind_rose.wind_directions, resampled_wind_rose.wind_speeds, resampled_wind_rose.ti_table, resampled_wind_rose.freq_table, resampled_wind_rose.value_table, resampled_wind_rose.compute_zero_freq_occurrence, resampled_wind_rose.heterogeneous_map, ) else: return resampled_wind_rose def plot( self, ax=None, color_map="viridis_r", wd_step=None, ws_step=None, legend_kwargs={"label": "Wind speed [m/s]"}, ): """ This method creates a wind rose plot showing the frequency of occurrence of the specified wind direction and wind speed bins. If no axis is provided, a new one is created. **Note**: Based on code provided by Patrick Murphy from the University of Colorado Boulder. Args: ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes on which the wind rose is plotted. Defaults to None. color_map (str, optional): Colormap to use. Defaults to 'viridis_r'. wd_step: Step size for wind direction (float, optional). If None, the current step size will be used. Defaults to None. ws_step: Step size for wind speed (float, optional). the current step size will be used. Defaults to None. legend_kwargs (dict, optional): Keyword arguments to be passed to ax.legend(). Defaults to {"label": "Wind speed [m/s]"}. Returns: :py:class:`matplotlib.pyplot.axes`: A figure axes object containing the plotted wind rose. """ # Get a aggregated (downsampled) wind_rose wind_rose_aggregate = self.downsample(wd_step, ws_step, inplace=False) wd_bins = wind_rose_aggregate.wind_directions ws_bins = wind_rose_aggregate.wind_speeds freq_table = wind_rose_aggregate.freq_table # Set up figure if ax is None: _, ax = plt.subplots(subplot_kw={"polar": True}) # Get the wd_step if wd_step is None: if len(wd_bins) >= 2: wd_step = wd_bins[1] - wd_bins[0] else: # This admittedly an odd edge case wd_step = 360.0 # Get a color array color_array = plt.get_cmap(color_map, len(ws_bins)) norm_ws = mpl.colors.Normalize(vmin=np.min(ws_bins), vmax=np.max(ws_bins)) sm_ws = mpl.cm.ScalarMappable(norm=norm_ws, cmap=color_array) for wd_idx, wd in enumerate(wd_bins): rects = [] freq_table_sub = freq_table[wd_idx, :].flatten() for ws_idx, ws in reversed(list(enumerate(ws_bins))): plot_val = freq_table_sub[: ws_idx + 1].sum() rects.append( ax.bar( np.radians(wd), plot_val, width=0.9 * np.radians(wd_step), color=color_array(ws_idx), edgecolor="k", ) ) # Configure the plot try: ax.figure.colorbar(sm_ws, ax=ax, **legend_kwargs) ax.figure.tight_layout() except TypeError: ax.legend(reversed(rects), ws_bins, **legend_kwargs) ax.figure.get_children()[-1].remove() # Remove the empty colorbar ax.set_theta_direction(-1) ax.set_theta_offset(np.pi / 2.0) ax.set_theta_zero_location("N") ax.set_xticks(np.arange(0, 2 * np.pi, np.pi / 4)) ax.set_xticklabels(["N", "NE", "E", "SE", "S", "SW", "W", "NW"]) return ax def assign_ti_using_wd_ws_function(self, func): """ Use the passed in function to assign new values to turbulence_intensities Args: func (function): Function which accepts wind_directions as its first argument and wind_speeds as second argument and returns turbulence_intensities """ self.ti_table = func(self.wd_grid, self.ws_grid) self._build_gridded_and_flattened_version() def assign_ti_using_IEC_method(self, Iref=0.07, offset=3.8): """ Define TI as a function of wind speed by specifying an Iref and offset value as in the normal turbulence model in the IEC 61400-1 standard Args: Iref (float): Reference turbulence level, defined as the expected value of TI at 15 m/s. Default = 0.07. Note this value is lower than the values of Iref for turbulence classes A, B, and C in the IEC standard (0.16, 0.14, and 0.12, respectively), but produces TI values more in line with those typically used in FLORIS. When the default Iref and offset are used, the TI at 8 m/s is 8.6%. offset (float): Offset value to equation. Default = 3.8, as defined in the IEC standard to give the expected value of TI for each wind speed. """ if (Iref < 0) or (Iref > 1): raise ValueError("Iref must be >= 0 and <=1") def iref_func(wind_directions, wind_speeds): sigma_1 = Iref * (0.75 * wind_speeds + offset) return sigma_1 / wind_speeds self.assign_ti_using_wd_ws_function(iref_func) def plot_ti_over_ws( self, ax=None, marker=".", ls="None", color="k", ): """ Scatter plot the turbulence_intensities against wind_speeds Args: ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes on which the turbulence intensity is plotted. Defaults to None. marker (str, optional): Scatter plot marker style. Defaults to ".". ls (str, optional): Scatter plot line style. Defaults to "None". color (str, optional): Scatter plot color. Defaults to "k". Returns: :py:class:`matplotlib.pyplot.axes`: A figure axes object containing the plotted turbulence intensities as a function of wind speed. """ # TODO: Plot mean and std. devs. of TI in each ws bin in addition to # individual points # Set up figure if ax is None: _, ax = plt.subplots() ax.plot(self.ws_flat, self.ti_table_flat * 100, marker=marker, ls=ls, color=color) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Turbulence Intensity (%)") ax.grid(True) def assign_value_using_wd_ws_function(self, func, normalize=False): """ Use the passed in function to assign new values to the value table. Args: func (function): Function which accepts wind_directions as its first argument and wind_speeds as second argument and returns values. normalize (bool, optional): If True, the value array will be normalized by the mean value. Defaults to False. """ self.value_table = func(self.wd_grid, self.ws_grid) if normalize: self.value_table /= np.sum(self.freq_table * self.value_table) self._build_gridded_and_flattened_version() def assign_value_piecewise_linear( self, value_zero_ws=1.425, ws_knee=4.5, slope_1=0.0, slope_2=-0.135, limit_to_zero=False, normalize=False, ): """ Define value as a continuous piecewise linear function of wind speed with two line segments. The default parameters yield a value function that approximates the normalized mean electricity price vs. wind speed curve for the SPP market in the U.S. for years 2018-2020 from figure 7 in Simley et al. "The value of wake steering wind farm flow control in US energy markets," Wind Energy Science, 2024. https://doi.org/10.5194/wes-9-219-2024. This default value function is constant at low wind speeds, then linearly decreases above 4.5 m/s. Args: value_zero_ws (float, optional): The value when wind speed is zero. Defaults to 1.425. ws_knee (float, optional): The wind speed separating line segments 1 and 2. Default = 4.5 m/s. slope_1 (float, optional): The slope of the first line segment (unit of value per m/s). Defaults to zero. slope_2 (float, optional): The slope of the second line segment (unit of value per m/s). Defaults to -0.135. limit_to_zero (bool, optional): If True, negative values will be set to zero. Defaults to False. normalize (bool, optional): If True, the value array will be normalized by the mean value. Defaults to False. """ def piecewise_linear_value_func(wind_directions, wind_speeds): value = np.zeros_like(wind_speeds, dtype=float) value[wind_speeds < ws_knee] = ( slope_1 * wind_speeds[wind_speeds < ws_knee] + value_zero_ws ) offset_2 = (slope_1 - slope_2) * ws_knee + value_zero_ws value[wind_speeds >= ws_knee] = slope_2 * wind_speeds[wind_speeds >= ws_knee] + offset_2 if limit_to_zero: value[value < 0] = 0.0 return value self.assign_value_using_wd_ws_function(piecewise_linear_value_func, normalize) def plot_value_over_ws( self, ax=None, marker=".", ls="None", color="k", ): """ Scatter plot the value of the energy generated against wind speed. Args: ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes on which the value is plotted. Defaults to None. marker (str, optional): Scatter plot marker style. Defaults to ".". ls (str, optional): Scatter plot line style. Defaults to "None". color (str, optional): Scatter plot color. Defaults to "k". Returns: :py:class:`matplotlib.pyplot.axes`: A figure axes object containing the plotted value as a function of wind speed. """ # TODO: Plot mean and std. devs. of value in each ws bin in addition to # individual points # Set up figure if ax is None: _, ax = plt.subplots() ax.plot(self.ws_flat, self.value_table_flat, marker=marker, ls=ls, color=color) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Value") ax.grid(True) @staticmethod def read_csv_long( file_path: str, ws_col: str = "wind_speeds", wd_col: str = "wind_directions", ti_col_or_value: str | float = "turbulence_intensities", freq_col: str | None = None, sep: str = ",", ) -> WindRose: """ Read a long-formatted CSV file into the wind rose object. By long, what is meant is that the wind speed, wind direction combination is given for each row in the CSV file. The wind speed, wind direction, are given in separate columns, and the frequency of occurrence of each combination is given in a separate column. The frequency column is optional, and if not provided, uniform frequency of all bins is assumed. The value of ti_col_or_value can be either a string or a float. If it is a string, it is assumed to be the name of the column in the CSV file that contains the turbulence intensity values. If it is a float, it is assumed to be a constant turbulence intensity value for all wind speed and direction combinations. Args: file_path (str): Path to the CSV file. ws_col (str): Name of the column in the CSV file that contains the wind speed values. Defaults to 'wind_speeds'. wd_col (str): Name of the column in the CSV file that contains the wind direction values. Defaults to 'wind_directions'. ti_col_or_value (str or float): Name of the column in the CSV file that contains the turbulence intensity values, or a constant turbulence intensity value. freq_col (str): Name of the column in the CSV file that contains the frequency values. Defaults to None in which case constant frequency assumed. sep (str): Delimiter to use. Defaults to ','. Returns: WindRose: Wind rose object created from the CSV file. """ # Read in the CSV file try: df = pd.read_csv(file_path, sep=sep) except FileNotFoundError: # If the file cannot be found, then attempt the level above base_fn = Path(inspect.stack()[-1].filename).resolve().parent file_path = base_fn / file_path df = pd.read_csv(file_path, sep=sep) # Check that ti_col_or_value is a string or a float if not isinstance(ti_col_or_value, (str, float)): raise TypeError("ti_col_or_value must be a string or a float") # Check that the required columns are present if ws_col not in df.columns: raise ValueError(f"Column {ws_col} not found in CSV file") if wd_col not in df.columns: raise ValueError(f"Column {wd_col} not found in CSV file") if ti_col_or_value not in df.columns and isinstance(ti_col_or_value, str): raise ValueError(f"Column {ti_col_or_value} not found in CSV file") if freq_col not in df.columns and freq_col is not None: raise ValueError(f"Column {freq_col} not found in CSV file") # Get the wind speed, wind direction, and turbulence intensity values wind_directions = df[wd_col].values wind_speeds = df[ws_col].values if isinstance(ti_col_or_value, str): turbulence_intensities = df[ti_col_or_value].values else: turbulence_intensities = ti_col_or_value * np.ones(len(wind_speeds)) if freq_col is not None: freq_values = df[freq_col].values else: freq_values = np.ones(len(wind_speeds)) # Normalize freq_values freq_values = freq_values / np.sum(freq_values) # Get the unique values of wind directions and wind speeds unique_wd = np.unique(wind_directions) unique_ws = np.unique(wind_speeds) # Get the step side for wind direction and wind speed wd_step = unique_wd[1] - unique_wd[0] ws_step = unique_ws[1] - unique_ws[0] # Now use TimeSeries to create a wind rose time_series = TimeSeries(wind_directions, wind_speeds, turbulence_intensities) # Now build a new wind rose using the new steps return time_series.to_WindRose(wd_step=wd_step, ws_step=ws_step, bin_weights=freq_values) class WindTIRose(WindDataBase): """ WindTIRose is similar to the WindRose class, but contains turbulence intensity as an additional wind rose dimension instead of being defined as a function of wind direction and wind speed. The class is used to drive FLORIS and optimization operations in which the inflow is characterized by the frequency of binned wind speed, wind direction, and turbulence intensity values. Args: wind_directions: NumPy array of wind directions (NDArrayFloat). wind_speeds: NumPy array of wind speeds (NDArrayFloat). turbulence_intensities: NumPy array of turbulence intensities (NDArrayFloat). freq_table: Frequency table for binned wind direction, wind speed, and turbulence intensity values (NDArrayFloat, optional). Must have dimension (n_wind_directions, n_wind_speeds, n_turbulence_intensities). Defaults to None in which case uniform frequency of all bins is assumed. value_table: Value table for binned wind direction, wind speed, and turbulence intensity values (NDArrayFloat, optional). Must have dimension (n_wind_directions, n_wind_speeds, n_turbulence_intensities). Defaults to None in which case uniform values are assumed. Value can be used to weight power in each bin to compute the total value of the energy produced. compute_zero_freq_occurrence: Flag indicating whether to compute zero frequency occurrences (bool, optional). Defaults to False. heterogeneous_map (HeterogeneousMap, optional): A HeterogeneousMap object to define background heterogeneous inflow condition as a function of wind direction and wind speed. Alternatively, a dictionary can be passed in to define a HeterogeneousMap object. Defaults to None. heterogeneous_inflow_config_by_wd (dict, optional): A dictionary containing the following which can be used to define a heterogeneous_map object (note this parameter is kept for backwards compatibility and is not recommended for use): * 'x': A 1D NumPy array (size num_points) of x-coordinates (meters). * 'y': A 1D NumPy array (size num_points) of y-coordinates (meters). * 'speed_multipliers': A 2D NumPy array (size num_wd (or num_ws) x num_points) of speed multipliers. If neither wind_directions nor wind_speeds are defined, then this should be a single row array * 'wind_directions': A 1D NumPy array (size num_wd) of wind directions (degrees). Optional. * 'wind_speeds': A 1D NumPy array (size num_ws) of wind speeds (m/s). Optional. Defaults to None. multidim_conditions (dict, optional): A dictionary containing multidimensional inflow conditions. Each key is the name of the condition, and each value is either a 3D NumPy array of shape (n_wind_speeds, n_wind_directions, n_turbulence_intensities) containing the condition values, or a scalar value that will be broadcast to size n_findex. Defaults to None. """ def __init__( self, wind_directions: NDArrayFloat, wind_speeds: NDArrayFloat, turbulence_intensities: NDArrayFloat, freq_table: NDArrayFloat | None = None, value_table: NDArrayFloat | None = None, compute_zero_freq_occurrence: bool = False, heterogeneous_map: HeterogeneousMap | dict | None = None, heterogeneous_inflow_config_by_wd: dict | None = None, multidim_conditions: dict | None = None, ): if not isinstance(wind_directions, np.ndarray) or wind_directions.ndim != 1: raise TypeError("wind_directions must be a 1D NumPy array") if not isinstance(wind_speeds, np.ndarray) or wind_speeds.ndim != 1: raise TypeError("wind_speeds must be a 1D NumPy array") if not isinstance(turbulence_intensities, np.ndarray) or turbulence_intensities.ndim != 1: raise TypeError("turbulence_intensities must be a 1D NumPy array") # Confirm that both wind_directions and wind_speeds # and turbulence intensities are monotonically # increasing and evenly spaced _n_wd = len(wind_directions) _n_ws = len(wind_speeds) _n_ti = len(turbulence_intensities) if _n_wd > 1: # Check monotonically increasing if not np.all(np.diff(wind_directions) > 0): raise ValueError("wind_directions must be monotonically increasing") # Check evenly spaced (Function will raise error if not) check_and_identify_step_size(wind_directions=wind_directions) if _n_ws > 1: # Check monotonically increasing if not np.all(np.diff(wind_speeds) > 0): raise ValueError("wind_speeds must be monotonically increasing") # Check evenly spaced if not np.allclose(np.diff(wind_speeds), wind_speeds[1] - wind_speeds[0]): raise ValueError("wind_speeds must be evenly spaced") if _n_ti > 1: # Check monotonically increasing if not np.all(np.diff(turbulence_intensities) > 0): raise ValueError("turbulence_intensities must be monotonically increasing") # Check evenly spaced if not np.allclose( np.diff(turbulence_intensities), turbulence_intensities[1] - turbulence_intensities[0], ): raise ValueError("turbulence_intensities must be evenly spaced") # Save the wind speeds and directions self.wind_directions = wind_directions self.wind_speeds = wind_speeds self.turbulence_intensities = turbulence_intensities # If freq_table is not None, confirm it has correct dimension, # otherwise initialize to uniform probability if freq_table is not None: if not freq_table.shape[0] == _n_wd: raise ValueError("freq_table first dimension must equal len(wind_directions)") if not freq_table.shape[1] == _n_ws: raise ValueError("freq_table second dimension must equal len(wind_speeds)") if not freq_table.shape[2] == _n_ti: raise ValueError( "freq_table third dimension must equal len(turbulence_intensities)" ) self.freq_table = freq_table else: self.freq_table = np.ones((_n_wd, _n_ws, _n_ti)) # Normalize freq table self.freq_table = self.freq_table / np.sum(self.freq_table) # If value_table is not None, confirm it has correct dimension, # otherwise initialize to all ones if value_table is not None: if not value_table.shape[0] == _n_wd: raise ValueError("value_table first dimension must equal len(wind_directions)") if not value_table.shape[1] == _n_ws: raise ValueError("value_table second dimension must equal len(wind_speeds)") if not value_table.shape[2] == _n_ti: raise ValueError( "value_table third dimension must equal len(turbulence_intensities)" ) self.value_table = value_table # Save whether zero occurrence cases should be computed self.compute_zero_freq_occurrence = compute_zero_freq_occurrence # Check that heterogeneous_map and heterogeneous_inflow_config_by_wd are not both defined if heterogeneous_map is not None and heterogeneous_inflow_config_by_wd is not None: raise ValueError( "Only one of heterogeneous_map and heterogeneous_inflow_config_by_wd can be" + " defined." ) # If heterogeneous_inflow_config_by_wd is not None, then create a HeterogeneousMap object # using the dictionary if heterogeneous_inflow_config_by_wd is not None: # TODO: In future, add deprectation warning for this parameter here self.heterogeneous_map = HeterogeneousMap(**heterogeneous_inflow_config_by_wd) # Else if heterogeneous_map is not None elif heterogeneous_map is not None: # If heterogeneous_map is a dictionary, then create a HeterogeneousMap object if isinstance(heterogeneous_map, dict): self.heterogeneous_map = HeterogeneousMap(**heterogeneous_map) # Else if heterogeneous_map is a HeterogeneousMap object, then save it elif isinstance(heterogeneous_map, HeterogeneousMap): self.heterogeneous_map = heterogeneous_map # Else raise an error else: raise ValueError( "heterogeneous_map must be a HeterogeneousMap object or a dictionary." ) # Else if neither heterogeneous_map nor heterogeneous_inflow_config_by_wd are defined, # then set heterogeneous_map to None else: self.heterogeneous_map = None # Handle the multidim_conditions if multidim_conditions is not None: # Check that each value in the dictionary is either scalar or a correctly-sized array if is_all_scalar_dict(multidim_conditions): # Leave all scalar for performance purposes pass else: for key, value in multidim_conditions.items(): if isinstance(value, np.ndarray): if value.shape != (_n_wd, _n_ws, _n_ti): raise ValueError( f"multidim_conditions[{key}] must be of size len(wind_directions) " "x len(wind_speeds) x len(turbulence_intensities) " f"[({_n_wd}, {_n_ws}, {_n_ti})] but currently has shape " f"{value.shape}." ) else: raise ValueError( f"multidim_conditions[{key}] must be a 2D NumPy array of size " "len(wind_directions) x len(wind_speeds) x len(turbulence_intensities)" " or a scalar value." ) self.multidim_conditions = multidim_conditions # Build the gridded and flatten versions self._build_gridded_and_flattened_version() def _build_gridded_and_flattened_version(self): """ Given the wind direction, wind speed, and turbulence intensity array, build the gridded versions covering all combinations, and then flatten versions which put all combinations into 1D array """ # Gridded wind speed and direction self.wd_grid, self.ws_grid, self.ti_grid = np.meshgrid( self.wind_directions, self.wind_speeds, self.turbulence_intensities, indexing="ij" ) # Flat wind direction, wind speed, and turbulence intensity self.wd_flat = self.wd_grid.flatten() self.ws_flat = self.ws_grid.flatten() self.ti_flat = self.ti_grid.flatten() # Flat frequency table self.freq_table_flat = self.freq_table.flatten() # value table if self.value_table is not None: self.value_table_flat = self.value_table.flatten() else: self.value_table_flat = None # Flatten multidim_conditions if defined if self.multidim_conditions is not None: if is_all_scalar_dict(self.multidim_conditions): # Leave all scalar for performance purposes self.multidim_conditions_flat = self.multidim_conditions else: self.multidim_conditions_flat = { k: v.flatten() for k, v in self.multidim_conditions.items() } else: self.multidim_conditions_flat = None # Set mask to non-zero frequency cases depending on compute_zero_freq_occurrence if self.compute_zero_freq_occurrence: # If computing zero freq occurrences, then this is all True self.non_zero_freq_mask = [True for i in range(len(self.freq_table_flat))] else: self.non_zero_freq_mask = self.freq_table_flat > 0.0 # N_findex should only be the calculated cases self.n_findex = np.sum(self.non_zero_freq_mask) def unpack(self): """ Unpack the flattened versions of the matrices and return the values accounting for the non_zero_freq_mask """ # The unpacked versions start as the flat version of each wind_directions_unpack = self.wd_flat.copy() wind_speeds_unpack = self.ws_flat.copy() turbulence_intensities_unpack = self.ti_flat.copy() freq_table_unpack = self.freq_table_flat.copy() # Now mask thes values according to self.non_zero_freq_mask wind_directions_unpack = wind_directions_unpack[self.non_zero_freq_mask] wind_speeds_unpack = wind_speeds_unpack[self.non_zero_freq_mask] turbulence_intensities_unpack = turbulence_intensities_unpack[self.non_zero_freq_mask] freq_table_unpack = freq_table_unpack[self.non_zero_freq_mask] # Now get unpacked value table if self.value_table_flat is not None: value_table_unpack = self.value_table_flat[self.non_zero_freq_mask].copy() else: value_table_unpack = None # If heterogeneous_map is not None, then get the heterogeneous_inflow_config if self.heterogeneous_map is not None: heterogeneous_inflow_config = self.heterogeneous_map.get_heterogeneous_inflow_config( wind_directions=wind_directions_unpack, wind_speeds=wind_speeds_unpack ) else: heterogeneous_inflow_config = None return ( wind_directions_unpack, wind_speeds_unpack, turbulence_intensities_unpack, freq_table_unpack, value_table_unpack, heterogeneous_inflow_config, ) def unpack_multidim_conditions(self): if self.multidim_conditions_flat is None: return None elif is_all_scalar_dict(self.multidim_conditions_flat): return self.multidim_conditions_flat else: return { k: v[self.non_zero_freq_mask] for k, v in self.multidim_conditions_flat.items() } def aggregate(self, wd_step=None, ws_step=None, ti_step=None, inplace=False): """ Wrapper for downsample method for backwards compatibility """ return self.downsample(wd_step, ws_step, ti_step, inplace) def downsample(self, wd_step=None, ws_step=None, ti_step=None, inplace=False): """ Aggregates the wind TI rose into fewer wind direction, wind speed and TI bins. It is necessary the wd_step and ws_step ti_step passed in are at least as large as the current wind direction and wind speed steps. If they are not, the function will raise an error. The function will return a new WindTIRose object with the aggregated wind direction, wind speed and TI bins. If inplace is set to True, the current WindTIRose object will be updated with the aggregated bins. Args: wd_step: Step size for wind direction resampling (float, optional). ws_step: Step size for wind speed resampling (float, optional). ti_step: Step size for turbulence intensity resampling (float, optional). inplace: Flag indicating whether to update the current WindTIRose. Defaults to False. Returns: WindTIRose: Aggregated wind TI rose based on the provided or default step sizes. Notes: - Returns an aggregated version of the wind TI rose using new `ws_step`, `wd_step`, and `ti_step`. - Uses the bin weights feature in TimeSeries to aggregate the wind rose. - If `ws_step`, `wd_step`, or `ti_step` are not specified, it uses the current values. """ # If ws_step is passed in, confirm is it at least as large as the current step if ws_step is not None: if len(self.wind_speeds) >= 2: current_ws_step = self.wind_speeds[1] - self.wind_speeds[0] if ws_step < current_ws_step: raise ValueError( "ws_step provided must be at least as large as the current ws_step " f"({current_ws_step} m/s)" ) # If wd_step is passed in, confirm is it at least as large as the current step if wd_step is not None: if len(self.wind_directions) >= 2: current_wd_step = check_and_identify_step_size(wind_directions=self.wind_directions) if wd_step < current_wd_step: raise ValueError( "wd_step provided must be at least as large as the current wd_step " f"({current_wd_step} degrees)" ) # If ti_step is passed in, confirm is it at least as large as the current step if ti_step is not None: if len(self.turbulence_intensities) >= 2: current_ti_step = self.turbulence_intensities[1] - self.turbulence_intensities[0] if ti_step < current_ti_step: raise ValueError( "ti_step provided must be at least as large as the current ti_step " f"({current_ti_step})" ) # If ws_step, wd_step or ti_step is none, set it to the current step if ws_step is None: if len(self.wind_speeds) >= 2: ws_step = self.wind_speeds[1] - self.wind_speeds[0] else: # wind rose will have only a single wind speed, and we assume a ws_step of 1 ws_step = 1.0 if wd_step is None: if len(self.wind_directions) >= 2: wd_step = check_and_identify_step_size(wind_directions=self.wind_directions) else: # wind rose will have only a single wind direction, and we assume a wd_step of 1 wd_step = 1.0 if ti_step is None: if len(self.turbulence_intensities) >= 2: ti_step = self.turbulence_intensities[1] - self.turbulence_intensities[0] else: # wind rose will have only a single TI, and we assume a ti_step of 1 ti_step = 1.0 # Pass the flat versions of each quantity to build a TimeSeries model time_series = TimeSeries( self.wd_flat, self.ws_flat, self.ti_flat, self.value_table_flat, self.heterogeneous_map, ) # Now build a new wind rose using the new steps aggregated_wind_rose = time_series.to_WindTIRose( wd_step=wd_step, ws_step=ws_step, ti_step=ti_step, bin_weights=self.freq_table_flat ) if inplace: self.__init__( aggregated_wind_rose.wind_directions, aggregated_wind_rose.wind_speeds, aggregated_wind_rose.turbulence_intensities, aggregated_wind_rose.freq_table, aggregated_wind_rose.value_table, aggregated_wind_rose.compute_zero_freq_occurrence, aggregated_wind_rose.heterogeneous_map, ) else: return aggregated_wind_rose def resample_by_interpolation(self, wd_step=None, ws_step=None, method="linear", inplace=False): """ Wrapper to upsample method for backwards compatibility """ return self.upsample(wd_step, ws_step, method, inplace) def upsample(self, wd_step=None, ws_step=None, ti_step=None, method="linear", inplace=False): """ Resample the wind TI rose using interpolation. The method can be either 'linear' or 'nearest'. If inplace is set to True, the current WindTIRose object will be updated with the resampled bins. Args: wd_step: Step size for wind direction resampling (float, optional). If None, the current step size will be used. Defaults to None. ws_step: Step size for wind speed resampling (float, optional). If None, the current step size will be used. Defaults to None. ti_step: Step size for turbulence intensity resampling (float, optional). If None, the current step size will be used. Defaults to None. method: Interpolation method to use (str, optional). Can be either 'linear' or 'nearest'. Defaults to "linear". inplace: Flag indicating whether to update the current WindRose object when True or return a new WindRose object when False (bool, optional). Defaults to False. Returns: WindRose: Resampled wind rose based on the provided or default step sizes. Only returned if inplace = False. """ if method == "linear": interpolator = LinearNDInterpolator elif method == "nearest": interpolator = NearestNDInterpolator else: raise ValueError( f"Unknown interpolation method: '{method}'. " "Available methods are 'linear' and 'nearest'" ) # First establish the current ws_step and wd_step and ti_step if len(self.wind_speeds) >= 2: ws_step_current = self.wind_speeds[1] - self.wind_speeds[0] else: # wind rose will have only a single wind speed, and we assume a ws_step of 1 ws_step_current = 1.0 if len(self.wind_directions) >= 2: wd_step_current = check_and_identify_step_size(wind_directions=self.wind_directions) else: # wind rose will have only a single wind direction, and we assume a wd_step of 1 wd_step_current = 1.0 if len(self.turbulence_intensities) >= 2: ti_step_current = self.turbulence_intensities[1] - self.turbulence_intensities[0] else: # wind rose will have only a single turbulence intensity, # and we assume a ti_step of 1 ti_step_current = 1.0 # If either ws_step or wd_step or ti_step is None, set it to the current step if ws_step is None: ws_step = ws_step_current if wd_step is None: wd_step = wd_step_current if ti_step is None: ti_step = ti_step_current # Make sure upsampling is appropriate if wd_step > wd_step_current: raise ValueError( f"Provided wd_step ({wd_step}) is larger than the current " f" wind direction step size. ({wd_step_current} degrees)" " Use the downsample method." ) if ws_step > ws_step_current: raise ValueError( f"Provided ws_step ({ws_step}) is larger than " f"the current wind speed step size. ({ws_step_current} m/s)" " Use the downsample method." ) if ti_step > ti_step_current: raise ValueError( f"Provided ti_step ({ti_step}) is larger than " f"the current turbulence intensity step size. ({ti_step_current})" " Use the downsample method." ) # Get the current wind directions in adjacent from (ie 0, 2 358 -> -2, 0 ,2) if len(self.wind_directions) >= 2: current_wind_directions, adjacent_sort_index = make_wind_directions_adjacent( self.wind_directions ) else: current_wind_directions = self.wind_directions adjacent_sort_index = np.arange(len(current_wind_directions)) # Identify the covered range of wind directions wd_range_min_current = np.min(current_wind_directions) - wd_step_current / 2.0 wd_range_max_current = np.max(current_wind_directions) + wd_step_current / 2.0 # Look for unlikely case where for example wind directions are 8, 28, ... 358 if wd_range_max_current > 360: # TODO: Handle this case without an error raise ValueError( "Cannot upsample wind rose for case when wind directions are defined" " such that 0 degrees is included by bins to the left of 0 degrees. " ) # Identify the new minimum wind direction wd_min_new = wd_range_min_current + wd_step / 2.0 wd_max_new = wd_range_max_current - wd_step / 2.0 new_wind_directions = np.arange(wd_min_new, wd_max_new + wd_step / 2.0, wd_step) # Set up the new wind speeds ws_range_min_current = np.min(self.wind_speeds) - ws_step_current / 2.0 ws_range_max_current = np.max(self.wind_speeds) + ws_step_current / 2.0 ws_min_new = ws_range_min_current + ws_step / 2.0 ws_max_new = ws_range_max_current - ws_step / 2.0 # Force the new ws_min to 0 if negative if ws_min_new < 0: ws_min_new = 0.0 new_wind_speeds = np.arange(ws_min_new, ws_max_new + ws_step / 2.0, ws_step) # Set up the new turbulence intensities ti_range_min_current = np.min(self.turbulence_intensities) - ti_step_current / 2.0 ti_range_max_current = np.max(self.turbulence_intensities) + ti_step_current / 2.0 ti_min_new = ti_range_min_current + ti_step / 2.0 ti_max_new = ti_range_max_current - ti_step / 2.0 # Force the new ti_min to 0 if negative if ti_min_new < 0: ti_min_new = 0.0 new_turbulence_intensities = np.arange(ti_min_new, ti_max_new + ti_step / 2.0, ti_step) # Set up for interpolation by copying the current values # and making sure they are sorted according to the adjacent wind directions wind_direction_column = current_wind_directions.copy() wind_speed_column = self.wind_speeds.copy() turbulence_intensity_column = self.turbulence_intensities.copy() freq_matrix = self.freq_table.copy()[adjacent_sort_index, :, :] if self.value_table is not None: value_matrix = self.value_table.copy()[adjacent_sort_index, :, :] else: value_matrix = None # For padding wind directions, there are two cases to consider. In the first, # say that the wind directions are 30, 40, 50. In this case it's important append # 30 and 50 to 35 and 55 to ensure the interpolation covers the full range of data # This is the case when wind directions doesn't cover the full range of possible # degrees (0-360) if np.abs((wd_range_min_current % 360.0) - (wd_range_max_current % 360.0)) > 1e-6: wind_direction_column = np.concatenate( ( np.array([wd_range_min_current]), wind_direction_column, np.array([wd_range_max_current]) ) ) freq_matrix = np.concatenate( (freq_matrix[0:1, :, :], freq_matrix, freq_matrix[-1:, :, :]), axis=0 ) if self.value_table is not None: value_matrix = np.concatenate( (value_matrix[0:1, :, :], value_matrix, value_matrix[-1:, :, :]), axis=0 ) # In the alternative case, where the wind directions cover the full range # ie, 0, 10, 20 30, ...350, then need to place 0 at 360 and 350 at -10 # to cover all interpolations else: # Pad wind direction column with min_wd + 360 wind_direction_column = np.concatenate( ( [np.max(self.wind_directions) - 360.0], wind_direction_column, [np.min(self.wind_directions) + 360.0], ) ) # Pad the remaining with the appropriate value freq_matrix = np.vstack( (freq_matrix[-1:, :, :], freq_matrix, freq_matrix[0:1, :, :]) ) if self.value_table is not None: value_matrix = np.vstack( (value_matrix[-1:, :, :], value_matrix, value_matrix[0:1, :, :]) ) # Pad out the wind speeds wind_speed_column = np.concatenate( ( np.array([ws_range_min_current]), wind_speed_column, np.array([ws_range_max_current]) ) ) freq_matrix = np.concatenate( (freq_matrix[:, 0, :][:, None, :], freq_matrix, freq_matrix[:, -1, :][:, None, :]), axis=1 ) if self.value_table is not None: value_matrix = np.concatenate( (value_matrix[:, 0:1, :], value_matrix, value_matrix[:, -1:, :]), axis=1 ) # Pad out the turbulence intensities turbulence_intensity_column = np.concatenate( ( np.array([ti_range_min_current]), turbulence_intensity_column, np.array([ti_range_max_current]) ) ) freq_matrix = np.concatenate( (freq_matrix[:, :, 0:1], freq_matrix, freq_matrix[:, :, -1:]), axis=2 ) if self.value_table is not None: value_matrix = np.concatenate( (value_matrix[:, :, 0:1], value_matrix, value_matrix[:, :, -1:]), axis=2 ) # Grid wind directions, wind speeds and turbulence intensities to match the # freq_matrix when flattened wd_grid, ws_grid, ti_grid = np.meshgrid( wind_direction_column, wind_speed_column, turbulence_intensity_column, indexing="ij" ) # Form wd_grid and ws_grid to a 2-column matrix wd_ws_ti_mat = np.array([wd_grid.flatten(), ws_grid.flatten(), ti_grid.flatten()]).T # Build the interpolator from wd_grid, ws_grid, to ti_matrix, freq_matrix and value_matrix freq_interpolator = interpolator(wd_ws_ti_mat, freq_matrix.flatten()) if self.value_table is not None: value_interpolator = interpolator(wd_ws_ti_mat, value_matrix.flatten()) # Grid the new wind directions and wind speeds new_wd_grid, new_ws_grid, new_ti_grid = np.meshgrid( new_wind_directions, new_wind_speeds, new_turbulence_intensities, indexing="ij" ) new_wd_ws_ti_mat = np.array( [new_wd_grid.flatten(), new_ws_grid.flatten(), new_ti_grid.flatten()] ).T # Create the new freq_matrix and value_matrix new_freq_matrix = freq_interpolator(new_wd_ws_ti_mat).reshape( (len(new_wind_directions), len(new_wind_speeds), len(new_turbulence_intensities)) ) if self.value_table is not None: new_value_matrix = value_interpolator(new_wd_ws_ti_mat).reshape( (len(new_wind_directions), len(new_wind_speeds), len(new_turbulence_intensities)) ) else: new_value_matrix = None # Wrap new_wind_directions to 0-360 new_wind_directions = new_wind_directions % 360 # Finally sort new_wind_directions, and re-order new_ti_matrix, new_freq_matrix # and new_value_matrix accordingly sort_indices = np.argsort(new_wind_directions) new_wind_directions = new_wind_directions[sort_indices] new_freq_matrix = new_freq_matrix[sort_indices, :, :] if self.value_table is not None: new_value_matrix = new_value_matrix[sort_indices, :, :] # Create the resampled wind rose resampled_wind_rose = WindTIRose( new_wind_directions, new_wind_speeds, new_turbulence_intensities, new_freq_matrix, new_value_matrix, self.compute_zero_freq_occurrence, self.heterogeneous_map, ) if inplace: self.__init__( resampled_wind_rose.wind_directions, resampled_wind_rose.wind_speeds, resampled_wind_rose.turbulence_intensities, resampled_wind_rose.freq_table, resampled_wind_rose.value_table, resampled_wind_rose.compute_zero_freq_occurrence, resampled_wind_rose.heterogeneous_map, ) else: return resampled_wind_rose def plot( self, ax=None, wind_rose_var="ws", color_map="viridis_r", wd_step=15.0, wind_rose_var_step=None, legend_kwargs={"label": "Wind speed [m/s]"}, ): """ This method creates a wind rose plot showing the frequency of occurrence of either the specified wind direction and wind speed bins or wind direction and turbulence intensity bins. If no axis is provided, a new one is created. **Note**: Based on code provided by Patrick Murphy from the University of Colorado Boulder. Args: ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes on which the wind rose is plotted. Defaults to None. wind_rose_var (str, optional): The variable to display in the wind rose plot in addition to wind direction. If wind_rose_var = "ws", wind speed frequencies will be plotted. If wind_rose_var = "ti", turbulence intensity frequencies will be plotted. Defaults to "ws". color_map (str, optional): Colormap to use. Defaults to 'viridis_r'. wd_step (float, optional): Step size for wind direction. Defaults to 15 degrees. wind_rose_var_step (float, optional): Step size for other wind rose variable. Defaults to None. If unspecified, a value of 5 m/s will be used if wind_rose_var = "ws", and a value of 4% will be used if wind_rose_var = "ti". legend_kwargs (dict, optional): Keyword arguments to be passed to ax.legend(). Defaults to {"label": "Wind speed [m/s]"}. Returns: :py:class:`matplotlib.pyplot.axes`: A figure axes object containing the plotted wind rose. """ if wind_rose_var not in {"ws", "ti"}: raise ValueError( 'wind_rose_var must be either "ws" or "ti" for wind speed or turbulence intensity.' ) # Get a aggregated wind_rose if wind_rose_var == "ws": if wind_rose_var_step is None: wind_rose_var_step = 5.0 wind_rose_aggregated = self.downsample(wd_step, ws_step=wind_rose_var_step) var_bins = wind_rose_aggregated.wind_speeds freq_table = wind_rose_aggregated.freq_table.sum(2) # sum along TI dimension else: # wind_rose_var == "ti" if wind_rose_var_step is None: wind_rose_var_step = 0.04 wind_rose_aggregated = self.downsample(wd_step, ti_step=wind_rose_var_step) var_bins = wind_rose_aggregated.turbulence_intensities freq_table = wind_rose_aggregated.freq_table.sum(1) # sum along wind speed dimension wd_bins = wind_rose_aggregated.wind_directions # Set up figure if ax is None: _, ax = plt.subplots(subplot_kw={"polar": True}) # Get a color array color_array = plt.get_cmap(color_map, len(var_bins)) norm_wv = mpl.colors.Normalize(vmin=np.min(var_bins), vmax=np.max(var_bins)) sm_wv = mpl.cm.ScalarMappable(norm=norm_wv, cmap=color_array) for wd_idx, wd in enumerate(wd_bins): rects = [] freq_table_sub = freq_table[wd_idx, :].flatten() for var_idx, ws in reversed(list(enumerate(var_bins))): plot_val = freq_table_sub[: var_idx + 1].sum() rects.append( ax.bar( np.radians(wd), plot_val, width=0.9 * np.radians(wd_step), color=color_array(var_idx), edgecolor="k", ) ) # Configure the plot try: ax.figure.colorbar(sm_wv, ax=ax, **legend_kwargs) ax.figure.tight_layout() except TypeError: ax.legend(reversed(rects), var_bins, **legend_kwargs) ax.figure.get_children()[-1].remove() # Remove the empty colorbar ax.set_theta_direction(-1) ax.set_theta_offset(np.pi / 2.0) ax.set_theta_zero_location("N") ax.set_xticks(np.arange(0, 2 * np.pi, np.pi / 4)) ax.set_xticklabels(["N", "NE", "E", "SE", "S", "SW", "W", "NW"]) return ax def plot_ti_over_ws( self, ax=None, marker=".", ls="-", color="k", ): """ Plot the mean turbulence intensity against wind speed. Args: ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes on which the mean turbulence intensity is plotted. Defaults to None. marker (str, optional): Scatter plot marker style. Defaults to ".". ls (str, optional): Scatter plot line style. Defaults to "None". color (str, optional): Scatter plot color. Defaults to "k". Returns: :py:class:`matplotlib.pyplot.axes`: A figure axes object containing the plotted mean turbulence intensities as a function of wind speed. """ # TODO: Plot individual points and std. devs. of TI in addition to mean # values # Set up figure if ax is None: _, ax = plt.subplots() # get mean TI for each wind speed by averaging along wind direction and # TI dimensions mean_ti_values = (self.ti_grid * self.freq_table).sum((0, 2)) / self.freq_table.sum((0, 2)) ax.plot(self.wind_speeds, mean_ti_values * 100, marker=marker, ls=ls, color=color) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Mean Turbulence Intensity (%)") ax.grid(True) def assign_value_using_wd_ws_ti_function(self, func, normalize=False): """ Use the passed in function to assign new values to the value table. Args: func (function): Function which accepts wind_directions as its first argument, wind_speeds as its second argument, and turbulence_intensities as its third argument and returns values. normalize (bool, optional): If True, the value array will be normalized by the mean value. Defaults to False. """ self.value_table = func(self.wd_grid, self.ws_grid, self.ti_grid) if normalize: self.value_table /= np.sum(self.freq_table * self.value_table) self._build_gridded_and_flattened_version() def assign_value_piecewise_linear( self, value_zero_ws=1.425, ws_knee=4.5, slope_1=0.0, slope_2=-0.135, limit_to_zero=False, normalize=False, ): """ Define value as a continuous piecewise linear function of wind speed with two line segments. The default parameters yield a value function that approximates the normalized mean electricity price vs. wind speed curve for the SPP market in the U.S. for years 2018-2020 from figure 7 in Simley et al. "The value of wake steering wind farm flow control in US energy markets," Wind Energy Science, 2024. https://doi.org/10.5194/wes-9-219-2024. This default value function is constant at low wind speeds, then linearly decreases above 4.5 m/s. Args: value_zero_ws (float, optional): The value when wind speed is zero. Defaults to 1.425. ws_knee (float, optional): The wind speed separating line segments 1 and 2. Default = 4.5 m/s. slope_1 (float, optional): The slope of the first line segment (unit of value per m/s). Defaults to zero. slope_2 (float, optional): The slope of the second line segment (unit of value per m/s). Defaults to -0.135. limit_to_zero (bool, optional): If True, negative values will be set to zero. Defaults to False. normalize (bool, optional): If True, the value array will be normalized by the mean value. Defaults to False. """ def piecewise_linear_value_func(wind_directions, wind_speeds, turbulence_intensities): value = np.zeros_like(wind_speeds, dtype=float) value[wind_speeds < ws_knee] = ( slope_1 * wind_speeds[wind_speeds < ws_knee] + value_zero_ws ) offset_2 = (slope_1 - slope_2) * ws_knee + value_zero_ws value[wind_speeds >= ws_knee] = slope_2 * wind_speeds[wind_speeds >= ws_knee] + offset_2 if limit_to_zero: value[value < 0] = 0.0 return value self.assign_value_using_wd_ws_ti_function(piecewise_linear_value_func, normalize) def plot_value_over_ws( self, ax=None, marker=".", ls="None", color="k", ): """ Scatter plot the value of the energy generated against wind speed. Args: ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes on which the value is plotted. Defaults to None. marker (str, optional): Scatter plot marker style. Defaults to ".". ls (str, optional): Scatter plot line style. Defaults to "None". color (str, optional): Scatter plot color. Defaults to "k". Returns: :py:class:`matplotlib.pyplot.axes`: A figure axes object containing the plotted value as a function of wind speed. """ # TODO: Plot mean and std. devs. of value in each ws bin in addition to # individual points # Set up figure if ax is None: _, ax = plt.subplots() ax.plot(self.ws_flat, self.value_table_flat, marker=marker, ls=ls, color=color) ax.set_xlabel("Wind Speed (m/s)") ax.set_ylabel("Value") ax.grid(True) @staticmethod def read_csv_long( file_path: str, ws_col: str = "wind_speeds", wd_col: str = "wind_directions", ti_col: str = "turbulence_intensities", freq_col: str | None = None, sep: str = ",", ) -> WindTIRose: """ Read a long-formatted CSV file into the WindTIRose object. By long, what is meant is that the wind speed, wind direction and turbulence intensities combination is given for each row in the CSV file. The wind speed, wind direction, and turbulence intensity are given in separate columns, and the frequency of occurrence of each combination is given in a separate column. The frequency column is optional, and if not provided, uniform frequency of all bins is assumed. Args: file_path (str): Path to the CSV file. ws_col (str): Name of the column in the CSV file that contains the wind speed values. Defaults to 'wind_speeds'. wd_col (str): Name of the column in the CSV file that contains the wind direction values. Defaults to 'wind_directions'. ti_col (str): Name of the column in the CSV file that contains the turbulence intensity values. freq_col (str): Name of the column in the CSV file that contains the frequency values. Defaults to None in which case constant frequency assumed. sep (str): Delimiter to use. Defaults to ','. Returns: WindRose: Wind rose object created from the CSV file. """ # Read in the CSV file df = pd.read_csv(file_path, sep=sep) # Check that the required columns are present if ws_col not in df.columns: raise ValueError(f"Column {ws_col} not found in CSV file") if wd_col not in df.columns: raise ValueError(f"Column {wd_col} not found in CSV file") if ti_col not in df.columns: raise ValueError(f"Column {ti_col} not found in CSV file") if freq_col not in df.columns and freq_col is not None: raise ValueError(f"Column {freq_col} not found in CSV file") # Get the wind speed, wind direction, and turbulence intensity values wind_directions = df[wd_col].values wind_speeds = df[ws_col].values turbulence_intensities = df[ti_col].values if freq_col is not None: freq_values = df[freq_col].values else: freq_values = np.ones(len(wind_speeds)) # Normalize freq_values freq_values = freq_values / np.sum(freq_values) # Get the unique values of wind directions and wind speeds unique_wd = np.unique(wind_directions) unique_ws = np.unique(wind_speeds) unique_ti = np.unique(turbulence_intensities) # Get the step side for wind direction and wind speed wd_step = unique_wd[1] - unique_wd[0] ws_step = unique_ws[1] - unique_ws[0] ti_step = unique_ti[1] - unique_ti[0] # Now use TimeSeries to create a wind rose time_series = TimeSeries(wind_directions, wind_speeds, turbulence_intensities) # Now build a new wind rose using the new steps return time_series.to_WindTIRose( wd_step=wd_step, ws_step=ws_step, ti_step=ti_step, bin_weights=freq_values ) class TimeSeries(WindDataBase): """ The TimeSeries class is used to drive FLORIS and optimization operations in which the inflow is by a sequence of wind direction, wind speed and turbulence intensity values. Each input of wind direction, wind speed, and turbulence intensity can be assigned as an array of values or a single value. At least one of wind_directions, wind_speeds, or turbulence_intensities must be an array. If arrays are provided, they must be the same length as the other arrays or the single values. If single values are provided, then an array of the same length as the other arrays will be created with the single value. Args: wind_directions (float, NDArrayFloat): Wind direction. Can be a single value or an array of values. wind_speeds (float, NDArrayFloat): Wind speed. Can be a single value or an array of values. turbulence_intensities (float, NDArrayFloat): Turbulence intensity. Can be a single value or an array of values. values (NDArrayFloat, optional): Values associated with each wind direction, wind speed, and turbulence intensity. Defaults to None. heterogeneous_map (HeterogeneousMap, optional): A HeterogeneousMap object to define background heterogeneous inflow condition as a function of wind direction and wind speed. Alternatively, a dictionary can be passed in to define a HeterogeneousMap object. Defaults to None. heterogeneous_inflow_config_by_wd (dict, optional): A dictionary containing the following which can be used to define a heterogeneous_map object (note this parameter is kept for backwards compatibility and is not recommended for use): * 'x': A 1D NumPy array (size num_points) of x-coordinates (meters). * 'y': A 1D NumPy array (size num_points) of y-coordinates (meters). * 'speed_multipliers': A 2D NumPy array (size num_wd (or num_ws) x num_points) of speed multipliers. If neither wind_directions nor wind_speeds are defined, then this should be a single row array * 'wind_directions': A 1D NumPy array (size num_wd) of wind directions (degrees). Optional. * 'wind_speeds': A 1D NumPy array (size num_ws) of wind speeds (m/s). Optional. Defaults to None. heterogeneous_inflow_config (dict, optional): A dictionary containing the following keys. Defaults to None. * 'speed_multipliers': A 2D NumPy array (size n_findex x num_points) of speed multipliers. * 'x': A 1D NumPy array (size num_points) of x-coordinates (meters). * 'y': A 1D NumPy array (size num_points) of y-coordinates (meters). multidim_conditions (dict, optional): A dictionary containing multidimensional inflow conditions. Each key is the name of the condition, and each value is either a 1D NumPy array of size n_findex containing the condition values, or a scalar value that will be broadcast to size n_findex. Defaults to None. """ def __init__( self, wind_directions: float | NDArrayFloat, wind_speeds: float | NDArrayFloat, turbulence_intensities: float | NDArrayFloat, values: NDArrayFloat | None = None, heterogeneous_map: HeterogeneousMap | dict | None = None, heterogeneous_inflow_config_by_wd: dict | None = None, heterogeneous_inflow_config: dict | None = None, multidim_conditions: dict | None = None, ): # Check that wind_directions, wind_speeds, and turbulence_intensities are either numpy array # of floats if not isinstance(wind_directions, (float, np.ndarray)): raise TypeError("wind_directions must be a float or a NumPy array") if not isinstance(wind_speeds, (float, np.ndarray)): raise TypeError("wind_speeds must be a float or a NumPy array") if not isinstance(turbulence_intensities, (float, np.ndarray)): raise TypeError("turbulence_intensities must be a float or a NumPy array") # At least one of wind_directions, wind_speeds, or turbulence_intensities must be an array if ( not isinstance(wind_directions, np.ndarray) and not isinstance(wind_speeds, np.ndarray) and not isinstance(turbulence_intensities, np.ndarray) ): raise TypeError( "At least one of wind_directions, wind_speeds, or " " turbulence_intensities must be a NumPy array" ) # For each of wind_directions, wind_speeds, and turbulence_intensities provided as # an array, confirm they are the same length if isinstance(wind_directions, np.ndarray) and isinstance(wind_speeds, np.ndarray): if len(wind_directions) != len(wind_speeds): raise ValueError( "wind_directions and wind_speeds must be the same length if provided as arrays" ) if isinstance(wind_directions, np.ndarray) and isinstance( turbulence_intensities, np.ndarray ): if len(wind_directions) != len(turbulence_intensities): raise ValueError( "wind_directions and turbulence_intensities must be " "the same length if provided as arrays" ) if isinstance(wind_speeds, np.ndarray) and isinstance(turbulence_intensities, np.ndarray): if len(wind_speeds) != len(turbulence_intensities): raise ValueError( "wind_speeds and turbulence_intensities must be the " "same length if provided as arrays" ) # For each of wind_directions, wind_speeds, and turbulence_intensities # provided as a single value, set them # to be the same length as those passed in as arrays if isinstance(wind_directions, float): if isinstance(wind_speeds, np.ndarray): wind_directions = np.full(len(wind_speeds), wind_directions) elif isinstance(turbulence_intensities, np.ndarray): wind_directions = np.full(len(turbulence_intensities), wind_directions) if isinstance(wind_speeds, float): if isinstance(wind_directions, np.ndarray): wind_speeds = np.full(len(wind_directions), wind_speeds) elif isinstance(turbulence_intensities, np.ndarray): wind_speeds = np.full(len(turbulence_intensities), wind_speeds) if isinstance(turbulence_intensities, float): if isinstance(wind_directions, np.ndarray): turbulence_intensities = np.full(len(wind_directions), turbulence_intensities) elif isinstance(wind_speeds, np.ndarray): turbulence_intensities = np.full(len(wind_speeds), turbulence_intensities) # Record findex self.n_findex = len(wind_directions) # If values is not None, must be same length as wind_directions/wind_speeds/ if values is not None: if len(wind_directions) != len(values): raise ValueError("wind_directions and values must be the same length") self.wind_directions = wind_directions self.wind_speeds = wind_speeds self.turbulence_intensities = turbulence_intensities self.values = values # Check that at most one of heterogeneous_inflow_config_by_wd, # heterogeneous_map and heterogeneous_inflow_config is not None if ( sum( [ heterogeneous_inflow_config_by_wd is not None, heterogeneous_map is not None, heterogeneous_inflow_config is not None, ] ) > 1 ): raise ValueError( "Only one of heterogeneous_inflow_config_by_wd, " + "heterogeneous_map, and heterogeneous_inflow_config can be not None." ) # if heterogeneous_inflow_config is not None, then the speed_multipliers # must be the same length as wind_directions # in the 0th dimension if heterogeneous_inflow_config is not None: if len(heterogeneous_inflow_config["speed_multipliers"]) != len(wind_directions): raise ValueError("speed_multipliers must be the same length as wind_directions") # Check heterogeneous_inflow_config and save self.check_heterogeneous_inflow_config(heterogeneous_inflow_config) self.heterogeneous_inflow_config = heterogeneous_inflow_config else: self.heterogeneous_inflow_config = None # If heterogeneous_inflow_config_by_wd is not None, then create a HeterogeneousMap object # using the dictionary if heterogeneous_inflow_config_by_wd is not None: # TODO: In future, add deprecation warning for this parameter here self.heterogeneous_map = HeterogeneousMap(**heterogeneous_inflow_config_by_wd) # Else if heterogeneous_map is not None elif heterogeneous_map is not None: # If heterogeneous_map is a dictionary, then create a HeterogeneousMap object if isinstance(heterogeneous_map, dict): self.heterogeneous_map = HeterogeneousMap(**heterogeneous_map) # Else if heterogeneous_map is a HeterogeneousMap object, then save it elif isinstance(heterogeneous_map, HeterogeneousMap): self.heterogeneous_map = heterogeneous_map # Else raise an error else: raise ValueError( "heterogeneous_map must be a HeterogeneousMap object or a dictionary." ) # Else if neither heterogeneous_map nor heterogeneous_inflow_config_by_wd are defined, # then set heterogeneous_map to None else: self.heterogeneous_map = None # Handle the multidim_conditions if multidim_conditions is not None: # Check that each value in the dictionary is either a 1D NumPy array of size n_findex # or a scalar value if is_all_scalar_dict(multidim_conditions): # Leave all scalar for performance purposes pass else: for key, value in multidim_conditions.items(): if isinstance(value, np.ndarray): if value.shape != (self.n_findex,): raise ValueError( f"multidim_conditions[{key}] must be of size n_findex" f"({self.n_findex})" ) else: raise ValueError( f"multidim_conditions[{key}] must be a 1D NumPy array of size n_findex " "or a scalar value." ) self.multidim_conditions = multidim_conditions def unpack(self): """ Unpack the time series data in a manner consistent with wind rose unpack """ # to match wind_rose, make a uniform frequency uniform_frequency = np.ones_like(self.wind_directions) uniform_frequency = uniform_frequency / uniform_frequency.sum() # If heterogeneous_map is not None, then update # heterogeneous_inflow_config to match wind_directions_unpack if self.heterogeneous_map is not None: heterogeneous_inflow_config = self.heterogeneous_map.get_heterogeneous_inflow_config( wind_directions=self.wind_directions, wind_speeds=self.wind_speeds ) else: heterogeneous_inflow_config = self.heterogeneous_inflow_config return ( self.wind_directions, self.wind_speeds, self.turbulence_intensities, uniform_frequency, self.values, heterogeneous_inflow_config, ) def _wrap_wind_directions_near_360(self, wind_directions, wd_step): """ Wraps the wind directions using `wd_step` to produce a wrapped version where values between [360 - wd_step/2.0, 360] get mapped to negative numbers for binning. Args: wind_directions (NDArrayFloat): NumPy array of wind directions. wd_step (float): Step size for wind direction. Returns: NDArrayFloat: Wrapped version of wind directions. """ wind_directions_wrapped = wind_directions.copy() mask = wind_directions_wrapped >= 360 - wd_step / 2.0 wind_directions_wrapped[mask] = wind_directions_wrapped[mask] - 360.0 return wind_directions_wrapped def assign_ti_using_wd_ws_function(self, func): """ Use the passed in function to new assign values to turbulence_intensities Args: func (function): Function which accepts wind_directions as its first argument and wind_speeds as second argument and returns turbulence_intensities """ self.turbulence_intensities = func(self.wind_directions, self.wind_speeds) def assign_ti_using_IEC_method(self, Iref=0.07, offset=3.8): """ Define TI as a function of wind speed by specifying an Iref and offset value as in the normal turbulence model in the IEC 61400-1 standard Args: Iref (float): Reference turbulence level, defined as the expected value of TI at 15 m/s. Default = 0.07. Note this value is lower than the values of Iref for turbulence classes A, B, and C in the IEC standard (0.16, 0.14, and 0.12, respectively), but produces TI values more in line with those typically used in FLORIS. When the default Iref and offset are used, the TI at 8 m/s is 8.6%. offset (float): Offset value to equation. Default = 3.8, as defined in the IEC standard to give the expected value of TI for each wind speed. """ if (Iref < 0) or (Iref > 1): raise ValueError("Iref must be >= 0 and <=1") def iref_func(wind_directions, wind_speeds): sigma_1 = Iref * (0.75 * wind_speeds + offset) return sigma_1 / wind_speeds self.assign_ti_using_wd_ws_function(iref_func) def assign_value_using_wd_ws_function(self, func, normalize=False): """ Use the passed in function to assign new values to the value table. Args: func (function): Function which accepts wind_directions as its first argument and wind_speeds as second argument and returns values. normalize (bool, optional): If True, the value array will be normalized by the mean value. Defaults to False. """ self.values = func(self.wind_directions, self.wind_speeds) if normalize: self.values /= np.mean(self.values) def assign_value_piecewise_linear( self, value_zero_ws=1.425, ws_knee=4.5, slope_1=0.0, slope_2=-0.135, limit_to_zero=False, normalize=False, ): """ Define value as a continuous piecewise linear function of wind speed with two line segments. The default parameters yield a value function that approximates the normalized mean electricity price vs. wind speed curve for the SPP market in the U.S. for years 2018-2020 from figure 7 in Simley et al. "The value of wake steering wind farm flow control in US energy markets," Wind Energy Science, 2024. https://doi.org/10.5194/wes-9-219-2024. This default value function is constant at low wind speeds, then linearly decreases above 4.5 m/s. Args: value_zero_ws (float, optional): The value when wind speed is zero. Defaults to 1.425. ws_knee (float, optional): The wind speed separating line segments 1 and 2. Default = 4.5 m/s. slope_1 (float, optional): The slope of the first line segment (unit of value per m/s). Defaults to zero. slope_2 (float, optional): The slope of the second line segment (unit of value per m/s). Defaults to -0.135. limit_to_zero (bool, optional): If True, negative values will be set to zero. Defaults to False. normalize (bool, optional): If True, the value array will be normalized by the mean value. Defaults to False. """ def piecewise_linear_value_func(wind_directions, wind_speeds): value = np.zeros_like(wind_speeds, dtype=float) value[wind_speeds < ws_knee] = ( slope_1 * wind_speeds[wind_speeds < ws_knee] + value_zero_ws ) offset_2 = (slope_1 - slope_2) * ws_knee + value_zero_ws value[wind_speeds >= ws_knee] = slope_2 * wind_speeds[wind_speeds >= ws_knee] + offset_2 if limit_to_zero: value[value < 0] = 0.0 return value self.assign_value_using_wd_ws_function(piecewise_linear_value_func, normalize) def to_WindRose(self, wd_step=2.0, ws_step=1.0, wd_edges=None, ws_edges=None, bin_weights=None): """ Converts the TimeSeries data to a WindRose. Args: wd_step (float, optional): Step size for wind direction (default is 2.0). ws_step (float, optional): Step size for wind speed (default is 1.0). wd_edges (NDArrayFloat, optional): Custom wind direction edges. Defaults to None. ws_edges (NDArrayFloat, optional): Custom wind speed edges. Defaults to None. bin_weights (NDArrayFloat, optional): Bin weights for resampling. Note these are primarily used by the downsample() method. Defaults to None. Returns: WindRose: A WindRose object based on the TimeSeries data. Notes: - If `wd_edges` is defined, it uses it to produce the bin centers. - If `wd_edges` is not defined, it determines `wd_edges` from the step and data. - If `ws_edges` is defined, it uses it for wind speed edges. - If `ws_edges` is not defined, it determines `ws_edges` from the step and data. """ # If wd_edges is defined, then use it to produce the bin centers if wd_edges is not None: wd_step = wd_edges[1] - wd_edges[0] # use wd_step to produce a wrapped version of wind_directions wind_directions_wrapped = self._wrap_wind_directions_near_360( self.wind_directions, wd_step ) # Else, determine wd_edges from the step and data else: wd_edges = np.arange(0.0 - wd_step / 2.0, 360.0, wd_step) # use wd_step to produce a wrapped version of wind_directions wind_directions_wrapped = self._wrap_wind_directions_near_360( self.wind_directions, wd_step ) # Only keep the range with values in it wd_edges = wd_edges[wd_edges + wd_step > wind_directions_wrapped.min()] wd_edges = wd_edges[wd_edges - wd_step <= wind_directions_wrapped.max()] # Define the centers from the edges wd_centers = wd_edges[:-1] + wd_step / 2.0 # Repeat for wind speeds if ws_edges is not None: ws_step = ws_edges[1] - ws_edges[0] else: ws_edges = np.arange(0.0 - ws_step / 2.0, 50.0, ws_step) # Only keep the range with values in it ws_edges = ws_edges[ws_edges + ws_step > self.wind_speeds.min()] ws_edges = ws_edges[ws_edges - ws_step <= self.wind_speeds.max()] # Define the centers from the edges ws_centers = ws_edges[:-1] + ws_step / 2.0 # Now use pandas to get the tables need for wind rose df = pd.DataFrame( { "wd": wind_directions_wrapped, "ws": self.wind_speeds, "freq_val": np.ones(len(wind_directions_wrapped)), } ) # If bin_weights are passed in, apply these to the frequency # this is mostly used when resampling the wind rose if bin_weights is not None: df = df.assign(freq_val=df["freq_val"] * bin_weights) # Add turbulence intensities to dataframe df = df.assign(turbulence_intensities=self.turbulence_intensities) # If values is not none, add to dataframe if self.values is not None: df = df.assign(values=self.values) # Bin wind speed and wind direction and then group things up df = ( df.assign( wd_bin=pd.cut( df.wd, bins=wd_edges, labels=wd_centers, right=False, include_lowest=True ) ) .assign( ws_bin=pd.cut( df.ws, bins=ws_edges, labels=ws_centers, right=False, include_lowest=True ) ) .drop(["wd", "ws"], axis=1) ) # Convert wd_bin and ws_bin to categoricals to ensure all combinations # are considered and then group wd_cat = CategoricalDtype(categories=wd_centers, ordered=True) ws_cat = CategoricalDtype(categories=ws_centers, ordered=True) df = ( df.assign(wd_bin=df["wd_bin"].astype(wd_cat)) .assign(ws_bin=df["ws_bin"].astype(ws_cat)) .groupby(["wd_bin", "ws_bin"], observed=False) .agg(["sum", "mean"]) ) # Flatten and combine levels using an underscore df.columns = ["_".join(col) for col in df.columns] # Collect the frequency table and reshape freq_table = df["freq_val_sum"].values.copy() freq_table = freq_table / freq_table.sum() freq_table = freq_table.reshape((len(wd_centers), len(ws_centers))) # Compute the TI table ti_table = df["turbulence_intensities_mean"].values.copy() ti_table = ti_table.reshape((len(wd_centers), len(ws_centers))) # If values is not none, compute the table if self.values is not None: value_table = df["values_mean"].values.copy() value_table = value_table.reshape((len(wd_centers), len(ws_centers))) else: value_table = None # Return a WindRose return WindRose( wd_centers, ws_centers, ti_table, freq_table, value_table, self.heterogeneous_map, ) def to_WindTIRose( self, wd_step=2.0, ws_step=1.0, ti_step=0.02, wd_edges=None, ws_edges=None, ti_edges=None, bin_weights=None, ): """ Converts the TimeSeries data to a WindTIRose. Args: wd_step (float, optional): Step size for wind direction (default is 2.0). ws_step (float, optional): Step size for wind speed (default is 1.0). ti_step (float, optional): Step size for turbulence intensity (default is 0.02). wd_edges (NDArrayFloat, optional): Custom wind direction edges. Defaults to None. ws_edges (NDArrayFloat, optional): Custom wind speed edges. Defaults to None. ti_edges (NDArrayFloat, optional): Custom turbulence intensity edges. Defaults to None. bin_weights (NDArrayFloat, optional): Bin weights for resampling. Note these are primarily used by the downsample() method. Defaults to None. Returns: WindRose: A WindTIRose object based on the TimeSeries data. Notes: - If `wd_edges` is defined, it uses it to produce the wind direction bin edges. - If `wd_edges` is not defined, it determines `wd_edges` from the step and data. - If `ws_edges` is defined, it uses it for wind speed edges. - If `ws_edges` is not defined, it determines `ws_edges` from the step and data. - If `ti_edges` is defined, it uses it for turbulence intensity edges. - If `ti_edges` is not defined, it determines `ti_edges` from the step and data. """ # If wd_edges is defined, then use it to produce the bin centers if wd_edges is not None: wd_step = wd_edges[1] - wd_edges[0] # use wd_step to produce a wrapped version of wind_directions wind_directions_wrapped = self._wrap_wind_directions_near_360( self.wind_directions, wd_step ) # Else, determine wd_edges from the step and data else: wd_edges = np.arange(0.0 - wd_step / 2.0, 360.0, wd_step) # use wd_step to produce a wrapped version of wind_directions wind_directions_wrapped = self._wrap_wind_directions_near_360( self.wind_directions, wd_step ) # Only keep the range with values in it wd_edges = wd_edges[wd_edges + wd_step > wind_directions_wrapped.min()] wd_edges = wd_edges[wd_edges - wd_step <= wind_directions_wrapped.max()] # Define the centers from the edges wd_centers = wd_edges[:-1] + wd_step / 2.0 # Repeat for wind speeds if ws_edges is not None: ws_step = ws_edges[1] - ws_edges[0] else: ws_edges = np.arange(0.0 - ws_step / 2.0, 50.0, ws_step) # Only keep the range with values in it ws_edges = ws_edges[ws_edges + ws_step > self.wind_speeds.min()] ws_edges = ws_edges[ws_edges - ws_step <= self.wind_speeds.max()] # Define the centers from the edges ws_centers = ws_edges[:-1] + ws_step / 2.0 # Repeat for turbulence intensities if ti_edges is not None: ti_step = ti_edges[1] - ti_edges[0] else: ti_edges = np.arange(0.0 - ti_step / 2.0, 1.0, ti_step) # Only keep the range with values in it ti_edges = ti_edges[ti_edges + ti_step > self.turbulence_intensities.min()] ti_edges = ti_edges[ti_edges - ti_step <= self.turbulence_intensities.max()] # Define the centers from the edges ti_centers = ti_edges[:-1] + ti_step / 2.0 # Now use pandas to get the tables need for wind rose df = pd.DataFrame( { "wd": wind_directions_wrapped, "ws": self.wind_speeds, "ti": self.turbulence_intensities, "freq_val": np.ones(len(wind_directions_wrapped)), } ) # If bin_weights are passed in, apply these to the frequency # this is mostly used when resampling the wind rose if bin_weights is not None: df = df.assign(freq_val=df["freq_val"] * bin_weights) # If values is not none, add to dataframe if self.values is not None: df = df.assign(values=self.values) # Bin wind speed, wind direction, and turbulence intensity and then group things up df = ( df.assign( wd_bin=pd.cut( df.wd, bins=wd_edges, labels=wd_centers, right=False, include_lowest=True ) ) .assign( ws_bin=pd.cut( df.ws, bins=ws_edges, labels=ws_centers, right=False, include_lowest=True ) ) .assign( ti_bin=pd.cut( df.ti, bins=ti_edges, labels=ti_centers, right=False, include_lowest=True ) ) .drop(["wd", "ws", "ti"], axis=1) ) # Convert wd_bin, ws_bin, and ti_bin to categoricals to ensure all # combinations are considered and then group wd_cat = CategoricalDtype(categories=wd_centers, ordered=True) ws_cat = CategoricalDtype(categories=ws_centers, ordered=True) ti_cat = CategoricalDtype(categories=ti_centers, ordered=True) df = ( df.assign(wd_bin=df["wd_bin"].astype(wd_cat)) .assign(ws_bin=df["ws_bin"].astype(ws_cat)) .assign(ti_bin=df["ti_bin"].astype(ti_cat)) .groupby(["wd_bin", "ws_bin", "ti_bin"], observed=False) .agg(["sum", "mean"]) ) # Flatten and combine levels using an underscore df.columns = ["_".join(col) for col in df.columns] # Collect the frequency table and reshape freq_table = df["freq_val_sum"].values.copy() freq_table = freq_table / freq_table.sum() freq_table = freq_table.reshape((len(wd_centers), len(ws_centers), len(ti_centers))) # If values is not none, compute the table if self.values is not None: value_table = df["values_mean"].values.copy() value_table = value_table.reshape((len(wd_centers), len(ws_centers), len(ti_centers))) else: value_table = None # Return a WindTIRose return WindTIRose( wd_centers, ws_centers, ti_centers, freq_table, value_table, self.heterogeneous_map, ) class WindRoseWRG(WindDataBase): """ The WindRoseWRG class is a WindData object the represents a wind resource grid (WRG) file to FLORIS. As a WindData object it can be passed to the FlorisModel.set method. A WRG file represents a wind resource as a grid of points where each point has a separate wind rose define by the frequency of each wind direction and the Weibull parameters for each wind direction. WindRoseWRG objects are provided the layout of a wind farm and computes a wind rose at each point in the layout. The wind rose at each point is computed by interpolating the weibull parameter in the WRG file to the point in the layout and using them to compute a WindRose object. Each WindRose object shares wind direction and wind speed, only the frequencies differ. When running a FlorisModel with a WindRoseWRG object, most behaviors are the same except functions which compute an expected value, use separate frequencies for each turbine to weight the individual power bins. Args: filename (str): The name of the WRG file to read. wd_step (float, optional): Step size to use resampling the wind directions given by the WRG file. If None, wd_step and wind_directions are set by the number of sectors in the WRG file. Defaults to None. wind_speeds (NDArrayFloat, optional): Wind speeds to use in the wind rose. Defaults to np.arange(0.0, 26.0, 1.0). ti_table (float, optional): Turbulence intensities table to use for each WindRose object. As in the WindRose ti_table, this can be a single value or an array of values. If an array of values is provided, it must be (len(wind_directions) x len(wind_speeds)). Defaults to 0.06. """ def __init__( self, filename, wd_step=None, wind_speeds=np.arange(0.0, 26.0, 1.0), ti_table=0.06 ): # Read in the WRG file self.filename = filename self.read_wrg_file(filename) # If wd_step is None, then use the wind directions in the WRG file if wd_step is None: self.wind_directions = self._wind_directions_wrg_file self.wd_step = self.wind_directions[1] - self.wind_directions[0] else: self.wind_directions = np.arange(0.0, 360.0, wd_step) self.wd_step = wd_step # Initialize the layouts which will need to be specified self.layout_x = None self.layout_y = None # Save the wind speeds and ti_table self.wind_speeds = wind_speeds self.ti_table = ti_table # Initialize the flat arrays, these will depend on the specified wind speeds self.wd_flat = None self.ws_flat = None self.non_zero_freq_mask = None def read_wrg_file(self, filename): """ Read the contents of a WRG file and store the data in the object. Args: filename (str): The name of the WRG file to read. """ # Read the file into data with open(filename, "r") as f: data = f.readlines() # Read the header header = data[0].split() self.nx = int(header[0]) self.ny = int(header[1]) self.xmin = float(header[2]) self.ymin = float(header[3]) self.grid_size = float(header[4]) # The grid of points is implied by the values above self.x_array = np.arange(self.nx) * self.grid_size + self.xmin self.y_array = np.arange(self.ny) * self.grid_size + self.ymin # The number of grid points (n_gid) is the product of the number of points in x and y self.n_gid = self.nx * self.ny # Finally get the number of sectors from the first line after the header self.n_sectors = int(data[1][70:72]) # The wind directions are implied by the number of sectors self._wind_directions_wrg_file = np.arange(0.0, 360.0, 360.0 / self.n_sectors) # Initialize the data arrays which have the same number of # elements as the number of grid points x_gid = np.zeros(self.n_gid) y_gid = np.zeros(self.n_gid) z_gid = np.zeros(self.n_gid) h_gid = np.zeros(self.n_gid) # Initialize the data arrays which are n_gid x n_sectors sector_freq_gid = np.zeros((self.n_gid, self.n_sectors)) weibull_A_gid = np.zeros((self.n_gid, self.n_sectors)) weibull_k_gid = np.zeros((self.n_gid, self.n_sectors)) # Loop through the data and extract the values for gid in range(self.n_gid): line = data[1 + gid] x_gid[gid] = float(line[10:20]) y_gid[gid] = float(line[20:30]) z_gid[gid] = float(line[30:38]) h_gid[gid] = float(line[38:43]) for sector in range(self.n_sectors): # The frequency of the wind in this sector is in probablility * 1000 sector_freq_gid[gid, sector] = ( float(line[72 + sector * 13 : 76 + sector * 13]) / 1000.0 ) # The A and k parameters are in the next 10 characters, with A stored * 10 # and k stored * 100 weibull_A_gid[gid, sector] = float(line[76 + sector * 13 : 80 + sector * 13]) / 10.0 weibull_k_gid[gid, sector] = ( float(line[80 + sector * 13 : 85 + sector * 13]) / 100.0 ) # Save the x_gid and y_gid form for iteration in het map self.x_gid = x_gid self.y_gid = y_gid self.weibull_A_gid = weibull_A_gid self.weibull_k_gid = weibull_k_gid # Save a single value of z and h for the entire grid self.z = z_gid[0] self.h = h_gid[0] # Index the by sector data by x and y self.sector_freq = np.zeros((self.nx, self.ny, self.n_sectors)) self.weibull_A = np.zeros((self.nx, self.ny, self.n_sectors)) self.weibull_k = np.zeros((self.nx, self.ny, self.n_sectors)) for x_idx, x in enumerate(self.x_array): for y_idx, y in enumerate(self.y_array): # Find the indices when x_gid and y_gid are equal to x and y idx = np.where((x_gid == x) & (y_gid == y))[0] # Assign the data to the correct location self.sector_freq[x_idx, y_idx, :] = sector_freq_gid[idx, :] self.weibull_A[x_idx, y_idx, :] = weibull_A_gid[idx, :] self.weibull_k[x_idx, y_idx, :] = weibull_k_gid[idx, :] # Build the interpolant function lists self.interpolant_sector_freq = self._build_interpolant_function_list( self.x_array, self.y_array, self.n_sectors, self.sector_freq ) self.interpolant_weibull_A = self._build_interpolant_function_list( self.x_array, self.y_array, self.n_sectors, self.weibull_A ) self.interpolant_weibull_k = self._build_interpolant_function_list( self.x_array, self.y_array, self.n_sectors, self.weibull_k ) def __str__(self) -> str: """ Return a string representation of the WindRose object """ return ( f"WindResourceGrid with {self.nx} x {self.ny} grid points, " f"min x: {self.xmin}, min y: {self.ymin}, grid size: {self.grid_size}, " f"z: {self.z}, h: {self.h}, {self.n_sectors} sectors\n" f"Wind directions in file: {self._wind_directions_wrg_file}\n" f"Wind directions: {self.wind_directions}\n" f"Wind speeds: {self.wind_speeds}\n" f"ti_table: {self.ti_table}" ) def _build_interpolant_function_list(self, x, y, n_sectors, data): """ Build a list of interpolant functions for the data. It is assumed that the function should return a list of interpolant functions, length n_sectors. Args: x (np.array): The x values of the data, length nx. y (np.array): The y values of the data, length ny. n_sectors (int): The number of sectors. data (np.array): The data to interpolate, shape (nx, ny, n_sectors). Returns: list: A list of interpolant functions, length n_sectors. """ function_list = [] for sector in range(n_sectors): function_list.append( RegularGridInterpolator( (x, y), data[:, :, sector], bounds_error=False, fill_value=None, ) ) return function_list def _interpolate_data(self, x, y, interpolant_function_list): """ Interpolate the data at a given x, y location using the interpolant function list. Args: x (float): The x location to interpolate. y (float): The y location to interpolate. interpolant_function_list (list): A list of interpolant functions. Returns: list: A list of interpolated data, length n_sectors. """ # Check if x and y are within the bounds of the self.x_array and self.y_array, if # so use the nearest method, otherwise use the linear method of interpolation if ( x < self.x_array[0] or x > self.x_array[-1] or y < self.y_array[0] or y > self.y_array[-1] ): method = "nearest" else: method = "linear" result = np.zeros(self.n_sectors) for sector in range(self.n_sectors): result[sector] = interpolant_function_list[sector]((x, y), method=method) return result def _weibull_cumulative(self, x, a, k): """ Calculate the Weibull cumulative distribution function. Args: x (np.array): The wind speed values. a (np.array): The Weibull A parameter values. k (np.array): The Weibull k parameter values. Returns: np.array: The cumulative distribution function values. """ exponent = -((x / a) ** k) result = 1.0 - np.exp(exponent) # Where x is less than 0, the result should be 0 result[x < 0] = 0.0 return result # Original code from PJ Stanley # if x >= 0.0: # exponent = -(x / a) ** k # return 1.0 - np.exp(exponent) # else: # return 0.0 def _generate_wind_speed_frequencies_from_weibull(self, A, k, wind_speeds=None): """ Generate the wind speed frequencies from the Weibull parameters. Use the cumulative form of the function and calculate the probability of the wind speed in a given bin via the difference in the cumulative function at the bin edges. Args: A (np.array): The Weibull A parameter. k (np.array): The Weibull k parameter. wind_speeds (np.array): The wind speeds to calculate the frequencies for. If None, the frequencies are calculated for 0 to 25 m/s in 1 m/s increments. Default is None. Returns: np.array: The wind speed frequencies. """ if wind_speeds is None: wind_speeds = self.wind_speeds ws_steps = np.diff(wind_speeds) if not np.all(np.isclose(ws_steps, ws_steps[0])): raise ValueError("wind_speeds must be equally spaced.") else: ws_step = ws_steps[0] # Define the wind speed edges (not half-open interval in np.arange) wind_speed_edges = np.arange( wind_speeds[0] - ws_step / 2, wind_speeds[-1] + ws_step, ws_step ) # Get the cumulative distribution function at the edges cdf_edges = self._weibull_cumulative(wind_speed_edges, A, k) # The frequency is the difference in the cumulative distribution function # at the edges # NOTE: The probability mass associated to each discrete wind speed (ws) is taken as the # cumulative mass under the continuous Weibull distribution from ws - ws_step/2 to # ws + ws_step/2, where ws_step is the step between the provided wind_speeds. freq = cdf_edges[1:] - cdf_edges[:-1] # Normalize the frequency freq = freq / freq.sum() return wind_speeds, freq def get_wind_rose_at_point(self, x, y, wind_directions=None, wind_speeds=None, ti_table=0.06): """ Get the wind rose at a given x, y location. Interpolate the parameters to the point and then generate the wind rose. Args: x (float): The x location to interpolate. y (float): The y location to interpolate. wind_directions (np.array): The wind directions to calculate the frequencies for. If None, use self.wind_directions. Default is None. wind_speeds (np.array): The wind speeds to calculate the frequencies for. If None, use self.wind_speeds. Default is None. ti_table (float): The ti_table to use in the wind rose. Default is 0.06. """ if wind_speeds is None: wind_speeds = self.wind_speeds # If wind directions is None, use the values stored if wind_directions is None: wind_directions = self.wind_directions wd_step = self.wd_step else: # Calculate wd_step for these directions wd_step = wind_directions[1] - wind_directions[0] # Get the interpolated data sector_freq = self._interpolate_data(x, y, self.interpolant_sector_freq) weibull_A = self._interpolate_data(x, y, self.interpolant_weibull_A) weibull_k = self._interpolate_data(x, y, self.interpolant_weibull_k) # Initialize the freq_table freq_table = np.zeros((self.n_sectors, len(wind_speeds))) # First fill in the rows of the table using the weibull distributions, # weighted by the sector freq for sector in range(self.n_sectors): wind_speeds, freq = self._generate_wind_speed_frequencies_from_weibull( weibull_A[sector], weibull_k[sector], wind_speeds=wind_speeds ) freq_table[sector, :] = sector_freq[sector] * freq # Normalize the table freq_table = freq_table / freq_table.sum() # First build the wind rose using the wind directions in the wrg file wind_rose = WindRose( wind_directions=self._wind_directions_wrg_file, wind_speeds=wind_speeds, freq_table=freq_table, ti_table=ti_table, compute_zero_freq_occurrence=True, ) # Now upsample or downsample the wind rose to the specified wind directions if wd_step == (self._wind_directions_wrg_file[1] - self._wind_directions_wrg_file[0]): # If the wind directions are the same, return the wind rose return wind_rose elif wd_step < (self._wind_directions_wrg_file[1] - self._wind_directions_wrg_file[0]): # If the wind directions are smaller, upsample return wind_rose.upsample(wd_step) else: # If the wind directions are larger, downsample return wind_rose.downsample(wd_step) def set_wd_step(self, wd_step): """ Set the wind directions for the WindRoseWRG object. Args: wind_directions (np.array): The wind directions to use for the wind roses. """ self.wind_directions = np.arange(0.0, 360.0, wd_step) self.wd_step = wd_step # Update the wind roses if the layout has been set if self.layout_x is not None: self._update_wind_roses() def set_wind_speeds(self, wind_speeds): """ Set the wind speeds for the WindRoseWRG object. Args: wind_speeds (np.array): The wind speeds to use for the wind roses. """ self.wind_speeds = wind_speeds # Update the wind roses if the layout has been set if self.layout_x is not None: self._update_wind_roses() def set_ti_table(self, ti_table): """ Set the fixed turbulence intensity value for the WindRoseWRG object. Args: ti_table (float): The ti_table value to use in the wind roses. """ self.ti_table = ti_table # Update the wind roses if the layout has been set if self.layout_x is not None: self._update_wind_roses() def set_layout(self, layout_x, layout_y): """ Set the layout for the WindRoseWRG object. Args: layout_x (np.array): The x coordinates of the layout. layout_y (np.array): The y coordinates of the layout. """ # Confirm that layout_x, layout_y, and wind_roses are the same length if len(layout_x) != len(layout_y): raise ValueError("layout_x and layout_y must be the same length") # If the current layout is the same as the new layout, return if self.layout_x is not None and self.layout_y is not None: if np.allclose(np.array(layout_x), self.layout_x) and np.allclose( np.array(layout_y), self.layout_y ): return # Save the layouts self.layout_x = np.array(layout_x) self.layout_y = np.array(layout_y) # Update the wind roses self._update_wind_roses() def _update_wind_roses(self): # Initialize the list of wind roses self.wind_roses = [] # Loop through the turbines and get the wind rose at each location for i in range(len(self.layout_x)): wind_rose = self.get_wind_rose_at_point( self.layout_x[i], self.layout_y[i], wind_directions=self.wind_directions, wind_speeds=self.wind_speeds, ti_table=self.ti_table, ) self.wind_roses.append(wind_rose) # Save also the wd_flat and ws_flat from the first wind rose as this could be needed # for unpacking and non_zero_freq_mask self.wd_flat = self.wind_roses[0].wd_flat self.ws_flat = self.wind_roses[0].ws_flat self.non_zero_freq_mask = self.wind_roses[0].non_zero_freq_mask def unpack(self): """ Implement the unpack method for WindRoseByTurbine by calling the unpack method for each of the WindRose objects in wind_roses. Mose of the variables can be passed as is but freq_table_unpack are combined and stacked along the 1th axis Returns: Tuple: Tuple containing the unpacked wind rose data. """ if self.layout_x is None: raise ValueError("WindRoseByTurbine must be initialized to a layout before unpacking") # Initialize freq_table_unpack freq_table_unpack = np.zeros((len(self.wd_flat), len(self.layout_x))) # Loop over remaining wind roses and stack freq_table_unpack for i, wind_rose in enumerate(self.wind_roses): ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, freq_table_unpack_0, value_table_unpack, heterogeneous_inflow_config, ) = wind_rose.unpack() freq_table_unpack[:, i] = freq_table_unpack_0 return ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, freq_table_unpack, value_table_unpack, heterogeneous_inflow_config, ) def plot_wind_roses( self, axarr=None, wd_step=None, ws_step=None, ): """ Plot the wind roses for each turbine in the WindRoseByTurbine object. Args: axarr (NDArrayAxes, optional): Array of axes to plot the wind roses on. Defaults to None. Must have length equal to the number of wind roses. wd_step (float, optional): Step size for wind direction. Defaults to None. ws_step (float, optional): Step size for wind speed. Defaults to None. """ if self.layout_x is None: raise ValueError("WindRoseByTurbine must be initialized to a layout before plotting") # If axarr is not defined, create a new figure if axarr is None: _, axarr = plt.subplots(1, len(self.wind_roses), subplot_kw={"polar": True}) # Test that axarr is the correct length if len(axarr) != len(self.wind_roses): raise ValueError("axarr must have the same length as the number of wind roses") # Plot the wind roses for each turbine for i, wind_rose in enumerate(self.wind_roses): wind_rose.plot(ax=axarr[i], wd_step=wd_step, ws_step=ws_step) axarr[i].set_title(f"Turbine {i}\n ({self.layout_x[i]:.1f}, {self.layout_y[i]:.1f})") def get_heterogeneous_wind_rose( self, fmodel, wind_speeds=None, x_loc=None, y_loc=None, representative_wind_speed=8.0, ): """ Get the heterogeneous map at each location in the grid, with the speeds ups defined relative the location indicated by gid_norm_index. Args: fmodel (FlorisModel): The FlorisModel object to use to generate the power curve. wind_speeds (np.array): The wind speeds to calculate the frequencies for. Default is np.arange(0.0, 25.0, 1.0). gid_norm_index (int): The index of the turbine to normalize the speed ups to. Default is 0. representative_wind_speed (float): The representative wind speed to use in the power curve. Returns: HeterogeneousMap: The heterogeneous map object. """ ############################ # Compute the power curve for combining the wind speeds ############################ if wind_speeds is None: wind_speeds = self.wind_speeds # Get a local copy fm = copy.deepcopy(fmodel) # Get the power curve for the turbine # TODO: Maybe the power curve could be directly extracted fm.set( layout_x=[0], layout_y=[0], wind_data=TimeSeries( wind_speeds=wind_speeds, wind_directions=270.0, turbulence_intensities=0.06, ), ) fm.run() turbine_power = fm.get_turbine_powers().flatten() ############################ # Identify the point on the original wrg grid closest to the x_loc and y_loc ############################ if x_loc is None or y_loc is None: # Simply use the first point gid_reference = 0 else: # Find the closest point gid_reference = np.argmin((self.x_gid - x_loc) ** 2 + (self.y_gid - y_loc) ** 2) # Assign x_loc and y_loc to this point x_loc = self.x_gid[gid_reference] y_loc = self.y_gid[gid_reference] print(f"Using point {gid_reference} at ({x_loc}, {y_loc}) as reference location") ############################ # Get the wind rose at this point ############################ wind_rose = self.get_wind_rose_at_point( x=x_loc, y=y_loc, ) # Subset to the representative wind speed # Check the represenative_wind_speed is valid if representative_wind_speed in wind_rose.wind_speeds: ws_idx = np.where(wind_rose.wind_speeds == representative_wind_speed)[0] else: raise ValueError("representative_wind_speed must be in original set") # Create a new wind rose with only the specified wind speeds wind_rose = WindRose( wind_rose.wind_directions, wind_rose.wind_speeds[ws_idx], wind_rose.ti_table[:, ws_idx], wind_rose.freq_table[:, ws_idx], wind_rose.value_table[:, ws_idx] if wind_rose.value_table is not None else None, wind_rose.compute_zero_freq_occurrence, wind_rose.heterogeneous_map, ) ############################ # Calculate speed multipliers ############################ speed_multipliers = np.zeros((self.n_sectors, self.n_gid)) for direction_sector in range(self.n_sectors): for gid in range(self.n_gid): _, freq = self._generate_wind_speed_frequencies_from_weibull( self.weibull_A_gid[gid, direction_sector], self.weibull_k_gid[gid, direction_sector], wind_speeds=wind_speeds, ) # Record the expected power speed_multipliers[direction_sector, gid] = np.sum(turbine_power * freq) # Normalize the speed ups speed_multipliers[direction_sector, :] = ( speed_multipliers[direction_sector, :] / speed_multipliers[direction_sector, gid_reference] ) # Take the cube root of the speed ups to place in the frame of wind speed ups speed_multipliers = np.cbrt(speed_multipliers) # Create the heterogeneous map heterogeneous_map = HeterogeneousMap( x=self.x_gid, y=self.y_gid, wind_directions=self._wind_directions_wrg_file, speed_multipliers=speed_multipliers, ) # Return the wind rose with the heterogeneous map return WindRose( wind_directions=wind_rose.wind_directions, wind_speeds=wind_rose.wind_speeds, freq_table=wind_rose.freq_table, ti_table=wind_rose.ti_table, heterogeneous_map=heterogeneous_map, ) ================================================ FILE: profiling/linux_perf.py ================================================ from contextlib import contextmanager from os import getpid from resource import getrusage, RUSAGE_SELF from signal import SIGINT from subprocess import Popen from time import ( perf_counter, sleep, time, ) # Additional events described here: # https://www.brendangregg.com/perf.html#SoftwareEvents events = [ "instructions", "cache-references", "cache-misses", "avx_insts.all", ] @contextmanager def perf(): """ Benchmark this process with Linux's perf util. Example usage: with perf(): x = run_some_code() more_code(x) all_this_code_will_be_measured() """ p = Popen([ # Run perf stat "perf", "stat", # for the current Python process "-p", str(getpid()), # record the list of events mentioned above "-e", ",".join(events) ]) # Ensure perf has started before running more # Python code. This will add ~0.1 to the elapsed # time reported by perf, so we also track elapsed # time separately. sleep(0.1) start = time() try: yield finally: print(f"Elapsed (seconds): {time() - start}") print("Peak memory (MiB):", int(getrusage(RUSAGE_SELF).ru_maxrss / 1024)) p.send_signal(SIGINT) ================================================ FILE: profiling/profiling.py ================================================ # import re # import sys # import time # import cProfile # from copy import deepcopy import copy from conftest import SampleInputs from floris.core import Core def run_floris(): core = Core.from_file("examples/example_input.yaml") return core if __name__=="__main__": # if len(sys.argv) > 1: # floris = Floris(sys.argv[1]) # else: # floris = Floris("example_input.yaml") # floris.farm.flow_field.calculate_wake() # start = time.time() # cProfile.run('re.compile("floris.steady_state_atmospheric_condition()")') # end = time.time() # print(start, end, end - start) sample_inputs = SampleInputs() sample_inputs.core["wake"]["model_strings"]["velocity_model"] = "gauss" sample_inputs.core["wake"]["model_strings"]["deflection_model"] = "gauss" sample_inputs.core["wake"]["enable_secondary_steering"] = True sample_inputs.core["wake"]["enable_yaw_added_recovery"] = True sample_inputs.core["wake"]["enable_transverse_velocities"] = True N_TURBINES = 100 N_FINDEX = 72 * 25 # Size of a characteristic wind rose TURBINE_DIAMETER = sample_inputs.core["farm"]["turbine_type"][0]["rotor_diameter"] sample_inputs.core["farm"]["layout_x"] = [5 * TURBINE_DIAMETER * i for i in range(N_TURBINES)] sample_inputs.core["farm"]["layout_y"] = [0.0 for i in range(N_TURBINES)] sample_inputs.core["flow_field"]["wind_directions"] = N_FINDEX * [270.0] sample_inputs.core["flow_field"]["wind_speeds"] = N_FINDEX * [8.0] sample_inputs.core["flow_field"]["turbulence_intensities"] = N_FINDEX * [0.06] N = 1 for i in range(N): core = Core.from_dict(copy.deepcopy(sample_inputs.core)) core.initialize_domain() core.steady_state_atmospheric_condition() ================================================ FILE: profiling/quality_metrics.py ================================================ import copy import time import warnings import numpy as np from linux_perf import perf from floris.core import Core wd_grid, ws_grid = np.meshgrid( np.arange(0, 360.0, 5), # wind directions np.arange(8.0, 12.0, 0.2), # wind speeds indexing="ij" ) WIND_DIRECTIONS = wd_grid.flatten() WIND_SPEEDS = ws_grid.flatten() TURBULENCE_INTENSITIES = np.ones_like(WIND_DIRECTIONS) * 0.1 N_FINDEX = len(WIND_DIRECTIONS) N_TURBINES = 3 X_COORDS, Y_COORDS = np.meshgrid( 5.0 * 126.0 * np.arange(0, N_TURBINES, 1), 5.0 * 126.0 * np.arange(0, N_TURBINES, 1), ) X_COORDS = X_COORDS.flatten() Y_COORDS = Y_COORDS.flatten() N_ITERATIONS = 20 def run_floris(input_dict): try: start = time.perf_counter() core = Core.from_dict(copy.deepcopy(input_dict.core)) core.initialize_domain() core.steady_state_atmospheric_condition() end = time.perf_counter() return end - start except KeyError: # Catch the errors when an invalid wake model was given because the model # was not yet implemented return -1.0 def time_profile(input_dict): # Run once to initialize Python and memory run_floris(input_dict) times = np.zeros(N_ITERATIONS) for i in range(N_ITERATIONS): times[i] = run_floris(input_dict) return np.sum(times) / N_ITERATIONS def test_time_jensen_jimenez(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = "jensen" sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = "jimenez" return time_profile(sample_inputs_fixture) def test_time_gauss(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = "gauss" sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = "gauss" return time_profile(sample_inputs_fixture) def test_time_gch(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = "gauss" sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = "gauss" sample_inputs_fixture.core["wake"]["enable_transverse_velocities"] = True sample_inputs_fixture.core["wake"]["enable_secondary_steering"] = True sample_inputs_fixture.core["wake"]["enable_yaw_added_recovery"] = True return time_profile(sample_inputs_fixture) def test_time_cumulative(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = "cc" sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = "gauss" return time_profile(sample_inputs_fixture) def memory_profile(input_dict): # Run once to initialize Python and memory core = Core.from_dict(copy.deepcopy(input_dict.core)) core.initialize_domain() core.steady_state_atmospheric_condition() with perf(): for i in range(N_ITERATIONS): core = Core.from_dict(copy.deepcopy(input_dict.core)) core.initialize_domain() core.steady_state_atmospheric_condition() print( "Size of one data array: " f"{64 * N_FINDEX * N_TURBINES * 25 / (1000 * 1000)} MB" ) def test_mem_jensen_jimenez(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = "jensen" sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = "jimenez" memory_profile(sample_inputs_fixture) if __name__=="__main__": warnings.filterwarnings('ignore') from conftest import SampleInputs sample_inputs = SampleInputs() sample_inputs.core["farm"]["layout_x"] = X_COORDS sample_inputs.core["farm"]["layout_y"] = Y_COORDS sample_inputs.core["flow_field"]["wind_directions"] = WIND_DIRECTIONS sample_inputs.core["flow_field"]["wind_speeds"] = WIND_SPEEDS sample_inputs.core["flow_field"]["turbulence_intensities"] = TURBULENCE_INTENSITIES print() print("### Memory profiling") test_mem_jensen_jimenez(sample_inputs) print() print("### Performance profiling") time_jensen = test_time_jensen_jimenez(sample_inputs) time_gauss = test_time_gauss(sample_inputs) time_gch = test_time_gch(sample_inputs) # TODO: reenable this after the cc model is fixed with multiturbine # time_cc = test_time_cumulative(sample_inputs) # print("{:.4f} {:.4f} {:.4f} {:.4f}".format(time_jensen, time_gauss, time_gch, time_cc)) print("{:.4f} {:.4f} {:.4f}".format(time_jensen, time_gauss, time_gch)) ================================================ FILE: profiling/serial_vectorize.py ================================================ import copy import time import matplotlib.pyplot as plt import numpy as np from conftest import SampleInputs from floris.simulation import Floris def time_vec(input_dict): start = time.time() floris = Floris(input_dict=input_dict.core) end = time.time() init_time = end - start start = time.time() floris.steady_state_atmospheric_condition() end = time.time() calc_time = end - start return init_time, calc_time def time_serial(input_dict, wd, ws): init_times = np.zeros(len(wd)) calc_times = np.zeros(len(wd)) for i, (d, s) in enumerate(zip(wd, ws)): input_dict.core["flow_field"]["wind_directions"] = [d] input_dict.core["flow_field"]["wind_speeds"] = [s] start = time.time() floris = Floris(input_dict=input_dict.core) end = time.time() init_times[i] = end - start start = time.time() floris.steady_state_atmospheric_condition() end = time.time() calc_times[i] = end - start return np.sum(init_times), np.sum(calc_times) if __name__=="__main__": plt.figure() sample_inputs = SampleInputs() sample_inputs.core["flow_field"]["wind_directions"] = [270.0] sample_inputs.core["flow_field"]["wind_speeds"] = [8.0] TURBINE_DIAMETER = sample_inputs.core["turbine"]["rotor_diameter"] N = 5 simulation_size = np.arange(N) # 1 turbine vectorize_init, vectorize_calc = np.zeros(N), np.zeros(N) for i in range(N): vectorize_scaling_inputs = copy.deepcopy(sample_inputs) factor = (i+1) * 50 vectorize_scaling_inputs.core["flow_field"]["wind_directions"] = [270.0] vectorize_scaling_inputs.core["flow_field"]["wind_speeds"] = factor * [8.0] vectorize_init[i], vectorize_calc[i] = time_vec(copy.deepcopy(vectorize_scaling_inputs)) print("vectorize", i, vectorize_calc[i]) serial_init, serial_calc = np.zeros(N), np.zeros(N) for i in range(N): serial_scaling_inputs = copy.deepcopy(sample_inputs) factor = (i+1) * 50 wind_directions = [270.0] wind_speeds = factor * [8.0] serial_init[i], serial_calc[i] = time_serial( copy.deepcopy(serial_scaling_inputs), wind_directions, wind_speeds ) print("serial", i, serial_calc[i]) plt.plot(simulation_size, vectorize_init, 'b--', label='vectorize init - 1 turbine') plt.plot(simulation_size, vectorize_calc, 'bo-', label='vectorize calc - 1 turbine') plt.plot(simulation_size, serial_init, 'g--', label='serial init - 1 turbine') plt.plot(simulation_size, serial_calc, 'go-', label='serial calc - 1 turbine') # More than 1 turbine n_turbines = 10 sample_inputs.core["farm"]["layout_x"] = [5 * TURBINE_DIAMETER * j for j in range(n_turbines)] sample_inputs.core["farm"]["layout_y"] = n_turbines * [0.0] vectorize_init, vectorize_calc = np.zeros(N), np.zeros(N) for i in range(N): vectorize_scaling_inputs = copy.deepcopy(sample_inputs) factor = (i+1) * 50 vectorize_scaling_inputs.core["flow_field"]["wind_speeds"] = factor * [8.0] vectorize_scaling_inputs.core["flow_field"]["wind_directions"] = [270.0] vectorize_init[i], vectorize_calc[i] = time_vec(copy.deepcopy(vectorize_scaling_inputs)) print("vectorize", i, vectorize_calc[i]) serial_init, serial_calc = np.zeros(N), np.zeros(N) for i in range(N): serial_scaling_inputs = copy.deepcopy(sample_inputs) factor = (i+1) * 50 speeds = factor * [8.0] wind_directions = [270.0] serial_init[i], serial_calc[i] = time_serial( copy.deepcopy(serial_scaling_inputs), wind_directions, speeds ) print("serial", i, serial_calc[i]) plt.plot(simulation_size, vectorize_init, 'c--', label='vectorize init - 10 turbine') plt.plot(simulation_size, vectorize_calc, 'co-', label='vectorize calc - 10 turbine') plt.plot(simulation_size, serial_init, 'y--', label='serial init - 10 turbine') plt.plot(simulation_size, serial_calc, 'yo-', label='serial calc - 10 turbine') ## Show plots plt.legend(loc="upper left") plt.grid(True) plt.show() ================================================ FILE: profiling/timing.py ================================================ import copy import time import matplotlib.pyplot as plt import memory_profiler import numpy as np from conftest import SampleInputs from floris.simulation import Floris def time_profile(input_dict): floris = Floris.from_dict(input_dict.core) start = time.perf_counter() floris.steady_state_atmospheric_condition() end = time.perf_counter() return end - start def internal_probe(input_dict): floris = Floris(input_dict=input_dict.core) internal_quantity = floris.steady_state_atmospheric_condition() return internal_quantity def memory_profile(input_dict): floris = Floris(input_dict=input_dict.core) mem_usage = memory_profiler.memory_usage( (floris.steady_state_atmospheric_condition, (), {}), max_usage=True ) return mem_usage if __name__=="__main__": sample_inputs = SampleInputs() TURBINE_DIAMETER = sample_inputs.core["turbine"]["rotor_diameter"] # Use Gauss models sample_inputs.core["wake"]["model_strings"] = { "velocity_model": "gauss", "deflection_model": "gauss", "combination_model": None, "turbulence_model": None, } ### Time scaling # N = 30 # wd_calc_time = np.zeros(N) # wd_size = np.zeros(N) # wind_direction_scaling_inputs = copy.deepcopy(sample_inputs) # for i in range(N): # factor = (i+1) * 50 # wind_direction_scaling_inputs.core["flow_field"]["wind_directions"] = factor * [270.0] # wind_direction_scaling_inputs.core["flow_field"]["wind_speeds"] = [8.0] # wd_calc_time[i] = time_profile(copy.deepcopy(wind_direction_scaling_inputs)) # wd_size[i] = factor # print("wind direction", i, wd_calc_time[i]) # ws_calc_time = np.zeros(N) # ws_size = np.zeros(N) # wind_speed_scaling_inputs = copy.deepcopy(sample_inputs) # for i in range(N): # factor = (i+1) * 50 # wind_speed_scaling_inputs.core["flow_field"]["wind_directions"] = [270.0] # wind_speed_scaling_inputs.core["flow_field"]["wind_speeds"] = factor * [8.0] # ws_calc_time[i] = time_profile(copy.deepcopy(wind_speed_scaling_inputs)) # ws_size[i] = factor # print("wind speed", i, ws_calc_time[i]) # turb_calc_time = np.zeros(N) # turb_size = np.zeros(N) # turbine_scaling_inputs = copy.deepcopy(sample_inputs) # for i in range(N): # factor = (i+1) * 3 # turbine_scaling_inputs.core["farm"]["layout_x"] = [ # 5 * TURBINE_DIAMETER * j # for j in range(factor) # ] # turbine_scaling_inputs.core["farm"]["layout_y"] = factor * [0.0] # turb_calc_time[i] = time_profile(copy.deepcopy(turbine_scaling_inputs)) # turb_size[i] = factor # print("n turbine", i, turb_calc_time[i]) # internal_quantity = np.zeros(N) # scaling_inputs = copy.deepcopy(sample_inputs) # for i in range(5): # factor = (i+1) * 2 # scaling_inputs.core["farm"]["layout_x"] = [ # 5 * TURBINE_DIAMETER * j # for j in range(factor) # ] # scaling_inputs.core["farm"]["layout_y"] = factor * [0.0] # factor = (i+1) * 20 # scaling_inputs.core["flow_field"]["wind_directions"] = factor * [270.0] # scaling_inputs.core["flow_field"]["wind_speeds"] = factor * [8.0] # internal_quantity[i] = time_profile(scaling_inputs) # print("n turbine", i, internal_quantity[i]) # plt.figure() # plt.plot(wd_size, wd_calc_time, 'b+-', label='wind direction') # plt.plot(ws_size, ws_calc_time, 'g+-', label='wind speed') # plt.plot(turb_size, turb_calc_time, 'r+-', label='n turbine') # # plt.plot(simulation_size, internal_quantity, 'b+-', label='internal quantity') # plt.legend(loc="upper left") # plt.grid(True) ### Timing larger sizes in each dimension n_wind_directions = 1 n_wind_speeds = 1 n_turbines = 3 sample_inputs.core["wake"]["model_strings"] = { # "velocity_model": "jensen", # "deflection_model": "jimenez", "velocity_model": "cc", "deflection_model": "gauss", "combination_model": None, "turbulence_model": None, } sample_inputs.core["solver"] = { "type": "turbine_grid", "turbine_grid_points": 5 } # sample_inputs.core["wake"]["enable_transverse_velocities"] = False # sample_inputs.core["wake"]["enable_secondary_steering"] = False # sample_inputs.core["wake"]["enable_yaw_added_recovery"] = False sample_inputs.core["flow_field"]["wind_directions"] = n_wind_directions * [270.0] sample_inputs.core["flow_field"]["wind_speeds"] = n_wind_speeds * [8.0] sample_inputs.core["farm"]["layout_x"] = [5 * TURBINE_DIAMETER * j for j in range(n_turbines)] sample_inputs.core["farm"]["layout_y"] = n_turbines * [0.0] N = 1 times = np.zeros(N) for i in range(N): print(f"Iteration {i}") times[i] = time_profile(copy.deepcopy(sample_inputs)) print(f" {times[i]}") print(f"Total time: {np.sum(times)}") print(f"Average per iteration: { np.sum(times) / N }") ### Memory scaling # N = 6 # simulation_size = np.arange(N) # wd_space = np.zeros(N) # wind_direction_scaling_inputs = copy.deepcopy(sample_inputs) # for i in range(N): # factor = (i+1) * 50 # wind_direction_scaling_inputs.core["farm"]["wind_directions"] = factor * [270.0] # wind_direction_scaling_inputs.core["farm"]["wind_speeds"] = [8.0] # wd_space[i] = memory_profile(wind_direction_scaling_inputs) # print("wind direction", i, wd_space[i]) # ws_space = np.zeros(N) # wind_speed_scaling_inputs = copy.deepcopy(sample_inputs) # for i in range(N): # factor = (i+1) * 50 # wind_speed_scaling_inputs.core["farm"]["wind_directions"] = [270.0] # wind_speed_scaling_inputs.core["farm"]["wind_speeds"] = factor * [8.0] # ws_space[i] = memory_profile(wind_speed_scaling_inputs) # print("wind speed", i, ws_space[i]) # turb_space = np.zeros(N) # turbine_scaling_inputs = copy.deepcopy(sample_inputs) # for i in range(N): # factor = (i+1) * 50 # turbine_scaling_inputs.core["farm"]["layout_x"] = [ # 5 * TURBINE_DIAMETER * j # for j in range(factor) # ] # turbine_scaling_inputs.core["farm"]["layout_y"] = factor * [0.0] # turb_space[i] = memory_profile(turbine_scaling_inputs) # print("n turbine", turb_space[i]) # # Remove the min from each test so that each starts at 0 # wd_space = wd_space - min(wd_space) # ws_space = ws_space - min(ws_space) # turb_space = turb_space - min(turb_space) # plt.figure() # plt.plot(simulation_size, wd_space, 'b+-', label='wind direction') # plt.plot(simulation_size, ws_space, 'g+-', label='wind speed') # plt.plot(simulation_size, turb_space, 'r+-', label='n turbine') # plt.legend(loc="upper left") # plt.grid(True) ### Show plots # plt.show() ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["setuptools >= 40.6.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "floris" version = "4.6.4" description = "A controls-oriented engineering wake model." readme = "README.md" requires-python = ">=3.10, <3.15" authors = [ { name = "Rafael Mudafort", email = "Rafael.Mudafort@nlr.gov" }, { name = "Paul Fleming", email = "Paul.Fleming@nlr.gov" }, { name = "Michael (Misha) Sinner", email = "Michael.Sinner@nlr.gov" }, { name = "Eric Simley", email = "Eric.Simley@nlr.gov" }, { name = "Christopher Bay", email = "Christopher.Bay@nlr.gov" }, ] license = { file = "LICENSE.txt" } keywords = ["floris"] classifiers = [ "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy" ] dependencies = [ "attrs", "pyyaml~=6.0", "numexpr~=2.0", "numpy~=2.0", "scipy~=1.1", "matplotlib~=3.0", "pandas~=2.0", "shapely~=2.0", "coloredlogs~=15.0", "pathos~=0.3", ] [project.optional-dependencies] docs = [ "jupyter-book~=1.0", "sphinx-book-theme~=1.0", "sphinx-autodoc-typehints>=2,<4", "sphinxcontrib-autoyaml~=1.0", "sphinxcontrib.mermaid>=1,<3", "bokeh~=3.7", "ruamel.yaml~=0.18.0", ] develop = [ "pytest>=8,<10", "pytest-benchmark~=5.1", "pre-commit~=4.0", "ruff~=0.9", "isort>=5,<8" ] [tool.setuptools.packages.find] include = ["floris*"] [tool.setuptools.package-data] floris = [ "turbine_library/*.yaml", "turbine_library/*.csv", "turbine_library/demo_cp_ct_surfaces/*.npz", "core/wake_velocity/turbopark_lookup_table.mat", "default_inputs.yaml" ] [project.urls] Homepage = "https://github.com/NatLabRockies/floris" Documentation = "https://natlabrockies.github.io/floris/" [coverage.run] # Coverage.py configuration file # https://coverage.readthedocs.io/en/latest/config.html branch = true source = "floris/*" omit = [ "tests/*", "benchmarks/*" ] [tool.pytest.ini_options] testpaths = "tests" filterwarnings = [ "ignore::DeprecationWarning:pandas.*:" ] ## Pyflakes (F) ## pycodestyle (E, W) # mccabe (C90) # isort (I) # Use isort directly until more isort features are included in ruff # pep8-naming (N) # pydocstyle (D) # pyupgrade (UP) # flake8-2020 (YTT) # flake8-annotations (ANN) # flake8-bandit (S) # flake8-blind-except (BLE) # flake8-boolean-trap (FBT) # flake8-bugbear (B) # flake8-builtins (A) # flake8-commas (COM) # flake8-comprehensions (C4) # flake8-datetimez (DTZ) # flake8-debugger (T10) # flake8-errmsg (EM) # flake8-executable (EXE) # flake8-implicit-str-concat (ISC) # flake8-import-conventions (ICN) # flake8-logging-format (G) # flake8-no-pep420 (INP) # flake8-pie (PIE) # flake8-print (T20) # flake8-pytest-style (PT) # flake8-quotes (Q) # flake8-return (RET) # flake8-simplify (SIM) # flake8-tidy-imports (TID) # flake8-type-checking (TCH) # flake8-unused-arguments (ARG) # flake8-use-pathlib (PTH) # eradicate (ERA) # pandas-vet (PD) # pygrep-hooks (PGH) # Pylint (PL) # - Convention (PLC) # - Error (PLE) # - Refactor (PLR) # - Warning (PLW) # tryceratops (TRY) # flake8-raise (RSE) # flake8-self (SLF) # Ruff-specific rules (RUF) [tool.ruff] src = ["floris", "tests"] line-length = 100 target-version = "py313" # See https://github.com/charliermarsh/ruff#supported-rules # for rules included and matching to prefix. lint.select = ["F", "E", "W", "C4", ] #"T20", "I" # I - isort is not fully implemented in ruff so there is not parity. Consider disabling I. # F401 unused-import: Ignore until all used isort flags are adopted in ruff lint.ignore = ["F401"] # Allow autofix for all enabled rules (when `--fix`) is provided. # fixable = ["A", "B", "C", "D", "E", "F"] lint.fixable = ["F", "E", "W", "C4"] #"T20", "I" lint.unfixable = [] # Exclude a variety of commonly ignored directories. exclude = [ "floris/version.py", ".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv", "docs", ] # Allow unused variables when underscore-prefixed. lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" [tool.ruff.lint.per-file-ignores] # F841 unused-variable: ignore since this file uses numexpr and many variables look unused "floris/core/wake_deflection/jimenez.py" = ["F841"] "floris/core/wake_turbulence/crespo_hernandez.py" = ["F841"] "floris/core/wake_deflection/gauss.py" = ["F841"] "floris/core/wake_velocity/jensen.py" = ["F841"] "floris/core/wake_velocity/gauss.py" = ["F841"] "floris/core/wake_velocity/empirical_gauss.py" = ["F841"] # Ignore `F401` (import violations) in all `__init__.py` files, and in `path/to/file.py`. "__init__.py" = ["F401"] # I001 unsorted-imports: ignore because the import order is meaningful to navigate # import dependencies "floris/core/__init__.py" = ["I001"] [tool.ruff.lint.isort] combine-as-imports = true known-first-party = ["floris"] order-by-type = false # lines-after-imports = 2 # [tool.ruff.mccabe] # # Unlike Flake8, default to a complexity level of 10. # max-complexity = 10 [tool.isort] sections = [ "FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER" ] known_first_party = [ "floris" ] multi_line_output = 3 combine_as_imports = true force_grid_wrap = 3 include_trailing_comma = true use_parentheses = true lines_after_imports = 2 line_length = 100 order_by_type = false split_on_trailing_comma = true # length_sort = true # case_sensitive: False # force_sort_within_sections: True # reverse_relative: True # sort_relative_in_force_sorted_sections: True ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/base_unit_test.py ================================================ import pytest from attr import define, field from attrs.exceptions import FrozenAttributeError from floris.core import BaseClass, BaseModel @define class ClassTest(BaseModel): x: int = field(default=1, converter=int) a_string: str = field(default="abc", converter=str) def prepare_function() -> dict: return {} def function() -> None: return None def test_get_model_values(): """ BaseClass and therefore BaseModel previously had a method `get_model_values` that returned the values of the model parameters. This was removed because it was redundant but this test was refactored to test the as_dict method from FromDictMixin. This tests that the parameters are changed when set by the user. """ cls = ClassTest(x=4, a_string="xyz") values = cls.as_dict() assert len(values) == 2 assert values["x"] == 4 assert values["a_string"] == "xyz" def test_NUM_EPS(): cls = ClassTest(x=4, a_string="xyz") assert cls.NUM_EPS == 0.001 with pytest.raises(FrozenAttributeError): cls.NUM_EPS = 2 ================================================ FILE: tests/conftest.py ================================================ import copy from pathlib import Path import numpy as np import pytest import floris from floris.core import ( Core, FlowField, FlowFieldGrid, PointsGrid, TurbineGrid, ) def turbines_to_array(turbine_list: list): return [[t.average_velocity, t.Ct, t.power, t.axial_induction] for t in turbine_list] def assert_results_arrays(test: np.array, baseline: np.array): if np.shape(test) != np.shape(baseline): raise ValueError("test and baseline results have mismatched shapes.") for test_dim0, baseline_dim0 in zip(test, baseline): for test_dim1, baseline_dim1 in zip(test_dim0, baseline_dim0): assert np.allclose(test_dim1, baseline_dim1) def assert_results(test: list, baseline: list): if len(test) != len(baseline): raise ValueError("assert_results: test and baseline results have mismatched lengths.") for i in range(len(test)): for j, (t, b) in enumerate(zip(test[i], baseline[i])): # print(j, t, b) assert t == pytest.approx(b) def print_test_values( average_velocities: list, thrusts: list, powers: list, axial_inductions: list, max_findex_print: int | None =None ): n_findex, n_turb = np.shape(average_velocities) if max_findex_print is not None: n_findex = min(n_findex, max_findex_print) for i in range(n_findex): print("[") for j in range(n_turb): print( " [{:.7f}, {:.7f}, {:.7f}, {:.7f}],".format( average_velocities[i,j], thrusts[i,j], powers[i,j], axial_inductions[i,j] ) ) print("],") WIND_DIRECTIONS = [ 270.0, 270.0, 270.0, 270.0, 360.0, 360.0, 360.0, 360.0, 285.0, 285.0, 285.0, 285.0, 315.0, 315.0, 315.0, 315.0, ] WIND_SPEEDS = [ 8.0, 9.0, 10.0, 11.0, 8.0, 9.0, 10.0, 11.0, 8.0, 9.0, 10.0, 11.0, 8.0, 9.0, 10.0, 11.0, ] TURBULENCE_INTENSITIES = [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ] # FINDEX is the length of the number of conditions, so it can be # len(WIND_DIRECTIONS) or len(WIND_SPEEDS N_FINDEX = len(WIND_DIRECTIONS) X_COORDS = [ 0.0, 5 * 126.0, 10 * 126.0 ] Y_COORDS = [ 0.0, 0.0, 0.0 ] Z_COORDS = [ 90.0, 90.0, 90.0 ] N_TURBINES = len(X_COORDS) ROTOR_DIAMETER = 126.0 TURBINE_GRID_RESOLUTION = 2 DEMO_CP_CT_5MW_FILE = ( Path(floris.__file__).resolve().parent / "turbine_library/demo_cp_ct_surfaces/nrel_5MW_demo_cp_ct_surface.npz" ) ## Unit test fixtures @pytest.fixture def flow_field_fixture(sample_inputs_fixture): flow_field_dict = sample_inputs_fixture.flow_field return FlowField.from_dict(flow_field_dict) @pytest.fixture def turbine_grid_fixture(sample_inputs_fixture) -> TurbineGrid: turbine_coordinates = np.array(list(zip(X_COORDS, Y_COORDS, Z_COORDS))) rotor_diameters = ROTOR_DIAMETER * np.ones( (N_TURBINES) ) return TurbineGrid( turbine_coordinates=turbine_coordinates, turbine_diameters=rotor_diameters, wind_directions=np.array(WIND_DIRECTIONS), grid_resolution=TURBINE_GRID_RESOLUTION, ) @pytest.fixture def flow_field_grid_fixture(sample_inputs_fixture) -> FlowFieldGrid: turbine_coordinates = np.array(list(zip(X_COORDS, Y_COORDS, Z_COORDS))) rotor_diameters = ROTOR_DIAMETER * np.ones( (N_FINDEX, N_TURBINES) ) return FlowFieldGrid( turbine_coordinates=turbine_coordinates, turbine_diameters=rotor_diameters, wind_directions=np.array(WIND_DIRECTIONS), grid_resolution=[3,2,2] ) @pytest.fixture def points_grid_fixture(sample_inputs_fixture) -> PointsGrid: turbine_coordinates = np.array(list(zip(X_COORDS, Y_COORDS, Z_COORDS))) rotor_diameters = ROTOR_DIAMETER * np.ones( (N_FINDEX, N_TURBINES) ) points_x = [0.0, 10.0] points_y = [0.0, 0.0] points_z = [1.0, 2.0] return PointsGrid( turbine_coordinates=turbine_coordinates, turbine_diameters=rotor_diameters, wind_directions=np.array(WIND_DIRECTIONS), grid_resolution=None, points_x=points_x, points_y=points_y, points_z=points_z, ) @pytest.fixture def floris_fixture(): sample_inputs = SampleInputs() return Core(sample_inputs.core) @pytest.fixture def sample_inputs_fixture(): return SampleInputs() class SampleInputs: """ SampleInputs class """ def __init__(self): self.turbine = { "turbine_type": "nrel_5mw", "rotor_diameter": 125.88, "hub_height": 90.0, "operation_model": "cosine-loss", "power_thrust_table": { "cosine_loss_exponent_yaw": 1.88, "cosine_loss_exponent_tilt": 1.88, "ref_air_density": 1.225, "ref_tilt": 5.0, "helix_a": 1.802, "helix_power_b": 4.568e-03, "helix_power_c": 1.629e-10, "helix_thrust_b": 1.027e-03, "helix_thrust_c": 1.378e-06, "peak_shaving_fraction": 0.2, "peak_shaving_TI_threshold": 0.1, "power": [ 0.0, 0.0, 40.51801151756921, 177.6716250641970, 403.900880943964, 737.5889584824021, 1187.177403061187, 1239.245945375778, 1292.518429372350, 1347.321314747710, 1403.257372557894, 1460.701189873070, 1519.641912597998, 1580.174365096404, 1642.110316691816, 1705.758292831, 1771.165952889397, 2518.553107505315, 3448.381605840943, 3552.140809000129, 3657.954543179412, 3765.121299313842, 3873.928844315059, 3984.480022695550, 4096.582833096852, 4210.721306623712, 4326.154305853405, 4443.395565353604, 4562.497934188341, 4683.419890251577, 4806.164748311019, 4929.931918769215, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 5000.00, 0.0, 0.0, ], "thrust_coefficient": [ 0.0, 0.0, 1.132034888, 0.999470963, 0.917697381, 0.860849503, 0.815371198, 0.811614904, 0.807939328, 0.80443352, 0.800993851, 0.79768116, 0.794529244, 0.791495834, 0.788560434, 0.787217182, 0.787127977, 0.785839257, 0.783812219, 0.783568108, 0.783328285, 0.781194418, 0.777292539, 0.773464375, 0.769690236, 0.766001924, 0.762348072, 0.758760824, 0.755242872, 0.751792927, 0.748434131, 0.745113997, 0.717806682, 0.672204789, 0.63831272, 0.610176496, 0.585456847, 0.563222111, 0.542912273, 0.399312061, 0.310517829, 0.248633226, 0.203543725, 0.169616419, 0.143478955, 0.122938861, 0.106515296, 0.093026095, 0.081648606, 0.072197368, 0.064388275, 0.057782745, 0.0, 0.0, ], "wind_speed": [ 0.0, 2.9, 3.0, 4.0, 5.0, 6.0, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 9.0, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 25.1, 50.0, ], }, "TSR": 8.0 } self.controller_dependent_turbine_parameters = { "ref_air_density": 1.225, "ref_tilt": 5.0, "rated_rpm": 12.1, "rotor_solidity": 0.05132, "generator_efficiency": 0.944, "rated_power": 5000.0, "rotor_diameter": 126, "beta": -0.45891, "cd": 0.0040638, "cl_alfa": 4.275049, "cp_ct_data_file": DEMO_CP_CT_5MW_FILE } self.turbine_floating = copy.deepcopy(self.turbine) self.turbine_floating["floating_tilt_table"] = { "tilt": [ 5.0, 5.0, 5.0, ], "wind_speed": [ 0.0, 25.0, 50.0, ], } self.turbine_floating["correct_cp_ct_for_tilt"] = True self.turbine_multi_dim = { "turbine_type": 'iea_15MW_multi_dim_cp_ct', "hub_height": 150.0, "rotor_diameter": 242.24, "TSR": 8.0, "multi_dimensional_cp_ct": True, "power_thrust_table": { "ref_air_density": 1.225, "ref_tilt": 6.0, "cosine_loss_exponent_yaw": 1.88, "cosine_loss_exponent_tilt": 1.88, "power_thrust_data_file": 'iea_15MW_multi_dim_Tp_Hs.csv', } } self.farm = { "layout_x": X_COORDS, "layout_y": Y_COORDS, "turbine_type": [self.turbine] } self.flow_field = { "wind_speeds": WIND_SPEEDS, "wind_directions": WIND_DIRECTIONS, "turbulence_intensities": TURBULENCE_INTENSITIES, "wind_shear": 0.12, "wind_veer": 0.0, "air_density": 1.225, "reference_wind_height": self.turbine["hub_height"], } self.wake = { "model_strings": { "velocity_model": "jensen", "deflection_model": "jimenez", "combination_model": "sosfs", "turbulence_model": "crespo_hernandez", }, "wake_deflection_parameters": { "gauss": { "ad": 0.0, "alpha": 0.58, "bd": 0.0, "beta": 0.077, "dm": 1.0, "ka": 0.38, "kb": 0.004 }, "jimenez": { "ad": 0.0, "bd": 0.0, "kd": 0.05, }, "empirical_gauss": { "horizontal_deflection_gain_D": 3.0, "vertical_deflection_gain_D": -1, "deflection_rate": 22, "mixing_gain_deflection": 0.0, "yaw_added_mixing_gain": 0.0 }, }, "wake_velocity_parameters": { "gauss": { "alpha": 0.58, "beta": 0.077, "ka": 0.38, "kb": 0.004 }, "jensen": { "we": 0.05, }, "cc": { "a_s": 0.179367259, "b_s": 0.0118889215, "c_s1": 0.0563691592, "c_s2": 0.13290157, "a_f": 3.11, "b_f": -0.68, "c_f": 2.41, "alpha_mod": 1.0 }, "turbopark": { "A": 0.04, "sigma_max_rel": 4.0 }, "turboparkgauss": { "A": 0.04, "include_mirror_wake": True }, "empirical_gauss": { "wake_expansion_rates": [0.023, 0.008], "breakpoints_D": [10], "sigma_0_D": 0.28, "smoothing_length_D": 2.0, "mixing_gain_velocity": 2.0, "awc_wake_exp": 1.2, "awc_wake_denominator": 400 }, }, "wake_turbulence_parameters": { "crespo_hernandez": { "initial": 0.1, "constant": 0.5, "ai": 0.8, "downstream": -0.32 }, "wake_induced_mixing": { "atmospheric_ti_gain": 0.0 } }, "enable_secondary_steering": False, "enable_yaw_added_recovery": False, "enable_active_wake_mixing": False, "enable_transverse_velocities": False, } self.core = { "farm": self.farm, "flow_field": self.flow_field, "wake": self.wake, "solver": { "type": "turbine_grid", "turbine_grid_points": 3, }, "logging": { "console": {"enable": True, "level": 1}, "file": {"enable": False, "level": 1}, }, "name": "conftest", "description": "Inputs used for testing", "floris_version": "v4", } self.v3type_turbine = { "turbine_type": "nrel_5mw_v3type", "rotor_diameter": 125.88, "hub_height": 90.0, "generator_efficiency": 0.944, "operation_model": "cosine-loss", "pP": 1.88, "pT": 1.88, "ref_density_cp_ct": 1.225, "ref_tilt_cp_ct": 5.0, "TSR": 8.0, "power_thrust_table": { "power": [ 0.0, 0.0, 0.208546508, 0.385795061, 0.449038264, 0.474546985, 0.480994449, 0.481172749, 0.481235678, 0.481305875, 0.481238912, 0.481167356, 0.481081935, 0.481007003, 0.480880409, 0.480789285, 0.480737341, 0.480111543, 0.479218839, 0.479120347, 0.479022984, 0.478834971, 0.478597234, 0.478324162, 0.477994289, 0.477665338, 0.477253698, 0.476819542, 0.476368667, 0.475896732, 0.475404347, 0.474814698, 0.469087611, 0.456886723, 0.445156758, 0.433837552, 0.422902868, 0.412332387, 0.402110045, 0.316270768, 0.253224057, 0.205881042, 0.169640239, 0.141430529, 0.119144335, 0.101304591, 0.086856409, 0.075029591, 0.065256635, 0.057109143, 0.050263779, 0.044470536, 0.0, 0.0, ], "thrust": [ 0.0, 0.0, 1.132034888, 0.999470963, 0.917697381, 0.860849503, 0.815371198, 0.811614904, 0.807939328, 0.80443352, 0.800993851, 0.79768116, 0.794529244, 0.791495834, 0.788560434, 0.787217182, 0.787127977, 0.785839257, 0.783812219, 0.783568108, 0.783328285, 0.781194418, 0.777292539, 0.773464375, 0.769690236, 0.766001924, 0.762348072, 0.758760824, 0.755242872, 0.751792927, 0.748434131, 0.745113997, 0.717806682, 0.672204789, 0.63831272, 0.610176496, 0.585456847, 0.563222111, 0.542912273, 0.399312061, 0.310517829, 0.248633226, 0.203543725, 0.169616419, 0.143478955, 0.122938861, 0.106515296, 0.093026095, 0.081648606, 0.072197368, 0.064388275, 0.057782745, 0.0, 0.0, ], "wind_speed": [ 0.0, 2.9, 3.0, 4.0, 5.0, 6.0, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 9.0, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 25.1, 50.0, ], }, } ================================================ FILE: tests/controller_dependent_operation_model_unit_test.py ================================================ import logging from pathlib import Path import numpy as np import pytest from floris import FlorisModel from floris.core.turbine.controller_dependent_operation_model import ControllerDependentTurbine from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT, SimpleTurbine from tests.conftest import SampleInputs def test_submodel_attributes(): assert hasattr(ControllerDependentTurbine, "power") assert hasattr(ControllerDependentTurbine, "thrust_coefficient") assert hasattr(ControllerDependentTurbine, "axial_induction") def test_ControllerDependentTurbine_power_curve(): """ Test that the power curve is correctly loaded and interpolated. """ n_turbines = 1 turbine_data = SampleInputs().turbine turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"] = ( SampleInputs().controller_dependent_turbine_parameters ) data_file_path = Path(__file__).resolve().parents[1] / "floris" / "turbine_library" turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"]["cp_ct_data"] = \ np.load( data_file_path / turbine_data["power_thrust_table"] ["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ) N_test = 20 wind_speeds = np.tile( np.linspace(0, 30, N_test)[:, None, None, None], (1, n_turbines, 3, 3) ) power_test = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=np.zeros((N_test, n_turbines)), tilt_angles=turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((N_test, n_turbines)), power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) ) # Check that the powers all between 0 and rated assert (power_test >= 0).all() assert (power_test <= 5e6).all() # Check that zero power is produced at zero wind speed assert power_test[0, 0] == 0 # Check power is monotonically increasing, and also that it is flat above rated # NOTE: no cut-out defined for the ControllerDependentTurbine assert (np.diff(power_test.squeeze()) >= -1e4).all() assert (power_test[wind_speeds.mean(axis=(2,3)) > 12.0] == 5e6).all() def test_ControllerDependentTurbine_derating(): n_turbines = 1 turbine_data = SampleInputs().turbine turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"] = ( SampleInputs().controller_dependent_turbine_parameters ) data_file_path = Path(__file__).resolve().parents[1] / "floris" / "turbine_library" turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"]["cp_ct_data"] = \ np.load( data_file_path / turbine_data["power_thrust_table"] ["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ) N_test = 20 tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((N_test, n_turbines)) # define power set points power_setpoints = np.linspace(1e6,5e6,N_test).reshape(N_test,1) * np.ones((N_test, n_turbines)) # Set wind speed to above rated wind_speeds = 15.0 * np.ones((N_test, n_turbines, 2, 2)) # First run without sending the power setpoints power_baseline = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=25 * np.ones((N_test, n_turbines)), tilt_angles=tilt_angles_nom, power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) ).squeeze() # Now with power setpoints power_test = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=25 * np.ones((N_test, n_turbines)), tilt_angles=tilt_angles_nom, power_setpoints=power_setpoints ) # Check that power produced does not exceed baseline available power, # and that power produced matches setpoints to within 0.1% assert (power_test <= power_baseline).all() assert np.allclose(power_test, power_setpoints, rtol=1e-4) def test_ControllerDependentTurbine_yawing(): n_turbines = 1 turbine_data = SampleInputs().turbine turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"] = ( SampleInputs().controller_dependent_turbine_parameters ) data_file_path = Path(__file__).resolve().parents[1] / "floris" / "turbine_library" turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"]["cp_ct_data"] = \ np.load( data_file_path / turbine_data["power_thrust_table"] ["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ) N_test = 20 tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((N_test, n_turbines)) # Choose a wind speed near rated for NREL 5MW ws_above_rated = 12.0 wind_speeds = ws_above_rated * np.ones((N_test, n_turbines, 2, 2)) yaw_angle_array = np.linspace(-30,0,N_test) power_test = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=yaw_angle_array.reshape(N_test,1) * np.ones((N_test, n_turbines)), power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)), tilt_angles=tilt_angles_nom, tilt_interp=None ) # Check that for small yaw angles, we still produce rated power assert np.allclose(power_test[-3:-1,0], 5e6) # Check that power is (non-strictly) monotonically increasing as yaw angle # increases from -30 to 0 assert (np.diff(power_test.squeeze()) >= -1e4).all() def test_ControllerDependentTurbine_shear(): n_turbines = 1 turbine_data = SampleInputs().turbine turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"] = ( SampleInputs().controller_dependent_turbine_parameters ) data_file_path = Path(__file__).resolve().parents[1] / "floris" / "turbine_library" turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"]["cp_ct_data"] = \ np.load( data_file_path / turbine_data["power_thrust_table"] ["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ) N_test = 31 tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((N_test, n_turbines)) # Create array of shear (3 values: 0, 0.15 0.3) (ws multiplier at top/bottom of rotor) shear_array = np.linspace(0, 0.3, 3) shear_points = 1 + shear_array[:, None] * np.linspace(-1, 1, 5)[None, :] # Define wind speed array with n_grid = 5 (free stream wind speed 8.0 m/s) wind_speeds_no_shear = 8.0 * np.ones((1, n_turbines, 5, 5)) wind_speeds_shear = wind_speeds_no_shear * shear_points[:, None, None, :] wind_speeds_test = np.repeat(wind_speeds_shear, N_test, axis=0) yaw_max = 30 # Maximum yaw to test yaw_angles_test = np.linspace(-yaw_max, yaw_max, N_test).reshape(-1,1) power_test = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds_test, air_density=1.1, yaw_angles=np.tile(yaw_angles_test, (3,1)), power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((N_test*3, n_turbines)), tilt_angles=np.tile(tilt_angles_nom, (3,1)), tilt_interp=None ).squeeze() idx_mid = round((N_test-1)/2) power_ratio_no_shear = power_test[:N_test] / power_test[idx_mid] power_ratio_mid_shear = power_test[N_test:2*N_test] / power_test[idx_mid+N_test] power_ratio_most_shear = power_test[2*N_test:] / power_test[idx_mid+2*N_test] # Check symmetry of zero shear case assert np.allclose(power_ratio_no_shear, power_ratio_no_shear[::-1]) # Check that shear ordering correct on the left assert (power_ratio_no_shear[:idx_mid] >= power_ratio_mid_shear[:idx_mid]).all() assert (power_ratio_mid_shear[:idx_mid] >= power_ratio_most_shear[:idx_mid]).all() # And inverted on the right assert (power_ratio_no_shear[idx_mid+1:] <= power_ratio_mid_shear[idx_mid+1:]).all() assert (power_ratio_mid_shear[idx_mid+1:] <= power_ratio_most_shear[idx_mid+1:]).all() def test_ControllerDependentTurbine_regression(): """ Adding a regression test so that we can work with the model and stay confident that results are not changing. """ n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"] = ( SampleInputs().controller_dependent_turbine_parameters ) data_file_path = Path(__file__).resolve().parents[1] / "floris" / "turbine_library" turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"]["cp_ct_data"] = \ np.load( data_file_path / turbine_data["power_thrust_table"] ["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ) N_test = 20 tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((N_test, n_turbines)) power_setpoints_nom = POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) yaw_max = 30 # Maximum yaw to test yaw_angles_test = np.linspace(-yaw_max, yaw_max, N_test).reshape(-1,1) power_base = np.array([ 2395927.92868139, 2527726.50920564, 2644989.24683195, 2748134.16149699, 2837129.46422222, 2911510.74331788, 2971011.54743479, 3015566.03081713, 3045213.16926206, 3060014.98468406, 3060014.98468406, 3045213.16926206, 3015566.03081713, 2971011.54743479, 2911510.74331788, 2837129.46422222, 2748134.16149699, 2644989.24683195, 2527726.50920564, 2395927.92868139, ]) thrust_coefficient_base = np.array([ 0.65966861, 0.68401903, 0.70532378, 0.72373957, 0.73936337, 0.75223810, 0.76241954, 0.76997771, 0.77497954, 0.77746593, 0.77746593, 0.77497954, 0.76997771, 0.76241954, 0.75223810, 0.73936337, 0.72373957, 0.70532378, 0.68401903, 0.65966861, ]) axial_induction_base = np.array([ 0.20864674, 0.21929141, 0.22894655, 0.23757787, 0.24512918, 0.25152384, 0.25669950, 0.26061385, 0.26323979, 0.26455600, 0.26455600, 0.26323979, 0.26061385, 0.25669950, 0.25152384, 0.24512918, 0.23757787, 0.22894655, 0.21929141, 0.20864674, ]) power = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((N_test, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, yaw_angles=yaw_angles_test, power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() thrust_coefficient = ControllerDependentTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((N_test, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, yaw_angles=yaw_angles_test, power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() axial_induction = ControllerDependentTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((N_test, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, yaw_angles=yaw_angles_test, power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() # print(power) # print(thrust_coefficient) # print(axial_induction) assert np.allclose(power, power_base) assert np.allclose(thrust_coefficient, thrust_coefficient_base) assert np.allclose(axial_induction, axial_induction_base) def test_ControllerDependentTurbine_integration(): """ Test the ControllerDependentTurbine model with a range of wind speeds, and then whether it works regardless of number of grid points. """ n_turbines = 1 turbine_data = SampleInputs().turbine turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"] = ( SampleInputs().controller_dependent_turbine_parameters ) data_file_path = Path(__file__).resolve().parents[1] / "floris" / "turbine_library" turbine_data["power_thrust_table"]["controller_dependent_turbine_parameters"]["cp_ct_data"] = \ np.load( data_file_path / turbine_data["power_thrust_table"] ["controller_dependent_turbine_parameters"] ["cp_ct_data_file"] ) N_test = 6 tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((N_test, n_turbines)) power_setpoints_nom = POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) # Check runs over a range of wind speeds wind_speeds = np.linspace(1, 30, N_test) wind_speeds = np.tile(wind_speeds[:,None,None,None], (1, 1, 3, 3)) power0 = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=0 * np.ones((N_test, n_turbines)), power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() power20 = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=20 * np.ones((N_test, n_turbines)), power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() assert (power0 - power20 >= -1e6).all() # Won't compare; just checking runs as expected ControllerDependentTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=0 * np.ones((N_test, n_turbines)), power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() ControllerDependentTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=20 * np.ones((N_test, n_turbines)), power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() # Try a set of wind speeds for 5 grid points; then 2; then a single grid point # without any shear N_test = 1 n_turbines = 1 tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((N_test, n_turbines)) power_setpoints_nom = POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) wind_speeds = 10.0 * np.ones((N_test, n_turbines, 5, 5)) power5gp = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=0 * np.ones((N_test, n_turbines)), power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() wind_speeds = 10.0 * np.ones((N_test, n_turbines, 2, 2)) power2gp = ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=0 * np.ones((N_test, n_turbines)), power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ).squeeze() assert np.allclose(power5gp, power2gp) # No shear information for the TUM model to use wind_speeds = 10.0 * np.ones((N_test, n_turbines, 1, 1)) with pytest.raises(ValueError): ControllerDependentTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds, air_density=1.1, yaw_angles=0 * np.ones((N_test, n_turbines)), power_setpoints=power_setpoints_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ) def test_CpCt_data_consistency(): """ Test that the Cp/Ct data is consistent, within reason, with the "normal" data. These tests currently do not pass, and the "assert" statements have been removed. However, the code has been left in place to highlight the differences and leave room for possibly updating the Cp/Ct data in future to match the reference power and thrust curves. """ n_turbines = 1 N_test = 6 wind_speeds = np.tile( np.linspace(0, 30, N_test)[:, None, None, None], (1, n_turbines, 3, 3) ) # Check power, thrust, and axial induction for IEA 15MW, IEA 10MW, and NREL 5MW for turbine in ["iea_15MW", "iea_10MW", "nrel_5MW"]: # Get the turbine_data yaml_file = Path(__file__).resolve().parent / "data" / "input_full.yaml" fmodel = FlorisModel(configuration=yaml_file) fmodel.set(turbine_type=[turbine]) power_thrust_table = fmodel.core.farm.turbine_map[0].power_thrust_table tilt_angles_nom = power_thrust_table["ref_tilt"] * np.ones((N_test, n_turbines)) power_base = SimpleTurbine.power( power_thrust_table=power_thrust_table, velocities=wind_speeds, air_density=1.1, ).squeeze() power_test = ControllerDependentTurbine.power( power_thrust_table=power_thrust_table, velocities=wind_speeds, air_density=1.1, yaw_angles=np.zeros((N_test, n_turbines)), tilt_angles=tilt_angles_nom, power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) ).squeeze() thrust_coefficient_base = SimpleTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=wind_speeds, air_density=1.1, ).squeeze() thrust_coefficient_test = ControllerDependentTurbine.thrust_coefficient( power_thrust_table=power_thrust_table, velocities=wind_speeds, air_density=1.1, yaw_angles=np.zeros((N_test, n_turbines)), tilt_angles=tilt_angles_nom, tilt_interp=None, power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) ).squeeze() axial_induction_base = SimpleTurbine.axial_induction( power_thrust_table=power_thrust_table, velocities=wind_speeds, air_density=1.1, ).squeeze() axial_induction_test = ControllerDependentTurbine.axial_induction( power_thrust_table=power_thrust_table, velocities=wind_speeds, air_density=1.1, yaw_angles=np.zeros((N_test, n_turbines)), tilt_angles=tilt_angles_nom, tilt_interp=None, power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((N_test, n_turbines)) ).squeeze() # Don't match below cut-in or above cut-out; this is known. Mask those out. nonzero_power = power_base > 0 # Check within 5% of the base data (currently fails, "asserts" removed) np.allclose(power_base[nonzero_power], power_test[nonzero_power], rtol=5e-2) np.allclose( thrust_coefficient_base[nonzero_power], thrust_coefficient_test[nonzero_power], rtol=5e-2 ) np.allclose( axial_induction_base[nonzero_power], axial_induction_test[nonzero_power], rtol=5e-2 ) def test_CpCt_warning(caplog): yaml_file = Path(__file__).resolve().parent / "data" / "input_full.yaml" fmodel = FlorisModel(configuration=yaml_file) with caplog.at_level(logging.WARNING): fmodel.set_operation_model("controller-dependent") assert "demonstration purposes only" in caplog.text caplog.clear() ================================================ FILE: tests/convert_v3_to_v4_test.py ================================================ import os from pathlib import Path import floris from floris import FlorisModel CONVERT_FOLDER = Path(__file__).resolve().parent / "v3_to_v4_convert_test" FLORIS_FOLDER = Path(floris.__file__).resolve().parent def test_v3_to_v4_convert(): # Note certain filenames filename_v3_floris = "gch.yaml" filename_v4_floris = "gch_v4.yaml" filename_v3_turbine = "nrel_5MW_v3.yaml" filename_v4_turbine = "nrel_5MW_v3_v4.yaml" # Copy convert scripts from FLORIS_FOLDER to CONVERT_FOLDER os.system(f"cp {FLORIS_FOLDER / 'convert_turbine_v3_to_v4.py'} {CONVERT_FOLDER}") os.system(f"cp {FLORIS_FOLDER / 'convert_floris_input_v3_to_v4.py'} {CONVERT_FOLDER}") # Change directory to the test folder os.chdir(CONVERT_FOLDER) # Run the converter on the turbine file os.system(f"python convert_turbine_v3_to_v4.py {filename_v3_turbine}") # Run the converter on the floris file os.system(f"python convert_floris_input_v3_to_v4.py {filename_v3_floris}") # Go through the file filename_v4_floris and replace f"!include {filename_v3_turbine}" # with f"!include {filename_v4_turbine}" with open(filename_v4_floris, "r") as file: filedata = file.read() filedata = filedata.replace( f"!include {filename_v3_turbine}", f"!include {filename_v4_turbine}" ) with open(filename_v4_floris, "w") as file: file.write(filedata) # Now confirm that the converted file can be loaded by FLORIS fmodel = FlorisModel(filename_v4_floris) # Now confirm this model runs fmodel.run() # Delete the newly created files to clean up os.system(f"rm {filename_v4_floris}") os.system(f"rm {filename_v4_turbine}") os.system("rm convert_turbine_v3_to_v4.py") os.system("rm convert_floris_input_v3_to_v4.py") ================================================ FILE: tests/core_unit_test.py ================================================ from pathlib import Path import yaml from floris.core import ( Core, Farm, FlowField, TurbineGrid, WakeModelManager, ) TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" DICT_INPUT = yaml.load(open(YAML_INPUT, "r"), Loader=yaml.SafeLoader) def test_read_yaml(): fmodel = Core.from_file(YAML_INPUT) assert isinstance(fmodel, Core) def test_read_dict(): fmodel = Core.from_dict(DICT_INPUT) assert isinstance(fmodel, Core) def test_init(): fmodel = Core.from_dict(DICT_INPUT) assert isinstance(fmodel.farm, Farm) assert isinstance(fmodel.wake, WakeModelManager) assert isinstance(fmodel.flow_field, FlowField) def test_asdict(turbine_grid_fixture: TurbineGrid): floris = Core.from_dict(DICT_INPUT) floris.flow_field.initialize_velocity_field(turbine_grid_fixture) dict1 = floris.as_dict() new_floris = Core.from_dict(dict1) new_floris.flow_field.initialize_velocity_field(turbine_grid_fixture) dict2 = new_floris.as_dict() assert dict1 == dict2 ================================================ FILE: tests/data/__init__.py ================================================ ================================================ FILE: tests/data/input_full.yaml ================================================ name: test_input description: Single turbine for testing floris_version: v4 logging: console: enable: false level: WARNING file: enable: false level: WARNING solver: type: turbine_grid turbine_grid_points: 3 farm: layout_x: - 0.0 layout_y: - 0.0 turbine_type: - nrel_5MW flow_field: air_density: 1.225 reference_wind_height: 90.0 turbulence_intensities: - 0.06 wind_directions: - 270.0 wind_shear: 0.12 wind_speeds: - 8.0 wind_veer: 0.0 wake: model_strings: combination_model: sosfs deflection_model: gauss turbulence_model: crespo_hernandez velocity_model: gauss enable_secondary_steering: true enable_yaw_added_recovery: true enable_active_wake_mixing: true enable_transverse_velocities: true wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 turboparkgauss: A: 0.04 include_mirror_wake: True wake_turbulence_parameters: crespo_hernandez: initial: 0.01 constant: 0.9 ai: 0.83 downstream: -0.25 ================================================ FILE: tests/data/nrel_5MW_custom.yaml ================================================ turbine_type: 'nrel_5MW_custom' hub_height: 90.0 rotor_diameter: 126.0 TSR: 8.0 power_thrust_table: cosine_loss_exponent_yaw: 1.88 cosine_loss_exponent_tilt: 1.88 ref_air_density: 1.225 ref_tilt: 5.0 power: - 0.0 - 0.0 - 40.518011517569214 - 177.67162506419703 - 403.900880943964 - 737.5889584824021 - 1187.1774030611875 - 1239.245945375778 - 1292.5184293723503 - 1347.3213147477102 - 1403.2573725578948 - 1460.7011898730707 - 1519.6419125979983 - 1580.174365096404 - 1642.1103166918167 - 1705.758292831 - 1771.1659528893977 - 2518.553107505315 - 3448.381605840943 - 3552.140809000129 - 3657.9545431794127 - 3765.121299313842 - 3873.928844315059 - 3984.4800226955504 - 4096.582833096852 - 4210.721306623712 - 4326.154305853405 - 4443.395565353604 - 4562.497934188341 - 4683.419890251577 - 4806.164748311019 - 4929.931918769215 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 5000.00 - 0.0 - 0.0 thrust_coefficient: - 0.0 - 0.0 - 1.132034888 - 0.999470963 - 0.917697381 - 0.860849503 - 0.815371198 - 0.811614904 - 0.807939328 - 0.80443352 - 0.800993851 - 0.79768116 - 0.794529244 - 0.791495834 - 0.788560434 - 0.787217182 - 0.787127977 - 0.785839257 - 0.783812219 - 0.783568108 - 0.783328285 - 0.781194418 - 0.777292539 - 0.773464375 - 0.769690236 - 0.766001924 - 0.762348072 - 0.758760824 - 0.755242872 - 0.751792927 - 0.748434131 - 0.745113997 - 0.717806682 - 0.672204789 - 0.63831272 - 0.610176496 - 0.585456847 - 0.563222111 - 0.542912273 - 0.399312061 - 0.310517829 - 0.248633226 - 0.203543725 - 0.169616419 - 0.143478955 - 0.122938861 - 0.106515296 - 0.093026095 - 0.081648606 - 0.072197368 - 0.064388275 - 0.057782745 - 0.0 - 0.0 wind_speed: - 0.0 - 2.9 - 3.0 - 4.0 - 5.0 - 6.0 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 7.5 - 7.6 - 7.7 - 7.8 - 7.9 - 8.0 - 9.0 - 10.0 - 10.1 - 10.2 - 10.3 - 10.4 - 10.5 - 10.6 - 10.7 - 10.8 - 10.9 - 11.0 - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 - 11.9 - 12.0 - 13.0 - 14.0 - 15.0 - 16.0 - 17.0 - 18.0 - 19.0 - 20.0 - 21.0 - 22.0 - 23.0 - 24.0 - 25.0 - 25.1 - 50.0 ================================================ FILE: tests/data/wind_rose.csv ================================================ ws,wd,freq_val 8,270,0.25 9,270,0.25 8,280,0.5 ================================================ FILE: tests/data/wind_ti_rose.csv ================================================ ws,wd,ti,freq_val 8,270,0.06,0.25 9,270,0.06,0.25 8,280,0.07,0.5 ================================================ FILE: tests/data/wrg_test.wrg ================================================ 2 3 0.0 0.0 1000.0 0 0 0 90 9.5 2.25 0 12 116 106 273 86 93 228 61 76 220 54 74 220 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 0 1000 0 90 9.6 2.31 0 12 116 107 341 86 93 228 61 76 220 54 74 220 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 0 2000 0 90 9.7 2.36 0 12 116 106 409 86 93 228 61 76 220 54 74 220 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 1000 0 0 90 9.6 2.34 0 12 116 106 273 86 93 228 61 76 220 54 82 407 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 1000 1000 0 90 9.6 2.40 0 12 116 107 341 86 93 228 61 76 220 54 82 407 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 1000 2000 0 90 9.7 2.46 0 12 116 106 409 86 93 228 61 76 220 54 82 407 66 79 220 121 98 244 177 107 279 84 89 232 43 70 195 36 75 188 53 100 201 98 111 267 ================================================ FILE: tests/farm_unit_test.py ================================================ from copy import deepcopy from pathlib import Path import numpy as np import pytest from floris.core import Farm from floris.utilities import load_yaml from tests.conftest import ( N_FINDEX, N_TURBINES, SampleInputs, ) def test_farm_init_homogenous_turbines(): farm_data = SampleInputs().farm turbine_data = SampleInputs().turbine layout_x = farm_data["layout_x"] layout_y = farm_data["layout_y"] coordinates = np.array([ np.array([x, y, turbine_data["hub_height"]]) for x, y in zip(layout_x, layout_y) ]) farm = Farm( layout_x=layout_x, layout_y=layout_y, turbine_type=[turbine_data] ) # TODO: these all pass on mac and fail on linux # turbine_type=[turbine_data] # turbine_type=[turbine_data["turbine_type"]] farm.construct_hub_heights() farm.set_yaw_angles_to_ref_yaw(N_FINDEX) # Check initial values np.testing.assert_array_equal(farm.coordinates, coordinates) assert isinstance(farm.layout_x, np.ndarray) assert isinstance(farm.layout_y, np.ndarray) def test_asdict(sample_inputs_fixture: SampleInputs): farm = Farm.from_dict(sample_inputs_fixture.farm) farm.construct_hub_heights() farm.construct_turbine_ref_tilts() farm.set_yaw_angles_to_ref_yaw(N_FINDEX) farm.set_tilt_to_ref_tilt(N_FINDEX) farm.set_power_setpoints_to_ref_power(N_FINDEX) farm.set_awc_modes_to_ref_mode(N_FINDEX) farm.set_awc_amplitudes_to_ref_amp(N_FINDEX) farm.set_awc_frequencies_to_ref_freq(N_FINDEX) dict1 = farm.as_dict() new_farm = farm.from_dict(dict1) new_farm.construct_hub_heights() new_farm.construct_turbine_ref_tilts() new_farm.set_yaw_angles_to_ref_yaw(N_FINDEX) new_farm.set_tilt_to_ref_tilt(N_FINDEX) new_farm.set_power_setpoints_to_ref_power(N_FINDEX) new_farm.set_awc_modes_to_ref_mode(N_FINDEX) new_farm.set_awc_amplitudes_to_ref_amp(N_FINDEX) new_farm.set_awc_frequencies_to_ref_freq(N_FINDEX) dict2 = new_farm.as_dict() assert dict1 == dict2 def test_check_turbine_type(sample_inputs_fixture: SampleInputs): # 1 definition for multiple turbines in the farm farm_data = deepcopy(sample_inputs_fixture.farm) farm_data["turbine_type"] = ["nrel_5MW"] farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) farm = Farm.from_dict(farm_data) assert len(farm.turbine_type) == 1 assert len(farm.turbine_definitions) == 5 # N definitions for M turbines farm_data = deepcopy(sample_inputs_fixture.farm) farm_data["turbine_type"] = ["nrel_5MW", "nrel_5MW"] farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) with pytest.raises(ValueError): Farm.from_dict(farm_data) # All list of strings from internal library farm_data = deepcopy(sample_inputs_fixture.farm) farm_data["turbine_type"] = ["nrel_5MW", "iea_10MW", "iea_15MW", "nrel_5MW", "nrel_5MW"] farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) farm = Farm.from_dict(farm_data) assert len(farm.turbine_type) == 5 assert len(farm.turbine_definitions) == 5 # String not found in internal library farm_data = deepcopy(sample_inputs_fixture.farm) farm_data["turbine_type"] = ["asdf"] farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) with pytest.raises(FileNotFoundError): Farm.from_dict(farm_data) # All list of dicts from external library farm_data = deepcopy(sample_inputs_fixture.farm) external_library = Path(__file__).parent / "data" turbine_def = load_yaml(external_library / "nrel_5MW_custom.yaml") farm_data["turbine_type"] = [turbine_def] * 5 farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) Farm.from_dict(farm_data) assert len(farm.turbine_type) == 5 assert len(farm.turbine_definitions) == 5 # Check that error is correctly raised if two turbines have the same name farm_data = deepcopy(sample_inputs_fixture.farm) external_library = Path(__file__).parent / "data" farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) turbine_def = load_yaml(external_library / "nrel_5MW_custom.yaml") turbine_def_mod = deepcopy(turbine_def) turbine_def_mod["hub_height"] = 100.0 # Change the hub height of the last turbine farm_data["turbine_type"] = [turbine_def]*4 + [turbine_def_mod] with pytest.raises(ValueError): farm = Farm.from_dict(farm_data) # Check this also raises an error in a nested level of the turbine definition turbine_def_mod = deepcopy(turbine_def) turbine_def_mod["power_thrust_table"]["wind_speed"][-1] = -100.0 farm_data["turbine_type"] = [turbine_def]*4 + [turbine_def_mod] with pytest.raises(ValueError): farm = Farm.from_dict(farm_data) # Check that no error is raised, and the expected hub heights are seen, # if turbine_type is correctly updated farm_data = deepcopy(sample_inputs_fixture.farm) external_library = Path(__file__).parent / "data" farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) turbine_def = load_yaml(external_library / "nrel_5MW_custom.yaml") turbine_def_mod = deepcopy(turbine_def) turbine_def_mod["hub_height"] = 100.0 # Change the hub height of the last turbine turbine_def_mod["turbine_type"] = "nrel_5MW_custom_2" farm_data["turbine_type"] = [turbine_def]*4 + [turbine_def_mod] farm = Farm.from_dict(farm_data) for i in range(4): assert farm.turbine_definitions[i]["hub_height"] == turbine_def["hub_height"] assert farm.turbine_definitions[-1]["hub_height"] == 100.0 farm.construct_turbine_map() for i in range(4): assert farm.turbine_map[i].hub_height == turbine_def["hub_height"] assert farm.turbine_map[-1].hub_height == 100.0 # Duplicate type found in external and internal library farm_data = deepcopy(sample_inputs_fixture.farm) external_library = Path(__file__).parent / "data" farm_data["turbine_library_path"] = external_library farm_data["turbine_type"] = ["nrel_5MW"] with pytest.raises(ValueError): Farm.from_dict(farm_data) # 1 turbine as string from internal library, 1 turbine as dict from external library farm_data = deepcopy(sample_inputs_fixture.farm) external_library = Path(__file__).parent / "data" turbine_def = load_yaml(external_library / "nrel_5MW_custom.yaml") farm_data["turbine_type"] = [turbine_def] * 5 farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) farm_data["turbine_type"] = ["nrel_5MW", turbine_def, "nrel_5MW", turbine_def, "nrel_5MW"] Farm.from_dict(farm_data) assert len(farm.turbine_type) == 5 assert len(farm.turbine_definitions) == 5 # 1 turbine as string from internal library, 1 turbine as string from external library farm_data = deepcopy(sample_inputs_fixture.farm) external_library = Path(__file__).parent / "data" farm_data["turbine_library_path"] = external_library farm_data["turbine_type"] = 4 * ["iea_10MW"] + ["nrel_5MW_custom"] farm_data["layout_x"] = np.arange(0, 500, 100) farm_data["layout_y"] = np.zeros(5) Farm.from_dict(farm_data) assert len(farm.turbine_type) == 5 assert len(farm.turbine_definitions) == 5 def test_farm_external_library(sample_inputs_fixture: SampleInputs): external_library = Path(__file__).parent / "data" # Demonstrate a passing case farm_data = deepcopy(SampleInputs().farm) farm_data["turbine_library_path"] = external_library farm_data["turbine_type"] = ["nrel_5MW_custom"] * N_TURBINES farm = Farm.from_dict(farm_data) assert farm.turbine_library_path == external_library # Demonstrate a file not existing in the user library, but exists in the internal library, so # the loading is successful farm_data["turbine_library_path"] = external_library farm_data["turbine_type"] = ["iea_10MW"] * N_TURBINES farm = Farm.from_dict(farm_data) assert farm.turbine_definitions[0]["turbine_type"] == "iea_10MW" # Demonstrate a failing case with an incorrect library location farm_data["turbine_library_path"] = external_library / "turbine_library_path" with pytest.raises(FileExistsError): Farm.from_dict(farm_data) # Demonstrate a failing case where there is a duplicated turbine between the internal # and external turbine libraries farm_data = deepcopy(SampleInputs().farm) farm_data["turbine_library_path"] = external_library farm_data["turbine_type"] = ["nrel_5MW"] * N_TURBINES with pytest.raises(ValueError): Farm.from_dict(farm_data) # Demonstrate a failing case where there a turbine does not exist in either farm_data = deepcopy(SampleInputs().farm) farm_data["turbine_library_path"] = external_library farm_data["turbine_type"] = ["FAKE_TURBINE"] * N_TURBINES with pytest.raises(FileNotFoundError): Farm.from_dict(farm_data) ================================================ FILE: tests/floris_model_integration_test.py ================================================ import logging from pathlib import Path import numpy as np import pytest import yaml from floris import ( FlorisModel, TimeSeries, WindRose, ) from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED from tests.conftest import SampleInputs TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" def test_default_init(): # Test getting the default dict defaults = FlorisModel.get_defaults() fmodel1 = FlorisModel(defaults) assert isinstance(fmodel1, FlorisModel) # Test that there are no side effects from changing the default dict defaults2 = FlorisModel.get_defaults() defaults2["farm"]["layout_x"] = [0, 1000] defaults2["farm"]["layout_y"] = [0, 0] fmodel2 = FlorisModel(defaults2) assert fmodel2.core.as_dict() != FlorisModel(defaults).core.as_dict() # Test using the "default" string # This checks that the init works and that the default dictionary hasn't changed fmodel3 = FlorisModel("defaults") assert fmodel1.core.as_dict() == fmodel3.core.as_dict() def test_read_yaml(): fmodel = FlorisModel(configuration=YAML_INPUT) assert isinstance(fmodel, FlorisModel) def test_assign_setpoints(): fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) # Test setting yaw angles via a list, integers, numpy array fmodel.set(yaw_angles=[[20.0, 30.0]]) fmodel.set(yaw_angles=[[20, 30]]) fmodel.set(yaw_angles=np.array([[20.0, 30.0]])) # Test setting power setpoints in various ways fmodel.set(power_setpoints=[[1e6, 2e6]]) fmodel.set(power_setpoints=np.array([[1e6, 2e6]])) # Disable turbines fmodel.set(disable_turbines=[[True, False]]) fmodel.set(disable_turbines=np.array([[True, False]])) # Combination fmodel.set(yaw_angles=[[0, 30]], power_setpoints=np.array([[1e6, None]])) # power_setpoints and disable_turbines (disable_turbines overrides power_setpoints) fmodel.set(power_setpoints=[[1e6, 2e6]], disable_turbines=[[True, False]]) assert np.allclose(fmodel.core.farm.power_setpoints, np.array([[POWER_SETPOINT_DISABLED, 2e6]])) # Setting sequentially is equivalent to setting together fmodel.reset_operation() fmodel.set(disable_turbines=[[True, False]]) fmodel.set(yaw_angles=[[0, 30]]) assert np.allclose( fmodel.core.farm.power_setpoints, np.array([[POWER_SETPOINT_DISABLED, POWER_SETPOINT_DEFAULT]]) ) assert np.allclose(fmodel.core.farm.yaw_angles, np.array([[0, 30]])) fmodel.set(disable_turbines=[[True, False]], yaw_angles=[[0, 30]]) assert np.allclose( fmodel.core.farm.power_setpoints, np.array([[POWER_SETPOINT_DISABLED, POWER_SETPOINT_DEFAULT]]) ) assert np.allclose(fmodel.core.farm.yaw_angles, np.array([[0, 30]])) def test_set_run(): """ These tests are designed to test the set / run sequence to ensure that inputs are set when they should be, not set when they shouldn't be, and that the run sequence retains or resets information as intended. """ # In FLORIS v3.2, running calculate_wake twice incorrectly set the yaw angles when the # first time has non-zero yaw settings but the second run had all-zero yaw settings. # The test below asserts that the yaw angles are correctly set in subsequent calls to run. fmodel = FlorisModel(configuration=YAML_INPUT) yaw_angles = 20 * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(yaw_angles=yaw_angles) fmodel.run() assert fmodel.core.farm.yaw_angles == yaw_angles yaw_angles = np.zeros((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(yaw_angles=yaw_angles) fmodel.run() assert fmodel.core.farm.yaw_angles == yaw_angles # Verify making changes to the layout, wind speed, wind direction and # turbulence intensity both before and after running the calculation fmodel.reset_operation() fmodel.set( layout_x=[0, 0], layout_y=[0, 1000], wind_speeds=[8, 8], wind_directions=[270, 270], turbulence_intensities=[0.06, 0.06] ) assert np.array_equal(fmodel.core.farm.layout_x, np.array([0, 0])) assert np.array_equal(fmodel.core.farm.layout_y, np.array([0, 1000])) assert np.array_equal(fmodel.core.flow_field.wind_speeds, np.array([8, 8])) assert np.array_equal(fmodel.core.flow_field.wind_directions, np.array([270, 270])) # Double check that nothing has changed after running the calculation fmodel.run() assert np.array_equal(fmodel.core.farm.layout_x, np.array([0, 0])) assert np.array_equal(fmodel.core.farm.layout_y, np.array([0, 1000])) assert np.array_equal(fmodel.core.flow_field.wind_speeds, np.array([8, 8])) assert np.array_equal(fmodel.core.flow_field.wind_directions, np.array([270, 270])) # Verify that changing wind shear doesn't change the other settings above fmodel.set(wind_shear=0.1) assert fmodel.core.flow_field.wind_shear == 0.1 assert np.array_equal(fmodel.core.farm.layout_x, np.array([0, 0])) assert np.array_equal(fmodel.core.farm.layout_y, np.array([0, 1000])) assert np.array_equal(fmodel.core.flow_field.wind_speeds, np.array([8, 8])) assert np.array_equal(fmodel.core.flow_field.wind_directions, np.array([270, 270])) # Verify that operation set-points are retained after changing other settings yaw_angles = 20 * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(yaw_angles=yaw_angles) assert np.array_equal(fmodel.core.farm.yaw_angles, yaw_angles) fmodel.set() assert np.array_equal(fmodel.core.farm.yaw_angles, yaw_angles) fmodel.set(wind_speeds=[10, 10]) assert np.array_equal(fmodel.core.farm.yaw_angles, yaw_angles) power_setpoints = 1e6 * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(power_setpoints=power_setpoints) assert np.array_equal(fmodel.core.farm.yaw_angles, yaw_angles) assert np.array_equal(fmodel.core.farm.power_setpoints, power_setpoints) # Test that setting power setpoints through the .set() function actually sets the # power setpoints in the floris object fmodel.reset_operation() power_setpoints = 1e6 * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(power_setpoints=power_setpoints) fmodel.run() assert np.array_equal(fmodel.core.farm.power_setpoints, power_setpoints) # Similar to above, any "None" set-points should be set to the default value power_setpoints = np.array([[1e6, None]]) fmodel.set(layout_x=[0, 0], layout_y=[0, 1000], power_setpoints=power_setpoints) fmodel.run() assert np.array_equal( fmodel.core.farm.power_setpoints, np.array([[power_setpoints[0, 0], POWER_SETPOINT_DEFAULT]]) ) def test_reset_operation(): # Calling the reset function should reset the power setpoints to the default values fmodel = FlorisModel(configuration=YAML_INPUT) yaw_angles = 20 * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) power_setpoints = 1e6 * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(power_setpoints=power_setpoints, yaw_angles=yaw_angles) fmodel.run() fmodel.reset_operation() assert fmodel.core.farm.yaw_angles == np.zeros( (fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines) ) assert fmodel.core.farm.power_setpoints == ( POWER_SETPOINT_DEFAULT * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) ) # Double check that running the calculate also doesn't change the operating set points fmodel.run() assert fmodel.core.farm.yaw_angles == np.zeros( (fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines) ) assert fmodel.core.farm.power_setpoints == ( POWER_SETPOINT_DEFAULT * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) ) def test_run_no_wake(): # In FLORIS v3.2, running calculate_no_wake twice incorrectly set the yaw angles when the first # time has non-zero yaw settings but the second run had all-zero yaw settings. The test below # asserts that the yaw angles are correctly set in subsequent calls to run_no_wake. fmodel = FlorisModel(configuration=YAML_INPUT) yaw_angles = 20 * np.ones((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(yaw_angles=yaw_angles) fmodel.run_no_wake() assert fmodel.core.farm.yaw_angles == yaw_angles yaw_angles = np.zeros((fmodel.core.flow_field.n_findex, fmodel.core.farm.n_turbines)) fmodel.set(yaw_angles=yaw_angles) fmodel.run_no_wake() assert fmodel.core.farm.yaw_angles == yaw_angles # With no wake and three turbines in a line, the power for all turbines with zero yaw # should be the same fmodel.reset_operation() fmodel.set(layout_x=[0, 200, 4000], layout_y=[0, 0, 0]) fmodel.run_no_wake() power_no_wake = fmodel.get_turbine_powers() assert len(np.unique(power_no_wake)) == 1 def test_get_turbine_powers(): # Get turbine powers should return n_findex x n_turbine powers # Apply the same wind speed and direction multiple times and confirm all equal fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 8.0, 8.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) n_findex = len(wind_directions) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) n_turbines = len(layout_x) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() assert turbine_powers.shape[0] == n_findex assert turbine_powers.shape[1] == n_turbines assert turbine_powers[0, 0] == turbine_powers[1, 0] def test_get_farm_power(): fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 8.0, 8.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) n_findex = len(wind_directions) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) # n_turbines = len(layout_x) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) fmodel.run() turbine_powers = fmodel.get_turbine_powers() farm_powers = fmodel.get_farm_power() assert farm_powers.shape[0] == n_findex # Assert farm power is the same as summing turbine powers # over the turbine axis farm_power_from_turbine = turbine_powers.sum(axis=1) np.testing.assert_almost_equal(farm_power_from_turbine, farm_powers) # Test using weights to disable the second turbine turbine_weights = np.array([1.0, 0.0]) farm_powers = fmodel.get_farm_power(turbine_weights=turbine_weights) # Assert farm power is now equal to the 0th turbine since 1st is # disabled farm_power_from_turbine = turbine_powers[:, 0] np.testing.assert_almost_equal(farm_power_from_turbine, farm_powers) # Finally, test using weights only disable the 1 turbine on the final # findex values turbine_weights = np.array([[1.0, 1.0], [1.0, 1.0], [1.0, 0.0]]) farm_powers = fmodel.get_farm_power(turbine_weights=turbine_weights) turbine_powers[-1, 1] = 0 farm_power_from_turbine = turbine_powers.sum(axis=1) np.testing.assert_almost_equal(farm_power_from_turbine, farm_powers) def test_disable_turbines(): fmodel = FlorisModel(configuration=YAML_INPUT) # Set to mixed turbine model with open( str( fmodel.core.as_dict()["farm"]["turbine_library_path"] / (fmodel.core.as_dict()["farm"]["turbine_type"][0] + ".yaml") ) ) as t: turbine_type = yaml.safe_load(t) turbine_type["operation_model"] = "mixed" fmodel.set(turbine_type=[turbine_type]) # Init to n-findex = 2, n_turbines = 3 fmodel.set( wind_speeds=np.array([8.,8.,]), wind_directions=np.array([270.,270.]), turbulence_intensities=np.array([0.06,0.06]), layout_x = [0,1000,2000], layout_y=[0,0,0] ) # Confirm that using a disable value with wrong n_findex raises error with pytest.raises(ValueError): fmodel.set(disable_turbines=np.zeros((10, 3), dtype=bool)) fmodel.run() # Confirm that using a disable value with wrong n_turbines raises error with pytest.raises(ValueError): fmodel.set(disable_turbines=np.zeros((2, 10), dtype=bool)) fmodel.run() # Confirm that if all turbines are disabled, power is near 0 for all turbines fmodel.set(disable_turbines=np.ones((2, 3), dtype=bool)) fmodel.run() turbines_powers = fmodel.get_turbine_powers() np.testing.assert_allclose(turbines_powers, 0, atol=0.1) # Confirm the same for run_no_wake fmodel.run_no_wake() turbines_powers = fmodel.get_turbine_powers() np.testing.assert_allclose(turbines_powers, 0, atol=0.1) # Confirm that if all disabled values set to false, equivalent to running normally fmodel.reset_operation() fmodel.run() turbines_powers_normal = fmodel.get_turbine_powers() fmodel.set(disable_turbines=np.zeros((2, 3), dtype=bool)) fmodel.run() turbines_powers_false_disable = fmodel.get_turbine_powers() np.testing.assert_allclose(turbines_powers_normal,turbines_powers_false_disable,atol=0.1) # Confirm the same for run_no_wake fmodel.run_no_wake() turbines_powers_normal = fmodel.get_turbine_powers() fmodel.set(disable_turbines=np.zeros((2, 3), dtype=bool)) fmodel.run_no_wake() turbines_powers_false_disable = fmodel.get_turbine_powers() np.testing.assert_allclose(turbines_powers_normal,turbines_powers_false_disable,atol=0.1) # Confirm the shutting off the middle turbine is like removing from the layout # In terms of impact on third turbine disable_turbines = np.zeros((2, 3), dtype=bool) disable_turbines[:,1] = [True, True] fmodel.set(disable_turbines=disable_turbines) fmodel.run() power_with_middle_disabled = fmodel.get_turbine_powers() # Two turbine case to compare against above fmodel_remove_middle = fmodel.copy() fmodel_remove_middle.set(layout_x=[0,2000], layout_y=[0, 0]) fmodel_remove_middle.run() power_with_middle_removed = fmodel_remove_middle.get_turbine_powers() np.testing.assert_almost_equal(power_with_middle_disabled[0,2], power_with_middle_removed[0,1]) np.testing.assert_almost_equal(power_with_middle_disabled[1,2], power_with_middle_removed[1,1]) # Check that yaw angles are correctly set when turbines are disabled fmodel.set( layout_x=[0, 1000, 2000], layout_y=[0, 0, 0], disable_turbines=disable_turbines, yaw_angles=np.ones((2, 3)) ) fmodel.run() assert (fmodel.core.farm.yaw_angles == np.array([[1.0, 0.0, 1.0], [1.0, 0.0, 1.0]])).all() def test_get_farm_aep(caplog): fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 8.0, 8.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) n_findex = len(wind_directions) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) # n_turbines = len(layout_x) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) fmodel.run() farm_powers = fmodel.get_farm_power() # Start with uniform frequency freq = np.ones(n_findex) freq = freq / np.sum(freq) # Check warning raised if freq not passed; no warning if freq passed with caplog.at_level(logging.WARNING): fmodel.get_farm_AEP() assert caplog.text != "" # Checking not empty caplog.clear() with caplog.at_level(logging.WARNING): fmodel.get_farm_AEP(freq=freq) assert caplog.text == "" # Checking empty farm_aep = fmodel.get_farm_AEP(freq=freq) aep = np.sum(np.multiply(freq, farm_powers) * 365 * 24) # In this case farm_aep should match farm powers np.testing.assert_allclose(farm_aep, aep) # Also check get_expected_farm_power expected_farm_power = fmodel.get_expected_farm_power(freq=freq) np.testing.assert_allclose(expected_farm_power, aep / (365 * 24)) def test_expected_farm_power_regression(): fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 8.0, 8.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) fmodel.run() expected_farm_power = fmodel.get_expected_farm_power() # Assert the expected farm power has not inadvetently changed np.testing.assert_allclose(expected_farm_power, 3507908.918358342, atol=1e-1) def test_expected_farm_power_equals_sum_of_expected_turbine_powers(): fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 8.0, 8.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) fmodel.run() expected_farm_power = fmodel.get_expected_farm_power() expected_turbine_powers = fmodel.get_expected_turbine_powers() # Assert the expected farm power is the sum of the expected turbine powers np.testing.assert_allclose(expected_farm_power, np.sum(expected_turbine_powers)) def test_expected_farm_value_regression(): # Ensure this calculation hasn't changed unintentionally fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 8.0, 9.0]) wind_directions = np.array([270.0, 270.0, 270.0]) values = np.array([30.0, 20.0, 10.0]) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06, values=values, ) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=time_series) fmodel.run() expected_farm_value = fmodel.get_expected_farm_value() assert np.allclose(expected_farm_value,75108001.05154414 , atol=1e-1) def test_get_farm_avp(caplog): fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([7.0, 8.0, 9.0]) wind_directions = np.array([260.0, 270.0, 280.0]) turbulence_intensities = np.array([0.07, 0.06, 0.05]) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) # n_turbines = len(layout_x) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) fmodel.run() farm_powers = fmodel.get_farm_power() # Define frequencies freq = np.array([0.25, 0.5, 0.25]) # Define values of energy produced (e.g., price per MWh) values = np.array([30.0, 20.0, 10.0]) # Check warning raised if values not passed; no warning if values passed with caplog.at_level(logging.WARNING): fmodel.get_farm_AVP(freq=freq) assert caplog.text != "" # Checking not empty caplog.clear() with caplog.at_level(logging.WARNING): fmodel.get_farm_AVP(freq=freq, values=values) assert caplog.text == "" # Checking empty # Check that AVP is equivalent to AEP when values not passed farm_aep = fmodel.get_farm_AEP(freq=freq) farm_avp = fmodel.get_farm_AVP(freq=freq) np.testing.assert_allclose(farm_avp, farm_aep) # Now check that AVP is what we expect when values passed farm_avp = fmodel.get_farm_AVP(freq=freq,values=values) farm_values = np.multiply(values, farm_powers) avp = np.sum(np.multiply(freq, farm_values) * 365 * 24) np.testing.assert_allclose(farm_avp, avp) # Also check get_expected_farm_value expected_farm_power = fmodel.get_expected_farm_value(freq=freq, values=values) np.testing.assert_allclose(expected_farm_power, avp / (365 * 24)) def test_set_ti(): fmodel = FlorisModel(configuration=YAML_INPUT) # Set wind directions, wind speeds and turbulence intensities with n_findex = 3 fmodel.set( wind_speeds=[8.0, 8.0, 8.0], wind_directions=[240.0, 250.0, 260.0], turbulence_intensities=[0.1, 0.1, 0.1], ) # Confirm can change turbulence intensities if not changing the length of the array fmodel.set(turbulence_intensities=[0.12, 0.12, 0.12]) # Confirm that changes to wind speeds and directions without changing turbulence intensities # raises an error with pytest.raises(ValueError): fmodel.set( wind_speeds=[8.0, 8.0, 8.0, 8.0], wind_directions=[240.0, 250.0, 260.0, 270.0], ) # Changing the length of TI alone is not allowed with pytest.raises(ValueError): fmodel.set(turbulence_intensities=[0.12]) # Test that applying a float however raises an error with pytest.raises(TypeError): fmodel.set(turbulence_intensities=0.12) def test_calculate_planes(caplog): fmodel = FlorisModel(configuration=YAML_INPUT) # The calculate_plane functions should run directly with the inputs as given fmodel.calculate_horizontal_plane(90.0) fmodel.calculate_y_plane(0.0) fmodel.calculate_cross_plane(500.0) # No longer support setting new wind conditions, must be done with set() fmodel.set( wind_speeds = [8.0, 8.0, 8.0], wind_directions = [270.0, 270.0, 270.0], turbulence_intensities = [0.1, 0.1, 0.1], ) fmodel.calculate_horizontal_plane( 90.0, findex_for_viz=1 ) fmodel.calculate_y_plane( 0.0, findex_for_viz=1 ) fmodel.calculate_cross_plane( 500.0, findex_for_viz=1 ) # Without specifying findex_for_viz should raise a logger warning. with caplog.at_level(logging.WARNING): fmodel.calculate_horizontal_plane(90.0) assert caplog.text != "" # Checking not empty caplog.clear() with caplog.at_level(logging.WARNING): fmodel.calculate_y_plane(0.0) assert caplog.text != "" # Checking not empty caplog.clear() with caplog.at_level(logging.WARNING): fmodel.calculate_cross_plane(500.0) assert caplog.text != "" # Checking not empty def test_get_turbine_powers_with_WindRose(): fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 10.0, 12.0, 8.0, 10.0, 12.0]) wind_directions = np.array([270.0, 270.0, 270.0, 280.0, 280.0, 280.0]) turbulence_intensities = 0.06 * np.ones_like(wind_speeds) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=[0, 1000, 2000, 3000], layout_y=[0, 0, 0, 0] ) fmodel.run() turbine_powers_simple = fmodel.get_turbine_powers() # Now declare a WindRose with 2 wind directions and 3 wind speeds # uniform TI and frequency wind_rose = WindRose( wind_directions=np.unique(wind_directions), wind_speeds=np.unique(wind_speeds), ti_table=0.06 ) # Set this wind rose, run fmodel.set(wind_data=wind_rose) fmodel.run() # Get the turbine powers in the wind rose turbine_powers_windrose = fmodel.get_turbine_powers() # Turbine power should have shape (n_wind_directions, n_wind_speeds, n_turbines) assert turbine_powers_windrose.shape == (2, 3, 4) assert np.allclose(turbine_powers_simple.reshape(2, 3, 4), turbine_powers_windrose) assert np.allclose(turbine_powers_simple, turbine_powers_windrose.reshape(2*3, 4)) # Test that if certain combinations in the wind rose have 0 frequency, the power in # those locations is nan wind_rose = WindRose( wind_directions = np.array([270.0, 280.0]), wind_speeds = np.array([8.0, 10.0, 12.0]), ti_table=0.06, freq_table=np.array([[0.25, 0.25, 0.0], [0.0, 0.0, 0.5]]) ) fmodel.set(wind_data=wind_rose) fmodel.run() turbine_powers = fmodel.get_turbine_powers() assert np.isnan(turbine_powers[0, 2, 0]) def test_get_powers_with_wind_data(): fmodel = FlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 10.0, 12.0, 8.0, 10.0, 12.0]) wind_directions = np.array([270.0, 270.0, 270.0, 280.0, 280.0, 280.0]) turbulence_intensities = 0.06 * np.ones_like(wind_speeds) fmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=[0, 1000, 2000, 3000], layout_y=[0, 0, 0, 0] ) fmodel.run() farm_power_simple = fmodel.get_farm_power() # Now declare a WindRose with 2 wind directions and 3 wind speeds # uniform TI and frequency wind_rose = WindRose( wind_directions=np.unique(wind_directions), wind_speeds=np.unique(wind_speeds), ti_table=0.06 ) # Set this wind rose, run fmodel.set(wind_data=wind_rose) fmodel.run() farm_power_windrose = fmodel.get_farm_power() # Check dimensions and that the farm power is the sum of the turbine powers assert farm_power_windrose.shape == (2, 3) assert np.allclose(farm_power_windrose, fmodel.get_turbine_powers().sum(axis=2)) # Check that simple and windrose powers are consistent assert np.allclose(farm_power_simple.reshape(2, 3), farm_power_windrose) assert np.allclose(farm_power_simple, farm_power_windrose.flatten()) # Test that if the last turbine's weight is set to 0, the farm power is the same as the # sum of the first 3 turbines turbine_weights = np.array([1.0, 1.0, 1.0, 0.0]) farm_power_weighted = fmodel.get_farm_power(turbine_weights=turbine_weights) assert np.allclose(farm_power_weighted, fmodel.get_turbine_powers()[:,:,:-1].sum(axis=2)) def test_get_and_set_param(): fmodel = FlorisModel(configuration=YAML_INPUT) # Get the wind speed wind_speeds = fmodel.get_param(['flow_field', 'wind_speeds']) assert wind_speeds[0] == 8.0 # Set the wind speed fmodel.set_param(['flow_field', 'wind_speeds'], 10.0, param_idx=0) wind_speed = fmodel.get_param(['flow_field', 'wind_speeds'], param_idx=0 ) assert wind_speed == 10.0 # Repeat with wake parameter fmodel.set_param(['wake', 'wake_velocity_parameters', 'gauss', 'alpha'], 0.1) alpha = fmodel.get_param(['wake', 'wake_velocity_parameters', 'gauss', 'alpha']) assert alpha == 0.1 def test_get_operation_model(): fmodel = FlorisModel(configuration=YAML_INPUT) assert fmodel.get_operation_model() == "cosine-loss" def test_set_operation_model(): fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set_operation_model("simple-derating") assert fmodel.get_operation_model() == "simple-derating" reference_wind_height = fmodel.reference_wind_height # Check multiple turbine types works fmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) fmodel.set_operation_model(["simple-derating", "cosine-loss"]) assert fmodel.get_operation_model() == ["simple-derating", "cosine-loss"] # Check that setting a single turbine type, and then altering the operation model works fmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) fmodel.set(turbine_type=["nrel_5MW"], reference_wind_height=reference_wind_height) fmodel.set_operation_model("simple-derating") assert fmodel.get_operation_model() == "simple-derating" # Check that setting over mutliple turbine types works fmodel.set(turbine_type=["nrel_5MW", "iea_15MW"], reference_wind_height=reference_wind_height) fmodel.set_operation_model("simple-derating") assert fmodel.get_operation_model() == "simple-derating" fmodel.set_operation_model(["simple-derating", "cosine-loss"]) assert fmodel.get_operation_model() == ["simple-derating", "cosine-loss"] # Check setting over single turbine type; then updating layout works fmodel.set(turbine_type=["nrel_5MW"], reference_wind_height=reference_wind_height) fmodel.set_operation_model("simple-derating") fmodel.set(layout_x=[0, 0, 0], layout_y=[0, 1000, 2000]) assert fmodel.get_operation_model() == "simple-derating" # Check that setting for multiple turbine types and then updating layout breaks fmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) fmodel.set(turbine_type=["nrel_5MW"], reference_wind_height=reference_wind_height) fmodel.set_operation_model(["simple-derating", "cosine-loss"]) assert fmodel.get_operation_model() == ["simple-derating", "cosine-loss"] with pytest.raises(ValueError): fmodel.set(layout_x=[0, 0, 0], layout_y=[0, 1000, 2000]) # Check one more variation fmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) fmodel.set(turbine_type=["nrel_5MW", "iea_15MW"], reference_wind_height=reference_wind_height) fmodel.set_operation_model("simple-derating") fmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) with pytest.raises(ValueError): fmodel.set(layout_x=[0, 0, 0], layout_y=[0, 1000, 2000]) def test_set_operation(): fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) # Check that not allowed to run(), then set_operation, then collect powers fmodel.run() fmodel.set_operation(yaw_angles=np.array([[25.0, 0.0]])) with pytest.raises(RuntimeError): fmodel.get_turbine_powers() # Check that no issue if run is called first fmodel.run() fmodel.get_turbine_powers() # Check that if arguments do not match number of turbines, raises error with pytest.raises(ValueError): fmodel.set_operation(yaw_angles=np.array([[25.0, 0.0, 20.0]])) # Check that if arguments do not match n_findex, raises error with pytest.raises(ValueError): fmodel.set_operation(yaw_angles=np.array([[25.0, 0.0], [25.0, 0.0]])) fmodel.run() def test_reference_wind_height_methods(caplog): fmodel = FlorisModel(configuration=YAML_INPUT) # Check that if the turbine type is changed, a warning is raised/not raised regarding the # reference wind height with caplog.at_level(logging.WARNING): fmodel.set(turbine_type=["iea_15MW"]) assert caplog.text != "" # Checking not empty caplog.clear() with caplog.at_level(logging.WARNING): fmodel.set(turbine_type=["iea_15MW"], reference_wind_height=100.0) assert caplog.text == "" # Checking empty # Check that assigning the reference wind height to the turbine hub height works assert fmodel.core.flow_field.reference_wind_height == 100.0 # Set in line above fmodel.assign_hub_height_to_ref_height() assert fmodel.core.flow_field.reference_wind_height == 150.0 # 150m is HH for IEA 15MW with pytest.raises(ValueError): fmodel.set( layout_x = [0.0, 0.0], layout_y = [0.0, 1000.0], turbine_type=["nrel_5MW", "iea_15MW"] ) fmodel.assign_hub_height_to_ref_height() # Shouldn't allow due to multiple turbine types def test_merge_floris_models(): # Check that the merge function extends the data as expected fmodel1 = FlorisModel(configuration=YAML_INPUT) fmodel1.set( layout_x=[0, 1000], layout_y=[0, 0] ) fmodel2 = FlorisModel(configuration=YAML_INPUT) fmodel2.set( layout_x=[2000, 3000], layout_y=[0, 0] ) merged_fmodel = FlorisModel.merge_floris_models([fmodel1, fmodel2]) assert merged_fmodel.n_turbines == 4 # Check that this model will run without error merged_fmodel.run() # Verify error handling ## Input list with incorrect types fmodel_list = [fmodel1, "not a floris model"] with pytest.raises(TypeError): merged_fmodel = FlorisModel.merge_floris_models(fmodel_list) def test_sample_flow_at_points(): fmodel = FlorisModel(configuration=YAML_INPUT) u_inf = 8.0 n_points = 10 fmodel.set( layout_x=[0], layout_y=[0], wind_speeds=[u_inf, u_inf], wind_directions=[270.0, 180.0], turbulence_intensities=[0.06, 0.06], ) # set up sample points p = np.linspace(1, 1001, n_points) x = np.concatenate((p, np.zeros_like(p))) y = np.concatenate((np.zeros_like(p), p)) z = 100 * np.ones(n_points * 2) # Use sample_flow_at_points to get the flow at the sample points u_sampled_1 = fmodel.sample_flow_at_points(x, y, z) # Check that the behind-turbine velocities match in the two directions assert np.allclose(u_sampled_1[0,:n_points], u_sampled_1[1,n_points:]) # Check also that points outside the wake are all reasonable assert np.allclose(u_sampled_1[0,n_points:], u_sampled_1[1,:n_points]) assert np.all(u_sampled_1[0,n_points:] > u_inf) assert np.all(u_sampled_1[0,n_points:] == u_sampled_1[0,n_points]) # Run again at a higher speed and check all sampled velocities are higher fmodel.set(wind_speeds=[u_inf + 2.0, u_inf + 2.0]) u_sampled_2 = fmodel.sample_flow_at_points(x, y, z) assert np.all(u_sampled_2 > u_sampled_1) def test_sample_ti_at_points(): fmodel = FlorisModel(configuration=YAML_INPUT) ti_inf = 0.8 n_points = 10 fmodel.set( layout_x=[0], layout_y=[0], wind_speeds=[8.0, 8.0], wind_directions=[270.0, 180.0], turbulence_intensities=[ti_inf, ti_inf], ) # set up sample points p = np.linspace(1, 1001, n_points) x = np.concatenate((p, np.zeros_like(p))) y = np.concatenate((np.zeros_like(p), p)) z = 100 * np.ones(n_points * 2) # Use sample_ti_at_points to get the TI at the sample points ti_sampled_1 = fmodel.sample_ti_at_points(x, y, z) # Check that the behind-turbine TIs match in the two directions assert np.allclose(ti_sampled_1[0,:n_points], ti_sampled_1[1,n_points:]) # Check also that points outside the wake are all reasonable assert np.allclose(ti_sampled_1[0,n_points:], ti_sampled_1[1,:n_points]) assert np.all(ti_sampled_1[0,n_points:] == ti_inf) # Run again at a higher TI and check all sampled TIs are higher fmodel.set(turbulence_intensities=[ti_inf + 0.02, ti_inf + 0.02]) ti_sampled_2 = fmodel.sample_ti_at_points(x, y, z) assert np.all(ti_sampled_2 > ti_sampled_1) def test_set_multidim(): fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set(turbine_type=[SampleInputs().turbine_multi_dim]) # No multidim condition has been set; should raise value error with pytest.raises(ValueError): fmodel.run() # Set a multidim condition that is not a valid type with pytest.raises(TypeError): fmodel.set(multidim_conditions="invalid_type") fmodel.run() # Set an invalid multidim condition (not all dimensions specified) with pytest.raises(ValueError): fmodel.set(multidim_conditions={"Hs": 1.0}) fmodel.run() # Set an invalid key (but the correct total number of keys) with pytest.raises(ValueError): fmodel.set(multidim_conditions={"invalid_key": 2.0, "Hs": 1.0}) fmodel.run() # Set with an invalid key (as well as other keys being correct) with pytest.raises(ValueError): fmodel.set(multidim_conditions={"invalid_key": 2.0, "Hs": 1.0, "Tp": 8.0}) fmodel.run() # Set a valid multidim condition, order of dictionary keys should not matter fmodel.set(multidim_conditions={"Hs": 1.0, "Tp": 8.0}) fmodel.run() powers_1 = fmodel.get_turbine_powers() fmodel.set(multidim_conditions={"Tp": 8.0, "Hs": 1.0}) fmodel.run() powers_2 = fmodel.get_turbine_powers() assert np.array_equal(powers_1, powers_2) # Create a single-dimensional table turbine = SampleInputs().turbine_multi_dim turbine["power_thrust_table"]["power_thrust_data_file"] = "iea_15MW_multi_dim_TI.csv" fmodel.set( turbine_type=[turbine], turbine_library_path=Path(__file__).resolve().parent / "data/" ) with pytest.raises(ValueError): fmodel.set(multidim_conditions={"TI": 0.06, "Tp": 8.0}) fmodel.run() fmodel.set(multidim_conditions={"TI": 0.06}) fmodel.run() # Test multiple conditions at once fmodel.set( wind_speeds=[8.0, 8.0], wind_directions=[270.0, 270.0], turbulence_intensities=[0.06, 0.06], multidim_conditions={"TI": [0.06, 0.08, 0.09]} # Too many ) with pytest.raises(ValueError): fmodel.run() # Correct number fmodel.set(multidim_conditions={"TI": [0.06, 0.08]}) fmodel.run() powers_multi = fmodel.get_turbine_powers() # Loop over conditions and check individuals for i, ti in enumerate([0.06, 0.08]): fmodel.set( wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06], multidim_conditions={"TI": ti} ) fmodel.run() powers_single = fmodel.get_turbine_powers() assert np.array_equal(powers_single, powers_multi[i:i+1,:]) ================================================ FILE: tests/flow_field_unit_test.py ================================================ import numpy as np import pytest from floris.core import FlowField, TurbineGrid from tests.conftest import N_FINDEX, N_TURBINES def test_n_findex(flow_field_fixture): assert flow_field_fixture.n_findex == N_FINDEX def test_initialize_velocity_field(flow_field_fixture, turbine_grid_fixture: TurbineGrid): flow_field_fixture.wind_shear = 1.0 flow_field_fixture.initialize_velocity_field(turbine_grid_fixture) # Check the shape of the velocity arrays: u_initial, v_initial, w_initial and u, v, w # Dimensions are (# findex, # turbines, N grid points, M grid points) assert np.shape(flow_field_fixture.u_sorted)[0] == flow_field_fixture.n_findex assert np.shape(flow_field_fixture.u_sorted)[1] == N_TURBINES assert np.shape(flow_field_fixture.u_sorted)[2] == turbine_grid_fixture.grid_resolution assert np.shape(flow_field_fixture.u_sorted)[3] == turbine_grid_fixture.grid_resolution # Check that the wind speed profile was created correctly. By setting the shear # exponent to 1.0 above, the shear profile is a linear function of height and # the points on the turbine rotor are equally spaced about the rotor. # This means that their average should equal the wind speed at the center # which is the input wind speed. shape = np.shape(flow_field_fixture.u_sorted[0, 0, :, :]) n_elements = shape[0] * shape[1] average = ( np.sum(flow_field_fixture.u_sorted[:, 0, :, :], axis=(-2, -1)) / np.array([n_elements]) ) assert np.array_equal(average, flow_field_fixture.wind_speeds) def test_asdict(flow_field_fixture: FlowField, turbine_grid_fixture: TurbineGrid): flow_field_fixture.initialize_velocity_field(turbine_grid_fixture) dict1 = flow_field_fixture.as_dict() new_ff = FlowField.from_dict(dict1) new_ff.initialize_velocity_field(turbine_grid_fixture) dict2 = new_ff.as_dict() assert dict1 == dict2 def test_len_ws_equals_len_wd(flow_field_fixture: FlowField, turbine_grid_fixture: TurbineGrid): flow_field_fixture.initialize_velocity_field(turbine_grid_fixture) dict1 = flow_field_fixture.as_dict() # Test that having the 3 equal in lenght raises no error dict1['wind_directions'] = np.array([180, 180]) dict1['wind_speeds'] = np.array([5., 6.]) dict1['turbulence_intensities'] = np.array([175., 175.]) FlowField.from_dict(dict1) # Set the wind speeds as a different length of wind directions and turbulence_intensities # And confirm error raised dict1['wind_directions'] = np.array([180, 180]) dict1['wind_speeds'] = np.array([5., 6., 7.]) dict1['turbulence_intensities'] = np.array([175., 175.]) with pytest.raises(ValueError): FlowField.from_dict(dict1) # Set the wind directions as a different length of wind speeds and turbulence_intensities dict1['wind_directions'] = np.array([180, 180, 180.]) # And confirm error raised dict1['wind_speeds'] = np.array([5., 6.]) dict1['turbulence_intensities'] = np.array([175., 175.]) with pytest.raises(ValueError): FlowField.from_dict(dict1) def test_dim_ws_wd_ti(flow_field_fixture: FlowField, turbine_grid_fixture: TurbineGrid): flow_field_fixture.initialize_velocity_field(turbine_grid_fixture) dict1 = flow_field_fixture.as_dict() # Test that having an extra dimension in wind_directions raises an error with pytest.raises(ValueError): dict1['wind_directions'] = np.array([[180, 180]]) dict1['wind_speeds'] = np.array([5., 6.]) dict1['turbulence_intensities'] = np.array([175., 175.]) FlowField.from_dict(dict1) # Test that having an extra dimension in wind_speeds raises an error with pytest.raises(ValueError): dict1['wind_directions'] = np.array([180, 180]) dict1['wind_speeds'] = np.array([[5., 6.]]) dict1['turbulence_intensities'] = np.array([175., 175.]) FlowField.from_dict(dict1) # Test that having an extra dimension in turbulence_intensities raises an error with pytest.raises(ValueError): dict1['wind_directions'] = np.array([180, 180]) dict1['wind_speeds'] = np.array([5., 6.]) dict1['turbulence_intensities'] = np.array([[175., 175.]]) FlowField.from_dict(dict1) def test_turbulence_intensities_to_n_findex(flow_field_fixture, turbine_grid_fixture): # Assert tubulence intensity has same length as n_findex assert len(flow_field_fixture.turbulence_intensities) == flow_field_fixture.n_findex # Assert turbulence_intensity_field is the correct shape flow_field_fixture.initialize_velocity_field(turbine_grid_fixture) assert flow_field_fixture.turbulence_intensity_field.shape == (N_FINDEX, N_TURBINES, 1, 1) # Assert that turbulence_intensity_field has values matched to turbulence_intensity for findex in range(N_FINDEX): for t in range(N_TURBINES): assert ( flow_field_fixture.turbulence_intensities[findex] == flow_field_fixture.turbulence_intensity_field[findex, t, 0, 0] ) ================================================ FILE: tests/geometric_yaw_unit_test.py ================================================ import numpy as np import pandas as pd from floris import FlorisModel from floris.optimization.yaw_optimization.yaw_optimizer_geometric import ( _process_layout, YawOptimizationGeometric, ) DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" # Inputs for basic yaw optimizations WIND_DIRECTIONS = [0.0, 90.0, 180.0, 270.0] WIND_SPEEDS = [8.0] * 4 TURBULENCE_INTENSITIES = [0.06] * 4 LAYOUT_X = [0.0, 600.0, 1200.0] LAYOUT_Y = [0.0, 0.0, 0.0] MAXIMUM_YAW_ANGLE = 25.0 def test_basic_optimization(sample_inputs_fixture): """ The Serial Refine (SR) method optimizes yaw angles based on a sequential, iterative yaw optimization scheme. This test checks basic properties of the optimization result. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) fmodel.set( layout_x=LAYOUT_X, layout_y=LAYOUT_Y, wind_directions=WIND_DIRECTIONS, wind_speeds=WIND_SPEEDS, turbulence_intensities=TURBULENCE_INTENSITIES ) fmodel.set_operation_model("cosine-loss") yaw_opt = YawOptimizationGeometric( fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=MAXIMUM_YAW_ANGLE ) df_opt = yaw_opt.optimize() # Unaligned conditions assert np.allclose(df_opt.loc[0, "yaw_angles_opt"], 0.0) assert np.allclose(df_opt.loc[2, "yaw_angles_opt"], 0.0) # Check aligned conditions # Check maximum and minimum are respected assert (df_opt.loc[1, "yaw_angles_opt"] <= MAXIMUM_YAW_ANGLE).all() assert (df_opt.loc[3, "yaw_angles_opt"] <= MAXIMUM_YAW_ANGLE).all() assert (df_opt.loc[1, "yaw_angles_opt"] >= 0.0).all() assert (df_opt.loc[3, "yaw_angles_opt"] >= 0.0).all() # Check 90.0 and 270.0 are symmetric assert np.allclose(df_opt.loc[1, "yaw_angles_opt"], np.flip(df_opt.loc[3, "yaw_angles_opt"])) # Check last turbine's angles are zero at 270.0 assert np.allclose(df_opt.loc[3, "yaw_angles_opt"][-1], 0.0) # YawOptimizationGeometric does not compute farm powers def test_disabled_turbines(sample_inputs_fixture): """ Tests SR when some turbines are disabled and checks that the results are equivalent to removing those turbines from the wind farm. Need a tight layout to ensure that the front-to-back distance is not too large. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) fmodel.set( layout_x=LAYOUT_X, layout_y=LAYOUT_Y, wind_directions=WIND_DIRECTIONS, wind_speeds=WIND_SPEEDS, turbulence_intensities=TURBULENCE_INTENSITIES ) fmodel.set_operation_model("mixed") # Disable the middle turbine in all wind conditions, run optimization, and extract results fmodel.set(disable_turbines=[[False, True, False]]*4) yaw_opt = YawOptimizationGeometric( fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=MAXIMUM_YAW_ANGLE ) df_opt = yaw_opt.optimize() yaw_angles_opt_disabled = df_opt.loc[3, "yaw_angles_opt"] # Set up a new wind farm with the middle turbine removed fmodel = FlorisModel(sample_inputs_fixture.core) fmodel.set( layout_x=np.array(LAYOUT_X)[[0, 2]], layout_y=np.array(LAYOUT_Y)[[0, 2]], wind_directions=WIND_DIRECTIONS, wind_speeds=WIND_SPEEDS, turbulence_intensities=TURBULENCE_INTENSITIES ) fmodel.set_operation_model("cosine-loss") yaw_opt = YawOptimizationGeometric( fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=MAXIMUM_YAW_ANGLE ) df_opt = yaw_opt.optimize() yaw_angles_opt_removed = df_opt.loc[3, "yaw_angles_opt"] assert np.allclose(yaw_angles_opt_disabled[[0, 2]], yaw_angles_opt_removed) def test_process_layout(): # Test inside Jensen-like wake dx, dy = _process_layout( turbine_x=np.array([0.0, 1000.0]), turbine_y=np.array([0.0, 499.0]), rotor_diameter=1.0, spread=0.5 ) assert dx[0] == 1000.0 # Distance to downstream turbine, which is inside the wake # Test outside Jensen-like wake dx, dy, = _process_layout( turbine_x=np.array([0.0, 1000.0]), turbine_y=np.array([0.0, 501.0]), rotor_diameter=1.0, spread=0.5 ) assert dx[0] == np.inf # Distance to downstream turbine, which is outside the wake # Test effect of rotor diameter dx, dy, = _process_layout( turbine_x=np.array([0.0, 1000.0]), turbine_y=np.array([0.0, 549.0]), rotor_diameter=100.0, spread=0.5 ) assert dx[0] != np.inf # This turbine is now inside the wake dx, dy = _process_layout( turbine_x=np.array([0.0, 1000.0]), turbine_y=np.array([0.0, 551.0]), rotor_diameter=100.0, spread=0.5 ) assert dx[0] == np.inf # This turbine is now outside the wake ================================================ FILE: tests/heterogeneous_map_integration_test.py ================================================ import logging from pathlib import Path import numpy as np import pytest from floris import FlorisModel, TimeSeries from floris.heterogeneous_map import HeterogeneousMap TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" def test_declare_by_parameters(): HeterogeneousMap( x=np.array([0.0, 0.0, 500.0, 500.0]), y=np.array([0.0, 500.0, 0.0, 500.0]), speed_multipliers=np.array( [ [1.0, 1.0, 1.0, 1.0], [1.0, 1.25, 1.0, 1.25], [1.0, 1.0, 1.0, 1.25], [1.0, 1.0, 1.0, 1.0], ] ), wind_directions=np.array([0.0, 0.0, 90.0, 90.0]), wind_speeds=np.array([5.0, 15.0, 5.0, 15.0]), ) def test_heterogeneous_map_no_ws_no_wd(): heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), } # Should be single value if no wind_directions or wind_speeds with pytest.raises(ValueError): HeterogeneousMap(**heterogeneous_map_config) heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array([[1.0, 1.1, 1.2]]), } HeterogeneousMap(**heterogeneous_map_config) def test_wind_direction_and_wind_speed_sizes(): heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), "wind_directions": np.array([0.0, 90.0]), "wind_speeds": np.array([10.0, 20.0, 30.0]), } # Should raise value error because wind_directions and wind_speeds are not the same size with pytest.raises(ValueError): HeterogeneousMap(**heterogeneous_map_config) heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), "wind_directions": np.array([0.0, 90.0]), "wind_speeds": np.array([10.0, 20.0]), } # Should raise value error because wind_directions and wind_speeds are not = to the # size of speed_multipliers in the 0th dimension with pytest.raises(ValueError): HeterogeneousMap(**heterogeneous_map_config) heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), "wind_directions": np.array([0.0, 90.0, 270.0]), "wind_speeds": np.array([10.0, 20.0, 15.0]), } HeterogeneousMap(**heterogeneous_map_config) def test_wind_direction_and_wind_speed_unique(): heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), "wind_directions": np.array([0.0, 0.0, 270.0]), "wind_speeds": np.array([10.0, 10.0, 15.0]), } # Raises error because of repeated wd/ws pair with pytest.raises(ValueError): HeterogeneousMap(**heterogeneous_map_config) heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), "wind_directions": np.array([0.0, 5.0, 270.0]), "wind_speeds": np.array([10.0, 10.0, 15.0]), } # Should not raise error HeterogeneousMap(**heterogeneous_map_config) def test_get_heterogeneous_inflow_config_by_wind_direction(): # Test the function when only wind_directions is defined heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), "wind_directions": np.array([0, 90, 270]), } # Check for correctness wind_directions = np.array([240, 80, 15]) wind_speeds = np.array([10.0, 20.0, 15.0]) expected_output = np.array([[1.3, 1.4, 1.5], [1.1, 1.1, 1.1], [1.0, 1.1, 1.2]]) hm = HeterogeneousMap(**heterogeneous_map_config) output_dict = hm.get_heterogeneous_inflow_config(wind_directions, wind_speeds) assert np.allclose(output_dict["speed_multipliers"], expected_output) def test_get_heterogeneous_inflow_config_by_wind_speed(): # Test the function when only wind_directions is defined heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], ] ), "wind_speeds": np.array([0, 10, 20]), } # Check for correctness wind_directions = np.array([240, 80, 15]) wind_speeds = np.array([10.0, 10.0, 18.0]) expected_output = np.array([[1.1, 1.1, 1.1], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5]]) hm = HeterogeneousMap(**heterogeneous_map_config) output_dict = hm.get_heterogeneous_inflow_config(wind_directions, wind_speeds) assert np.allclose(output_dict["speed_multipliers"], expected_output) def test_get_heterogeneous_inflow_config_by_wind_direction_and_wind_speed(): # Test the function when only wind_directions is defined heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [[1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5], [1.4, 1.5, 1.6]] ), "wind_directions": np.array([0, 0, 90, 90]), "wind_speeds": np.array([5.0, 15.0, 5.0, 15.0]), } hm = HeterogeneousMap(**heterogeneous_map_config) # Check for correctness wind_directions = np.array([91, 89, 350]) wind_speeds = np.array([4.0, 18.0, 12.0]) expected_output = np.array([[1.3, 1.4, 1.5], [1.4, 1.5, 1.6], [1.1, 1.1, 1.1]]) output_dict = hm.get_heterogeneous_inflow_config(wind_directions, wind_speeds) assert np.allclose(output_dict["speed_multipliers"], expected_output) def test_get_heterogeneous_inflow_config_no_wind_direction_no_wind_speed(): # Test the function when only wind_directions is defined heterogeneous_map_config = { "x": np.array([0.0, 1.0, 2.0]), "y": np.array([0.0, 1.0, 2.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2], ] ), } hm = HeterogeneousMap(**heterogeneous_map_config) # Check for correctness wind_directions = np.array([91, 89, 350]) wind_speeds = np.array([4.0, 18.0, 12.0]) expected_output = np.array([[1.0, 1.1, 1.2], [1.0, 1.1, 1.2], [1.0, 1.1, 1.2]]) output_dict = hm.get_heterogeneous_inflow_config(wind_directions, wind_speeds) assert np.allclose(output_dict["speed_multipliers"], expected_output) def test_get_2d_heterogenous_map_from_3d(): hm_3d = HeterogeneousMap( x=np.array( [ 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, ] ), y=np.array( [ 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, ] ), z=np.array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0]), speed_multipliers=np.array( [ [1.0, 1.1, 1.2, 2.0, 2.1, 2.2], [1.1, 1.1, 1.1, 2.1, 2.1, 2.1], [1.3, 1.4, 1.5, 2.3, 2.4, 2.5], ] ), wind_directions=np.array([0, 90, 270]), ) hm_2d_0 = hm_3d.get_heterogeneous_map_2d(0.0) hm_2d_1 = hm_3d.get_heterogeneous_map_2d(1.0) # Test that x values in both cases are 0, 1, 2 assert np.allclose(hm_2d_0.x, np.array([0.0, 1.0, 2.0])) assert np.allclose(hm_2d_1.x, np.array([0.0, 1.0, 2.0])) # Test that the speed multipliers are correct assert np.allclose( hm_2d_0.speed_multipliers, np.array([[1.0, 1.1, 1.2], [1.1, 1.1, 1.1], [1.3, 1.4, 1.5]]) ) assert np.allclose( hm_2d_1.speed_multipliers, np.array([[2.0, 2.1, 2.2], [2.1, 2.1, 2.1], [2.3, 2.4, 2.5]]) ) # Test that wind directions are the same between 2d and 3d assert np.allclose(hm_2d_0.wind_directions, hm_3d.wind_directions) # Test that wind speed is None in all cases assert hm_3d.wind_speeds is None assert hm_2d_0.wind_speeds is None assert hm_2d_1.wind_speeds is None def test_3d_het_and_shear(): # Define a 3D het map with 4 z locations and a single x and y where # the speed ups are defined by the usual power low wind_speed = 8.0 z_values_array = np.array([0.0, 100.0, 200.0, 300.0]) reference_wind_height = 90.0 wind_shear = 0.12 speed_multipliers_array = (z_values_array / reference_wind_height) ** wind_shear # Define the x and y locations to be corners of a square that repeats # for 4 different z locations x_values = np.tile(np.array([-500.0, 500.0, -500.0, 500.0]), 4) y_values = np.tile(np.array([-500.0, -500.0, 500.0, 500.0]), 4) # Repeat each of the elements of z_values 4 times z_values = np.repeat(z_values_array, 4) speed_multipliers = np.repeat(speed_multipliers_array, 4) # Reshape speed_multipliers to be (1,16) speed_multipliers = speed_multipliers.reshape(1, -1) # Build the 3d HeterogeneousMap hm_3d = HeterogeneousMap( x=x_values, y=y_values, z=z_values, speed_multipliers=speed_multipliers, ) # Get the FLORIS model fmodel = FlorisModel(configuration=YAML_INPUT) # Set the model to a single wind direction, speed and turbine fmodel.set( wind_directions=[270.0], wind_speeds=[wind_speed], layout_x=[0], layout_y=[0], wind_shear=wind_shear, ) # Run the model fmodel.run() # Get the velocities 100 m in front of the turbine # Use the calculate_y_plane method because sample_flow_at_points does not currently work # with heterogeneous inflow # u_at_points_shear = fmodel.sample_flow_at_points(x= -100.0 * np.ones_like(z_values_array), # y= 0.0 * np.ones_like(z_values_array), # z=z_values_array) y_plane = fmodel.calculate_y_plane( x_resolution=1, z_resolution=4, crossstream_dist=0.0, x_bounds=[-100.0, -100.0], z_bounds=[0.0, 300.0], ) u_at_points_shear = y_plane.df["u"].values # Check that the velocities are as expected, ie the shear exponent of 0.12 # produces speeds expeted from the power law assert np.allclose(u_at_points_shear, wind_speed * speed_multipliers_array) # Now change the model such that shear is 0, while the 3D het map is applied fmodel.set( wind_shear=0.0, heterogeneous_inflow_config=hm_3d.get_heterogeneous_inflow_config( wind_directions=[270.0], wind_speeds=[wind_speed] ), ) # Run the model fmodel.run() # # Get the velocities 100 m in front of the turbine y_plane_het = fmodel.calculate_y_plane( x_resolution=1, z_resolution=4, crossstream_dist=0.0, x_bounds=[-100.0, -100.0], z_bounds=[0.0, 300.0], ) u_at_points_het = y_plane_het.df["u"].values # Confirm this produces the same results as the shear model assert np.allclose(u_at_points_het, u_at_points_shear) # Finally confirm that applying both shear and het raises a value error with pytest.raises(ValueError): fmodel.set( wind_shear=0.12, heterogeneous_inflow_config=hm_3d.get_heterogeneous_inflow_config( wind_directions=[270.0], wind_speeds=[wind_speed] ), ) def test_run_2d_het_map(caplog): # Define a 2D het map and confirm the results are as expected # when applied to FLORIS # The side of the flow which is accelerated reverses for east versus west het_map = HeterogeneousMap( x=np.array([0.0, 0.0, 500.0, 500.0]), y=np.array([0.0, 500.0, 0.0, 500.0]), speed_multipliers=np.array( [ [1.0, 2.0, 1.0, 2.0], # Top accelerated [2.0, 1.0, 2.0, 1.0], # Bottom accelerated ] ), wind_directions=np.array([270.0, 90.0]), wind_speeds=np.array([8.0, 8.0]), ) # Get the FLORIS model fmodel = FlorisModel(configuration=YAML_INPUT) time_series = TimeSeries( wind_directions=np.array([270.0, 90.0]), wind_speeds=8.0, turbulence_intensities=0.06, heterogeneous_map=het_map, ) # Set the model to a turbines perpendicular to east/west flow with 0 turbine closer to bottom # and turbine 1 closer to top, while turbine 2 is outside of heterogeneous specification. fmodel.set( wind_data=time_series, layout_x=[250.0, 250.0, 250.0], layout_y=[100.0, 400.0, 700.0], wind_shear=0.0, ) # Run the model. Should raise warning due to turbine outside of bounds with linear interpolation with caplog.at_level(logging.WARNING): fmodel.run() assert caplog.text != "" # Checking not empty # Get the turbine powers powers = fmodel.get_turbine_powers() # Assert that in the first condition, turbine 1 has higher power assert powers[0, 1] > powers[0, 0] # Assert that in the second condition, turbine 0 has higher power assert powers[1, 0] > powers[1, 1] # Assert that the power of turbine 1 equals in the first condition # equals the power of turbine 0 in the second condition assert powers[0, 1] == powers[1, 0] # Check that turbine 2 is simply seeing the freestream wind speed velocities = fmodel.turbine_average_velocities assert np.allclose(velocities[:, 2], 8.0) # Check that sample_flow_at_points runs # Middle of bottom, middle of left v = fmodel.sample_flow_at_points( np.array([250, 0]), np.array([0, 250]), np.array([90, 90]) ) assert np.allclose(v[:,0], np.array([8.0, 16.0])) # Not waked assert np.allclose(v[0,1], 12.0) # Not waked assert v[1,1] < 12.0 # Slightly waked def test_run_2d_het_map_nearest_neighbor(caplog): # Define a 2D het map and confirm the results are as expected # when applied to FLORIS # The side of the flow which is accelerated reverses for east versus west het_map = HeterogeneousMap( x=np.array([0.0, 0.0, 500.0, 500.0]), y=np.array([0.0, 500.0, 0.0, 500.0]), speed_multipliers=np.array( [ [1.0, 2.0, 1.0, 2.0], # Top accelerated [2.0, 1.0, 2.0, 1.0], # Bottom accelerated ] ), wind_directions=np.array([270.0, 90.0]), wind_speeds=np.array([8.0, 8.0]), interp_method='nearest', ) # Get the FLORIS model fmodel = FlorisModel(configuration=YAML_INPUT) time_series = TimeSeries( wind_directions=np.array([270.0, 90.0]), wind_speeds=8.0, turbulence_intensities=0.06, heterogeneous_map=het_map, ) # Set the model to a turbines perpendicular to east/west flow with 0 turbine closer to bottom # and turbine 1 closer to top, while turbine 2 is outside of heterogeneous specification # (but will still take on the nearest neighbor value). fmodel.set( wind_data=time_series, layout_x=[250.0, 250.0, 250.0], layout_y=[100.0, 400.0, 700.0], wind_shear=0.0, ) # Run the model. No turbine outside of bounds warning raised for nearest neighbor interpolation with caplog.at_level(logging.WARNING): fmodel.run() assert caplog.text == "" # Checking empty # Get the turbine powers powers = fmodel.get_turbine_powers() # Assert that in the first condition, turbine 1 has higher power assert powers[0, 1] > powers[0, 0] # Assert that in the second condition, turbine 0 has higher power assert powers[1, 0] > powers[1, 1] # Assert that the power of turbine 1 equals in the first condition # equals the power of turbine 0 in the second condition assert powers[0, 1] == powers[1, 0] # Check that turbine 2 is takes the value of the top row. velocities = fmodel.turbine_average_velocities assert np.allclose(velocities[:, 2], 8.0*np.array([2.0, 1.0])) # Check that sample_flow_at_points runs # Middle of bottom, middle of left (slightly up) v = fmodel.sample_flow_at_points( np.array([250.0, 0]), np.array([0, 251]), np.array([90, 90]) ) assert np.allclose(v[:,0], np.array([8.0, 16.0])) # Not waked assert np.allclose(v[0,1], 16.0) # Not waked assert v[1,1] < 8.0 # Slightly waked def test_het_config(): # Test that setting FLORIS with a heterogeneous inflow configuration # works as expected and consistent with previous results # Get the FLORIS model fmodel = FlorisModel(configuration=YAML_INPUT) # Change the layout to a 4 turbine layout in a box fmodel.set(layout_x=[0, 0, 500.0, 500.0], layout_y=[0, 500.0, 0, 500.0]) # Set FLORIS to run for a single condition fmodel.set(wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06]) # Define the speed-ups of the heterogeneous inflow, and their locations. # Note that heterogeneity is only applied within the bounds of the points defined in the # heterogeneous_inflow_config dictionary. In this case, set the inflow to be 1.25x the ambient # wind speed for the upper turbines at y = 500m. speed_ups = [[1.0, 1.25, 1.0, 1.25]] # Note speed-ups has dimensions of n_findex X n_points x_locs = [-500.0, -500.0, 1000.0, 1000.0] y_locs = [-500.0, 1000.0, -500.0, 1000.0] # Create the configuration dictionary to be used for the heterogeneous inflow. heterogeneous_inflow_config = { "speed_multipliers": speed_ups, "x": x_locs, "y": y_locs, } # Set the heterogeneous inflow configuration fmodel.set(heterogeneous_inflow_config=heterogeneous_inflow_config) # Run the FLORIS simulation fmodel.run() turbine_powers = fmodel.get_turbine_powers() / 1000.0 # Test that the turbine powers are consistent with previous implementation # 2248.2, 2800.1, 466.2, 601.5 before the change # Using almost equal assert assert np.allclose( turbine_powers, np.array([[2248.2, 2800.0, 466.2, 601.4]]), atol=1.0, ) ================================================ FILE: tests/layout_optimization_integration_test.py ================================================ import logging from pathlib import Path import numpy as np import pytest from floris import ( FlorisModel, TimeSeries, WindRose, ) from floris.optimization.layout_optimization.layout_optimization_base import ( LayoutOptimization, ) from floris.optimization.layout_optimization.layout_optimization_gridded import ( LayoutOptimizationGridded, ) from floris.optimization.layout_optimization.layout_optimization_random_search import ( LayoutOptimizationRandomSearch, ) from floris.optimization.layout_optimization.layout_optimization_scipy import ( LayoutOptimizationScipy, ) from floris.wind_data import WindDataBase TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" test_boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)] def test_base_class(caplog): # Get a test fi fmodel = FlorisModel(configuration=YAML_INPUT) # Now initiate layout optimization with a frequency matrix passed in the 3rd position # (this should fail) freq = np.ones((5, 5)) freq = freq / freq.sum() # Check that warning is raised if fmodel does not contain wind_data with caplog.at_level(logging.WARNING): LayoutOptimization(fmodel, test_boundaries, 5) assert caplog.text != "" # Checking not empty caplog.clear() with caplog.at_level(logging.WARNING): LayoutOptimization(fmodel=fmodel, boundaries=test_boundaries, min_dist=5,) assert caplog.text != "" # Checking not empty time_series = TimeSeries( wind_directions=fmodel.core.flow_field.wind_directions, wind_speeds=fmodel.core.flow_field.wind_speeds, turbulence_intensities=fmodel.core.flow_field.turbulence_intensities, ) fmodel.set(wind_data=time_series) caplog.clear() with caplog.at_level(logging.WARNING): LayoutOptimization(fmodel, test_boundaries, 5) assert caplog.text != "" # Not empty, because get_farm_AEP called on TimeSeries # Passing without keyword arguments should work, or with keyword arguments LayoutOptimization(fmodel, test_boundaries, 5) LayoutOptimization(fmodel=fmodel, boundaries=test_boundaries, min_dist=5) # Check with WindRose on fmodel fmodel.set(wind_data=time_series.to_WindRose()) caplog.clear() with caplog.at_level(logging.WARNING): LayoutOptimization(fmodel, test_boundaries, 5) assert caplog.text == "" # Empty LayoutOptimization(fmodel, test_boundaries, 5) LayoutOptimization(fmodel=fmodel, boundaries=test_boundaries, min_dist=5) def test_LayoutOptimizationRandomSearch(): fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set(layout_x=[0, 500], layout_y=[0, 0]) layout_opt = LayoutOptimizationRandomSearch( fmodel=fmodel, boundaries=test_boundaries, min_dist_D=5, seconds_per_iteration=1, total_optimization_seconds=1, use_dist_based_init=False, ) # Check that the optimization runs layout_opt.optimize() def test_LayoutOptimizationGridded_initialization(caplog): fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set(layout_x=[0, 500], layout_y=[0, 0]) with pytest.raises(ValueError): LayoutOptimizationGridded( fmodel=fmodel, boundaries=test_boundaries, min_dist=None, min_dist_D=None, ) # No min_dist specified with pytest.raises(ValueError): LayoutOptimizationGridded( fmodel=fmodel, boundaries=test_boundaries, min_dist=500, min_dist_D=5 ) # min_dist specified in two ways fmodel.core.farm.rotor_diameters[1] = 100.0 caplog.clear() with caplog.at_level(logging.WARNING): LayoutOptimizationGridded( fmodel, test_boundaries, min_dist_D=5 ) def test_LayoutOptimizationGridded_basic(): fmodel = FlorisModel(configuration=YAML_INPUT) min_dist = 60 layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=test_boundaries, min_dist=min_dist, rotation_step=5, rotation_range=(0, 360), translation_step=50, hexagonal_packing=False, ) n_turbs_opt, x_opt, y_opt = layout_opt.optimize() # Check that the number of turbines is correct assert n_turbs_opt == len(x_opt) # Check that min_dist is respected xx_diff = x_opt.reshape(-1,1) - x_opt.reshape(1,-1) yy_diff = y_opt.reshape(-1,1) - y_opt.reshape(1,-1) dists = np.sqrt(xx_diff**2 + yy_diff**2) dists[np.arange(0, len(dists), 1), np.arange(0, len(dists), 1)] = np.inf assert (dists > min_dist - 1e-6).all() # Check all are indeed in bounds assert (np.all(x_opt > 0.0) & np.all(x_opt < 1000.0) & np.all(y_opt > 0.0) & np.all(y_opt < 1000.0)) # Check that the layout is at least as good as the basic rectangular fill n_turbs_subopt = (1000 // min_dist + 1) ** 2 assert n_turbs_opt >= n_turbs_subopt def test_LayoutOptimizationGridded_diagonal(): fmodel = FlorisModel(configuration=YAML_INPUT) turbine_spacing = 1000.0 corner = 2*turbine_spacing / np.sqrt(2) # Create a "thin" boundary area at a 45 degree angle boundaries_diag = [ (0.0, 0.0), (0.0, 100.0), (corner, corner+100.0), (corner+100.0, corner+100.0), (0.0, 0.0) ] layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=boundaries_diag, min_dist=turbine_spacing, rotation_step=45, # To speed up test rotation_range=(0, 360), translation_step=50, hexagonal_packing=False, ) n_turbs_opt, x_opt, y_opt = layout_opt.optimize() # Confirm that spacing is respected xx_diff = x_opt.reshape(-1,1) - x_opt.reshape(1,-1) yy_diff = y_opt.reshape(-1,1) - y_opt.reshape(1,-1) dists = np.sqrt(xx_diff**2 + yy_diff**2) dists[np.arange(0, len(dists), 1), np.arange(0, len(dists), 1)] = np.inf assert (dists > turbine_spacing - 1e-6).all() assert n_turbs_opt == 3 # 3 should fit in the diagonal # Test a limited range of rotation layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=boundaries_diag, min_dist=turbine_spacing, rotation_step=5, rotation_range=(0, 10), translation_step=50, hexagonal_packing=False, ) n_turbs_opt, x_opt, y_opt = layout_opt.optimize() assert n_turbs_opt < 3 # Test a coarse rotation layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=boundaries_diag, min_dist=turbine_spacing, rotation_step=60, # Not fine enough to find ideal 45 deg rotation rotation_range=(0, 360), translation_step=50, hexagonal_packing=False, ) n_turbs_opt, x_opt, y_opt = layout_opt.optimize() assert n_turbs_opt < 3 # Test a coarse translation layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=boundaries_diag, min_dist=turbine_spacing, rotation_step=45, rotation_range=(0, 10), translation_step=300, hexagonal_packing=False, ) n_turbs_opt, x_opt, y_opt = layout_opt.optimize() assert n_turbs_opt < 3 def test_LayoutOptimizationGridded_separate_boundaries(): fmodel = FlorisModel(configuration=YAML_INPUT) separate_boundaries = [ [(0.0, 0.0), (0.0, 100.0), (100.0, 100.0), (100.0, 0.0), (0.0, 0.0)], [(200.0, 0.0), (200.0, 100.0), (300.0, 100.0), (300.0, 0.0), (200.0, 0.0)] ] layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=separate_boundaries, min_dist=150, rotation_step=5, rotation_range=(0, 360), translation_step=50, hexagonal_packing=False, ) n_turbs_opt, x_opt, y_opt = layout_opt.optimize() assert n_turbs_opt == 2 # One in each of the boundary areas # Check they're inside as expected assert ((0.0 <= y_opt) & (y_opt <= 100.0)).all() assert (((0.0 <= x_opt) & (x_opt <= 100.0)) | ((200.0 <= x_opt) & (x_opt <= 300.0))).all() def test_LayoutOptimizationGridded_hexagonal(): fmodel = FlorisModel(configuration=YAML_INPUT) spacing = 200 # First, run a square layout layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=test_boundaries, min_dist=spacing, rotation_step=5, rotation_range=(0, 360), translation_step=50, hexagonal_packing=False, ) n_turbs_opt_square = layout_opt.optimize()[0] # Now, run a hexagonal layout layout_opt = LayoutOptimizationGridded( fmodel=fmodel, boundaries=test_boundaries, min_dist=spacing, rotation_step=5, rotation_range=(0, 360), translation_step=50, hexagonal_packing=True, ) n_turbs_opt_hex = layout_opt.optimize()[0] # Check that the hexagonal layout is better assert n_turbs_opt_hex >= n_turbs_opt_square ================================================ FILE: tests/layout_visualization_test.py ================================================ from pathlib import Path import matplotlib.pyplot as plt import numpy as np import floris.layout_visualization as layoutviz from floris import FlorisModel TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" def test_get_wake_direction(): # Turbine 0 wakes Turbine 1 at 270 degrees assert np.isclose(layoutviz.get_wake_direction(0, 0, 1, 0), 270.0) # Turbine 0 wakes Turbine 1 at 0 degrees assert np.isclose(layoutviz.get_wake_direction(0, 1, 0, 0), 0.0) # Winds from the south assert np.isclose(layoutviz.get_wake_direction(0, -1, 0, 0), 180.0) def test_plotting_functions(): fmodel = FlorisModel(configuration=YAML_INPUT) ax = layoutviz.plot_turbine_points(fmodel=fmodel) assert isinstance(ax, plt.Axes) ax = layoutviz.plot_turbine_labels(fmodel=fmodel) assert isinstance(ax, plt.Axes) ax = layoutviz.plot_turbine_rotors(fmodel=fmodel) assert isinstance(ax, plt.Axes) ax = layoutviz.plot_waking_directions(fmodel=fmodel) assert isinstance(ax, plt.Axes) # Add additional turbines to test plot farm terrain fmodel.set( layout_x=[0, 1000, 0, 1000, 3000], layout_y=[0, 0, 2000, 2000, 3000], ) ax = layoutviz.plot_farm_terrain(fmodel=fmodel) assert isinstance(ax, plt.Axes) ================================================ FILE: tests/load_optimization_test.py ================================================ import numpy as np from floris import ( FlorisModel, TimeSeries, WindRose, ) from floris.optimization.load_optimization.load_optimization import ( compute_farm_revenue, compute_farm_voc, compute_lti, compute_net_revenue, compute_turbine_voc, find_A_to_satisfy_rev_voc_ratio, find_A_to_satisfy_target_VOC_per_MW, optimize_power_setpoints, ) def test_compute_lti_no_wake(): # If we pass in a two-turbine, two-findex case where the turbines are # not aligned in flow, would expect to get back np.ones((2,2)) * ambient fmodel = FlorisModel(configuration="defaults") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) fmodel.set( wind_speeds=[8.0, 8.0], wind_directions=[0.0, 0.0], turbulence_intensities=[0.06, 0.06] ) fmodel.run() ambient_lti = [0.12, 0.1] load_ti = compute_lti(fmodel, ambient_lti) assert load_ti.shape == (2, 2) assert load_ti[0, 0] == ambient_lti[0] assert load_ti[0, 1] == ambient_lti[0] assert load_ti[1, 0] == ambient_lti[1] assert load_ti[1, 1] == ambient_lti[1] def test_compute_lti_wake(): # Test two turbines in a wake, n_findex = 1 fmodel = FlorisModel(configuration="defaults") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) fmodel.set(wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06]) fmodel.run() ambient_lti = [0.1] load_ti = compute_lti(fmodel, ambient_lti) assert load_ti.shape == (1, 2) np.testing.assert_almost_equal(load_ti[0, 0], ambient_lti) assert load_ti[0, 1] > load_ti[0, 0] def test_compute_turbine_voc_no_wake(): # If we pass in a two-turbine, two-findex case where the turbines are # not aligned in flow, would expect to get back a 2x2 numpy array where # all values are the same fmodel = FlorisModel(configuration="defaults") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) fmodel.set( wind_speeds=[8.0, 8.0], wind_directions=[0.0, 0.0], turbulence_intensities=[0.06, 0.06] ) fmodel.run() ambient_lti = [0.12, 0.12] voc = compute_turbine_voc(fmodel, 0.01, ambient_lti) assert voc.shape == (2, 2) assert voc[0, 0] == voc[0, 1] assert voc[0, 0] == voc[1, 0] assert voc[0, 0] == voc[1, 1] def test_compute_turbine_voc_wake(): # Test two turbines in a wake, n_findex = 1 fmodel = FlorisModel(configuration="defaults") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) fmodel.set(wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06]) fmodel.run() ambient_lti = [0.1] voc = compute_turbine_voc(fmodel, 0.01, ambient_lti) assert voc.shape == (1, 2) assert voc[0, 1] > voc[0, 0] def test_compute_net_revenue_no_wake(): # Assuming two turbines, two findex, no wake, and uniform value # net_revenue should be a 2-element array with the same value fmodel = FlorisModel(configuration="defaults") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) time_series = TimeSeries( wind_directions=np.array([0, 0]), wind_speeds=8.0, turbulence_intensities=0.06, values=np.array([0.5, 0.5]), ) fmodel.set(wind_data=time_series) fmodel.run() ambient_lti = [0.12, 0.12] net_revenue = compute_net_revenue(fmodel, 1, ambient_lti) assert net_revenue.shape == (2,) assert net_revenue[0] == net_revenue[1] def test_find_A_to_satisfy_rev_voc_ratio(): # Test the function that finds the A value that satisfies the revenue to voc ratio target_rev_voc_ratio = 10.0 fmodel = FlorisModel(configuration="defaults") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) N = 100 time_series = TimeSeries( wind_directions=np.ones(N) * 270.0, wind_speeds=8.0, turbulence_intensities=0.06, values=np.ones(N) ) fmodel.set(wind_data=time_series) fmodel.run() ambient_lti = np.ones(N) * 0.1 A = find_A_to_satisfy_rev_voc_ratio(fmodel, target_rev_voc_ratio, ambient_lti) farm_revenue = compute_farm_revenue(fmodel) farm_voc = compute_farm_voc(fmodel, A, ambient_lti) assert np.allclose(farm_revenue.sum() / farm_voc.sum(), target_rev_voc_ratio, atol=1e-4) def test_find_A_to_satisfy_target_VOC_per_MW(): # Test the function that finds the A value that satisfies the average VOC/MW/findex target_VOC_per_MW_findex = 10.0 fmodel = FlorisModel(configuration="defaults") fmodel.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) N = 100 time_series = TimeSeries( wind_directions=np.ones(N) * 270.0, wind_speeds=8.0, turbulence_intensities=0.06, values=np.ones(N) ) fmodel.set(wind_data=time_series) fmodel.run() ambient_lti = np.ones(N) * 0.1 A = find_A_to_satisfy_target_VOC_per_MW(fmodel, target_VOC_per_MW_findex, ambient_lti) farm_power = fmodel.get_farm_power() farm_voc = compute_farm_voc(fmodel, A, ambient_lti) assert np.allclose(1e6 * farm_voc.sum() / farm_power.sum(), target_VOC_per_MW_findex, atol=1e-4) ================================================ FILE: tests/par_floris_model_unit_test.py ================================================ import copy import logging import numpy as np import pytest from floris import ( FlorisModel, TimeSeries, WindRose, ) from floris.par_floris_model import ParFlorisModel DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" def test_None_interface(sample_inputs_fixture): """ With interface=None, the ParFlorisModel should behave exactly like the FlorisModel. (ParFlorisModel.run() simply calls the parent FlorisModel.run()). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface=None, n_wind_condition_splits=2 # Not used when interface=None ) fmodel.run() pfmodel.run() f_turb_powers = fmodel.get_turbine_powers() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) def test_multiprocessing_interface(sample_inputs_fixture): """ With interface="multiprocessing", the ParFlorisModel should return the same powers as the FlorisModel. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="multiprocessing", n_wind_condition_splits=2 ) fmodel.run() pfmodel.run() f_turb_powers = fmodel.get_turbine_powers() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) def test_pathos_interface(sample_inputs_fixture): """ With interface="pathos", the ParFlorisModel should return the same powers as the FlorisModel. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="pathos", n_wind_condition_splits=2 ) fmodel.run() pfmodel.run() f_turb_powers = fmodel.get_turbine_powers() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) # Run in powers_only mode pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="pathos", n_wind_condition_splits=2, return_turbine_powers_only=True ) pfmodel.run() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) def test_concurrent_interface(sample_inputs_fixture): """ With interface="concurrent", the ParFlorisModel should return the same powers as the FlorisModel. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="concurrent", n_wind_condition_splits=2, ) fmodel.run() pfmodel.run() f_turb_powers = fmodel.get_turbine_powers() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) # Run in powers_only mode pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="concurrent", n_wind_condition_splits=2, return_turbine_powers_only=True ) pfmodel.run() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) def test_return_turbine_powers_only(sample_inputs_fixture): """ With return_turbine_powers_only=True, the ParFlorisModel should return only the turbine powers, not the full results. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="multiprocessing", n_wind_condition_splits=2, return_turbine_powers_only=True ) fmodel.run() pfmodel.run() f_turb_powers = fmodel.get_turbine_powers() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) def test_run_error(sample_inputs_fixture, caplog): """ Check that an error is raised if an output is requested before calling run(). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="multiprocessing", n_wind_condition_splits=2 ) # In future versions, error will be raised # with pytest.raises(RuntimeError): # pfmodel.get_turbine_powers() # with pytest.raises(RuntimeError): # pfmodel.get_farm_AEP() # For now, only a warning is raised for backwards compatibility with caplog.at_level(logging.WARNING): pfmodel.get_turbine_powers() assert caplog.text != "" # Checking not empty caplog.clear() def test_configuration_compatibility(sample_inputs_fixture, caplog): """ Check that the ParFlorisModel is compatible with FlorisModel and UncertainFlorisModel configurations. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) # Allowed to instantiate ParFlorisModel using fmodel with caplog.at_level(logging.WARNING): ParFlorisModel(fmodel) assert caplog.text == "" # Checking empty caplog.clear() pfmodel = ParFlorisModel(sample_inputs_fixture.core) with caplog.at_level(logging.WARNING): pfmodel.fmodel assert caplog.text != "" # Checking not empty caplog.clear() with pytest.raises(AttributeError): pfmodel.fmodel.core def test_wind_data_objects(sample_inputs_fixture): """ Check that the ParFlorisModel is compatible with WindData objects. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel(sample_inputs_fixture.core, max_workers=2) # Create a wind rose and set onto both models wind_speeds = np.array([8.0, 10.0, 12.0, 8.0, 10.0, 12.0]) wind_directions = np.array([270.0, 270.0, 270.0, 280.0, 280.0, 280.0]) wind_rose = WindRose( wind_directions=np.unique(wind_directions), wind_speeds=np.unique(wind_speeds), ti_table=0.06 ) fmodel.set(wind_data=wind_rose) pfmodel.set(wind_data=wind_rose) # Run; get turbine powers; compare results fmodel.run() powers_fmodel_wr = fmodel.get_turbine_powers() pfmodel.run() powers_pfmodel_wr = pfmodel.get_turbine_powers() assert powers_fmodel_wr.shape == powers_pfmodel_wr.shape assert np.allclose(powers_fmodel_wr, powers_pfmodel_wr) # Test a TimeSeries object wind_speeds = np.array([8.0, 8.0, 9.0]) wind_directions = np.array([270.0, 270.0, 270.0]) values = np.array([30.0, 20.0, 10.0]) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06, values=values, ) fmodel.set(wind_data=time_series) pfmodel.set(wind_data=time_series) fmodel.run() powers_fmodel_ts = fmodel.get_turbine_powers() pfmodel.run() powers_pfmodel_ts = pfmodel.get_turbine_powers() assert powers_fmodel_ts.shape == powers_pfmodel_ts.shape assert np.allclose(powers_fmodel_ts, powers_pfmodel_ts) def test_control_setpoints(sample_inputs_fixture): """ Check that the ParFlorisModel is compatible with control set points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel(sample_inputs_fixture.core, n_wind_condition_splits=2) # Set yaw angles yaw_angles = np.tile(np.array([[10.0, 20.0, 30.0]]), (fmodel.n_findex,1)) fmodel.set(yaw_angles=yaw_angles) pfmodel.set(yaw_angles=yaw_angles) # Run; get turbine powers; compare results fmodel.run() powers_fmodel = fmodel.get_turbine_powers() pfmodel.run() powers_pfmodel = pfmodel.get_turbine_powers() assert powers_fmodel.shape == powers_pfmodel.shape assert np.allclose(powers_fmodel, powers_pfmodel) # Reset yaw angles and test power setpoints power_setpoints = np.tile(np.array([[1.0e6, 2.0e6, 1.0e12]]), (fmodel.n_findex,1)) fmodel.set_operation_model("simple-derating") pfmodel.set_operation_model("simple-derating") fmodel.reset_operation() pfmodel.reset_operation() fmodel.set(power_setpoints=power_setpoints) pfmodel.set(power_setpoints=power_setpoints) fmodel.run() powers_fmodel = fmodel.get_turbine_powers() pfmodel.run() powers_pfmodel = pfmodel.get_turbine_powers() assert powers_fmodel.shape == powers_pfmodel.shape assert np.allclose(powers_fmodel, powers_pfmodel) # Reset power setpoints and test disable_turbines disable_turbines = np.tile(np.array([[False, True, False]]), (fmodel.n_findex,1)) fmodel.reset_operation() pfmodel.reset_operation() fmodel.set(disable_turbines=disable_turbines) pfmodel.set(disable_turbines=disable_turbines) fmodel.run() powers_fmodel = fmodel.get_turbine_powers() pfmodel.run() powers_pfmodel = pfmodel.get_turbine_powers() assert powers_fmodel.shape == powers_pfmodel.shape assert np.allclose(powers_fmodel, powers_pfmodel) # Test AWC set points awc_modes = np.tile([["helix", "helix", "baseline"]], (fmodel.n_findex,1)) awc_amplitudes = np.tile([[0.0, 2.0, 0.0]], (fmodel.n_findex,1)) fmodel.set_operation_model("awc") pfmodel.set_operation_model("awc") fmodel.reset_operation() pfmodel.reset_operation() # Run once without AWC as a check fmodel.run() powers_base = fmodel.get_turbine_powers() # Now run with AWC fmodel.set(awc_modes=awc_modes, awc_amplitudes=awc_amplitudes) pfmodel.set(awc_modes=awc_modes, awc_amplitudes=awc_amplitudes) fmodel.run() powers_fmodel = fmodel.get_turbine_powers() pfmodel.run() powers_pfmodel = pfmodel.get_turbine_powers() assert powers_fmodel.shape == powers_pfmodel.shape assert np.allclose(powers_fmodel, powers_pfmodel) # Confirm that AWC changed the powers from baseline assert np.allclose(powers_base[:, 0], powers_fmodel[:, 0]) # 0 amplitude assert not np.isclose(powers_base[:, 1], powers_fmodel[:, 1]).any() # Helix applied def test_sample_flow_at_points(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) wind_speeds = np.array([8.0, 8.0, 9.0]) wind_directions = np.array([270.0, 270.0, 270.0]) fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06 * np.ones_like(wind_speeds.flatten()), ) x_test = np.array([500.0, 750.0, 1000.0, 1250.0, 1500.0]) y_test = np.array([0.0, 0.0, 0.0, 0.0, 0.0]) z_test = np.array([90.0, 90.0, 90.0, 90.0, 90.0]) ws_base = fmodel.sample_flow_at_points(x_test, y_test, z_test) for interface in ["multiprocessing", "pathos", "concurrent"]: pfmodel = ParFlorisModel(fmodel, max_workers=2, interface=interface) ws_test = pfmodel.sample_flow_at_points(x_test, y_test, z_test) assert np.allclose(ws_base, ws_test) def test_sample_ti_at_points(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = "crespo_hernandez" fmodel = FlorisModel(sample_inputs_fixture.core) wind_speeds = np.array([8.0, 8.0, 9.0]) wind_directions = np.array([270.0, 270.0, 270.0]) fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06 * np.ones_like(wind_speeds.flatten()), ) x_test = np.array([500.0, 750.0, 1000.0, 1250.0, 1500.0]) y_test = np.array([0.0, 0.0, 0.0, 0.0, 0.0]) z_test = np.array([90.0, 90.0, 90.0, 90.0, 90.0]) ti_base = fmodel.sample_ti_at_points(x_test, y_test, z_test) for interface in ["multiprocessing", "pathos", "concurrent"]: pfmodel = ParFlorisModel(fmodel, max_workers=2, interface=interface) ti_test = pfmodel.sample_ti_at_points(x_test, y_test, z_test) assert np.allclose(ti_base, ti_test) def test_copy(sample_inputs_fixture): """ Check that the ParFlorisModel copies correctly as a ParFlorisModel. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL pfmodel = ParFlorisModel(sample_inputs_fixture.core, max_workers=2) pfmodel_copy = pfmodel.copy() assert isinstance(pfmodel_copy, ParFlorisModel) assert pfmodel_copy.max_workers == 2 def test_heterogeneous_inflow_config(sample_inputs_fixture): """ Check that the ParFlorisModel works with heterogeneous_inflow_config set. """ heterogeneous_inflow_config = { "x": np.array([-200.0, 2000.0, -200.0, 2000.0]), "y": np.array([-200.0, -200.0, 200.0, 200.0]), "speed_multipliers": np.array( [ [1.0, 1.1, 1.2, 1.2], [1.1, 1.1, 1.1, 1.1], [1.0, 1.1, 1.2, 1.1], ] ), } wind_directions = np.array([270.0, 270.0, 270.0]) wind_speeds = np.array([8.0, 8.0, 9.0]) turbulence_intensities = 0.06 * np.ones_like(wind_speeds) fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="multiprocessing", n_wind_condition_splits=2, ) # Temporarily, run fmodel without heterogeneous_inflow_config to get baseline fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, ) fmodel.run() baseline_powers = fmodel.get_turbine_powers() # Set heterogeneous_flow_config cases and run fmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, heterogeneous_inflow_config=heterogeneous_inflow_config, ) pfmodel.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities, heterogeneous_inflow_config=heterogeneous_inflow_config, ) fmodel.run() pfmodel.run() powers_fmodel = fmodel.get_turbine_powers() assert (powers_fmodel != baseline_powers).any() # Check no overlap to ensure test is valid powers_pfmodel = pfmodel.get_turbine_powers() # Confirm that the powers computed using the ParFlorisModel match those from the FlorisModel assert np.allclose(powers_fmodel, powers_pfmodel) # Repeat test with z component added heterogeneous_inflow_config["z"] = np.array([0.0, 0.0, 0.0, 1000.0]) fmodel.set(heterogeneous_inflow_config=heterogeneous_inflow_config, wind_shear=0.0) pfmodel.set(heterogeneous_inflow_config=heterogeneous_inflow_config, wind_shear=0.0) fmodel.run() pfmodel.run() powers_fmodel = fmodel.get_turbine_powers() assert (powers_fmodel != baseline_powers).any() # Check no overlap to ensure test is valid powers_pfmodel = pfmodel.get_turbine_powers() # Confirm that the powers computed using the ParFlorisModel match those from the FlorisModel assert np.allclose(powers_fmodel, powers_pfmodel) def test_multidim_conditions(sample_inputs_fixture): """ Check that the ParFlorisModel works with multidim_conditions set in the TimeSeries object. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) fmodel.set(turbine_type=[sample_inputs_fixture.turbine_multi_dim]) pfmodel = ParFlorisModel( sample_inputs_fixture.core, interface="multiprocessing", n_wind_condition_splits=2 ) pfmodel.set(turbine_type=[sample_inputs_fixture.turbine_multi_dim]) # Create a TimeSeries object with multidim_conditions wind_speeds = np.array([8.0]*6) wind_directions = np.array([270.0]*6) multidim_conditions = { "Tp": np.array([5.0, 5.0, 6.0, 6.0, 7.0, 7.0]), "Hs": np.array([3.0, 3.0, 3.0, 4.0, 4.0, 4.0]), } time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06 * np.ones_like(wind_speeds.flatten()), multidim_conditions=multidim_conditions ) fmodel.set(wind_data=time_series) pfmodel.set(wind_data=time_series) fmodel.run() pfmodel.run() f_turb_powers = fmodel.get_turbine_powers() pf_turb_powers = pfmodel.get_turbine_powers() assert np.allclose(f_turb_powers, pf_turb_powers) ================================================ FILE: tests/parallel_floris_model_integration_test.py ================================================ import copy import logging import numpy as np import pytest from floris import ( FlorisModel, ParallelFlorisModel, UncertainFlorisModel, ) from tests.conftest import ( assert_results_arrays, ) DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" def test_raise_deprecation_warning(sample_inputs_fixture, caplog): """ Test that a warning is raised when instantiating the ParallelFlorisModel. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) caplog.clear() with caplog.at_level(logging.WARNING): ParallelFlorisModel( fmodel=fmodel, max_workers=2, n_wind_condition_splits=2, interface="concurrent", print_timings=False, ) assert caplog.text != "" # Checking not empty caplog.clear() def test_parallel_turbine_powers(sample_inputs_fixture): """ The parallel computing interface behaves like the floris interface, but distributes calculations among available cores to speed up the necessary computations. This test compares the individual turbine powers computed with the parallel interface to those computed with the serial floris interface. The expected result is that the turbine powers should be exactly the same. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel_input = copy.deepcopy(fmodel) fmodel.run() serial_turbine_powers = fmodel.get_turbine_powers() pfmodel = ParallelFlorisModel( fmodel=pfmodel_input, max_workers=2, n_wind_condition_splits=2, interface="concurrent", print_timings=False, ) parallel_turbine_powers = pfmodel.get_turbine_powers() if DEBUG: print(serial_turbine_powers) print(parallel_turbine_powers) assert_results_arrays(parallel_turbine_powers, serial_turbine_powers) def test_parallel_get_AEP(sample_inputs_fixture): sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL freq=np.linspace(0, 1, 16)/8 fmodel = FlorisModel(sample_inputs_fixture.core) pfmodel_input = copy.deepcopy(fmodel) fmodel.run() serial_farm_AEP = fmodel.get_farm_AEP(freq=freq) pfmodel = ParallelFlorisModel( fmodel=pfmodel_input, max_workers=2, n_wind_condition_splits=2, interface="concurrent", print_timings=False, ) parallel_farm_AEP = pfmodel.get_farm_AEP(freq=freq) assert np.allclose(parallel_farm_AEP, serial_farm_AEP) def test_parallel_uncertain_error(sample_inputs_fixture): """ """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL ufmodel = UncertainFlorisModel( sample_inputs_fixture.core, wd_sample_points=[-3, 0, 3], wd_std=3 ) with pytest.raises(ValueError): ParallelFlorisModel( fmodel=ufmodel, max_workers=2, n_wind_condition_splits=2, interface="multiprocessing", print_timings=False, ) ================================================ FILE: tests/reg_tests/cumulative_curl_regression_test.py ================================================ import numpy as np from floris.core import ( average_velocity, axial_induction, Core, power, rotor_effective_velocity, thrust_coefficient, ) from tests.conftest import ( assert_results_arrays, N_FINDEX, N_TURBINES, print_test_values, ) DEBUG = False VELOCITY_MODEL = "cc" DEFLECTION_MODEL = "gauss" baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [5.4510872, 0.8920540, 554423.2959292, 0.3357243], [5.0438692, 0.9152035, 418539.5184876, 0.3544008], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [6.1342847, 0.8547425, 797961.8242685, 0.3094367], [5.6482366, 0.8808465, 620209.7062129, 0.3274069], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [6.8191059, 0.8235980, 1105849.4970759, 0.2899988], [6.2802136, 0.8481059, 863569.7643645, 0.3051320], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [7.5591728, 0.7958161, 1495578.0671426, 0.2740664], [6.9317813, 0.8184737, 1156507.0595179, 0.2869705], ], ] ) yawed_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.4955257, 0.8895278, 569251.8849842, 0.3338132], [5.0512690, 0.9147828, 421008.7273674, 0.3540401], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.1842430, 0.8524704, 820422.5044532, 0.3079521], [5.6590417, 0.8802323, 623815.2315242, 0.3269626], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [6.8745497, 0.8210765, 1130776.3831297, 0.2885032], [6.2938285, 0.8474867, 869690.8728188, 0.3047352], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [7.6186441, 0.7939637, 1530927.6220300, 0.2730439], [6.9469619, 0.8177833, 1163332.0650645, 0.2865657], ], ] ) yaw_added_recovery_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.5123171, 0.8885732, 574854.9880625, 0.3330968], [5.0653039, 0.9139850, 425692.0104596, 0.3533584], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.2030677, 0.8516143, 828885.8701797, 0.3073957], [5.6761588, 0.8792592, 629527.0166369, 0.3262611], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [6.8953509, 0.8201305, 1140128.3768208, 0.2879449], [6.3135442, 0.8465900, 878554.8061141, 0.3041621], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [7.6397253, 0.7933242, 1543688.6272448, 0.2726920], [6.9675202, 0.8168483, 1172574.8397092, 0.2860189], ], ] ) secondary_steering_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.4955262, 0.8895278, 569252.0553799, 0.3338132], [5.0564287, 0.9144895, 422730.4667041, 0.3537891], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.1842436, 0.8524704, 820422.7619472, 0.3079521], [5.6652985, 0.8798766, 625903.0435126, 0.3267059], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [6.8745503, 0.8210764, 1130776.6678583, 0.2885032], [6.3010138, 0.8471599, 872921.3000764, 0.3045262], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [7.6186447, 0.7939637, 1530928.0140962, 0.2730439], [6.9547367, 0.8174297, 1166827.5280695, 0.2863588], ], ] ) full_flow_baseline = np.array( [ [ [ [7.88772361, 8.00000000, 8.10178821], [7.88772361, 8.00000000, 8.10178821], [7.88772361, 8.00000000, 8.10178821], [7.88772361, 8.00000000, 8.10178821], [7.88772361, 8.00000000, 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.85396979, 7.96487892, 8.06803439], [4.19559099, 4.28925565, 4.40965558], [7.85396979, 7.96487892, 8.06803439], [7.88772361, 8. , 8.10178821], ], [ [7.88769642, 7.99997223, 8.10176102], [7.58415314, 7.69072103, 7.79821773], [4.16725762, 4.26342392, 4.38132221], [7.58415314, 7.69072103, 7.79821773], [7.88769642, 7.99997223, 8.10176102], ], [ [7.88513176, 7.99737618, 8.09919636], [7.21888868, 7.32333558, 7.43301511], [4.30201226, 4.40270245, 4.51689213], [7.21888868, 7.32333558, 7.43301511], [7.88513176, 7.99737618, 8.09919636], ], [ [7.86539121, 7.97748824, 8.0794561 ], [7.0723371 , 7.1790733 , 7.28645574], [5.8436738 , 5.95178931, 6.05791862], [7.0723371 , 7.1790733 , 7.28645574], [7.86539121, 7.97748824, 8.0794561 ], ] ] ] ) # Note: compare the yawed vs non-yawed results. The upstream turbine # power should be lower in the yawed case. The following turbine # powers should higher in the yawed case. def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], baseline) def test_regression_rotation(sample_inputs_fixture): """ Turbines in tandem and rotated. The result from 270 degrees should match the results from 360 degrees. Wind from the West (Left) ^ | y 1|1 3 | | | 0|0 2 |----------| 0 1 x-> Wind from the North (Top), rotated ^ | y 1|3 2 | | | 0|1 0 |----------| 0 1 x-> In 270, turbines 2 and 3 are waked. In 360, turbines 0 and 2 are waked. The test compares turbines 2 and 3 with 0 and 2 from 270 and 360. """ TURBINE_DIAMETER = 126.0 sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["farm"]["layout_x"] = [ 0.0, 0.0, 5 * TURBINE_DIAMETER, 5 * TURBINE_DIAMETER, ] sample_inputs_fixture.core["farm"]["layout_y"] = [ 0.0, 5 * TURBINE_DIAMETER, 0.0, 5 * TURBINE_DIAMETER ] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1, 0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() farm_avg_velocities = average_velocity(floris.flow_field.u) t0_270 = farm_avg_velocities[0, 0] # upstream t1_270 = farm_avg_velocities[0, 1] # upstream t2_270 = farm_avg_velocities[0, 2] # waked t3_270 = farm_avg_velocities[0, 3] # waked t0_360 = farm_avg_velocities[1, 0] # waked t1_360 = farm_avg_velocities[1, 1] # upstream t2_360 = farm_avg_velocities[1, 2] # waked t3_360 = farm_avg_velocities[1, 3] # upstream assert np.allclose(t0_270, t1_360) assert np.allclose(t1_270, t3_360) assert np.allclose(t2_270, t0_360) assert np.allclose(t3_270, t2_360) def test_regression_yaw(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], yawed_baseline) def test_regression_yaw_added_recovery(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed and yaw added recovery correction enabled """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["enable_transverse_velocities"] = True sample_inputs_fixture.core["wake"]["enable_secondary_steering"] = False sample_inputs_fixture.core["wake"]["enable_yaw_added_recovery"] = True floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], yaw_added_recovery_baseline) def test_regression_secondary_steering(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed and secondary steering enabled """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["enable_transverse_velocities"] = True sample_inputs_fixture.core["wake"]["enable_secondary_steering"] = True sample_inputs_fixture.core["wake"]["enable_yaw_added_recovery"] = False floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], secondary_steering_baseline) def test_regression_small_grid_rotation(sample_inputs_fixture): """ This utilizes a 5x5 wind farm with the layout in a regular grid oriented along the cardinal directions. The wind direction in this test is from 285 degrees which is slightly north of west. The objective of this test is to create a case with a very slight rotation of the wind farm to target the rotation and masking routines. Where wake models are masked based on the x-location of a turbine, numerical precision can cause masking to fail unexpectedly. For example, in the configuration here one of the turbines has these delta x values; [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]] and therefore the masking statement is False when it should be True. This causes the current turbine to be affected by its own wake. This test requires that at least in this particular configuration the masking correctly filters grid points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL X, Y = np.meshgrid( 6.0 * 126.0 * np.arange(0, 5, 1), 6.0 * 126.0 * np.arange(0, 5, 1) ) X = X.flatten() Y = Y.flatten() sample_inputs_fixture.core["farm"]["layout_x"] = X sample_inputs_fixture.core["farm"]["layout_y"] = Y floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) # A "column" is oriented parallel to the wind direction # Columns 1 - 4 should have the same power profile # Column 5 leading turbine is completely unwaked # and the rest of the turbines have a partial wake from their immediate upstream turbine assert np.allclose(farm_powers[8,0:5], farm_powers[8,5:10]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,10:15]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,15:20]) assert np.allclose(farm_powers[8,20], farm_powers[8,0]) assert np.allclose(farm_powers[8,21], farm_powers[8,21:25]) def test_full_flow_solver(sample_inputs_fixture): """ Full flow solver test with the flow field planar grid. This requires one wind condition, and the grid is deliberately coarse to allow for visually comparing results, as needed. The u-component of velocity is compared, and the array has the shape (n_findex, n_turbines, n grid points in x, n grid points in y, 3 grid points in z). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["solver"] = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": sample_inputs_fixture.core["farm"]["turbine_type"][0]["hub_height"], "flow_field_grid_points": [5, 5], "flow_field_bounds": [None, None], } sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.solve_for_viz() velocities = floris.flow_field.u_sorted assert_results_arrays(velocities, full_flow_baseline) ================================================ FILE: tests/reg_tests/empirical_gauss_regression_test.py ================================================ import numpy as np from floris.core import ( average_velocity, axial_induction, Core, power, rotor_effective_velocity, thrust_coefficient, ) from tests.conftest import ( assert_results_arrays, N_FINDEX, N_TURBINES, print_test_values, ) DEBUG = False VELOCITY_MODEL = "empirical_gauss" DEFLECTION_MODEL = "empirical_gauss" TURBULENCE_MODEL = "wake_induced_mixing" baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [5.8239250, 0.8708590, 678834.8317748, 0.3203190], [5.9004356, 0.8665095, 704365.4950630, 0.3173183], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [6.5562701, 0.8355513, 987681.5731429, 0.2972386], [6.6949231, 0.8292456, 1050018.3472064, 0.2933878], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [7.2923306, 0.8047024, 1343118.2404618, 0.2790376], [7.4934722, 0.7978974, 1456951.3486441, 0.2752209], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [8.1353345, 0.7869536, 1872313.2273018, 0.2692152], [8.2936951, 0.7867495, 1990669.8925423, 0.2691047], ], ] ) yawed_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.8720857, 0.8681212, 694905.4822543, 0.3184244], [5.9231111, 0.8652205, 711932.0521602, 0.3164383], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.6102438, 0.8330967, 1011947.5002467, 0.2957310], [6.7207579, 0.8280707, 1061633.3882586, 0.2926782], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.3519418, 0.8026469, 1376375.4821341, 0.2778778], [7.5221584, 0.7969827, 1473761.4857038, 0.2747128], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.1956906, 0.7868758, 1917422.6059783, 0.2691731], [8.3187504, 0.7867172, 2009395.8987459, 0.2690872], ], ] ) tilted_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7824685, 1734488.7059275, 0.2658985], [5.8875889, 0.8705526, 704778.1875928, 0.3212047], [5.9110415, 0.8692142, 712622.7895048, 0.3202651], ], # 9 m/s [ [8.9703965, 0.7812020, 2471404.7824861, 0.2652278], [6.6276158, 0.8354859, 1026885.3722728, 0.2980366], [6.7091646, 0.8317630, 1063636.4762428, 0.2957326], ], # 10 m/s [ [9.9671073, 0.7792154, 3383206.6144193, 0.2641794], [7.3711242, 0.8050505, 1396968.1317078, 0.2799135], [7.5109456, 0.8003819, 1477742.2826442, 0.2772650], ], # 11 m/s [ [10.9638180, 0.7520150, 4470666.5322783, 0.2502624], [8.2150645, 0.7898565, 1946589.2518193, 0.2714076], [8.3045772, 0.7897407, 2013649.9637290, 0.2713440], ] ] ) yaw_added_recovery_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.8812867, 0.8675981, 697975.7537581, 0.3180646], [5.9300836, 0.8648241, 714258.6740264, 0.3161686], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.6205487, 0.8326280, 1016580.4631213, 0.2954444], [6.7286194, 0.8277131, 1065167.8381647, 0.2924627], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.3633114, 0.8022558, 1382735.2369962, 0.2776578], [7.5308334, 0.7967093, 1478874.6141430, 0.2745612], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.2070431, 0.7868612, 1925907.3101195, 0.2691652], [8.3266654, 0.7867070, 2015311.4552010, 0.2690817], ], ] ) helix_added_recovery_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [5.8239250, 0.8708590, 678834.8317748, 0.3203190], [5.9004356, 0.8665095, 704365.4950630, 0.3173183], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [6.5562701, 0.8355513, 987681.5731429, 0.2972386], [6.6949231, 0.8292456, 1050018.3472064, 0.2933878], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [7.2923306, 0.8047024, 1343118.2404618, 0.2790376], [7.4934722, 0.7978974, 1456951.3486441, 0.2752209], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [8.1353345, 0.7869536, 1872313.2273018, 0.2692152], [8.2936951, 0.7867495, 1990669.8925423, 0.2691047], ], ] ) full_flow_baseline = np.array( [ [ [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772294, 7.99999928, 8.10178747], [7.81880864, 7.9261404 , 8.02651415], [4.66160854, 4.54241201, 4.57798522], [7.81880864, 7.9261404 , 8.02651415], [7.88772294, 7.99999928, 8.10178747], ], [ [7.88733339, 7.99958656, 8.10136247], [7.60765422, 7.70390457, 7.79791213], [5.19792855, 5.15875115, 5.18986616], [7.60765422, 7.70390457, 7.79791213], [7.88733339, 7.99958656, 8.10136247], ], [ [7.87220134, 7.98400571, 8.08549566], [7.41124269, 7.50382311, 7.59416296], [5.65108754, 5.65881944, 5.70295049], [7.41124269, 7.50382311, 7.59416296], [7.87220134, 7.98400571, 8.08549566], ], [ [7.83300625, 7.94438006, 8.04560619], [7.37461427, 7.47355048, 7.56659807], [6.47381486, 6.53210142, 6.59762329], [7.37461427, 7.47355048, 7.56659807], [7.83300625, 7.94438006, 8.04560619], ], ] ] ) # Note: compare the yawed vs non-yawed results. The upstream turbine # power should be lower in the yawed case. The following turbine # powers should higher in the yawed case. def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], baseline) def test_regression_rotation(sample_inputs_fixture): """ Turbines in tandem and rotated. The result from 270 degrees should match the results from 360 degrees. Wind from the West (Left) ^ | y 1|1 3 | | | 0|0 2 |----------| 0 1 x-> Wind from the North (Top), rotated ^ | y 1|3 2 | | | 0|1 0 |----------| 0 1 x-> In 270, turbines 2 and 3 are waked. In 360, turbines 0 and 2 are waked. The test compares turbines 2 and 3 with 0 and 2 from 270 and 360. """ TURBINE_DIAMETER = 126.0 sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL sample_inputs_fixture.core["farm"]["layout_x"] = [ 0.0, 0.0, 5 * TURBINE_DIAMETER, 5 * TURBINE_DIAMETER, ] sample_inputs_fixture.core["farm"]["layout_y"] = [ 0.0, 5 * TURBINE_DIAMETER, 0.0, 5 * TURBINE_DIAMETER ] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1, 0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() farm_avg_velocities = average_velocity(floris.flow_field.u) t0_270 = farm_avg_velocities[0, 0] # upstream t1_270 = farm_avg_velocities[0, 1] # upstream t2_270 = farm_avg_velocities[0, 2] # waked t3_270 = farm_avg_velocities[0, 3] # waked t0_360 = farm_avg_velocities[1, 0] # waked t1_360 = farm_avg_velocities[1, 1] # upstream t2_360 = farm_avg_velocities[1, 2] # waked t3_360 = farm_avg_velocities[1, 3] # upstream assert np.allclose(t0_270, t1_360) assert np.allclose(t1_270, t3_360) assert np.allclose(t2_270, t0_360) assert np.allclose(t3_270, t2_360) def test_regression_yaw(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], yawed_baseline) def test_regression_tilt(sample_inputs_fixture): """ Tandem turbines with the upstream turbine tilted """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL floris = Core.from_dict(sample_inputs_fixture.core) tilt_angles = np.zeros((N_FINDEX, N_TURBINES)) tilt_angles[:,0] = 8.0 floris.farm.tilt_angles = tilt_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], tilted_baseline) def test_regression_yaw_added_recovery(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed and yaw added recovery correction enabled """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL # Turn on yaw added recovery sample_inputs_fixture.core["wake"]["enable_yaw_added_recovery"] = True # First pass, leave at default value of 0; should then do nothing floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] # Compare to case where enable_yaw_added_recovery = False, since # default gains are 0. assert_results_arrays(test_results[0:4], yawed_baseline) # Second pass, use nonzero gain sample_inputs_fixture.core["wake"]["wake_deflection_parameters"]\ ["empirical_gauss"]["yaw_added_mixing_gain"] = 0.1 floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], yaw_added_recovery_baseline) def test_regression_helix(sample_inputs_fixture): """ Tandem turbines with the upstream turbine applying the helix """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL floris = Core.from_dict(sample_inputs_fixture.core) awc_modes = np.array([["helix"]*N_TURBINES]*N_FINDEX) awc_amplitudes = np.zeros((N_FINDEX, N_TURBINES)) awc_amplitudes[:,0] = 5.0 floris.farm.awc_amplitudes = awc_amplitudes floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], helix_added_recovery_baseline) def test_regression_small_grid_rotation(sample_inputs_fixture): """ This utilizes a 5x5 wind farm with the layout in a regular grid oriented along the cardinal directions. The wind direction in this test is from 285 degrees which is slightly north of west. The objective of this test is to create a case with a very slight rotation of the wind farm to target the rotation and masking routines. Where wake models are masked based on the x-location of a turbine, numerical precision can cause masking to fail unexpectedly. For example, in the configuration here one of the turbines has these delta x values; [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]] and therefore the masking statement is False when it should be True. This causes the current turbine to be affected by its own wake. This test requires that at least in this particular configuration the masking correctly filters grid points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL X, Y = np.meshgrid( 6.0 * 126.0 * np.arange(0, 5, 1), 6.0 * 126.0 * np.arange(0, 5, 1) ) X = X.flatten() Y = Y.flatten() sample_inputs_fixture.core["farm"]["layout_x"] = X sample_inputs_fixture.core["farm"]["layout_y"] = Y floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field # farm_eff_velocities = rotor_effective_velocity( # floris.flow_field.air_density, # floris.farm.ref_air_densities, # velocities, # yaw_angles, # tilt_angles, # floris.farm.ref_tilts, # floris.farm.pPs, # floris.farm.pTs, # floris.farm.turbine_tilt_interps, # floris.farm.correct_cp_ct_for_tilt, # floris.farm.turbine_type_map, # ) farm_powers = power( velocities, turbulence_intensities, floris.flow_field.air_density, floris.farm.turbine_power_functions, floris.farm.yaw_angles, floris.farm.tilt_angles, floris.farm.power_setpoints, floris.farm.awc_modes, floris.farm.awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) # A "column" is oriented parallel to the wind direction # Columns 1 - 4 should have the same power profile # Column 5 is completely unwaked in this model assert np.allclose(farm_powers[8,0:5], farm_powers[8,5:10]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,10:15]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,15:20]) assert np.allclose(farm_powers[8,20], farm_powers[8,0]) assert np.allclose(farm_powers[8,21], farm_powers[8,21:25]) def test_full_flow_solver(sample_inputs_fixture): """ Full flow solver test with the flow field planar grid. This requires one wind condition, and the grid is deliberately coarse to allow for visually comparing results, as needed. The u-component of velocity is compared, and the array has the shape (n_findex, n_turbines, n grid points in x, n grid points in y, 3 grid points in z). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL sample_inputs_fixture.core["solver"] = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": sample_inputs_fixture.core["farm"]["turbine_type"][0]["hub_height"], "flow_field_grid_points": [5, 5], "flow_field_bounds": [None, None], } sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.solve_for_viz() velocities = floris.flow_field.u_sorted if DEBUG: print(velocities) assert_results_arrays(velocities, full_flow_baseline) ================================================ FILE: tests/reg_tests/gauss_regression_test.py ================================================ import numpy as np from floris.core import ( average_velocity, axial_induction, Core, power, rotor_effective_velocity, thrust_coefficient, ) from tests.conftest import ( assert_results_arrays, N_FINDEX, N_TURBINES, print_test_values, ) DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [5.9186455, 0.8654743, 710441.9192938, 0.3166113], [6.0090150, 0.8604395, 741642.0177873, 0.3132110], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [6.6606465, 0.8308044, 1034608.0101396, 0.2943330], [6.7947466, 0.8247058, 1094897.8563374, 0.2906592], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [7.4045198, 0.8008441, 1405853.7207176, 0.2768656], [7.5868432, 0.7949439, 1511887.2179035, 0.2735844], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [8.2046271, 0.7868643, 1924101.6501936, 0.2691669], [8.3491997, 0.7866780, 2032153.3223547, 0.2690660], ], ] ) yawed_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.9521551, 0.8635694, 721623.6989382, 0.3153174], [6.0131307, 0.8602523, 743492.3616581, 0.3130858], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.6982609, 0.8290938, 1051519.0079315, 0.2932960], [6.7996516, 0.8244827, 1097103.0727816, 0.2905261], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.4461669, 0.7994645, 1429777.3846192, 0.2760940], [7.5922658, 0.7947730, 1515083.3259879, 0.2734901], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.2481957, 0.7868081, 1956664.2629680, 0.2691365], [8.3531097, 0.7866729, 2035075.5955678, 0.2690633], ], ] ) full_flow_baseline = np.array( [ [ [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772264, 7.99999899, 8.10178721], [7.80183828, 7.91077933, 8.01357204], [4.05787708, 4.02142188, 4.16800363], [7.80183828, 7.91077933, 8.01357204], [7.88772264, 7.99999899, 8.10178721], ], [ [7.88365433, 7.9958357 , 8.09760849], [7.54214774, 7.64551046, 7.74683377], [4.99852407, 5.0247459 , 5.13417881], [7.54214774, 7.64551046, 7.74683377], [7.88365433, 7.9958357 , 8.09760849], ], [ [7.85066049, 7.96222083, 8.06371923], [7.39444624, 7.49602334, 7.5951238 ], [5.50716692, 5.55540288, 5.65662569], [7.39444624, 7.49602334, 7.5951238 ], [7.85066049, 7.96222083, 8.06371923], ], [ [7.79761973, 7.90832696, 8.009239 ], [7.41896092, 7.52268669, 7.62030379], [6.98565022, 7.0811275 , 7.17523349], [7.41896092, 7.52268669, 7.62030379], [7.79761973, 7.90832696, 8.009239 ], ] ] ] ) """ # These are the results from v2.4 develop branch gch_baseline = np.array( [ # 8 m/s [ [7.9803783, 0.7605249, 1683956.3885389, 0.2548147], [5.8920347, 0.8409478, 669953.8921404, 0.3005933], [5.9690770, 0.8370054, 696678.9863587, 0.2981370], ], # 9 m/s [ [8.9779256, 0.7596713, 2397237.3791443, 0.2543815], [6.6299831, 0.8071465, 970496.1338006, 0.2804246], [6.7527627, 0.8022061, 1027643.3724351, 0.2776299], ], # 10 m/s [ [9.9754729, 0.7499157, 3283592.6005045, 0.2494847], [7.3852773, 0.7796129, 1346745.9407360, 0.2652730], [7.5343901, 0.7749587, 1428106.9252795, 0.2628074], ], # 11 m/s [ [10.9730201, 0.7276532, 4344217.6993801, 0.2386508], [8.1727131, 0.7624523, 1824643.2726943, 0.2563057], [8.2996789, 0.7621064, 1911032.3885037, 0.2561283], ] ] ) secondary_steering_baseline = np.array( [ # 8 m/s [ [7.9803783, 0.7605249, 1683956.3885389, 0.2548147], [5.8728752, 0.8419282, 663307.6815433, 0.3012088], [5.9488299, 0.8380415, 689655.4839532, 0.2987797], ], # 9 m/s [ [8.9779256, 0.7596713, 2397237.3791443, 0.2543815], [6.6084854, 0.8080115, 960490.1060497, 0.2809176], [6.7305708, 0.8030991, 1017314.2281904, 0.2781324], ], # 10 m/s [ [9.9754729, 0.7499157, 3283592.6005045, 0.2494847], [7.3621072, 0.7803734, 1334476.0326665, 0.2656783], [7.5106613, 0.7755721, 1413887.2753700, 0.2631309], ], # 11 m/s [ [10.9730201, 0.7276532, 4344217.6993801, 0.2386508], [8.1489930, 0.7625169, 1808503.8150366, 0.2563388], [8.2759469, 0.7621711, 1894884.8361479, 0.2561615], ] ] ) """ gch_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.9689340, 0.8626155, 727222.6050018, 0.3146730], [6.0360908, 0.8592082, 753814.9629960, 0.3123888], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.7170645, 0.8282386, 1059972.8615898, 0.2927795], [6.8249569, 0.8233319, 1108480.0451319, 0.2898405], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.4669332, 0.7987766, 1441706.3550352, 0.2757103], [7.6196359, 0.7939336, 1531527.9847411, 0.2730273], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.2691610, 0.7867811, 1972333.4291742, 0.2691218], [8.3808845, 0.7866371, 2055834.1618762, 0.2690439], ], ] ) yaw_added_recovery_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.9689332, 0.8626156, 727222.3540334, 0.3146730], [6.0305406, 0.8594606, 751319.6495844, 0.3125571], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.7170636, 0.8282387, 1059972.4826657, 0.2927795], [6.8187909, 0.8236123, 1105707.8700965, 0.2900073], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.4669323, 0.7987766, 1441705.8203841, 0.2757103], [7.6128912, 0.7941382, 1527445.2805280, 0.2731400], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.2691601, 0.7867811, 1972332.7278100, 0.2691218], [8.3736743, 0.7866464, 2050445.3384596, 0.2690489], ], ] ) secondary_steering_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.9521559, 0.8635693, 721623.9542957, 0.3153174], [6.0187788, 0.8599955, 746031.6889128, 0.3129141], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.6982618, 0.8290937, 1051519.3934629, 0.2932959], [6.8059255, 0.8241974, 1099923.7444659, 0.2903559], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.4461678, 0.7994645, 1429777.9285494, 0.2760940], [7.5991268, 0.7945568, 1519127.2504621, 0.2733708], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.2481967, 0.7868081, 1956664.9757307, 0.2691365], [8.3604363, 0.7866635, 2040551.4040835, 0.2690582], ], ] ) # Note: compare the yawed vs non-yawed results. The upstream turbine # power should be lower in the yawed case. The following turbine # powers should higher in the yawed case. def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], baseline) def test_regression_rotation(sample_inputs_fixture): """ Turbines in tandem and rotated. The result from 270 degrees should match the results from 360 degrees. Wind from the West (Left) ^ | y 1|1 3 | | | 0|0 2 |----------| 0 1 x-> Wind from the North (Top), rotated ^ | y 1|3 2 | | | 0|1 0 |----------| 0 1 x-> In 270, turbines 2 and 3 are waked. In 360, turbines 0 and 2 are waked. The test compares turbines 2 and 3 with 0 and 2 from 270 and 360. """ TURBINE_DIAMETER = 126.0 sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["farm"]["layout_x"] = [ 0.0, 0.0, 5 * TURBINE_DIAMETER, 5 * TURBINE_DIAMETER, ] sample_inputs_fixture.core["farm"]["layout_y"] = [ 0.0, 5 * TURBINE_DIAMETER, 0.0, 5 * TURBINE_DIAMETER ] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1, 0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() farm_avg_velocities = average_velocity(floris.flow_field.u) t0_270 = farm_avg_velocities[0, 0] # upstream t1_270 = farm_avg_velocities[0, 1] # upstream t2_270 = farm_avg_velocities[0, 2] # waked t3_270 = farm_avg_velocities[0, 3] # waked t0_360 = farm_avg_velocities[1, 0] # waked t1_360 = farm_avg_velocities[1, 1] # upstream t2_360 = farm_avg_velocities[1, 2] # waked t3_360 = farm_avg_velocities[1, 3] # upstream assert np.allclose(t0_270, t1_360) assert np.allclose(t1_270, t3_360) assert np.allclose(t2_270, t0_360) assert np.allclose(t3_270, t2_360) def test_regression_yaw(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], yawed_baseline) def test_regression_gch(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed, yaw added recovery correction enabled, and secondary steering enabled """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL ### With GCH off (via conftest), GCH should be same as Gauss floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] # Don't use the test values here, gch is off! See the docstring. # if DEBUG: # print_test_values( # farm_avg_velocities, # farm_cts, # farm_powers, # farm_axial_inductions, # ) assert_results_arrays(test_results[0:4], yawed_baseline) ### With GCH on, the results should change sample_inputs_fixture.core["wake"]["enable_transverse_velocities"] = True sample_inputs_fixture.core["wake"]["enable_secondary_steering"] = True sample_inputs_fixture.core["wake"]["enable_yaw_added_recovery"] = True floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], gch_baseline) def test_regression_yaw_added_recovery(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed and yaw added recovery correction enabled """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["enable_transverse_velocities"] = True sample_inputs_fixture.core["wake"]["enable_secondary_steering"] = False sample_inputs_fixture.core["wake"]["enable_yaw_added_recovery"] = True floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], yaw_added_recovery_baseline) def test_regression_secondary_steering(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed and secondary steering enabled """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["enable_transverse_velocities"] = True sample_inputs_fixture.core["wake"]["enable_secondary_steering"] = True sample_inputs_fixture.core["wake"]["enable_yaw_added_recovery"] = False floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], secondary_steering_baseline) def test_regression_small_grid_rotation(sample_inputs_fixture): """ This utilizes a 5x5 wind farm with the layout in a regular grid oriented along the cardinal directions. The wind direction in this test is from 285 degrees which is slightly north of west. The objective of this test is to create a case with a very slight rotation of the wind farm to target the rotation and masking routines. Where wake models are masked based on the x-location of a turbine, numerical precision can cause masking to fail unexpectedly. For example, in the configuration here one of the turbines has these delta x values; [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]] and therefore the masking statement is False when it should be True. This causes the current turbine to be affected by its own wake. This test requires that at least in this particular configuration the masking correctly filters grid points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL X, Y = np.meshgrid( 6.0 * 126.0 * np.arange(0, 5, 1), 6.0 * 126.0 * np.arange(0, 5, 1) ) X = X.flatten() Y = Y.flatten() sample_inputs_fixture.core["farm"]["layout_x"] = X sample_inputs_fixture.core["farm"]["layout_y"] = Y floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes farm_powers = power( velocities, turbulence_intensities, floris.flow_field.air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) # A "column" is oriented parallel to the wind direction # Columns 1 - 4 should have the same power profile # Column 5 leading turbine is completely unwaked # and the rest of the turbines have a partial wake from their immediate upstream turbine assert np.allclose(farm_powers[8,0:5], farm_powers[8,5:10]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,10:15]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,15:20]) assert np.allclose(farm_powers[8,20], farm_powers[8,0]) assert np.allclose(farm_powers[8,21], farm_powers[8,21:25]) def test_full_flow_solver(sample_inputs_fixture): """ Full flow solver test with the flow field planar grid. This requires one wind condition, and the grid is deliberately coarse to allow for visually comparing results, as needed. The u-component of velocity is compared, and the array has the shape (n_findex, n_turbines, n grid points in x, n grid points in y, 3 grid points in z). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["solver"] = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": sample_inputs_fixture.core["farm"]["turbine_type"][0]["hub_height"], "flow_field_grid_points": [5, 5], "flow_field_bounds": [None, None], } sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.solve_for_viz() velocities = floris.flow_field.u_sorted assert_results_arrays(velocities, full_flow_baseline) ================================================ FILE: tests/reg_tests/jensen_jimenez_regression_test.py ================================================ import numpy as np from floris.core import ( average_velocity, axial_induction, Core, power, rotor_effective_velocity, thrust_coefficient, ) from tests.conftest import ( assert_results_arrays, N_FINDEX, N_TURBINES, print_test_values, ) DEBUG = False VELOCITY_MODEL = "jensen" DEFLECTION_MODEL = "jimenez" baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [6.0660565, 0.8578454, 767287.2198744, 0.3114830], [5.5204712, 0.8881097, 577575.9208353, 0.3327500], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [6.8298067, 0.8231113, 1110660.4518964, 0.2897093], [6.3668912, 0.8441639, 902538.9934586, 0.3026196], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [7.5982117, 0.7945856, 1518587.8467982, 0.2733867], [7.2042504, 0.8077903, 1294847.7809883, 0.2807914], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [8.4970746, 0.7864874, 2142673.1558338, 0.2689629], [7.9997342, 0.7871282, 1770992.0756703, 0.2693098], ], ] ) yawed_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [6.0816475, 0.8571363, 774296.7271893, 0.3110134], [5.5272875, 0.8877222, 579850.4298177, 0.3324606], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.8472506, 0.8223180, 1118503.0309148, 0.2892383], [6.3747452, 0.8438067, 906070.0511419, 0.3023935], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.6174285, 0.7940006, 1530191.8035935, 0.2730642], [7.2119500, 0.8075204, 1299067.3876318, 0.2806375], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.5159500, 0.7864631, 2156780.3499849, 0.2689497], [8.0047998, 0.7871218, 1774753.2988553, 0.2693064], ], ] ) full_flow_baseline = np.array( [ [ [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [5.55736296, 5.63646825, 5.708184 ], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [5.11849406, 5.19135235, 5.25740466], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.18032699, 7.28253407, 7.37519358], [4.98829055, 5.05929547, 5.12366755], [7.18032699, 7.28253407, 7.37519358], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [6.97109947, 6.37648724, 7.16028784], [6.28699612, 6.37648724, 6.45761864], [6.97109947, 6.37648724, 7.16028784], [7.88772361, 8. , 8.10178821], ] ] ] ) # Note: compare the yawed vs non-yawed results. The upstream turbine # power should be lower in the yawed case. The following turbine # powers should higher in the yawed case. def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], baseline) def test_regression_rotation(sample_inputs_fixture): """ Turbines in tandem and rotated. The result from 270 degrees should match the results from 360 degrees. Wind from the West (Left) ^ | y 1|1 3 | | | 0|0 2 |----------| 0 1 x-> Wind from the North (Top), rotated ^ | y 1|3 2 | | | 0|1 0 |----------| 0 1 x-> In 270, turbines 2 and 3 are waked. In 360, turbines 0 and 2 are waked. The test compares turbines 2 and 3 with 0 and 2 from 270 and 360. """ TURBINE_DIAMETER = 126.0 sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["farm"]["layout_x"] = [ 0.0, 0.0, 5 * TURBINE_DIAMETER, 5 * TURBINE_DIAMETER, ] sample_inputs_fixture.core["farm"]["layout_y"] = [ 0.0, 5 * TURBINE_DIAMETER, 0.0, 5 * TURBINE_DIAMETER ] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1, 0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() farm_avg_velocities = average_velocity(floris.flow_field.u) t0_270 = farm_avg_velocities[0, 0] # upstream t1_270 = farm_avg_velocities[0, 1] # upstream t2_270 = farm_avg_velocities[0, 2] # waked t3_270 = farm_avg_velocities[0, 3] # waked t0_360 = farm_avg_velocities[1, 0] # waked t1_360 = farm_avg_velocities[1, 1] # upstream t2_360 = farm_avg_velocities[1, 2] # waked t3_360 = farm_avg_velocities[1, 3] # upstream assert np.allclose(t0_270, t1_360) assert np.allclose(t1_270, t3_360) assert np.allclose(t2_270, t0_360) assert np.allclose(t3_270, t2_360) def test_regression_yaw(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4 ) assert_results_arrays(test_results[0:4], yawed_baseline) def test_regression_small_grid_rotation(sample_inputs_fixture): """ This utilizes a 5x5 wind farm with the layout in a regular grid oriented along the cardinal directions. The wind direction in this test is from 285 degrees which is slightly north of west. The objective of this test is to create a case with a very slight rotation of the wind farm to target the rotation and masking routines. Where wake models are masked based on the x-location of a turbine, numerical precision can cause masking to fail unexpectedly. For example, in the configuration here one of the turbines has these delta x values; [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]] and therefore the masking statement is False when it should be True. This causes the current turbine to be affected by its own wake. This test requires that at least in this particular configuration the masking correctly filters grid points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL X, Y = np.meshgrid( 6.0 * 126.0 * np.arange(0, 5, 1), 6.0 * 126.0 * np.arange(0, 5, 1) ) X = X.flatten() Y = Y.flatten() sample_inputs_fixture.core["farm"]["layout_x"] = X sample_inputs_fixture.core["farm"]["layout_y"] = Y floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes # farm_eff_velocities = rotor_effective_velocity( # floris.flow_field.air_density, # floris.farm.ref_air_densities, # velocities, # yaw_angles, # tilt_angles, # floris.farm.ref_tilts, # floris.farm.pPs, # floris.farm.pTs, # floris.farm.turbine_tilt_interps, # floris.farm.correct_cp_ct_for_tilt, # floris.farm.turbine_type_map, # ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) # A "column" is oriented parallel to the wind direction # Columns 1 - 4 should have the same power profile # Column 5 is completely unwaked in this model assert np.allclose(farm_powers[8,0:5], farm_powers[8,5:10]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,10:15]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,15:20]) assert np.allclose(farm_powers[8,20], farm_powers[8,20:25]) def test_full_flow_solver(sample_inputs_fixture): """ Full flow solver test with the flow field planar grid. This requires one wind condition, and the grid is deliberately coarse to allow for visually comparing results, as needed. The u-component of velocity is compared, and the array has the shape (n_findex, n_turbines, n grid points in x, n grid points in y, 3 grid points in z). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["solver"] = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": sample_inputs_fixture.core["farm"]["turbine_type"][0]["hub_height"], "flow_field_grid_points": [5, 5], "flow_field_bounds": [None, None], } sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.solve_for_viz() velocities = floris.flow_field.u_sorted assert_results_arrays(velocities, full_flow_baseline) ================================================ FILE: tests/reg_tests/none_regression_test.py ================================================ import numpy as np import pytest from floris.core import ( average_velocity, axial_induction, Core, power, rotor_effective_velocity, thrust_coefficient, ) from tests.conftest import ( assert_results_arrays, N_FINDEX, N_TURBINES, print_test_values, ) DEBUG = False VELOCITY_MODEL = "none" DEFLECTION_MODEL = "none" baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], ], ] ) yawed_baseline = np.array( [ # 8 m/s [ [7.9803783, 0.7605249, 1683956.3885389, 0.2548147], [7.9803783, 0.7605249, 1683956.3885389, 0.2548147], [7.9803783, 0.7605249, 1683956.3885389, 0.2548147], ], # 9 m/s [ [8.9779256, 0.7596713, 2397237.3791443, 0.2543815], [8.9779256, 0.7596713, 2397237.3791443, 0.2543815], [8.9779256, 0.7596713, 2397237.3791443, 0.2543815], ], # 10 m/s [ [9.9754729, 0.7499157, 3283592.6005045, 0.2494847], [9.9754729, 0.7499157, 3283592.6005045, 0.2494847], [9.9754729, 0.7499157, 3283592.6005045, 0.2494847], ], # 11 m/s [ [10.9730201, 0.7276532, 4344217.6993801, 0.2386508], [10.9730201, 0.7276532, 4344217.6993801, 0.2386508], [10.9730201, 0.7276532, 4344217.6993801, 0.2386508], ] ] ) full_flow_baseline = np.array( [ [ [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], ] ] ) # Note: compare the yawed vs non-yawed results. The upstream turbine # power should be lower in the yawed case. The following turbine # powers should higher in the yawed case. def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], baseline) def test_regression_rotation(sample_inputs_fixture): """ Turbines in tandem and rotated. The result from 270 degrees should match the results from 360 degrees. Wind from the West (Left) ^ | y 1|1 3 | | | 0|0 2 |----------| 0 1 x-> Wind from the North (Top), rotated ^ | y 1|3 2 | | | 0|1 0 |----------| 0 1 x-> In 270, turbines 2 and 3 are waked. In 360, turbines 0 and 2 are waked. The test compares turbines 2 and 3 with 0 and 2 from 270 and 360. """ TURBINE_DIAMETER = 126.0 sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["farm"]["layout_x"] = [ 0.0, 0.0, 5 * TURBINE_DIAMETER, 5 * TURBINE_DIAMETER, ] sample_inputs_fixture.core["farm"]["layout_y"] = [ 0.0, 5 * TURBINE_DIAMETER, 0.0, 5 * TURBINE_DIAMETER ] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1, 0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() farm_avg_velocities = average_velocity(floris.flow_field.u) t0_270 = farm_avg_velocities[0, 0] # upstream t1_270 = farm_avg_velocities[0, 1] # upstream t2_270 = farm_avg_velocities[0, 2] # waked t3_270 = farm_avg_velocities[0, 3] # waked t0_360 = farm_avg_velocities[1, 0] # waked t1_360 = farm_avg_velocities[1, 1] # upstream t2_360 = farm_avg_velocities[1, 2] # waked t3_360 = farm_avg_velocities[1, 3] # upstream assert np.allclose(t0_270, t1_360) assert np.allclose(t1_270, t3_360) assert np.allclose(t2_270, t0_360) assert np.allclose(t3_270, t2_360) def test_regression_yaw(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() with pytest.raises(ValueError): floris.steady_state_atmospheric_condition() def test_regression_small_grid_rotation(sample_inputs_fixture): """ This utilizes a 5x5 wind farm with the layout in a regular grid oriented along the cardinal directions. The wind direction in this test is from 285 degrees which is slightly north of west. The objective of this test is to create a case with a very slight rotation of the wind farm to target the rotation and masking routines. Where wake models are masked based on the x-location of a turbine, numerical precision can cause masking to fail unexpectedly. For example, in the configuration here one of the turbines has these delta x values; [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]] and therefore the masking statement is False when it should be True. This causes the current turbine to be affected by its own wake. This test requires that at least in this particular configuration the masking correctly filters grid points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL X, Y = np.meshgrid( 6.0 * 126.0 * np.arange(0, 5, 1), 6.0 * 126.0 * np.arange(0, 5, 1) ) X = X.flatten() Y = Y.flatten() sample_inputs_fixture.core["farm"]["layout_x"] = X sample_inputs_fixture.core["farm"]["layout_y"] = Y floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) # A "column" is oriented parallel to the wind direction # Columns 1 - 4 should have the same power profile # Column 5 leading turbine is completely unwaked # and the rest of the turbines have a partial wake from their immediate upstream turbine assert np.allclose(farm_powers[8,0:5], farm_powers[8,5:10]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,10:15]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,15:20]) assert np.allclose(farm_powers[8,20], farm_powers[8,0]) assert np.allclose(farm_powers[8,21], farm_powers[8,21:25]) def test_full_flow_solver(sample_inputs_fixture): """ Full flow solver test with the flow field planar grid. This requires one wind condition, and the grid is deliberately coarse to allow for visually comparing results, as needed. The u-component of velocity is compared, and the array has the shape (n_findex, n_turbines, n grid points in x, n grid points in y, 3 grid points in z). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["solver"] = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": sample_inputs_fixture.core["farm"]["turbine_type"][0]["hub_height"], "flow_field_grid_points": [5, 5], "flow_field_bounds": [None, None], } sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.solve_for_viz() velocities = floris.flow_field.u_sorted assert_results_arrays(velocities, full_flow_baseline) ================================================ FILE: tests/reg_tests/random_search_layout_opt_regression_test.py ================================================ import numpy as np import pandas as pd from floris import FlorisModel, WindRose from floris.optimization.layout_optimization.layout_optimization_random_search import ( LayoutOptimizationRandomSearch, ) from tests.conftest import ( assert_results_arrays, ) DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" locations_baseline_aep = np.array( [ [0.0, 243.05304475, 1260.0], [0.0, 959.83979244, 0.0], ] ) baseline_aep = 45226182795.34081 locations_baseline_value = np.array( [ [387.0, 100.0, 200.0, 300.0], [192.0, 300.0, 100.0, 300.0], ] ) baseline_value = 8780876351.32277 def test_random_search_layout_opt(sample_inputs_fixture): """ The SciPy optimization method optimizes turbine layout using SciPy's minimize method. This test compares the optimization results from the SciPy layout optimization for a simple farm with a simple wind rose to stored baseline results. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)] fmodel = FlorisModel(sample_inputs_fixture.core) wd_array = np.arange(0, 360.0, 5.0) ws_array = np.array([8.0]) wind_rose = WindRose( wind_directions=wd_array, wind_speeds=ws_array, ti_table=0.1, ) D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_data=wind_rose ) layout_opt = LayoutOptimizationRandomSearch( fmodel=fmodel, boundaries=boundaries, min_dist_D=5, seconds_per_iteration=1, total_optimization_seconds=1, use_dist_based_init=False, random_seed=0, ) sol = layout_opt._test_optimize() optimized_aep = sol[0] locations_opt = np.array([sol[1], sol[2]]) if DEBUG: print(locations_opt) print(optimized_aep) assert_results_arrays(locations_opt, locations_baseline_aep) assert np.isclose(optimized_aep, baseline_aep) def test_random_search_layout_opt_value(sample_inputs_fixture): """ This test compares the optimization results from the SciPy layout optimization for a simple farm with a simple wind rose to stored baseline results, optimizing for annual value production instead of AEP. The value of the energy produced depends on the wind direction, causing the optimal layout to differ from the case where the objective is maximum AEP. In this case, because the value is much higher when the wind is from the north or south, the turbines are staggered to avoid wake interactions for northerly and southerly winds. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL boundaries = [(0.0, 0.0), (0.0, 400.0), (400.0, 400.0), (400.0, 0.0), (0.0, 0.0)] fmodel = FlorisModel(sample_inputs_fixture.core) # set wind conditions and values using a WindData object with the default uniform frequency wd_array = np.arange(0, 360.0, 5.0) ws_array = np.array([8.0]) # Define the value table such that the value of the energy produced is # significantly higher when the wind direction is close to the north or # south, and zero when the wind is from the east or west. value_table = (0.5 + 0.5*np.cos(2*np.radians(wd_array)))**10 value_table = value_table.reshape((len(wd_array),1)) wind_rose = WindRose( wind_directions=wd_array, wind_speeds=ws_array, ti_table=0.1, value_table=value_table ) # Start with a rectangular 4-turbine array with 2D spacing D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=200 + np.array([-1 * D, -1 * D, 1 * D, 1 * D]), layout_y=200 + np.array([-1* D, 1 * D, -1 * D, 1 * D]), wind_data=wind_rose, ) layout_opt = LayoutOptimizationRandomSearch( fmodel=fmodel, boundaries=boundaries, min_dist_D=5, seconds_per_iteration=1, total_optimization_seconds=1, use_dist_based_init=True, random_seed=0, use_value=True, ) sol = layout_opt._test_optimize() optimized_value = sol[0] locations_opt = np.array([sol[1], sol[2]]) if DEBUG: print(locations_opt) print(optimized_value) assert_results_arrays(locations_opt, locations_baseline_value) assert np.isclose(optimized_value, baseline_value) ================================================ FILE: tests/reg_tests/scipy_layout_opt_regression.py ================================================ import numpy as np import pandas as pd from floris import FlorisModel, WindRose from floris.optimization.layout_optimization.layout_optimization_scipy import ( LayoutOptimizationScipy, ) from tests.conftest import ( assert_results_arrays, ) DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" baseline = np.array( [ [0.0, 495.37587653, 1000.0], [5.0, 11.40800868, 24.93196392], ] ) baseline_value = np.array( [ [8.68262334e+01, 1.04360964e-12, 4.00000000e+02, 2.36100415e+02], [1.69954798e-14, 4.00000000e+02, 0.00000000e+00, 4.00000000e+02], ] ) def test_scipy_layout_opt(sample_inputs_fixture): """ The SciPy optimization method optimizes turbine layout using SciPy's minimize method. This test compares the optimization results from the SciPy layout optimization for a simple farm with a simple wind rose to stored baseline results. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL opt_options = { "maxiter": 5, "disp": True, "iprint": 2, "ftol": 1e-12, "eps": 0.01, } boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)] fmodel = FlorisModel(sample_inputs_fixture.core) wd_array = np.arange(0, 360.0, 5.0) ws_array = 8.0 * np.ones_like(wd_array) ti_array = 0.1 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array, ) layout_opt = LayoutOptimizationScipy(fmodel, boundaries, optOptions=opt_options) sol = layout_opt.optimize() locations_opt = np.array([sol[0], sol[1]]) if DEBUG: print(baseline) print(locations_opt) assert_results_arrays(locations_opt, baseline) def test_scipy_layout_opt_value(sample_inputs_fixture): """ This test compares the optimization results from the SciPy layout optimization for a simple farm with a simple wind rose to stored baseline results, optimizing for annual value production instead of AEP. The value of the energy produced depends on the wind direction, causing the optimal layout to differ from the case where the objective is maximum AEP. In this case, because the value is much higher when the wind is from the north or south, the turbines are staggered to avoid wake interactions for northerly and southerly winds. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL opt_options = { "maxiter": 5, "disp": True, "iprint": 2, "ftol": 1e-12, "eps": 0.1, } boundaries = [(0.0, 0.0), (0.0, 400.0), (400.0, 400.0), (400.0, 0.0), (0.0, 0.0)] fmodel = FlorisModel(sample_inputs_fixture.core) # set wind conditions and values using a WindData object with the default uniform frequency wd_array = np.arange(0, 360.0, 5.0) ws_array = np.array([8.0]) # Define the value table such that the value of the energy produced is # significantly higher when the wind direction is close to the north or # south, and zero when the wind is from the east or west. value_table = (0.5 + 0.5*np.cos(2*np.radians(wd_array)))**10 value_table = value_table.reshape((len(wd_array),1)) wind_rose = WindRose( wind_directions=wd_array, wind_speeds=ws_array, ti_table=0.1, value_table=value_table ) # Start with a rectangular 4-turbine array with 2D spacing D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=200 + np.array([-1 * D, -1 * D, 1 * D, 1 * D]), layout_y=200 + np.array([-1* D, 1 * D, -1 * D, 1 * D]), wind_data=wind_rose, ) layout_opt = LayoutOptimizationScipy( fmodel, boundaries, optOptions=opt_options, use_value=True ) sol = layout_opt.optimize() locations_opt = np.array([sol[0], sol[1]]) if DEBUG: print(baseline) print(locations_opt) assert_results_arrays(locations_opt, baseline_value) ================================================ FILE: tests/reg_tests/turbopark_regression_test.py ================================================ import numpy as np from floris.core import ( average_velocity, axial_induction, Core, power, rotor_effective_velocity, thrust_coefficient, ) from tests.conftest import ( assert_results_arrays, N_FINDEX, N_TURBINES, print_test_values, ) DEBUG = False VELOCITY_MODEL = "turbopark" DEFLECTION_MODEL = "gauss" COMBINATION_MODEL = "fls" baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [6.0332948, 0.8593353, 752557.9240063, 0.3124735], [5.4029800, 0.8947888, 538370.5108659, 0.3378186], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [6.7887441, 0.8249788, 1092199.1775234, 0.2908223], [6.0678594, 0.8577634, 768097.7785191, 0.3114286], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [7.5453629, 0.7962514, 1487438.4031455, 0.2743074], [6.7548552, 0.8265200, 1076963.1412833, 0.2917453], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [8.3436376, 0.7866851, 2027996.3027579, 0.2690699], [7.4626804, 0.7989174, 1439263.3915910, 0.2757889], ], ] ) yawed_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [6.0523119, 0.8584704, 761107.7639542, 0.3118979], [5.4177841, 0.8939472, 543310.4550423, 0.3371713], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.8101438, 0.8240055, 1101820.2623232, 0.2902415], [6.0851644, 0.8569764, 775877.8906008, 0.3109077], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [7.5691494, 0.7955016, 1501458.3309846, 0.2738925], [6.7745474, 0.8256244, 1085816.5021615, 0.2912085], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [8.3695194, 0.7866518, 2047340.0279521, 0.2690518], [7.4830530, 0.7982426, 1450966.1620998, 0.2754129], ], ] ) # Note: compare the yawed vs non-yawed results. The upstream turbine # power should be lower in the yawed case. The following turbine # powers should higher in the yawed case. def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["combination_model"] = COMBINATION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], baseline) def test_regression_rotation(sample_inputs_fixture): """ Turbines in tandem and rotated. The result from 270 degrees should match the results from 360 degrees. Wind from the West (Left) ^ | y 1|1 3 | | | 0|0 2 |----------| 0 1 x-> Wind from the North (Top), rotated ^ | y 1|3 2 | | | 0|1 0 |----------| 0 1 x-> In 270, turbines 2 and 3 are waked. In 360, turbines 0 and 2 are waked. The test compares turbines 2 and 3 with 0 and 2 from 270 and 360. """ TURBINE_DIAMETER = 126.0 sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["combination_model"] = COMBINATION_MODEL sample_inputs_fixture.core["farm"]["layout_x"] = [ 0.0, 0.0, 5 * TURBINE_DIAMETER, 5 * TURBINE_DIAMETER, ] sample_inputs_fixture.core["farm"]["layout_y"] = [ 0.0, 5 * TURBINE_DIAMETER, 0.0, 5 * TURBINE_DIAMETER ] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1, 0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() farm_avg_velocities = average_velocity(floris.flow_field.u) t0_270 = farm_avg_velocities[0, 0] # upstream t1_270 = farm_avg_velocities[0, 1] # upstream t2_270 = farm_avg_velocities[0, 2] # waked t3_270 = farm_avg_velocities[0, 3] # waked t0_360 = farm_avg_velocities[1, 0] # waked t1_360 = farm_avg_velocities[1, 1] # upstream t2_360 = farm_avg_velocities[1, 2] # waked t3_360 = farm_avg_velocities[1, 3] # upstream assert np.allclose(t0_270, t1_360) assert np.allclose(t1_270, t3_360) assert np.allclose(t2_270, t0_360) assert np.allclose(t3_270, t2_360) def test_regression_yaw(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], yawed_baseline) def test_regression_small_grid_rotation(sample_inputs_fixture): """ Where wake models are masked based on the x-location of a turbine, numerical precision can cause masking to fail unexpectedly. For example, in the configuration here one of the turbines has these delta x values; [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]] and therefore the masking statement is False when it should be True. This causes the current turbine to be affected by its own wake. This test requires that at least in this particular configuration the masking correctly filters grid points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["combination_model"] = COMBINATION_MODEL X, Y = np.meshgrid( 6.0 * 126.0 * np.arange(0, 5, 1), 6.0 * 126.0 * np.arange(0, 5, 1) ) X = X.flatten() Y = Y.flatten() sample_inputs_fixture.core["farm"]["layout_x"] = X sample_inputs_fixture.core["farm"]["layout_y"] = Y floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes farm_powers = power( velocities, turbulence_intensities, floris.flow_field.air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) # A "column" is oriented parallel to the wind direction # Columns 1 - 4 should have the same power profile # Column 5 leading turbine is completely unwaked # and the rest of the turbines have a partial wake from their immediate upstream turbine assert np.allclose(farm_powers[8,0:5], farm_powers[8,5:10]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,10:15]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,15:20]) assert np.allclose(farm_powers[8,20], farm_powers[8,0]) assert np.allclose(farm_powers[8,21], farm_powers[8,21:25]) ''' ## Not implemented in TurbOPark def test_full_flow_solver(sample_inputs_fixture): """ Full flow solver test with the flow field planar grid. This requires one wind condition, and the grid is deliberately coarse to allow for visually comparing results, as needed. The u-component of velocity is compared, and the array has the shape (n_findex, n_turbines, n grid points in x, n grid points in y, 3 grid points in z). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["solver"] = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": sample_inputs_fixture.core["farm"]["turbine_type"][0]["hub_height"], "flow_field_grid_points": [5, 5], "flow_field_bounds": [None, None], } sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0] floris = Core.from_dict(sample_inputs_fixture.core) floris.solve_for_viz() velocities = floris.flow_field.u_sorted print(velocities) assert_results_arrays(velocities, full_flow_baseline) ''' ================================================ FILE: tests/reg_tests/turboparkgauss_regression_test.py ================================================ import numpy as np from floris.core import ( average_velocity, axial_induction, Core, power, rotor_effective_velocity, thrust_coefficient, ) from tests.conftest import ( assert_results_arrays, N_FINDEX, N_TURBINES, print_test_values, ) DEBUG = False VELOCITY_MODEL = "turboparkgauss" DEFLECTION_MODEL = "gauss" COMBINATION_MODEL = "sosfs" baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7871515, 1753954.4591792, 0.2693224], [5.3669227, 0.8968386, 526338.6265211, 0.3394063], [4.7291434, 0.9398463, 342625.1907593, 0.3773687], ], # 9 m/s [ [8.9703965, 0.7858774, 2496427.8618358, 0.2686331], [6.0385619, 0.8590958, 754925.9561188, 0.3123139], [5.2198714, 0.9051982, 477269.3475684, 0.3460505], ], # 10 m/s [ [9.9671073, 0.7838789, 3417797.0050916, 0.2675559], [6.7109723, 0.8285157, 1057233.8964038, 0.2929467], [5.7609373, 0.8744397, 657816.5966079, 0.3228276], ], # 11 m/s [ [10.9638180, 0.7565157, 4519404.3072862, 0.2532794], [7.4177796, 0.8004049, 1413470.6329668, 0.2766196], [6.3467168, 0.8450814, 893468.8191848, 0.3032015], ], ] ) yawed_baseline = np.array( [ # 8 m/s [ [7.9736858, 0.7841561, 1741508.6722008, 0.2671213], [5.3686096, 0.8967427, 526901.4969868, 0.3393316], [4.7296392, 0.9398058, 342737.3617937, 0.3773274], ], # 9 m/s [ [8.9703965, 0.7828869, 2480428.8963141, 0.2664440], [6.0405714, 0.8590044, 755829.3886024, 0.3122531], [5.2206194, 0.9051556, 477518.9548881, 0.3460159], ], # 10 m/s [ [9.9671073, 0.7808960, 3395681.0032992, 0.2653854], [6.7133964, 0.8284054, 1058323.7446597, 0.2928801], [5.7619351, 0.8743830, 658149.5244311, 0.3227875], ], # 11 m/s [ [10.9638180, 0.7536370, 4488242.9153943, 0.2513413], [7.4229011, 0.8002352, 1416412.6499511, 0.2765247], [6.3490875, 0.8449736, 894534.6529145, 0.3031330], ], ] ) full_flow_baseline = np.array( [ [ [ [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], [7.88772361, 8. , 8.10178821], ], [ [7.88772229, 7.99999863, 8.10178685], [7.79725047, 7.90606371, 8.00885965], [4.18190854, 4.15233328, 4.29539865], [7.79725047, 7.90606371, 8.00885965], [7.88772229, 7.99999863, 8.10178685], ], [ [7.88768632, 7.99996148, 8.1017499 ], [7.66326846, 7.7681154 , 7.87123883], [3.69538982, 3.66849132, 3.79562999], [7.66326846, 7.7681154 , 7.87123883], [7.88768632, 7.99996148, 8.1017499 ], ], [ [7.88740669, 7.99967377, 8.10146266], [7.50793067, 7.6089272 , 7.71165714], [3.64994795, 3.63535913, 3.74869 ], [7.50793067, 7.6089272 , 7.71165714], [7.88740669, 7.99967377, 8.10146266], ], [ [7.88664826, 7.99889554, 8.10068331], [7.44424308, 7.54429736, 7.64614946], [4.32643439, 4.33927499, 4.44299895], [7.44424308, 7.54429736, 7.64614946], [7.88664826, 7.99889554, 8.10068331], ] ] ] ) # Note: compare the yawed vs non-yawed results. The upstream turbine # power should be lower in the yawed case. The following turbine # powers should higher in the yawed case. def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["combination_model"] = COMBINATION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], baseline) def test_regression_rotation(sample_inputs_fixture): """ Turbines in tandem and rotated. The result from 270 degrees should match the results from 360 degrees. Wind from the West (Left) ^ | y 1|1 3 | | | 0|0 2 |----------| 0 1 x-> Wind from the North (Top), rotated ^ | y 1|3 2 | | | 0|1 0 |----------| 0 1 x-> In 270, turbines 2 and 3 are waked. In 360, turbines 0 and 2 are waked. The test compares turbines 2 and 3 with 0 and 2 from 270 and 360. """ TURBINE_DIAMETER = 126.0 sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["combination_model"] = COMBINATION_MODEL sample_inputs_fixture.core["farm"]["layout_x"] = [ 0.0, 0.0, 5 * TURBINE_DIAMETER, 5 * TURBINE_DIAMETER, ] sample_inputs_fixture.core["farm"]["layout_y"] = [ 0.0, 5 * TURBINE_DIAMETER, 0.0, 5 * TURBINE_DIAMETER ] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1, 0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() farm_avg_velocities = average_velocity(floris.flow_field.u) t0_270 = farm_avg_velocities[0, 0] # upstream t1_270 = farm_avg_velocities[0, 1] # upstream t2_270 = farm_avg_velocities[0, 2] # waked t3_270 = farm_avg_velocities[0, 3] # waked t0_360 = farm_avg_velocities[1, 0] # waked t1_360 = farm_avg_velocities[1, 1] # upstream t2_360 = farm_avg_velocities[1, 2] # waked t3_360 = farm_avg_velocities[1, 3] # upstream assert np.allclose(t0_270, t1_360) assert np.allclose(t1_270, t3_360) assert np.allclose(t2_270, t0_360) assert np.allclose(t3_270, t2_360) def test_regression_yaw(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL floris = Core.from_dict(sample_inputs_fixture.core) yaw_angles = np.zeros((N_FINDEX, N_TURBINES)) yaw_angles[:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field air_density = floris.flow_field.air_density yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = thrust_coefficient( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_powers = power( velocities, turbulence_intensities, air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, turbulence_intensities, air_density, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) for i in range(n_findex): for j in range(n_turbines): test_results[i, j, 0] = farm_avg_velocities[i, j] test_results[i, j, 1] = farm_cts[i, j] test_results[i, j, 2] = farm_powers[i, j] test_results[i, j, 3] = farm_axial_inductions[i, j] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, max_findex_print=4, ) assert_results_arrays(test_results[0:4], yawed_baseline) def test_regression_small_grid_rotation(sample_inputs_fixture): """ Where wake models are masked based on the x-location of a turbine, numerical precision can cause masking to fail unexpectedly. For example, in the configuration here one of the turbines has these delta x values; [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13] [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]] and therefore the masking statement is False when it should be True. This causes the current turbine to be affected by its own wake. This test requires that at least in this particular configuration the masking correctly filters grid points. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["combination_model"] = COMBINATION_MODEL X, Y = np.meshgrid( 6.0 * 126.0 * np.arange(0, 5, 1), 6.0 * 126.0 * np.arange(0, 5, 1) ) X = X.flatten() Y = Y.flatten() sample_inputs_fixture.core["farm"]["layout_x"] = X sample_inputs_fixture.core["farm"]["layout_y"] = Y floris = Core.from_dict(sample_inputs_fixture.core) floris.initialize_domain() floris.steady_state_atmospheric_condition() # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u turbulence_intensities = floris.flow_field.turbulence_intensity_field yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles power_setpoints = floris.farm.power_setpoints awc_modes = floris.farm.awc_modes awc_amplitudes = floris.farm.awc_amplitudes farm_powers = power( velocities, turbulence_intensities, floris.flow_field.air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, power_setpoints, awc_modes, awc_amplitudes, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) # A "column" is oriented parallel to the wind direction # Columns 1 - 4 should have the same power profile # Column 5 leading turbine is completely unwaked # and the rest of the turbines have a partial wake from their immediate upstream turbine assert np.allclose(farm_powers[8,0:5], farm_powers[8,5:10]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,10:15]) assert np.allclose(farm_powers[8,0:5], farm_powers[8,15:20]) assert np.allclose(farm_powers[8,20], farm_powers[8,0]) assert np.allclose(farm_powers[8,21], farm_powers[8,21:25]) # TurboParkGauss enables full_flow_solver def test_full_flow_solver(sample_inputs_fixture): """ Full flow solver test with the flow field planar grid. This requires one wind condition, and the grid is deliberately coarse to allow for visually comparing results, as needed. The u-component of velocity is compared, and the array has the shape (n_findex, n_turbines, n grid points in x, n grid points in y, 3 grid points in z). """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["solver"] = { "type": "flow_field_planar_grid", "normal_vector": "z", "planar_coordinate": sample_inputs_fixture.core["farm"]["turbine_type"][0]["hub_height"], "flow_field_grid_points": [5, 5], "flow_field_bounds": [None, None], } sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = [0.1] floris = Core.from_dict(sample_inputs_fixture.core) floris.solve_for_viz() velocities = floris.flow_field.u_sorted if DEBUG: print(velocities) assert_results_arrays(velocities, full_flow_baseline) ================================================ FILE: tests/reg_tests/turbulence_models_regression_test.py ================================================ from floris.core import Core from floris.core.wake_turbulence import NoneWakeTurbulence VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" def test_NoneWakeTurbulence(sample_inputs_fixture): turbulence_intensities = [0.1, 0.05] sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = "none" sample_inputs_fixture.core["farm"]["layout_x"] = [0.0, 0.0, 600.0, 600.0] sample_inputs_fixture.core["farm"]["layout_y"] = [0.0, 600.0, 0.0, 600.0] sample_inputs_fixture.core["flow_field"]["wind_directions"] = [270.0, 360.0] sample_inputs_fixture.core["flow_field"]["wind_speeds"] = [8.0, 8.0] sample_inputs_fixture.core["flow_field"]["turbulence_intensities"] = turbulence_intensities core = Core.from_dict(sample_inputs_fixture.core) core.initialize_domain() core.steady_state_atmospheric_condition() assert ( core.flow_field.turbulence_intensity_field_sorted[0,:] == turbulence_intensities[0] ).all() assert ( core.flow_field.turbulence_intensity_field_sorted[1,:] == turbulence_intensities[1] ).all() ================================================ FILE: tests/reg_tests/yaw_optimization_regression_test.py ================================================ import numpy as np import pandas as pd from floris import FlorisModel from floris.optimization.yaw_optimization.yaw_optimizer_geometric import ( YawOptimizationGeometric, ) from floris.optimization.yaw_optimization.yaw_optimizer_scipy import YawOptimizationScipy from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" # These inputs and baseline power are common for all optimization methods WIND_DIRECTIONS = [0.0, 90.0, 180.0, 270.0] WIND_SPEEDS = [8.0] * 4 TURBULENCE_INTENSITIES = [0.1] * 4 FARM_POWER_BASELINE = [5.261863e+06, 3.206038e+06, 5.261863e+06, 3.206038e+06] # These are the input data structures for each optimization method along with the output # optimized yaw angles baseline_serial_refine = pd.DataFrame( { "wind_direction": WIND_DIRECTIONS, "wind_speed": WIND_SPEEDS, "turbulence_intensity": TURBULENCE_INTENSITIES, "yaw_angles_opt": [ [0.0, 0.0, 0.0], [0.0, 25.0, 15.625], [0.0, 0.0, 0.0], [15.625, 25.0, 0.0], ], "farm_power_opt": [5.261863e+06, 3.262218e+06, 5.261863e+06, 3.262218e+06], "farm_power_baseline": FARM_POWER_BASELINE, } ) baseline_geometric_yaw = pd.DataFrame( { "wind_direction": WIND_DIRECTIONS, "wind_speed": WIND_SPEEDS, "turbulence_intensity": TURBULENCE_INTENSITIES, "yaw_angles_opt": [ [0.0, 0.0, 0.0], [0.0, 19.9952335557674, 19.9952335557674], [0.0, 0.0, 0.0], [19.9952335557674, 19.9952335557674, 0.0], ], "farm_power_opt": [5.261863e+06, 3.252509e+06, 5.261863e+06, 3.252509e+06], "farm_power_baseline": FARM_POWER_BASELINE, } ) baseline_scipy = pd.DataFrame( { "wind_direction": WIND_DIRECTIONS, "wind_speed": WIND_SPEEDS, "turbulence_intensity": TURBULENCE_INTENSITIES, "yaw_angles_opt": [ [0.0, 0.0, 0.0], [0.0, 24.999999999999982, 12.165643400939755], [0.0, 0.0, 0.0], [12.165643399558299, 25.0, 0.0], ], "farm_power_opt": [5.261863e+06, 3.264975e+06, 5.261863e+06, 3.264975e+06], "farm_power_baseline": FARM_POWER_BASELINE, } ) def test_serial_refine(sample_inputs_fixture): """ The Serial Refine (SR) method optimizes yaw angles based on a sequential, iterative yaw optimization scheme. This test compares the optimization results from the SR method for a simple farm with a simple wind rose to stored baseline results. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) wd_array = np.arange(0.0, 360.0, 90.0) ws_array = 8.0 * np.ones_like(wd_array) ti_array = 0.1 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array, ) yaw_opt = YawOptimizationSR(fmodel) df_opt = yaw_opt.optimize() if DEBUG: print(baseline_serial_refine.to_string()) print(df_opt.to_string()) pd.testing.assert_frame_equal(df_opt, baseline_serial_refine) def test_geometric_yaw(sample_inputs_fixture): """ The Geometric Yaw optimization method optimizes yaw angles using geometric data and derived optimal yaw relationships. This test compares the optimization results from the Geometric Yaw optimization for a simple farm with a simple wind rose to stored baseline results. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) wd_array = np.arange(0.0, 360.0, 90.0) ws_array = 8.0 * np.ones_like(wd_array) ti_array = 0.1 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array, ) fmodel.run() baseline_farm_power = fmodel.get_farm_power().squeeze() yaw_opt = YawOptimizationGeometric(fmodel) df_opt = yaw_opt.optimize() yaw_angles_opt_geo = np.vstack(yaw_opt.yaw_angles_opt) fmodel.set(yaw_angles=yaw_angles_opt_geo) fmodel.run() geo_farm_power = fmodel.get_farm_power().squeeze() df_opt['farm_power_baseline'] = baseline_farm_power df_opt['farm_power_opt'] = geo_farm_power if DEBUG: print(baseline_geometric_yaw.to_string()) print(df_opt.to_string()) pd.testing.assert_frame_equal(df_opt, baseline_geometric_yaw) def test_scipy_yaw_opt(sample_inputs_fixture): """ The SciPy optimization method optimizes yaw angles using SciPy's minimize method. This test compares the optimization results from the SciPy yaw optimization for a simple farm with a simple wind rose to stored baseline results. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL opt_options = { "maxiter": 5, "disp": True, "iprint": 2, "ftol": 1e-12, "eps": 0.5, } fmodel = FlorisModel(sample_inputs_fixture.core) wd_array = np.arange(0.0, 360.0, 90.0) ws_array = 8.0 * np.ones_like(wd_array) ti_array = 0.1 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array, ) yaw_opt = YawOptimizationScipy(fmodel, opt_options=opt_options) df_opt = yaw_opt.optimize() if DEBUG: print(baseline_scipy.to_string()) print(df_opt.to_string()) # Only require matching up until 2 decimal places pd.testing.assert_frame_equal(df_opt, baseline_scipy, check_exact=False, rtol=1e-5, atol=1e-2) ================================================ FILE: tests/rotor_velocity_unit_test.py ================================================ import numpy as np from floris.core import Turbine from floris.core.rotor_velocity import ( average_velocity, compute_tilt_angles_for_floating_turbines, compute_tilt_angles_for_floating_turbines_map, cubic_cubature, rotor_velocity_air_density_correction, rotor_velocity_tilt_cosine_correction, rotor_velocity_yaw_cosine_correction, simple_cubature, ) from tests.conftest import SampleInputs, WIND_SPEEDS def test_rotor_velocity_air_density_correction(): wind_speed = 10. ref_air_density = 1.225 test_density = 1.2 test_speed = rotor_velocity_air_density_correction(wind_speed, ref_air_density, ref_air_density) assert test_speed == wind_speed test_speed = rotor_velocity_air_density_correction(wind_speed, test_density, test_density) assert test_speed == wind_speed test_speed = rotor_velocity_air_density_correction(0., test_density, ref_air_density) assert test_speed == 0. test_speed = rotor_velocity_air_density_correction(wind_speed, test_density, ref_air_density) assert np.allclose((test_speed/wind_speed)**3, test_density/ref_air_density) def test_rotor_velocity_yaw_cosine_correction(): N_TURBINES = 4 wind_speed = average_velocity(10.0 * np.ones((1, 1, 3, 3))) wind_speed_N_TURBINES = average_velocity(10.0 * np.ones((1, N_TURBINES, 3, 3))) # Test a single turbine for zero yaw yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=0.0, rotor_effective_velocities=wind_speed, ) np.testing.assert_allclose(yaw_corrected_velocities, wind_speed) # Test a single turbine for non-zero yaw yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=60.0, rotor_effective_velocities=wind_speed, ) np.testing.assert_allclose(yaw_corrected_velocities, 0.5 * wind_speed) # Test multiple turbines for zero yaw yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=np.zeros((1, N_TURBINES)), rotor_effective_velocities=wind_speed_N_TURBINES, ) np.testing.assert_allclose(yaw_corrected_velocities, wind_speed_N_TURBINES) # Test multiple turbines for non-zero yaw yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=np.ones((1, N_TURBINES)) * 60.0, rotor_effective_velocities=wind_speed_N_TURBINES, ) np.testing.assert_allclose(yaw_corrected_velocities, 0.5 * wind_speed_N_TURBINES) def test_rotor_velocity_tilt_cosine_correction(): N_TURBINES = 4 wind_speed = average_velocity(10.0 * np.ones((1, 1, 3, 3))) wind_speed_N_TURBINES = average_velocity(10.0 * np.ones((1, N_TURBINES, 3, 3))) turbine_data = SampleInputs().turbine turbine_floating_data = SampleInputs().turbine_floating turbine = Turbine.from_dict(turbine_data) turbine_floating = Turbine.from_dict(turbine_floating_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] # Test single non-floating turbine tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map=np.array([turbine_type_map[:, 0]]), tilt_angles=5.0*np.ones((1, 1)), ref_tilt=np.array([turbine.power_thrust_table["ref_tilt"]]), cosine_loss_exponent_tilt=np.array( [turbine.power_thrust_table["cosine_loss_exponent_tilt"]] ), tilt_interp=turbine.tilt_interp, correct_cp_ct_for_tilt=np.array([[False]]), rotor_effective_velocities=wind_speed, ) np.testing.assert_allclose(tilt_corrected_velocities, wind_speed) # Test multiple non-floating turbines tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map=turbine_type_map, tilt_angles=5.0*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine.power_thrust_table["ref_tilt"]] * N_TURBINES), cosine_loss_exponent_tilt=np.array( [turbine.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES ), tilt_interp=turbine.tilt_interp, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), rotor_effective_velocities=wind_speed_N_TURBINES, ) np.testing.assert_allclose(tilt_corrected_velocities, wind_speed_N_TURBINES) # Test single floating turbine tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map=np.array([turbine_type_map[:, 0]]), tilt_angles=5.0*np.ones((1, 1)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]]), cosine_loss_exponent_tilt=np.array( [turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] ), tilt_interp=turbine_floating.tilt_interp, correct_cp_ct_for_tilt=np.array([[True]]), rotor_effective_velocities=wind_speed, ) np.testing.assert_allclose(tilt_corrected_velocities, wind_speed) # Test multiple floating turbines tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map, tilt_angles=5.0*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES), cosine_loss_exponent_tilt=np.array( [turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES ), tilt_interp=turbine_floating.tilt_interp, correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]), rotor_effective_velocities=wind_speed_N_TURBINES, ) np.testing.assert_allclose(tilt_corrected_velocities, wind_speed_N_TURBINES) ## Test angles that are not the same as the reference tilt # Test tilted "back" from reference tilt of 5 degrees tilt_angle = 10.0 # Greater than the reference tilt tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( tilt_angles=tilt_angle*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES), cosine_loss_exponent_tilt=np.array( [turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES ), tilt_interp=None, # Override wind-speed-based tilt interpolation correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]), rotor_effective_velocities=wind_speed_N_TURBINES, ) assert (tilt_corrected_velocities < wind_speed_N_TURBINES).all() # Test tilted "forward" from reference tilt of 5 degrees tilt_angle = 0.0 # Less than the reference tilt tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( tilt_angles=tilt_angle*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES), cosine_loss_exponent_tilt=np.array( [turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES ), tilt_interp=None, # Override wind-speed-based tilt interpolation correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]), rotor_effective_velocities=wind_speed_N_TURBINES, ) assert (tilt_corrected_velocities > wind_speed_N_TURBINES).all() # Test symmetry around zero tilt tilt_angle = 3.0 tilt_negative = rotor_velocity_tilt_cosine_correction( tilt_angles=-tilt_angle*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES), cosine_loss_exponent_tilt=np.array( [turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES ), tilt_interp=None, # Override wind-speed-based tilt interpolation correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]), rotor_effective_velocities=wind_speed_N_TURBINES, ) tilt_positive = rotor_velocity_tilt_cosine_correction( tilt_angles=tilt_angle*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES), cosine_loss_exponent_tilt=np.array( [turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES ), tilt_interp=None, # Override wind-speed-based tilt interpolation correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]), rotor_effective_velocities=wind_speed_N_TURBINES, ) np.testing.assert_allclose(tilt_negative, tilt_positive) def test_compute_tilt_angles_for_floating_turbines(): N_TURBINES = 4 wind_speed = 25.0 rotor_effective_velocities = average_velocity(wind_speed * np.ones((1, 1, 3, 3))) rotor_effective_velocities_N_TURBINES = average_velocity( wind_speed * np.ones((1, N_TURBINES, 3, 3)) ) turbine_floating_data = SampleInputs().turbine_floating turbine_floating = Turbine.from_dict(turbine_floating_data) turbine_type_map = np.array(N_TURBINES * [turbine_floating.turbine_type]) turbine_type_map = turbine_type_map[None, :] # Single turbine tilt = compute_tilt_angles_for_floating_turbines( #turbine_type_map=np.array([turbine_type_map[:, 0]]), tilt_angles=5.0*np.ones((1, 1)), tilt_interp=turbine_floating.tilt_interp, rotor_effective_velocities=rotor_effective_velocities, ) # calculate tilt again truth_index = turbine_floating_data["floating_tilt_table"]["wind_speed"].index(wind_speed) tilt_truth = turbine_floating_data["floating_tilt_table"]["tilt"][truth_index] np.testing.assert_allclose(tilt, tilt_truth) # Multiple turbines tilt_N_turbines = compute_tilt_angles_for_floating_turbines_map( turbine_type_map=np.array(turbine_type_map), tilt_angles=5.0*np.ones((1, N_TURBINES)), tilt_interps={turbine_floating.turbine_type: turbine_floating.tilt_interp}, rotor_effective_velocities=rotor_effective_velocities_N_TURBINES, ) # calculate tilt again truth_index = turbine_floating_data["floating_tilt_table"]["wind_speed"].index(wind_speed) tilt_truth = turbine_floating_data["floating_tilt_table"]["tilt"][truth_index] np.testing.assert_allclose(tilt_N_turbines, [[tilt_truth] * N_TURBINES]) def test_simple_cubature(): # Define a velocity array velocities = np.ones((1, 1, 3, 3)) # Define sample cubature weights cubature_weights = np.array([1., 1., 1.]) # Define the axis as last 2 dimensions axis = (velocities.ndim-2, velocities.ndim-1) # Calculate expected output based on the given inputs expected_output = 1.0 # Call the function with the given inputs result = simple_cubature(velocities, cubature_weights, axis) # Check if the result matches the expected output np.testing.assert_allclose(result, expected_output) def test_cubic_cubature(): # Define a velocity array velocities = np.ones((1, 1, 3, 3)) # Define sample cubature weights cubature_weights = np.array([1., 1., 1.]) # Define the axis as last 2 dimensions axis = (velocities.ndim-2, velocities.ndim-1) # Calculate expected output based on the given inputs expected_output = 1.0 # Call the function with the given inputs result = cubic_cubature(velocities, cubature_weights, axis) # Check if the result matches the expected output np.testing.assert_allclose(result, expected_output) ================================================ FILE: tests/serial_refine_unit_test.py ================================================ import numpy as np import pandas as pd from floris import FlorisModel from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" # Inputs for basic yaw optimizations WIND_DIRECTIONS = [0.0, 90.0, 180.0, 270.0] WIND_SPEEDS = [8.0] * 4 TURBULENCE_INTENSITIES = [0.06] * 4 LAYOUT_X = [0.0, 600.0, 1200.0] LAYOUT_Y = [0.0, 0.0, 0.0] MAXIMUM_YAW_ANGLE = 25.0 def test_basic_optimization(sample_inputs_fixture): """ The Serial Refine (SR) method optimizes yaw angles based on a sequential, iterative yaw optimization scheme. This test checks basic properties of the optimization result. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) fmodel.set( layout_x=LAYOUT_X, layout_y=LAYOUT_Y, wind_directions=WIND_DIRECTIONS, wind_speeds=WIND_SPEEDS, turbulence_intensities=TURBULENCE_INTENSITIES ) fmodel.set_operation_model("cosine-loss") yaw_opt = YawOptimizationSR(fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=MAXIMUM_YAW_ANGLE) df_opt = yaw_opt.optimize() # Unaligned conditions assert np.allclose(df_opt.loc[0, "yaw_angles_opt"], 0.0) assert np.allclose(df_opt.loc[2, "yaw_angles_opt"], 0.0) # Check aligned conditions # Check maximum and minimum are respected assert (df_opt.loc[1, "yaw_angles_opt"] <= MAXIMUM_YAW_ANGLE).all() assert (df_opt.loc[3, "yaw_angles_opt"] <= MAXIMUM_YAW_ANGLE).all() assert (df_opt.loc[1, "yaw_angles_opt"] >= 0.0).all() assert (df_opt.loc[3, "yaw_angles_opt"] >= 0.0).all() # Check 90.0 and 270.0 are symmetric assert np.allclose(df_opt.loc[1, "yaw_angles_opt"], np.flip(df_opt.loc[3, "yaw_angles_opt"])) # Check last turbine's angles are zero at 270.0 assert np.allclose(df_opt.loc[3, "yaw_angles_opt"][-1], 0.0) # Check that optimizer reports a power improvement assert (df_opt["farm_power_opt"] >= df_opt["farm_power_baseline"]).all() def test_disabled_turbines(sample_inputs_fixture): """ Tests SR when some turbines are disabled and checks that the results are equivalent to removing those turbines from the wind farm. Need a tight layout to ensure that the front-to-back distance is not too large. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) fmodel.set( layout_x=LAYOUT_X, layout_y=LAYOUT_Y, wind_directions=WIND_DIRECTIONS, wind_speeds=WIND_SPEEDS, turbulence_intensities=TURBULENCE_INTENSITIES ) fmodel.set_operation_model("mixed") # Disable the middle turbine in all wind conditions, run optimization, and extract results fmodel.set(disable_turbines=[[False, True, False]]*4) yaw_opt = YawOptimizationSR(fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=MAXIMUM_YAW_ANGLE) df_opt = yaw_opt.optimize() yaw_angles_opt_disabled = df_opt.loc[3, "yaw_angles_opt"] farm_power_opt_disabled = df_opt.loc[3, "farm_power_opt"] # Set up a new wind farm with the middle turbine removed fmodel = FlorisModel(sample_inputs_fixture.core) fmodel.set( layout_x=np.array(LAYOUT_X)[[0, 2]], layout_y=np.array(LAYOUT_Y)[[0, 2]], wind_directions=WIND_DIRECTIONS, wind_speeds=WIND_SPEEDS, turbulence_intensities=TURBULENCE_INTENSITIES ) fmodel.set_operation_model("cosine-loss") yaw_opt = YawOptimizationSR(fmodel, minimum_yaw_angle=0.0, maximum_yaw_angle=MAXIMUM_YAW_ANGLE) df_opt = yaw_opt.optimize() yaw_angles_opt_removed = df_opt.loc[3, "yaw_angles_opt"] farm_power_opt_removed = df_opt.loc[3, "farm_power_opt"] assert np.allclose(yaw_angles_opt_disabled[[0, 2]], yaw_angles_opt_removed) assert np.allclose(farm_power_opt_disabled, farm_power_opt_removed) ================================================ FILE: tests/turbine_grid_unit_test.py ================================================ import numpy as np from floris.core import TurbineGrid from tests.conftest import ( N_FINDEX, N_TURBINES, TURBINE_GRID_RESOLUTION, ) # def test_from_dict_as_dict(turbine_grid_fixture): # grid_dict = turbine_grid_fixture.as_dict() # new_grid = TurbineGrid.from_dict(grid_dict) # assert new_grid == turbine_grid_fixture def test_set_grid(turbine_grid_fixture): expected_x_grid = [ [[0.0, 0.0], [0.0, 0.0]], [[630.0, 630.0], [630.0, 630.0]], [[1260.0, 1260.0], [1260.0, 1260.0]] ] expected_y_grid = [ [[-31.5, -31.5], [31.5, 31.5]], [[-31.5, -31.5], [31.5, 31.5]], [[-31.5, -31.5], [31.5, 31.5]] ] expected_z_grid = [ [[58.5, 121.5], [58.5, 121.5]], [[58.5, 121.5], [58.5, 121.5]], [[58.5, 121.5], [58.5, 121.5]] ] # subtract the test and expected values which should result in 0's # then, search for any elements that are true and negate the results # if an element is zero, the not will return true # if an element is non-zero, the not will return false np.testing.assert_array_equal(turbine_grid_fixture.x_sorted[0], expected_x_grid) np.testing.assert_array_equal(turbine_grid_fixture.y_sorted[0], expected_y_grid) np.testing.assert_array_equal(turbine_grid_fixture.z_sorted[0], expected_z_grid) # These should have the following shape: # (n findex, n turbines, grid resolution, grid resolution) expected_shape = (N_FINDEX,N_TURBINES,TURBINE_GRID_RESOLUTION,TURBINE_GRID_RESOLUTION) assert np.shape(turbine_grid_fixture.x_sorted) == expected_shape assert np.shape(turbine_grid_fixture.y_sorted) == expected_shape assert np.shape(turbine_grid_fixture.z_sorted) == expected_shape assert np.shape(turbine_grid_fixture.x_sorted_inertial_frame) == expected_shape assert np.shape(turbine_grid_fixture.y_sorted_inertial_frame) == expected_shape assert np.shape(turbine_grid_fixture.z_sorted_inertial_frame) == expected_shape def test_dimensions(turbine_grid_fixture): assert np.shape(turbine_grid_fixture.x_sorted) == ( N_FINDEX, N_TURBINES, TURBINE_GRID_RESOLUTION, TURBINE_GRID_RESOLUTION ) assert np.shape(turbine_grid_fixture.y_sorted) == ( N_FINDEX, N_TURBINES, TURBINE_GRID_RESOLUTION, TURBINE_GRID_RESOLUTION ) assert np.shape(turbine_grid_fixture.z_sorted) == ( N_FINDEX, N_TURBINES, TURBINE_GRID_RESOLUTION, TURBINE_GRID_RESOLUTION ) def test_dynamic_properties(turbine_grid_fixture): assert turbine_grid_fixture.n_turbines == N_TURBINES assert turbine_grid_fixture.n_findex == N_FINDEX turbine_grid_fixture.turbine_coordinates = np.append( turbine_grid_fixture.turbine_coordinates, np.array([[100.0, 200.0, 300.0]]), axis=0 ) assert turbine_grid_fixture.n_turbines == N_TURBINES + 1 turbine_grid_fixture.wind_directions = [*turbine_grid_fixture.wind_directions, 0.0] assert turbine_grid_fixture.n_findex == N_FINDEX + 1 ================================================ FILE: tests/turbine_multi_dim_unit_test.py ================================================ from pathlib import Path import numpy as np import pandas as pd import pytest from floris.core import ( Turbine, ) from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.core.turbine.turbine import ( axial_induction, power, thrust_coefficient, ) from tests.conftest import SampleInputs, WIND_SPEEDS # size 16 x 1 x 1 x 1 # 16 wind speed and wind direction combinations from conftest WIND_CONDITION_BROADCAST = np.reshape(np.array(WIND_SPEEDS), (-1, 1, 1, 1)) INDEX_FILTER = [0, 2] # NOTE: MultiDimensionalPowerThrustTable not used anywhere, so I'm commenting # this out. # def test_multi_dimensional_power_thrust_table(): # turbine_data = SampleInputs().turbine_multi_dim # turbine_data["power_thrust_data_file"] = CSV_INPUT # df_data = pd.read_csv(turbine_data["power_thrust_data_file"]) # flattened_dict = MultiDimensionalPowerThrustTable.from_dataframe(df_data) # flattened_dict_base = { # ('Tp', '2', 'Hs', '1'): [], # ('Tp', '2', 'Hs', '5'): [], # ('Tp', '4', 'Hs', '1'): [], # ('Tp', '4', 'Hs', '5'): [], # } # assert flattened_dict == flattened_dict_base # # Test for initialization errors # for el in ("ws", "Cp", "Ct"): # df_data = pd.read_csv(turbine_data["power_thrust_data_file"]) # df = df_data.drop(el, axis=1) # with pytest.raises(ValueError): # MultiDimensionalPowerThrustTable.from_dataframe(df) def test_turbine_init(): turbine_data = SampleInputs().turbine_multi_dim turbine = Turbine.from_dict(turbine_data) condition_tuple = (2, 1) assert turbine.rotor_diameter == turbine_data["rotor_diameter"] assert turbine.hub_height == turbine_data["hub_height"] assert ( turbine.power_thrust_table[condition_tuple]["cosine_loss_exponent_yaw"] == turbine_data["power_thrust_table"]["cosine_loss_exponent_yaw"] ) assert ( turbine.power_thrust_table[condition_tuple]["cosine_loss_exponent_tilt"] == turbine_data["power_thrust_table"]["cosine_loss_exponent_tilt"] ) assert isinstance(turbine.power_thrust_table, dict) assert callable(turbine.thrust_coefficient_function) assert callable(turbine.power_function) assert turbine.rotor_radius == turbine_data["rotor_diameter"] / 2.0 def test_ct(): N_TURBINES = 4 turbine_data = SampleInputs().turbine_multi_dim turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] condition = {"Tp":2, "Hs":1} # Single turbine # yaw angle / fCt are (n wind direction, n wind speed, n turbine) wind_speed = 10.0 thrust = thrust_coefficient( velocities=wind_speed * np.ones((1, 1, 3, 3)), turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT,\ awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, 1)), thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=condition ) np.testing.assert_allclose(thrust, np.array([[0.77958497]])) # Multiple turbines with index filter # 4 turbines with 3 x 3 grid arrays thrusts = thrust_coefficient( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 16 x 4 x 3 x 3 turbulence_intensities=( 0.06 * np.ones((N_TURBINES, 3, 3)) * np.ones_like(WIND_CONDITION_BROADCAST) ), air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, N_TURBINES)), thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), turbine_type_map=turbine_type_map, ix_filter=INDEX_FILTER, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=condition ) assert len(thrusts[0]) == len(INDEX_FILTER) thrusts_truth = np.array( [ [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.66749069, 0.66749069], [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.66749069, 0.66749069], [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.66749069, 0.66749069], [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.77958497, 0.77958497], [0.66749069, 0.66749069] ] ) np.testing.assert_allclose(thrusts, thrusts_truth) def test_power(): N_TURBINES = 4 AIR_DENSITY = 1.225 turbine_data = SampleInputs().turbine_multi_dim turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] condition = {"Tp":2, "Hs":1} condition_tuple = tuple(condition[k] for k in condition.keys()) # Single turbine wind_speed = 10.0 p = power( velocities=wind_speed * np.ones((1, 1, 3, 3)), turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=AIR_DENSITY, power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine tilt_angles=turbine.power_thrust_table[condition_tuple]["ref_tilt"] * np.ones((1, 1)), power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, 1)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=condition, ) power_truth = 12424759.67683091 np.testing.assert_allclose(p, power_truth) # Multiple turbines with ix filter velocities = np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST p = power( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 16 x 4 x 3 x 3 turbulence_intensities=( 0.06 * np.ones((N_TURBINES, 3, 3)) * np.ones_like(WIND_CONDITION_BROADCAST) ), air_density=AIR_DENSITY, power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, N_TURBINES)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, ix_filter=INDEX_FILTER, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=condition ) assert len(p[0]) == len(INDEX_FILTER) power_truth = turbine.power_function( power_thrust_table=turbine.power_thrust_table[condition_tuple], velocities=velocities, air_density=AIR_DENSITY, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, tilt_interp=turbine.tilt_interp, ) np.testing.assert_allclose(p, power_truth[:, INDEX_FILTER[0]:INDEX_FILTER[1]]) def test_axial_induction(): N_TURBINES = 4 turbine_data = SampleInputs().turbine_multi_dim turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] condition = {"Tp":2, "Hs":1} baseline_ai = np.array([[0.26551081]]) # Single turbine wind_speed = 10.0 ai = axial_induction( velocities=wind_speed * np.ones((1, 1, 3, 3)), turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, power_setpoints = np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, 1)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map[0,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=condition ) np.testing.assert_allclose(ai, baseline_ai) # Multiple turbines with ix filter ai = axial_induction( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 16 x 4 x 3 x 3 turbulence_intensities=( 0.06 * np.ones((N_TURBINES, 3, 3)) * np.ones_like(WIND_CONDITION_BROADCAST) ), air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, N_TURBINES)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), turbine_type_map=turbine_type_map, ix_filter=INDEX_FILTER, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=condition ) assert len(ai[0]) == len(INDEX_FILTER) # Test the 10 m/s wind speed to use the same baseline as above np.testing.assert_allclose(ai[2][0], baseline_ai) def test_asdict(sample_inputs_fixture: SampleInputs): turbine = Turbine.from_dict(sample_inputs_fixture.turbine) dict1 = turbine.as_dict() new_turb = Turbine.from_dict(dict1) dict2 = new_turb.as_dict() assert dict1 == dict2 def test_multiple_conditions(): N_TURBINES = 4 N_CONDITIONS = 2 turbine_data = SampleInputs().turbine_multi_dim turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] # First, test the same condition repeated conditions = {"Tp":[2, 2], "Hs":[1, 1]} # Single turbine wind_speed = 10.0 thrust = thrust_coefficient( velocities=wind_speed * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=None, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=np.ones((N_CONDITIONS, N_TURBINES)) * 5.0, power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions ) assert np.allclose(thrust, 0.77958497) ai = axial_induction( velocities=wind_speed * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=None, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=np.ones((N_CONDITIONS, N_TURBINES)) * 5.0, power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions ) assert np.allclose(ai, 0.26551081) p = power( velocities=wind_speed * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=1.225, power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=turbine.power_thrust_table[(2,1)]["ref_tilt"] * np.ones( (N_CONDITIONS, N_TURBINES) ), # Same ref_tilt on all power_thrust_tables power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions, correct_cp_ct_for_tilt=np.zeros((N_CONDITIONS, N_TURBINES), dtype=bool) ) assert np.allclose(p, 12424759.67683091) # Next, test different conditions (one which must be inferred) conditions = {"Tp":[2, 4], "Hs":[1, 4]} thrust = thrust_coefficient( velocities=wind_speed * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=None, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=np.ones((N_CONDITIONS, N_TURBINES)) * 5.0, power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions ) assert np.allclose(thrust, np.array([[0.77958497], [0.09744812]])) ai = axial_induction( velocities=wind_speed * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=None, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=np.ones((N_CONDITIONS, N_TURBINES)) * 5.0, power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions ) assert np.allclose(ai, np.array([[0.26551081], [0.02498745]])) p = power( velocities=wind_speed * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=1.225, power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=turbine.power_thrust_table[(2,1)]["ref_tilt"] * np.ones( (N_CONDITIONS, N_TURBINES) ), # Same ref_tilt on all power_thrust_tables power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions, ) assert np.allclose(p, np.array([[12424759.67683091], [ 1553094.95985386]])) # Multiple findices with broadcast multidim conditions wind_speeds = np.array([10., 11.]) conditions = {"Tp":2, "Hs":1} thrust = thrust_coefficient( velocities=np.tile(wind_speeds[:,None,None,None], (1, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=None, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=np.ones((N_CONDITIONS, N_TURBINES)) * 5.0, power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions ) assert np.allclose(thrust, np.array([[0.77958497], [0.66749069]])) ai = axial_induction( velocities=np.tile(wind_speeds[:,None,None,None], (1, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=None, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=np.ones((N_CONDITIONS, N_TURBINES)) * 5.0, power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions ) assert np.allclose(ai, np.array([[0.26551081], [0.2118128]])) p = power( velocities=np.tile(wind_speeds[:,None,None,None], (1, N_TURBINES, 3, 3)), turbulence_intensities=0.06 * np.ones((N_CONDITIONS, N_TURBINES, 3, 3)), air_density=1.225, power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_angles=turbine.power_thrust_table[(2,1)]["ref_tilt"] * np.ones( (N_CONDITIONS, N_TURBINES) ), # Same ref_tilt on all power_thrust_tables power_setpoints=np.ones((N_CONDITIONS, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*N_CONDITIONS), awc_amplitudes=np.zeros((N_CONDITIONS, N_TURBINES)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, multidim_condition=conditions, ) assert np.allclose(p, np.array([[12424759.67683091], [15000000.0]])) ================================================ FILE: tests/turbine_operation_models_unit_test.py ================================================ import numpy as np import pytest from floris.core.turbine.operation_models import ( AWCTurbine, CosineLossTurbine, MixedOperationTurbine, PeakShavingTurbine, POWER_SETPOINT_DEFAULT, SimpleDeratingTurbine, SimpleTurbine, ) from floris.utilities import cosd from tests.conftest import SampleInputs, WIND_SPEEDS def test_submodel_attributes(): assert hasattr(SimpleTurbine, "power") assert hasattr(SimpleTurbine, "thrust_coefficient") assert hasattr(SimpleTurbine, "axial_induction") assert hasattr(CosineLossTurbine, "power") assert hasattr(CosineLossTurbine, "thrust_coefficient") assert hasattr(CosineLossTurbine, "axial_induction") assert hasattr(SimpleDeratingTurbine, "power") assert hasattr(SimpleDeratingTurbine, "thrust_coefficient") assert hasattr(SimpleDeratingTurbine, "axial_induction") assert hasattr(MixedOperationTurbine, "power") assert hasattr(MixedOperationTurbine, "thrust_coefficient") assert hasattr(MixedOperationTurbine, "axial_induction") assert hasattr(AWCTurbine, "power") assert hasattr(AWCTurbine, "thrust_coefficient") assert hasattr(AWCTurbine, "axial_induction") assert hasattr(PeakShavingTurbine, "power") assert hasattr(PeakShavingTurbine, "thrust_coefficient") assert hasattr(PeakShavingTurbine, "axial_induction") def test_SimpleTurbine(): n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine # Check that power works as expected test_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], # Matches ref_air_density ) truth_index = turbine_data["power_thrust_table"]["wind_speed"].index(wind_speed) baseline_power = turbine_data["power_thrust_table"]["power"][truth_index] * 1000 assert np.allclose(baseline_power, test_power) # Check that yaw and tilt angle have no effect test_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], # Matches ref_air_density yaw_angles=20 * np.ones((1, n_turbines)), tilt_angles=5 * np.ones((1, n_turbines)) ) assert np.allclose(baseline_power, test_power) # Check that a lower air density decreases power appropriately test_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, ) assert test_power < baseline_power # Check that thrust coefficient works as expected test_Ct = SimpleTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused ) baseline_Ct = turbine_data["power_thrust_table"]["thrust_coefficient"][truth_index] assert np.allclose(baseline_Ct, test_Ct) # Check that yaw and tilt angle have no effect test_Ct = SimpleTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused yaw_angles=20 * np.ones((1, n_turbines)), tilt_angles=5 * np.ones((1, n_turbines)) ) assert np.allclose(baseline_Ct, test_Ct) # Check that axial induction works as expected test_ai = SimpleTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused ) baseline_ai = ( 1 - np.sqrt(1 - turbine_data["power_thrust_table"]["thrust_coefficient"][truth_index]) )/2 assert np.allclose(baseline_ai, test_ai) # Check that yaw and tilt angle have no effect test_ai = SimpleTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused yaw_angles=20 * np.ones((1, n_turbines)), tilt_angles=5 * np.ones((1, n_turbines)) ) assert np.allclose(baseline_ai, test_ai) def test_CosineLossTurbine(): n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine yaw_angles_nom = 0 * np.ones((1, n_turbines)) tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((1, n_turbines)) yaw_angles_test = 20 * np.ones((1, n_turbines)) tilt_angles_test = 0 * np.ones((1, n_turbines)) # Check that power works as expected test_power = CosineLossTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], # Matches ref_air_density yaw_angles=yaw_angles_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ) truth_index = turbine_data["power_thrust_table"]["wind_speed"].index(wind_speed) baseline_power = turbine_data["power_thrust_table"]["power"][truth_index] * 1000 assert np.allclose(baseline_power, test_power) # Check that yaw and tilt angle have an effect test_power = CosineLossTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], # Matches ref_air_density yaw_angles=yaw_angles_test, tilt_angles=tilt_angles_test, tilt_interp=None ) assert test_power < baseline_power # Check that a lower air density decreases power appropriately test_power = CosineLossTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, yaw_angles=yaw_angles_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ) assert test_power < baseline_power # Check that thrust coefficient works as expected test_Ct = CosineLossTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused yaw_angles=yaw_angles_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ) baseline_Ct = turbine_data["power_thrust_table"]["thrust_coefficient"][truth_index] assert np.allclose(baseline_Ct, test_Ct) # Check that yaw and tilt angle have the expected effect test_Ct = CosineLossTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused yaw_angles=yaw_angles_test, tilt_angles=tilt_angles_test, tilt_interp=None ) assert test_Ct == ( baseline_Ct * cosd(yaw_angles_test) * cosd(tilt_angles_test) / cosd(turbine_data["power_thrust_table"]["ref_tilt"]) ) # Check that thrust coefficient works as expected test_ai = CosineLossTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused yaw_angles=yaw_angles_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ) baseline_misalignment_loss = ( cosd(yaw_angles_nom) * cosd(tilt_angles_nom) / cosd(turbine_data["power_thrust_table"]["ref_tilt"]) ) baseline_ai = ( 1 - np.sqrt(1 - turbine_data["power_thrust_table"]["thrust_coefficient"][truth_index]) ) / 2 / baseline_misalignment_loss assert np.allclose(baseline_ai, test_ai) # Check that yaw and tilt angle have the expected effect test_ai = CosineLossTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, # Unused yaw_angles=yaw_angles_test, tilt_angles=tilt_angles_test, tilt_interp=None ) assert test_Ct == ( baseline_Ct * cosd(yaw_angles_test) * cosd(tilt_angles_test) / cosd(turbine_data["power_thrust_table"]["ref_tilt"]) ) def test_SimpleDeratingTurbine(): n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine # Check that for no specified derating, matches SimpleTurbine test_Ct = SimpleDeratingTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=None, ) base_Ct = SimpleTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) assert np.allclose(test_Ct, base_Ct) test_power = SimpleDeratingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=None, ) base_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], ) assert np.allclose(test_power, base_power) test_ai = SimpleDeratingTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=None, ) base_ai = SimpleTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) assert np.allclose(test_ai, base_ai) # When power_setpoints are 0, turbine is shut down. test_Ct = SimpleDeratingTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.zeros((1, n_turbines)), ) assert np.allclose(test_Ct, 0) test_power = SimpleDeratingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.zeros((1, n_turbines)), ) assert np.allclose(test_power, 0) test_ai = SimpleDeratingTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.zeros((1, n_turbines)), ) assert np.allclose(test_ai, 0) # When power setpoints are less than available, results should be less than when no setpoint wind_speed = 20 # High, so that turbine is above rated nominally derated_power = 4.0e6 rated_power = 5.0e6 test_power = SimpleDeratingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=derated_power * np.ones((1, n_turbines)), ) rated_power = 5.0e6 test_Ct = SimpleDeratingTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=derated_power * np.ones((1, n_turbines)), ) base_Ct = SimpleTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=derated_power * np.ones((1, n_turbines)), ) assert np.allclose(test_Ct, derated_power/rated_power * base_Ct) # Is this correct? # Mixed below and above rated n_turbines = 2 wind_speeds_test = np.ones((1, n_turbines, 3, 3)) wind_speeds_test[0,0,:,:] = 20.0 # Above rated wind_speeds_test[0,1,:,:] = 5.0 # Well below eated test_power = SimpleDeratingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds_test, # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=derated_power * np.ones((1, n_turbines)), ) base_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds_test, # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=derated_power * np.ones((1, n_turbines)), ) assert test_power[0,0] < base_power[0,0] assert test_power[0,0] == derated_power assert test_power[0,1] == base_power[0,1] assert test_power[0,1] < derated_power def test_MixedOperationTurbine(): n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((1, n_turbines)) # Check that for no specified derating or yaw angle, matches SimpleTurbine test_Ct = MixedOperationTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((1, n_turbines)), yaw_angles=np.zeros((1, n_turbines)), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_Ct = SimpleTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) assert np.allclose(test_Ct, base_Ct) test_power = MixedOperationTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((1, n_turbines)), yaw_angles=np.zeros((1, n_turbines)), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], ) assert np.allclose(test_power, base_power) test_ai = MixedOperationTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((1, n_turbines)), yaw_angles=np.zeros((1, n_turbines)), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_ai = SimpleTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) assert np.allclose(test_ai, base_ai) # Check that when power_setpoints are set, matches SimpleDeratingTurbine, # while when yaw angles are set, matches CosineLossTurbine n_turbines = 2 derated_power = 2.0e6 test_Ct = MixedOperationTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), yaw_angles=np.array([[20.0, 0.0]]), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_Ct_dr = SimpleDeratingTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), ) base_Ct_yaw = CosineLossTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid yaw_angles=np.array([[20.0, 0.0]]), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_Ct = np.array([[base_Ct_yaw[0,0], base_Ct_dr[0,1]]]) assert np.allclose(test_Ct, base_Ct) # Do the same as above for power() test_power = MixedOperationTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), yaw_angles=np.array([[20.0, 0.0]]), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_power_dr = SimpleDeratingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), ) base_power_yaw = CosineLossTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], yaw_angles=np.array([[20.0, 0.0]]), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_power = np.array([[base_power_yaw[0,0], base_power_dr[0,1]]]) assert np.allclose(test_power, base_power) # Finally, check axial induction test_ai = MixedOperationTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), yaw_angles=np.array([[20.0, 0.0]]), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_ai_dr = SimpleDeratingTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), ) base_ai_yaw = CosineLossTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], yaw_angles=np.array([[20.0, 0.0]]), tilt_angles=tilt_angles_nom, tilt_interp=None ) base_ai = np.array([[base_ai_yaw[0,0], base_ai_dr[0,1]]]) assert np.allclose(test_ai, base_ai) # Check error raised when both yaw and power setpoints are set with pytest.raises(ValueError): # Second turbine has both a power setpoint and a yaw angle MixedOperationTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), yaw_angles=np.array([[0.0, 20.0]]), tilt_angles=tilt_angles_nom, tilt_interp=None ) def test_AWCTurbine(): n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine # Baseline base_Ct = SimpleTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) base_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], ) base_ai = SimpleTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) # Test no change to Ct, power, or ai when helix amplitudes are 0 test_Ct = AWCTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid awc_modes=np.array([["helix"]*n_turbines]*1), awc_amplitudes=np.zeros((1, n_turbines)), ) assert np.allclose(test_Ct, base_Ct) test_power = AWCTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], awc_modes=np.array([["helix"]*n_turbines]*1), awc_amplitudes=np.zeros((1, n_turbines)), ) assert np.allclose(test_power, base_power) test_ai = AWCTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid awc_modes=np.array([["helix"]*n_turbines]*1), awc_amplitudes=np.zeros((1, n_turbines)), ) assert np.allclose(test_ai, base_ai) # Test that Ct, power, and ai all decrease when helix amplitudes are non-zero test_Ct = AWCTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid awc_modes=np.array([["helix"]*n_turbines]*1), awc_amplitudes=2*np.ones((1, n_turbines)), ) assert test_Ct < base_Ct assert test_Ct > 0 test_power = AWCTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], awc_modes=np.array([["helix"]*n_turbines]*1), awc_amplitudes=2*np.ones((1, n_turbines)), ) assert test_power < base_power assert test_power > 0 test_ai = AWCTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid awc_modes=np.array([["helix"]*n_turbines]*1), awc_amplitudes=2*np.ones((1, n_turbines)), ) assert test_ai < base_ai assert test_ai > 0 # Test a mixture of "baseline" and "helix" modes n_turbines = 3 awc_modes_test = np.array( [ ["baseline", "baseline", "baseline"], ["baseline", "helix", "helix"] ] ) test_power = AWCTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((2, n_turbines, 3, 3)), # 2 findices, 3 turbines, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], awc_modes=awc_modes_test, awc_amplitudes=2*np.ones((2, n_turbines)), ) assert np.allclose(test_power[0,:], base_power) assert np.allclose(test_power[1,0], base_power) assert (test_power[1,1:] < base_power).all() test_Ct = AWCTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((2, n_turbines, 3, 3)), # 2 findices, 3 turbines, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], awc_modes=awc_modes_test, awc_amplitudes=2*np.ones((2, n_turbines)), ) assert np.allclose(test_Ct[0,:], base_Ct) assert np.allclose(test_Ct[1,0], base_Ct) assert (test_Ct[1,1:] < base_Ct).all() def test_PeakShavingTurbine(): n_turbines = 1 wind_speed = 10.0 turbulence_intensity_low = 0.05 turbulence_intensity_high = 0.2 turbine_data = SampleInputs().turbine # Baseline base_Ct = SimpleTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) base_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], ) base_ai = SimpleTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid ) # Test no change to Ct, power, or ai when below TI threshold test_Ct = PeakShavingTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid turbulence_intensities=turbulence_intensity_low * np.ones((1, n_turbines, 3, 3)), ) assert np.allclose(test_Ct, base_Ct) test_power = PeakShavingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], turbulence_intensities=turbulence_intensity_low * np.ones((1, n_turbines, 3, 3)), ) assert np.allclose(test_power, base_power) test_ai = PeakShavingTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid turbulence_intensities=turbulence_intensity_low * np.ones((1, n_turbines, 3, 3)), ) assert np.allclose(test_ai, base_ai) # Test that Ct, power, and ai all decrease when above TI threshold test_Ct = PeakShavingTurbine.thrust_coefficient( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid turbulence_intensities=turbulence_intensity_high * np.ones((1, n_turbines, 3, 3)), ) assert test_Ct < base_Ct assert test_Ct > 0 test_power = PeakShavingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid turbulence_intensities=turbulence_intensity_high * np.ones((1, n_turbines, 3, 3)), air_density=turbine_data["power_thrust_table"]["ref_air_density"], ) assert test_power < base_power assert test_power > 0 test_ai = PeakShavingTurbine.axial_induction( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid turbulence_intensities=turbulence_intensity_high * np.ones((1, n_turbines, 3, 3)), ) assert test_ai < base_ai assert test_ai > 0 # Test that, for an array of wind speeds, only wind speeds near rated are affected wind_speeds = np.linspace(1, 20, 10) turbulence_intensities = turbulence_intensity_high * np.ones_like(wind_speeds) base_power = SimpleTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds[:, None, None, None], air_density=turbine_data["power_thrust_table"]["ref_air_density"], ) test_power = PeakShavingTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speeds[:, None, None, None], turbulence_intensities=turbulence_intensities[:, None, None, None], air_density=turbine_data["power_thrust_table"]["ref_air_density"], ) assert (test_power <= base_power).all() assert test_power[0,0] == base_power[0,0] assert test_power[-1,0] == base_power[-1,0] ================================================ FILE: tests/turbine_unit_test.py ================================================ import os from pathlib import Path import attr import numpy as np import pytest import yaml from floris.core import ( average_velocity, axial_induction, power, thrust_coefficient, Turbine, ) from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT from tests.conftest import SampleInputs, WIND_SPEEDS # size 16 x 1 x 1 x 1 # 16 wind speed and wind direction combinations from conftest WIND_CONDITION_BROADCAST = np.reshape(np.array(WIND_SPEEDS), (-1, 1, 1, 1)) INDEX_FILTER = [0, 2] def test_turbine_init(): turbine_data = SampleInputs().turbine turbine = Turbine.from_dict(turbine_data) assert turbine.turbine_type == turbine_data["turbine_type"] assert turbine.rotor_diameter == turbine_data["rotor_diameter"] assert turbine.hub_height == turbine_data["hub_height"] assert ( turbine.power_thrust_table["cosine_loss_exponent_yaw"] == turbine_data["power_thrust_table"]["cosine_loss_exponent_yaw"] ) assert ( turbine.power_thrust_table["cosine_loss_exponent_tilt"] == turbine_data["power_thrust_table"]["cosine_loss_exponent_tilt"] ) assert turbine.TSR == turbine_data["TSR"] assert ( turbine.power_thrust_table["ref_air_density"] == turbine_data["power_thrust_table"]["ref_air_density"] ) assert turbine.power_thrust_table["ref_tilt"] == turbine_data["power_thrust_table"]["ref_tilt"] assert np.array_equal( turbine.power_thrust_table["wind_speed"], turbine_data["power_thrust_table"]["wind_speed"] ) assert np.array_equal( turbine.power_thrust_table["power"], turbine_data["power_thrust_table"]["power"] ) assert np.array_equal( turbine.power_thrust_table["thrust_coefficient"], turbine_data["power_thrust_table"]["thrust_coefficient"] ) assert turbine.rotor_radius == turbine.rotor_diameter / 2.0 assert turbine.rotor_area == np.pi * turbine.rotor_radius ** 2.0 assert callable(turbine.thrust_coefficient_function) assert callable(turbine.power_function) def test_rotor_radius(): turbine_data = SampleInputs().turbine turbine = Turbine.from_dict(turbine_data) # Test that the radius is set correctly from the input file assert turbine.rotor_radius == turbine_data["rotor_diameter"] / 2.0 # Test the radius setter method since it actually sets the diameter turbine.rotor_radius = 200.0 assert turbine.rotor_diameter == 400.0 # Test the getter-method again assert turbine.rotor_radius == 200.0 def test_rotor_area(): turbine_data = SampleInputs().turbine turbine = Turbine.from_dict(turbine_data) # Test that the area is set correctly from the input file assert turbine.rotor_area == np.pi * (turbine_data["rotor_diameter"] / 2.0) ** 2 # Test the area setter method since it actually sets the radius and then the diameter turbine.rotor_area = np.pi assert turbine.rotor_radius == 1 assert turbine.rotor_diameter == 2 # Test the getter-method again assert turbine.rotor_area == np.pi def test_average_velocity(): # TODO: why do we use cube root - mean - cube (like rms) instead of a simple average (np.mean)? # Dimensions are (n_findex, n turbines, grid x, grid y) velocities = np.ones((1, 1, 5, 5)) assert average_velocity(velocities, method="cubic-mean") == 1 # Constructs an array of shape 1 x 2 x 3 x 3 with first turbine all 1, second turbine all 2 velocities = np.stack( ( np.ones((1, 3, 3)), # The first dimension here is the findex dimension and the second 2 * np.ones((1, 3, 3)), # is the n turbine since we are stacking on axis=1 ), axis=1, ) # Pull out the first findex for the test np.testing.assert_array_equal( average_velocity(velocities, method="cubic-mean")[0], np.array([1, 2]) ) # Test boolean filter ix_filter = [True, False, True, False] velocities = np.stack( # 4 turbines with 3 x 3 velocity array; shape (1,4,3,3) [i * np.ones((1, 3, 3)) for i in range(1,5)], # ( # # The first dimension here is the findex dimension # # and second is the turbine dimension since we are stacking on axis=1 # np.ones( # (1, 3, 3) # ), # 2 * np.ones((1, 3, 3)), # 3 * np.ones((1, 3, 3)), # 4 * np.ones((1, 3, 3)), # ), axis=1, ) avg = average_velocity(velocities, ix_filter, method="cubic-mean") assert avg.shape == (1, 2) # 1 = n_findex, 2 turbines filtered # Pull out the first findex for the comparison assert np.allclose(avg[0], np.array([1.0, 3.0])) # This fails in GitHub Actions due to a difference in precision: # E assert 3.0000000000000004 == 3.0 # np.testing.assert_array_equal(avg[0], np.array([1.0, 3.0])) # Test integer array filter # np.arange(1, 5).reshape((-1,1,1)) * np.ones((1, 1, 3, 3)) velocities = np.stack( # 4 turbines with 3 x 3 velocity array; shape (1,4,3,3) [i * np.ones((1, 3, 3)) for i in range(1,5)], axis=1, ) avg = average_velocity(velocities, INDEX_FILTER, method="cubic-mean") assert avg.shape == (1, 2) # 1 findex, 2 turbines filtered # Pull out the first findex for the comparison assert np.allclose(avg[0], np.array([1.0, 3.0])) def test_ct(): N_TURBINES = 4 turbine_data = SampleInputs().turbine turbine_floating_data = SampleInputs().turbine_floating turbine = Turbine.from_dict(turbine_data) turbine_floating = Turbine.from_dict(turbine_floating_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) # Add the findex (0th) dimension turbine_type_map = turbine_type_map[None, :] # Single turbine # yaw angle / fCt are (n_findex, n turbine) wind_speed = 10.0 thrust = thrust_coefficient( velocities=wind_speed * np.ones((1, 1, 3, 3)), turbulence_intensities=0.06 * np.zeros((1, 1, 3, 3)), air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array("baseline"), awc_amplitudes=np.zeros((1, 1)), thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) truth_index = turbine_data["power_thrust_table"]["wind_speed"].index(wind_speed) np.testing.assert_allclose( thrust, turbine_data["power_thrust_table"]["thrust_coefficient"][truth_index] ) # Multiple turbines with index filter # 4 turbines with 3 x 3 grid arrays thrusts = thrust_coefficient( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 12 x 4 x 3 x 3 turbulence_intensities=( 0.06 * np.ones((N_TURBINES, 3, 3)) * np.ones_like(WIND_CONDITION_BROADCAST) ), air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, N_TURBINES)), thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ix_filter=INDEX_FILTER, ) assert len(thrusts[0]) == len(INDEX_FILTER) for i in range(len(INDEX_FILTER)): truth_index = turbine_data["power_thrust_table"]["wind_speed"].index(WIND_SPEEDS[0]) np.testing.assert_allclose( thrusts[0, i], turbine_data["power_thrust_table"]["thrust_coefficient"][truth_index] ) # Single floating turbine; note that 'tilt_interp' is not set to None thrust = thrust_coefficient( velocities=wind_speed * np.ones((1, 1, 3, 3)), # One findex, one turbine turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array("baseline"), awc_amplitudes=np.zeros((1, 1)), thrust_coefficient_functions={ turbine.turbine_type: turbine_floating.thrust_coefficient_function }, tilt_interps={turbine_floating.turbine_type: turbine_floating.tilt_interp}, correct_cp_ct_for_tilt=np.array([[True]]), turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) truth_index = turbine_floating_data["power_thrust_table"]["wind_speed"].index(wind_speed) np.testing.assert_allclose( thrust, turbine_floating_data["power_thrust_table"]["thrust_coefficient"][truth_index] ) def test_power(): # AIR_DENSITY = 1.225 # Test that power is computed as expected for a single turbine n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(n_turbines * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] test_power = power( velocities=wind_speed * np.ones((1, 1, 3, 3)), # 1 findex, 1 turbine, 3x3 grid turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=turbine.power_thrust_table["ref_air_density"], power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array("baseline"), awc_amplitudes=np.zeros((1, 1)), tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, 1)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) # Recompute using the provided power truth_index = turbine_data["power_thrust_table"]["wind_speed"].index(wind_speed) baseline_power = turbine_data["power_thrust_table"]["power"][truth_index] * 1000 assert np.allclose(baseline_power, test_power) # At rated, the power calculated should be 5MW since the test data is the NREL 5MW turbine wind_speed = 18.0 rated_power = power( velocities=wind_speed * np.ones((1, 1, 3, 3)), turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=turbine.power_thrust_table["ref_air_density"], power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, 1)), power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array("baseline"), awc_amplitudes=np.zeros((1, 1)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) assert np.allclose(rated_power, 5e6) # At wind speed = 0.0, the power should be 0 based on the provided Cp curve wind_speed = 0.0 zero_power = power( velocities=wind_speed * np.ones((1, 1, 3, 3)), turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=turbine.power_thrust_table["ref_air_density"], power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, 1)), power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array("baseline"), awc_amplitudes=np.zeros((1, 1)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) assert np.allclose(zero_power, 0.0) # Test 4-turbine velocities array n_turbines = 4 wind_speed = 10.0 turbine_data = SampleInputs().turbine turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(n_turbines * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] test_4_power = power( velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), turbulence_intensities=0.06 * np.ones((1, n_turbines, 3, 3)), air_density=turbine.power_thrust_table["ref_air_density"], power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, n_turbines)), tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, n_turbines)), power_setpoints=np.ones((1, n_turbines)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*n_turbines]*1), awc_amplitudes=np.zeros((1, n_turbines)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) baseline_4_power = baseline_power * np.ones((1, n_turbines)) assert np.allclose(baseline_4_power, test_4_power) assert np.shape(baseline_4_power) == np.shape(test_4_power) # Same as above but with the grid collapsed in the velocities array turbine_data = SampleInputs().turbine turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(n_turbines * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] test_grid_power = power( velocities=wind_speed * np.ones((1, n_turbines, 1)), turbulence_intensities=0.06 * np.ones((1, n_turbines, 3)), air_density=turbine.power_thrust_table["ref_air_density"], power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, n_turbines)), tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, n_turbines)), power_setpoints=np.ones((1, n_turbines)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*n_turbines]*1), awc_amplitudes=np.zeros((1, n_turbines)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) baseline_grid_power = baseline_power * np.ones((1, n_turbines)) assert np.allclose(baseline_grid_power, test_grid_power) assert np.shape(baseline_grid_power) == np.shape(test_grid_power) def test_axial_induction(): N_TURBINES = 4 turbine_data = SampleInputs().turbine turbine_floating_data = SampleInputs().turbine_floating turbine = Turbine.from_dict(turbine_data) turbine_floating = Turbine.from_dict(turbine_floating_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, :] baseline_ai = 0.26752001107622186415 # Single turbine wind_speed = 10.0 ai = axial_induction( velocities=wind_speed * np.ones((1, 1, 3, 3)), # 1 findex, 1 Turbine turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array("baseline"), awc_amplitudes=np.zeros((1, 1)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), turbine_type_map=turbine_type_map[0,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) np.testing.assert_allclose(ai, baseline_ai) # Multiple turbines with ix filter ai = axial_induction( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 12 x 4 x 3 x 3 turbulence_intensities=( 0.06 * np.ones((N_TURBINES, 3, 3)) * np.ones_like(WIND_CONDITION_BROADCAST) ), air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array([["baseline"]*N_TURBINES]*1), awc_amplitudes=np.zeros((1, N_TURBINES)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ix_filter=INDEX_FILTER, ) assert len(ai[0]) == len(INDEX_FILTER) # Test the 10 m/s wind speed to use the same baseline as above np.testing.assert_allclose(ai[2], baseline_ai) # Single floating turbine; note that 'tilt_interp' is not set to None ai = axial_induction( velocities=wind_speed * np.ones((1, 1, 3, 3)), turbulence_intensities=0.06 * np.ones((1, 1, 3, 3)), air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, awc_modes=np.array("baseline"), awc_amplitudes=np.zeros((1, 1)), axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine_floating.turbine_type: turbine_floating.tilt_interp}, correct_cp_ct_for_tilt=np.array([[True]]), turbine_type_map=turbine_type_map[0,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, ) np.testing.assert_allclose(ai, baseline_ai) def test_asdict(sample_inputs_fixture: SampleInputs): turbine = Turbine.from_dict(sample_inputs_fixture.turbine) dict1 = turbine.as_dict() new_turb = Turbine.from_dict(dict1) dict2 = new_turb.as_dict() assert dict1 == dict2 ================================================ FILE: tests/turbine_utilities_unit_test.py ================================================ import os from pathlib import Path import numpy as np import pytest import yaml from floris.turbine_library import build_cosine_loss_turbine_dict, check_smooth_power_curve from tests.conftest import SampleInputs def test_build_turbine_dict(): turbine_data_v3 = SampleInputs().v3type_turbine # Mocked up turbine data turbine_data_dict = { "wind_speed":turbine_data_v3["power_thrust_table"]["wind_speed"], "power_coefficient":turbine_data_v3["power_thrust_table"]["power"], "thrust_coefficient":turbine_data_v3["power_thrust_table"]["thrust"] } test_dict = build_cosine_loss_turbine_dict( turbine_data_dict, "test_turbine", generator_efficiency=turbine_data_v3["generator_efficiency"], hub_height=turbine_data_v3["hub_height"], cosine_loss_exponent_yaw=turbine_data_v3["pP"], cosine_loss_exponent_tilt=turbine_data_v3["pT"], rotor_diameter=turbine_data_v3["rotor_diameter"], TSR=turbine_data_v3["TSR"], ref_air_density=turbine_data_v3["ref_density_cp_ct"], ref_tilt=turbine_data_v3["ref_tilt_cp_ct"] ) # Test correct error raised if power_coefficient version passed and generator efficiency # not specified with pytest.raises(KeyError): build_cosine_loss_turbine_dict( turbine_data_dict, "test_turbine", #generator_efficiency=turbine_data_v3["generator_efficiency"], hub_height=turbine_data_v3["hub_height"], cosine_loss_exponent_yaw=turbine_data_v3["pP"], cosine_loss_exponent_tilt=turbine_data_v3["pT"], rotor_diameter=turbine_data_v3["rotor_diameter"], TSR=turbine_data_v3["TSR"], ref_air_density=turbine_data_v3["ref_density_cp_ct"], ref_tilt=turbine_data_v3["ref_tilt_cp_ct"] ) # Directly compute power, thrust values Cp = np.array(turbine_data_v3["power_thrust_table"]["power"]) Ct = np.array(turbine_data_v3["power_thrust_table"]["thrust"]) ws = np.array(turbine_data_v3["power_thrust_table"]["wind_speed"]) P = ( 0.5 * turbine_data_v3["ref_density_cp_ct"] * turbine_data_v3["generator_efficiency"] * (np.pi * turbine_data_v3["rotor_diameter"]**2/4) * Cp * ws**3 ) T = ( 0.5 * turbine_data_v3["ref_density_cp_ct"] * (np.pi * turbine_data_v3["rotor_diameter"]**2/4) * Ct * ws**2 ) # Compare direct computation to those generated by build_cosine_loss_turbine_dict assert np.allclose(Ct, test_dict["power_thrust_table"]["thrust_coefficient"]) assert np.allclose(P/1000, test_dict["power_thrust_table"]["power"]) # Check that dict keys match the v4 structure turbine_data_v4 = SampleInputs().turbine assert set(turbine_data_v4.keys()) >= set(test_dict.keys()) assert ( set(turbine_data_v4["power_thrust_table"].keys()) >= set(test_dict["power_thrust_table"].keys()) ) # Check thrust conversion from absolute value turbine_data_dict = { "wind_speed":turbine_data_v3["power_thrust_table"]["wind_speed"], "power": P/1000, "thrust": T/1000 } test_dict_2 = build_cosine_loss_turbine_dict( turbine_data_dict, "test_turbine", hub_height=turbine_data_v4["hub_height"], cosine_loss_exponent_yaw=turbine_data_v4["power_thrust_table"]["cosine_loss_exponent_yaw"], cosine_loss_exponent_tilt=turbine_data_v4["power_thrust_table"]["cosine_loss_exponent_tilt"], rotor_diameter=turbine_data_v4["rotor_diameter"], TSR=turbine_data_v4["TSR"], ref_air_density=turbine_data_v4["power_thrust_table"]["ref_air_density"], ref_tilt=turbine_data_v4["power_thrust_table"]["ref_tilt"] ) assert np.allclose(Ct, test_dict_2["power_thrust_table"]["thrust_coefficient"]) def test_check_smooth_power_curve(): p1 = np.array([0, 1, 2, 3, 3, 3, 3, 2, 1], dtype=float)*1000 # smooth p2 = np.array([0, 1, 2, 3, 2.99, 3.01, 3, 2, 1], dtype=float)*1000 # non-smooth p3 = p1.copy() p3[5] = p3[5] + 9e-4 # just smooth enough p4 = p1.copy() p4[5] = p4[5] + 1.1e-3 # just not smooth enough # Without a shutdown region p5 = p1[:-3] # smooth p6 = p2[:-3] # non-smooth assert check_smooth_power_curve(p1) assert not check_smooth_power_curve(p2) assert check_smooth_power_curve(p3) assert not check_smooth_power_curve(p4) assert check_smooth_power_curve(p5) assert not check_smooth_power_curve(p6) ================================================ FILE: tests/turboparkgauss_unit_test.py ================================================ from pathlib import Path import numpy as np from floris import FlorisModel from floris.turbine_library import build_cosine_loss_turbine_dict TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" def test_row_of_turbines(): fmodel = FlorisModel(configuration=YAML_INPUT) # Configure as turboparkgauss fmodel_dict = fmodel.core.as_dict() fmodel_dict["wake"]["model_strings"]["velocity_model"] = "turboparkgauss" fmodel_dict["wake"]["model_strings"]["turbulence_model"] = "none" fmodel_dict["wake"]["model_strings"]["deflection_model"] = "none" fmodel_dict["wake"]["model_strings"]["combination_model"] = "sosfs" fmodel_dict["wake"]["enable_secondary_steering"] = False fmodel_dict["wake"]["enable_yaw_added_recovery"] = False fmodel_dict["wake"]["enable_active_wake_mixing"] = False fmodel_dict["wake"]["enable_transverse_velocities"] = False fmodel_dict["solver"]["type"] = "turbine_cubature_grid" fmodel_dict["solver"]["turbine_grid_points"] = 6 fmodel = FlorisModel(configuration=fmodel_dict) # Define turbine const_CT_turb = build_cosine_loss_turbine_dict( turbine_data_dict={ "wind_speed":[0.0, 30.0], "power":[0.0, 1.0], # Not realistic but won't be used here "thrust_coefficient":[0.75, 0.75] }, turbine_name="ConstantCT", rotor_diameter=120.0, hub_height=100.0, ref_tilt=0.0, ) # Set up problem and run fmodel.set( layout_x=np.linspace(0.0, 5400.0, 10), layout_y=np.zeros(10), wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06], wind_shear=0.0, turbine_type=[const_CT_turb], ) fmodel.run() # Run and extract flow velocities at the turbines velocities_row_normalized = fmodel.turbine_average_velocities[0,:] / 8.0 # Comparison data generated by running Ørsted's Matlab code # https://github.com/OrstedRD/TurbOPark velocities_comparison = np.array([ 1.0, 0.709920677983239, 0.615355749367675, 0.551410465937128, 0.502600655337247, 0.463167556093190, 0.430238792036599, 0.402137593655074, 0.377783142608699, 0.356429516711137, ]) # Compare the results print(velocities_row_normalized) np.testing.assert_allclose( velocities_row_normalized, velocities_comparison, rtol=1e-2, ) # Within 1% tolerance ================================================ FILE: tests/type_dec_unit_test.py ================================================ from pathlib import Path from typing import List import numpy as np import pytest from attrs import define, field from floris.type_dec import ( convert_to_path, floris_array_converter, floris_numeric_dict_converter, FromDictMixin, iter_validator, ) @define class AttrsDemoClass(FromDictMixin): w: int x: int = field(converter=int) y: float = field(converter=float, default=2.1) z: str = field(converter=str, default="z") non_initd: float = field(init=False) def __attrs_post_init__(self): self.non_initd = 1.1 liststr: List[str] = field( factory=lambda:["qwerty", "asdf"], validator=iter_validator(list, str) ) array: np.ndarray = field( factory=lambda:[1.0, 2.0], converter=floris_array_converter, # validator=iter_validator(np.ndarray, floris_float_type) ) def test_as_dict(): # Non-initialized attributes should not be exported cls = AttrsDemoClass(w=0, x=1, liststr=["a", "b"]) exported_dict = cls.as_dict() assert "non_initd" not in exported_dict def test_FromDictMixin_defaults(): # Test that the defaults set in the class definition are actually used inputs = {"w": 0, "x": 1} cls = AttrsDemoClass.from_dict(inputs) defaults = {a.name: a.default for a in AttrsDemoClass.__attrs_attrs__ if a.default} assert cls.y == defaults["y"] assert cls.z == defaults["z"] np.testing.assert_array_equal(cls.liststr, defaults["liststr"].factory()) np.testing.assert_array_equal(cls.array, defaults["array"].factory()) # Test that defaults can be overwritten inputs = {"w": 0, "x": 1, "y": 4.5} cls = AttrsDemoClass.from_dict(inputs) defaults = {a.name: a.default for a in AttrsDemoClass.__attrs_attrs__ if a.default} assert cls.y != defaults["y"] def test_FromDictMixin_custom(): inputs = { "w": 0, "x": 1, "y": 2.3, "z": "asdf", "liststr": ["a", "b"], "array": np.array([[1,2,3], [4,5,6]]) } # Check that custom inputs are accepted AttrsDemoClass.from_dict(inputs) # Ensure extraneous inputs are not applied to the class inputs2 = {**inputs, "extra": [3, 4, 5.5]} with pytest.raises(AttributeError): AttrsDemoClass.from_dict(inputs2) # Test that missing required inputs raises an error inputs = {} with pytest.raises(AttributeError): AttrsDemoClass.from_dict(inputs) def test_iter_validator(): # Check the correct values work _ = AttrsDemoClass(w=0, x=1, liststr=["a", "b"]) # Check wrong member type with pytest.raises(TypeError): AttrsDemoClass(w=0, x=1, liststr=[4.3, 1]) # Check mixed member types with pytest.raises(TypeError): AttrsDemoClass(w=0, x=1, liststr=[4.3, "1"]) # Check wrong iterable type with pytest.raises(TypeError): AttrsDemoClass(w=0, x=1, liststr=("a", "b")) def test_array_converter(): array_input = [[1, 2, 3], [4.5, 6.3, 2.2]] test_array = np.array(array_input) # Test conversion on initialization cls = AttrsDemoClass(w=0, x=1, array=array_input) np.testing.assert_allclose(test_array, cls.array) # Test conversion on reset cls.array = array_input np.testing.assert_allclose(test_array, cls.array) # Test that a non-iterable item like a scalar number fails with pytest.raises(TypeError): cls.array = 1 def test_numeric_dict_converter(): """ This function converts data in a dictionary to a numeric type. If it can't convert the data, it will raise a TypeError. It should support scalar, list, and numpy array types for values in the dictionary. """ test_dict = { "scalar_string": "1", "scalar_int": 1, "scalar_float": 1.0, "list_string": ["1", "2", "3"], "list_int": [1, 2, 3], "list_float": [1.0, 2.0, 3.0], "array_string": np.array(["1", "2", "3"]), "array_int": np.array([1, 2, 3]), "array_float": np.array([1.0, 2.0, 3.0]), } numeric_dict = floris_numeric_dict_converter(test_dict) assert numeric_dict["scalar_string"] == 1 assert numeric_dict["scalar_int"] == 1 assert numeric_dict["scalar_float"] == 1.0 np.testing.assert_allclose(numeric_dict["list_string"], [1, 2, 3]) np.testing.assert_allclose(numeric_dict["list_int"], [1, 2, 3]) np.testing.assert_allclose(numeric_dict["list_float"], [1.0, 2.0, 3.0]) np.testing.assert_allclose(numeric_dict["array_string"], [1, 2, 3]) np.testing.assert_allclose(numeric_dict["array_int"], [1, 2, 3]) np.testing.assert_allclose(numeric_dict["array_float"], [1.0, 2.0, 3.0]) test_dict = {"scalar_fail": "a"} with pytest.raises(TypeError): floris_numeric_dict_converter(test_dict) test_dict = {"list_fail": ["a", "2", "3"]} with pytest.raises(TypeError): floris_numeric_dict_converter(test_dict) test_dict = {"array_fail": np.array(["a", "2", "3"])} with pytest.raises(TypeError): floris_numeric_dict_converter(test_dict) def test_convert_to_path(): str_input = Path(__file__) expected_path = str_input.resolve() # Test that a string works test_str_input = convert_to_path(str_input) assert test_str_input == expected_path # Test that a pathlib.Path works path_input = Path(str_input) test_path_input = convert_to_path(path_input) assert test_path_input == expected_path # Test that both of those inputs are the same # NOTE These first three asserts tests the relative path search assert test_str_input == test_path_input # Test absolute path abs_path = expected_path test_abs_path = convert_to_path(abs_path) assert test_abs_path == expected_path # Test a file file_input = Path(__file__) test_file = convert_to_path(file_input) assert test_file == file_input # Test that a non-existent folder fails, now that the conversion has a multi-pronged search str_input = str(Path(__file__).parent / "bad_path") with pytest.raises(FileExistsError): convert_to_path(str_input) # Test that invalid data types fail with pytest.raises(TypeError): convert_to_path(1) with pytest.raises(TypeError): convert_to_path(1.2) with pytest.raises(TypeError): convert_to_path({"one": 1}) with pytest.raises(TypeError): convert_to_path(["a", 1]) ================================================ FILE: tests/uncertain_floris_model_integration_test.py ================================================ from pathlib import Path import numpy as np import pytest import yaml from floris import ( FlorisModel, ParFlorisModel, TimeSeries, ) from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.uncertain_floris_model import ( ApproxFlorisModel, UncertainFlorisModel, WindRose, ) TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" def test_read_yaml(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) assert isinstance(ufmodel, UncertainFlorisModel) def test_rounded_inputs(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) # Using defaults # Example input array input_array = np.array([[45.3, 7.6, 0.24, 90.7, 749], [60.1, 8.2, 0.3, 95.3, 751]]) # Expected output array after rounding expected_output = np.array([[45.0, 8.0, 0.25, 91.0, 700.0], [60.0, 8.0, 0.3, 95.0, 800.0]]) # Call the function rounded_inputs = ufmodel._get_rounded_inputs(input_array) np.testing.assert_almost_equal(rounded_inputs, expected_output) def test_expand_wind_directions(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) input_array = np.array( [[1, 20, 30], [40, 50, 60], [70, 80, 90], [100, 110, 120], [359, 140, 150]] ) # Test even length with pytest.raises(ValueError): wd_sample_points = [-15, -10, -5, 5, 10, 15] # Even lenght ufmodel._expand_wind_directions(input_array, wd_sample_points) # Test middle element not 0 with pytest.raises(ValueError): wd_sample_points = [-15, -10, -5, 1, 5, 10, 15] # Odd length, not 0 at the middle ufmodel._expand_wind_directions(input_array, wd_sample_points) # Test correction operations wd_sample_points = [-15, -10, -5, 0, 5, 10, 15] # Odd length, 0 at the middle output_array = ufmodel._expand_wind_directions(input_array, wd_sample_points) # Check if output shape is correct assert output_array.shape[0] == 35 # Check 360 wrapping # 1 - 15 = -14 -> 346 np.testing.assert_almost_equal(output_array[0, 0], 346.0) # Check 360 wrapping # 359 + 15 = 374 -> 14 np.testing.assert_almost_equal(output_array[-1, 0], 14.0) def test_expand_wind_directions_with_yaw_nom(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) # Assume 2 turbine n_turbines = 2 # Assume n_findex = 2 input_array = np.array( [[270.0, 8.0, 0.6, 0.0, 0.0, 0.0, 0.0], [270.0, 8.0, 0.6, 0.0, 2.0, 0.0, 0.0]] ) # 3 sample points wd_sample_points = [-3, 0, 3] # Test correction operations output_array = ufmodel._expand_wind_directions(input_array, wd_sample_points, True, n_turbines) # Check the first direction np.testing.assert_almost_equal(output_array[0, 0], 267) # Check the first yaw np.testing.assert_almost_equal(output_array[0, 4], -3) # Rerun with fix_yaw_to_nominal_direction = False, and now the yaw should be 0 output_array = ufmodel._expand_wind_directions(input_array, wd_sample_points, False, n_turbines) # Check the first direction np.testing.assert_almost_equal(output_array[0, 0], 267) # Check the first yaw np.testing.assert_almost_equal(output_array[0, 4], 0) def test_get_unique_inputs(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) input_array = np.array( [ [0, 1], [0, 2], [0, 1], [1, 1], [0, 1], ] ) expected_unique_inputs = np.array([[0, 1], [0, 2], [1, 1]]) unique_inputs, map_to_expanded_inputs = ufmodel._get_unique_inputs(input_array) # test expected result assert np.array_equal(unique_inputs, expected_unique_inputs) # Test gets back to original assert np.array_equal(unique_inputs[map_to_expanded_inputs], input_array) def test_get_weights(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) weights = ufmodel._get_weights(3.0, [-6, -3, 0, 3, 6]) np.testing.assert_allclose( weights, np.array([0.05448868, 0.24420134, 0.40261995, 0.24420134, 0.05448868]) ) def test_uncertain_floris_model(): # Recompute uncertain result using certain result with 1 deg fmodel = FlorisModel(configuration=YAML_INPUT) ufmodel = UncertainFlorisModel(configuration=YAML_INPUT, wd_sample_points=[-3, 0, 3], wd_std=3) fmodel.set( layout_x=[0, 300], layout_y=[0, 0], wind_speeds=[8.0, 8.0, 8.0], wind_directions=[267.0, 270.0, 273], turbulence_intensities=[0.06, 0.06, 0.06], ) ufmodel.set( layout_x=[0, 300], layout_y=[0, 0], wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06], ) fmodel.run() ufmodel.run() nom_powers = fmodel.get_turbine_powers()[:, 1].flatten() unc_powers = ufmodel.get_turbine_powers()[:, 1].flatten() weights = ufmodel.weights np.testing.assert_allclose(np.sum(nom_powers * weights), unc_powers) def test_uncertain_floris_model_setpoints(): fmodel = FlorisModel(configuration=YAML_INPUT) ufmodel = UncertainFlorisModel(configuration=YAML_INPUT, wd_sample_points=[-3, 0, 3], wd_std=3) fmodel.set( layout_x=[0, 300], layout_y=[0, 0], wind_speeds=[8.0, 8.0, 8.0], wind_directions=[267.0, 270.0, 273], turbulence_intensities=[0.06, 0.06, 0.06], ) ufmodel.set( layout_x=[0, 300], layout_y=[0, 0], wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06], ) weights = ufmodel.weights # Check setpoints dimensions are respected and reset_operation works # Note that fmodel.set() does NOT raise ValueError---an AttributeError is raised only at # fmodel.run()---whereas ufmodel.set raises ValueError immediately. # fmodel.set(yaw_angles=np.array([[0.0, 0.0]])) # with pytest.raises(AttributeError): # fmodel.run() # with pytest.raises(ValueError): # ufmodel.set(yaw_angles=np.array([[0.0, 0.0]])) fmodel.set(yaw_angles=np.array([[20.0, 0.0], [20.0, 0.0], [20.0, 0.0]])) fmodel.run() nom_powers = fmodel.get_turbine_powers()[:, 1].flatten() ufmodel.set(yaw_angles=np.array([[20.0, 0.0]])) ufmodel.run() unc_powers = ufmodel.get_turbine_powers()[:, 1].flatten() np.testing.assert_allclose(np.sum(nom_powers * weights), unc_powers) # Drop yaw setpoints and rerun fmodel.reset_operation() fmodel.run() nom_powers = fmodel.get_turbine_powers()[:, 1].flatten() ufmodel.reset_operation() ufmodel.run() unc_powers = ufmodel.get_turbine_powers()[:, 1].flatten() np.testing.assert_allclose(np.sum(nom_powers * weights), unc_powers) def test_get_powers_with_wind_data(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 10.0, 12.0, 8.0, 10.0, 12.0]) wind_directions = np.array([270.0, 270.0, 270.0, 280.0, 280.0, 280.0]) turbulence_intensities = 0.06 * np.ones_like(wind_speeds) ufmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=[0, 1000, 2000, 3000], layout_y=[0, 0, 0, 0], ) ufmodel.run() farm_power_simple = ufmodel.get_farm_power() # Now declare a WindRose with 2 wind directions and 3 wind speeds # uniform TI and frequency wind_rose = WindRose( wind_directions=np.unique(wind_directions), wind_speeds=np.unique(wind_speeds), ti_table=0.06, ) # Set this wind rose, run ufmodel.set(wind_data=wind_rose) ufmodel.run() farm_power_windrose = ufmodel.get_farm_power() # Check dimensions and that the farm power is the sum of the turbine powers assert farm_power_windrose.shape == (2, 3) assert np.allclose(farm_power_windrose, ufmodel.get_turbine_powers().sum(axis=2)) # Check that simple and windrose powers are consistent assert np.allclose(farm_power_simple.reshape(2, 3), farm_power_windrose) assert np.allclose(farm_power_simple, farm_power_windrose.flatten()) # Test that if the last turbine's weight is set to 0, the farm power is the same as the # sum of the first 3 turbines turbine_weights = np.array([1.0, 1.0, 1.0, 0.0]) farm_power_weighted = ufmodel.get_farm_power(turbine_weights=turbine_weights) assert np.allclose(farm_power_weighted, ufmodel.get_turbine_powers()[:, :, :-1].sum(axis=2)) def test_AEP_with_wind_data(): wind_speeds = np.array([8.0, 10.0]) wind_directions = np.array([270.0, 280.0]) frequencies = np.array([[0.25, 0.25], [0.1, 0.4]]) wind_rose = WindRose( wind_directions=np.unique(wind_directions), wind_speeds=np.unique(wind_speeds), freq_table=frequencies, ti_table=0.06, ) # Set wind_data on UncertainFlorisModel directly ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) ufmodel.set(wind_data=wind_rose) ufmodel.run() aep_1 = ufmodel.get_farm_AEP() # Set wind_data on FlorisModel and then set on UncertainFlorisModel fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set(wind_data=wind_rose) ufmodel = UncertainFlorisModel(fmodel) ufmodel.run() aep_2 = ufmodel.get_farm_AEP() # Check AEPs match assert np.allclose(aep_1, aep_2) def test_approx_floris_model(): afmodel = ApproxFlorisModel(configuration=YAML_INPUT, wd_resolution=1.0) time_series = TimeSeries( wind_directions=np.array([270.0, 270.1, 271.0, 271.1]), wind_speeds=8.0, turbulence_intensities=0.06, ) afmodel.set(layout_x=np.array([0, 500]), layout_y=np.array([0, 0]), wind_data=time_series) # Test that 0th and 1th values are the same, as are the 2nd and 3rd afmodel.run() power = afmodel.get_farm_power() np.testing.assert_almost_equal(power[0], power[1]) np.testing.assert_almost_equal(power[2], power[3]) # Test with wind direction and wind speed varying afmodel = ApproxFlorisModel(configuration=YAML_INPUT, wd_resolution=1.0, ws_resolution=1.0) time_series = TimeSeries( wind_directions=np.array([270.0, 270.1, 271.0, 271.1]), wind_speeds=np.array([8.0, 8.1, 8.0, 9.0]), turbulence_intensities=0.06, ) afmodel.set(layout_x=np.array([0, 500]), layout_y=np.array([0, 0]), wind_data=time_series) afmodel.run() # In this case the 0th and 1st should be the same, but not the 2nd and 3rd power = afmodel.get_farm_power() np.testing.assert_almost_equal(power[0], power[1]) assert not np.allclose(power[2], power[3]) # Test copy method assert isinstance(afmodel.copy(), ApproxFlorisModel) def test_expected_farm_power_regression(): ufmodel = UncertainFlorisModel( configuration=YAML_INPUT, wd_sample_points=[0], ) # Force equal to nominal wind_speeds = np.array([8.0, 8.0, 8.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) ufmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) ufmodel.run() expected_farm_power = ufmodel.get_expected_farm_power() # Assert the expected farm power has not inadvetently changed np.testing.assert_allclose(expected_farm_power, 3507908.918358342, atol=1e-1) def test_expected_farm_power_equals_sum_of_expected_turbine_powers(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) wind_speeds = np.array([8.0, 8.0, 8.0]) wind_directions = np.array([270.0, 270.0, 270.0]) turbulence_intensities = np.array([0.06, 0.06, 0.06]) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) ufmodel.set( wind_speeds=wind_speeds, wind_directions=wind_directions, turbulence_intensities=turbulence_intensities, layout_x=layout_x, layout_y=layout_y, ) ufmodel.run() expected_farm_power = ufmodel.get_expected_farm_power() expected_turbine_powers = ufmodel.get_expected_turbine_powers() # Assert the expected farm power is the sum of the expected turbine powers np.testing.assert_allclose(expected_farm_power, np.sum(expected_turbine_powers)) def test_expected_farm_value_regression(): # Ensure this calculation hasn't changed unintentionally ufmodel = UncertainFlorisModel( configuration=YAML_INPUT, wd_sample_points=[0], ) # Force equal to nominal wind_speeds = np.array([8.0, 8.0, 9.0]) wind_directions = np.array([270.0, 270.0, 270.0]) values = np.array([30.0, 20.0, 10.0]) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06, values=values, ) layout_x = np.array([0, 0]) layout_y = np.array([0, 1000]) ufmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=time_series) ufmodel.run() expected_farm_value = ufmodel.get_expected_farm_value() assert np.allclose(expected_farm_value, 75108001.05154414, atol=1e-1) def test_get_and_set_param(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) # Set the wake parameter ufmodel.set_param(["wake", "wake_velocity_parameters", "gauss", "alpha"], 0.1) alpha = ufmodel.get_param(["wake", "wake_velocity_parameters", "gauss", "alpha"]) assert alpha == 0.1 # Confirm also correct in expanded floris model alpha_e = ufmodel.fmodel_expanded.get_param( ["wake", "wake_velocity_parameters", "gauss", "alpha"] ) assert alpha_e == 0.1 def test_get_operation_model(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) assert ufmodel.get_operation_model() == "cosine-loss" def test_set_operation_model(): # Define a reference wind height for cases when there are changes to # turbine_type ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) ufmodel.set_operation_model("simple-derating") assert ufmodel.get_operation_model() == "simple-derating" reference_wind_height = ufmodel.reference_wind_height # Check multiple turbine types works ufmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) ufmodel.set_operation_model(["simple-derating", "cosine-loss"]) assert ufmodel.get_operation_model() == ["simple-derating", "cosine-loss"] # Confirm this passed through to expanded model assert ufmodel.fmodel_expanded.get_operation_model() == ["simple-derating", "cosine-loss"] # Check that setting a single turbine type, and then altering the operation model works ufmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) ufmodel.set(turbine_type=["nrel_5MW"], reference_wind_height=reference_wind_height) ufmodel.set_operation_model("simple-derating") assert ufmodel.get_operation_model() == "simple-derating" # Check that setting over mutliple turbine types works ufmodel.set(turbine_type=["nrel_5MW", "iea_15MW"], reference_wind_height=reference_wind_height) ufmodel.set_operation_model("simple-derating") assert ufmodel.get_operation_model() == "simple-derating" ufmodel.set_operation_model(["simple-derating", "cosine-loss"]) assert ufmodel.get_operation_model() == ["simple-derating", "cosine-loss"] # Check setting over single turbine type; then updating layout works ufmodel.set(turbine_type=["nrel_5MW"], reference_wind_height=reference_wind_height) ufmodel.set_operation_model("simple-derating") ufmodel.set(layout_x=[0, 0, 0], layout_y=[0, 1000, 2000]) assert ufmodel.get_operation_model() == "simple-derating" # Check that setting for multiple turbine types and then updating layout breaks ufmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) ufmodel.set(turbine_type=["nrel_5MW"], reference_wind_height=reference_wind_height) ufmodel.set_operation_model(["simple-derating", "cosine-loss"]) assert ufmodel.get_operation_model() == ["simple-derating", "cosine-loss"] with pytest.raises(ValueError): ufmodel.set(layout_x=[0, 0, 0], layout_y=[0, 1000, 2000]) # Check one more variation ufmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) ufmodel.set(turbine_type=["nrel_5MW", "iea_15MW"], reference_wind_height=reference_wind_height) ufmodel.set_operation_model("simple-derating") ufmodel.set(layout_x=[0, 0], layout_y=[0, 1000]) with pytest.raises(ValueError): ufmodel.set(layout_x=[0, 0, 0], layout_y=[0, 1000, 2000]) def test_parallel_uncertain_model(): ufmodel = UncertainFlorisModel(FlorisModel(configuration=YAML_INPUT)) pufmodel = UncertainFlorisModel(ParFlorisModel(configuration=YAML_INPUT)) assert isinstance(ufmodel.fmodel_expanded, FlorisModel) assert isinstance(pufmodel.fmodel_expanded, ParFlorisModel) # Run the models and compare outputs ufmodel.run() pufmodel.run() powers_unc = ufmodel.get_turbine_powers() powers_punc = pufmodel.get_turbine_powers() assert np.allclose(powers_unc, powers_punc) def test_copy(): """ Check that the UncertainFlorisModel copy method works as expected for both FlorisModel and ParFlorisModel. """ ufmodel = UncertainFlorisModel(FlorisModel(configuration=YAML_INPUT)) ufmodel_copy = ufmodel.copy() assert isinstance(ufmodel_copy, UncertainFlorisModel) assert isinstance(ufmodel_copy.fmodel_expanded, FlorisModel) pufmodel = UncertainFlorisModel(ParFlorisModel(configuration=YAML_INPUT)) pufmodel_copy = pufmodel.copy() assert isinstance(pufmodel_copy, UncertainFlorisModel) assert isinstance(pufmodel_copy.fmodel_expanded, ParFlorisModel) def test_invalid_wd_std(): """ Test that the UncertainFlorisModel raises asn error with a wd_std of 0 or negative. """ with pytest.raises(ValueError): UncertainFlorisModel(configuration=YAML_INPUT, wd_std=0.0) with pytest.raises(ValueError): UncertainFlorisModel(configuration=YAML_INPUT, wd_std=-1.0) def test_turbine_average_velocities_shape_and_type(): """ Test that turbine_average_velocities returns the correct shape and type. """ ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) # Set up a simple 2-turbine wind farm ufmodel.set( layout_x=[0, 500], layout_y=[0, 0], wind_speeds=[8.0, 10.0], wind_directions=[270.0, 280.0], turbulence_intensities=[0.06, 0.06], ) ufmodel.run() # Get turbine average velocities velocities = ufmodel.turbine_average_velocities # Check type assert isinstance(velocities, np.ndarray) # Check shape: should be (n_findex, n_turbines) expected_shape = (ufmodel.n_findex, ufmodel.n_turbines) assert velocities.shape == expected_shape # Check that values are positive and reasonable assert np.all(velocities > 0) assert np.all(velocities < 20) # Reasonable upper bound for wind speeds def test_turbine_average_velocities_free_stream(): """ Test that turbine_average_velocities returns the correct shape and type. """ ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) # Set up a simple 2-turbine wind farm with no wake interactions ufmodel.set( layout_x=[0, 0], layout_y=[0, 1000], wind_speeds=[8.0, 10.0], wind_directions=[270.0, 280.0], turbulence_intensities=[0.06, 0.06], wind_shear=0.0, # Turn off shear to simplify the test ) ufmodel.run() # Get turbine average velocities velocities = ufmodel.turbine_average_velocities # Velocities should be the same as the wind speeds but n_turbines columns repeated assert np.allclose(velocities, np.array([[8.0, 8.0], [10.0, 10.0]])) def test_turbine_average_velocities_uncertain_vs_certain(): """ Test that turbine_average_velocities returns the same values for uncertain and certain models. """ # Set up (certain) FlorisModel fmodel = FlorisModel(configuration=YAML_INPUT) fmodel.set( layout_x=[0, 500], layout_y=[0, 0], wind_speeds=[8.0, 10.0], wind_directions=[270.0, 270.0], turbulence_intensities=[0.06, 0.06], ) fmodel.run() velocities_certain = fmodel.turbine_average_velocities # Create equivalent uncertain model ufmodel = UncertainFlorisModel(configuration=fmodel) ufmodel.run() velocities_uncertain = ufmodel.turbine_average_velocities # Check that the upstream turbine matches assert np.allclose(velocities_uncertain[:, 0], velocities_certain[:, 0]) # Downstream turbine higher than certain when aligned assert np.all(velocities_uncertain[:, 1] > velocities_certain[:, 1]) # Create a near 0-std uncertain model ufmodel_zero_std = UncertainFlorisModel(configuration=fmodel, wd_std=1e-5) ufmodel_zero_std.run() velocities_uncertain_zero_std = ufmodel_zero_std.turbine_average_velocities # Check that the uncertain model with 0 std matches the certain model assert np.allclose(velocities_uncertain_zero_std, velocities_certain) def test_multidim_conditions_flag(sample_inputs_fixture): # make sure multidim conditions is correctly flagged # Set wind speed and wind direction for n_findex = 2 wind_speeds = np.array([8.0, 8.0]) wind_directions = np.array([270.0, 270.0]) time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06, ) ufmodel = UncertainFlorisModel(configuration=YAML_INPUT, wd_sample_points=[0]) ufmodel.set(wind_data=time_series) assert ufmodel.fmodel_unexpanded.core.flow_field.multidim_conditions is None # Define the multidim conditions multidim_conditions = { "Tp": np.array([5.0, 6.0]), "Hs": np.array([3.0, 4.0]), } time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06, multidim_conditions=multidim_conditions, ) ufmodel.set(wind_data=time_series, turbine_type=[sample_inputs_fixture.turbine_multi_dim]) assert ufmodel.fmodel_unexpanded.core.flow_field.multidim_conditions is not None def test_multidim_conditions(sample_inputs_fixture): """ Test that the UncertainFlorisModel works with multidim_conditions set in the TimeSeries object. """ # Set wind speed and wind direction for n_findex = 2 wind_speeds = np.array([8.0, 8.0]) wind_directions = np.array([270.0, 270.0]) # Define the multidim conditions multidim_conditions = { "Tp": np.array([5.0, 6.0]), "Hs": np.array([3.0, 4.0]), } time_series = TimeSeries( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=0.06, multidim_conditions=multidim_conditions, ) ufmodel = UncertainFlorisModel(configuration=YAML_INPUT, wd_sample_points=[0]) # Set the ufmodel to two turbine case ufmodel.set(layout_x=[0, 5000], layout_y=[0, 0]) # Set the ufmodel to use the multidim turbine ufmodel.set(wind_data=time_series, turbine_type=[sample_inputs_fixture.turbine_multi_dim]) ufmodel.run() powers_unc = ufmodel.get_turbine_powers() # Rerun with multidim_conditions set to scalar values Tp = 5.0 and Hs = 3.0 ufmodel.set(multidim_conditions={"Tp": 5.0, "Hs": 3.0}) ufmodel.run() powers_unc_scalar = ufmodel.get_turbine_powers() # Check that the powers are the same for the first findex and different for the second findex assert np.allclose(powers_unc[0, :], powers_unc_scalar[0, :]) assert not np.allclose(powers_unc[1, :], powers_unc_scalar[1, :]) ================================================ FILE: tests/unified_momentum_operation_model_unit_test.py ================================================ import numpy as np from floris.core.turbine.unified_momentum_model import UnifiedMomentumModelTurbine from tests.conftest import SampleInputs def test_submodel_attributes(): assert hasattr(UnifiedMomentumModelTurbine, "power") assert hasattr(UnifiedMomentumModelTurbine, "thrust_coefficient") assert hasattr(UnifiedMomentumModelTurbine, "axial_induction") def test_UnifiedMomentumModelTurbine(): n_turbines = 1 wind_speed = 10.0 turbine_data = SampleInputs().turbine yaw_angles_nom = 0 * np.ones((1, n_turbines)) tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((1, n_turbines)) yaw_angles_test = 20 * np.ones((1, n_turbines)) tilt_angles_test = 0 * np.ones((1, n_turbines)) # Check that power works as expected test_power = UnifiedMomentumModelTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], # Matches ref_air_density yaw_angles=yaw_angles_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ) truth_index = turbine_data["power_thrust_table"]["wind_speed"].index(wind_speed) baseline_power = turbine_data["power_thrust_table"]["power"][truth_index] * 1000 assert np.allclose(baseline_power, test_power) # Check that yaw and tilt angle have an effect test_power = UnifiedMomentumModelTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=turbine_data["power_thrust_table"]["ref_air_density"], # Matches ref_air_density yaw_angles=yaw_angles_test, tilt_angles=tilt_angles_test, tilt_interp=None ) assert test_power < baseline_power # Check that a lower air density decreases power appropriately test_power = UnifiedMomentumModelTurbine.power( power_thrust_table=turbine_data["power_thrust_table"], velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid air_density=1.1, yaw_angles=yaw_angles_nom, tilt_angles=tilt_angles_nom, tilt_interp=None ) assert test_power < baseline_power ================================================ FILE: tests/utilities_unit_test.py ================================================ from pathlib import Path import attr import numpy as np import pytest from floris.utilities import ( check_and_identify_step_size, cosd, is_all_scalar_dict, make_wind_directions_adjacent, nested_get, nested_set, reverse_rotate_coordinates_rel_west, rotate_coordinates_rel_west, sind, tand, wind_delta, wrap_180, wrap_360, ) from tests.conftest import ( X_COORDS, Y_COORDS, Z_COORDS, ) TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" def test_cosd(): assert pytest.approx(cosd(0.0)) == 1.0 assert pytest.approx(cosd(90.0)) == 0.0 assert pytest.approx(cosd(180.0)) == -1.0 assert pytest.approx(cosd(270.0)) == 0.0 def test_sind(): assert pytest.approx(sind(0.0)) == 0.0 assert pytest.approx(sind(90.0)) == 1.0 assert pytest.approx(sind(180.0)) == 0.0 assert pytest.approx(sind(270.0)) == -1.0 def test_tand(): assert pytest.approx(tand(0.0)) == 0.0 assert pytest.approx(tand(45.0)) == 1.0 assert pytest.approx(tand(135.0)) == -1.0 assert pytest.approx(tand(180.0)) == 0.0 assert pytest.approx(tand(225.0)) == 1.0 assert pytest.approx(tand(315.0)) == -1.0 def test_wrap_180(): assert wrap_180(-180.0) == -180.0 assert wrap_180(180.0) == -180.0 assert wrap_180(-181.0) == 179.0 assert wrap_180(-179.0) == -179.0 assert wrap_180(179.0) == 179.0 assert wrap_180(181.0) == -179.0 def test_wrap_360(): assert wrap_360(0.0) == 0.0 assert wrap_360(360.0) == 0.0 assert wrap_360(-1.0) == 359.0 assert wrap_360(1.0) == 1.0 assert wrap_360(359.0) == 359.0 assert wrap_360(361.0) == 1.0 def test_wind_delta(): assert wind_delta(270.0) == 0.0 assert wind_delta(280.0) == 10.0 assert wind_delta(360.0) == 90.0 assert wind_delta(180.0) == 270.0 assert wind_delta(-10.0) == 80.0 assert wind_delta(-100.0) == 350.0 def test_make_wind_directions_adjacent(): test_conditions = [ [[0.0, 10.0], [0.0, 10.0]], [[0.0, 350.], [-10., 0.]], [[20.0, 25., 30.], [20.0, 25., 30.]], [[0.0, 350., 355., ], [-10., -5, 0]], [[0 ,2, 358], [-2, 0, 2]], [[0, 1, 359], [-1, 0, 1]], [np.arange(0,360,1), np.arange(0,360,1)], [sorted(np.arange(330,390,1)%360),np.arange(-30,30,1) ], ] for test_cond in test_conditions: wind_directions = np.array(test_cond[0]) expected_wind_directions = np.array(test_cond[1]) wind_directions_adjacent, sort_indices = make_wind_directions_adjacent(wind_directions) np.testing.assert_array_equal(wind_directions_adjacent, expected_wind_directions) np.testing.assert_array_equal(wind_directions[sort_indices]%360.0, wind_directions_adjacent%360.0) def test_check_and_identify_step_size(): # First set up a matrix of input directions, upsampling steps and expected ouputs test_conditions = [ [[270.0, 280.0], 10.0], [[0.0, 4.0], 4.0], [[0.0, 358.0], 2.0], [[0, 358], 2], [[10, 20, 30], 10], [[0, 10, 350], 10], [[0,1,359],1.0], [[0,356,358],2.0], [[4, 8, 12, 16], 4], [[0, 90, 180, 270], 90], [[0, 5, 10,355], 5], [np.arange(0,360,1), 1], [sorted(np.arange(330,390,1)%360), 1], ] for test_cond in test_conditions: wind_directions = np.array(test_cond[0]) expected_step = test_cond[1] step_size = check_and_identify_step_size(wind_directions) assert step_size == expected_step def test_check_and_identify_step_size_value_error(): # First set up a matrix of input directions, upsampling steps and expected ouputs test_conditions = [ [1,3,7], # Inconsistent step size [4, 3, 2], # Decreasing [5, 10, 15, 45], #Inconsistent step not connected to a wrapping ] for wind_directions in test_conditions: with pytest.raises(ValueError): check_and_identify_step_size(wind_directions) def test_rotate_coordinates_rel_west(): coordinates = np.array(list(zip(X_COORDS, Y_COORDS, Z_COORDS))) # For 270, the coordinates should not change. wind_directions = np.array([270.0]) x_rotated, y_rotated, z_rotated, _, _ = rotate_coordinates_rel_west( wind_directions, coordinates ) # Test that x_rotated has 2 dimensions np.testing.assert_equal(np.ndim(x_rotated), 2) # Assert the rotating to 270 doesn't change coordinates np.testing.assert_array_equal(X_COORDS, x_rotated[0]) np.testing.assert_array_equal(Y_COORDS, y_rotated[0]) np.testing.assert_array_equal(Z_COORDS, z_rotated[0]) # For 360, the coordinates should be rotated 90 degrees counter clockwise # from looking fown at the wind farm from above. The series of turbines # in a line (same y, increasing x) should change to a series of turbines # in parallel (same x, increasing y). # Since the rotation in `rotate_coordinates_rel_west` happens about the # center of all the points, adjust the coordinates so that the results are # adjusted to the baseline values. # NOTE: These adjustments are not general and will fail if the coordinates in # conftest change. wind_directions = np.array([360.0]) x_rotated, y_rotated, z_rotated, _, _ = rotate_coordinates_rel_west( wind_directions, coordinates ) np.testing.assert_almost_equal(Y_COORDS, x_rotated[0] - np.min(x_rotated[0])) np.testing.assert_almost_equal(X_COORDS, y_rotated[0] - np.min(y_rotated[0])) np.testing.assert_almost_equal( Z_COORDS + np.min(Z_COORDS), z_rotated[0] + np.min(z_rotated[0]) ) wind_directions = np.array([90.0]) x_rotated, y_rotated, z_rotated, _, _ = rotate_coordinates_rel_west( wind_directions, coordinates ) np.testing.assert_almost_equal(X_COORDS[-1:-4:-1], x_rotated[0]) np.testing.assert_almost_equal(Y_COORDS, y_rotated[0]) np.testing.assert_almost_equal(Z_COORDS, z_rotated[0]) def test_reverse_rotate_coordinates_rel_west(): # Test that appplying the rotation, and then the reverse produces the original coordinates # Test the reverse rotation coordinates = np.array([[x, y, z] for x, y, z in zip(X_COORDS, Y_COORDS, Z_COORDS)]) # Rotate to 360 (as in above function) wind_directions = np.array([360.0]) # Get the rotated coordinates ( x_rotated, y_rotated, z_rotated, x_center_of_rotation, y_center_of_rotation, ) = rotate_coordinates_rel_west(wind_directions, coordinates) # Go up to 4 dimensions (reverse function is expecting grid) grid_x = x_rotated[:, :, None, None] grid_y = y_rotated[:, :, None, None] grid_z = z_rotated[:, :, None, None] # Perform reverse rotation grid_x_reversed, grid_y_reversed, grid_z_reversed = reverse_rotate_coordinates_rel_west( wind_directions, grid_x, grid_y, grid_z, x_center_of_rotation, y_center_of_rotation, ) np.testing.assert_almost_equal(grid_x_reversed.squeeze(), coordinates[:,0].squeeze()) np.testing.assert_almost_equal(grid_y_reversed.squeeze(), coordinates[:,1].squeeze()) np.testing.assert_almost_equal(grid_z_reversed.squeeze(), coordinates[:,2].squeeze()) def test_nested_get(): example_dict = { 'a': { 'b': { 'c': 10 } } } assert nested_get(example_dict, ['a', 'b', 'c']) == 10 def test_nested_set(): example_dict = { 'a': { 'b': { 'c': 10 } } } nested_set(example_dict, ['a', 'b', 'c'], 20) assert nested_get(example_dict, ['a', 'b', 'c']) == 20 def test_is_all_scalar_dict(): example_dict_1 = { "a": 10, "b": 20.5, } assert is_all_scalar_dict(example_dict_1) example_dict_2 = { "a": 10, "b": np.array([1, 2, 3]), } assert not is_all_scalar_dict(example_dict_2) example_dict_3 = { "a": "hello", "b": 5, } assert is_all_scalar_dict(example_dict_3) example_dict_4 = { "a": 1, "b": {"c": 2}, } assert not is_all_scalar_dict(example_dict_4) ================================================ FILE: tests/v3_to_v4_convert_test/gch.yaml ================================================ ### # A name for this input file. # This is not currently only for the user's reference. name: GCH ### # A description of the contents of this input file. # This is not currently only for the user's reference. description: Three turbines using Gauss Curl Hybrid model ### # The earliest verion of FLORIS this input file supports. # This is not currently only for the user's reference. floris_version: v3.0.0 ### # Configure the logging level and where to show the logs. logging: ### # Settings for logging to the console (i.e. terminal). console: ### # Can be "true" or "false". enable: true ### # Set the severity to show output. Messages at this level or higher will be shown. # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING ### # Settings for logging to a file. file: ### # Can be "true" or "false". enable: false ### # Set the severity to show output. Messages at this level or higher will be shown. # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG". level: WARNING ### # Configure the solver for the type of simulation. solver: ### # Select the solver type. # Can be one of: "turbine_grid", "flow_field_grid", "flow_field_planar_grid". type: turbine_grid ### # Options for the turbine type selected above. See the solver documentation for available parameters. turbine_grid_points: 3 ### # Configure the turbine types and their placement within the wind farm. farm: ### # Coordinates for the turbine locations in the x-direction which is typically considered # to be the streamwise direction (left, right) when the wind is out of the west. # The order of the coordinates here corresponds to the index of the turbine in the primary # data structures. layout_x: - 0.0 ### # Coordinates for the turbine locations in the y-direction which is typically considered # to be the spanwise direction (up, down) when the wind is out of the west. # The order of the coordinates here corresponds to the index of the turbine in the primary # data structures. layout_y: - 0.0 ### # Listing of turbine types for placement at the x and y coordinates given above. # The list length must be 1 or the same as ``layout_x`` and ``layout_y``. If it is a # single value, all turbines are of the same type. Otherwise, the turbine type # is mapped to the location at the same index in ``layout_x`` and ``layout_y``. # The types can be either a name included in the turbine_library or # a full definition of a wind turbine directly. turbine_type: - !include nrel_5MW_v3.yaml ### # Configure the atmospheric conditions. flow_field: ### # Air density. air_density: 1.225 ### # The height to consider the "center" of the vertical wind speed profile # due to shear. With a shear exponent not 1, the wind speed at this height # will be the value given in ``wind_speeds``. Above and below this height, # the wind speed will change according to the shear profile; see # :py:meth:`.FlowField.initialize_velocity_field`. # For farms consisting of one wind turbine type, use ``reference_wind_height: -1`` # to use the hub height of the wind turbine definition. For multiple wind turbine # types, the reference wind height must be given explicitly. reference_wind_height: -1 ### # The level of turbulence intensity level in the wind. turbulence_intensity: 0.06 ### # The wind directions to include in the simulation. # 0 is north and 270 is west. wind_directions: - 270.0 ### # The exponent used to model the wind shear profile; see # :py:meth:`.FlowField.initialize_velocity_field`. wind_shear: 0.12 ### # The wind speeds to include in the simulation. wind_speeds: - 8.0 ### # The wind veer as a constant value for all points in the grid. wind_veer: 0.0 ### # The conditions that are specified for use with the multi-dimensional Cp/Ct capbility. # These conditions are external to FLORIS and specified by the user. They are used internally # through a nearest-neighbor selection process to choose the correct Cp/Ct interpolants # to use. These conditions are only used with the ``multidim_cp_ct`` velocity deficit model. multidim_conditions: Tp: 2.5 Hs: 3.01 ### # Configure the wake model. wake: ### # Select the models to use for the simulation. # See :py:mod:`~.wake` for a list # of available models and their descriptions. model_strings: ### # Select the wake combination model. combination_model: sosfs ### # Select the wake deflection model. deflection_model: gauss ### # Select the wake turbulence model. turbulence_model: crespo_hernandez ### # Select the wake velocity deficit model. velocity_model: gauss ### # Can be "true" or "false". enable_secondary_steering: true ### # Can be "true" or "false". enable_yaw_added_recovery: true ### # Can be "true" or "false". enable_transverse_velocities: true ### # Configure the parameters for the wake deflection model # selected above. # Additional blocks can be provided for # models that are not enabled, but the enabled model # must have a corresponding parameter block. wake_deflection_parameters: gauss: ad: 0.0 alpha: 0.58 bd: 0.0 beta: 0.077 dm: 1.0 ka: 0.38 kb: 0.004 jimenez: ad: 0.0 bd: 0.0 kd: 0.05 ### # Configure the parameters for the wake velocity deficit model # selected above. # Additional blocks can be provided for # models that are not enabled, but the enabled model # must have a corresponding parameter block. wake_velocity_parameters: cc: a_s: 0.179367259 b_s: 0.0118889215 c_s1: 0.0563691592 c_s2: 0.13290157 a_f: 3.11 b_f: -0.68 c_f: 2.41 alpha_mod: 1.0 gauss: alpha: 0.58 beta: 0.077 ka: 0.38 kb: 0.004 jensen: we: 0.05 ### # Configure the parameters for the wake turbulence model # selected above. # Additional blocks can be provided for # models that are not enabled, but the enabled model # must have a corresponding parameter block. wake_turbulence_parameters: crespo_hernandez: initial: 0.1 constant: 0.5 ai: 0.8 downstream: -0.32 ================================================ FILE: tests/v3_to_v4_convert_test/nrel_5MW_v3.yaml ================================================ ### # An ID for this type of turbine definition. # This is not currently used, but it will be enabled in the future. This should typically # match the root name of the file. turbine_type: 'nrel_5MW' ### # Setting for generator losses to power. generator_efficiency: 1.0 ### # Hub height. hub_height: 90.0 ### # Cosine exponent for power loss due to yaw misalignment. pP: 1.88 ### # Cosine exponent for power loss due to tilt. pT: 1.88 ### # Rotor diameter. rotor_diameter: 126.0 ### # Tip speed ratio defined as linear blade tip speed normalized by the incoming wind speed. TSR: 8.0 ### # The air density at which the Cp and Ct curves are defined. ref_density_cp_ct: 1.225 ### # The tilt angle at which the Cp and Ct curves are defined. This is used to capture # the effects of a floating platform on a turbine's power and wake. ref_tilt_cp_ct: 5.0 ### # Cp and Ct as a function of wind speed for the turbine's full range of operating conditions. power_thrust_table: power: - 0.0 - 0.000000 - 0.000000 - 0.178085 - 0.289075 - 0.349022 - 0.384728 - 0.406059 - 0.420228 - 0.428823 - 0.433873 - 0.436223 - 0.436845 - 0.436575 - 0.436511 - 0.436561 - 0.436517 - 0.435903 - 0.434673 - 0.433230 - 0.430466 - 0.378869 - 0.335199 - 0.297991 - 0.266092 - 0.238588 - 0.214748 - 0.193981 - 0.175808 - 0.159835 - 0.145741 - 0.133256 - 0.122157 - 0.112257 - 0.103399 - 0.095449 - 0.088294 - 0.081836 - 0.075993 - 0.070692 - 0.065875 - 0.061484 - 0.057476 - 0.053809 - 0.050447 - 0.047358 - 0.044518 - 0.041900 - 0.039483 - 0.0 - 0.0 thrust: - 0.0 - 0.0 - 0.0 - 0.99 - 0.99 - 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 - 0.0 - 0.0 wind_speed: - 0.0 - 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.01 - 25.02 - 50.0 ### # A boolean flag used when the user wants FLORIS to use the user-supplied multi-dimensional # Cp/Ct information. multi_dimensional_cp_ct: False ### # The path to the .csv file that contains the multi-dimensional Cp/Ct data. The format of this # file is such that any external conditions, such as wave height or wave period, that the # Cp/Ct data is dependent on come first, in column format. The last three columns of the .csv # file must be ``ws``, ``Cp``, and ``Ct``, in that order. An example of fictional data is given # in ``floris/turbine_library/iea_15MW_multi_dim_Tp_Hs.csv``. power_thrust_data_file: '../floris/turbine_library/iea_15MW_multi_dim_Tp_Hs.csv' ================================================ FILE: tests/wake_unit_tests.py ================================================ from floris.core import WakeModelManager from tests.conftest import SampleInputs def test_asdict(sample_inputs_fixture: SampleInputs): wake_model_manager = WakeModelManager.from_dict(sample_inputs_fixture.wake) dict1 = wake_model_manager.as_dict() new_wake = WakeModelManager.from_dict(dict1) dict2 = new_wake.as_dict() assert dict1 == dict2 ================================================ FILE: tests/wind_data_integration_test.py ================================================ import copy from pathlib import Path import numpy as np import pytest from floris import ( TimeSeries, WindRose, WindTIRose, ) from floris.wind_data import WindDataBase TEST_DATA = Path(__file__).resolve().parent / "data" class ChildClassTest(WindDataBase): def __init__(self): pass def test_bad_inheritance(): """ Verifies that a child class of WindDataBase must implement the unpack method. """ test_class = ChildClassTest() with pytest.raises(NotImplementedError): test_class.unpack() def test_time_series_instantiation(): wind_directions = np.array([270, 280, 290]) wind_speeds = np.array([5, 5, 5]) # Test that TI require with pytest.raises(TypeError): TimeSeries(wind_directions, wind_speeds) # Test that passing a float TI returns a list of length matched to wind directions time_series = TimeSeries(wind_directions, wind_speeds, turbulence_intensities=0.06) np.testing.assert_allclose(time_series.turbulence_intensities, [0.06, 0.06, 0.06]) # Test that passing floats to wind directions and wind speeds returns a list of # length turbulence intensities time_series = TimeSeries(270.0, 8.0, turbulence_intensities=np.array([0.06, 0.07, 0.08])) np.testing.assert_allclose(time_series.wind_directions, [270, 270, 270]) np.testing.assert_allclose(time_series.wind_speeds, [8, 8, 8]) # Test that passing in all floats raises a type error with pytest.raises(TypeError): TimeSeries(270.0, 8.0, 0.06) # Test casting of both wind speeds and TI time_series = TimeSeries(wind_directions, 8.0, 0.06) np.testing.assert_allclose(time_series.wind_speeds, [8, 8, 8]) np.testing.assert_allclose(time_series.turbulence_intensities, [0.06, 0.06, 0.06]) # Test the passing in a 1D array of turbulence intensities which is longer than the # wind directions and wind speeds raises an error with pytest.raises(ValueError): TimeSeries( wind_directions, wind_speeds, turbulence_intensities=np.array([0.06, 0.07, 0.08, 0.09]) ) def test_wind_rose_init(): """ The wind directions and wind speeds can have any length, but the frequency array must have shape (n wind directions, n wind speeds) """ # Test that passing in unevenly spaced wind directions or wind speeds raises an error with pytest.raises(ValueError): WindRose(np.array([270, 280, 290, 310]), np.array([6, 7]), 0.06) with pytest.raises(ValueError): WindRose(np.array([270, 280, 290]), np.array([6, 7, 10]), 0.06) # Test that passing in decreasing wind directions raises an error with pytest.raises(ValueError): WindRose(np.array([290, 280, 270]), np.array([6, 7]), 0.06) wind_directions = np.array([270, 280, 290]) wind_speeds = np.array([6, 7]) # Pass ti_table in as a single float and confirm it is broadcast to the correct shape wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06) np.testing.assert_allclose( wind_rose.ti_table, np.array([[0.06, 0.06], [0.06, 0.06], [0.06, 0.06]]) ) # Pass ti_table in as a 2D array and confirm it is used as is ti_table = np.array([[0.06, 0.06], [0.06, 0.06], [0.06, 0.06]]) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=ti_table) np.testing.assert_allclose(wind_rose.ti_table, ti_table) # Confirm passing in a ti_table that is 1D raises an error with pytest.raises(ValueError): WindRose( wind_directions, wind_speeds, ti_table=np.array([0.06, 0.06, 0.06, 0.06, 0.06, 0.06]) ) # Confirm passing in a ti_table that is wrong dimensions raises an error with pytest.raises(ValueError): WindRose(wind_directions, wind_speeds, ti_table=np.ones((3, 3))) # This should be ok since the frequency array shape matches the wind directions # and wind speeds _ = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=np.ones((3, 2))) # This should raise an error since the frequency array shape does not # match the wind directions and wind speeds with pytest.raises(ValueError): WindRose(wind_directions, wind_speeds, 0.06, np.ones((3, 3))) # Test that instantiating with non-integer wind directions and wind speeds works wind_directions = np.array([270.0, 280.0, 290.0]) wind_speeds = np.array([6.0, 7.0]) _ = WindRose(wind_directions, wind_speeds, ti_table=0.06) wind_directions = np.array([270.5, 280.5, 290.5]) wind_speeds = np.array([6.5, 7.5]) _ = WindRose(wind_directions, wind_speeds, ti_table=0.06) # Test that instantiating with non-integer steps works _ = WindRose( wind_directions=np.linspace(0.0, 360.0, 100, endpoint=False), wind_speeds=np.array([5.0]), ti_table=0.06, ) def test_wind_rose_grid(): wind_directions = np.array([270, 280, 290]) wind_speeds = np.array([6, 7]) wind_rose = WindRose(wind_directions, wind_speeds, 0.06) # Wind direction grid has the same dimensions as the frequency table assert wind_rose.wd_grid.shape == wind_rose.freq_table.shape # Flattening process occurs wd first # This is each wind direction for each wind speed: np.testing.assert_allclose(wind_rose.wd_flat, [270, 270, 280, 280, 290, 290]) def test_wind_rose_unpack(): wind_directions = np.array([270, 280, 290]) wind_speeds = np.array([6, 7]) freq_table = np.array([[1.0, 0.0], [0, 1.0], [0, 0]]) # First test using default assumption only non-zero frequency cases computed wind_rose = WindRose(wind_directions, wind_speeds, 0.06, freq_table) ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, freq_table_unpack, value_table_unpack, heterogeneous_inflow_config, ) = wind_rose.unpack() # Given the above frequency table with zeros for a few elements, # we expect only the (270 deg, 6 m/s) and (280 deg, 7 m/s) rows np.testing.assert_allclose(wind_directions_unpack, [270, 280]) np.testing.assert_allclose(wind_speeds_unpack, [6, 7]) np.testing.assert_allclose(ti_table_unpack, [0.06, 0.06]) np.testing.assert_allclose(freq_table_unpack, [0.5, 0.5]) # In this case n_findex is the length of the wind combinations that are # non-zero frequency assert wind_rose.n_findex == 2 # Now test computing 0-freq cases too wind_rose = WindRose( wind_directions, wind_speeds, freq_table, compute_zero_freq_occurrence=True ) ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, freq_table_unpack, value_table_unpack, heterogeneous_inflow_config, ) = wind_rose.unpack() # Expect now to compute all combinations np.testing.assert_allclose(wind_directions_unpack, [270, 270, 280, 280, 290, 290]) # In this case n_findex is the total number of wind combinations assert wind_rose.n_findex == 6 # Adding tests of unpack_multidim_conditions here, since these will be included in unpack() # in a future release multidim_conditions_scalar = {"TI": 0.06, "Hs": 3.1} wind_rose = WindRose( wind_directions, wind_speeds, 0.06, freq_table, multidim_conditions=multidim_conditions_scalar ) multidim_conditions_unpacked = wind_rose.unpack_multidim_conditions() for k, v in multidim_conditions_unpacked.items(): np.testing.assert_allclose(v, [multidim_conditions_scalar[k]] * wind_rose.n_findex) # Array multi-dimensional conditions that are not the correct shape (n_wd x n_ws) multidim_conditions_array_bad_shape = {"a": np.array([1, 2, 3, 4, 5, 6])} with pytest.raises(ValueError): WindRose( wind_directions, wind_speeds, 0.06, freq_table, multidim_conditions=multidim_conditions_array_bad_shape ) # Correct shape multi-dimensional conditions, wrong number of values multidim_conditions_array_wrong_size = {"a": np.array([[0.06, 0.07], [0.08, 0.09]])} with pytest.raises(ValueError): WindRose( wind_directions, wind_speeds, 0.06, freq_table, multidim_conditions=multidim_conditions_array_wrong_size ) # Correct shape and size multidim_conditions_array = {"a": np.array([[0.06, 0.07], [0.08, 0.09], [0.10, 0.11]])} wind_rose = WindRose( wind_directions, wind_speeds, 0.06, freq_table, multidim_conditions=multidim_conditions_array ) multidim_conditions_unpacked = wind_rose.unpack_multidim_conditions() for k, v in multidim_conditions_unpacked.items(): assert k in multidim_conditions_array.keys() assert v.shape == (2,) # 2 remaining conditions after frequency filtering def test_unpack_for_reinitialize(): wind_directions = np.array([270, 280, 290]) wind_speeds = np.array([6, 7]) freq_table = np.array([[1.0, 0.0], [0, 1.0], [0, 0]]) # First test using default assumption only non-zero frequency cases computed wind_rose = WindRose(wind_directions, wind_speeds, 0.06, freq_table) ( wind_directions_unpack, wind_speeds_unpack, ti_table_unpack, heterogeneous_inflow_config, ) = wind_rose.unpack_for_reinitialize() # Given the above frequency table, would only expect the # (270 deg, 6 m/s) and (280 deg, 7 m/s) rows np.testing.assert_allclose(wind_directions_unpack, [270, 280]) np.testing.assert_allclose(wind_speeds_unpack, [6, 7]) np.testing.assert_allclose(ti_table_unpack, [0.06, 0.06]) def test_wind_rose_downsample(): wind_directions = np.array([0, 2, 4, 6, 8, 10]) wind_speeds = np.array([8]) freq_table = np.array([[1.0], [1.0], [1.0], [1.0], [1.0], [1.0]]) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) # Test that aggregating without specifying new steps returns the same wind_rose_aggregate = wind_rose.downsample(inplace=False) np.testing.assert_allclose(wind_rose.wind_directions, wind_rose_aggregate.wind_directions) np.testing.assert_allclose(wind_rose.wind_speeds, wind_rose_aggregate.wind_speeds) np.testing.assert_allclose(wind_rose.freq_table_flat, wind_rose_aggregate.freq_table_flat) # Now test aggregating the wind direction to 5 deg bins wind_rose_aggregate = wind_rose.downsample(wd_step=5.0, inplace=False) np.testing.assert_allclose(wind_rose_aggregate.wind_directions, [0, 5, 10]) np.testing.assert_allclose(wind_rose_aggregate.freq_table_flat, [2 / 6, 2 / 6, 2 / 6]) # Test that the default inplace behavior is to modifies the original object as expected wind_rose_2 = copy.deepcopy(wind_rose) wind_rose_2.downsample(inplace=True) np.testing.assert_allclose(wind_rose.wind_directions, wind_rose_2.wind_directions) np.testing.assert_allclose(wind_rose.wind_speeds, wind_rose_2.wind_speeds) np.testing.assert_allclose(wind_rose.freq_table_flat, wind_rose_2.freq_table_flat) wind_rose_2.downsample(wd_step=5.0, inplace=True) np.testing.assert_allclose(wind_rose_aggregate.wind_directions, wind_rose_2.wind_directions) np.testing.assert_allclose(wind_rose_aggregate.wind_speeds, wind_rose_2.wind_speeds) np.testing.assert_allclose(wind_rose_aggregate.freq_table_flat, wind_rose_2.freq_table_flat) # Test that aggregate function returns identical result to downsample function wind_rose_aggregate = wind_rose.aggregate(wd_step=5.0, inplace=False) wind_rose_downsample = wind_rose.downsample(wd_step=5.0, inplace=False) np.testing.assert_allclose( wind_rose_aggregate.freq_table_flat, wind_rose_downsample.freq_table_flat ) def test_wind_rose_upsample_directions(): # Test wind direction upsampling under a range of conditions # First set up a matrix of input directions, upsampling steps and expected ouputs test_conditions = [ [[270.0, 280.0], 5.0, [267.5, 272.5, 277.5, 282.5]], [[10, 12, 14.0], 1.0, [9.5, 10.5, 11.5, 12.5, 13.5, 14.5]], [[0.0, 90.0, 180.0, 270.0], 1.0, np.arange(0.5, 360, 1.0)], [[0.0, 10., 20.,], 5.0, [2.5, 7.5,12.5, 17.5, 22.5, 357.5]], ] for test_cond in test_conditions: wind_directions = np.array(test_cond[0]) wd_step = test_cond[1] expected_directions = np.array(test_cond[2]) # Single wind speed case wind_speeds = np.array([8.0]) freq_table = np.ones((len(wind_directions), len(wind_speeds))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones((len(expected_directions), len(wind_speeds))) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) wind_rose_resample = wind_rose.upsample(wd_step=wd_step) np.testing.assert_allclose(wind_rose_resample.wind_directions, expected_directions) np.testing.assert_allclose(wind_rose_resample.wind_speeds, wind_speeds) np.testing.assert_allclose(wind_rose_resample.freq_table, expected_freq_table) # Two wind speed case wind_speeds = np.array([8.0, 10.0]) freq_table = np.ones((len(wind_directions), len(wind_speeds))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones((len(expected_directions), len(wind_speeds))) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) wind_rose_resample = wind_rose.upsample(wd_step=wd_step) np.testing.assert_allclose(wind_rose_resample.wind_directions, expected_directions) np.testing.assert_allclose(wind_rose_resample.wind_speeds, wind_speeds) np.testing.assert_allclose(wind_rose_resample.freq_table, expected_freq_table) def test_wind_rose_upsample_speeds(): # Test wind speed upsampling under a range of conditions # First set up a matrix of input directions, upsampling steps and expected outputs test_conditions = [ [[5, 10.0], 1.0, [3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]], [np.arange(0, 25, 2.0), 1.0, np.arange(0, 25, 1.0)], ] for test_cond in test_conditions: wind_speeds = np.array(test_cond[0]) ws_step = test_cond[1] expected_speeds = np.array(test_cond[2]) # Single wind direction case wind_directions = np.array([270.0]) freq_table = np.ones((len(wind_directions), len(wind_speeds))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones((len(wind_directions), len(expected_speeds))) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) wind_rose_resample = wind_rose.upsample(ws_step=ws_step) np.testing.assert_allclose(wind_rose_resample.wind_directions, wind_directions) np.testing.assert_allclose(wind_rose_resample.wind_speeds, expected_speeds) np.testing.assert_allclose(wind_rose_resample.freq_table, expected_freq_table) # Two wind direction case wind_directions = np.array([270.0, 280.0]) freq_table = np.ones((len(wind_directions), len(wind_speeds))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones((len(wind_directions), len(expected_speeds))) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) wind_rose_resample = wind_rose.upsample(ws_step=ws_step) np.testing.assert_allclose(wind_rose_resample.wind_directions, wind_directions) np.testing.assert_allclose(wind_rose_resample.wind_speeds, expected_speeds) np.testing.assert_allclose(wind_rose_resample.freq_table, expected_freq_table) def test_wind_rose_upsample(): # Test that interpolating without specifying new steps returns the same wind rose wind_directions = np.array([0, 2, 4, 6, 8, 10]) wind_speeds = np.array([8, 10]) freq_table = np.ones((6, 2)) freq_table = freq_table / np.sum(freq_table) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) wind_rose_resample = wind_rose.upsample(inplace=False) np.testing.assert_allclose(wind_rose.wind_directions, wind_rose_resample.wind_directions) np.testing.assert_allclose(wind_rose.wind_speeds, wind_rose_resample.wind_speeds) np.testing.assert_allclose(wind_rose.freq_table_flat, wind_rose_resample.freq_table_flat) # Test interpolating TI along the wind direction axis wind_directions = np.array([270, 280]) wind_speeds = np.array([6, 7]) ti_table = np.array([[0.06, 0.06], [0.07, 0.07]]) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=ti_table) wind_rose_resample = wind_rose.upsample(wd_step=5.0, ws_step=1.0, inplace=False) # Check that the resample ti_table is correct np.testing.assert_allclose(wind_rose_resample.wind_directions, [267.5, 272.5, 277.5, 282.5]) np.testing.assert_allclose(wind_rose_resample.wind_speeds, [6, 7]) np.testing.assert_allclose( wind_rose_resample.ti_table, np.array([[0.06, 0.06], [0.0625, 0.0625], [0.0675, 0.0675], [0.07, 0.07]]), ) # Test interpolating frequency along the wind speed axis freq_table = np.array([[1 / 6, 2 / 6], [1 / 6, 2 / 6]]) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) wind_rose_resample = wind_rose.upsample(wd_step=10.0, ws_step=0.5, inplace=False) freq_table_expected = np.array( [[1 / 6, 1.25 / 6, 1.75 / 6, 2 / 6], [1 / 6, 1.25 / 6, 1.75 / 6, 2 / 6]] ) freq_table_expected = freq_table_expected / np.sum(freq_table_expected) # Check that the resample freq_table is correct np.testing.assert_allclose(wind_rose_resample.wind_directions, [270, 280]) np.testing.assert_allclose(wind_rose_resample.wind_speeds, [5.75, 6.25, 6.75, 7.25]) np.testing.assert_allclose(wind_rose_resample.freq_table, freq_table_expected) # Test resampling both wind speed and wind directions ti_table = np.array([[0.01, 0.02], [0.03, 0.04]]) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=ti_table) wind_rose_resample = wind_rose.upsample(wd_step=5.0, ws_step=0.5, inplace=False) # Check that the resample ti_table is correct np.testing.assert_allclose(wind_rose_resample.wind_directions, [267.5, 272.5, 277.5, 282.5]) np.testing.assert_allclose(wind_rose_resample.wind_speeds, [5.75, 6.25, 6.75, 7.25]) # Test the using the old function name returns the same result wind_rose_resample_old = wind_rose.resample_by_interpolation( wd_step=5.0, ws_step=0.5, inplace=False ) np.testing.assert_allclose( wind_rose_resample.wind_directions, wind_rose_resample_old.wind_directions ) np.testing.assert_allclose(wind_rose_resample.wind_speeds, wind_rose_resample_old.wind_speeds) np.testing.assert_allclose(wind_rose_resample.ti_table, wind_rose_resample_old.ti_table) np.testing.assert_allclose(wind_rose_resample.freq_table, wind_rose_resample_old.freq_table) def test_wind_rose_upsample_wrapping(): # Test that interpolating without specifying new steps returns the same wind rose wind_directions = np.array([0, 10, 350]) wind_speeds = np.array([8]) freq_table = np.ones((3, 1)) freq_table[-1:, :] = 0.0 freq_table[1, :] = 2.0 freq_table = freq_table / np.sum(freq_table) wind_rose = WindRose(wind_directions, wind_speeds, ti_table=0.06, freq_table=freq_table) wind_rose_resample = wind_rose.upsample(wd_step = 5.0, inplace=False) np.testing.assert_allclose(wind_rose_resample.wind_directions, [2.5, 7.5, 12.5, 347.5, 352.5, 357.5]) np.testing.assert_allclose(wind_rose.wind_speeds, wind_rose_resample.wind_speeds) expected_freq_table = np.ones((6, 1)) expected_freq_table[0, :] = 1.25 expected_freq_table[1, :] = 1.75 expected_freq_table[2, :] = 2.0 expected_freq_table[3, :] = 0.0 expected_freq_table[4, :] = 0.25 expected_freq_table[5, :] = 0.75 expected_freq_table = expected_freq_table / np.sum(expected_freq_table) np.testing.assert_allclose(wind_rose_resample.freq_table, expected_freq_table) def test_wind_ti_rose_upsample_directions(): # Test wind direction upsampling under a range of conditions # First set up a matrix of input directions, upsampling steps and expected ouputs test_conditions = [ [[270.0, 280.0], 5.0, [267.5, 272.5, 277.5, 282.5]], [[10, 12, 14.0], 1.0, [9.5, 10.5, 11.5, 12.5, 13.5, 14.5]], [[0.0, 90.0, 180.0, 270.0], 1.0, np.arange(0.5, 360, 1.0)], ] for test_cond in test_conditions: wind_directions = np.array(test_cond[0]) wd_step = test_cond[1] expected_directions = np.array(test_cond[2]) # Single wind speed / ti case wind_speeds = np.array([8.0]) turbulence_intensities = np.array([0.06]) freq_table = np.ones((len(wind_directions), len(wind_speeds), len(turbulence_intensities))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones( (len(expected_directions), len(wind_speeds), len(turbulence_intensities)) ) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_ti_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities=turbulence_intensities, freq_table=freq_table, ) wind_ti_rose_resample = wind_ti_rose.upsample(wd_step=wd_step) np.testing.assert_allclose(wind_ti_rose_resample.wind_directions, expected_directions) np.testing.assert_allclose(wind_ti_rose_resample.wind_speeds, wind_speeds) np.testing.assert_allclose( wind_ti_rose_resample.turbulence_intensities, turbulence_intensities ) np.testing.assert_allclose(wind_ti_rose_resample.freq_table, expected_freq_table) # Multiple wind speed / ti case wind_speeds = np.array([8.0, 10.0]) turbulence_intensities = np.array([0.06, 0.07]) freq_table = np.ones((len(wind_directions), len(wind_speeds), len(turbulence_intensities))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones( (len(expected_directions), len(wind_speeds), len(turbulence_intensities)) ) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_ti_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities=turbulence_intensities, freq_table=freq_table, ) wind_ti_rose_resample = wind_ti_rose.upsample(wd_step=wd_step) np.testing.assert_allclose(wind_ti_rose_resample.wind_directions, expected_directions) np.testing.assert_allclose(wind_ti_rose_resample.wind_speeds, wind_speeds) np.testing.assert_allclose( wind_ti_rose_resample.turbulence_intensities, turbulence_intensities ) np.testing.assert_allclose(wind_ti_rose_resample.freq_table, expected_freq_table) def test_wind_ti_rose_upsample_speeds(): # Test wind speed upsampling under a range of conditions # First set up a matrix of input directions, upsampling steps and expected ouputs test_conditions = [ [[5, 10.0], 1.0, [3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]], [np.arange(0, 25, 2.0), 1.0, np.arange(0, 25, 1.0)], ] for test_cond in test_conditions: wind_speeds = np.array(test_cond[0]) ws_step = test_cond[1] expected_speeds = np.array(test_cond[2]) # Single wind direction / ti case wind_directions = np.array([270.0]) turbulence_intensities = np.array([0.06]) freq_table = np.ones((len(wind_directions), len(wind_speeds), len(turbulence_intensities))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones( (len(wind_directions), len(expected_speeds), len(turbulence_intensities)) ) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_ti_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities=turbulence_intensities, freq_table=freq_table, ) wind_ti_rose_resample = wind_ti_rose.upsample(ws_step=ws_step) np.testing.assert_allclose(wind_ti_rose_resample.wind_directions, wind_directions) np.testing.assert_allclose(wind_ti_rose_resample.wind_speeds, expected_speeds) np.testing.assert_allclose( wind_ti_rose_resample.turbulence_intensities, turbulence_intensities ) np.testing.assert_allclose(wind_ti_rose_resample.freq_table, expected_freq_table) # Multiple wind direction / ti case wind_directions = np.array([270.0, 280.0]) turbulence_intensities = np.array([0.06, 0.07]) freq_table = np.ones((len(wind_directions), len(wind_speeds), len(turbulence_intensities))) freq_table = freq_table / np.sum(freq_table) expected_freq_table = np.ones( (len(wind_directions), len(expected_speeds), len(turbulence_intensities)) ) expected_freq_table = expected_freq_table / np.sum(expected_freq_table) wind_ti_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities=turbulence_intensities, freq_table=freq_table, ) wind_ti_rose_resample = wind_ti_rose.upsample(ws_step=ws_step) np.testing.assert_allclose(wind_ti_rose_resample.wind_directions, wind_directions) np.testing.assert_allclose(wind_ti_rose_resample.wind_speeds, expected_speeds) np.testing.assert_allclose( wind_ti_rose_resample.turbulence_intensities, turbulence_intensities ) np.testing.assert_allclose(wind_ti_rose_resample.freq_table, expected_freq_table) def test_upsample_ti_rose(): wind_directions = np.array([0, 2, 4, 6, 8, 10]) wind_speeds = np.array([8, 10]) turbulence_intensities = np.array([0.05, 0.1]) freq_table = np.ones((6, 2, 2)) freq_table = freq_table / np.sum(freq_table) wind_ti_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table=freq_table ) # Test that interpolating without specifying new steps returns the same wind_ti_rose_resample = wind_ti_rose.upsample(inplace=False) np.testing.assert_allclose(wind_ti_rose.wind_directions, wind_ti_rose_resample.wind_directions) np.testing.assert_allclose(wind_ti_rose.wind_speeds, wind_ti_rose_resample.wind_speeds) np.testing.assert_allclose( wind_ti_rose.turbulence_intensities, wind_ti_rose_resample.turbulence_intensities ) np.testing.assert_allclose(wind_ti_rose.freq_table_flat, wind_ti_rose_resample.freq_table_flat) # Test interpolating frequency along the wind speed axis wind_directions = np.array([270, 280]) wind_speeds = np.array([6, 8]) turbulence_intensities = np.array([0.05, 0.1]) freq_table = np.ones((2, 2, 2)) freq_table[:, 1, :] = 2.0 freq_table = freq_table / np.sum(freq_table) wind_ti_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table=freq_table ) wind_ti_rose_resample = wind_ti_rose.upsample( wd_step=10.0, ws_step=1.0, ti_step=0.05, inplace=False ) freq_table_expected = np.ones((2, 4, 2)) freq_table_expected[:, 0, :] = 1.0 freq_table_expected[:, 1, :] = 1.25 freq_table_expected[:, 2, :] = 1.75 freq_table_expected[:, 3, :] = 2.0 freq_table_expected = freq_table_expected / np.sum(freq_table_expected) # Check that the resample freq_table is correct np.testing.assert_allclose(wind_ti_rose_resample.wind_directions, [270, 280]) np.testing.assert_allclose(wind_ti_rose_resample.wind_speeds, [5.5, 6.5, 7.5, 8.5]) np.testing.assert_allclose(wind_ti_rose_resample.turbulence_intensities, [0.05, 0.1]) np.testing.assert_allclose(wind_ti_rose_resample.freq_table, freq_table_expected) # Test interpolating frequency along the wind direction axis with wrapping wind_directions = np.array([0, 350]) wind_speeds = np.array([7]) turbulence_intensities = np.array([0.1]) freq_table = np.ones((2, 1, 1)) freq_table[1, :, :] = 2.0 freq_table = freq_table / np.sum(freq_table) wind_ti_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table=freq_table ) wind_ti_rose_resample = wind_ti_rose.upsample( wd_step=5.0, inplace=False ) freq_table_expected = np.ones((4, 1, 1)) freq_table_expected[0, :, :] = 1.0 freq_table_expected[1, :, :] = 2.0 freq_table_expected[2, :, :] = 1.75 freq_table_expected[3, :, :] = 1.25 freq_table_expected = freq_table_expected / np.sum(freq_table_expected) # Check that the resample freq_table is correct np.testing.assert_allclose(wind_ti_rose_resample.wind_directions, [ 2.5, 347.5, 352.5, 357.5]) np.testing.assert_allclose(wind_ti_rose_resample.wind_speeds, [7.0]) np.testing.assert_allclose(wind_ti_rose_resample.turbulence_intensities, [0.1]) np.testing.assert_allclose(wind_ti_rose_resample.freq_table, freq_table_expected) def test_wrap_wind_directions_near_360(): wd_step = 5.0 wd_values = np.array([0, 180, 357, 357.5, 358]) time_series = TimeSeries(np.array([0]), np.array([0]), 0.06) wd_wrapped = time_series._wrap_wind_directions_near_360(wd_values, wd_step) expected_result = np.array([0, 180, 357, -wd_step / 2.0, -2.0]) assert np.allclose(wd_wrapped, expected_result) def test_time_series_to_WindRose(): # Test just 1 wind speed wind_directions = np.array([259.8, 260.2, 264.3]) wind_speeds = np.array([5.0, 5.0, 5.1]) time_series = TimeSeries(wind_directions, wind_speeds, 0.06) wind_rose = time_series.to_WindRose(wd_step=2.0, ws_step=1.0) # The wind directions should be 260, 262 and 264 because they're binned # to the nearest 2 deg increment assert np.allclose(wind_rose.wind_directions, [260, 262, 264]) # Freq table should have dimension of 3 wd x 1 ws because the wind speeds # are all binned to the same value given the `ws_step` size freq_table = wind_rose.freq_table assert freq_table.shape[0] == 3 assert freq_table.shape[1] == 1 # The frequencies should [2/3, 0, 1/3] given that 2 of the data points # fall in the 260 deg bin, 0 in the 262 deg bin and 1 in the 264 deg bin assert np.allclose(freq_table.squeeze(), [2 / 3, 0, 1 / 3]) # Test just 2 wind speeds wind_directions = np.array([259.8, 260.2, 264.3]) wind_speeds = np.array([5.0, 5.0, 6.1]) time_series = TimeSeries(wind_directions, wind_speeds, 0.06) wind_rose = time_series.to_WindRose(wd_step=2.0, ws_step=1.0) # The wind directions should be 260, 262 and 264 assert np.allclose(wind_rose.wind_directions, [260, 262, 264]) # The wind speeds should be 5 and 6 assert np.allclose(wind_rose.wind_speeds, [5, 6]) # Freq table should have dimension of 3 wd x 2 ws freq_table = wind_rose.freq_table assert freq_table.shape[0] == 3 assert freq_table.shape[1] == 2 # The frequencies should [2/3, 0, 1/3] assert freq_table[0, 0] == 2 / 3 assert freq_table[2, 1] == 1 / 3 # The turbulence intensity table should be 0.06 for all bins ti_table = wind_rose.ti_table # Assert that table entires which are not nan are equal to 0.06 assert np.allclose(ti_table[~np.isnan(ti_table)], 0.06) def test_time_series_to_WindRose_wrapping(): wind_directions = np.arange(0.0, 360.0, 0.25) wind_speeds = 8.0 * np.ones_like(wind_directions) time_series = TimeSeries(wind_directions, wind_speeds, 0.06) wind_rose = time_series.to_WindRose(wd_step=2.0, ws_step=1.0) # Expert for the first bin in this case to be 0, and the final to be 358 # and both to have equal numbers of points np.testing.assert_almost_equal(wind_rose.wind_directions[0], 0) np.testing.assert_almost_equal(wind_rose.wind_directions[-1], 358) np.testing.assert_almost_equal(wind_rose.freq_table[0, 0], wind_rose.freq_table[-1, 0]) def test_time_series_to_WindRose_with_ti(): wind_directions = np.array([259.8, 260.2, 260.3, 260.1]) wind_speeds = np.array([5.0, 5.0, 5.1, 7.2]) turbulence_intensities = np.array([0.5, 1.0, 1.5, 2.0]) time_series = TimeSeries( wind_directions, wind_speeds, turbulence_intensities=turbulence_intensities, ) wind_rose = time_series.to_WindRose(wd_step=2.0, ws_step=1.0) # Turbulence intensity should average to 1 in the 5 m/s bin and 2 in the 7 m/s bin ti_table = wind_rose.ti_table np.testing.assert_almost_equal(ti_table[0, 0], 1) np.testing.assert_almost_equal(ti_table[0, 2], 2) # The 6 m/s bin should be empty freq_table = wind_rose.freq_table np.testing.assert_almost_equal(freq_table[0, 1], 0) def test_wind_ti_rose_init(): """ The wind directions, wind speeds, and turbulence intensities can have any length, but the frequency array must have shape (n wind directions, n wind speeds, n turbulence intensities) """ wind_directions = np.array([270, 280, 290, 300]) wind_speeds = np.array([6, 7, 8]) turbulence_intensities = np.array([0.05, 0.1]) # This should be ok _ = WindTIRose(wind_directions, wind_speeds, turbulence_intensities) # This should be ok since the frequency array shape matches the wind directions # and wind speeds _ = WindTIRose(wind_directions, wind_speeds, turbulence_intensities, np.ones((4, 3, 2))) # This should raise an error since the frequency array shape does not # match the wind directions and wind speeds with pytest.raises(ValueError): WindTIRose(wind_directions, wind_speeds, turbulence_intensities, np.ones((3, 3, 3))) # Test that instantiating with non-integer wind directions and wind speeds works _ = WindTIRose(wind_directions+0.5, wind_speeds+0.2, turbulence_intensities) # Test that instantiating with non-integer steps works _ = WindTIRose( wind_directions=np.linspace(0.0, 360.0, 100, endpoint=False), wind_speeds=np.array([5.0]), turbulence_intensities=turbulence_intensities, ) def test_wind_ti_rose_grid(): wind_directions = np.array([270, 280, 290, 300]) wind_speeds = np.array([6, 7, 8]) turbulence_intensities = np.array([0.05, 0.1]) wind_rose = WindTIRose(wind_directions, wind_speeds, turbulence_intensities) # Wind direction grid has the same dimensions as the frequency table assert wind_rose.wd_grid.shape == wind_rose.freq_table.shape # Flattening process occurs wd first # This is each wind direction for each wind speed: np.testing.assert_allclose(wind_rose.wd_flat, 6 * [270] + 6 * [280] + 6 * [290] + 6 * [300]) def test_wind_ti_rose_unpack(): wind_directions = np.array([270, 280, 290, 300]) wind_speeds = np.array([6, 7, 8]) turbulence_intensities = np.array([0.05, 0.1]) freq_table = np.array( [ [[1.0, 0.0], [1.0, 0.0], [0.0, 0.0]], [[1.0, 0.0], [1.0, 0.0], [0.0, 0.0]], [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], ] ) # First test using default assumption only non-zero frequency cases computed wind_rose = WindTIRose(wind_directions, wind_speeds, turbulence_intensities, freq_table) ( wind_directions_unpack, wind_speeds_unpack, turbulence_intensities_unpack, freq_table_unpack, value_table_unpack, heterogeneous_inflow_config, ) = wind_rose.unpack() # Given the above frequency table with zeros for a few elements, # we expect only combinations of wind directions of 270 and 280 deg, # wind speeds of 6 and 7 m/s, and a TI of 5% np.testing.assert_allclose(wind_directions_unpack, [270, 270, 280, 280]) np.testing.assert_allclose(wind_speeds_unpack, [6, 7, 6, 7]) np.testing.assert_allclose(turbulence_intensities_unpack, [0.05, 0.05, 0.05, 0.05]) np.testing.assert_allclose(freq_table_unpack, [0.25, 0.25, 0.25, 0.25]) # In this case n_findex is the length of the wind combinations that are # non-zero frequency assert wind_rose.n_findex == 4 # Now test computing 0-freq cases too wind_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table, compute_zero_freq_occurrence=True, ) ( wind_directions_unpack, wind_speeds_unpack, turbulence_intensities_unpack, freq_table_unpack, value_table_unpack, heterogeneous_inflow_config, ) = wind_rose.unpack() # Expect now to compute all combinations np.testing.assert_allclose( wind_directions_unpack, 6 * [270] + 6 * [280] + 6 * [290] + 6 * [300] ) # In this case n_findex is the total number of wind combinations assert wind_rose.n_findex == 24 # Adding tests of unpack_multidim_conditions here, since these will be included in unpack() # in a future release multidim_conditions_scalar = {"TI": 0.06, "Hs": 3.1} wind_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table, multidim_conditions=multidim_conditions_scalar ) multidim_conditions_unpacked = wind_rose.unpack_multidim_conditions() for k, v in multidim_conditions_unpacked.items(): np.testing.assert_allclose(v, [multidim_conditions_scalar[k]] * wind_rose.n_findex) # Array multi-dimensional conditions that are not the correct shape (n_wd x n_ws x n_ti) multidim_conditions_array_bad_shape = {"a": np.array([1, 2, 3, 4, 5, 6])} with pytest.raises(ValueError): WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table, multidim_conditions=multidim_conditions_array_bad_shape ) # Correct shape multi-dimensional conditions, wrong number of values # [(4, 3, 1) instead of (4, 3, 2)] multidim_conditions_array_wrong_size = {"a": np.array( [ [[0.06], [0.07], [0.08]], [[0.08], [0.07], [0.06]], [[0.07], [0.07], [0.07]], [[0.06], [0.06], [0.06]], ] )} with pytest.raises(ValueError): WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table, multidim_conditions=multidim_conditions_array_wrong_size ) # Correct shape and size multidim_conditions_array = {"a": np.array( [ [[0.06, 0.05], [0.07, 0.06], [0.08, 0.07]], [[0.08, 0.08], [0.07, 0.07], [0.06, 0.06]], [[0.07, 0.07], [0.07, 0.07], [0.07, 0.07]], [[0.06, 0.05], [0.06, 0.05], [0.06, 0.05]], ] )} wind_rose = WindTIRose( wind_directions, wind_speeds, turbulence_intensities, freq_table, multidim_conditions=multidim_conditions_array ) multidim_conditions_unpacked = wind_rose.unpack_multidim_conditions() for k, v in multidim_conditions_unpacked.items(): assert k in multidim_conditions_array.keys() assert v.shape == (4,) # 4 remaining conditions after frequency filtering def test_wind_ti_rose_unpack_for_reinitialize(): wind_directions = np.array([270, 280, 290, 300]) wind_speeds = np.array([6, 7, 8]) turbulence_intensities = np.array([0.05, 0.1]) freq_table = np.array( [ [[1.0, 0.0], [1.0, 0.0], [0.0, 0.0]], [[1.0, 0.0], [1.0, 0.0], [0.0, 0.0]], [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], ] ) # First test using default assumption only non-zero frequency cases computed wind_rose = WindTIRose(wind_directions, wind_speeds, turbulence_intensities, freq_table) ( wind_directions_unpack, wind_speeds_unpack, turbulence_intensities_unpack, heterogeneous_inflow_config, ) = wind_rose.unpack_for_reinitialize() # Given the above frequency table with zeros for a few elements, # we expect only combinations of wind directions of 270 and 280 deg, # wind speeds of 6 and 7 m/s, and a TI of 5% np.testing.assert_allclose(wind_directions_unpack, [270, 270, 280, 280]) np.testing.assert_allclose(wind_speeds_unpack, [6, 7, 6, 7]) np.testing.assert_allclose(turbulence_intensities_unpack, [0.05, 0.05, 0.05, 0.05]) def test_wind_ti_rose_aggregate(): wind_directions = np.array([0, 2, 4, 6, 8, 10]) wind_speeds = np.array([7, 8]) turbulence_intensities = np.array([0.02, 0.04, 0.06, 0.08, 0.1]) freq_table = np.ones((6, 2, 5)) wind_rose = WindTIRose(wind_directions, wind_speeds, turbulence_intensities, freq_table) # Test that resampling with a new step size returns the same wind_rose_aggregate = wind_rose.downsample() np.testing.assert_allclose(wind_rose.wind_directions, wind_rose_aggregate.wind_directions) np.testing.assert_allclose(wind_rose.wind_speeds, wind_rose_aggregate.wind_speeds) np.testing.assert_allclose( wind_rose.turbulence_intensities, wind_rose_aggregate.turbulence_intensities ) np.testing.assert_allclose(wind_rose.freq_table_flat, wind_rose_aggregate.freq_table_flat) # Now test resampling the turbulence intensities to 4% bins wind_rose_aggregate = wind_rose.downsample(ti_step=0.04) np.testing.assert_allclose(wind_rose_aggregate.turbulence_intensities, [0.04, 0.08, 0.12]) np.testing.assert_allclose( wind_rose_aggregate.freq_table_flat, (1 / 60) * np.array(12 * [2, 2, 1]) ) # Test tha that inplace behavior is to modify the original object as expected wind_rose_2 = copy.deepcopy(wind_rose) wind_rose_2.downsample(inplace=True) np.testing.assert_allclose(wind_rose.wind_directions, wind_rose_2.wind_directions) np.testing.assert_allclose(wind_rose.wind_speeds, wind_rose_2.wind_speeds) np.testing.assert_allclose(wind_rose.turbulence_intensities, wind_rose_2.turbulence_intensities) wind_rose_2 = copy.deepcopy(wind_rose) wind_rose_2.downsample(ti_step=0.04, inplace=True) np.testing.assert_allclose( wind_rose_aggregate.turbulence_intensities, wind_rose_2.turbulence_intensities ) np.testing.assert_allclose(wind_rose_aggregate.freq_table_flat, wind_rose_2.freq_table_flat) def test_time_series_to_WindTIRose(): wind_directions = np.array([259.8, 260.2, 260.3, 260.1]) wind_speeds = np.array([5.0, 5.0, 5.1, 7.2]) turbulence_intensities = np.array([0.05, 0.1, 0.15, 0.2]) time_series = TimeSeries( wind_directions, wind_speeds, turbulence_intensities=turbulence_intensities, ) wind_rose = time_series.to_WindTIRose(wd_step=2.0, ws_step=1.0, ti_step=0.1) # The binning should result in turbulence intensity bins of 0.1 and 0.2 tis_windrose = wind_rose.turbulence_intensities np.testing.assert_almost_equal(tis_windrose, [0.1, 0.2]) # The 6 m/s bin should be empty freq_table = wind_rose.freq_table np.testing.assert_almost_equal(freq_table[0, 1, :], [0, 0]) def test_gen_heterogeneous_inflow_config(): wind_directions = np.array([259.8, 260.2, 260.3, 260.1, 270.0]) wind_speeds = 8.0 turbulence_intensities = 0.06 heterogeneous_map_config = { "speed_multipliers": np.array( [ [0.9, 0.9], [1.0, 1.0], [1.1, 1.2], ] ), "wind_directions": np.array([250, 260, 270]), "x": np.array([0, 1000]), "y": np.array([0, 0]), } time_series = TimeSeries( wind_directions, wind_speeds, turbulence_intensities=turbulence_intensities, heterogeneous_map=heterogeneous_map_config, ) (_, _, _, _, _, heterogeneous_inflow_config) = time_series.unpack() expected_result = np.array([[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.1, 1.2]]) np.testing.assert_allclose(heterogeneous_inflow_config["speed_multipliers"], expected_result) np.testing.assert_allclose(heterogeneous_inflow_config["x"], heterogeneous_inflow_config["x"]) def test_heterogeneous_inflow_config_by_wd(): # Show that passing the config dict to the old heterogeneous_inflow_config_by_wd input # is equivalent to passing it to the heterogeneous_map input wind_directions = np.array([250.0, 260.0]) wind_speeds = np.array([8.0]) heterogeneous_map_config = { "speed_multipliers": np.array( [ [0.9, 0.9], [1.0, 1.0], [1.1, 1.2], ] ), "wind_directions": np.array([250, 260, 270]), "x": np.array([0, 1000]), "y": np.array([0, 0]), } # Using heterogeneous_map input wind_rose = WindRose( wind_directions, wind_speeds, ti_table=0.06, heterogeneous_map=heterogeneous_map_config, ) (_, _, _, _, _, heterogeneous_inflow_config_a) = wind_rose.unpack() # Using heterogeneous_inflow_config_by_wd input wind_rose = WindRose( wind_directions, wind_speeds, ti_table=0.06, heterogeneous_inflow_config_by_wd=heterogeneous_map_config, ) (_, _, _, _, _, heterogeneous_inflow_config_b) = wind_rose.unpack() np.testing.assert_allclose( heterogeneous_inflow_config_a["speed_multipliers"], heterogeneous_inflow_config_b["speed_multipliers"], ) def test_gen_heterogeneous_inflow_config_with_wind_directions_and_wind_speeds(): heterogeneous_map_config = { "speed_multipliers": np.array([[0.9, 0.9], [1.0, 1.0], [1.1, 1.2], [1.2, 1.3]]), "wind_directions": np.array([250, 260, 250, 260]), "wind_speeds": np.array([5, 5, 10, 10]), "x": np.array([0, 1000]), "y": np.array([0, 0]), } time_series = TimeSeries( wind_directions=np.array([259.8, 260.2, 260.3, 260.1, 200.0]), wind_speeds=np.array([4, 9, 4, 9, 4]), turbulence_intensities=0.06, heterogeneous_map=heterogeneous_map_config, ) (_, _, _, _, _, heterogeneous_inflow_config) = time_series.unpack() expected_result = np.array([[1.0, 1.0], [1.2, 1.3], [1.0, 1.0], [1.2, 1.3], [0.9, 0.9]]) np.testing.assert_allclose(heterogeneous_inflow_config["speed_multipliers"], expected_result) def test_gen_heterogeneous_inflow_config_with_wind_directions_and_wind_speeds_wind_rose(): heterogeneous_map_config = { "speed_multipliers": np.array([[0.9, 0.9], [1.0, 1.0], [1.1, 1.2], [1.2, 1.3]]), "wind_directions": np.array([250, 260, 250, 260]), "wind_speeds": np.array([5, 5, 10, 10]), "x": np.array([0, 1000]), "y": np.array([0, 0]), } wind_rose = WindRose( wind_directions=np.array([250.0, 260.0]), wind_speeds=np.array([9.0]), ti_table=0.06, heterogeneous_map=heterogeneous_map_config, ) (_, _, _, _, _, heterogeneous_inflow_config) = wind_rose.unpack() expected_result = np.array([[1.1, 1.2], [1.2, 1.3]]) np.testing.assert_allclose(heterogeneous_inflow_config["speed_multipliers"], expected_result) def test_read_csv_long(): # Read in the wind rose data from the csv file # First confirm that the data raises value error when wrong columns passed with pytest.raises(ValueError): wind_rose = WindRose.read_csv_long(TEST_DATA / "wind_rose.csv") # Since TI not specified in table, not giving a fixed TI should raise an error with pytest.raises(ValueError): wind_rose = WindRose.read_csv_long( TEST_DATA / "wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val" ) # Now read in with correct columns wind_rose = WindRose.read_csv_long( TEST_DATA / "wind_rose.csv", wd_col="wd", ws_col="ws", freq_col="freq_val", ti_col_or_value=0.06, ) # Confirm that data read in correctly, and the missing wd/ws bins are filled with zeros expected_result = np.array([[0.25, 0.25], [0.5, 0]]) np.testing.assert_allclose(wind_rose.freq_table, expected_result) # Confirm expected wind direction and wind speed values expected_result = np.array([270, 280]) np.testing.assert_allclose(wind_rose.wind_directions, expected_result) expected_result = np.array([8, 9]) np.testing.assert_allclose(wind_rose.wind_speeds, expected_result) # Confirm expected TI values expected_result = np.array([[0.06, 0.06], [0.06, np.nan]]) # Confirm all elements which aren't nan are close np.testing.assert_allclose( wind_rose.ti_table[~np.isnan(wind_rose.ti_table)], expected_result[~np.isnan(expected_result)], ) def test_read_csv_long_ti(): # Read in the wind rose data from the csv file # Now read in with correct columns wind_ti_rose = WindTIRose.read_csv_long( TEST_DATA / "wind_ti_rose.csv", wd_col="wd", ws_col="ws", ti_col="ti", freq_col="freq_val", ) # Confirm the shape of the frequency table assert wind_ti_rose.freq_table.shape == (2, 2, 2) # Confirm expected wind direction and wind speed values expected_result = np.array([270, 280]) np.testing.assert_allclose(wind_ti_rose.wind_directions, expected_result) expected_result = np.array([8, 9]) np.testing.assert_allclose(wind_ti_rose.wind_speeds, expected_result) expected_result = np.array([0.06, 0.07]) np.testing.assert_allclose(wind_ti_rose.turbulence_intensities, expected_result) def test_time_series_multidim(): # Test that TimeSeries can handle multidimensional wind speeds and turbulence intensities wind_directions = np.array([270.0, 280.0, 290.0]) wind_speeds = np.array([5.0, 6.0, 7.0]) turbulence_intensities = np.array([0.06, 0.07, 0.06]) # Test scalar multidimensional conditions multidim_conditions_scalar = {"TI": 0.06, "Hs": 3.1} time_series = TimeSeries( wind_directions, wind_speeds, turbulence_intensities, multidim_conditions=copy.deepcopy(multidim_conditions_scalar), ) multidim_conditions_unpacked = time_series.unpack_multidim_conditions() for k, v in multidim_conditions_unpacked.items(): assert np.allclose(v, [multidim_conditions_scalar[k]] * len(wind_directions)) # Array multi-dimensional conditions that are not the correct length (n_findex) multidim_conditions_array_bad_size = {"TI": np.array([1, 2, 3, 4])} with pytest.raises(ValueError): TimeSeries( wind_directions, wind_speeds, turbulence_intensities, multidim_conditions=multidim_conditions_array_bad_size ) # Incorrect shape multidim_conditions_array_bad_shape = { "TI": np.array([[0.06, 0.07], [0.08, 0.09], [0.10, 0.11]]) } with pytest.raises(ValueError): TimeSeries( wind_directions, wind_speeds, turbulence_intensities, multidim_conditions=multidim_conditions_array_bad_shape ) # Correct size and shape multidim_conditions_array = {"TI": np.array([0.06, 0.07, 0.08])} time_series = TimeSeries( wind_directions, wind_speeds, turbulence_intensities, multidim_conditions=copy.deepcopy(multidim_conditions_array), ) multidim_conditions_unpacked = time_series.unpack_multidim_conditions() for k, v in multidim_conditions_unpacked.items(): assert k in multidim_conditions_array.keys() assert v.shape == (len(wind_directions),) ================================================ FILE: tests/wind_rose_wrg_test.py ================================================ import math from pathlib import Path import numpy as np import pytest import floris from floris import ( FlorisModel, UncertainFlorisModel, WindRoseWRG, ) TEST_DATA = Path(__file__).resolve().parent / "data" YAML_INPUT = TEST_DATA / "input_full.yaml" WRG_FILE_FILE = TEST_DATA / "wrg_test.wrg" def test_load_wrg(): WindRoseWRG(WRG_FILE_FILE) def test_read_header(): """Test reading the header of a WRG file. The header of a WRG file is the first line of the file and contains the number of grid points in x and y, the minimum x and y coordinates, and the grid size. """ wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) assert wind_rose_wrg.nx == 2 assert wind_rose_wrg.ny == 3 assert wind_rose_wrg.xmin == 0.0 assert wind_rose_wrg.ymin == 0.0 assert wind_rose_wrg.grid_size == 1000.0 # Test x and y arrays assert np.allclose(wind_rose_wrg.x_array, np.array([0.0, 1000.0])) assert np.allclose(wind_rose_wrg.y_array, np.array([0.0, 1000.0, 2000.0])) # Test number of grid points assert wind_rose_wrg.n_gid == 6 # Test number of sectors assert wind_rose_wrg.n_sectors == 12 def test_read_data(): """Test reading the data of a WRG file. The data of a WRG file is the information about each grid point, including the x, y, z, and h coordinates, the frequency of each sector, and the Weibull parameters for each sector. """ wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) # Test z and h values assert wind_rose_wrg.z == 0.0 assert wind_rose_wrg.h == 90.0 # Test the first and last gid for sector_freq, weibull_A, and weibull_k assert wind_rose_wrg.sector_freq[0, 0, 0] == 116 / 1000.0 assert wind_rose_wrg.sector_freq[-1, -1, -1] == 98 / 1000.0 assert wind_rose_wrg.weibull_A[0, 0, 0] == 106 / 10.0 assert wind_rose_wrg.weibull_A[-1, -1, -1] == 111 / 10.0 assert wind_rose_wrg.weibull_k[0, 0, 0] == 273 / 100.0 assert wind_rose_wrg.weibull_k[-1, -1, -1] == 267 / 100.0 def test_build_interpolant_function_list(): wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) # Initialize the values x = np.array([0.0, 1000.0]) y = np.array([0.0, 500.0, 1000.0]) n_sectors = 3 data = np.ones((2, 3, 3)) # For the first sector, the point at (1000, 0) is 2.0 data[1, 0, 0] = 2.0 # For the second sector, the point at (1000, 500) is 3.0 data[1, 1, 1] = 3.0 function_list = wind_rose_wrg._build_interpolant_function_list(x, y, n_sectors, data) # Length of the function list should be n_sectors assert len(function_list) == n_sectors # Test the interpolation in the first sector test_value = function_list[0]((500, 0)) assert test_value == 1.5 # Test the interpolation in the second sector test_value = function_list[1]((1000, 250)) assert test_value == 2.0 # Test using linear method test_value = function_list[0]((500, 0), method="linear") assert test_value == 1.5 # Test using nearest method test_value = function_list[0]((1001, 0), method="nearest") assert test_value == 2.0 def test_interpolate_data(): wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) sector_freq = wind_rose_wrg.sector_freq # Test that interpolating onto the point 0,0 returns the 1st row of the sector_freq test_value = wind_rose_wrg._interpolate_data(0, 0, wind_rose_wrg.interpolant_sector_freq) assert np.allclose(test_value, sector_freq[0, 0, :]) # Test the interpolating just out of bounds of 0,0 also yields the 1st row of the sector_freq test_value = wind_rose_wrg._interpolate_data(-1, -1, wind_rose_wrg.interpolant_sector_freq) assert np.allclose(test_value, sector_freq[0, 0, :]) # Test that value at x=500, y0, this is the average of the rows at [0,0] and [1,0] test_value = wind_rose_wrg._interpolate_data(500, 0, wind_rose_wrg.interpolant_sector_freq) assert np.allclose(test_value, (sector_freq[0, 0, :] + sector_freq[1, 0, :]) / 2) # Test that value at x=0, y=500, this is the average of the rows at [0,0] and [0,1] test_value = wind_rose_wrg._interpolate_data(0, 500, wind_rose_wrg.interpolant_sector_freq) assert np.allclose(test_value, (sector_freq[0, 0, :] + sector_freq[0, 1, :]) / 2) def test_generate_wind_speed_frequencies_from_weibull(): wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) wind_speeds_in = np.array([0.0, 5.0, 10.0, 15.0]) wind_speeds, freq = wind_rose_wrg._generate_wind_speed_frequencies_from_weibull( 10.0, 2.0, wind_speeds=wind_speeds_in ) # Test that the wind speeds are the same assert np.allclose(wind_speeds, wind_speeds_in) # Test that freq is the same length as wind_speeds assert len(freq) == len(wind_speeds) # Test that the frequencies sum to 1.0 assert np.allclose(np.sum(freq), 1.0) # Test the correctness of the frequencies by reversing the process wind_speeds = np.arange(0.0, 100.0, 1.0) A_test = 9.0 k_test = 1.8 wind_speeds, freq = wind_rose_wrg._generate_wind_speed_frequencies_from_weibull( A_test, k_test, wind_speeds=wind_speeds ) # Test that the mean value matches theory mean_value = np.sum(wind_speeds * freq) assert np.allclose(mean_value, A_test * math.gamma(1 + 1 / k_test), rtol=0.1) def test_get_wind_rose_at_point(): wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) wind_speeds = np.arange(0.0, 26.0, 1.0) n_wind_speeds = len(wind_speeds) wind_rose = wind_rose_wrg.get_wind_rose_at_point(0, 0) # Test that there are 12 wind directions at n_wind_speeds wind speeds assert wind_rose.freq_table.shape == (12, n_wind_speeds) assert len(wind_rose.wind_speeds) == n_wind_speeds assert len(wind_rose.wind_directions) == 12 # Test that freq table generated at (0, 0) is the same at that of (-1 , -1) wind_rose2 = wind_rose_wrg.get_wind_rose_at_point(-1, -1) assert np.allclose(wind_rose.freq_table, wind_rose2.freq_table) # Test that uneven spacing in wind_speeds is not allowed with pytest.raises(ValueError): _ = wind_rose_wrg.get_wind_rose_at_point(0, 0, wind_speeds=np.delete(wind_speeds, [2])) def test_wind_rose_wrg_integration(): wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) # Set a layout with two turbines layout_x = np.array([0, 1000]) layout_y = np.array([0, 2000]) # Apply the layout wind_rose_wrg.set_layout(layout_x, layout_y) # Get a wind rose at the second turbine wind_rose = wind_rose_wrg.get_wind_rose_at_point(1000, 2000) # Also take the second wind rose from the wind_roses list wind_rose2 = wind_rose_wrg.wind_roses[1] # Show these are the same by compare the freq_table assert np.allclose(wind_rose.freq_table, wind_rose2.freq_table) # Check multidim_conditions are None assert wind_rose_wrg.unpack_multidim_conditions() is None assert wind_rose.unpack_multidim_conditions() is None def test_apply_wrg_to_floris_model(): fmodel = FlorisModel(configuration=YAML_INPUT) wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) fmodel.set(wind_data=wind_rose_wrg) fmodel.run() def test_apply_wrg_to_ufloris_model(): ufmodel = UncertainFlorisModel(configuration=YAML_INPUT) wind_rose_wrg = WindRoseWRG(WRG_FILE_FILE) ufmodel.set(wind_data=wind_rose_wrg) ufmodel.run() ================================================ FILE: tests/yaw_optimization_integration_test.py ================================================ import numpy as np import pandas as pd import pytest from floris import FlorisModel from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR DEBUG = False VELOCITY_MODEL = "gauss" DEFLECTION_MODEL = "gauss" def test_yaw_optimization_limits(sample_inputs_fixture): """ The Serial Refine (SR) method optimizes yaw angles based on a sequential, iterative yaw optimization scheme. This test compares the optimization results from the SR method for a simple farm with a simple wind rose to stored baseline results. """ sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL fmodel = FlorisModel(sample_inputs_fixture.core) wd_array = np.arange(0.0, 360.0, 90.0) ws_array = 8.0 * np.ones_like(wd_array) ti_array = 0.1 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW fmodel.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array, ) # Asymmetric limits yaw_opt = YawOptimizationSR( fmodel, minimum_yaw_angle=-10.0, maximum_yaw_angle=20.0, ) yaw_opt.optimize() # Strictly positive limits yaw_opt = YawOptimizationSR( fmodel, minimum_yaw_angle=5.0, maximum_yaw_angle=20.0, ) yaw_opt.optimize() # Strictly negative limits yaw_opt = YawOptimizationSR( fmodel, minimum_yaw_angle=-20.0, maximum_yaw_angle=-5.0, ) yaw_opt.optimize() # Infeasible limits with pytest.raises(ValueError): yaw_opt = YawOptimizationSR( fmodel, minimum_yaw_angle=20.0, maximum_yaw_angle=5.0, )