/** * 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 org.apache.commons.lang.Validate; import com.opengamma.analytics.math.cube.Cube; /** * Craig-Sneyd splitting * <b>Note</b> this is for testing purposes and is not recommended for actual use * */ @SuppressWarnings("deprecation") public class CraigSneydFiniteDifference2D implements ConvectionDiffusionPDESolver2D { // private static final Decomposition<?> DCOMP = new LUDecompositionCommons(); // Theta = 0 - explicit private static final double THETA = 0.5; @Override public double[][] solve(final ConvectionDiffusion2DPDEDataBundle pdeData, final int tSteps, final int xSteps, final int ySteps, final double tMax, final BoundaryCondition2D xLowerBoundary, final BoundaryCondition2D xUpperBoundary, final BoundaryCondition2D yLowerBoundary, final BoundaryCondition2D yUpperBoundary) { return solve(pdeData, tSteps, xSteps, ySteps, tMax, xLowerBoundary, xUpperBoundary, yLowerBoundary, yUpperBoundary, null); } @Override public double[][] solve(final ConvectionDiffusion2DPDEDataBundle pdeData, final int tSteps, final int xSteps, final int ySteps, final double tMax, final BoundaryCondition2D xLowerBoundary, final BoundaryCondition2D xUpperBoundary, final BoundaryCondition2D yLowerBoundary, final BoundaryCondition2D yUpperBoundary, final Cube<Double, Double, Double, Double> freeBoundary) { final double dt = tMax / (tSteps); final double dx = (xUpperBoundary.getLevel() - xLowerBoundary.getLevel()) / (xSteps); final double dy = (yUpperBoundary.getLevel() - yLowerBoundary.getLevel()) / (ySteps); final double dtdx2 = dt / dx / dx; final double dtdx = dt / dx; final double dtdy2 = dt / dy / dy; final double dtdy = dt / dy; final double dtdxdy = dt / dx / dy; final double[][] v = new double[xSteps + 1][ySteps + 1]; final double[][] vt = new double[xSteps + 1][ySteps + 1]; final double[] x = new double[xSteps + 1]; final double[] y = new double[ySteps + 1]; final double[] q = new double[xSteps + 1]; final double[] r = new double[ySteps + 1]; final double[][] mx = new double[xSteps + 1][xSteps + 1]; final double[][] my = new double[ySteps + 1][ySteps + 1]; initializeMatrices(pdeData, xSteps, ySteps, xLowerBoundary, yLowerBoundary, dx, dy, v, x, y); double t = 0.0; double a, b, c, d, e, f; for (int n = 0; n < tSteps; n++) { // stag 1 full Explicit for (int i = 1; i < xSteps; i++) { for (int j = 1; j < ySteps; j++) { a = pdeData.getA(t, x[i], y[j]); b = pdeData.getB(t, x[i], y[j]); c = pdeData.getC(t, x[i], y[j]); d = pdeData.getD(t, x[i], y[j]); e = pdeData.getE(t, x[i], y[j]); f = pdeData.getF(t, x[i], y[j]); vt[i][j] = (1 - dt * (1 - 0.5 * THETA) * c) * v[i][j]; vt[i][j] -= dtdx2 * a * (1 - THETA) * (v[i + 1][j] + v[i - 1][j] - 2 * v[i][j]); vt[i][j] -= 0.5 * dtdx * b * (1 - THETA) * (v[i + 1][j] - v[i - 1][j]); vt[i][j] -= dtdy2 * d * (v[i][j + 1] + v[i][j - 1] - 2 * v[i][j]); // upwind // if (f > 0) { // vt[i][j] -= dtdy * f * (v[i][j] - v[i][j - 1]); // } else if (f < 0) { // vt[i][j] -= dtdy * f * (v[i][j + 1] - v[i][j]); // } vt[i][j] -= 0.5 * dtdy * f * (v[i][j + 1] - v[i][j - 1]); vt[i][j] -= 0.25 * dtdxdy * e * (v[i + 1][j + 1] + v[i - 1][j - 1] - v[i + 1][j - 1] - v[i - 1][j + 1]); } // really not sure what to do with boundary conditions in these intermediate steps vt[i][0] = v[i][0]; vt[i][ySteps] = v[i][ySteps]; } // for (int i = 0; i <= xSteps; i++) { // double[] temp = yLowerBoundary.getRightMatrixCondition(pdeData, t, x[i]); // double sum = 0; // for (int k = 0; k < temp.length; k++) { // sum += temp[k] * v[i][k]; // } // sum += yLowerBoundary.getConstant(pdeData, t, x[i], dy); // // temp = yLowerBoundary.getLeftMatrixCondition(pdeData, t, x[i]); // for (int k = 1; k < temp.length; k++) { // sum -= temp[k] * vt[i][k]; // } // vt[i][0] = sum / temp[0]; // // temp = yUpperBoundary.getRightMatrixCondition(pdeData, t, x[i]); // sum = 0; // for (int k = 0; k < temp.length; k++) { // sum += temp[k] * v[i][ySteps - k]; // } // sum += yUpperBoundary.getConstant(pdeData, t, x[i], dy); // // temp = yUpperBoundary.getLeftMatrixCondition(pdeData, t, x[i]); // for (int k = 1; k < temp.length; k++) { // sum -= temp[k] * vt[i][ySteps - k]; // } // vt[i][ySteps] = sum / temp[0]; // } // // for (int j = 1; j < ySteps; j++) { // double[] temp = xLowerBoundary.getRightMatrixCondition(pdeData, t, y[j]); // double sum = 0; // for (int k = 0; k < temp.length; k++) { // sum += temp[k] * v[k][j]; // } // sum += xLowerBoundary.getConstant(pdeData, t, y[j], dx); // // temp = xLowerBoundary.getLeftMatrixCondition(pdeData, t, y[j]); // for (int k = 1; k < temp.length; k++) { // sum -= temp[k] * vt[k][j]; // } // vt[0][j] = sum / temp[0]; // // temp = xUpperBoundary.getRightMatrixCondition(pdeData, t, y[j]); // sum = 0; // for (int k = 0; k < temp.length; k++) { // sum += temp[k] * v[xSteps - k][j]; // } // sum += xUpperBoundary.getConstant(pdeData, t, y[j], dx); // // temp = xUpperBoundary.getLeftMatrixCondition(pdeData, t, y[j]); // for (int k = 1; k < temp.length; k++) { // sum -= temp[k] * vt[xSteps - k][j]; // } // vt[xSteps][j] = sum / temp[0]; // } // stag 2 implicit in x t += dt / 2; for (int j = 0; j <= ySteps; j++) { for (int i = 1; i < xSteps; i++) { a = pdeData.getA(t, x[i], y[j]); b = pdeData.getB(t, x[i], y[j]); c = pdeData.getC(t, x[i], y[j]); mx[i][i - 1] = THETA * (dtdx2 * a - 0.5 * dtdx * b); mx[i][i] = 1 + THETA * (-2 * dtdx2 * a + 0.5 * dt * c); mx[i][i + 1] = THETA * (dtdx2 * a + 0.5 * dtdx * b); q[i] = vt[i][j]; } double[] temp = xLowerBoundary.getLeftMatrixCondition(t, y[j]); for (int k = 0; k < temp.length; k++) { mx[0][k] = temp[k]; } temp = xUpperBoundary.getLeftMatrixCondition(t, y[j]); for (int k = 0; k < temp.length; k++) { mx[xSteps][xSteps - k] = temp[k]; } temp = xLowerBoundary.getRightMatrixCondition(t, y[j]); double sum = 0; for (int k = 0; k < temp.length; k++) { sum += temp[k] * v[k][j]; } q[0] = sum + xLowerBoundary.getConstant(t, y[j], dx); temp = xUpperBoundary.getRightMatrixCondition(t, y[j]); sum = 0; for (int k = 0; k < temp.length; k++) { sum += temp[k] * v[xSteps - k][j]; } q[xSteps] = sum + xUpperBoundary.getConstant(t, y[j], dx); // SOR final double omega = 1.5; final int count = sor(xSteps, vt, q, mx, j, omega); Validate.isTrue(count < 1000, "SOR exceeded max iterations"); } // stag 3 explicit in y for (int i = 0; i <= xSteps; i++) { for (int j = 1; j < ySteps; j++) { c = pdeData.getC(t, x[i], y[j]); d = pdeData.getD(t, x[i], y[j]); f = pdeData.getF(t, x[i], y[j]); vt[i][j] += THETA * 0.5 * dt * c * v[i][j]; vt[i][j] += THETA * dtdy2 * d * (v[i][j + 1] + v[i][j - 1] - 2 * v[i][j]); // upwind // if (f > 0) { // vt[i][j] += THETA * dtdy * f * (v[i][j] - v[i][j - 1]); // } else if (f < 0) { // vt[i][j] += THETA * dtdy * f * (v[i][j + 1] - v[i][j]); // } vt[i][j] += THETA * 0.5 * dtdy * f * (v[i][j + 1] - v[i][j - 1]); } // double[] temp = yLowerBoundary.getRightMatrixCondition(pdeData, t, x[i]); // double sum = 0; // for (int k = 0; k < temp.length; k++) { // sum += temp[k] * v[i][k]; // } // sum += yLowerBoundary.getConstant(pdeData, t, x[i], dy); // // temp = yLowerBoundary.getLeftMatrixCondition(pdeData, t, x[i]); // for (int k = 1; k < temp.length; k++) { // sum -= temp[k] * vt[i][k]; // } // vt[i][0] = sum / temp[0]; // // temp = yUpperBoundary.getRightMatrixCondition(pdeData, t, x[i]); // sum = 0; // for (int k = 0; k < temp.length; k++) { // sum += temp[k] * v[i][ySteps - k]; // } // sum += yUpperBoundary.getConstant(pdeData, t, x[i], dy); // // temp = yUpperBoundary.getLeftMatrixCondition(pdeData, t, x[i]); // for (int k = 1; k < temp.length; k++) { // sum -= temp[k] * vt[i][ySteps - k]; // } // vt[i][ySteps] = sum / temp[0]; } // The y = 0 and y = yStep boundary values are assumed the same as the previous sub-step // Again we could apply the y boundary conditions here // stag 4 implicit in y for (int i = 0; i <= xSteps; i++) { for (int j = 1; j < ySteps; j++) { c = pdeData.getC(t, x[i], y[j]); d = pdeData.getD(t, x[i], y[j]); f = pdeData.getF(t, x[i], y[j]); // upwind // if (f > 0) { // my[j][j - 1] = THETA * (dtdy2 * d - dtdy * f); // my[j][j] = 1 + THETA * (-2 * dtdy2 * d + dtdy * f + 0.5 * dt * c); // my[j][j + 1] = THETA * (dtdy2 * d); // } else if (f < 0) { // my[j][j - 1] = THETA * (dtdy2 * d); // my[j][j] = 1 + THETA * (-2 * dtdy2 * d - dtdy * f + 0.5 * dt * c); // my[j][j + 1] = THETA * (dtdy2 * d + dtdy * f); // } my[j][j - 1] = THETA * (dtdy2 * d - 0.5 * dtdy * f); my[j][j] = 1 + THETA * (-2 * dtdy2 * d + 0.5 * dt * c); my[j][j + 1] = THETA * (dtdy2 * d + 0.5 * dtdy * f); r[j] = vt[i][j]; } double[] temp = yLowerBoundary.getLeftMatrixCondition(t, x[i]); for (int k = 0; k < temp.length; k++) { my[0][k] = temp[k]; } temp = yUpperBoundary.getLeftMatrixCondition(t, x[i]); for (int k = 0; k < temp.length; k++) { my[ySteps][ySteps - k] = temp[k]; } temp = yLowerBoundary.getRightMatrixCondition(t, x[i]); double sum = 0; for (int k = 0; k < temp.length; k++) { sum += temp[k] * v[i][k]; } r[0] = sum + yLowerBoundary.getConstant(t, x[i], dy); temp = yUpperBoundary.getRightMatrixCondition(t, x[i]); sum = 0; for (int k = 0; k < temp.length; k++) { sum += temp[k] * v[i][ySteps - k]; } r[ySteps] = sum + yUpperBoundary.getConstant(t, x[i], dy); // SOR final double omega = 1.5; double scale = 1.0; double errorSqr = Double.POSITIVE_INFINITY; int count = 0; while (errorSqr / (scale + 1e-10) > 1e-18 && count < 1000) { errorSqr = 0.0; scale = 0.0; int min, max; for (int l = 0; l <= ySteps; l++) { min = (l == ySteps ? 0 : Math.max(0, l - 1)); max = (l == 0 ? ySteps : Math.min(ySteps, l + 1)); sum = 0; // for (int k = 0; k <= ySteps; k++) { for (int k = min; k <= max; k++) { sum += my[l][k] * v[i][k]; } final double correction = omega / my[l][l] * (r[l] - sum); // if (freeBoundary != null) { // correction = Math.max(correction, freeBoundary.getZValue(t, x[j]) - f[j]); // } errorSqr += correction * correction; v[i][l] += correction; scale += v[i][l] * v[i][l]; } count++; } Validate.isTrue(count < 1000, "SOR exceeded max interations"); } } // time loop return v; } // TODO there is almost identical code lines 297-319 private int sor(final int steps, final double[][] v, final double[] q, final double[][] mx, final int j, final double omega) { double sum; int min; int max; int count = 0; double scale = 1.0; double errorSqr = Double.POSITIVE_INFINITY; while (errorSqr / (scale + 1e-10) > 1e-18 && count < 1000) { errorSqr = 0.0; scale = 0.0; for (int l = 0; l <= steps; l++) { min = (l == steps ? 0 : Math.max(0, l - 1)); max = (l == 0 ? steps : Math.min(steps, l + 1)); sum = 0; // for (int k = 0; k <= xSteps; k++) { for (int k = min; k <= max; k++) { // mx is tri-diagonal so only need 3 steps here sum += mx[l][k] * v[k][j]; } final double correction = omega / mx[l][l] * (q[l] - sum); // if (freeBoundary != null) { // correction = Math.max(correction, freeBoundary.getZValue(t, x[j]) - f[j]); // } errorSqr += correction * correction; v[l][j] += correction; scale += v[l][j] * v[l][j]; } count++; } return count; } private void initializeMatrices(final ConvectionDiffusion2DPDEDataBundle pdeData, final int xSteps, final int ySteps, final BoundaryCondition2D xLowerBoundary, final BoundaryCondition2D yLowerBoundary, final double dx, final double dy, final double[][] v, final double[] x, final double[] y) { double currentX = 0; double currentY = 0; for (int j = 0; j <= ySteps; j++) { currentY = yLowerBoundary.getLevel() + j * dy; y[j] = currentY; } for (int i = 0; i <= xSteps; i++) { currentX = xLowerBoundary.getLevel() + i * dx; x[i] = currentX; for (int j = 0; j <= ySteps; j++) { v[i][j] = pdeData.getInitialValue(x[i], y[j]); } } } // private double[][] solveSOR(double[][] m, double[][] v) }