def initialize_problem(Analysis_Level): # Initialize blade design refBlade = ReferenceBlade() refBlade.verbose = True refBlade.NINPUT = 200 Nsection_Tow = 19 refBlade.NPTS = 200 refBlade.spar_var = ['Spar_cap_ss', 'Spar_cap_ps'] # SS, then PS refBlade.te_var = 'TE_reinforcement' refBlade.validate = False refBlade.fname_schema = fname_schema blade = refBlade.initialize(fname_input) FASTpref = {} FASTpref['Analysis_Level'] = Analysis_Level # Set FAST Inputs if Analysis_Level >= 1: # File management FASTpref['FAST_ver'] = 'OpenFAST' FASTpref['dev_branch'] = True FASTpref['FAST_exe'] = '~/local/bin/openfast' FASTpref['FAST_directory'] = '../OpenFAST' # Path to fst directory files FASTpref['FAST_InputFile'] = 'IEA-15-240-RWT.fst' # FAST input file (ext=.fst) FASTpref['Turbsim_exe'] = '~/local/bin/turbsim' FASTpref['FAST_namingOut'] = 'IEA-15-240-RWT' FASTpref['FAST_runDirectory'] = 'temp/' + FASTpref['FAST_namingOut'] # Run Settings FASTpref['cores'] = 1 FASTpref['debug_level'] = 2 # verbosity: set to 0 for quiet, 1 & 2 for increasing levels of output # DLCs FASTpref['DLC_gust'] = None # Max deflection # FASTpref['DLC_gust'] = RotorSE_DLC_1_4_Rated # Max deflection ### Not in place yet FASTpref['DLC_extrm'] = None # Max strain # FASTpref['DLC_extrm'] = RotorSE_DLC_7_1_Steady # Max strain ### Not in place yet FASTpref['DLC_turbulent'] = None # FASTpref['DLC_turbulent'] = RotorSE_DLC_1_1_Turb # Alternate turbulent case, replacing rated and extreme DLCs for calculating max deflection and strain FASTpref['DLC_powercurve'] = None # AEP # FASTpref['DLC_powercurve'] = None # AEP # Initialize, read initial FAST files to avoid doing it iteratively fast = InputReader_OpenFAST(FAST_ver=FASTpref['FAST_ver'], dev_branch=FASTpref['dev_branch']) fast.FAST_InputFile = FASTpref['FAST_InputFile'] fast.FAST_directory = FASTpref['FAST_directory'] fast.execute() fst_vt = fast.fst_vt else: fst_vt = {} prob = om.Problem() prob.model=MonopileTurbine(RefBlade=blade, Nsection_Tow=Nsection_Tow, VerbosityCosts=False, FASTpref=FASTpref) prob.model.nonlinear_solver = om.NonlinearRunOnce() prob.model.linear_solver = om.DirectSolver() return prob, blade, fst_vt
) #TURBINE_CLASS['I'] # (Enum): IEC turbine class return rotor if __name__ == "__main__": # Turbine Ontology input fname_input = "turbine_inputs/nrel5mw_mod_update.yaml" # Initialize blade design refBlade = ReferenceBlade() refBlade.verbose = True refBlade.NINPUT = NINPUT refBlade.NPITS = 50 refBlade.validate = False refBlade.fname_schema = "turbine_inputs/IEAontology_schema.yaml" refBlade.spar_var = ['Spar_Cap_SS', 'Spar_Cap_PS'] refBlade.te_var = 'TE_reinforcement' blade = refBlade.initialize(fname_input) # setup rotor = Problem() rotor.model = RotorGeometry(RefBlade=blade, topLevelFlag=True) rotor.setup() rotor = Init_RotorGeometry_wRefBlade(rotor, blade) rotor.run_driver() print(rotor['total_blade_cost'])
def run_problem(optFlag=False, prob_ref=None): # Initialize blade design refBlade = ReferenceBlade() if rank == 0: refBlade.verbose = True else: refBlade.verbose = False refBlade.NINPUT = 8 Nsection_Tow = 19 refBlade.NPTS = 30 refBlade.spar_var = ['Spar_cap_ss', 'Spar_cap_ps'] # SS, then PS refBlade.te_var = 'TE_reinforcement' refBlade.le_var = 'le_reinf' refBlade.validate = False refBlade.fname_schema = fname_schema blade = refBlade.initialize(fname_input) Analysis_Level = 0 FASTpref = {} FASTpref['Analysis_Level'] = Analysis_Level fst_vt = {} # Initialize and execute OpenMDAO problem with input data if MPI: num_par_fd = MPI.COMM_WORLD.Get_size() prob = om.Problem(model=om.Group(num_par_fd=num_par_fd)) prob.model.approx_totals(method='fd') prob.model.add_subsystem('comp', Optimize_MonopileTurbine( RefBlade=blade, Nsection_Tow=Nsection_Tow, folder_output=folder_output), promotes=['*']) else: prob = om.Problem() prob.model = Optimize_MonopileTurbine(RefBlade=blade, Nsection_Tow=Nsection_Tow, folder_output=folder_output) prob.model.nonlinear_solver = om.NonlinearRunOnce() prob.model.linear_solver = om.DirectSolver() if optFlag and not prob_ref is None: if MPI: num_par_fd = MPI.COMM_WORLD.Get_size() prob = om.Problem(model=om.Group(num_par_fd=num_par_fd)) prob.model.approx_totals(method='fd') prob.model.add_subsystem('comp', Optimize_MonopileTurbine( RefBlade=blade, Nsection_Tow=Nsection_Tow, folder_output=folder_output), promotes=['*']) else: prob = om.Problem() prob.model = Optimize_MonopileTurbine(RefBlade=blade, Nsection_Tow=Nsection_Tow, folder_output=folder_output) # --- Driver --- prob.driver = om.pyOptSparseDriver() prob.driver.options['optimizer'] = 'CONMIN' prob.driver.opt_settings['ITMAX'] = 15 prob.driver.opt_settings['IPRINT'] = 4 # ---------------------- # --- Objective --- # prob.model.add_objective('lcoe') prob.model.add_objective('AEP', scaler=-1.) #prob.model.add_objective('mass_one_blade') # ---------------------- # --- Design Variables --- indices_no_root = range(2, refBlade.NINPUT) indices_no_root_no_tip = range(2, refBlade.NINPUT - 1) indices_no_max_chord = range(3, refBlade.NINPUT) prob.model.add_design_var('sparT_in', indices=indices_no_root_no_tip, lower=0.001, upper=0.200) prob.model.add_design_var('chord_in', indices=indices_no_max_chord, lower=0.5, upper=7.0) prob.model.add_design_var('theta_in', indices=indices_no_root, lower=-7.5, upper=20.0) prob.model.add_design_var('teT_in', lower=prob_ref['teT_in'] * 0.5, upper=0.1) #prob.model.add_design_var('leT_in', lower=prob_ref['leT_in']*0.5, upper=0.1) # ---------------------- # --- Constraints --- prob.model.add_subsystem('freq_check', blade_freq_check(), promotes=['freq_check_out']) prob.model.connect('freq_curvefem', 'freq_check.freq_curvefem') #, src_indices=[0]) # Rotor prob.model.add_constraint('tip_deflection_ratio', upper=1.0) # prob.model.add_constraint('no_stall_constraint', upper=1.0) prob.model.add_constraint('freq_check_out', lower=1.1) #prob.model.add_constraint('rated_Q', lower=21.4e6, upper=21.6e6) # prob.model.add_constraint('mass_one_blade', upper=prob_ref['mass_one_blade']*1.02) prob.model.add_constraint('AEP', lower=0.99 * prob_ref['AEP']) # ---------------------- # --- Recorder --- filename_opt_log = folder_output + 'log_opt_' + blade['config']['name'] prob.driver.add_recorder(om.SqliteRecorder(filename_opt_log)) prob.driver.recording_options['includes'] = [ 'AEP', 'total_blade_cost', 'lcoe', 'tip_deflection_ratio', 'mass_one_blade', 'theta_in' ] prob.driver.recording_options['record_objectives'] = True prob.driver.recording_options['record_constraints'] = True prob.driver.recording_options['record_desvars'] = True # ---------------------- # Initialize variable inputs prob = initialize_variables(prob, blade, Analysis_Level, fst_vt) # Run initial condition no matter what print('Running at Initial Position:') prob.run_model() print('########################################') print('') print('Control variables') print('Rotor diam: {:8.3f} m'.format(prob['diameter'][0])) print('TSR: {:8.3f} -'.format(prob['control_tsr'][0])) print('Rated vel: {:8.3f} m/s'.format(prob['rated_V'][0])) print('Rated rpm: {:8.3f} rpm'.format(prob['rated_Omega'][0])) print('Rated pitch: {:8.3f} deg'.format(prob['rated_pitch'][0])) print('Rated thrust: {:8.3f} N'.format(prob['rated_T'][0])) print('Rated torque: {:8.3f} N-m'.format(prob['rated_Q'][0])) print('') print('Constraints') print('Max TD: {:8.3f} m'.format(prob['tip_deflection'][0])) print('TD ratio: {:8.3f} -'.format(prob['tip_deflection_ratio'][0])) print('Blade root M: {:8.3f} N-m'.format(prob['root_bending_moment'][0])) print('') print('Objectives') print('AEP: {:8.3f} GWh'.format(prob['AEP'][0])) print('LCoE: {:8.4f} $/MWh'.format(prob['lcoe'][0])) print('') print('Blades') print('Blade mass: {:8.3f} kg'.format(prob['mass_one_blade'][0])) print('Blade cost: {:8.3f} $'.format(prob['total_blade_cost'][0])) print('Blade freq: {:8.3f} Hz'.format(prob['freq_curvefem'][0])) print('3 blade M_of_I: ', prob['I_all_blades'], ' kg-m^2') print('Hub M: ', prob['Mxyz_total'], ' kg-m^2') print('') print('RNA Summary') print('RNA mass: {:8.3f} kg'.format(prob['tow.pre.mass'][0])) print('RNA C_of_G (TT): ', prob['rna_cg'], ' m') print('RNA M_of_I: ', prob['tow.pre.mI'], ' kg-m^2') print('') print('Tower') print('Tower top F: ', prob['tow.pre.rna_F'], ' N') print('Tower top M: ', prob['tow.pre.rna_M'], ' N-m') print('Tower freqs: ', prob['tow.post.structural_frequencies'], ' Hz') print('Tower vel: {:8.3f} kg'.format(prob['tow.wind.Uref'][0])) print('Tower mass: {:8.3f} kg'.format(prob['tower_mass'][0])) print('Tower cost: {:8.3f} $'.format(prob['tower_cost'][0])) print('########################################') # Angle of attack and stall angle faoa, axaoa = plt.subplots(1, 1, figsize=(5.3, 4)) axaoa.plot(prob['r'], prob['nostallconstraint.aoa_along_span'], label='Initial aoa') axaoa.plot(prob['r'], prob['nostallconstraint.stall_angle_along_span'], '.', label='Initial stall') axaoa.legend(fontsize=12) plt.xlabel('Blade Span [m]', fontsize=14, fontweight='bold') plt.ylabel('Angle [deg]', fontsize=14, fontweight='bold') plt.xticks(fontsize=12) plt.yticks(fontsize=12) plt.grid(color=[0.8, 0.8, 0.8], linestyle='--') plt.subplots_adjust(bottom=0.15, left=0.15) fig_name = 'aoa.png' faoa.savefig(folder_output + fig_name) # Complete data dump #prob.model.list_inputs(units=True) #prob.model.list_outputs(units=True) if optFlag: if rank == 0: print('Running Optimization:') print('N design var: ', 2 * len(indices_no_root_no_tip) + len(indices_no_root) + 1) if not MPI: prob.model.approx_totals() prob.run_driver() if rank == 0: # --- Save output .yaml --- refBlade.write_ontology(fname_output, prob['blade_out'], refBlade.wt_ref) # --- Outputs plotting --- print('AEP: \t\t\t %f\t%f GWh \t Difference: %f %%' % (prob_ref['AEP'] * 1e-6, prob['AEP'] * 1e-6, (prob['AEP'] - prob_ref['AEP']) / prob_ref['AEP'] * 100.)) print( 'LCoE: \t\t\t %f\t%f USD/MWh \t Difference: %f %%' % (prob_ref['lcoe'] * 1.e003, prob['lcoe'] * 1.e003, (prob['lcoe'] - prob_ref['lcoe']) / prob_ref['lcoe'] * 100.)) print('Blade cost: \t\t\t %f\t%f USD \t Difference: %f %%' % (prob_ref['total_blade_cost'], prob['total_blade_cost'], (prob['total_blade_cost'] - prob_ref['total_blade_cost']) / prob_ref['total_blade_cost'] * 100.)) print('Blade mass: \t\t\t %f\t%f kg \t Difference: %f %%' % (prob_ref['total_blade_mass'], prob['total_blade_mass'], (prob['total_blade_mass'] - prob_ref['total_blade_mass']) / prob_ref['total_blade_mass'] * 100.)) print('Tower cost: \t\t\t %f\t%f USD \t Difference: %f %%' % (prob_ref['tower_cost'], prob['tower_cost'], (prob['tower_cost'] - prob_ref['tower_cost']) / prob_ref['tower_cost'] * 100.)) print('Tower mass: \t\t\t %f\t%f kg \t Difference: %f %%' % (prob_ref['tower_mass'], prob['tower_mass'], (prob['tower_mass'] - prob_ref['tower_mass']) / prob_ref['tower_mass'] * 100.)) # ---------------------- # Theta ft, axt = plt.subplots(1, 1, figsize=(5.3, 4)) axt.plot(prob_ref['r'], prob_ref['theta'], label='Initial') axt.plot(prob_ref['r_in'], prob_ref['theta_in'], '.') axt.plot(prob['r'], prob['theta'], label='Optimized') axt.plot(prob['r_in'], prob['theta_in'], '.') axt.legend(fontsize=12) plt.xlabel('Blade Span [m]', fontsize=14, fontweight='bold') plt.ylabel('Twist [deg]', fontsize=14, fontweight='bold') plt.xticks(fontsize=12) plt.yticks(fontsize=12) plt.grid(color=[0.8, 0.8, 0.8], linestyle='--') plt.subplots_adjust(bottom=0.15, left=0.15) fig_name = 'theta.png' ft.savefig(folder_output + fig_name) # Angle of attack and stall angle faoa, axaoa = plt.subplots(1, 1, figsize=(5.3, 4)) axaoa.plot(prob_ref['r'], prob_ref['nostallconstraint.aoa_along_span'], label='Initial aoa') axaoa.plot(prob_ref['r'], prob_ref['nostallconstraint.stall_angle_along_span'], '.', label='Initial stall') axaoa.plot(prob['r'], prob['nostallconstraint.aoa_along_span'], label='Optimized aoa') axaoa.plot(prob['r'], prob['nostallconstraint.stall_angle_along_span'], '.', label='Optimized stall') axaoa.legend(fontsize=12) plt.xlabel('Blade Span [m]', fontsize=14, fontweight='bold') plt.ylabel('Angle [deg]', fontsize=14, fontweight='bold') plt.xticks(fontsize=12) plt.yticks(fontsize=12) plt.grid(color=[0.8, 0.8, 0.8], linestyle='--') plt.subplots_adjust(bottom=0.15, left=0.15) fig_name = 'aoa.png' ft.savefig(folder_output + fig_name) plt.show() return prob, blade
def testAssembly(self): # Global inputs and outputs fname_schema = mydir + os.sep + 'IEAontology_schema.yaml' fname_input = mydir + os.sep + 'IEA-15-240-RWT.yaml' # Initialize blade design refBlade = ReferenceBlade() refBlade.verbose = True refBlade.NINPUT = 8 Nsection_Tow = 19 refBlade.NPTS = 30 refBlade.spar_var = ['Spar_cap_ss', 'Spar_cap_ps'] # SS, then PS refBlade.te_var = 'TE_reinforcement' refBlade.validate = False refBlade.fname_schema = fname_schema blade = refBlade.initialize(fname_input) Analysis_Level = 0 FASTpref = {} FASTpref['Analysis_Level'] = Analysis_Level fst_vt = {} prob = om.Problem() prob.model = MonopileTurbine(RefBlade=blade, Nsection_Tow=Nsection_Tow, VerbosityCosts=False, FASTpref=FASTpref) prob.model.nonlinear_solver = om.NonlinearRunOnce() prob.model.linear_solver = om.DirectSolver() prob.setup() prob = Init_RotorSE_wRefBlade(prob, blade, Analysis_Level=Analysis_Level, fst_vt=fst_vt) # Environmental parameters for the tower prob['significant_wave_height'] = 4.52 prob['significant_wave_period'] = 9.45 prob['water_depth'] = 30. prob['wind_reference_height'] = prob['hub_height'] = 150. prob['shearExp'] = 0.11 prob['rho'] = 1.225 prob['mu'] = 1.7934e-5 prob['water_density'] = 1025.0 prob['water_viscosity'] = 1.3351e-3 prob['wind_beta'] = prob['wave_beta'] = 0.0 # Steel properties for the tower prob['material_density'] = 7850.0 prob['E'] = 210e9 prob['G'] = 79.3e9 prob['yield_stress'] = 345e6 prob['soil_G'] = 140e6 prob['soil_nu'] = 0.4 # Design constraints prob['max_taper_ratio'] = 0.4 prob['min_diameter_thickness_ratio'] = 120.0 # Safety factors prob['gamma_fatigue'] = 1.755 # (Float): safety factor for fatigue prob['gamma_f'] = 1.35 # (Float): safety factor for loads/stresses prob['gamma_m'] = 1.3 # (Float): safety factor for materials prob[ 'gamma_freq'] = 1.1 # (Float): safety factor for resonant frequencies prob['gamma_n'] = 1.0 prob['gamma_b'] = 1.1 # Tower prob['tower_buckling_length'] = 30.0 prob['tower_outfitting_factor'] = 1.07 prob['foundation_height'] = -30. prob['suctionpile_depth'] = 45. prob['tower_section_height'] = np.array([ 5., 5., 5., 5., 5., 5., 5., 5., 5., 13., 13., 13., 13., 13., 13., 13., 13., 13., 12.58244309 ]) prob['tower_outer_diameter'] = np.array([ 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 9.92647687, 9.44319282, 8.83283769, 8.15148167, 7.38976138, 6.90908962, 6.74803581, 6.57231775, 6.5 ]) prob['tower_wall_thickness'] = np.array([ 0.05534138, 0.05344902, 0.05150928, 0.04952705, 0.04751736, 0.04551709, 0.0435267, 0.04224176, 0.04105759, 0.0394965, 0.03645589, 0.03377851, 0.03219233, 0.03070819, 0.02910109, 0.02721289, 0.02400931, 0.0208264, 0.02399756 ]) prob['tower_buckling_length'] = 15.0 prob['transition_piece_mass'] = 100e3 prob['transition_piece_height'] = 15.0 prob['DC'] = 80.0 prob['shear'] = True prob['geom'] = True prob['tower_force_discretization'] = 5.0 prob['nM'] = 2 prob['Mmethod'] = 1 prob['lump'] = 0 prob['tol'] = 1e-9 prob['shift'] = 0.0 # Offshore BOS prob['wtiv'] = 'example_wtiv' prob['feeder'] = 'future_feeder' prob['num_feeders'] = 1 prob['oss_install_vessel'] = 'example_heavy_lift_vessel' prob['site_distance'] = 40.0 prob['site_distance_to_landfall'] = 40.0 prob['site_distance_to_interconnection'] = 40.0 prob['plant_turbine_spacing'] = 7 prob['plant_row_spacing'] = 7 prob['plant_substation_distance'] = 1 prob['tower_deck_space'] = 0. prob['nacelle_deck_space'] = 0. prob['blade_deck_space'] = 0. prob['port_cost_per_month'] = 2e6 prob['monopile_deck_space'] = 0. prob['transition_piece_deck_space'] = 0. prob['commissioning_pct'] = 0.01 prob['decommissioning_pct'] = 0.15 prob['project_lifetime'] = prob['lifetime'] = 20.0 prob['number_of_turbines'] = 40 prob['annual_opex'] = 43.56 # $/kW/yr #prob['bos_costs'] = 1234.5 # $/kW prob['tower_add_gravity'] = True # For turbine costs prob['offshore'] = True prob['crane'] = False prob['bearing_number'] = 2 prob['crane_cost'] = 0.0 prob['labor_cost_rate'] = 3.0 prob['material_cost_rate'] = 2.0 prob['painting_cost_rate'] = 28.8 # Drivetrain prob['tilt'] = 6.0 prob['overhang'] = 11.075 prob['hub_cm'] = np.array([-10.685, 0.0, 5.471]) prob['nac_cm'] = np.array([-5.718, 0.0, 4.048]) prob['hub_I'] = np.array( [1382171.187, 2169261.099, 2160636.794, 0.0, 0.0, 0.0]) prob['nac_I'] = np.array( [13442265.552, 21116729.439, 18382414.385, 0.0, 0.0, 0.0]) prob['hub_mass'] = 190e3 prob['nac_mass'] = 797.275e3 - 190e3 prob['hss_mass'] = 0.0 prob['lss_mass'] = 19.504e3 prob['cover_mass'] = 0.0 prob['pitch_system_mass'] = 50e3 prob['platforms_mass'] = 0.0 prob['spinner_mass'] = 0.0 prob['transformer_mass'] = 0.0 prob['vs_electronics_mass'] = 0.0 prob['yaw_mass'] = 100e3 prob['gearbox_mass'] = 0.0 prob['generator_mass'] = 226.7e3 + 145.25e3 prob['bedplate_mass'] = 39.434e3 prob['main_bearing_mass'] = 4.699e3 prob.run_model() # Make sure we get here self.assertTrue(True)