コード例 #1
0
        public SecondDerivativeOp(int direction, FdmMesher mesher)
            : base(direction, mesher)
        {
            FdmLinearOpLayout   layout  = mesher.layout();
            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter; ++iter)
            {
                int    i  = iter.index();
                double?hm = mesher.dminus(iter, direction_);
                double?hp = mesher.dplus(iter, direction_);

                double?zetam1 = hm * (hm + hp);
                double?zeta0  = hm * hp;
                double?zetap1 = hp * (hm + hp);

                int co = iter.coordinates()[direction_];
                if (co == 0 || co == layout.dim()[direction] - 1)
                {
                    lower_[i] = diag_[i] = upper_[i] = 0.0;
                }
                else
                {
                    lower_[i] = 2.0 / zetam1.Value;
                    diag_[i]  = -2.0 / zeta0.Value;
                    upper_[i] = 2.0 / zetap1.Value;
                }
            }
        }
コード例 #2
0
        protected Vector getLeverageFctSlice(double t1, double t2)
        {
            FdmLinearOpLayout layout = mesher_.layout();
            Vector            v      = new Vector(layout.size(), 1.0);

            if (leverageFct_ == null)
            {
                return(v);
            }

            double t    = 0.5 * (t1 + t2);
            double time = Math.Min(leverageFct_ == null ? 1000.0 : leverageFct_.maxTime(), t);

            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin();
                 iter != endIter; ++iter)
            {
                int nx = iter.coordinates()[0];

                if (iter.coordinates()[1] == 0)
                {
                    double x    = Math.Exp(mesher_.location(iter, 0));
                    double spot = Math.Min(leverageFct_ == null ? 100000.0 : leverageFct_.maxStrike(),
                                           Math.Max(leverageFct_ == null ? -100000.0 : leverageFct_.minStrike(), x));
                    v[nx] = Math.Max(0.01, leverageFct_ == null ? 0.0 : leverageFct_.localVol(time, spot, true));
                }
                else
                {
                    v[iter.index()] = v[nx];
                }
            }
            return(v);
        }
コード例 #3
0
        public FdmIndicesOnBoundary(FdmLinearOpLayout layout,
                                    int direction, FdmDirichletBoundary.Side side)
        {
            List <int> newDim = new List <int>(layout.dim());

            newDim[direction] = 1;
            int hyperSize
                = newDim.accumulate(0, newDim.Count, 1,
                                    (a, b) => (a * b));

            indices_ = new InitializedList <int>(hyperSize);

            int i = 0;
            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin();
                 iter != endIter;
                 ++iter)
            {
                if ((side == FdmDirichletBoundary.Side.Lower &&
                     iter.coordinates()[direction] == 0) ||
                    (side == FdmDirichletBoundary.Side.Upper &&
                     iter.coordinates()[direction]
                     == layout.dim()[direction] - 1))
                {
                    Utils.QL_REQUIRE(hyperSize > i, () => "index missmatch");
                    indices_[i++] = iter.index();
                }
            }
        }
コード例 #4
0
        public Fdm1DimSolver(FdmSolverDesc solverDesc,
                             FdmSchemeDesc schemeDesc,
                             FdmLinearOpComposite op)
        {
            solverDesc_     = solverDesc;
            schemeDesc_     = schemeDesc;
            op_             = op;
            thetaCondition_ = new FdmSnapshotCondition(
                0.99 * Math.Min(1.0 / 365.0,
                                solverDesc.condition.stoppingTimes().empty()
                            ? solverDesc.maturity
                            : solverDesc.condition.stoppingTimes().First()));

            conditions_ = FdmStepConditionComposite.joinConditions(thetaCondition_,
                                                                   solverDesc.condition);
            x_             = new InitializedList <double>(solverDesc.mesher.layout().size());
            initialValues_ = new InitializedList <double>(solverDesc.mesher.layout().size());
            resultValues_  = new Vector(solverDesc.mesher.layout().size());

            FdmMesher         mesher = solverDesc.mesher;
            FdmLinearOpLayout layout = mesher.layout();

            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter;
                 ++iter)
            {
                initialValues_[iter.index()]
                    = solverDesc_.calculator.avgInnerValue(iter,
                                                           solverDesc.maturity);
                x_[iter.index()] = mesher.location(iter, 0);
            }
        }
