/**
* 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;
/**
* Operating splitting (as in Duffy chapter 22) with boundary conditions applied at each of the 4 steps
* <b>Note</b> this is for testing purposes and is not recommended for actual use
*/
@SuppressWarnings("deprecation")
public class OperatorSplittingFiniteDifference2D implements ConvectionDiffusionPDESolver2D {
// private static final Decomposition<?> DCOMP = new LUDecompositionCommons();
// Theta = 0 - explicit
// private static final double THETA = 0.5;
private static final int SOR_MAX = 5000;
@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];
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]);
}
}
double t;
double a, b, c, d, e, f;
for (int n = 0; n < tSteps; n++) {
t = n * dt;
// stag 1 Explicit in the cross
for (int i = 1; i < xSteps; i++) {
for (int j = 1; j < ySteps; j++) {
e = pdeData.getE(t, x[i], y[j]);
vt[i][j] = v[i][j];
vt[i][j] -= 0.125 * dtdxdy * e * (v[i + 1][j + 1] + v[i - 1][j - 1] - v[i + 1][j - 1] - v[i - 1][j + 1]);
}
// the explicit intermediate stag vt is missed the boundary
vt[i][0] = v[i][0];
vt[i][ySteps] = v[i][ySteps];
}
// stag 2 - Implicit in x
t += 0.5 * dt;
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] = (dtdx2 * a - 0.5 * dtdx * b);
mx[i][i] = 1 + (-2 * dtdx2 * a + dt * c);
mx[i][i + 1] = (dtdx2 * a + 0.5 * dtdx * b);
q[i] = vt[i][j];
}
// it is not clear that these boundary conditions apply in the intermediate stage of operator splitting
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;
double scale = 1.0;
double errorSqr = Double.POSITIVE_INFINITY;
int min, max;
int count = 0;
while (errorSqr / (scale + 1e-10) > 1e-18 && count < SOR_MAX) {
errorSqr = 0.0;
scale = 0.0;
for (int l = 0; l <= xSteps; l++) {
min = (l == xSteps ? 0 : Math.max(0, l - 1));
max = (l == 0 ? xSteps : Math.min(xSteps, 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] * vt[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;
vt[l][j] += correction;
scale += vt[l][j] * vt[l][j];
}
count++;
}
Validate.isTrue(count < SOR_MAX, "SOR exceeded max interations");
}
for (int j = 1; j < ySteps; j++) {
for (int i = 1; i < xSteps; i++) {
e = pdeData.getE(t, x[i], y[j]);
v[i][j] = vt[i][j];
v[i][j] -= 0.125 * dtdxdy * e * (vt[i + 1][j + 1] + vt[i - 1][j - 1] - vt[i + 1][j - 1] - vt[i - 1][j + 1]);
}
// again now v on the boundary is undefined
v[0][j] = vt[0][j];
v[xSteps][j] = vt[xSteps][j];
}
// stag 4 - implicit in y
t = (n + 1) * dt;
for (int i = 0; i <= xSteps; i++) {
for (int j = 1; j < ySteps; j++) {
d = pdeData.getD(t, x[i], y[j]);
f = pdeData.getF(t, x[i], y[j]);
my[j][j - 1] = (dtdy2 * d - 0.5 * dtdy * f);
my[j][j] = 1 + (-2 * dtdy2 * d);
my[j][j + 1] = (dtdy2 * d + 0.5 * dtdy * f);
r[j] = v[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] * vt[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] * vt[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 < SOR_MAX) {
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 < SOR_MAX, "SOR exceeded max interations");
}
} // time loop
return v;
}
// private double[][] solveSOR(double[][] m, double[][] v)
}