private double logContactPriceFromPureSpot(final LocalVolatilitySurfaceMoneyness lv) {

    final double fT = DIV_CURVES.getF(EXPIRY);
    final double dT = DIV_CURVES.getD(EXPIRY);

    final double dStar = dT / (fT - dT);

    final ConvectionDiffusionPDE1DCoefficients pde =
        PDE_PROVIDER.getLogBackwardsLocalVol(EXPIRY, lv);

    final double theta = 0.5;
    final double yL = -0.5;
    final double yH = 0.5;
    final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(theta, false);

    final BoundaryCondition lower =
        new NeumannBoundaryCondition(1 / (1 + dStar * Math.exp(-yL)), yL, true);
    final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, yH, false);

    final MeshingFunction timeMesh = new ExponentialMeshing(0.0, EXPIRY, 100, 0.0);
    final MeshingFunction spaceMesh = new ExponentialMeshing(yL, yH, 101, 0.0);

    final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh);
    final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db =
        new PDE1DDataBundle<>(pde, PURE_LOG_PAY_OFF, lower, upper, grid);
    final PDEResults1D res = solver.solve(db);

    final int n = res.getNumberSpaceNodes();

    final double val = res.getFunctionValue(n / 2);
    return val;
  }
 /**
  * Check the the log-contract is correctly prices using a backwards PDE expressed in terms of (the
  * log of) the real stock price - this requires having jumps conditions in the PDE. The local
  * volatility surface is derived from the flat pure local volatility surface.
  */
 @Test
 public void backwardsLogSpotPDEtest() {
   final double fT = DIV_CURVES.getF(EXPIRY);
   final double lnFT = Math.log(fT);
   final double val = logContractPriceFromSpotPDE(LOCAL_VOL);
   assertEquals(PURE_VOL, Math.sqrt(-2 * (val - lnFT) / EXPIRY), 1e-4);
   //   System.out.println(val + "\t" + Math.sqrt(-2 * (val - lnFT) / EXPIRY));
 }
 /**
  * Price the log-contact using the PDE in spot (with the jump conditions) with a flat local
  * volatility surface, and the PDE in pure spot using the pure local volatility surface derived
  * from the flat surface. They MUST give the same answer
  */
 @Test
 public void backwardsPDETest() {
   final double fT = DIV_CURVES.getF(EXPIRY);
   final double lnFT = Math.log(fT);
   final double val1 = logContractPriceFromSpotPDE(LOCAL_VOL_FLAT);
   final double val2 = logContactPriceFromPureSpot(PURE_LOCAL_VOL);
   // convert to realised vol
   final double vol1 = Math.sqrt(-2 * (val1 - lnFT) / EXPIRY);
   final double vol2 = Math.sqrt(-2 * (val2 - lnFT) / EXPIRY);
   assertEquals(vol1, vol2, 1e-3);
   //   System.out.println(vol1 + "\t" + vol2);
 }
  /**
   * Check the the log-contract is correctly prices using a backwards PDE expressed in terms of (the
   * log of) the forward F(t,T) - this requires NO jumps conditions in the PDE
   */
  @Test
  public void backwardsDebugPDEtest() {
    final double fT = DIV_CURVES.getF(EXPIRY);
    final double lnFT = Math.log(fT);

    final Function1D<Double, Double> payoff =
        new Function1D<Double, Double>() {

          @Override
          public Double evaluate(final Double y) {
            return y - lnFT;
          }
        };

    // ZZConvectionDiffusionPDEDataBundle pdeBundle1 = getBackwardsPDEDataBundle(EXPIRY, LOCAL_VOL,
    // payoff);
    // ConvectionDiffusionPDE1DCoefficients pde =
    // PDE_PROVIDER.getLogBackwardsLocalVol(FORWARD_CURVE, EXPIRY, LOCAL_VOL);
    final ConvectionDiffusionPDE1DCoefficients pde =
        PDE_PROVIDER.getLogBackwardsLocalVol(0.0, 0.0, EXPIRY, LOCAL_VOL_SPECIAL);

    final double theta = 0.5;
    final double range = Math.log(5);
    final double yL = lnFT - range;
    final double yH = lnFT + range;
    final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(theta, false);

    final BoundaryCondition lower = new NeumannBoundaryCondition(1.0, yL, true);
    final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, yH, false);

    final MeshingFunction timeMesh = new ExponentialMeshing(0, EXPIRY, 100, 0.0);
    final MeshingFunction spaceMesh = new ExponentialMeshing(yL, yH, 101, 0.0);

    final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh);
    final double[] sNodes = grid.getSpaceNodes();

    // run the PDE solver backward to the dividend date
    // PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db1 = new
    // PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde, initialCon, lower1, upper1,
    // grid1);
    final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db1 =
        new PDE1DDataBundle<>(pde, payoff, lower, upper, grid);
    final PDETerminalResults1D res = (PDETerminalResults1D) solver.solve(db1);

    final Interpolator1DDataBundle interpolDB =
        INTEPOLATOR1D.getDataBundle(sNodes, res.getTerminalResults());

    final double val = INTEPOLATOR1D.interpolate(interpolDB, lnFT);
    assertEquals(0.41491529, Math.sqrt(-2 * (val) / EXPIRY), 5e-4); // Number from backwardsPDETest
    //   System.out.println(val + "\t" + Math.sqrt(-2 * val / EXPIRY));
  }