コード例 #5
0
        public void applyTo(object o, double t)
        {
            Vector a = (Vector)o;

            if (exerciseTimes_.BinarySearch(t) >= 0)
            {
                FdmLinearOpLayout   layout  = mesher_.layout();
                FdmLinearOpIterator endIter = layout.end();

                int    dims      = layout.dim().Count;
                Vector locations = new Vector(dims);

                for (FdmLinearOpIterator iter = layout.begin();
                     iter != endIter;
                     ++iter)
                {
                    for (int i = 0; i < dims; ++i)
                    {
                        locations[i] = mesher_.location(iter, i);
                    }

                    double innerValue = calculator_.innerValue(iter, t);
                    if (innerValue > a[iter.index()])
                    {
                        a[iter.index()] = innerValue;
                    }
                }
            }
        }
コード例 #6
0
        public FdmHestonEquityPart(FdmMesher mesher,
                                   YieldTermStructure rTS,
                                   YieldTermStructure qTS,
                                   FdmQuantoHelper quantoHelper      = null,
                                   LocalVolTermStructure leverageFct = null)
        {
            varianceValues_ = new Vector(0.5 * mesher.locations(1));
            dxMap_          = new FirstDerivativeOp(0, mesher);
            dxxMap_         = new SecondDerivativeOp(0, mesher).mult(0.5 * mesher.locations(1));
            mapT_           = new TripleBandLinearOp(0, mesher);
            mesher_         = mesher;
            rTS_            = rTS;
            qTS_            = qTS;
            quantoHelper_   = quantoHelper;
            leverageFct_    = leverageFct;

            // on the boundary s_min and s_max the second derivative
            // d^2V/dS^2 is zero and due to Ito's Lemma the variance term
            // in the drift should vanish.
            FdmLinearOpLayout   layout  = mesher_.layout();
            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter;
                 ++iter)
            {
                if (iter.coordinates()[0] == 0 ||
                    iter.coordinates()[0] == layout.dim()[0] - 1)
                {
                    varianceValues_[iter.index()] = 0.0;
                }
            }
            volatilityValues_ = Vector.Sqrt(2 * varianceValues_);
        }
コード例 #7
0
 public FdmMesherComposite(FdmLinearOpLayout layout, List <Fdm1dMesher> mesher)
     : base(layout)
 {
     mesher_ = mesher;
     for (int i = 0; i < mesher.Count; ++i)
     {
         Utils.QL_REQUIRE(mesher[i].size() == layout.dim()[i],
                          () => "size of 1d mesher " + i + " does not fit to layout");
     }
 }
コード例 #8
0
        public Vector solve_splitting(Vector r, double a, double b = 1.0)
        {
            FdmLinearOpLayout layout = mesher_.layout();

            Utils.QL_REQUIRE(r.size() == layout.size(), () => "inconsistent size of rhs");

            for (FdmLinearOpIterator iter = layout.begin();
                 iter != layout.end(); ++iter)
            {
                List <int> coordinates = iter.coordinates();
                Utils.QL_REQUIRE(coordinates[direction_] != 0 ||
                                 lower_[iter.index()] == 0, () => "removing non zero entry!");
                Utils.QL_REQUIRE(coordinates[direction_] != layout.dim()[direction_] - 1 ||
                                 upper_[iter.index()] == 0, () => "removing non zero entry!");
            }

            Vector retVal = new Vector(r.size()), tmp = new Vector(r.size());

            // Thomson algorithm to solve a tridiagonal system.
            // Example code taken from Tridiagonalopertor and
            // changed to fit for the triple band operator.
            int    rim1 = reverseIndex_[0];
            double bet  = 1.0 / (a * diag_[rim1] + b);

            Utils.QL_REQUIRE(bet != 0.0, () => "division by zero");
            retVal[reverseIndex_[0]] = r[rim1] * bet;

            for (int j = 1; j <= layout.size() - 1; j++)
            {
                int ri = reverseIndex_[j];
                tmp[j] = a * upper_[rim1] * bet;

                bet = b + a * (diag_[ri] - tmp[j] * lower_[ri]);
                Utils.QL_REQUIRE(bet != 0.0, () => "division by zero"); //QL_ENSURE
                bet = 1.0 / bet;

                retVal[ri] = (r[ri] - a * lower_[ri] * retVal[rim1]) * bet;
                rim1       = ri;
            }
            // cannot be j>=0 with Size j
            for (int j = layout.size() - 2; j > 0; --j)
            {
                retVal[reverseIndex_[j]] -= tmp[j + 1] * retVal[reverseIndex_[j + 1]];
            }
            retVal[reverseIndex_[0]] -= tmp[1] * retVal[reverseIndex_[1]];

            return(retVal);
        }
