Package com.opengamma.analytics.financial.model.finitedifference

Source Code of com.opengamma.analytics.financial.model.finitedifference.ThetaMethodFiniteDifference$ExtendedSolverImpl

/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.finitedifference;

import static com.opengamma.analytics.math.linearalgebra.TridiagonalSolver.solvTriDag;

import java.util.Arrays;

import org.apache.commons.lang.NotImplementedException;

import com.opengamma.analytics.math.MathException;
import com.opengamma.analytics.math.linearalgebra.Decomposition;
import com.opengamma.analytics.math.linearalgebra.DecompositionResult;
import com.opengamma.analytics.math.linearalgebra.LUDecompositionCommons;
import com.opengamma.analytics.math.linearalgebra.TridiagonalMatrix;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.math.surface.Surface;
import com.opengamma.util.ArgumentChecker;

/**
* A theta (i.e. weighted between explicit and implicit time stepping) scheme using SOR algorithm to solve the matrix system at each time step
* This uses the exponentially fitted scheme of duffy
*/
public class ThetaMethodFiniteDifference implements ConvectionDiffusionPDESolver {
  private static final Decomposition<?> DCOMP = new LUDecompositionCommons();
  // private static final DEFAULT
  private final double _theta;
  private final boolean _showFullResults;

  /**
   * Sets up a standard Crank-Nicolson scheme
   */
  public ThetaMethodFiniteDifference() {
    _theta = 0.5;
    _showFullResults = false;
  }

  /**
   * Sets up a scheme that is the weighted average of an explicit and an implicit scheme
   * @param theta The weight. theta = 0 - fully explicit, theta = 0.5 - Crank-Nicolson, theta = 1.0 - fully implicit
   * @param showFullResults Show the full results
   */
  public ThetaMethodFiniteDifference(final double theta, final boolean showFullResults) {
    ArgumentChecker.isTrue(theta >= 0 && theta <= 1.0, "theta must be in the range 0 to 1");
    _theta = theta;
    _showFullResults = showFullResults;
  }

  public double getTheta() {
    return _theta;
  }

  @Override
  //TODO This is so ugly
  public PDEResults1D solve(final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> pdeData) {
    ArgumentChecker.notNull(pdeData, "pde data");
    final ConvectionDiffusionPDE1DCoefficients coeff = pdeData.getCoefficients();
    if (coeff instanceof ConvectionDiffusionPDE1DStandardCoefficients) {
      final PDE1DDataBundle<ConvectionDiffusionPDE1DStandardCoefficients> temp = convertPDE1DDataBundle(pdeData);
      final SolverImpl solver = new SolverImpl(temp);
      return solver.solve();
    } else if (coeff instanceof ConvectionDiffusionPDE1DFullCoefficients) {
      final ConvectionDiffusionPDE1DFullCoefficients temp = (ConvectionDiffusionPDE1DFullCoefficients) coeff;
      final ExtendedSolverImpl solver = new ExtendedSolverImpl(temp, pdeData.getInitialCondition(), pdeData.getLowerBoundary(), pdeData.getUpperBoundary(),
          pdeData.getFreeBoundary(), pdeData.getGrid());
      return solver.solve();
    }
    throw new IllegalArgumentException(coeff.getClass() + " not handled");
  }

  private static PDE1DDataBundle<ConvectionDiffusionPDE1DStandardCoefficients> convertPDE1DDataBundle(final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> pdeData) {
    if (pdeData.getFreeBoundary() == null) {
      return new PDE1DDataBundle<>(
          (ConvectionDiffusionPDE1DStandardCoefficients) pdeData.getCoefficients(), pdeData.getInitialCondition(), pdeData.getLowerBoundary(),
          pdeData.getUpperBoundary(), pdeData.getGrid());
    }
    return new PDE1DDataBundle<>(
        (ConvectionDiffusionPDE1DStandardCoefficients) pdeData.getCoefficients(), pdeData.getInitialCondition(), pdeData.getLowerBoundary(),
        pdeData.getUpperBoundary(), pdeData.getFreeBoundary(), pdeData.getGrid());
  }

  private enum SolverMode {
    tridiagonal,
    luDecomp,
    psor;
  }

