def test_guess_nonlinear_inputs_read_only_reset(self): class ImpWithInitial(ImplicitComponent): def setup(self): self.add_input('x', 3.0) self.add_output('y', 4.0) def guess_nonlinear(self, inputs, outputs, resids): raise AnalysisError("It's just a scratch.") group = Group() group.add_subsystem('px', IndepVarComp('x', 77.0)) group.add_subsystem('comp1', ImpWithInitial()) group.add_subsystem('comp2', ImpWithInitial()) group.connect('px.x', 'comp1.x') group.connect('comp1.y', 'comp2.x') group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['maxiter'] = 1 prob = Problem(model=group) prob.set_solver_print(level=0) prob.setup(check=False) with self.assertRaises(AnalysisError): prob.run_model() # verify read_only status is reset after AnalysisError prob['comp1.x'] = 111.
def test_guess_nonlinear_resids_read_only(self): class ImpWithInitial(ImplicitComponent): def setup(self): self.add_input('x', 3.0) self.add_output('y', 4.0) def guess_nonlinear(self, inputs, outputs, resids): # inputs is read_only, should not be allowed resids['y'] = 0. group = Group() group.add_subsystem('px', IndepVarComp('x', 77.0)) group.add_subsystem('comp1', ImpWithInitial()) group.add_subsystem('comp2', ImpWithInitial()) group.connect('px.x', 'comp1.x') group.connect('comp1.y', 'comp2.x') group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['maxiter'] = 1 prob = Problem(model=group) prob.set_solver_print(level=0) prob.setup(check=False) with self.assertRaises(ValueError) as cm: prob.run_model() self.assertEqual(str(cm.exception), "Attempt to set value of 'y' in residual vector " "when it is read only.")
def test_shape(self): n = 100 bal = BalanceComp() bal.add_balance('x', shape=(n,)) tgt = IndepVarComp(name='y_tgt', val=4*np.ones(n)) exe = ExecComp('y=x**2', x=np.zeros(n), y=np.zeros(n)) model = Group() model.add_subsystem('tgt', tgt, promotes_outputs=['y_tgt']) model.add_subsystem('exe', exe) model.add_subsystem('bal', bal) model.connect('y_tgt', 'bal.rhs:x') model.connect('bal.x', 'exe.x') model.connect('exe.y', 'bal.lhs:x') model.linear_solver = DirectSolver(assemble_jac=True) model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) prob = Problem(model) prob.setup() prob['bal.x'] = np.random.rand(n) prob.run_model() assert_almost_equal(prob['bal.x'], 2.0*np.ones(n), decimal=7)
def test_shape(self): n = 100 bal = BalanceComp() bal.add_balance('x', shape=(n, )) tgt = IndepVarComp(name='y_tgt', val=4 * np.ones(n)) exe = ExecComp('y=x**2', x=np.zeros(n), y=np.zeros(n)) model = Group() model.add_subsystem('tgt', tgt, promotes_outputs=['y_tgt']) model.add_subsystem('exe', exe) model.add_subsystem('bal', bal) model.connect('y_tgt', 'bal.rhs:x') model.connect('bal.x', 'exe.x') model.connect('exe.y', 'bal.lhs:x') model.linear_solver = DirectSolver(assemble_jac=True) model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) prob = Problem(model) prob.setup() prob['bal.x'] = np.random.rand(n) prob.run_model() assert_almost_equal(prob['bal.x'], 2.0 * np.ones(n), decimal=7)
def test_guess_nonlinear_resids_read_only(self): class ImpWithInitial(ImplicitComponent): def setup(self): self.add_input('x', 3.0) self.add_output('y', 4.0) def guess_nonlinear(self, inputs, outputs, resids): # inputs is read_only, should not be allowed resids['y'] = 0. group = Group() group.add_subsystem('px', IndepVarComp('x', 77.0)) group.add_subsystem('comp1', ImpWithInitial()) group.add_subsystem('comp2', ImpWithInitial()) group.connect('px.x', 'comp1.x') group.connect('comp1.y', 'comp2.x') group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['maxiter'] = 1 prob = Problem(model=group) prob.set_solver_print(level=0) prob.setup(check=False) with self.assertRaises(ValueError) as cm: prob.run_model() self.assertEqual( str(cm.exception), "Attempt to set value of 'y' in residual vector " "when it is read only.")
def test_scalar_with_guess_func_additional_input(self): model = Group(assembled_jac_type='dense') bal = BalanceComp() bal.add_balance('x') bal.add_input('guess_x', val=0.0) ivc = IndepVarComp() ivc.add_output(name='y_tgt', val=4) ivc.add_output(name='guess_x', val=2.5) exec_comp = ExecComp('y=x**2', x={'value': 1}, y={'value': 1}) model.add_subsystem(name='ivc', subsys=ivc, promotes_outputs=['y_tgt', 'guess_x']) model.add_subsystem(name='exec', subsys=exec_comp) model.add_subsystem(name='balance', subsys=bal) model.connect('guess_x', 'balance.guess_x') model.connect('y_tgt', 'balance.rhs:x') model.connect('balance.x', 'exec.x') model.connect('exec.y', 'balance.lhs:x') model.linear_solver = DirectSolver(assemble_jac=True) model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) prob = Problem(model) prob.setup() # run problem without a guess function prob['balance.x'] = .5 prob.run_model() assert_almost_equal(prob['balance.x'], 2.0, decimal=7) iters_no_guess = model.nonlinear_solver._iter_count # run problem with same initial value and a guess function def guess_function(inputs, outputs, resids): outputs['x'] = inputs['guess_x'] bal.options['guess_func'] = guess_function prob['balance.x'] = .5 prob.run_model() assert_almost_equal(prob['balance.x'], 2.0, decimal=7) iters_with_guess = model.nonlinear_solver._iter_count # verify it converges faster with the guess function self.assertTrue(iters_with_guess < iters_no_guess)
def setup(self): #Design variables self.add_subsystem('z_ini', IndepVarComp( 'z', np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0])), promotes=['*']) self.add_subsystem('x_aer_ini', IndepVarComp('x_aer', 1.0), promotes=['*']) self.add_subsystem('x_str_ini', IndepVarComp('x_str', np.array([1.0, 1.0])), promotes=['*']) self.add_subsystem('x_pro_ini', IndepVarComp('x_pro', 1.0), promotes=['*']) #Disciplines sap_group = Group() sap_group.add_subsystem('Structure', Structure(self.scalers), promotes=['*']) sap_group.add_subsystem('Aerodynamics', Aerodynamics(self.scalers), promotes=['*']) sap_group.add_subsystem('Propulsion', Propulsion(self.scalers), promotes=['*']) sap_group.nonlinear_solver = NonlinearBlockGS() sap_group.nonlinear_solver.options['atol'] = 1.0e-3 sap_group.linear_solver = ScipyKrylov() self.add_subsystem('Mda', sap_group, promotes=['*']) self.add_subsystem('Performance', Performance(self.scalers), promotes=['*']) #Constraints cstrs = [ 'con_theta_up = Theta*' + str(self.scalers['Theta']) + '-1.04', 'con_theta_low = 0.96-Theta*' + str(self.scalers['Theta']), 'con_dpdx = dpdx*' + str(self.scalers['dpdx']) + '-1.04', 'con1_esf = ESF*' + str(self.scalers['ESF']) + '-1.5', 'con2_esf = 0.5-ESF*' + str(self.scalers['ESF']), 'con_temp = Temp*' + str(self.scalers['Temp']) + '-1.02', 'con_dt=DT' ] for i in range(5): cstrs.append('con_sigma' + str(i + 1) + ' = sigma[' + str(i) + ']*' + str(self.scalers['sigma'][i]) + '-1.09') self.add_subsystem('Constraints', ExecComp(cstrs, sigma=np.zeros(5)), promotes=['*'])
def coupled_group(self): # type: () -> Optional[Group] """:obj:`Group`, optional: Group wrapping the coupled blocks with a converger specified in the CMDOWS file. If no coupled blocks are specified in the CMDOWS file this property is `None`. """ if self.coupled_blocks: coupled_group = Group() for uid in self.coupled_blocks: # Get the correct DisciplineComponent discipline_component = self.discipline_components[uid] # Change input variable names if they are provided as copies of coupling variables promotes = ['*'] # type: List[Union[str, Tuple[str, str]]] if not self.has_converger: for i in discipline_component.inputs_from_xml.keys(): if i in self.coupling_vars: promotes.append((i, self.coupling_vars[i]['copy'])) # Add the DisciplineComponent to the group coupled_group.add_subsystem(uid, self.discipline_components[uid], promotes) # Find the convergence type of the coupled group if self.has_converger: conv_type = self.elem_problem_def.find('problemFormulation/convergerType').text if conv_type == 'Gauss-Seidel': coupled_group.linear_solver = LinearBlockGS() coupled_group.nonlinear_solver = NonlinearBlockGS() elif conv_type == 'Jacobi': coupled_group.linear_solver = LinearBlockJac() coupled_group.nonlinear_solver = NonlinearBlockJac() else: raise RuntimeError('Specified convergerType "%s" is not supported.' % conv_type) else: coupled_group.linear_solver = LinearRunOnce() coupled_group.nonlinear_solver = NonLinearRunOnce() return coupled_group return None
def test_scalar_guess_func_using_outputs(self): # Implicitly solve -(ax^2 + bx) = c using a BalanceComp. # For a=1, b=-4 and c=3, there are solutions at x=1 and x=3. # Verify that we can set the guess value (and target a solution) based on outputs. ind = IndepVarComp() ind.add_output('a', 1) ind.add_output('b', -4) ind.add_output('c', 3) lhs = ExecComp('lhs=-(a*x**2+b*x)') bal = BalanceComp() def guess_function(inputs, outputs, residuals): if outputs['x'] < 0: return 5. else: return 0. bal.add_balance(name='x', rhs_name='c', guess_func=guess_function) model = Group() model.add_subsystem('ind_comp', ind, promotes_outputs=['a', 'b', 'c']) model.add_subsystem('lhs_comp', lhs, promotes_inputs=['a', 'b', 'x']) model.add_subsystem('bal_comp', bal, promotes_inputs=['c'], promotes_outputs=['x']) model.connect('lhs_comp.lhs', 'bal_comp.lhs:x') model.linear_solver = DirectSolver() model.nonlinear_solver = NewtonSolver(maxiter=1000, iprint=0) prob = Problem(model) prob.setup() # initial value of 'x' less than zero, guess should steer us to solution of 3. prob['bal_comp.x'] = -1 prob.run_model() assert_almost_equal(prob['bal_comp.x'], 3.0, decimal=7) # initial value of 'x' greater than zero, guess should steer us to solution of 1. prob['bal_comp.x'] = 99 prob.run_model() assert_almost_equal(prob['bal_comp.x'], 1.0, decimal=7)
def test_scalar_with_guess_func(self): n = 1 model = Group(assembled_jac_type='dense') def guess_function(inputs, outputs, residuals): outputs['x'] = np.sqrt(inputs['rhs:x']) bal = BalanceComp( 'x', guess_func=guess_function) # test guess_func as kwarg tgt = IndepVarComp(name='y_tgt', val=4) exec_comp = ExecComp('y=x**2', x={'value': 1}, y={'value': 1}) model.add_subsystem(name='target', subsys=tgt, promotes_outputs=['y_tgt']) model.add_subsystem(name='exec', subsys=exec_comp) model.add_subsystem(name='balance', subsys=bal) model.connect('y_tgt', 'balance.rhs:x') model.connect('balance.x', 'exec.x') model.connect('exec.y', 'balance.lhs:x') model.linear_solver = DirectSolver(assemble_jac=True) model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) prob = Problem(model) prob.setup() prob['balance.x'] = np.random.rand(n) prob.run_model() assert_almost_equal(prob['balance.x'], 2.0, decimal=7) # should converge with no iteration due to the guess function self.assertEqual(model.nonlinear_solver._iter_count, 1) cpd = prob.check_partials(out_stream=None) for (of, wrt) in cpd['balance']: assert_almost_equal(cpd['balance'][of, wrt]['abs error'], 0.0, decimal=5)
def test_guess_nonlinear_transfer_subbed2(self): # Test that data is transfered to a component before calling guess_nonlinear. class ImpWithInitial(ImplicitComponent): def setup(self): self.add_input('x', 3.0) self.add_output('y', 4.0) def solve_nonlinear(self, inputs, outputs): """ Do nothing. """ pass def apply_nonlinear(self, inputs, outputs, resids): """ Do nothing. """ resids['y'] = 1.0e-6 pass def guess_nonlinear(self, inputs, outputs, resids): # Passthrough outputs['y'] = inputs['x'] group = Group() sub = Group() group.add_subsystem('px', IndepVarComp('x', 77.0)) sub.add_subsystem('comp1', ImpWithInitial()) sub.add_subsystem('comp2', ImpWithInitial()) group.connect('px.x', 'sub.comp1.x') group.connect('sub.comp1.y', 'sub.comp2.x') group.add_subsystem('sub', sub) sub.nonlinear_solver = NewtonSolver() sub.nonlinear_solver.options['maxiter'] = 1 prob = Problem(model=group) prob.set_solver_print(level=0) prob.setup(check=False) prob.run_model() assert_rel_error(self, prob['sub.comp2.y'], 77., 1e-5)
def test_guess_nonlinear(self): class ImpWithInitial(QuadraticLinearize): def solve_nonlinear(self, inputs, outputs): """ Do nothing. """ pass def guess_nonlinear(self, inputs, outputs, resids): # Solution at x=1 and x=3. Default value takes us to the x=1 solution. Here # we set it to a value that will take us to the x=3 solution. outputs['x'] = 5.0 group = Group() group.add_subsystem('pa', IndepVarComp('a', 1.0)) group.add_subsystem('pb', IndepVarComp('b', 1.0)) group.add_subsystem('pc', IndepVarComp('c', 1.0)) group.add_subsystem('comp2', ImpWithInitial()) group.connect('pa.a', 'comp2.a') group.connect('pb.b', 'comp2.b') group.connect('pc.c', 'comp2.c') prob = Problem(model=group) group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['solve_subsystems'] = True group.nonlinear_solver.options['max_sub_solves'] = 1 group.linear_solver = ScipyIterativeSolver() prob.setup(check=False) prob['pa.a'] = 1. prob['pb.b'] = -4. prob['pc.c'] = 3. # Making sure that guess_nonlinear is called early enough to eradicate this. prob['comp2.x'] = np.NaN prob.run_model() assert_rel_error(self, prob['comp2.x'], 3.)
def test_scalar_with_guess_func(self): n = 1 model=Group(assembled_jac_type='dense') def guess_function(inputs, outputs, residuals): outputs['x'] = np.sqrt(inputs['rhs:x']) bal = BalanceComp('x', guess_func=guess_function) # test guess_func as kwarg tgt = IndepVarComp(name='y_tgt', val=4) exec_comp = ExecComp('y=x**2', x={'value': 1}, y={'value': 1}) model.add_subsystem(name='target', subsys=tgt, promotes_outputs=['y_tgt']) model.add_subsystem(name='exec', subsys=exec_comp) model.add_subsystem(name='balance', subsys=bal) model.connect('y_tgt', 'balance.rhs:x') model.connect('balance.x', 'exec.x') model.connect('exec.y', 'balance.lhs:x') model.linear_solver = DirectSolver(assemble_jac=True) model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) prob = Problem(model) prob.setup() prob['balance.x'] = np.random.rand(n) prob.run_model() assert_almost_equal(prob['balance.x'], 2.0, decimal=7) # should converge with no iteration due to the guess function self.assertEqual(model.nonlinear_solver._iter_count, 1) cpd = prob.check_partials(out_stream=None) for (of, wrt) in cpd['balance']: assert_almost_equal(cpd['balance'][of, wrt]['abs error'], 0.0, decimal=5)
def test_guess_nonlinear(self): class ImpWithInitial(QuadraticLinearize): def solve_nonlinear(self, inputs, outputs): """ Do nothing. """ pass def guess_nonlinear(self, inputs, outputs, resids): # Solution at x=1 and x=3. Default value takes us to the x=1 solution. Here # we set it to a value that will take us to the x=3 solution. outputs['x'] = 5.0 group = Group() group.add_subsystem('pa', IndepVarComp('a', 1.0)) group.add_subsystem('pb', IndepVarComp('b', 1.0)) group.add_subsystem('pc', IndepVarComp('c', 1.0)) group.add_subsystem('comp2', ImpWithInitial()) group.connect('pa.a', 'comp2.a') group.connect('pb.b', 'comp2.b') group.connect('pc.c', 'comp2.c') prob = Problem(model=group) group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['solve_subsystems'] = True group.nonlinear_solver.options['max_sub_solves'] = 1 group.linear_solver = ScipyKrylov() prob.setup(check=False) prob['pa.a'] = 1. prob['pb.b'] = -4. prob['pc.c'] = 3. # Making sure that guess_nonlinear is called early enough to eradicate this. prob['comp2.x'] = np.NaN prob.run_model() assert_rel_error(self, prob['comp2.x'], 3.)
def setup(self): surfaces = self.options['surfaces'] coupled = Group() for surface in surfaces: name = surface['name'] # Connect the output of the loads component with the FEM # displacement parameter. This links the coupling within the coupled # group that necessitates the subgroup solver. coupled.connect(name + '_loads.loads', name + '.loads') # Perform the connections with the modified names within the # 'aero_states' group. coupled.connect(name + '.normals', 'aero_states.' + name + '_normals') coupled.connect(name + '.def_mesh', 'aero_states.' + name + '_def_mesh') # Connect the results from 'coupled' to the performance groups coupled.connect(name + '.def_mesh', name + '_loads.def_mesh') coupled.connect('aero_states.' + name + '_sec_forces', name + '_loads.sec_forces') # Connect the results from 'aero_states' to the performance groups self.connect('coupled.aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connection performance functional variables self.connect(name + '_perf.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf.CD', 'total_perf.' + name + '_CD') self.connect('coupled.aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.connect('coupled.' + name + '.chords', name + '_perf.aero_funcs.chords') # Connect parameters from the 'coupled' group to the performance # groups for the individual surfaces. self.connect('coupled.' + name + '.disp', name + '_perf.disp') self.connect('coupled.' + name + '.S_ref', name + '_perf.S_ref') self.connect('coupled.' + name + '.widths', name + '_perf.widths') # self.connect('coupled.' + name + '.chords', name + '_perf.chords') self.connect('coupled.' + name + '.lengths', name + '_perf.lengths') self.connect('coupled.' + name + '.cos_sweep', name + '_perf.cos_sweep') # Connect parameters from the 'coupled' group to the total performance group. self.connect('coupled.' + name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect('coupled.' + name + '.widths', 'total_perf.' + name + '_widths') self.connect('coupled.' + name + '.chords', 'total_perf.' + name + '_chords') self.connect('coupled.' + name + '.b_pts', 'total_perf.' + name + '_b_pts') # Add components to the 'coupled' group for each surface. # The 'coupled' group must contain all components and parameters # needed to converge the aerostructural system. coupled_AS_group = CoupledAS(surface=surface) if surface['distributed_fuel_weight']: promotes = ['load_factor'] else: promotes = [] coupled.add_subsystem(name, coupled_AS_group, promotes_inputs=promotes) # Add a single 'aero_states' component for the whole system within the # coupled group. coupled.add_subsystem('aero_states', VLMStates(surfaces=surfaces), promotes_inputs=['v', 'alpha', 'rho']) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. for surface in surfaces: name = surface['name'] # Add a loads component to the coupled group coupled.add_subsystem(name + '_loads', LoadTransfer(surface=surface)) """ ### Change the solver settings here ### """ # Set solver properties for the coupled group # coupled.linear_solver = ScipyIterativeSolver() # coupled.linear_solver.precon = LinearRunOnce() coupled.nonlinear_solver = NonlinearBlockGS(use_aitken=True) coupled.nonlinear_solver.options['maxiter'] = 100 coupled.nonlinear_solver.options['atol'] = 1e-7 coupled.nonlinear_solver.options['rtol'] = 1e-30 # coupled.linear_solver = DirectSolver() coupled.linear_solver = DirectSolver(assemble_jac=True) coupled.options['assembled_jac_type'] = 'csc' # coupled.nonlinear_solver = NewtonSolver(solve_subsystems=True) # coupled.nonlinear_solver.options['maxiter'] = 50 coupled.nonlinear_solver.options['iprint'] = 2 """ ### End change of solver settings ### """ # Add the coupled group to the model problem self.add_subsystem('coupled', coupled, promotes_inputs=['v', 'alpha', 'rho']) for surface in surfaces: name = surface['name'] # Add a performance group which evaluates the data after solving # the coupled system perf_group = CoupledPerformance(surface=surface) self.add_subsystem(name + '_perf', perf_group, promotes_inputs=['rho', 'v', 'alpha', 're', 'Mach_number']) # Add functionals to evaluate performance of the system. # Note that only the interesting results are promoted here; not all # of the parameters. self.add_subsystem('total_perf', TotalPerformance(surfaces=surfaces, user_specified_Sref=self.options['user_specified_Sref'], internally_connect_fuelburn=self.options['internally_connect_fuelburn']), promotes_inputs=['v', 'rho', 'empty_cg', 'total_weight', 'CT', 'speed_of_sound', 'R', 'Mach_number', 'W0', 'load_factor', 'S_ref_total'], promotes_outputs=['L_equals_W', 'fuelburn', 'CL', 'CD', 'CM', 'cg'])
def test_scalar_guess_func_using_outputs(self): model = Group() ind = IndepVarComp() ind.add_output('a', 1) ind.add_output('b', -4) ind.add_output('c', 3) lhs = ExecComp('lhs=-(a*x**2+b*x)') bal = BalanceComp(name='x', rhs_name='c') model.add_subsystem('ind_comp', ind, promotes_outputs=['a', 'b', 'c']) model.add_subsystem('lhs_comp', lhs, promotes_inputs=['a', 'b', 'x']) model.add_subsystem('bal_comp', bal, promotes_inputs=['c'], promotes_outputs=['x']) model.connect('lhs_comp.lhs', 'bal_comp.lhs:x') model.linear_solver = DirectSolver() model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) # first verify behavior of the balance comp without the guess function # at initial conditions x=5, x=0 and x=-1 prob = Problem(model) prob.setup() # default solution with initial value of 5 is x=3. prob['x'] = 5 prob.run_model() assert_almost_equal(prob['x'], 3.0, decimal=7) # default solution with initial value of 0 is x=1. prob['x'] = 0 prob.run_model() assert_almost_equal(prob['x'], 1.0, decimal=7) # default solution with initial value of -1 is x=1. prob['x'] = -1 prob.run_model() assert_almost_equal(prob['x'], 1.0, decimal=7) # now use a guess function that steers us to the x=3 solution only # if the initial value of x is less than zero def guess_function(inputs, outputs, residuals): if outputs['x'] < 0: outputs['x'] = 3. bal.options['guess_func'] = guess_function # solution with initial value of 5 is still x=3. prob['x'] = 5 prob.run_model() assert_almost_equal(prob['x'], 3.0, decimal=7) # solution with initial value of 0 is still x=1. prob['x'] = 0 prob.run_model() assert_almost_equal(prob['x'], 1.0, decimal=7) # solution with initial value of -1 is now x=3. prob['x'] = -1 prob.run_model() assert_almost_equal(prob['x'], 3.0, decimal=7)
def test_simple_external_code_implicit_comp(self): from openmdao.api import Group, NewtonSolver, Problem, IndepVarComp, DirectSolver, \ ExternalCodeImplicitComp class MachExternalCodeComp(ExternalCodeImplicitComp): def initialize(self): self.options.declare('super_sonic', types=bool) def setup(self): self.add_input('area_ratio', val=1.0, units=None) self.add_output('mach', val=1., units=None) self.declare_partials(of='mach', wrt='area_ratio', method='fd') self.input_file = 'mach_input.dat' self.output_file = 'mach_output.dat' # providing these are optional; the component will verify that any input # files exist before execution and that the output files exist after. self.options['external_input_files'] = [self.input_file] self.options['external_output_files'] = [self.output_file] self.options['command_apply'] = [ 'python', 'extcode_mach.py', self.input_file, self.output_file, ] self.options['command_solve'] = [ 'python', 'extcode_mach.py', self.input_file, self.output_file, ] def apply_nonlinear(self, inputs, outputs, residuals): with open(self.input_file, 'w') as input_file: input_file.write('residuals\n') input_file.write('{}\n'.format(inputs['area_ratio'][0])) input_file.write('{}\n'.format(outputs['mach'][0])) # the parent apply_nonlinear function actually runs the external code super(MachExternalCodeComp, self).apply_nonlinear(inputs, outputs, residuals) # parse the output file from the external code and set the value of mach with open(self.output_file, 'r') as output_file: mach = float(output_file.read()) residuals['mach'] = mach def solve_nonlinear(self, inputs, outputs): with open(self.input_file, 'w') as input_file: input_file.write('outputs\n') input_file.write('{}\n'.format(inputs['area_ratio'][0])) input_file.write('{}\n'.format(self.options['super_sonic'])) # the parent apply_nonlinear function actually runs the external code super(MachExternalCodeComp, self).solve_nonlinear(inputs, outputs) # parse the output file from the external code and set the value of mach with open(self.output_file, 'r') as output_file: mach = float(output_file.read()) outputs['mach'] = mach group = Group() group.add_subsystem('ar', IndepVarComp('area_ratio', 0.5)) mach_comp = group.add_subsystem('comp', MachExternalCodeComp(), promotes=['*']) prob = Problem(model=group) group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['solve_subsystems'] = True group.nonlinear_solver.options['iprint'] = 0 group.nonlinear_solver.options['maxiter'] = 20 group.linear_solver = DirectSolver() prob.setup(check=False) area_ratio = 1.3 super_sonic = False prob['area_ratio'] = area_ratio mach_comp.options['super_sonic'] = super_sonic prob.run_model() assert_rel_error(self, prob['mach'], mach_solve(area_ratio, super_sonic=super_sonic), 1e-8) area_ratio = 1.3 super_sonic = True prob['area_ratio'] = area_ratio mach_comp.options['super_sonic'] = super_sonic prob.run_model() assert_rel_error(self, prob['mach'], mach_solve(area_ratio, super_sonic=super_sonic), 1e-8)
def setup(self): super(ImplicitTMIntegrator, self).setup() ode_function = self.options['ode_function'] method = self.options['method'] starting_coeffs = self.options['starting_coeffs'] has_starting_method = method.starting_method is not None is_starting_method = starting_coeffs is not None states = ode_function._states static_parameters = ode_function._static_parameters dynamic_parameters = ode_function._dynamic_parameters time_units = ode_function._time_options['units'] starting_norm_times, my_norm_times = self._get_meta() glm_A, glm_B, glm_U, glm_V, num_stages, num_step_vars = self._get_method() num_times = len(my_norm_times) num_stages = method.num_stages num_step_vars = method.num_values glm_A = method.A glm_B = method.B glm_U = method.U glm_V = method.V # ------------------------------------------------------------------------------------ integration_group = Group() self.add_subsystem('integration_group', integration_group) for i_step in range(len(my_norm_times) - 1): group = Group(assembled_jac_type='dense') group_old_name = 'integration_group.step_%i' % (i_step - 1) group_new_name = 'integration_group.step_%i' % i_step integration_group.add_subsystem(group_new_name.split('.')[1], group) comp = self._create_ode(num_stages) group.add_subsystem('ode_comp', comp) if ode_function._time_options['targets']: self.connect('time_comp.stage_times', ['.'.join((group_new_name + '.ode_comp', t)) for t in ode_function._time_options['targets']], src_indices=i_step * (num_stages) + np.arange(num_stages)) if len(static_parameters) > 0: self._connect_multiple( self._get_static_parameter_names('static_parameter_comp', 'out'), self._get_static_parameter_names(group_new_name + '.ode_comp', 'targets'), src_indices_list=[[0] * num_stages for _ in range(len(static_parameters))] ) if len(dynamic_parameters) > 0: src_indices_list = [] for parameter_name, value in iteritems(dynamic_parameters): size = np.prod(value['shape']) shape = value['shape'] arange = np.arange(((len(my_norm_times) - 1) * num_stages * size)).reshape( ((len(my_norm_times) - 1, num_stages,) + shape)) src_indices = arange[i_step, :, :] src_indices_list.append(src_indices.flat) self._connect_multiple( self._get_dynamic_parameter_names('dynamic_parameter_comp', 'out'), self._get_dynamic_parameter_names(group_new_name + '.ode_comp', 'targets'), src_indices_list, ) comp = ImplicitTMStageComp( states=states, time_units=time_units, num_stages=num_stages, num_step_vars=num_step_vars, glm_A=glm_A, glm_U=glm_U, i_step=i_step, ) group.add_subsystem('stage_comp', comp) self.connect('time_comp.h_vec', group_new_name + '.stage_comp.h', src_indices=i_step) comp = ImplicitTMStepComp( states=states, time_units=time_units, num_stages=num_stages, num_step_vars=num_step_vars, glm_B=glm_B, glm_V=glm_V, i_step=i_step, ) group.add_subsystem('step_comp', comp) self.connect('time_comp.h_vec', group_new_name + '.step_comp.h', src_indices=i_step) self._connect_multiple( self._get_state_names(group_new_name + '.ode_comp', 'rate_source'), self._get_state_names(group_new_name + '.step_comp', 'F', i_step=i_step), ) self._connect_multiple( self._get_state_names(group_new_name + '.ode_comp', 'rate_source'), self._get_state_names(group_new_name + '.stage_comp', 'F', i_step=i_step), ) self._connect_multiple( self._get_state_names(group_new_name + '.stage_comp', 'Y', i_step=i_step), self._get_state_names(group_new_name + '.ode_comp', 'targets'), ) if i_step == 0: self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names(group_new_name + '.step_comp', 'y_old', i_step=i_step), ) self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names(group_new_name + '.stage_comp', 'y_old', i_step=i_step), ) else: self._connect_multiple( self._get_state_names(group_old_name + '.step_comp', 'y_new', i_step=i_step - 1), self._get_state_names(group_new_name + '.step_comp', 'y_old', i_step=i_step), ) self._connect_multiple( self._get_state_names(group_old_name + '.step_comp', 'y_new', i_step=i_step - 1), self._get_state_names(group_new_name + '.stage_comp', 'y_old', i_step=i_step), ) group.nonlinear_solver = NewtonSolver(iprint=2, maxiter=100) group.linear_solver = DirectSolver(assemble_jac=True) promotes = [] promotes.extend([get_name('state', state_name) for state_name in states]) if is_starting_method: promotes.extend([get_name('starting', state_name) for state_name in states]) comp = TMOutputComp( states=states, num_starting_times=len(starting_norm_times), num_my_times=len(my_norm_times), num_step_vars=num_step_vars, starting_coeffs=starting_coeffs) self.add_subsystem('output_comp', comp, promotes_outputs=promotes) if has_starting_method: self._connect_multiple( self._get_state_names('starting_system', 'state'), self._get_state_names('output_comp', 'starting_state'), ) for i_step in range(len(my_norm_times)): if i_step == 0: self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names('output_comp', 'y', i_step=i_step), ) else: self._connect_multiple( self._get_state_names('integration_group.step_%i' % (i_step - 1) + '.step_comp', 'y_new', i_step=i_step - 1), self._get_state_names('output_comp', 'y', i_step=i_step), )
def setup(self): num_times = self.options['num_times'] num_cp = self.options['num_cp'] step_size = self.options['step_size'] cubesat = self.options['cubesat'] mtx = self.options['mtx'] shape = (3, num_times) drag_unit_vec = np.outer( np.array([0., 0., 1.]), np.ones(num_times), ) comp = IndepVarComp() comp.add_output('drag_unit_vec_t_3xn', val=drag_unit_vec) comp.add_output('dry_mass', val=cubesat['dry_mass'], shape=num_times) comp.add_output('radius_earth_km', val=cubesat['radius_earth_km'], shape=num_times) for var_name in ['initial_orbit_state']: comp.add_output(var_name, val=cubesat[var_name]) self.add_subsystem('input_comp', comp, promotes=['*']) # comp = InitialOrbitComp() # self.add_subsystem('initial_orbit_comp', comp, promotes=['*']) comp = LinearCombinationComp( shape=(num_times, ), out_name='mass', coeffs_dict=dict(dry_mass=1., propellant_mass=1.), ) self.add_subsystem('mass_comp', comp, promotes=['*']) if 1: coupled_group = Group() comp = LinearCombinationComp( shape=shape, out_name='force_3xn', coeffs_dict=dict(thrust_3xn=1., drag_3xn=1.), ) coupled_group.add_subsystem('force_3xn_comp', comp, promotes=['*']) comp = RelativeOrbitRK4Comp( num_times=num_times, step_size=step_size, ) coupled_group.add_subsystem('relative_orbit_rk4_comp', comp, promotes=['*']) comp = LinearCombinationComp( shape=(6, num_times), out_name='orbit_state', coeffs_dict=dict( relative_orbit_state=1., reference_orbit_state=1., ), ) coupled_group.add_subsystem('orbit_state_comp', comp, promotes=['*']) comp = LinearCombinationComp( shape=(6, num_times), out_name='orbit_state_km', coeffs_dict=dict(orbit_state=1.e-3), ) coupled_group.add_subsystem('orbit_state_km_comp', comp, promotes=['*']) comp = RotMtxTIComp(num_times=num_times) coupled_group.add_subsystem('rot_mtx_t_i_3x3xn_comp', comp, promotes=['*']) comp = ArrayReorderComp( in_shape=(3, 3, num_times), out_shape=(3, 3, num_times), in_subscripts='ijn', out_subscripts='jin', in_name='rot_mtx_t_i_3x3xn', out_name='rot_mtx_i_t_3x3xn', ) coupled_group.add_subsystem('rot_mtx_i_t_3x3xn_comp', comp, promotes=['*']) comp = MtxVecComp( num_times=num_times, mtx_name='rot_mtx_i_t_3x3xn', vec_name='drag_unit_vec_t_3xn', out_name='drag_unit_vec_3xn', ) coupled_group.add_subsystem('drag_unit_vec_3xn_comp', comp, promotes=['*']) comp = PowerCombinationComp(shape=shape, out_name='drag_3xn', powers_dict=dict( drag_unit_vec_3xn=1., drag_scalar_3xn=1., )) coupled_group.add_subsystem('drag_3xn_comp', comp, promotes=['*']) coupled_group.nonlinear_solver = NonlinearBlockGS(iprint=0, maxiter=40, atol=1e-14, rtol=1e-12) coupled_group.linear_solver = LinearBlockGS(iprint=0, maxiter=40, atol=1e-14, rtol=1e-12) self.add_subsystem('coupled_group', coupled_group, promotes=['*']) comp = OrbitStateDecompositionComp( num_times=num_times, position_name='position_km', velocity_name='velocity_km_s', orbit_state_name='orbit_state_km', ) self.add_subsystem('orbit_state_decomposition_comp', comp, promotes=['*']) comp = LinearCombinationComp( shape=shape, out_name='position', coeffs_dict=dict(position_km=1.e3), ) self.add_subsystem('position_comp', comp, promotes=['*']) comp = LinearCombinationComp( shape=shape, out_name='velocity', coeffs_dict=dict(velocity_km_s=1.e3), ) self.add_subsystem('velocity_comp', comp, promotes=['*']) # group = DecomposeVectorGroup( num_times=num_times, vec_name='position_km', norm_name='radius_km', unit_vec_name='position_unit_vec', ) self.add_subsystem('position_decomposition_group', group, promotes=['*']) group = DecomposeVectorGroup( num_times=num_times, vec_name='velocity_km_s', norm_name='speed_km_s', unit_vec_name='velocity_unit_vec', ) self.add_subsystem('velocity_decomposition_group', group, promotes=['*']) # comp = LinearCombinationComp( shape=(num_times, ), out_name='altitude_km', coeffs_dict=dict(radius_km=1., radius_earth_km=-1.), ) self.add_subsystem('altitude_km_comp', comp, promotes=['*']) comp = KSComp( in_name='altitude_km', out_name='ks_altitude_km', shape=(1, ), constraint_size=num_times, rho=100., lower_flag=True, ) comp.add_constraint('ks_altitude_km', lower=450.) self.add_subsystem('ks_altitude_km_comp', comp, promotes=['*']) comp = PowerCombinationComp(shape=( 6, num_times, ), out_name='relative_orbit_state_sq', powers_dict={ 'relative_orbit_state': 2., }) self.add_subsystem('relative_orbit_state_sq_comp', comp, promotes=['*']) comp = ScalarContractionComp( shape=( 6, num_times, ), out_name='relative_orbit_state_sq_sum', in_name='relative_orbit_state_sq', ) self.add_subsystem('relative_orbit_state_sq_sum_comp', comp, promotes=['*'])
def test_simple_external_code_implicit_comp(self): from openmdao.api import Group, NewtonSolver, Problem, IndepVarComp, DirectSolver, \ ExternalCodeImplicitComp class MachExternalCodeComp(ExternalCodeImplicitComp): def initialize(self): self.options.declare('super_sonic', types=bool) def setup(self): self.add_input('area_ratio', val=1.0, units=None) self.add_output('mach', val=1., units=None) self.declare_partials(of='mach', wrt='area_ratio', method='fd') self.input_file = 'mach_input.dat' self.output_file = 'mach_output.dat' # providing these are optional; the component will verify that any input # files exist before execution and that the output files exist after. self.options['external_input_files'] = [self.input_file] self.options['external_output_files'] = [self.output_file] self.options['command_apply'] = [ 'python', 'extcode_mach.py', self.input_file, self.output_file, ] self.options['command_solve'] = [ 'python', 'extcode_mach.py', self.input_file, self.output_file, ] def apply_nonlinear(self, inputs, outputs, residuals): with open(self.input_file, 'w') as input_file: input_file.write('residuals\n') input_file.write('{}\n'.format(inputs['area_ratio'][0])) input_file.write('{}\n'.format(outputs['mach'][0])) # the parent apply_nonlinear function actually runs the external code super(MachExternalCodeComp, self).apply_nonlinear(inputs, outputs, residuals) # parse the output file from the external code and set the value of mach with open(self.output_file, 'r') as output_file: mach = float(output_file.read()) residuals['mach'] = mach def solve_nonlinear(self, inputs, outputs): with open(self.input_file, 'w') as input_file: input_file.write('outputs\n') input_file.write('{}\n'.format(inputs['area_ratio'][0])) input_file.write('{}\n'.format( self.options['super_sonic'])) # the parent apply_nonlinear function actually runs the external code super(MachExternalCodeComp, self).solve_nonlinear(inputs, outputs) # parse the output file from the external code and set the value of mach with open(self.output_file, 'r') as output_file: mach = float(output_file.read()) outputs['mach'] = mach group = Group() group.add_subsystem('ar', IndepVarComp('area_ratio', 0.5)) mach_comp = group.add_subsystem('comp', MachExternalCodeComp(), promotes=['*']) prob = Problem(model=group) group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['solve_subsystems'] = True group.nonlinear_solver.options['iprint'] = 0 group.nonlinear_solver.options['maxiter'] = 20 group.linear_solver = DirectSolver() prob.setup(check=False) area_ratio = 1.3 super_sonic = False prob['area_ratio'] = area_ratio mach_comp.options['super_sonic'] = super_sonic prob.run_model() assert_rel_error(self, prob['mach'], mach_solve(area_ratio, super_sonic=super_sonic), 1e-8) area_ratio = 1.3 super_sonic = True prob['area_ratio'] = area_ratio mach_comp.options['super_sonic'] = super_sonic prob.run_model() assert_rel_error(self, prob['mach'], mach_solve(area_ratio, super_sonic=super_sonic), 1e-8)
def setup(self): super(VectorizedIntegrator, self).setup() ode_function = self.options['ode_function'] method = self.options['method'] starting_coeffs = self.options['starting_coeffs'] formulation = self.options['formulation'] has_starting_method = method.starting_method is not None is_starting_method = starting_coeffs is not None states = ode_function._states static_parameters = ode_function._static_parameters dynamic_parameters = ode_function._dynamic_parameters time_units = ode_function._time_options['units'] starting_norm_times, my_norm_times = self._get_meta() glm_A, glm_B, glm_U, glm_V, num_stages, num_step_vars = self._get_method( ) num_times = len(my_norm_times) # ------------------------------------------------------------------------------------ integration_group = Group(assembled_jac_type='dense') self.add_subsystem('integration_group', integration_group) if formulation == 'optimizer-based': comp = IndepVarComp() for state_name, state in iteritems(states): comp.add_output('Y:%s' % state_name, shape=( num_times - 1, num_stages, ) + state['shape'], units=state['units']) comp.add_design_var('Y:%s' % state_name) integration_group.add_subsystem('desvars_comp', comp) elif formulation == 'solver-based': comp = IndepVarComp() for state_name, state in iteritems(states): comp.add_output('Y:%s' % state_name, val=0., shape=( num_times - 1, num_stages, ) + state['shape'], units=state['units']) integration_group.add_subsystem('dummy_comp', comp) comp = self._create_ode((num_times - 1) * num_stages) integration_group.add_subsystem('ode_comp', comp) if ode_function._time_options['targets']: self.connect( 'time_comp.stage_times', [ '.'.join(('integration_group.ode_comp', t)) for t in ode_function._time_options['targets'] ], ) if len(static_parameters) > 0: self._connect_multiple( self._get_static_parameter_names('static_parameter_comp', 'out'), self._get_static_parameter_names('integration_group.ode_comp', 'targets'), [ np.array([0] * self._get_stage_norm_times(), np.int) for _ in range(len(static_parameters)) ]) if len(dynamic_parameters) > 0: self._connect_multiple( self._get_dynamic_parameter_names('dynamic_parameter_comp', 'out'), self._get_dynamic_parameter_names('integration_group.ode_comp', 'targets'), ) comp = VectorizedStageStepComp( states=states, time_units=time_units, num_times=num_times, num_stages=num_stages, num_step_vars=num_step_vars, glm_A=glm_A, glm_U=glm_U, glm_B=glm_B, glm_V=glm_V, ) integration_group.add_subsystem('vectorized_stagestep_comp', comp) self.connect('time_comp.h_vec', 'integration_group.vectorized_stagestep_comp.h_vec') comp = VectorizedStepComp( states=states, time_units=time_units, num_times=num_times, num_stages=num_stages, num_step_vars=num_step_vars, glm_B=glm_B, glm_V=glm_V, ) self.add_subsystem('vectorized_step_comp', comp) self.connect('time_comp.h_vec', 'vectorized_step_comp.h_vec') self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names( 'integration_group.vectorized_stagestep_comp', 'y0'), ) self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names('vectorized_step_comp', 'y0'), ) comp = VectorizedOutputComp( states=states, num_starting_times=len(starting_norm_times), num_my_times=len(my_norm_times), num_step_vars=num_step_vars, starting_coeffs=starting_coeffs, ) promotes = [] promotes.extend( [get_name('state', state_name) for state_name in states]) if is_starting_method: promotes.extend( [get_name('starting', state_name) for state_name in states]) self.add_subsystem('output_comp', comp, promotes_outputs=promotes) if has_starting_method: self._connect_multiple( self._get_state_names('starting_system', 'state'), self._get_state_names('output_comp', 'starting_state'), ) src_indices_to_ode = [] src_indices_from_ode = [] for state_name, state in iteritems(states): size = np.prod(state['shape']) shape = state['shape'] src_indices_to_ode.append( np.arange( (num_times - 1) * num_stages * size).reshape(((num_times - 1) * num_stages, ) + shape)) src_indices_from_ode.append( np.arange((num_times - 1) * num_stages * size).reshape(( num_times - 1, num_stages, ) + shape)) src_indices_to_ode = [ np.array(idx).squeeze() for idx in src_indices_to_ode ] self._connect_multiple( self._get_state_names('vectorized_step_comp', 'y'), self._get_state_names('output_comp', 'y'), ) self._connect_multiple( self._get_state_names('integration_group.ode_comp', 'rate_source'), self._get_state_names('vectorized_step_comp', 'F'), src_indices_from_ode, ) self._connect_multiple( self._get_state_names('integration_group.ode_comp', 'rate_source'), self._get_state_names( 'integration_group.vectorized_stagestep_comp', 'F'), src_indices_from_ode, ) if formulation == 'solver-based': self._connect_multiple( self._get_state_names( 'integration_group.vectorized_stagestep_comp', 'Y_out'), self._get_state_names('integration_group.ode_comp', 'targets'), src_indices_to_ode, ) self._connect_multiple( self._get_state_names('integration_group.dummy_comp', 'Y'), self._get_state_names( 'integration_group.vectorized_stagestep_comp', 'Y_in'), ) elif formulation == 'optimizer-based': self._connect_multiple( self._get_state_names('integration_group.desvars_comp', 'Y'), self._get_state_names('integration_group.ode_comp', 'targets'), src_indices_to_ode, ) self._connect_multiple( self._get_state_names('integration_group.desvars_comp', 'Y'), self._get_state_names( 'integration_group.vectorized_stagestep_comp', 'Y_in'), ) for state_name, state in iteritems(states): integration_group.add_constraint( 'vectorized_stagestep_comp.Y_out:%s' % state_name, equals=0., vectorize_derivs=True, ) if has_starting_method: self.starting_system.options['formulation'] = self.options[ 'formulation'] if formulation == 'solver-based': if 1: integration_group.nonlinear_solver = NonlinearBlockGS( iprint=2, maxiter=40, atol=1e-14, rtol=1e-12) else: integration_group.nonlinear_solver = NewtonSolver(iprint=2, maxiter=100) if 1: integration_group.linear_solver = LinearBlockGS(iprint=1, maxiter=40, atol=1e-14, rtol=1e-12) else: integration_group.linear_solver = DirectSolver( assemble_jac=True, iprint=1)
def setup(self): surfaces = self.options['surfaces'] coupled = Group() for surface in surfaces: name = surface['name'] # Connect the output of the loads component with the FEM # displacement parameter. This links the coupling within the coupled # group that necessitates the subgroup solver. coupled.connect(name + '_loads.loads', name + '.loads') # Perform the connections with the modified names within the # 'aero_states' group. coupled.connect(name + '.normals', 'aero_states.' + name + '_normals') coupled.connect(name + '.def_mesh', 'aero_states.' + name + '_def_mesh') # Connect the results from 'coupled' to the performance groups coupled.connect(name + '.def_mesh', name + '_loads.def_mesh') coupled.connect('aero_states.' + name + '_sec_forces', name + '_loads.sec_forces') # Connect the results from 'aero_states' to the performance groups self.connect('coupled.aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connection performance functional variables self.connect(name + '_perf.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf.CD', 'total_perf.' + name + '_CD') self.connect('coupled.aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.connect('coupled.' + name + '.chords', name + '_perf.aero_funcs.chords') # Connect parameters from the 'coupled' group to the performance # groups for the individual surfaces. self.connect('coupled.' + name + '.disp', name + '_perf.disp') self.connect('coupled.' + name + '.S_ref', name + '_perf.S_ref') self.connect('coupled.' + name + '.widths', name + '_perf.widths') # self.connect('coupled.' + name + '.chords', name + '_perf.chords') self.connect('coupled.' + name + '.lengths', name + '_perf.lengths') self.connect('coupled.' + name + '.cos_sweep', name + '_perf.cos_sweep') # Connect parameters from the 'coupled' group to the total performance group. self.connect('coupled.' + name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect('coupled.' + name + '.widths', 'total_perf.' + name + '_widths') self.connect('coupled.' + name + '.chords', 'total_perf.' + name + '_chords') self.connect('coupled.' + name + '.b_pts', 'total_perf.' + name + '_b_pts') # Add components to the 'coupled' group for each surface. # The 'coupled' group must contain all components and parameters # needed to converge the aerostructural system. coupled_AS_group = CoupledAS(surface=surface) if surface['distributed_fuel_weight']: promotes = ['load_factor'] else: promotes = [] coupled.add_subsystem(name, coupled_AS_group, promotes_inputs=promotes) # Add a single 'aero_states' component for the whole system within the # coupled group. coupled.add_subsystem('aero_states', VLMStates(surfaces=surfaces), promotes_inputs=['v', 'alpha', 'rho']) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. for surface in surfaces: name = surface['name'] # Add a loads component to the coupled group coupled.add_subsystem(name + '_loads', LoadTransfer(surface=surface)) """ ### Change the solver settings here ### """ # Set solver properties for the coupled group # coupled.linear_solver = ScipyIterativeSolver() # coupled.linear_solver.precon = LinearRunOnce() coupled.nonlinear_solver = NonlinearBlockGS(use_aitken=True) coupled.nonlinear_solver.options['maxiter'] = 50 coupled.nonlinear_solver.options['atol'] = 5e-6 coupled.nonlinear_solver.options['rtol'] = 1e-12 # coupled.linear_solver = DirectSolver() coupled.linear_solver = DirectSolver(assemble_jac=True) coupled.options['assembled_jac_type'] = 'csc' # coupled.nonlinear_solver = NewtonSolver(solve_subsystems=True) # coupled.nonlinear_solver.options['maxiter'] = 50 coupled.nonlinear_solver.options['iprint'] = 0 """ ### End change of solver settings ### """ # Add the coupled group to the model problem self.add_subsystem('coupled', coupled, promotes_inputs=['v', 'alpha', 'rho']) for surface in surfaces: name = surface['name'] # Add a performance group which evaluates the data after solving # the coupled system perf_group = CoupledPerformance(surface=surface) self.add_subsystem(name + '_perf', perf_group, promotes_inputs=["rho", "v", "alpha", "re", "M"]) # Add functionals to evaluate performance of the system. # Note that only the interesting results are promoted here; not all # of the parameters. self.add_subsystem('total_perf', TotalPerformance(surfaces=surfaces, user_specified_Sref=self.options['user_specified_Sref'], internally_connect_fuelburn=self.options['internally_connect_fuelburn']), promotes_inputs=['v', 'rho', 'empty_cg', 'total_weight', 'CT', 'a', 'R', 'M', 'W0', 'load_factor', 'S_ref_total'], promotes_outputs=['L_equals_W', 'fuelburn', 'CL', 'CD', 'CM', 'cg'])
prob.model.connect("ivc.Cla_t", "Aerodynamics.cla_tail.Cla_t") prob.model.connect("ivc.S_t", "Aerodynamics.cla_tail.S_t") prob.model.connect("ivc.d_fuse_t", "Aerodynamics.cla_tail.d_fuse_t") prob.model.connect("ivc.b_t", "Aerodynamics.cla_tail.b_t") prob.model.connect("ivc.b", "Aerodynamics.wing_param.b") prob.model.connect("ivc.taper", "Aerodynamics.wing_param.taper") prob.model.connect("ivc.croot", "Aerodynamics.wing_param.croot") prob.model.connect("ivc.W_0", "Weights.W_lg.W_0") prob.model.connect("ivc.S_t", "Weights.W_tail.S_t") prob.model.connect("ivc.S", "Weights.W_wing.S") prob.model.connect("ivc.AR", "Weights.W_wing.AR") prob.model.connect("ivc.q", "Weights.W_wing.q") prob.model.connect("ivc.taper", "Weights.W_wing.taper") prob.model.connect("ivc.thickness_ratio", "Weights.W_wing.thickness_ratio") prob.model.connect("ivc.n", "Weights.W_wing.n") prob.model.connect("ivc.W_0", "Weights.W_wing.W_0") prob.model.connect("ivc.W_0", "Weights.W_wing_control.W_0") # replace all "ivc.W_0" with "Weights.GrossWeightComp.W_0" group.nonlinear_solver = NonlinearBlockGS(iprint=2, maxiter=30) prob.setup() prob.run_model() #print(prob['cla_wing.CLa']) #prob.check_partials(compact_print=True) # TO RUN: 'python run.py' # TO VISUALIZE: 'openmdao view_model run.py' view_model(prob)
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 = 0.1 * m_full * g / n_props prob = Problem() comp = IndepVarComp() comp.add_discrete_output('B', val=num_blades) comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=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 = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) balance_group.add_subsystem( 'inflow_comp', comp, promotes_inputs=['v', 'omega', 'radii', 'precone'], promotes_outputs=['Vx', 'Vy']) comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, af_filename=af_filename, turbine=False) balance_group.add_subsystem( 'ccblade_group', comp, promotes_inputs=[ 'B', 'radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'precone', 'omega', 'Vx', 'Vy', 'precone', '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=50, iprint=2) balance_group.nonlinear_solver.options['solve_subsystems'] = True # prob.model.nonlinear_solver.linesearch = BoundsEnforceLS() balance_group.nonlinear_solver.options['atol'] = 1e-9 prob.model.add_subsystem('thrust_balance_group', balance_group, promotes=['*']) 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('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(-3.5, -2.6, 10) climb_velocity_nondim = np.linspace(-5.0, -2.1, 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('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 descent. ax.plot(climb_velocity_nondim, -induced_velocity_nondim, label='CCBlade.jl (descent)') # 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.)) 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('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')