コード例 #9
0
        public void applyTo(object o, double t)
        {
            Vector              a       = (Vector)o;
            FdmLinearOpLayout   layout  = mesher_.layout();
            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter;
                 ++iter)
            {
                double innerValue = calculator_.innerValue(iter, t);
                if (innerValue > a[iter.index()])
                {
                    a[iter.index()] = innerValue;
                }
            }
        }
コード例 #10
0
        public override Vector apply(Vector r)
        {
            FdmLinearOpLayout index = mesher_.layout();

            Utils.QL_REQUIRE(r.size() == index.size(), () => "inconsistent length of r");

            Vector retVal = new Vector(r.size());

            //#pragma omp parallel for
            for (int i = 0; i < index.size(); ++i)
            {
                retVal[i] = r[i0_[i]] * lower_[i] + r[i] * diag_[i] + r[i2_[i]] * upper_[i];
            }

            return(retVal);
        }
コード例 #11
0
        public override SparseMatrix toMatrix()
        {
            FdmLinearOpLayout index = mesher_.layout();
            int n = index.size();

            SparseMatrix retVal = new SparseMatrix(n, n);

            for (int i = 0; i < n; ++i)
            {
                retVal[i, i0_[i]] += lower_[i];
                retVal[i, i]      += diag_[i];
                retVal[i, i2_[i]] += upper_[i];
            }

            return(retVal);
        }
コード例 #12
0
        public NinePointLinearOp(int d0, int d1, FdmMesher mesher)
        {
            d0_     = d0;
            d1_     = d1;
            i00_    = new InitializedList <int>(mesher.layout().size());
            i10_    = new InitializedList <int>(mesher.layout().size());
            i20_    = new InitializedList <int>(mesher.layout().size());
            i01_    = new InitializedList <int>(mesher.layout().size());
            i21_    = new InitializedList <int>(mesher.layout().size());
            i02_    = new InitializedList <int>(mesher.layout().size());
            i12_    = new InitializedList <int>(mesher.layout().size());
            i22_    = new InitializedList <int>(mesher.layout().size());
            a00_    = new InitializedList <double>(mesher.layout().size());
            a10_    = new InitializedList <double>(mesher.layout().size());
            a20_    = new InitializedList <double>(mesher.layout().size());
            a01_    = new InitializedList <double>(mesher.layout().size());
            a11_    = new InitializedList <double>(mesher.layout().size());
            a21_    = new InitializedList <double>(mesher.layout().size());
            a02_    = new InitializedList <double>(mesher.layout().size());
            a12_    = new InitializedList <double>(mesher.layout().size());
            a22_    = new InitializedList <double>(mesher.layout().size());
            mesher_ = mesher;

            Utils.QL_REQUIRE(d0_ != d1_ &&
                             d0_ < mesher.layout().dim().Count &&
                             d1_ < mesher.layout().dim().Count,
                             () => "inconsistent derivative directions");

            FdmLinearOpLayout   layout  = mesher.layout();
            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter; ++iter)
            {
                int i = iter.index();

                i10_[i] = layout.neighbourhood(iter, d1_, -1);
                i01_[i] = layout.neighbourhood(iter, d0_, -1);
                i21_[i] = layout.neighbourhood(iter, d0_, 1);
                i12_[i] = layout.neighbourhood(iter, d1_, 1);
                i00_[i] = layout.neighbourhood(iter, d0_, -1, d1_, -1);
                i20_[i] = layout.neighbourhood(iter, d0_, 1, d1_, -1);
                i02_[i] = layout.neighbourhood(iter, d0_, -1, d1_, 1);
                i22_[i] = layout.neighbourhood(iter, d0_, 1, d1_, 1);
            }
        }
