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

WISDEM / WEIS / 19347261321

13 Nov 2025 10:05PM UTC coverage: 58.181% (-0.5%) from 58.701%
19347261321

push

github

web-flow
WEIS 2.0 (#437)

* updates on WEIS side for intersection mesh (#377)

* updates on WEIS side for intersection mesh

* Added an example for running RAFT with BEM meshing

* Add BEM mesh size parameters

* Install meshing dependencies in CI

* Make test_BEM into a unit test, update paths

* Try adding libglu install

* Remove openfast_runs from test_BEM each time

* Fix apt install in CI

* Make ubuntu specific deps only on ubuntu

* Enable intersection mesh in WEIS/RAFT and increase char lengths in test

* Put test bem outputs in own folder, only run test in ubuntu

* Try meshmagick master

---------

Co-authored-by: dzalkind <dzalkind@nrel.gov>
Co-authored-by: Pietro Bortolotti <ptrbortolotti@gmail.com>

* fix formatting readme

* NSGA2 submodule (#407)

* constrainted nsga2 working

* remove archive

* got the openmdao driver working with constraints

* numba compilation now optimal

* recompile for automatic numba or non-numba implementations

* further updates with automatic imports

* added beginnings of unit testing

* actually adding the unit tests...

* added crowding distance tests

* Allow optional input to ROSCO in WEIS

* Update ROSCO DISCONs

* Increase rosco version requirement

* Pip install pcrunch for testing

* Pin moorpy until wisdem 4.0

* Ttf adisp (#428)

* initialize TTFAdisp_initial

* fix typo

* again

* lower it to 3pc

* compute P_out_std

* add openmdao outputs

* fix output power curve

* initialize rho air earlier

* fix if n_seed=1

* compute AEP quantities if DLC 1.1 or DLC AEP, skip otherwise

* 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_of... (continued)

835 of 956 new or added lines in 20 files covered. (87.34%)

126 existing lines in 8 files now uncovered.

7958 of 13678 relevant lines covered (58.18%)

0.58 hits per line

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

65.37
/weis/glue_code/gc_LoadInputs.py
1
import os
1✔
2
import os.path as osp
1✔
3
import copy, logging
1✔
4
import numpy as np
1✔
5

6
from rosco import discon_lib_path
1✔
7
import weis.inputs as sch
1✔
8
from openfast_io.FAST_reader import InputReader_OpenFAST
1✔
9
from wisdem.glue_code.gc_LoadInputs import WindTurbineOntologyPython
1✔
10
from weis.dlc_driver.dlc_generator    import DLCGenerator
1✔
11
from openmdao.utils.mpi import MPI
1✔
12
from rosco.toolbox.inputs.validation import load_rosco_yaml
1✔
13
from wisdem.inputs import load_yaml
1✔
14
from weis.control.tune_rosco import update_rosco_options
1✔
15

16
logger = logging.getLogger("wisdem/weis")
1✔
17

18
def update_options(options,override):
1✔
19
    for key, value in override.items():
1✔
20
        if isinstance(value, dict) and key in options:
1✔
21
            update_options(options[key], value)
1✔
22
        elif key in options:
1✔
23
            options[key] = value
1✔
24
        else:
25
            raise Exception(f'Error updating option overrides. {key} is not part of {options.keys()}')
×
26

27
class WindTurbineOntologyPythonWEIS(WindTurbineOntologyPython):
1✔
28
    # Pure python class inheriting the class WindTurbineOntologyPython from WISDEM
29
    # and adding the WEIS options, namely the paths to the WEIS submodules
30
    # (OpenFAST, ROSCO, TurbSim, XFoil) and initializing the control parameters.
31

32
    def __init__(
1✔
33
            self, 
34
            fname_input_wt, 
35
            fname_input_modeling, 
36
            fname_input_analysis,
37
            modeling_override = None,
38
            analysis_override = None,
39
            ):
40

41
        self.modeling_options = sch.load_modeling_yaml(fname_input_modeling)
1✔
42
        self.modeling_options['fname_input_modeling'] = fname_input_modeling
1✔
43
        self.wt_init          = sch.load_geometry_yaml(fname_input_wt)
1✔
44
        self.analysis_options = sch.load_analysis_yaml(fname_input_analysis)
1✔
45
        self.analysis_options['fname_input_analysis'] = fname_input_analysis
1✔
46

47
        # Update options to maintain some backwards compatibility
48
        self.backwards_compatibility()
1✔
49

50
        if modeling_override:
1✔
51
            update_options(self.modeling_options, modeling_override)
1✔
52
            sch.load_modeling_yaml(self.modeling_options)
1✔
53
        
54
        if analysis_override:
1✔
55
            update_options(self.analysis_options, analysis_override)
1✔
56
            sch.load_analysis_yaml(self.analysis_options)
1✔
57

58
        self.set_run_flags()
1✔
59
        self.set_openmdao_vectors()
1✔
60
        self.set_openmdao_vectors_control()
1✔
61
        self.set_weis_data()
1✔
62
        self.set_opt_flags()
1✔
63

64
    def set_weis_data(self):
1✔
65

66
        # Directory of modeling option input, if we want to use it for relative paths
67
        mod_opt_dir = osp.dirname(self.modeling_options['fname_input_modeling'])
1✔
68
        ana_opt_dir = osp.dirname(self.analysis_options['fname_input_analysis'])
1✔
69

70
        # OpenFAST prefixes
71
        if self.modeling_options['General']['openfast_configuration']['OF_run_fst'] in ['','None','NONE','none']:
1✔
72
            self.modeling_options['General']['openfast_configuration']['OF_run_fst'] = 'weis_job'
1✔
73
            
74
        if self.modeling_options['General']['openfast_configuration']['OF_run_dir'] in ['','None','NONE','none']:
1✔
75
            self.modeling_options['General']['openfast_configuration']['OF_run_dir'] = osp.join(
1✔
76
                ana_opt_dir,        # If it's a relative path, will be relative to analysis folder_output directory
77
                self.analysis_options['general']['folder_output'], 
78
                'openfast_runs'
79
                )
80

81
        # BEM dir, all levels
82
        base_run_dir = os.path.join(mod_opt_dir,self.modeling_options['General']['openfast_configuration']['OF_run_dir'])
1✔
83
        if MPI:
1✔
84
            rank    = MPI.COMM_WORLD.Get_rank()
×
85
            bemDir = osp.join(base_run_dir,'rank_%000d'%int(rank),'BEM')
×
86
        else:
87
            bemDir = osp.join(base_run_dir,'BEM')
1✔
88

89
        self.modeling_options["RAFT"]['BEM_dir'] = bemDir
1✔
90
        if MPI:
1✔
91
            # If running MPI, RAFT won't be able to save designs in parallel
92
            self.modeling_options["RAFT"]['save_designs'] = False
×
93

94

95
        # Openfast
96
        if self.modeling_options['OpenFAST_Linear']['flag'] or self.modeling_options['OpenFAST']['flag']:
1✔
97
            fast = InputReader_OpenFAST()
1✔
98
            self.modeling_options['General']['openfast_configuration']['fst_vt'] = {}
1✔
99
            self.modeling_options['General']['openfast_configuration']['fst_vt']['outlist'] = fast.fst_vt['outlist']
1✔
100

101
                
102
            # User-defined control dylib (path2dll)
103
            path2dll = self.modeling_options['General']['openfast_configuration']['path2dll']
1✔
104
            if path2dll == 'none':   #Default option, use above
1✔
105
                self.modeling_options['General']['openfast_configuration']['path2dll'] = discon_lib_path
1✔
106
            else:
107
                if not osp.isabs(path2dll):  # make relative path absolute
×
108
                    self.modeling_options['General']['openfast_configuration']['path2dll'] = \
×
109
                        osp.join(osp.dirname(self.options['modeling_options']['fname_input_modeling']), path2dll)
110
            path2dll = self.modeling_options['General']['openfast_configuration']['path2dll']
1✔
111
            if not osp.exists( path2dll ):
1✔
112
                raise NameError("Cannot find DISCON library: "+path2dll)
×
113

114
            # Activate HAMS in RAFT if requested for OpenFAST
115
            if self.modeling_options["flags"]["offshore"] or self.modeling_options["OpenFAST"]["from_openfast"]:
1✔
116
                if self.modeling_options["RAFT"]["potential_model_override"] == 2:
1✔
117
                    self.modeling_options["OpenFAST"]["HydroDyn"]["PotMod"] = 1
1✔
118
                elif ( (self.modeling_options["RAFT"]["potential_model_override"] == 0) and
1✔
119
                       (len(self.modeling_options["RAFT"]["potential_bem_members"]) > 0) ):
120
                    self.modeling_options["OpenFAST"]["HydroDyn"]["PotMod"] = 1
×
121
                elif self.modeling_options["RAFT"]["potential_model_override"] == 1:
1✔
122
                    self.modeling_options["OpenFAST"]["HydroDyn"]["PotMod"] = 0
×
123
                else:
124
                    # Keep user defined value of PotMod
125
                    pass
1✔
126

127
                if self.modeling_options["OpenFAST"]["HydroDyn"]["PotMod"] == 1:
1✔
128

129
                    # If user requested PotMod but didn't specify any override or members, just run everything (potential_model_override = 2)
130
                    if ( (self.modeling_options["RAFT"]["potential_model_override"] == 0) and
1✔
131
                       (len(self.modeling_options["RAFT"]["potential_bem_members"]) == 0) ):
132
                        self.modeling_options["RAFT"]["potential_model_override"] = 2
×
133
                        
134
                    cwd = os.getcwd()
1✔
135
                    weis_dir = osp.dirname(osp.dirname(osp.dirname(osp.abspath(__file__))))
1✔
136
                    potpath = self.modeling_options["OpenFAST"]["HydroDyn"]["PotFile"].replace('.hst','').replace('.12','').replace('.3','').replace('.1','')
1✔
137
                    if ( (len(potpath) == 0) or (potpath.lower() in ['unused','default','none']) ):
1✔
138
                        
139
                        self.modeling_options['RAFT']['flag'] = True
×
140
                        self.modeling_options["OpenFAST"]["HydroDyn"]["PotFile"] = osp.join(bemDir,'Output','Wamit_format','Buoy')
×
141
                        
142

143
                    else:
144
                        if self.modeling_options['RAFT']['runPyHAMS']:
1✔
145
                            print('Found existing potential model: {}\n    - Trying to use this instead of running PyHAMS.'.format(potpath))
1✔
146
                            self.modeling_options['RAFT']['runPyHAMS'] = False
1✔
147
                        if osp.exists( potpath+'.1' ):
1✔
148
                            self.modeling_options["OpenFAST"]["HydroDyn"]["PotFile"] = osp.realpath(potpath)
1✔
149
                        elif osp.exists( osp.join(cwd, potpath+'.1') ):
×
150
                            self.modeling_options["OpenFAST"]["HydroDyn"]["PotFile"] = osp.realpath( osp.join(cwd, potpath) )
×
151
                        elif osp.exists( osp.join(weis_dir, potpath+'.1') ):
×
152
                            self.modeling_options["OpenFAST"]["HydroDyn"]["PotFile"] = osp.realpath( osp.join(weis_dir, potpath) )
×
153
                        elif osp.exists( osp.join(mod_opt_dir, potpath+'.1') ):
×
154
                            self.modeling_options["OpenFAST"]["HydroDyn"]["PotFile"] = osp.realpath( osp.join(mod_opt_dir, potpath) )
×
155
                        else:
156
                            raise Exception(f'No valid Wamit-style output found for specified PotFile option, {potpath}.1')
×
157

158
        # OpenFAST dir
159
        if self.modeling_options["OpenFAST"]["from_openfast"]:
1✔
160
            if not osp.isabs(self.modeling_options['OpenFAST']['openfast_dir']):
1✔
161
                # Make relative to modeling options input
162
                self.modeling_options['OpenFAST']['openfast_dir'] = osp.realpath(osp.join(
1✔
163
                    mod_opt_dir, self.modeling_options['OpenFAST']['openfast_dir'] ))
164
        
165
        # BEM dir, all levels
166
        base_run_dir = os.path.join(mod_opt_dir,self.modeling_options['General']['openfast_configuration']['OF_run_dir'])
1✔
167
        if MPI:
1✔
168
            rank    = MPI.COMM_WORLD.Get_rank()
×
169
            bemDir = osp.join(base_run_dir,'rank_%000d'%int(rank),'BEM')
×
170
        else:
171
            bemDir = osp.join(base_run_dir,'BEM')
1✔
172

173
        self.modeling_options["Level1"]['BEM_dir'] = bemDir
1✔
174
        if MPI:
1✔
175
            # If running MPI, RAFT won't be able to save designs in parallel
176
            self.modeling_options["Level1"]['save_designs'] = False
×
177
        
178
        # RAFT
179
        if self.modeling_options["flags"]["floating"]:
1✔
180
            bool_init = True if self.modeling_options["RAFT"]["potential_model_override"]==2 else False
1✔
181
            self.modeling_options["RAFT"]["model_potential"] = [bool_init] * self.modeling_options["floating"]["members"]["n_members"]
1✔
182

183
            if self.modeling_options["RAFT"]["potential_model_override"] == 0:
1✔
184
                for k in self.modeling_options["RAFT"]["potential_bem_members"]:
1✔
185
                    idx = self.modeling_options["floating"]["members"]["name"].index(k)
1✔
186
                    self.modeling_options["RAFT"]["model_potential"][idx] = True
1✔
187
        elif self.modeling_options["flags"]["offshore"]:
1✔
188
            self.modeling_options["RAFT"]["model_potential"] = [False]*1000
×
189
            
190
        # ROSCO
191
        self.modeling_options['ROSCO']['flag'] = (self.modeling_options['RAFT']['flag'] or
1✔
192
                                                  self.modeling_options['OpenFAST_Linear']['flag'] or
193
                                                  self.modeling_options['OpenFAST']['flag'])
194
        
195
        if self.modeling_options['ROSCO']['tuning_yaml'] != 'none':  # default is empty
1✔
196
            # Make path absolute if not, relative to modeling options input
197
            if not osp.isabs(self.modeling_options['ROSCO']['tuning_yaml']):
1✔
198
                self.modeling_options['ROSCO']['tuning_yaml'] = osp.realpath(osp.join(
1✔
199
                    mod_opt_dir, self.modeling_options['ROSCO']['tuning_yaml'] ))
200
                
201
        # Apply tuning yaml input if available, this needs to be here for sizing tune_rosco_ivc
202
        if os.path.split(self.modeling_options['ROSCO']['tuning_yaml'])[1] != 'none':  # default is none
1✔
203
            update_rosco_options(self.modeling_options)
1✔
204
        
205
        # XFoil
206
        if not osp.isfile(self.modeling_options['OpenFAST']["xfoil"]["path"]) and self.modeling_options['ROSCO']['Flp_Mode']:
1✔
207
            raise Exception("A distributed aerodynamic control device is defined in the geometry yaml, but the path to XFoil in the modeling options is not defined correctly")
×
208

209
        # Compute the number of DLCs that will be run
210
        DLCs = self.modeling_options['DLC_driver']['DLCs']
1✔
211
        # Initialize the DLC generator
212
        cut_in = self.wt_init['control']['supervisory']['Vin']
1✔
213
        cut_out = self.wt_init['control']['supervisory']['Vout']
1✔
214
        metocean = self.modeling_options['DLC_driver']['metocean_conditions']
1✔
215
        dlc_driver_options = self.modeling_options['DLC_driver']
1✔
216
        dlc_generator = DLCGenerator(cut_in, cut_out, dlc_driver_options=dlc_driver_options, metocean=metocean)
1✔
217
        # Generate cases from user inputs
218
        for i_DLC in range(len(DLCs)):
1✔
219
            DLCopt = DLCs[i_DLC]
1✔
220
            dlc_generator.generate(DLCopt['DLC'], DLCopt)
1✔
221
        self.modeling_options['DLC_driver']['n_cases'] = dlc_generator.n_cases
1✔
222
        
223
        # Determine wind speeds that will be used to calculate AEP (using DLC AEP or 1.1)
224
        DLCs = [i_dlc['DLC'] for i_dlc in self.modeling_options['DLC_driver']['DLCs']]
1✔
225
        if 'AEP' in DLCs:
1✔
226
            DLC_label_for_AEP = 'AEP'
×
227
        else:
228
            DLC_label_for_AEP = '1.1'
1✔
229
        dlc_aep_ws = [c.URef for c in dlc_generator.cases if c.label == DLC_label_for_AEP]
1✔
230
        self.modeling_options['DLC_driver']['n_ws_aep'] = len(np.unique(dlc_aep_ws))
1✔
231

232
        # TMD modeling
233
        self.modeling_options['flags']['TMDs'] = False
1✔
234
        if 'TMDs' in self.wt_init:
1✔
235
            if self.modeling_options['OpenFAST']['flag']:
×
236
                self.modeling_options['flags']['TMDs'] = True
×
237
            else:
238
                raise Exception("TMDs in Levels 1 and 2 are not supported yet")
×
239

240

241
    def set_openmdao_vectors_control(self):
1✔
242
        # Distributed aerodynamic control devices along blade
243
        self.modeling_options['WISDEM']['RotorSE']['n_te_flaps']      = 0
1✔
244
        if 'aerodynamic_control' in self.wt_init['components']['blade']:
1✔
245
            if 'te_flaps' in self.wt_init['components']['blade']['aerodynamic_control']:
×
246
                self.modeling_options['WISDEM']['RotorSE']['n_te_flaps'] = len(self.wt_init['components']['blade']['aerodynamic_control']['te_flaps'])
×
247
                self.modeling_options['WISDEM']['RotorSE']['n_tab']   = 3
×
248
            else:
249
                raise Exception('A distributed aerodynamic control device is provided in the yaml input file, but not supported by wisdem.')
×
250

251
        if 'TMDs' in self.wt_init:
1✔
252
            n_TMDs = len(self.wt_init['TMDs'])
×
253
            self.modeling_options['TMDs'] = {}
×
254
            self.modeling_options['TMDs']['n_TMDs']                 = n_TMDs
×
255
            # TODO: come back and check how many of these need to be modeling options
256
            self.modeling_options['TMDs']['name']                   = [tmd['name'] for  tmd in self.wt_init['TMDs']]
×
257
            self.modeling_options['TMDs']['component']              = [tmd['component'] for  tmd in self.wt_init['TMDs']]
×
258
            self.modeling_options['TMDs']['location']               = [tmd['location'] for  tmd in self.wt_init['TMDs']]
×
259
            self.modeling_options['TMDs']['mass']                   = [tmd['mass'] for  tmd in self.wt_init['TMDs']]
×
260
            self.modeling_options['TMDs']['stiffness']              = [tmd['stiffness'] for  tmd in self.wt_init['TMDs']]
×
261
            self.modeling_options['TMDs']['damping']                = [tmd['damping'] for  tmd in self.wt_init['TMDs']]
×
262
            self.modeling_options['TMDs']['natural_frequency']      = [tmd['natural_frequency'] for  tmd in self.wt_init['TMDs']]
×
263
            self.modeling_options['TMDs']['damping_ratio']          = [tmd['damping_ratio'] for  tmd in self.wt_init['TMDs']]
×
264
            self.modeling_options['TMDs']['X_DOF']                  = [tmd['X_DOF'] for  tmd in self.wt_init['TMDs']]
×
265
            self.modeling_options['TMDs']['Y_DOF']                  = [tmd['Y_DOF'] for  tmd in self.wt_init['TMDs']]
×
266
            self.modeling_options['TMDs']['Z_DOF']                  = [tmd['Z_DOF'] for  tmd in self.wt_init['TMDs']]
×
267
            self.modeling_options['TMDs']['preload_spring']         = [tmd['preload_spring'] for  tmd in self.wt_init['TMDs']]
×
268

269
            # Check that TMD locations map to somewhere valid (tower or platform member)
270
            self.modeling_options['TMDs']['num_tower_TMDs'] = 0
×
271
            self.modeling_options['TMDs']['num_ptfm_TMDs']  = 0
×
272
            
273
            for i_TMD, component in enumerate(self.modeling_options['TMDs']['component']):
×
274
                if self.modeling_options['flags']['floating'] and component in self.modeling_options['floating']['members']['name']:
×
275
                    self.modeling_options['TMDs']['num_ptfm_TMDs'] += 1
×
276
                elif component == 'tower':
×
277
                    self.modeling_options['TMDs']['num_tower_TMDs'] += 1
×
278
                else:
279
                    raise Exception('Invalid TMD component mapping for {} on {}'.format(
×
280
                        self.modeling_options['TMDs']['name'][i_TMD],component))      
281

282
            # Set TMD group  mapping: list of length n_groups, with i_TMDs in each group
283
            # Loop through TMD names, assign to own group if not in an analysis group
284
            if 'TMDs' in self.analysis_options['design_variables']:
×
285
                tmd_group_map = []
×
286
                tmd_names = self.modeling_options['TMDs']['name']
×
287
                
288
                for i_group, tmd_group in enumerate(self.analysis_options['design_variables']['TMDs']['groups']):
×
289
                    tmds_in_group_i = [tmd_names.index(tmd_name) for tmd_name in tmd_group['names']]
×
290

291
                    tmd_group_map.append(tmds_in_group_i)
×
292
                
293
                self.modeling_options['TMDs']['group_mapping'] = tmd_group_map
×
294

295
    def update_ontology(self, wt_opt):
1✔
296
        # Call the WISDEM version first
297
        super(WindTurbineOntologyPythonWEIS, self).update_ontology(wt_opt)
1✔
298

299
        '''
1✔
300
        # Likely outdated
301
        if self.modeling_options['flags']['control']:
302
            self.wt_init['control']['pitch']['omega_pc'] = wt_opt['tune_rosco_ivc.omega_pc']
303
            self.wt_init['control']['pitch']['zeta_pc']  = wt_opt['tune_rosco_ivc.zeta_pc']
304
            self.wt_init['control']['torque']['omega_vs'] = float(wt_opt['tune_rosco_ivc.omega_vs'])
305
            self.wt_init['control']['torque']['zeta_vs']  = float(wt_opt['tune_rosco_ivc.zeta_vs'])
306
            self.wt_init['control']['pitch']['Kp_float']  = float(wt_opt['tune_rosco_ivc.Kp_float'])
307
            self.wt_init['control']['pitch']['ptfm_freq']  = float(wt_opt['tune_rosco_ivc.ptfm_freq'])
308
            self.wt_init['control']['IPC']['IPC_Ki_1P'] = float(wt_opt['tune_rosco_ivc.IPC_Kp1p'])
309
            self.wt_init['control']['IPC']['IPC_Kp_1P'] = float(wt_opt['tune_rosco_ivc.IPC_Ki1p'])
310
            if self.modeling_options['ROSCO']['Flp_Mode'] > 0:
311
                self.wt_init['control']['dac']['flp_kp_norm']= float(wt_opt['tune_rosco_ivc.flp_kp_norm'])
312
                self.wt_init['control']['dac']['flp_tau'] = float(wt_opt['tune_rosco_ivc.flp_tau'])
313
        '''
314

315
        if self.modeling_options['flags']['TMDs']:
1✔
NEW
316
            for k in range( self.modeling_options['TMDs']['n_TMDs'] ):
×
NEW
317
                for m in ['mass', 'stiffness', 'damping']: #, 'natural_frequency', 'damping_ratio']:
×
NEW
318
                    self.wt_init['TMDs'][k][m] = float(wt_opt[f'TMDs.{m}'][k])
×
319

320

321
    def write_outputs(self, fname_output):
1✔
322
        # Override the WISDEM version to ensure that the WEIS options files are written instead
323
        sch.write_geometry_yaml(self.wt_init, fname_output)
1✔
324
        sch.write_modeling_yaml(self.modeling_options, fname_output)
1✔
325
        sch.write_analysis_yaml(self.analysis_options, fname_output)
1✔
326

327

328
    def backwards_compatibility(self):
1✔
329

330
        modopts_no_defaults = load_yaml(self.modeling_options['fname_input_modeling'])
1✔
331

332
        if 'Level1' in modopts_no_defaults:
1✔
333
            self.modeling_options['RAFT'] = copy.deepcopy(self.modeling_options['Level1'])
×
334
            logger.warning('Level1 is no longer a WEIS modeling option.  Please use RAFT instead.  Level1 will be depreciated in a future release.')
×
335

336
        if 'Level2' in modopts_no_defaults:
1✔
337
            self.modeling_options['OpenFAST_Linear'] = copy.deepcopy(self.modeling_options['Level2'])
×
338
            logger.warning('Level2 is no longer a WEIS modeling option.  Please use OpenFAST_Linear instead.  Level2 will be depreciated in a future release.')
×
339

340
        if 'Level3' in modopts_no_defaults:
1✔
341
            self.modeling_options['OpenFAST'] = copy.deepcopy(self.modeling_options['Level3'])
×
342
            logger.warning('Level3 is no longer a WEIS modeling option.  Please use OpenFAST instead.  Level3 will be depreciated in a future release.')
×
343

344

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