wakemarch 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. wakemarch-0.1.0/CHANGELOG.md +19 -0
  2. wakemarch-0.1.0/CITATION.cff +24 -0
  3. wakemarch-0.1.0/CITATION.md +8 -0
  4. wakemarch-0.1.0/LICENSE +28 -0
  5. wakemarch-0.1.0/MANIFEST.in +11 -0
  6. wakemarch-0.1.0/PKG-INFO +197 -0
  7. wakemarch-0.1.0/PROVENANCE.md +40 -0
  8. wakemarch-0.1.0/README.md +159 -0
  9. wakemarch-0.1.0/docs/RELEASING.md +77 -0
  10. wakemarch-0.1.0/docs/SCIENCE.md +74 -0
  11. wakemarch-0.1.0/docs/TURBINES.md +161 -0
  12. wakemarch-0.1.0/docs/USER_GUIDE.md +192 -0
  13. wakemarch-0.1.0/docs/images/wake.png +0 -0
  14. wakemarch-0.1.0/docs/release-metadata.json +20 -0
  15. wakemarch-0.1.0/examples/first_wake.py +25 -0
  16. wakemarch-0.1.0/examples/plot_wake.py +53 -0
  17. wakemarch-0.1.0/examples/wakemarch_api_test.ipynb +287 -0
  18. wakemarch-0.1.0/examples/wakemarch_performance.ipynb +147 -0
  19. wakemarch-0.1.0/examples/wakemarch_piv_validation.ipynb +134 -0
  20. wakemarch-0.1.0/examples/wakemarch_turbines.ipynb +142 -0
  21. wakemarch-0.1.0/pyproject.toml +47 -0
  22. wakemarch-0.1.0/setup.cfg +4 -0
  23. wakemarch-0.1.0/src/wakemarch/__init__.py +9 -0
  24. wakemarch-0.1.0/src/wakemarch/_kernel.py +538 -0
  25. wakemarch-0.1.0/src/wakemarch/_progress.py +57 -0
  26. wakemarch-0.1.0/src/wakemarch/data/FLORIS_LICENSE.txt +26 -0
  27. wakemarch-0.1.0/src/wakemarch/data/iea_10MW.json +89 -0
  28. wakemarch-0.1.0/src/wakemarch/data/iea_15MW.json +179 -0
  29. wakemarch-0.1.0/src/wakemarch/data/nrel_5MW.json +179 -0
  30. wakemarch-0.1.0/src/wakemarch/model.py +219 -0
  31. wakemarch-0.1.0/src/wakemarch/turbine.py +260 -0
  32. wakemarch-0.1.0/src/wakemarch.egg-info/PKG-INFO +197 -0
  33. wakemarch-0.1.0/src/wakemarch.egg-info/SOURCES.txt +39 -0
  34. wakemarch-0.1.0/src/wakemarch.egg-info/dependency_links.txt +1 -0
  35. wakemarch-0.1.0/src/wakemarch.egg-info/requires.txt +16 -0
  36. wakemarch-0.1.0/src/wakemarch.egg-info/top_level.txt +1 -0
  37. wakemarch-0.1.0/tests/test_api.py +184 -0
  38. wakemarch-0.1.0/tests/test_turbine.py +154 -0
  39. wakemarch-0.1.0/tools/check_docs.py +48 -0
  40. wakemarch-0.1.0/tools/check_release.py +126 -0
  41. wakemarch-0.1.0/tools/run_notebooks.py +67 -0
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-11-02
4
+
5
+ First public release.
6
+
7
+ - Single-turbine steady wake model with curled wake shape: the recommended
8
+ configuration of the authors' research solver (thrust exponent 2, vortex
9
+ diffusivity factor 0.5, momentum fraction 0.95, near-wake display ramp).
10
+ - `WakeModel`, `Numerics` and `WakeResult`: immutable inputs, explicit deficit
11
+ and velocity sampling, read-only histories, live progress display.
12
+ - `Turbine`: power/CT and CP/CT tables, three reference presets pinned from
13
+ FLORIS 4.6.2 (NREL 5 MW, IEA 10 MW, IEA 15 MW), rotor-equivalent operating
14
+ point. The tabulated speed range is the operating range; table CT ≥ 1 is capped
15
+ at 0.97 for the wake with a warning. `Turbine.fixed` for constant coefficients.
16
+ - Input limits: H ≥ D/2 and |yaw| ≤ 50° (validated to 40°).
17
+ - Runtime dependencies NumPy ≥ 1.24 and SciPy ≥ 1.10; Python ≥ 3.11.
18
+ - Four example notebooks (API, resolution/cost, turbines, optional public PIV
19
+ comparison).
@@ -0,0 +1,24 @@
1
+ cff-version: 1.2.0
2
+ message: >-
3
+ If you use WakeMarch, please cite it using these metadata. The paper
4
+ describing the model will be added here once it has a DOI.
5
+ title: WakeMarch
6
+ abstract: >-
7
+ Steady cross-plane marching wake model for a single yawed wind turbine with
8
+ curled wake shape.
9
+ type: software
10
+ authors:
11
+ - family-names: Bui
12
+ given-names: Hoang Hai
13
+ orcid: "https://orcid.org/0000-0003-4485-0325"
14
+ affiliation: University of Bergen
15
+ version: 0.1.0
16
+ date-released: "2026-11-02"
17
+ license: BSD-3-Clause
18
+ repository-code: "https://github.com/haibuihoang/wakemarch"
19
+ keywords:
20
+ - wind energy
21
+ - wake model
22
+ - yaw
23
+ - wake steering
24
+ - curled wake
@@ -0,0 +1,8 @@
1
+ # Citation
2
+
3
+ Citation metadata are in [CITATION.cff](CITATION.cff) (GitHub shows a "Cite this
4
+ repository" button for it). The paper describing the model and a software DOI
5
+ will be added there when available.
6
+
7
+ For reproducibility, also record `wakemarch.__version__`, the Python, NumPy and
8
+ SciPy versions, all physical and numerical inputs, and your sampling definitions.
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, University of Bergen
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,11 @@
1
+ include README.md PROVENANCE.md pyproject.toml
2
+ include CITATION.md CITATION.cff CHANGELOG.md LICENSE
3
+ recursive-include docs *.md *.json *.png
4
+ recursive-include tools *.py
5
+ recursive-include examples *.py
6
+ recursive-include examples *.ipynb
7
+ recursive-include tests *.py
8
+ prune examples/.ipynb_checkpoints
9
+ global-exclude .DS_Store
10
+
11
+ recursive-include src/wakemarch/data *.json *.txt
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.4
2
+ Name: wakemarch
3
+ Version: 0.1.0
4
+ Summary: Cross-plane marching wake model for yawed wind turbines with curled wakes
5
+ Author: Hoang Hai Bui
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/haibuihoang/wakemarch
8
+ Project-URL: Issues, https://github.com/haibuihoang/wakemarch/issues
9
+ Project-URL: Changelog, https://github.com/haibuihoang/wakemarch/blob/main/CHANGELOG.md
10
+ Keywords: wind energy,wake model,yaw,wake steering,curled wake
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Scientific/Engineering :: Physics
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ License-File: src/wakemarch/data/FLORIS_LICENSE.txt
24
+ Requires-Dist: numpy<3,>=1.24
25
+ Requires-Dist: scipy<2,>=1.10
26
+ Provides-Extra: plot
27
+ Requires-Dist: matplotlib>=3.8; extra == "plot"
28
+ Provides-Extra: notebooks
29
+ Requires-Dist: matplotlib>=3.8; extra == "notebooks"
30
+ Requires-Dist: jupyterlab>=4; extra == "notebooks"
31
+ Requires-Dist: ipykernel>=6; extra == "notebooks"
32
+ Requires-Dist: nbclient>=0.10; extra == "notebooks"
33
+ Requires-Dist: nbconvert>=7; extra == "notebooks"
34
+ Provides-Extra: validation
35
+ Requires-Dist: h5py>=3.10; extra == "validation"
36
+ Requires-Dist: netCDF4>=1.6; extra == "validation"
37
+ Dynamic: license-file
38
+
39
+ # WakeMarch
40
+
41
+ WakeMarch is a steady, cross-plane marching wake model for a single yawed wind
42
+ turbine. It resolves the curled (kidney-shaped) wake produced by the
43
+ counter-rotating streamwise vortex pair of a yawed rotor, in uniform or
44
+ power-law-sheared inflow, and returns the wake deficit, streamwise vorticity and
45
+ velocity anywhere downstream.
46
+
47
+ The core depends only on NumPy (≥ 1.24) and SciPy (≥ 1.10) and needs
48
+ Python 3.11 or later. The upper bounds `<3`/`<2` are compatibility boundaries,
49
+ not evidence of tests on every release.
50
+
51
+ ![WakeMarch hub-height and cross-plane wake](https://raw.githubusercontent.com/haibuihoang/wakemarch/main/docs/images/wake.png)
52
+
53
+ Start with the [user guide](https://github.com/haibuihoang/wakemarch/blob/main/docs/USER_GUIDE.md), then the
54
+ [API notebook](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_api_test.ipynb) and
55
+ [resolution/cost notebook](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_performance.ipynb).
56
+ [Turbine curves and presets](https://github.com/haibuihoang/wakemarch/blob/main/docs/TURBINES.md) and an
57
+ [interactive turbine notebook](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_turbines.ipynb) accept user CP/CT
58
+ or power/CT tables, including rotor-wide velocity samples.
59
+ An optional [PIV comparison](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_piv_validation.ipynb) uses a
60
+ separately downloaded public dataset and discloses inherited calibration.
61
+ Read [scientific scope](https://github.com/haibuihoang/wakemarch/blob/main/docs/SCIENCE.md) and [citation](https://github.com/haibuihoang/wakemarch/blob/main/CITATION.md) before
62
+ using results in research. The figure is generated by `examples/plot_wake.py`.
63
+
64
+ ## Install and run
65
+
66
+ ```sh
67
+ python -m pip install wakemarch
68
+ ```
69
+
70
+ Or, from a source checkout (the directory containing `pyproject.toml`):
71
+
72
+ ```sh
73
+ python -m pip install .
74
+ python -I examples/first_wake.py
75
+ python -I tests/test_api.py
76
+ ```
77
+
78
+ ### Jupyter notebooks
79
+
80
+ The interactive entry point is `examples/wakemarch_api_test.ipynb`. It contains
81
+ editable inputs, live progress, hub-height/cross-plane plots, yaw comparisons,
82
+ and executable API checks. Run every cell with a fresh kernel.
83
+
84
+ ```sh
85
+ python -m pip install 'wakemarch[notebooks]' # or '.[notebooks]' from source
86
+ python -m jupyterlab examples/wakemarch_api_test.ipynb
87
+ ```
88
+
89
+ No measurement data, FLORIS, plotting or Jupyter are needed for the core example
90
+ or tests. For verifying built artifacts, see [release checks](https://github.com/haibuihoang/wakemarch/blob/main/docs/RELEASING.md).
91
+
92
+ ```python
93
+ from wakemarch import Numerics, WakeModel
94
+
95
+ model = WakeModel(
96
+ D=126, H=90, U=8, CT=0.8, yaw_deg=25, TI=0.06,
97
+ numerics=Numerics(h_D=1 / 20),
98
+ )
99
+ result = model.solve(max_dist_D=10)
100
+ deficit = result.sample_deficit(x=5 * 126, y=0, z=90)
101
+ velocity = result.sample_streamwise_velocity(x=5 * 126, y=0, z=90)
102
+ print(deficit, velocity) # m/s
103
+ ```
104
+
105
+ Progress is enabled by default. Jupyter uses a native HTML progress bar (no widget
106
+ extension needed); terminals use a text bar. `model.solve(progress=False)` is
107
+ silent. A callable such as `progress=lambda completed_D, total_D: ...` receives
108
+ an initial zero and updates after each marching step. Total distance includes
109
+ the reconstruction buffer. The display closes on success, errors and interrupts;
110
+ callback exceptions propagate. Progress observes the solve without changing its
111
+ numerical operations. It measures downstream advancement, not a guaranteed time
112
+ remaining estimate.
113
+
114
+ ## Define turbine performance once
115
+
116
+ ```python
117
+ from wakemarch import Turbine, Numerics
118
+
119
+ turbine = Turbine.preset("nrel_5MW")
120
+ point = turbine.operating_point([6.0, 10.0], yaw_deg=20)
121
+ print(point.power_W, point.CT)
122
+ model = turbine.wake_model([6.0, 10.0], yaw_deg=20, TI=0.1,
123
+ numerics=Numerics(h_D=0.1))
124
+ ```
125
+
126
+ Use `Turbine.from_cp_ct(...)` or `Turbine.from_power_ct(...)` for your own curves,
127
+ or `Turbine.fixed(...)` for constant coefficients. See
128
+ [units, efficiency, averaging and table limits](https://github.com/haibuihoang/wakemarch/blob/main/docs/TURBINES.md). The scalar-CT
129
+ `WakeModel` API does not require a power curve.
130
+
131
+ ## Conventions and API
132
+
133
+ - Physical inputs: `D`, `H` in metres (H ≥ D/2); `U` in m/s; `CT` is the
134
+ **unyawed** thrust coefficient; `TI` is a fraction, e.g. 0.06, not 6; yaw is in
135
+ degrees, |yaw| ≤ 50 (validated to 40).
136
+ - Positive yaw deflects towards negative `y`. `x` is downstream from the turbine,
137
+ `y` is lateral from its centre, and sampled `z` is height above ground.
138
+ - `Numerics` groups grid and stepping choices. Defaults are grid D/30, lateral
139
+ width 8D, top 3.5D **above hub**, dx=0.1D, requested storage interval=0.25D.
140
+ There is no automatic coarsening. Ground and open boundaries are enabled.
141
+ - `WakeModel` uses the recommended thrust exponent 2, vortex diffusivity factor
142
+ 0.5, momentum fraction 0.95, and near-wake display ramp (0.5, 1D). Historical
143
+ empirical additions are not exposed in the public API.
144
+ - `solve()` returns an independent `WakeResult`; solving again never changes a
145
+ previous result. Inputs/settings are immutable dataclasses.
146
+ - Sampling accepts scalars or broadcastable arrays. `sample_deficit` includes
147
+ reconstruction and the display ramp; `sample_streamwise_velocity` subtracts
148
+ this from the same power-law background used by the model.
149
+ - Upstream of the tilted rotor (`x + y*tan(yaw) < 0`), deficit is zero. Outside
150
+ downstream stored support the default is an error. `bounds="zero"` explicitly
151
+ requests a zero-deficit extension (background streamwise velocity).
152
+ Nonfinite coordinates and negative heights raise errors.
153
+ - `result.x` is the stored **marching** coordinate x_s, not global x on a yawed
154
+ cross-plane. `result.z` is absolute height; `z_relative` is relative to hub.
155
+ `marching_deficit`, `streamwise_vorticity`, and
156
+ `streamwise_velocity_derivative` have axes `(x_s, z_relative, y)` and units m/s,
157
+ 1/s and 1/s. They are read-only views; the histories do not include the display
158
+ ramp. Use the sampling methods for physical-space visualizations.
159
+
160
+ The kernel marches an extra yaw-dependent distance for reconstruction. With
161
+ dx=0.1D, requested storage 0.25D stores at 0.3D spacing (storage happens on
162
+ marching steps). Sampling uses actual stored support, not the nominal requested
163
+ distance.
164
+
165
+ ## Scientific scope
166
+
167
+ This is a steady engineering model of one turbine in uniform or power-law-sheared
168
+ inflow, not an active downstream-turbine or farm solver. Input bounds are numerical
169
+ guards, not claims of validation over their entire range. It does not resolve
170
+ ground-boundary-layer dynamics or wake pressure gradients. It has known near-wake,
171
+ high-TI and high-yaw/far-wake limitations.
172
+
173
+ The vorticity advection construction is approximately conservative; the full
174
+ variable-coefficient diffusion equation does not establish exact circulation
175
+ conservation. Force-based initialization does not imply exact agreement of the
176
+ discrete vortex integral with its target. Better empirical predictions are not
177
+ proof of exact force or conservation consistency. Validation evidence is in the
178
+ accompanying paper; installing the package does not add new observational
179
+ validation.
180
+
181
+ Optional dependency groups: `[plot]`, `[notebooks]`, `[validation]`. They are
182
+ conveniences for documentation and data workflows, not required by the core.
183
+ Measurement and simulation datasets are not distributed with the package; each
184
+ keeps its own licence and access terms.
185
+
186
+ ## Development and provenance
187
+
188
+ See [provenance](https://github.com/haibuihoang/wakemarch/blob/main/PROVENANCE.md). `wakemarch._kernel` is private; its many
189
+ historical research switches are not a supported public interface. The package
190
+ reproduces eight frozen reference cases of the authors' research solver to
191
+ rtol=1e-7, atol=1e-8 (`tools/check_release.py` runs the portable checks).
192
+
193
+ ## Licence
194
+
195
+ BSD 3-Clause; see [LICENSE](https://github.com/haibuihoang/wakemarch/blob/main/LICENSE). The bundled reference turbine tables come
196
+ from FLORIS 4.6.2 and keep their own BSD 3-Clause licence
197
+ (`src/wakemarch/data/FLORIS_LICENSE.txt`).
@@ -0,0 +1,40 @@
1
+ # Provenance
2
+
3
+ WakeMarch packages the recommended configuration
4
+ (`YawedWakeSolver.recommended(...)`) of the authors' research solver
5
+ `src/curl_pns.py`, SHA-256
6
+ `f3dce8e95c77942caf59c8a0745d9ff6d2f9ed4ab642062cec46e752856a33fc`
7
+ (research repository commit `3027932`). The solver is project research code,
8
+ not the official implementation of any comparison model.
9
+
10
+ ## Kernel
11
+
12
+ `src/wakemarch/_kernel.py` is a copy of that solver with:
13
+
14
+ - explanatory comments and an optional progress observer;
15
+ - caching of fixed advection and multipole geometry;
16
+ - reuse of the exact variable-coefficient diffusion factorization as the next
17
+ preconditioner after a stalled refinement.
18
+
19
+ The discrete operators, residual threshold (1e-9), precision, grid, physics and
20
+ fitted constants are unchanged. Iteration paths can differ, so equality is
21
+ checked numerically, not textually: the installed package reproduces eight
22
+ frozen reference cases of the research solver (zero, positive, negative and
23
+ 40° yaw; shear; low/high turbulence; coarse and fine grids) at rtol=1e-7,
24
+ atol=1e-8. This holds from NumPy 1.24.4/SciPy 1.10.1 to NumPy 2.4/SciPy 1.17.
25
+ The reference arrays are kept with the authors' development records and are not
26
+ shipped.
27
+
28
+ ## Added code
29
+
30
+ - The public API (`WakeModel`, `Numerics`, `WakeResult`), input checks,
31
+ explicitly named sampling methods and tests. No change to model equations or
32
+ fitted constants.
33
+ - `Turbine` (power/CT and CP/CT tables, presets, rotor-equivalent operating
34
+ point) and `Turbine.fixed` (constant coefficients). Three reference tables are
35
+ pinned from FLORIS 4.6.2 with their source hashes and licence
36
+ (`src/wakemarch/data/`).
37
+
38
+ An independently developed set of additional wake corrections was evaluated and
39
+ not adopted; it is not part of the package. Packaging does not establish new
40
+ scientific claims; see [scientific scope](docs/SCIENCE.md).
@@ -0,0 +1,159 @@
1
+ # WakeMarch
2
+
3
+ WakeMarch is a steady, cross-plane marching wake model for a single yawed wind
4
+ turbine. It resolves the curled (kidney-shaped) wake produced by the
5
+ counter-rotating streamwise vortex pair of a yawed rotor, in uniform or
6
+ power-law-sheared inflow, and returns the wake deficit, streamwise vorticity and
7
+ velocity anywhere downstream.
8
+
9
+ The core depends only on NumPy (≥ 1.24) and SciPy (≥ 1.10) and needs
10
+ Python 3.11 or later. The upper bounds `<3`/`<2` are compatibility boundaries,
11
+ not evidence of tests on every release.
12
+
13
+ ![WakeMarch hub-height and cross-plane wake](https://raw.githubusercontent.com/haibuihoang/wakemarch/main/docs/images/wake.png)
14
+
15
+ Start with the [user guide](https://github.com/haibuihoang/wakemarch/blob/main/docs/USER_GUIDE.md), then the
16
+ [API notebook](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_api_test.ipynb) and
17
+ [resolution/cost notebook](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_performance.ipynb).
18
+ [Turbine curves and presets](https://github.com/haibuihoang/wakemarch/blob/main/docs/TURBINES.md) and an
19
+ [interactive turbine notebook](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_turbines.ipynb) accept user CP/CT
20
+ or power/CT tables, including rotor-wide velocity samples.
21
+ An optional [PIV comparison](https://github.com/haibuihoang/wakemarch/blob/main/examples/wakemarch_piv_validation.ipynb) uses a
22
+ separately downloaded public dataset and discloses inherited calibration.
23
+ Read [scientific scope](https://github.com/haibuihoang/wakemarch/blob/main/docs/SCIENCE.md) and [citation](https://github.com/haibuihoang/wakemarch/blob/main/CITATION.md) before
24
+ using results in research. The figure is generated by `examples/plot_wake.py`.
25
+
26
+ ## Install and run
27
+
28
+ ```sh
29
+ python -m pip install wakemarch
30
+ ```
31
+
32
+ Or, from a source checkout (the directory containing `pyproject.toml`):
33
+
34
+ ```sh
35
+ python -m pip install .
36
+ python -I examples/first_wake.py
37
+ python -I tests/test_api.py
38
+ ```
39
+
40
+ ### Jupyter notebooks
41
+
42
+ The interactive entry point is `examples/wakemarch_api_test.ipynb`. It contains
43
+ editable inputs, live progress, hub-height/cross-plane plots, yaw comparisons,
44
+ and executable API checks. Run every cell with a fresh kernel.
45
+
46
+ ```sh
47
+ python -m pip install 'wakemarch[notebooks]' # or '.[notebooks]' from source
48
+ python -m jupyterlab examples/wakemarch_api_test.ipynb
49
+ ```
50
+
51
+ No measurement data, FLORIS, plotting or Jupyter are needed for the core example
52
+ or tests. For verifying built artifacts, see [release checks](https://github.com/haibuihoang/wakemarch/blob/main/docs/RELEASING.md).
53
+
54
+ ```python
55
+ from wakemarch import Numerics, WakeModel
56
+
57
+ model = WakeModel(
58
+ D=126, H=90, U=8, CT=0.8, yaw_deg=25, TI=0.06,
59
+ numerics=Numerics(h_D=1 / 20),
60
+ )
61
+ result = model.solve(max_dist_D=10)
62
+ deficit = result.sample_deficit(x=5 * 126, y=0, z=90)
63
+ velocity = result.sample_streamwise_velocity(x=5 * 126, y=0, z=90)
64
+ print(deficit, velocity) # m/s
65
+ ```
66
+
67
+ Progress is enabled by default. Jupyter uses a native HTML progress bar (no widget
68
+ extension needed); terminals use a text bar. `model.solve(progress=False)` is
69
+ silent. A callable such as `progress=lambda completed_D, total_D: ...` receives
70
+ an initial zero and updates after each marching step. Total distance includes
71
+ the reconstruction buffer. The display closes on success, errors and interrupts;
72
+ callback exceptions propagate. Progress observes the solve without changing its
73
+ numerical operations. It measures downstream advancement, not a guaranteed time
74
+ remaining estimate.
75
+
76
+ ## Define turbine performance once
77
+
78
+ ```python
79
+ from wakemarch import Turbine, Numerics
80
+
81
+ turbine = Turbine.preset("nrel_5MW")
82
+ point = turbine.operating_point([6.0, 10.0], yaw_deg=20)
83
+ print(point.power_W, point.CT)
84
+ model = turbine.wake_model([6.0, 10.0], yaw_deg=20, TI=0.1,
85
+ numerics=Numerics(h_D=0.1))
86
+ ```
87
+
88
+ Use `Turbine.from_cp_ct(...)` or `Turbine.from_power_ct(...)` for your own curves,
89
+ or `Turbine.fixed(...)` for constant coefficients. See
90
+ [units, efficiency, averaging and table limits](https://github.com/haibuihoang/wakemarch/blob/main/docs/TURBINES.md). The scalar-CT
91
+ `WakeModel` API does not require a power curve.
92
+
93
+ ## Conventions and API
94
+
95
+ - Physical inputs: `D`, `H` in metres (H ≥ D/2); `U` in m/s; `CT` is the
96
+ **unyawed** thrust coefficient; `TI` is a fraction, e.g. 0.06, not 6; yaw is in
97
+ degrees, |yaw| ≤ 50 (validated to 40).
98
+ - Positive yaw deflects towards negative `y`. `x` is downstream from the turbine,
99
+ `y` is lateral from its centre, and sampled `z` is height above ground.
100
+ - `Numerics` groups grid and stepping choices. Defaults are grid D/30, lateral
101
+ width 8D, top 3.5D **above hub**, dx=0.1D, requested storage interval=0.25D.
102
+ There is no automatic coarsening. Ground and open boundaries are enabled.
103
+ - `WakeModel` uses the recommended thrust exponent 2, vortex diffusivity factor
104
+ 0.5, momentum fraction 0.95, and near-wake display ramp (0.5, 1D). Historical
105
+ empirical additions are not exposed in the public API.
106
+ - `solve()` returns an independent `WakeResult`; solving again never changes a
107
+ previous result. Inputs/settings are immutable dataclasses.
108
+ - Sampling accepts scalars or broadcastable arrays. `sample_deficit` includes
109
+ reconstruction and the display ramp; `sample_streamwise_velocity` subtracts
110
+ this from the same power-law background used by the model.
111
+ - Upstream of the tilted rotor (`x + y*tan(yaw) < 0`), deficit is zero. Outside
112
+ downstream stored support the default is an error. `bounds="zero"` explicitly
113
+ requests a zero-deficit extension (background streamwise velocity).
114
+ Nonfinite coordinates and negative heights raise errors.
115
+ - `result.x` is the stored **marching** coordinate x_s, not global x on a yawed
116
+ cross-plane. `result.z` is absolute height; `z_relative` is relative to hub.
117
+ `marching_deficit`, `streamwise_vorticity`, and
118
+ `streamwise_velocity_derivative` have axes `(x_s, z_relative, y)` and units m/s,
119
+ 1/s and 1/s. They are read-only views; the histories do not include the display
120
+ ramp. Use the sampling methods for physical-space visualizations.
121
+
122
+ The kernel marches an extra yaw-dependent distance for reconstruction. With
123
+ dx=0.1D, requested storage 0.25D stores at 0.3D spacing (storage happens on
124
+ marching steps). Sampling uses actual stored support, not the nominal requested
125
+ distance.
126
+
127
+ ## Scientific scope
128
+
129
+ This is a steady engineering model of one turbine in uniform or power-law-sheared
130
+ inflow, not an active downstream-turbine or farm solver. Input bounds are numerical
131
+ guards, not claims of validation over their entire range. It does not resolve
132
+ ground-boundary-layer dynamics or wake pressure gradients. It has known near-wake,
133
+ high-TI and high-yaw/far-wake limitations.
134
+
135
+ The vorticity advection construction is approximately conservative; the full
136
+ variable-coefficient diffusion equation does not establish exact circulation
137
+ conservation. Force-based initialization does not imply exact agreement of the
138
+ discrete vortex integral with its target. Better empirical predictions are not
139
+ proof of exact force or conservation consistency. Validation evidence is in the
140
+ accompanying paper; installing the package does not add new observational
141
+ validation.
142
+
143
+ Optional dependency groups: `[plot]`, `[notebooks]`, `[validation]`. They are
144
+ conveniences for documentation and data workflows, not required by the core.
145
+ Measurement and simulation datasets are not distributed with the package; each
146
+ keeps its own licence and access terms.
147
+
148
+ ## Development and provenance
149
+
150
+ See [provenance](https://github.com/haibuihoang/wakemarch/blob/main/PROVENANCE.md). `wakemarch._kernel` is private; its many
151
+ historical research switches are not a supported public interface. The package
152
+ reproduces eight frozen reference cases of the authors' research solver to
153
+ rtol=1e-7, atol=1e-8 (`tools/check_release.py` runs the portable checks).
154
+
155
+ ## Licence
156
+
157
+ BSD 3-Clause; see [LICENSE](https://github.com/haibuihoang/wakemarch/blob/main/LICENSE). The bundled reference turbine tables come
158
+ from FLORIS 4.6.2 and keep their own BSD 3-Clause licence
159
+ (`src/wakemarch/data/FLORIS_LICENSE.txt`).
@@ -0,0 +1,77 @@
1
+ # Development and release verification
2
+
3
+ ## Portable checks
4
+
5
+ From the package root, in an environment with Python 3.11+ and setuptools/wheel:
6
+
7
+ ```sh
8
+ python -m pip install 'setuptools>=77' wheel
9
+ python -I -B tools/check_release.py --output /tmp/wakemarch-check --notebooks
10
+ ```
11
+
12
+ Use any new writable output directory on Windows instead of `/tmp/...`. The
13
+ driver builds a wheel and sdist, creates fresh isolated environments, resolves
14
+ dependencies, checks import origins, runs `pip check`, tests, the example and
15
+ executable documentation. It checks the built source contains the guide and
16
+ notebooks. `--notebooks` adds figure generation and fresh-kernel execution from
17
+ the extracted source using the installed wheel. Outputs and logs stay outside
18
+ the source tree. `--minimum` pins the declared floors, NumPy 1.24.4 and
19
+ SciPy 1.10.1.
20
+
21
+ For an offline local check only:
22
+
23
+ ```sh
24
+ python -I -B tools/check_release.py --offline --notebooks --output /tmp/wakemarch-offline
25
+ ```
26
+
27
+ This mode inherits local numerical/notebook/build dependencies; it is **not**
28
+ fresh dependency resolution. Its result records `isolated_dependencies=false`.
29
+ The authors additionally compare every release with eight frozen reference
30
+ cases of the research solver at rtol=1e-7, atol=1e-8; those reference arrays are
31
+ not shipped.
32
+
33
+ ## Continuous integration
34
+
35
+ `.github/workflows/package.yml` runs wheel/sdist checks on Linux, macOS and
36
+ Windows with Python 3.11–3.14, plus a Linux Python 3.11 minimum-dependency job.
37
+ The Linux Python 3.11 job also executes the data-free notebooks. Dependency
38
+ upper bounds are not evidence of compatibility with every future version.
39
+ The workflow has read-only repository access.
40
+
41
+ ## Publishing
42
+
43
+ `.github/workflows/publish.yml` builds the distributions and uploads them with
44
+ PyPI trusted publishing (no API tokens): manually to TestPyPI
45
+ (`workflow_dispatch`, target `testpypi`) or to PyPI when a GitHub release is
46
+ published. The trusted publisher must be configured on (Test)PyPI for this
47
+ repository, workflow `publish.yml` and environments `testpypi`/`pypi`.
48
+ The workflow refuses to run while any release file still contains the marker
49
+ `PENDING-D2` (author/copyright metadata not yet supplied) or the classifier
50
+ `Private :: Do Not Upload`.
51
+
52
+ Release steps: update `version`, `CHANGELOG.md` and `CITATION.cff`; run the
53
+ portable checks (normal, `--minimum`, `--notebooks`); confirm CI is green;
54
+ rehearse on TestPyPI; then publish a GitHub release.
55
+
56
+ This follows the [GitHub Python CI guide](https://docs.github.com/en/actions/tutorials/build-and-test-code/python),
57
+ the [PyPA packaging guide](https://packaging.python.org/en/latest/tutorials/packaging-projects/)
58
+ and [PyPA trusted publishing](https://docs.pypi.org/trusted-publishers/).
59
+
60
+ ## Paper reproducibility
61
+
62
+ The paper's figures were produced with the authors' research solver. Record the
63
+ package version used for any figure only after reproducing it with that version.
64
+ `docs/release-metadata.json` records licence, repository and DOI metadata.
65
+
66
+ ## Notebooks
67
+
68
+ To run all four notebooks with a registered local kernel and an existing
69
+ download of the PIV data (no automatic data access):
70
+
71
+ ```sh
72
+ python -I -B tools/run_notebooks.py --output /tmp/wakemarch-notebooks --piv-data /path/to/Bastankhah_jfm_2016.h5
73
+ ```
74
+
75
+ The CI notebook job executes only the three data-free examples. The PIV notebook
76
+ is checked locally against the checksum-pinned public file; this does not create
77
+ a redistributable data fixture or a blind-validation claim.
@@ -0,0 +1,74 @@
1
+ # Scientific scope and evidence
2
+
3
+ WakeMarch implements the accepted recommended single-turbine model: thrust
4
+ exponent 2, vortex diffusivity factor 0.5, momentum fraction 0.95, and display
5
+ ramp (0.5, 1D). The package does not expose historical optional empirical
6
+ corrections as supported public settings. The combined independent-correction
7
+ experiment remains archived and is not the production model.
8
+
9
+ The intended first audience is researchers. The model has known near-wake,
10
+ high-turbulence and high-yaw/far-wake limitations. It does not resolve wake
11
+ pressure gradients or ground-boundary-layer dynamics. Input guards are not an
12
+ experimental validation envelope. Engineering and operational use requires
13
+ application-specific assessment; no farm solver or active downstream-turbine
14
+ model is provided.
15
+
16
+ Advection is approximately conservative; variable-coefficient diffusion does not
17
+ establish exact circulation conservation. Force-based initialization does not
18
+ guarantee exact equality of the discrete vortex integral and its target. Better
19
+ empirical predictions are not proof of exact conservation or force consistency.
20
+
21
+ ## What the software checks establish
22
+
23
+ - API tests: input conventions, sampling, progress, independent results and
24
+ diffusion residual checks.
25
+ - Frozen-array comparisons: implementation agreement with eight accepted
26
+ research cases at rtol=1e-7, atol=1e-8. These are not observational error bars.
27
+ - Grid notebook: differences between numerical resolutions for a specified case.
28
+ Its finest grid is a reference, not a converged solution or observation.
29
+ - Historical numerical tests: manufactured operators, symmetry and approximate
30
+ invariants, subject to their stated tolerances.
31
+ - Historical measurement comparisons: scientific evidence with calibration and
32
+ preprocessing choices described in the research reports, not recreated by a
33
+ successful package installation.
34
+
35
+ ## Data and validation boundary
36
+
37
+ The accompanying paper compares the model with PALM LES, hub-height PIV,
38
+ Oldenburg lidar and NTNU/ForWind LDA data. The LES data used in the paper are not
39
+ distributed with this package. Other datasets have their own access and license
40
+ terms; a code license cannot grant redistribution rights to them. No measurement
41
+ files are included in the wheel or source distribution.
42
+
43
+ The optional [PIV measurement notebook](../examples/wakemarch_piv_validation.ipynb)
44
+ uses [Bastankhah and Porté-Agel's Zenodo v1 dataset](https://zenodo.org/records/3607598),
45
+ DOI 10.5281/zenodo.3607598. Obtain the HDF5 file separately and inspect its terms;
46
+ the notebook checks the record's MD5 and records SHA-256. No data are bundled.
47
+ It inherits the historical unyawed CT=0.9 and k=0.024 calibration, converts k to
48
+ equivalent TI and performs no new fit. These cases were previously examined
49
+ during model development: this is not a new blind test. Its same-point RMS
50
+ comparison differs from historical scoreboard preprocessing and metrics.
51
+ Do not label the introductory or grid notebooks as measurement validation.
52
+ Record geometry, inflow/CT values, yaw sign and coordinate mapping, uncertainty
53
+ and calibration exposure. Evaluate model and data on the same measurement support.
54
+ For tower/floor-contaminated laboratory planes, document the rotor-band mask.
55
+
56
+ Do not silently substitute synthetic data when measurements are missing. Farm
57
+ validation additionally requires turbine interaction, local inflow and thrust
58
+ definitions; the single-wake downstream rotor u³ proxy is insufficient.
59
+
60
+ ## Paper linkage
61
+
62
+ `release-metadata.json` records publication metadata. The paper's figures were
63
+ produced with the authors' research solver, which this package reproduces to
64
+ rtol=1e-7 on eight reference cases; the paper-specific software version is
65
+ recorded once figure reproduction with the package has been verified. Until the
66
+ paper reference is available, see [citation](../CITATION.md).
67
+
68
+ ## Reference turbine performance inputs
69
+
70
+ The package now includes reference power/CT tables and a rotor-equivalent
71
+ operating model; see [TURBINES.md](TURBINES.md). This adds no farm validation or
72
+ new wake physics. CP converted from the power tables is electrical-equivalent.
73
+ User CP inputs require an explicit efficiency convention. Effective-speed yaw
74
+ power mapping is an approximation, not a controller simulation.