def test_direct_solver_comp(self): """ Test the direct solver on a component. """ for jac in [None, 'csc', 'dense']: prob = Problem(model=ImplComp4Test()) prob.model.nonlinear_solver = NewtonSolver() if jac in ('csc', 'dense'): prob.model.options['assembled_jac_type'] = jac prob.model.linear_solver = DirectSolver(assemble_jac=jac in ('csc','dense')) prob.set_solver_print(level=0) prob.setup(check=False) prob.run_model() assert_rel_error(self, prob['y'], [-1., 1.]) d_inputs, d_outputs, d_residuals = prob.model.get_linear_vectors() d_residuals.set_const(2.0) d_outputs.set_const(0.0) prob.model.run_solve_linear(['linear'], 'fwd') result = d_outputs.get_data() assert_rel_error(self, result, [-2., 2.]) d_outputs.set_const(2.0) d_residuals.set_const(0.0) prob.model.run_solve_linear(['linear'], 'rev') result = d_residuals.get_data() assert_rel_error(self, result, [2., -2.])
def test_direct_solver_comp(self): """ Test the direct solver on a component. """ for jac in [None, 'csc', 'dense']: prob = Problem(model=ImplComp4Test()) prob.model.nonlinear_solver = NewtonSolver(solve_subsystems=False) if jac in ('csc', 'dense'): prob.model.options['assembled_jac_type'] = jac prob.model.linear_solver = DirectSolver(assemble_jac=jac in ('csc','dense')) prob.set_solver_print(level=0) prob.setup() prob.run_model() assert_near_equal(prob['y'], [-1., 1.]) d_inputs, d_outputs, d_residuals = prob.model.get_linear_vectors() d_residuals.set_val(2.0) d_outputs.set_val(0.0) prob.model.run_solve_linear('fwd') result = d_outputs.asarray() assert_near_equal(result, [-2., 2.]) d_outputs.set_val(2.0) d_residuals.set_val(0.0) prob.model.run_solve_linear('rev') result = d_residuals.asarray() assert_near_equal(result, [2., -2.])
def test_direct_solver_comp(self): """ Test the direct solver on a component. """ for jac in [None, 'csc', 'dense']: prob = Problem(model=ImplComp4Test()) prob.model.nonlinear_solver = NewtonSolver() if jac in ('csc', 'dense'): prob.model.options['assembled_jac_type'] = jac prob.model.linear_solver = DirectSolver(assemble_jac=jac in ('csc','dense')) prob.set_solver_print(level=0) prob.setup(check=False) prob.run_model() assert_rel_error(self, prob['y'], [-1., 1.]) d_inputs, d_outputs, d_residuals = prob.model.get_linear_vectors() d_residuals.set_const(2.0) d_outputs.set_const(0.0) prob.model.run_solve_linear(['linear'], 'fwd') result = d_outputs._data assert_rel_error(self, result, [-2., 2.]) d_outputs.set_const(2.0) d_residuals.set_const(0.0) prob.model.run_solve_linear(['linear'], 'rev') result = d_residuals._data assert_rel_error(self, result, [2., -2.])
def test_direct_solver_comp(self): """ Test the direct solver on a component. """ for jac in ['dict', 'coo', 'csr', 'csc', 'dense']: prob = Problem(model=ImplComp4Test()) prob.model.nonlinear_solver = NewtonSolver() prob.model.linear_solver = DirectSolver() prob.set_solver_print(level=0) if jac == 'dict': pass elif jac == 'csr': prob.model.jacobian = CSRJacobian() elif jac == 'csc': prob.model.jacobian = CSCJacobian() elif jac == 'coo': prob.model.jacobian = COOJacobian() elif jac == 'dense': prob.model.jacobian = DenseJacobian() prob.setup(check=False) if jac == 'coo': with self.assertRaises(Exception) as context: prob.run_model() self.assertEqual( str(context.exception), "Direct solver is not compatible with matrix type COOMatrix in system ''." ) continue prob.run_model() assert_rel_error(self, prob['y'], [-1., 1.]) d_inputs, d_outputs, d_residuals = prob.model.get_linear_vectors() d_residuals.set_const(2.0) d_outputs.set_const(0.0) prob.model.run_solve_linear(['linear'], 'fwd') result = d_outputs.get_data() assert_rel_error(self, result, [-2., 2.]) d_outputs.set_const(2.0) d_residuals.set_const(0.0) prob.model.run_solve_linear(['linear'], 'rev') result = d_residuals.get_data() assert_rel_error(self, result, [2., -2.])
def test_shape(self): import numpy as np from openmdao.api import Group, Problem, IndepVarComp from openmdao.components.meta_model_structured import MetaModelStructured # create input param training data, of sizes 25, 5, and 10 points resp. p1 = np.linspace(0, 100, 25) p2 = np.linspace(-10, 10, 5) p3 = np.linspace(0, 1, 10) # can use meshgrid to create a 3D array of test data P1, P2, P3 = np.meshgrid(p1, p2, p3, indexing='ij') f = np.sqrt(P1) + P2 * P3 # verify the shape matches the order and size of the input params print(f.shape) # Create regular grid interpolator instance interp = MetaModelStructured(method='cubic') interp.add_input('p1', 0.5, p1) interp.add_input('p2', 0.0, p2) interp.add_input('p3', 3.14, p3) interp.add_output('f', 0.0, f) # Set up the OpenMDAO model model = Group() model.add_subsystem('comp', interp, promotes=["*"]) prob = Problem(model) prob.setup() # set inputs prob['p1'] = 55.12 prob['p2'] = -2.14 prob['p3'] = 0.323 prob.run_model() computed = prob['f'] actual = 6.73306472 assert_almost_equal(computed, actual) # we can verify all gradients by checking against finit-difference prob.check_partials(compact_print=True)
def test_training_derivatives(self): import numpy as np from openmdao.api import Group, Problem, IndepVarComp from openmdao.components.meta_model_structured import MetaModelStructured # create input param training data, of sizes 25, 5, and 10 points resp. p1 = np.linspace(0, 100, 25) p2 = np.linspace(-10, 10, 5) p3 = np.linspace(0, 1, 10) # can use meshgrid to create a 3D array of test data P1, P2, P3 = np.meshgrid(p1, p2, p3, indexing='ij') f = np.sqrt(P1) + P2 * P3 # verify the shape matches the order and size of the input params print(f.shape) # Create regular grid interpolator instance interp = MetaModelStructured(method='cubic', training_data_gradients=True) interp.add_input('p1', 0.5, p1) interp.add_input('p2', 0.0, p2) interp.add_input('p3', 3.14, p3) interp.add_output('f', 0.0, f) # Set up the OpenMDAO model model = Group() model.add_subsystem('comp', interp, promotes=["*"]) prob = Problem(model) prob.setup() # set inputs prob['p1'] = 55.12 prob['p2'] = -2.14 prob['p3'] = 0.323 prob.run_model() computed = prob['f'] actual = 6.73306472 assert_almost_equal(computed, actual) # we can verify all gradients by checking against finit-difference prob.check_partials(compact_print=True)
def test_meta_model_structured_deprecated(self): # run same test as above, only with the deprecated component, # to ensure we get the warning and the correct answer. # self-contained, to be removed when class name goes away. import numpy as np from openmdao.api import Group, Problem, IndepVarComp from openmdao.components.meta_model_structured_comp import MetaModelStructured # deprecated import warnings with warnings.catch_warnings(record=True) as w: xor_interp = MetaModelStructured(method='slinear') self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, DeprecationWarning)) self.assertEqual(str(w[0].message), "'MetaModelStructured' has been deprecated. Use " "'MetaModelStructuredComp' instead.") # set up inputs and outputs xor_interp.add_input('x', 0.0, training_data=np.array([0.0, 1.0]), units=None) xor_interp.add_input('y', 1.0, training_data=np.array([0.0, 1.0]), units=None) xor_interp.add_output('xor', 1.0, training_data=np.array([[0.0, 1.0], [1.0, 0.0]]), units=None) # Set up the OpenMDAO model model = Group() ivc = IndepVarComp() ivc.add_output('x', 0.0) ivc.add_output('y', 1.0) model.add_subsystem('ivc', ivc, promotes=["*"]) model.add_subsystem('comp', xor_interp, promotes=["*"]) prob = Problem(model) prob.setup() # Now test out a 'fuzzy' XOR prob['x'] = 0.9 prob['y'] = 0.001242 prob.run_model() computed = prob['xor'] actual = 0.8990064 assert_almost_equal(computed, actual) # we can verify all gradients by checking against finite-difference prob.check_partials(compact_print=True)
def test_direct_solver_comp(self): """ Test the direct solver on a component. """ for jac in ['dict', 'coo', 'csr', 'csc', 'dense']: prob = Problem(model=ImplComp4Test()) prob.model.nonlinear_solver = NewtonSolver() prob.model.linear_solver = DirectSolver() prob.set_solver_print(level=0) if jac == 'dict': pass elif jac == 'csr': prob.model.jacobian = CSRJacobian() elif jac == 'csc': prob.model.jacobian = CSCJacobian() elif jac == 'coo': prob.model.jacobian = COOJacobian() elif jac == 'dense': prob.model.jacobian = DenseJacobian() prob.setup(check=False) if jac == 'coo': with self.assertRaises(Exception) as context: prob.run_model() self.assertEqual(str(context.exception), "Direct solver is not compatible with matrix type COOMatrix in system ''.") continue prob.run_model() assert_rel_error(self, prob['y'], [-1., 1.]) d_inputs, d_outputs, d_residuals = prob.model.get_linear_vectors() d_residuals.set_const(2.0) d_outputs.set_const(0.0) prob.model.run_solve_linear(['linear'], 'fwd') result = d_outputs.get_data() assert_rel_error(self, result, [-2., 2.]) d_outputs.set_const(2.0) d_residuals.set_const(0.0) prob.model.run_solve_linear(['linear'], 'rev') result = d_residuals.get_data() assert_rel_error(self, result, [2., -2.])
def test_training_gradient(self): model = Group() ivc = IndepVarComp() mapdata = SampleMap() params = mapdata.param_data outs = mapdata.output_data ivc.add_output('x', np.array([-0.3, 0.7, 1.2])) ivc.add_output('y', np.array([0.14, 0.313, 1.41])) ivc.add_output('z', np.array([-2.11, -1.2, 2.01])) ivc.add_output('f_train', outs[0]['values']) ivc.add_output('g_train', outs[1]['values']) comp = MetaModelStructured(training_data_gradients=True, method='cubic', num_nodes=3) for param in params: comp.add_input(param['name'], param['default'], param['values']) for out in outs: comp.add_output(out['name'], out['default'], out['values']) model.add_subsystem('ivc', ivc, promotes=["*"]) model.add_subsystem('comp', comp, promotes=["*"]) prob = Problem(model) prob.setup() prob.run_model() val0 = np.array([ 50.26787317, 49.76106232, 19.66117913]) val1 = np.array([-32.62094041, -31.67449135, -27.46959668]) tol = 1e-5 assert_rel_error(self, prob['f'], val0, tol) assert_rel_error(self, prob['g'], val1, tol) self.run_and_check_derivs(prob)
def test_training_gradient(self): model = Group() ivc = IndepVarComp() mapdata = SampleMap() params = mapdata.param_data outs = mapdata.output_data ivc.add_output('x', np.array([-0.3, 0.7, 1.2])) ivc.add_output('y', np.array([0.14, 0.313, 1.41])) ivc.add_output('z', np.array([-2.11, -1.2, 2.01])) ivc.add_output('f_train', outs[0]['values']) ivc.add_output('g_train', outs[1]['values']) comp = MetaModelStructuredComp(training_data_gradients=True, method='cubic', num_nodes=3) for param in params: comp.add_input(param['name'], param['default'], param['values']) for out in outs: comp.add_output(out['name'], out['default'], out['values']) model.add_subsystem('ivc', ivc, promotes=["*"]) model.add_subsystem('comp', comp, promotes=["*"]) prob = Problem(model) prob.setup() prob.run_model() val0 = np.array([ 50.26787317, 49.76106232, 19.66117913]) val1 = np.array([-32.62094041, -31.67449135, -27.46959668]) tol = 1e-5 assert_rel_error(self, prob['f'], val0, tol) assert_rel_error(self, prob['g'], val1, tol) self.run_and_check_derivs(prob)
def test_xor(self): import numpy as np from openmdao.api import Group, Problem, IndepVarComp from openmdao.components.meta_model_structured import MetaModelStructured # Create regular grid interpolator instance xor_interp = MetaModelStructured(method='slinear') # set up inputs and outputs xor_interp.add_input('x', 0.0, np.array([0.0, 1.0]), units=None) xor_interp.add_input('y', 1.0, np.array([0.0, 1.0]), units=None) xor_interp.add_output('xor', 1.0, np.array([[0.0, 1.0], [1.0, 0.0]]), units=None) # Set up the OpenMDAO model model = Group() ivc = IndepVarComp() ivc.add_output('x', 0.0) ivc.add_output('y', 1.0) model.add_subsystem('ivc', ivc, promotes=["*"]) model.add_subsystem('comp', xor_interp, promotes=["*"]) prob = Problem(model) prob.setup() # Now test out a 'fuzzy' XOR prob['x'] = 0.9 prob['y'] = 0.001242 prob.run_model() computed = prob['xor'] actual = 0.8990064 assert_almost_equal(computed, actual) # we can verify all gradients by checking against finit-difference prob.check_partials(compact_print=True)
def test_solver_debug_print_feature(self): from openmdao.api import Problem, IndepVarComp, NewtonSolver from openmdao.test_suite.test_examples.test_circuit_analysis import Circuit p = Problem() model = p.model model.add_subsystem('ground', IndepVarComp('V', 0., units='V')) model.add_subsystem('source', IndepVarComp('I', 0.1, units='A')) model.add_subsystem('circuit', Circuit()) model.connect('source.I', 'circuit.I_in') model.connect('ground.V', 'circuit.Vg') p.setup() nl = model.circuit.nonlinear_solver = NewtonSolver() nl.options['iprint'] = 2 nl.options['debug_print'] = True # set some poor initial guesses so that we don't converge p['circuit.n1.V'] = 10. p['circuit.n2.V'] = 1e-3 opts = {} # formatting has changed in numpy 1.14 and beyond. if LooseVersion(np.__version__) >= LooseVersion("1.14"): opts["legacy"] = '1.13' with printoptions(**opts): # run the model p.run_model() with open('rank0_root_0_NLRunOnce_0_circuit_0.dat', 'r') as f: self.assertEqual(f.read(), self.expected_data)
class ODEIntegrationInterface(object): """ Given a system class, create a callable object with the same signature as that required by scipy.integrate.ode:: f(t, x, *args) Internally, this is accomplished by constructing an OpenMDAO problem using the ODE with a single node. The interface populates the values of the time, states, and controls, and then calls `run_model()` on the problem. The state rates generated by the ODE are then returned back to scipy ode, which continues the integration. Parameters ---------- ode_class : class The ODEClass belonging to the phase being simulated. time_options : dict of {str: TimeOptionsDictionary} The time options for the phase being simulated. state_options : dict of {str: StateOptionsDictionary} The state options for the phase being simulated. control_options : dict of {str: ControlOptionsDictionary} The control options for the phase being simulated. design_parameter_options : dict of {str: DesignParameterOptionsDictionary} The design parameter options for the phase being simulated. input_parameter_options : dict of {str: InputParameterOptionsDictionary} The input parameter options for the phase being simulated. ode_init_kwargs : dict Keyword argument dictionary passed to the ODE at initialization. """ def __init__(self, ode_class, time_options, state_options, control_options, polynomial_control_options, design_parameter_options, input_parameter_options, traj_parameter_options, ode_init_kwargs=None): # Get the state vector. This isn't necessarily ordered # so just pick the default ordering and go with it. self.state_options = OrderedDict() self.time_options = time_options self.control_options = control_options self.polynomial_control_options = polynomial_control_options self.design_parameter_options = design_parameter_options self.input_parameter_options = input_parameter_options self.traj_parameter_options = traj_parameter_options self.control_interpolants = {} self.polynomial_control_interpolants = {} pos = 0 for state, options in iteritems(state_options): self.state_options[state] = { 'rate_source': options['rate_source'], 'pos': pos, 'shape': options['shape'], 'size': np.prod(options['shape']), 'units': options['units'], 'targets': options['targets'] } pos += self.state_options[state]['size'] self._state_vec = np.zeros(pos, dtype=float) self._state_rate_vec = np.zeros(pos, dtype=float) # # Build odeint problem interface # self.prob = Problem(model=Group()) model = self.prob.model # The time IVC ivc = IndepVarComp() time_units = self.time_options['units'] ivc.add_output('time', val=0.0, units=time_units) ivc.add_output('time_phase', val=-88.0, units=time_units) ivc.add_output('t_initial', val=-99.0, units=time_units) ivc.add_output('t_duration', val=-111.0, units=time_units) model.add_subsystem('time_input', ivc, promotes_outputs=['*']) model.connect( 'time', ['ode.{0}'.format(tgt) for tgt in self.time_options['targets']]) model.connect('time_phase', [ 'ode.{0}'.format(tgt) for tgt in self.time_options['time_phase_targets'] ]) model.connect('t_initial', [ 'ode.{0}'.format(tgt) for tgt in self.time_options['t_initial_targets'] ]) model.connect('t_duration', [ 'ode.{0}'.format(tgt) for tgt in self.time_options['t_duration_targets'] ]) # The States Comp for name, options in iteritems(self.state_options): ivc.add_output('states:{0}'.format(name), shape=(1, np.prod(options['shape'])), units=options['units']) rate_src = self._get_rate_source_path(name) model.connect( rate_src, 'state_rate_collector.state_rates_in:{0}_rate'.format(name)) if options['targets'] is not None: model.connect( 'states:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in options['targets']]) if self.control_options or self.polynomial_control_options: self._interp_comp = \ ODEIntControlInterpolationComp(time_units=time_units, control_options=self.control_options, polynomial_control_options=self.polynomial_control_options) self._interp_comp.control_interpolants = self.control_interpolants self._interp_comp.polynomial_control_interpolants = self.polynomial_control_interpolants model.add_subsystem('indep_controls', self._interp_comp, promotes_outputs=['*']) model.connect('time', ['indep_controls.time']) if self.control_options: for name, options in iteritems(self.control_options): if options['targets']: model.connect( 'controls:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in options['targets']]) if options['rate_targets']: model.connect('control_rates:{0}_rate'.format(name), [ 'ode.{0}'.format(tgt) for tgt in options['rate_targets'] ]) if options['rate2_targets']: model.connect('control_rates:{0}_rate2'.format(name), [ 'ode.{0}'.format(tgt) for tgt in options['rate2_targets'] ]) if self.polynomial_control_options: for name, options in iteritems(self.polynomial_control_options): tgts = options['targets'] rate_tgts = options['rate_targets'] rate2_tgts = options['rate2_targets'] if options['targets']: if isinstance(tgts, string_types): tgts = [tgts] model.connect('polynomial_controls:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in tgts]) if options['rate_targets']: if isinstance(rate_tgts, string_types): rate_tgts = [rate_tgts] model.connect( 'polynomial_control_rates:{0}_rate'.format(name), ['ode.{0}'.format(tgt) for tgt in rate_tgts]) if options['rate2_targets']: if isinstance(rate2_tgts, string_types): rate2_tgts = [rate2_tgts] model.connect( 'polynomial_control_rates:{0}_rate2'.format(name), ['ode.{0}'.format(tgt) for tgt in rate2_tgts]) if self.design_parameter_options: for name, options in iteritems(self.design_parameter_options): ivc.add_output('design_parameters:{0}'.format(name), shape=options['shape'], units=options['units']) if options['targets'] is not None: tgts = options['targets'] if isinstance(tgts, string_types): tgts = [tgts] model.connect('design_parameters:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in tgts]) if self.input_parameter_options: for name, options in iteritems(self.input_parameter_options): ivc.add_output('input_parameters:{0}'.format(name), shape=options['shape'], units=options['units']) if options['targets'] is not None: tgts = options['targets'] if isinstance(tgts, string_types): tgts = [tgts] model.connect('input_parameters:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in tgts]) if self.traj_parameter_options: for name, options in iteritems(self.traj_parameter_options): ivc.add_output('traj_parameters:{0}'.format(name), shape=options['shape'], units=options['units']) if options['targets'] is not None: tgts = options['targets'] if isinstance(tgts, string_types): tgts = [tgts] model.connect('traj_parameters:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in tgts]) # The ODE System if ode_class is not None: model.add_subsystem('ode', subsys=ode_class(num_nodes=1, **ode_init_kwargs)) # The state rate collector comp self.prob.model.add_subsystem( 'state_rate_collector', StateRateCollectorComp(state_options=self.state_options, time_units=time_options['units'])) # Flag that is set to true if has_controls is called self._has_dynamic_controls = False def _get_rate_source_path(self, state_var): var = self.state_options[state_var]['rate_source'] if var == 'time': rate_path = 'time' elif var == 'time_phase': rate_path = 'time_phase' elif self.state_options is not None and var in self.state_options: rate_path = 'states:{0}'.format(var) elif self.control_options is not None and var in self.control_options: rate_path = 'controls:{0}'.format(var) elif self.polynomial_control_options is not None and var in self.polynomial_control_options: rate_path = 'polynomial_controls:{0}'.format(var) elif self.design_parameter_options is not None and var in self.design_parameter_options: rate_path = 'design_parameters:{0}'.format(var) elif self.input_parameter_options is not None and var in self.input_parameter_options: rate_path = 'input_parameters:{0}'.format(var) elif self.traj_parameter_options is not None and var in self.traj_parameter_options: rate_path = 'traj_parameters:{0}'.format(var) elif var.endswith('_rate') and self.control_options is not None and \ var[:-5] in self.control_options: rate_path = 'control_rates:{0}'.format(var) elif var.endswith('_rate2') and self.control_options is not None and \ var[:-6] in self.control_options: rate_path = 'control_rates:{0}'.format(var) elif var.endswith('_rate') and self.polynomial_control_options is not None and \ var[:-5] in self.polynomial_control_options: rate_path = 'polynomial_control_rates:{0}'.format(var) elif var.endswith('_rate2') and self.polynomial_control_options is not None and \ var[:-6] in self.polynomial_control_options: rate_path = 'polynomial_control_rates:{0}'.format(var) else: rate_path = 'ode.{0}'.format(var) return rate_path def _unpack_state_vec(self, x): """ Given the state vector in 1D, extract the values corresponding to each state into the ode integrators problem states. Parameters ---------- x : np.array The 1D state vector. Returns ------- None """ for state_name, state_options in self.state_options.items(): pos = state_options['pos'] size = state_options['size'] self.prob['states:{0}'.format(state_name)][0, ...] = x[pos:pos + size] def _pack_state_rate_vec(self): """ Pack the state rates into a 1D vector for use by scipy odeint. Returns ------- dXdt: np.array The 1D state-rate vector. """ for state_name, state_options in self.state_options.items(): pos = state_options['pos'] size = state_options['size'] self._state_rate_vec[pos:pos + size] = \ np.ravel(self.prob['state_rate_collector.' 'state_rates:{0}_rate'.format(state_name)]) return self._state_rate_vec def _pack_state_vec(self, x_dict): """ Pack the state into a 1D vector for use by scipy.integrate.ode. Returns ------- x: np.array The 1D state vector. """ self._state_vec[:] = 0.0 for state_name, state_options in self.state_options.items(): pos = state_options['pos'] size = state_options['size'] self._state_vec[pos:pos + size] = np.ravel(x_dict[state_name]) return self._state_vec def __call__(self, t, x): """ The function interface used by scipy.ode Parameters ---------- t : float The current time, t. x : np.array The 1D state vector. Returns ------- xdot : np.array The 1D vector of state time-derivatives. """ self.prob['time'] = t self.prob['time_phase'] = t - self.prob['t_initial'] self._unpack_state_vec(x) self.prob.run_model() xdot = self._pack_state_rate_vec() return xdot
g = p.model if 'gmres' in sys.argv: from openmdao.solvers.linear.scipy_iter_solver import ScipyKrylov p.root.linear_solver = ScipyKrylov() g.add_subsystem("P", IndepVarComp('x', numpy.ones(vec_size))) g.add_design_var("P.x") par = g.add_subsystem("par", ParallelGroup()) for pt in range(pts): ptname = "G%d"%pt ptg = par.add_subsystem(ptname, SubGroup()) #create_dyncomps(ptg, num_comps, 2, 2, 2, #var_factory=lambda: numpy.zeros(vec_size)) g.connect("P.x", "par.%s.C0.i0" % ptname) #cname = ptname + '.' + "C%d"%(num_comps-1) #g.add_objective("par.%s.o0" % cname) #g.add_constraint("par.%s.o1" % cname, lower=0.0) p.setup(vector_class=vec_class) p.final_setup() p.run_model() # from openmdao.devtools.debug import max_mem_usage print("mem:", max_mem_usage()) config_summary(p)
des_vars.add_output('MN', 0.5) des_vars.add_output('dTs', 0.0, units='degR') fc = p1.model.add_subsystem("fc", FlightConditions()) p1.model.connect('des_vars.W', 'fc.W') p1.model.connect('des_vars.alt', 'fc.alt') p1.model.connect('des_vars.MN', 'fc.MN') p1.model.connect('des_vars.dTs', 'fc.dTs') p1.setup() # p1.root.list_connections() p1['des_vars.alt'] = 17868.79060515557 p1['des_vars.MN'] = 2.101070288213628 p1['des_vars.dTs'] = 0.0 p1['des_vars.W'] = 1.0 p1.run_model() print('Ts_atm: ', p1['fc.ambient.Ts']) print('Ts_set: ', p1['fc.Fl_O:stat:T']) print('Ps_atm: ', p1['fc.ambient.Ps']) print('Ps_set: ', p1['fc.Fl_O:stat:P']) print('rhos_atm: ', p1['fc.ambient.rhos'] * 32.175) print('rhos_set: ', p1['fc.Fl_O:stat:rho']) print('W', p1['fc.Fl_O:stat:W']) print('Pt: ', p1['fc.Fl_O:tot:P'])
g = p.model if 'gmres' in sys.argv: from openmdao.solvers.linear.scipy_iter_solver import ScipyKrylov g.linear_solver = ScipyKrylov() g.add_subsystem("P", IndepVarComp('x', numpy.ones(vec_size))) g.add_design_var("P.x") par = g.add_subsystem("par", ParallelGroup()) for pt in range(pts): ptname = "G%d" % pt ptg = par.add_subsystem(ptname, SubGroup()) #create_dyncomps(ptg, num_comps, 2, 2, 2, #var_factory=lambda: numpy.zeros(vec_size)) g.connect("P.x", "par.%s.C0.i0" % ptname) #cname = ptname + '.' + "C%d"%(num_comps-1) #g.add_objective("par.%s.o0" % cname) #g.add_constraint("par.%s.o1" % cname, lower=0.0) p.setup() p.final_setup() p.run_model() # from openmdao.devtools.memory import max_mem_usage print("mem:", max_mem_usage()) config_summary(p)
y = inputs['y'] partials['f_xy', 'x'] = 2.0 * x - 6.0 + y partials['f_xy', 'y'] = 2.0 * y + 8.0 + x if __name__ == "__main__": from openmdao.core.problem import Problem from openmdao.core.group import Group from openmdao.core.indepvarcomp import IndepVarComp model = Group() ivc = IndepVarComp() ivc.add_output('x', 3.0) ivc.add_output('y', -4.0) model.add_subsystem('des_vars', ivc) model.add_subsystem('parab_comp', Paraboloid()) model.connect('des_vars.x', 'parab_comp.x') model.connect('des_vars.y', 'parab_comp.y') prob = Problem(model) prob.setup() prob.run_model() print(prob['parab_comp.f_xy']) prob['des_vars.x'] = 5.0 prob['des_vars.y'] = -2.0 prob.run_model() print(prob['parab_comp.f_xy'])
x = inputs['x'] y = inputs['y'] outputs['f_xy'] = (x-3.0)**2 + x*y + (y+4.0)**2 - 3.0 if __name__ == "__main__": from openmdao.core.problem import Problem from openmdao.core.group import Group from openmdao.core.indepvarcomp import IndepVarComp model = Group() ivc = IndepVarComp() ivc.add_output('x', 3.0) ivc.add_output('y', -4.0) model.add_subsystem('des_vars', ivc) model.add_subsystem('parab_comp', Paraboloid()) model.connect('des_vars.x', 'parab_comp.x') model.connect('des_vars.y', 'parab_comp.y') prob = Problem(model) prob.setup() prob.run_model() print(prob['parab_comp.f_xy']) prob['des_vars.x'] = 5.0 prob['des_vars.y'] = -2.0 prob.run_model() print(prob['parab_comp.f_xy'])
class MetaModelVisualization(object): """ Top-level container for the Meta Model Visualization. Attributes ---------- prob : Problem Name of variable corresponding to Problem Component meta_model : MetaModel Name of empty Meta Model Component object reference resolution : int Number used to calculate width and height of contour plot is_structured_meta_model : Bool Boolean used to signal whether the meta model is structured or unstructured slider_source : ColumnDataSource Data source containing dictionary of sliders contour_training_data_source : ColumnDataSource Data source containing dictionary of training data points bottom_plot_source : ColumnDataSource Data source containing data for the bottom subplot bottom_plot_scatter_source : ColumnDataSource Data source containing scatter point data for the bottom subplot right_plot_source : ColumnDataSource Data source containing data for the right subplot right_plot_scatter_source : ColumnDataSource Data source containing scatter point data for the right subplot contour_plot_source : ColumnDataSource Data source containing data for the contour plot input_names : list List of input data titles as strings output_names : list List of output data titles as strings training_inputs : dict Dictionary of input training data x_input_select : Select Bokeh Select object containing a list of inputs for the x axis y_input_select : Select Bokeh Select object containing a list of inputs for the y axis output_select : Select Bokeh Select object containing a list of inputs for the outputs x_input_slider : Slider Bokeh Slider object containing a list of input values for the x axis y_input_slider : Slider Bokeh Slider object containing a list of input values for the y axis slider_dict : dict Dictionary of slider names and their respective slider objects predict_inputs : dict Dictionary containing training data points to predict at. num_inputs : int Number of inputs num_outputs : int Number of outputs limit_range : array Array containing the range of each input scatter_distance : TextInput Text input for user to enter custom value to calculate distance of training points around slice line right_alphas : array Array of points containing alpha values for right plot bottom_alphas : array Array of points containing alpha values for bottom plot dist_range : float Value taken from scatter_distance used for calculating distance of training points around slice line x_index : int Value of x axis column y_index : int Value of y axis column output_variable : int Value of output axis column sliders_and_selects : layout Layout containing the sliders and select elements doc_layout : layout Contains first row of plots doc_layout2 : layout Contains second row of plots Z : array A 2D array containing contour plot data """ def __init__(self, model, resolution=50, doc=None): """ Initialize parameters. Parameters ---------- model : MetaModelComponent Reference to meta model component resolution : int Value used to calculate the size of contour plot meshgrid doc : Document The bokeh document to build. """ self.prob = Problem() self.resolution = resolution logging.getLogger("bokeh").setLevel(logging.ERROR) # If the surrogate model coming in is structured if isinstance(model, MetaModelUnStructuredComp): self.is_structured_meta_model = False # Create list of input names, check if it has more than one input, then create list # of outputs self.input_names = [name[0] for name in model._surrogate_input_names] if len(self.input_names) < 2: raise ValueError('Must have more than one input value') self.output_names = [name[0] for name in model._surrogate_output_names] # Create reference for untructured component self.meta_model = MetaModelUnStructuredComp( default_surrogate=model.options['default_surrogate']) # If the surrogate model coming in is unstructured elif isinstance(model, MetaModelStructuredComp): self.is_structured_meta_model = True self.input_names = [name for name in model._var_rel_names['input']] if len(self.input_names) < 2: raise ValueError('Must have more than one input value') self.output_names = [name for name in model._var_rel_names['output']] self.meta_model = MetaModelStructuredComp( distributed=model.options['distributed'], extrapolate=model.options['extrapolate'], method=model.options['method'], training_data_gradients=model.options['training_data_gradients'], vec_size=1) # Pair input list names with their respective data self.training_inputs = {} self._setup_empty_prob_comp(model) # Setup dropdown menus for x/y inputs and the output value self.x_input_select = Select(title="X Input:", value=[x for x in self.input_names][0], options=[x for x in self.input_names]) self.x_input_select.on_change('value', self._x_input_update) self.y_input_select = Select(title="Y Input:", value=[x for x in self.input_names][1], options=[x for x in self.input_names]) self.y_input_select.on_change('value', self._y_input_update) self.output_select = Select(title="Output:", value=[x for x in self.output_names][0], options=[x for x in self.output_names]) self.output_select.on_change('value', self._output_value_update) # Create sliders for each input self.slider_dict = {} self.predict_inputs = {} for title, values in self.training_inputs.items(): slider_data = np.linspace(min(values), max(values), self.resolution) self.predict_inputs[title] = slider_data # Calculates the distance between slider ticks slider_step = slider_data[1] - slider_data[0] slider_object = Slider(start=min(values), end=max(values), value=min(values), step=slider_step, title=str(title)) self.slider_dict[title] = slider_object self._slider_attrs() # Length of inputs and outputs self.num_inputs = len(self.input_names) self.num_outputs = len(self.output_names) # Precalculate the problem bounds. limits = np.array([[min(value), max(value)] for value in self.training_inputs.values()]) self.limit_range = limits[:, 1] - limits[:, 0] # Positional indicies self.x_index = 0 self.y_index = 1 self.output_variable = self.output_names.index(self.output_select.value) # Data sources are filled with initial values # Slider Column Data Source self.slider_source = ColumnDataSource(data=self.predict_inputs) # Contour plot Column Data Source self.contour_plot_source = ColumnDataSource(data=dict( z=np.random.rand(self.resolution, self.resolution))) self.contour_training_data_source = ColumnDataSource( data=dict(x=np.repeat(0, self.resolution), y=np.repeat(0, self.resolution))) # Bottom plot Column Data Source self.bottom_plot_source = ColumnDataSource(data=dict( x=np.repeat(0, self.resolution), y=np.repeat(0, self.resolution))) self.bottom_plot_scatter_source = ColumnDataSource(data=dict( bot_slice_x=np.repeat(0, self.resolution), bot_slice_y=np.repeat(0, self.resolution))) # Right plot Column Data Source self.right_plot_source = ColumnDataSource(data=dict( x=np.repeat(0, self.resolution), y=np.repeat(0, self.resolution))) self.right_plot_scatter_source = ColumnDataSource(data=dict( right_slice_x=np.repeat(0, self.resolution), right_slice_y=np.repeat(0, self.resolution))) # Text input to change the distance of reach when searching for nearest data points self.scatter_distance = TextInput(value="0.1", title="Scatter Distance") self.scatter_distance.on_change('value', self._scatter_input) self.dist_range = float(self.scatter_distance.value) # Grouping all of the sliders and dropdowns into one column sliders = [value for value in self.slider_dict.values()] sliders.extend( [self.x_input_select, self.y_input_select, self.output_select, self.scatter_distance]) self.sliders_and_selects = row( column(*sliders)) # Layout creation self.doc_layout = row(self._contour_data(), self._right_plot(), self.sliders_and_selects) self.doc_layout2 = row(self._bottom_plot()) if doc is None: doc = curdoc() doc.add_root(self.doc_layout) doc.add_root(self.doc_layout2) doc.title = 'Meta Model Visualization' def _setup_empty_prob_comp(self, metamodel): """ Take data from surrogate ref and pass it into new surrogate model with empty Problem model. Parameters ---------- metamodel : MetaModelComponent Reference to meta model component """ # Check for structured or unstructured if self.is_structured_meta_model: # Loop through the input names for idx, name in enumerate(self.input_names): # Check for no training data try: # Append the input data/titles to a dictionary self.training_inputs[name] = metamodel.inputs[idx] # Also, append the data as an 'add_input' to the model reference self.meta_model.add_input(name, 0., training_data=metamodel.inputs[idx]) except TypeError: msg = "No training data present for one or more parameters" raise TypeError(msg) # Add the outputs to the model reference for idx, name in enumerate(self.output_names): self.meta_model.add_output( name, 0., training_data=metamodel.training_outputs[name]) else: for name in self.input_names: try: self.training_inputs[name] = { title for title in metamodel.options['train:' + str(name)]} self.meta_model.add_input( name, 0., training_data=[ title for title in metamodel.options['train:' + str(name)]]) except TypeError: msg = "No training data present for one or more parameters" raise TypeError(msg) for name in self.output_names: self.meta_model.add_output( name, 0., training_data=[ title for title in metamodel.options['train:' + str(name)]]) # Add the subsystem and setup self.prob.model.add_subsystem('interp', self.meta_model) self.prob.setup() def _slider_attrs(self): """ Assign data to slider objects and callback functions. Parameters ---------- None """ for name, slider_object in self.slider_dict.items(): # Checks if there is a callback previously assigned and then clears it if len(slider_object._callbacks) == 1: slider_object._callbacks.clear() # Check if the name matches the 'x input' title if name == self.x_input_select.value: # Set the object and add an event handler self.x_input_slider = slider_object self.x_input_slider.on_change('value', self._scatter_plots_update) # Check if the name matches the 'y input' title elif name == self.y_input_select.value: # Set the object and add an event handler self.y_input_slider = slider_object self.y_input_slider.on_change('value', self._scatter_plots_update) else: # If it is not an x or y input then just assign it the event handler slider_object.on_change('value', self._update) def _make_predictions(self, data): """ Run the data parameter through the surrogate model which is given in prob. Parameters ---------- data : dict Dictionary containing training points. Returns ------- array np.stack of predicted points. """ # Create dictionary with an empty list outputs = {name: [] for name in self.output_names} # Parse dict into shape [n**2, number of inputs] list inputs = np.empty([self.resolution**2, self.num_inputs]) for idx, values in enumerate(data.values()): inputs[:, idx] = values.flatten() # Check for structured or unstructured if self.is_structured_meta_model: # Assign each row of the data coming in to a tuple. Loop through the tuple, and append # the name of the input and value. for idx, tup in enumerate(inputs): for name, val in zip(data.keys(), tup): self.prob[self.meta_model.name + '.' + name] = val self.prob.run_model() # Append the predicted value(s) for title in self.output_names: outputs[title].append( np.array(self.prob[self.meta_model.name + '.' + title])) else: for idx, tup in enumerate(inputs): for name, val in zip(data.keys(), tup): self.prob[self.meta_model.name + '.' + name] = val self.prob.run_model() for title in self.output_names: outputs[title].append( float(self.prob[self.meta_model.name + '.' + title])) return stack_outputs(outputs) def _contour_data_calcs(self): """ Parse input data into a dictionary to be predicted at. Parameters ---------- None Returns ------- dict Dictionary of training data to be predicted at. """ # Create initial data array of training points resolution = self.resolution x_data = np.zeros((resolution, resolution, self.num_inputs)) self._slider_attrs() # Broadcast the inputs to every row of x_data array x_data[:, :, :] = np.array(self.input_point_list) # Find the x/y input titles and match their index positions for idx, (title, values) in enumerate(self.slider_source.data.items()): if title == self.x_input_select.value: self.xlins_mesh = values x_index_position = idx if title == self.y_input_select.value: self.ylins_mesh = values y_index_position = idx # Make meshgrid from the x/y inputs to be plotted X, Y = np.meshgrid(self.xlins_mesh, self.ylins_mesh) # Move the x/y inputs to their respective positions in x_data x_data[:, :, x_index_position] = X x_data[:, :, y_index_position] = Y pred_dict = {} for idx, title in enumerate(self.slider_source.data): pred_dict.update({title: x_data[:, :, idx]}) return pred_dict def _contour_data(self): """ Create a contour plot. Parameters ---------- None Returns ------- Bokeh Image Plot """ resolution = self.resolution # Output data array initialization y_data = np.zeros((resolution, resolution, self.num_outputs)) self.input_point_list = [point.value for point in self.slider_dict.values()] # Pass the dict to make predictions and then reshape the output to # (resolution, resolution, number of outputs) y_data[:, :, :] = self._make_predictions(self._contour_data_calcs()).reshape( (resolution, resolution, self.num_outputs)) # Use the output variable to pull the correct column of data from the predicted # data (y_data) self.Z = y_data[:, :, self.output_variable] # Reshape it to be 2D self.Z = self.Z.reshape(resolution, resolution) # Update the data source with new data self.contour_plot_source.data = dict(z=[self.Z]) # Min to max of training data self.contour_x_range = xlins = self.xlins_mesh self.contour_y_range = ylins = self.ylins_mesh # Color bar formatting color_mapper = LinearColorMapper( palette="Viridis11", low=np.amin(self.Z), high=np.amax(self.Z)) color_bar = ColorBar(color_mapper=color_mapper, ticker=BasicTicker(), label_standoff=12, location=(0, 0)) # Contour Plot self.contour_plot = contour_plot = figure( match_aspect=False, tooltips=[(self.x_input_select.value, "$x"), (self.y_input_select.value, "$y"), (self.output_select.value, "@z")], tools='') contour_plot.x_range.range_padding = 0 contour_plot.y_range.range_padding = 0 contour_plot.plot_width = 600 contour_plot.plot_height = 500 contour_plot.xaxis.axis_label = self.x_input_select.value contour_plot.yaxis.axis_label = self.y_input_select.value contour_plot.min_border_left = 0 contour_plot.add_layout(color_bar, 'right') contour_plot.x_range = Range1d(min(xlins), max(xlins)) contour_plot.y_range = Range1d(min(ylins), max(ylins)) contour_plot.image(image='z', source=self.contour_plot_source, x=min(xlins), y=min(ylins), dh=(max(ylins) - min(ylins)), dw=(max(xlins) - min(xlins)), palette="Viridis11") # Adding training data points overlay to contour plot if self.is_structured_meta_model: data = self._structured_training_points() else: data = self._unstructured_training_points() if len(data): # Add training data points overlay to contour plot data = np.array(data) if self.is_structured_meta_model: self.contour_training_data_source.data = dict(x=data[:, 0], y=data[:, 1], z=self.meta_model.training_outputs[ self.output_select.value].flatten()) else: self.contour_training_data_source.data = dict(x=data[:, 0], y=data[:, 1], z=self.meta_model._training_output[ self.output_select.value]) training_data_renderer = self.contour_plot.circle( x='x', y='y', source=self.contour_training_data_source, size=5, color='white', alpha=0.50) self.contour_plot.add_tools(HoverTool(renderers=[training_data_renderer], tooltips=[ (self.x_input_select.value + " (train)", '@x'), (self.y_input_select.value + " (train)", '@y'), (self.output_select.value + " (train)", '@z'), ])) return self.contour_plot def _right_plot(self): """ Create the right side subplot to view the projected slice. Parameters ---------- None Returns ------- Bokeh figure """ # List of the current positions of the sliders self.input_point_list = [point.value for point in self.slider_dict.values()] # Find the title of the y input and match it with the data y_idx = self.y_input_select.value y_data = self.predict_inputs[y_idx] # Find the position of the x_input slider x_value = self.x_input_slider.value # Rounds the x_data to match the predict_inputs value subplot_value_index = np.where( np.around(self.predict_inputs[self.x_input_select.value], 5) == np.around(x_value, 5))[0] # Make slice in Z data at the point calculated before and add it to the data source z_data = self.Z[:, subplot_value_index].flatten() x = z_data y = self.slider_source.data[y_idx] # Update the data source with new data self.right_plot_source.data = dict(x=x, y=y) # Create and format figure self.right_plot_fig = right_plot_fig = figure( plot_width=250, plot_height=500, title="{} vs {}".format(y_idx, self.output_select.value), tools="pan") right_plot_fig.xaxis.axis_label = self.output_select.value right_plot_fig.yaxis.axis_label = y_idx right_plot_fig.xaxis.major_label_orientation = math.pi / 9 right_plot_fig.line(x='x', y='y', source=self.right_plot_source) right_plot_fig.x_range.range_padding = 0.1 right_plot_fig.y_range.range_padding = 0.02 # Determine distance and alpha opacity of training points if self.is_structured_meta_model: data = self._structured_training_points(compute_distance=True, source='right') else: data = self._unstructured_training_points(compute_distance=True, source='right') self.right_alphas = 1.0 - data[:, 2] / self.dist_range # Training data scatter plot scatter_renderer = right_plot_fig.scatter(x=data[:, 3], y=data[:, 1], line_color=None, fill_color='#000000', fill_alpha=self.right_alphas.tolist()) right_plot_fig.add_tools(HoverTool(renderers=[scatter_renderer], tooltips=[ (self.output_select.value + " (train)", '@x'), (y_idx + " (train)", '@y'), ])) right_plot_fig.scatter(x=data[:, 3], y=data[:, 1], line_color=None, fill_color='#000000', fill_alpha=self.right_alphas.tolist()) span_width = self.dist_range * (max(y_data) - min(y_data)) # Set the right_plot data source to new values self.right_plot_scatter_source.data = dict( right_slice_x=np.repeat(x_value, self.resolution), right_slice_y=y_data, left_dashed=[i - span_width for i in np.repeat(x_value, self.resolution)], right_dashed=[i + span_width for i in np.repeat(x_value, self.resolution)]) self.contour_plot.line( 'right_slice_x', 'right_slice_y', source=self.right_plot_scatter_source, color='black', line_width=2) self.contour_plot.line( 'left_dashed', 'right_slice_y', line_dash='dashed', source=self.right_plot_scatter_source, color='black', line_width=2) self.contour_plot.line( 'right_dashed', 'right_slice_y', line_dash='dashed', source=self.right_plot_scatter_source, color='black', line_width=2) return self.right_plot_fig def _bottom_plot(self): """ Create the bottom subplot to view the projected slice. Parameters ---------- None Returns ------- Bokeh figure """ # List of the current positions of the sliders self.input_point_list = [point.value for point in self.slider_dict.values()] # Find the title of the x input and match it with the data x_idx = self.x_input_select.value x_data = self.predict_inputs[x_idx] # Find the position of the y_input slider y_value = self.y_input_slider.value # Rounds the y_data to match the predict_inputs value subplot_value_index = np.where( np.around(self.predict_inputs[self.y_input_select.value], 5) == np.around(y_value, 5))[0] # Make slice in Z data at the point calculated before and add it to the data source z_data = self.Z[subplot_value_index, :].flatten() x = self.slider_source.data[x_idx] y = z_data # Update the data source with new data self.bottom_plot_source.data = dict(x=x, y=y) # Create and format figure self.bottom_plot_fig = bottom_plot_fig = figure( plot_width=550, plot_height=250, title="{} vs {}".format(x_idx, self.output_select.value), tools="") bottom_plot_fig.xaxis.axis_label = x_idx bottom_plot_fig.yaxis.axis_label = self.output_select.value bottom_plot_fig.line(x='x', y='y', source=self.bottom_plot_source) bottom_plot_fig.x_range.range_padding = 0.02 bottom_plot_fig.y_range.range_padding = 0.1 # Determine distance and alpha opacity of training points if self.is_structured_meta_model: data = self._structured_training_points(compute_distance=True) else: data = self._unstructured_training_points(compute_distance=True) self.bottom_alphas = 1.0 - data[:, 2] / self.dist_range # Training data scatter plot scatter_renderer = bottom_plot_fig.scatter(x=data[:, 0], y=data[:, 3], line_color=None, fill_color='#000000', fill_alpha=self.bottom_alphas.tolist()) bottom_plot_fig.add_tools(HoverTool(renderers=[scatter_renderer], tooltips=[ (x_idx + " (train)", '@x'), (self.output_select.value + " (train)", '@y'), ])) span_width = self.dist_range * (max(x_data) - min(x_data)) # Set the right_plot data source to new values self.bottom_plot_scatter_source.data = dict( bot_slice_x=x_data, bot_slice_y=np.repeat(y_value, self.resolution), upper_dashed=[i + span_width for i in np.repeat(y_value, self.resolution)], lower_dashed=[i - span_width for i in np.repeat(y_value, self.resolution)]) self.contour_plot.line( 'bot_slice_x', 'bot_slice_y', source=self.bottom_plot_scatter_source, color='black', line_width=2) self.contour_plot.line( 'bot_slice_x', 'upper_dashed', line_dash='dashed', source=self.bottom_plot_scatter_source, color='black', line_width=2) self.contour_plot.line( 'bot_slice_x', 'lower_dashed', line_dash='dashed', source=self.bottom_plot_scatter_source, color='black', line_width=2) return self.bottom_plot_fig def _unstructured_training_points(self, compute_distance=False, source='bottom'): """ Calculate the training points and returns and array containing the position and alpha. Parameters ---------- compute_distance : bool If true, compute the distance of training points from surrogate line. source : str Which subplot the method is being called from. Returns ------- array The array of training points and their alpha opacity with respect to the surrogate line """ # Input training data and output training data x_training = self.meta_model._training_input training_output = np.squeeze(stack_outputs(self.meta_model._training_output), axis=1) # Index of input/output variables x_index = self.x_input_select.options.index(self.x_input_select.value) y_index = self.y_input_select.options.index(self.y_input_select.value) output_variable = self.output_names.index(self.output_select.value) # Vertically stack the x/y inputs and then transpose them infos = np.vstack((x_training[:, x_index], x_training[:, y_index])).transpose() if not compute_distance: return infos points = x_training.copy() # Normalize so each dimension spans [0, 1] points = np.divide(points, self.limit_range) dist_limit = np.linalg.norm(self.dist_range * self.limit_range) scaled_x0 = np.divide(self.input_point_list, self.limit_range) # Query the nearest neighbors tree for the closest points to the scaled x0 array # Nearest points to x slice if x_training.shape[1] < 3: tree = cKDTree(points) # Query the nearest neighbors tree for the closest points to the scaled x0 array dists, idxs = tree.query( scaled_x0, k=len(x_training), distance_upper_bound=self.dist_range) # kdtree query always returns requested k even if there are not enough valid points idx_finite = np.where(np.isfinite(dists)) dists = dists[idx_finite] idxs = idxs[idx_finite] else: dists, idxs = self._multidimension_input(scaled_x0, points, source=source) # data contains: # [x_value, y_value, ND-distance, func_value] data = np.zeros((len(idxs), 4)) for dist_index, j in enumerate(idxs): data[dist_index, 0:2] = infos[j, :] data[dist_index, 2] = dists[dist_index] data[dist_index, 3] = training_output[j, output_variable] return data def _structured_training_points(self, compute_distance=False, source='bottom'): """ Calculate the training points and return an array containing the position and alpha. Parameters ---------- compute_distance : bool If true, compute the distance of training points from surrogate line. source : str Which subplot the method is being called from. Returns ------- array The array of training points and their alpha opacity with respect to the surrogate line """ # Create tuple of the input parameters input_dimensions = tuple(self.meta_model.inputs) # Input training data and output training data x_training = np.array([z for z in product(*input_dimensions)]) training_output = self.meta_model.training_outputs[self.output_select.value].flatten() # Index of input/output variables x_index = self.x_input_select.options.index(self.x_input_select.value) y_index = self.y_input_select.options.index(self.y_input_select.value) # Vertically stack the x/y inputs and then transpose them infos = np.vstack((x_training[:, x_index], x_training[:, y_index])).transpose() if not compute_distance: return infos points = x_training.copy() # Normalize so each dimension spans [0, 1] points = np.divide(points, self.limit_range) self.dist_limit = np.linalg.norm(self.dist_range * self.limit_range) scaled_x0 = np.divide(self.input_point_list, self.limit_range) # Query the nearest neighbors tree for the closest points to the scaled x0 array # Nearest points to x slice if x_training.shape[1] < 3: x_tree, x_idx = self._two_dimension_input(scaled_x0, points, source=source) else: x_tree, x_idx = self._multidimension_input(scaled_x0, points, source=source) # format for 'data' # [x_value, y_value, ND-distance_(x or y), func_value] n = len(x_tree) data = np.zeros((n, 4)) for dist_index, j in enumerate(x_idx): data[dist_index, 0:2] = infos[j, :] data[dist_index, 2] = x_tree[dist_index] data[dist_index, 3] = training_output[j] return data def _two_dimension_input(self, scaled_points, training_points, source='bottom'): """ Calculate the distance of training points to the surrogate line. Parameters ---------- scaled_points : array Array of normalized slider positions. training_points : array Array of input training data. source : str Which subplot the method is being called from. Returns ------- idxs : array Index of closest points that are within the dist range. x_tree : array One dimentional array of points that are within the dist range. """ # Column of the input if source == 'right': col_idx = self.y_input_select.options.index(self.y_input_select.value) else: col_idx = self.x_input_select.options.index(self.x_input_select.value) # Delete the axis of input from source to predicted 1D distance x = np.delete(scaled_points, col_idx, axis=0) x_training_points = np.delete(training_points, col_idx, axis=1).flatten() # Tree of point distances x_tree = np.abs(x - x_training_points) # Only return points that are within our distance-viewing paramter. idx = np.where(x_tree <= self.dist_range) x_tree = x_tree[idx] return x_tree, idx[0] def _multidimension_input(self, scaled_points, training_points, source='bottom'): """ Calculate the distance of training points to the surrogate line. Parameters ---------- scaled_points : array Array of normalized slider positions. training_points : array Array of input training data. source : str Which subplot the method is being called from. Returns ------- idxs : array Index of closest points that are within the dist range. x_tree : array Array of points that are within the dist range. """ # Column of the input if source == 'right': col_idx = self.y_input_select.options.index(self.y_input_select.value) else: col_idx = self.x_input_select.options.index(self.x_input_select.value) # Delete the axis of input from source to predicted distance x = np.delete(scaled_points, col_idx, axis=0) x_training_points = np.delete(training_points, col_idx, axis=1) # Tree of point distances x_tree = cKDTree(x_training_points) # Query the nearest neighbors tree for the closest points to the scaled array dists, idx = x_tree.query(x, k=len(x_training_points), distance_upper_bound=self.dist_range) # kdtree query always returns requested k even if there are not enough valid points idx_finite = np.where(np.isfinite(dists)) dists_finite = dists[idx_finite] idx = idx[idx_finite] return dists_finite, idx # Event handler functions def _update_all_plots(self): self.doc_layout.children[0] = self._contour_data() self.doc_layout.children[1] = self._right_plot() self.doc_layout2.children[0] = self._bottom_plot() def _update_subplots(self): self.doc_layout.children[1] = self._right_plot() self.doc_layout2.children[0] = self._bottom_plot() def _update(self, attr, old, new): self._update_all_plots() def _scatter_plots_update(self, attr, old, new): self._update_subplots() def _scatter_input(self, attr, old, new): # Text input update function of dist range value self.dist_range = float(new) self._update_all_plots() def _x_input_update(self, attr, old, new): # Checks that x and y inputs are not equal to each other if new == self.y_input_select.value: raise ValueError("Inputs should not equal each other") else: self.x_input_select.value = new self._update_all_plots() def _y_input_update(self, attr, old, new): # Checks that x and y inputs are not equal to each other if new == self.x_input_select.value: raise ValueError("Inputs should not equal each other") else: self.y_input_select.value = new self._update_all_plots() def _output_value_update(self, attr, old, new): self.output_variable = self.output_names.index(new) self._update_all_plots()
class ScipyODEIntegrator(object): """ Given a system class, create a callable object with the same signature as that required by scipy.integrate.ode:: f(t, x, *args) Internally, this is accomplished by constructing an OpenMDAO problem using the ODE with a single node. The interface populates the values of the time, states, and controls, and then calls `run_model()` on the problem. The state rates generated by the ODE are then returned back to scipy ode, which continues the integration. Parameters ---------- phase_name : str The name of the phase being simulated. ode_class : class The ODEClass belonging to the phase being simulated. time_options : dict of {str: TimeOptionsDictionary} The time options for the phase being simulated. state_options : dict of {str: StateOptionsDictionary} The state options for the phase being simulated. control_options : dict of {str: ControlOptionsDictionary} The control options for the phase being simulated. design_parameter_options : dict of {str: DesignParameterOptionsDictionary} The design parameter options for the phase being simulated. input_parameter_options : dict of {str: InputParameterOptionsDictionary} The input parameter options for the phase being simulated. ode_init_kwargs : dict Keyword argument dictionary passed to the ODE at initialization. """ def __init__(self, phase_name, ode_class, time_options, state_options, control_options, design_parameter_options, input_parameter_options, ode_init_kwargs=None): self.phase_name = phase_name self.prob = Problem(model=Group()) self.ode = ode # The ODE System self.prob.model.add_subsystem('ode', subsys=ode_class(num_nodes=1, **ode_init_kwargs)) self.ode_options = ode_class.ode_options # Get the state vector. This isn't necessarily ordered # so just pick the default ordering and go with it. self.state_options = OrderedDict() self.time_options = time_options self.control_options = control_options self.design_parameter_options = design_parameter_options self.input_parameter_options = input_parameter_options pos = 0 for state, options in iteritems(state_options): self.state_options[state] = { 'rate_source': options['rate_source'], 'pos': pos, 'shape': options['shape'], 'size': np.prod(options['shape']), 'units': options['units'], 'targets': options['targets'] } pos += self.state_options[state]['size'] self._state_vec = np.zeros(pos, dtype=float) self._state_rate_vec = np.zeros(pos, dtype=float) # The Time Comp self.prob.model.add_subsystem( 'time_input', IndepVarComp('time', val=0.0, units=self.ode_options._time_options['units']), promotes_outputs=['time']) if self.ode_options._time_options['targets'] is not None: self.prob.model.connect('time', [ 'ode.{0}'.format(tgt) for tgt in self.ode_options._time_options['targets'] ]) # The States Comp indep = IndepVarComp() for name, options in iteritems(self.state_options): indep.add_output('states:{0}'.format(name), shape=(1, np.prod(options['shape'])), units=options['units']) if options['targets'] is not None: self.prob.model.connect( 'states:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in options['targets']]) self.prob.model.connect( 'ode.{0}'.format(options['rate_source']), 'state_rate_collector.state_rates_in:{0}_rate'.format(name)) self.prob.model.add_subsystem('indep_states', subsys=indep, promotes_outputs=['*']) # The Control interpolation comp time_units = self.ode_options._time_options['units'] self._interp_comp = ControlInterpolationComp( time_units=time_units, control_options=control_options) # The state rate collector comp self.prob.model.add_subsystem( 'state_rate_collector', StateRateCollectorComp(state_options=self.state_options, time_units=time_options['units'])) # Flag that is set to true if has_controls is called self._has_dynamic_controls = False def setup(self, check=False): """ Call setup on the ScipyODEIntegrator's problem instance. Parameters ---------- check : bool If True, run setup on the problem instance with checks enabled. Default is False. """ model = self.prob.model order = ['time_input', 'indep_states'] if self.control_options: model.add_subsystem('indep_controls', self._interp_comp, promotes_outputs=['*']) order += ['indep_controls'] model.connect('time', ['indep_controls.time']) for name, options in iteritems(self.control_options): if name in self.ode_options._parameters: targets = self.ode_options._parameters[name]['targets'] model.connect('controls:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in targets]) if options['rate_param']: rate_param = options['rate_param'] rate_targets = self.ode_options._parameters[rate_param][ 'targets'] model.connect( 'control_rates:{0}_rate'.format(name), ['ode.{0}'.format(tgt) for tgt in rate_targets]) if options['rate2_param']: rate2_param = options['rate2_param'] rate2_targets = self.ode_options._parameters[rate2_param][ 'targets'] model.connect( 'control_rates:{0}_rate2'.format(name), ['ode.{0}'.format(tgt) for tgt in rate2_targets]) if self.design_parameter_options: ivc = model.add_subsystem('design_params', IndepVarComp(), promotes_outputs=['*']) order += ['design_params'] for name, options in iteritems(self.design_parameter_options): ivc.add_output('design_parameters:{0}'.format(name), val=np.zeros(options['shape']), units=options['units']) if name in self.ode_options._parameters: targets = self.ode_options._parameters[name]['targets'] model.connect('design_parameters:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in targets]) if self.input_parameter_options: ivc = model.add_subsystem('input_params', IndepVarComp(), promotes_outputs=['*']) order += ['input_params'] for name, options in iteritems(self.input_parameter_options): ivc.add_output('input_parameters:{0}'.format(name), val=np.zeros(options['shape']), units=options['units']) if options['target_param'] in self.ode_options._parameters: targets = self.ode_options._parameters[ options['target_param']]['targets'] model.connect('input_parameters:{0}'.format(name), ['ode.{0}'.format(tgt) for tgt in targets]) order += ['ode', 'state_rate_collector'] model.set_order(order) self.prob.setup(check=check) def set_interpolant(self, name, interpolant): """ Set the interpolator to be used for the control of the given name. Parameters ---------- name : str The name of the control whose interpolant is being specified. interpolant : object An object that provides interpolation for the control as a function of time. The object must have methods `eval(t)` which returns the interpolated value of the control at time t, and `eval_deriv(t)` which returns the interpolated value of the first time-derivative of the control at time t. """ self._interp_comp.interpolants[name] = interpolant def set_design_param_value(self, name, val, units=None): """ Sets the values of design parameters in the problem, once self.prob has been setup. Parameters ---------- name : str The name of the design parameter whose value is being set val : float or ndarray The value of the design parameter units : str or None The units in which the design parameter value is set. """ self.prob.set_val('design_parameters:{0}'.format(name), val, units) def set_input_param_value(self, name, val, units=None): """ Sets the values of input parameters in the problem, once self.prob has been setup. Parameters ---------- name : str The name of the design parameter whose value is being set val : float or ndarray The value of the design parameter units : str or None The units in which the design parameter value is set. """ self.prob.set_val('input_parameters:{0}'.format(name), val, units) def _unpack_state_vec(self, x): """ Given the state vector in 1D, extract the values corresponding to each state into the ode integrators problem states. Parameters ---------- x : np.array The 1D state vector. Returns ------- None """ for state_name, state_options in self.state_options.items(): pos = state_options['pos'] size = state_options['size'] self.prob['states:{0}'.format(state_name)][0, ...] = x[pos:pos + size] def _pack_state_rate_vec(self): """ Pack the state rates into a 1D vector for use by scipy odeint. Returns ------- dXdt: np.array The 1D state-rate vector. """ for state_name, state_options in self.state_options.items(): pos = state_options['pos'] size = state_options['size'] self._state_rate_vec[pos:pos + size] = \ np.ravel(self.prob['state_rate_collector.' 'state_rates:{0}_rate'.format(state_name)]) return self._state_rate_vec def _pack_state_vec(self, x_dict): """ Pack the state into a 1D vector for use by scipy.integrate.ode. Returns ------- x: np.array The 1D state vector. """ self._state_vec[:] = 0.0 for state_name, state_options in self.state_options.items(): pos = state_options['pos'] size = state_options['size'] self._state_vec[pos:pos + size] = np.ravel(x_dict[state_name]) return self._state_vec def _f_ode(self, t, x, *args): """ The function interface used by scipy.ode Parameters ---------- t : float The current time, t. x : np.array The 1D state vector. Returns ------- xdot : np.array The 1D vector of state time-derivatives. """ self.prob['time'] = t self._unpack_state_vec(x) self.prob.run_model() xdot = self._pack_state_rate_vec() return xdot def _store_results(self, results, append=False): """ Save the outputs of the integrators problem object into the given PhaseSimulationResults instance. Parameters ---------- results : PhaseSimulationResults The PhaseSimulationResults object into which results of the integration are to be saved. """ model_outputs = self.prob.model.list_outputs(units=True, shape=True, values=True, implicit=True, explicit=True, out_stream=None) for output_name, options in model_outputs: prom_name = self.prob.model._var_abs2prom['output'][output_name] if prom_name.startswith('time'): var_type = 'indep' name = 'time' shape = (1, ) elif prom_name.startswith('states:'): var_type = 'states' name = prom_name.replace('states:', '', 1) shape = self.state_options[name]['shape'] elif prom_name.startswith('controls:'): var_type = 'controls' name = prom_name.replace('controls:', '', 1) shape = self.control_options[name]['shape'] elif prom_name.startswith('control_rates:'): var_type = 'control_rates' name = prom_name.replace('control_rates:', '', 1) if name.endswith('_rate'): control_name = name[:-5] if name.endswith('_rate2'): control_name = name[:-6] shape = self.control_options[control_name]['shape'] elif prom_name.startswith('design_parameters:'): var_type = 'design_parameters' name = prom_name.replace('design_parameters:', '', 1) shape = self.design_parameter_options[name]['shape'] elif prom_name.startswith('input_parameters:'): var_type = 'input_parameters' name = prom_name.replace('input_parameters:', '', 1) shape = self.input_parameter_options[name]['shape'] elif prom_name.startswith('ode.'): var_type = 'ode' name = prom_name.replace('ode.', '', 1) shape = options['shape'] if len( options['shape']) == 1 else options['shape'][1:] elif prom_name.startswith('state_rate_collector.'): # skip this variable since this is just a simulation artifact continue else: raise RuntimeWarning('Unexpected output encountered during' 'simulation: {0}'.format(prom_name)) continue if append: results.outputs[var_type][name]['value'] = \ np.concatenate((results.outputs[var_type][name]['value'], np.atleast_2d(options['value'])), axis=0) else: results.outputs[var_type][name] = {} results.outputs[var_type][name]['value'] = np.atleast_2d( options['value']).copy() results.outputs[var_type][name]['units'] = options['units'] results.outputs[var_type][name]['shape'] = shape def integrate_times(self, x0_dict, times, integrator='vode', integrator_params=None, observer=None): """ Integrate the RHS with the given initial state, and record the values at the specified times. Parameters ---------- x0_dict : dict A dictionary keyed by state variable name that contains the initial values for the states. If absent the initial value is assumed to be zero. times : sequence The sequence of times at which output for the integration is desired. integrator : str The integrator to be used by scipy.ode. This is one of: vode, zvode, lsoda, dopri5, or dopri853. integrator_params : dict, None Parameters specific to the chosen integrator. See the Scipy documentation for details. observer : callable, str, None A callable function to be called at the specified timesteps in `integrate_times`. This can be used to record the integrated trajectory. If 'default', a StdOutObserver will be used, which outputs all variables in the model to standard output by default. If None, no observer will be called. Returns ------- PhaseSimulationResults A dictionary of variables in the RHS and their values at the given times. """ # Prepare the observer if observer == 'stdout': _observer = StdOutObserver(self) elif observer == 'progress-bar': _observer = ProgressBarObserver(self, out_stream=sys.stdout, t0=times[0], tf=times[-1]) else: _observer = observer int_params = integrator_params if integrator_params else {} solver = ode(self._f_ode) x0 = self._pack_state_vec(x0_dict) solver.set_integrator(integrator, **int_params) solver.set_initial_value(x0, times[0]) delta_times = np.diff(times) # Run the Model once to get the initial values of all variables self._f_ode(solver.t, solver.y) # Prepare the output dictionary results = \ PhaseSimulationResults(time_options=self.time_options, state_options=self.state_options, control_options=self.control_options, design_parameter_options=self.design_parameter_options, input_parameter_options=self.input_parameter_options) self._store_results(results) if _observer: _observer(solver.t, solver.y, self.prob) terminate = False for dt in delta_times: try: solver.integrate(solver.t + dt) self._f_ode(solver.t, solver.y) except AnalysisError: terminate = True self._store_results(results, append=True) if _observer: _observer(solver.t, solver.y, self.prob) if terminate: break return results
def _run_nl_ln_drv(self, ndv, nstate, nproc, flag): """ Benchmark a single point. Nonlinear solve is always run. Linear Solve and Driver are optional. Parameters ---------- ndv : int Number of design variables requested. nstate : int Number of states requested. nproc : int Number of processors requested. flag : bool User assignable flag that will be False or True. """ prob = Problem() # User hook pre setup self.setup(prob, ndv, nstate, nproc, flag) prob.setup(mode=self.mode) # User hook post setup self.post_setup(prob, ndv, nstate, nproc, flag) prob.final_setup() # Time Execution t0 = time() prob.run_model() t1 = time() - t0 print("Nonlinear Execution complete:", t1, 'sec') if self.time_driver: t4 = time() prob.run_driver() t5 = time() - t4 print("Driver Execution complete:", t5, 'sec') else: t5 = 0.0 if self.time_linear: if self.sub_timing: prob.model.linear_solver.time_lu_fact = 0 prob.model.linear_solver.time_lu_solve = 0 t2 = time() prob.compute_totals(of=self.ln_of, wrt=self.ln_wrt, return_format='dict') t3 = time() - t2 print("Linear Execution complete:", t3, 'sec') if self.sub_timing: t3a = prob.model.linear_solver.time_lu_fact t3b = prob.model.linear_solver.time_lu_solve t3c = prob.total_jac.time_linearize_sys t3d = prob.total_jac.time_lienarize_solver t3e = prob.total_jac.time_solve else: t3 = 0.0 self.post_run(prob, ndv, nstate, nproc, flag) if self.sub_timing and self.time_linear: return t1, t3, t5, t3a, t3b, t3c, t3d, t3e else: return t1, t3, t5