コード例 #13
0
        public TripleBandLinearOp multR(Vector u)
        {
            FdmLinearOpLayout layout = mesher_.layout();
            int size = layout.size();

            Utils.QL_REQUIRE(u.size() == size, () => "inconsistent size of rhs");
            TripleBandLinearOp retVal = new TripleBandLinearOp(direction_, mesher_);

            for (int i = 0; i < size; ++i)
            {
                double sm1 = i > 0 ? u[i - 1] : 1.0;
                double s0  = u[i];
                double sp1 = i < size - 1 ? u[i + 1] : 1.0;
                retVal.lower_[i] = lower_[i] * sm1;
                retVal.diag_[i]  = diag_[i] * s0;
                retVal.upper_[i] = upper_[i] * sp1;
            }

            return(retVal);
        }
コード例 #14
0
        public UniformGridMesher(FdmLinearOpLayout layout, List <Pair <double?, double?> > boundaries)
            : base(layout)
        {
            dx_        = new Vector(layout.dim().Count);
            locations_ = new InitializedList <List <double> >(layout.dim().Count);

            Utils.QL_REQUIRE(boundaries.Count == layout.dim().Count,
                             () => "inconsistent boundaries given");

            for (int i = 0; i < layout.dim().Count; ++i)
            {
                dx_[i] = (boundaries[i].second.Value - boundaries[i].first.Value)
                         / (layout.dim()[i] - 1);

                locations_[i] = new InitializedList <double>(layout.dim()[i]);
                for (int j = 0; j < layout.dim()[i]; ++j)
                {
                    locations_[i][j] = boundaries[i].first.Value + j * dx_[i];
                }
            }
        }
コード例 #15
0
        public TripleBandLinearOp(int direction, FdmMesher mesher)
        {
            direction_    = direction;
            i0_           = new InitializedList <int>(mesher.layout().size());
            i2_           = new InitializedList <int>(mesher.layout().size());
            reverseIndex_ = new InitializedList <int>(mesher.layout().size());
            lower_        = new InitializedList <double>(mesher.layout().size());
            diag_         = new InitializedList <double>(mesher.layout().size());
            upper_        = new InitializedList <double>(mesher.layout().size());
            mesher_       = mesher;

            FdmLinearOpLayout   layout  = mesher.layout();
            FdmLinearOpIterator endIter = layout.end();

            int        tmp;
            List <int> newDim = new List <int>(layout.dim());

            tmp = newDim[direction_];
            newDim[direction_] = newDim[0];
            newDim[0]          = tmp;

            List <int> newSpacing = new FdmLinearOpLayout(newDim).spacing();

            tmp = newSpacing[direction_];
            newSpacing[direction_] = newSpacing[0];
            newSpacing[0]          = tmp;

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter; ++iter)
            {
                int i = iter.index();

                i0_[i] = layout.neighbourhood(iter, direction, -1);
                i2_[i] = layout.neighbourhood(iter, direction, 1);

                List <int> coordinates = iter.coordinates();

                int newIndex = coordinates.inner_product(0, coordinates.Count, 0, newSpacing, 0);
                reverseIndex_[newIndex] = i;
            }
        }
コード例 #16
0
        public override SparseMatrix toMatrix()
        {
            FdmLinearOpLayout index = mesher_.layout();
            int n = index.size();

            SparseMatrix retVal = new SparseMatrix(n, n);

            for (int i = 0; i < index.size(); ++i)
            {
                retVal[i, i00_[i]] += a00_[i];
                retVal[i, i01_[i]] += a01_[i];
                retVal[i, i02_[i]] += a02_[i];
                retVal[i, i10_[i]] += a10_[i];
                retVal[i, i]       += a11_[i];
                retVal[i, i12_[i]] += a12_[i];
                retVal[i, i20_[i]] += a20_[i];
                retVal[i, i21_[i]] += a21_[i];
                retVal[i, i22_[i]] += a22_[i];
            }

            return(retVal);
        }
