def solve_nonlinear(self, params, unknowns, resids): """Runs the component """ self.return_code = -12345678 if not self.options['command']: raise ValueError('Empty command list') if self.options['fail_hard']: err_class = RuntimeError else: err_class = AnalysisError return_code = None try: missing = self._check_for_files( self.options['external_input_files']) if missing: raise err_class("The following input files are missing: %s" % sorted(missing)) return_code, error_msg = self._execute_local() if return_code is None: raise AnalysisError('Timed out after %s sec.' % self.options['timeout']) elif return_code: if isinstance(self.stderr, str): if os.path.exists(self.stderr): stderrfile = open(self.stderr, 'r') error_desc = stderrfile.read() stderrfile.close() err_fragment = "\nError Output:\n%s" % error_desc else: err_fragment = "\n[stderr %r missing]" % self.stderr else: err_fragment = error_msg raise err_class('return_code = %d%s' % (return_code, err_fragment)) missing = self._check_for_files( self.options['external_output_files']) if missing: raise err_class("The following output files are missing: %s" % sorted(missing)) finally: self.return_code = -999999 if return_code is None else return_code
def solve_nonlinear(self, params, unknowns, resids): if MPI: unknowns['case_rank'] = MPI.COMM_WORLD.rank if self.trace: print(self.pathname, "solve_nonlinear") try: super(ExecComp4Test, self).solve_nonlinear(params, unknowns, resids) time.sleep(self.nl_delay) if self.num_nl_solves in self.fails: if self.critical: raise RuntimeError("OMG, a critical error!") else: raise AnalysisError("just an analysis error") finally: self.num_nl_solves += 1
def solve_nonlinear(self, params, unknowns, resids): if MPI: myrank = unknowns['case_rank'] = MPI.COMM_WORLD.rank else: myrank = unknowns['case_rank'] = int( os.environ.get('OPENMDAO_WORKER_ID', '0')) if self.trace: print(self.pathname, "solve_nonlinear") try: if myrank in self.fail_rank and self.num_nl_solves in self.fails: if self.fail_hard: raise RuntimeError("OMG, a critical error!") else: raise AnalysisError("just an analysis error") super(ExecComp4Test, self).solve_nonlinear(params, unknowns, resids) time.sleep(self.nl_delay) finally: self.num_nl_solves += 1
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 solve(self, rhs_mat, system, mode): """ Solves the linear system for the problem in self.system. The full solution vector is returned. Args ---- rhs_mat : dict of ndarray Dictionary containing one ndarry per top level quantity of interest. Each array contains the right-hand side for the linear solve. system : `System` Parent `System` object. mode : string Derivative mode, can be 'fwd' or 'rev'. Returns ------- dict of ndarray : Solution vectors """ options = self.options self.mode = mode unknowns_mat = OrderedDict() maxiter = options['maxiter'] atol = options['atol'] rtol = options['rtol'] iprint = self.options['iprint'] for voi, rhs in iteritems(rhs_mat): ksp = self.ksp[voi] ksp.setTolerances(max_it=maxiter, atol=atol, rtol=rtol) sol_vec = np.zeros(rhs.shape) # Set these in the system if trace: # pragma: no cover debug("creating sol_buf petsc vec for voi", voi) self.sol_buf_petsc = PETSc.Vec().createWithArray(sol_vec, comm=system.comm) if trace: # pragma: no cover debug("sol_buf creation DONE") debug("creating rhs_buf petsc vec for voi", voi) self.rhs_buf_petsc = PETSc.Vec().createWithArray(rhs, comm=system.comm) if trace: debug("rhs_buf creation DONE") # Petsc can only handle one right-hand-side at a time for now self.voi = voi self.system = system self.iter_count = 0 ksp.solve(self.rhs_buf_petsc, self.sol_buf_petsc) self.system = None # Final residual print if you only want the last one if iprint == 1: mon = ksp.getMonitor()[0][0] self.print_norm(self.print_name, system, self.iter_count, mon._norm, mon._norm0, indent=1, solver='LN') if self.iter_count >= maxiter: msg = 'FAILED to converge in %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, 0, 0, msg=msg, indent=1, solver='LN') unknowns_mat[voi] = sol_vec if fail and self.options['err_on_maxiter']: raise AnalysisError("Solve in '%s': PetscKSP %s" % (system.pathname, msg)) #print system.name, 'Linear solution vec', d_unknowns self.system = None return unknowns_mat
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, 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, rhs_mat, system, mode): """ Solves the linear system for the problem in self.system. The full solution vector is returned. Args ---- rhs_mat : dict of ndarray Dictionary containing one ndarry per top level quantity of interest. Each array contains the right-hand side for the linear solve. system : `System` Parent `System` object. mode : string Derivative mode, can be 'fwd' or 'rev'. Returns ------- dict of ndarray : Solution vectors """ options = self.options self.mode = mode iprint = self.options['iprint'] unknowns_mat = OrderedDict() for voi, rhs in iteritems(rhs_mat): # Scipy can only handle one right-hand-side at a time. self.voi = voi n_edge = len(rhs) A = LinearOperator((n_edge, n_edge), matvec=self.mult, dtype=float) # Support a preconditioner if self.preconditioner: M = LinearOperator((n_edge, n_edge), matvec=self._precon, dtype=float) else: M = None # Call GMRES to solve the linear system self.system = system self.iter_count = 0 d_unknowns, info = gmres(A, rhs, M=M, tol=options['atol'], maxiter=options['maxiter'], restart=options['restart'], callback=self.monitor) self.system = None # Final residual print if you only want the last one if iprint == 1: self.print_norm(self.print_name, system, self.iter_count, self._norm, self._norm0, indent=1, solver='LN') if info > 0: msg = "Solve in '%s': ScipyGMRES failed to converge " \ "after %d iterations" % (system.pathname, self.iter_count) #logger.error(msg, system.name, info) if self.options['err_on_maxiter']: raise AnalysisError(msg) print(msg) msg = 'FAILED to converge after max iterations' failed = True elif info < 0: msg = "ERROR in solve in '{}': gmres failed with code {}" raise RuntimeError(msg.format(system.pathname, info)) else: msg = 'Converged in %d iterations' % self.iter_count failed = False if iprint > 0 or (failed and iprint > -1): self.print_norm(self.print_name, system, self.iter_count, 0, 0, msg=msg, indent=1, solver='LN') unknowns_mat[voi] = d_unknowns #print(system.name, 'Linear solution vec', d_unknowns) return unknowns_mat
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, 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 solve(self, rhs_mat, system, mode): """ Solves the linear system for the problem in self.system. The full solution vector is returned. Args ---- rhs_mat : dict of ndarray Dictionary containing one ndarry per top level quantity of interest. Each array contains the right-hand side for the linear solve. system : `System` Parent `System` object. mode : string Derivative mode, can be 'fwd' or 'rev'. Returns ------- dict of ndarray : Solution vectors """ dumat = system.dumat drmat = system.drmat dpmat = system.dpmat gs_outputs = system._get_gs_outputs(mode, self._vois) relevance = system._probdata.relevance fwd = mode == 'fwd' system.clear_dparams() for voi in rhs_mat: dumat[voi].vec[:] = 0.0 vois = rhs_mat.keys() # John starts with the following. It is not necessary, but # uncommenting it helps to debug when comparing print outputs to his. # for voi in vois: # drmat[voi].vec[:] = -rhs_mat[voi] sol_buf = OrderedDict() f_norm0, f_norm = 1.0, 1.0 self.iter_count = 0 maxiter = self.options['maxiter'] while self.iter_count < maxiter and f_norm > self.options['atol'] \ and f_norm/f_norm0 > self.options['rtol']: if fwd: for sub in itervalues(system._subsystems): for voi in vois: #print('pre scatter', sub.pathname, 'dp', dpmat[voi].vec, # 'du', dumat[voi].vec, 'dr', drmat[voi].vec) system._transfer_data(sub.name, deriv=True, var_of_interest=voi) #print('pre apply', sub.pathname, 'dp', dpmat[voi].vec, # 'du', dumat[voi].vec, 'dr', drmat[voi].vec) # we need to loop over all subsystems in order to make # the necessary collective calls to scatter, but only # active subsystems do anything else if not sub.is_active(): continue # print(sub.name, sorted(gs_outputs['fwd'][sub.name][None])) # Groups and all other systems just call their own # apply_linear. sub._sys_apply_linear( mode, system._do_apply, vois=vois, gs_outputs=gs_outputs['fwd'][sub.name]) # for voi in vois: # print('post apply', dpmat[voi].vec, dumat[voi].vec, drmat[voi].vec) for voi in vois: drmat[voi].vec *= -1.0 drmat[voi].vec += rhs_mat[voi] dpmat[voi].vec[:] = 0.0 with sub._dircontext: sub.solve_linear(sub.dumat, sub.drmat, vois, mode=mode) # for voi in vois: # print('post solve', dpmat[voi].vec, dumat[voi].vec, drmat[voi].vec) for voi in vois: sol_buf[voi] = dumat[voi].vec else: for sub in reversed(list(itervalues(system._subsystems))): active = sub.is_active() for voi in vois: if active: dumat[voi].vec *= 0.0 #print('pre scatter', sub.pathname, voi, dpmat[voi].vec, dumat[voi].vec, drmat[voi].vec) system._transfer_data(sub.name, mode='rev', deriv=True, var_of_interest=voi) #print('post scatter', sub.pathname, voi, dpmat[voi].vec, dumat[voi].vec, drmat[voi].vec) if active: dumat[voi].vec *= -1.0 dumat[voi].vec += rhs_mat[voi] # we need to loop over all subsystems in order to make # the necessary collective calls to scatter, but only # active subsystems do anything else if not active: continue with sub._dircontext: sub.solve_linear(sub.dumat, sub.drmat, vois, mode=mode) #for voi in vois: #print('post solve', dpmat[voi].vec, dumat[voi].vec, drmat[voi].vec) #print(sub.name, sorted(gs_outputs['rev'][sub.name][None])) # Groups and all other systems just call their own # apply_linear. sub._sys_apply_linear( mode, system._do_apply, vois=vois, gs_outputs=gs_outputs['rev'][sub.name]) #for voi in vois: #print('post apply', system.dpmat[voi].vec, dumat[voi].vec, drmat[voi].vec) for voi in vois: sol_buf[voi] = drmat[voi].vec self.iter_count += 1 if maxiter == 1: f_norm = 0.0 else: f_norm = self._norm(system, mode, rhs_mat) if self.options['iprint'] > 0: self.print_norm(self.print_name, system.pathname, self.iter_count, f_norm, f_norm0, indent=1, solver='LN') if maxiter > 1 and self.iter_count >= maxiter: msg = 'FAILED to converge after %d iterations' % self.iter_count failed = True else: failed = False if self.options['iprint'] > 0: if not failed: msg = 'converged' self.print_norm(self.print_name, system.pathname, self.iter_count, f_norm, f_norm0, indent=1, solver='LN', msg=msg) if failed and self.options['err_on_maxiter']: raise AnalysisError("Solve in '%s': LinearGaussSeidel %s" % (system.pathname, msg)) return sol_buf
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 with system._dircontext: system.solve_linear(system.dumat, system.drmat, [None], mode='fwd', solver=self.ln_solver) # 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.iter_count >= maxiter or isnan(f_norm): msg = 'FAILED to converge after %d iterations' % self.iter_count fail = True else: fail = False if self.options['iprint'] > 0: if not fail: msg = 'converged' self.print_norm(self.print_name, system.pathname, 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.iter_count >= maxiter or isnan(normval): msg = 'FAILED to converge after %d iterations' % self.iter_count fail = True else: fail = False if self.options['iprint'] > 0: if not fail: msg = 'converged' self.print_norm(self.print_name, system.pathname, 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))