/**
* 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.FunctionUtils;
import com.opengamma.analytics.math.TrigonometricFunctionUtils;
import com.opengamma.util.ArgumentChecker;
/**
*/
public class HyperbolicMeshing extends MeshingFunction {
private final UniformMeshing _um;
private final double[] _fpValues;
private final double _alpha;
private final double _beta;
private final double _gamma;
private final double _delta;
private final double _l;
private final double _r;
/**
* Creates a non-uniform set of points according to the formula x_i = alpha * beta*Sinh(i/N*gamma + delta), where the points run from
* x_0 to x_N (i.e. there are N+1 points) and the highest concentration is around some specified point (e.g. the strike for solving option problems)
* @param xMin The value of x_0
* @param xMax The value of x_N
* @param xCent The value where the concentration of points is highest (<b>Note</b> there is no guarantee the a point will correspond exactly
* with this value)
* @param nPoints Number of Points (equal to N+1 in the above formula)
* @param beta Bunching parameter. A value great than zero. Very small values gives a very high density of points around the specified point, with the
* density quickly falling away in both directions (the total number of points is fixed), while the distribution tends to uniform for large values. Value
* greater than 1 are fairly uniform
*/
public HyperbolicMeshing(final double xMin, final double xMax, final double xCent, final int nPoints, final double beta) {
super(nPoints);
Validate.isTrue(xMax > xMin, "need xMax > xMin");
Validate.isTrue(xMax >= xCent && xCent >= xMin, "need xCent between upper and lower bounds");
Validate.isTrue(beta > 0, "need beta > 0");
_l = xMin;
_r = xMax;
_alpha = xCent;
_beta = beta * (xMax - xMin);
_delta = TrigonometricFunctionUtils.asinh((xMin - xCent) / _beta);
_gamma = (TrigonometricFunctionUtils.asinh((xMax - xCent) / _beta) - _delta);
_um = new UniformMeshing(nPoints);
_fpValues = null;
}
public HyperbolicMeshing(final double xMin, final double xMax, final double xCent, final int nPoints, final double beta, final double[] fixedPoints) {
super(nPoints);
Validate.isTrue(xMax > xMin, "need xMax > xMin");
Validate.isTrue(xMax >= xCent && xCent >= xMin, "need xCent between upper and lower bounds");
Validate.isTrue(beta > 0, "need beta > 0");
ArgumentChecker.notNull(fixedPoints, "null fixedPoints");
_l = xMin;
_r = xMax;
_alpha = xCent;
_beta = beta * (xMax - xMin);
_delta = TrigonometricFunctionUtils.asinh((xMin - xCent) / _beta);
_gamma = (TrigonometricFunctionUtils.asinh((xMax - xCent) / _beta) - _delta);
_fpValues = FunctionUtils.unique(fixedPoints);
int m = _fpValues.length;
final double[] fp = new double[m];
for (int ii = 0; ii < m; ii++) {
fp[ii] = (TrigonometricFunctionUtils.asinh((_fpValues[ii] - _alpha) / _beta) - _delta) / _gamma;
}
_um = new UniformMeshing(nPoints, fp);
}
@Override
public Double evaluate(Integer i) {
Validate.isTrue(i >= 0 && i < getNumberOfPoints(), "i out of range");
if (i == 0) {
return _l;
}
if (i == getNumberOfPoints() - 1) {
return _r;
}
//short cut if required point is one of the specified fixed points
if (_fpValues != null) {
int index = _um.getFixedPointIndex(i);
if (index >= 0) {
return _fpValues[index];
}
}
final double z = _um.evaluate(i);
return _alpha + _beta * Math.sinh(_gamma * z + _delta);
}
}