pybmodes 1.8.0__py3-none-any.whl
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.
- pybmodes/__init__.py +188 -0
- pybmodes/_examples/__init__.py +45 -0
- pybmodes/_examples/reference_decks/FLOATING_CASES.md +144 -0
- pybmodes/_examples/reference_decks/README.md +150 -0
- pybmodes/_examples/reference_decks/VALIDATION_SUMMARY.md +63 -0
- pybmodes/_examples/reference_decks/iea15mw_umainesemi/IEA-15-240-RWT-UMaineSemi_ElastoDyn.dat +261 -0
- pybmodes/_examples/reference_decks/iea15mw_umainesemi/IEA-15-240-RWT-UMaineSemi_Tower.dat +51 -0
- pybmodes/_examples/reference_decks/iea15mw_umainesemi/IEA-15-240-RWT_Blade.dat +82 -0
- pybmodes/_examples/reference_decks/iea15mw_umainesemi/before_patch.txt +17 -0
- pybmodes/_examples/reference_decks/iea15mw_umainesemi/validation_report.txt +42 -0
- pybmodes/_examples/reference_decks/iea34_land/IEA-3.4-130-RWT_Blade.dat +62 -0
- pybmodes/_examples/reference_decks/iea34_land/IEA-3.4-130-RWT_ElastoDyn.dat +162 -0
- pybmodes/_examples/reference_decks/iea34_land/IEA-3.4-130-RWT_Tower.dat +51 -0
- pybmodes/_examples/reference_decks/iea34_land/before_patch.txt +17 -0
- pybmodes/_examples/reference_decks/iea34_land/validation_report.txt +16 -0
- pybmodes/_examples/reference_decks/nrel5mw_land/NRELOffshrBsline5MW_Blade.dat +83 -0
- pybmodes/_examples/reference_decks/nrel5mw_land/NRELOffshrBsline5MW_Onshore_ElastoDyn.dat +185 -0
- pybmodes/_examples/reference_decks/nrel5mw_land/NRELOffshrBsline5MW_Tower.dat +54 -0
- pybmodes/_examples/reference_decks/nrel5mw_land/before_patch.txt +17 -0
- pybmodes/_examples/reference_decks/nrel5mw_land/validation_report.txt +16 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3monopile/NRELOffshrBsline5MW_Blade.dat +83 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3monopile/NRELOffshrBsline5MW_OC3Monopile_ElastoDyn.dat +165 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3monopile/NRELOffshrBsline5MW_OC3Monopile_SubDyn.dat +109 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3monopile/NRELOffshrBsline5MW_OC3Monopile_Tower.dat +54 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3monopile/before_patch.txt +17 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3monopile/validation_report.txt +16 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3spar/NRELOffshrBsline5MW_Blade.dat +83 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3spar/NRELOffshrBsline5MW_OC3Hywind_ElastoDyn.dat +234 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3spar/NRELOffshrBsline5MW_OC3Hywind_Tower.dat +54 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3spar/before_patch.txt +17 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc3spar/validation_report.txt +16 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc4semi/NRELOffshrBsline5MW_Blade.dat +83 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc4semi/NRELOffshrBsline5MW_OC4DeepCwindSemi_ElastoDyn.dat +189 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc4semi/NRELOffshrBsline5MW_OC4DeepCwindSemi_Tower.dat +54 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc4semi/before_patch.txt +17 -0
- pybmodes/_examples/reference_decks/nrel5mw_oc4semi/validation_report.txt +16 -0
- pybmodes/_examples/sample_inputs/01_uniform_blade/README.md +75 -0
- pybmodes/_examples/sample_inputs/01_uniform_blade/uniform_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/01_uniform_blade/uniform_blade_sec_props.dat +7 -0
- pybmodes/_examples/sample_inputs/02_tower_topmass/README.md +82 -0
- pybmodes/_examples/sample_inputs/02_tower_topmass/tower_topmass.bmi +54 -0
- pybmodes/_examples/sample_inputs/02_tower_topmass/tower_topmass_sec_props.dat +7 -0
- pybmodes/_examples/sample_inputs/03_rotating_uniform_blade/README.md +103 -0
- pybmodes/_examples/sample_inputs/03_rotating_uniform_blade/rotating_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/03_rotating_uniform_blade/rotating_blade_sec_props.dat +7 -0
- pybmodes/_examples/sample_inputs/04_pinned_free_cable/README.md +94 -0
- pybmodes/_examples/sample_inputs/04_pinned_free_cable/cable.bmi +51 -0
- pybmodes/_examples/sample_inputs/04_pinned_free_cable/cable_sec_props.dat +7 -0
- pybmodes/_examples/sample_inputs/README.md +115 -0
- pybmodes/_examples/sample_inputs/reference_turbines/01_nrel5mw_land/01_nrel5mw_land_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/01_nrel5mw_land/01_nrel5mw_land_blade_sec_props.dat +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/01_nrel5mw_land/01_nrel5mw_land_tower.bmi +60 -0
- pybmodes/_examples/sample_inputs/reference_turbines/01_nrel5mw_land/01_nrel5mw_land_tower_sec_props.dat +16 -0
- pybmodes/_examples/sample_inputs/reference_turbines/01_nrel5mw_land/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/02_nrel5mw_oc3monopile/02_nrel5mw_oc3monopile_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/02_nrel5mw_oc3monopile/02_nrel5mw_oc3monopile_blade_sec_props.dat +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/02_nrel5mw_oc3monopile/02_nrel5mw_oc3monopile_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/02_nrel5mw_oc3monopile/02_nrel5mw_oc3monopile_tower_sec_props.dat +20 -0
- pybmodes/_examples/sample_inputs/reference_turbines/02_nrel5mw_oc3monopile/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/03_iea34_land/03_iea34_land_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/03_iea34_land/03_iea34_land_blade_sec_props.dat +35 -0
- pybmodes/_examples/sample_inputs/reference_turbines/03_iea34_land/03_iea34_land_tower.bmi +60 -0
- pybmodes/_examples/sample_inputs/reference_turbines/03_iea34_land/03_iea34_land_tower_sec_props.dat +15 -0
- pybmodes/_examples/sample_inputs/reference_turbines/03_iea34_land/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/04_iea10_monopile/04_iea10_monopile_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/04_iea10_monopile/04_iea10_monopile_blade_sec_props.dat +35 -0
- pybmodes/_examples/sample_inputs/reference_turbines/04_iea10_monopile/04_iea10_monopile_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/04_iea10_monopile/04_iea10_monopile_tower_sec_props.dat +35 -0
- pybmodes/_examples/sample_inputs/reference_turbines/04_iea10_monopile/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/05_iea15_monopile/05_iea15_monopile_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/05_iea15_monopile/05_iea15_monopile_blade_sec_props.dat +55 -0
- pybmodes/_examples/sample_inputs/reference_turbines/05_iea15_monopile/05_iea15_monopile_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/05_iea15_monopile/05_iea15_monopile_tower_sec_props.dat +44 -0
- pybmodes/_examples/sample_inputs/reference_turbines/05_iea15_monopile/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/06_iea22_monopile/06_iea22_monopile_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/06_iea22_monopile/06_iea22_monopile_blade_sec_props.dat +64 -0
- pybmodes/_examples/sample_inputs/reference_turbines/06_iea22_monopile/06_iea22_monopile_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/06_iea22_monopile/06_iea22_monopile_tower_sec_props.dat +45 -0
- pybmodes/_examples/sample_inputs/reference_turbines/06_iea22_monopile/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/07_nrel5mw_oc3hywind_spar/07_nrel5mw_oc3hywind_spar_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/07_nrel5mw_oc3hywind_spar/07_nrel5mw_oc3hywind_spar_blade_sec_props.dat +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/07_nrel5mw_oc3hywind_spar/07_nrel5mw_oc3hywind_spar_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/07_nrel5mw_oc3hywind_spar/07_nrel5mw_oc3hywind_spar_tower_sec_props.dat +16 -0
- pybmodes/_examples/sample_inputs/reference_turbines/07_nrel5mw_oc3hywind_spar/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/08_nrel5mw_oc4semi/08_nrel5mw_oc4semi_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/08_nrel5mw_oc4semi/08_nrel5mw_oc4semi_blade_sec_props.dat +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/08_nrel5mw_oc4semi/08_nrel5mw_oc4semi_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/08_nrel5mw_oc4semi/08_nrel5mw_oc4semi_tower_sec_props.dat +16 -0
- pybmodes/_examples/sample_inputs/reference_turbines/08_nrel5mw_oc4semi/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/09_iea15_umainesemi/09_iea15_umainesemi_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/09_iea15_umainesemi/09_iea15_umainesemi_blade_sec_props.dat +55 -0
- pybmodes/_examples/sample_inputs/reference_turbines/09_iea15_umainesemi/09_iea15_umainesemi_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/09_iea15_umainesemi/09_iea15_umainesemi_tower_sec_props.dat +15 -0
- pybmodes/_examples/sample_inputs/reference_turbines/09_iea15_umainesemi/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/10_iea22_semi/10_iea22_semi_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/10_iea22_semi/10_iea22_semi_blade_sec_props.dat +64 -0
- pybmodes/_examples/sample_inputs/reference_turbines/10_iea22_semi/10_iea22_semi_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/10_iea22_semi/10_iea22_semi_tower_sec_props.dat +35 -0
- pybmodes/_examples/sample_inputs/reference_turbines/10_iea22_semi/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/11_upscale25_centraltower/11_upscale25_centraltower_blade.bmi +51 -0
- pybmodes/_examples/sample_inputs/reference_turbines/11_upscale25_centraltower/11_upscale25_centraltower_blade_sec_props.dat +58 -0
- pybmodes/_examples/sample_inputs/reference_turbines/11_upscale25_centraltower/11_upscale25_centraltower_tower.bmi +100 -0
- pybmodes/_examples/sample_inputs/reference_turbines/11_upscale25_centraltower/11_upscale25_centraltower_tower_sec_props.dat +25 -0
- pybmodes/_examples/sample_inputs/reference_turbines/11_upscale25_centraltower/README.md +54 -0
- pybmodes/_examples/sample_inputs/reference_turbines/README.md +170 -0
- pybmodes/_examples/sample_inputs/reference_turbines/build.py +1585 -0
- pybmodes/_examples/sample_inputs/verify.py +169 -0
- pybmodes/campbell/__init__.py +112 -0
- pybmodes/campbell/_classify.py +119 -0
- pybmodes/campbell/_mac.py +90 -0
- pybmodes/campbell/_models.py +210 -0
- pybmodes/campbell/_plot.py +393 -0
- pybmodes/campbell/_sweep.py +347 -0
- pybmodes/campbell/result.py +271 -0
- pybmodes/checks.py +445 -0
- pybmodes/cli.py +780 -0
- pybmodes/coords.py +120 -0
- pybmodes/elastodyn/__init__.py +44 -0
- pybmodes/elastodyn/params.py +798 -0
- pybmodes/elastodyn/validate.py +432 -0
- pybmodes/elastodyn/writer.py +97 -0
- pybmodes/fem/__init__.py +14 -0
- pybmodes/fem/assembly.py +314 -0
- pybmodes/fem/boundary.py +165 -0
- pybmodes/fem/element.py +394 -0
- pybmodes/fem/gauss.py +40 -0
- pybmodes/fem/nondim.py +376 -0
- pybmodes/fem/normalize.py +141 -0
- pybmodes/fem/platform_modes.py +153 -0
- pybmodes/fem/solver.py +245 -0
- pybmodes/fitting/__init__.py +18 -0
- pybmodes/fitting/poly_fit.py +153 -0
- pybmodes/io/__init__.py +56 -0
- pybmodes/io/_elastodyn/__init__.py +92 -0
- pybmodes/io/_elastodyn/adapter.py +527 -0
- pybmodes/io/_elastodyn/lex.py +139 -0
- pybmodes/io/_elastodyn/parser.py +558 -0
- pybmodes/io/_elastodyn/types.py +213 -0
- pybmodes/io/_elastodyn/writer.py +233 -0
- pybmodes/io/_precomp/__init__.py +42 -0
- pybmodes/io/_precomp/arc_resolver.py +430 -0
- pybmodes/io/_precomp/decouple.py +257 -0
- pybmodes/io/_precomp/laminate.py +214 -0
- pybmodes/io/_precomp/profile.py +201 -0
- pybmodes/io/_precomp/reduction.py +329 -0
- pybmodes/io/_serialize.py +148 -0
- pybmodes/io/bmi.py +733 -0
- pybmodes/io/elastodyn_reader.py +129 -0
- pybmodes/io/errors.py +191 -0
- pybmodes/io/geometry.py +135 -0
- pybmodes/io/out_parser.py +314 -0
- pybmodes/io/sec_props.py +187 -0
- pybmodes/io/subdyn_reader.py +612 -0
- pybmodes/io/wamit_reader.py +534 -0
- pybmodes/io/windio.py +322 -0
- pybmodes/io/windio_blade.py +588 -0
- pybmodes/io/windio_floating.py +466 -0
- pybmodes/mac.py +306 -0
- pybmodes/models/__init__.py +20 -0
- pybmodes/models/_pipeline.py +260 -0
- pybmodes/models/_platform.py +139 -0
- pybmodes/models/_shared.py +69 -0
- pybmodes/models/blade.py +271 -0
- pybmodes/models/result.py +443 -0
- pybmodes/models/tower.py +968 -0
- pybmodes/mooring/__init__.py +104 -0
- pybmodes/mooring/_catenary.py +113 -0
- pybmodes/mooring/_moordyn_parser.py +255 -0
- pybmodes/mooring/_rotation.py +41 -0
- pybmodes/mooring/system.py +608 -0
- pybmodes/mooring/types.py +248 -0
- pybmodes/options.py +173 -0
- pybmodes/plots/__init__.py +54 -0
- pybmodes/plots/environmental.py +365 -0
- pybmodes/plots/mode_shapes.py +791 -0
- pybmodes/plots/style.py +163 -0
- pybmodes/py.typed +0 -0
- pybmodes/report.py +623 -0
- pybmodes/workflows/__init__.py +118 -0
- pybmodes/workflows/_base.py +61 -0
- pybmodes/workflows/batch.py +400 -0
- pybmodes/workflows/campbell.py +195 -0
- pybmodes/workflows/examples.py +197 -0
- pybmodes/workflows/patch.py +387 -0
- pybmodes/workflows/report.py +206 -0
- pybmodes/workflows/validate.py +179 -0
- pybmodes/workflows/windio.py +552 -0
- pybmodes-1.8.0.dist-info/METADATA +369 -0
- pybmodes-1.8.0.dist-info/RECORD +193 -0
- pybmodes-1.8.0.dist-info/WHEEL +5 -0
- pybmodes-1.8.0.dist-info/entry_points.txt +2 -0
- pybmodes-1.8.0.dist-info/licenses/LICENSE +202 -0
- pybmodes-1.8.0.dist-info/top_level.txt +1 -0
pybmodes/__init__.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Copyright 2024-2026 Jae Hoon Seo
|
|
2
|
+
# Marine Structural Mechanics and Integrity Lab (SMI Lab), Inha University
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""pybmodes — Python finite-element library for wind turbine modal analysis.
|
|
17
|
+
|
|
18
|
+
Public API
|
|
19
|
+
==========
|
|
20
|
+
|
|
21
|
+
The following subpackage entry points are the stable, semver-protected
|
|
22
|
+
1.x surface (see the *Compatibility policy* section in the README).
|
|
23
|
+
Anything else in the package tree is internal and may change between
|
|
24
|
+
minor releases.
|
|
25
|
+
|
|
26
|
+
from pybmodes.models import RotatingBlade, Tower, ModalResult
|
|
27
|
+
from pybmodes.elastodyn import (
|
|
28
|
+
compute_blade_params,
|
|
29
|
+
compute_tower_params,
|
|
30
|
+
compute_tower_params_report,
|
|
31
|
+
patch_dat,
|
|
32
|
+
validate_dat_coefficients,
|
|
33
|
+
BladeElastoDynParams,
|
|
34
|
+
TowerElastoDynParams,
|
|
35
|
+
ValidationResult,
|
|
36
|
+
CoeffBlockResult, # tower blocks carry fa/ss/torsion
|
|
37
|
+
# participation + rejected_modes
|
|
38
|
+
)
|
|
39
|
+
from pybmodes.fitting import PolyFitResult, fit_mode_shape
|
|
40
|
+
from pybmodes.campbell import (
|
|
41
|
+
CampbellResult, # save / load / to_csv
|
|
42
|
+
campbell_sweep,
|
|
43
|
+
plot_campbell,
|
|
44
|
+
)
|
|
45
|
+
from pybmodes.checks import check_model, ModelWarning
|
|
46
|
+
from pybmodes.mac import (
|
|
47
|
+
mac_matrix,
|
|
48
|
+
compare_modes,
|
|
49
|
+
ModeComparison,
|
|
50
|
+
plot_mac,
|
|
51
|
+
shape_to_vector,
|
|
52
|
+
)
|
|
53
|
+
from pybmodes.report import generate_report
|
|
54
|
+
from pybmodes.mooring import LineType, Point, Line, MooringSystem
|
|
55
|
+
from pybmodes.io import (
|
|
56
|
+
HydroDynReader, WamitReader, WamitData,
|
|
57
|
+
PlatformSupport, TipMassProps,
|
|
58
|
+
read_out, BModeOutParseError, # read_out(..., strict=True)
|
|
59
|
+
)
|
|
60
|
+
from pybmodes.coords import DOF_NAMES, DOF_INDEX # 6-DOF convention
|
|
61
|
+
from pybmodes.plots import (
|
|
62
|
+
apply_style,
|
|
63
|
+
plot_mode_shapes,
|
|
64
|
+
plot_fit_quality,
|
|
65
|
+
bir_mode_shape_plot,
|
|
66
|
+
bir_mode_shape_subplot,
|
|
67
|
+
plot_environmental_spectra, # wind/wave + 1P/3P vs tower
|
|
68
|
+
kaimal_spectrum,
|
|
69
|
+
jonswap_spectrum,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# On Tower:
|
|
73
|
+
# Tower.from_bmi(bmi_path)
|
|
74
|
+
# Tower.from_elastodyn(main_dat)
|
|
75
|
+
# Tower.from_elastodyn_with_subdyn(main_dat, subdyn_dat)
|
|
76
|
+
# Tower.from_elastodyn_with_mooring(main_dat, moordyn_dat,
|
|
77
|
+
# hydrodyn_dat=None)
|
|
78
|
+
# Tower.from_geometry(station_grid, outer_diameter,
|
|
79
|
+
# wall_thickness, *, flexible_length,
|
|
80
|
+
# E, rho, nu, outfitting_factor)
|
|
81
|
+
# Tower.from_windio(yaml_path, *, component, thickness_interp,
|
|
82
|
+
# hub_conn, tip_mass, n_nodes)
|
|
83
|
+
# tip_mass: TipMassProps | float (RNA mass kg); n_nodes:
|
|
84
|
+
# refine the FE mesh onto N even stations (issue #35)
|
|
85
|
+
# Tower.from_windio_floating(yaml_path, *, water_depth,
|
|
86
|
+
# hydrodyn_dat, moordyn_dat,
|
|
87
|
+
# elastodyn_dat) # coupled FOWT
|
|
88
|
+
|
|
89
|
+
# On RotatingBlade:
|
|
90
|
+
# RotatingBlade.from_bmi(bmi_path)
|
|
91
|
+
# RotatingBlade.from_elastodyn(main_dat)
|
|
92
|
+
# RotatingBlade.from_windio(yaml_path, *, component, n_span,
|
|
93
|
+
# rot_rpm, elastic)
|
|
94
|
+
# elastic="auto" (default) uses the WindIO *published*
|
|
95
|
+
# distributed elastic properties when present — the full 6×6
|
|
96
|
+
# decoupled at the elastic/shear centre + principal elastic
|
|
97
|
+
# axes (issue #50; pybmodes.io._precomp.decouple), not the
|
|
98
|
+
# raw diagonal — else the PreComp-class layup reduction;
|
|
99
|
+
# "precomp" forces the reduction; "file" requires the
|
|
100
|
+
# published properties.
|
|
101
|
+
|
|
102
|
+
from pybmodes.io.geometry import tubular_section_props
|
|
103
|
+
# WindIO .yaml input needs the optional [windio] extra (PyYAML);
|
|
104
|
+
# the runtime core stays numpy+scipy only — same stance as
|
|
105
|
+
# [plots]/[notebook]. Tower (tubular tower/monopile):
|
|
106
|
+
from pybmodes.io.windio import read_windio_tubular, WindIOTubular
|
|
107
|
+
# Blade (composite layup -> PreComp-class thin-wall reduction):
|
|
108
|
+
from pybmodes.io.windio_blade import (
|
|
109
|
+
read_windio_blade,
|
|
110
|
+
windio_blade_section_props,
|
|
111
|
+
WindIOBlade,
|
|
112
|
+
)
|
|
113
|
+
# Floating substructure (member-Morison hydro + catenary mooring;
|
|
114
|
+
# used by Tower.from_windio_floating, yaml-first + deck-fallback):
|
|
115
|
+
from pybmodes.io.windio_floating import (
|
|
116
|
+
read_windio_floating,
|
|
117
|
+
hydrostatic_restoring,
|
|
118
|
+
added_mass,
|
|
119
|
+
rigid_body_inertia,
|
|
120
|
+
WindIOFloating,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
``ModalResult`` ships ``save(.npz)`` / ``load(.npz)`` /
|
|
124
|
+
``to_json(.json)`` / ``from_json(.json)`` with metadata (pyBmodes
|
|
125
|
+
version, source file, timestamp, git hash) and optional
|
|
126
|
+
``participation`` + ``fit_residuals`` + ``mode_labels`` fields
|
|
127
|
+
(``mode_labels`` names the floating-platform rigid-body modes —
|
|
128
|
+
surge / sway / heave / roll / pitch / yaw — for a free-free model;
|
|
129
|
+
``None`` otherwise). ``CampbellResult`` ships ``save(.npz)`` /
|
|
130
|
+
``load(.npz)`` / ``to_csv(.csv)``.
|
|
131
|
+
|
|
132
|
+
Known limitations of the 1.0 surface:
|
|
133
|
+
|
|
134
|
+
- ``pybmodes.mooring`` is catenary-only quasi-static — no seabed
|
|
135
|
+
friction (``CB > 0``), no sloped seabed, no U-shape lines, no
|
|
136
|
+
time-domain dynamics, no line drag / added mass.
|
|
137
|
+
- ``pybmodes.io.WamitReader`` extracts ``A_inf`` (infinite-frequency
|
|
138
|
+
added mass), ``A_0`` (zero-frequency), and ``C_hst`` (hydrostatic
|
|
139
|
+
restoring); finite-period frequency-dependent ``A(ω)`` / ``B(ω)``
|
|
140
|
+
are skipped.
|
|
141
|
+
- ``Tower.from_elastodyn_with_mooring`` assembles a free-free floating
|
|
142
|
+
BMI for coupled-frequency prediction; ElastoDyn polynomial-
|
|
143
|
+
coefficient generation continues to use the cantilever
|
|
144
|
+
``Tower.from_elastodyn`` regardless of platform configuration (see
|
|
145
|
+
``cases/ECOSYSTEM_FINDING.md`` for the source-code citation).
|
|
146
|
+
- ``BMIFile.support.distr_m`` (distributed hydrodynamic added mass
|
|
147
|
+
per unit tower length) is parsed by ``pybmodes.io.bmi.read_bmi``
|
|
148
|
+
but NOT wired into the FEM mass matrix; ``distr_k`` (distributed
|
|
149
|
+
soil stiffness) IS consumed. A ``UserWarning`` fires at parse time
|
|
150
|
+
if a deck specifies non-empty ``distr_m`` so the gap is not
|
|
151
|
+
silent.
|
|
152
|
+
|
|
153
|
+
Internal modules (``pybmodes.fem.*``, the underscore-prefixed
|
|
154
|
+
``pybmodes.models._pipeline``, and the private sub-package
|
|
155
|
+
``pybmodes.io._elastodyn``) carry the implementation and should not
|
|
156
|
+
be imported directly by user code; their signatures may change
|
|
157
|
+
between minor releases. The per-format submodules under
|
|
158
|
+
``pybmodes.io`` (``pybmodes.io.bmi``, ``elastodyn_reader``,
|
|
159
|
+
``subdyn_reader``, ``wamit_reader``) are reachable directly but the
|
|
160
|
+
public-freeze contract covers only the re-exports listed above.
|
|
161
|
+
|
|
162
|
+
The CLI is exposed via ``pybmodes`` (see ``pybmodes --help``) and is
|
|
163
|
+
declared in ``[project.scripts]``.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
167
|
+
|
|
168
|
+
# Numerical-options dataclasses (Phase 1 of the v1.x architecture
|
|
169
|
+
# refactor) — centralised thresholds for the FEM solver, polynomial
|
|
170
|
+
# fit, and pre-solve sanity checker. Defaults preserve every
|
|
171
|
+
# previously-embedded magic number, so importing this module changes
|
|
172
|
+
# no behaviour; the public-API value is that callers can now find
|
|
173
|
+
# every numerical threshold in one place. Future PRs will accept
|
|
174
|
+
# instances on ``Tower.run()`` / ``RotatingBlade.run()`` /
|
|
175
|
+
# ``check_model()`` for per-call override.
|
|
176
|
+
from pybmodes.options import CheckOptions, FitOptions, SolverOptions
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
__version__ = version("pybmodes")
|
|
180
|
+
except PackageNotFoundError:
|
|
181
|
+
__version__ = "1.8.0-dev"
|
|
182
|
+
|
|
183
|
+
__all__ = [
|
|
184
|
+
"__version__",
|
|
185
|
+
"CheckOptions",
|
|
186
|
+
"FitOptions",
|
|
187
|
+
"SolverOptions",
|
|
188
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright 2024-2026 Jae Hoon Seo
|
|
2
|
+
# Marine Structural Mechanics and Integrity Lab (SMI Lab), Inha University
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Bundled example inputs and reference decks.
|
|
17
|
+
|
|
18
|
+
This sub-package vendors two trees that ship inside the wheel so a
|
|
19
|
+
``pip install pybmodes`` user can copy them out to a working directory
|
|
20
|
+
without keeping a full git clone of the repository.
|
|
21
|
+
|
|
22
|
+
- ``sample_inputs/`` — pyBmodes-authored, Apache 2.0-licensed ``.bmi`` and
|
|
23
|
+
section-property ``.dat`` files. Four analytical-reference cases
|
|
24
|
+
(uniform blade, tower with top mass, rotating uniform blade,
|
|
25
|
+
pinned-free cable) plus seven reference-wind-turbine sub-cases under
|
|
26
|
+
``reference_turbines/``. ``verify.py`` runs the four analytical
|
|
27
|
+
cases against closed-form references.
|
|
28
|
+
- ``reference_decks/`` — six pre-patched ElastoDyn decks (three
|
|
29
|
+
land/monopile + three floating) whose polynomial blocks have been
|
|
30
|
+
regenerated from the structural inputs via ``Tower.from_elastodyn``.
|
|
31
|
+
|
|
32
|
+
The trees are intentionally treated as opaque data; the ``.py``
|
|
33
|
+
helpers inside them (``verify.py``, ``reference_turbines/build.py``)
|
|
34
|
+
are intended to be run *after* vendoring out via
|
|
35
|
+
``pybmodes examples --copy <dir>``, not imported.
|
|
36
|
+
|
|
37
|
+
Users discover the bundles through three paths:
|
|
38
|
+
|
|
39
|
+
- ``pybmodes examples --copy DIR`` — CLI that copies one or both
|
|
40
|
+
trees into a user-supplied directory.
|
|
41
|
+
- Browsing the wheel install directly under
|
|
42
|
+
``site-packages/pybmodes/_examples/``.
|
|
43
|
+
- For developers working from a source checkout, the GitHub source
|
|
44
|
+
tree under the same path.
|
|
45
|
+
"""
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD013 -->
|
|
2
|
+
# Floating cases — the correct ElastoDyn polynomial basis is *cantilever*
|
|
3
|
+
|
|
4
|
+
For floating platforms, ElastoDyn polynomial coefficients
|
|
5
|
+
(`TwFAM1Sh`, `TwFAM2Sh`, `TwSSM1Sh`, `TwSSM2Sh`) must be derived
|
|
6
|
+
from a **cantilever** (`hub_conn = 1`) tower model with the RNA
|
|
7
|
+
lumped at the tower top — **NOT** from a platform-coupled floating
|
|
8
|
+
model.
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
ElastoDyn represents floating tower dynamics as a clamped-base
|
|
13
|
+
cantilever **in the platform-attached frame**, with platform 6-DOF
|
|
14
|
+
motion handled separately as independent generalised coordinates
|
|
15
|
+
(`Sg / Sw / Hv / R / P / Y`). Three independent code-level evidences
|
|
16
|
+
in OpenFAST `modules/elastodyn/src/ElastoDyn.f90` (main branch):
|
|
17
|
+
|
|
18
|
+
1. The polynomial ansatz in `SHP` evaluates
|
|
19
|
+
`Σ_{i=1..PolyOrd-1} c_i · (h/H)^(i+1)` (lines 2486–2495). The
|
|
20
|
+
lowest power is `Fract²`, so `SHP(0) = SHP'(0) = 0`
|
|
21
|
+
*identically*. A free-free or pinned-pinned mode shape with
|
|
22
|
+
non-zero base slope cannot be represented in this format.
|
|
23
|
+
2. The base node is hard-coded zero: `p%TwrFASF(:,0,0:1) = 0`,
|
|
24
|
+
`p%TwrSSSF(:,0,0:1) = 0` (lines 5147–5148).
|
|
25
|
+
3. The internal tower modal eigenproblem (`Coeff` subroutine,
|
|
26
|
+
lines 5141–5267) integrates `MTFA = TwrTpMass + ∫ ρA φ² dh`
|
|
27
|
+
and `KTFA = ∫ EI φ'' φ'' dh + KTFAGrav`. **No** `PlatformMass`,
|
|
28
|
+
**no** `hydro_K`, **no** `mooring_K`, **no** `i_matrix` enter
|
|
29
|
+
this assembly. The only tip-end inertia is the scalar
|
|
30
|
+
`TwrTpMass` (lumped RNA mass).
|
|
31
|
+
|
|
32
|
+
Platform 6-DOF motion enters the absolute tower kinematics via the
|
|
33
|
+
**rigid-body sum** (lines 7485–7540):
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
v_T(J) = v_Z + ω_X × rZT(J) + Σ_k φ_k(h_J) · q̇_k
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`Sg/Sw/Hv/R/P/Y` and the tower modal coordinates `q_TFA1 / q_TFA2 /
|
|
40
|
+
q_TSS1 / q_TSS2` are **independent** generalised coordinates;
|
|
41
|
+
platform motion does NOT appear as forcing on `q_TFA1`. Feeding
|
|
42
|
+
ElastoDyn polynomials that already encode platform-coupling
|
|
43
|
+
**double-counts** the platform restoring forces because ElastoDyn
|
|
44
|
+
re-derives those effects independently through the platform DOFs.
|
|
45
|
+
|
|
46
|
+
Same BC for land and floating — only the runtime treatment of the
|
|
47
|
+
clamp point differs (locked in Earth for land; rigidly attached to
|
|
48
|
+
the moving platform for floating).
|
|
49
|
+
|
|
50
|
+
## How
|
|
51
|
+
|
|
52
|
+
Floating-platform polynomial coefficients are generated with the
|
|
53
|
+
**existing** pyBmodes path — `Tower.from_elastodyn(...)` is
|
|
54
|
+
*already* the cantilever path. It clamps at `TowerBsHt` with the RNA
|
|
55
|
+
lumped at the top, ignores any platform / hydro / mooring matrices,
|
|
56
|
+
and produces exactly the basis ElastoDyn assumes. No flag is needed.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from pybmodes.models import Tower
|
|
60
|
+
from pybmodes.elastodyn import compute_tower_params, patch_dat
|
|
61
|
+
from pybmodes.io.elastodyn_reader import read_elastodyn_main
|
|
62
|
+
|
|
63
|
+
main_path = "Floating_ElastoDyn.dat"
|
|
64
|
+
tower = Tower.from_elastodyn(main_path) # cantilever, RNA at top, no platform
|
|
65
|
+
result = tower.run(n_modes=10)
|
|
66
|
+
params = compute_tower_params(result)
|
|
67
|
+
|
|
68
|
+
# patch_dat rewrites the *tower* .dat file (where the polynomial
|
|
69
|
+
# blocks live), not the main ElastoDyn .dat.
|
|
70
|
+
main = read_elastodyn_main(main_path)
|
|
71
|
+
patch_dat(main_path.replace("ElastoDyn.dat", main.twr_file), params)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
No WAMIT files, no HydroDyn parsing, and no MoorDyn parsing are
|
|
75
|
+
required. The cantilever path is correct and self-contained — the
|
|
76
|
+
ElastoDyn `.dat` (plus the tower file it references) carries every
|
|
77
|
+
input needed.
|
|
78
|
+
|
|
79
|
+
## What about `Tower.from_bmi()` with `hub_conn = 2`?
|
|
80
|
+
|
|
81
|
+
`Tower.from_bmi("OC3Hywind.bmi")` and similar BModes-format decks
|
|
82
|
+
with a populated `PlatformSupport` block solve the **coupled**
|
|
83
|
+
tower-and-platform eigenproblem (free-free root, full 6×6 hydro /
|
|
84
|
+
mooring / inertia matrices). That path:
|
|
85
|
+
|
|
86
|
+
- **Correctly predicts coupled-system frequencies** for validation
|
|
87
|
+
against BModes JJ. pyBmodes matches BModes JJ to ~ 0.0003 % across
|
|
88
|
+
the first nine OC3 Hywind modes (`test_certtest_oc3hywind`). If
|
|
89
|
+
the goal is "what does the floating tower vibrate at when coupled
|
|
90
|
+
to its platform?", this is the right path.
|
|
91
|
+
- **Produces eigenvectors that include platform rigid-body motion**
|
|
92
|
+
— i.e. the modes have non-zero base displacement and non-zero
|
|
93
|
+
base slope, which the ElastoDyn `SHP` ansatz cannot represent.
|
|
94
|
+
Feeding these eigenvectors into a polynomial fit produces
|
|
95
|
+
coefficients ElastoDyn cannot consume without double-counting the
|
|
96
|
+
platform.
|
|
97
|
+
|
|
98
|
+
The two paths answer different questions; both are correct for
|
|
99
|
+
their intended use:
|
|
100
|
+
|
|
101
|
+
| Goal | Use | BC |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| ElastoDyn polynomial coefficients (any floating deck) | `Tower.from_elastodyn(...)` | `hub_conn = 1`, RNA at top |
|
|
104
|
+
| Coupled-system frequency validation against BModes JJ | `Tower.from_bmi("OC3Hywind.bmi")` | `hub_conn = 2`, full PlatformSupport |
|
|
105
|
+
|
|
106
|
+
## Configurations included in `reference_decks/`
|
|
107
|
+
|
|
108
|
+
This directory now ships pre-patched ElastoDyn decks for three
|
|
109
|
+
floating configurations alongside the original three fixed-base
|
|
110
|
+
decks:
|
|
111
|
+
|
|
112
|
+
- [`nrel5mw_oc3spar/`](nrel5mw_oc3spar/) — *NREL 5MW* on the OC3
|
|
113
|
+
Hywind spar (Jonkman 2010). Source: OpenFAST `r-test`
|
|
114
|
+
`5MW_OC3Spar_DLL_WTurb_WavesIrr/`.
|
|
115
|
+
- [`nrel5mw_oc4semi/`](nrel5mw_oc4semi/) — *NREL 5MW* on the OC4
|
|
116
|
+
DeepCwind semi-submersible (Robertson et al. 2014). Source:
|
|
117
|
+
OpenFAST `r-test` `5MW_OC4Semi_WSt_WavesWN/`.
|
|
118
|
+
- [`iea15mw_umainesemi/`](iea15mw_umainesemi/) — *IEA-15-240-RWT*
|
|
119
|
+
on the UMaine VolturnUS-S semi (Allen et al. 2020). Source:
|
|
120
|
+
upstream `IEA-15-240-RWT/OpenFAST/IEA-15-240-RWT-UMaineSemi/`.
|
|
121
|
+
|
|
122
|
+
Each deck is built by `scripts/build_reference_decks.py` using the
|
|
123
|
+
cantilever path documented above; the validator passes on all four
|
|
124
|
+
tower coefficient blocks after patching.
|
|
125
|
+
|
|
126
|
+
## Citations
|
|
127
|
+
|
|
128
|
+
- Jonkman, J., Butterfield, S., Musial, W., & Scott, G. (2009).
|
|
129
|
+
*Definition of a 5-MW Reference Wind Turbine for Offshore System
|
|
130
|
+
Development*. NREL/TP-500-38060.
|
|
131
|
+
- Jonkman, J. (2010). *Definition of the Floating System for Phase
|
|
132
|
+
IV of OC3*. NREL/TP-500-47535.
|
|
133
|
+
- Robertson, A., Jonkman, J., Masciola, M., Song, H., Goupee, A.,
|
|
134
|
+
Coulling, A., & Luan, C. (2014). *Definition of the
|
|
135
|
+
Semisubmersible Floating System for Phase II of OC4*.
|
|
136
|
+
NREL/TP-5000-60601.
|
|
137
|
+
- Allen, C., Viselli, A., Dagher, H., Goupee, A., Gaertner, E.,
|
|
138
|
+
Abbas, N., Hall, M., & Barter, G. (2020). *Definition of the
|
|
139
|
+
UMaine VolturnUS-S Reference Platform Developed for the
|
|
140
|
+
IEA Wind 15-Megawatt Offshore Reference Wind Turbine*.
|
|
141
|
+
NREL/TP-5000-76773.
|
|
142
|
+
- Gaertner, E., Rinker, J., Sethuraman, L., Zahle, F., Anderson, B.,
|
|
143
|
+
Barter, G., et al. (2020). *Definition of the IEA 15-Megawatt
|
|
144
|
+
Offshore Reference Wind Turbine*. NREL/TP-5000-75698.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD013 -->
|
|
2
|
+
# Reference decks with corrected mode-shape polynomial coefficients
|
|
3
|
+
|
|
4
|
+
## What is this?
|
|
5
|
+
|
|
6
|
+
Copies of OpenFAST ElastoDyn input files with mode-shape polynomial
|
|
7
|
+
coefficients (`TwFAM1Sh`, `TwFAM2Sh`, `TwSSM1Sh`, `TwSSM2Sh`,
|
|
8
|
+
`BldFl1Sh`, `BldFl2Sh`, `BldEdgSh`) regenerated by pyBmodes from the
|
|
9
|
+
structural-property blocks in the same files.
|
|
10
|
+
|
|
11
|
+
Six turbine configurations are included — three fixed-base and three
|
|
12
|
+
floating:
|
|
13
|
+
|
|
14
|
+
- [`nrel5mw_land/`](nrel5mw_land/) — *NREL 5MW Reference Turbine*
|
|
15
|
+
(Jonkman et al. 2009), land-based.
|
|
16
|
+
- [`nrel5mw_oc3monopile/`](nrel5mw_oc3monopile/) — *NREL 5MW* on the
|
|
17
|
+
OC3 Monopile (Jonkman & Musial 2010); rigid base, no soil flexibility,
|
|
18
|
+
SubDyn substructure included for provenance.
|
|
19
|
+
- [`iea34_land/`](iea34_land/) — *IEA-3.4-130-RWT*
|
|
20
|
+
(Bortolotti et al. 2019, IEA Wind Task 37), land-based.
|
|
21
|
+
- [`nrel5mw_oc3spar/`](nrel5mw_oc3spar/) — *NREL 5MW* on the OC3
|
|
22
|
+
Hywind floating spar (Jonkman 2010).
|
|
23
|
+
- [`nrel5mw_oc4semi/`](nrel5mw_oc4semi/) — *NREL 5MW* on the OC4
|
|
24
|
+
DeepCwind semi-submersible (Robertson et al. 2014).
|
|
25
|
+
- [`iea15mw_umainesemi/`](iea15mw_umainesemi/) — *IEA-15-240-RWT* on
|
|
26
|
+
the UMaine VolturnUS-S semi (Allen et al. 2020); the patched
|
|
27
|
+
TwSSM2Sh ends at WARN — the constrained 6th-order polynomial form
|
|
28
|
+
cannot represent this deck's 2nd-SS bending shape below the 1 %
|
|
29
|
+
PASS gate, a property of the IEA-15 tower's section-property
|
|
30
|
+
gradient, not a pyBmodes bug.
|
|
31
|
+
|
|
32
|
+
The floating decks use the same cantilever (`hub_conn = 1`) modal
|
|
33
|
+
basis as the fixed-base decks — see
|
|
34
|
+
[`FLOATING_CASES.md`](FLOATING_CASES.md) for why this is the correct
|
|
35
|
+
basis for ElastoDyn polynomial coefficients on any floating deck.
|
|
36
|
+
|
|
37
|
+
## Why?
|
|
38
|
+
|
|
39
|
+
The official upstream `.dat` files for these reference turbines contain
|
|
40
|
+
polynomial coefficients that **do not represent the mode shapes
|
|
41
|
+
produced by the same files' structural inputs**. The 2nd-mode tower
|
|
42
|
+
coefficients (`TwFAM2Sh`, `TwSSM2Sh`) are the worst offenders, with
|
|
43
|
+
RMS residuals 170×–2,500× higher than pyBmodes' own fits. See
|
|
44
|
+
[`cases/ECOSYSTEM_FINDING.md`](https://github.com/SMI-Lab-Inha/pyBModes/blob/master/cases/ECOSYSTEM_FINDING.md)
|
|
45
|
+
for the quantified evidence and the [`VALIDATION_SUMMARY.md`](VALIDATION_SUMMARY.md)
|
|
46
|
+
in this directory for per-block before-and-after numbers across all
|
|
47
|
+
three cases.
|
|
48
|
+
|
|
49
|
+
The cause is upstream pipeline drift: the polynomial blocks were
|
|
50
|
+
generated against an earlier revision of each turbine's structural
|
|
51
|
+
model and have not been regenerated as the structural blocks were
|
|
52
|
+
iteratively refined. OpenFAST accepts any coefficient set whose
|
|
53
|
+
polynomial evaluates to 1 at the tip — there is no warning when the
|
|
54
|
+
polynomials describe a different mode shape than the structural model
|
|
55
|
+
in the same deck. This directory provides drop-in replacements where
|
|
56
|
+
the polynomial block and the structural block describe the same modes.
|
|
57
|
+
|
|
58
|
+
## Provenance
|
|
59
|
+
|
|
60
|
+
| Source | Repository | Commit | Date |
|
|
61
|
+
| --- | --- | --- | --- |
|
|
62
|
+
| NREL 5MW (land + OC3 monopile) | OpenFAST `r-test` | `dd5feaaa` | 2026-03-12 |
|
|
63
|
+
| IEA-3.4-130-RWT | IEA Wind Task 37 RWT repo | `824f8d6` | 2025-09-26 |
|
|
64
|
+
| Regenerated with | pyBmodes | `0753ec2` | (current) |
|
|
65
|
+
|
|
66
|
+
**Method**: pyBmodes solves the modal eigenvalue problem on the deck's
|
|
67
|
+
structural inputs (15-DOF Bernoulli-Euler beam element, vectorised
|
|
68
|
+
einsum core), then fits constrained 6th-order polynomials with the
|
|
69
|
+
clamped-base + unit-tip constraints
|
|
70
|
+
($\phi(0)=\phi'(0)=0$, $\phi(1)=1$). For tower modes, the rigid-body
|
|
71
|
+
root component is removed before fitting (the **Improved Direct
|
|
72
|
+
Method** described in BModes' Excel mode-shape worksheet,
|
|
73
|
+
`external/BModes/docs/ModeShapePolyFitting.xls`). After regeneration
|
|
74
|
+
every block is pyBmodes' best constrained fit and no block FAILs;
|
|
75
|
+
all blocks reach PASS except one known WARN
|
|
76
|
+
(`iea15mw_umainesemi/TwSSM2Sh` at 1.6 % RMS, an unavoidable
|
|
77
|
+
representation limit of the constrained 6th-order polynomial form
|
|
78
|
+
for that tower's section-property gradient — see
|
|
79
|
+
[`FLOATING_CASES.md`](FLOATING_CASES.md) for the per-deck
|
|
80
|
+
explanation). See [`VALIDATION_SUMMARY.md`](VALIDATION_SUMMARY.md)
|
|
81
|
+
for the per-block before/after numbers.
|
|
82
|
+
|
|
83
|
+
## How to use
|
|
84
|
+
|
|
85
|
+
Drop the patched `.dat` files into your OpenFAST case directory,
|
|
86
|
+
replacing the originals. Each case directory is self-contained — the
|
|
87
|
+
ElastoDyn main file references only its sibling `_Tower.dat` and
|
|
88
|
+
`_Blade.dat`, no external `..` traversal — so you can copy the whole
|
|
89
|
+
directory or pick individual files.
|
|
90
|
+
|
|
91
|
+
The structural-property blocks (mass density, EI, drag coefficients,
|
|
92
|
+
geometric data) are **identical to the upstream files**; only the
|
|
93
|
+
polynomial-coefficient lines are rewritten.
|
|
94
|
+
|
|
95
|
+
## How to regenerate
|
|
96
|
+
|
|
97
|
+
The reference decks here are produced by
|
|
98
|
+
[`scripts/build_reference_decks.py`](https://github.com/SMI-Lab-Inha/pyBModes/blob/master/scripts/build_reference_decks.py),
|
|
99
|
+
which copies the upstream sources, runs `pybmodes patch`, and re-runs
|
|
100
|
+
the validator. To regenerate from scratch:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
python scripts/build_reference_decks.py
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
To patch your own ElastoDyn deck:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install pybmodes
|
|
110
|
+
pybmodes validate path/to/your_ElastoDyn.dat # check consistency
|
|
111
|
+
pybmodes patch path/to/your_ElastoDyn.dat --backup # regenerate
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Or programmatically:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from pybmodes.elastodyn import compute_tower_params, compute_blade_params, patch_dat
|
|
118
|
+
from pybmodes.models import Tower, RotatingBlade
|
|
119
|
+
|
|
120
|
+
tower = Tower.from_elastodyn("path/to/MyTurbine_ElastoDyn.dat")
|
|
121
|
+
patch_dat("path/to/MyTurbine_Tower.dat", compute_tower_params(tower.run(n_modes=10)))
|
|
122
|
+
|
|
123
|
+
blade = RotatingBlade.from_elastodyn("path/to/MyTurbine_ElastoDyn.dat")
|
|
124
|
+
patch_dat("path/to/MyTurbine_Blade.dat", compute_blade_params(blade.run(n_modes=10)))
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Floating configurations
|
|
128
|
+
|
|
129
|
+
This directory ships three floating cases (OC3 Hywind spar, OC4
|
|
130
|
+
DeepCwind semi, UMaine VolturnUS-S semi) generated via the same
|
|
131
|
+
`Tower.from_elastodyn(...)` path as the fixed-base cases. ElastoDyn
|
|
132
|
+
represents floating tower dynamics as a clamped-base cantilever in
|
|
133
|
+
the platform-attached frame, with platform 6-DOF motion handled
|
|
134
|
+
separately as independent generalised coordinates — so the correct
|
|
135
|
+
modal basis for ElastoDyn polynomials is exactly the cantilever path
|
|
136
|
+
this script already uses, regardless of land or floating. See
|
|
137
|
+
[`FLOATING_CASES.md`](FLOATING_CASES.md) for the ElastoDyn-source-code
|
|
138
|
+
justification and the contrast with `Tower.from_bmi()` (which solves
|
|
139
|
+
the coupled tower + platform eigenproblem and is correct for
|
|
140
|
+
frequency validation against BModes JJ but **not** for ElastoDyn
|
|
141
|
+
polynomial generation — its eigenvectors include platform rigid-body
|
|
142
|
+
motion that the ElastoDyn `SHP` ansatz cannot represent).
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
The original upstream `.dat` files retain their respective licenses
|
|
147
|
+
(see the `r-test` and IEA Wind Task 37 repositories). pyBmodes' patched
|
|
148
|
+
versions are released under the same Apache 2.0 licence as pyBmodes itself —
|
|
149
|
+
the structural content is unchanged from the upstream files; only the
|
|
150
|
+
polynomial-coefficient lines were rewritten.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD013 -->
|
|
2
|
+
# Reference-deck coefficient validation summary
|
|
3
|
+
|
|
4
|
+
Per-block RMS residual of the polynomial coefficients shipped in each upstream deck (Before) and after pyBmodes regenerated them from the structural inputs in the same deck (After). The ratio column is the upstream `file_rms / pybmodes_rms` — values >> 1 indicate the upstream polynomial does not represent the mode shape produced by the deck's structural inputs.
|
|
5
|
+
|
|
6
|
+
| Case | Block | Before RMS | After RMS | Ratio before | Status |
|
|
7
|
+
| --- | --- | ---: | ---: | ---: | :---: |
|
|
8
|
+
| nrel5mw_land | TwFAM1Sh | 0.0081 | 0.0000 | 313× | PASS |
|
|
9
|
+
| nrel5mw_land | TwFAM2Sh | 5.0783 | 0.0024 | 2101× | PASS |
|
|
10
|
+
| nrel5mw_land | TwSSM1Sh | 0.0075 | 0.0000 | 293× | PASS |
|
|
11
|
+
| nrel5mw_land | TwSSM2Sh | 5.9009 | 0.0023 | 2529× | PASS |
|
|
12
|
+
| nrel5mw_land | BldFl1Sh | 0.0022 | 0.0008 | 2.82× | PASS |
|
|
13
|
+
| nrel5mw_land | BldFl2Sh | 0.0088 | 0.0035 | 2.48× | PASS |
|
|
14
|
+
| nrel5mw_land | BldEdgSh | 0.0006 | 0.0002 | 3.17× | PASS |
|
|
15
|
+
| nrel5mw_oc3monopile | TwFAM1Sh | 0.0037 | 0.0000 | 140× | PASS |
|
|
16
|
+
| nrel5mw_oc3monopile | TwFAM2Sh | 5.7805 | 0.0032 | 1813× | PASS |
|
|
17
|
+
| nrel5mw_oc3monopile | TwSSM1Sh | 0.0045 | 0.0000 | 173× | PASS |
|
|
18
|
+
| nrel5mw_oc3monopile | TwSSM2Sh | 7.3266 | 0.0033 | 2220× | PASS |
|
|
19
|
+
| nrel5mw_oc3monopile | BldFl1Sh | 0.0022 | 0.0008 | 2.82× | PASS |
|
|
20
|
+
| nrel5mw_oc3monopile | BldFl2Sh | 0.0088 | 0.0035 | 2.48× | PASS |
|
|
21
|
+
| nrel5mw_oc3monopile | BldEdgSh | 0.0006 | 0.0002 | 3.17× | PASS |
|
|
22
|
+
| iea34_land | TwFAM1Sh | 0.0098 | 0.0002 | 56.9× | PASS |
|
|
23
|
+
| iea34_land | TwFAM2Sh | 0.7230 | 0.0042 | 172× | PASS |
|
|
24
|
+
| iea34_land | TwSSM1Sh | 0.0110 | 0.0002 | 64.0× | PASS |
|
|
25
|
+
| iea34_land | TwSSM2Sh | 1.5494 | 0.0041 | 380× | PASS |
|
|
26
|
+
| iea34_land | BldFl1Sh | 0.0106 | 0.0014 | 7.61× | PASS |
|
|
27
|
+
| iea34_land | BldFl2Sh | 0.0051 | 0.0047 | 1.09× | PASS |
|
|
28
|
+
| iea34_land | BldEdgSh | 0.0065 | 0.0014 | 4.62× | PASS |
|
|
29
|
+
| nrel5mw_oc3spar | TwFAM1Sh | 0.0111 | 0.0000 | 350× | PASS |
|
|
30
|
+
| nrel5mw_oc3spar | TwFAM2Sh | 10.6264 | 0.0024 | 4476× | PASS |
|
|
31
|
+
| nrel5mw_oc3spar | TwSSM1Sh | 0.0165 | 0.0000 | 524× | PASS |
|
|
32
|
+
| nrel5mw_oc3spar | TwSSM2Sh | 14.1050 | 0.0027 | 5311× | PASS |
|
|
33
|
+
| nrel5mw_oc3spar | BldFl1Sh | 0.0022 | 0.0008 | 2.82× | PASS |
|
|
34
|
+
| nrel5mw_oc3spar | BldFl2Sh | 0.0088 | 0.0035 | 2.48× | PASS |
|
|
35
|
+
| nrel5mw_oc3spar | BldEdgSh | 0.0006 | 0.0002 | 3.17× | PASS |
|
|
36
|
+
| nrel5mw_oc4semi | TwFAM1Sh | 0.0034 | 0.0000 | 106× | PASS |
|
|
37
|
+
| nrel5mw_oc4semi | TwFAM2Sh | 8.2805 | 0.0024 | 3488× | PASS |
|
|
38
|
+
| nrel5mw_oc4semi | TwSSM1Sh | 0.0048 | 0.0000 | 152× | PASS |
|
|
39
|
+
| nrel5mw_oc4semi | TwSSM2Sh | 9.0396 | 0.0027 | 3404× | PASS |
|
|
40
|
+
| nrel5mw_oc4semi | BldFl1Sh | 0.0033 | 0.0008 | 3.94× | PASS |
|
|
41
|
+
| nrel5mw_oc4semi | BldFl2Sh | 0.0093 | 0.0036 | 2.59× | PASS |
|
|
42
|
+
| nrel5mw_oc4semi | BldEdgSh | 0.0004 | 0.0002 | 2.29× | PASS |
|
|
43
|
+
| iea15mw_umainesemi | TwFAM1Sh | 0.0078 | 0.0001 | 95.8× | PASS |
|
|
44
|
+
| iea15mw_umainesemi | TwFAM2Sh | 0.7922 | 0.0013 | 619× | PASS |
|
|
45
|
+
| iea15mw_umainesemi | TwSSM1Sh | 0.0088 | 0.0001 | 118× | PASS |
|
|
46
|
+
| iea15mw_umainesemi | TwSSM2Sh | 102.4075 | 0.0163 | 6276× | WARN |
|
|
47
|
+
| iea15mw_umainesemi | BldFl1Sh | 0.0048 | 0.0002 | 24.7× | PASS |
|
|
48
|
+
| iea15mw_umainesemi | BldFl2Sh | 0.0021 | 0.0018 | 1.21× | PASS |
|
|
49
|
+
| iea15mw_umainesemi | BldEdgSh | 0.0028 | 0.0008 | 3.73× | PASS |
|
|
50
|
+
|
|
51
|
+
## Pattern
|
|
52
|
+
|
|
53
|
+
- **2nd-mode tower coefficients** (`TwFAM2Sh`, `TwSSM2Sh`) show the largest inconsistency on every upstream deck: ratios from ~170× (IEA-3.4) to ~2,500× (NREL 5MW). The shipped polynomials do not represent the 2nd bending mode of the structural inputs by any reasonable metric.
|
|
54
|
+
- **1st-mode tower coefficients** (`TwFAM1Sh`, `TwSSM1Sh`) and blade coefficients (`BldFl1Sh`, `BldFl2Sh`, `BldEdgSh`) show a smaller but non-zero inconsistency (typical ratio ~ 2–300×). Their absolute file RMS values still classify as PASS under the 1 % per-block gate, but they are still drift artefacts from the same generation pipeline.
|
|
55
|
+
- **After patching every block is pyBmodes' best constrained fit and no block FAILs; most blocks reach PASS, one known WARN (`iea15mw_umainesemi / TwSSM2Sh` at 1.6 % RMS) reflects an ElastoDyn basis representation limit for that specific tower's section-property gradient, not a pyBmodes bug.** The After-RMS column matches the pyBmodes-RMS column from the Before report; the polynomials in the patched files are exactly pyBmodes' fits, so the file polynomial reproduces the pyBmodes mode shape modulo the writer's text-precision (~7 sig figs).
|
|
56
|
+
|
|
57
|
+
## How to reproduce
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python scripts/build_reference_decks.py
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The script copies the upstream sources, runs `pybmodes patch`, and re-runs the validator. See `before_patch.txt` and `validation_report.txt` in each case directory for the raw CLI output.
|