Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f0892d1
enable passing opt_settings to openmdao
jaredthomas68 Aug 22, 2025
7c75195
Merge remote-tracking branch 'origin/develop' into develop
jaredthomas68 Aug 22, 2025
af6132c
some touch-ups to get started
cfrontin Aug 23, 2025
807ef7a
Merge branch 'develop' into feature/docs_overhaul
cfrontin Sep 8, 2025
0761572
updates to logo and documentation
cfrontin Sep 9, 2025
735f16c
cherrypick some improvements for beta out of offshore-development
cfrontin Sep 11, 2025
9fa8446
black update
cfrontin Sep 11, 2025
8a66076
Merge branch 'develop' into feature/docs_overhaul
cfrontin Sep 11, 2025
a7e9d98
black reformat
cfrontin Sep 11, 2025
3474c24
Merge branch 'develop' into feature/docs_overhaul
cfrontin Sep 11, 2025
f6fb9eb
address copilot comments
cfrontin Sep 11, 2025
46944a9
Merge branch 'develop' into feature/docs_overhaul
cfrontin Sep 11, 2025
025de27
update some docs
cfrontin Sep 11, 2025
8776d0a
addressing jared comments
cfrontin Sep 11, 2025
5eee45e
update installation page
cfrontin Sep 11, 2025
de46a70
a tranche of docs mods
cfrontin Sep 11, 2025
f9e8d25
a new tranche of edits
cfrontin Sep 11, 2025
a2cf1d7
more cleanup
cfrontin Sep 11, 2025
48773a2
adjust some busted windio yamls and involved documentation
cfrontin Sep 11, 2025
457870f
update with examples
cfrontin Sep 11, 2025
37ae184
example one working it appears
cfrontin Sep 11, 2025
d2993b5
attempt nb example stuff
cfrontin Sep 12, 2025
abd664e
adjust find nb script
cfrontin Sep 12, 2025
f451954
adjust again
cfrontin Sep 12, 2025
22af041
save example 02 notebook
cfrontin Sep 12, 2025
e4015a8
added example 02 to docs
cfrontin Sep 12, 2025
f496539
add jupyter to dev requirements
cfrontin Sep 12, 2025
299c4e4
temporary withhold non-notebook tests, and update 02 notebook.
cfrontin Sep 12, 2025
0feec78
update file location
cfrontin Sep 12, 2025
2a8d330
update examples
cfrontin Sep 12, 2025
70ee75d
try try again
cfrontin Sep 12, 2025
1516eef
actually relative paths are apparently better...
cfrontin Sep 12, 2025
b645d47
update through ex 03
cfrontin Sep 12, 2025
f4f4929
re-enable full testing sweet
cfrontin Sep 12, 2025
15f82f7
tiny fix
cfrontin Sep 12, 2025
277fc32
fix wordiness
cfrontin Sep 12, 2025
cedc5de
docs playing nicely now
cfrontin Sep 12, 2025
c594ea6
reorganize mv typo
cfrontin Sep 12, 2025
e0a61c0
fix consolidated CI/CD to restore precedence.
cfrontin Sep 12, 2025
ea82c2e
incorporate pietro's comments from cfrontin/Ard PR#4
cfrontin Sep 12, 2025
0dfd0d0
Merge remote-tracking branch 'upstream/develop' into feature/docs_ove…
cfrontin Sep 12, 2025
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
80 changes: 80 additions & 0 deletions .github/workflows/python-tests-consolidated.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,83 @@ jobs:
cwd=str(path_script.parent),
env=env,
)

find-examples_nb:
name: Find all example notebooks
needs: [test-unit, test-system]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Find example python notebooks
id: find_notebooks
run: |
echo "scripts<<EOF"
find examples -mindepth 2 -maxdepth 2 -name "*.ipynb" | jq -R -s -c 'split("\n")[:-1]'
echo "EOF"
echo "scripts<<EOF" >> $GITHUB_OUTPUT
find examples -mindepth 2 -maxdepth 2 -name "*.ipynb" | jq -R -s -c 'split("\n")[:-1]' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
outputs:
scripts: ${{ steps.find_notebooks.outputs.scripts }}

test-examples_nb:
name: Run all example notebooks
needs: find-examples_nb
strategy:
fail-fast: false
matrix:
python-version: [3.12] # ["3.10", "3.11", "3.12", "3.13"]
os: [macos-latest, ubuntu-latest, windows-latest]
include:
- os: ubuntu-latest
path: ~/.cache/pip
- os: macos-latest
path: ~/Library/Caches/pip
- os: windows-latest
path: ~\AppData\Local\pip\Cache
script: ${{fromJson( needs.find-examples_nb.outputs.scripts )}}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Get cache dependencies
id: cache
uses: actions/cache@v4
with:
path: ${{ matrix.path }}
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys:
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Install Ard
run: |
pip install .[dev]
- name: Run examples
shell: python
run: |
import os
import pathlib
import subprocess

