• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

WISDEM / WEIS / 11975795765

22 Nov 2024 03:50PM UTC coverage: 78.802% (-0.9%) from 79.662%
11975795765

push

github

web-flow
WEIS v1.4 (#322)

* Viz tool integration (#301)

* utils update for viz tool

* hotfix for length error in viz utils

* slightly improved hotfix

* trim edge case

* working integration with weis

* add text field for reloading diff yaml file

* f-string typo fix to pass py3.11 unit test

* remove duplicated fields on raft opt

* elevate 'weis_viz' to a command within the conda env

* add optimization type

* reformating vizgen and adding 'weis_viz_input_gen' as a command

---------

Co-authored-by: Cory Frontin <cory.frontin@nrel.gov>
Co-authored-by: sryu <Sora.Ryu@nrel.gov>
Co-authored-by: Sora Ryu <sryu@x1007c0s0b0n0.hsn.cm.kestrel.hpc.nrel.gov>
Co-authored-by: Sora Ryu <sryu@kl3.head.cm.kestrel.hpc.nrel.gov>

* bug fix in vizFileGen and changes to handle Jul 2024 Kestrel updates (#306)

* bug fix in vizFileGen and fixes to handle Jul 2024 Kestrel updates

* change type settings and default channels

* match type with WEIS level

* minor update on type setting - dlc

---------

Co-authored-by: Sora Ryu <sryu@x1000c0s0b0n0.hsn.cm.kestrel.hpc.nrel.gov>
Co-authored-by: Sora Ryu <sryu@kl2.head.cm.kestrel.hpc.nrel.gov>

* no need to manipulate turbsim grid for olaf anymore (#313)

* Debug arg parsing error while launching the app & Contribute Initial Documentation (#307)

* fix bug of args parse while running app

* delete unnecessary prints

* change horizontal subplots to vertical ones

* initial documentation

* delete readme file

* minor changes on graph layout

* update on kestrel set up

* merge weis viz docs into existing weis docs

* delete initial weis viz documentation

* update on docs after changing opt type setting

* Revise documentation

---------

Co-authored-by: sryu <Sora.Ryu@nrel.gov>
Co-authored-by: dzalkind <dzalkind@nrel.gov>

* Remove duplicate numpydoc

* Sync readthedocs yaml with WISDEM

* Add numpydoc to environment

* Set up readthedocs inputs... (continued)

43 of 375 new or added lines in 12 files covered. (11.47%)

6 existing lines in 3 files now uncovered.

21658 of 27484 relevant lines covered (78.8%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

32.94
/weis/glue_code/gc_PoseOptimization.py
1
from wisdem.glue_code.gc_PoseOptimization import PoseOptimization
1✔
2
import numpy as np
1✔
3

4
class PoseOptimizationWEIS(PoseOptimization):
1✔
5

6
    def __init__(self, wt_init, modeling_options, analysis_options):
1✔
7
        
8
        self.level_flags = np.array([modeling_options[level]['flag'] for level in ['Level1','Level2','Level3']])
1✔
9
        # if sum(self.level_flags) > 1:
10
            # raise Exception('Only one level in WEIS can be enabled at the same time')
11

12
        super(PoseOptimizationWEIS, self).__init__(wt_init, modeling_options, analysis_options)
1✔
13

14
        # Set solve component for some optimization constraints, and merit figures (RAFT or openfast)
15
        if modeling_options['Level3']['flag']:
1✔
16
            self.floating_solve_component = 'aeroelastic'
1✔
17
        elif modeling_options['Level1']['flag']:
1✔
18
            self.floating_solve_component = 'raft'
1✔
19
        else:
20
            self.floating_solve_component = 'floatingse'
×
21

22
        # aeroelastic won't compute floating period, execpt in special sims
23
        if modeling_options['Level1']['flag']:
1✔
24
            self.floating_period_solve_component = 'raft'
1✔
25
        else:
26
            self.floating_period_solve_component = 'floatingse'
1✔
27
        
28
        
29
    def get_number_design_variables(self):
1✔
30
        # Determine the number of design variables
NEW
31
        n_DV = 0
×
32

NEW
33
        rotorD_opt = self.opt["design_variables"]["rotor_diameter"]
×
NEW
34
        blade_opt = self.opt["design_variables"]["blade"]
×
NEW
35
        tower_opt = self.opt["design_variables"]["tower"]
×
NEW
36
        mono_opt = self.opt["design_variables"]["monopile"]
×
NEW
37
        jacket_opt = self.opt["design_variables"]["jacket"]
×
NEW
38
        hub_opt = self.opt["design_variables"]["hub"]
×
NEW
39
        drive_opt = self.opt["design_variables"]["drivetrain"]
×
NEW
40
        float_opt = self.opt["design_variables"]["floating"]
×
NEW
41
        mooring_opt = self.opt["design_variables"]["mooring"]
×
42

NEW
43
        if rotorD_opt["flag"]:
×
NEW
44
            n_DV += 1
×
NEW
45
        if blade_opt["aero_shape"]["twist"]["flag"]:
×
NEW
46
            if blade_opt["aero_shape"]["twist"]["index_end"] > blade_opt["aero_shape"]["twist"]["n_opt"]:
×
NEW
47
                raise Exception(
×
48
                    "Check the analysis options yaml, index_end of the blade twist is higher than the number of DVs n_opt"
49
                )
NEW
50
            elif blade_opt["aero_shape"]["twist"]["index_end"] == 0:
×
NEW
51
                blade_opt["aero_shape"]["twist"]["index_end"] = blade_opt["aero_shape"]["twist"]["n_opt"]
×
NEW
52
            n_DV += blade_opt["aero_shape"]["twist"]["index_end"] - blade_opt["aero_shape"]["twist"]["index_start"]
×
NEW
53
        if blade_opt["aero_shape"]["chord"]["flag"]:
×
NEW
54
            if blade_opt["aero_shape"]["chord"]["index_end"] > blade_opt["aero_shape"]["chord"]["n_opt"]:
×
NEW
55
                raise Exception(
×
56
                    "Check the analysis options yaml, index_end of the blade chord is higher than the number of DVs n_opt"
57
                )
NEW
58
            elif blade_opt["aero_shape"]["chord"]["index_end"] == 0:
×
NEW
59
                blade_opt["aero_shape"]["chord"]["index_end"] = blade_opt["aero_shape"]["chord"]["n_opt"]
×
NEW
60
            n_DV += blade_opt["aero_shape"]["chord"]["index_end"] - blade_opt["aero_shape"]["chord"]["index_start"]
×
NEW
61
        if blade_opt["aero_shape"]["af_positions"]["flag"]:
×
NEW
62
            n_DV += (
×
63
                self.modeling["WISDEM"]["RotorSE"]["n_af_span"]
64
                - blade_opt["aero_shape"]["af_positions"]["af_start"]
65
                - 1
66
            )
NEW
67
        if "structure" in blade_opt:
×
NEW
68
            if len(blade_opt["structure"])>0:
×
NEW
69
                for i in range(len(blade_opt["structure"])):
×
NEW
70
                    if blade_opt["structure"][i]["index_end"] > blade_opt["structure"][i]["n_opt"]:
×
NEW
71
                        raise Exception(
×
72
                            "Check the analysis options yaml, the index_end of a blade layer is higher than the number of DVs n_opt"
73
                        )
NEW
74
                    elif blade_opt["structure"][i]["index_end"] == 0:
×
NEW
75
                        blade_opt["structure"][i]["index_end"] = blade_opt["structure"][i]["n_opt"]
×
NEW
76
                    n_DV += (
×
77
                        blade_opt["structure"][i]["index_end"]
78
                        - blade_opt["structure"][i]["index_start"]
79
                    )
NEW
80
        if self.opt["design_variables"]["control"]["tsr"]["flag"]:
×
NEW
81
            n_DV += 1
×
82

NEW
83
        if tower_opt["outer_diameter"]["flag"]:
×
NEW
84
            n_DV += self.modeling["WISDEM"]["TowerSE"]["n_height"]
×
NEW
85
        if tower_opt["layer_thickness"]["flag"]:
×
NEW
86
            n_DV += self.modeling["WISDEM"]["TowerSE"]["n_height"] * self.modeling["WISDEM"]["TowerSE"]["n_layers"]
×
NEW
87
        if mono_opt["outer_diameter"]["flag"]:
×
NEW
88
            n_DV += self.modeling["WISDEM"]["FixedBottomSE"]["n_height"]
×
NEW
89
        if mono_opt["layer_thickness"]["flag"]:
×
NEW
90
            n_DV += (
×
91
                self.modeling["WISDEM"]["FixedBottomSE"]["n_height"]
92
                * self.modeling["WISDEM"]["FixedBottomSE"]["n_layers"]
93
            )
94
        # TODO: FIX THIS
95
        # if jacket_opt["outer_diameter"]["flag"]:
96
        #    n_DV += self.modeling["WISDEM"]["FixedBottomSE"]["n_height"]
97
        # if jacket_opt["layer_thickness"]["flag"]:
98
        #    n_DV += (
99
        #        self.modeling["WISDEM"]["FixedBottomSE"]["n_height"]
100
        #        * self.modeling["WISDEM"]["FixedBottomSE"]["n_layers"]
101
        #    )
NEW
102
        if hub_opt["cone"]["flag"]:
×
NEW
103
            n_DV += 1
×
NEW
104
        if hub_opt["hub_diameter"]["flag"]:
×
NEW
105
            n_DV += 1
×
NEW
106
        for k in [
×
107
            "uptilt",
108
            "overhang",
109
            "distance_tt_hub",
110
            "distance_hub_mb",
111
            "distance_mb_mb",
112
            "generator_length",
113
            "gear_ratio",
114
            "generator_length",
115
            "bedplate_web_thickness",
116
            "bedplate_flange_thickness",
117
            "bedplate_flange_width",
118
        ]:
NEW
119
            if drive_opt[k]["flag"]:
×
NEW
120
                n_DV += 1
×
NEW
121
        for k in [
×
122
            "lss_diameter",
123
            "lss_wall_thickness",
124
            "hss_diameter",
125
            "hss_wall_thickness",
126
            "nose_diameter",
127
            "nose_wall_thickness",
128
        ]:
NEW
129
            if drive_opt[k]["flag"]:
×
NEW
130
                n_DV += 2
×
NEW
131
        if drive_opt["bedplate_wall_thickness"]["flag"]:
×
NEW
132
            n_DV += 4
×
133

NEW
134
        if float_opt["joints"]["flag"]:
×
NEW
135
            n_DV += len(float_opt["joints"]["z_coordinate"]) + len(float_opt["joints"]["r_coordinate"])
×
136

NEW
137
        if float_opt["members"]["flag"]:
×
NEW
138
            for k, kgrp in enumerate(float_opt["members"]["groups"]):
×
NEW
139
                memname = kgrp["names"][0]
×
NEW
140
                memidx = self.modeling["floating"]["members"]["name"].index(memname)
×
NEW
141
                n_grid = len(self.modeling["floating"]["members"]["grid_member_" + memname])
×
NEW
142
                n_layers = self.modeling["floating"]["members"]["n_layers"][memidx]
×
NEW
143
                if "diameter" in kgrp:
×
NEW
144
                    if "constant" in kgrp["diameter"]:
×
NEW
145
                        n_DV += 1
×
146
                    else:
NEW
147
                        n_DV += n_grid
×
NEW
148
                if "thickness" in kgrp:
×
NEW
149
                    n_DV += n_grid * n_layers
×
NEW
150
                if "ballast" in kgrp:
×
NEW
151
                    n_DV += self.modeling["floating"]["members"]["ballast_flag_member_" + memname].count(False)
×
NEW
152
                if "stiffeners" in kgrp:
×
NEW
153
                    if "ring" in kgrp["stiffeners"]:
×
NEW
154
                        if "size" in kgrp["stiffeners"]["ring"]:
×
NEW
155
                            pass
×
NEW
156
                        if "spacing" in kgrp["stiffeners"]["ring"]:
×
NEW
157
                            n_DV += 1
×
NEW
158
                    if "longitudinal" in kgrp["stiffeners"]:
×
NEW
159
                        if "size" in kgrp["stiffeners"]["longitudinal"]:
×
NEW
160
                            pass
×
NEW
161
                        if "spacing" in kgrp["stiffeners"]["longitudinal"]:
×
NEW
162
                            n_DV += 1
×
NEW
163
                if "axial_joints" in kgrp:
×
NEW
164
                    n_DV += len(kgrp["axial_joints"])
×
NEW
165
        if self.modeling["flags"]["mooring"]:
×
NEW
166
            n_design = 1 if self.modeling["mooring"]["symmetric"] else self.modeling["mooring"]["n_lines"]
×
NEW
167
            if mooring_opt["line_length"]["flag"]:
×
NEW
168
                n_DV += n_design
×
NEW
169
            if mooring_opt["line_diameter"]["flag"]:
×
NEW
170
                n_DV += n_design
×
171

172
        # Count and add design variables from WEIS
173
        if self.opt['design_variables']['control']['servo']['pitch_control']['omega']['flag']:
×
NEW
174
            if hasattr(self.modeling['ROSCO']['omega_pc'],'__len__'):
×
NEW
175
                n_add += len(self.modeling['ROSCO']['omega_pc'])
×
176
            else:
NEW
177
                n_add += 1
×
178
        if self.opt['design_variables']['control']['servo']['pitch_control']['zeta']['flag']:
×
NEW
179
            if hasattr(self.modeling['ROSCO']['zeta_pc'],'__len__'):
×
NEW
180
                n_add += len(self.modeling['ROSCO']['zeta_pc'])
×
181
            else:
NEW
182
                n_add += 1
×
183
        if self.opt['design_variables']['control']['servo']['pitch_control']['Kp_float']['flag']:
×
NEW
184
            n_DV += 1
×
185
        if self.opt['design_variables']['control']['servo']['pitch_control']['ptfm_freq']['flag']:
×
NEW
186
            n_DV += 1
×
187
        if self.opt['design_variables']['control']['servo']['torque_control']['omega']['flag']:
×
NEW
188
            n_DV += 1
×
189
        if self.opt['design_variables']['control']['servo']['torque_control']['zeta']['flag']:
×
NEW
190
            n_DV += 1
×
191
        if self.opt['design_variables']['control']['servo']['flap_control']['flp_kp_norm']['flag']:
×
NEW
192
            n_DV += 1
×
193
        if self.opt['design_variables']['control']['servo']['flap_control']['flp_tau']['flag']:
×
NEW
194
            n_DV += 1
×
195
        if self.opt['design_variables']['control']['flaps']['te_flap_end']['flag']:
×
NEW
196
            n_DV += self.modeling['WISDEM']['RotorSE']['n_te_flaps']
×
197
        if self.opt['design_variables']['control']['flaps']['te_flap_ext']['flag']:
×
NEW
198
            n_DV += self.modeling['WISDEM']['RotorSE']['n_te_flaps']
×
199
        if self.opt['design_variables']['control']['ps_percent']['flag']:
×
NEW
200
            n_DV += 1
×
201
        
202
        if self.opt['driver']['optimization']['form'] == 'central':
×
NEW
203
            n_DV *= 2
×
204

205
        # TMD DVs
206
        if self.opt['design_variables']['TMDs']['flag']:
×
207
            TMD_opt = self.opt['design_variables']['TMDs']
×
208

209
            # We only support one TMD for now
210
            for tmd_group in TMD_opt['groups']:
×
211
                if 'mass' in tmd_group:
×
NEW
212
                    n_DV += 1
×
213
                if 'stiffness' in tmd_group:
×
NEW
214
                    n_DV += 1
×
215
                if 'damping' in tmd_group:
×
NEW
216
                    n_DV += 1
×
217

NEW
218
        return n_DV
×
219

220

221
    
222
    def set_objective(self, wt_opt):
1✔
223
        # Set merit figure. Each objective has its own scaling.  Check first for user override
224
        if self.opt["merit_figure_user"]["name"] != "":
1✔
225
            coeff = -1.0 if self.opt["merit_figure_user"]["max_flag"] else 1.0
×
226
            wt_opt.model.add_objective(self.opt["merit_figure_user"]["name"],
×
227
                                       ref=coeff*np.abs(self.opt["merit_figure_user"]["ref"]))
228
            
229
        elif self.opt['merit_figure'] == 'blade_tip_deflection':
1✔
230
            wt_opt.model.add_objective('tcons_post.tip_deflection_ratio')
×
231
            
232
        elif self.opt['merit_figure'] == 'DEL_RootMyb':   # for DAC optimization on root-flap-bending moments
1✔
233
            wt_opt.model.add_objective('aeroelastic.DEL_RootMyb', ref = 1.e3)
×
234
            
235
        elif self.opt['merit_figure'] == 'DEL_TwrBsMyt':   # for pitch controller optimization
1✔
236
            wt_opt.model.add_objective('aeroelastic.DEL_TwrBsMyt', ref=1.e4)
1✔
237
            
238
        elif self.opt['merit_figure'] == 'rotor_overspeed':
1✔
239
            if not any(self.level_flags):
×
240
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize rotor overspeed constraints.')
×
241
            wt_opt.model.add_objective(f'{self.floating_solve_component}.rotor_overspeed')
×
242
        
243
        elif self.opt['merit_figure'] == 'Std_PtfmPitch':
1✔
244
            wt_opt.model.add_objective('aeroelastic.Std_PtfmPitch')
1✔
245
        
246
        elif self.opt['merit_figure'] == 'Max_PtfmPitch':
1✔
247
            wt_opt.model.add_objective('aeroelastic.Max_PtfmPitch')
×
248

249
        elif self.opt['merit_figure'] == 'Cp':
1✔
250
            wt_opt.model.add_objective('aeroelastic.Cp_out', ref=-1.)
×
251
        
252
        elif self.opt['merit_figure'] == 'weis_lcoe' or self.opt['merit_figure'].lower() == 'lcoe':
1✔
253
            wt_opt.model.add_objective('financese_post.lcoe')
×
254
        
255
        elif self.opt['merit_figure'] == 'OL2CL_pitch':
1✔
256
            wt_opt.model.add_objective('aeroelastic.OL2CL_pitch')
×
257
        
258
        else:
259
            super(PoseOptimizationWEIS, self).set_objective(wt_opt)
1✔
260
                
261
        return wt_opt
1✔
262

263
    
264
    def set_design_variables(self, wt_opt, wt_init):
1✔
265
        super(PoseOptimizationWEIS, self).set_design_variables(wt_opt, wt_init)
1✔
266

267
        # -- Control --
268
        control_opt = self.opt['design_variables']['control']
1✔
269
        if control_opt['servo']['pitch_control']['omega']['flag']:
1✔
270
            wt_opt.model.add_design_var('tune_rosco_ivc.omega_pc', lower=control_opt['servo']['pitch_control']['omega']['min'], 
1✔
271
                                                            upper=control_opt['servo']['pitch_control']['omega']['max'])
272
        if control_opt['servo']['pitch_control']['zeta']['flag']:                            
1✔
273
            wt_opt.model.add_design_var('tune_rosco_ivc.zeta_pc', lower=control_opt['servo']['pitch_control']['zeta']['min'], 
1✔
274
                                                           upper=control_opt['servo']['pitch_control']['zeta']['max'])
275
        if control_opt['servo']['torque_control']['omega']['flag']:
1✔
276
            wt_opt.model.add_design_var('tune_rosco_ivc.omega_vs', lower=control_opt['servo']['torque_control']['omega']['min'], 
×
277
                                                            upper=control_opt['servo']['torque_control']['omega']['max'])
278
        if control_opt['servo']['torque_control']['zeta']['flag']:                                                    
1✔
279
            wt_opt.model.add_design_var('tune_rosco_ivc.zeta_vs', lower=control_opt['servo']['torque_control']['zeta']['min'], 
×
280
                                                           upper=control_opt['servo']['torque_control']['zeta']['max'])
281
        if control_opt['servo']['ipc_control']['Kp']['flag']:
1✔
282
            wt_opt.model.add_design_var('tune_rosco_ivc.IPC_Kp1p', lower=control_opt['servo']['ipc_control']['Kp']['min'],
×
283
                                                            upper=control_opt['servo']['ipc_control']['Kp']['max'],
284
                                                            ref=control_opt['servo']['ipc_control']['Kp']['ref'])
285
        if control_opt['servo']['ipc_control']['Ki']['flag']:
1✔
286
            wt_opt.model.add_design_var('tune_rosco_ivc.IPC_Ki1p', lower=control_opt['servo']['ipc_control']['Ki']['min'],
×
287
                                                            upper=control_opt['servo']['ipc_control']['Ki']['max'],
288
                                                            ref=control_opt['servo']['ipc_control']['Kp']['ref'])
289
        if control_opt['servo']['pitch_control']['stability_margin']['flag']:
1✔
290
            wt_opt.model.add_design_var('tune_rosco_ivc.stability_margin', lower=control_opt['servo']['pitch_control']['stability_margin']['min'],
×
291
                                                            upper=control_opt['servo']['pitch_control']['stability_margin']['max'])
292
        if control_opt['flaps']['te_flap_end']['flag']:
1✔
293
            wt_opt.model.add_design_var('dac_ivc.te_flap_end', lower=control_opt['flaps']['te_flap_end']['min'],
×
294
                                                            upper=control_opt['flaps']['te_flap_end']['max'])
295
        if control_opt['flaps']['te_flap_ext']['flag']:
1✔
296
            wt_opt.model.add_design_var('dac_ivc.te_flap_ext', lower=control_opt['flaps']['te_flap_ext']['min'],
×
297
                                                            upper=control_opt['flaps']['te_flap_ext']['max'])
298
        if 'flap_control' in control_opt['servo']:
1✔
299
            if control_opt['servo']['flap_control']['flp_kp_norm']['flag']:
1✔
300
                wt_opt.model.add_design_var('tune_rosco_ivc.flp_kp_norm', 
×
301
                                    lower=control_opt['servo']['flap_control']['flp_kp_norm']['min'], 
302
                                    upper=control_opt['servo']['flap_control']['flp_kp_norm']['max'])
303
            if control_opt['servo']['flap_control']['flp_tau']['flag']:
1✔
304
                wt_opt.model.add_design_var('tune_rosco_ivc.flp_tau', 
×
305
                                    lower=control_opt['servo']['flap_control']['flp_tau']['min'], 
306
                                    upper=control_opt['servo']['flap_control']['flp_tau']['max'])
307

308
        if control_opt['ps_percent']['flag']:
1✔
309
            wt_opt.model.add_design_var('tune_rosco_ivc.ps_percent', lower=control_opt['ps_percent']['lower_bound'],
×
310
                                                            upper=control_opt['ps_percent']['upper_bound'])
311

312
        if control_opt['servo']['pitch_control']['Kp_float']['flag']:
1✔
313
            wt_opt.model.add_design_var('tune_rosco_ivc.Kp_float', lower=control_opt['servo']['pitch_control']['Kp_float']['min'], 
1✔
314
                                                           upper=control_opt['servo']['pitch_control']['Kp_float']['max'])
315

316
        if control_opt['servo']['pitch_control']['ptfm_freq']['flag']:
1✔
317
            wt_opt.model.add_design_var('tune_rosco_ivc.ptfm_freq', lower=control_opt['servo']['pitch_control']['ptfm_freq']['min'], 
1✔
318
                                                           upper=control_opt['servo']['pitch_control']['ptfm_freq']['max'])
319

320
        if self.opt['design_variables']['TMDs']['flag']:
1✔
321
            TMD_opt = self.opt['design_variables']['TMDs']
1✔
322

323
            # We only support one TMD for now
324
            for i_group, tmd_group in enumerate(TMD_opt['groups']):
1✔
325
                if 'mass' in tmd_group:
1✔
326
                    wt_opt.model.add_design_var(
1✔
327
                        f'TMDs.TMD_IVCs.group_{i_group}_mass', 
328
                        lower=tmd_group['mass']['lower_bound'],
329
                        upper=tmd_group['mass']['upper_bound'],
330
                        )
331
                if 'stiffness' in tmd_group:
1✔
332
                    wt_opt.model.add_design_var(
×
333
                        f'TMDs.TMD_IVCs.group_{i_group}_stiffness', 
334
                        lower=tmd_group['stiffness']['lower_bound'],
335
                        upper=tmd_group['stiffness']['upper_bound']
336
                        )
337
                    if 'natural_frequency' in tmd_group:
×
338
                        raise Exception("natural_frequency and stiffness can not be design variables in the same group")
×
339
                if 'damping' in tmd_group:
1✔
340
                    wt_opt.model.add_design_var(
×
341
                        f'TMDs.TMD_IVCs.group_{i_group}_damping', 
342
                        lower=tmd_group['damping']['lower_bound'],
343
                        upper=tmd_group['damping']['upper_bound']
344
                        )
345
                    if 'damping_ratio' in tmd_group:
×
346
                        raise Exception("damping_ratio and damping can not be design variables in the same group")
×
347
                if 'natural_frequency' in tmd_group:
1✔
348
                    wt_opt.model.add_design_var(
1✔
349
                        f'TMDs.TMD_IVCs.group_{i_group}_natural_frequency', 
350
                        lower=tmd_group['natural_frequency']['lower_bound'],
351
                        upper=tmd_group['natural_frequency']['upper_bound']
352
                        )
353
                if 'damping_ratio' in tmd_group:
1✔
354
                    wt_opt.model.add_design_var(
1✔
355
                        f'TMDs.TMD_IVCs.group_{i_group}_damping_ratio', 
356
                        lower=tmd_group['damping_ratio']['lower_bound'],
357
                        upper=tmd_group['damping_ratio']['upper_bound']
358
                        )
359
        
360
        return wt_opt
1✔
361

362
    
363
    def set_constraints(self, wt_opt):
1✔
364
        super(PoseOptimizationWEIS, self).set_constraints(wt_opt)
1✔
365

366
        blade_opt = self.opt["design_variables"]["blade"]
1✔
367
        blade_constr = self.opt["constraints"]["blade"]
1✔
368
        if blade_constr['tip_deflection']['flag']:
1✔
369
            # Remove generic WISDEM one
370
            name = 'tcons.tip_deflection_ratio'
×
371
            if name in wt_opt.model._responses:
×
372
                wt_opt.model._responses.pop( name )
×
373
            if name in wt_opt.model._static_responses:
×
374
                wt_opt.model._static_responses.pop( name )
×
375
                
376
            if blade_opt['structure']['spar_cap_ss']['flag'] or blade_opt['structure']['spar_cap_ps']['flag']:
×
377
                wt_opt.model.add_constraint('tcons_post.tip_deflection_ratio', upper=1.0)
×
378
            else:
379
                print('WARNING: the tip deflection is set to be constrained, but spar caps thickness is not an active design variable. The constraint is not enforced.')
×
380

381
        if blade_constr["strains_spar_cap_ss"]["flag"]:
1✔
382
            # Remove generic WISDEM one
383
            name = 'rotorse.rs.constr.constr_max_strainU_spar'
×
384
            if name in wt_opt.model._responses:
×
385
                wt_opt.model._responses.pop( name )
×
386
            if name in wt_opt.model._static_responses:
×
387
                wt_opt.model._static_responses.pop( name )
×
388
            if blade_opt["structure"]["spar_cap_ss"]["flag"]:
×
389
                indices_strains_spar_cap_ss = range(blade_constr["strains_spar_cap_ss"]["index_start"], blade_constr["strains_spar_cap_ss"]["index_end"])
×
390
                wt_opt.model.add_constraint("rlds_post.constr.constr_max_strainU_spar", indices = indices_strains_spar_cap_ss, upper=1.0)
×
391

392
        if blade_constr["strains_spar_cap_ps"]["flag"]:
1✔
393
            if (
×
394
                blade_opt["structure"]["spar_cap_ps"]["flag"]
395
                or blade_opt["structure"]["spar_cap_ps"]["equal_to_suction"]
396
            ):
397
                # Remove generic WISDEM one
398
                name = 'rotorse.rs.constr.constr_max_strainL_spar'
×
399
                if name in wt_opt.model._responses:
×
400
                    wt_opt.model._responses.pop( name )
×
401
                if name in wt_opt.model._static_responses:
×
402
                    wt_opt.model._static_responses.pop( name )
×
403
                indices_strains_spar_cap_ps = range(blade_constr["strains_spar_cap_ps"]["index_start"], blade_constr["strains_spar_cap_ps"]["index_end"])
×
404
                wt_opt.model.add_constraint("rlds_post.constr.constr_max_strainL_spar", indices = indices_strains_spar_cap_ps, upper=1.0)
×
405

406
        ### CONTROL CONSTRAINTS
407
        control_constraints = self.opt['constraints']['control']
1✔
408
        
409
        # Flap control
410
        if control_constraints['flap_control']['flag']:
1✔
411
            if self.modeling['Level3']['flag'] != True:
×
412
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize trailing edge flaps.')
×
413
            wt_opt.model.add_constraint('sse_tune.tune_rosco.flptune_coeff1',
×
414
                lower = control_constraints['flap_control']['min'],
415
                upper = control_constraints['flap_control']['max'])
416
            wt_opt.model.add_constraint('sse_tune.tune_rosco.flptune_coeff2', 
×
417
                lower = control_constraints['flap_control']['min'],
418
                upper = control_constraints['flap_control']['max'])    
419
        
420
        # Rotor overspeed
421
        if control_constraints['rotor_overspeed']['flag']:
1✔
422
            if not any(self.level_flags):
1✔
423
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize rotor overspeed constraints.')
×
424
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.rotor_overspeed',
1✔
425
                lower = control_constraints['rotor_overspeed']['min'],
426
                upper = control_constraints['rotor_overspeed']['max'])
427
        
428
        # Add PI gains if overspeed is merit_figure or constraint
429
        if control_constraints['rotor_overspeed']['flag'] or self.opt['merit_figure'] == 'rotor_overspeed':
1✔
430
            wt_opt.model.add_constraint('sse_tune.tune_rosco.PC_Kp',
1✔
431
                upper = 0.0)
432
            wt_opt.model.add_constraint('sse_tune.tune_rosco.PC_Ki', 
1✔
433
                upper = 0.0)  
434
        
435
        # Nacelle Accelleration magnitude
436
        if control_constraints['nacelle_acceleration']['flag']:
1✔
437
            if not any(self.level_flags):
×
438
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize with nacelle_acceleration constraint.')
×
439
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.max_nac_accel',
×
440
                    upper = control_constraints['nacelle_acceleration']['max'])
441
        
442
        # Max platform pitch
443
        if control_constraints['Max_PtfmPitch']['flag']:
1✔
444
            if not any(self.level_flags):
1✔
445
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize Max_PtfmPitch constraints.')
×
446
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.Max_PtfmPitch',
1✔
447
                upper = control_constraints['Max_PtfmPitch']['max'])
448
        
449
        # Platform pitch motion
450
        if control_constraints['Std_PtfmPitch']['flag']:
1✔
451
            if not any(self.level_flags):
×
452
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize Std_PtfmPitch constraints.')
×
453
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.Std_PtfmPitch',
×
454
                upper = control_constraints['Std_PtfmPitch']['max'])
455
        if control_constraints['Max_TwrBsMyt']['flag']:
1✔
456
            if self.modeling['Level3']['flag'] != True:
×
457
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize Max_TwrBsMyt constraints.')
×
458
            wt_opt.model.add_constraint('aeroelastic.max_TwrBsMyt_ratio', 
×
459
                upper = 1.0)
460
        if control_constraints['DEL_TwrBsMyt']['flag']:
1✔
461
            if self.modeling['Level3']['flag'] != True:
×
462
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize Max_TwrBsMyt constraints.')
×
463
            wt_opt.model.add_constraint('aeroelastic.DEL_TwrBsMyt_ratio', 
×
464
                upper = 1.0)
465
            
466
        # Blade pitch travel
467
        if control_constraints['avg_pitch_travel']['flag']:
1✔
468
            if self.modeling['Level3']['flag'] != True:
×
469
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize avg_pitch_travel constraints.')
×
470
            wt_opt.model.add_constraint('aeroelastic.avg_pitch_travel',
×
471
                upper = control_constraints['avg_pitch_travel']['max'])
472

473
        # Blade pitch duty cycle (number of direction changes)
474
        if control_constraints['pitch_duty_cycle']['flag']:
1✔
475
            if self.modeling['Level3']['flag'] != True:
×
476
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize pitch_duty_cycle constraints.')
×
477
            wt_opt.model.add_constraint('aeroelastic.pitch_duty_cycle',
×
478
                upper = control_constraints['pitch_duty_cycle']['max'])
479

480
        # OpenFAST failure
481
        if self.opt['constraints']['openfast_failed']['flag']:
1✔
482
            if self.modeling['Level3']['flag'] != True:
1✔
483
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize with openfast_failed constraint.')
×
484
            wt_opt.model.add_constraint('aeroelastic.openfast_failed',upper = 1.)
1✔
485

486
        # Max offset
487
        if self.opt['constraints']['floating']['Max_Offset']['flag']:
1✔
488
            if not any(self.level_flags):
×
489
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize with openfast_failed constraint.')
×
490
            wt_opt.model.add_constraint(
×
491
                f'{self.floating_solve_component}.Max_Offset',
492
                upper = self.opt['constraints']['floating']['Max_Offset']['max']
493
                )
494
                
495
        # Tower constraints
496
        tower_opt = self.opt["design_variables"]["tower"]
1✔
497
        tower_constr = self.opt["constraints"]["tower"]
1✔
498
        if tower_constr["global_buckling"]["flag"] and self.modeling['Level3']['flag']:
1✔
499
            # Remove generic WISDEM one
500
            name = 'towerse.post.constr_global_buckling'
×
501
            if name in wt_opt.model._responses:
×
502
                wt_opt.model._responses.pop( name )
×
503
            if name in wt_opt.model._static_responses:
×
504
                wt_opt.model._static_responses.pop( name )
×
505
                
506
            wt_opt.model.add_constraint("towerse_post.constr_global_buckling", upper=1.0)
×
507
        
508
        if tower_constr["shell_buckling"]["flag"] and self.modeling['Level3']['flag']:
1✔
509
            # Remove generic WISDEM one
510
            name = 'towerse.post.constr_shell_buckling'
×
511
            if name in wt_opt.model._responses:
×
512
                wt_opt.model._responses.pop( name )
×
513
            if name in wt_opt.model._static_responses:
×
514
                wt_opt.model._static_responses.pop( name )
×
515
                
516
            wt_opt.model.add_constraint("towerse_post.constr_shell_buckling", upper=1.0)
×
517
        
518
        if tower_constr["stress"]["flag"] and self.modeling['Level3']['flag']:
1✔
519
            # Remove generic WISDEM one
520
            name = 'towerse.post.constr_stress'
×
521
            if name in wt_opt.model._responses:
×
522
                wt_opt.model._responses.pop( name )
×
523
            if name in wt_opt.model._static_responses:
×
524
                wt_opt.model._static_responses.pop( name )
×
525
                
526
            wt_opt.model.add_constraint("towerse_post.constr_stress", upper=1.0)
×
527

528
        # Damage constraints
529
        damage_constraints = self.opt['constraints']['damage']
1✔
530
        if damage_constraints['tower_base']['flag'] and (self.modeling['Level2']['flag'] or self.modeling['Level3']['flag']):
1✔
531
            if self.modeling['Level3']['flag'] != True:
1✔
532
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize with tower_base damage constraint.')
×
533

534
            tower_base_damage_max = damage_constraints['tower_base']['max']
1✔
535
            if damage_constraints['tower_base']['log']:
1✔
536
                tower_base_damage_max = np.log(tower_base_damage_max)
1✔
537

538
            wt_opt.model.add_constraint('aeroelastic.damage_tower_base',upper = tower_base_damage_max)
1✔
539

540
        return wt_opt
1✔
541

542

543
    def set_initial_weis(self, wt_opt):
1✔
544

545
        if self.modeling["flags"]["blade"]:
1✔
546
            blade_constr = self.opt["constraints"]["blade"]
1✔
547
            wt_opt["rlds_post.constr.max_strainU_spar"] = blade_constr["strains_spar_cap_ss"]["max"]
1✔
548
            wt_opt["rlds_post.constr.max_strainL_spar"] = blade_constr["strains_spar_cap_ps"]["max"]
1✔
549
            wt_opt["stall_check_of.stall_margin"] = blade_constr["stall"]["margin"] * 180.0 / np.pi
1✔
550
            wt_opt["tcons_post.max_allowable_td_ratio"] = blade_constr["tip_deflection"]["margin"]
1✔
551

552
        return wt_opt
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc