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

WISDEM / WEIS / 17959868633

23 Sep 2025 09:43PM UTC coverage: 58.153% (-0.5%) from 58.657%
17959868633

push

github

web-flow
Support for WindIO 2.0 and WISDEM 4.0 (#430)

* beginning migration to windiov2

* can now run through at least one example

* more examples running

* fixing more tests and examples

* testing improvements

* more test fixes

* trying with wisdem windio2

* fixing command option

* trying less restrictive model options writing

* making sure to test py313

* some progress, but have to sync on raft changes too

* fix section_offset_y and section_offset_x in visualizer

* gha update

* some more viz app progress

* Joints have names, openfast only

* Fix Ca_coarse definition

* fixed unit tests

* consistency in 15mw files, enabling unit tests

* wisdem integration and examples debugging progress

* testing improvements

* setting truth values

* trap all zeros in mode shapes

* Make merit figure checks lower case

* remove conflicting schema for merit figure

* trying again with kestrel-generated truth archive

* fix rectangular by pulling from windio branch directly

* redo tagging

* typo in url

* Try m2-pkg-config in windows

* changing to strip theory so hams does not choke on the size of the mesh

* Revert into to RAFT viz test

* Install bs4 in conda

* Sync tower design geometry/modeling with regular IEA-15 inputs

* Sync TMD example with vanilla Volturnus geometry/modeling

* Activate test mode for overrides example

* make sure ontology is updated in both weis and wisdem before being written out

* Fix tiny typo

* Reduce SeaState nodes to reduce memory usage

---------

Co-authored-by: Garrett Barter <garrett.barter@nrel.gov>
Co-authored-by: ptrbortolotti <ptrbortolotti@gmail.com>

681 of 775 new or added lines in 18 files covered. (87.87%)

126 existing lines in 8 files now uncovered.

7949 of 13669 relevant lines covered (58.15%)

0.58 hits per line

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

47.62
/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 ['RAFT','OpenFAST_Linear','OpenFAST']])
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['OpenFAST']['flag']:
1✔
16
            self.floating_solve_component = 'aeroelastic'
1✔
17
        elif modeling_options['RAFT']['flag']:
1✔
18
            self.floating_solve_component = 'raft'
1✔
19
        else:
UNCOV
20
            self.floating_solve_component = 'floatingse'
×
21

22
        # aeroelastic won't compute floating period, execpt in special sims
23
        if modeling_options['RAFT']['flag']:
1✔
24
            self.floating_period_solve_component = 'raft'
1✔
25
        else:
26
            self.floating_period_solve_component = 'floatingse'
1✔
27
        
28
        if modeling_options['OpenFAST']['flag']:
1✔
29
            self.n_OF_runs = modeling_options['DLC_driver']['n_cases']
1✔
30
        elif modeling_options['OpenFAST_Linear']['flag']:
1✔
31
            self.n_OF_runs = modeling_options['OpenFAST_Linear']['linearization']['NLinTimes']
×
32
        else:
33
            self.n_OF_runs = 0
1✔
34
    
35
    def set_merit_figure(self, wt_opt, merit_figure):
1✔
36
            
37
        if merit_figure.lower() == 'blade_tip_deflection':
1✔
38
            wt_opt.model.add_objective('tcons_post.tip_deflection_ratio')
×
39
            
40
        elif merit_figure.lower() == 'del_rootmyb':   # for DAC optimization on root-flap-bending moments
1✔
41
            wt_opt.model.add_objective('aeroelastic.DEL_RootMyb', ref = 1.e3)
×
42
            
43
        elif merit_figure.lower() == 'del_twrbsmyt':   # for pitch controller optimization
1✔
44
            wt_opt.model.add_objective('aeroelastic.DEL_TwrBsMyt', ref=1.e4)
1✔
45
            
46
        elif merit_figure.lower() == 'rotor_overspeed':
1✔
47
            if not any(self.level_flags):
×
48
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize rotor overspeed constraints.')
×
49
            wt_opt.model.add_objective(f'{self.floating_solve_component}.rotor_overspeed')
×
50

51
        elif merit_figure.lower() == 'std_ptfmpitch':
1✔
UNCOV
52
            wt_opt.model.add_objective('aeroelastic.Std_PtfmPitch')
×
53

54
        elif merit_figure.lower() == 'max_ptfmpitch':
1✔
UNCOV
55
            wt_opt.model.add_objective('aeroelastic.Max_PtfmPitch')
×
56

57
        elif merit_figure.lower() == 'cp':
1✔
58
            wt_opt.model.add_objective('aeroelastic.Cp_out', ref=-1.)
×
59

60
        elif merit_figure.lower() == 'weis_lcoe' or merit_figure.lower() == 'lcoe':
1✔
UNCOV
61
            wt_opt.model.add_objective('financese_post.lcoe')
×
62

63
        elif merit_figure.lower() == 'ol2cl_pitch':
1✔
UNCOV
64
            wt_opt.model.add_objective('aeroelastic.OL2CL_pitch')
×
65
        
66
        else:
67
            super(PoseOptimizationWEIS, self).set_merit_figure(wt_opt, merit_figure)
1✔
68
                
69
        return wt_opt
1✔
70

71
    
72
    def set_design_variables(self, wt_opt, wt_init):
1✔
73
        super(PoseOptimizationWEIS, self).set_design_variables(wt_opt, wt_init)
1✔
74

75
        # -- Control --
76
        control_opt = self.opt['design_variables']['control']
1✔
77
        if control_opt['servo']['pitch_control']['omega']['flag']:
1✔
78
            wt_opt.model.add_design_var('tune_rosco_ivc.omega_pc', lower=control_opt['servo']['pitch_control']['omega']['min'], 
1✔
79
                                                            upper=control_opt['servo']['pitch_control']['omega']['max'])
80
        if control_opt['servo']['pitch_control']['zeta']['flag']:                            
1✔
81
            wt_opt.model.add_design_var('tune_rosco_ivc.zeta_pc', lower=control_opt['servo']['pitch_control']['zeta']['min'], 
1✔
82
                                                           upper=control_opt['servo']['pitch_control']['zeta']['max'])
83
        if control_opt['servo']['torque_control']['omega']['flag']:
1✔
84
            wt_opt.model.add_design_var('tune_rosco_ivc.omega_vs', lower=control_opt['servo']['torque_control']['omega']['min'], 
×
85
                                                            upper=control_opt['servo']['torque_control']['omega']['max'])
86
        if control_opt['servo']['torque_control']['zeta']['flag']:                                                    
1✔
87
            wt_opt.model.add_design_var('tune_rosco_ivc.zeta_vs', lower=control_opt['servo']['torque_control']['zeta']['min'], 
×
88
                                                           upper=control_opt['servo']['torque_control']['zeta']['max'])
89
        if control_opt['servo']['ipc_control']['Kp']['flag']:
1✔
90
            wt_opt.model.add_design_var('tune_rosco_ivc.IPC_Kp1p', lower=control_opt['servo']['ipc_control']['Kp']['min'],
×
91
                                                            upper=control_opt['servo']['ipc_control']['Kp']['max'],
92
                                                            ref=control_opt['servo']['ipc_control']['Kp']['ref'])
93
        if control_opt['servo']['ipc_control']['Ki']['flag']:
1✔
94
            wt_opt.model.add_design_var('tune_rosco_ivc.IPC_Ki1p', lower=control_opt['servo']['ipc_control']['Ki']['min'],
×
95
                                                            upper=control_opt['servo']['ipc_control']['Ki']['max'],
96
                                                            ref=control_opt['servo']['ipc_control']['Kp']['ref'])
97
        if control_opt['servo']['pitch_control']['stability_margin']['flag']:
1✔
98
            wt_opt.model.add_design_var('tune_rosco_ivc.stability_margin', lower=control_opt['servo']['pitch_control']['stability_margin']['min'],
×
99
                                                            upper=control_opt['servo']['pitch_control']['stability_margin']['max'])
100
        if control_opt['flaps']['te_flap_end']['flag']:
1✔
101
            wt_opt.model.add_design_var('dac_ivc.te_flap_end', lower=control_opt['flaps']['te_flap_end']['min'],
×
102
                                                            upper=control_opt['flaps']['te_flap_end']['max'])
103
        if control_opt['flaps']['te_flap_ext']['flag']:
1✔
104
            wt_opt.model.add_design_var('dac_ivc.te_flap_ext', lower=control_opt['flaps']['te_flap_ext']['min'],
×
105
                                                            upper=control_opt['flaps']['te_flap_ext']['max'])
106
        if 'flap_control' in control_opt['servo']:
1✔
107
            if control_opt['servo']['flap_control']['flp_kp_norm']['flag']:
1✔
108
                wt_opt.model.add_design_var('tune_rosco_ivc.flp_kp_norm', 
×
109
                                    lower=control_opt['servo']['flap_control']['flp_kp_norm']['min'], 
110
                                    upper=control_opt['servo']['flap_control']['flp_kp_norm']['max'])
111
            if control_opt['servo']['flap_control']['flp_tau']['flag']:
1✔
112
                wt_opt.model.add_design_var('tune_rosco_ivc.flp_tau', 
×
113
                                    lower=control_opt['servo']['flap_control']['flp_tau']['min'], 
114
                                    upper=control_opt['servo']['flap_control']['flp_tau']['max'])
115

116
        if control_opt['ps_percent']['flag']:
1✔
117
            wt_opt.model.add_design_var('tune_rosco_ivc.ps_percent', lower=control_opt['ps_percent']['lower_bound'],
×
118
                                                            upper=control_opt['ps_percent']['upper_bound'])
119

120
        if control_opt['servo']['pitch_control']['Kp_float']['flag']:
1✔
121
            wt_opt.model.add_design_var('tune_rosco_ivc.Kp_float', lower=control_opt['servo']['pitch_control']['Kp_float']['min'], 
1✔
122
                                                           upper=control_opt['servo']['pitch_control']['Kp_float']['max'])
123

124
        if control_opt['servo']['pitch_control']['ptfm_freq']['flag']:
1✔
125
            wt_opt.model.add_design_var('tune_rosco_ivc.ptfm_freq', lower=control_opt['servo']['pitch_control']['ptfm_freq']['min'], 
1✔
126
                                                           upper=control_opt['servo']['pitch_control']['ptfm_freq']['max'])
127

128
        if self.opt['design_variables']['TMDs']['flag']:
1✔
129
            TMD_opt = self.opt['design_variables']['TMDs']
×
130

131
            # We only support one TMD for now
132
            for i_group, tmd_group in enumerate(TMD_opt['groups']):
×
133
                if 'mass' in tmd_group:
×
134
                    wt_opt.model.add_design_var(
×
135
                        f'TMDs.TMD_IVCs.group_{i_group}_mass', 
136
                        lower=tmd_group['mass']['lower_bound'],
137
                        upper=tmd_group['mass']['upper_bound'],
138
                        )
139
                if 'stiffness' in tmd_group:
×
140
                    wt_opt.model.add_design_var(
×
141
                        f'TMDs.TMD_IVCs.group_{i_group}_stiffness', 
142
                        lower=tmd_group['stiffness']['lower_bound'],
143
                        upper=tmd_group['stiffness']['upper_bound']
144
                        )
145
                    if 'natural_frequency' in tmd_group:
×
146
                        raise Exception("natural_frequency and stiffness can not be design variables in the same group")
×
147
                if 'damping' in tmd_group:
×
148
                    wt_opt.model.add_design_var(
×
149
                        f'TMDs.TMD_IVCs.group_{i_group}_damping', 
150
                        lower=tmd_group['damping']['lower_bound'],
151
                        upper=tmd_group['damping']['upper_bound']
152
                        )
153
                    if 'damping_ratio' in tmd_group:
×
154
                        raise Exception("damping_ratio and damping can not be design variables in the same group")