  class SolverImpl {

    // grid
    private final int _nNodesX;
    private final int _nNodesT;
    private final double[] _dt;
    private final PDEGrid1D _grid;
    private final double[][] _x1st;
    private final double[][] _x2nd;
    private final double[] _dx;
    //initial and boundary conditions
    private final double[] _initial;
    private final BoundaryCondition _lower;
    private final BoundaryCondition _upper;
    //PDE coefficients
    private final ConvectionDiffusionPDE1DStandardCoefficients _coeff;
    //free boundary problems
    private final SolverMode _mode;
    private final Surface<Double, Double, Double> _freeB;

    public SolverImpl(final PDE1DDataBundle<ConvectionDiffusionPDE1DStandardCoefficients> pdeData) {

      //unpack pdeData
      _grid = pdeData.getGrid();
      _coeff = pdeData.getCoefficients();
      _lower = pdeData.getLowerBoundary();
      _upper = pdeData.getUpperBoundary();

      _nNodesX = _grid.getNumSpaceNodes();
      _nNodesT = _grid.getNumTimeNodes();

      _x1st = new double[_nNodesX - 2][];
      _x2nd = new double[_nNodesX - 2][];
      for (int ii = 0; ii < _nNodesX - 2; ii++) {
        _x1st[ii] = _grid.getFirstDerivativeCoefficients(ii + 1);
        _x2nd[ii] = _grid.getSecondDerivativeCoefficients(ii + 1);
      }
      _dx = new double[_nNodesX - 1];
      for (int ii = 0; ii < _nNodesX - 1; ii++) {
        _dx[ii] = _grid.getSpaceStep(ii);
      }

      _initial = pdeData.getInitialCondition();
      _dt = new double[_nNodesT - 1];
      for (int jj = 0; jj < _nNodesT - 1; jj++) {
        _dt[jj] = _grid.getTimeStep(jj);
      }

      //free boundary
      _freeB = pdeData.getFreeBoundary();
      if (_freeB == null) {
        _mode = SolverMode.tridiagonal;
      } else {
        _mode = SolverMode.psor;
      }
    }

