public FdmHestonLocalVolatilityVarianceMesher(int size,
                                                      HestonProcess process,
                                                      LocalVolTermStructure leverageFct,
                                                      double maturity,
                                                      int tAvgSteps  = 10,
                                                      double epsilon = 0.0001)
            : base(size)
        {
            leverageFct_ = leverageFct;
            FdmHestonVarianceMesher mesher = new FdmHestonVarianceMesher(size, process, maturity, tAvgSteps, epsilon);

            for (int i = 0; i < size; ++i)
            {
                dplus_[i]     = mesher.dplus(i);
                dminus_[i]    = mesher.dminus(i);
                locations_[i] = mesher.location(i);
            }

            volaEstimate_ = mesher.volaEstimate();

            if (leverageFct != null)
            {
                double s0 = process.s0().currentLink().value();

                List <double> acc = new List <double>();
                acc.Add(leverageFct.localVol(0.0, s0, true));

                Handle <YieldTermStructure> rTS = process.riskFreeRate();
                Handle <YieldTermStructure> qTS = process.dividendYield();

                for (int l = 1; l <= tAvgSteps; ++l)
                {
                    double t = (maturity * l) / tAvgSteps;
                    double vol = volaEstimate_ * acc.Average();
                    double fwd = s0 * qTS.currentLink().discount(t) / rTS.currentLink().discount(t);
                    int    sAvgSteps = 50;
                    Vector u = new Vector(sAvgSteps), sig = new Vector(sAvgSteps);

                    for (int i = 0; i < sAvgSteps; ++i)
                    {
                        u[i] = epsilon + ((1.0 - 2.0 * epsilon) / (sAvgSteps - 1.0)) * i;
                        double x  = new InverseCumulativeNormal().value(u[i]);
                        double gf = x * vol * Math.Sqrt(t);
                        double f  = fwd * Math.Exp(gf);
                        sig[i] = Math.Pow(leverageFct.localVol(t, f, true), 2.0);
                    }

                    double leverageAvg = new GaussLobattoIntegral(10000, 1E-4).value(new interpolated_volatility(u, sig).value,
                                                                                     u.First(),
                                                                                     u.Last())
                                         / (1.0 - 2.0 * epsilon);

                    acc.Add(leverageAvg);
                }

                volaEstimate_ *= acc.Average();
            }
        }
        public FdmSolverDesc getSolverDesc(double x)
        {
            // 1. Mesher
            HestonProcess process  = model_.currentLink().process();
            double        maturity = process.time(arguments_.exercise.lastDate());

            // 1.1 The variance mesher
            int tGridMin = 5;
            FdmHestonVarianceMesher varianceMesher =
                new FdmHestonVarianceMesher(vGrid_, process,
                                            maturity, Math.Max(tGridMin, tGrid_ / 50));

            // 1.2 The equity mesher
            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

            Fdm1dMesher equityMesher;

            if (strikes_.empty())
            {
                equityMesher = new FdmBlackScholesMesher(
                    xGrid_,
                    FdmBlackScholesMesher.processHelper(
                        process.s0(), process.dividendYield(),
                        process.riskFreeRate(), varianceMesher.volaEstimate()),
                    maturity, payoff.strike(),
                    null, null, 0.0001, x,
                    new Pair <double?, double?>(payoff.strike(), 0.1),
                    arguments_.cashFlow);
            }
            else
            {
                Utils.QL_REQUIRE(arguments_.cashFlow.empty(), () => "multiple strikes engine "
                                 + "does not work with discrete dividends");
                equityMesher = new FdmBlackScholesMultiStrikeMesher(
                    xGrid_,
                    FdmBlackScholesMesher.processHelper(
                        process.s0(), process.dividendYield(),
                        process.riskFreeRate(), varianceMesher.volaEstimate()),
                    maturity, strikes_, 0.0001, x,
                    new Pair <double?, double?>(payoff.strike(), 0.075));
            }

            FdmMesher mesher = new FdmMesherComposite(equityMesher, varianceMesher);

            // 2. Calculator
            FdmInnerValueCalculator calculator = new FdmLogInnerValue(arguments_.payoff, mesher, 0);

            // 3. Step conditions
            FdmStepConditionComposite conditions =
                FdmStepConditionComposite.vanillaComposite(
                    arguments_.cashFlow, arguments_.exercise,
                    mesher, calculator,
                    process.riskFreeRate().currentLink().referenceDate(),
                    process.riskFreeRate().currentLink().dayCounter());

            // 4. Boundary conditions
            FdmBoundaryConditionSet boundaries = new FdmBoundaryConditionSet();

            // 5. Solver
            FdmSolverDesc solverDesc = new FdmSolverDesc();

            solverDesc.mesher       = mesher;
            solverDesc.bcSet        = boundaries;
            solverDesc.condition    = conditions;
            solverDesc.calculator   = calculator;
            solverDesc.maturity     = maturity;
            solverDesc.dampingSteps = dampingSteps_;
            solverDesc.timeSteps    = tGrid_;

            return(solverDesc);
        }