diff --git a/README.md b/README.md index 3e2b3afe5..b6cbc3206 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The installation instructions below use the environment name, "weis-env," but an 2. Add in final packages and install the software - conda install -y petsc4py mpi4py pyoptsparse # (Mac / Linux only) + conda install -y petsc4py mpi4py pyoptsparse # (Mac / Linux only, sometimes Windows users may need to install mpi4py) pip install -e . 3. Instructions specific for DOE HPC system Eagle. Before executing the setup script, do: diff --git a/examples/02_run_openfast_cases/IEA-15-240-RWT.yaml b/examples/02_run_openfast_cases/IEA-15-240-RWT.yaml index 249e90ff9..2bd40e979 100644 --- a/examples/02_run_openfast_cases/IEA-15-240-RWT.yaml +++ b/examples/02_run_openfast_cases/IEA-15-240-RWT.yaml @@ -745,20 +745,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 diff --git a/examples/03_NREL5MW_OC3_spar/modeling_options.yaml b/examples/03_NREL5MW_OC3_spar/modeling_options.yaml index fb50fb28e..be01c1533 100644 --- a/examples/03_NREL5MW_OC3_spar/modeling_options.yaml +++ b/examples/03_NREL5MW_OC3_spar/modeling_options.yaml @@ -2,7 +2,6 @@ General: verbosity: False # When set to True, the code prints to screen many infos openfast_configuration: OF_run_fst: NREL5MW_OC3_spar - OF_run_dir: outputs/03_NREL5MW_OC3_spar use_exe: True WISDEM: @@ -18,7 +17,6 @@ WISDEM: flag: True FloatingSE: flag: True - rank_and_file: True BOS: flag: True @@ -55,9 +53,9 @@ Level3: # Options for WEIS fidelity level 3 = nonlinear time domain GenDOF: True YawDOF: False TwFADOF1 : True - TwFADOF2 : True + TwFADOF2 : False TwSSDOF1 : True - TwSSDOF2 : True + TwSSDOF2 : False PtfmSgDOF: True PtfmSwDOF: True PtfmHvDOF: True diff --git a/examples/03_NREL5MW_OC3_spar/nrel5mw-spar_oc3.yaml b/examples/03_NREL5MW_OC3_spar/nrel5mw-spar_oc3.yaml index 02b2631ec..3eeab30ab 100644 --- a/examples/03_NREL5MW_OC3_spar/nrel5mw-spar_oc3.yaml +++ b/examples/03_NREL5MW_OC3_spar/nrel5mw-spar_oc3.yaml @@ -818,11 +818,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 - distance_to_substation: 50.0 - distance_to_interconnection: 8. - interconnect_voltage: 130 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 + distance_to_substation: 1.0 + distance_to_interconnection: 8.5 + interconnect_voltage: 130. + distance_to_site: 115. + distance_to_landfall: 50. + port_cost_per_month: 2e6 + boem_review_cost: 0.0 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 diff --git a/examples/04_NREL5MW_OC4_semi/nrel5mw-semi_oc4.yaml b/examples/04_NREL5MW_OC4_semi/nrel5mw-semi_oc4.yaml index aa97e9a2d..5347ccebd 100644 --- a/examples/04_NREL5MW_OC4_semi/nrel5mw-semi_oc4.yaml +++ b/examples/04_NREL5MW_OC4_semi/nrel5mw-semi_oc4.yaml @@ -991,12 +991,23 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 - distance_to_substation: 50.0 - distance_to_interconnection: 8. - interconnect_voltage: 130 - + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 + distance_to_substation: 1.0 + distance_to_interconnection: 8.5 + interconnect_voltage: 130. + distance_to_site: 115. + distance_to_landfall: 50. + port_cost_per_month: 2e6 + boem_review_cost: 0.0 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 + costs: wake_loss_factor: 0.15 fixed_charge_rate: 0.075 diff --git a/examples/05_IEA-3.4-130-RWT/IEA-3p4-130-RWT.yaml b/examples/05_IEA-3.4-130-RWT/IEA-3p4-130-RWT.yaml index a1316d6d1..a839f6429 100644 --- a/examples/05_IEA-3.4-130-RWT/IEA-3p4-130-RWT.yaml +++ b/examples/05_IEA-3.4-130-RWT/IEA-3p4-130-RWT.yaml @@ -686,8 +686,8 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 50.0 distance_to_interconnection: 8. interconnect_voltage: 130 diff --git a/examples/05_IEA-3.4-130-RWT/weis_driver_model_only.py b/examples/05_IEA-3.4-130-RWT/weis_driver_model_only.py new file mode 100644 index 000000000..94372c677 --- /dev/null +++ b/examples/05_IEA-3.4-130-RWT/weis_driver_model_only.py @@ -0,0 +1,73 @@ + +from weis.glue_code.runWEIS import run_weis +from wisdem.commonse.mpi_tools import MPI +import os, time, sys + +## File management +run_dir = os.path.dirname( os.path.realpath(__file__) ) + os.sep +fname_wt_input = run_dir + os.sep + "IEA-3p4-130-RWT.yaml" +fname_modeling_options = run_dir + "modeling_options.yaml" +fname_analysis_options = run_dir + "analysis_options.yaml" + +plot_flag = False + +# Test model_only flag here +modeling_override = {} +modeling_override['General'] = {} +modeling_override['General']['openfast_configuration'] = {} +modeling_override['General']['openfast_configuration']['model_only'] = True + +tt = time.time() +wt_opt, modeling_options, opt_options = run_weis(fname_wt_input, fname_modeling_options, fname_analysis_options, modeling_override=modeling_override) + +rank = MPI.COMM_WORLD.Get_rank() if MPI else 0 + +if rank == 0 and plot_flag: + print('Run time: %f'%(time.time()-tt)) + sys.stdout.flush() + + import matplotlib.pyplot as plt + import numpy as np + from matplotlib import cm + + X = wt_opt['sse_tune.aeroperf_tables.pitch_vector'] + Y = wt_opt['sse_tune.aeroperf_tables.tsr_vector'] + X, Y = np.meshgrid(X, Y) + pitch_schedule = wt_opt['rotorse.rp.powercurve.pitch'] + tsr_schedule = wt_opt['rotorse.rp.powercurve.Omega'] / 30. * np.pi * wt_opt['rotorse.Rtip'] / wt_opt['rotorse.rp.powercurve.V'] + + # Plot the Cp surface + fig, ax = plt.subplots() + Z = wt_opt['sse_tune.aeroperf_tables.Cp'] + cs = ax.contourf(X, Y, Z[:,:,0], cmap=cm.inferno, levels = [0, 0.1, 0.2, 0.3, 0.4, 0.44, 0.47, 0.50, 0.53, 0.56]) + ax.plot(pitch_schedule, tsr_schedule, 'w--', label = 'Regulation trajectory') + ax.set_xlabel('Pitch angle (deg)', fontweight = 'bold') + ax.set_ylabel('Tip speed ratio (-)', fontweight = 'bold') + cbar = fig.colorbar(cs) + cbar.ax.set_ylabel('Aerodynamic power coefficient (-)', fontweight = 'bold') + plt.legend() + + # Plot the Ct surface + fig, ax = plt.subplots() + Z = wt_opt['sse_tune.aeroperf_tables.Ct'] + cs = ax.contourf(X, Y, Z[:,:,0], cmap=cm.inferno) + ax.plot(pitch_schedule, tsr_schedule, 'w--', label = 'Regulation trajectory') + ax.set_xlabel('Pitch angle (deg)', fontweight = 'bold') + ax.set_ylabel('Tip speed ratio (-)', fontweight = 'bold') + cbar = fig.colorbar(cs) + cbar.ax.set_ylabel('Aerodynamic thrust coefficient (-)', fontweight = 'bold') + plt.legend() + + # Plot the Cq surface + fig, ax = plt.subplots() + Z = wt_opt['sse_tune.aeroperf_tables.Cq'] + cs = ax.contourf(X, Y, Z[:,:,0], cmap=cm.inferno) + ax.plot(pitch_schedule, tsr_schedule, 'w--', label = 'Regulation trajectory') + ax.set_xlabel('Pitch angle (deg)', fontweight = 'bold') + ax.set_ylabel('Tip speed ratio (-)', fontweight = 'bold') + cbar = fig.colorbar(cs) + cbar.ax.set_ylabel('Aerodynamic torque coefficient (-)', fontweight = 'bold') + plt.legend() + + plt.show() + diff --git a/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_Monopile.yaml b/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_Monopile.yaml index 39f0c35d8..06d96c744 100644 --- a/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_Monopile.yaml +++ b/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_Monopile.yaml @@ -1014,20 +1014,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 fixed_charge_rate: 0.056 diff --git a/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_VolturnUS-S.yaml b/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_VolturnUS-S.yaml index bfcf06d5a..cf399f79c 100644 --- a/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_VolturnUS-S.yaml +++ b/examples/06_IEA-15-240-RWT/IEA-15-240-RWT_VolturnUS-S.yaml @@ -1278,20 +1278,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 fixed_charge_rate: 0.056 diff --git a/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs.yaml b/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs.yaml index 5888e64aa..7593e4d6f 100644 --- a/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs.yaml +++ b/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs.yaml @@ -1278,20 +1278,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 fixed_charge_rate: 0.056 diff --git a/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs_tower.yaml b/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs_tower.yaml index 3eac655fa..24a2f130d 100644 --- a/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs_tower.yaml +++ b/examples/06_IEA-15-240-RWT/IEA-15-floating_wTMDs_tower.yaml @@ -1216,20 +1216,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 diff --git a/examples/07_te_flaps/BAR_USC_flaps.yaml b/examples/07_te_flaps/BAR_USC_flaps.yaml index 42db4d19b..9bc878fe4 100644 --- a/examples/07_te_flaps/BAR_USC_flaps.yaml +++ b/examples/07_te_flaps/BAR_USC_flaps.yaml @@ -913,5 +913,12 @@ control: setpoint_smooth: {ss_vsgain: 1, ss_pcgain: 0.001} shutdown: {limit_type: gen_speed, limit_value: 2.0} environment: {air_density: 1.225, air_dyn_viscosity: 1.81e-05, weib_shape_parameter: 2.0, air_speed_sound: 340.0, shear_exp: 0.2, water_density: 1025.0, water_dyn_viscosity: 0.0013351, soil_shear_modulus: 140000000.0, soil_poisson: 0.4, gravity: 9.80665, air_pressure: 103500.0, air_vapor_pressure: 1700.0, water_depth: 0.0, V_mean: 0.0} -bos: {plant_turbine_spacing: 7, plant_row_spacing: 7, commissioning_pct: 0.01, decommissioning_pct: 0.15, distance_to_substation: 50.0, distance_to_interconnection: 8.0, interconnect_voltage: 130, distance_to_landfall: 100, distance_to_site: 100, port_cost_per_month: 2000000.0, site_auction_price: 0.0, site_assessment_plan_cost: 0.0, site_assessment_cost: 0.0, construction_operations_plan_cost: 0.0, boem_review_cost: 0.0, design_install_plan_cost: 0.0} +bos: + plant_turbine_spacing: 7 + plant_row_spacing: 7 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 + distance_to_substation: 50.0 + distance_to_interconnection: 8. + interconnect_voltage: 130 costs: {wake_loss_factor: 0.15, fixed_charge_rate: 0.0578, bos_per_kW: 441.0, opex_per_kW: 43.0, turbine_number: 120, labor_rate: 58.8, painting_rate: 30.0, blade_mass_cost_coeff: 14.6, hub_mass_cost_coeff: 3.9, pitch_system_mass_cost_coeff: 22.1, spinner_mass_cost_coeff: 11.1, lss_mass_cost_coeff: 11.9, bearing_mass_cost_coeff: 4.5, gearbox_mass_cost_coeff: 12.9, hss_mass_cost_coeff: 6.8, generator_mass_cost_coeff: 12.4, bedplate_mass_cost_coeff: 2.9, yaw_mass_cost_coeff: 8.3, converter_mass_cost_coeff: 18.8, transformer_mass_cost_coeff: 18.8, hvac_mass_cost_coeff: 124.0, cover_mass_cost_coeff: 5.7, elec_connec_machine_rating_cost_coeff: 41.85, platforms_mass_cost_coeff: 17.1, tower_mass_cost_coeff: 2.9, controls_machine_rating_cost_coeff: 21.15, crane_cost: 12000.0} diff --git a/examples/08_OLAF/BAR_USC.yaml b/examples/08_OLAF/BAR_USC.yaml index c1950ff0e..9dce47363 100644 --- a/examples/08_OLAF/BAR_USC.yaml +++ b/examples/08_OLAF/BAR_USC.yaml @@ -898,5 +898,12 @@ control: setpoint_smooth: {ss_vsgain: 1, ss_pcgain: 0.001} shutdown: {limit_type: gen_speed, limit_value: 2.0} environment: {air_density: 1.225, air_dyn_viscosity: 1.81e-05, weib_shape_parameter: 2.0, air_speed_sound: 340.0, shear_exp: 0.2, water_density: 1025.0, water_dyn_viscosity: 0.0013351, soil_shear_modulus: 140000000.0, soil_poisson: 0.4, gravity: 9.80665, air_pressure: 103500.0, air_vapor_pressure: 1700.0, water_depth: 0.0, V_mean: 0.0} -bos: {plant_turbine_spacing: 7, plant_row_spacing: 7, commissioning_pct: 0.01, decommissioning_pct: 0.15, distance_to_substation: 50.0, distance_to_interconnection: 8.0, interconnect_voltage: 130, distance_to_landfall: 100, distance_to_site: 100, port_cost_per_month: 2000000.0, site_auction_price: 0.0, site_assessment_plan_cost: 0.0, site_assessment_cost: 0.0, construction_operations_plan_cost: 0.0, boem_review_cost: 0.0, design_install_plan_cost: 0.0} +bos: + plant_turbine_spacing: 7 + plant_row_spacing: 7 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 + distance_to_substation: 50.0 + distance_to_interconnection: 8. + interconnect_voltage: 130 costs: {wake_loss_factor: 0.15, fixed_charge_rate: 0.0578, bos_per_kW: 441.0, opex_per_kW: 43.0, turbine_number: 120, labor_rate: 58.8, painting_rate: 30.0, blade_mass_cost_coeff: 14.6, hub_mass_cost_coeff: 3.9, pitch_system_mass_cost_coeff: 22.1, spinner_mass_cost_coeff: 11.1, lss_mass_cost_coeff: 11.9, bearing_mass_cost_coeff: 4.5, gearbox_torque_cost: 50., hss_mass_cost_coeff: 6.8, generator_mass_cost_coeff: 12.4, bedplate_mass_cost_coeff: 2.9, yaw_mass_cost_coeff: 8.3, converter_mass_cost_coeff: 18.8, transformer_mass_cost_coeff: 18.8, hvac_mass_cost_coeff: 124.0, cover_mass_cost_coeff: 5.7, elec_connec_machine_rating_cost_coeff: 41.85, platforms_mass_cost_coeff: 17.1, tower_mass_cost_coeff: 2.9, controls_machine_rating_cost_coeff: 21.15, crane_cost: 12000.0} diff --git a/examples/12_linearization/IEA-15-floating.yaml b/examples/12_linearization/IEA-15-floating.yaml index 03d73e374..5ec918cde 100644 --- a/examples/12_linearization/IEA-15-floating.yaml +++ b/examples/12_linearization/IEA-15-floating.yaml @@ -1175,20 +1175,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 diff --git a/examples/14_level2ccd/IEA-15-floating.yaml b/examples/14_level2ccd/IEA-15-floating.yaml index cba58b6fc..561a08ea1 100644 --- a/examples/14_level2ccd/IEA-15-floating.yaml +++ b/examples/14_level2ccd/IEA-15-floating.yaml @@ -1175,20 +1175,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 diff --git a/examples/15_RAFT_Studies/IEA-15-240-RWT_VolturnUS-S_rectangular.yaml b/examples/15_RAFT_Studies/IEA-15-240-RWT_VolturnUS-S_rectangular.yaml index 7c04e369b..6c7c71f7b 100644 --- a/examples/15_RAFT_Studies/IEA-15-240-RWT_VolturnUS-S_rectangular.yaml +++ b/examples/15_RAFT_Studies/IEA-15-240-RWT_VolturnUS-S_rectangular.yaml @@ -1291,20 +1291,22 @@ environment: bos: plant_turbine_spacing: 7 plant_row_spacing: 7 - commissioning_pct: 0.01 - decommissioning_pct: 0.15 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 distance_to_substation: 1.0 distance_to_interconnection: 8.5 interconnect_voltage: 130. distance_to_site: 115. distance_to_landfall: 50. port_cost_per_month: 2e6 - site_auction_price: 100e6 - site_assessment_plan_cost: 1e6 - site_assessment_cost: 25e6 - construction_operations_plan_cost: 2.5e6 boem_review_cost: 0.0 - design_install_plan_cost: 2.5e6 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: wake_loss_factor: 0.15 fixed_charge_rate: 0.056 diff --git a/examples/17_IEA22_Optimization/IEA-22-280-RWT-Semi.yaml b/examples/17_IEA22_Optimization/IEA-22-280-RWT-Semi.yaml index 31b11faa4..178132fc6 100644 --- a/examples/17_IEA22_Optimization/IEA-22-280-RWT-Semi.yaml +++ b/examples/17_IEA22_Optimization/IEA-22-280-RWT-Semi.yaml @@ -1073,5 +1073,23 @@ control: setpoint_smooth: {ss_vsgain: 1, ss_pcgain: 0.001} shutdown: {limit_type: gen_speed, limit_value: 2.0} environment: {air_density: 1.225, air_dyn_viscosity: 1.81e-05, weib_shape_parameter: 2.0, air_speed_sound: 340.0, shear_exp: 0.12, water_density: 1025.0, water_dyn_viscosity: 0.0013351, soil_shear_modulus: 140000000.0, soil_poisson: 0.4, water_depth: 200.0, significant_wave_height: 4.52, significant_wave_period: 9.45, gravity: 9.80665, air_pressure: 103500.0, air_vapor_pressure: 1700.0, V_mean: 0.0} -bos: {plant_turbine_spacing: 7, plant_row_spacing: 7, commissioning_pct: 0.01, decommissioning_pct: 0.15, distance_to_substation: 1.0, distance_to_interconnection: 8.5, interconnect_voltage: 130.0, distance_to_site: 115.0, distance_to_landfall: 50.0, port_cost_per_month: 2000000.0, site_auction_price: 100000000.0, site_assessment_plan_cost: 1000000.0, site_assessment_cost: 25000000.0, construction_operations_plan_cost: 2500000.0, boem_review_cost: 0.0, design_install_plan_cost: 2500000.0} +bos: + plant_turbine_spacing: 7 + plant_row_spacing: 7 + commissioning_cost_kW: 44.0 + decommissioning_cost_kW: 58.0 + distance_to_substation: 1.0 + distance_to_interconnection: 8.5 + interconnect_voltage: 130. + distance_to_site: 115. + distance_to_landfall: 50. + port_cost_per_month: 2e6 + boem_review_cost: 0.0 + construction_insurance: 44.0 + construction_financing: 183.0 + contingency: 316.0 + site_auction_price: 100e6 + site_assessment_cost: 50e6 + construction_plan_cost: 2.5e5 + installation_plan_cost: 1e6 costs: {wake_loss_factor: 0.15, fixed_charge_rate: 0.058, bos_per_kW: 4050, opex_per_kW: 118.0, turbine_number: 27.0, labor_rate: 58.8, painting_rate: 30.0, blade_mass_cost_coeff: 13.291168594347853, hub_mass_cost_coeff: 3.9, pitch_system_mass_cost_coeff: 22.1, spinner_mass_cost_coeff: 11.1, lss_mass_cost_coeff: 11.9, bearing_mass_cost_coeff: 4.5, gearbox_mass_cost_coeff: 12.9, hss_mass_cost_coeff: 6.8, generator_mass_cost_coeff: 23.247612965626796, bedplate_mass_cost_coeff: 2.9, yaw_mass_cost_coeff: 8.3, converter_mass_cost_coeff: 18.8, transformer_mass_cost_coeff: 18.8, hvac_mass_cost_coeff: 362.3333772011725, cover_mass_cost_coeff: 16.60090142324545, elec_connec_machine_rating_cost_coeff: 41.85, platforms_mass_cost_coeff: 17.1, tower_mass_cost_coeff: 2.7071858084019347, controls_machine_rating_cost_coeff: 21.15, crane_cost: 12000.0, electricity_price: 0.04, reserve_margin_price: 120.0, capacity_credit: 0.0, benchmark_price: 0.071} diff --git a/weis/aeroelasticse/openmdao_openfast.py b/weis/aeroelasticse/openmdao_openfast.py index bc98d595c..b70460ab8 100644 --- a/weis/aeroelasticse/openmdao_openfast.py +++ b/weis/aeroelasticse/openmdao_openfast.py @@ -10,7 +10,7 @@ from pathlib import Path from scipy.interpolate import PchipInterpolator from openmdao.api import ExplicitComponent -from wisdem.commonse.mpi_tools import MPI +from openmdao.utils.mpi import MPI from wisdem.commonse import NFREQ from wisdem.commonse.cylinder_member import get_nfull import wisdem.commonse.utilities as util @@ -38,9 +38,6 @@ from weis.aeroelasticse.CaseGen_General import case_naming from wisdem.inputs import load_yaml -if MPI: - from mpi4py import MPI - logger = logging.getLogger("wisdem/weis") weis_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) @@ -364,9 +361,9 @@ def setup(self): OFmgmt = modopt['General']['openfast_configuration'] self.model_only = OFmgmt['model_only'] FAST_directory_base = OFmgmt['OF_run_dir'] - # If the path is relative, make it an absolute path to current working directory + # If the path is relative, make it an absolute path to modeling options file if not os.path.isabs(FAST_directory_base): - FAST_directory_base = os.path.join(os.getcwd(), FAST_directory_base) + FAST_directory_base = os.path.join(os.path.dirname(modopt['fname_input_modeling']), FAST_directory_base) # Flag to clear OpenFAST run folder. Use it only if disk space is an issue self.clean_FAST_directory = False self.FAST_InputFile = OFmgmt['OF_run_fst'] @@ -472,6 +469,8 @@ def setup(self): # Hub outputs self.add_output('hub_Fxyz', val=np.zeros(3), units='kN', desc = 'Maximum hub forces in the non rotating frame') self.add_output('hub_Mxyz', val=np.zeros(3), units='kN*m', desc = 'Maximum hub moments in the non rotating frame') + self.add_output('hub_Fxyz_aero', val=np.zeros(3), units='N', desc = 'Aero-only maximum hub forces in the non rotating frame') + self.add_output('hub_Mxyz_aero', val=np.zeros(3), units='N*m', desc = 'Aero-only maximum hub moments in the non rotating frame') self.add_output('max_TwrBsMyt',val=0.0, units='kN*m', desc='maximum of tower base bending moment in fore-aft direction') self.add_output('max_TwrBsMyt_ratio',val=0.0, desc='ratio of maximum of tower base bending moment in fore-aft direction to maximum allowable bending moment') @@ -609,6 +608,8 @@ def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): if self.model_only == True: # Write input OF files, but do not run OF + fst_vt['Fst']['TMax'] = 10. + fst_vt['Fst']['TStart'] = 0. self.write_FAST(fst_vt, discrete_outputs) else: # Write OF model and run @@ -1429,7 +1430,7 @@ def update_FAST_model(self, fst_vt, inputs, discrete_inputs): # Potential modeling of fixed substructres not supported if modopt['flags']['floating']: idx = modopt['floating']['members']['platform_elem_memid'][k] - PropPotBool[k] = modopt["Level1"]["model_potential"][idx] + PropPotBool[k] = modopt["Level1"]["model_potential"][idx] fst_vt['HydroDyn']['PropPot'] = PropPotBool if fst_vt['HydroDyn']['NBody'] > 1: @@ -1651,6 +1652,7 @@ def output_channels(self,fst_vt): channels_out += ["Spn1MLxb3", "Spn2MLxb3", "Spn3MLxb3", "Spn4MLxb3", "Spn5MLxb3", "Spn6MLxb3", "Spn7MLxb3", "Spn8MLxb3", "Spn9MLxb3"] channels_out += ["Spn1MLyb3", "Spn2MLyb3", "Spn3MLyb3", "Spn4MLyb3", "Spn5MLyb3", "Spn6MLyb3", "Spn7MLyb3", "Spn8MLyb3", "Spn9MLyb3"] channels_out += ["RtFldCp", "RtFldCt"] + channels_out += ["RtFldFxh", "RtFldFyh", "RtFldFzh", "RtFldMxh", "RtFldMyh", "RtFldMzh"] channels_out += ["RotSpeed", "GenSpeed", "NacYaw", "Azimuth"] channels_out += ["GenPwr", "GenTq", "BldPitch1", "BldPitch2", "BldPitch3"] channels_out += ["Wind1VelX", "Wind1VelY", "Wind1VelZ"] @@ -1663,7 +1665,6 @@ def output_channels(self,fst_vt): channels_out += ["TwHt1MLxt", "TwHt2MLxt", "TwHt3MLxt", "TwHt4MLxt", "TwHt5MLxt", "TwHt6MLxt", "TwHt7MLxt", "TwHt8MLxt", "TwHt9MLxt"] channels_out += ["TwHt1MLyt", "TwHt2MLyt", "TwHt3MLyt", "TwHt4MLyt", "TwHt5MLyt", "TwHt6MLyt", "TwHt7MLyt", "TwHt8MLyt", "TwHt9MLyt"] channels_out += ["TwHt1MLzt", "TwHt2MLzt", "TwHt3MLzt", "TwHt4MLzt", "TwHt5MLzt", "TwHt6MLzt", "TwHt7MLzt", "TwHt8MLzt", "TwHt9MLzt"] - channels_out += ["RtFldFxh", "RtFldFyh", "RtFldFzh"] channels_out += ["RotThrust", "LSShftFxs", "LSShftFys", "LSShftFzs", "LSShftFxa", "LSShftFya", "LSShftFza"] channels_out += ["RotTorq", "LSSTipMxs", "LSSTipMys", "LSSTipMzs", "LSSTipMxa", "LSSTipMya", "LSSTipMza"] channels_out += ["B1N1Alpha", "B1N2Alpha", "B1N3Alpha", "B1N4Alpha", "B1N5Alpha", "B1N6Alpha", "B1N7Alpha", "B1N8Alpha", "B1N9Alpha", "B2N1Alpha", "B2N2Alpha", "B2N3Alpha", "B2N4Alpha", "B2N5Alpha", "B2N6Alpha", "B2N7Alpha", "B2N8Alpha","B2N9Alpha"] @@ -2090,6 +2091,10 @@ def run_FAST(self, inputs, discrete_inputs, fst_vt): else: magnitude_channels[f'LSShft{s}{k}{x}a'] = ['LSShftFya', 'LSShftFza'] if ik==0 else ['LSSTipMya', 'LSSTipMza'] + # Aero-only hub loads + magnitude_channels["RtFldF"] = ["RtFldFxh", "RtFldFyh", "RtFldFzh"] + magnitude_channels["RtFldM"] = ["RtFldMxh", "RtFldMyh", "RtFldMzh"] + # Fatigue at the tower base # Convert ultstress and S_intercept values to kPa with 1e-3 factor tower_fatigue_base = FatigueParams(load2stress=1.0, @@ -2272,6 +2277,14 @@ def get_blade_loading(self, sum_stats, extreme_table, inputs, discrete_inputs, o outputs['hub_Mxyz'] = np.array([extreme_table['LSShftM'][np.argmax(sum_stats['LSShftM']['max'])]['RotTorq'], extreme_table['LSShftM'][np.argmax(sum_stats['LSShftM']['max'])]['LSSTipMys'], extreme_table['LSShftM'][np.argmax(sum_stats['LSShftM']['max'])]['LSSTipMzs']])*1.e3 + + # Aero-only for WISDEM (outputs are in N and N-m) + outputs['hub_Fxyz_aero'] = np.array([extreme_table['RtFldF'][np.argmax(sum_stats['RtFldF']['max'])]['RtFldFxh'], + extreme_table['RtFldF'][np.argmax(sum_stats['RtFldF']['max'])]['RtFldFyh'], + extreme_table['RtFldF'][np.argmax(sum_stats['RtFldF']['max'])]['RtFldFzh']]) + outputs['hub_Mxyz_aero'] = np.array([extreme_table['RtFldM'][np.argmax(sum_stats['RtFldM']['max'])]['RtFldMxh'], + extreme_table['RtFldM'][np.argmax(sum_stats['RtFldM']['max'])]['RtFldMyh'], + extreme_table['RtFldM'][np.argmax(sum_stats['RtFldM']['max'])]['RtFldMzh']]) ## Post process aerodynamic data # Angles of attack - max, std, mean diff --git a/weis/aeroelasticse/runFAST_pywrapper.py b/weis/aeroelasticse/runFAST_pywrapper.py index 97f68c290..870d00178 100644 --- a/weis/aeroelasticse/runFAST_pywrapper.py +++ b/weis/aeroelasticse/runFAST_pywrapper.py @@ -428,7 +428,7 @@ def run_multi(self, cores=None): def run_mpi(self, mpi_comm_map_down): # Run in parallel with mpi - from mpi4py import MPI + from openmdao.utils.mpi import MPI # mpi comm management comm = MPI.COMM_WORLD diff --git a/weis/control/dac.py b/weis/control/dac.py index 629bd9eab..33f9d8a5b 100644 --- a/weis/control/dac.py +++ b/weis/control/dac.py @@ -10,7 +10,7 @@ import multiprocessing as mp from functools import partial -from wisdem.commonse.mpi_tools import MPI +from openmdao.utils.mpi import MPI def runXfoil(xfoil_path, x, y, Re, AoA_min=-9, AoA_max=25, AoA_inc=0.5, Ma=0.0, multi_run=False, MPI_run=False): #This function is used to create and run xfoil simulations for a given set of airfoil coordinates diff --git a/weis/glue_code/gc_LoadInputs.py b/weis/glue_code/gc_LoadInputs.py index be6a72c91..e60e6a653 100644 --- a/weis/glue_code/gc_LoadInputs.py +++ b/weis/glue_code/gc_LoadInputs.py @@ -7,7 +7,7 @@ from weis.aeroelasticse.FAST_reader import InputReader_OpenFAST from wisdem.glue_code.gc_LoadInputs import WindTurbineOntologyPython from weis.dlc_driver.dlc_generator import DLCGenerator -from wisdem.commonse.mpi_tools import MPI +from openmdao.utils.mpi import MPI from rosco.toolbox.inputs.validation import load_rosco_yaml from wisdem.inputs import load_yaml @@ -61,6 +61,17 @@ def set_weis_data(self): # Directory of modeling option input, if we want to use it for relative paths mod_opt_dir = osp.split(self.modeling_options['fname_input_modeling'])[0] + # OpenFAST prefixes + if self.modeling_options['General']['openfast_configuration']['OF_run_fst'] in ['','None','NONE','none']: + self.modeling_options['General']['openfast_configuration']['OF_run_fst'] = 'weis_job' + + if self.modeling_options['General']['openfast_configuration']['OF_run_dir'] in ['','None','NONE','none']: + self.modeling_options['General']['openfast_configuration']['OF_run_dir'] = osp.join( + mod_opt_dir, # If it's a relative path, will be relative to mod_opt directory + self.analysis_options['general']['folder_output'], + 'openfast_runs' + ) + # BEM dir, all levels base_run_dir = os.path.join(mod_opt_dir,self.modeling_options['General']['openfast_configuration']['OF_run_dir']) if MPI: @@ -81,15 +92,7 @@ def set_weis_data(self): self.modeling_options['General']['openfast_configuration']['fst_vt'] = {} self.modeling_options['General']['openfast_configuration']['fst_vt']['outlist'] = fast.fst_vt['outlist'] - # OpenFAST prefixes - if self.modeling_options['General']['openfast_configuration']['OF_run_fst'] in ['','None','NONE','none']: - self.modeling_options['General']['openfast_configuration']['OF_run_fst'] = 'weis_job' - - if self.modeling_options['General']['openfast_configuration']['OF_run_dir'] in ['','None','NONE','none']: - self.modeling_options['General']['openfast_configuration']['OF_run_dir'] = osp.join( - self.analysis_options['general']['folder_output'], - 'openfast_runs' - ) + # User-defined control dylib (path2dll) path2dll = self.modeling_options['General']['openfast_configuration']['path2dll'] diff --git a/weis/glue_code/gc_PoseOptimization.py b/weis/glue_code/gc_PoseOptimization.py index 45ec06b9c..030da6331 100644 --- a/weis/glue_code/gc_PoseOptimization.py +++ b/weis/glue_code/gc_PoseOptimization.py @@ -28,9 +28,148 @@ def __init__(self, wt_init, modeling_options, analysis_options): def get_number_design_variables(self): # Determine the number of design variables - n_DV = super(PoseOptimizationWEIS, self).get_number_design_variables() + n_DV = 0 - n_add = 0 + rotorD_opt = self.opt["design_variables"]["rotor_diameter"] + blade_opt = self.opt["design_variables"]["blade"] + tower_opt = self.opt["design_variables"]["tower"] + mono_opt = self.opt["design_variables"]["monopile"] + jacket_opt = self.opt["design_variables"]["jacket"] + hub_opt = self.opt["design_variables"]["hub"] + drive_opt = self.opt["design_variables"]["drivetrain"] + float_opt = self.opt["design_variables"]["floating"] + mooring_opt = self.opt["design_variables"]["mooring"] + + if rotorD_opt["flag"]: + n_DV += 1 + if blade_opt["aero_shape"]["twist"]["flag"]: + if blade_opt["aero_shape"]["twist"]["index_end"] > blade_opt["aero_shape"]["twist"]["n_opt"]: + raise Exception( + "Check the analysis options yaml, index_end of the blade twist is higher than the number of DVs n_opt" + ) + elif blade_opt["aero_shape"]["twist"]["index_end"] == 0: + blade_opt["aero_shape"]["twist"]["index_end"] = blade_opt["aero_shape"]["twist"]["n_opt"] + n_DV += blade_opt["aero_shape"]["twist"]["index_end"] - blade_opt["aero_shape"]["twist"]["index_start"] + if blade_opt["aero_shape"]["chord"]["flag"]: + if blade_opt["aero_shape"]["chord"]["index_end"] > blade_opt["aero_shape"]["chord"]["n_opt"]: + raise Exception( + "Check the analysis options yaml, index_end of the blade chord is higher than the number of DVs n_opt" + ) + elif blade_opt["aero_shape"]["chord"]["index_end"] == 0: + blade_opt["aero_shape"]["chord"]["index_end"] = blade_opt["aero_shape"]["chord"]["n_opt"] + n_DV += blade_opt["aero_shape"]["chord"]["index_end"] - blade_opt["aero_shape"]["chord"]["index_start"] + if blade_opt["aero_shape"]["af_positions"]["flag"]: + n_DV += ( + self.modeling["WISDEM"]["RotorSE"]["n_af_span"] + - blade_opt["aero_shape"]["af_positions"]["af_start"] + - 1 + ) + if "structure" in blade_opt: + if len(blade_opt["structure"])>0: + for i in range(len(blade_opt["structure"])): + if blade_opt["structure"][i]["index_end"] > blade_opt["structure"][i]["n_opt"]: + raise Exception( + "Check the analysis options yaml, the index_end of a blade layer is higher than the number of DVs n_opt" + ) + elif blade_opt["structure"][i]["index_end"] == 0: + blade_opt["structure"][i]["index_end"] = blade_opt["structure"][i]["n_opt"] + n_DV += ( + blade_opt["structure"][i]["index_end"] + - blade_opt["structure"][i]["index_start"] + ) + if self.opt["design_variables"]["control"]["tsr"]["flag"]: + n_DV += 1 + + if tower_opt["outer_diameter"]["flag"]: + n_DV += self.modeling["WISDEM"]["TowerSE"]["n_height"] + if tower_opt["layer_thickness"]["flag"]: + n_DV += self.modeling["WISDEM"]["TowerSE"]["n_height"] * self.modeling["WISDEM"]["TowerSE"]["n_layers"] + if mono_opt["outer_diameter"]["flag"]: + n_DV += self.modeling["WISDEM"]["FixedBottomSE"]["n_height"] + if mono_opt["layer_thickness"]["flag"]: + n_DV += ( + self.modeling["WISDEM"]["FixedBottomSE"]["n_height"] + * self.modeling["WISDEM"]["FixedBottomSE"]["n_layers"] + ) + # TODO: FIX THIS + # if jacket_opt["outer_diameter"]["flag"]: + # n_DV += self.modeling["WISDEM"]["FixedBottomSE"]["n_height"] + # if jacket_opt["layer_thickness"]["flag"]: + # n_DV += ( + # self.modeling["WISDEM"]["FixedBottomSE"]["n_height"] + # * self.modeling["WISDEM"]["FixedBottomSE"]["n_layers"] + # ) + if hub_opt["cone"]["flag"]: + n_DV += 1 + if hub_opt["hub_diameter"]["flag"]: + n_DV += 1 + for k in [ + "uptilt", + "overhang", + "distance_tt_hub", + "distance_hub_mb", + "distance_mb_mb", + "generator_length", + "gear_ratio", + "generator_length", + "bedplate_web_thickness", + "bedplate_flange_thickness", + "bedplate_flange_width", + ]: + if drive_opt[k]["flag"]: + n_DV += 1 + for k in [ + "lss_diameter", + "lss_wall_thickness", + "hss_diameter", + "hss_wall_thickness", + "nose_diameter", + "nose_wall_thickness", + ]: + if drive_opt[k]["flag"]: + n_DV += 2 + if drive_opt["bedplate_wall_thickness"]["flag"]: + n_DV += 4 + + if float_opt["joints"]["flag"]: + n_DV += len(float_opt["joints"]["z_coordinate"]) + len(float_opt["joints"]["r_coordinate"]) + + if float_opt["members"]["flag"]: + for k, kgrp in enumerate(float_opt["members"]["groups"]): + memname = kgrp["names"][0] + memidx = self.modeling["floating"]["members"]["name"].index(memname) + n_grid = len(self.modeling["floating"]["members"]["grid_member_" + memname]) + n_layers = self.modeling["floating"]["members"]["n_layers"][memidx] + if "diameter" in kgrp: + if "constant" in kgrp["diameter"]: + n_DV += 1 + else: + n_DV += n_grid + if "thickness" in kgrp: + n_DV += n_grid * n_layers + if "ballast" in kgrp: + n_DV += self.modeling["floating"]["members"]["ballast_flag_member_" + memname].count(False) + if "stiffeners" in kgrp: + if "ring" in kgrp["stiffeners"]: + if "size" in kgrp["stiffeners"]["ring"]: + pass + if "spacing" in kgrp["stiffeners"]["ring"]: + n_DV += 1 + if "longitudinal" in kgrp["stiffeners"]: + if "size" in kgrp["stiffeners"]["longitudinal"]: + pass + if "spacing" in kgrp["stiffeners"]["longitudinal"]: + n_DV += 1 + if "axial_joints" in kgrp: + n_DV += len(kgrp["axial_joints"]) + if self.modeling["flags"]["mooring"]: + n_design = 1 if self.modeling["mooring"]["symmetric"] else self.modeling["mooring"]["n_lines"] + if mooring_opt["line_length"]["flag"]: + n_DV += n_design + if mooring_opt["line_diameter"]["flag"]: + n_DV += n_design + + # Count and add design variables from WEIS if self.opt['design_variables']['control']['servo']['pitch_control']['omega']['flag']: if hasattr(self.modeling['ROSCO']['omega_pc'],'__len__'): n_add += len(self.modeling['ROSCO']['omega_pc']) @@ -42,26 +181,26 @@ def get_number_design_variables(self): else: n_add += 1 if self.opt['design_variables']['control']['servo']['pitch_control']['Kp_float']['flag']: - n_add += 1 + n_DV += 1 if self.opt['design_variables']['control']['servo']['pitch_control']['ptfm_freq']['flag']: - n_add += 1 + n_DV += 1 if self.opt['design_variables']['control']['servo']['torque_control']['omega']['flag']: - n_add += 1 + n_DV += 1 if self.opt['design_variables']['control']['servo']['torque_control']['zeta']['flag']: - n_add += 1 + n_DV += 1 if self.opt['design_variables']['control']['servo']['flap_control']['flp_kp_norm']['flag']: - n_add += 1 + n_DV += 1 if self.opt['design_variables']['control']['servo']['flap_control']['flp_tau']['flag']: - n_add += 1 + n_DV += 1 if self.opt['design_variables']['control']['flaps']['te_flap_end']['flag']: - n_add += self.modeling['WISDEM']['RotorSE']['n_te_flaps'] + n_DV += self.modeling['WISDEM']['RotorSE']['n_te_flaps'] if self.opt['design_variables']['control']['flaps']['te_flap_ext']['flag']: - n_add += self.modeling['WISDEM']['RotorSE']['n_te_flaps'] + n_DV += self.modeling['WISDEM']['RotorSE']['n_te_flaps'] if self.opt['design_variables']['control']['ps_percent']['flag']: - n_add += 1 + n_DV += 1 if self.opt['driver']['optimization']['form'] == 'central': - n_add *= 2 + n_DV *= 2 # TMD DVs if self.opt['design_variables']['TMDs']['flag']: @@ -70,17 +209,13 @@ def get_number_design_variables(self): # We only support one TMD for now for tmd_group in TMD_opt['groups']: if 'mass' in tmd_group: - n_add += 1 + n_DV += 1 if 'stiffness' in tmd_group: - n_add += 1 + n_DV += 1 if 'damping' in tmd_group: - n_add += 1 - - - - + n_DV += 1 - return n_DV+n_add + return n_DV diff --git a/weis/glue_code/gc_RunTools.py b/weis/glue_code/gc_RunTools.py index 816a7132f..b6dc7c462 100644 --- a/weis/glue_code/gc_RunTools.py +++ b/weis/glue_code/gc_RunTools.py @@ -2,7 +2,7 @@ import matplotlib.pyplot as plt import openmdao.api as om import numpy as np -from wisdem.commonse.mpi_tools import MPI +from openmdao.utils.mpi import MPI class Outputs_2_Screen(om.ExplicitComponent): # Class to print outputs on screen diff --git a/weis/glue_code/glue_code.py b/weis/glue_code/glue_code.py index b14bd4f8f..b96b063b5 100644 --- a/weis/glue_code/glue_code.py +++ b/weis/glue_code/glue_code.py @@ -661,8 +661,8 @@ def setup(self): self.connect('rotorse.rp.powercurve.rated_Q', 'drivese_post.rated_torque') self.connect('configuration.rated_power', 'drivese_post.machine_rating') self.connect('tower.diameter', 'drivese_post.D_top', src_indices=[-1]) - self.connect('aeroelastic.hub_Fxyz', 'drivese_post.F_hub') - self.connect('aeroelastic.hub_Mxyz', 'drivese_post.M_hub') + self.connect('aeroelastic.hub_Fxyz_aero', 'drivese_post.F_aero_hub') + self.connect('aeroelastic.hub_Mxyz_aero', 'drivese_post.M_aero_hub') self.connect('aeroelastic.max_RootMyb', 'drivese_post.pitch_system.BRFM') self.connect('blade.pa.chord_param', 'drivese_post.blade_root_diameter', src_indices=[0]) self.connect('rotorse.blade_mass', 'drivese_post.blade_mass') diff --git a/weis/glue_code/mpi_tools.py b/weis/glue_code/mpi_tools.py new file mode 100644 index 000000000..e53fcc852 --- /dev/null +++ b/weis/glue_code/mpi_tools.py @@ -0,0 +1,139 @@ +import os +import sys + +from openmdao.utils.mpi import MPI + + +def under_mpirun(): + """Return True if we're being executed under mpirun.""" + # this is a bit of a hack, but there appears to be + # no consistent set of environment vars between MPI + # implementations. + for name in os.environ.keys(): + if ( + name == "OMPI_COMM_WORLD_RANK" + or name == "MPIEXEC_HOSTNAME" + or name.startswith("MPIR_") + or name.startswith("MPICH_") + or name.startswith("INTEL_ONEAPI_MPI_") + or name.startswith("I_MPI_") + ): + return True + return False + + +if under_mpirun(): + + def debug(*msg): # pragma: no cover + newmsg = ["%d: " % MPI.COMM_WORLD.rank] + list(msg) + for m in newmsg: + sys.stdout.write("%s " % m) + sys.stdout.write("\n") + sys.stdout.flush() + +else: + MPI = None + + +def map_comm_heirarchical(n_DV, n_OF, openmp=False): + """ + Heirarchical parallelization communicator mapping. Assumes a number of top level processes + equal to the number of design variables (x2 if central finite differencing is used), each + with its associated number of openfast simulations. + When openmp flag is turned on, the code spreads the openfast simulations across nodes to + lavereage the opnemp parallelization of OpenFAST. The cores that will run under openmp, are marked + in the color map as 1000000. The ones handling python and the DV are marked as 0, and + finally the master ones for each openfast run are marked with a 1. + """ + if openmp: + n_procs_per_node = 36 # Number of + num_procs = MPI.COMM_WORLD.Get_size() + n_nodes = num_procs / n_procs_per_node + + comm_map_down = {} + comm_map_up = {} + color_map = [1000000] * num_procs + + n_DV_per_node = n_DV / n_nodes + + # for m in range(n_DV_per_node): + for nn in range(int(n_nodes)): + for n_dv in range(int(n_DV_per_node)): + comm_map_down[nn * n_procs_per_node + n_dv] = [ + int(n_DV_per_node) + n_dv * n_OF + nn * (n_procs_per_node) + j for j in range(n_OF) + ] + + # This core handles python, so in the colormap the entry is 0 + color_map[nn * n_procs_per_node + n_dv] = int(0) + # These cores handles openfast, so in the colormap the entry is 1 + for k in comm_map_down[nn * n_procs_per_node + n_dv]: + color_map[k] = int(1) + + for j in comm_map_down[nn * n_procs_per_node + n_dv]: + comm_map_up[j] = nn * n_procs_per_node + n_dv + else: + N = n_DV + n_DV * n_OF + comm_map_down = {} + comm_map_up = {} + color_map = [0] * n_DV + + for i in range(n_DV): + comm_map_down[i] = [n_DV + j + i * n_OF for j in range(n_OF)] + color_map.extend([i + 1] * n_OF) + + for j in comm_map_down[i]: + comm_map_up[j] = i + + return comm_map_down, comm_map_up, color_map + + +def subprocessor_loop(comm_map_up): + """ + Subprocessors loop, waiting to receive a function and its arguements to evaluate. + Output of the function is returned. Loops until a stop signal is received + + Input data format: + data[0] = function to be evaluated + data[1] = [list of arguments] + If the function to be evaluated does not fit this format, then a wrapper function + should be created and passed, that handles the setup, argument assignment, etc + for the actual function. + + Stop sigal: + data[0] = False + """ + # comm = impl.world_comm() + rank = MPI.COMM_WORLD.Get_rank() + rank_target = comm_map_up[rank] + + keep_running = True + while keep_running: + data = MPI.COMM_WORLD.recv(source=(rank_target), tag=0) + if data[0] == False: + break + else: + func_execution = data[0] + args = data[1] + output = func_execution(args) + MPI.COMM_WORLD.send(output, dest=(rank_target), tag=1) + + +def subprocessor_stop(comm_map_down): + """ + Send stop signal to subprocessors + """ + # comm = MPI.COMM_WORLD + for rank in comm_map_down.keys(): + subranks = comm_map_down[rank] + for subrank_i in subranks: + MPI.COMM_WORLD.send([False], dest=subrank_i, tag=0) + print("All MPI subranks closed.") + + +if __name__ == "__main__": + + ( + _, + _, + _, + ) = map_comm_heirarchical(2, 4) diff --git a/weis/glue_code/runWEIS.py b/weis/glue_code/runWEIS.py index ec1b59cf0..92e6d9176 100644 --- a/weis/glue_code/runWEIS.py +++ b/weis/glue_code/runWEIS.py @@ -5,7 +5,7 @@ from wisdem.glue_code.gc_WT_InitModel import yaml2openmdao from weis.glue_code.gc_PoseOptimization import PoseOptimizationWEIS from weis.glue_code.glue_code import WindPark -from wisdem.commonse.mpi_tools import MPI +from openmdao.utils.mpi import MPI from wisdem.commonse import fileIO from weis.glue_code.gc_ROSCOInputs import assign_ROSCO_values from weis.control.tmd import assign_TMD_values @@ -16,7 +16,7 @@ evolutionary_methods = ['DE', 'NSGA2'] if MPI: - from wisdem.commonse.mpi_tools import map_comm_heirarchical, subprocessor_loop, subprocessor_stop + from weis.glue_code.mpi_tools import map_comm_heirarchical, subprocessor_loop, subprocessor_stop def run_weis(fname_wt_input, fname_modeling_options, fname_opt_options, geometry_override=None, modeling_override=None, analysis_override=None): # Load all yaml inputs and validate (also fills in defaults) @@ -231,26 +231,7 @@ def run_weis(fname_wt_input, fname_modeling_options, fname_opt_options, geometry desvar_opts=["lower", "upper",], cons_opts=["lower", "upper", "equals",], ) - def simple_types_temp(indict : dict) -> dict: # DEBUG!!!!! - """ - until the changes to WISDEM go through... - """ - def convert(value): - if isinstance(value, np.ndarray): - return convert(value.tolist()) - elif isinstance(value, dict): - return {key: convert(value) for key, value in value.items()} - elif isinstance(value, (list, tuple, set)): - return [convert(item) for item in value] # treat all as list - elif isinstance(value, (np.generic)): - return value.item() # convert numpy primatives to python primative underlying - elif isinstance(value, (float, int, bool, str)): - return value # this should be the end case - else: - return "" - return convert(indict) - save_yaml(folder_output, "problem_vars.yaml", simple_types_temp(problem_var_dict)) - # save_yaml(folder_output, "problem_vars.yaml", simple_types(problem_var_dict)) + save_yaml(folder_output, "problem_vars.yaml", simple_types(problem_var_dict)) # Save data to numpy and matlab arrays fileIO.save_data(froot_out, wt_opt) diff --git a/weis/test/run_examples.py b/weis/test/run_examples.py index 535c9b963..f04d1ed34 100644 --- a/weis/test/run_examples.py +++ b/weis/test/run_examples.py @@ -6,7 +6,7 @@ "01_aeroelasticse/run_OLAF", "01_aeroelasticse/run_nodalOutputs", "02_run_openfast_cases/weis_driver_loads", - #"03_NREL5MW_OC3_spar/weis_driver", # executed in the test_OC3.py + "03_NREL5MW_OC3_spar/weis_driver", # "03_NREL5MW_OC3_spar/weis_freq_driver", # executed in examples_skinny # "04_NREL5MW_OC4_semi/weis_driver", # skipping until we resolve multiple variable ballasts "04_NREL5MW_OC4_semi/weis_freq_driver", diff --git a/weis/test/test_examples_skinny.py b/weis/test/test_examples_skinny.py index de0d31f48..ae74a7d77 100644 --- a/weis/test/test_examples_skinny.py +++ b/weis/test/test_examples_skinny.py @@ -6,6 +6,7 @@ "02_run_openfast_cases/weis_driver_sm", #Not as fast as weis_driver, but not too bad (120 sec. locally) #"03_NREL5MW_OC3_spar/weis_driver", "03_NREL5MW_OC3_spar/weis_freq_driver", + "05_IEA-3.4-130-RWT/weis_driver_model_only", "06_IEA-15-240-RWT/weis_driver_monopile", "06_IEA-15-240-RWT/weis_driver_TMDs", "09_design_of_experiments/DOE_openfast",