    @SuppressWarnings({"synthetic-access" })
    public PDEResults1D solve() {

      double[][] full = null;
      if (_showFullResults) {
        full = new double[_nNodesT][_nNodesX];
        full[0] = _initial;
      }
      double[] h = _initial;

      double t = _grid.getTimeNode(0);

      double[] topRow = _lower.getLeftMatrixCondition(_coeff, _grid, t);
      double[] bottomRow = _upper.getLeftMatrixCondition(_coeff, _grid, t);
      final double[] cDag = new double[_nNodesX - 2];
      final double[] lDag = new double[_nNodesX - 2];
      final double[] uDag = new double[_nNodesX - 2];
      for (int ii = 0; ii < _nNodesX - 2; ii++) { //tri-diagonal form
        final double x = _grid.getSpaceNode(ii + 1);
        final double a = _coeff.getA(t, x);
        final double b = _coeff.getB(t, x);
        final double c = _coeff.getC(t, x);
        cDag[ii] = _x2nd[ii][1] * a + _x1st[ii][1] * b + c;
        lDag[ii] = _x2nd[ii][0] * a + _x1st[ii][0] * b;
        uDag[ii] = _x2nd[ii][2] * a + _x1st[ii][2] * b;
      }

      for (int jj = 0; jj < _nNodesT - 1; jj++) {
        final double dt = _dt[jj];

        //RHS of system
        final double[] y = new double[_nNodesX];
        //main part of RHS
        for (int ii = 1; ii < _nNodesX - 1; ii++) { //tri-diagonal form
          y[ii] = (1 - (1 - _theta) * dt * cDag[ii - 1]) * h[ii] - (1 - _theta) * dt * (lDag[ii - 1] * h[ii - 1] + +uDag[ii - 1] * h[ii + 1]);
        }

        t = _grid.getTimeNode(jj + 1);
        //lower & upper boundaries
        y[0] = _lower.getConstant(_coeff, t);
        y[_nNodesX - 1] = _upper.getConstant(_coeff, t);

        //put the LHS of system in tri-diagonal form
        final double[] d = new double[_nNodesX]; //main diag
        final double[] u = new double[_nNodesX - 1]; //upper
        final double[] l = new double[_nNodesX - 1]; //lower
        //lower boundary conditions
        topRow = _lower.getLeftMatrixCondition(_coeff, _grid, t);
        final int p2 = topRow.length;
        d[0] = topRow[0];
        if (p2 > 1) {
          u[0] = topRow[1];
          //Review do we need this?
          ArgumentChecker.isFalse(p2 > 2, "Boundary condition means that system is not tri-diagonal");
        }
        bottomRow = _upper.getLeftMatrixCondition(_coeff, _grid, t);
        final int q2 = bottomRow.length;
        d[_nNodesX - 1] = bottomRow[q2 - 1];
        if (q2 > 1) {
          l[_nNodesX - 2] = bottomRow[q2 - 2];
          ArgumentChecker.isFalse(q2 > 2, "Boundary condition means that system is not tri-diagonal");
        }

        for (int ii = 0; ii < _nNodesX - 2; ii++) { //tri-diagonal form
          final double x = _grid.getSpaceNode(ii + 1);
          final double a = _coeff.getA(t, x);
          final double b = _coeff.getB(t, x);
          final double c = _coeff.getC(t, x);
          //debug - fitting par
          //a = getFittingParameter(a, b, ii);
          cDag[ii] = _x2nd[ii][1] * a + _x1st[ii][1] * b + c;
          lDag[ii] = _x2nd[ii][0] * a + _x1st[ii][0] * b;
          uDag[ii] = _x2nd[ii][2] * a + _x1st[ii][2] * b;
        }


        for (int ii = 1; ii < _nNodesX - 1; ii++) {
          d[ii] = 1 + _theta * dt * cDag[ii - 1];
          u[ii] = _theta * dt * uDag[ii - 1];
          l[ii - 1] = _theta * dt * lDag[ii - 1];
        }
        final TridiagonalMatrix lhs = new TridiagonalMatrix(d, u, l);

        //solve the system (update h)
        switch (_mode) {
          case tridiagonal:
            h = solvTriDag(lhs, y);
            break;
          case luDecomp:
            h = solveLU(lhs, y);
            break;
          case psor:
            h = solvTriDag(lhs, y);
            final double[] free = new double[_nNodesX];
            for (int ii = 0; ii < _nNodesX; ii++) {
              final double x = _grid.getSpaceNode(ii);
              free[ii] = _freeB.getZValue(t, x);
            }
            h = solvePSOR(lhs, y, h, free);
            break;
          default:
            throw new NotImplementedException("SolverMode " + _mode.toString() + " not implemented");
        }

        if (_showFullResults && full != null) {
          full[jj + 1] = Arrays.copyOf(h, _nNodesX);
        }
      }
      PDEResults1D res;
      if (_showFullResults) {
        res = new PDEFullResults1D(_grid, full);
      } else {
        res = new PDETerminalResults1D(_grid, h);
      }
      return res;
    }

    @SuppressWarnings("synthetic-access")
    private double[] solveLU(final TridiagonalMatrix lM, final double[] y) {
      final DecompositionResult res = DCOMP.evaluate(lM.toDoubleMatrix2D());
      return res.solve(y);
    }

