Source code for openmdao.solvers.ln_direct

""" OpenMDAO LinearSolver that explicitly solves the linear system using
linalg.solve or scipy LU factor/solve. Inherits from MultLinearSolver just
for the mult function."""

from collections import OrderedDict

import numpy as np
from scipy.linalg import lu_factor, lu_solve

from openmdao.solvers.solver_base import MultLinearSolver


[docs]class DirectSolver(MultLinearSolver): """ OpenMDAO LinearSolver that explicitly solves the linear system using linalg.solve. The user can choose to have the jacobian assembled directly or through matrix-vector product. Options ------- options['iprint'] : int(0) Set to 0 to print only failures, set to 1 to print iteration totals to stdout, set to 2 to print the residual each iteration to stdout, or -1 to suppress all printing. options['mode'] : str('auto') Derivative calculation mode, set to 'fwd' for forward mode, 'rev' for reverse mode, or 'auto' to let OpenMDAO determine the best mode. options['jacobian_method'] : str('MVP') Method to assemble the jacobian to solve. Select 'MVP' to build the Jacobian by calling apply_linear with columns of identity. Select 'assemble' to build the Jacobian by taking the calculated Jacobians in each component and placing them directly into a clean identity matrix. options['solve_method'] : str('LU') Solution method, either 'solve' for linalg.solve, or 'LU' for linalg.lu_factor and linalg.lu_solve. """ def __init__(self): super(DirectSolver, self).__init__() self.options.remove_option("err_on_maxiter") self.options.add_option('mode', 'auto', values=['fwd', 'rev', 'auto'], desc="Derivative calculation mode, set to 'fwd' for " + "forward mode, 'rev' for reverse mode, or 'auto' to " + "let OpenMDAO determine the best mode.", lock_on_setup=True) self.options.add_option('jacobian_method', 'MVP', values=['MVP', 'assemble'], desc="Method to assemble the jacobian to solve. " + "Select 'MVP' to build the Jacobian by calling " + "apply_linear with columns of identity. Select " + "'assemble' to build the Jacobian by taking the " + "calculated Jacobians in each component and placing " + "them directly into a clean identity matrix.") self.options.add_option('solve_method', 'LU', values=['LU', 'solve'], desc="Solution method, either 'solve' for linalg.solve, " + "or 'LU' for linalg.lu_factor and linalg.lu_solve.") self.jacobian = None self.lup = None self.mode = None
[docs] def setup(self, system): """ Initialization. Allocate Jacobian and set up some helpers. Args ---- system: `System` System that owns this solver. """ # Only need to setup if we are assembling the whole jacobian if self.options['jacobian_method'] == 'MVP': return # Note, we solve a slightly modified version of the unified # derivatives equations in OpenMDAO. # (dR/du) * (du/dr) = -I u_vec = system.unknowns self.jacobian = -np.eye(u_vec.vec.size) # Clear the index cache system._icache = {}
[docs] 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 """ self.system = system if self.mode is None: self.mode = mode sol_buf = OrderedDict() for voi, rhs in rhs_mat.items(): self.voi = None if system._jacobian_changed: method = self.options['jacobian_method'] # Must clear the jacobian if we switch modes if method == 'assemble' and self.mode != mode: self.setup(system) self.mode = mode self.jacobian, _ = system.assemble_jacobian(mode=mode, method=method, mult=self.mult) system._jacobian_changed = False if self.options['solve_method'] == 'LU': self.lup = lu_factor(self.jacobian) if self.options['solve_method'] == 'LU': deriv = lu_solve(self.lup, rhs) else: deriv = np.linalg.solve(self.jacobian, rhs) self.system = None sol_buf[voi] = deriv return sol_buf