/** * Copyright (C) 2012 - 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 com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.surface.Surface; import com.opengamma.util.ArgumentChecker; /** * This class contains all the relevant information to solve a 1D PDE on a finite difference grid - the (functional) coefficients that * describe the PDE; the initial condition; the boundary conditions; the free boundary (if any); and the grid. * @param <T> The type of PDE */ public class PDE1DDataBundle<T extends PDE1DCoefficients> { private final T _coefficients; private final double[] _initialCondition; private final BoundaryCondition _lowerBoundary; private final BoundaryCondition _upperBoundary; private final Surface<Double, Double, Double> _freeBoundary; private final PDEGrid1D _grid; /** * All the relevant information to solve a 1D PDE on a finite difference grid * @param coefficients The description of the PDE $\mathcal{D}[V(t,x)]=0$ * @param initialCondition The function V(0,x) * @param lowerBoundary Boundary condition at the lowest value of x * @param upperBoundary Boundary condition at the highest value of x * @param grid 2D grid (t & x) on which the PDE will be solved */ public PDE1DDataBundle(final T coefficients, final Function1D<Double, Double> initialCondition, final BoundaryCondition lowerBoundary, final BoundaryCondition upperBoundary, final PDEGrid1D grid) { _initialCondition = getInitialConditions(coefficients, initialCondition, lowerBoundary, upperBoundary, grid); _coefficients = coefficients; _lowerBoundary = lowerBoundary; _upperBoundary = upperBoundary; _grid = grid; _freeBoundary = null; } /** * All the relevant information to solve a 1D PDE on a finite difference grid * @param coefficients The description of the PDE $\mathcal{D}[V(t,x)]=0$ * @param initialCondition The function V(0,x) * @param lowerBoundary Boundary condition at the lowest value of x * @param upperBoundary Boundary condition at the highest value of x * @param freeBoundary for a free-boundary function, $H(t,x)$, the solution to the PDE at $(t,x)$ is $max(V^*(t,x),H(t,x))$ * where $max(V^*(t,x)$ is the value calculated before the free-boundary condition is applied * @param grid 2D grid (t & x) on which the PDE will be solved */ public PDE1DDataBundle(final T coefficients, final Function1D<Double, Double> initialCondition, final BoundaryCondition lowerBoundary, final BoundaryCondition upperBoundary, final Surface<Double, Double, Double> freeBoundary, final PDEGrid1D grid) { ArgumentChecker.notNull(freeBoundary, "null freeBoundary"); _initialCondition = getInitialConditions(coefficients, initialCondition, lowerBoundary, upperBoundary, grid); _coefficients = coefficients; _lowerBoundary = lowerBoundary; _upperBoundary = upperBoundary; _grid = grid; _freeBoundary = freeBoundary; } /** * All the relevant information to solve a 1D PDE on a finite difference grid * @param coefficients The description of the PDE $\mathcal{D}[V(t,x)]=0$ * @param initialCondition The values $V(0,x_i)$ where $x_i$ are the spacial grid points * @param lowerBoundary Boundary condition at the lowest value of x * @param upperBoundary Boundary condition at the highest value of x * @param grid 2D grid (t & x) on which the PDE will be solved */ public PDE1DDataBundle(final T coefficients, final double[] initialCondition, final BoundaryCondition lowerBoundary, final BoundaryCondition upperBoundary, final PDEGrid1D grid) { checkData(coefficients, initialCondition, lowerBoundary, upperBoundary, grid); _coefficients = coefficients; _initialCondition = initialCondition; _lowerBoundary = lowerBoundary; _upperBoundary = upperBoundary; _grid = grid; _freeBoundary = null; } /** * All the relevant information to solve a 1D PDE on a finite difference grid * @param coefficients The description of the PDE $\mathcal{D}[V(t,x)]=0$ * @param initialCondition The values $V(0,x_i)$ where $x_i$ are the spacial grid points * @param lowerBoundary Boundary condition at the lowest value of x * @param upperBoundary Boundary condition at the highest value of x * @param freeBoundary for a free-boundary function, $H(t,x)$, the solution to the PDE at $(t,x)$ is $max(V^*(t,x),H(t,x))$ * where $max(V^*(t,x)$ is the value calculated before the free-boundary condition is applied * @param grid 2D grid (t & x) on which the PDE will be solved */ public PDE1DDataBundle(final T coefficients, final double[] initialCondition, final BoundaryCondition lowerBoundary, final BoundaryCondition upperBoundary, final Surface<Double, Double, Double> freeBoundary, final PDEGrid1D grid) { ArgumentChecker.notNull(freeBoundary, "null freeBoundary"); checkData(coefficients, initialCondition, lowerBoundary, upperBoundary, grid); _coefficients = coefficients; _initialCondition = initialCondition; _lowerBoundary = lowerBoundary; _upperBoundary = upperBoundary; _freeBoundary = freeBoundary; _grid = grid; } /** * Gets the coefficients. * @return the coefficients */ public T getCoefficients() { return _coefficients; } /** * Gets the initialCondition. * @return the initialCondition */ public double[] getInitialCondition() { return _initialCondition; } /** * Gets the lowerBoundary. * @return the lowerBoundary */ public BoundaryCondition getLowerBoundary() { return _lowerBoundary; } /** * Gets the upperBoundary. * @return the upperBoundary */ public BoundaryCondition getUpperBoundary() { return _upperBoundary; } /** * Gets the freeBoundary. * @return the freeBoundary */ public Surface<Double, Double, Double> getFreeBoundary() { return _freeBoundary; } /** * Gets the grid. * @return the grid */ public PDEGrid1D getGrid() { return _grid; } public PDE1DDataBundle<T> withInitialConditions(final Function1D<Double, Double> initialCondition) { if (_freeBoundary == null) { return new PDE1DDataBundle<>(_coefficients, initialCondition, _lowerBoundary, _upperBoundary, _grid); } return new PDE1DDataBundle<>(_coefficients, initialCondition, _lowerBoundary, _upperBoundary, _freeBoundary, _grid); } public PDE1DDataBundle<T> withInitialConditions(final double[] initialCondition) { if (_freeBoundary == null) { return new PDE1DDataBundle<>(_coefficients, initialCondition, _lowerBoundary, _upperBoundary, _grid); } return new PDE1DDataBundle<>(_coefficients, initialCondition, _lowerBoundary, _upperBoundary, _freeBoundary, _grid); } public PDE1DDataBundle<T> withGrid(final PDEGrid1D grid) { if (_freeBoundary == null) { return new PDE1DDataBundle<>(_coefficients, _initialCondition, _lowerBoundary, _upperBoundary, grid); } return new PDE1DDataBundle<>(_coefficients, _initialCondition, _lowerBoundary, _upperBoundary, _freeBoundary, grid); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((_coefficients == null) ? 0 : _coefficients.hashCode()); result = prime * result + ((_freeBoundary == null) ? 0 : _freeBoundary.hashCode()); result = prime * result + ((_grid == null) ? 0 : _grid.hashCode()); result = prime * result + Arrays.hashCode(_initialCondition); result = prime * result + ((_lowerBoundary == null) ? 0 : _lowerBoundary.hashCode()); result = prime * result + ((_upperBoundary == null) ? 0 : _upperBoundary.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } @SuppressWarnings("unchecked") final PDE1DDataBundle<T> other = (PDE1DDataBundle<T>) obj; if (_coefficients == null) { if (other._coefficients != null) { return false; } } else if (!_coefficients.equals(other._coefficients)) { return false; } if (_freeBoundary == null) { if (other._freeBoundary != null) { return false; } } else if (!_freeBoundary.equals(other._freeBoundary)) { return false; } if (_grid == null) { if (other._grid != null) { return false; } } else if (!_grid.equals(other._grid)) { return false; } if (!Arrays.equals(_initialCondition, other._initialCondition)) { return false; } if (_lowerBoundary == null) { if (other._lowerBoundary != null) { return false; } } else if (!_lowerBoundary.equals(other._lowerBoundary)) { return false; } if (_upperBoundary == null) { if (other._upperBoundary != null) { return false; } } else if (!_upperBoundary.equals(other._upperBoundary)) { return false; } return true; } private static double[] getInitialConditions(final PDE1DCoefficients coefficients, final Function1D<Double, Double> initialCondition, final BoundaryCondition lowerBoundary, final BoundaryCondition upperBoundary, final PDEGrid1D grid) { ArgumentChecker.notNull(coefficients, "null coefficients"); ArgumentChecker.notNull(initialCondition, "null initialCondition"); ArgumentChecker.notNull(lowerBoundary, "null lowerBoundary"); ArgumentChecker.notNull(upperBoundary, "null upperBoundary"); ArgumentChecker.notNull(grid, "null grid"); final int n = grid.getNumSpaceNodes(); ArgumentChecker.isTrue(Math.abs((grid.getSpaceNode(0) - lowerBoundary.getLevel()) / (1.0 + Math.abs(lowerBoundary.getLevel()))) < 1e-12, "space grid not consistent with lower boundary level. Lowerst grid point at " + grid.getSpaceNode(0) + " but lower boundary at " + lowerBoundary.getLevel()); ArgumentChecker.isTrue(Math.abs((grid.getSpaceNode(n - 1) - upperBoundary.getLevel()) / (1.0 + Math.abs(upperBoundary.getLevel()))) < 1e-12, "space grid not consistent with upper boundary level. Highest grid point at " + grid.getSpaceNode(n - 1) + " but upper boundary at " + upperBoundary.getLevel()); final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = initialCondition.evaluate(grid.getSpaceNode(i)); } return res; } private static void checkData(final PDE1DCoefficients coefficients, final double[] initialCondition, final BoundaryCondition lowerBoundary, final BoundaryCondition upperBoundary, final PDEGrid1D grid) { ArgumentChecker.notNull(coefficients, "null coefficients"); ArgumentChecker.notNull(initialCondition, "null initialCondition"); ArgumentChecker.notNull(lowerBoundary, "null lowerBoundary"); ArgumentChecker.notNull(upperBoundary, "null upperBoundary"); ArgumentChecker.notNull(grid, "null grid"); final int n = grid.getNumSpaceNodes(); ArgumentChecker.isTrue(initialCondition.length == n, n + " space nodes, but " + initialCondition.length + " values specified for initial condition"); ArgumentChecker.isTrue(Math.abs((grid.getSpaceNode(0) - lowerBoundary.getLevel()) / (1.0 + lowerBoundary.getLevel())) < 1e-12, "space grid not consistent with lower boundary level. Lowerst grid point at " + grid.getSpaceNode(0) + " but lower boundary at " + lowerBoundary.getLevel()); ArgumentChecker.isTrue(Math.abs((grid.getSpaceNode(n - 1) - upperBoundary.getLevel()) / (1.0 + upperBoundary.getLevel())) < 1e-12, "space grid not consistent with upper boundary level. Highest grid point at " + grid.getSpaceNode(n - 1) + " but upper boundary at " + upperBoundary.getLevel()); //TODO check that boundary condition is consistent with initial condition } }