    private double[] solvePSOR(final TridiagonalMatrix lM, final double[] b, final double[] x, final double[] minVal) {

      final double[] d = lM.getDiagonalData();
      final double[] u = lM.getUpperSubDiagonalData();
      final double[] l = lM.getLowerSubDiagonalData();

      final int maxInt = 100000;
      final double omega = 1.0;
      final double[] invD = new double[_nNodesX];
      for (int ii = 0; ii < _nNodesX; ii++) {
        if (d[ii] == 0.0) {
          throw new MathException("Cannot solve by PSOR - zero on diagonal");
        }
        invD[ii] = 1.0 / d[ii];
      }

      final int n = b.length;
      double maxErr = 1.0;
      int count = 0;
      double temp;

      for (int ii = 0; ii < n; ii++) {
        x[ii] = Math.max(minVal[ii], x[ii]);
      }
      final double small = 1e-15;

      while (count < maxInt && maxErr > 1e-8) {
        temp = Math.max(minVal[0], (1 - omega) * x[0] + omega * invD[0] * (b[0] - u[0] * x[1]));
        // errSqr = (temp - x[0]) * (temp - x[0]);
        maxErr = Math.abs(temp - x[0]) / (Math.abs(x[0]) + small);
        x[0] = temp;
        for (int ii = 1; ii < n - 1; ii++) {
          temp = Math.max(minVal[ii], (1 - omega) * x[ii] + omega * invD[ii] * (b[ii] - l[ii - 1] * x[ii - 1] - u[ii] * x[ii + 1]));
          // errSqr += (temp - x[ii]) * (temp - x[ii]);
          maxErr = Math.max(Math.abs(temp - x[ii]) / (Math.abs(x[ii]) + small), maxErr);
          x[ii] = temp;
        }
        temp = Math.max(minVal[n - 1], (1 - omega) * x[n - 1] + omega * invD[n - 1] * (b[n - 1] - l[n - 2] * x[n - 2]));
        maxErr = Math.max(Math.abs(temp - x[n - 1]) / (Math.abs(x[n - 1]) + small), maxErr);
        //errSqr += (temp - x[n - 1]) * (temp - x[n - 1]);
        x[n - 1] = temp;

        //   errSqr = Math.sqrt(errSqr);
        count++;
      }

      if (count == maxInt) {
        throw new MathException("PSOR failed to converge");
      }
      return x;
    }

    /**
     * This is modified from the Exponential Fitting of Duffy. We find no effect on the accuracy from using this adjustment, but leave it as a stub for further investigation
     * @param a The diffusion coefficient
     * @param b The convection coefficient
     * @param i The index of the (internal) space node
     * @return The adjusted diffusion coefficient (rho)
     */
    @SuppressWarnings("unused")
    private double getFittingParameter(final double a, final double b, final int i) {
      double rho;
      if (a == 0 && b == 0) {
        return 0.0;
      }

      final double[] x1st = _x1st[i];
      final double[] x2nd = _x2nd[i];
      final double bdx1 = (b * _dx[i]);
      final double bdx2 = (b * _dx[i + 1]);

      // convection dominated
      if (Math.abs(bdx1) > 10 * Math.abs(a) || Math.abs(bdx2) > 10 * Math.abs(a)) {
        // a > 0 is unphysical as it corresponds to a negative diffusion
        final double sign = (a > 0.0 ? -1.0 : 1.0);
        if (b > 0) {
          rho = sign * b * x1st[0] / x2nd[0];
        } else {
          rho = -sign * b * x1st[2] / x2nd[2];
        }
      } else if (Math.abs(a) > 10 * Math.abs(bdx1) || Math.abs(a) > 10 * Math.abs(bdx2)) {
        rho = a; // diffusion dominated
      } else {
        final double expo1 = Math.exp(bdx1 / a);
        final double expo2 = Math.exp(-bdx2 / a);
        rho = -b * (x1st[0] * expo1 + x1st[1] + x1st[2] * expo2) / (x2nd[0] * expo1 + x2nd[1] + x2nd[2] * expo2);
      }
      return rho;
    }
  }

  /**
   * @deprecated Use SolverImpl
   */
  @Deprecated
  class SolverImplDeprecated {
    // private final ConvectionDiffusionPDEDataBundle _pdeData;
    private final ConvectionDiffusionPDE1DStandardCoefficients _coefficients;
    private final double[] _initialCondition;
    private final PDEGrid1D _grid;
    private final BoundaryCondition _lowerBoundary;
    private final BoundaryCondition _upperBoundary;
    private final Surface<Double, Double, Double> _freeBoundary;
    private double[] _f;
    private double[][] _full;

    private final double[] _q;
    private final double[][] _m;

    private final double[] _rho;
    private final double[] _a;
    private final double[] _b;
    private final double[] _c;

    private double _t1;
    private double _t2;

