Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6081d17
Fully disable 2nd tower mode shapes if DOF is disabled
dzalkind Mar 27, 2026
84ea9a2
Fully disable 2nd tower mode shapes if DOF is disabled (#466)
dzalkind Apr 6, 2026
6beab9c
Add moorpy version 1.2.1 to environment.yml
dzalkind Apr 6, 2026
98c6f9c
move some pypi packages to the conda forge
Apr 8, 2026
c38266d
fix readme (#467)
ptrbortolotti Apr 10, 2026
7272a93
Add Mean_PtfmPitch as an optimization constraint
dzalkind Apr 16, 2026
4dbbbf1
Merge remote-tracking branch 'origin/develop' into ptfm_opt_updates
dzalkind Apr 16, 2026
b6cd281
trying updated wisdem to reduce package dependencies
Apr 21, 2026
0ecded5
Merge branch 'develop' into conda-pip
Apr 21, 2026
0811d30
unlock openmdao
Apr 21, 2026
87032d7
Add some helper functions for platform design
dzalkind Apr 21, 2026
e75972c
Cd into notebooks for testing
dzalkind Apr 21, 2026
9c2f9d4
Tidy platform optimization in RAFT
dzalkind Apr 21, 2026
30b6f39
Merge remote-tracking branch 'origin/develop' into ptfm_opt_updates
dzalkind Apr 21, 2026
0f16a91
Remove plot_tradeoff
dzalkind Apr 21, 2026
f9262cb
convert radians to degrees for heel constraints
Apr 21, 2026
309f68d
seeing if openmpi speeds up installation
Apr 21, 2026
459db93
maybe without pyoptsparse for speedup?
Apr 21, 2026
85150cc
one more combo
Apr 21, 2026
9867d3e
native mpi
Apr 21, 2026
fc29223
found solution, restoring full test matrix
Apr 21, 2026
99ffe29
caught more rad to deg conversions
Apr 22, 2026
3bd4454
Merge pull request #468 from NLRWindSystems/conda-pip
gbarter Apr 22, 2026
489c374
Merge remote-tracking branch 'origin/develop' into ptfm_opt_updates
dzalkind Apr 22, 2026
eda876f
Merge pull request #469 from dzalkind/ptfm_opt_updates
gbarter Apr 22, 2026
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
18 changes: 9 additions & 9 deletions .github/workflows/CI_WEIS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
fail-fast: false
matrix:
os: ["ubuntu-latest", "macOS-14", "windows-latest"] # mac13 is intel, mac-14 arm
python-version: ["3.11","3.12","3.13"]
python-version: ["3.12","3.13","3.14"]

steps:
- uses: actions/checkout@v5
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
# - name: Add dependencies windows specific
# if: contains( matrix.os, 'windows')
# run: |
# conda install -y gfortran libpython
# conda install -y gfortran gcc
# pip install --no-deps control


Expand All @@ -62,12 +62,12 @@ jobs:

# Don't delete after this


# Note- could add mpich through conda, but takes forever to resolve the environment in testing
- name: Add dependencies ubuntu specific
if: contains( matrix.os, 'ubuntu')
run: |
sudo apt install -y libglu1-mesa
conda install -c conda-forge -y petsc4py mpi4py pyoptsparse mpich
sudo apt install -y libglu1-mesa mpich
conda install -y petsc4py mpi4py pyoptsparse
pip install pygmsh==7.1.17
pip install https://github.com/LHEEA/meshmagick/archive/master.zip
which -a mpiexec
Expand Down Expand Up @@ -156,10 +156,10 @@ jobs:
- name: Test model creation notebooks and drivers
if: contains( matrix.os, 'ubuntu') && contains( github.event_name, 'pull_request')
run: |
cd examples/11_model_creation_process
treon 0_notebooks/chapter1.ipynb
treon 0_notebooks/chapter2.ipynb
treon 0_notebooks/chapter3.ipynb
cd examples/11_model_creation_process/0_notebooks
treon chapter1.ipynb
treon chapter2.ipynb
treon chapter3.ipynb

# Run parallel script calling OpenFAST
- name: Run parallel cases
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ The installation instructions below use the environment name, "weis-env," but an

3. If you are on Kestrel, first load some modules and then install:

module load intel-oneapi-compilers intel-oneapi-mpi intel-oneapi-mkl conda
module load intel-oneapi-compilers intel-oneapi-mpi intel-oneapi-mkl
pip install --no-deps -e . -v

**NOTE:** To use WEIS again after installation is complete, you will always need to activate the conda environment first with `conda activate weis-env` (or `source activate weis-env`). On Kestrel, make sure to reload the necessary modules
Expand Down
17 changes: 8 additions & 9 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,35 @@ channels:
dependencies:
- bs4
- dash
- dash-bootstrap-components
- dash-mantine-components
- dill
- jsonmerge
- mat4py
- moorpy==1.2.1
- nlopt
- numpydoc
- numpy
- openfast==4.2
- openfast-io==4.2
- pyopenfast==4.2
- openraft>=2.0.3
- openmdao==3.42
- osqp
- openmdao
- osqp
- pcrunch>=2.1.5
- pip
- pyhams>=1.3
- pyvista
#- pyoptsparse
- pyvista
- rosco>=2.10.1
- trimesh
- wisdem>=4.1
- wisdem>=4.2
- pip:
- control
- dash-bootstrap-components
- dash-mantine-components
- dash-vtk
- pyvista
- dearpygui
- orbit-nrel
- smt
- windIO>=2.0.1
- wombat>=0.13.1
# - git+https://github.com/NLRWindSystems/RAFT.git@dev
# Needs to be done outside of environment file:
# - m2w64-toolchain # [win]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ constraints:
lower_bound: 0.37
floating:
survival_heel:
upper_bound: 0.17453292519943295 # 10 deg
upper_bound: 10.0 # deg
metacentric_height:
flag: False
lower_bound: 15.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ design_variables:
lower_bound: -30.0
upper_bound: -12.0
flag: True
# - names: [col1_freeboard, col2_freeboard, col3_freeboard]
# lower_bound: 10.0
# upper_bound: 20.0
r_coordinate:
- names: [col1_keel, col1_freeboard, col2_keel, col2_freeboard, col3_keel, col3_freeboard]
lower_bound: 50.0
Expand All @@ -35,18 +32,7 @@ design_variables:
lower_bound: 12.0
upper_bound: 16.0
constant: True
# ballast:
# lower_bound: 0
# upper_bound: 1000
# - names: [main_column]
# ballast:
# lower_bound: 0
# upper_bound: 1000
# - names: [Y_pontoon_lower1, Y_pontoon_lower2, Y_pontoon_lower3]
# diameter:
# lower_bound: 10.0
# upper_bound: 10.8
# constant: True

constraints:
tower:
height_constraint:
Expand All @@ -73,7 +59,7 @@ constraints:
lower_bound: 0.37
floating:
survival_heel:
upper_bound: 0.17453292519943295 # 10 deg
upper_bound: 10.0 # deg
metacentric_height:
flag: True
lower_bound: 15.0
Expand All @@ -91,14 +77,6 @@ constraints:
flag: True
buoyancy:
flag: False
stress:
flag: False
global_buckling:
flag: False
shell_buckling:
flag: False
mooring_heel:
flag: False
freeboard_margin: # keep freeboard from being submerged below water during survival_heel, largest wave
flag: True
draft_margin: # keep draft from raising above water line during survival_heel, largest wave
Expand All @@ -109,9 +87,6 @@ constraints:
Max_PtfmPitch:
flag: True
max: 6.0
Std_PtfmPitch:
flag: False
max: 1.25 # Same as IEA-15MW with same DLCs
nacelle_acceleration:
flag: True
max: 2.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@
"constraints:\n",
" floating:\n",
" survival_heel:\n",
" upper_bound: 0.17453292519943295 # 10 deg\n",
" upper_bound: 10.0 # deg\n",
" metacentric_height:\n",
" flag: True\n",
" lower_bound: 1.0 # 15.0 --> Dan's experience\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ constraints:
lower_bound: 0.37
floating:
survival_heel:
upper_bound: 0.17453292519943295 # 10 deg
upper_bound: 10.0 # deg
metacentric_height:
flag: True
lower_bound: 15.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ constraints:
lower_bound: 0.37
floating:
survival_heel:
upper_bound: 0.17453292519943295 # 10 deg
upper_bound: 10.0 # deg
metacentric_height:
flag: True
lower_bound: 15.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ constraints:
lower_bound: 0.37
floating:
survival_heel:
upper_bound: 0.17453292519943295 # 10 deg
upper_bound: 10.0 # deg
metacentric_height:
flag: True
lower_bound: 15.0
Expand Down
14 changes: 13 additions & 1 deletion weis/aeroelasticse/openmdao_openfast.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ def setup(self):

# Floating outputs
self.add_output('Max_PtfmPitch', val=0.0, desc='Maximum platform pitch angle over a set of OpenFAST simulations')
self.add_output('Mean_PtfmPitch', val=0.0, units='deg', desc='Maximum (across cases) mean (of each case) platform pitch angle over a set of OpenFAST simulations')
self.add_output('Std_PtfmPitch', val=0.0, units='deg', desc='standard deviation of platform pitch angle')
self.add_output('Max_Offset', val=0.0, units='m', desc='Maximum distance in surge/sway direction')

Expand Down Expand Up @@ -1200,9 +1201,18 @@ def update_FAST_model(self, fst_vt, inputs, discrete_inputs):
if not np.any(inputs[f'{fass}_modes'][idir,:]):
logger.warning(f'WARNING: {fass} tower shape coefficients are zero which will cause errors in using ElastoDyn')
fst_vt['ElastoDynTower']['TwFAM1Sh'] = inputs['fore_aft_modes'][0, :] / np.sum(inputs['fore_aft_modes'][0, :])
fst_vt['ElastoDynTower']['TwFAM2Sh'] = inputs['fore_aft_modes'][1, :] / np.sum(inputs['fore_aft_modes'][1, :])
fst_vt['ElastoDynTower']['TwSSM1Sh'] = inputs['side_side_modes'][0, :] / np.sum(inputs['side_side_modes'][0, :])

# Since the 2nd tower modes are sometimes problematic, if the DOF is not enabled, let's give it a safe, dummy value that won't cause errors in ElastoDyn
fst_vt['ElastoDynTower']['TwFAM2Sh'] = inputs['fore_aft_modes'][1, :] / np.sum(inputs['fore_aft_modes'][1, :])
if not fst_vt['ElastoDyn']['TwFADOF2']:
fst_vt['ElastoDynTower']['TwFAM2Sh'] = np.zeros_like(inputs['fore_aft_modes'][1, :])
fst_vt['ElastoDynTower']['TwFAM2Sh'][0] = 1.0

fst_vt['ElastoDynTower']['TwSSM2Sh'] = inputs['side_side_modes'][1, :] / np.sum(inputs['side_side_modes'][1, :])
if not fst_vt['ElastoDyn']['TwSSDOF2']:
fst_vt['ElastoDynTower']['TwSSM2Sh'] = np.zeros_like(inputs['side_side_modes'][1, :])
fst_vt['ElastoDynTower']['TwSSM2Sh'][0] = 1.0

# Calculate yaw stiffness of tower (springs in series) and use in servodyn as yaw spring constant
k_tow_tor = inputs['tor_stff'] / np.diff(inputs['tower_z'])
Expand Down Expand Up @@ -3450,6 +3460,7 @@ def get_floating_measures(self, inputs, outputs):
calculate floating measures:
- Std_PtfmPitch (max over all dlcs if constraint, mean otheriwse)
- Max_PtfmPitch
- Mean_PtfmPitch

given:
- sum_stats : pd.DataFrame
Expand All @@ -3463,6 +3474,7 @@ def get_floating_measures(self, inputs, outputs):
outputs['Std_PtfmPitch'] = np.mean(sum_stats['PtfmPitch']['std'])

outputs['Max_PtfmPitch'] = np.max(sum_stats['PtfmPitch']['max'])
outputs['Mean_PtfmPitch'] = np.max(sum_stats['PtfmPitch']['mean'])

# Max platform offset
outputs['Max_Offset'] = np.max(sum_stats['PtfmOffset']['max'])
Expand Down
9 changes: 8 additions & 1 deletion weis/glue_code/gc_PoseOptimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,14 @@ def set_constraints(self, wt_opt):
raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize Max_PtfmPitch constraints.')
wt_opt.model.add_constraint(f'{self.floating_solve_component}.Max_PtfmPitch',
upper = control_constraints['Max_PtfmPitch']['max'])


# Mean platform pitch
if control_constraints['Mean_PtfmPitch']['flag']:
if not any(self.level_flags):
raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize Mean_PtfmPitch constraints.')
wt_opt.model.add_constraint(f'{self.floating_solve_component}.Mean_PtfmPitch',
upper = control_constraints['Mean_PtfmPitch']['max'])

# Platform pitch motion
if control_constraints['Std_PtfmPitch']['flag']:
if not any(self.level_flags):
Expand Down
14 changes: 13 additions & 1 deletion weis/inputs/analysis_schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,21 @@ properties:
minimum: 0.0
maximum: 30.0
unit: deg
Mean_PtfmPitch:
type: object
description: The maximum mean platform pitch displacement over all cases. Can be computed in both RAFT and OpenFAST. The higher fidelity option will be used when active.
default: {}
properties:
flag: *flag
max:
type: number
default: 3.0
minimum: 0.0
maximum: 30.0
unit: deg
Std_PtfmPitch:
type: object
description: Maximum platform pitch standard deviation over all cases. Can be computed in both RAFT and OpenFAST. The higher fidelity option will be used when active.
description: Maximum platform pitch standard deviation over all cases. Can be computed in both RAFT and OpenFAST. The higher fidelity option will be used when active.
default: {}
properties:
flag: *flag
Expand Down
38 changes: 37 additions & 1 deletion weis/inputs/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,40 @@ def write_analysis_yaml(instance, foutput):
sfx_str = "-analysis.yaml"
wisval.write_yaml(instance, foutput + sfx_str)
return foutput + sfx_str



def make_paths_absolute(data, base_dir=None):
"""Recursively convert relative paths in a nested dict/list to absolute paths.

Any string value that contains ``/`` or ``\\`` and is not already an
absolute path is joined with *base_dir* and resolved to an absolute path.
This is useful when loading YAML configuration files that contain
relative references to other files or directories.

Parameters
----------
data : dict, list, or str
The data structure to process (typically the result of
``yaml.safe_load``).
base_dir : str, optional
The directory that relative paths are relative to. Defaults to
the current working directory.

Returns
-------
data : dict, list, or str
A copy of the input with relative paths replaced by absolute paths.
"""
if base_dir is None:
base_dir = os.getcwd()

if isinstance(data, dict):
return {k: make_paths_absolute(v, base_dir) for k, v in data.items()}
elif isinstance(data, list):
return [make_paths_absolute(item, base_dir) for item in data]
elif isinstance(data, str):
if ("/" in data or "\\" in data) and not os.path.isabs(data):
return os.path.abspath(os.path.join(base_dir, data))
return data
else:
return data
Loading
Loading