1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
/*
* MLNLS.C - non-linear Newton solver for PML
* - courtesy of Jeff Painter
*
* Source Version: 2.0
* Software Release #92-0043
*
*/
#include "cpyright.h"
#include "pml.h"
static void
SC_DECLARE(_PM_settol,
(int neqns, REAL *tol, REAL *y, double atol, double rtol));
static double
SC_DECLARE(_PM_wvnorm, (int neqns, REAL *x, REAL *tol));
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/* PM_NEWTONDL - a simple nonlinear solver designed to be conveniently called
* - by Alpal-generated codes. This uses Newton's method to
* - solve f(Y)=0, with Y an array of length NEQNS. On input,
* - NEQNS and the maximum number of iterations MAXITER need to
* - be set along with ATOL and RTOL which are the absolute and
* - relative error tolerance parameters (good values are 1.0e-4
* - and 1.0e-3 respectively). Initial y values come in through Y
* - and the solution values are returned in Y.
* - One subroutine must be passed in through LINSOLV.
* - LINSOLV(x, y, iter) computes the Jacobian and the right-hand
* - side in the equation:
* -
* - J.dy = -f(y1) where J = (df/dy) at y1
* - y1 = y[iter-1]
* - dy = y[iter] - y1
* -
* - and returns the solution of the equation. Iter is the Newton
* - iteration index.
* -
* - Returns 0 iff we converged.
* - The convergence criterion is simple minded:
* -
* - Norm(dy)/(atol + rtol*abs(y2[i])) < 1
* -
* - where, dy is the correction to the solution found in
* - the last iteration.
*/
double PM_newtondl(neqns, y, dy, tol, maxiter, atol, rtol, linsolv, arg)
int neqns;
REAL *y, *dy, *tol;
int maxiter;
double atol, rtol;
PFVoid linsolv;
byte *arg;
{double err;
int i, n, iter;
iter = 0;
err = 2.0;
n = neqns*sizeof(double);
while ((err > 1.0) && (iter++ < maxiter))
/* initial iterate */
{memset(dy, 0, n);
/* solve J.dy = -f(y) */
linsolv(neqns, dy, y, iter, arg);
/* update y */
for (i = 0; i < neqns; i++)
y[i] += dy[i];
_PM_settol(neqns, tol, y, atol, rtol);
err = _PM_wvnorm(neqns, dy, tol);};
return((err <= 1.0) ? 0.0 : err);}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/* PM_NEWTONUL - a simple nonlinear solver designed to be conveniently called
* - by Alpal-generated codes. This uses Newton's method to
* - solve f(Y)=0, with Y an array of length NEQNS. On input,
* - NEQNS and the maximum number of iterations MAXITER need to
* - be set along with ATOL and RTOL which are the absolute and
* - relative error tolerance parameters (good values are 1.0e-4
* - and 1.0e-3 respectively). Initial y values come in through Y
* - and the solution values are returned in Y.
* - One subroutine must be passed in through LINSOLV.
* - LINSOLV(x, y, iter) computes the Jacobian and the right-hand
* - side in the equation:
* -
* - J.y2 = J.y1 - f(y1) where J = (df/dy) at y1
* - y1 = y[iter-1]
* - y2 = y[iter]
* -
* - and returns the solution of the equation. Iter is the Newton
* - iteration index.
* -
* - Returns 0 iff we converged.
* - The convergence criterion is simple minded:
* -
* - Norm(delta - y1[i])/(atol + rtol*abs(y2[i])) < 1
* -
* - where, delta - y1 is the correction to the solution found in
* - the last iteration.
*/
double PM_newtonul(neqns, y2, y1, tol, maxiter, atol, rtol, linsolv, arg)
int neqns;
REAL *y2, *y1, *tol;
int maxiter;
double atol, rtol;
PFVoid linsolv;
byte *arg;
{double err;
int i, iter, n;
iter = 0;
err = 2.0;
n = neqns*sizeof(double);
while ((err > 1.0) && (iter++ < maxiter))
/* initial iterate */
{memset(y1, 0, n);
/* solve J.y1 = -f(y2) */
linsolv(neqns, y1, y2, iter, arg);
/* update y2 and set y1 to the correction made in this iteration
* use tol for temporary storage to allow vectorization
*/
for (i = 0; i < neqns; i++)
tol[i] = y1[i] - y2[i];
for (i = 0; i < neqns; i++)
y2[i] = y1[i];
for (i = 0; i < neqns; i++)
y1[i] = tol[i];
_PM_settol(neqns, tol, y2, atol, rtol);
err = _PM_wvnorm(neqns, y1, tol);};
return((err <= 1.0) ? 0.0 : err);}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/* _PM_SETTOL - set the tolerances */
static void _PM_settol(neqns, tol, x, atol, rtol)
int neqns;
REAL *tol, *x;
double atol, rtol;
{int i;
for (i = 0; i < neqns; i++)
tol[i] = 1.0 / (atol + rtol*ABS(x[i]));
return;}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/* _PM_WVNORM - weighted vector norm */
static double _PM_wvnorm(neqns, x, tol)
int neqns;
REAL *x, *tol;
{int i;
double val, t;
val = 0.0;
for (i = 0; i < neqns; i++)
{t = tol[i]*x[i];
val += t*t;};
return(sqrt(val));}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
|