    @SuppressWarnings("synthetic-access")
    public SolverImplDeprecated(final ConvectionDiffusionPDE1DStandardCoefficients coeff, final double[] initialCondition, final BoundaryCondition lowerBoundary,
        final BoundaryCondition upperBoundary, final Surface<Double, Double, Double> freeBoundary, final PDEGrid1D grid) {
      _coefficients = coeff;
      _initialCondition = initialCondition;
      _lowerBoundary = lowerBoundary;
      _upperBoundary = upperBoundary;
      _freeBoundary = freeBoundary;
      _grid = grid;

      final int tNodes = _grid.getNumTimeNodes();
      final int xNodes = _grid.getNumSpaceNodes();

      _f = new double[xNodes];
      if (_showFullResults) {
        _full = new double[tNodes][xNodes];
      }

      _q = new double[xNodes];
      _m = new double[xNodes][xNodes];
      _rho = new double[xNodes - 2];
      _a = new double[xNodes - 2];
      _b = new double[xNodes - 2];
      _c = new double[xNodes - 2];
    }

    @SuppressWarnings("synthetic-access")
    public PDEResults1D solve() {

      initialise();

      for (int n = 1; n < getGrid().getNumTimeNodes(); n++) {

        setT2(getGrid().getTimeNode(n));
        updateRHSVector();
        updateRHSBoundary();
        updateCoefficents();
        updateLHSMatrix();
        updateLHSBoundary();
        solveMatrixSystem();
        if (_showFullResults) {
          _full[n] = Arrays.copyOf(_f, _f.length);
        }
        setT1(getT2());
      }

      PDEResults1D res;
      if (_showFullResults) {
        res = new PDEFullResults1D(getGrid(), _full);
      } else {
        res = new PDETerminalResults1D(getGrid(), _f);
      }
      return res;

    }

    @SuppressWarnings("synthetic-access")
    void initialise() {
      final double t0 = getGrid().getTimeNode(0);
      setT1(t0);
      _f = Arrays.copyOf(_initialCondition, getGrid().getNumSpaceNodes());
      if (_showFullResults) {
        _full[0] = _initialCondition;
      }

      double x;
      for (int i = 0; i < getGrid().getNumSpaceNodes() - 2; i++) {
        x = getGrid().getSpaceNode(i + 1);
        setA(i, _coefficients.getA(t0, x));
        setB(i, _coefficients.getB(t0, x));
        setC(i, _coefficients.getC(t0, x));
        setRho(i, getFittingParameter(getGrid(), getA(i), getB(i), i + 1));
      }
    }

    @SuppressWarnings("synthetic-access")
    void updateRHSVector() {
      final double dt = getT2() - getT1();
      double[] x1st, x2nd;
      double temp;
      for (int i = 1; i < getGrid().getNumSpaceNodes() - 1; i++) {
        x1st = getGrid().getFirstDerivativeCoefficients(i);
        x2nd = getGrid().getSecondDerivativeCoefficients(i);

        temp = getF(i);
        temp -= (1 - _theta) * dt * (x2nd[0] * getRho(i - 1) + x1st[0] * getB(i - 1)) * _f[i - 1];
        temp -= (1 - _theta) * dt * (x2nd[1] * getRho(i - 1) + x1st[1] * getB(i - 1) + getC(i - 1)) * _f[i];
        temp -= (1 - _theta) * dt * (x2nd[2] * getRho(i - 1) + x1st[2] * getB(i - 1)) * _f[i + 1];
        setQ(i, temp);
      }
    }

    /**
     *
     */
    void updateRHSBoundary() {
      double[] temp = _lowerBoundary.getRightMatrixCondition(_coefficients, getGrid(), getT1());
      double sum = 0;
      for (int k = 0; k < temp.length; k++) {
        sum += temp[k] * getF(k);
      }
      setQ(0, sum + _lowerBoundary.getConstant(_coefficients, getT2()));

      temp = _upperBoundary.getRightMatrixCondition(_coefficients, getGrid(), getT1());
      sum = 0;
      for (int k = 0; k < temp.length; k++) {
        sum += temp[k] * getF(getGrid().getNumSpaceNodes() - 1 - k);
      }

      setQ(getGrid().getNumSpaceNodes() - 1, sum + _upperBoundary.getConstant(_coefficients, getT2()));
    }