コード例 #17
0
        public override double avgInnerValue(FdmLinearOpIterator iter, double t)
        {
            if (avgInnerValues_.empty())
            {
                // calculate caching values
                avgInnerValues_ = new InitializedList <double>(mesher_.layout().dim()[direction_]);
                List <bool> initialized = new InitializedList <bool>(avgInnerValues_.Count, false);

                FdmLinearOpLayout   layout  = mesher_.layout();
                FdmLinearOpIterator endIter = layout.end();
                for (FdmLinearOpIterator new_iter = layout.begin(); new_iter != endIter;
                     ++new_iter)
                {
                    int xn = new_iter.coordinates()[direction_];
                    if (!initialized[xn])
                    {
                        initialized[xn]     = true;
                        avgInnerValues_[xn] = avgInnerValueCalc(new_iter, t);
                    }
                }
            }
            return(avgInnerValues_[iter.coordinates()[direction_]]);
        }
コード例 #18
0
        public FirstDerivativeOp(int direction, FdmMesher mesher)
            : base(direction, mesher)
        {
            FdmLinearOpLayout   layout  = mesher.layout();
            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter; ++iter)
            {
                int    i  = iter.index();
                double?hm = mesher.dminus(iter, direction_);
                double?hp = mesher.dplus(iter, direction_);

                double?zetam1 = hm * (hm + hp);
                double?zeta0  = hm * hp;
                double?zetap1 = hp * (hm + hp);

                if (iter.coordinates()[direction_] == 0)
                {
                    //upwinding scheme
                    lower_[i] = 0.0;
                    diag_[i]  = -(upper_[i] = 1 / hp.Value);
                }
                else if (iter.coordinates()[direction_]
                         == layout.dim()[direction] - 1)
                {
                    // downwinding scheme
                    lower_[i] = -(diag_[i] = 1 / hm.Value);
                    upper_[i] = 0.0;
                }
                else
                {
                    lower_[i] = -hp.Value / zetam1.Value;
                    diag_[i]  = (hp.Value - hm.Value) / zeta0.Value;
                    upper_[i] = hm.Value / zetap1.Value;
                }
            }
        }
コード例 #19
0
        public override Vector apply(Vector r)
        {
            FdmLinearOpLayout index = mesher_.layout();

            Utils.QL_REQUIRE(r.size() == index.size(), () => "inconsistent length of r "
                             + r.size() + " vs " + index.size());

            Vector retVal = new Vector(r.size());

            //#pragma omp parallel for
            for (int i = 0; i < retVal.size(); ++i)
            {
                retVal[i] = a00_[i] * r[i00_[i]]
                            + a01_[i] * r[i01_[i]]
                            + a02_[i] * r[i02_[i]]
                            + a10_[i] * r[i10_[i]]
                            + a11_[i] * r[i]
                            + a12_[i] * r[i12_[i]]
                            + a20_[i] * r[i20_[i]]
                            + a21_[i] * r[i21_[i]]
                            + a22_[i] * r[i22_[i]];
            }
            return(retVal);
        }
コード例 #20
0
 public FdmMesher(FdmLinearOpLayout layout)
 {
     layout_ = layout;
 }