env = os.environ.copy()
env["MPLBACKEND"] = "Agg" # Non-interactive backend

path_script = pathlib.Path("${{ matrix.script }}").absolute()
print(f"RUNNING {path_script}")
subprocess.run(
["jupyter", "nbconvert", "--execute", "--to", "notebook", str(path_script.name)],
check=True,
cwd=str(path_script.parent),
env=env,
)
94 changes: 54 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
[![CI/CD test suite](https://github.com/WISDEM/Ard/actions/workflows/python-tests-consolidated.yaml/badge.svg?branch=develop)](https://github.com/WISDEM/Ard/actions/workflows/python-tests-consolidated.yaml)

# Ard

**Dig into wind farm design.**
[![CI/CD test suite](https://github.com/WISDEM/Ard/actions/workflows/python-tests-consolidated.yaml/badge.svg?branch=develop)](https://github.com/WISDEM/Ard/actions/workflows/python-tests-consolidated.yaml)

![Ard logo](assets/logomaker/logo.png)

**Dig in to wind farm design.**

<!-- The (aspirationally) foolproof tool for preparing wind farm layouts. -->

[An ard is a type of simple and lightweight plow](https://en.wikipedia.org/wiki/Ard_\(plough\)), used through the single-digit centuries to prepare a farm for planting.
The intent of `Ard` is to be a modular, full-stack multi-disciplinary optimization tool for wind farms.

The problem with wind farms is that they are complicated, multi-disciplinary objects.
They are aerodynamic machines, with complicated control systems, power electronic devices, social and political objects, and the core value (and cost) of complicated financial instruments.
Moreover, the design of *one* of these aspects affects all the rest!
Wind farms are complicated, multi-disciplinary systems.
They are aerodynamic machines (composed of complicated control systems, power electronic devices, etc.), social and political objects, generators of electrical power and consumers of electrical demand, and the core value generator (and cost) of complicated financial instruments.
Moreover, the design of any *one* of these aspects affects all the rest!

`Ard` seeks to make plant-level design choices that can incorporate these different aspects _and their interactions_ to make wind energy projects more successful.
`Ard` is a platform for wind farm layout optimization that seeks to enable plant-level design choices that can incorporate these different aspects _and their interactions_ to make wind energy projects more successful.
In brief, we are designing `Ard` to be: principled, modular, extensible, and effective, to allow resource-specific wind farm layout optimization with realistic, well-posed constraints, holistic and complex objectives, and natural incorporation of multiple fidelities and disciplines.

## Documentation
Ard documentation is available at [https://wisdem.github.io/Ard/]()
Expand Down Expand Up @@ -45,7 +50,6 @@ For a basic and static installation, type:
```shell
pip install .
```

For development (and really for everyone during pre-release), we recommend a full development installation:
```shell
pip install -e .[dev,docs]
Expand All @@ -60,59 +64,69 @@ mamba install wisdem -y
pip install -e .[dev,docs]
```

To test the installation, from the `Ard` folder run unit and regression tests:
## Testing instructions

The installation can be tested comprehensively using `pytest` from the top-level directory.
The developers also provide some convenience scripts for testing new installations; from the `Ard` folder run unit and regression tests:
```shell
source test/run_local_test_unit.sh
source test/run_local_test_system.sh
```
These enable the generation of HTML-based coverage reports by default and can be used to track "coverage", or the percentage of software lines of code that are run by the testing systems.
`Ard`'s git repository includes requirements for both the `main` and `develop` branches to have 80% coverage on unit testing and 50% testing in system testing, which are, respectively, tests of individual parts of `Ard` and "systems" composed of multiple parts.
Failures are not tolerated in code that is merged onto these branches and code found therein *should* never cause a testing failure if it has been found there.
If the process of installation and testing fails, please open a new issue [here](https://github.com/WISDEM/Ard/issues).

For user information, in pre-release, we are using some co-developed changes to the `FLORIS` library.
## Design philosophy

If the installation fails, please open a new issue [here](https://github.com/WISDEM/Ard/issues).
The design of `Ard` was inspired by two use cases in particular:
1) systems energy researchers who are focusing on one specific subdiscipline (e.g. layout strategies, social impacts, or aerodynamic modeling) but want to be able to easily keep track of how a change in one discipline impacts the entire value chain down to production, cost, value, and/or societal outcomes of energy or even optimize with respect to these, and
2) private industry researchers who run business cases and may want to drop in proprietary analysis modules for specific disciplines while preserving some of the open-source modules of `Ard`.

## OptiWindNet

We currently have experimental support for [Mauricio Souza de Alencar's OptiWindNet package for collection system cable path-planning optimization](https://gitlab.windenergy.dtu.dk/TOPFARM/OptiWindNet).
`Ard` is being developed as a modular tool to enable these types of research queries.
The goals during the development of `Ard` are to be:
1) principled:
- robustly documented
- adhering to [best-practices for code development](https://doi.org/10.2172/2479115)
2) modular and extensible:
- choose the analysis components you want
- skip the ones you don't
- build yourself the ones we don't have
3) effective
- robustly tested and testable at both unit and system levels
These principles guide us to implement, using [`OpenMDAO`](https://openmdao.org) as a backbone, a multi-disciplinary design, analysis, and optimization (MDAO) model of the wind farm layout problem, a toolset to accomplish the capability goals of `Ard`, to:
1) allow optimization of wind farm layouts for specific wind resource profiles
2) enable the incorporation of realistic but well-posed constraints
3) target holistic and complex system-level optimization objectives like LCOE and beyond-LCOE metrics
4) naturally incorporate analyses across fidelities to efficiently integrate advanced simulation

## Current capabilities

For the alpha pre-release of `Ard`, we have concentrated on optimization of wind plants, starting from a structured layout and optimizing it to minimize the levelized cost of energy, or LCOE.
This capability is demonstrated for a land-based (LB) wind farm in `examples/LCOE_LB_stack` and tested in an abridged form in `test/system/LCOE_stack/test_LCOE_LB_stack.py`.
In this example, the wind farm layout is parametrized with two angles, named orientation and skewed, and turbine distancing for rows and columns.
In the alpha pre-release stage, the constituent subcomponents of these problems are known to work and fully tested; any capabilities not touched in the layout-to-LCOE stack should be treated as experimental.
For the beta pre-release of `Ard`, we concentrate on optimization problems for wind plants, starting from structured layouts to minimize LCOE.
This capability is demonstrated for a land-based (LB) wind farm in `examples/01_onshore` and tested in an abridged form in `test/system/ard/api/test_LCOE_LB_stack.py`.
In this example, the wind farm layout is parametrized with two angles, named orientation and skew, and turbine distancing for rows and columns.
Additionally, we have offshore examples adjacent to the onshore example in the `examples` subdirectory.
In the beta pre-release stage, the constituent subcomponents of these problems are known to work and have full testing coverage.

These cases start from a four parameter farm layout, compute landuse area, make FLORIS AEP estimates, compute turbine capital costs, balance-of-station (BOS), and operational costs using WISDEM components, and finally give summary estimates of plant finance figures.
These cases start from a four parameter farm layout, compute land use area, make FLORIS estimates of annual energy production (AEP), compute turbine capital costs, balance-of-station (BOS), and operational costs elements of NREL's turbine systems engineering tool [WISDEM](https://github.com/wisdem/wisdem), and finally give summary estimates of plant finance figures.
The components that achieve this can be assembled to either run a single top-down analysis run, or run an optimization.

A second example is in progress to reoptimize the layout of two offshore wind farms, one fixed bottom (OFB) and one floating (OFL).
Both wind farms are made of the [22 MW reference wind turbine](https://github.com/IEAWindSystems/IEA-22-280-RWT).
In this example, BOS costs are estimated using the tool [ORBIT](https://github.com/WISDEM/ORBIT).

## Roadmap to future capabilities

The future development of `Ard` is centered around two user cases:
1) systems energy researchers who are focusing on one specific subdiscipline (e.g. layout strategies, social impacts, or aerodynamic modeling) but want to be able to easily keep track of how it impacts the entire value chain down to production, cost, and/or value of energy or even optimize with respect to it, and
2) private industry researchers who are interested in how public-sector research results change when proprietary analysis tools are dropped in and coupled the other tools in a systems-level simulation.

`Ard` is being developed as a modular tool to enable these types of research queries.
This starts from our research goals, which are that `Ard` should be:
1) principled: fully documented, and adhering to best-practices for code development
2) modular and extensible: choose the parts you want, skip the ones you don't, build yourself the ones we don't have
3) effective: fully tested and testable at the unit and system level, and built with a derivative-forward approach
# Contributing to `Ard`

