comp.add_output('precone', val=0., units='deg') # comp.add_output('omega', val=236, shape=NUM_NODES, units='rad/s') comp.add_output('hub_diameter', val=30, units='cm') comp.add_output('prop_diameter', val=150, units='cm') comp.add_output('pitch', val=0, shape=NUM_NODES, units='rad') comp.add_output('chord_dv', val=10, shape=NUM_CP, units='cm') comp.add_output('theta_dv', val=np.linspace(65., 25., NUM_CP)*np.pi/180., shape=NUM_CP, units='rad') comp.add_output('omega_max', val=500, units='rad/s') comp.add_output('omega_min', val=200, units='rad/s') comp.add_output('tsr2', val=200) p.model.add_subsystem('ivc', comp, promotes=['*']) comp = GeometryGroup(num_nodes=NUM_NODES, num_cp=NUM_CP, num_radial=NUM_RADIAL) p.model.add_subsystem('geom', comp, promotes_inputs=['chord_dv', 'theta_dv', 'pitch'], promotes_outputs=['radii', 'dradii', 'chord', 'theta'] ) p.model.connect('hub_diameter', 'geom.hub_diameter', src_indices=[0]*NUM_NODES) p.model.connect('prop_diameter', 'geom.prop_diameter', src_indices=[0]*NUM_NODES) comp = ccb.CCBladeGroup(num_nodes=NUM_NODES, num_radial=NUM_RADIAL, airfoil_interp=ccblade_interp, turbine=False) p.model.add_subsystem('omega_calc', Omega(num_nodes=NUM_NODES), promotes_inputs=['prop_diameter', 'omega_min', 'omega_max', 'tsr2', 'v'],
def main(): interp = ViternaAirfoil().create_akima('mh117', Re_scaling=False, extend_alpha=True) def ccblade_interp(alpha, Re, Mach): shape = alpha.shape x = np.concatenate( [alpha.flatten()[:, np.newaxis], Re.flatten()[:, np.newaxis]], axis=-1) y = interp(x) y.shape = shape + (2, ) return y[..., 0], y[..., 1] num_nodes = 1 num_blades = 3 num_radial = 15 num_cp = 6 chord = 10. theta = np.linspace(65., 25., num_cp) * np.pi / 180. pitch = 0. prop_data = { 'num_radial': num_radial, 'num_cp': num_cp, 'pitch': pitch, 'chord': chord, 'theta': theta, 'spline_type': 'akima', 'B': num_blades, 'interp': interp } hub_diameter = 30. # cm prop_diameter = 150. # cm c0 = np.sqrt(1.4 * 287.058 * 300.) # meters/second rho0 = 1.4 * 98600. / (c0 * c0) # kg/m^3 omega = 236. prob = Problem() comp = IndepVarComp() comp.add_discrete_input('B', val=num_blades) comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=77.2, shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., units='deg') comp.add_output('omega', val=omega, shape=num_nodes, units='rad/s') comp.add_output('hub_diameter', val=hub_diameter, shape=num_nodes, units='cm') comp.add_output('prop_diameter', val=prop_diameter, shape=num_nodes, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=num_cp, units='cm') comp.add_output('theta_dv', val=theta, shape=num_cp, units='rad') prob.model.add_subsystem('inputs_comp', comp, promotes=['*']) prob.model.add_subsystem('bemt_group', BEMTGroup(num_nodes=num_nodes, prop_data=prop_data), promotes_inputs=[ 'rho', 'mu', 'v', 'alpha', 'incidence', 'omega', 'hub_diameter', 'prop_diameter', 'pitch', 'chord_dv', 'theta_dv' ], promotes_outputs=[ ('normal_load_dist', 'openbemt_normal_load'), ('circum_load_dist', 'openbemt_circum_load') ]) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob.model.add_subsystem( 'geometry_group', comp, promotes_inputs=[ 'hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch' ], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob.model.add_subsystem( 'inflow_comp', comp, promotes_inputs=['v', 'omega', 'radii', 'precone'], promotes_outputs=['Vx', 'Vy']) comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, airfoil_interp=ccblade_interp, turbine=False, phi_residual_solve_nonlinear='bracketing') prob.model.add_subsystem('ccblade_group', comp, promotes_inputs=[ 'B', 'radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'Vx', 'Vy', 'v', 'precone', 'omega', 'hub_diameter', 'prop_diameter' ], promotes_outputs=[('Np', 'ccblade_normal_load'), ('Tp', 'ccblade_circum_load')]) prob.setup() prob.final_setup() prob.run_model() make_plots(prob)
def main(): num_nodes = 1 num_blades = 3 num_radial = 15 num_cp = 6 af_filename = 'mh117.dat' chord = np.tile(10., (num_nodes, num_cp)) theta = np.tile(np.linspace(65., 25., num_cp)*np.pi/180., (num_nodes, 1)) pitch = 0. hub_diameter = 30. # cm prop_diameter = 150. # cm c0 = np.sqrt(1.4*287.058*300.) # meters/second rho0 = 1.4*98600./(c0*c0) # kg/m^3 omega = 236. prob = Problem() v = np.linspace(1., 1.5, num_nodes)*77.2 comp = IndepVarComp() comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=v, shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., shape=num_nodes, units='deg') comp.add_output('omega', val=omega, shape=num_nodes, units='rad/s') comp.add_output('hub_diameter', val=hub_diameter, shape=num_nodes, units='cm') comp.add_output('prop_diameter', val=prop_diameter, shape=num_nodes, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=(num_nodes, num_cp), units='cm') comp.add_output('theta_dv', val=theta, shape=(num_nodes, num_cp), units='rad') prob.model.add_subsystem('indep_var_comp', comp, promotes=['*']) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob.model.add_subsystem( 'geometry_group', comp, promotes_inputs=['hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch'], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob.model.add_subsystem( 'inflow_comp', comp, promotes_inputs=['v', 'omega', 'radii', 'precone'], promotes_outputs=['Vx', 'Vy']) comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, af_filename=af_filename, turbine=False) prob.model.add_subsystem( 'ccblade_group', comp, promotes_inputs=['radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'precone', 'omega', 'Vx', 'Vy', 'precone', 'hub_diameter', 'prop_diameter'], promotes_outputs=['thrust', 'torque', 'efficiency']) # prob.model.add_design_var('chord_dv', lower=1., upper=20., # scaler=5e-2) # prob.model.add_design_var('theta_dv', # lower=20.*np.pi/180., upper=90*np.pi/180.) # prob.model.add_objective('efficiency', scaler=-1.,) # prob.model.add_constraint('thrust', equals=700., scaler=1e-3, # indices=np.arange(num_nodes)) # prob.driver = pyOptSparseDriver() # prob.driver.options['optimizer'] = 'SNOPT' prob.setup() prob.final_setup() st = time.time() # prob.run_driver() prob.run_model() elapsed_time = time.time() - st make_plots(prob) return elapsed_time
def main(): num_nodes = 1 num_blades = 10 num_radial = 15 num_cp = 6 af_filename = 'airfoils/mh117.dat' chord = 20. theta = 5.0*np.pi/180.0 pitch = 0. # Numbers taken from the Aviary group's study of the RVLT tiltwing # turboelectric concept vehicle. n_props = 4 hub_diameter = 30. # cm prop_diameter = 15*30.48 # 15 ft in cm c0 = np.sqrt(1.4*287.058*300.) # meters/second rho0 = 1.4*98600./(c0*c0) # kg/m^3 omega = 236. # rad/s # Find the thrust per rotor from the vehicle's mass. m_full = 6367 # kg g = 9.81 # m/s**2 thrust_vtol = m_full*g/n_props prob = Problem() comp = IndepVarComp() comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=2., shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., units='deg') comp.add_output('hub_diameter', val=hub_diameter, shape=num_nodes, units='cm') comp.add_output('prop_diameter', val=prop_diameter, shape=num_nodes, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=num_cp, units='cm') comp.add_output('theta_dv', val=theta, shape=num_cp, units='rad') comp.add_output('thrust_vtol', val=thrust_vtol, shape=num_nodes, units='N') prob.model.add_subsystem('indep_var_comp', comp, promotes=['*']) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob.model.add_subsystem( 'geometry_group', comp, promotes_inputs=['hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch'], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) balance_group = Group() comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, af_filename=af_filename, turbine=False) balance_group.add_subsystem( 'ccblade_group', comp, promotes_inputs=['radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'precone', 'omega', 'hub_diameter', 'prop_diameter'], promotes_outputs=['thrust', 'torque', 'efficiency']) comp = BalanceComp() comp.add_balance( name='omega', eq_units='N', lhs_name='thrust', rhs_name='thrust_vtol', val=omega, units='rad/s', lower=0.) balance_group.add_subsystem('thrust_balance_comp', comp, promotes=['*']) balance_group.linear_solver = DirectSolver(assemble_jac=True) # balance_group.nonlinear_solver = NewtonSolver(maxiter=20, iprint=2) # balance_group.nonlinear_solver.options['solve_subsystems'] = True # balance_group.nonlinear_solver.options['atol'] = 1e-9 prob.model.add_subsystem('thrust_balance_group', balance_group, promotes=['*']) prob.model.nonlinear_solver = NewtonSolver(maxiter=20, iprint=2) prob.model.nonlinear_solver.options['solve_subsystems'] = True prob.model.nonlinear_solver.options['atol'] = 1e-9 prob.model.nonlinear_solver.linesearch = BoundsEnforceLS() prob.model.nonlinear_solver.linesearch.options['iprint'] = 2 prob.setup() prob.final_setup() # Calculate the induced axial velocity at the rotor for hover, used for # non-diminsionalation. rho = prob.get_val('rho', units='kg/m**3')[0] hub_diameter = prob.get_val('hub_diameter', units='m')[0] prop_diameter = prob.get_val('prop_diameter', units='m')[0] thrust_vtol = prob.get_val('thrust_vtol', units='N')[0] A_rotor = 0.25*np.pi*(prop_diameter**2 - hub_diameter**2) v_h = np.sqrt(thrust_vtol/(2*rho*A_rotor)) # Climb: climb_velocity_nondim = np.linspace(0.1, 2., 10) induced_velocity_nondim = np.zeros_like(climb_velocity_nondim) for vc, vi in np.nditer( [climb_velocity_nondim, induced_velocity_nondim], op_flags=[['readonly'], ['writeonly']]): # Run the model with the requested climb velocity. prob.set_val('v', vc*v_h, units='m/s') print(f"v = {prob.get_val('v', units='m/s')}") prob.run_model() # Calculate the area-weighted average induced velocity at the rotor. # Need the area of each blade section. radii = prob.get_val('radii', units='m') dradii = prob.get_val('dradii', units='m') dArea = 2*np.pi*radii*dradii # Get the induced velocity at the rotor plane for each blade section. Vx = prob.get_val('ccblade_group.Vx', units='m/s') a = prob.get_val('ccblade_group.ccblade_comp.a') # Get the area-weighted average of the induced velocity. vi[...] = np.sum(a*Vx*dArea/A_rotor)/v_h # Induced velocity from plain old momentum theory (for climb). induced_velocity_mt = ( -0.5*climb_velocity_nondim + np.sqrt((0.5*climb_velocity_nondim)**2 + 1.)) fig, ax = plt.subplots() ax.plot(climb_velocity_nondim, -induced_velocity_nondim, label='CCBlade.jl (climb)') ax.plot(climb_velocity_nondim, induced_velocity_mt, label='Momentum Theory (climb)') # Descent: climb_velocity_nondim = np.linspace(-4., -2.5, 10) induced_velocity_nondim = np.zeros_like(climb_velocity_nondim) for vc, vi in np.nditer( [climb_velocity_nondim, induced_velocity_nondim], op_flags=[['readonly'], ['writeonly']]): # Run the model with the requested climb velocity. prob.set_val('v', vc*v_h, units='m/s') print(f"vc = {vc}, v = {prob.get_val('v', units='m/s')}") prob.run_model() # Calculate the area-weighted average induced velocity at the rotor. # Need the area of each blade section. radii = prob.get_val('radii', units='m') dradii = prob.get_val('dradii', units='m') dArea = 2*np.pi*radii*dradii # Get the induced velocity at the rotor plane for each blade section. Vx = prob.get_val('ccblade_group.Vx', units='m/s') a = prob.get_val('ccblade_group.ccblade_comp.a') # Get the area-weighted average of the induced velocity. vi[...] = np.sum(a*Vx*dArea/A_rotor)/v_h # Induced velocity from plain old momentum theory (for descent). induced_velocity_mt = ( -0.5*climb_velocity_nondim - np.sqrt((0.5*climb_velocity_nondim)**2 - 1.)) # Plot the induced velocity for descent. ax.plot(climb_velocity_nondim, -induced_velocity_nondim, label='CCBlade.jl (descent)') ax.plot(climb_velocity_nondim, induced_velocity_mt, label='Momentum Theory (descent)') # # Empirical region: # climb_velocity_nondim = np.linspace(-1.9, -1.5, 5) # induced_velocity_nondim = np.zeros_like(climb_velocity_nondim) # for vc, vi in np.nditer( # [climb_velocity_nondim, induced_velocity_nondim], # op_flags=[['readonly'], ['writeonly']]): # # Run the model with the requested climb velocity. # prob.set_val('v', vc*v_h, units='m/s') # print(f"vc = {vc}, v = {prob.get_val('v', units='m/s')}") # prob.run_model() # # Calculate the area-weighted average induced velocity at the rotor. # # Need the area of each blade section. # radii = prob.get_val('radii', # units='m') # dradii = prob.get_val('dradii', # units='m') # dArea = 2*np.pi*radii*dradii # # Get the induced velocity at the rotor plane for each blade section. # Vx = prob.get_val('ccblade_group.Vx', units='m/s') # a = prob.get_val('ccblade_group.ccblade_comp.a') # # Get the area-weighted average of the induced velocity. # vi[...] = np.sum(a*Vx*dArea/A_rotor)/v_h # # Plot the induced velocity for the empirical region. # ax.plot(climb_velocity_nondim, -induced_velocity_nondim, # label='CCBlade.jl (empirical region)') ax.set_xlabel('Vc/vh') ax.set_ylabel('Vi/vh') ax.legend() fig.savefig('induced_velocity.png')
def main(): interp = ViternaAirfoil().create_akima( 'mh117', Re_scaling=False, extend_alpha=True) num_nodes = 1 num_blades = 3 num_radial = 15 num_cp = 6 af_filename = 'airfoils/mh117.dat' chord = 10. theta = np.linspace(65., 25., num_cp)*np.pi/180. pitch = 0. prop_data = { 'num_radial': num_radial, 'num_cp': num_cp, 'pitch': pitch, 'chord': chord, 'theta': theta, 'spline_type': 'akima', 'B': num_blades, 'interp': interp} hub_diameter = 30. # cm prop_diameter = 150. # cm c0 = np.sqrt(1.4*287.058*300.) # meters/second rho0 = 1.4*98600./(c0*c0) # kg/m^3 omega = 236. prob = Problem() comp = IndepVarComp() comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=77.2, shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., units='deg') comp.add_output('omega', val=omega, shape=num_nodes, units='rad/s') comp.add_output('hub_diameter', val=hub_diameter, shape=num_nodes, units='cm') comp.add_output('prop_diameter', val=prop_diameter, shape=num_nodes, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=num_cp, units='cm') comp.add_output('theta_dv', val=theta, shape=num_cp, units='rad') prob.model.add_subsystem('inputs_comp', comp, promotes=['*']) prob.model.add_subsystem( 'bemt_group', BEMTGroup(num_nodes=num_nodes, prop_data=prop_data), promotes_inputs=['rho', 'mu', 'v', 'alpha', 'incidence', 'omega', 'hub_diameter', 'prop_diameter', 'pitch', 'chord_dv', 'theta_dv'], promotes_outputs=[('normal_load_dist', 'openbemt_normal_load'), ('circum_load_dist', 'openbemt_circum_load')]) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob.model.add_subsystem( 'geometry_group', comp, promotes_inputs=['hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch'], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, af_filename=af_filename, turbine=False) prob.model.add_subsystem( 'ccblade_group', comp, promotes_inputs=['radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'precone', 'omega', 'hub_diameter', 'prop_diameter'], promotes_outputs=[('Np', 'ccblade_normal_load'), ('Tp', 'ccblade_circum_load')]) prob.setup() prob.final_setup() prob.run_model() make_plots(prob)
def main(): interp = ViternaAirfoil().create_akima('mh117', Re_scaling=False, extend_alpha=True) def ccblade_interp(alpha, Re, Mach): shape = alpha.shape x = np.concatenate( [alpha.flatten()[:, np.newaxis], Re.flatten()[:, np.newaxis]], axis=-1) y = interp(x) y.shape = shape + (2, ) return y[..., 0], y[..., 1] num_nodes = 1 num_blades = 3 num_radial = 15 num_cp = 6 chord = 10. theta = np.linspace(65., 25., num_cp) * np.pi / 180. pitch = 0. hub_diameter = 30. # cm prop_diameter = 150. # cm c0 = np.sqrt(1.4 * 287.058 * 300.) # meters/second rho0 = 1.4 * 98600. / (c0 * c0) # kg/m^3 omega = 236. prob = Problem() comp = IndepVarComp() comp.add_discrete_input('B', val=num_blades) comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=77.2, shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., units='deg') comp.add_output('omega', val=omega, shape=num_nodes, units='rad/s') comp.add_output('hub_diameter', val=hub_diameter, shape=num_nodes, units='cm') comp.add_output('prop_diameter', val=prop_diameter, shape=num_nodes, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=num_cp, units='cm') comp.add_output('theta_dv', val=theta, shape=num_cp, units='rad') prob.model.add_subsystem('inputs_comp', comp, promotes=['*']) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob.model.add_subsystem( 'geometry_group', comp, promotes_inputs=[ 'hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch' ], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, airfoil_interp=ccblade_interp, turbine=False, phi_residual_solve_nonlinear=False) prob.model.add_subsystem('ccblade_group', comp, promotes_inputs=[ 'B', 'radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'precone', 'omega', 'hub_diameter', 'prop_diameter' ], promotes_outputs=[('Np', 'ccblade_normal_load'), ('Tp', 'ccblade_circum_load')]) prob.setup() prob.final_setup() eps = 1e-2 num_phi = 45 phi = np.linspace(-0.5 * np.pi + eps, 0.0 - eps, num_phi) phi = np.tile(phi[:, np.newaxis, np.newaxis], (1, num_nodes, num_radial)) phi_residual = np.zeros_like(phi) for i in range(num_phi): p = phi[i, :, :] prob.set_val('ccblade_group.ccblade_comp.phi', p, units='rad') prob.run_model() prob.model.run_apply_nonlinear() inputs, outputs, residuals = prob.model.get_nonlinear_vectors() phi_residual[i, :, :] = residuals['ccblade_group.ccblade_comp.phi'] make_individual_plots(prob, phi, phi_residual, 'phi_residual-r{:02d}.png')
def main(): interp = ViternaAirfoil().create_akima( 'mh117', Re_scaling=False, extend_alpha=True) def ccblade_interp(alpha, Re, Mach): shape = alpha.shape x = np.concatenate( [ alpha.flatten()[:, np.newaxis], Re.flatten()[:, np.newaxis] ], axis=-1) y = interp(x) y.shape = shape + (2,) return y[..., 0], y[..., 1] num_nodes = 1 num_blades = 3 num_radial = 15 num_cp = 6 chord = 10. theta = np.linspace(65., 25., num_cp)*np.pi/180. pitch = 0. hub_diameter = 30. # cm prop_diameter = 150. # cm c0 = np.sqrt(1.4*287.058*300.) # meters/second rho0 = 1.4*98600./(c0*c0) # kg/m^3 omega = 236. prob = Problem() comp = IndepVarComp() comp.add_discrete_input('B', val=num_blades) comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=77.2, shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., units='deg') comp.add_output('omega', val=omega, shape=num_nodes, units='rad/s') comp.add_output('hub_diameter', val=hub_diameter, shape=num_nodes, units='cm') comp.add_output('prop_diameter', val=prop_diameter, shape=num_nodes, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=num_cp, units='cm') comp.add_output('theta_dv', val=theta, shape=num_cp, units='rad') prob.model.add_subsystem('indep_var_comp', comp, promotes=['*']) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob.model.add_subsystem( 'geometry_group', comp, promotes_inputs=['hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch'], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob.model.add_subsystem( 'inflow_comp', comp, promotes_inputs=['v', 'omega', 'radii', 'precone'], promotes_outputs=['Vx', 'Vy']) comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, airfoil_interp=ccblade_interp, turbine=False, phi_residual_solve_nonlinear='bracketing') prob.model.add_subsystem( 'ccblade_group', comp, promotes_inputs=['radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'omega', 'Vx', 'Vy', 'precone', 'hub_diameter', 'prop_diameter'], promotes_outputs=['thrust', 'torque', 'efficiency']) prob.model.add_design_var('chord_dv', lower=1., upper=20., scaler=5e-2) prob.model.add_design_var('theta_dv', lower=20.*np.pi/180., upper=90*np.pi/180.) prob.model.add_objective('efficiency', scaler=-1.,) prob.model.add_constraint('thrust', equals=700., scaler=1e-3, indices=np.arange(num_nodes)) prob.driver = pyOptSparseDriver() prob.driver.options['optimizer'] = 'SNOPT' prob.setup() prob.final_setup() st = time.time() prob.run_driver() elapsed_time = time.time() - st make_plots(prob) return elapsed_time
def test_openbemt_optimization(self): interp = ViternaAirfoil().create_akima('mh117', Re_scaling=False, extend_alpha=True) def ccblade_interp(alpha, Re, Mach): shape = alpha.shape x = np.concatenate( [alpha.flatten()[:, np.newaxis], Re.flatten()[:, np.newaxis]], axis=-1) y = interp(x) y.shape = shape + (2, ) return y[..., 0], y[..., 1] num_nodes = 1 num_blades = 3 num_radial = 15 num_cp = 6 chord = 10. theta = np.linspace(65., 25., num_cp) * np.pi / 180. pitch = 0. prop_data = { 'num_radial': num_radial, 'num_cp': num_cp, 'pitch': pitch, 'chord': chord, 'theta': theta, 'spline_type': 'akima', 'B': num_blades, 'interp': interp } hub_diameter = 30. # cm prop_diameter = 150. # cm c0 = np.sqrt(1.4 * 287.058 * 300.) # meters/second rho0 = 1.4 * 98600. / (c0 * c0) # kg/m^3 omega = 236. prob_ccblade = om.Problem() prob_openbemt = om.Problem() comp = om.IndepVarComp() comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=77.2, shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., units='deg') comp.add_output('omega', val=omega, shape=num_nodes, units='rad/s') comp.add_output('hub_diameter', val=hub_diameter, units='cm') comp.add_output('prop_diameter', val=prop_diameter, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=num_cp, units='cm') comp.add_output('theta_dv', val=theta, shape=num_cp, units='rad') prob_ccblade.model.add_subsystem('indep_var_comp', comp, promotes=['*']) prob_openbemt.model.add_subsystem('indep_var_comp', comp, promotes=['*']) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob_ccblade.model.add_subsystem( 'geometry_group', comp, promotes_inputs=[ 'hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch' ], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob_ccblade.model.add_subsystem( 'inflow_comp', comp, promotes_inputs=['v', 'omega', 'radii', 'precone'], promotes_outputs=['Vx', 'Vy']) comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, airfoil_interp=ccblade_interp, turbine=False) prob_ccblade.model.add_subsystem( 'ccblade_group', comp, promotes_inputs=[ 'radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'omega', 'Vx', 'Vy', 'precone', 'hub_diameter', 'prop_diameter' ], promotes_outputs=['thrust', 'torque', 'efficiency']) prob_ccblade.model.add_design_var('chord_dv', lower=1., upper=20., scaler=5e-2) prob_ccblade.model.add_design_var('theta_dv', lower=20. * np.pi / 180., upper=90 * np.pi / 180.) prob_ccblade.model.add_objective( 'efficiency', scaler=-1., ) prob_ccblade.model.add_constraint('thrust', equals=700., scaler=1e-3, indices=np.arange(num_nodes)) prob_ccblade.driver = om.pyOptSparseDriver() prob_ccblade.driver.options['optimizer'] = 'SNOPT' prob_ccblade.setup() prob_ccblade.final_setup() prob_ccblade.run_driver() ccblade_normal_load = prob_ccblade.get_val('ccblade_group.Np', units='N/m') * num_blades ccblade_circum_load = prob_ccblade.get_val('ccblade_group.Tp', units='N/m') * num_blades prob_openbemt.model.add_subsystem( 'bemt_group', BEMTGroup(num_nodes=num_nodes, prop_data=prop_data), promotes_inputs=[ 'rho', 'mu', 'v', 'alpha', 'incidence', 'omega', 'hub_diameter', 'prop_diameter', 'pitch', 'chord_dv', 'theta_dv' ], promotes_outputs=[('normal_load_dist', 'openbemt_normal_load'), ('circum_load_dist', 'openbemt_circum_load'), 'dradii', 'thrust', 'efficiency']) prob_openbemt.model.add_design_var('chord_dv', lower=1., upper=20., scaler=5e-2) prob_openbemt.model.add_design_var('theta_dv', lower=20. * np.pi / 180., upper=90 * np.pi / 180.) prob_openbemt.model.add_objective( 'efficiency', scaler=-1., ) prob_openbemt.model.add_constraint('thrust', equals=700., scaler=1e-3, indices=np.arange(num_nodes)) prob_openbemt.driver = om.pyOptSparseDriver() prob_openbemt.driver.options['optimizer'] = 'SNOPT' prob_openbemt.setup() prob_openbemt.final_setup() prob_openbemt.model.bemt_group.phi_group.nonlinear_solver.options[ 'iprint'] = 0 prob_openbemt.run_driver() openbemt_normal_load = prob_openbemt.get_val('openbemt_normal_load', units='N') openbemt_circum_load = prob_openbemt.get_val('openbemt_circum_load', units='N') dradii = prob_openbemt.get_val('dradii', units='m') openbemt_normal_load /= dradii openbemt_circum_load /= dradii assert_rel_error(self, ccblade_normal_load, openbemt_normal_load, 1e-2) assert_rel_error(self, ccblade_circum_load, openbemt_circum_load, 1e-2)