コード例 #21
0
        //! Time \f$t1 <= t2\f$ is required
        public override void setTime(double t1, double t2)
        {
            double r = rTS_.forwardRate(t1, t2, Compounding.Continuous).rate();
            double q = qTS_.forwardRate(t1, t2, Compounding.Continuous).rate();

            if (localVol_ != null)
            {
                FdmLinearOpLayout   layout  = mesher_.layout();
                FdmLinearOpIterator endIter = layout.end();

                Vector v = new Vector(layout.size());
                for (FdmLinearOpIterator iter = layout.begin();
                     iter != endIter; ++iter)
                {
                    int i = iter.index();

                    if (illegalLocalVolOverwrite_ == null)
                    {
                        double t = localVol_.localVol(0.5 * (t1 + t2), x_[i], true);
                        v[i] = t * t;
                    }
                    else
                    {
                        try
                        {
                            double t = localVol_.localVol(0.5 * (t1 + t2), x_[i], true);
                            v[i] = t * t;
                        }
                        catch
                        {
                            v[i] = illegalLocalVolOverwrite_.Value * illegalLocalVolOverwrite_.Value;
                        }
                    }
                }
                if (quantoHelper_ != null)
                {
                    mapT_.axpyb(r - q - 0.5 * v
                                - quantoHelper_.quantoAdjustment(Vector.Sqrt(v), t1, t2),
                                dxMap_,
                                dxxMap_.mult(0.5 * v), new Vector(1, -r));
                }
                else
                {
                    mapT_.axpyb(r - q - 0.5 * v, dxMap_,
                                dxxMap_.mult(0.5 * v), new Vector(1, -r));
                }
            }
            else
            {
                double vv = volTS_.blackForwardVariance(t1, t2, strike_) / (t2 - t1);

                if (quantoHelper_ != null)
                {
                    mapT_.axpyb(new Vector(1, r - q - 0.5 * vv)
                                - quantoHelper_.quantoAdjustment(new Vector(1, Math.Sqrt(vv)), t1, t2),
                                dxMap_,
                                dxxMap_.mult(0.5 * new Vector(mesher_.layout().size(), vv)),
                                new Vector(1, -r));
                }
                else
                {
                    mapT_.axpyb(new Vector(1, r - q - 0.5 * vv), dxMap_,
                                dxxMap_.mult(0.5 * new Vector(mesher_.layout().size(), vv)),
                                new Vector(1, -r));
                }
            }
        }