    @SuppressWarnings("synthetic-access")
    void updateLHSMatrix() {
      final double dt = getT2() - getT1();
      double[] x1st, x2nd;
      for (int i = 1; i < getGrid().getNumSpaceNodes() - 1; i++) {
        x1st = getGrid().getFirstDerivativeCoefficients(i);
        x2nd = getGrid().getSecondDerivativeCoefficients(i);

        setM(i, i - 1, _theta * dt * (x2nd[0] * getRho(i - 1) + x1st[0] * getB(i - 1)));
        setM(i, i, 1 + _theta * dt * (x2nd[1] * getRho(i - 1) + x1st[1] * getB(i - 1) + getC(i - 1)));
        setM(i, i + 1, _theta * dt * (x2nd[2] * getRho(i - 1) + x1st[2] * getB(i - 1)));
      }
    }

    void updateLHSBoundary() {
      double[] temp = _lowerBoundary.getLeftMatrixCondition(_coefficients, getGrid(), getT2());
      for (int k = 0; k < temp.length; k++) {
        setM(0, k, temp[k]);
      }

      temp = _upperBoundary.getLeftMatrixCondition(_coefficients, getGrid(), getT2());
      for (int k = 0; k < temp.length; k++) {
        setM(getGrid().getNumSpaceNodes() - 1, getGrid().getNumSpaceNodes() - temp.length + k, temp[k]);
      }
    }

    void updateCoefficents() {
      double x;
      for (int i = 1; i < getGrid().getNumSpaceNodes() - 1; i++) {
        x = getGrid().getSpaceNode(i);
        setA(i - 1, _coefficients.getA(getT2(), x));
        setB(i - 1, _coefficients.getB(getT2(), x));
        setC(i - 1, _coefficients.getC(getT2(), x));
        //TODO R White 19/09/2012 change this back debug
        //setRho(i - 1, getA(i - 1));
        setRho(i - 1, getFittingParameter(getGrid(), getA(i - 1), getB(i - 1), i));
      }
    }

    private void solveMatrixSystem() {
      //   @SuppressWarnings("unused")
      //NOTE get this working again with dynamic omega
      //final int count = solveBySOR(omega);
      solveByLU();
      //      if (oldCount > 0) {
      //        if ((omegaIncrease && count > oldCount) || (!omegaIncrease && count < oldCount)) {
      //          omega = Math.max(1.0, omega * 0.9);
      //          omegaIncrease = false;
      //        } else {
      //          omega = 1.1 * omega;
      //          omegaIncrease = true;
      //        }
      //      }
      //      oldCount = count;

    }

    @SuppressWarnings({"synthetic-access" })
    private void solveByLU() {
      final DoubleMatrix2D temp = new DoubleMatrix2D(_m);
      final DecompositionResult res = DCOMP.evaluate(temp);
      final double[] f = res.solve(_q);
      for (int i = 0; i < f.length; i++) {
        _f[i] = f[i];
      }
    }

    @SuppressWarnings("unused")
    private int solveBySOR(final double omega) {

      double sum;
      int count = 0;
      double scale = 1.0;
      double errorSqr = Double.POSITIVE_INFINITY;
      while (errorSqr / (scale + 1e-10) > 1e-18) {
        errorSqr = 0.0;
        scale = 0.0;
        for (int j = 0; j < getGrid().getNumSpaceNodes(); j++) {
          sum = 0;
          for (int k = 0; k < getGrid().getNumSpaceNodes(); k++) {
            sum += getM(j, k) * getF(k);
          }
          double correction = omega / getM(j, j) * (getQ(j) - sum);
          if (_freeBoundary != null) {
            correction = Math.max(correction, _freeBoundary.getZValue(getT2(), getGrid().getSpaceNode(j)) - getF(j));
          }
          errorSqr += correction * correction;
          setF(j, getF(j) + correction); //TODO don't like this
          scale += getF(j) * getF(j);
        }
        count++;
      }
      return count;
    }

