Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FLORIS v4.3 #1073

Merged
merged 18 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-working-examples.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
os: [ubuntu-latest] #, macos-latest, windows-latest]
fail-fast: False

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/continuous-integration-workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
os: [ubuntu-latest] #, macos-latest, windows-latest]
fail-fast: False
env:
Expand Down
59 changes: 51 additions & 8 deletions .github/workflows/deploy-pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ on:
push:
branches:
- develop
paths:
- docs/**


workflow_dispatch: # Allows manual triggering of the workflow

# This job installs dependencies, builds the book, and pushes it to `gh-pages`
jobs:
Expand All @@ -22,7 +23,7 @@ jobs:

- name: Install dependencies
run: |
pip install -e ".[docs]"
pip install -e ".[docs, develop]"

# Make a copy of the examples folder within the docs folder
- name: Copy examples to docs
Expand All @@ -35,12 +36,8 @@ jobs:
- name: Convert examples to notebooks
working-directory: ${{runner.workspace}}/floris/docs/examples/
run: |
# Print the working directory
pwd

# Show the contents
ls

python _convert_examples_to_notebooks.py

# Build the book
Expand All @@ -51,7 +48,53 @@ jobs:

# Push the book's HTML to github-pages
- name: GitHub Pages action
uses: peaceiris/actions-gh-pages@v3.6.1
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
4 changes: 2 additions & 2 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
pip install build twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m build
twine upload dist/*
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repos:
- id: mixed-line-ending

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.241
rev: v0.9.6
hooks:
- id: ruff

Expand Down
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
FLORIS is a controls-focused wind farm simulation software incorporating
steady-state engineering wake models into a performance-focused Python
framework. It has been in active development at NREL since 2013 and the latest
release is [FLORIS v4.2.2](https://github.com/NREL/floris/releases/latest).
release is [FLORIS v4.3](https://github.com/NREL/floris/releases/latest).
Online documentation is available at https://nrel.github.io/floris.

The software is in active development and engagement with the development team
Expand All @@ -13,9 +13,6 @@ the conversation in [GitHub Discussions](https://github.com/NREL/floris/discussi

## Installation

**WARNING:**
Support for python version 3.8 will be dropped in FLORIS v4.3. See [Installation documentation](https://nrel.github.io/floris/installation.html#installation) for details.

**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.
Expand Down Expand Up @@ -82,7 +79,7 @@ PACKAGE CONTENTS
wind_data

VERSION
4.2.2
4.3

FILE
~/floris/floris/__init__.py
Expand Down
163 changes: 163 additions & 0 deletions benchmarks/bench.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
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)
Loading