×
155
                if 'natural_frequency' in tmd_group:
×
156
                    wt_opt.model.add_design_var(
×
157
                        f'TMDs.TMD_IVCs.group_{i_group}_natural_frequency', 
158
                        lower=tmd_group['natural_frequency']['lower_bound'],
159
                        upper=tmd_group['natural_frequency']['upper_bound']
160
                        )
161
                if 'damping_ratio' in tmd_group:
×
162
                    wt_opt.model.add_design_var(
×
163
                        f'TMDs.TMD_IVCs.group_{i_group}_damping_ratio', 
164
                        lower=tmd_group['damping_ratio']['lower_bound'],
165
                        upper=tmd_group['damping_ratio']['upper_bound']
166
                        )
167
        
168
        return wt_opt
1✔
169

170
    
171
    def set_constraints(self, wt_opt):
1✔
172
        super(PoseOptimizationWEIS, self).set_constraints(wt_opt)
1✔
173

174
        blade_opt = self.opt["design_variables"]["blade"]
1✔
175
        blade_constr = self.opt["constraints"]["blade"]
1✔
176
        if blade_constr['tip_deflection']['flag']:
1✔
177
            # Remove generic WISDEM one
178
            name = 'tcons.tip_deflection_ratio'
×
179
            if name in wt_opt.model._responses:
×
180
                wt_opt.model._responses.pop( name )
×
181
            if name in wt_opt.model._static_responses:
×
182
                wt_opt.model._static_responses.pop( name )
×
183
                
184
            if len(blade_opt["structure"]) > 0:
×
185
                wt_opt.model.add_constraint('tcons_post.tip_deflection_ratio', upper=1.0)
×
186
            else:
187
                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.')
×
188

189
        if blade_constr["strains_spar_cap_ss"]["flag"]:
1✔
190
            # Remove generic WISDEM one
191
            name = 'rotorse.rs.constr.constr_max_strainU_spar'
×
192
            if name in wt_opt.model._responses:
×
193
                wt_opt.model._responses.pop( name )
×
194
            if name in wt_opt.model._static_responses:
×
195
                wt_opt.model._static_responses.pop( name )
×
196
            indices_strains_spar_cap_ss = range(
×
197
                blade_constr["strains_spar_cap_ss"]["index_start"], 
198
                blade_constr["strains_spar_cap_ss"]["index_end"]
199
            )
200
            wt_opt.model.add_constraint("rlds_post.constr.constr_max_strainU_spar", 
×
201
                                        indices = indices_strains_spar_cap_ss, 
202
                                        upper=1.0
203
            )
204

205
        if blade_constr["strains_spar_cap_ps"]["flag"]:
1✔
206
            # Remove generic WISDEM one
207
            name = 'rotorse.rs.constr.constr_max_strainL_spar'
×
208
            if name in wt_opt.model._responses:
×
209
                wt_opt.model._responses.pop( name )
×
210
            if name in wt_opt.model._static_responses:
×
211
                wt_opt.model._static_responses.pop( name )
×
212
            indices_strains_spar_cap_ps = range(
×
213
                blade_constr["strains_spar_cap_ps"]["index_start"], 
214
                blade_constr["strains_spar_cap_ps"]["index_end"]
215
            )
216
            wt_opt.model.add_constraint("rlds_post.constr.constr_max_strainL_spar", 
×
217
                                        indices = indices_strains_spar_cap_ps,
218
                                        upper=1.0
219
            )
220

221
        ### CONTROL CONSTRAINTS
222
        control_constraints = self.opt['constraints']['control']
1✔
223
        
224
        # Flap control
225
        if control_constraints['flap_control']['flag']:
1✔
226
            if self.modeling['OpenFAST']['flag'] != True:
×
227
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize trailing edge flaps.')
×
228
            wt_opt.model.add_constraint('sse_tune.tune_rosco.flptune_coeff1',
×
229
                lower = control_constraints['flap_control']['min'],
230
                upper = control_constraints['flap_control']['max'])
