com.opengamma.analytics.financial.model.finitedifference.CoupledFiniteDifference.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.model.finitedifference.CoupledFiniteDifference.java

Source

/**
 * 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 java.util.Arrays;

import org.apache.commons.lang.Validate;

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.matrix.DoubleMatrix2D;
import com.opengamma.analytics.math.surface.Surface;
import com.opengamma.util.ArgumentChecker;

/**
 * 
 */
public class CoupledFiniteDifference {
    private static final Decomposition<?> DCOMP = new LUDecompositionCommons();

    private final double _theta;
    private final boolean _showFullResults;

    /**
     * 
     */
    public CoupledFiniteDifference() {
        _theta = 0.5;
        _showFullResults = true;
    }

    public CoupledFiniteDifference(final double theta, final boolean showFullResults) {
        _theta = theta;
        _showFullResults = showFullResults;
    }

    public double getTheta() {
        return _theta;
    }

    public boolean showFullResults() {
        return false;
    }

    public Decomposition<?> getDecomposition() {
        return DCOMP;
    }

    public PDEResults1D[] solve(final CoupledPDEDataBundle pdeData1, final CoupledPDEDataBundle pdeData2) {
        Validate.notNull(pdeData1, "pde1 data");
        Validate.notNull(pdeData2, "pde2 data");

        final PDEGrid1D grid = pdeData1.getGrid();
        ArgumentChecker.isTrue(grid == pdeData2.getGrid(), "grids must be same object");
        final ConvectionDiffusionPDE1DCoupledCoefficients coeff1 = pdeData1.getCoefficients();
        final ConvectionDiffusionPDE1DCoupledCoefficients coeff2 = pdeData2.getCoefficients();
        final double[] initalCond1 = pdeData1.getInitialCondition();
        final double[] initalCond2 = pdeData2.getInitialCondition();
        final BoundaryCondition lower1 = pdeData1.getLowerBoundary();
        final BoundaryCondition lower2 = pdeData2.getLowerBoundary();
        final BoundaryCondition upper1 = pdeData1.getUpperBoundary();
        final BoundaryCondition upper2 = pdeData2.getUpperBoundary();
        final double lambda1 = coeff1.getLambda();
        final double lambda2 = coeff2.getLambda();

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

        double[] f = new double[2 * xNodes];
        double[][] full1 = null;
        double[][] full2 = null;
        if (_showFullResults) {
            full1 = new double[tNodes][xNodes];
            full2 = new double[tNodes][xNodes];
        }
        final double[] q = new double[2 * xNodes];
        final double[][] m = new double[2 * xNodes][2 * xNodes];

        double[][] a1 = new double[2][xNodes - 2];
        final double[][] a2 = new double[2][xNodes - 2];
        double[][] b1 = new double[2][xNodes - 2];
        final double[][] b2 = new double[2][xNodes - 2];
        double[][] c1 = new double[2][xNodes - 2];
        final double[][] c2 = new double[2][xNodes - 2];

        //    final double omega = 1.5;
        //    final int oldCount = 0;
        //    final boolean omegaIncrease = false;

        double dt, t1, t2, x;
        double[] x1st, x2nd;

        System.arraycopy(initalCond1, 0, f, 0, xNodes);
        System.arraycopy(initalCond2, 0, f, xNodes, xNodes);

        if (_showFullResults) {
            if (full1 != null && full2 != null) {
                full1[0] = Arrays.copyOfRange(f, 0, xNodes);
                full2[0] = Arrays.copyOfRange(f, xNodes, 2 * xNodes);
            }
        }

        for (int i = 0; i < xNodes - 2; i++) {
            x = grid.getSpaceNode(i + 1);
            a1[0][i] = coeff1.getA(0, x);
            b1[0][i] = coeff1.getB(0, x);
            c1[0][i] = coeff1.getC(0, x);

            a1[1][i] = coeff2.getA(0, x);
            b1[1][i] = coeff2.getB(0, x);
            c1[1][i] = coeff2.getC(0, x);
        }

        final boolean first = true;
        DecompositionResult decompRes = null;

        for (int n = 1; n < tNodes; n++) {

            t1 = grid.getTimeNode(n - 1);
            t2 = grid.getTimeNode(n);
            dt = grid.getTimeStep(n - 1);

            for (int i = 1; i < xNodes - 1; i++) {

                x = grid.getSpaceNode(i);
                x1st = grid.getFirstDerivativeCoefficients(i);
                x2nd = grid.getSecondDerivativeCoefficients(i);

                q[i] = f[i];
                q[i] -= (1 - _theta) * dt * (x2nd[0] * a1[0][i - 1] + x1st[0] * b1[0][i - 1]) * f[i - 1];
                q[i] -= (1 - _theta) * dt * (x2nd[1] * a1[0][i - 1] + x1st[1] * b1[0][i - 1] + c1[0][i - 1]) * f[i];
                q[i] -= (1 - _theta) * dt * (x2nd[2] * a1[0][i - 1] + x1st[2] * b1[0][i - 1]) * f[i + 1];
                q[i] -= (1 - _theta) * dt * lambda1 * f[i + xNodes];

                q[xNodes + i] = f[xNodes + i];
                q[xNodes + i] -= (1 - _theta) * dt * (x2nd[0] * a1[1][i - 1] + x1st[0] * b1[1][i - 1])
                        * f[xNodes + i - 1];
                q[xNodes + i] -= (1 - _theta) * dt
                        * (x2nd[1] * a1[1][i - 1] + x1st[1] * b1[1][i - 1] + c1[1][i - 1]) * f[xNodes + i];
                q[xNodes + i] -= (1 - _theta) * dt * (x2nd[2] * a1[1][i - 1] + x1st[2] * b1[1][i - 1])
                        * f[xNodes + i + 1];
                q[xNodes + i] -= (1 - _theta) * dt * lambda2 * f[i];

                a2[0][i - 1] = coeff1.getA(t2, x);
                b2[0][i - 1] = coeff1.getB(t2, x);
                c2[0][i - 1] = coeff1.getC(t2, x);

                a2[1][i - 1] = coeff2.getA(t2, x);
                b2[1][i - 1] = coeff2.getB(t2, x);
                c2[1][i - 1] = coeff2.getC(t2, x);

                m[i][i - 1] = _theta * dt * (x2nd[0] * a2[0][i - 1] + x1st[0] * b2[0][i - 1]);
                m[i][i] = 1 + _theta * dt * (x2nd[1] * a2[0][i - 1] + x1st[1] * b2[0][i - 1] + c2[0][i - 1]);
                m[i][i + 1] = _theta * dt * (x2nd[2] * a2[0][i - 1] + x1st[2] * b2[0][i - 1]);
                m[i][i + xNodes] = dt * _theta * lambda1;

                m[xNodes + i][xNodes + i - 1] = _theta * dt * (x2nd[0] * a2[1][i - 1] + x1st[0] * b2[1][i - 1]);
                m[xNodes + i][xNodes + i] = 1
                        + _theta * dt * (x2nd[1] * a2[1][i - 1] + x1st[1] * b2[1][i - 1] + c2[1][i - 1]);
                m[xNodes + i][xNodes + i + 1] = _theta * dt * (x2nd[2] * a2[1][i - 1] + x1st[2] * b2[1][i - 1]);
                m[xNodes + i][i] = dt * _theta * lambda2;
            }

            double[] temp = lower1.getLeftMatrixCondition(pdeData1.getCoefficients(), grid, t2);
            for (int k = 0; k < temp.length; k++) {
                m[0][k] = temp[k];
            }

            temp = upper1.getLeftMatrixCondition(pdeData1.getCoefficients(), grid, t2);
            for (int k = 0; k < temp.length; k++) {
                m[xNodes - 1][xNodes - temp.length + k] = temp[k];
            }

            temp = lower2.getLeftMatrixCondition(pdeData2.getCoefficients(), grid, t2);
            for (int k = 0; k < temp.length; k++) {
                m[xNodes][xNodes + k] = temp[k];
            }

            temp = upper2.getLeftMatrixCondition(pdeData2.getCoefficients(), grid, t2);
            for (int k = 0; k < temp.length; k++) {
                m[2 * xNodes - 1][2 * xNodes - temp.length + k] = temp[k];
            }

            temp = lower1.getRightMatrixCondition(pdeData1.getCoefficients(), grid, t1);
            double sum = 0;
            for (int k = 0; k < temp.length; k++) {
                sum += temp[k] * f[k];
            }
            q[0] = sum + lower1.getConstant(pdeData1.getCoefficients(), t2);

            temp = upper1.getRightMatrixCondition(pdeData1.getCoefficients(), grid, t1);
            sum = 0;
            for (int k = 0; k < temp.length; k++) {
                sum += temp[k] * f[xNodes - 1 - k];
            }

            q[xNodes - 1] = sum + upper1.getConstant(pdeData1.getCoefficients(), t2);

            temp = lower2.getRightMatrixCondition(pdeData2.getCoefficients(), grid, t1);
            sum = 0;
            for (int k = 0; k < temp.length; k++) {
                sum += temp[k] * f[k];
            }
            q[xNodes] = sum + lower2.getConstant(pdeData2.getCoefficients(), t2);

            temp = upper2.getRightMatrixCondition(pdeData2.getCoefficients(), grid, t1);
            sum = 0;
            for (int k = 0; k < temp.length; k++) {
                sum += temp[k] * f[xNodes - 1 - k];
            }

            q[2 * xNodes - 1] = sum + upper2.getConstant(pdeData2.getCoefficients(), t2);

            //TODO work out why SOR does not converge here
            //      final DoubleMatrix2D mM = new DoubleMatrix2D(m);
            //      final DecompositionResult res = DCOMP.evaluate(mM);
            //      f = res.solve(q);

            //      // SOR
            //
            //      int count = sor(omega, grid, freeBoundary, xNodes, f, q, m, t2);
            //      if (oldCount > 0) {
            //        if ((omegaIncrease && count > oldCount) || (!omegaIncrease && count < oldCount)) {
            //          omega = Math.max(1.0, omega * 0.9);
            //          omegaIncrease = false;
            //        } else {
            //          omega = Math.min(1.99, 1.1 * omega);
            //          omegaIncrease = true;
            //        }
            //      }
            //      oldCount = count;

            if (first) {
                final DoubleMatrix2D mM = new DoubleMatrix2D(m);
                decompRes = DCOMP.evaluate(mM);

                // first = false;
            }

            f = decompRes.solve(q);

            a1 = a2;
            b1 = b2;
            c1 = c2;

            if (_showFullResults) {
                if (full1 != null && full2 != null) {
                    full1[n] = Arrays.copyOfRange(f, 0, xNodes);
                    full2[n] = Arrays.copyOfRange(f, xNodes, 2 * xNodes);
                }
            }

        }
        final PDEResults1D[] res = new PDEResults1D[2];

        if (_showFullResults) {
            res[0] = new PDEFullResults1D(grid, full1);
            res[1] = new PDEFullResults1D(grid, full2);
        } else {
            final double[] res1 = Arrays.copyOfRange(f, 0, xNodes);
            final double[] res2 = Arrays.copyOfRange(f, xNodes, 2 * xNodes);
            res[0] = new PDETerminalResults1D(grid, res1);
            res[1] = new PDETerminalResults1D(grid, res2);
        }

        return res;

    }

    @SuppressWarnings("unused")
    private 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 < 2 * xNodes; j++) {
                sum = 0;
                for (int k = 0; k < 2 * 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++;
        }
        return count;
    }

}