コード例 #22
0
        public SecondOrderMixedDerivativeOp(int d0, int d1, FdmMesher mesher)
            : base(d0, d1, mesher)
        {
            FdmLinearOpLayout   layout  = mesher.layout();
            FdmLinearOpIterator endIter = layout.end();

            for (FdmLinearOpIterator iter = layout.begin(); iter != endIter; ++iter)
            {
                int    i     = iter.index();
                double?hm_d0 = mesher.dminus(iter, d0_);
                double?hp_d0 = mesher.dplus(iter, d0_);
                double?hm_d1 = mesher.dminus(iter, d1_);
                double?hp_d1 = mesher.dplus(iter, d1_);

                double?zetam1 = hm_d0 * (hm_d0 + hp_d0);
                double?zeta0  = hm_d0 * hp_d0;
                double?zetap1 = hp_d0 * (hm_d0 + hp_d0);
                double?phim1  = hm_d1 * (hm_d1 + hp_d1);
                double?phi0   = hm_d1 * hp_d1;
                double?phip1  = hp_d1 * (hm_d1 + hp_d1);

                int c0 = iter.coordinates()[d0_];
                int c1 = iter.coordinates()[d1_];
                if (c0 == 0 && c1 == 0)
                {
                    // lower left corner
                    a00_[i] = a01_[i] = a02_[i] = a10_[i] = a20_[i] = 0.0;
                    a11_[i] = a22_[i] = 1.0 / (hp_d0.Value * hp_d1.Value);
                    a21_[i] = a12_[i] = -a11_[i];
                }
                else if (c0 == layout.dim()[d0_] - 1 && c1 == 0)
                {
                    // upper left corner
                    a22_[i] = a21_[i] = a20_[i] = a10_[i] = a00_[i] = 0.0;
                    a01_[i] = a12_[i] = 1.0 / (hm_d0.Value * hp_d1.Value);
                    a11_[i] = a02_[i] = -a01_[i];
                }
                else if (c0 == 0 && c1 == layout.dim()[d1_] - 1)
                {
                    // lower right corner
                    a00_[i] = a01_[i] = a02_[i] = a12_[i] = a22_[i] = 0.0;
                    a10_[i] = a21_[i] = 1.0 / (hp_d0.Value * hm_d1.Value);
                    a20_[i] = a11_[i] = -a10_[i];
                }
                else if (c0 == layout.dim()[d0_] - 1 && c1 == layout.dim()[d1_] - 1)
                {
                    // upper right corner
                    a20_[i] = a21_[i] = a22_[i] = a12_[i] = a02_[i] = 0.0;
                    a00_[i] = a11_[i] = 1.0 / (hm_d0.Value * hm_d1.Value);
                    a10_[i] = a01_[i] = -a00_[i];
                }
                else if (c0 == 0)
                {
                    // lower side
                    a00_[i] = a01_[i] = a02_[i] = 0.0;
                    a10_[i] = hp_d1.Value / (hp_d0.Value * phim1.Value);
                    a20_[i] = -a10_[i];
                    a21_[i] = (hp_d1.Value - hm_d1.Value) / (hp_d0.Value * phi0.Value);
                    a11_[i] = -a21_[i];
                    a22_[i] = hm_d1.Value / (hp_d0.Value * phip1.Value);
                    a12_[i] = -a22_[i];
                }
                else if (c0 == layout.dim()[d0_] - 1)
                {
                    // upper side
                    a20_[i] = a21_[i] = a22_[i] = 0.0;
                    a00_[i] = hp_d1.Value / (hm_d0.Value * phim1.Value);
                    a10_[i] = -a00_[i];
                    a11_[i] = (hp_d1.Value - hm_d1.Value) / (hm_d0.Value * phi0.Value);
                    a01_[i] = -a11_[i];
                    a12_[i] = hm_d1.Value / (hm_d0.Value * phip1.Value);
                    a02_[i] = -a12_[i];
                }
                else if (c1 == 0)
                {
                    // left side
                    a00_[i] = a10_[i] = a20_[i] = 0.0;
                    a01_[i] = hp_d0.Value / (zetam1.Value * hp_d1.Value);
                    a02_[i] = -a01_[i];
                    a12_[i] = (hp_d0.Value - hm_d0.Value) / (zeta0.Value * hp_d1.Value);
                    a11_[i] = -a12_[i];
                    a22_[i] = hm_d0.Value / (zetap1.Value * hp_d1.Value);
                    a21_[i] = -a22_[i];
                }
                else if (c1 == layout.dim()[d1_] - 1)
                {
                    // right side
                    a22_[i] = a12_[i] = a02_[i] = 0.0;
                    a00_[i] = hp_d0.Value / (zetam1.Value * hm_d1.Value);
                    a01_[i] = -a00_[i];
                    a11_[i] = (hp_d0.Value - hm_d0.Value) / (zeta0.Value * hm_d1.Value);
                    a10_[i] = -a11_[i];
                    a21_[i] = hm_d0.Value / (zetap1.Value * hm_d1.Value);
                    a20_[i] = -a21_[i];
                }
                else
                {
                    a00_[i] = hp_d0.Value * hp_d1.Value / (zetam1.Value * phim1.Value);
                    a10_[i] = -(hp_d0.Value - hm_d0.Value) * hp_d1.Value / (zeta0.Value * phim1.Value);
                    a20_[i] = -hm_d0.Value * hp_d1.Value / (zetap1.Value * phim1.Value);
                    a01_[i] = -hp_d0.Value * (hp_d1.Value - hm_d1.Value) / (zetam1.Value * phi0.Value);
                    a11_[i] = (hp_d0.Value - hm_d0.Value) * (hp_d1.Value - hm_d1.Value) / (zeta0.Value * phi0.Value);
                    a21_[i] = hm_d0.Value * (hp_d1.Value - hm_d1.Value) / (zetap1.Value * phi0.Value);
                    a02_[i] = -hp_d0.Value * hm_d1.Value / (zetam1.Value * phip1.Value);
                    a12_[i] = hm_d1.Value * (hp_d0.Value - hm_d0.Value) / (zeta0.Value * phip1.Value);
                    a22_[i] = hm_d0.Value * hm_d1.Value / (zetap1.Value * phip1.Value);
                }
            }
        }