def test_brachistochrone_base_phase_class_gl(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm class BrachistochronePhase(dm.Phase): def setup(self): self.options['ode_class'] = BrachistochroneODE self.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') self.add_state('x', fix_initial=True, rate_source='xdot', units='m') self.add_state('y', fix_initial=True, rate_source='ydot', units='m') self.add_state('v', fix_initial=True, rate_source='vdot', targets=['v'], units='m/s') self.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9, targets=['theta']) self.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) super(BrachistochronePhase, self).setup() p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = BrachistochronePhase( transcription=dm.GaussLobatto(num_segments=20, order=3)) p.model.add_subsystem('phase0', phase) phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) exp_out = phase.simulate() assert_near_equal(exp_out.get_val('phase0.timeseries.states:x')[-1], 10, tolerance=1.0E-3) assert_near_equal(exp_out.get_val('phase0.timeseries.states:y')[-1], 5, tolerance=1.0E-3)
def test_propeller_aero4students(self): # Copied from CCBlade.jl/test/runtests.jl # -------- verification: propellers. using script at http://www.aerodynamics4students.com/propulsion/blade-element-propeller-theory.php ------ # I increased their tolerance to 1e-6 # inputs chord = 0.10 D = 1.6 RPM = 2100 rho = 1.225 pitch = 1.0 # pitch distance in meters. # --- rotor definition --- turbine = False # Rhub = 0.0 # Rtip = D/2 Rhub_eff = 1e-6 # something small to eliminate hub effects Rtip_eff = 100.0 # something large to eliminate tip effects B = 2 # number of blades # --- section definitions --- num_nodes = 60 R = D / 2.0 r = np.linspace(R / 10, R, 11) dr = r[1] - r[0] r = np.tile(r, (num_nodes, 1)) theta = np.arctan(pitch / (2 * np.pi * r)) def affunc(alpha, Re, M): cl = 6.2 * alpha cd = 0.008 - 0.003 * cl + 0.01 * cl * cl return cl, cd prob = om.Problem() num_radial = r.shape[-1] comp = om.IndepVarComp() comp.add_output('v', val=np.arange(1, 60 + 1)[:num_nodes], units='m/s') comp.add_output('omega', val=np.tile(RPM, num_nodes), units='rpm') comp.add_output('radii', val=r, units='m') comp.add_output('chord', val=chord, shape=(num_nodes, num_radial), units='m') comp.add_output('theta', val=theta, shape=(num_nodes, num_radial), units='rad') comp.add_output('precone', val=0., shape=num_nodes, units='rad') comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3') comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s') comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s') comp.add_output('hub_radius', val=Rhub_eff, shape=num_nodes, units='m') comp.add_output('prop_radius', val=Rtip_eff, shape=num_nodes, units='m') comp.add_output('pitch', val=0.0, shape=num_nodes, units='rad') prob.model.add_subsystem('ivc', comp, promotes_outputs=['*']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob.model.add_subsystem( "simple_inflow", comp, promotes_inputs=["v", "omega", "radii", "precone"], promotes_outputs=["Vx", "Vy"]) comp = ccb.LocalInflowAngleComp(num_nodes=num_nodes, num_radial=num_radial, num_blades=B, airfoil_interp=affunc, turbine=turbine, debug_print=False) comp.linear_solver = om.DirectSolver(assemble_jac=True) prob.model.add_subsystem('ccblade', comp, promotes_inputs=[ 'radii', 'chord', 'theta', 'Vx', 'Vy', 'rho', 'mu', 'asound', 'hub_radius', 'prop_radius', 'precone', 'pitch' ], promotes_outputs=['Np', 'Tp']) prob.setup() prob.final_setup() prob.run_model() Np = prob.get_val('Np', units='N/m') Tp = prob.get_val('Tp', units='N/m') T = np.sum(Np * dr, axis=1) * B Q = np.sum(r * Tp * dr, axis=1) * B tsim = 1e3 * np.array([ 1.045361193032356, 1.025630300048415, 1.005234466788998, 0.984163367036026, 0.962407923825140, 0.939960208707079, 0.916813564966455, 0.892962691000145, 0.868403981825492, 0.843134981103815, 0.817154838249790, 0.790463442573673, 0.763063053839278, 0.734956576558370, 0.706148261507327, 0.676643975451150, 0.646450304160057, 0.615575090105131, 0.584027074365864, 0.551815917391907, 0.518952127358381, 0.485446691671386, 0.451311288662196, 0.416557935286392, 0.381199277009438, 0.345247916141561, 0.308716772800348, 0.271618894441869, 0.233967425339051, 0.195775319296371, 0.157055230270717, 0.117820154495231, 0.078082266879117, 0.037854059197644, -0.002852754149850, -0.044026182837742, -0.085655305814570, -0.127728999394140, -0.170237722799272, -0.213169213043848, -0.256515079286031, -0.300266519551194, -0.344414094748869, -0.388949215983616, -0.433863576642539, -0.479150401337354, -0.524801553114807, -0.570810405128802, -0.617169893200684, -0.663873474163182, -0.710915862524620, -0.758291877949762, -0.805995685105502, -0.854022273120508, -0.902366919041604, -0.951025170820984, -0.999992624287163, -1.049265666456123, -1.098840222937414, -1.148712509929845 ]) qsim = 1e2 * np.array([ 0.803638686218187, 0.806984572453978, 0.809709290183008, 0.811743686838315, 0.813015017103876, 0.813446921530685, 0.812959654049620, 0.811470393912576, 0.808893852696513, 0.805141916379142, 0.800124489784850, 0.793748780791057, 0.785921727832179, 0.776548246109426, 0.765532528164390, 0.752778882688809, 0.738190986274448, 0.721673076180745, 0.703129918771009, 0.682467282681955, 0.659592296506578, 0.634413303042323, 0.606840565246423, 0.576786093366321, 0.544164450503912, 0.508891967461804, 0.470887571011192, 0.430072787279711, 0.386371788290446, 0.339711042057184, 0.290019539402947, 0.237229503458026, 0.181274942660876, 0.122093307308376, 0.059623821454727, -0.006190834182631, -0.075406684829235, -0.148076528546541, -0.224253047813501, -0.303980950928302, -0.387309291734422, -0.474283793689904, -0.564946107631716, -0.659336973911858, -0.757495165410553, -0.859460291551374, -0.965266648683888, -1.074949504731187, -1.188540970723477, -1.306072104649531, -1.427575034895290, -1.553080300508925, -1.682614871422754, -1.816205997296014, -1.953879956474228, -2.095662107769925, -2.241576439746701, -2.391647474158875, -2.545897099743367, -2.704346566395035 ]) assert_rel_error(self, T, tsim[:num_nodes], 1e-5) assert_rel_error(self, Q, qsim[:num_nodes], 1e-5) # Run with v = 20.0 m/s. prob.set_val('v', 20.0, units='m/s', indices=0) prob.run_model() Vhub = prob.get_val('v', units='m/s', indices=0) omega = prob.get_val('omega', units='rad/s', indices=0) Np = prob.get_val('Np', units='N/m', indices=0) Tp = prob.get_val('Tp', units='N/m', indices=0) T = np.sum(Np * dr) * B Q = np.sum(r[0, :] * Tp * dr) * B P = Q * omega n = omega / (2 * np.pi) CT = T / (rho * n**2 * D**4) CQ = Q / (rho * n**2 * D**5) eff = T * Vhub / P assert_rel_error(self, CT, 0.056110238632657, 1e-6) assert_rel_error(self, CQ, 0.004337202960642, 1e-6) assert_rel_error(self, eff, 0.735350632777002, 1e-5)
def test_multiple_adv_ratios(self): # num_nodes = 20 nstart = 0 nend = 19 num_nodes = nend - nstart + 1 r = 0.0254 * np.array([ 0.7526, 0.7928, 0.8329, 0.8731, 0.9132, 0.9586, 1.0332, 1.1128, 1.1925, 1.2722, 1.3519, 1.4316, 1.5114, 1.5911, 1.6708, 1.7505, 1.8302, 1.9099, 1.9896, 2.0693, 2.1490, 2.2287, 2.3084, 2.3881, 2.4678, 2.5475, 2.6273, 2.7070, 2.7867, 2.8661, 2.9410 ]).reshape(1, -1) num_radial = r.shape[-1] r = np.tile(r, (num_nodes, 1)) chord = 0.0254 * np.array([ 0.6270, 0.6255, 0.6231, 0.6199, 0.6165, 0.6125, 0.6054, 0.5973, 0.5887, 0.5794, 0.5695, 0.5590, 0.5479, 0.5362, 0.5240, 0.5111, 0.4977, 0.4836, 0.4689, 0.4537, 0.4379, 0.4214, 0.4044, 0.3867, 0.3685, 0.3497, 0.3303, 0.3103, 0.2897, 0.2618, 0.1920 ]).reshape((1, num_radial)) chord = np.tile(chord, (num_nodes, 1)) theta = np.pi / 180.0 * np.array([ 40.2273, 38.7657, 37.3913, 36.0981, 34.8803, 33.5899, 31.6400, 29.7730, 28.0952, 26.5833, 25.2155, 23.9736, 22.8421, 21.8075, 20.8586, 19.9855, 19.1800, 18.4347, 17.7434, 17.1005, 16.5013, 15.9417, 15.4179, 14.9266, 14.4650, 14.0306, 13.6210, 13.2343, 12.8685, 12.5233, 12.2138 ]).reshape((1, num_radial)) theta = np.tile(theta, (num_nodes, 1)) rho = 1.225 Rhub = 0.0254 * .5 Rtip = 0.0254 * 3.0 B = 2 # number of blades turbine = False J = np.linspace(0.1, 0.9, 20)[nstart:nend + 1] # advance ratio # J = np.linspace(0.1, 0.9, 20) omega = 8000.0 * np.pi / 30 n = omega / (2 * np.pi) D = 2 * Rtip Vinf = J * D * n dr = r[0, 1] - r[0, 0] # Airfoil interpolator. af = af_from_files(["airfoils/NACA64_A17.dat"])[0] prob = om.Problem() comp = om.IndepVarComp() comp.add_output('v', val=Vinf, units='m/s') comp.add_output('omega', val=np.tile(omega, num_nodes), units='rad/s') comp.add_output('radii', val=r, shape=(num_nodes, num_radial), units='m') comp.add_output('dradii', val=dr, shape=(num_nodes, num_radial), units='m') comp.add_output('chord', val=chord, shape=(num_nodes, num_radial), units='m') comp.add_output('theta', val=theta, shape=(num_nodes, num_radial), units='rad') comp.add_output('precone', val=0., shape=num_nodes, units='rad') comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3') comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s') comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s') comp.add_output('hub_radius', val=Rhub, shape=num_nodes, units='m') comp.add_output('prop_radius', val=Rtip, shape=num_nodes, units='m') comp.add_output('pitch', val=0.0, shape=num_nodes, units='rad') prob.model.add_subsystem('ivc', comp, promotes_outputs=['*']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob.model.add_subsystem( "simple_inflow", comp, promotes_inputs=["v", "omega", "radii", "precone"], promotes_outputs=["Vx", "Vy"]) comp = ccb.LocalInflowAngleComp(num_nodes=num_nodes, num_radial=num_radial, num_blades=B, airfoil_interp=af, turbine=turbine, debug_print=False) comp.linear_solver = om.DirectSolver(assemble_jac=True) prob.model.add_subsystem('ccblade', comp, promotes_inputs=[ 'radii', 'chord', 'theta', 'Vx', 'Vy', 'rho', 'mu', 'asound', 'hub_radius', 'prop_radius', 'precone', 'pitch' ], promotes_outputs=['Np', 'Tp']) comp = ccb.FunctionalsComp(num_nodes=num_nodes, num_radial=num_radial, num_blades=B) prob.model.add_subsystem( 'ccblade_torquethrust_comp', comp, promotes_inputs=[ 'hub_radius', 'prop_radius', 'radii', 'Np', 'Tp', 'v', 'omega' ], promotes_outputs=['thrust', 'torque', 'efficiency']) prob.setup() prob.final_setup() prob.run_model() etatest = np.array([ 0.24598190455626265, 0.3349080300487075, 0.4155652767326253, 0.48818637673414306, 0.5521115225679999, 0.6089123481436948, 0.6595727776885079, 0.7046724703349897, 0.7441662053086512, 0.7788447616541276, 0.8090611349633181, 0.8347808848055981, 0.8558196582739432, 0.8715046719672315, 0.8791362131978436, 0.8670633642311274, 0.7974063895510229, 0.2715632768892098, 0.0, 0.0 ])[nstart:nend + 1] thrust = prob.get_val('thrust', units='N') eff = prob.get_val('efficiency') assert_array_almost_equal(eff[thrust > 0.0], etatest[thrust > 0.0], decimal=2)
p = om.Problem() dvs = p.model.add_subsystem('dvs', om.IndepVarComp(), promotes=['*']) dvs.add_output('h', val=np.ones(NUM_ELEMENTS)*1.0) p.model.add_subsystem('FEM', FEMBeam(E=1, L=1, b=0.1, num_elements=NUM_ELEMENTS), promotes_inputs=['h'], promotes_outputs=['compliance', 'volume']) p.driver = om.ScipyOptimizeDriver() p.driver.options['tol'] = 1e-4 p.driver.options['disp'] = True p.model.add_design_var('h', lower=0.01, upper=10.0) p.model.add_objective('compliance') p.model.add_constraint('volume', equals=0.01) # p.model.approx_totals(method='fd', step=1e-4, step_calc='abs') p.model.linear_solver = om.DirectSolver() p.setup() # p['h'] = [0.14007896, 0.12362061, 0.1046475 , 0.08152954, 0.05012339] p.run_driver() p.model.list_outputs(print_arrays=True)
def test_parameter_boundary_constraint(self): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=20, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=True) phase.add_state('y', fix_initial=True, fix_final=True) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, rate2_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', opt=True, units='m/s**2', val=9.80665) # We'll let g vary, but make sure it hits the desired value. # It's a static parameter, so it shouldn't matter whether we enforce it # at the start or the end of the phase, so here we'll do both. # Note if we make these equality constraints, some optimizers (SLSQP) will # see the problem as infeasible. phase.add_boundary_constraint('g', loc='initial', units='m/s**2', upper=9.80665) phase.add_boundary_constraint('g', loc='final', units='m/s**2', upper=9.80665) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interp('x', [0, 10]) p['phase0.states:y'] = phase.interp('y', [10, 5]) p['phase0.states:v'] = phase.interp('v', [0, 9.9]) p['phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['phase0.parameters:g'] = 5 p.run_driver() assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-4) assert_near_equal(p.get_val('phase0.timeseries.parameters:g')[0], 9.80665, tolerance=1.0E-6) assert_near_equal(p.get_val('phase0.timeseries.parameters:g')[-1], 9.80665, tolerance=1.0E-6)
def setup(self): thermo_data = self.options['thermo_data'] elements = self.options['elements'] nozzType = self.options['nozzType'] lossCoef = self.options['lossCoef'] num_element = len(elements) 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='CEA', thermo_kwargs={ 'elements': elements, '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='CEA', thermo_kwargs={ 'elements': elements, '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='CEA', thermo_kwargs={ 'elements': elements, '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='CEA', thermo_kwargs={ 'elements': elements, '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)
def test_double_integrator_for_docs(self): import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.double_integrator.double_integrator_ode import DoubleIntegratorODE # Initialize the problem and assign the driver p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() # Setup the trajectory and its phase traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=30, order=3, compressed=False) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=DoubleIntegratorODE, transcription=transcription)) # # Set the options for our variables. # phase.set_time_options(fix_initial=True, fix_duration=True, units='s') phase.add_state('v', fix_initial=True, fix_final=True, rate_source='u', units='m/s') phase.add_state('x', fix_initial=True, rate_source='v', units='m') phase.add_control('u', units='m/s**2', scaler=0.01, continuity=False, rate_continuity=False, rate2_continuity=False, shape=(1, ), lower=-1.0, upper=1.0) # # Maximize distance travelled. # phase.add_objective('x', loc='final', scaler=-1) p.model.linear_solver = om.DirectSolver() # # Setup the problem and set our initial values. # p.setup(check=True) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 1.0 p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 0.25], nodes='state_input') p['traj.phase0.states:v'] = phase.interpolate(ys=[0, 0], nodes='state_input') p['traj.phase0.controls:u'] = phase.interpolate(ys=[1, -1], nodes='control_input') # # Solve the problem. # dm.run_problem(p) # # Verify that the results are correct. # x = p.get_val('traj.phase0.timeseries.states:x') v = p.get_val('traj.phase0.timeseries.states:v') assert_near_equal(x[0], 0.0, tolerance=1.0E-4) assert_near_equal(x[-1], 0.25, tolerance=1.0E-4) assert_near_equal(v[0], 0.0, tolerance=1.0E-4) assert_near_equal(v[-1], 0.0, tolerance=1.0E-4) # # Simulate the explicit solution and plot the results. # exp_out = traj.simulate() plot_results( [('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:x', 'time (s)', 'x $(m)$'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:v', 'time (s)', 'v $(m/s)$'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:u', 'time (s)', 'u $(m/s^2)$')], title='Double Integrator Solution\nRadau Pseudospectral Method', p_sol=p, p_sim=exp_out) plt.show()
def main(a): prob = om.Problem() ivc = om.IndepVarComp() ivc.add_output("x", 2.0) ivc.add_output("y", 3.0) prob.model.add_subsystem("ivc", ivc, promotes=["*"]) comp = make_component(julia.SimpleExplicit(a)) prob.model.add_subsystem("square_it_comp0", comp, promotes_inputs=['x', 'y'], promotes_outputs=[('z1', 'z1_0'), ('z2', 'z2_0')]) comp = make_component(julia.SimpleExplicit(a + 1)) prob.model.add_subsystem("square_it_comp1", comp, promotes_inputs=['x', 'y'], promotes_outputs=[('z1', 'z1_1'), ('z2', 'z2_1')]) comp = make_component(julia.SimpleExplicit(a + 2)) prob.model.add_subsystem("square_it_comp2", comp, promotes_inputs=['x', 'y'], promotes_outputs=[('z1', 'z1_2'), ('z2', 'z2_2')]) comp = make_component(julia.SimpleImplicit(a)) comp.linear_solver = om.DirectSolver(assemble_jac=True) comp.nonlinear_solver = om.NewtonSolver(solve_subsystems=True, iprint=0, err_on_non_converge=True) prob.model.add_subsystem("square_it_comp3", comp, promotes_inputs=["x", "y"], promotes_outputs=[("z1", "z1_3"), ("z2", "z2_3")]) comp = make_component(julia.SimpleImplicit(a + 1)) comp.linear_solver = om.DirectSolver(assemble_jac=True) comp.nonlinear_solver = om.NewtonSolver(solve_subsystems=True, iprint=0, err_on_non_converge=True) prob.model.add_subsystem("square_it_comp4", comp, promotes_inputs=["x", "y"], promotes_outputs=[("z1", "z1_4"), ("z2", "z2_4")]) comp = make_component(julia.SimpleImplicit(a + 2)) comp.linear_solver = om.DirectSolver(assemble_jac=True) comp.nonlinear_solver = om.NewtonSolver(solve_subsystems=True, iprint=0, err_on_non_converge=True) prob.model.add_subsystem("square_it_comp5", comp, promotes_inputs=["x", "y"], promotes_outputs=[("z1", "z1_5"), ("z2", "z2_5")]) prob.setup() prob.run_model() prob.get_val("z1_0") prob.get_val("z2_0") prob.get_val("z1_1") prob.get_val("z2_1") prob.get_val("z1_2") prob.get_val("z2_2") prob.compute_totals(of=["z1_0", "z2_0"], wrt=["x", "y"]) prob.compute_totals(of=["z1_1", "z2_1"], wrt=["x", "y"]) prob.compute_totals(of=["z1_2", "z2_2"], wrt=["x", "y"])
def test_control_rate2_path_constraint_gl(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10, order=5)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.add_state( 'x', fix_initial=True, fix_final=True, rate_source=BrachistochroneODE.states['x']['rate_source']) phase.add_state( 'y', fix_initial=True, fix_final=True, rate_source=BrachistochroneODE.states['y']['rate_source']) phase.add_state( 'v', fix_initial=True, rate_source=BrachistochroneODE.states['v']['rate_source']) phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) phase.add_path_constraint('theta_rate2', lower=-200, upper=200, units='rad/s**2') p.model.linear_solver = om.DirectSolver() p.model.options['assembled_jac_type'] = 'csc' p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3)
def test_sellar_specify_linear_direct_solver(self): prob = om.Problem() model = prob.model model.add_subsystem('px', om.IndepVarComp('x', 1.0), promotes=['x']) model.add_subsystem('pz', om.IndepVarComp('z', np.array([5.0, 2.0])), promotes=['z']) proms = [ 'x', 'z', 'y1', 'state_eq.y2_actual', 'state_eq.y2_command', 'd1.y2', 'd2.y2' ] sub = model.add_subsystem('sub', om.Group(), promotes=proms) subgrp = sub.add_subsystem( 'state_eq_group', om.Group(), promotes=['state_eq.y2_actual', 'state_eq.y2_command']) subgrp.linear_solver = om.ScipyKrylov() subgrp.add_subsystem('state_eq', StateConnection()) sub.add_subsystem('d1', SellarDis1withDerivatives(), promotes=['x', 'z', 'y1']) sub.add_subsystem('d2', SellarDis2withDerivatives(), promotes=['z', 'y1']) model.connect('state_eq.y2_command', 'd1.y2') model.connect('d2.y2', 'state_eq.y2_actual') model.add_subsystem('obj_cmp', om.ExecComp('obj = x**2 + z[1] + y1 + exp(-y2)', z=np.array([0.0, 0.0]), x=0.0, y1=0.0, y2=0.0), promotes=['x', 'z', 'y1', 'obj']) model.connect('d2.y2', 'obj_cmp.y2') model.add_subsystem('con_cmp1', om.ExecComp('con1 = 3.16 - y1'), promotes=['con1', 'y1']) model.add_subsystem('con_cmp2', om.ExecComp('con2 = y2 - 24.0'), promotes=['con2']) model.connect('d2.y2', 'con_cmp2.y2') model.nonlinear_solver = om.NewtonSolver() # Use bad settings for this one so that problem doesn't converge. # That way, we test that we are really using Newton's Lin Solver # instead. sub.linear_solver = om.ScipyKrylov() sub.linear_solver.options['maxiter'] = 1 # The good solver model.nonlinear_solver.linear_solver = om.DirectSolver() prob.set_solver_print(level=0) prob.setup() prob.run_model() assert_rel_error(self, prob['y1'], 25.58830273, .00001) assert_rel_error(self, prob['state_eq.y2_command'], 12.05848819, .00001) # Make sure we aren't iterating like crazy self.assertLess(model.nonlinear_solver._iter_count, 8) self.assertEqual(model.linear_solver._iter_count, 0)
def test_solve_subsystems_internals(self): # Here we test that this feature is doing what it should do by counting the # number of calls in various places. class CountNewton(om.NewtonSolver): """ This version of Newton also counts how many times it runs in total.""" def __init__(self, **kwargs): super(CountNewton, self).__init__(**kwargs) self.total_count = 0 def _single_iteration(self): super(CountNewton, self)._single_iteration() self.total_count += 1 class CountDS(om.DirectSolver): """ This version of Newton also counts how many times it linearizes""" def __init__(self, **kwargs): super(CountDS, self).__init__(**kwargs) self.lin_count = 0 def _linearize(self): super(CountDS, self)._linearize() self.lin_count += 1 prob = om.Problem(model=DoubleSellar()) model = prob.model # each SubSellar group converges itself g1 = model.g1 g1.nonlinear_solver = CountNewton() g1.nonlinear_solver.options['rtol'] = 1.0e-5 g1.linear_solver = CountDS() # used for derivatives g2 = model.g2 g2.nonlinear_solver = CountNewton() g2.nonlinear_solver.options['rtol'] = 1.0e-5 g2.linear_solver = om.DirectSolver() # Converge the outer loop with Gauss Seidel, with a looser tolerance. model.nonlinear_solver = om.NewtonSolver() model.linear_solver = om.ScipyKrylov() # Enfore behavior: max_sub_solves = 0 means we run once during init model.nonlinear_solver.options['maxiter'] = 5 model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 0 prob.set_solver_print(level=0) prob.setup() prob.run_model() # Verifying subsolvers ran self.assertEqual(g1.nonlinear_solver.total_count, 2) self.assertEqual(g2.nonlinear_solver.total_count, 2) self.assertEqual(g1.linear_solver.lin_count, 2) prob = om.Problem(model=DoubleSellar()) model = prob.model # each SubSellar group converges itself g1 = model.g1 g1.nonlinear_solver = CountNewton() g1.nonlinear_solver.options['rtol'] = 1.0e-5 g1.linear_solver = CountDS() # used for derivatives g2 = model.g2 g2.nonlinear_solver = CountNewton() g2.nonlinear_solver.options['rtol'] = 1.0e-5 g2.linear_solver = om.DirectSolver() # Converge the outer loop with Gauss Seidel, with a looser tolerance. model.nonlinear_solver = om.NewtonSolver() model.linear_solver = om.ScipyKrylov() # Enforce Behavior: baseline model.nonlinear_solver.options['maxiter'] = 5 model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 5 prob.set_solver_print(level=0) prob.setup() prob.run_model() # Verifying subsolvers ran self.assertEqual(g1.nonlinear_solver.total_count, 5) self.assertEqual(g2.nonlinear_solver.total_count, 5) self.assertEqual(g1.linear_solver.lin_count, 5) prob = om.Problem(model=DoubleSellar()) model = prob.model # each SubSellar group converges itself g1 = model.g1 g1.nonlinear_solver = CountNewton() g1.nonlinear_solver.options['rtol'] = 1.0e-5 g1.linear_solver = CountDS() # used for derivatives g2 = model.g2 g2.nonlinear_solver = CountNewton() g2.nonlinear_solver.options['rtol'] = 1.0e-5 g2.linear_solver = om.DirectSolver() # Converge the outer loop with Gauss Seidel, with a looser tolerance. model.nonlinear_solver = om.NewtonSolver() model.linear_solver = om.ScipyKrylov() # Enfore behavior: max_sub_solves = 1 means we run during init and first iteration of iter_execute model.nonlinear_solver.options['maxiter'] = 5 model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 1 prob.set_solver_print(level=0) prob.setup() prob.run_model() # Verifying subsolvers ran self.assertEqual(g1.nonlinear_solver.total_count, 4) self.assertEqual(g2.nonlinear_solver.total_count, 4) self.assertEqual(g1.linear_solver.lin_count, 4)
def test_brachistochrone_vector_boundary_constraints_radau_partial_indices( self): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneVectorStatesODE, transcription=dm.Radau(num_segments=20, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state( 'pos', shape=(2, ), rate_source=BrachistochroneVectorStatesODE.states['pos'] ['rate_source'], units=BrachistochroneVectorStatesODE.states['pos']['units'], fix_initial=True, fix_final=[True, False]) phase.add_state( 'v', rate_source=BrachistochroneVectorStatesODE.states['v'] ['rate_source'], targets=BrachistochroneVectorStatesODE.states['v']['targets'], units=BrachistochroneVectorStatesODE.states['v']['units'], fix_initial=True, fix_final=False) phase.add_control( 'theta', units='deg', targets=BrachistochroneVectorStatesODE.parameters['theta'] ['targets'], rate_continuity=False, lower=0.01, upper=179.9) phase.add_design_parameter( 'g', targets=BrachistochroneVectorStatesODE.parameters['g']['targets'], units='m/s**2', opt=False, val=9.80665) phase.add_boundary_constraint('pos', loc='final', equals=5, indices=[1]) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True, force_alloc_complex=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 pos0 = [0, 10] posf = [10, 5] p['phase0.states:pos'] = phase.interpolate(ys=[pos0, posf], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100], nodes='control_input') p['phase0.design_parameters:g'] = 9.80665 p.run_driver() assert_rel_error(self, p.get_val('phase0.time')[-1], 1.8016, tolerance=1.0E-3) # Plot results if SHOW_PLOTS: p.run_driver() exp_out = phase.simulate(times_per_seg=20) fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.states:pos')[:, 0] y_imp = p.get_val('phase0.timeseries.states:pos')[:, 1] x_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 0] y_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 1] ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('x (m)') ax.set_ylabel('y (m)') ax.grid(True) ax.legend(loc='upper right') fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.time') y_imp = p.get_val('phase0.timeseries.control_rates:theta_rate2') x_exp = exp_out.get_val('phase0.timeseries.time') y_exp = exp_out.get_val( 'phase0.timeseries.control_rates:theta_rate2') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('time (s)') ax.set_ylabel('theta rate2 (rad/s**2)') ax.grid(True) ax.legend(loc='lower right') plt.show() return p
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 test_brachistochrone_undecorated_ode_gl(self): import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', targets=['v'], units='m/s') phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9, targets=['theta']) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3)
def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3, compressed=True, optimizer='SLSQP'): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.declare_coloring() if transcription == 'gauss-lobatto': t = dm.GaussLobatto(num_segments=num_segments, order=transcription_order, compressed=compressed) elif transcription == 'radau-ps': t = dm.Radau(num_segments=num_segments, order=transcription_order, compressed=compressed) elif transcription == 'runge-kutta': t = dm.RungeKutta(num_segments=num_segments, order=transcription_order, compressed=compressed) phase = dm.Phase(ode_class=BrachistochroneODE, transcription=t) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state('y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state('v', rate_source=BrachistochroneODE.states['v']['rate_source'], targets=BrachistochroneODE.states['v']['targets'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_control( 'theta', targets=BrachistochroneODE.parameters['theta']['targets'], continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_input_parameter( 'g', targets=BrachistochroneODE.parameters['g']['targets'], units='m/s**2', val=9.80665) phase.add_timeseries('timeseries2', transcription=dm.Radau(num_segments=num_segments * 5, order=transcription_order, compressed=compressed), subset='control_input') phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=['unconnected_inputs']) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100], nodes='control_input') p['phase0.input_parameters:g'] = 9.80665 p.run_driver() # Plot results if SHOW_PLOTS: exp_out = phase.simulate() fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.states:x') y_imp = p.get_val('phase0.timeseries.states:y') x_exp = exp_out.get_val('phase0.timeseries.states:x') y_exp = exp_out.get_val('phase0.timeseries.states:y') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('x (m)') ax.set_ylabel('y (m)') ax.grid(True) ax.legend(loc='upper right') fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.time_phase') y_imp = p.get_val('phase0.timeseries.controls:theta') x_exp = exp_out.get_val('phase0.timeseries.time_phase') y_exp = exp_out.get_val('phase0.timeseries.controls:theta') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('time (s)') ax.set_ylabel('theta (rad)') ax.grid(True) ax.legend(loc='lower right') plt.show() return p
def test_brachistochrone_integrated_parameter_radau_ps(self): import numpy as np import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.Radau(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', units='m/s') phase.add_state('theta', fix_initial=False, rate_source='theta_dot', lower=1E-3) # theta_dot has no target, therefore we need to explicitly set the units and shape. phase.add_parameter('theta_dot', units='deg/s', shape=(1, ), opt=True, lower=0, upper=100) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.states:theta'] = np.radians( phase.interpolate(ys=[0.05, 100.0], nodes='state_input')) p['phase0.parameters:theta_dot'] = 60.0 # Solve for the optimal trajectory dm.run_problem(p, refine_iteration_limit=5) # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) sim_out = phase.simulate(times_per_seg=20) t_sol = p.get_val('phase0.timeseries.time') x_sol = p.get_val('phase0.timeseries.states:x') y_sol = p.get_val('phase0.timeseries.states:y') v_sol = p.get_val('phase0.timeseries.states:v') theta_sol = p.get_val('phase0.timeseries.states:theta') t_sim = sim_out.get_val('phase0.timeseries.time') x_sim = sim_out.get_val('phase0.timeseries.states:x') y_sim = sim_out.get_val('phase0.timeseries.states:y') v_sim = sim_out.get_val('phase0.timeseries.states:v') theta_sim = sim_out.get_val('phase0.timeseries.states:theta') assert_timeseries_near_equal(t_sol, x_sol, t_sim, x_sim, tolerance=1.0E-3, num_points=10) assert_timeseries_near_equal(t_sol, y_sol, t_sim, y_sim, tolerance=1.0E-3, num_points=10) assert_timeseries_near_equal(t_sol, v_sol, t_sim, v_sim, tolerance=1.0E-3, num_points=10) assert_timeseries_near_equal(t_sol, theta_sol, t_sim, theta_sim, tolerance=1.0E-3, num_points=10)
def test_radau(self): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() t = dm.Radau(num_segments=20, order=[3, 5]*10, compressed=True) phase = dm.Phase(ode_class=BrachistochroneODE, transcription=t) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state('y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state('v', rate_source=BrachistochroneODE.states['v']['rate_source'], targets=BrachistochroneODE.states['v']['targets'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_control('theta', targets=BrachistochroneODE.parameters['theta']['targets'], continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', targets=BrachistochroneODE.parameters['g']['targets'], units='m/s**2', val=9.80665) phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100], nodes='control_input') p['phase0.parameters:g'] = 9.80665 p.run_driver() exp_out = phase.simulate() t_initial = p.get_val('phase0.timeseries.time')[0] tf = p.get_val('phase0.timeseries.time')[-1] x0 = p.get_val('phase0.timeseries.states:x')[0] xf = p.get_val('phase0.timeseries.states:x')[-1] y0 = p.get_val('phase0.timeseries.states:y')[0] yf = p.get_val('phase0.timeseries.states:y')[-1] v0 = p.get_val('phase0.timeseries.states:v')[0] vf = p.get_val('phase0.timeseries.states:v')[-1] g = p.get_val('phase0.timeseries.parameters:g')[0] thetaf = exp_out.get_val('phase0.timeseries.controls:theta')[-1] assert_almost_equal(t_initial, 0.0) assert_almost_equal(x0, 0.0) assert_almost_equal(y0, 10.0) assert_almost_equal(v0, 0.0) assert_almost_equal(tf, 1.8016, decimal=3) assert_almost_equal(xf, 10.0, decimal=3) assert_almost_equal(yf, 5.0, decimal=3) assert_almost_equal(vf, 9.902, decimal=3) assert_almost_equal(g, 9.80665, decimal=3) assert_almost_equal(thetaf, 100.12, decimal=0)
def test_simulate_array_param(self): # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10))) # # Set the variables # phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot') phase.add_state('v', fix_initial=True, fix_final=False, rate_source='vdot') phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', val=9.80665) phase.add_parameter('array', units=None, shape=(10, ), static_target=True) # dummy array of data indeps = p.model.add_subsystem('indeps', om.IndepVarComp(), promotes=['*']) indeps.add_output('array', np.linspace(1, 10, 10), units=None) # add dummy array as a parameter and connect it p.model.connect('array', 'traj.phase0.parameters:array') # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() # # Setup the Problem # p.setup() # # Set the initial values # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p['traj.phase0.states:x'] = phase.interp('x', [0, 10]) p['traj.phase0.states:y'] = phase.interp('y', [10, 5]) p['traj.phase0.states:v'] = phase.interp('v', [0, 9.9]) p['traj.phase0.controls:theta'] = phase.interp('theta', [5, 100.5]) # # Solve for the optimal trajectory # dm.run_problem(p, simulate=True) # Test the results sol_results = om.CaseReader('dymos_solution.db').get_case('final') sim_results = om.CaseReader('dymos_solution.db').get_case('final') sol = sol_results.get_val('traj.phase0.timeseries.parameters:array') sim = sim_results.get_val('traj.phase0.timeseries.parameters:array') assert_near_equal(sol - sim, np.zeros_like(sol)) # Test that the parameter is available in the solution and simulation files sol = sol_results.get_val('traj.phase0.parameters:array') sim = sim_results.get_val('traj.phase0.parameters:array') assert_near_equal(sol - sim, np.zeros_like(sol))
def test_connect_control_to_parameter(self): """ Test that the final value of a control in one phase can be connected as the value of a parameter in a subsequent phase. """ import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.cannonball.size_comp import CannonballSizeComp from dymos.examples.cannonball.cannonball_phase import CannonballPhase p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() external_params = p.model.add_subsystem('external_params', om.IndepVarComp()) external_params.add_output('radius', val=0.10, units='m') external_params.add_output('dens', val=7.87, units='g/cm**3') external_params.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10) p.model.add_subsystem('size_comp', CannonballSizeComp()) traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=5, order=3, compressed=True) ascent = CannonballPhase(transcription=transcription) ascent = traj.add_phase('ascent', ascent) # All initial states except flight path angle are fixed # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee) ascent.set_time_options(fix_initial=True, duration_bounds=(1, 100), duration_ref=100, units='s') ascent.set_state_options('r', fix_initial=True, fix_final=False) ascent.set_state_options('h', fix_initial=True, fix_final=False) ascent.set_state_options('gam', fix_initial=False, fix_final=True) ascent.set_state_options('v', fix_initial=False, fix_final=False) ascent.add_parameter('S', targets=['aero.S'], units='m**2') ascent.add_parameter('mass', targets=['eom.m', 'kinetic_energy.m'], units='kg') ascent.add_control('CD', targets=['aero.CD'], opt=False, val=0.05) # Limit the muzzle energy ascent.add_boundary_constraint('kinetic_energy.ke', loc='initial', upper=400000, lower=0, ref=100000) # Second Phase (descent) transcription = dm.GaussLobatto(num_segments=5, order=3, compressed=True) descent = CannonballPhase(transcription=transcription) traj.add_phase('descent', descent) # All initial states and time are free (they will be linked to the final states of ascent. # Final altitude is fixed (we will set it to zero so that the phase ends at ground impact) descent.set_time_options(initial_bounds=(.5, 100), duration_bounds=(.5, 100), duration_ref=100, units='s') descent.add_state('r', ) descent.add_state('h', fix_initial=False, fix_final=True) descent.add_state('gam', fix_initial=False, fix_final=False) descent.add_state('v', fix_initial=False, fix_final=False) descent.add_parameter('S', targets=['aero.S'], units='m**2') descent.add_parameter('mass', targets=['eom.m', 'kinetic_energy.m'], units='kg') descent.add_parameter('CD', targets=['aero.CD'], val=0.01) descent.add_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_parameter('CL', targets={ 'ascent': ['aero.CL'], 'descent': ['aero.CL'] }, val=0.0, units=None, opt=False) traj.add_parameter('T', targets={ 'ascent': ['eom.T'], 'descent': ['eom.T'] }, val=0.0, units='N', opt=False) traj.add_parameter('alpha', targets={ 'ascent': ['eom.alpha'], 'descent': ['eom.alpha'] }, val=0.0, units='deg', opt=False) # Add externally-provided design parameters to the trajectory. # In this case, we connect 'm' to pre-existing input parameters named 'mass' in each phase. traj.add_parameter('m', units='kg', val=1.0, targets={ 'ascent': 'mass', 'descent': 'mass' }) # In this case, by omitting targets, we're connecting these parameters to parameters # with the same name in each phase. traj.add_parameter('S', units='m**2', val=0.005) # Link Phases (link time and all state variables) traj.link_phases(phases=['ascent', 'descent'], vars=['*']) # Issue Connections p.model.connect('external_params.radius', 'size_comp.radius') p.model.connect('external_params.dens', 'size_comp.dens') p.model.connect('size_comp.mass', 'traj.parameters:m') p.model.connect('size_comp.S', 'traj.parameters:S') traj.connect('ascent.timeseries.controls:CD', 'descent.parameters:CD', src_indices=[-1]) # A linear solver at the top level can improve performance. p.model.linear_solver = om.DirectSolver() # Finish Problem Setup p.setup() # Set Initial Guesses p.set_val('external_params.radius', 0.05, units='m') p.set_val('external_params.dens', 7.87, units='g/cm**3') p.set_val('traj.ascent.controls:CD', 0.5) p.set_val('traj.parameters:CL', 0.0) p.set_val('traj.parameters:T', 0.0) p.set_val('traj.ascent.t_initial', 0.0) p.set_val('traj.ascent.t_duration', 10.0) p.set_val('traj.ascent.states:r', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:h', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:v', ascent.interpolate(ys=[200, 150], nodes='state_input')) p.set_val('traj.ascent.states:gam', ascent.interpolate(ys=[25, 0], nodes='state_input'), units='deg') p.set_val('traj.descent.t_initial', 10.0) p.set_val('traj.descent.t_duration', 10.0) p.set_val('traj.descent.states:r', descent.interpolate(ys=[100, 200], nodes='state_input')) p.set_val('traj.descent.states:h', descent.interpolate(ys=[100, 0], nodes='state_input')) p.set_val('traj.descent.states:v', descent.interpolate(ys=[150, 200], nodes='state_input')) p.set_val('traj.descent.states:gam', descent.interpolate(ys=[0, -45], nodes='state_input'), units='deg') dm.run_problem(p, simulate=True) assert_near_equal(p.get_val('traj.descent.states:r')[-1], 3183.25, tolerance=1.0E-2) assert_near_equal( p.get_val('traj.ascent.timeseries.controls:CD')[-1], p.get_val('traj.descent.timeseries.parameters:CD')[0])
def flying_robot_direct_collocation(transcription='gauss-lobatto', compressed=True): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() if transcription == 'gauss-lobatto': t = dm.GaussLobatto(num_segments=8, order=5, compressed=compressed) elif transcription == "radau-ps": t = dm.Radau(num_segments=8, order=5, compressed=compressed) else: raise ValueError('invalid transcription') traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase('phase0', dm.Phase(ode_class=FlyingRobotODE, transcription=t)) phase.set_time_options(fix_initial=True, fix_duration=False, duration_bounds=(0.1, 1E4), units='s') phase.add_state('x', shape=(2, ), fix_initial=True, fix_final=True, rate_source='v', units='m') phase.add_state('v', shape=(2, ), fix_initial=True, fix_final=True, rate_source='u', units='m/s') phase.add_state('J', fix_initial=True, fix_final=False, rate_source='u_mag2', units='m**2/s**3') phase.add_control('u', units='m/s**2', shape=(2, ), scaler=0.01, continuity=False, rate_continuity=False, rate2_continuity=False, lower=-1, upper=1) # Minimize the control effort phase.add_objective('time', loc='final') p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 1.0 p['traj.phase0.states:x'] = phase.interp('x', [[0.0, 0.0], [-100.0, 100.0]]) p['traj.phase0.states:v'] = phase.interp('v', [[0.0, 0.0], [0.0, 0.0]]) p['traj.phase0.controls:u'] = phase.interp('u', [[1, 1], [-1, -1]]) p.run_driver() return p
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.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) # setup the optimization prob.driver = om.pyOptSparseDriver() prob.driver.options['optimizer'] = 'SNOPT' prob.driver.options['debug_print'] = ['desvars', 'nl_cons', 'objs'] prob.driver.opt_settings={'Major step limit': 0.05} prob.model.add_design_var('fan:PRdes', lower=1.20, upper=1.4) prob.model.add_design_var('lpc:PRdes', lower=2.0, upper=4.0) prob.model.add_design_var('OPR', lower=40.0, upper=70.0, ref0=40.0, ref=70.0) prob.model.add_design_var('RTO:T4max', lower=3000.0, upper=3600.0, ref0=3000.0, ref=3600.0) prob.model.add_design_var('CRZ:VjetRatio', lower=1.35, upper=1.45, ref0=1.35, ref=1.45) prob.model.add_design_var('TR', lower=0.5, upper=0.95, ref0=0.5, ref=0.95)
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, map_extrap=True), promotes_inputs=[('Nmech', 'HP_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, map_extrap=True), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem('pt', pyc.Turbine(map_data=pyc.LPT2269, design=design, thermo_data=thermo_spec, elements=pyc.AIR_FUEL_MIX, map_extrap=True), promotes_inputs=[('Nmech', 'LP_Nmech')]) self.add_subsystem( 'nozz', pyc.Nozzle(nozzType='CV', lossCoef='Cv', thermo_data=thermo_spec, elements=pyc.AIR_FUEL_MIX)) self.add_subsystem('HP_shaft', pyc.Shaft(num_ports=2), promotes_inputs=[('Nmech', 'HP_Nmech')]) self.add_subsystem('LP_shaft', pyc.Shaft(num_ports=1), promotes_inputs=[('Nmech', 'LP_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', 'pt.Fl_I') pyc.connect_flow(self, 'pt.Fl_O', 'nozz.Fl_I') # Connect turbomachinery elements to shaft self.connect('comp.trq', 'HP_shaft.trq_0') self.connect('turb.trq', 'HP_shaft.trq_1') self.connect('pt.trq', 'LP_shaft.trq_0') # Connnect nozzle exhaust to freestream static conditions self.connect('fc.Fl_O:stat:P', 'nozz.Ps_exhaust') # Connect outputs to pefromance element self.connect('inlet.Fl_O:tot:P', 'perf.Pt2') self.connect('comp.Fl_O:tot:P', 'perf.Pt3') self.connect('burner.Wfuel', 'perf.Wfuel_0') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('nozz.Fg', 'perf.Fg_0') self.connect('LP_shaft.pwr_net', 'perf.power') # Add balances for design and off-design balance = self.add_subsystem('balance', om.BalanceComp()) if design: balance.add_balance('W', val=27.0, units='lbm/s', eq_units=None) self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('nozz.PR', 'balance.lhs:W') balance.add_balance('FAR', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR') balance.add_balance('turb_PR', val=3.0, lower=1.001, upper=8, eq_units='hp', rhs_val=0.) self.connect('balance.turb_PR', 'turb.PR') self.connect('HP_shaft.pwr_net', 'balance.lhs:turb_PR') balance.add_balance('pt_PR', val=3.0, lower=1.001, upper=8, eq_units='hp') self.connect('balance.pt_PR', 'pt.PR') self.connect('LP_shaft.pwr_net', 'balance.lhs:pt_PR') else: balance.add_balance('FAR', eq_units='hp', lower=1e-4, val=.3) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('LP_shaft.pwr_net', 'balance.lhs:FAR') balance.add_balance('HP_Nmech', val=1.5, units='rpm', lower=500., eq_units='hp', rhs_val=0.) self.connect('balance.HP_Nmech', 'HP_Nmech') self.connect('HP_shaft.pwr_net', 'balance.lhs:HP_Nmech') balance.add_balance('W', val=27.0, units='lbm/s', eq_units='inch**2') self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('nozz.Throat:stat:area', 'balance.lhs:W') # Setup solver to converge engine self.set_order([ 'balance', 'fc', 'inlet', 'comp', 'burner', 'turb', 'pt', 'nozz', 'HP_shaft', 'LP_shaft', 'perf' ]) newton = self.nonlinear_solver = 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 = om.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 test_control_rate2_boundary_constraint_gl(self): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=20, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=False) phase.add_state('y', fix_initial=True, fix_final=False) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, rate2_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', opt=False, units='m/s**2', val=9.80665) phase.add_boundary_constraint('theta_rate2', loc='final', equals=0.0, units='deg/s**2') # Minimize time at the end of the phase phase.add_objective('time') p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interp('x', [0, 10]) p['phase0.states:y'] = phase.interp('y', [10, 5]) p['phase0.states:v'] = phase.interp('v', [0, 9.9]) p['phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['phase0.parameters:g'] = 8 p.run_driver() plt.plot(p.get_val('phase0.timeseries.states:x'), p.get_val('phase0.timeseries.states:y'), 'ko') plt.figure() plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.controls:theta'), 'ro') plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.control_rates:theta_rate'), 'bo') plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.control_rates:theta_rate2'), 'go') plt.show() assert_near_equal( p.get_val('phase0.timeseries.control_rates:theta_rate2')[-1], 0, tolerance=1.0E-6)
def min_time_climb(optimizer='SLSQP', num_seg=3, transcription='gauss-lobatto', transcription_order=3, force_alloc_complex=False): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.declare_coloring() if optimizer == 'SNOPT': p.driver.opt_settings['Major iterations limit'] = 1000 p.driver.opt_settings['iSumm'] = 6 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6 p.driver.opt_settings['Function precision'] = 1.0E-12 p.driver.opt_settings['Linesearch tolerance'] = 0.1 p.driver.opt_settings['Major step limit'] = 0.5 t = { 'gauss-lobatto': dm.GaussLobatto(num_segments=num_seg, order=transcription_order), 'radau-ps': dm.Radau(num_segments=num_seg, order=transcription_order) } traj = dm.Trajectory() phase = dm.Phase(ode_class=_TestODE, transcription=t[transcription]) traj.add_phase('phase0', phase) p.model.add_subsystem('traj', traj) phase.set_time_options(fix_initial=True, duration_bounds=(50, 400), duration_ref=100.0) phase.add_state('r', fix_initial=True, lower=0, upper=1.0E6, ref=1.0E3, defect_ref=1.0E3, units='m', rate_source='flight_dynamics.r_dot') phase.add_state('h', fix_initial=True, lower=0, upper=20000.0, ref=1.0E2, defect_ref=1.0E2, units='m', rate_source='flight_dynamics.h_dot', targets=['h']) phase.add_state('v', fix_initial=True, lower=10.0, ref=1.0E2, defect_ref=1.0E2, units='m/s', rate_source='flight_dynamics.v_dot', targets=['v']) phase.add_state('gam', fix_initial=True, lower=-1.5, upper=1.5, ref=1.0, defect_ref=1.0, units='rad', rate_source='flight_dynamics.gam_dot', targets=['gam']) phase.add_state('m', fix_initial=True, lower=10.0, upper=1.0E5, ref=1.0E3, defect_ref=1.0E3, units='kg', rate_source='prop.m_dot', targets=['m']) phase.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0, rate_continuity=True, rate_continuity_scaler=100.0, rate2_continuity=False, targets=['alpha']) phase.add_parameter('S', val=49.2386, units='m**2', opt=False, targets=['S']) phase.add_parameter('Isp', val=1600.0, units='s', opt=False, targets=['Isp']) phase.add_parameter('throttle', val=1.0, opt=False, targets=['throttle']) phase.add_parameter('test', val=40 * [1], opt=False, static_target=True, targets=['test']) phase.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3) phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0) phase.add_boundary_constraint('gam', loc='final', equals=0.0) phase.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000) phase.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8) # Unnecessary but included to test capability phase.add_path_constraint(name='alpha', lower=-8, upper=8, units='deg') # Minimize time at the end of the phase phase.add_objective('time', loc='final', ref=1.0) # test mixing wildcard ODE variable expansion and unit overrides phase.add_timeseries_output(['aero.*', 'prop.thrust', 'prop.m_dot'], units={ 'aero.f_lift': 'lbf', 'prop.thrust': 'lbf' }) phase.set_refine_options(max_order=5) p.model.linear_solver = om.DirectSolver() p.setup(check=True, force_alloc_complex=force_alloc_complex) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 300.0 p['traj.phase0.states:r'] = phase.interp('r', [0.0, 111319.54]) p['traj.phase0.states:h'] = phase.interp('h', [100.0, 20000.0]) p['traj.phase0.states:v'] = phase.interp('v', [135.964, 283.159]) p['traj.phase0.states:gam'] = phase.interp('gam', [0.0, 0.0]) p['traj.phase0.states:m'] = phase.interp('m', [19030.468, 16841.431]) p['traj.phase0.controls:alpha'] = phase.interp('alpha', [0.0, 0.0]) dm.run_problem(p, refine_iteration_limit=1) return p
def test_propeller_example(self): # Example from the tutorial in the CCBlade documentation. num_nodes = 1 turbine = False Rhub = np.array([0.5 * 0.0254]).reshape((1, 1)) Rtip = np.array([3.0 * 0.0254]).reshape((1, 1)) num_blades = 2 precone = np.array([0.0]).reshape((1, 1)) r = 0.0254 * np.array([ 0.7526, 0.7928, 0.8329, 0.8731, 0.9132, 0.9586, 1.0332, 1.1128, 1.1925, 1.2722, 1.3519, 1.4316, 1.5114, 1.5911, 1.6708, 1.7505, 1.8302, 1.9099, 1.9896, 2.0693, 2.1490, 2.2287, 2.3084, 2.3881, 2.4678, 2.5475, 2.6273, 2.7070, 2.7867, 2.8661, 2.9410 ]).reshape(1, -1) num_radial = r.shape[-1] chord = 0.0254 * np.array([ 0.6270, 0.6255, 0.6231, 0.6199, 0.6165, 0.6125, 0.6054, 0.5973, 0.5887, 0.5794, 0.5695, 0.5590, 0.5479, 0.5362, 0.5240, 0.5111, 0.4977, 0.4836, 0.4689, 0.4537, 0.4379, 0.4214, 0.4044, 0.3867, 0.3685, 0.3497, 0.3303, 0.3103, 0.2897, 0.2618, 0.1920 ]).reshape((1, num_radial)) theta = np.pi / 180.0 * np.array([ 40.2273, 38.7657, 37.3913, 36.0981, 34.8803, 33.5899, 31.6400, 29.7730, 28.0952, 26.5833, 25.2155, 23.9736, 22.8421, 21.8075, 20.8586, 19.9855, 19.1800, 18.4347, 17.7434, 17.1005, 16.5013, 15.9417, 15.4179, 14.9266, 14.4650, 14.0306, 13.6210, 13.2343, 12.8685, 12.5233, 12.2138 ]).reshape((1, num_radial)) rho = np.array([1.225]).reshape((num_nodes, 1)) Vinf = np.array([10.0]).reshape((num_nodes, 1)) omega = np.array([8000.0 * (2 * np.pi / 60.0)]).reshape((num_nodes, 1)) Vy = omega * r * np.cos(precone) Vx = np.tile(Vinf * np.cos(precone), (1, num_radial)) mu = np.array([1.]).reshape((num_nodes, 1)) asound = np.array([1.]).reshape((num_nodes, 1)) pitch = np.array([0.0]).reshape((num_nodes, 1)) # Airfoil interpolator. af = af_from_files(["airfoils/NACA64_A17.dat"])[0] prob = om.Problem() comp = ccb.LocalInflowAngleComp(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, airfoil_interp=af, turbine=turbine, debug_print=False) comp.linear_solver = om.DirectSolver(assemble_jac=True) prob.model.add_subsystem('ccblade', comp) prob.setup() prob.final_setup() prob['ccblade.phi'] = 1. prob['ccblade.radii'] = r prob['ccblade.chord'] = chord prob['ccblade.theta'] = theta prob['ccblade.Vx'] = Vx prob['ccblade.Vy'] = Vy prob['ccblade.rho'] = rho prob['ccblade.mu'] = mu prob['ccblade.asound'] = asound prob['ccblade.hub_radius'] = Rhub prob['ccblade.prop_radius'] = Rtip prob['ccblade.precone'] = precone prob['ccblade.pitch'] = pitch prob.run_model() Nptest = np.array([ 1.8660880922356378, 2.113489633244873, 2.35855792055661, 2.60301402945597, 2.844874233881403, 3.1180230827072126, 3.560077224628854, 4.024057801497014, 4.480574891998562, 4.9279550384928275, 5.366395080074933, 5.79550918136406, 6.21594163851808, 6.622960527391846, 7.017012349498324, 7.3936834781240774, 7.751945902955048, 8.086176029603802, 8.393537672577372, 8.67090062789216, 8.912426896510306, 9.111379449026037, 9.264491105426602, 9.361598738055728, 9.397710628068818, 9.360730779314666, 9.236967116872792, 9.002418776792911, 8.617229305924996, 7.854554211296309, 5.839491141636506 ]) Tptest = np.array([ 1.481919153409856, 1.5816880353415623, 1.6702432911163534, 1.7502397903925069, 1.822089134395204, 1.8965254874252537, 2.0022647148294554, 2.097706171361262, 2.178824887386094, 2.2475057498944886, 2.3058616094094666, 2.355253913018444, 2.3970643308370168, 2.4307239254050717, 2.4574034513165794, 2.4763893383410522, 2.488405728268889, 2.492461784055084, 2.4887264544021237, 2.4772963155708783, 2.457435891854637, 2.4282986089025607, 2.3902927838322237, 2.3418848562229155, 2.283388513786012, 2.2134191689454954, 2.130781255778788, 2.0328865955896, 1.9153448642630952, 1.7308451522888118, 1.2736544011110416 ]) unormtest = np.array([ 0.1138639166930624, 0.1215785884908478, 0.12836706426605704, 0.13442694075150818, 0.13980334658952898, 0.14527249541450538, 0.15287706861957473, 0.15952130275430465, 0.16497198426904225, 0.16942902209255595, 0.17308482185019125, 0.17607059901193356, 0.17850357997819633, 0.1803781237713692, 0.18177831882512596, 0.18267665167815783, 0.18311924527883217, 0.18305367052760668, 0.182487470134022, 0.1814205780851561, 0.17980424363698078, 0.1775794222894007, 0.1747493664535781, 0.1712044765873724, 0.1669243629724566, 0.16177907188793106, 0.155616714947619, 0.14815634397029886, 0.13888287431637295, 0.12464247057085576, 0.09292645450646951 ]) vnormtest = np.array([ 0.09042291182918308, 0.090986677078917, 0.09090479653774426, 0.09038728871285233, 0.08954144817337718, 0.08836143378908702, 0.08598138211326231, 0.08315706129440237, 0.08022297890584436, 0.07727195122065926, 0.07437202068064472, 0.07155384528141737, 0.06883664444997291, 0.0662014244622726, 0.06365995181515205, 0.061184457505937574, 0.05878201223442723, 0.056423985398129595, 0.05410845965501752, 0.05183227774671869, 0.04957767474023305, 0.04732717658478895, 0.04508635659098153, 0.04282827989707323, 0.04055808783300249, 0.03825394697198818, 0.0358976247398962, 0.033456013675480394, 0.030869388594900515, 0.027466462150913407, 0.020268236545135546 ]) assert_array_almost_equal(prob.get_val('ccblade.Np', units='N/m')[0, :], Nptest, decimal=2) assert_array_almost_equal(prob.get_val('ccblade.Tp', units='N/m')[0, :], Tptest, decimal=2) assert_array_almost_equal( (prob.get_val('ccblade.u', units='m/s') / Vinf)[0, :], unormtest, decimal=2) assert_array_almost_equal( (prob.get_val('ccblade.v', units='m/s') / Vinf)[0, :], vnormtest, decimal=2)
def test_brachistochrone_for_docs_coloring_demo_solve_segments(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.brachistochrone import BrachistochroneODE # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver(optimizer='IPOPT') p.driver.opt_settings['print_level'] = 4 # p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) # # In this case the phase has many segments to demonstrate the impact of coloring. # phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, transcription=dm.Radau(num_segments=100, solve_segments='forward'))) # # Set the variables # phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True) phase.add_state('y', fix_initial=True) phase.add_state('v', fix_initial=True) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', val=9.80665) # # Replace state terminal bounds with nonlinear constraints # phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() # # Setup the Problem # p.setup() # # Set the initial values # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p.set_val('traj.phase0.states:x', phase.interp('x', ys=[0, 10])) p.set_val('traj.phase0.states:y', phase.interp('y', ys=[10, 5])) p.set_val('traj.phase0.states:v', phase.interp('v', ys=[0, 9.9])) p.set_val('traj.phase0.controls:theta', phase.interp('theta', ys=[5, 100.5])) # # Solve for the optimal trajectory # dm.run_problem(p) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = traj.simulate() plot_results( [('traj.phase0.timeseries.states:x', 'traj.phase0.timeseries.states:y', 'x (m)', 'y (m)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:theta', 'time (s)', 'theta (deg)')], title='Brachistochrone Solution\nRadau Pseudospectral Method', p_sol=p, p_sim=exp_out) plt.show()
def test_camber(self): # Copied from CCBlade.jl/test/runtests.jl # inputs chord = 0.10 D = 1.6 RPM = 2100 rho = 1.225 pitch = 1.0 # pitch distance in meters. # --- rotor definition --- turbine = False Rhub = 0.0 Rtip = D / 2 Rhub_eff = 1e-6 # something small to eliminate hub effects Rtip_eff = 100.0 # something large to eliminate tip effects B = 2 # number of blades # --- section definitions --- num_nodes = 1 R = D / 2.0 r = np.linspace(R / 10, R, 11) dr = r[1] - r[0] r = np.tile(r, (num_nodes, 1)) dr = np.tile(dr, r.shape) theta = np.arctan(pitch / (2 * np.pi * r)) def affunc(alpha, Re, M): alpha0 = -3 * np.pi / 180 cl = 6.2 * (alpha - alpha0) cd = 0.008 - 0.003 * cl + 0.01 * cl * cl return cl, cd prob = om.Problem() num_radial = r.shape[-1] comp = om.IndepVarComp() comp.add_output('v', val=[5.0], units='m/s') comp.add_output('omega', val=np.tile(RPM, num_nodes), units='rpm') comp.add_output('radii', val=r, units='m') comp.add_output('dradii', val=dr, units='m') comp.add_output('chord', val=chord, shape=(num_nodes, num_radial), units='m') comp.add_output('theta', val=theta, shape=(num_nodes, num_radial), units='rad') comp.add_output('precone', val=0., shape=num_nodes, units='rad') comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3') comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s') comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s') comp.add_output('hub_radius_eff', val=Rhub_eff, shape=num_nodes, units='m') comp.add_output('prop_radius_eff', val=Rtip_eff, shape=num_nodes, units='m') comp.add_output('hub_radius', val=Rhub, shape=num_nodes, units='m') comp.add_output('prop_radius', val=Rtip, shape=num_nodes, units='m') comp.add_output('pitch', val=0.0, shape=num_nodes, units='rad') prob.model.add_subsystem('ivc', comp, promotes_outputs=['*']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob.model.add_subsystem( "simple_inflow", comp, promotes_inputs=["v", "omega", "radii", "precone"], promotes_outputs=["Vx", "Vy"]) comp = ccb.LocalInflowAngleComp(num_nodes=num_nodes, num_radial=num_radial, num_blades=B, airfoil_interp=affunc, turbine=turbine, debug_print=False) comp.linear_solver = om.DirectSolver(assemble_jac=True) prob.model.add_subsystem('ccblade', comp, promotes_inputs=[ 'radii', 'chord', 'theta', 'Vx', 'Vy', 'rho', 'mu', 'asound', ('hub_radius', 'hub_radius_eff'), ('prop_radius', 'prop_radius_eff'), 'precone', 'pitch' ], promotes_outputs=['Np', 'Tp']) prob.setup() prob.final_setup() prob.run_model() Np = prob.get_val('Np', units='N/m') Tp = prob.get_val('Tp', units='N/m') T = np.sum(Np * dr) * B Q = np.sum(r * Tp * dr) * B assert_rel_error(self, 1223.0506862888788, T, 1e-9) assert_rel_error(self, 113.79919472569034, Q, 1e-10) assert_rel_error(self, prob.get_val('Np', units='N/m')[0, 3], 427.3902632382494, 1e-10) assert_rel_error(self, prob.get_val('Tp', units='N/m')[0, 3], 122.38414345762305, 1e-10) assert_rel_error(self, prob.get_val('ccblade.a')[0, 3], 2.2845512476210943, 1e-8) assert_rel_error(self, prob.get_val('ccblade.ap')[0, 3], 0.05024950801920044, 1e-8) assert_rel_error(self, prob.get_val('ccblade.u', units='m/s')[0, 3], 11.422756238105471, 1e-8) assert_rel_error(self, prob.get_val('ccblade.v', units='m/s')[0, 3], 3.2709314141649575, 1e-8) assert_rel_error(self, prob.get_val('ccblade.phi', units='rad')[0, 3], 0.2596455971546484, 1e-8) assert_rel_error(self, prob.get_val('ccblade.alpha', units='rad')[0, 3], 0.23369406105568025, 1e-8) assert_rel_error(self, prob.get_val('ccblade.W', units='m/s')[0, 3], 63.96697566502531, 1e-8) assert_rel_error(self, prob.get_val('ccblade.cl')[0, 3], 1.773534419416163, 1e-8) assert_rel_error(self, prob.get_val('ccblade.cd')[0, 3], 0.03413364011028978, 1e-8) assert_rel_error(self, prob.get_val('ccblade.cn')[0, 3], 1.7053239640124302, 1e-8) assert_rel_error(self, prob.get_val('ccblade.ct')[0, 3], 0.48832327407767123, 1e-8) assert_rel_error(self, prob.get_val('ccblade.F')[0, 3], 1.0, 1e-8) assert_rel_error(self, prob.get_val('ccblade.G')[0, 3], 1.0, 1e-8) theta = np.arctan(pitch / (2 * np.pi * r)) - 3 * np.pi / 180 prob.set_val('theta', theta, units='rad') prob.run_model() Np = prob.get_val('Np', units='N/m') Tp = prob.get_val('Tp', units='N/m') T = np.sum(Np * dr) * B Q = np.sum(r * Tp * dr) * B assert_rel_error(self, T, 1e3 * 0.962407923825140, 1e-6) assert_rel_error(self, Q, 1e2 * 0.813015017103876, 1e-6)
def test_brachistochrone_for_docs_gauss_lobatto(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.brachistochrone import BrachistochroneODE import matplotlib.pyplot as plt # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10))) # # Set the variables # phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=True) phase.add_state('y', fix_initial=True, fix_final=True) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', val=9.80665) # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() # # Setup the Problem # p.setup() # # Set the initial values # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p.set_val('traj.phase0.states:x', phase.interp('x', ys=[0, 10])) p.set_val('traj.phase0.states:y', phase.interp('y', ys=[10, 5])) p.set_val('traj.phase0.states:v', phase.interp('v', ys=[0, 9.9])) p.set_val('traj.phase0.controls:theta', phase.interp('theta', ys=[5, 100.5])) # # Solve for the optimal trajectory # dm.run_problem(p) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = traj.simulate() plot_results( [('traj.phase0.timeseries.states:x', 'traj.phase0.timeseries.states:y', 'x (m)', 'y (m)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:theta', 'time (s)', 'theta (deg)')], title='Brachistochrone Solution\nHigh-Order Gauss-Lobatto Method', p_sol=p, p_sim=exp_out) plt.show()
def test_hover(self): # num_nodes = 40 nstart = 0 nend = 39 num_nodes = nend - nstart + 1 chord = 0.060 theta = 0.0 Rtip = 0.656 Rhub = 0.19 * Rtip rho = 1.225 omega = 800.0 * np.pi / 30 Vinf = 0.0 turbine = False B = 3 r = np.linspace(Rhub + 0.01 * Rtip, Rtip - 0.01 * Rtip, 30) num_radial = r.shape[-1] r = np.tile(r, (num_nodes, 1)) chord = np.tile(chord, (num_nodes, num_radial)) theta = np.tile(theta, (num_nodes, num_radial)) # Airfoil interpolator. af = af_from_files(["airfoils/naca0012v2.txt"])[0] pitch = np.linspace(1e-4, 20 * np.pi / 180, 40)[nstart:nend + 1] prob = om.Problem() comp = om.IndepVarComp() comp.add_output('v', val=Vinf, shape=num_nodes, units='m/s') comp.add_output('omega', val=np.tile(omega, num_nodes), units='rad/s') comp.add_output('radii', val=r, shape=(num_nodes, num_radial), units='m') comp.add_output('chord', val=chord, shape=(num_nodes, num_radial), units='m') comp.add_output('theta', val=theta, shape=(num_nodes, num_radial), units='rad') comp.add_output('precone', val=0., shape=num_nodes, units='rad') comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3') comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s') comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s') comp.add_output('hub_radius', val=Rhub, shape=num_nodes, units='m') comp.add_output('prop_radius', val=Rtip, shape=num_nodes, units='m') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') prob.model.add_subsystem('ivc', comp, promotes_outputs=['*']) comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial) prob.model.add_subsystem( "simple_inflow", comp, promotes_inputs=["v", "omega", "radii", "precone"], promotes_outputs=["Vx", "Vy"]) comp = ccb.LocalInflowAngleComp(num_nodes=num_nodes, num_radial=num_radial, num_blades=B, airfoil_interp=af, turbine=turbine, debug_print=False) comp.linear_solver = om.DirectSolver(assemble_jac=True) prob.model.add_subsystem('ccblade', comp, promotes_inputs=[ 'radii', 'chord', 'theta', 'Vx', 'Vy', 'rho', 'mu', 'asound', 'hub_radius', 'prop_radius', 'precone', 'pitch' ], promotes_outputs=['Np', 'Tp']) comp = ccb.FunctionalsComp(num_nodes=num_nodes, num_radial=num_radial, num_blades=B) prob.model.add_subsystem( 'ccblade_torquethrust_comp', comp, promotes_inputs=[ 'hub_radius', 'prop_radius', 'radii', 'Np', 'Tp', 'v', 'omega' ], promotes_outputs=['thrust', 'torque', 'efficiency']) prob.setup() prob.final_setup() prob.run_model() # these are not directly from the experimental data, but have been compared to the experimental data and compare favorably. # this is more of a regression test on the Vx=0 case CTcomp = np.array([ 9.452864991304056e-9, 6.569947366946672e-5, 0.00022338783939012262, 0.0004420355541809959, 0.0007048495858030926, 0.0010022162314665929, 0.0013268531109981317, 0.0016736995380106938, 0.0020399354072946035, 0.0024223576277264307, 0.0028189858460418893, 0.0032281290309981213, 0.003649357660426685, 0.004081628946214875, 0.004526034348853718, 0.004982651929181267, 0.0054553705714941, 0.005942700094508395, 0.006447634897014323, 0.006963626871239654, 0.007492654931894796, 0.00803866268066438, 0.008597914974368199, 0.009163315934297088, 0.00973817187875574, 0.010309276997090536, 0.010827599471613264, 0.011322361524464346, 0.01180210507896255, 0.012276543435307877, 0.012749323136224754, 0.013223371028562213, 0.013697833731701945, 0.01417556699620018, 0.014646124777465859, 0.015112116772851365, 0.015576452747370885, 0.01602507607909594, 0.016461827164870473, 0.016880126012974343 ]) CQcomp = np.array([ 0.000226663607327854, 0.0002270862930229147, 0.0002292742856722754, 0.00023412703235791698, 0.00024192624628054639, 0.0002525855612031453, 0.00026638347417704255, 0.00028314784456601373, 0.00030299181501156373, 0.0003259970210015136, 0.00035194661281707764, 0.00038102864688744595, 0.0004132249034847219, 0.00044859355432807347, 0.0004873204055790553, 0.0005293656187218555, 0.0005753409000182888, 0.0006250099998058788, 0.0006788861946930185, 0.0007361096750412038, 0.0007970800153713466, 0.0008624036743669367, 0.0009315051772818803, 0.0010035766105979213, 0.0010791941808362153, 0.0011566643573792704, 0.001229236439467123, 0.0013007334425769355, 0.001372124993921022, 0.0014449961686871802, 0.0015197156782734364, 0.0015967388663224156, 0.0016761210460920718, 0.0017578748614666766, 0.0018409716992061841, 0.0019248522013432586, 0.0020103360819251357, 0.002096387027559033, 0.002182833604491109, 0.0022686470790128036 ]) T = prob.get_val('thrust', units='N') Q = prob.get_val('torque', units='N*m') A = np.pi * Rtip**2 CT = T / (rho * A * (omega * Rtip)**2) CQ = Q / (rho * A * (omega * Rtip)**2 * Rtip) assert_array_almost_equal(CT, CTcomp[nstart:nend + 1], decimal=3) assert_array_almost_equal(CQ, CQcomp[nstart:nend + 1], decimal=3)
def test_control_boundary_constraint_gl(self): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=20, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') phase.add_state( 'x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state( 'y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state( 'v', rate_source=BrachistochroneODE.states['v']['rate_source'], targets=BrachistochroneODE.states['v']['targets'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_control( 'theta', targets=BrachistochroneODE.parameters['theta']['targets'], units='deg', lower=0.01, upper=179.9) phase.add_parameter( 'g', targets=BrachistochroneODE.parameters['g']['targets'], opt=False, units='m/s**2', val=9.80665) phase.add_boundary_constraint('theta', loc='final', lower=90.0, upper=90.0, units='deg') # Minimize time at the end of the phase phase.add_objective('time') p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100], nodes='control_input') p['phase0.parameters:g'] = 8 p.run_driver() assert_near_equal( p.get_val('phase0.timeseries.controls:theta', units='deg')[-1], 90.0)