import numpy as np
from kona.user import UserSolver
[docs]class SpiralSolver(object):
[docs] def linearize(self, at_design, at_state=None):
self.X = at_design[0]
if at_state is not None:
self.U = at_state.data
else:
self.U = None
@property
def theta(self):
return 0.5*(self.X + np.pi)
@property
def alpha(self):
return 0.5*(self.X - np.pi)
@property
def dRdX(self):
dRdX1 = -np.sin(self.theta)*self.U[0] + np.cos(self.theta)*self.U[1] \
+ (self.X**2)*np.sin(self.alpha) - 4.0*self.X*np.cos(self.alpha)
dRdX2 = -np.cos(self.theta)*self.U[0] - np.sin(self.theta)*self.U[1] \
- (self.X**2)*np.cos(self.alpha) - 4.0*self.X*np.sin(self.alpha)
return 0.5*np.array([[dRdX1],[dRdX2]])
@property
def dRdU(self):
return np.array([[np.cos(self.theta), np.sin(self.theta)],
[-np.sin(self.theta), np.cos(self.theta)]])
@property
def rhs(self):
return np.array([(self.X**2)*np.cos(self.alpha),
(self.X**2)*np.sin(self.alpha)])
@property
def R(self):
return self.dRdU.dot(self.U) - self.rhs
@property
def F(self):
return 0.5*(self.X**2 + self.U[0]**2 + self.U[1]**2)
@property
def dFdX(self):
return np.array([self.X])
@property
def dFdU(self):
return np.array([self.U[0], self.U[1]])
[docs]class Spiral(UserSolver):
def __init__(self):
super(Spiral, self).__init__(
num_design=1,
num_state=2,
num_eq=0,
num_ineq=0)
self.PDE = SpiralSolver()
self.x_hist = []
self.u1_hist = []
self.u2_hist = []
self.obj_hist = []
self.grad_hist = []
[docs] def eval_obj(self, at_design, at_state):
self.PDE.linearize(at_design, at_state)
return self.PDE.F
[docs] def eval_residual(self, at_design, at_state, store_here):
self.PDE.linearize(at_design, at_state)
store_here.data[:] = self.PDE.R
[docs] def multiply_dRdX(self, at_design, at_state, in_vec, out_vec):
self.PDE.linearize(at_design, at_state)
out_vec.data[:] = self.PDE.dRdX.dot(in_vec)
[docs] def multiply_dRdU(self, at_design, at_state, in_vec, out_vec):
self.PDE.linearize(at_design, at_state)
out_vec.data[:] = self.PDE.dRdU.dot(in_vec.data)
[docs] def multiply_dRdX_T(self, at_design, at_state, in_vec):
self.PDE.linearize(at_design, at_state)
return self.PDE.dRdX.T.dot(in_vec.data)
[docs] def multiply_dRdU_T(self, at_design, at_state, in_vec, out_vec):
self.PDE.linearize(at_design, at_state)
out_vec.data[:] = self.PDE.dRdU.T.dot(in_vec.data)
[docs] def eval_dFdX(self, at_design, at_state):
self.PDE.linearize(at_design, at_state)
return self.PDE.dFdX
[docs] def eval_dFdU(self, at_design, at_state, store_here):
self.PDE.linearize(at_design, at_state)
store_here.data[:] = self.PDE.dFdU
[docs] def init_design(self):
return np.array([8.0*np.pi])
[docs] def solve_nonlinear(self, at_design, result):
# start with initial guess for solution and linearize
self.PDE.linearize(at_design)
self.PDE.U = np.zeros(2)
# calculate initial residual
rel_tol = 1e-8
norm0 = np.linalg.norm(self.PDE.R)
max_iter = 100
converged = False
# start Newton loop
for iters in xrange(max_iter):
# check convergence with new residual
if np.linalg.norm(self.PDE.R) <= rel_tol*norm0:
converged = True
break
# run linear solution and update state variables
self.PDE.U += np.linalg.solve(self.PDE.dRdU, -self.PDE.R)
# write result and return cost
result.data[:] = self.PDE.U
if converged:
return iters
else:
return -iters
[docs] def solve_linear(self, at_design, at_state, rhs_vec, rel_tol, result):
self.PDE.linearize(at_design, at_state)
result.data[:] = np.linalg.solve(self.PDE.dRdU, rhs_vec.data)
return 1
[docs] def solve_adjoint(self, at_design, at_state, rhs_vec, rel_tol, result):
self.PDE.linearize(at_design, at_state)
result.data[:] = np.linalg.solve(self.PDE.dRdU.T, rhs_vec.data)
return 1