Skip to content

Commit

Permalink
Merge pull request #25 from NREL/develop
Browse files Browse the repository at this point in the history
WHOC v0.1
  • Loading branch information
misi9170 authored Dec 22, 2023
2 parents d344eaf + 98250d2 commit 73c0762
Show file tree
Hide file tree
Showing 43 changed files with 1,777 additions and 49 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/continuous-integration-workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ jobs:

runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11"]
os: [ubuntu-latest]
fail-fast: False

steps:
- uses: actions/checkout@v3
Expand All @@ -22,6 +22,11 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -e ".[develop]"
# - uses: pre-commit/action@v3.0.0
- name: Run ruff
run: |
ruff .
ruff format
- name: Run tests and collect coverage
run: |
# -rA displays the captured output for all tests after they're run
Expand Down
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ data.db
*.DS_Store
slices

# Data files
*.csv
*.log
examples/*/logstandin
examples/*/loghercules

# macOS files
.DS_Store

# ide settings and files
.idea
.vscode


# Outputs
*echo

26 changes: 26 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

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/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.7
hooks:
# Run the linter.
- id: ruff
types_or: [ python, pyi, jupyter ]
args: [ --fix ]
# Run the formatter.
- id: ruff-format
types_or: [ python, pyi, jupyter ]
11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,5 @@ controllers, including:
WHOC controllers will also call on design tools such as
[FLORIS](https://github.com/NREL/floris).

## Code development
To contribute to WHOC, please consider forking the main github repository,
with the [main repo](https://github.com/NREL/wind-hybrid-open-controller) as an
upstream remote. To submit a new feature or bug fix, create a new branch
in your fork and submit a pull request back to the `develop` branch in the
main repo. The pull request will be reviewed by other WHOC developers and
merged (using "squash and merge") into the `develop` branch. Periodically,
the `develop` branch will be merged into the `main` branch and a version
number will be assigned.
Documentation for WHOC can be found on
[nrel.github.io](https://nrel.github.io/wind-hybrid-open-controller/intro.html)
68 changes: 68 additions & 0 deletions demo_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright 2021 NREL

# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

# See https://nrel.github.io/wind-hybrid-open-controller for documentation

import matplotlib.pyplot as plt
import numpy as np
from whoc.controllers.wake_steering_actuatordisk_standin import WakeSteeringADStandin
from whoc.interfaces.hercules_actuator_disk_yaw_interface import HerculesADYawInterface

demo_hercules_dict = {
"dt": 1.0,
"hercules_comms": {
"amr_wind": {
"wind_farm_0": {
"type": "amr_wind_local",
"amr_wind_input_file": "amr_input.inp",
}
}
},
"controller": {"num_turbines": 2, "initial_conditions": {"yaw": [10.0, 15.0]}},
}

interface = HerculesADYawInterface(demo_hercules_dict)

controller = WakeSteeringADStandin(interface, demo_hercules_dict)

# Create a little loop to demonstrate how the controller works
wd_base = np.linspace(280, 300, 50)
np.random.seed(0)
wind_dir = np.tile(wd_base, (2, 1)).T + np.random.normal(scale=5.0, size=(len(wd_base), 2))

yaw_angles = []
for i in range(wind_dir.shape[0]):
demo_hercules_dict["hercules_comms"]["amr_wind"]["wind_farm_0"][
"turbine_wind_directions"
] = wind_dir[i, :]
demo_hercules_dict["hercules_comms"]["amr_wind"]["wind_farm_0"]["turbine_wind_speeds"] = 8
demo_hercules_dict["hercules_comms"]["amr_wind"]["wind_farm_0"]["turbine_powers"] = 2000
demo_hercules_dict["time"] = float(i)

demo_hercules_dict = controller.step(hercules_dict=demo_hercules_dict)

yaw_angles.append(
demo_hercules_dict["hercules_comms"]["amr_wind"]["wind_farm_0"]["turbine_yaw_angles"]
)

yaw_angles = np.array(yaw_angles)
fig, ax = plt.subplots(2, 1, sharex=True, sharey=True)
for i in range(2):
ax[i].plot(range(wind_dir.shape[0]), wind_dir[:, i], color="C0", label="wd")
ax[i].plot(range(wind_dir.shape[0]), yaw_angles[:, i], color="black", label="yaw stpt")
ax[i].set_ylabel("Direction, T{0} [deg]".format(i))
ax[i].grid()
ax[1].set_xlabel("Time")
ax[1].set_xlim([0, wind_dir.shape[0]])
ax[0].legend()

plt.show()
2 changes: 1 addition & 1 deletion docs/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ html:
use_issues_button: true
use_repository_button: true
use_edit_page_button: true
google_analytics_id: G-XXXX TODO: Update when public
google_analytics_id: G-BJZY823DHG


# Sphinx for API doc generation
Expand Down
21 changes: 11 additions & 10 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

format: jb-book
root: intro
# parts:
# - caption: Installation
# chapters:
# - file: install_instructions
# - file: install_old
# - file: install_on_kestrel
# - file: install_spack
# - caption: Usage
# chapters:
# - file: examples
parts:
- caption: General information
chapters:
- file: install_instructions
- file: code_development
- caption: Controllers
chapters:
- file: controllers
- caption: Interfaces
chapters:
- file: interfaces
# - file: order_of_op
23 changes: 23 additions & 0 deletions docs/code_development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Code development
To contribute to WHOC, please consider forking the main github repository,
with the [NREL repo](https://github.com/NREL/wind-hybrid-open-controller) as an
upstream remote. See the [Installation instructions](install_instructions)
for details about how to set up your repository as a developer.

To submit a new feature or bug fix, create a new branch
in your fork and submit a pull request back to the `develop` branch in the
main repo. The pull request will be reviewed by other WHOC maintainers and
merged (using "squash and merge") into the `develop` branch. Periodically,
the `develop` branch will be merged into the `main` branch and a version
number will be assigned.

Unless an existing controller or interface exist to suit your needs, most
users will need to generate:
- A new interface class inheriting from `InterfaceBase`
- A new controller class, implementing the desired control algorithm and
inheriting from `ControllerBase`

Additionally, if you'd like to contribute to this base repository, please
include in your pull request:
- Unit tests for the implemented controller
- Possibly unit tests for the implemented interface, if needed
18 changes: 18 additions & 0 deletions docs/controllers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Controllers

The `whoc.controllers` module contains a library of wind and hybrid power plant
controllers. Each controller must inherit from `ControllerBase` (see
controller_base.py) and implement a
mandatory `compute_controls()` method, which contains the relevant control
algorithm and writes final control signals to the `controls_dict` attribute
as key-value pairs. `compute_controls()` is, in turn, called in the `step()`
method of `ControllerBase`.

## Available controllers

### WakeSteeringADStandin
For yaw controller of actuator disk-type turbines (as a stand-in, will be
updated).

### WakeSteeringROSCOStandin
May be combined into a universal simple wake steeringcontroller.
Binary file added docs/graphics/main_attribution_inheritance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions docs/install_instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Installation

WHOC is _not_ designed to be used as a stand-alone package. Most likely,
you'll want to add WHOC to an existing conda environment that contains your
simulation testbed, such as [Hercules](https://github.com/NREL/hercules).
For example, see the [Hercules installation instuctions](\
https://nrel.github.io/hercules/install_instructions.html) for how to set up
an appropriate conda environment.

## General users

If you intend to use WHOC, but not contribute, the following lines should
be sufficient to install WHOC (presumably, after activating your conda
environment):

```
git clone https://github.com/NREL/wind-hybrid-open-controller
pip install wind-hybrid-open-controller/
```

## Developers

If you intend to contribute to WHOC, we request that your fork the WHOC
repository on github. You can then install WHOC (again, assuming you have
already activated your conda environment) according to:

```
git clone https://github.com/your-github-id/wind-hybrid-open-controller
pip install -e "wind-hybrid-open-controller/[develop]"
```
To contribute back to the base repository
https://github.com/NREL/wind-hybrid-open-controller, please do the following:
- Create a branch from the base repository's `develop` branch on your fork
containing your code changes (e.g. `your-github-id:feature/your-new-feature`)
- Open a pull request into the base repository's `NREL:develop` branch, and provide
a description of the new/updated capabilities
- The maintainers will review your pull request and provide feedback before
possibly merging the pull request (via the "squash and merge" method) into the
`NREL:develop` branch
- At the next release, `NREL:develop` will be merged into `NREL:main`, and your changes
contributions will appear there

For more information on what your pull request should contain, see
[Code development](code_development.md).


33 changes: 33 additions & 0 deletions docs/interfaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Interfaces

The `whoc.interfaces` module contains a library of interfaces for connecting
WHOC with various simulation platforms and other repositories. Each controller
run will require an `interface`, which is an instantiated object of a class
in this library. All interface classes should inherit from `InterfaceBase`,
which can be found n interface_base.py, and should implement three methods:
- `get_measurements()`: Recieve measurements from simulation assets and
organize into a dictionary that the calling controller can utilize. Optionally,
receives a large dictionary (for example, the Hercules `main_dict`), from which
useable measurements can be extracted/repackaged for easy use in the controller.
- `check_controls()`: Check that the keys in `controls_dict` are viable for
the receiving plant.
- `send_controls()`: Send controls to the simulation assets. Controls are
created as specific keyword arguements, which match those controls generated
by the calling controller. Optionally, receives a large dictionary
(for example, the Hercules `main_dict`), which can be written to and returned
with controls as needed.

These methods will all be called in the `step()` method of `ControllerBase`.

## Available interfaces

### HerculesADYawInterface
For direct python communication with Hercules. This should be instantiated
in a runscript that is running Hercules; used to generate a `controller` from
the WHOC controllers submodule; and that `controller` should be passed to the
Hercules `Emulator` upon its instantiation.

### ROSCO_ZMQInterface
For sending and receiving communications from one or more ROSCO instances
(which are likely connected to OpenFAST and FAST.Farm). Uses ZeroMQ to pass
messages between workers.
35 changes: 32 additions & 3 deletions docs/intro.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
# Wind Hybrid Open Controller (WHOC)

Wind Hybrid Open Controller
The Wind Hybrid Open Controller (WHOC) is a python-based tool for real-time
plant-level wind farm control and wind-based hybrid plant control.
WHOC will primarily be run in simulation, although we intend that it could be
used for physical plants in future.

Online documentation for Wind Hybrid Open Controller
The graphic below demonstrates the organization of the WHOC repository and
the controller objects that it contains.
![](graphics/main_attribution_inheritance.png)

More info
`ControllerBase` is the core underlying class, which newly implemented
controllers should inherit from. Inheritance is shown using arrows in the
diagram above. The key method of `ControllerBase` is the `step()` method,
which progresses the controller by receiving plant measurements; computing
controls (which must be implemented in the children of
`ControllerBase`); and sending the controls back to the plant. Children of
`ControllerBase` should inherit `step()` rather than overloading it.
Additionally, on instantiation, `ControllerBase` expects to receive an
instantiated `interface` object (discussed next). For information can be
found in controllers.md.

The `interface` object handles communications with the plant simulator,
allowing WHOC to be used with various simulation platforms (e.g. Hercules,
FAST.Farm) while keeping the controllers agnostic to the simulation platform
and the boilderplate code needed to handle different platforms. `interface`
objects should inherit from `InterfaceBase`. More information can be found in
interfaces.md.


We anticipate that, in future, multiple levels of inheritance may be useful
when defining top-level controllers that operate hybrid power plants with
various different assets. This situation is shown below, and we intend to
support such usage of WHOC.

![](graphics/second-level_attribution_inheritance.png)
Loading

0 comments on commit 73c0762

Please sign in to comment.