def test_specify_precon(self): import numpy as np from openmdao.api import Problem, ScipyKrylov, NewtonSolver, LinearBlockGS, \ DirectSolver from openmdao.test_suite.components.double_sellar import DoubleSellar prob = Problem(model=DoubleSellar()) model = prob.model model.nonlinear_solver = NewtonSolver() model.nonlinear_solver.linesearch = BoundsEnforceLS() model.linear_solver = ScipyKrylov() model.g1.linear_solver = DirectSolver() model.g2.linear_solver = DirectSolver() model.linear_solver.precon = LinearBlockGS() # TODO: This should work with 1 iteration. #model.linear_solver.precon.options['maxiter'] = 1 prob.setup() prob.set_solver_print(level=2) prob.run_model() assert_rel_error(self, prob['g1.y1'], 0.64, .00001) assert_rel_error(self, prob['g1.y2'], 0.80, .00001) assert_rel_error(self, prob['g2.y1'], 0.64, .00001) assert_rel_error(self, prob['g2.y2'], 0.80, .00001)
def setup(self): nn = self.options['num_nodes'] self.add_subsystem('aero', subsys=AerodynamicsGroup(num_nodes=nn)) self.add_subsystem( 'thrust_eq_comp', subsys=ThrustEquilibriumComp(num_nodes=nn), promotes_inputs=['q', 'S', 'gam', 'alpha', 'W_total'], promotes_outputs=['CT']) self.add_subsystem( 'lift_eq_comp', subsys=LiftEquilibriumComp(num_nodes=nn), promotes_inputs=['q', 'S', 'gam', 'alpha', 'W_total', 'CT'], promotes_outputs=['CL_eq']) bal = self.add_subsystem(name='alpha_eta_balance', subsys=BalanceComp(), promotes_outputs=['alpha', 'eta']) self.connect('alpha', ('aero.alpha')) self.connect('eta', ('aero.eta')) bal.add_balance('alpha', units='rad', eq_units=None, lhs_name='CL_eq', rhs_name='CL', val=0.01 * np.ones(nn), lower=-20, upper=30, res_ref=1.0) bal.add_balance('eta', units='rad', val=0.01 * np.ones(nn), eq_units=None, lhs_name='CM', lower=-30, upper=30, res_ref=1.0) self.connect('aero.CL', 'alpha_eta_balance.CL') self.connect('aero.CD', 'thrust_eq_comp.CD') self.connect('aero.CM', 'alpha_eta_balance.CM') self.connect('CL_eq', ('alpha_eta_balance.CL_eq')) self.linear_solver = DirectSolver() self.nonlinear_solver = NewtonSolver() self.nonlinear_solver.options['atol'] = 1e-14 self.nonlinear_solver.options['rtol'] = 1e-14 self.nonlinear_solver.options['solve_subsystems'] = True self.nonlinear_solver.options['err_on_maxiter'] = True self.nonlinear_solver.options['max_sub_solves'] = 10 self.nonlinear_solver.options['maxiter'] = 150 self.nonlinear_solver.options['iprint'] = -1 # self.nonlinear_solver.linesearch = ArmijoGoldsteinLS() self.nonlinear_solver.linesearch = BoundsEnforceLS() self.nonlinear_solver.linesearch.options['print_bound_enforce'] = True
def test_backtracking(self): top = Problem() top.model.add_subsystem('px', IndepVarComp('x', 1.0)) top.model.add_subsystem('comp', ImplCompTwoStates()) top.model.connect('px.x', 'comp.x') top.model.nonlinear_solver = BroydenSolver() top.model.nonlinear_solver.options['maxiter'] = 25 top.model.nonlinear_solver.options['diverge_limit'] = 0.5 top.model.nonlinear_solver.options['state_vars'] = ['comp.y', 'comp.z'] top.model.linear_solver = DirectSolver() top.setup(check=False) top.model.nonlinear_solver.linesearch = BoundsEnforceLS( bound_enforcement='vector') # Setup again because we assigned a new linesearch top.setup(check=False) top.set_solver_print(level=2) # Test lower bound: should go to the lower bound and stall top['px.x'] = 2.0 top['comp.y'] = 0.0 top['comp.z'] = 1.6 top.run_model() assert_rel_error(self, top['comp.z'], 1.5, 1e-8) # Test upper bound: should go to the upper bound and stall top['px.x'] = 0.5 top['comp.y'] = 0.0 top['comp.z'] = 2.4 top.run_model() assert_rel_error(self, top['comp.z'], 2.5, 1e-8)
def setUp(self): self.prob = Problem() self.prob.model.add_subsystem('map', CompressorMap(map_data=AXI5, design=False), promotes=['*']) self.prob.model.set_input_defaults('Wc', 322.60579101811692, units='lbm/s') self.prob.model.set_input_defaults('Nc', 172.11870165984794, units='rpm') self.prob.model.set_input_defaults('alphaMap', 1.0) self.prob.model.set_input_defaults('s_Nc', 1.721074624) self.prob.model.set_input_defaults('s_PR', 0.147473296) self.prob.model.set_input_defaults('s_Wc', 2.152309293) self.prob.model.set_input_defaults('s_eff', 0.9950409659) newton = self.prob.model.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-8 newton.options['rtol'] = 1e-8 newton.options['iprint'] = 2 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.prob.model.linear_solver = DirectSolver(assemble_jac=True) self.prob.setup(check=False)
def test_implicit_cycle_precon(self): prob = Problem() model = prob.model = Group() model.add_subsystem('p1', IndepVarComp('x', 1.0)) model.add_subsystem('d1', SellarImplicitDis1()) model.add_subsystem('d2', SellarImplicitDis2()) model.connect('d1.y1', 'd2.y1') model.connect('d2.y2', 'd1.y2') model.nonlinear_solver = NewtonSolver() model.nonlinear_solver.options['maxiter'] = 5 model.nonlinear_solver.linesearch = BoundsEnforceLS() model.linear_solver = ScipyKrylov() model.linear_solver.precon = self.linear_solver_class() prob.setup(check=False) prob['d1.y1'] = 4.0 prob.set_solver_print() prob.run_model() res = model._residuals.get_norm() # Newton is kinda slow on this for some reason, this is how far it gets with directsolver too. self.assertLess(res, 2.0e-2)
def setup_solvers(self, phase): if self.any_solved_segs: newton = phase.nonlinear_solver = NewtonSolver() newton.options['solve_subsystems'] = True newton.options['iprint'] = -1 newton.linesearch = BoundsEnforceLS() phase.linear_solver = DirectSolver()
def test_record_line_search_bounds_enforce(self, m): self.setup_endpoints(m) recorder = WebRecorder(self._accepted_token, suppress_output=True) self.setup_sellar_model() model = self.prob.model model.nonlinear_solver = NewtonSolver() model.linear_solver = ScipyIterativeSolver() model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 4 ls = model.nonlinear_solver.linesearch = BoundsEnforceLS( bound_enforcement='vector') ls.add_recorder(recorder) self.prob.setup(check=False) t0, t1 = run_driver(self.prob) self.prob.cleanup() expected_abs_error = 7.02783609310096e-10 expected_rel_error = 8.078674883382422e-07 solver_iteration = json.loads(self.solver_iterations) self.assertAlmostEqual(solver_iteration['abs_err'], expected_abs_error) self.assertAlmostEqual(solver_iteration['rel_err'], expected_rel_error) self.assertEqual(solver_iteration['solver_output'], []) self.assertEqual(solver_iteration['solver_residuals'], [])
def setup(self): thermo_spec = species_data.janaf self.add_subsystem( 'fc', FlightConditions(thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem( 'inlet', Inlet(design=True, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem( 'fan', Compressor(thermo_data=thermo_spec, elements=AIR_MIX, design=True)) self.add_subsystem('nozz', Nozzle(thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('perf', Performance(num_nozzles=1, num_burners=0)) self.add_subsystem('shaft', IndepVarComp('Nmech', 1., units='rpm')) self.add_subsystem('pwr_balance', BalanceComp('W', units='lbm/s', eq_units='hp', val=50., lower=1., upper=500.), promotes_inputs=[('rhs:W', 'pwr_target')]) # self.set_order(['pwr_balance', 'pwr_target', 'fc', 'inlet', 'shaft', 'fan', 'nozz', 'perf']) connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I') connect_flow(self, 'inlet.Fl_O', 'fan.Fl_I') connect_flow(self, 'fan.Fl_O', 'nozz.Fl_I') self.connect('shaft.Nmech', 'fan.Nmech') self.connect('fc.Fl_O:stat:P', 'nozz.Ps_exhaust') self.connect('inlet.Fl_O:tot:P', 'perf.Pt2') self.connect('fan.Fl_O:tot:P', 'perf.Pt3') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('nozz.Fg', 'perf.Fg_0') self.connect('pwr_balance.W', 'fc.W') self.connect('fan.power', 'pwr_balance.lhs:W') newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-12 newton.options['rtol'] = 1e-12 newton.options['iprint'] = 2 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 # newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' # newton.linesearch.options['print_bound_enforce'] = True # newton.linesearch.options['iprint'] = -1 # self.linear_solver = DirectSolver(assemble_jac=True)
def setUp(self): self.prob = Problem() des_vars = self.prob.model.add_subsystem('des_vars', IndepVarComp(), promotes=['*']) des_vars.add_output('P', 17., units='psi') des_vars.add_output('T', 500., units='degR') des_vars.add_output('W', 0., units='lbm/s') des_vars.add_output('Nmech', 0., units='rpm') des_vars.add_output('area_targ', 50., units='inch**2') des_vars.add_output('s_PR', val=1.) des_vars.add_output('s_eff', val=1.) des_vars.add_output('s_Wc', val=1.) des_vars.add_output('s_Nc', val=1.) des_vars.add_output('alphaMap', val=0.) self.prob.model.connect("P", "flow_start.P") self.prob.model.connect("T", "flow_start.T") self.prob.model.connect("W", "flow_start.W") self.prob.model.connect("Nmech", "compressor.Nmech") self.prob.model.connect("area_targ", "compressor.area") self.prob.model.connect("s_PR", "compressor.s_PR") self.prob.model.connect("s_eff", "compressor.s_eff") self.prob.model.connect("s_Wc", "compressor.s_Wc") self.prob.model.connect("s_Nc", "compressor.s_Nc") self.prob.model.connect('alphaMap', 'compressor.map.alphaMap') self.prob.model.add_subsystem( 'flow_start', FlowStart(thermo_data=janaf, elements=AIR_MIX)) self.prob.model.add_subsystem( 'compressor', Compressor(map_data=AXI5, design=False, elements=AIR_MIX, map_extrap=False)) connect_flow(self.prob.model, "flow_start.Fl_O", "compressor.Fl_I") newton = self.prob.model.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-8 newton.options['rtol'] = 1e-8 newton.options['iprint'] = 2 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.prob.model.linear_solver = DirectSolver(assemble_jac=True) self.prob.set_solver_print(level=-1) self.prob.setup(check=False)
def run_tbm_analysis(): # Set up OpenMDAO to analyze the airplane num_nodes = 11 prob = Problem() prob.model = TBMAnalysisGroup() prob.model.nonlinear_solver = NewtonSolver(iprint=2) prob.model.options['assembled_jac_type'] = 'csc' prob.model.linear_solver = DirectSolver(assemble_jac=True) prob.model.nonlinear_solver.options['solve_subsystems'] = True prob.model.nonlinear_solver.options['maxiter'] = 10 prob.model.nonlinear_solver.options['atol'] = 1e-6 prob.model.nonlinear_solver.options['rtol'] = 1e-6 prob.model.nonlinear_solver.linesearch = BoundsEnforceLS( bound_enforcement='scalar', print_bound_enforce=False) prob.setup(check=True, mode='fwd') # set some (required) mission parameters. Each pahse needs a vertical and air-speed # the entire mission needs a cruise altitude and range prob.set_val('climb.fltcond|vs', np.ones((num_nodes, )) * 1500, units='ft/min') prob.set_val('climb.fltcond|Ueas', np.ones((num_nodes, )) * 124, units='kn') prob.set_val('cruise.fltcond|vs', np.ones((num_nodes, )) * 0.01, units='ft/min') prob.set_val('cruise.fltcond|Ueas', np.ones((num_nodes, )) * 201, units='kn') prob.set_val('descent.fltcond|vs', np.ones((num_nodes, )) * (-600), units='ft/min') prob.set_val('descent.fltcond|Ueas', np.ones((num_nodes, )) * 140, units='kn') prob.set_val('cruise|h0', 28000., units='ft') prob.set_val('mission_range', 1250, units='NM') # (optional) guesses for takeoff speeds may help with convergence prob.set_val('v0v1.fltcond|Utrue', np.ones((num_nodes)) * 50, units='kn') prob.set_val('v1vr.fltcond|Utrue', np.ones((num_nodes)) * 85, units='kn') prob.set_val('v1v0.fltcond|Utrue', np.ones((num_nodes)) * 85, units='kn') # set some airplane-specific values. The throttle edits are to derate the takeoff power of the PT6A prob['climb.OEW.structural_fudge'] = 1.67 prob['v0v1.throttle'] = np.ones((num_nodes)) / 1.21 prob['v1vr.throttle'] = np.ones((num_nodes)) / 1.21 prob['rotate.throttle'] = np.ones((num_nodes)) / 1.21 prob.run_model() return prob
def configure_problem(): prob = Problem() prob.model = ElectricTwinAnalysisGroup() prob.model.nonlinear_solver = NewtonSolver(iprint=2) prob.model.options['assembled_jac_type'] = 'csc' prob.model.linear_solver = DirectSolver(assemble_jac=True) prob.model.nonlinear_solver.options['solve_subsystems'] = True prob.model.nonlinear_solver.options['maxiter'] = 10 prob.model.nonlinear_solver.options['atol'] = 1e-8 prob.model.nonlinear_solver.options['rtol'] = 1e-8 prob.model.nonlinear_solver.linesearch = BoundsEnforceLS() # prob.model.nonlinear_solver.linesearch.options['print_bound_enforce'] = True return prob
def setup(self): thermo_data = self.options['thermo_data'] elements = self.options['elements'] self.add_subsystem('ambient', Ambient(), promotes=('alt', 'dTs')) # inputs conv = self.add_subsystem('conv', Group(), promotes=['*']) conv.add_subsystem('fs', FlowStart(thermo_data=thermo_data, elements=elements), promotes=['Fl_O:*', 'MN', 'W']) balance = conv.add_subsystem('balance', BalanceComp()) balance.add_balance('Tt', val=500.0, lower=1e-4, units='degR', desc='Total temperature', eq_units='degR') balance.add_balance('Pt', val=14.696, lower=1e-4, units='psi', desc='Total pressure', eq_units='psi') # sub.set_order(['fs','balance']) newton = conv.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-10 newton.options['rtol'] = 1e-10 newton.options['maxiter'] = 10 newton.options['iprint'] = -1 newton.options['solve_subsystems'] = True newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 # newton.linesearch.options['solve_subsystems'] = True conv.linear_solver = DirectSolver(assemble_jac=True) self.connect('ambient.Ps', 'balance.rhs:Pt') self.connect('ambient.Ts', 'balance.rhs:Tt') self.connect('balance.Pt', 'fs.P') self.connect('balance.Tt', 'fs.T') self.connect('Fl_O:stat:P', 'balance.lhs:Pt') self.connect('Fl_O:stat:T', 'balance.lhs:Tt')
def setUp(self): thermo = Thermo(janaf, constants.AIR_MIX) self.prob = Problem() self.prob.model.add_subsystem( 'flow_start', FlowStart(thermo_data=janaf, elements=AIR_MIX)) self.prob.model.add_subsystem( 'compressor', Compressor(map_data=AXI5, design=False, elements=AIR_MIX, map_extrap=False)) self.prob.model.set_input_defaults('compressor.s_PR', val=1.) self.prob.model.set_input_defaults('compressor.s_eff', val=1.) self.prob.model.set_input_defaults('compressor.s_Wc', val=1.) self.prob.model.set_input_defaults('compressor.s_Nc', val=1.) self.prob.model.set_input_defaults('compressor.map.alphaMap', val=0.) self.prob.model.set_input_defaults('compressor.Nmech', 0., units='rpm') self.prob.model.set_input_defaults('flow_start.P', 17., units='psi') self.prob.model.set_input_defaults('flow_start.T', 500., units='degR') self.prob.model.set_input_defaults('flow_start.W', 0., units='lbm/s') self.prob.model.set_input_defaults('compressor.area', 50., units='inch**2') connect_flow(self.prob.model, "flow_start.Fl_O", "compressor.Fl_I") newton = self.prob.model.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-8 newton.options['rtol'] = 1e-8 newton.options['iprint'] = 2 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.prob.model.linear_solver = DirectSolver(assemble_jac=True) self.prob.set_solver_print(level=-1) self.prob.setup(check=False)
def setup_solvers(self, phase): """ Add a NewtonSolver to converge continuity errors in the state between steps. Parameters ---------- phase The phase to which this transcription instance applies. Returns ------- """ phase.nonlinear_solver = NewtonSolver() phase.nonlinear_solver.options['iprint'] = -1 phase.nonlinear_solver.options['solve_subsystems'] = True phase.nonlinear_solver.options['err_on_maxiter'] = True phase.nonlinear_solver.linesearch = BoundsEnforceLS()
def setUp(self): self.prob = Problem() cycle = self.prob.model = Cycle() cycle.options['design'] = False cycle.options['thermo_method'] = 'CEA' cycle.options['thermo_data'] = species_data.janaf cycle.add_subsystem('flow_start', FlowStart()) cycle.add_subsystem( 'compressor', Compressor(map_data=AXI5, design=False, map_extrap=False)) cycle.set_input_defaults('compressor.s_PR', val=1.) cycle.set_input_defaults('compressor.s_eff', val=1.) cycle.set_input_defaults('compressor.s_Wc', val=1.) cycle.set_input_defaults('compressor.s_Nc', val=1.) cycle.set_input_defaults('compressor.map.alphaMap', val=0.) cycle.set_input_defaults('compressor.Nmech', 0., units='rpm') cycle.set_input_defaults('flow_start.P', 17., units='psi') cycle.set_input_defaults('flow_start.T', 500., units='degR') cycle.set_input_defaults('flow_start.W', 0., units='lbm/s') cycle.set_input_defaults('compressor.area', 50., units='inch**2') cycle.pyc_connect_flow("flow_start.Fl_O", "compressor.Fl_I") newton = self.prob.model.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-8 newton.options['rtol'] = 1e-8 newton.options['iprint'] = 2 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.prob.model.linear_solver = DirectSolver(assemble_jac=True) self.prob.set_solver_print(level=-1) self.prob.setup(check=False, force_alloc_complex=True)
def configure_problem(): prob = Problem() prob.model = ElectricTBMAnalysisGroup() prob.model.nonlinear_solver = NewtonSolver(iprint=2) prob.model.options['assembled_jac_type'] = 'csc' prob.model.linear_solver = DirectSolver(assemble_jac=True) prob.model.nonlinear_solver.options['solve_subsystems'] = True prob.model.nonlinear_solver.options['maxiter'] = 20 prob.model.nonlinear_solver.options['atol'] = 1e-8 prob.model.nonlinear_solver.options['rtol'] = 1e-8 prob.model.nonlinear_solver.linesearch = BoundsEnforceLS( bound_enforcement='scalar', print_bound_enforce=False) prob.model.add_design_var('mission_range', lower=100, upper=300, scaler=1e-2) prob.model.add_constraint('descent.propmodel.batt1.SOC_final', lower=0.0) prob.model.add_objective('mission_range', scaler=-1.0) prob.driver = ScipyOptimizeDriver() return prob
def test_record_line_search_bounds_enforce(self, m): self.setup_endpoints(m) self.setup_sellar_model() model = self.prob.model model.nonlinear_solver = NewtonSolver() model.linear_solver = ScipyKrylov() model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 4 ls = model.nonlinear_solver.linesearch = BoundsEnforceLS(bound_enforcement='vector') ls.add_recorder(self.recorder) self.prob.setup(check=False) t0, t1 = run_driver(self.prob) self.prob.cleanup() upload(self.filename, self._accepted_token) expected_abs_error = 7.02783609310096e-10 expected_rel_error = 8.078674883382422e-07 expected_solver_output = [ {'name': 'con_cmp1.con1', 'values': [-22.42830237]}, {'name': 'd1.y1', 'values': [25.58830237]}, {'name': 'con_cmp2.con2', 'values': [-11.941511849]}, {'name': 'pz.z', 'values': [5.0, 2.0]}, {'name': 'obj_cmp.obj', 'values': [28.588308165]}, {'name': 'd2.y2', 'values': [12.058488150]}, {'name': 'px.x', 'values': [1.0]} ] solver_iteration = json.loads(self.solver_iterations) self.assertAlmostEqual(solver_iteration['abs_err'], expected_abs_error) self.assertAlmostEqual(solver_iteration['rel_err'], expected_rel_error) self.assertEqual(solver_iteration['solver_residuals'], []) for o in expected_solver_output: self.assert_array_close(o, solver_iteration['solver_output'])
def configure(self): for_statics = self.options['for_statics'] if for_statics and for_statics != 'Ps': self.ceq.nonlinear_solver.options['atol'] = 1e-10 self.ceq.nonlinear_solver.options['rtol'] = 1e-6 # statics need an newton solver to converge the outer loop with Ps newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-10 newton.options['rtol'] = 1e-10 newton.options['maxiter'] = 50 newton.options['iprint'] = 2 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 50 self.options['assembled_jac_type'] = 'dense' newton.linear_solver = DirectSolver(assemble_jac=True) ln_bt = newton.linesearch = BoundsEnforceLS() ln_bt.options['bound_enforcement'] = 'scalar' ln_bt.options['iprint'] = -1
promotes_inputs=['*'], promotes_outputs=['*']) if __name__ == "__main__": num_nodes = 11 prob = Problem() prob.model = KingAirAnalysisGroup() prob.model.nonlinear_solver = NewtonSolver(iprint=2) prob.model.options['assembled_jac_type'] = 'csc' prob.model.linear_solver = DirectSolver(assemble_jac=True) prob.model.nonlinear_solver.options['solve_subsystems'] = True prob.model.nonlinear_solver.options['maxiter'] = 10 prob.model.nonlinear_solver.options['atol'] = 1e-6 prob.model.nonlinear_solver.options['rtol'] = 1e-6 prob.model.nonlinear_solver.linesearch = BoundsEnforceLS( bound_enforcement='scalar', print_bound_enforce=True) prob.setup(check=True, mode='fwd') # set some (required) mission parameters. Each pahse needs a vertical and air-speed # the entire mission needs a cruise altitude and range prob.set_val('climb.fltcond|vs', np.ones((num_nodes, )) * 1500, units='ft/min') prob.set_val('climb.fltcond|Ueas', np.ones((num_nodes, )) * 124, units='kn') prob.set_val('cruise.fltcond|vs', np.ones((num_nodes, )) * 0.01, units='ft/min') prob.set_val('cruise.fltcond|Ueas', np.ones((num_nodes, )) * 170,
def setup(self): thermo_spec = species_data.janaf design = self.options['design'] statics = self.options['statics'] self.add_subsystem( 'fc', FlightConditions(thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem( 'inlet', Inlet(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem( 'duct1', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics)) self.add_subsystem('lpc', Compressor(map_data=LPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics), promotes_inputs=[('Nmech', 'IP_Nmech')]) self.add_subsystem( 'icduct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics)) self.add_subsystem('hpc_axi', Compressor(map_data=HPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem( 'bld25', BleedOut(design=design, statics=statics, bleed_names=['cool1', 'cool2'])) self.add_subsystem('hpc_centri', Compressor(map_data=HPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem( 'bld3', BleedOut(design=design, statics=statics, bleed_names=['cool3', 'cool4'])) self.add_subsystem( 'duct6', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics)) self.add_subsystem( 'burner', Combustor(design=design, thermo_data=thermo_spec, inflow_elements=AIR_MIX, air_fuel_elements=AIR_FUEL_MIX, fuel_type='Jet-A(g)', statics=statics)) self.add_subsystem('hpt', Turbine(map_data=HPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, bleed_names=['cool3', 'cool4'], statics=statics), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem( 'duct43', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, statics=statics)) self.add_subsystem('lpt', Turbine(map_data=LPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, bleed_names=['cool1', 'cool2'], statics=statics), promotes_inputs=[('Nmech', 'IP_Nmech')]) self.add_subsystem( 'itduct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, statics=statics)) self.add_subsystem('pt', Turbine(map_data=LPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, statics=statics), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.add_subsystem( 'duct12', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, statics=statics)) self.add_subsystem( 'nozzle', Nozzle(nozzType='CV', lossCoef='Cv', thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) self.add_subsystem('lp_shaft', Shaft(num_ports=1), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.add_subsystem('ip_shaft', Shaft(num_ports=2), promotes_inputs=[('Nmech', 'IP_Nmech')]) self.add_subsystem('hp_shaft', Shaft(num_ports=3), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem('perf', Performance(num_nozzles=1, num_burners=1)) self.connect('duct1.Fl_O:tot:P', 'perf.Pt2') self.connect('hpc_centri.Fl_O:tot:P', 'perf.Pt3') self.connect('burner.Wfuel', 'perf.Wfuel_0') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('nozzle.Fg', 'perf.Fg_0') self.connect('lp_shaft.pwr_in', 'perf.power') self.connect('pt.trq', 'lp_shaft.trq_0') self.connect('lpc.trq', 'ip_shaft.trq_0') self.connect('lpt.trq', 'ip_shaft.trq_1') self.connect('hpc_axi.trq', 'hp_shaft.trq_0') self.connect('hpc_centri.trq', 'hp_shaft.trq_1') self.connect('hpt.trq', 'hp_shaft.trq_2') self.connect('fc.Fl_O:stat:P', 'nozzle.Ps_exhaust') balance = self.add_subsystem('balance', BalanceComp()) if design: balance.add_balance('W', units='lbm/s', eq_units=None) self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('nozzle.PR', 'balance.lhs:W') balance.add_balance('FAR', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR') balance.add_balance('lpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.lpt_PR', 'lpt.PR') self.connect('ip_shaft.pwr_net', 'balance.lhs:lpt_PR') balance.add_balance('hpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.hpt_PR', 'hpt.PR') self.connect('hp_shaft.pwr_net', 'balance.lhs:hpt_PR') balance.add_balance('pt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.pt_PR', 'pt.PR') self.connect('lp_shaft.pwr_net', 'balance.lhs:pt_PR') # self.set_order(['fc', 'inlet', 'duct1', 'comp', 'burner', 'turb', 'ab', 'nozz', 'shaft', 'perf', 'thrust_balance', 'temp_balance', 'shaft_balance']) # self.set_order(['balance', 'fc', 'inlet', 'duct1', 'comp', 'burner', 'turb', 'ab', 'nozz', 'shaft', 'perf']) else: # Need to check all these balances once power turbine map is updated balance.add_balance('FAR', eq_units='lbf', lower=1e-4, val=.017) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('perf.Fn', 'balance.lhs:FAR') balance.add_balance('W', units='lbm/s', eq_units=None) self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('nozzle.Throat:stat:area', 'balance.lhs:W') balance.add_balance('IP_Nmech', val=12000.0, units='rpm', lower=1.001, eq_units='hp', rhs_val=0.) self.connect('balance.IP_Nmech', 'IP_Nmech') self.connect('ip_shaft.pwr_net', 'balance.lhs:IP_Nmech') balance.add_balance('HP_Nmech', val=14800.0, units='rpm', lower=1.001, eq_units='hp', rhs_val=0.) self.connect('balance.HP_Nmech', 'HP_Nmech') self.connect('hp_shaft.pwr_net', 'balance.lhs:HP_Nmech') balance.add_balance('LP_Nmech', val=1800.0, units='rpm', lower=1.001, eq_units='hp', rhs_val=0.) self.connect('balance.LP_Nmech', 'LP_Nmech') self.connect('lp_shaft.pwr_net', 'balance.lhs:LP_Nmech') # self.set_order(['balance', 'fc', 'inlet', 'fan', 'splitter', 'duct4', 'lpc', 'duct6', 'hpc', 'bld3', 'burner', 'hpt', 'duct11', # 'lpt', 'duct13', 'nozzle', 'byp_bld', 'duct15', 'lp_shaft', 'ip_shaft', 'hp_shaft', 'perf']) if statics: connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I', connect_w=False) connect_flow(self, 'inlet.Fl_O', 'duct1.Fl_I') connect_flow(self, 'duct1.Fl_O', 'lpc.Fl_I') connect_flow(self, 'lpc.Fl_O', 'icduct.Fl_I') connect_flow(self, 'icduct.Fl_O', 'hpc_axi.Fl_I') connect_flow(self, 'hpc_axi.Fl_O', 'bld25.Fl_I') connect_flow(self, 'bld25.Fl_O', 'hpc_centri.Fl_I') connect_flow(self, 'hpc_centri.Fl_O', 'bld3.Fl_I') connect_flow(self, 'bld3.Fl_O', 'duct6.Fl_I') connect_flow(self, 'duct6.Fl_O', 'burner.Fl_I') connect_flow(self, 'burner.Fl_O', 'hpt.Fl_I') connect_flow(self, 'hpt.Fl_O', 'duct43.Fl_I') connect_flow(self, 'duct43.Fl_O', 'lpt.Fl_I') connect_flow(self, 'lpt.Fl_O', 'itduct.Fl_I') connect_flow(self, 'itduct.Fl_O', 'pt.Fl_I') connect_flow(self, 'pt.Fl_O', 'duct12.Fl_I') connect_flow(self, 'duct12.Fl_O', 'nozzle.Fl_I') else: connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I', connect_w=False) connect_flow(self, 'inlet.Fl_O', 'duct1.Fl_I', connect_stat=False) connect_flow(self, 'duct1.Fl_O', 'lpc.Fl_I', connect_stat=False) connect_flow(self, 'lpc.Fl_O', 'icduct.Fl_I', connect_stat=False) connect_flow(self, 'icduct.Fl_O', 'hpc_axi.Fl_I', connect_stat=False) connect_flow(self, 'hpc_axi.Fl_O', 'bld25.Fl_I', connect_stat=False) connect_flow(self, 'bld25.Fl_O', 'hpc_centri.Fl_I', connect_stat=False) connect_flow(self, 'hpc_centri.Fl_O', 'bld3.Fl_I', connect_stat=False) connect_flow(self, 'bld3.Fl_O', 'duct6.Fl_I', connect_stat=False) connect_flow(self, 'duct6.Fl_O', 'burner.Fl_I', connect_stat=False) connect_flow(self, 'burner.Fl_O', 'hpt.Fl_I', connect_stat=False) connect_flow(self, 'hpt.Fl_O', 'duct43.Fl_I', connect_stat=False) connect_flow(self, 'duct43.Fl_O', 'lpt.Fl_I', connect_stat=False) connect_flow(self, 'lpt.Fl_O', 'itduct.Fl_I', connect_stat=False) connect_flow(self, 'itduct.Fl_O', 'pt.Fl_I', connect_stat=False) connect_flow(self, 'pt.Fl_O', 'duct12.Fl_I', connect_stat=False) connect_flow(self, 'duct12.Fl_O', 'nozzle.Fl_I', connect_stat=False) connect_flow(self, 'bld25.cool1', 'lpt.cool1', connect_stat=False) connect_flow(self, 'bld25.cool2', 'lpt.cool2', connect_stat=False) connect_flow(self, 'bld3.cool3', 'hpt.cool3', connect_stat=False) connect_flow(self, 'bld3.cool4', 'hpt.cool4', connect_stat=False) newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-6 newton.options['rtol'] = 1e-6 newton.options['iprint'] = 2 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 100 newton.linesearch = BoundsEnforceLS() # newton.linesearch = ArmijoGoldsteinLS() # newton.linesearch.options['c'] = .0001 newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = DirectSolver(assemble_jac=True)
def setup(self): thermo_data = self.options['thermo_data'] elements = self.options['elements'] nozzType = self.options['nozzType'] lossCoef = self.options['lossCoef'] gas_thermo = species_data.Thermo(thermo_data, init_reacts=elements) self.gas_prods = gas_thermo.products num_prod = len(self.gas_prods) self.add_subsystem('mach_choked', IndepVarComp( 'MN', 1.000, )) # Create inlet flow station in_flow = FlowIn(fl_name="Fl_I", num_prods=num_prod) self.add_subsystem('in_flow', in_flow, promotes_inputs=['Fl_I:*']) # PR_bal = self.add_subsystem('PR_bal', BalanceComp()) # PR_bal.add_balance('PR', units=None, eq_units='lbf/inch**2', lower=1.001) # self.connect('PR_bal.PR', 'PR') # self.connect('Ps_exhaust', 'PR_bal.lhs:PR') # self.connect('Ps_calc', 'PR_bal.rhs:PR') self.add_subsystem('PR_bal', PR_bal(), promotes_inputs=['*'], promotes_outputs=['*']) # Calculate pressure at the throat prom_in = [('Pt_in', 'Fl_I:tot:P'), 'PR', 'dPqP'] self.add_subsystem('press_calcs', PressureCalcs(), promotes_inputs=prom_in, promotes_outputs=['Ps_calc']) # Calculate throat total flow properties throat_total = SetTotal(thermo_data=thermo_data, mode="h", init_reacts=elements, fl_name="Fl_O:tot") prom_in = [('h', 'Fl_I:tot:h'), ('init_prod_amounts', 'Fl_I:tot:n')] self.add_subsystem('throat_total', throat_total, promotes_inputs=prom_in, promotes_outputs=['Fl_O:*']) self.connect('press_calcs.Pt_th', 'throat_total.P') # Calculate static properties for sonic flow prom_in = [('ht', 'Fl_I:tot:h'), ('W', 'Fl_I:stat:W'), ('init_prod_amounts', 'Fl_I:tot:n')] self.add_subsystem('staticMN', SetStatic(mode="MN", thermo_data=thermo_data, init_reacts=elements), promotes_inputs=prom_in) self.connect('throat_total.S', 'staticMN.S') self.connect('mach_choked.MN', 'staticMN.MN') self.connect('press_calcs.Pt_th', 'staticMN.guess:Pt') self.connect('throat_total.gamma', 'staticMN.guess:gamt') # self.connect('Fl_I.flow:flow_products','staticMN.init_prod_amounts') # Calculate static properties based on exit static pressure prom_in = [('ht', 'Fl_I:tot:h'), ('W', 'Fl_I:stat:W'), ('Ps', 'Ps_calc'), ('init_prod_amounts', 'Fl_I:tot:n')] self.add_subsystem('staticPs', SetStatic(mode="Ps", thermo_data=thermo_data, init_reacts=elements), promotes_inputs=prom_in) self.connect('throat_total.S', 'staticPs.S') # self.connect('press_calcs.Ps_calc', 'staticPs.Ps') # self.connect('Fl_I.flow:flow_products','staticPs.init_prod_amounts') # Calculate ideal exit flow properties prom_in = [('ht', 'Fl_I:tot:h'), ('S', 'Fl_I:tot:S'), ('W', 'Fl_I:stat:W'), ('Ps', 'Ps_calc'), ('init_prod_amounts', 'Fl_I:tot:n')] self.add_subsystem('ideal_flow', SetStatic(mode="Ps", thermo_data=thermo_data, init_reacts=elements), promotes_inputs=prom_in) # self.connect('press_calcs.Ps_calc', 'ideal_flow.Ps') # self.connect('Fl_I.flow:flow_products','ideal_flow.init_prod_amounts') # Determine throat and exit flow properties based on nozzle type and exit static pressure mux = Mux(nozzType=nozzType, fl_out_name='Fl_O') prom_in = [('Ps:W', 'Fl_I:stat:W'), ('MN:W', 'Fl_I:stat:W'), ('Ps:P', 'Ps_calc'), 'Ps_calc'] self.add_subsystem('mux', mux, promotes_inputs=prom_in, promotes_outputs=['*:stat:*']) self.connect('throat_total.S', 'mux.S') self.connect('staticPs.h', 'mux.Ps:h') self.connect('staticPs.T', 'mux.Ps:T') self.connect('staticPs.rho', 'mux.Ps:rho') self.connect('staticPs.gamma', 'mux.Ps:gamma') self.connect('staticPs.Cp', 'mux.Ps:Cp') self.connect('staticPs.Cv', 'mux.Ps:Cv') self.connect('staticPs.V', 'mux.Ps:V') self.connect('staticPs.Vsonic', 'mux.Ps:Vsonic') self.connect('staticPs.MN', 'mux.Ps:MN') self.connect('staticPs.area', 'mux.Ps:area') self.connect('staticMN.h', 'mux.MN:h') self.connect('staticMN.T', 'mux.MN:T') self.connect('staticMN.Ps', 'mux.MN:P') self.connect('staticMN.rho', 'mux.MN:rho') self.connect('staticMN.gamma', 'mux.MN:gamma') self.connect('staticMN.Cp', 'mux.MN:Cp') self.connect('staticMN.Cv', 'mux.MN:Cv') self.connect('staticMN.V', 'mux.MN:V') self.connect('staticMN.Vsonic', 'mux.MN:Vsonic') self.connect('mach_choked.MN', 'mux.MN:MN') self.connect('staticMN.area', 'mux.MN:area') # Calculate nozzle performance paramters based on perf_calcs = PerformanceCalcs(lossCoef=lossCoef) if lossCoef == "Cv": other_inputs = ['Cv', 'Ps_calc'] else: other_inputs = ['Cfg', 'Ps_calc'] prom_in = [('W_in', 'Fl_I:stat:W')] + other_inputs self.add_subsystem('perf_calcs', perf_calcs, promotes_inputs=prom_in, promotes_outputs=['Fg']) self.connect('ideal_flow.V', 'perf_calcs.V_ideal') # self.connect('ideal_flow.area', 'perf_calcs.A_ideal') if lossCoef == 'Cv': self.connect('Fl_O:stat:V', 'perf_calcs.V_actual') self.connect('Fl_O:stat:area', 'perf_calcs.A_actual') self.connect('Fl_O:stat:P', 'perf_calcs.Ps_actual') if self.options['internal_solver']: newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-10 newton.options['rtol'] = 1e-10 newton.options['maxiter'] = 20 newton.options['iprint'] = 2 newton.options['solve_subsystems'] = True newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = DirectSolver(assemble_jac=True)
def setup(self): thermo_spec = species_data.janaf design = self.options['design'] ########################################## # Elements ########################################## self.add_subsystem( 'fc', FlightConditions(thermo_data=thermo_spec, elements=AIR_MIX)) # Inlet Components self.add_subsystem('mil_spec', MilSpecRecovery()) self.add_subsystem( 'inlet', Inlet(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) # Fan Components - Split here for CFD integration Add a CFDStart Compomponent self.add_subsystem('fan', Compressor(map_data=AXI5, design=design, thermo_data=thermo_spec, elements=AIR_MIX, map_extrap=True), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.add_subsystem( 'splitter', Splitter(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) # Bypass Stream ################### self.add_subsystem( 'bypass_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) # Core Stream ############## self.add_subsystem( 'ic_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('hpc', Compressor(map_data=HPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, bleed_names=['cool1', 'cool2'], map_extrap=True), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem( 'duct_3', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('bleed_3', BleedOut(design=design, bleed_names=['cust_bleed'])) self.add_subsystem( 'burner', Combustor(design=design, thermo_data=thermo_spec, inflow_elements=AIR_MIX, air_fuel_elements=AIR_FUEL_MIX, fuel_type='Jet-A(g)')) self.add_subsystem('hpt', Turbine(map_data=HPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, bleed_names=['chargable', 'non_chargable'], map_extrap=True), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem( 'it_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) # uncooled lpt self.add_subsystem('lpt', Turbine(map_data=LPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, map_extrap=True), promotes_inputs=[('Nmech', 'LP_Nmech')]) if not design: self.add_subsystem( 'vabi', ExecComp('Fl1_area = Fl1_area_des*fact', Fl1_area_des={'units': 'inch**2'}, Fl1_area={ 'units': 'inch**2', 'value': 164. })) self.add_subsystem( 'mixer', Mixer(design=design, designed_stream=1, Fl_I1_elements=AIR_FUEL_MIX, Fl_I2_elements=AIR_MIX)) # augmentor Components self.add_subsystem( 'augmentor', Combustor(design=design, thermo_data=thermo_spec, inflow_elements=AIR_FUEL_MIX, air_fuel_elements=AIR_FUEL_MIX, fuel_type='Jet-A(g)')) # End CFD HERE # Nozzle self.add_subsystem( 'nozzle', Nozzle(nozzType='CD', lossCoef='Cfg', thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) # Mechanical components self.add_subsystem('lp_shaft', Shaft(num_ports=2), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.add_subsystem('hp_shaft', Shaft(num_ports=2), promotes_inputs=[('Nmech', 'HP_Nmech')]) # Aggregating component self.add_subsystem('perf', Performance(num_nozzles=1, num_burners=2)) ########################################## # Connecting the Flow Path ########################################## connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I') connect_flow(self, 'inlet.Fl_O', 'fan.Fl_I') connect_flow(self, 'fan.Fl_O', 'splitter.Fl_I') # Bypass Connections connect_flow(self, 'splitter.Fl_O2', 'bypass_duct.Fl_I') connect_flow(self, 'bypass_duct.Fl_O', 'mixer.Fl_I2') # Core connections connect_flow(self, 'splitter.Fl_O1', 'ic_duct.Fl_I') connect_flow(self, 'ic_duct.Fl_O', 'hpc.Fl_I') connect_flow(self, 'hpc.Fl_O', 'duct_3.Fl_I') connect_flow(self, 'duct_3.Fl_O', 'bleed_3.Fl_I') connect_flow(self, 'bleed_3.Fl_O', 'burner.Fl_I') connect_flow(self, 'burner.Fl_O', 'hpt.Fl_I') connect_flow(self, 'hpt.Fl_O', 'it_duct.Fl_I') connect_flow(self, 'it_duct.Fl_O', 'lpt.Fl_I') connect_flow(self, 'lpt.Fl_O', 'mixer.Fl_I1') connect_flow(self, 'mixer.Fl_O', 'augmentor.Fl_I') connect_flow(self, 'augmentor.Fl_O', 'nozzle.Fl_I') # Connect cooling flows connect_flow(self, 'hpc.cool1', 'hpt.non_chargable', connect_stat=False) connect_flow(self, 'hpc.cool2', 'hpt.chargable', connect_stat=False) ########################################## # Additional Connections ########################################## # Make additional model connections self.connect('fc.Fl_O:stat:MN', 'mil_spec.MN') self.connect('mil_spec.ram_recovery', 'inlet.ram_recovery') self.connect('inlet.Fl_O:tot:P', 'perf.Pt2') self.connect('hpc.Fl_O:tot:P', 'perf.Pt3') self.connect('burner.Wfuel', 'perf.Wfuel_0') self.connect('augmentor.Wfuel', 'perf.Wfuel_1') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('nozzle.Fg', 'perf.Fg_0') self.connect('fan.trq', 'lp_shaft.trq_0') self.connect('lpt.trq', 'lp_shaft.trq_1') self.connect('hpc.trq', 'hp_shaft.trq_0') self.connect('hpt.trq', 'hp_shaft.trq_1') self.connect('fc.Fl_O:stat:P', 'nozzle.Ps_exhaust') if not design: self.connect('vabi.Fl1_area', 'mixer.Fl_I1_stat_calc.area') ########################################## # Balances to define cycle convergence ########################################## balance = self.add_subsystem('balance', BalanceComp()) if design: balance.add_balance('W', lower=1e-3, upper=200., units='lbm/s', eq_units='lbf') self.connect('balance.W', 'fc.fs.W') self.connect('perf.Fn', 'balance.lhs:W') # self.add_subsystem('wDV',IndepVarComp('wDes',100,units='lbm/s')) # self.connect('wDV.wDes','fc.fs.W') balance.add_balance('BPR', eq_units=None, lower=0.25, val=5.0) self.connect('balance.BPR', 'splitter.BPR') self.connect('mixer.ER', 'balance.lhs:BPR') balance.add_balance('FAR_core', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR_core', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR_core') balance.add_balance('FAR_ab', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR_ab', 'augmentor.Fl_I:FAR') self.connect('augmentor.Fl_O:tot:T', 'balance.lhs:FAR_ab') balance.add_balance('lpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.lpt_PR', 'lpt.PR') self.connect('lp_shaft.pwr_in', 'balance.lhs:lpt_PR') self.connect('lp_shaft.pwr_out', 'balance.rhs:lpt_PR') balance.add_balance('hpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.hpt_PR', 'hpt.PR') self.connect('hp_shaft.pwr_in', 'balance.lhs:hpt_PR') self.connect('hp_shaft.pwr_out', 'balance.rhs:hpt_PR') else: # off design balance.add_balance('W', lower=1e-3, upper=400., units='lbm/s', eq_units='inch**2') self.connect('balance.W', 'fc.fs.W') self.connect('nozzle.Throat:stat:area', 'balance.lhs:W') balance.add_balance('BPR', lower=0.25, upper=3.0, eq_units='psi') self.connect('balance.BPR', 'splitter.BPR') self.connect('mixer.Fl_I1_calc:stat:P', 'balance.lhs:BPR') self.connect('bypass_duct.Fl_O:stat:P', 'balance.rhs:BPR') # balance.add_balance('FAR_core', eq_units='degR', lower=1e-4, upper=.045, val=.017) # self.connect('balance.FAR_core', 'burner.Fl_I:FAR') # self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR_core') balance.add_balance('FAR_ab', eq_units='degR', lower=1e-4, upper=.045, val=.017) self.connect('balance.FAR_ab', 'augmentor.Fl_I:FAR') self.connect('augmentor.Fl_O:tot:T', 'balance.lhs:FAR_ab') far_core_bal = ConstrainedTempBalance() self.add_subsystem('far_core_bal', far_core_bal) self.connect('far_core_bal.FAR', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'far_core_bal.T_computed') self.connect('fan.map.shaftNc.NcMap', 'far_core_bal.Nc_computed') balance.add_balance('LP_Nmech', val=1., units='rpm', lower=0.5, upper=2., eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.LP_Nmech', 'LP_Nmech') self.connect('lp_shaft.pwr_in', 'balance.lhs:LP_Nmech') self.connect('lp_shaft.pwr_out', 'balance.rhs:LP_Nmech') balance.add_balance('HP_Nmech', val=1., units='rpm', lower=0.5, upper=2., eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.HP_Nmech', 'HP_Nmech') self.connect('hp_shaft.pwr_in', 'balance.lhs:HP_Nmech') self.connect('hp_shaft.pwr_out', 'balance.rhs:HP_Nmech') newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-6 newton.options['rtol'] = 1e-10 newton.options['iprint'] = 2 newton.options['maxiter'] = 30 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' # newton.linesearch.options['print_bound_enforce'] = True newton.linesearch.options['iprint'] = -1 # newton.linesearch = ArmijoGoldsteinLS() # newton.linesearch.options['c'] = -.1 self.linear_solver = DirectSolver(assemble_jac=True) # TODO: re-factor pycycle so this block isn't needed in default use case!!! if design: ########################################## # Model Parameters ########################################## element_params = self.add_subsystem('element_params', IndepVarComp(), promotes=["*"]) # element_params.add_output('inlet:ram_recovery', 0.9990) element_params.add_output('inlet:MN_out', 0.65) # self.connect('inlet:ram_recovery', 'inlet.ram_recovery') self.connect('inlet:MN_out', 'inlet.MN') element_params.add_output('fan:effDes', 0.8700) element_params.add_output('fan:MN_out', 0.4578) self.connect('fan:effDes', 'fan.map.effDes') self.connect('fan:MN_out', 'fan.MN') element_params.add_output('splitter:MN_out1', 0.3104) element_params.add_output('splitter:MN_out2', 0.4518) self.connect('splitter:MN_out1', 'splitter.MN1') self.connect('splitter:MN_out2', 'splitter.MN2') element_params.add_output('ic_duct:dPqP', 0.0048) element_params.add_output('ic_duct:MN_out', 0.3121) self.connect('ic_duct:dPqP', 'ic_duct.dPqP') self.connect('ic_duct:MN_out', 'ic_duct.MN') element_params.add_output('hpc:effDes', 0.8707) element_params.add_output('hpc:MN_out', 0.2442) self.connect('hpc:effDes', 'hpc.map.effDes') self.connect('hpc:MN_out', 'hpc.MN') element_params.add_output('hpc:cool1:frac_W', 0.09) element_params.add_output('hpc:cool1:frac_P', 1.0) element_params.add_output('hpc:cool1:frac_work', 1.0) self.connect('hpc:cool1:frac_W', 'hpc.cool1:frac_W') self.connect('hpc:cool1:frac_P', 'hpc.cool1:frac_P') self.connect('hpc:cool1:frac_work', 'hpc.cool1:frac_work') element_params.add_output('hpc:cool2:frac_W', 0.07) element_params.add_output('hpc:cool2:frac_P', 0.5) element_params.add_output('hpc:cool2:frac_work', 0.5) self.connect('hpc:cool2:frac_W', 'hpc.cool2:frac_W') self.connect('hpc:cool2:frac_P', 'hpc.cool2:frac_P') self.connect('hpc:cool2:frac_work', 'hpc.cool2:frac_work') element_params.add_output('duct_3:dPqP', 0.0048) element_params.add_output('duct_3:MN_out', 0.2) self.connect('duct_3:dPqP', 'duct_3.dPqP') self.connect('duct_3:MN_out', 'duct_3.MN') element_params.add_output('bleed_3:MN_out', 0.3000) element_params.add_output('bleed_3:cust_bleed:frac_W', 0.07) self.connect('bleed_3:MN_out', 'bleed_3.MN') self.connect('bleed_3:cust_bleed:frac_W', 'bleed_3.cust_bleed:frac_W') element_params.add_output('burner:dPqP', 0.0540) element_params.add_output('burner:MN_out', 0.1025) self.connect('burner:dPqP', 'burner.dPqP') self.connect('burner:MN_out', 'burner.MN') element_params.add_output('hpt:effDes', 0.8888) element_params.add_output('hpt:MN_out', 0.3650) element_params.add_output('hpt:chargable:frac_P', 0.0) element_params.add_output('hpt:non_chargable:frac_P', 1.0) self.connect('hpt:effDes', 'hpt.map.effDes') self.connect('hpt:MN_out', 'hpt.MN') self.connect('hpt:chargable:frac_P', 'hpt.chargable:frac_P') self.connect('hpt:non_chargable:frac_P', 'hpt.non_chargable:frac_P') element_params.add_output('it_duct:dPqP', 0.0051) element_params.add_output('it_duct:MN_out', 0.3063) self.connect('it_duct:dPqP', 'it_duct.dPqP') self.connect('it_duct:MN_out', 'it_duct.MN') element_params.add_output('lpt:effDes', 0.8996) element_params.add_output('lpt:MN_out', 0.4127) self.connect('lpt:effDes', 'lpt.map.effDes') self.connect('lpt:MN_out', 'lpt.MN') element_params.add_output('bypass_duct:dPqP', 0.0107) element_params.add_output('bypass_duct:MN_out', 0.4463) self.connect('bypass_duct:dPqP', 'bypass_duct.dPqP') self.connect('bypass_duct:MN_out', 'bypass_duct.MN') # No params for mixer element_params.add_output('augmentor:dPqP', 0.0540) element_params.add_output('augmentor:MN_out', 0.1025) self.connect('augmentor:dPqP', 'augmentor.dPqP') self.connect('augmentor:MN_out', 'augmentor.MN') element_params.add_output('nozzle:Cfg', 0.9933) self.connect('nozzle:Cfg', 'nozzle.Cfg') element_params.add_output('lp_shaft:Nmech', 1, units='rpm') element_params.add_output('hp_shaft:Nmech', 1, units='rpm') element_params.add_output('hp_shaft:HPX', 250.0, units='hp') self.connect('lp_shaft:Nmech', 'LP_Nmech') self.connect('hp_shaft:Nmech', 'HP_Nmech') self.connect('hp_shaft:HPX', 'hp_shaft.HPX')
def setup(self): design = self.options['design'] thermo_data = self.options['thermo_data'] flow1_elements = self.options['Fl_I1_elements'] flow1_thermo = Thermo(thermo_data, init_reacts=flow1_elements) n_flow1_prods = len(flow1_thermo.products) in_flow = FlowIn(fl_name='Fl_I1', num_prods=n_flow1_prods) self.add_subsystem('in_flow1', in_flow, promotes=['Fl_I1:*']) flow2_elements = self.options['Fl_I2_elements'] flow2_thermo = Thermo(thermo_data, init_reacts=flow2_elements) n_flow2_prods = len(flow2_thermo.products) in_flow = FlowIn(fl_name='Fl_I2', num_prods=n_flow2_prods) self.add_subsystem('in_flow2', in_flow, promotes=['Fl_I2:*']) if design: # internal flow station to compute the area that is needed to match the static pressures if self.options['designed_stream'] == 1: Fl1_stat = SetStatic(mode="Ps", thermo_data=thermo_data, init_reacts=flow1_elements, fl_name="Fl_I1_calc:stat") self.add_subsystem('Fl_I1_stat_calc', Fl1_stat, promotes_inputs=[('init_prod_amounts', 'Fl_I1:stat:n'), ('S', 'Fl_I1:tot:S'), ('ht', 'Fl_I1:tot:h'), ('W', 'Fl_I1:stat:W'), ('Ps', 'Fl_I2:stat:P')], promotes_outputs=['Fl_I1_calc:stat*']) self.add_subsystem('area_calc', AreaSum(), promotes_inputs=['Fl_I2:stat:area'], promotes_outputs=[('area_sum', 'area')]) self.connect('Fl_I1_calc:stat:area', 'area_calc.Fl_I1:stat:area') else: Fl2_stat = SetStatic(mode="Ps", thermo_data=thermo_data, init_reacts=flow2_elements, fl_name="Fl_I2_calc:stat") self.add_subsystem('Fl_I2_stat_calc', Fl2_stat, promotes_inputs=[('init_prod_amounts', 'Fl_I2:tot:n'), ('S', 'Fl_I2:tot:S'), ('ht', 'Fl_I2:tot:h'), ('W', 'Fl_I2:stat:W'), ('Ps', 'Fl_I1:stat:P')], promotes_outputs=['Fl_I2_calc:stat:*']) self.add_subsystem('area_calc', AreaSum(), promotes_inputs=['Fl_I1:stat:area'], promotes_outputs=[('area_sum', 'area')]) self.connect('Fl_I2_calc:stat:area', 'area_calc.Fl_I2:stat:area') else: if self.options['designed_stream'] == 1: Fl1_stat = SetStatic(mode="area", thermo_data=thermo_data, init_reacts=flow1_elements, fl_name="Fl_I1_calc:stat") self.add_subsystem('Fl_I1_stat_calc', Fl1_stat, promotes_inputs=[ ('init_prod_amounts', 'Fl_I1:tot:n'), ('S', 'Fl_I1:tot:S'), ('ht', 'Fl_I1:tot:h'), ('W', 'Fl_I1:stat:W'), ('guess:Pt', 'Fl_I1:tot:P'), ('guess:gamt', 'Fl_I1:tot:gamma') ], promotes_outputs=['Fl_I1_calc:stat*']) else: Fl2_stat = SetStatic(mode="area", thermo_data=thermo_data, init_reacts=flow2_elements, fl_name="Fl_I2_calc:stat") self.add_subsystem('Fl_I2_stat_calc', Fl2_stat, promotes_inputs=[ ('init_prod_amounts', 'Fl_I2:tot:n'), ('S', 'Fl_I2:tot:S'), ('ht', 'Fl_I2:tot:h'), ('W', 'Fl_I2:stat:W'), ('guess:Pt', 'Fl_I2:tot:P'), ('guess:gamt', 'Fl_I2:tot:gamma') ], promotes_outputs=['Fl_I2_calc:stat*']) self.add_subsystem('extraction_ratio', ExecComp('ER=Pt1/Pt2', Pt1={'units': 'Pa'}, Pt2={'units': 'Pa'}), promotes_inputs=[('Pt1', 'Fl_I1:tot:P'), ('Pt2', 'Fl_I2:tot:P')], promotes_outputs=['ER']) mix_flow = MixFlow(thermo_data=thermo_data, Fl_I1_elements=self.options['Fl_I1_elements'], Fl_I2_elements=self.options['Fl_I2_elements']) if self.options['designed_stream'] == 1: self.add_subsystem('mix_flow', mix_flow, promotes_inputs=[ 'Fl_I1:tot:h', 'Fl_I1:tot:n', ('Fl_I1:stat:W', 'Fl_I1_calc:stat:W'), ('Fl_I1:stat:P', 'Fl_I1_calc:stat:P'), ('Fl_I1:stat:V', 'Fl_I1_calc:stat:V'), ('Fl_I1:stat:area', 'Fl_I1_calc:stat:area'), 'Fl_I2:tot:h', 'Fl_I2:tot:n', 'Fl_I2:stat:W', 'Fl_I2:stat:P', 'Fl_I2:stat:V', 'Fl_I2:stat:area' ]) else: self.add_subsystem('mix_flow', mix_flow, promotes_inputs=[ 'Fl_I1:tot:h', 'Fl_I1:tot:n', 'Fl_I1:stat:W', 'Fl_I1:stat:P', 'Fl_I1:stat:V', 'Fl_I1:stat:area', 'Fl_I2:tot:h', 'Fl_I2:tot:n', ('Fl_I2:stat:W', 'Fl_I2_calc:stat:W'), ('Fl_I2:stat:P', 'Fl_I2_calc:stat:P'), ('Fl_I2:stat:V', 'Fl_I2_calc:stat:V'), ('Fl_I2:stat:area', 'Fl_I2_calc:stat:area') ]) # group to converge for the impulse balance conv = self.add_subsystem('impulse_converge', Group(), promotes=['*']) if self.options['internal_solver']: newton = conv.nonlinear_solver = NewtonSolver() newton.options['maxiter'] = 30 newton.options['atol'] = 1e-2 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 20 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 conv.linear_solver = DirectSolver(assemble_jac=True) out_tot = SetTotal(thermo_data=thermo_data, mode='h', init_reacts=self.options['Fl_I1_elements'], fl_name="Fl_O:tot") conv.add_subsystem('out_tot', out_tot, promotes_outputs=['Fl_O:tot:*']) self.connect('mix_flow.n_mix', 'out_tot.init_prod_amounts') self.connect('mix_flow.ht_mix', 'out_tot.h') # note: gets Pt from the balance comp out_stat = SetStatic(mode="area", thermo_data=thermo_data, init_reacts=self.options['Fl_I1_elements'], fl_name="Fl_O:stat") conv.add_subsystem('out_stat', out_stat, promotes_outputs=['Fl_O:stat:*'], promotes_inputs=[ 'area', ]) self.connect('mix_flow.n_mix', 'out_stat.init_prod_amounts') self.connect('mix_flow.W_mix', 'out_stat.W') conv.connect('Fl_O:tot:S', 'out_stat.S') self.connect('mix_flow.ht_mix', 'out_stat.ht') conv.connect('Fl_O:tot:P', 'out_stat.guess:Pt') conv.connect('Fl_O:tot:gamma', 'out_stat.guess:gamt') conv.add_subsystem('imp_out', Impulse()) conv.connect('Fl_O:stat:P', 'imp_out.P') conv.connect('Fl_O:stat:area', 'imp_out.area') conv.connect('Fl_O:stat:V', 'imp_out.V') conv.connect('Fl_O:stat:W', 'imp_out.W') balance = conv.add_subsystem('balance', BalanceComp()) balance.add_balance('P_tot', val=100, units='psi', eq_units='N', lower=1e-3, upper=10000) conv.connect('balance.P_tot', 'out_tot.P') conv.connect('imp_out.impulse', 'balance.lhs:P_tot') self.connect( 'mix_flow.impulse_mix', 'balance.rhs:P_tot' ) #note that this connection comes from outside the convergence group
def setup(self): thermo_spec = species_data.janaf design = self.options['design'] # Add engine elements self.add_subsystem( 'fc', FlightConditions(thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem( 'inlet', Inlet(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('comp', Compressor( map_data=AXI5, design=design, thermo_data=thermo_spec, elements=AIR_MIX, ), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem( 'burner', Combustor(design=design, thermo_data=thermo_spec, inflow_elements=AIR_MIX, air_fuel_elements=AIR_FUEL_MIX, fuel_type='JP-7')) self.add_subsystem('turb', Turbine( map_data=LPT2269, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, ), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem('pt', Turbine( map_data=LPT2269, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, ), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.add_subsystem( 'nozz', Nozzle(nozzType='CV', lossCoef='Cv', thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) self.add_subsystem('HP_shaft', Shaft(num_ports=2), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem('LP_shaft', Shaft(num_ports=1), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.add_subsystem('perf', Performance(num_nozzles=1, num_burners=1)) # Connect flow stations connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I', connect_w=False) connect_flow(self, 'inlet.Fl_O', 'comp.Fl_I') connect_flow(self, 'comp.Fl_O', 'burner.Fl_I') connect_flow(self, 'burner.Fl_O', 'turb.Fl_I') connect_flow(self, 'turb.Fl_O', 'pt.Fl_I') connect_flow(self, 'pt.Fl_O', 'nozz.Fl_I') # Connect turbomachinery elements to shaft self.connect('comp.trq', 'HP_shaft.trq_0') self.connect('turb.trq', 'HP_shaft.trq_1') self.connect('pt.trq', 'LP_shaft.trq_0') # Connnect nozzle exhaust to freestream static conditions self.connect('fc.Fl_O:stat:P', 'nozz.Ps_exhaust') # Connect outputs to pefromance element self.connect('inlet.Fl_O:tot:P', 'perf.Pt2') self.connect('comp.Fl_O:tot:P', 'perf.Pt3') self.connect('burner.Wfuel', 'perf.Wfuel_0') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('nozz.Fg', 'perf.Fg_0') self.connect('LP_shaft.pwr_net', 'perf.power') # Add balances for design and off-design balance = self.add_subsystem('balance', BalanceComp()) if design: balance.add_balance('W', val=27.0, units='lbm/s', eq_units=None) self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('nozz.PR', 'balance.lhs:W') balance.add_balance('FAR', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR') balance.add_balance('turb_PR', val=3.0, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.turb_PR', 'turb.PR') self.connect('HP_shaft.pwr_net', 'balance.lhs:turb_PR') balance.add_balance('pt_PR', val=3.0, lower=1.001, upper=8, eq_units='hp') self.connect('balance.pt_PR', 'pt.PR') self.connect('LP_shaft.pwr_net', 'balance.lhs:pt_PR') else: balance.add_balance('FAR', eq_units='hp', lower=1e-4, val=.3) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('LP_shaft.pwr_net', 'balance.lhs:FAR') balance.add_balance('HP_Nmech', val=1.5, units='rpm', lower=500., eq_units='hp', rhs_val=0.) self.connect('balance.HP_Nmech', 'HP_Nmech') self.connect('HP_shaft.pwr_net', 'balance.lhs:HP_Nmech') balance.add_balance('W', val=27.0, units='lbm/s', eq_units='inch**2') self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('nozz.Throat:stat:area', 'balance.lhs:W') # Setup solver to converge engine self.set_order([ 'balance', 'fc', 'inlet', 'comp', 'burner', 'turb', 'pt', 'nozz', 'HP_shaft', 'LP_shaft', 'perf' ]) newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-6 newton.options['rtol'] = 1e-6 newton.options['iprint'] = 2 newton.options['maxiter'] = 15 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 100 newton.linesearch = BoundsEnforceLS() # newton.linesearch = ArmijoGoldsteinLS() # newton.linesearch.options['c'] = .0001 newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = DirectSolver(assemble_jac=True)
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')
prob.model.connect('T4_ratio.TOC_T4', 'TOC.balance.rhs:FAR') prob.model.connect('TR', 'T4_ratio.TR') prob.model.set_order(['des_vars', 'T4_ratio', 'TOC', 'RTO', 'SLS', 'CRZ', 'bal']) newton = prob.model.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-6 newton.options['rtol'] = 1e-6 newton.options['iprint'] = 2 newton.options['maxiter'] = 20 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 newton.options['err_on_non_converge'] = True newton.options['reraise_child_analysiserror'] = False # newton.linesearch = ArmijoGoldsteinLS() newton.linesearch = BoundsEnforceLS() # newton.linesearch.options['maxiter'] = 2 newton.linesearch.options['bound_enforcement'] = 'scalar' # newton.linesearch.options['print_bound_enforce'] = True newton.linesearch.options['iprint'] = -1 # newton.linesearch.options['print_bound_enforce'] = False # newton.linesearch.options['alpha'] = 0.5 prob.model.linear_solver = DirectSolver(assemble_jac=True) # prob.model.jacobian = CSCJacobian() # recorder = SqliteRecorder('N3_MDP.sql') # prob.model.linear_solver = PETScKrylov() # prob.model.linear_solver.options['iprint'] = 2 # prob.model.linear_solver.precon = DirectSolver()
def setup(self): thermo_spec = species_data.janaf design = self.options['design'] self.add_subsystem('fc', FlightConditions(thermo_data=thermo_spec, elements=AIR_MIX)) # Inlet Components self.add_subsystem('inlet', Inlet(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('inlet_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) # Fan Components - Split here for CFD integration Add a CFDStart Compomponent self.add_subsystem('fan', Compressor(map_data=AXI5, design=design, thermo_data=thermo_spec, elements=AIR_MIX, map_extrap=True),promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('splitter', Splitter(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) # Core Stream components self.add_subsystem('splitter_core_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('lpc', Compressor(map_data=LPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX,map_extrap=True), promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('lpc_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('hpc', Compressor(map_data=HPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, bleed_names=['cool1'],map_extrap=True),promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('bld3', BleedOut(design=design, bleed_names=['cool3'])) self.add_subsystem('burner', Combustor(design=design,thermo_data=thermo_spec, inflow_elements=AIR_MIX, air_fuel_elements=AIR_FUEL_MIX, fuel_type='Jet-A(g)')) self.add_subsystem('hpt', Turbine(map_data=HPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, bleed_names=['cool3'],map_extrap=True),promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('hpt_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) self.add_subsystem('lpt', Turbine(map_data=LPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, bleed_names=['cool1'],map_extrap=True), promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('lpt_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) # Bypass Components self.add_subsystem('bypass_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) # Mixer component self.add_subsystem('mixer', Mixer(design=design, designed_stream=1, Fl_I1_elements=AIR_FUEL_MIX, Fl_I2_elements=AIR_MIX)) self.add_subsystem('mixer_duct', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) # Afterburner Components self.add_subsystem('afterburner', Combustor(design=design,thermo_data=thermo_spec, inflow_elements=AIR_FUEL_MIX, air_fuel_elements=AIR_FUEL_MIX, fuel_type='Jet-A(g)')) # End CFD HERE # Nozzle self.add_subsystem('mixed_nozz', Nozzle(nozzType='CD', lossCoef='Cfg', thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) # Mechanical components self.add_subsystem('lp_shaft', Shaft(num_ports=3),promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('hp_shaft', Shaft(num_ports=2),promotes_inputs=[('Nmech','HP_Nmech')]) # Aggregating component self.add_subsystem('perf', Performance(num_nozzles=1, num_burners=2)) # Connnect flow path connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I') connect_flow(self, 'inlet.Fl_O', 'inlet_duct.Fl_I') connect_flow(self, 'inlet_duct.Fl_O', 'fan.Fl_I') connect_flow(self, 'fan.Fl_O', 'splitter.Fl_I') # Core connections connect_flow(self, 'splitter.Fl_O1', 'splitter_core_duct.Fl_I') connect_flow(self, 'splitter_core_duct.Fl_O', 'lpc.Fl_I') connect_flow(self, 'lpc.Fl_O', 'lpc_duct.Fl_I') connect_flow(self, 'lpc_duct.Fl_O', 'hpc.Fl_I') connect_flow(self, 'hpc.Fl_O', 'bld3.Fl_I') connect_flow(self, 'bld3.Fl_O', 'burner.Fl_I') connect_flow(self, 'burner.Fl_O', 'hpt.Fl_I') connect_flow(self, 'hpt.Fl_O', 'hpt_duct.Fl_I') connect_flow(self, 'hpt_duct.Fl_O', 'lpt.Fl_I') connect_flow(self, 'lpt.Fl_O', 'lpt_duct.Fl_I') connect_flow(self, 'lpt_duct.Fl_O','mixer.Fl_I1') # Bypass Connections connect_flow(self, 'splitter.Fl_O2', 'bypass_duct.Fl_I') connect_flow(self, 'bypass_duct.Fl_O', 'mixer.Fl_I2') #Mixer Connections connect_flow(self, 'mixer.Fl_O', 'mixer_duct.Fl_I') # After Burner connect_flow(self,'mixer_duct.Fl_O','afterburner.Fl_I') # Nozzle connect_flow(self,'afterburner.Fl_O','mixed_nozz.Fl_I') # Connect cooling flows connect_flow(self, 'hpc.cool1', 'lpt.cool1', connect_stat=False) connect_flow(self, 'bld3.cool3', 'hpt.cool3', connect_stat=False) # Make additional model connections self.connect('inlet.Fl_O:tot:P', 'perf.Pt2') self.connect('hpc.Fl_O:tot:P', 'perf.Pt3') self.connect('burner.Wfuel', 'perf.Wfuel_0') self.connect('afterburner.Wfuel', 'perf.Wfuel_1') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('mixed_nozz.Fg', 'perf.Fg_0') self.connect('fan.trq', 'lp_shaft.trq_0') self.connect('lpc.trq', 'lp_shaft.trq_1') self.connect('lpt.trq', 'lp_shaft.trq_2') self.connect('hpc.trq', 'hp_shaft.trq_0') self.connect('hpt.trq', 'hp_shaft.trq_1') self.connect('fc.Fl_O:stat:P', 'mixed_nozz.Ps_exhaust') # Add balence components to close the implicit components balance = self.add_subsystem('balance', BalanceComp()) if design: balance.add_balance('W', lower=1e-3, upper=200., units='lbm/s', eq_units='lbf') self.connect('balance.W', 'fc.W') self.connect('perf.Fn', 'balance.lhs:W') # self.add_subsystem('wDV',IndepVarComp('wDes',100,units='lbm/s')) # self.connect('wDV.wDes','fc.W') balance.add_balance('BPR', eq_units=None, lower=0.25, val=5.0) self.connect('balance.BPR', 'splitter.BPR') self.connect('mixer.ER', 'balance.lhs:BPR') balance.add_balance('FAR_core', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR_core', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR_core') balance.add_balance('FAR_ab', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR_ab', 'afterburner.Fl_I:FAR') self.connect('afterburner.Fl_O:tot:T', 'balance.lhs:FAR_ab') balance.add_balance('lpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.lpt_PR', 'lpt.PR') self.connect('lp_shaft.pwr_in', 'balance.lhs:lpt_PR') self.connect('lp_shaft.pwr_out', 'balance.rhs:lpt_PR') balance.add_balance('hpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.hpt_PR', 'hpt.PR') self.connect('hp_shaft.pwr_in', 'balance.lhs:hpt_PR') self.connect('hp_shaft.pwr_out', 'balance.rhs:hpt_PR') else: balance.add_balance('W', lower=1e-3, upper=200., units='lbm/s', eq_units='inch**2') self.connect('balance.W', 'fc.W') self.connect('mixed_nozz.Throat:stat:area', 'balance.lhs:W') balance.add_balance('BPR', lower=0.25, upper=5.0, eq_units='psi') self.connect('balance.BPR', 'splitter.BPR') self.connect('mixer.Fl_I1_calc:stat:P', 'balance.lhs:BPR') self.connect('bypass_duct.Fl_O:stat:P', 'balance.rhs:BPR') balance.add_balance('FAR_core', eq_units='degR', lower=1e-4, upper=.06, val=.017) self.connect('balance.FAR_core', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR_core') balance.add_balance('FAR_ab', eq_units='degR', lower=1e-4, upper=.06, val=.017) self.connect('balance.FAR_ab', 'afterburner.Fl_I:FAR') self.connect('afterburner.Fl_O:tot:T', 'balance.lhs:FAR_ab') balance.add_balance('LP_Nmech', val=1., units='rpm', lower=500., eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.LP_Nmech', 'LP_Nmech') self.connect('lp_shaft.pwr_in', 'balance.lhs:LP_Nmech') self.connect('lp_shaft.pwr_out', 'balance.rhs:LP_Nmech') balance.add_balance('HP_Nmech', val=1., units='rpm', lower=500., eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.HP_Nmech', 'HP_Nmech') self.connect('hp_shaft.pwr_in', 'balance.lhs:HP_Nmech') self.connect('hp_shaft.pwr_out', 'balance.rhs:HP_Nmech') # Off design newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-6 newton.options['rtol'] = 1e-10 newton.options['iprint'] = 2 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 100 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = DirectSolver(assemble_jac=True)