This, then, allows us to attempt to accomplish the technical goals of `Ard`, to:
1) allow optimization of wind farm layouts for specific wind resource profiles
2) target wholistic and complex system-level optimization objectives like LCOE and beyond-LCOE metrics
3) naturally incorporate multi-fidelity analyses to efficiently integrate physics-resolving simulation
We have striven towards best-practices documentation and testing for `Ard`.
Contribution is welcome, and we are happy [to field pull requests from github](https://github.com/WISDEM/Ard/pulls).
For acceptance, PRs must:
- be formatted using [`black`](https://github.com/psf/black)
- not fail any unit tests or system tests
- achieve coverage criteria for unit & system testing
- be documented enough for continued maintenance by core `Ard` developers

## Building Documentation

To build the documentation locally run the following from the top `Ard/` directory.

To build the documentation locally, run the following from the top-level `Ard/` directory:
```shell
jupyter-book build docs/
```

You can then open `Ard/docs/_build/html/index.html` to view the docs.

---
Expand Down
11 changes: 11 additions & 0 deletions ard/api/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,17 @@ def set_up_system_recursive(
prob.add_recorder(recorder)
prob.driver.add_recorder(recorder)

prob.model.set_input_defaults(
"x_turbines",
# input_dict["modeling_options"]["windIO_plant"]["wind_farm"]["layouts"]["coordinates"]["x"],
units="m",
)
prob.model.set_input_defaults(
"y_turbines",
# input_dict["modeling_options"]["windIO_plant"]["wind_farm"]["layouts"]["coordinates"]["y"],
units="m",
)

prob.setup()

return prob
33 changes: 31 additions & 2 deletions ard/viz/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def plot_layout(
save_path: os.PathLike = None,
save_kwargs: dict = {},
include_cable_routing: bool = False,
include_mooring_system: bool = False,
):
"""
plot the layout of a farm
Expand Down Expand Up @@ -108,8 +109,8 @@ def plot_layout(

# adjust plot limits
x_lim, y_lim = get_limits(windIO_dict)
ax.set_xlim([x * 1e3 for x in x_lim])
ax.set_ylim([y * 1e3 for y in y_lim])
ax.set_xlim(x_lim)
ax.set_ylim(y_lim)

if include_cable_routing:
optiwindnet.plotting.gplot(
Expand All @@ -122,6 +123,34 @@ def plot_layout(
landscape=False,
)

if include_mooring_system:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you successfully run this with cable and mooring? I think we may need to have the mooring plot last.

# get the coordinates of the anchors
x_anchors = ard_prob.get_val("x_anchors", units="m")
y_anchors = ard_prob.get_val("y_anchors", units="m")

# loop over the anchors and plot from their originating turbine to each
for idx_turbine in range(
input_dict["modeling_options"]["layout"]["N_turbines"]
):
for idx_anchor in range(
input_dict["modeling_options"]["platform"]["N_anchors"]
):
ax.plot(
[x_turbines[idx_turbine], x_anchors[idx_turbine, idx_anchor]],
[y_turbines[idx_turbine], y_anchors[idx_turbine, idx_anchor]],
"-r",
alpha=0.25,
)
# plot the anchors as red circles
ax.plot(
x_anchors[idx_turbine, :],
y_anchors[idx_turbine, :],
"or",
alpha=0.25,
)

ax.axis("equal")

# show, save, or return
if save_path is not None:
plt.savefig(save_path, save_kwargs)
Expand Down
83 changes: 83 additions & 0 deletions assets/logomaker/inputs/ard_system.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
modeling_options: &modeling_options
windIO_plant: !include windio.yaml
layout:
N_turbines: 25
N_substations: 1
site_depth: 200.0
collection:
max_turbines_per_string: 8
solver_name: "highs"
solver_options:
time_limit: 60
mip_gap: 0.02
model_options:
topology: "branched"
feeder_route: "segmented"
feeder_limit: "unlimited"

system:
type: group
systems:
aepFLORIS:
type: component
module: ard.farm_aero.floris
object: FLORISAEP
promotes: ["x_turbines", "y_turbines", "AEP_farm"]
kwargs:
modeling_options: *modeling_options
case_title: "offshore-floating"
data_path:
collection:
type: component
module: ard.collection
object: OptiwindnetCollection
promotes: ["x_turbines", "y_turbines"]
kwargs:
modeling_options: *modeling_options
spacing_constraint:
type: component
module: ard.layout.spacing
object: TurbineSpacing
promotes: ["x_turbines", "y_turbines"]
kwargs:
modeling_options: *modeling_options
boundary:
type: component
module: ard.layout.boundary
object: FarmBoundaryDistancePolygon
promotes: ["*"]
kwargs:
modeling_options: *modeling_options

analysis_options:
driver:
name: ScipyOptimizeDriver
options:
optimizer: COBYLA # SLSQP
# maxiter: 100
design_variables:
x_turbines:
units: "m"
lower: -0.0
upper: 3000.0
scaler: 0.0005897339121 # 1/(7*D_rotor)
y_turbines:
units: "m"
lower: -0.0
upper: 1250.0
scaler: 0.0005897339121 # 1/(7*D_rotor)
constraints:
spacing_constraint.turbine_spacing:
units: "m"
lower: 0.001376045795 # 1/(3*D_rotor)
scaler: 0.004128137384 # 1/D_rotor
boundary_distances:
units: "m"
upper: 0.0
scaler: 0.004128137384 # 1/D_rotor
objective:
name: AEP_farm
options:
scaler: -1.0e-9
recorder:
filepath: opt_results.sql
Loading
Loading