    /**
     * @param grid
     * @param a
     * @param b
     * @param i
     * @return
     */
    private double getFittingParameter(final PDEGrid1D grid, final double a, final double b, final int i) {
      double rho;
      if (a == 0 && b == 0) {
        return 0.0;
      }

      final double[] x1st = grid.getFirstDerivativeCoefficients(i);
      final double[] x2nd = grid.getSecondDerivativeCoefficients(i);
      final double bdx1 = (b * grid.getSpaceStep(i - 1));
      final double bdx2 = (b * grid.getSpaceStep(i));

      // convection dominated
      if (Math.abs(bdx1) > 10 * Math.abs(a) || Math.abs(bdx2) > 10 * Math.abs(a)) {
        // a > 0 is unphysical as it corresponds to a negative diffusion
        final double sign = (a > 0.0 ? -1.0 : 1.0);
        if (b > 0) {
          rho = sign * b * x1st[0] / x2nd[0];
        } else {
          rho = -sign * b * x1st[2] / x2nd[2];
        }
      } else if (Math.abs(a) > 10 * Math.abs(bdx1) || Math.abs(a) > 10 * Math.abs(bdx2)) {
        rho = a; // diffusion dominated
      } else {
        final double expo1 = Math.exp(bdx1 / a);
        final double expo2 = Math.exp(-bdx2 / a);
        rho = -b * (x1st[0] * expo1 + x1st[1] + x1st[2] * expo2) / (x2nd[0] * expo1 + x2nd[1] + x2nd[2] * expo2);
      }
      return rho;
    }

    /**
     * Gets the grid.
     * @return the grid
     */
    public PDEGrid1D getGrid() {
      return _grid;
    }

    public void setT1(final double t1) {
      _t1 = t1;
    }

    public double getT1() {
      return _t1;
    }

    public void setT2(final double t2) {
      _t2 = t2;
    }

    public double getT2() {
      return _t2;
    }

    public double getQ(final int i) {
      return _q[i];
    }

    public void setQ(final int i, final double value) {
      _q[i] = value;
    }

    public double getM(final int i, final int j) {
      return _m[i][j];
    }

    public void setM(final int i, final int j, final double value) {
      _m[i][j] = value;
    }

    public double getF(final int i) {
      return _f[i];
    }

    public void setF(final int i, final double value) {
      _f[i] = value;
    }

    public double getRho(final int i) {
      return _rho[i];
    }

    public void setRho(final int i, final double value) {
      _rho[i] = value;
    }

    public double getA(final int i) {
      return _a[i];
    }

    public void setA(final int i, final double value) {
      _a[i] = value;
    }

    public double getB(final int i) {
      return _b[i];
    }

    public void setB(final int i, final double value) {
      _b[i] = value;
    }

    public double getC(final int i) {
      return _c[i];
    }

    public void setC(final int i, final double value) {
      _c[i] = value;
    }

  }

  private class ExtendedSolverImpl extends SolverImplDeprecated {

    //private final ExtendedConvectionDiffusionPDEDataBundle _pdeData;
    private final ConvectionDiffusionPDE1DFullCoefficients _coeff;
    private final double[] _alpha;
    private final double[] _beta;

    public ExtendedSolverImpl(final ConvectionDiffusionPDE1DFullCoefficients coeff, final double[] initialCondition,
        final BoundaryCondition lowerBoundary, final BoundaryCondition upperBoundary, final Surface<Double, Double, Double> freeBoundary, final PDEGrid1D grid) {
      super(coeff.getStandardCoefficients(), initialCondition, lowerBoundary, upperBoundary, freeBoundary, grid);
      _coeff = coeff;
      final int xNodes = grid.getNumSpaceNodes();
      _alpha = new double[xNodes];
      _beta = new double[xNodes];
    }

    /**
     * @param pdeData
     * @param grid
     * @param lowerBoundary
     * @param upperBoundary
     * @param freeBoundary
     */
    //    public ExtendedSolverImpl(ExtendedConvectionDiffusionPDEDataBundle pdeData, PDEGrid1D grid, BoundaryCondition lowerBoundary, BoundaryCondition upperBoundary,
    //        Surface<Double, Double, Double> freeBoundary) {
    //      super(pdeData, grid, lowerBoundary, upperBoundary, freeBoundary);
    //      _pdeData = pdeData;
    //      final int xNodes = grid.getNumSpaceNodes();
    //      _alpha = new double[xNodes];
    //      _beta = new double[xNodes];
    //    }

