def test_create_sub_meta(self): name = 'Testing' meta = create_local_meta(self.meta, name) self.assertEqual(meta['name'], name) self.assertEqual(meta['coord'], ['', (0,), name, (0,)])
def solve(self, params, unknowns, resids, system, metadata=None): """ Executes each item in the system hierarchy sequentially. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. """ self.iter_count += 1 # Metadata setup local_meta = create_local_meta(metadata, system.name) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count,)) system.children_solve_nonlinear(local_meta) for recorder in self.recorders: recorder.raw_record(system.params, system.unknowns, system.resids, local_meta)
def solve(self, params, unknowns, resids, system, metadata=None): """ Executes each item in the system hierarchy sequentially. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. """ self.iter_count += 1 # Metadata setup local_meta = create_local_meta(metadata, system.name) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count, )) system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta)
def run(self, problem): """Build a runlist and execute the Problem for each set of generated parameters. """ self.iter_count = 0 if MPI and self._num_par_doe > 1: if self._load_balance: runlist = self._distrib_lb_build_runlist() if self._full_comm.rank == 0: try: next(runlist) except StopIteration: pass return # we're done sending cases else: runlist = self._distrib_build_runlist() else: runlist = self._build_runlist() # For each runlist entry, run the system and record the results for run in runlist: for dv_name, dv_val in run: self.set_desvar(dv_name, dv_val) metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (self.iter_count, )) problem.root.solve_nonlinear(metadata=metadata) self.recorders.record_iteration(problem.root, metadata) self.iter_count += 1
def solve(self, params, unknowns, resids, system, metadata=None): self.sys = system self.local_meta = create_local_meta(metadata, system.pathname) if self.var_lower_bound is not None: lower = params[self.var_lower_bound] else: lower = self.options['lower_bound'] if self.var_upper_bound is not None: upper = params[self.var_upper_bound] else: upper = self.options['upper_bound'] kwargs = {'maxiter': self.options['max_iter'], 'a': lower, 'b': upper, 'full_output': True, 'args': (params, unknowns, resids)} if self.options['xtol']: kwargs['xtol'] = self.options['xtol'] if self.options['rtol'] > 0: kwargs['rtol'] = self.options['rtol'] # Brent's method xstar, r = brentq(self._eval, **kwargs) brent_iterations = r.iterations
def run(self, problem): """Build a runlist and execute the Problem for each set of generated parameters. """ self.iter_count = 0 if MPI and self._num_par_doe > 1: if self._load_balance: runlist = self._distrib_lb_build_runlist() if self._full_comm.rank == 0: try: next(runlist) except StopIteration: pass return # we're done sending cases else: runlist = self._distrib_build_runlist() else: runlist = self._build_runlist() # For each runlist entry, run the system and record the results for run in runlist: for dv_name, dv_val in run: self.set_desvar(dv_name, dv_val) metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (self.iter_count,)) problem.root.solve_nonlinear(metadata=metadata) self.recorders.record_iteration(problem.root, metadata) self.iter_count += 1
def _prep_case(self, case, iter_count): """Create metadata for the case and set design variables. """ metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (iter_count,)) for dv_name, dv_val in case: self.set_desvar(dv_name, dv_val) return metadata
def _prep_case(self, case): """Create metadata for the case and set design variables. """ metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (self.iter_count, )) for dv_name, dv_val in case: self.set_desvar(dv_name, dv_val) return metadata
def init_design(self, store_here): """ KONA METHOD: Initialize the first design point. Store the design vector at ``store_here``. The optimization will start from this point. """ self._pull_primal_nl(store_here) self.metadata = create_local_meta(None, 'Kona')
def solve(self, params, unknowns, resids, system, metadata=None): self.sys = system self.metadata = metadata self.local_meta = create_local_meta(self.metadata, self.sys.pathname) self.sys.ln_solver.local_meta = self.local_meta # update_local_meta(self.local_meta, (self.iter_count, 0)) if self.var_lower_bound is not None: lower = params[self.var_lower_bound] else: lower = self.options['lower_bound'] if self.var_upper_bound is not None: upper = params[self.var_upper_bound] else: upper = self.options['upper_bound'] kwargs = { 'maxiter': self.options['max_iter'], 'a': lower, 'b': upper, 'full_output': True, 'args': (params, unknowns, resids) } if self.options['xtol']: kwargs['xtol'] = self.options['xtol'] if self.options['rtol'] > 0: kwargs['rtol'] = self.options['rtol'] # Brent's method self.iter_count = 0 # initial run to compute initial_norm self.sys.children_solve_nonlinear(self.local_meta) self.sys.apply_nonlinear(params, unknowns, resids) resid_norm_0 = resids[self.s_var_name] xstar, r = brentq(self._eval, **kwargs) resid_norm = resids[self.s_var_name] if self.options['iprint'] > 0: if self.iter_count == self.options['max_iter'] or isnan( resid_norm): msg = 'FAILED to converge after max iterations' else: msg = 'converged' self.print_norm(self.print_name, system.pathname, self.iter_count, resid_norm, resid_norm_0, msg=msg)
def test_format_coord(self): name = 'Sub' coord = (1, 2, 3) meta = create_local_meta(self.meta, name) update_local_meta(meta, coord) s = format_iteration_coordinate(meta['coord']) self.assertEqual(s, '/0/Sub/1-2-3')
def run(self, problem): self.problem = problem optns = self.options['algorithm_options'] algorithm = self.options['algorithm'] self.iter_count = 0 self.metadata = create_local_meta(None, 'Kona') update_local_meta(self.metadata, (self.iter_count,)) optimizer = kona.Optimizer(self, algorithm, optns) optimizer.solve()
def run(self, problem): """Build a runlist and execute the Problem for each set of generated parameters. """ run_list = self._build_runlist() # For each runlist entry, run the system and record the results for run in run_list: for dv_name, dv_val in iteritems(run): self.set_desvar(dv_name, dv_val) metadata = create_local_meta(None, 'Driver') problem.root.solve_nonlinear(metadata=metadata) self.recorders.record_iteration(problem.root, metadata)
def solve(self, params, unknowns, resids, system, metadata=None): self.sys = system self.metadata = metadata self.local_meta = create_local_meta(self.metadata, self.sys.pathname) self.sys.ln_solver.local_meta = self.local_meta # update_local_meta(self.local_meta, (self.iter_count, 0)) if self.var_lower_bound is not None: lower = params[self.var_lower_bound] else: lower = self.options['lower_bound'] if self.var_upper_bound is not None: upper = params[self.var_upper_bound] else: upper = self.options['upper_bound'] kwargs = {'maxiter': self.options['max_iter'], 'a': lower, 'b': upper, 'full_output': True, 'args': (params, unknowns, resids)} if self.options['xtol']: kwargs['xtol'] = self.options['xtol'] if self.options['rtol'] > 0: kwargs['rtol'] = self.options['rtol'] # Brent's method self.iter_count = 0 # initial run to compute initial_norm self.sys.children_solve_nonlinear(self.local_meta) self.sys.apply_nonlinear(params, unknowns, resids) resid_norm_0 = resids[self.s_var_name] xstar, r = brentq(self._eval, **kwargs) resid_norm = resids[self.s_var_name] if self.options['iprint'] > 0: if self.iter_count == self.options['max_iter'] or isnan(resid_norm): msg = 'FAILED to converge after max iterations' else: msg = 'converged' self.print_norm(self.print_name, system.pathname, self.iter_count, resid_norm, resid_norm_0, msg=msg)
def run_one(self, problem, run): for dv_name, dv_val in run: self.set_desvar(dv_name, dv_val) metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (self.iter_count,)) try: problem.root.solve_nonlinear(metadata=metadata) except AnalysisError: metadata['msg'] = traceback.format_exc() metadata['success'] = 0 self.recorders.record_iteration(problem.root, metadata) self.iter_count += 1 if self.use_restart: self.restart.record_iteration()
def run_one(self, problem, run): for dv_name, dv_val in run: self.set_desvar(dv_name, dv_val) metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (self.iter_count, )) try: problem.root.solve_nonlinear(metadata=metadata) except AnalysisError: metadata['msg'] = traceback.format_exc() metadata['success'] = 0 self.recorders.record_iteration(problem.root, metadata) self.iter_count += 1 if self.use_restart: self.restart.record_iteration()
def run(self, problem): """ Runs the driver. This function should be overriden when inheriting. Args ---- problem : `Problem` Our parent `Problem`. """ system = problem.root # Metadata Setup self.iter_count += 1 metadata = create_local_meta(None, 'Driver') system.ln_solver.local_meta = metadata update_local_meta(metadata, (self.iter_count,)) # Solve the system once and record results. system.solve_nonlinear(metadata=metadata) self.recorders.record_iteration(system, metadata)
def run(self, problem): """ Runs the driver. This function should be overriden when inheriting. Args ---- problem : `Problem` Our parent `Problem`. """ system = problem.root # Metadata Setup self.iter_count += 1 metadata = create_local_meta(None, "Driver") system.ln_solver.local_meta = metadata update_local_meta(metadata, (self.iter_count,)) # Solve the system once and record results. system.solve_nonlinear(metadata=metadata) for recorder in self.recorders: recorder.raw_record(system.params, system.unknowns, system.resids, metadata)
def run_once(self, problem): """ Runs root's solve_nonlinear one time Args ---- problem : `Problem` Our parent `Problem`. """ system = problem.root # Metadata Setup self.iter_count += 1 metadata = self.metadata = create_local_meta(None, 'Driver') system.ln_solver.local_meta = metadata update_local_meta(metadata, (self.iter_count,)) # Solve the system once and record results. with system._dircontext: system.solve_nonlinear(metadata=metadata) self.recorders.record_iteration(system, metadata)
def run(self, problem): """ Save away scaled info.""" self._problem = problem self.metadata = create_local_meta(None, 'test') self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count,)) params = self.get_desvars() param_meta = self.get_desvar_metadata() self.set_desvar('x', np.array([22.0, 404.0, 9009.0, 121000.0])) problem.root.solve_nonlinear() objective = self.get_objectives() constraint = self.get_constraints() # Stuff we saved should be in the scaled coordinates. self.param = params['x'] self.obj_scaled = objective['y'] self.con_scaled = constraint['con'] self.param_low = param_meta['x']['lower']
def run(self, problem): """Build a runlist and execute the Problem for each set of generated parameters. """ self.iter_count = 0 if MPI and self._num_par_doe > 1: runlist = self._distrib_build_runlist() else: runlist = self._build_runlist() # For each runlist entry, run the system and record the results for run in runlist: for dv_name, dv_val in run: self.set_desvar(dv_name, dv_val) metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (self.iter_count,)) problem.root.solve_nonlinear(metadata=metadata) self.recorders.record_iteration(problem.root, metadata) self.iter_count += 1
def run(self, problem): """Build a runlist and execute the Problem for each set of generated parameters. """ self.iter_count = 0 if MPI and self._num_par_doe > 1: if self._load_balance: runlist = self._distrib_lb_build_runlist() else: runlist = self._get_case_w_nones(self._distrib_build_runlist()) else: runlist = self._build_runlist() with problem.root._dircontext: # For each runlist entry, run the system and record the results for case in runlist: if MPI and self._load_balance and self._full_comm.rank == 0: # we're the master rank and case is a completed case self.recorders.record_case(problem.root, case) elif case is not None: # dummy cases have case == None metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (self.iter_count,)) for dv_name, dv_val in case: self.set_desvar(dv_name, dv_val) problem.root.solve_nonlinear(metadata=metadata) if self._load_balance: # keep meta for worker to send to master self._last_meta = metadata if not MPI or not self._load_balance: self.recorders.record_iteration(problem.root, metadata, dummy=(case is None)) self.iter_count += 1
def run(self, problem): """ Save away scaled info.""" self._problem = problem self.metadata = create_local_meta(None, "test") self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count,)) params = self.get_desvars() param_meta = self.get_desvar_metadata() self.set_desvar("x", 0.5) problem.root.solve_nonlinear() objective = self.get_objectives() constraint = self.get_constraints() # Stuff we saved should be in the scaled coordinates. self.param = params["x"] self.obj_scaled = objective["f_xy"] self.con_scaled = constraint["con"] self.param_high = param_meta["x"]["upper"] self.param_low = param_meta["x"]["lower"]
def setUp(self): self.meta = create_local_meta(None, '')
def run(self, problem): """Optimize the problem using your choice of Scipy optimizer. Args ---- problem : `Problem` Our parent `Problem`. """ # Metadata Setup opt = self.options['optimizer'] self.metadata = create_local_meta(None, opt) self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count, )) # Initial Run with problem.root._dircontext: problem.root.solve_nonlinear(metadata=self.metadata) pmeta = self.get_desvar_metadata() self.params = list(pmeta) self.objs = list(self.get_objectives()) con_meta = self.get_constraint_metadata() self.cons = list(con_meta) self.con_cache = self.get_constraints() self.opt_settings['maxiter'] = self.options['maxiter'] self.opt_settings['disp'] = self.options['disp'] # Size Problem nparam = 0 for param in itervalues(pmeta): nparam += param['size'] x_init = np.empty(nparam) # Initial Parameters i = 0 use_bounds = (opt in _bounds_optimizers) if use_bounds: bounds = [] else: bounds = None for name, val in iteritems(self.get_desvars()): size = pmeta[name]['size'] x_init[i:i + size] = val i += size # Bounds if our optimizer supports them if use_bounds: meta_low = pmeta[name]['lower'] meta_high = pmeta[name]['upper'] for j in range(0, size): if isinstance(meta_low, np.ndarray): p_low = meta_low[j] else: p_low = meta_low if isinstance(meta_high, np.ndarray): p_high = meta_high[j] else: p_high = meta_high bounds.append((p_low, p_high)) # Constraints constraints = [] i = 0 if opt in _constraint_optimizers: for name, meta in con_meta.items(): size = meta['size'] dblcon = meta['upper'] is not None and meta['lower'] is not None for j in range(0, size): con_dict = OrderedDict() if meta['equals'] is not None: con_dict['type'] = 'eq' else: con_dict['type'] = 'ineq' con_dict['fun'] = self._confunc if opt in _constraint_grad_optimizers: con_dict['jac'] = self._congradfunc con_dict['args'] = [name, j] constraints.append(con_dict) self.con_idx[name] = i i += size # Add extra constraint if double-sided if dblcon: name = '2bl-' + name for j in range(0, size): con_dict = OrderedDict() con_dict['type'] = 'ineq' con_dict['fun'] = self._confunc if opt in _constraint_grad_optimizers: con_dict['jac'] = self._congradfunc con_dict['args'] = [name, j] constraints.append(con_dict) # Provide gradients for optimizers that support it if opt in _gradient_optimizers: jac = self._gradfunc else: jac = None # optimize self._problem = problem result = minimize( self._objfunc, x_init, #args=(), method=opt, jac=jac, #hess=None, #hessp=None, bounds=bounds, constraints=constraints, tol=self.options['tol'], #callback=None, options=self.opt_settings) self._problem = None self.result = result self.exit_flag = 1 if self.result.success else 0 if self.options['disp']: print('Optimization Complete') print('-' * 35)
def solve(self, params, unknowns, resids, system, solver, alpha, fnorm, fnorm0, metadata=None): """ Take the gradient calculated by the parent solver and figure out how far to go. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). solver : `Solver` Parent solver instance. alpha : float Initial over-relaxation factor as used in parent solver. fnorm : float Initial norm of the residual for absolute tolerance check. fnorm0 : float Initial norm of the residual for relative tolerance check. """ atol = self.options['atol'] rtol = self.options['rtol'] maxiter = self.options['maxiter'] result = system.dumat[None] local_meta = create_local_meta(metadata, system.pathname) itercount = 0 ls_alpha = alpha # Backtracking Line Search while itercount < maxiter and \ fnorm > atol and \ fnorm/fnorm0 > rtol: ls_alpha *= 0.5 unknowns.vec -= ls_alpha*result.vec itercount += 1 # Metadata update update_local_meta(local_meta, (solver.iter_count, itercount)) # Just evaluate the model with the new points if self.options['solve_subsystems'] is True: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) solver.recorders.record_iteration(system, local_meta) fnorm = resids.norm() if self.options['iprint'] > 0: self.print_norm('BK_TKG', system.pathname, itercount, fnorm, fnorm0, indent=1, solver='LS')
def test_root_name(self): meta1 = create_local_meta(None, 'Driver') meta2 = create_local_meta(meta1, '') self.assertEqual(meta2['coord'], ['Driver', (0,), 'root', (0,)])
def run(self, problem): """Optimize the problem using your choice of Scipy optimizer. Args ---- problem : `Problem` Our parent `Problem`. """ # Metadata Setup opt = self.options['optimizer'] self.metadata = create_local_meta(None, opt) self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count, )) # Initial Run problem.root.solve_nonlinear(metadata=self.metadata) pmeta = self.get_param_metadata() self.params = list(iterkeys(pmeta)) self.objs = list(iterkeys(self.get_objectives())) con_meta = self.get_constraint_metadata() self.cons = list(iterkeys(con_meta)) self.opt_settings['maxiter'] = self.options['maxiter'] self.opt_settings['disp'] = self.options['disp'] # Size Problem nparam = 0 for param in itervalues(pmeta): nparam += param['size'] x_init = np.zeros(nparam) # Initial Parameters i = 0 use_bounds = (opt in _bounds_optimizers) if use_bounds: bounds = [] else: bounds = None for name, val in iteritems(self.get_params()): size = pmeta[name]['size'] x_init[i:i + size] = val i += size # Bounds if our optimizer supports them if use_bounds: meta_low = pmeta[name]['low'] meta_high = pmeta[name]['high'] for j in range(0, size): if isinstance(meta_low, np.ndarray): p_low = meta_low[j] else: p_low = meta_low if isinstance(meta_high, np.ndarray): p_high = meta_high[j] else: p_high = meta_high bounds.append((p_low, p_high)) # Constraints constraints = [] i = 0 if opt in _constraint_optimizers: for name, meta in con_meta.items(): size = meta['size'] for j in range(0, size): con_dict = {} con_dict['type'] = meta['ctype'] con_dict['fun'] = self.confunc if opt in _constraint_grad_optimizers: con_dict['jac'] = self.congradfunc con_dict['args'] = [name, j] constraints.append(con_dict) self.con_idx[name] = i i += size # Provide gradients for optimizers that support it if opt in _gradient_optimizers: jac = self.gradfunc else: jac = None # optimize self._problem = problem result = minimize( self.objfunc, x_init, #args=(), method=opt, jac=jac, #hess=None, #hessp=None, bounds=bounds, constraints=constraints, tol=self.options['tol'], #callback=None, options=self.opt_settings) self._problem = None self.result = result print('Optimization Complete') print('-' * 35)
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using a Netwon's Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] maxiter = self.options['maxiter'] alpha = self.options['alpha'] # Metadata setup self.iter_count = 0 local_meta = create_local_meta(metadata, system.pathname) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count, 0)) # Perform an initial run to propagate srcs to targets. system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids) f_norm = resids.norm() f_norm0 = f_norm if self.options['iprint'] > 0: self.print_norm(self.print_name, system.pathname, 0, f_norm, f_norm0) arg = system.drmat[None] result = system.dumat[None] while self.iter_count < maxiter and f_norm > atol and \ f_norm/f_norm0 > rtol: # Linearize Model with partial derivatives system._sys_linearize(params, unknowns, resids, total_derivs=False) # Calculate direction to take step arg.vec[:] = resids.vec system.solve_linear(system.dumat, system.drmat, [None], mode='fwd') # Step in that direction, self.iter_count += 1 f_norm = self.line_search.solve(params, unknowns, resids, system, self, alpha, f_norm0, metadata) # Need to make sure the whole workflow is executed at the final # point, not just evaluated. #self.iter_count += 1 #update_local_meta(local_meta, (self.iter_count, 0)) #system.children_solve_nonlinear(local_meta) if self.options['iprint'] > 0: if self.iter_count == maxiter or isnan(f_norm): msg = 'FAILED to converge after max iterations' else: msg = 'converged' self.print_norm(self.print_name, system.pathname, self.iter_count, f_norm, f_norm0, msg=msg)
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using a Netwon's Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] utol = self.options['utol'] maxiter = self.options['maxiter'] alpha_scalar = self.options['alpha'] iprint = self.options['iprint'] ls = self.line_search unknowns_cache = self.unknowns_cache # Metadata setup self.iter_count = 0 local_meta = create_local_meta(metadata, system.pathname) if self.ln_solver: self.ln_solver.local_meta = local_meta else: system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count, 0)) # Perform an initial run to propagate srcs to targets. system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids) if ls: base_u = np.zeros(unknowns.vec.shape) f_norm = resids.norm() f_norm0 = f_norm if iprint == 2: self.print_norm(self.print_name, system, 0, f_norm, f_norm0) arg = system.drmat[None] result = system.dumat[None] u_norm = 1.0e99 # Can't have the system trying to FD itself when it also contains Newton. save_type = system.deriv_options['type'] system.deriv_options.locked = False system.deriv_options['type'] = 'user' while self.iter_count < maxiter and f_norm > atol and \ f_norm/f_norm0 > rtol and u_norm > utol: # Linearize Model with partial derivatives system._sys_linearize(params, unknowns, resids, total_derivs=False) # Calculate direction to take step arg.vec[:] = -resids.vec with system._dircontext: system.solve_linear(system.dumat, system.drmat, [None], mode='fwd', solver=self.ln_solver) self.iter_count += 1 # Allow different alphas for each value so we can keep moving when we # hit a bound. alpha = alpha_scalar * np.ones(len(unknowns.vec)) # If our step will violate any upper or lower bounds, then reduce # alpha in just that direction so that we only step to that # boundary. alpha = unknowns.distance_along_vector_to_limit(alpha, result) # Cache the current norm if ls: base_u[:] = unknowns.vec base_norm = f_norm # Apply step that doesn't violate bounds unknowns_cache[:] = unknowns.vec unknowns.vec += alpha * result.vec # Metadata update update_local_meta(local_meta, (self.iter_count, 0)) # Just evaluate (and optionally solve) the model with the new # points if self.options['solve_subsystems']: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) self.recorders.record_iteration(system, local_meta) f_norm = resids.norm() u_norm = np.linalg.norm(unknowns.vec - unknowns_cache) if iprint == 2: self.print_norm(self.print_name, system, self.iter_count, f_norm, f_norm0, u_norm=u_norm) # Line Search to determine how far to step in the Newton direction if ls: f_norm = ls.solve(params, unknowns, resids, system, self, alpha_scalar, alpha, base_u, base_norm, f_norm, f_norm0, metadata) # Final residual print if you only want the last one if iprint == 1: self.print_norm(self.print_name, system, self.iter_count, f_norm, f_norm0, u_norm=u_norm) # Return system's FD status back to what it was system.deriv_options['type'] = save_type system.deriv_options.locked = True if self.iter_count >= maxiter or isnan(f_norm): msg = 'FAILED to converge after %d iterations' % self.iter_count fail = True else: msg = 'Converged in %d iterations' % self.iter_count fail = False if iprint > 0 or (fail and iprint > -1): self.print_norm(self.print_name, system, self.iter_count, f_norm, f_norm0, msg=msg) if fail and self.options['err_on_maxiter']: raise AnalysisError("Solve in '%s': Newton %s" % (system.pathname, msg))
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using Gauss Seidel. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] maxiter = self.options['maxiter'] iprint = self.options['iprint'] # Initial run self.iter_count = 1 # Metadata setup local_meta = create_local_meta(metadata, system.pathname) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count, )) # Initial Solve system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Bail early if the user wants to. if maxiter == 1: return resids = system.resids # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() basenorm = normval if normval > atol else 1.0 if self.options['iprint'] > 0: self.print_norm(self.print_name, system.pathname, 0, normval, basenorm) while self.iter_count < maxiter and \ normval > atol and \ normval/basenorm > rtol: # Metadata update self.iter_count += 1 update_local_meta(local_meta, (self.iter_count, )) # Runs an iteration system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() if self.options['iprint'] > 0: self.print_norm(self.print_name, system.pathname, self.iter_count, normval, basenorm) if self.options['iprint'] > 0: if self.iter_count == maxiter or isnan(normval): msg = 'FAILED to converge after max iterations' else: msg = 'converged' self.print_norm(self.print_name, system.pathname, self.iter_count, normval, basenorm, msg=msg)
def solve(self, params, unknowns, resids, system, solver, alpha, fnorm0, metadata=None): """ Take the gradient calculated by the parent solver and figure out how far to go. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). solver : `Solver` Parent solver instance. alpha : float Initial over-relaxation factor as used in parent solver. fnorm0 : float Initial norm of the residual for relative tolerance check. Returns -------- float Norm of the final residual """ atol = self.options['atol'] rtol = self.options['rtol'] maxiter = self.options['maxiter'] result = system.dumat[None] local_meta = create_local_meta(metadata, system.pathname) # If our step will violate any upper or lower bounds, then reduce # alpha so that we only step to that boundary. alpha = unknowns.distance_along_vector_to_limit(alpha, result) # Apply step that doesn't violate bounds unknowns.vec += alpha * result.vec # Metadata update update_local_meta(local_meta, (solver.iter_count, 0)) # Just evaluate the model with the new points if solver.options['solve_subsystems']: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) self.recorders.record_iteration(system, local_meta) # Initial execution really belongs to our parent driver's iteration, # so use its info. fnorm = resids.norm() if solver.options['iprint'] > 0: self.print_norm(solver.print_name, system.pathname, solver.iter_count, fnorm, fnorm0) itercount = 0 ls_alpha = alpha # Further backtacking if needed. while itercount < maxiter and \ fnorm > atol and \ fnorm/fnorm0 > rtol: ls_alpha *= 0.5 unknowns.vec -= ls_alpha * result.vec itercount += 1 # Metadata update update_local_meta(local_meta, (solver.iter_count, itercount)) # Just evaluate the model with the new points if self.options['solve_subsystems']: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) solver.recorders.record_iteration(system, local_meta) fnorm = resids.norm() if self.options['iprint'] > 0: self.print_norm(self.print_name, system.pathname, itercount, fnorm, fnorm0, indent=1, solver='LS') if itercount >= maxiter and self.options['err_on_maxiter']: raise AnalysisError( "Solve in '%s': BackTracking failed to converge after %d " "iterations." % (system.pathname, maxiter)) return fnorm
def solve(self, params, unknowns, resids, system, solver, alpha_scalar, alpha, base_u, base_norm, fnorm, fnorm0, metadata=None): """ Take the gradient calculated by the parent solver and figure out how far to go. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. solver : `Solver` Parent solver instance. alpha_scalar : float Initial over-relaxation factor as used in parent solver. alpha : ndarray Initial over-relaxation factor as used in parent solver, vector (so we don't re-allocate). base_u : ndarray Initial value of unknowns before the Newton step. base_norm : float Norm of the residual prior to taking the Newton step. fnorm : float Norm of the residual after taking the Newton step. fnorm0 : float Initial norm of the residual for iteration printing. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). Returns -------- float Norm of the final residual """ maxiter = self.options['maxiter'] rho = self.options['rho'] c = self.options['c'] iprint = self.options['iprint'] result = system.dumat[None] local_meta = create_local_meta(metadata, system.pathname) itercount = 0 ls_alpha = alpha_scalar # Further backtacking if needed. # The Armijo-Goldstein is basically a slope comparison --actual vs predicted. # We don't have an actual gradient, but we have the Newton vector that should # take us to zero, and our "runs" are the same, and we can just compare the # "rise". while itercount < maxiter and (base_norm - fnorm) < c * ls_alpha * base_norm: ls_alpha *= rho # If our step will violate any upper or lower bounds, then reduce # alpha in just that direction so that we only step to that # boundary. unknowns.vec[:] = base_u alpha[:] = ls_alpha alpha = unknowns.distance_along_vector_to_limit(alpha, result) unknowns.vec += alpha * result.vec itercount += 1 # Metadata update update_local_meta(local_meta, (solver.iter_count, itercount)) # Just evaluate the model with the new points if self.options['solve_subsystems']: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) solver.recorders.record_iteration(system, local_meta) fnorm = resids.norm() if iprint == 2: self.print_norm(self.print_name, system, itercount, fnorm, fnorm0, indent=1, solver='LS') # Final residual print if you only want the last one if iprint == 1: self.print_norm(self.print_name, system, itercount, fnorm, fnorm0, indent=1, solver='LS') if itercount >= maxiter or isnan(fnorm): if self.options['err_on_maxiter']: msg = "Solve in '{}': BackTracking failed to converge after {} " \ "iterations." raise AnalysisError(msg.format(system.pathname, maxiter)) msg = 'FAILED to converge after %d iterations' % itercount fail = True else: msg = 'Converged in %d iterations' % itercount fail = False if iprint > 0 or (fail and iprint > -1): self.print_norm(self.print_name, system, itercount, fnorm, fnorm0, msg=msg, indent=1, solver='LS') return fnorm
def _save_case(self, case, meta=None): if self._num_par_doe > 1: if self._load_balance: self.recorders.record_completed_case(self.root, case) else: self.recorders.record_iteration(self.root, meta, dummy=(case is None)) else: self.recorders.record_iteration(self.root, meta) def _prep_case(self, case, iter_count): """Create metadata for the case and set design variables. """ metadata = create_local_meta(None, 'Driver') update_local_meta(metadata, (iter_count, )) for dv_name, dv_val in case: self.set_desvar(dv_name, dv_val) return metadata def _try_case(self, root, metadata): """Run a case and save exception info and mark the metadata if the case fails. """ terminate = False exc = None metadata['terminate'] = 0
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using a Netwon's Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] utol = self.options['utol'] maxiter = self.options['maxiter'] alpha_scalar = self.options['alpha'] iprint = self.options['iprint'] ls = self.line_search unknowns_cache = self.unknowns_cache # Metadata setup self.iter_count = 0 local_meta = create_local_meta(metadata, system.pathname) if self.ln_solver: self.ln_solver.local_meta = local_meta else: system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count, 0)) # Perform an initial run to propagate srcs to targets. system.apply_nonlinear(params, unknowns, resids) f_norm = resids.norm() if f_norm < atol: if iprint == 2: self.print_norm(self.print_name, system, self.iter_count, f_norm, 1.0, u_norm=1.0) return if self.options['solve_subsystems']: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids) if ls: base_u = np.zeros(unknowns.vec.shape) f_norm = resids.norm() f_norm0 = f_norm if iprint == 2: self.print_norm(self.print_name, system, 0, f_norm, f_norm0) arg = system.drmat[None] result = system.dumat[None] u_norm = 1.0e99 # Can't have the system trying to FD itself when it also contains Newton. save_type = system.deriv_options['type'] system.deriv_options.locked = False system.deriv_options['type'] = 'user' while self.iter_count < maxiter and f_norm > atol and \ f_norm/f_norm0 > rtol and u_norm > utol: # Linearize Model with partial derivatives system._sys_linearize(params, unknowns, resids, total_derivs=False) # Calculate direction to take step arg.vec[:] = -resids.vec with system._dircontext: system.solve_linear(system.dumat, system.drmat, [None], mode='fwd', solver=self.ln_solver) system.clear_dparams() self.iter_count += 1 # Allow different alphas for each value so we can keep moving when we # hit a bound. alpha = alpha_scalar*np.ones(len(unknowns.vec)) # If our step will violate any upper or lower bounds, then reduce # alpha in just that direction so that we only step to that # boundary. alpha = unknowns.distance_along_vector_to_limit(alpha, result) # Cache the current norm if ls: base_u[:] = unknowns.vec base_norm = f_norm # Apply step that doesn't violate bounds unknowns_cache[:] = unknowns.vec unknowns.vec += alpha*result.vec # Metadata update update_local_meta(local_meta, (self.iter_count, 0)) # Just evaluate (and optionally solve) the model with the new # points sub_solve_limit = self.options['solve_subsystems_limit'] if self.options['solve_subsystems'] and (sub_solve_limit == -1 or self.iter_count <= sub_solve_limit): system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) self.recorders.record_iteration(system, local_meta) f_norm = resids.norm() u_norm = np.linalg.norm(unknowns.vec - unknowns_cache) if iprint == 2: self.print_norm(self.print_name, system, self.iter_count, f_norm, f_norm0, u_norm=u_norm) # Line Search to determine how far to step in the Newton direction if ls: f_norm = ls.solve(params, unknowns, resids, system, self, alpha_scalar, alpha, base_u, base_norm, f_norm, f_norm0, metadata) # Final residual print if you only want the last one if iprint == 1: self.print_norm(self.print_name, system, self.iter_count, f_norm, f_norm0, u_norm=u_norm) # Return system's FD status back to what it was system.deriv_options['type'] = save_type system.deriv_options.locked = True if self.iter_count >= maxiter or isnan(f_norm): msg = 'FAILED to converge after %d iterations' % self.iter_count fail = True else: msg = 'Converged in %d iterations' % self.iter_count fail = False if iprint > 0 or (fail and iprint > -1 ): self.print_norm(self.print_name, system, self.iter_count, f_norm, f_norm0, msg=msg) if fail and self.options['err_on_maxiter']: raise AnalysisError("Solve in '%s': Newton %s" % (system.pathname, msg))
def run(self, problem): """Optimize the problem using your choice of Scipy optimizer. Args ---- problem : `Problem` Our parent `Problem`. """ # Metadata Setup opt = self.options['optimizer'] self.metadata = create_local_meta(None, opt) self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count,)) # Initial Run problem.root.solve_nonlinear(metadata=self.metadata) pmeta = self.get_desvar_metadata() self.params = list(iterkeys(pmeta)) self.objs = list(iterkeys(self.get_objectives())) con_meta = self.get_constraint_metadata() self.cons = list(iterkeys(con_meta)) self.con_cache = self.get_constraints() self.opt_settings['maxiter'] = self.options['maxiter'] self.opt_settings['disp'] = self.options['disp'] # Size Problem nparam = 0 for param in itervalues(pmeta): nparam += param['size'] x_init = np.zeros(nparam) # Initial Parameters i = 0 use_bounds = (opt in _bounds_optimizers) if use_bounds: bounds = [] else: bounds = None for name, val in iteritems(self.get_desvars()): size = pmeta[name]['size'] x_init[i:i+size] = val i += size # Bounds if our optimizer supports them if use_bounds: meta_low = pmeta[name]['lower'] meta_high = pmeta[name]['upper'] for j in range(0, size): if isinstance(meta_low, np.ndarray): p_low = meta_low[j] else: p_low = meta_low if isinstance(meta_high, np.ndarray): p_high = meta_high[j] else: p_high = meta_high bounds.append((p_low, p_high)) # Constraints constraints = [] i = 0 if opt in _constraint_optimizers: for name, meta in con_meta.items(): size = meta['size'] for j in range(0, size): con_dict = {} if meta['equals'] is not None: con_dict['type'] = 'eq' else: con_dict['type'] = 'ineq' con_dict['fun'] = self._confunc if opt in _constraint_grad_optimizers: con_dict['jac'] = self._congradfunc con_dict['args'] = [name, j] constraints.append(con_dict) self.con_idx[name] = i i += size # Provide gradients for optimizers that support it if opt in _gradient_optimizers: jac = self._gradfunc else: jac = None # optimize self._problem = problem result = minimize(self._objfunc, x_init, #args=(), method=opt, jac=jac, #hess=None, #hessp=None, bounds=bounds, constraints=constraints, tol=self.options['tol'], #callback=None, options=self.opt_settings) self._problem = None self.result = result self.exit_flag = 1 if self.result.success else 0 print('Optimization Complete') print('-'*35)
def run(self, problem): self.set_desvar('p1.x', 'var_x') self.set_desvar('p2.y', 123.0) metadata = create_local_meta(None, 'Driver') problem.root.solve_nonlinear(metadata=metadata)
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using the Brent Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ self.sys = system self.metadata = metadata self.local_meta = create_local_meta(self.metadata, self.sys.pathname) self.sys.ln_solver.local_meta = self.local_meta idx = self.options['state_var_idx'] if self.var_lower_bound is not None: lower = params[self.var_lower_bound] else: lower = self.options['lower_bound'] if self.var_upper_bound is not None: upper = params[self.var_upper_bound] else: upper = self.options['upper_bound'] kwargs = { 'maxiter': self.options['max_iter'], 'a': lower, 'b': upper, 'full_output': True, 'args': (params, unknowns, resids) } if self.options['xtol']: kwargs['xtol'] = self.options['xtol'] if self.options['rtol'] > 0: kwargs['rtol'] = self.options['rtol'] # Brent's method self.iter_count = 0 # initial run to compute initial_norm self.sys.children_solve_nonlinear(self.local_meta) self.recorders.record_iteration(system, self.local_meta) # Evaluate Norm self.sys.apply_nonlinear(params, unknowns, resids) self.basenorm = resid_norm_0 = abs( resids._dat[self.s_var_name].val[idx]) xstar, r = brentq(self._eval, **kwargs) self.sys = None resid_norm = abs(resids._dat[self.s_var_name].val[idx]) if self.options['iprint'] > 0: if self.iter_count == self.options['max_iter'] or isnan( resid_norm): msg = 'FAILED to converge after max iterations' else: msg = 'converged' self.print_norm(self.print_name, system.pathname, self.iter_count, resid_norm, resid_norm_0, msg=msg)
def run(self, problem): """pyOpt execution. Note that pyOpt controls the execution, and the individual optimizers (i.e., SNOPT) control the iteration. Args ---- problem : `Problem` Our parent `Problem`. """ self.pyopt_solution = None rel = problem.root._probdata.relevance # Metadata Setup self.metadata = create_local_meta(None, self.options['optimizer']) self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count,)) # Initial Run problem.root.solve_nonlinear(metadata=self.metadata) opt_prob = Optimization(self.options['title'], self._objfunc) # Add all parameters param_meta = self.get_desvar_metadata() self.indep_list = indep_list = list(iterkeys(param_meta)) param_vals = self.get_desvars() for name, meta in iteritems(param_meta): opt_prob.addVarGroup(name, meta['size'], type='c', value=param_vals[name], lower=meta['lower'], upper=meta['upper']) opt_prob.finalizeDesignVariables() # Add all objectives objs = self.get_objectives() self.quantities = list(iterkeys(objs)) self.sparsity = OrderedDict() #{} for name in objs: opt_prob.addObj(name) self.sparsity[name] = self.indep_list # Calculate and save gradient for any linear constraints. lcons = self.get_constraints(lintype='linear').keys() if len(lcons) > 0: self.lin_jacs = problem.calc_gradient(indep_list, lcons, return_format='dict') #print("Linear Gradient") #print(self.lin_jacs) # Add all equality constraints econs = self.get_constraints(ctype='eq', lintype='nonlinear') con_meta = self.get_constraint_metadata() self.quantities += list(iterkeys(econs)) for name in self.get_constraints(ctype='eq'): size = con_meta[name]['size'] lower = upper = con_meta[name]['equals'] # Sparsify Jacobian via relevance wrt = rel.relevant[name].intersection(indep_list) self.sparsity[name] = wrt if con_meta[name]['linear'] is True: opt_prob.addConGroup(name, size, lower=lower, upper=upper, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: opt_prob.addConGroup(name, size, lower=lower, upper=upper, wrt=wrt) # Add all inequality constraints incons = self.get_constraints(ctype='ineq', lintype='nonlinear') self.quantities += list(iterkeys(incons)) for name in self.get_constraints(ctype='ineq'): size = con_meta[name]['size'] # Bounds - double sided is supported lower = con_meta[name]['lower'] upper = con_meta[name]['upper'] # Sparsify Jacobian via relevance wrt = rel.relevant[name].intersection(indep_list) self.sparsity[name] = wrt if con_meta[name]['linear'] is True: opt_prob.addConGroup(name, size, upper=upper, lower=lower, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: opt_prob.addConGroup(name, size, upper=upper, lower=lower, wrt=wrt) # Instantiate the requested optimizer optimizer = self.options['optimizer'] try: exec('from pyoptsparse import %s' % optimizer) except ImportError: msg = "Optimizer %s is not available in this installation." % \ optimizer raise ImportError(msg) optname = vars()[optimizer] opt = optname() #Set optimization options for option, value in self.opt_settings.items(): opt.setOption(option, value) self._problem = problem # Execute the optimization problem if self.options['pyopt_diff'] is True: # Use pyOpt's internal finite difference fd_step = problem.root.fd_options['step_size'] sol = opt(opt_prob, sens='FD', sensStep=fd_step, storeHistory=self.hist_file) else: # Use OpenMDAO's differentiator for the gradient sol = opt(opt_prob, sens=self._gradfunc, storeHistory=self.hist_file) self._problem = None # Print results if self.options['print_results'] is True: print(sol) # Pull optimal parameters back into framework and re-run, so that # framework is left in the right final state dv_dict = sol.getDVs() for name in indep_list: val = dv_dict[name] self.set_desvar(name, val) self.root.solve_nonlinear(metadata=self.metadata) # Save the most recent solution. self.pyopt_solution = sol try: exit_status = sol.optInform['value'] self.exit_flag = 1 if exit_status > 2: # bad self.exit_flag = 0 except KeyError: #nothing is here, so something bad happened! self.exit_flag = 0
def run(self, problem): """pyOpt execution. Note that pyOpt controls the execution, and the individual optimizers (i.e., SNOPT) control the iteration. Args ---- problem : `Problem` Our parent `Problem`. """ self.pyopt_solution = None rel = problem.root._probdata.relevance # Metadata Setup self.metadata = create_local_meta(None, self.options['optimizer']) self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count,)) # Initial Run with problem.root._dircontext: problem.root.solve_nonlinear(metadata=self.metadata) opt_prob = Optimization(self.options['title'], self._objfunc) # Add all parameters param_meta = self.get_desvar_metadata() self.indep_list = indep_list = list(param_meta) param_vals = self.get_desvars() for name, meta in iteritems(param_meta): opt_prob.addVarGroup(name, meta['size'], type='c', value=param_vals[name], lower=meta['lower'], upper=meta['upper']) opt_prob.finalizeDesignVariables() # Figure out parameter subsparsity for paramcomp index connections. # sub_param_conns is empty unless there are some index conns. # full_param_conns gets filled with the connections to the entire # parameter so that those params can be filtered out of the sparse # set if the full path is also relevant sub_param_conns = {} full_param_conns = {} for name in indep_list: pathname = problem.root.unknowns.metadata(name)['pathname'] sub_param_conns[name] = {} full_param_conns[name] = set() for target, info in iteritems(problem.root.connections): src, indices = info if src == pathname: if indices is not None: # Need to map the connection indices onto the desvar # indices if both are declared. dv_idx = param_meta[name].get('indices') indices = set(indices) if dv_idx is not None: indices.intersection_update(dv_idx) ldv_idx = list(dv_idx) mapped_idx = [ldv_idx.index(item) for item in indices] sub_param_conns[name][target] = mapped_idx else: sub_param_conns[name][target] = indices else: full_param_conns[name].add(target) # Add all objectives objs = self.get_objectives() self.quantities = list(objs) self.sparsity = OrderedDict() self.sub_sparsity = OrderedDict() for name in objs: opt_prob.addObj(name) self.sparsity[name] = self.indep_list # Calculate and save gradient for any linear constraints. lcons = self.get_constraints(lintype='linear').keys() if len(lcons) > 0: self.lin_jacs = problem.calc_gradient(indep_list, lcons, return_format='dict') #print("Linear Gradient") #print(self.lin_jacs) # Add all equality constraints econs = self.get_constraints(ctype='eq', lintype='nonlinear') con_meta = self.get_constraint_metadata() self.quantities += list(econs) for name in self.get_constraints(ctype='eq'): meta = con_meta[name] size = meta['size'] lower = upper = meta['equals'] # Sparsify Jacobian via relevance rels = rel.relevant[name] wrt = rels.intersection(indep_list) self.sparsity[name] = wrt if meta['linear']: opt_prob.addConGroup(name, size, lower=lower, upper=upper, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: jac = self._build_sparse(name, wrt, size, param_vals, sub_param_conns, full_param_conns, rels) opt_prob.addConGroup(name, size, lower=lower, upper=upper, wrt=wrt, jac=jac) # Add all inequality constraints incons = self.get_constraints(ctype='ineq', lintype='nonlinear') self.quantities += list(incons) for name in self.get_constraints(ctype='ineq'): meta = con_meta[name] size = meta['size'] # Bounds - double sided is supported lower = meta['lower'] upper = meta['upper'] # Sparsify Jacobian via relevance rels = rel.relevant[name] wrt = rels.intersection(indep_list) self.sparsity[name] = wrt if meta['linear']: opt_prob.addConGroup(name, size, upper=upper, lower=lower, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: jac = self._build_sparse(name, wrt, size, param_vals, sub_param_conns, full_param_conns, rels) opt_prob.addConGroup(name, size, upper=upper, lower=lower, wrt=wrt, jac=jac) # Instantiate the requested optimizer optimizer = self.options['optimizer'] try: exec('from pyoptsparse import %s' % optimizer) except ImportError: msg = "Optimizer %s is not available in this installation." % \ optimizer raise ImportError(msg) optname = vars()[optimizer] opt = optname() #Set optimization options for option, value in self.opt_settings.items(): opt.setOption(option, value) self._problem = problem # Execute the optimization problem if self.options['pyopt_diff']: # Use pyOpt's internal finite difference fd_step = problem.root.fd_options['step_size'] sol = opt(opt_prob, sens='FD', sensStep=fd_step, storeHistory=self.hist_file) else: # Use OpenMDAO's differentiator for the gradient sol = opt(opt_prob, sens=self._gradfunc, storeHistory=self.hist_file) self._problem = None # Print results if self.options['print_results']: print(sol) # Pull optimal parameters back into framework and re-run, so that # framework is left in the right final state dv_dict = sol.getDVs() for name in indep_list: val = dv_dict[name] self.set_desvar(name, val) with self.root._dircontext: self.root.solve_nonlinear(metadata=self.metadata) # Save the most recent solution. self.pyopt_solution = sol try: exit_status = sol.optInform['value'] self.exit_flag = 1 if exit_status > 2: # bad self.exit_flag = 0 except KeyError: #nothing is here, so something bad happened! self.exit_flag = 0
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using Gauss Seidel. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] utol = self.options['utol'] maxiter = self.options['maxiter'] iprint = self.options['iprint'] unknowns_cache = self.unknowns_cache # Initial run self.iter_count = 1 # Metadata setup local_meta = create_local_meta(metadata, system.pathname) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count,)) # Initial Solve system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Bail early if the user wants to. if maxiter == 1: return resids = system.resids unknowns_cache = np.zeros(unknowns.vec.shape) # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() basenorm = normval if normval > atol else 1.0 u_norm = 1.0e99 if iprint == 2: self.print_norm(self.print_name, system, 1, normval, basenorm) while self.iter_count < maxiter and \ normval > atol and \ normval/basenorm > rtol and \ u_norm > utol: # Metadata update self.iter_count += 1 update_local_meta(local_meta, (self.iter_count,)) unknowns_cache[:] = unknowns.vec # Runs an iteration system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() u_norm = np.linalg.norm(unknowns.vec - unknowns_cache) if iprint == 2: self.print_norm(self.print_name, system, self.iter_count, normval, basenorm, u_norm=u_norm) # Final residual print if you only want the last one if iprint == 1: self.print_norm(self.print_name, system, self.iter_count, normval, basenorm, u_norm=u_norm) if self.iter_count >= maxiter or isnan(normval): msg = 'FAILED to converge after %d iterations' % self.iter_count fail = True else: fail = False if iprint > 0 or (fail and iprint > -1 ): if not fail: msg = 'Converged in %d iterations' % self.iter_count self.print_norm(self.print_name, system, self.iter_count, normval, basenorm, msg=msg) if fail and self.options['err_on_maxiter']: raise AnalysisError("Solve in '%s': NLGaussSeidel %s" % (system.pathname, msg))
def run(self, problem): """pyOpt execution. Note that pyOpt controls the execution, and the individual optimizers (i.e., SNOPT) control the iteration. Args ---- problem : `Problem` Our parent `Problem`. """ self.pyopt_solution = None rel = problem.root._relevance # Metadata Setup self.metadata = create_local_meta(None, self.options['optimizer']) self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count,)) # Initial Run problem.root.solve_nonlinear(metadata=self.metadata) opt_prob = Optimization(self.options['title'], self.objfunc) # Add all parameters param_meta = self.get_param_metadata() param_list = list(iterkeys(param_meta)) param_vals = self.get_params() for name, meta in iteritems(param_meta): opt_prob.addVarGroup(name, meta['size'], type='c', value=param_vals[name], lower=meta['low'], upper=meta['high']) opt_prob.finalizeDesignVariables() # Add all objectives objs = self.get_objectives() self.quantities = list(iterkeys(objs)) for name in objs: opt_prob.addObj(name) # Calculate and save gradient for any linear constraints. lcons = self.get_constraints(lintype='linear').values() if len(lcons) > 0: self.lin_jacs = problem.calc_gradient(param_list, lcons, return_format='dict') #print("Linear Gradient") #print(self.lin_jacs) # Add all equality constraints econs = self.get_constraints(ctype='eq', lintype='nonlinear') con_meta = self.get_constraint_metadata() self.quantities += list(iterkeys(econs)) for name in econs: size = con_meta[name]['size'] lower = np.zeros((size)) upper = np.zeros((size)) # Sparsify Jacobian via relevance wrt = rel.relevant[name].intersection(param_list) if con_meta[name]['linear'] is True: opt_prob.addConGroup(name, size, lower=lower, upper=upper, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: opt_prob.addConGroup(name, size, lower=lower, upper=upper, wrt=wrt) # Add all inequality constraints incons = self.get_constraints(ctype='ineq', lintype='nonlinear') self.quantities += list(iterkeys(incons)) for name in incons: size = con_meta[name]['size'] upper = np.zeros((size)) # Sparsify Jacobian via relevance wrt = rel.relevant[name].intersection(param_list) if con_meta[name]['linear'] is True: opt_prob.addConGroup(name, size, upper=upper, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: opt_prob.addConGroup(name, size, upper=upper, wrt=wrt) # TODO: Support double-sided constraints in openMDAO # Add all double_sided constraints #for name, con in iteritems(self.get_2sided_constraints()): #size = con_meta[name]['size'] #upper = con.high * np.ones((size)) #lower = con.low * np.ones((size)) #name = '%s.out0' % con.pcomp_name #if con.linear is True: #opt_prob.addConGroup(name, #size, upper=upper, lower=lower, #linear=True, wrt=param_list, #jac=self.lin_jacs[name]) #else: #opt_prob.addConGroup(name, # size, upper=upper, lower=lower) # Instantiate the requested optimizer optimizer = self.options['optimizer'] try: exec('from pyoptsparse import %s' % optimizer) except ImportError: msg = "Optimizer %s is not available in this installation." % \ optimizer raise ImportError(msg) optname = vars()[optimizer] opt = optname() #Set optimization options for option, value in self.opt_settings.items(): opt.setOption(option, value) self._problem = problem # Execute the optimization problem if self.options['pyopt_diff'] is True: # Use pyOpt's internal finite difference fd_step = problem.root.fd_options['step_size'] sol = opt(opt_prob, sens='FD', sensStep=fd_step) else: # Use OpenMDAO's differentiator for the gradient sol = opt(opt_prob, sens=self.gradfunc) self._problem = None # Print results if self.options['print_results'] is True: print(sol) # Pull optimal parameters back into framework and re-run, so that # framework is left in the right final state dv_dict = sol.getDVs() for name in self.get_params(): val = dv_dict[name] self.set_param(name, val) self.root.solve_nonlinear(metadata=self.metadata) # Save the most recent solution. self.pyopt_solution = sol try: exit_status = sol.optInform['value'] self.exit_flag = 1 if exit_status > 2: # bad self.exit_flag = 0 except KeyError: #nothing is here, so something bad happened! self.exit_flag = 0
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using a Netwon's Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] maxiter = self.options['maxiter'] ls_atol = self.options['ls_atol'] ls_rtol = self.options['ls_rtol'] ls_maxiter = self.options['ls_maxiter'] alpha = self.options['alpha'] # Metadata setup self.iter_count = 0 ls_itercount = 0 local_meta = create_local_meta(metadata, system.name) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count, ls_itercount)) # Perform an initial run to propagate srcs to targets. system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids) f_norm = resids.norm() f_norm0 = f_norm if self.options['iprint'] > 0: self.print_norm('NEWTON', local_meta, 0, f_norm, f_norm0) arg = system.drmat[None] result = system.dumat[None] alpha_base = alpha while self.iter_count < maxiter and f_norm > atol and \ f_norm/f_norm0 > rtol: # Linearize Model system.jacobian(params, unknowns, resids) # Calculate direction to take step arg.vec[:] = resids.vec[:] system.solve_linear(system.dumat, system.drmat, [None], mode='fwd') unknowns.vec[:] += alpha*result.vec[:] # Metadata update self.iter_count += 1 ls_itercount = 0 update_local_meta(local_meta, (self.iter_count, ls_itercount)) # Just evaluate the model with the new points system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) for recorder in self.recorders: recorder.raw_record(params, unknowns, resids, local_meta) f_norm = resids.norm() if self.options['iprint'] > 0: self.print_norm('NEWTON', local_meta, self.iter_count, f_norm, f_norm0) # Backtracking Line Search while ls_itercount < ls_maxiter and \ f_norm > ls_atol and \ f_norm/f_norm0 > ls_rtol: alpha *= 0.5 unknowns.vec[:] -= alpha*result.vec[:] ls_itercount += 1 # Metadata update update_local_meta(local_meta, (self.iter_count, ls_itercount)) # Just evaluate the model with the new points system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) for recorder in self.recorders: recorder.raw_record(params, unknowns, resids, local_meta) f_norm = resids.norm() if self.options['iprint'] > 1: self.print_norm('BK_TKG', local_meta, ls_itercount, f_norm, f_norm/f_norm0, indent=1, solver='LS') # Reset backtracking alpha = alpha_base for recorder in self.recorders: recorder.raw_record(params, unknowns, resids, local_meta) # Need to make sure the whole workflow is executed at the final # point, not just evaluated. #self.iter_count += 1 #update_local_meta(local_meta, (self.iter_count, 0)) #system.children_solve_nonlinear(local_meta) if self.options['iprint'] > 0: self.print_norm('NEWTON', local_meta, self.iter_count, f_norm, f_norm0, msg='Converged')
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using the Brent Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ self.sys = system self.metadata = metadata self.local_meta = create_local_meta(self.metadata, self.sys.pathname) self.sys.ln_solver.local_meta = self.local_meta idx = self.options["state_var_idx"] if self.var_lower_bound is not None: lower = params[self.var_lower_bound] else: lower = self.options["lower_bound"] if self.var_upper_bound is not None: upper = params[self.var_upper_bound] else: upper = self.options["upper_bound"] kwargs = { "maxiter": self.options["max_iter"], "a": lower, "b": upper, "full_output": True, "args": (params, unknowns, resids), } if self.options["xtol"]: kwargs["xtol"] = self.options["xtol"] if self.options["rtol"] > 0: kwargs["rtol"] = self.options["rtol"] # Brent's method self.iter_count = 0 # initial run to compute initial_norm self.sys.children_solve_nonlinear(self.local_meta) self.recorders.record_iteration(system, self.local_meta) # Evaluate Norm self.sys.apply_nonlinear(params, unknowns, resids) self.basenorm = resid_norm_0 = abs(resids._dat[self.s_var_name].val[idx]) xstar, r = brentq(self._eval, **kwargs) self.sys = None resid_norm = abs(resids._dat[self.s_var_name].val[idx]) if self.options["iprint"] > 0: if self.iter_count == self.options["max_iter"] or isnan(resid_norm): msg = "FAILED to converge after max iterations" else: msg = "converged" self.print_norm(self.print_name, system.pathname, self.iter_count, resid_norm, resid_norm_0, msg=msg)
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using Gauss Seidel. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] utol = self.options['utol'] maxiter = self.options['maxiter'] rutol = self.options['rutol'] iprint = self.options['iprint'] unknowns_cache = self.unknowns_cache # Initial run self.iter_count = 1 # Metadata setup local_meta = create_local_meta(metadata, system.pathname) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count,)) # Initial Solve system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Bail early if the user wants to. if maxiter == 1: return resids = system.resids unknowns_cache = np.zeros(unknowns.vec.shape) # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() basenorm = normval if normval > atol else 1.0 u_norm = 1.0e99 ru_norm = 1.0e99 if iprint == 2: self.print_norm(self.print_name, system, 1, normval, basenorm) while self.iter_count < maxiter and \ normval > atol and \ normval/basenorm > rtol and \ u_norm > utol and \ ru_norm > rutol: # Metadata update self.iter_count += 1 update_local_meta(local_meta, (self.iter_count,)) unknowns_cache[:] = unknowns.vec # Runs an iteration system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() u_norm = np.linalg.norm(unknowns.vec - unknowns_cache) ru_norm = np.linalg.norm(unknowns.vec - unknowns_cache)/np.linalg.norm(unknowns.vec) if self.options['use_aitken']: # If Aitken acceleration is enabled # This method is used by Kenway et al. in "Scalable Parallel # Approach for High-Fidelity Steady-State Aeroelastic Analysis # and Adjoint Derivative Computations" (line 22 of Algorithm 1) # It is based on "A version of the Aitken accelerator for # computer iteration" by Irons et al. # Use relaxation after second iteration # self.delta_u_n_1 is a string for the first iteration if (type(self.delta_u_n_1) is not str) and \ normval > atol and \ normval/basenorm > rtol and \ u_norm > utol and \ ru_norm > rutol: delta_u_n = unknowns.vec - unknowns_cache delta_u_n_1 = self.delta_u_n_1 # Compute relaxation factor self.aitken_alpha = self.aitken_alpha * \ (1. - np.dot((delta_u_n - delta_u_n_1), delta_u_n) \ / np.linalg.norm((delta_u_n - delta_u_n_1), 2)**2) # Limit relaxation factor to desired range self.aitken_alpha = max(self.options['aitken_alpha_min'], min(self.options['aitken_alpha_max'], self.aitken_alpha)) if iprint == 1 or iprint == 2: print("Aitken relaxation factor is", self.aitken_alpha) self.delta_u_n_1 = delta_u_n.copy() # Update unknowns vector unknowns.vec[:] = unknowns_cache + self.aitken_alpha * delta_u_n elif (type(self.delta_u_n_1) is str): # For the first iteration # Initially self.delta_u_n_1 is a string then it is replaced # by the following vector self.delta_u_n_1 = unknowns.vec - unknowns_cache if iprint == 2: self.print_norm(self.print_name, system, self.iter_count, normval, basenorm, u_norm=u_norm) # Final residual print if you only want the last one if iprint == 1: self.print_norm(self.print_name, system, self.iter_count, normval, basenorm, u_norm=u_norm) if self.iter_count >= maxiter or isnan(normval): msg = 'FAILED to converge after %d iterations' % self.iter_count fail = True else: fail = False if iprint > 0 or (fail and iprint > -1 ): if not fail: msg = 'Converged in %d iterations' % self.iter_count self.print_norm(self.print_name, system, self.iter_count, normval, basenorm, msg=msg) if fail and self.options['err_on_maxiter']: raise AnalysisError("Solve in '%s': NLGaussSeidel %s" % (system.pathname, msg))
def run(self, problem): """Optimize the problem using your choice of Scipy optimizer. Args ---- problem : `Problem` Our parent `Problem`. """ # Metadata Setup self.metadata = create_local_meta(None, "BayesOpt") self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count, )) # Initial Run with problem.root._dircontext: problem.root.solve_nonlinear(metadata=self.metadata) pmeta = self.get_desvar_metadata() self.params = list(pmeta) self.objs = list(self.get_objectives()) con_meta = self.get_constraint_metadata() self.cons = list(con_meta) self.con_cache = self.get_constraints() self.opt_settings['disp'] = self.options['disp'] bopt_params = {} bopt_params['n_iterations'] = self.options['n_iterations'] bopt_params['n_inner_iterations'] = self.options['n_inner_iterations'] bopt_params['n_iter_relearn'] = self.options['n_iter_relearn'] bopt_params['n_init_samples'] = self.options['n_init_samples'] bopt_params['noise'] = self.options['noise'] bopt_params['surr_name'] = self.options['surr_name'] # Size Problem nparam = 0 for param in itervalues(pmeta): nparam += param['size'] x_init = np.empty(nparam) i = 0 # Initial Parameters lower_bounds = [] upper_bounds = [] for name, val in iteritems(self.get_desvars()): size = pmeta[name]['size'] x_init[i:i + size] = val i += size # Bounds if our optimizer supports them meta_low = pmeta[name]['lower'] meta_high = pmeta[name]['upper'] for j in range(0, size): if isinstance(meta_low, np.ndarray): p_low = meta_low[j] else: p_low = meta_low if isinstance(meta_high, np.ndarray): p_high = meta_high[j] else: p_high = meta_high lower_bounds.append(p_low) upper_bounds.append(p_high) # optimize self._problem = problem min_value, xout, error = bayesopt.optimize(self._objfunc, len(lower_bounds), np.asarray(lower_bounds), np.asarray(upper_bounds), bopt_params) # Run one more iteration, at the computed minimum self._objfunc(xout) self._problem = None self.result = min_value # TODO: what is this supposed to return? self.exit_flag = 1 # TODO: handle optimization failure? if self.options['disp']: print('Optimization Complete') print('-' * 35)
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using the Brent Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ self.sys = system self.metadata = metadata self.local_meta = create_local_meta(self.metadata, self.sys.pathname) self.sys.ln_solver.local_meta = self.local_meta idx = self.options['state_var_idx'] if self.var_lower_bound is not None: lower = params[self.var_lower_bound] else: lower = self.options['lower_bound'] if self.var_upper_bound is not None: upper = params[self.var_upper_bound] else: upper = self.options['upper_bound'] kwargs = { 'maxiter': self.options['maxiter'], 'a': lower, 'b': upper, 'full_output': False, # False, because we don't use the info, so just wastes operations 'args': (params, unknowns, resids) } if self.options['xtol']: kwargs['xtol'] = self.options['xtol'] if self.options['rtol'] > 0: kwargs['rtol'] = self.options['rtol'] # Brent's method self.iter_count = 0 # initial run to compute initial_norm self.sys.children_solve_nonlinear(self.local_meta) self.recorders.record_iteration(system, self.local_meta) # Evaluate Norm self.sys.apply_nonlinear(params, unknowns, resids) self.basenorm = resid_norm_0 = abs( resids._dat[self.s_var_name].val[idx]) failed = False try: xstar = brentq(self._eval, **kwargs) except RuntimeError as err: msg = str(err) if 'different signs' in msg: raise failed = True self.sys = None resid_norm = abs(resids._dat[self.s_var_name].val[idx]) if self.options['iprint'] > 0: if not failed: msg = 'converged' self.print_norm(self.print_name, system.pathname, self.iter_count, resid_norm, resid_norm_0, msg=msg) if failed and self.options['err_on_maxiter']: raise AnalysisError(msg)
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using Gauss Seidel. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] maxiter = self.options['maxiter'] iprint = self.options['iprint'] # Initial run self.iter_count = 1 # Metadata setup local_meta = create_local_meta(metadata, system.pathname) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count,)) # Initial Solve system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Bail early if the user wants to. if maxiter == 1: return resids = system.resids # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() basenorm = normval if normval > atol else 1.0 if self.options['iprint'] > 0: self.print_norm('NLN_GS', system.pathname, 0, normval, basenorm) while self.iter_count < maxiter and \ normval > atol and \ normval/basenorm > rtol: # Metadata update self.iter_count += 1 update_local_meta(local_meta, (self.iter_count,)) # Runs an iteration system.children_solve_nonlinear(local_meta) self.recorders.record_iteration(system, local_meta) # Evaluate Norm system.apply_nonlinear(params, unknowns, resids) normval = resids.norm() if self.options['iprint'] > 0: self.print_norm('NLN_GS', system.pathname, self.iter_count, normval, basenorm) if self.options['iprint'] > 0: if self.iter_count == maxiter or isnan(normval): msg = 'FAILED to converge after max iterations' else: msg = 'converged' self.print_norm('NLN_GS', system.pathname, self.iter_count, normval, basenorm, msg=msg)
def run(self, problem): """pyOpt execution. Note that pyOpt controls the execution, and the individual optimizers (i.e., SNOPT) control the iteration. Args ---- problem : `Problem` Our parent `Problem`. """ self.pyopt_solution = None rel = problem.root._probdata.relevance # Metadata Setup self.metadata = create_local_meta(None, self.options['optimizer']) self.iter_count = 0 update_local_meta(self.metadata, (self.iter_count, )) # Initial Run with problem.root._dircontext: problem.root.solve_nonlinear(metadata=self.metadata) opt_prob = Optimization(self.options['title'], self._objfunc) # Add all parameters param_meta = self.get_desvar_metadata() self.indep_list = indep_list = list(param_meta) param_vals = self.get_desvars() for name, meta in iteritems(param_meta): opt_prob.addVarGroup(name, meta['size'], type='c', value=param_vals[name], lower=meta['lower'], upper=meta['upper']) opt_prob.finalizeDesignVariables() # Figure out parameter subsparsity for paramcomp index connections. # sub_param_conns is empty unless there are some index conns. # full_param_conns gets filled with the connections to the entire # parameter so that those params can be filtered out of the sparse # set if the full path is also relevant sub_param_conns = {} full_param_conns = {} for name in indep_list: pathname = problem.root.unknowns.metadata(name)['pathname'] sub_param_conns[name] = {} full_param_conns[name] = set() for target, info in iteritems(problem.root.connections): src, indices = info if src == pathname: if indices is not None: # Need to map the connection indices onto the desvar # indices if both are declared. dv_idx = param_meta[name].get('indices') indices = set(indices) if dv_idx is not None: indices.intersection_update(dv_idx) ldv_idx = list(dv_idx) mapped_idx = [ ldv_idx.index(item) for item in indices ] sub_param_conns[name][target] = mapped_idx else: sub_param_conns[name][target] = indices else: full_param_conns[name].add(target) # Add all objectives objs = self.get_objectives() self.quantities = list(objs) self.sparsity = OrderedDict() self.sub_sparsity = OrderedDict() for name in objs: opt_prob.addObj(name) self.sparsity[name] = self.indep_list # Calculate and save gradient for any linear constraints. lcons = self.get_constraints(lintype='linear').keys() self._problem = problem if len(lcons) > 0: self.lin_jacs = self.calc_gradient(indep_list, lcons, return_format='dict') #print("Linear Gradient") #print(self.lin_jacs) # Add all equality constraints econs = self.get_constraints(ctype='eq', lintype='nonlinear') con_meta = self.get_constraint_metadata() self.quantities += list(econs) self.active_tols = {} for name in self.get_constraints(ctype='eq'): meta = con_meta[name] size = meta['size'] lower = upper = meta['equals'] # Sparsify Jacobian via relevance rels = rel.relevant[name] wrt = rels.intersection(indep_list) self.sparsity[name] = wrt if meta['linear']: opt_prob.addConGroup(name, size, lower=lower, upper=upper, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: jac = self._build_sparse(name, wrt, size, param_vals, sub_param_conns, full_param_conns, rels) opt_prob.addConGroup(name, size, lower=lower, upper=upper, wrt=wrt, jac=jac) active_tol = meta.get('active_tol') if active_tol: self.active_tols[name] = active_tol # Add all inequality constraints incons = self.get_constraints(ctype='ineq', lintype='nonlinear') self.quantities += list(incons) for name in self.get_constraints(ctype='ineq'): meta = con_meta[name] size = meta['size'] # Bounds - double sided is supported lower = meta['lower'] upper = meta['upper'] # Sparsify Jacobian via relevance rels = rel.relevant[name] wrt = rels.intersection(indep_list) self.sparsity[name] = wrt if meta['linear']: opt_prob.addConGroup(name, size, upper=upper, lower=lower, linear=True, wrt=wrt, jac=self.lin_jacs[name]) else: jac = self._build_sparse(name, wrt, size, param_vals, sub_param_conns, full_param_conns, rels) opt_prob.addConGroup(name, size, upper=upper, lower=lower, wrt=wrt, jac=jac) active_tol = meta.get('active_tol') if active_tol is not None: self.active_tols[name] = active_tol # Instantiate the requested optimizer optimizer = self.options['optimizer'] try: _tmp = __import__('pyoptsparse', globals(), locals(), [optimizer], 0) opt = getattr(_tmp, optimizer)() except ImportError: msg = "Optimizer %s is not available in this installation." % \ optimizer raise ImportError(msg) #Set optimization options for option, value in self.opt_settings.items(): opt.setOption(option, value) self.opt_prob = opt_prob # Execute the optimization problem if self.options['gradient method'] == 'pyopt_fd': # Use pyOpt's internal finite difference fd_step = problem.root.deriv_options['step_size'] sol = opt(opt_prob, sens='FD', sensStep=fd_step, storeHistory=self.hist_file, hotStart=self.hotstart_file) elif self.options['gradient method'] == 'snopt_fd': if self.options['optimizer'] == 'SNOPT': # Use SNOPT's internal finite difference fd_step = problem.root.deriv_options['step_size'] sol = opt(opt_prob, sens=None, sensStep=fd_step, storeHistory=self.hist_file, hotStart=self.hotstart_file) else: msg = "SNOPT's internal finite difference can only be used with SNOPT" raise Exception(msg) else: # Use OpenMDAO's differentiator for the gradient sol = opt(opt_prob, sens=self._gradfunc, storeHistory=self.hist_file, hotStart=self.hotstart_file) self._problem = None # Print results if self.options['print_results']: print(sol) # Pull optimal parameters back into framework and re-run, so that # framework is left in the right final state dv_dict = sol.getDVs() for name in indep_list: val = dv_dict[name] self.set_desvar(name, val) with self.root._dircontext: self.root.solve_nonlinear(metadata=self.metadata) # Save the most recent solution. self.pyopt_solution = sol try: exit_status = sol.optInform['value'] self.exit_flag = 1 if exit_status > 2: # bad self.exit_flag = 0 except KeyError: #nothing is here, so something bad happened! self.exit_flag = 0
def solve(self, params, unknowns, resids, system, metadata=None): """ Solves the system using a Netwon's Method. Args ---- params : `VecWrapper` `VecWrapper` containing parameters. (p) unknowns : `VecWrapper` `VecWrapper` containing outputs and states. (u) resids : `VecWrapper` `VecWrapper` containing residuals. (r) system : `System` Parent `System` object. metadata : dict, optional Dictionary containing execution metadata (e.g. iteration coordinate). """ atol = self.options['atol'] rtol = self.options['rtol'] maxiter = self.options['maxiter'] ls_atol = self.options['ls_atol'] ls_rtol = self.options['ls_rtol'] ls_maxiter = self.options['ls_maxiter'] alpha = self.options['alpha'] # Metadata setup self.iter_count = 0 ls_itercount = 0 local_meta = create_local_meta(metadata, system.name) system.ln_solver.local_meta = local_meta update_local_meta(local_meta, (self.iter_count, ls_itercount)) # Perform an initial run to propagate srcs to targets. if self.options['solve_subsystems'] is True: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids) f_norm = resids.norm() f_norm0 = f_norm if self.options['iprint'] > 0: self.print_norm('NEWTON', local_meta, 0, f_norm, f_norm0) arg = system.drmat[None] result = system.dumat[None] alpha_base = alpha while self.iter_count < maxiter and f_norm > atol and \ f_norm/f_norm0 > rtol: # Linearize Model system.jacobian(params, unknowns, resids) # Calculate direction to take step arg.vec[:] = resids.vec[:] system.solve_linear(system.dumat, system.drmat, [None], mode='fwd') unknowns.vec[:] += alpha * result.vec[:] # Metadata update self.iter_count += 1 ls_itercount = 0 update_local_meta(local_meta, (self.iter_count, ls_itercount)) # Just evaluate the model with the new points if self.options['solve_subsystems'] is True: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) for recorder in self.recorders: recorder.raw_record(params, unknowns, resids, local_meta) f_norm = resids.norm() if self.options['iprint'] > 0: self.print_norm('NEWTON', local_meta, self.iter_count, f_norm, f_norm0) # Backtracking Line Search while ls_itercount < ls_maxiter and \ f_norm > ls_atol and \ f_norm/f_norm0 > ls_rtol: alpha *= 0.5 unknowns.vec[:] -= alpha * result.vec[:] ls_itercount += 1 # Metadata update update_local_meta(local_meta, (self.iter_count, ls_itercount)) # Just evaluate the model with the new points if self.options['solve_subsystems'] is True: system.children_solve_nonlinear(local_meta) system.apply_nonlinear(params, unknowns, resids, local_meta) for recorder in self.recorders: recorder.raw_record(params, unknowns, resids, local_meta) f_norm = resids.norm() if self.options['iprint'] > 1: self.print_norm('BK_TKG', local_meta, ls_itercount, f_norm, f_norm0, indent=1, solver='LS') # Reset backtracking alpha = alpha_base # Need to make sure the whole workflow is executed at the final # point, not just evaluated. #self.iter_count += 1 #update_local_meta(local_meta, (self.iter_count, 0)) #system.children_solve_nonlinear(local_meta) if self.options['iprint'] > 0: self.print_norm('NEWTON', local_meta, self.iter_count, f_norm, f_norm0, msg='Converged')