def test_linesearch_vector_bound_enforcement(self): top = self.top ls = top.model.nonlinear_solver.linesearch = om.BoundsEnforceLS(bound_enforcement='vector') ls.options['print_bound_enforce'] = True # Setup again because we assigned a new linesearch top.setup() # Test lower bounds: should go to the lower bound and stall top['px.x'] = 2.0 top['comp.y'] = 0. top['comp.z'] = 1.6 top.run_model() for ind in range(3): assert_rel_error(self, top['comp.z'][ind], [1.5], 1e-8) # Test upper bounds: should go to the minimum upper bound and stall top['px.x'] = 0.5 top['comp.y'] = 0. top['comp.z'] = 2.4 stdout = sys.stdout strout = StringIO() sys.stdout = strout try: top.run_model() finally: sys.stdout = stdout txt = strout.getvalue() self.assertTrue("'comp.z' exceeds upper bound" in txt) for ind in range(3): assert_rel_error(self, top['comp.z'][ind], [2.5], 1e-8)
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 = om.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 newton.options['reraise_child_analysiserror'] = False self.options['assembled_jac_type'] = 'dense' newton.linear_solver = om.DirectSolver(assemble_jac=True) ln_bt = newton.linesearch = om.BoundsEnforceLS() ln_bt.options['bound_enforcement'] = 'scalar' ln_bt.options['iprint'] = -1
def test_linesearch_wall_bound_enforcement_wall(self): top = self.top top.model.nonlinear_solver.linesearch = om.BoundsEnforceLS( bound_enforcement='wall') # Setup again because we assigned a new linesearch top.setup() # Test lower bounds: should go to the lower bound and stall top['px.x'] = 2.0 top['comp.y'] = 0. top['comp.z'] = 1.6 top.run_model() for ind in range(3): assert_rel_error(self, top['comp.z'][ind], [1.5], 1e-8) # Test upper bounds: should go to the upper bound and stall top['px.x'] = 0.5 top['comp.y'] = 0. top['comp.z'] = 2.4 top.run_model() for ind in range(3): assert_rel_error(self, top['comp.z'][ind], [self.ub[ind]], 1e-8)
def test_feature_boundscheck_vector(self): top = om.Problem() top.model.add_subsystem('comp', ImplCompTwoStatesArrays(), promotes_inputs=['x']) top.model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False) top.model.nonlinear_solver.options['maxiter'] = 10 top.model.linear_solver = om.ScipyKrylov() top.model.nonlinear_solver.linesearch = om.BoundsEnforceLS( bound_enforcement='vector') top.setup() top.set_val('x', np.array([2., 2, 2]).reshape(3, 1)) # Test lower bounds: should go to the lower bound and stall top.set_val('comp.y', 0.) top.set_val('comp.z', 1.6) top.run_model() for ind in range(3): assert_near_equal(top.get_val('comp.z', indices=ind), [1.5], 1e-8)
def test_linesearch_wall_bound_enforcement_scalar(self): top = self.top top.model.nonlinear_solver.linesearch = om.BoundsEnforceLS(bound_enforcement='scalar') # Setup again because we assigned a new linesearch top.setup() # Test lower bounds: should stop just short of the lower bound top['px.x'] = 2.0 top['comp.y'] = 0. top['comp.z'] = 1.6 top.run_model() for ind in range(3): self.assertGreaterEqual(top['comp.z'][ind], 1.5) self.assertLessEqual(top['comp.z'][ind], 1.6) # Test upper bounds: should stop just short of the minimum upper bound top['px.x'] = 0.5 top['comp.y'] = 0. top['comp.z'] = 2.4 top.run_model() for ind in range(3): self.assertTrue(2.4 <= top['comp.z'][ind] <= self.ub[ind])
def setup(self): newton = self.nonlinear_solver = om.NewtonSolver() newton.options['maxiter'] = 100 newton.options['iprint'] = 2 newton.options['atol'] = 1e-7 newton.options['rtol'] = 1e-7 newton.options['stall_limit'] = 4 newton.options['stall_tol'] = 1e-10 newton.options['solve_subsystems'] = True newton.options['reraise_child_analysiserror'] = False self.options['assembled_jac_type'] = 'dense' self.linear_solver = om.DirectSolver(assemble_jac=True) ln_bt = newton.linesearch = om.BoundsEnforceLS() # ln_bt = newton.linesearch = om.ArmijoGoldsteinLS() # ln_bt.options['maxiter'] = 2 ln_bt.options['iprint'] = -1 # ln_bt.options['print_bound_enforce'] = True # Once the concentration of a species reaches its minimum, we # can essentially remove it from the problem. This switch controls # whether to do this. self.remove_trace_species = False # multiply a damping function that scales down the residual for trace species self.use_trace_damping = True thermo = self.options['thermo'] mode = self.options['mode'] num_prod = thermo.num_prod num_element = thermo.num_element # Input vars self.add_input('composition', val=thermo.b0, desc='moles of atoms present in mixture') self.add_input('P', val=1.0, units="bar", desc="Pressure") if mode == "T": # T is an input self.add_input('T', val=400., units="degK", desc="Temperature") elif mode == "h" or mode == "S": # T becomes another state variable if mode == "h": # hP solve self.add_input('h', val=0., units="cal/g", desc="Enthalpy") elif mode == "S": # SP solve self.add_input('S', val=0., units="cal/(g*degK)", desc="Entropy") self.T_idx = num_prod + num_element self.add_output('T', val=400., units="degK", desc="Temperature", lower=1., res_ref=100) # State vars self.n_init = np.ones(num_prod) / num_prod / 10 # initial guess for n self.add_output('n', shape=num_prod, val=self.n_init, desc="mole fractions of the mixture", lower=MIN_VALID_CONCENTRATION, upper=1e2, res_ref=10000.) self.add_output( 'pi', val=np.ones(num_element), desc="modified lagrange multipliers from the Gibbs lagrangian") # Explicit Outputs self.add_output('n_moles', lower=1e-10, val=0.034, shape=1, desc="1/molecular weight of gas") # allocate the newton Jacobian self.size = size = num_prod + num_element if mode != "T": size += 1 # added T as a state variable self._dRdy = np.zeros((size, size)) self._rhs = np.zeros(size) # used for solve_linear # Cached stuff for speed self.H0_T = None self.S0_T = None self.dH0_dT = None self.dS0_dT = None self.sum_n_H0_T = None # self.deriv_options['check_type'] = 'cs' # self.deriv_options['check_step_size'] = 1e-50 # self.deriv_options['type'] = 'fd' # self.deriv_options['step_size'] = 1e-5 self.declare_partials('n', ['n', 'pi', 'P', 'T']) self.declare_partials('pi', ['n', 'composition']) self.declare_partials('n_moles', 'n') self.declare_partials('n_moles', 'n_moles', val=-1) if mode == 'h': self.declare_partials('T', ['n', 'h', 'T']) elif mode == 'S': self.declare_partials('T', ['n', 'S', 'T', 'P'])
def setup(self): thermo_spec = pyc.species_data.janaf design = self.options['design'] self.add_subsystem( 'fc', pyc.FlightConditions(thermo_data=thermo_spec, elements=pyc.AIR_MIX)) self.add_subsystem( 'inlet', pyc.Inlet(design=design, thermo_data=thermo_spec, elements=pyc.AIR_MIX)) self.add_subsystem( 'fan', pyc.Compressor(thermo_data=thermo_spec, elements=pyc.AIR_MIX, design=design, map_data=pyc.FanMap, map_extrap=True)) self.add_subsystem( 'nozz', pyc.Nozzle(thermo_data=thermo_spec, elements=pyc.AIR_MIX)) self.add_subsystem('perf', pyc.Performance(num_nozzles=1, num_burners=0)) balance = om.BalanceComp() if design: self.add_subsystem('shaft', om.IndepVarComp('Nmech', 1., units='rpm')) self.connect('shaft.Nmech', 'fan.Nmech') balance.add_balance('W', units='lbm/s', eq_units='hp', val=50., lower=1., upper=500.) self.add_subsystem('balance', balance, promotes_inputs=[('rhs:W', 'pwr_target')]) self.connect('fan.power', 'balance.lhs:W') else: # vary mass flow till the nozzle area matches the design values balance.add_balance('W', units='lbm/s', eq_units='inch**2', val=50, lower=1., upper=500.) self.connect('nozz.Throat:stat:area', 'balance.lhs:W') balance.add_balance('Nmech', val=1., units='rpm', lower=0.1, upper=2.0, eq_units='hp') self.connect('balance.Nmech', 'fan.Nmech') self.connect('fan.power', 'balance.lhs:Nmech') # self.add_subsystem('shaft', om.IndepVarComp('Nmech', 1., units='rpm')) # self.connect('shaft.Nmech', 'fan.Nmech') self.add_subsystem('balance', balance, promotes_inputs=[('rhs:Nmech', 'pwr_target')]) pyc.connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I') pyc.connect_flow(self, 'inlet.Fl_O', 'fan.Fl_I') pyc.connect_flow(self, 'fan.Fl_O', 'nozz.Fl_I') 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('balance.W', 'fc.W') newton = self.nonlinear_solver = om.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 = om.ArmijoGoldsteinLS() # newton.linesearch.options['maxiter'] = 3 newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' # newton.linesearch.options['print_bound_enforce'] = True # newton.linesearch.options['iprint'] = -1 # self.linear_solver = om.DirectSolver(assemble_jac=True)
def setup(self): thermo_method = self.options['thermo_method'] thermo_data = self.options['thermo_data'] nozzType = self.options['nozzType'] lossCoef = self.options['lossCoef'] # elements = self.options['elements'] composition = self.Fl_I_data['Fl_I'] self.add_subsystem('mach_choked', om.IndepVarComp( 'MN', 1.000, )) # Create inlet flow station in_flow = FlowIn(fl_name="Fl_I") 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 = Thermo(mode='total_hP', fl_name='Fl_O:tot', method=thermo_method, thermo_kwargs={ 'composition': composition, 'spec': thermo_data }) prom_in = [('h', 'Fl_I:tot:h'), ('composition', 'Fl_I:tot:composition')] 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 throat_static_MN = Thermo(mode='static_MN', method=thermo_method, thermo_kwargs={ 'composition': composition, 'spec': thermo_data }) prom_in = [('ht', 'Fl_I:tot:h'), ('W', 'Fl_I:stat:W'), ('composition', 'Fl_I:tot:composition')] self.add_subsystem('staticMN', throat_static_MN, 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 throat_static_Ps = Thermo(mode='static_Ps', method=thermo_method, thermo_kwargs={ 'composition': composition, 'spec': thermo_data }) prom_in = [('ht', 'Fl_I:tot:h'), ('W', 'Fl_I:stat:W'), ('Ps', 'Ps_calc'), ('composition', 'Fl_I:tot:composition')] self.add_subsystem('staticPs', throat_static_Ps, 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 ideal_flow = Thermo(mode='static_Ps', method=thermo_method, thermo_kwargs={ 'composition': composition, 'spec': thermo_data }) prom_in = [('ht', 'Fl_I:tot:h'), ('S', 'Fl_I:tot:S'), ('W', 'Fl_I:stat:W'), ('Ps', 'Ps_calc'), ('composition', 'Fl_I:tot:composition')] self.add_subsystem('ideal_flow', ideal_flow, 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 = om.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.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver(assemble_jac=True) super().setup()
def setup(self): design = self.options['design'] USE_TABULAR = False if USE_TABULAR: self.options['thermo_method'] = 'TABULAR' self.options['thermo_data'] = pyc.AIR_JETA_TAB_SPEC else: self.options['thermo_method'] = 'CEA' self.options['thermo_data'] = pyc.species_data.janaf FUEL_TYPE = 'JP-7' self.add_subsystem('fc', pyc.FlightConditions()) # ram_recovery | ram_recovery # MN | area self.add_subsystem('inlet', pyc.Inlet()) # dPqP | s_dPqP # Q_dot | Q_dot # MN | area self.add_subsystem('duct', pyc.Duct()) # Ps_exhaust # dPqP self.add_subsystem('nozz', pyc.Nozzle(lossCoef='Cfg')) self.add_subsystem( 'perf', pyc.Performance(num_nozzles=1, num_burners=0)) balance = om.BalanceComp() if design: self.add_subsystem( 'iv', om.IndepVarComp('nozzle_area', 60., units='inch**2')) balance.add_balance('W', units='kg/s', eq_units='inch**2', val=2., lower=0.05, upper=10.) self.add_subsystem('balance', balance) self.connect('iv.nozzle_area', 'balance.rhs:W') self.connect('nozz.Throat:stat:area', 'balance.lhs:W') else: balance.add_balance('W', units='kg/s', eq_units='inch**2', val=2., lower=0.05, upper=10.) self.add_subsystem('balance', balance) self.connect('nozz.Throat:stat:area', 'balance.lhs:W') self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I') self.pyc_connect_flow('inlet.Fl_O', 'duct.Fl_I') self.pyc_connect_flow('duct.Fl_O', 'nozz.Fl_I') self.connect('fc.Fl_O:stat:P', 'nozz.Ps_exhaust') self.connect('inlet.Fl_O:tot:P', 'perf.Pt2') self.connect('duct.Fl_O:tot:P', 'perf.Pt3') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('nozz.Fg', 'perf.Fg_0') self.connect('balance.W', 'fc.W') newton = self.nonlinear_solver = om.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.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' # newton.linesearch.options['print_bound_enforce'] = True # newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver(assemble_jac=True) super().setup()
def setup(self): thermo_spec = pyc.species_data.janaf design = self.options['design'] # Add engine elements self.add_subsystem( 'fc', pyc.FlightConditions(thermo_data=thermo_spec, elements=pyc.AIR_MIX)) self.add_subsystem( 'inlet', pyc.Inlet(design=design, thermo_data=thermo_spec, elements=pyc.AIR_MIX)) self.add_subsystem('comp', pyc.Compressor( map_data=pyc.AXI5, design=design, thermo_data=thermo_spec, elements=pyc.AIR_MIX, ), promotes_inputs=['Nmech']) self.add_subsystem( 'burner', pyc.Combustor(design=design, thermo_data=thermo_spec, inflow_elements=pyc.AIR_MIX, air_fuel_elements=pyc.AIR_FUEL_MIX, fuel_type='JP-7')) self.add_subsystem('turb', pyc.Turbine( map_data=pyc.LPT2269, design=design, thermo_data=thermo_spec, elements=pyc.AIR_FUEL_MIX, ), promotes_inputs=['Nmech']) self.add_subsystem( 'nozz', pyc.Nozzle(nozzType='CD', lossCoef='Cv', thermo_data=thermo_spec, elements=pyc.AIR_FUEL_MIX)) self.add_subsystem('shaft', pyc.Shaft(num_ports=2), promotes_inputs=['Nmech']) self.add_subsystem('perf', pyc.Performance(num_nozzles=1, num_burners=1)) # Connect flow stations pyc.connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I', connect_w=False) pyc.connect_flow(self, 'inlet.Fl_O', 'comp.Fl_I') pyc.connect_flow(self, 'comp.Fl_O', 'burner.Fl_I') pyc.connect_flow(self, 'burner.Fl_O', 'turb.Fl_I') pyc.connect_flow(self, 'turb.Fl_O', 'nozz.Fl_I') # Connect turbomachinery elements to shaft self.connect('comp.trq', 'shaft.trq_0') self.connect('turb.trq', 'shaft.trq_1') # 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') # Add balances for design and off-design balance = self.add_subsystem('balance', om.BalanceComp()) if design: balance.add_balance('W', units='lbm/s', eq_units='lbf') self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('perf.Fn', '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=1.5, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.turb_PR', 'turb.PR') self.connect('shaft.pwr_net', 'balance.lhs:turb_PR') else: balance.add_balance('FAR', eq_units='lbf', lower=1e-4, val=.3) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('perf.Fn', 'balance.lhs:FAR') balance.add_balance('Nmech', val=1.5, units='rpm', lower=500., eq_units='hp', rhs_val=0.) self.connect('balance.Nmech', 'Nmech') self.connect('shaft.pwr_net', 'balance.lhs:Nmech') balance.add_balance('W', val=168.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', 'nozz', 'shaft', 'perf' ]) newton = self.nonlinear_solver = om.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 = om.BoundsEnforceLS() # newton.linesearch = ArmijoGoldsteinLS() # newton.linesearch.options['c'] = .0001 newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver(assemble_jac=True)
def setup(self): thermo_method = self.options['thermo_method'] design = self.options['design'] thermo_data = self.options['thermo_data'] flow1_elements = self.options['Fl_I1_elements'] in_flow = FlowIn(fl_name='Fl_I1') self.add_subsystem('in_flow1', in_flow, promotes=['Fl_I1:*']) flow2_elements = self.options['Fl_I2_elements'] in_flow = FlowIn(fl_name='Fl_I2') 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 = Thermo(mode='static_Ps', fl_name="Fl_I1_calc:stat", method=thermo_method, thermo_kwargs={ 'elements': flow1_elements, 'spec': thermo_data }) self.add_subsystem('Fl_I1_stat_calc', Fl1_stat, promotes_inputs=[('composition', 'Fl_I1:tot:composition'), ('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 = Thermo(mode='static_Ps', fl_name="Fl_I2_calc:stat", method=thermo_method, thermo_kwargs={ 'elements': flow2_elements, 'spec': thermo_data }) self.add_subsystem('Fl_I2_stat_calc', Fl2_stat, promotes_inputs=[('composition', 'Fl_I2:tot:composition'), ('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 = Thermo(mode='static_A', fl_name="Fl_I1_calc:stat", method=thermo_method, thermo_kwargs={ 'elements': flow1_elements, 'spec': thermo_data }) self.add_subsystem('Fl_I1_stat_calc', Fl1_stat, promotes_inputs=[ ('composition', 'Fl_I1:tot:composition'), ('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 = Thermo(mode='static_A', fl_name="Fl_I2_calc:stat", method=thermo_method, thermo_kwargs={ 'elements': flow2_elements, 'spec': thermo_data }) self.add_subsystem('Fl_I2_stat_calc', Fl2_stat, promotes_inputs=[ ('composition', 'Fl_I2:tot:composition'), ('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', om.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']) self.add_subsystem('mix_flow', ThermoAdd(mix_thermo_data=thermo_data, mix_mode='flow', mix_names='mix', inflow_elements=flow1_elements, mix_elements=flow2_elements), promotes_inputs=[('Fl_I:stat:W', 'Fl_I1:stat:W'), ('Fl_I:tot:composition', 'Fl_I1:tot:composition'), ('Fl_I:tot:h', 'Fl_I1:tot:h'), ('mix:W', 'Fl_I2:stat:W'), ('mix:composition', 'Fl_I2:tot:composition'), ('mix:h', 'Fl_I2:tot:h')]) if self.options['designed_stream'] == 1: self.add_subsystem('impulse_mix', MixImpulse(), promotes_inputs=[ ('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:stat:W', 'Fl_I2:stat:P', 'Fl_I2:stat:V', 'Fl_I2:stat:area' ]) else: self.add_subsystem('impulse_mix', MixImpulse(), promotes_inputs=[ 'Fl_I1:stat:W', 'Fl_I1:stat:P', 'Fl_I1:stat:V', 'Fl_I1:stat:area', ('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', om.Group(), promotes=['*']) if self.options['internal_solver']: newton = conv.nonlinear_solver = om.NewtonSolver() newton.options['maxiter'] = 30 newton.options['atol'] = 1e-2 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 20 newton.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 conv.linear_solver = om.DirectSolver(assemble_jac=True) out_tot = Thermo(mode='total_hP', fl_name='Fl_O:tot', method=thermo_method, thermo_kwargs={ 'elements': self.options['Fl_I1_elements'], 'spec': thermo_data }) conv.add_subsystem('out_tot', out_tot, promotes_outputs=['Fl_O:tot:*']) self.connect('mix_flow.composition_out', 'out_tot.composition') self.connect('mix_flow.mass_avg_h', 'out_tot.h') # note: gets Pt from the balance comp out_stat = Thermo(mode='static_A', fl_name='Fl_O:stat', method=thermo_method, thermo_kwargs={ 'elements': self.options['Fl_I1_elements'], 'spec': thermo_data }) conv.add_subsystem('out_stat', out_stat, promotes_outputs=['Fl_O:stat:*'], promotes_inputs=[ 'area', ]) self.connect('mix_flow.composition_out', 'out_stat.composition') self.connect('mix_flow.Wout', 'out_stat.W') conv.connect('Fl_O:tot:S', 'out_stat.S') self.connect('mix_flow.mass_avg_h', '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', om.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( 'impulse_mix.impulse_mix', 'balance.rhs:P_tot' ) #note that this connection comes from outside the convergence group
def setup(self): thermo_spec = pyc.species_data.janaf design = self.options['design'] self.pyc_add_element( 'fc', pyc.FlightConditions(thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS)) # Inlet Components self.pyc_add_element( 'inlet', pyc.Inlet(design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS)) self.pyc_add_element( 'inlet_duct', pyc.Duct(design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS)) # Fan Components - Split here for CFD integration Add a CFDStart Compomponent self.pyc_add_element('fan', pyc.Compressor(map_data=pyc.AXI5, design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS, map_extrap=True), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.pyc_add_element( 'splitter', pyc.Splitter(design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS)) # Core Stream components self.pyc_add_element( 'splitter_core_duct', pyc.Duct(design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS)) self.pyc_add_element('lpc', pyc.Compressor(map_data=pyc.LPCMap, design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS, map_extrap=True), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.pyc_add_element( 'lpc_duct', pyc.Duct(design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS)) self.pyc_add_element('hpc', pyc.Compressor(map_data=pyc.HPCMap, design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS, bleed_names=['cool1'], map_extrap=True), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.pyc_add_element( 'bld3', pyc.BleedOut(design=design, bleed_names=['cool3'])) self.pyc_add_element( 'burner', pyc.Combustor(design=design, thermo_data=thermo_spec, inflow_elements=pyc.AIR_ELEMENTS, air_fuel_elements=pyc.AIR_FUEL_ELEMENTS, fuel_type='Jet-A(g)')) self.pyc_add_element('hpt', pyc.Turbine(map_data=pyc.HPTMap, design=design, thermo_data=thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS, bleed_names=['cool3'], map_extrap=True), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.pyc_add_element( 'hpt_duct', pyc.Duct(design=design, thermo_data=thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS)) self.pyc_add_element('lpt', pyc.Turbine(map_data=pyc.LPTMap, design=design, thermo_data=thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS, bleed_names=['cool1'], map_extrap=True), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.pyc_add_element( 'lpt_duct', pyc.Duct(design=design, thermo_data=thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS)) # Bypass Components self.pyc_add_element( 'bypass_duct', pyc.Duct(design=design, thermo_data=thermo_spec, elements=pyc.AIR_ELEMENTS)) # Mixer component self.pyc_add_element( 'mixer', pyc.Mixer(design=design, designed_stream=1, Fl_I1_elements=pyc.AIR_FUEL_ELEMENTS, Fl_I2_elements=pyc.AIR_ELEMENTS)) self.pyc_add_element( 'mixer_duct', pyc.Duct(design=design, thermo_data=thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS)) # Afterburner Components self.pyc_add_element( 'afterburner', pyc.Combustor(design=design, thermo_data=thermo_spec, inflow_elements=pyc.AIR_FUEL_ELEMENTS, air_fuel_elements=pyc.AIR_FUEL_ELEMENTS, fuel_type='Jet-A(g)')) # End CFD HERE # Nozzle self.pyc_add_element( 'mixed_nozz', pyc.Nozzle(nozzType='CD', lossCoef='Cfg', thermo_data=thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS)) # Mechanical components self.pyc_add_element('lp_shaft', pyc.Shaft(num_ports=3), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.pyc_add_element('hp_shaft', pyc.Shaft(num_ports=2), promotes_inputs=[('Nmech', 'HP_Nmech')]) # Aggregating component self.pyc_add_element('perf', pyc.Performance(num_nozzles=1, num_burners=2)) # Connnect flow path self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I') self.pyc_connect_flow('inlet.Fl_O', 'inlet_duct.Fl_I') self.pyc_connect_flow('inlet_duct.Fl_O', 'fan.Fl_I') self.pyc_connect_flow('fan.Fl_O', 'splitter.Fl_I') # Core connections self.pyc_connect_flow('splitter.Fl_O1', 'splitter_core_duct.Fl_I') self.pyc_connect_flow('splitter_core_duct.Fl_O', 'lpc.Fl_I') self.pyc_connect_flow('lpc.Fl_O', 'lpc_duct.Fl_I') self.pyc_connect_flow('lpc_duct.Fl_O', 'hpc.Fl_I') self.pyc_connect_flow('hpc.Fl_O', 'bld3.Fl_I') self.pyc_connect_flow('bld3.Fl_O', 'burner.Fl_I') self.pyc_connect_flow('burner.Fl_O', 'hpt.Fl_I') self.pyc_connect_flow('hpt.Fl_O', 'hpt_duct.Fl_I') self.pyc_connect_flow('hpt_duct.Fl_O', 'lpt.Fl_I') self.pyc_connect_flow('lpt.Fl_O', 'lpt_duct.Fl_I') self.pyc_connect_flow('lpt_duct.Fl_O', 'mixer.Fl_I1') # Bypass Connections self.pyc_connect_flow('splitter.Fl_O2', 'bypass_duct.Fl_I') self.pyc_connect_flow('bypass_duct.Fl_O', 'mixer.Fl_I2') #Mixer Connections self.pyc_connect_flow('mixer.Fl_O', 'mixer_duct.Fl_I') # After Burner self.pyc_connect_flow('mixer_duct.Fl_O', 'afterburner.Fl_I') # Nozzle self.pyc_connect_flow('afterburner.Fl_O', 'mixed_nozz.Fl_I') # Connect cooling flows self.pyc_connect_flow('hpc.cool1', 'lpt.cool1', connect_stat=False) self.pyc_connect_flow('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', om.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 = om.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.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver(assemble_jac=True)
def setup(self): design = self.options['design'] # NOTE: DEFAULT TABULAR thermo doesn't include WAR, so must use CEA here # (or build your own thermo tables) self.options['thermo_method'] = 'CEA' self.options['thermo_data'] = pyc.species_data.wet_air self.add_subsystem( 'fc', pyc.FlightConditions(composition=pyc.CEA_AIR_COMPOSITION, reactant='Water', mix_ratio_name='WAR')) self.add_subsystem('inlet', pyc.Inlet()) self.add_subsystem( 'fan', pyc.Compressor(map_data=pyc.FanMap, map_extrap=True)) self.add_subsystem('nozz', pyc.Nozzle()) self.add_subsystem('perf', pyc.Performance(num_nozzles=1, num_burners=0)) balance = om.BalanceComp() if design: self.add_subsystem('shaft', om.IndepVarComp('Nmech', 1., units='rpm')) self.connect('shaft.Nmech', 'fan.Nmech') balance.add_balance('W', units='lbm/s', eq_units='hp', val=50., lower=1., upper=500.) self.add_subsystem('balance', balance, promotes_inputs=[('rhs:W', 'pwr_target')]) self.connect('fan.power', 'balance.lhs:W') else: # vary mass flow till the nozzle area matches the design values balance.add_balance('W', units='lbm/s', eq_units='inch**2', val=50, lower=1., upper=500.) self.connect('nozz.Throat:stat:area', 'balance.lhs:W') balance.add_balance('Nmech', val=1., units='rpm', lower=0.1, upper=2.0, eq_units='hp') self.connect('balance.Nmech', 'fan.Nmech') self.connect('fan.power', 'balance.lhs:Nmech') # self.add_subsystem('shaft', om.IndepVarComp('Nmech', 1., units='rpm')) # self.connect('shaft.Nmech', 'fan.Nmech') self.add_subsystem('balance', balance, promotes_inputs=[('rhs:Nmech', 'pwr_target')]) self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I') self.pyc_connect_flow('inlet.Fl_O', 'fan.Fl_I') self.pyc_connect_flow('fan.Fl_O', 'nozz.Fl_I') 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('balance.W', 'fc.W') newton = self.nonlinear_solver = om.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.options['reraise_child_analysiserror'] = False # # newton.linesearch = om.ArmijoGoldsteinLS() # newton.linesearch.options['maxiter'] = 3 newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' # newton.linesearch.options['print_bound_enforce'] = True # newton.linesearch.options['iprint'] = -1 # self.linear_solver = om.DirectSolver(assemble_jac=True) # base_class setup should be called as the last thing in your setup super().setup()
def setup(self): design = self.options['design'] # NOTE: DEFAULT TABULAR thermo doesn't include WAR, so must use CEA here # (or build your own thermo tables) self.options['thermo_method'] = 'CEA' self.options['thermo_data'] = pyc.species_data.wet_air # Add engine elements self.add_subsystem('fc', pyc.FlightConditions(composition=pyc.CEA_AIR_COMPOSITION, reactant='Water', mix_ratio_name='WAR')) self.add_subsystem('inlet', pyc.Inlet()) self.add_subsystem('comp', pyc.Compressor(map_data=pyc.AXI5), promotes_inputs=['Nmech']) self.add_subsystem('burner', pyc.Combustor(fuel_type='JP-7')) self.add_subsystem('turb', pyc.Turbine(map_data=pyc.LPT2269), promotes_inputs=['Nmech']) self.add_subsystem('nozz', pyc.Nozzle(nozzType='CD', lossCoef='Cv')) self.add_subsystem('shaft', pyc.Shaft(num_ports=2), promotes_inputs=['Nmech']) self.add_subsystem('perf', pyc.Performance(num_nozzles=1, num_burners=1)) # Connect flow stations self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I', connect_w=False) self.pyc_connect_flow('inlet.Fl_O', 'comp.Fl_I') self.pyc_connect_flow('comp.Fl_O', 'burner.Fl_I') self.pyc_connect_flow('burner.Fl_O', 'turb.Fl_I') self.pyc_connect_flow('turb.Fl_O', 'nozz.Fl_I') # Connect turbomachinery elements to shaft self.connect('comp.trq', 'shaft.trq_0') self.connect('turb.trq', 'shaft.trq_1') # 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') # Add balances for design and off-design balance = self.add_subsystem('balance', om.BalanceComp()) if design: balance.add_balance('W', units='lbm/s', eq_units='lbf') self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('perf.Fn', '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=1.5, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.turb_PR', 'turb.PR') self.connect('shaft.pwr_net', 'balance.lhs:turb_PR') else: balance.add_balance('FAR', eq_units='lbf', lower=1e-4, val=.3) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('perf.Fn', 'balance.lhs:FAR') balance.add_balance('Nmech', val=1.5, units='rpm', lower=500., eq_units='hp', rhs_val=0.) self.connect('balance.Nmech', 'Nmech') self.connect('shaft.pwr_net', 'balance.lhs:Nmech') balance.add_balance('W', val=168.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', 'nozz', 'shaft', 'perf']) newton = self.nonlinear_solver = om.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.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() # newton.linesearch = ArmijoGoldsteinLS() # newton.linesearch.options['c'] = .0001 newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver(assemble_jac=True) super().setup()
def setup(self): thermo_method = self.options['thermo_method'] thermo_data = self.options['thermo_data'] reactant = self.options['reactant'] mix_ratio_name = self.options['mix_ratio_name'] # composition = self.Fl_O_data['Fl_O'] composition = self.options['composition'] self.add_subsystem('ambient', Ambient(), promotes=('alt', 'dTs')) # inputs conv = self.add_subsystem('conv', om.Group(), promotes=['*']) if reactant is not False: proms = ['Fl_O:*', 'MN', 'W', mix_ratio_name] else: proms = ['Fl_O:*', 'MN', 'W'] fs_start = conv.add_subsystem('fs', FlowStart(thermo_method=thermo_method, thermo_data=thermo_data, composition=composition, reactant=reactant, mix_ratio_name=mix_ratio_name), promotes=proms) # need to manually call this in this setup, because we have an element within an element fs_start.pyc_setup_output_ports() balance = conv.add_subsystem('balance', om.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 = om.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.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 # newton.linesearch.options['solve_subsystems'] = True conv.linear_solver = om.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') # self.set_order(['ambient', 'subgroup']) super().setup()
def setup(self): thermo_spec = pyc.species_data.wet_air #special species library is called that allows for using initial compositions that include both H and C design = self.options['design'] self.pyc_add_element( 'fc', pyc.FlightConditions(thermo_data=thermo_spec, use_WAR=True, elements=pyc.WET_AIR_ELEMENTS) ) #WET_AIR_ELEMENTS contains standard dry air compounds as well as H2O self.pyc_add_element( 'inlet', pyc.Inlet(design=design, thermo_data=thermo_spec, elements=pyc.WET_AIR_ELEMENTS)) self.pyc_add_element( 'fan', pyc.Compressor(thermo_data=thermo_spec, elements=pyc.WET_AIR_ELEMENTS, design=design, map_data=pyc.FanMap, map_extrap=True)) self.pyc_add_element( 'nozz', pyc.Nozzle(thermo_data=thermo_spec, elements=pyc.WET_AIR_ELEMENTS)) self.pyc_add_element('perf', pyc.Performance(num_nozzles=1, num_burners=0)) balance = om.BalanceComp() if design: self.add_subsystem('shaft', om.IndepVarComp('Nmech', 1., units='rpm')) self.connect('shaft.Nmech', 'fan.Nmech') balance.add_balance('W', units='lbm/s', eq_units='hp', val=50., lower=1., upper=500.) self.add_subsystem('balance', balance, promotes_inputs=[('rhs:W', 'pwr_target')]) self.connect('fan.power', 'balance.lhs:W') else: # vary mass flow till the nozzle area matches the design values balance.add_balance('W', units='lbm/s', eq_units='inch**2', val=50, lower=1., upper=500.) self.connect('nozz.Throat:stat:area', 'balance.lhs:W') balance.add_balance('Nmech', val=1., units='rpm', lower=0.1, upper=2.0, eq_units='hp') self.connect('balance.Nmech', 'fan.Nmech') self.connect('fan.power', 'balance.lhs:Nmech') # self.add_subsystem('shaft', om.IndepVarComp('Nmech', 1., units='rpm')) # self.connect('shaft.Nmech', 'fan.Nmech') self.add_subsystem('balance', balance, promotes_inputs=[('rhs:Nmech', 'pwr_target')]) self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I') self.pyc_connect_flow('inlet.Fl_O', 'fan.Fl_I') self.pyc_connect_flow('fan.Fl_O', 'nozz.Fl_I') 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('balance.W', 'fc.W') newton = self.nonlinear_solver = om.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.options['reraise_child_analysiserror'] = False # # newton.linesearch = om.ArmijoGoldsteinLS() # newton.linesearch.options['maxiter'] = 3 newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' # newton.linesearch.options['print_bound_enforce'] = True # newton.linesearch.options['iprint'] = -1 # self.linear_solver = om.DirectSolver(assemble_jac=True)
def run_problem(ram_recovery=1.0, dPqP=0.0, heat_in=0.0, cfg=0.98, oc_use_dpqp=False, list_output=True, oc_areas=None, oc_delta_p=0.0): prob = om.Problem() model = prob.model = om.Group() iv = model.add_subsystem('iv', om.IndepVarComp()) iv.add_output('area_2', val=408, units='inch**2') # add the pycycle duct if HAS_PYCYCLE: mp_duct = model.add_subsystem('pyduct', MPDuct()) prob.model.connect('pyduct.design.fc.Fl_O:stat:T', 'fltcond|T') prob.model.connect('pyduct.design.fc.Fl_O:stat:P', 'fltcond|p') prob.model.connect('pyduct.design.fc.Fl_O:stat:V', 'fltcond|Utrue') else: iv.add_output('fltcond|T', val=223.15013852435118, units='K') iv.add_output('fltcond|p', val=26436.23048846425, units='Pa') iv.add_output('fltcond|Utrue', val=0.8 * 299.57996571373235, units='m/s') prob.model.connect('iv.fltcond|T', 'fltcond|T') prob.model.connect('iv.fltcond|p', 'fltcond|p') prob.model.connect('iv.fltcond|Utrue', 'fltcond|Utrue') # add the openconcept duct oc = model.add_subsystem('oc', ImplicitCompressibleDuct_ExternalHX(num_nodes=1, cfg=cfg), promotes_inputs=[('p_inf', 'fltcond|p'), ('T_inf', 'fltcond|T'), ('Utrue', 'fltcond|Utrue')]) newton = oc.nonlinear_solver = om.NewtonSolver() newton.options['atol'] = 1e-12 newton.options['rtol'] = 1e-12 newton.options['iprint'] = -1 newton.options['maxiter'] = 10 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 10 newton.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' oc.linear_solver = om.DirectSolver(assemble_jac=True) prob.model.connect('iv.area_2', ['oc.area_2', 'oc.area_3']) # iv.add_output('cp', val=1002.93, units='J/kg/K') # iv.add_output('pressure_recovery_1', val=np.ones((nn,))) # iv.add_output('loss_factor_1', val=0.0) # iv.add_output('delta_p_2', val=np.ones((nn,))*0., units='Pa') # iv.add_output('heat_in_2', val=np.ones((nn,))*0., units='W') # iv.add_output('pressure_recovery_2', val=np.ones((nn,))) # iv.add_output('pressure_recovery_3', val=np.ones((nn,))) prob.setup() prob.set_val('oc.area_1', val=64, units='inch**2') prob.set_val('oc.convergence_hack', val=0.0, units='Pa') prob.set_val('oc.area_nozzle_in', val=60.0, units='inch**2') prob.set_val('oc.inlet.totalpressure.eta_ram', val=ram_recovery) if HAS_PYCYCLE: #Define the design point prob.set_val('pyduct.design.fc.alt', 10000, units='m') prob.set_val('pyduct.design.fc.MN', 0.8) prob.set_val('pyduct.design.inlet.MN', 0.6) prob.set_val('pyduct.design.inlet.ram_recovery', ram_recovery) prob.set_val('pyduct.design.duct.MN', 0.08) prob.set_val('pyduct.design.duct.dPqP', dPqP) prob.set_val('pyduct.design.duct.Q_dot', heat_in, units='kW') prob.set_val('pyduct.design.nozz.Cfg', cfg, units=None) # Set initial guesses for balances prob['pyduct.design.balance.W'] = 8. prob.model.pyduct.design.nonlinear_solver.options['atol'] = 1e-6 prob.model.pyduct.design.nonlinear_solver.options['rtol'] = 1e-6 prob.set_solver_print(level=-1) prob.set_solver_print(level=-1, depth=2) # do a first run to get the duct areas from pycycle prob.run_model() if HAS_PYCYCLE: # set areas based on pycycle design point prob.set_val('oc.area_1', val=prob.get_val('pyduct.design.inlet.Fl_O:stat:area', units='inch**2'), units='inch**2') prob.set_val('iv.area_2', val=prob.get_val('pyduct.design.duct.Fl_O:stat:area', units='inch**2'), units='inch**2') else: prob.set_val('oc.area_1', val=oc_areas[0], units='inch**2') prob.set_val('iv.area_2', val=oc_areas[1], units='inch**2') prob.set_val('oc.sta3.heat_in', val=heat_in, units='kW') if oc_use_dpqp: prob.set_val('oc.sta3.pressure_recovery', val=(1 - dPqP), units=None) else: if HAS_PYCYCLE: delta_p = prob.get_val( 'pyduct.design.inlet.Fl_O:tot:P', units='Pa') - prob.get_val( 'pyduct.design.nozz.Fl_O:tot:P', units='Pa') else: delta_p = oc_delta_p prob.set_val('oc.sta3.delta_p', -delta_p, units='Pa') prob.run_model() if list_output and HAS_PYCYCLE: prob.model.list_outputs(units=True, excludes=['*chem_eq*', '*props*']) # prob.model.list_outputs(includes=['*oc.*force*','*perf*','*mdot*'], units=True) print(prob.get_val('pyduct.design.inlet.Fl_O:stat:W', units='kg/s')) print(prob.get_val('pyduct.design.perf.Fn', units='N')) for pt in ['design'] + mp_duct.od_pts: print('\n', '#' * 10, pt, '#' * 10) viewer(prob, 'pyduct.' + pt) elif list_output: prob.model.list_outputs(units=True) return prob
prob.model.connect('RTO:T4max', 'T4_ratio.RTO_T4') 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 = om.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_maxiter'] = True # newton.linesearch = om.ArmijoGoldsteinLS() newton.linesearch = om.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 = om.DirectSolver(assemble_jac=True) # prob.model.jacobian = CSCJacobian() # recorder = SqliteRecorder('N3_MDP.sql') # setup the optimization prob.driver = om.pyOptSparseDriver() prob.driver.options['optimizer'] = 'SNOPT'
def setup(self): wet_thermo_spec = pyc.species_data.wet_air #special species library is called that allows for using initial compositions that include both H and C janaf_thermo_spec = pyc.species_data.janaf #standard species library is called for use in and after burner design = self.options['design'] # Add engine elements self.pyc_add_element('fc', pyc.FlightConditions(thermo_data=wet_thermo_spec, use_WAR=True, elements=pyc.WET_AIR_ELEMENTS))#WET_AIR_ELEMENTS contains standard dry air compounds as well as H2O self.pyc_add_element('inlet', pyc.Inlet(design=design, thermo_data=wet_thermo_spec, elements=pyc.WET_AIR_ELEMENTS)) self.pyc_add_element('comp', pyc.Compressor(map_data=pyc.AXI5, design=design, thermo_data=wet_thermo_spec, elements=pyc.WET_AIR_ELEMENTS,), promotes_inputs=['Nmech']) ###Note### #The Combustor element automatically assumes that the thermo data to use for both the inflowing air #and the outflowing mixed air and fuel is the data specified by the thermo_data option #unless the inflow_thermo_data option is set. If the inflow_thermo_data option is set, #the Combustor element will use the thermo data specified by inflow_thermo_data for the inflowing air #to the burner, and it will use the thermo data specified by thermo_data for the outflowing mixed #air and fuel. This is necessary to do if the airflow upstream of the burner contains both C and H #within its compounds, because without the addition of the hydrocarbons from fuel, the solver has #a difficult time converging the trace amount of hydrocarbons "present" in the original flow. self.pyc_add_element('burner', pyc.Combustor(design=design,inflow_thermo_data=wet_thermo_spec, thermo_data=janaf_thermo_spec, inflow_elements=pyc.WET_AIR_ELEMENTS, air_fuel_elements=pyc.AIR_FUEL_ELEMENTS, fuel_type='JP-7')) self.pyc_add_element('turb', pyc.Turbine(map_data=pyc.LPT2269, design=design, thermo_data=janaf_thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS,), promotes_inputs=['Nmech']) self.pyc_add_element('nozz', pyc.Nozzle(nozzType='CD', lossCoef='Cv', thermo_data=janaf_thermo_spec, elements=pyc.AIR_FUEL_ELEMENTS)) self.pyc_add_element('shaft', pyc.Shaft(num_ports=2),promotes_inputs=['Nmech']) self.pyc_add_element('perf', pyc.Performance(num_nozzles=1, num_burners=1)) # Connect flow stations self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I', connect_w=False) self.pyc_connect_flow('inlet.Fl_O', 'comp.Fl_I') self.pyc_connect_flow('comp.Fl_O', 'burner.Fl_I') self.pyc_connect_flow('burner.Fl_O', 'turb.Fl_I') self.pyc_connect_flow('turb.Fl_O', 'nozz.Fl_I') # Connect turbomachinery elements to shaft self.connect('comp.trq', 'shaft.trq_0') self.connect('turb.trq', 'shaft.trq_1') # 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') # Add balances for design and off-design balance = self.add_subsystem('balance', om.BalanceComp()) if design: balance.add_balance('W', units='lbm/s', eq_units='lbf') self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('perf.Fn', '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=1.5, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.turb_PR', 'turb.PR') self.connect('shaft.pwr_net', 'balance.lhs:turb_PR') else: balance.add_balance('FAR', eq_units='lbf', lower=1e-4, val=.3) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('perf.Fn', 'balance.lhs:FAR') balance.add_balance('Nmech', val=1.5, units='rpm', lower=500., eq_units='hp', rhs_val=0.) self.connect('balance.Nmech', 'Nmech') self.connect('shaft.pwr_net', 'balance.lhs:Nmech') balance.add_balance('W', val=168.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', 'nozz', 'shaft', 'perf']) newton = self.nonlinear_solver = om.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.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() # newton.linesearch = ArmijoGoldsteinLS() # newton.linesearch.options['c'] = .0001 newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver(assemble_jac=True)
def setup(self): # TOC POINT (DESIGN) self.pyc_add_pnt('TOC', N3(), promotes_inputs=[('fan.PR', 'fan:PRdes'), ('lpc.PR', 'lpc:PRdes'), ('opr_calc.FPR', 'fan:PRdes'), ('opr_calc.LPCPR', 'lpc:PRdes')]) # POINT 1: Top-of-climb (TOC) self.set_input_defaults('TOC.fc.alt', 35000., units='ft'), self.set_input_defaults('TOC.fc.MN', 0.8), self.set_input_defaults('TOC.inlet.ram_recovery', 0.9980), self.set_input_defaults('TOC.balance.rhs:fan_eff', 0.97) self.set_input_defaults('TOC.duct2.dPqP', 0.0100) self.set_input_defaults('TOC.balance.rhs:lpc_eff', 0.905), self.set_input_defaults('TOC.duct25.dPqP', 0.0150), self.set_input_defaults('TOC.balance.rhs:hpt_eff', 0.91), self.set_input_defaults('TOC.duct45.dPqP', 0.0050), self.set_input_defaults('TOC.balance.rhs:lpt_eff', 0.92), self.set_input_defaults('TOC.duct5.dPqP', 0.0100), self.set_input_defaults('TOC.duct17.dPqP', 0.0150), self.set_input_defaults('TOC.Fan_Nmech', 2184.5, units='rpm'), self.set_input_defaults('TOC.LP_Nmech', 6772.0, units='rpm'), self.set_input_defaults('TOC.HP_Nmech', 20871.0, units='rpm'), self.set_input_defaults('TOC.inlet.MN', 0.625), self.set_input_defaults('TOC.fan.MN', 0.45) self.set_input_defaults('TOC.splitter.MN1', 0.45) self.set_input_defaults('TOC.splitter.MN2', 0.45) self.set_input_defaults('TOC.duct2.MN', 0.45), self.set_input_defaults('TOC.lpc.MN', 0.45), self.set_input_defaults('TOC.bld25.MN', 0.45), self.set_input_defaults('TOC.duct25.MN', 0.45), self.set_input_defaults('TOC.hpc.MN', 0.30), self.set_input_defaults('TOC.bld3.MN', 0.30) self.set_input_defaults('TOC.burner.MN', 0.10), self.set_input_defaults('TOC.hpt.MN', 0.30), self.set_input_defaults('TOC.duct45.MN', 0.45), self.set_input_defaults('TOC.lpt.MN', 0.35), self.set_input_defaults('TOC.duct5.MN', 0.25), self.set_input_defaults('TOC.byp_bld.MN', 0.45), self.set_input_defaults('TOC.duct17.MN', 0.45), self.pyc_add_cycle_param('burner.dPqP', 0.0400), self.pyc_add_cycle_param('core_nozz.Cv', 0.9999), self.pyc_add_cycle_param('byp_nozz.Cv', 0.9975), self.pyc_add_cycle_param('lp_shaft.fracLoss', 0.01) self.pyc_add_cycle_param('hp_shaft.HPX', 350.0, units='hp'), self.pyc_add_cycle_param('bld25.sbv:frac_W', 0.0), self.pyc_add_cycle_param('hpc.bld_inlet:frac_W', 0.0), self.pyc_add_cycle_param('hpc.bld_inlet:frac_P', 0.1465), self.pyc_add_cycle_param('hpc.bld_inlet:frac_work', 0.5), self.pyc_add_cycle_param('hpc.bld_exit:frac_W', 0.02), self.pyc_add_cycle_param('hpc.bld_exit:frac_P', 0.1465), self.pyc_add_cycle_param('hpc.bld_exit:frac_work', 0.5), self.pyc_add_cycle_param('hpc.cust:frac_W', 0.0), self.pyc_add_cycle_param('hpc.cust:frac_P', 0.1465), self.pyc_add_cycle_param('hpc.cust:frac_work', 0.35), self.pyc_add_cycle_param('hpt.bld_inlet:frac_P', 1.0), self.pyc_add_cycle_param('hpt.bld_exit:frac_P', 0.0), self.pyc_add_cycle_param('lpt.bld_inlet:frac_P', 1.0), self.pyc_add_cycle_param('lpt.bld_exit:frac_P', 0.0), self.pyc_add_cycle_param('byp_bld.bypBld:frac_W', 0.0), # OTHER POINTS (OFF-DESIGN) self.od_pts = ['RTO', 'SLS', 'CRZ'] self.cooling = [True, False, False] self.od_MNs = [0.25, 0.000001, 0.8] self.od_alts = [0.0, 0.0, 35000.0] self.od_dTs = [27.0, 27.0, 0.0] self.od_BPRs = [1.75, 1.75, 1.9397] self.od_recoveries = [0.9970, 0.9950, 0.9980] for i, pt in enumerate(self.od_pts): self.pyc_add_pnt(pt, N3(design=False, cooling=self.cooling[i])) self.set_input_defaults(pt + '.fc.MN', val=self.od_MNs[i]) self.set_input_defaults(pt + '.fc.alt', val=self.od_alts[i], units='ft') self.set_input_defaults(pt + '.fc.dTs', val=self.od_dTs[i], units='degR') self.set_input_defaults(pt + '.balance.rhs:BPR', val=self.od_BPRs[i]) self.set_input_defaults(pt + '.inlet.ram_recovery', val=self.od_recoveries[i]) # Extra set input for Rolling Takeoff self.set_input_defaults('RTO.balance.rhs:FAR', 22800.0, units='lbf'), self.pyc_connect_des_od('fan.s_PR', 'fan.s_PR') self.pyc_connect_des_od('fan.s_Wc', 'fan.s_Wc') self.pyc_connect_des_od('fan.s_eff', 'fan.s_eff') self.pyc_connect_des_od('fan.s_Nc', 'fan.s_Nc') self.pyc_connect_des_od('lpc.s_PR', 'lpc.s_PR') self.pyc_connect_des_od('lpc.s_Wc', 'lpc.s_Wc') self.pyc_connect_des_od('lpc.s_eff', 'lpc.s_eff') self.pyc_connect_des_od('lpc.s_Nc', 'lpc.s_Nc') self.pyc_connect_des_od('hpc.s_PR', 'hpc.s_PR') self.pyc_connect_des_od('hpc.s_Wc', 'hpc.s_Wc') self.pyc_connect_des_od('hpc.s_eff', 'hpc.s_eff') self.pyc_connect_des_od('hpc.s_Nc', 'hpc.s_Nc') self.pyc_connect_des_od('hpt.s_PR', 'hpt.s_PR') self.pyc_connect_des_od('hpt.s_Wp', 'hpt.s_Wp') self.pyc_connect_des_od('hpt.s_eff', 'hpt.s_eff') self.pyc_connect_des_od('hpt.s_Np', 'hpt.s_Np') self.pyc_connect_des_od('lpt.s_PR', 'lpt.s_PR') self.pyc_connect_des_od('lpt.s_Wp', 'lpt.s_Wp') self.pyc_connect_des_od('lpt.s_eff', 'lpt.s_eff') self.pyc_connect_des_od('lpt.s_Np', 'lpt.s_Np') self.pyc_connect_des_od('gearbox.gear_ratio', 'gearbox.gear_ratio') self.pyc_connect_des_od('core_nozz.Throat:stat:area', 'balance.rhs:W') if self.options['statics'] is True: self.pyc_connect_des_od('inlet.Fl_O:stat:area', 'inlet.area') self.pyc_connect_des_od('fan.Fl_O:stat:area', 'fan.area') self.pyc_connect_des_od('splitter.Fl_O1:stat:area', 'splitter.area1') self.pyc_connect_des_od('splitter.Fl_O2:stat:area', 'splitter.area2') self.pyc_connect_des_od('duct2.Fl_O:stat:area', 'duct2.area') self.pyc_connect_des_od('lpc.Fl_O:stat:area', 'lpc.area') self.pyc_connect_des_od('bld25.Fl_O:stat:area', 'bld25.area') self.pyc_connect_des_od('duct25.Fl_O:stat:area', 'duct25.area') self.pyc_connect_des_od('hpc.Fl_O:stat:area', 'hpc.area') self.pyc_connect_des_od('bld3.Fl_O:stat:area', 'bld3.area') self.pyc_connect_des_od('burner.Fl_O:stat:area', 'burner.area') self.pyc_connect_des_od('hpt.Fl_O:stat:area', 'hpt.area') self.pyc_connect_des_od('duct45.Fl_O:stat:area', 'duct45.area') self.pyc_connect_des_od('lpt.Fl_O:stat:area', 'lpt.area') self.pyc_connect_des_od('duct5.Fl_O:stat:area', 'duct5.area') self.pyc_connect_des_od('byp_bld.Fl_O:stat:area', 'byp_bld.area') self.pyc_connect_des_od('duct17.Fl_O:stat:area', 'duct17.area') self.pyc_connect_des_od('duct2.s_dPqP', 'duct2.s_dPqP') self.pyc_connect_des_od('duct25.s_dPqP', 'duct25.s_dPqP') self.pyc_connect_des_od('duct45.s_dPqP', 'duct45.s_dPqP') self.pyc_connect_des_od('duct5.s_dPqP', 'duct5.s_dPqP') self.pyc_connect_des_od('duct17.s_dPqP', 'duct17.s_dPqP') self.connect('RTO.balance.hpt_chrg_cool_frac', 'TOC.bld3.bld_exit:frac_W') self.connect('RTO.balance.hpt_nochrg_cool_frac', 'TOC.bld3.bld_inlet:frac_W') self.connect('RTO.balance.hpt_chrg_cool_frac', 'SLS.bld3.bld_exit:frac_W') self.connect('RTO.balance.hpt_nochrg_cool_frac', 'SLS.bld3.bld_inlet:frac_W') self.connect('RTO.balance.hpt_chrg_cool_frac', 'CRZ.bld3.bld_exit:frac_W') self.connect('RTO.balance.hpt_nochrg_cool_frac', 'CRZ.bld3.bld_inlet:frac_W') self.add_subsystem('T4_ratio', om.ExecComp('TOC_T4 = RTO_T4*TR', RTO_T4={ 'value': 3400.0, 'units': 'degR' }, TOC_T4={ 'value': 3150.0, 'units': 'degR' }, TR={ 'value': 0.926470588, 'units': None }), promotes_inputs=[ 'RTO_T4', ]) self.connect('T4_ratio.TOC_T4', 'TOC.balance.rhs:FAR') initial_order = ['T4_ratio', 'TOC', 'RTO', 'SLS', 'CRZ'] self.set_order(self.options['order_start'] + initial_order + self.options['order_add']) newton = self.nonlinear_solver = om.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 = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver(assemble_jac=True)
def setup(self): design = self.options['design'] maxiter = self.options['maxiter'] self.options['thermo_method'] = 'CEA' self.options['thermo_data'] = pyc.species_data.janaf self.add_subsystem('fc', pyc.FlightConditions()) self.add_subsystem('inlet', pyc.Inlet()) self.add_subsystem('duct1', pyc.Duct()) self.add_subsystem('lpc', pyc.Compressor(map_data=pyc.LPCMap), promotes_inputs=[('Nmech','IP_Nmech')]) self.add_subsystem('icduct', pyc.Duct()) self.add_subsystem('hpc_axi', pyc.Compressor(map_data=pyc.HPCMap), promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('bld25', pyc.BleedOut(bleed_names=['cool1','cool2'])) self.add_subsystem('hpc_centri', pyc.Compressor(map_data=pyc.HPCMap), promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('bld3', pyc.BleedOut(bleed_names=['cool3','cool4'])) self.add_subsystem('duct6', pyc.Duct()) self.add_subsystem('burner', pyc.Combustor(fuel_type='Jet-A(g)')) self.add_subsystem('hpt', pyc.Turbine(map_data=pyc.HPTMap, bleed_names=['cool3','cool4']), promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('duct43', pyc.Duct()) self.add_subsystem('lpt', pyc.Turbine(map_data=pyc.LPTMap, bleed_names=['cool1','cool2']), promotes_inputs=[('Nmech','IP_Nmech')]) self.add_subsystem('itduct', pyc.Duct()) self.add_subsystem('pt', pyc.Turbine(map_data=pyc.LPTMap), promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('duct12', pyc.Duct()) self.add_subsystem('nozzle', pyc.Nozzle(nozzType='CV', lossCoef='Cv')) self.add_subsystem('lp_shaft', pyc.Shaft(num_ports=1),promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('ip_shaft', pyc.Shaft(num_ports=2),promotes_inputs=[('Nmech','IP_Nmech')]) self.add_subsystem('hp_shaft', pyc.Shaft(num_ports=3),promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('perf', pyc.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', om.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') else: balance.add_balance('FAR', eq_units='hp', lower=1e-4, val=.017) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('lp_shaft.pwr_net', 'balance.lhs:FAR') balance.add_balance('W', units='lbm/s', eq_units='inch**2') 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') self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I', connect_w=False) self.pyc_connect_flow('inlet.Fl_O', 'duct1.Fl_I') self.pyc_connect_flow('duct1.Fl_O', 'lpc.Fl_I') self.pyc_connect_flow('lpc.Fl_O', 'icduct.Fl_I') self.pyc_connect_flow('icduct.Fl_O', 'hpc_axi.Fl_I') self.pyc_connect_flow('hpc_axi.Fl_O', 'bld25.Fl_I') self.pyc_connect_flow('bld25.Fl_O', 'hpc_centri.Fl_I') self.pyc_connect_flow('hpc_centri.Fl_O', 'bld3.Fl_I') self.pyc_connect_flow('bld3.Fl_O', 'duct6.Fl_I') self.pyc_connect_flow('duct6.Fl_O', 'burner.Fl_I') self.pyc_connect_flow('burner.Fl_O', 'hpt.Fl_I') self.pyc_connect_flow('hpt.Fl_O', 'duct43.Fl_I') self.pyc_connect_flow('duct43.Fl_O', 'lpt.Fl_I') self.pyc_connect_flow('lpt.Fl_O', 'itduct.Fl_I') self.pyc_connect_flow('itduct.Fl_O', 'pt.Fl_I') self.pyc_connect_flow('pt.Fl_O', 'duct12.Fl_I') self.pyc_connect_flow('duct12.Fl_O', 'nozzle.Fl_I') self.pyc_connect_flow('bld25.cool1', 'lpt.cool1', connect_stat=False) self.pyc_connect_flow('bld25.cool2', 'lpt.cool2', connect_stat=False) self.pyc_connect_flow('bld3.cool3', 'hpt.cool3', connect_stat=False) self.pyc_connect_flow('bld3.cool4', 'hpt.cool4', connect_stat=False) newton = self.nonlinear_solver = om.NewtonSolver() newton.options['atol'] = 1e-6 newton.options['rtol'] = 1e-6 newton.options['iprint'] = 2 newton.options['maxiter'] = maxiter newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 100 newton.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 self.linear_solver = om.DirectSolver() super().setup()
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', om.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', om.Group(), promotes=['*']) if self.options['internal_solver']: newton = conv.nonlinear_solver = om.NewtonSolver() newton.options['maxiter'] = 30 newton.options['atol'] = 1e-2 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 20 newton.options['reraise_child_analysiserror'] = False newton.linesearch = om.BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 conv.linear_solver = om.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', om.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): nn = self.options['num_nodes'] self.add_subsystem('thermal_properties', ThermalGroup(num_nodes=nn), promotes_inputs=[ 'B_pk', 'alpha_stein', 'beta_stein', 'k_stein', 'rpm', 'sta_mass', 'resistivity_wire', 'stack_length', 'n_slots', 'n_strands', 'n_m', 'mu_o', 'f_e', 'n_turns', 'T_coeff_cu', 'I', 'T_windings', 'r_strand', 'mu_r' ], promotes_outputs=[ 'A_cu', 'r_litz', 'P_steinmetz', 'P_dc', 'P_ac', 'P_wire', 'L_wire', 'R_dc', 'skin_depth', 'temp_resistivity', 'f_e' ]) self.add_subsystem( 'em_properties', EmGroup(num_nodes=nn), promotes_inputs=[ 'w_slot', 'w_t', 'T_coef_rem_mag', 'T_mag', 'gap', 'carters_coef', 'k_sat', 'stack_length', 'Br', 'Br_20', 'mu_r', 'g_eq', 't_mag', 'B_g', 'n_m', 'n_turns', 'I', 'rot_or', 'rpm', 'P_wire', 'P_steinmetz', 'P_shaft', 'Tq_shaft', 'omega' ], # 'l_slot_opening', promotes_outputs=[ 'Br', 'carters_coef', 'Tq_shaft', 'Tq_max', 'g_eq', 'omega', 'P_in', 'Eff', 'B_g' ]) # 'mech_angle', 't_1', if self.options['design']: self.add_subsystem('geometry', SizeGroup(), promotes_inputs=[ 'gap', 'B_g', 'k', 'b_ry', 'n_m', 'b_sy', 'b_t', 'n_turns', 'I', 'k_wb', 'rho', 'radius_motor', 'n_slots', 'sta_ir', 'w_t', 'stack_length', 's_d', 'rot_or', 'rot_ir', 't_mag', 'rho_mag' ], promotes_outputs=[ 'J', 'w_ry', 'w_sy', 'w_t', 'sta_ir', 'rot_ir', 's_d', 'mag_mass', 'sta_mass', 'rot_mass', 'slot_area', 'w_slot' ]) bal = om.BalanceComp(num_nodes=nn) bal.add_balance('rot_or', val=0.05, units='cm', eq_units='A/mm**2', lower=1e-4) #, use_mult=True, mult_val=0.5) tgt = om.IndepVarComp(name='J_tgt', val=10.47, units='A/mm**2') self.add_subsystem(name='target', subsys=tgt, promotes_outputs=['J_tgt']) self.add_subsystem(name='balance', subsys=bal, promotes_outputs=['rot_or']) self.connect('J_tgt', 'balance.rhs:rot_or') self.connect('J', 'balance.lhs:rot_or') self.linear_solver = om.DirectSolver() newton = self.nonlinear_solver = om.NewtonSolver() newton.options['maxiter'] = 50 newton.options['iprint'] = 2 newton.options['solve_subsystems'] = True # ls = newton.linesearch = om.ArmijoGoldsteinLS() # ls.options['maxiter'] = 3 # ls.options['iprint'] = 2 # ls.options['print_bound_enforce'] = True ls = newton.linesearch = om.BoundsEnforceLS() ls.options['print_bound_enforce'] = True