    @Override
    void initialise() {
      super.initialise();
      double x;
      final double t = getT1();
      for (int i = 0; i < getGrid().getNumSpaceNodes(); i++) {
        x = getGrid().getSpaceNode(i);
        _alpha[i] = _coeff.getAlpha(t, x);
        _beta[i] = _coeff.getBeta(t, x);
      }
    }

    @Override
    void updateRHSVector() {
      final double dt = getT2() - getT1();
      double[] x1st, x2nd;
      double temp;
      for (int i = 1; i < getGrid().getNumSpaceNodes() - 1; i++) {
        x1st = getGrid().getFirstDerivativeCoefficients(i);
        x2nd = getGrid().getSecondDerivativeCoefficients(i);
        //TODO Work out a fitting scheme
        temp = getF(i);
        temp -= (1 - getTheta()) * dt * (x2nd[0] * getA(i - 1) * _alpha[i - 1] + x1st[0] * getB(i - 1) * _beta[i - 1]) * getF(i - 1);
        temp -= (1 - getTheta()) * dt * (x2nd[1] * getA(i - 1) * _alpha[i] + x1st[1] * getB(i - 1) * _beta[i] + getC(i - 1)) * getF(i);
        temp -= (1 - getTheta()) * dt * (x2nd[2] * getA(i - 1) * _alpha[i + 1] + x1st[2] * getB(i - 1) * _beta[i + 1]) * getF(i + 1);
        setQ(i, temp);
      }
    }

    @Override
    void updateLHSMatrix() {
      final double dt = getT2() - getT1();
      double[] x1st, x2nd;
      for (int i = 1; i < getGrid().getNumSpaceNodes() - 1; i++) {
        x1st = getGrid().getFirstDerivativeCoefficients(i);
        x2nd = getGrid().getSecondDerivativeCoefficients(i);

        setM(i, i - 1, getTheta() * dt * (x2nd[0] * getA(i - 1) * _alpha[i - 1] + x1st[0] * getB(i - 1) * _beta[i - 1]));
        setM(i, i, 1 + getTheta() * dt * (x2nd[1] * getA(i - 1) * _alpha[i] + x1st[1] * getB(i - 1) * _beta[i] + getC(i - 1)));
        setM(i, i + 1, getTheta() * dt * (x2nd[2] * getA(i - 1) * _alpha[i + 1] + x1st[2] * getB(i - 1) * _beta[i + 1]));
      }
    }

    @Override
    void updateCoefficents() {
      super.updateCoefficents();
      double x;
      for (int i = 0; i < getGrid().getNumSpaceNodes(); i++) {
        x = getGrid().getSpaceNode(i);
        _alpha[i] = _coeff.getAlpha(getT2(), x);
        _beta[i] = _coeff.getBeta(getT2(), x);
      }
    }

  }

  /**
   * @param omega omega
   * @param grid the grid
   * @param freeBoundary the free boundary
   * @param xNodes x nodes
   * @param f f
   * @param q q
   * @param m m
   * @param t2 t2
   * @return an int
   */
  protected int sor(final double omega, final PDEGrid1D grid, final Surface<Double, Double, Double> freeBoundary, final int xNodes, final double[] f, final double[] q, final double[][] m,
      final double t2) {
    double sum;
    int count = 0;
    double scale = 1.0;
    double errorSqr = Double.POSITIVE_INFINITY;
    while (errorSqr / (scale + 1e-10) > 1e-18) {
      errorSqr = 0.0;
      scale = 0.0;
      for (int j = 0; j < xNodes; j++) {
        sum = 0;
        for (int k = 0; k < xNodes; k++) {
          sum += m[j][k] * f[k];
        }
        double correction = omega / m[j][j] * (q[j] - sum);
        if (freeBoundary != null) {
          correction = Math.max(correction, freeBoundary.getZValue(t2, grid.getSpaceNode(j)) - f[j]);
        }
        errorSqr += correction * correction;
        f[j] += correction;
        scale += f[j] * f[j];
      }
      count++;
    }
    //debug
    return count;
  }

}
TOP

Related Classes of com.opengamma.analytics.financial.model.finitedifference.ThetaMethodFiniteDifference$ExtendedSolverImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.