231
            wt_opt.model.add_constraint('sse_tune.tune_rosco.flptune_coeff2', 
×
232
                lower = control_constraints['flap_control']['min'],
233
                upper = control_constraints['flap_control']['max'])    
234
        
235
        # Rotor overspeed
236
        if control_constraints['rotor_overspeed']['flag']:
1✔
237
            if not any(self.level_flags):
1✔
238
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize rotor overspeed constraints.')
×
239
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.rotor_overspeed',
1✔
240
                lower = control_constraints['rotor_overspeed']['min'],
241
                upper = control_constraints['rotor_overspeed']['max'])
242
        
243
        # Add PI gains if overspeed is merit_figure or constraint
244
        if control_constraints['rotor_overspeed']['flag'] or 'rotor_overspeed' in self.opt['merit_figure']:
1✔
245
            wt_opt.model.add_constraint('sse_tune.tune_rosco.PC_Kp',
1✔
246
                upper = 0.0)
247
            wt_opt.model.add_constraint('sse_tune.tune_rosco.PC_Ki', 
1✔
248
                upper = 0.0)  
249
        
250
        # Nacelle Accelleration magnitude
251
        if control_constraints['nacelle_acceleration']['flag']:
1✔
252
            if not any(self.level_flags):
1✔
253
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize with nacelle_acceleration constraint.')
×
254
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.max_nac_accel',
1✔
255
                    upper = control_constraints['nacelle_acceleration']['max'])
256
        
257
        # Max platform pitch
258
        if control_constraints['Max_PtfmPitch']['flag']:
1✔
259
            if not any(self.level_flags):
1✔
260
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize Max_PtfmPitch constraints.')
×
261
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.Max_PtfmPitch',
1✔
262
                upper = control_constraints['Max_PtfmPitch']['max'])
263
        
264
        # Platform pitch motion
265
        if control_constraints['Std_PtfmPitch']['flag']:
1✔
266
            if not any(self.level_flags):
1✔
267
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize Std_PtfmPitch constraints.')
×
268
            wt_opt.model.add_constraint(f'{self.floating_solve_component}.Std_PtfmPitch',
1✔
269
                upper = control_constraints['Std_PtfmPitch']['max'])
270
        if control_constraints['Max_TwrBsMyt']['flag']:
1✔
271
            if self.modeling['OpenFAST']['flag'] != True:
×
272
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize Max_TwrBsMyt constraints.')
×
273
            wt_opt.model.add_constraint('aeroelastic.max_TwrBsMyt_ratio', 
×
274
                upper = 1.0)
275
        if control_constraints['DEL_TwrBsMyt']['flag']:
1✔
276
            if self.modeling['OpenFAST']['flag'] != True:
×
277
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize Max_TwrBsMyt constraints.')
×
278
            wt_opt.model.add_constraint('aeroelastic.DEL_TwrBsMyt_ratio', 
×
279
                upper = 1.0)
280
            
281
        # Blade pitch travel
282
        if control_constraints['avg_pitch_travel']['flag']:
1✔
283
            if self.modeling['OpenFAST']['flag'] != True:
×
284
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize avg_pitch_travel constraints.')
×
285
            wt_opt.model.add_constraint('aeroelastic.avg_pitch_travel',
×
286
                upper = control_constraints['avg_pitch_travel']['max'])
287

288
        # Blade pitch duty cycle (number of direction changes)
289
        if control_constraints['pitch_duty_cycle']['flag']:
1✔
290
            if self.modeling['OpenFAST']['flag'] != True:
×
291
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize pitch_duty_cycle constraints.')
×
292
            wt_opt.model.add_constraint('aeroelastic.pitch_duty_cycle',
×
293
                upper = control_constraints['pitch_duty_cycle']['max'])
294

295
        # OpenFAST failure
296
        if self.opt['constraints']['openfast_failed']['flag']:
1✔
297
            if self.modeling['OpenFAST']['flag'] != True:
×
298
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize with openfast_failed constraint.')
×
299
            wt_opt.model.add_constraint('aeroelastic.openfast_failed',upper = 1.)
×
300

301
        # Max offset
302
        if self.opt['constraints']['floating']['Max_Offset']['flag']:
1✔
303
            if not any(self.level_flags):
×
304
                raise Exception('Please turn on the call to OpenFAST or RAFT if you are trying to optimize with openfast_failed constraint.')
×
305
            wt_opt.model.add_constraint(
×
306
                f'{self.floating_solve_component}.Max_Offset',
307
                upper = self.opt['constraints']['floating']['Max_Offset']['max']
308
                )
309
                
310
        # Tower constraints
311
        tower_opt = self.opt["design_variables"]["tower"]
1✔
312
        tower_constr = self.opt["constraints"]["tower"]
1✔
313
        if tower_constr["global_buckling"]["flag"] and self.modeling['OpenFAST']['flag']:
1✔
314
            # Remove generic WISDEM one
315
            name = 'towerse.post.constr_global_buckling'
×
316
            if name in wt_opt.model._responses:
×
317
                wt_opt.model._responses.pop( name )
×
318
            if name in wt_opt.model._static_responses:
×
319
                wt_opt.model._static_responses.pop( name )
×
320
                
321
            wt_opt.model.add_constraint("towerse_post.constr_global_buckling", upper=1.0)
×
322
        
323
        if tower_constr["shell_buckling"]["flag"] and self.modeling['OpenFAST']['flag']:
1✔
324
            # Remove generic WISDEM one
325
            name = 'towerse.post.constr_shell_buckling'
×
326
            if name in wt_opt.model._responses:
×
327
                wt_opt.model._responses.pop( name )
×
328
            if name in wt_opt.model._static_responses:
×
329
                wt_opt.model._static_responses.pop( name )
×
330
                
331
            wt_opt.model.add_constraint("towerse_post.constr_shell_buckling", upper=1.0)
×
332
        
333
        if tower_constr["stress"]["flag"] and self.modeling['OpenFAST']['flag']:
1✔
334
            # Remove generic WISDEM one
335
            name = 'towerse.post.constr_stress'
×
336
            if name in wt_opt.model._responses:
×
337
                wt_opt.model._responses.pop( name )
×
338
            if name in wt_opt.model._static_responses:
×
339
                wt_opt.model._static_responses.pop( name )
×
340
                
341
            wt_opt.model.add_constraint("towerse_post.constr_stress", upper=1.0)
×
342

343
        # Damage constraints
344
        damage_constraints = self.opt['constraints']['damage']
1✔
345
        if damage_constraints['tower_base']['flag'] and (self.modeling['OpenFAST_Linear']['flag'] or self.modeling['OpenFAST']['flag']):
1✔
346
            if self.modeling['OpenFAST']['flag'] != True:
×
347
                raise Exception('Please turn on the call to OpenFAST if you are trying to optimize with tower_base damage constraint.')
×
348

349
            tower_base_damage_max = damage_constraints['tower_base']['max']
×
350
            if damage_constraints['tower_base']['log']:
×
351
                tower_base_damage_max = np.log(tower_base_damage_max)
×
352

353
            wt_opt.model.add_constraint('aeroelastic.damage_tower_base',upper = tower_base_damage_max)
×
354

355
        return wt_opt
1✔
356

357

358
    def set_initial_weis(self, wt_opt):
1✔
359

360
        if self.modeling["flags"]["blade"]:
1✔
361
            blade_constr = self.opt["constraints"]["blade"]
1✔
362
            wt_opt["rlds_post.constr.max_strainU_spar"] = blade_constr["strains_spar_cap_ss"]["max"]
1✔
363
            wt_opt["rlds_post.constr.max_strainL_spar"] = blade_constr["strains_spar_cap_ps"]["max"]
1✔
364
            wt_opt["stall_check_of.stall_margin"] = blade_constr["stall"]["margin"] * 180.0 / np.pi
1✔
365
            wt_opt["tcons_post.max_allowable_td_ratio"] = blade_constr["tip_deflection"]["margin"]
1✔
366

367
        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