Пример #1
0
        /// <summary>
        /// integrates some arbitrary function <paramref name="f"/> (which
        /// depends on DG fields <paramref name="Fields"/>) over some domain
        /// implied by <paramref name="scheme"/>
        /// </summary>
        /// <param name="scheme">
        /// specification of quadrature rule and integration domain
        /// </param>
        /// <param name="f">
        /// integrand
        /// </param>
        /// <param name="Fields">
        /// input arguments for function <paramref name="f"/>
        /// </param>
        /// <returns>
        /// the integral of <paramref name="f"/> over the domain implied by
        /// <paramref name="scheme"/>
        /// </returns>
        /// <param name="OverIntegrationMultiplier">
        /// Multiplier for Quadrature Order
        /// </param>
        public static double IntegralOverEx(CellQuadratureScheme scheme, Func f, int OverIntegrationMultiplier = 2, params DGField[] Fields)
        {
            using (new FuncTrace()) {
                ilPSP.MPICollectiveWatchDog.Watch(MPI.Wrappers.csMPI.Raw._COMM.WORLD);
                var g = Fields[0].Basis.GridDat;

                int order = Fields.Max(x => x.Basis.Degree) * OverIntegrationMultiplier;

                for (int i = 1; i < Fields.Length; i++)
                {
                    if (!Object.ReferenceEquals(g, Fields[i].Basis.GridDat))
                    {
                        throw new ArgumentException("all fields must be defined on the same grid");
                    }
                }



                IntegralOverExQuadrature q = new IntegralOverExQuadrature(g, Fields, scheme.SaveCompile(g, order), f);
                q.Execute();

                unsafe {
                    double locRes = q.result, glRes = 0;
                    csMPI.Raw.Allreduce((IntPtr)(&locRes), (IntPtr)(&glRes), 1, csMPI.Raw._DATATYPE.DOUBLE, csMPI.Raw._OP.SUM, csMPI.Raw._COMM.WORLD);

                    return(glRes);
                }
            }
        }
Пример #2
0
            /// <summary>
            /// ctor
            /// </summary>
            /// <param name="a">1st field</param>
            /// <param name="b">2nd field</param>
            /// <param name="quaddegree">
            /// for the quadrature rule to chose, the
            /// the degree of a polynomial that can be exactly integrated;
            /// </param>
            /// <param name="quadScheme"></param>
            public InnerProductQuadrature(DGField a, DGField b, CellQuadratureScheme quadScheme, int quaddegree)
                : base(new int[] { 1 }, a.GridDat, quadScheme.SaveCompile(a.GridDat, quaddegree))
            {
                if (!object.ReferenceEquals(a.GridDat, b.GridDat))
                {
                    throw new ArgumentException("Fields cannot be assigned to different grids.");
                }

                m_FieldA = a;
                m_FieldB = b;
            }
Пример #3
0
        /// <summary>
        /// Update the actual sensor field
        /// </summary>
        public void Update(DGField fieldToTest)
        {
            sensorField.Clear();
            fieldToTestRestricted.Clear();

            fieldToTestRestricted.AccLaidBack(1.0, fieldToTest);

            DGField difference = fieldToTestRestricted - fieldToTest;

            foreach (Chunk chunk in CellMask.GetFullMask(fieldToTest.GridDat))
            {
                for (int i = 0; i < chunk.Len; i++)
                {
                    int cell = i + chunk.i0;

                    CellMask singleCellMask = new CellMask(fieldToTest.GridDat, Chunk.GetSingleElementChunk(cell));

                    CellQuadratureScheme scheme = new CellQuadratureScheme(domain: singleCellMask);
                    var rule = scheme.SaveCompile(fieldToTest.GridDat, 2 * fieldToTest.Basis.Degree);

                    double[] a             = difference.LocalLxError((ScalarFunction)null, null, rule);
                    double[] b             = fieldToTest.LocalLxError((ScalarFunction)null, null, rule);
                    double   mySensorValue = a[0] / b[0];

                    // not using L2-norm, but rather only the scalar product
                    mySensorValue = mySensorValue * mySensorValue;

                    if (double.IsNaN(mySensorValue))
                    {
                        mySensorValue = 0.0;
                    }

                    sensorField.SetMeanValue(cell, mySensorValue);
                }
            }
        }
Пример #4
0
        /// <summary>
        /// Computation of mean curvature according to Bonnet's formula.
        /// </summary>
        /// <param name="scale"></param>
        /// <param name="Output">
        /// output.
        /// </param>
        /// <param name="quadScheme"></param>
        /// <param name="UseCenDiffUpTo">
        /// Either 0, 1, or 2:
        /// If 0, all derivatives are computed locally (broken derivative);
        /// if 1, the first order derivatives are computed by central
        /// differences, while the second order ones are computed locally,
        /// based on the first order ones;
        /// if 2, all derivatives are computed by central differences.
        /// </param>
        /// <param name="_1stDerivDegree">
        /// Relative DG polynomial degree for the 1st order derivatives, i.e.
        /// degree is <paramref name="_1stDerivDegree"/>+<em>p</em>, where
        /// <em>p</em> is the degree of this field.
        /// Only active if <paramref name="UseCenDiffUpTo"/> is greater than 0.
        /// </param>
        /// <param name="_2ndDerivDegree">
        /// Relative DG polynomial degree for the 2nd order derivatives, i.e.
        /// degree is <paramref name="_2ndDerivDegree"/>+<em>p</em>, where
        /// <em>p</em> is the degree of this field. Only active if
        /// <paramref name="UseCenDiffUpTo"/> is greater than 1.
        /// </param>
        /// <remarks>
        /// using central differences causes memory allocation: <em>D</em>
        /// fields for <paramref name="UseCenDiffUpTo"/>=1, and
        /// <em>D</em>*(<em>D</em>+1) for <paramref name="UseCenDiffUpTo"/>=2,
        /// where <em>D</em> notates the spatial dimension.
        /// </remarks>
        public void ProjectTotalcurvature2(
            double scale,
            SinglePhaseField Output,
            int UseCenDiffUpTo,
            int _1stDerivDegree             = 0,
            int _2ndDerivDegree             = 0,
            CellQuadratureScheme quadScheme = null)
        {
            using (new FuncTrace()) {
                if (UseCenDiffUpTo < 0 || UseCenDiffUpTo > 2)
                {
                    throw new ArgumentOutOfRangeException();
                }

                //int M = Output.Basis.Length;
                //int N = this.Basis.Length;
                int D = this.GridDat.SpatialDimension;
                //var NSC = m_context.NSC;

                SubGrid sgrd = null;
                SpatialOperator.SubGridBoundaryModes bndMode = SpatialOperator.SubGridBoundaryModes.InnerEdge;
                if (UseCenDiffUpTo >= 1 && quadScheme != null && quadScheme.Domain != null)
                {
                    sgrd    = new SubGrid(quadScheme.Domain);
                    bndMode = SpatialOperator.SubGridBoundaryModes.OpenBoundary;
                }

                // compute 1st order derivatives by central differences, if desired
                // ================================================================
                Basis B2 = new Basis(this.GridDat, this.Basis.Degree + _1stDerivDegree);

                SinglePhaseField[] GradientVector = null;
                if (UseCenDiffUpTo >= 1)
                {
                    GradientVector = new SinglePhaseField[D];
                    for (int d = 0; d < D; d++)
                    {
                        GradientVector[d] = new SinglePhaseField(B2);
                        GradientVector[d].DerivativeByFlux(1.0, this, d, sgrd, bndMode);
                    }
                }

                // compute 2nd order derivatives by central differences, if desired
                // ===============================================================
                Basis B3 = new Basis(this.GridDat, this.Basis.Degree + _2ndDerivDegree);

                SinglePhaseField[,] HessianTensor = null;
                if (UseCenDiffUpTo >= 2)
                {
                    HessianTensor = new SinglePhaseField[D, D];
                    for (int d1 = 0; d1 < D; d1++)
                    {
                        for (int d2 = 0; d2 < D; d2++)
                        {
                            HessianTensor[d1, d2] = new SinglePhaseField(B3);
                            HessianTensor[d1, d2].DerivativeByFlux(1.0, GradientVector[d1], d2, sgrd, bndMode);
                        }
                    }
                }

                // compute and project
                // ===================

                // buffers:
                MultidimensionalArray Phi     = new MultidimensionalArray(2);
                MultidimensionalArray GradPhi = new MultidimensionalArray(3);
                MultidimensionalArray HessPhi = new MultidimensionalArray(4);

                MultidimensionalArray ooNormGrad = new MultidimensionalArray(2);
                MultidimensionalArray Laplace    = new MultidimensionalArray(2);
                MultidimensionalArray Q          = new MultidimensionalArray(3);

                // evaluate/project:
                //double Erracc = 0;
                Output.ProjectField(scale,
                                    (ScalarFunctionEx) delegate(int j0, int Len, NodeSet NodeSet, MultidimensionalArray result) { // ScalarFunction2
                    Debug.Assert(result.Dimension == 2);
                    Debug.Assert(Len == result.GetLength(0));
                    int K = result.GetLength(1); // number of nodes


                    // alloc buffers
                    // -------------

                    if (Phi.GetLength(0) != Len || Phi.GetLength(1) != K)
                    {
                        Phi.Allocate(Len, K);
                        GradPhi.Allocate(Len, K, D);
                        HessPhi.Allocate(Len, K, D, D);
                        ooNormGrad.Allocate(Len, K);
                        Laplace.Allocate(Len, K);
                        Q.Allocate(Len, K, D);
                    }
                    else
                    {
                        Phi.Clear();
                        GradPhi.Clear();
                        HessPhi.Clear();
                        ooNormGrad.Clear();
                        Laplace.Clear();
                        Q.Clear();
                    }

                    // evaluate Gradient and Hessian
                    // -----------------------------

                    if (UseCenDiffUpTo >= 1)
                    {
                        for (int d = 0; d < D; d++)
                        {
                            GradientVector[d].Evaluate(j0, Len, NodeSet, GradPhi.ExtractSubArrayShallow(-1, -1, d));
                        }
                    }
                    else
                    {
                        this.EvaluateGradient(j0, Len, NodeSet, GradPhi);
                    }

                    if (UseCenDiffUpTo == 2)
                    {
                        for (int d1 = 0; d1 < D; d1++)
                        {
                            for (int d2 = 0; d2 < D; d2++)
                            {
                                HessianTensor[d1, d2].Evaluate(j0, Len, NodeSet, HessPhi.ExtractSubArrayShallow(-1, -1, d1, d2));
                            }
                        }
                    }
                    else if (UseCenDiffUpTo == 1)
                    {
                        for (int d = 0; d < D; d++)
                        {
                            var GradientVector_d = GradientVector[d];
                            GradientVector_d.EvaluateGradient(j0, Len, NodeSet, HessPhi.ExtractSubArrayShallow(-1, -1, d, -1), 0, 0.0);
                        }
                    }
                    else if (UseCenDiffUpTo == 0)
                    {
                        this.EvaluateHessian(j0, Len, NodeSet, HessPhi);
                    }
                    else
                    {
                        Debug.Assert(false);
                    }

                    // compute the monstrous formula
                    // -----------------------------

                    // norm of Gradient:
                    for (int d = 0; d < D; d++)
                    {
                        var GradPhi_d = GradPhi.ExtractSubArrayShallow(-1, -1, d);
                        ooNormGrad.Multiply(1.0, GradPhi_d, GradPhi_d, 1.0, "ik", "ik", "ik");
                    }
                    ooNormGrad.ApplyAll(x => 1.0 / Math.Sqrt(x));

                    // laplacian of phi:
                    for (int d = 0; d < D; d++)
                    {
                        var HessPhi_d_d = HessPhi.ExtractSubArrayShallow(-1, -1, d, d);
                        Laplace.Acc(1.0, HessPhi_d_d);
                    }

                    // result = Laplacian(phi)/|Grad phi|
                    result.Multiply(1.0, Laplace, ooNormGrad, 0.0, "ik", "ik", "ik");


                    // result = Grad(1/|Grad(phi)|)
                    for (int d1 = 0; d1 < D; d1++)
                    {
                        var Qd = Q.ExtractSubArrayShallow(-1, -1, d1);

                        for (int d2 = 0; d2 < D; d2++)
                        {
                            var Grad_d2    = GradPhi.ExtractSubArrayShallow(-1, -1, d2);
                            var Hess_d2_d1 = HessPhi.ExtractSubArrayShallow(-1, -1, d2, d1);

                            Qd.Multiply(-1.0, Grad_d2, Hess_d2_d1, 1.0, "ik", "ik", "ik");
                        }
                    }

                    ooNormGrad.ApplyAll(x => x * x * x);

                    result.Multiply(1.0, GradPhi, Q, ooNormGrad, 1.0, "ik", "ikd", "ikd", "ik");

                    //for (int i = 0; i < Len; i++) {
                    //    for (int k = 0; k < K; k++) {
                    //        double acc = 0;
                    //        for (int d = 0; d < D; d++) {
                    //            acc += GradPhi[i,k,d]*Q[i,k,d]*ooNormGrad[i,k];
                    //        }

                    //        Erracc += (acc - result[i,k]).Abs();
                    //    }
                    //}
                },
                                    quadScheme.SaveCompile(this.GridDat, (Output.Basis.Degree + this.m_Basis.Degree * (this.m_Basis.Degree - 1) * D) * 2)
                                    );
            }
        }
Пример #5
0
 /// <summary>
 /// Computes L2 measure with default quadrature
 /// </summary>
 /// <param name="function"></param>
 /// <param name="scheme"></param>
 /// <returns></returns>
 public double L2Error(ScalarFunction function, CellQuadratureScheme scheme = null)
 {
     return(LxError(function, null, scheme.SaveCompile(this.GridDat, this.Basis.Degree * 2 + 1)).Sqrt());
 }
Пример #6
0
 /// <summary>
 /// Computes L2 measure with default quadrature
 /// </summary>
 public double L2Error(ScalarFunction function, int order, CellQuadratureScheme scheme = null)
 {
     return(LxError(function, null, scheme.SaveCompile(this.GridDat, order)).Sqrt());
 }
Пример #7
0
        /// <summary>
        /// Projects a DG field <paramref name="source"/>, which may be defined on some different mesh,
        /// onto the DG field <paramref name="target"/>.
        /// </summary>
        static public void ProjectFromForeignGrid(this ConventionalDGField target, double alpha, ConventionalDGField source, CellQuadratureScheme scheme = null)
        {
            using (new FuncTrace()) {
                Console.WriteLine(string.Format("Projecting {0} onto {1}... ", source.Identification, target.Identification));
                int maxDeg       = Math.Max(target.Basis.Degree, source.Basis.Degree);
                var CompQuadRule = scheme.SaveCompile(target.GridDat, maxDeg * 3 + 3); // use over-integration
                int D            = target.GridDat.SpatialDimension;



                if (object.ReferenceEquals(source.GridDat, target.GridDat))
                {
                    // +++++++++++++++++
                    // equal grid branch
                    // +++++++++++++++++

                    target.ProjectField(alpha, source.Evaluate, CompQuadRule);
                }
                else
                {
                    // +++++++++++++++++++++
                    // different grid branch
                    // +++++++++++++++++++++

                    if (source.GridDat.SpatialDimension != D)
                    {
                        throw new ArgumentException("Spatial Dimension Mismatch.");
                    }

                    var eval = new FieldEvaluation(GridHelper.ExtractGridData(source.GridDat));


                    //
                    // Difficulty with MPI parallelism:
                    // While the different meshes may represent the same geometrical domain,
                    // their MPI partitioning may be different.
                    //
                    // Solution Approach: we circulate unlocated points among all MPI processes.
                    //

                    int MPIsize = target.GridDat.MpiSize;


                    // pass 1: collect all points
                    // ==========================
                    MultidimensionalArray allNodes = null;
                    void CollectPoints(MultidimensionalArray input, MultidimensionalArray output)
                    {
                        Debug.Assert(input.Dimension == 2);
                        Debug.Assert(input.NoOfCols == D);

                        if (allNodes == null)
                        {
                            allNodes = input.CloneAs();
                        }
                        else
                        {
                            /*
                             * var newNodes = MultidimensionalArray.Create(allNodes.NoOfRows + input.NoOfRows, D);
                             * newNodes.ExtractSubArrayShallow(new[] { 0, 0 }, new[] { allNodes.NoOfRows - 1, D - 1 })
                             *  .Acc(1.0, allNodes);
                             * newNodes.ExtractSubArrayShallow(new[] {  allNodes.NoOfRows, 0 }, new[] { newNodes.NoOfRows - 1, D - 1 })
                             *  .Acc(1.0, allNodes);
                             */
                            allNodes = allNodes.CatVert(input);
                        }
                        Debug.Assert(allNodes.NoOfCols == D);
                    }

                    target.ProjectField(alpha, CollectPoints, CompQuadRule);

                    int L = allNodes != null?allNodes.GetLength(0) : 0;

                    // evaluate
                    // ========

                    //allNodes = MultidimensionalArray.Create(2, 2);
                    //allNodes[0, 0] = -1.5;
                    //allNodes[0, 1] = 2.0;
                    //allNodes[1, 0] = -0.5;
                    //allNodes[1, 1] = 2.0;
                    //L = 2;
                    var Res           = L > 0 ? MultidimensionalArray.Create(L, 1) : default(MultidimensionalArray);
                    int NoOfUnlocated = eval.EvaluateParallel(1.0, new DGField[] { source }, allNodes, 0.0, Res);

                    int TotalNumberOfUnlocated = NoOfUnlocated.MPISum();
                    if (TotalNumberOfUnlocated > 0)
                    {
                        Console.Error.WriteLine($"WARNING: {TotalNumberOfUnlocated} unlocalized points in 'ProjectFromForeignGrid(...)'");
                    }

                    // perform the real projection
                    // ===========================


                    int lc = 0;
                    void ProjectionIntegrand(MultidimensionalArray input, MultidimensionalArray output)
                    {
                        Debug.Assert(input.Dimension == 2);
                        Debug.Assert(input.NoOfCols == D);

                        int LL = input.GetLength(0);

                        output.Set(Res.ExtractSubArrayShallow(new int[] { lc, 0 }, new int[] { lc + LL - 1, -1 }));
                        lc += LL;
                    }

                    target.ProjectField(alpha, ProjectionIntegrand, CompQuadRule);
                }
            }
        }
Пример #8
0
        /// <summary>
        /// Approximate L2 distance between two DG fields; this also supports DG fields on different meshes,
        /// it could be used for convergence studies.
        /// </summary>
        /// <param name="A"></param>
        /// <param name="B"></param>
        /// <param name="IgnoreMeanValue">
        /// if true, the mean value (mean over entire domain) will be subtracted - this mainly useful for comparing pressures
        /// </param>
        /// <param name="scheme">
        /// a cell quadrature scheme on the coarse of the two meshes
        /// </param>
        /// <returns></returns>
        static public double L2Distance(this ConventionalDGField A, ConventionalDGField B, bool IgnoreMeanValue = false, CellQuadratureScheme scheme = null)
        {
            int maxDeg    = Math.Max(A.Basis.Degree, B.Basis.Degree);
            int quadOrder = maxDeg * 3 + 3;

            if (A.GridDat.SpatialDimension != B.GridDat.SpatialDimension)
            {
                throw new ArgumentException("Both fields must have the same spatial dimension.");
            }

            if (object.ReferenceEquals(A.GridDat, B.GridDat) && false)
            {
                // ++++++++++++++
                // equal meshes
                // ++++++++++++++
                CellMask domain = scheme != null ? scheme.Domain : null;

                double errPow2 = A.L2Error(B, domain).Pow2();

                if (IgnoreMeanValue)
                {
                    // domain volume
                    double Vol = 0;
                    int    J   = A.GridDat.iGeomCells.NoOfLocalUpdatedCells;
                    for (int j = 0; j < J; j++)
                    {
                        Vol += A.GridDat.iGeomCells.GetCellVolume(j);
                    }
                    Vol = Vol.MPISum();

                    // mean value
                    double mean = A.GetMeanValueTotal(domain) - B.GetMeanValueTotal(domain);

                    // Note: for a field p, we have
                    // || p - <p> ||^2 = ||p||^2 - <p>^2*vol
                    return(Math.Sqrt(errPow2 - mean * mean * Vol));
                }
                else
                {
                    return(Math.Sqrt(errPow2));
                }
            }
            else
            {
                // ++++++++++++++++++
                // different meshes
                // ++++++++++++++++++


                DGField fine, coarse;
                if (A.GridDat.CellPartitioning.TotalLength > B.GridDat.CellPartitioning.TotalLength)
                {
                    fine   = A;
                    coarse = B;
                }
                else
                {
                    fine   = B;
                    coarse = A;
                }

                var CompQuadRule = scheme.SaveCompile(coarse.GridDat, maxDeg * 3 + 3); // use over-integration

                var eval = new FieldEvaluation(GridHelper.ExtractGridData(fine.GridDat));

                void FineEval(MultidimensionalArray input, MultidimensionalArray output)
                {
                    int L = input.GetLength(0);

                    Debug.Assert(output.GetLength(0) == L);

                    eval.Evaluate(1.0, new DGField[] { fine }, input, 0.0, output.ResizeShallow(L, 1));
                }

                double errPow2 = coarse.LxError(FineEval, (double[] X, double fC, double fF) => (fC - fF).Pow2(), CompQuadRule, Quadrature_ChunkDataLimitOverride: int.MaxValue);

                if (IgnoreMeanValue == true)
                {
                    // domain volume
                    double Vol = 0;
                    int    J   = coarse.GridDat.iGeomCells.NoOfLocalUpdatedCells;
                    for (int j = 0; j < J; j++)
                    {
                        Vol += coarse.GridDat.iGeomCells.GetCellVolume(j);
                    }
                    Vol = Vol.MPISum();

                    // mean value times domain volume
                    double meanVol = coarse.LxError(FineEval, (double[] X, double fC, double fF) => fC - fF, CompQuadRule, Quadrature_ChunkDataLimitOverride: int.MaxValue);


                    // Note: for a field p, we have
                    // || p - <p> ||^2 = ||p||^2 - <p>^2*vol
                    return(Math.Sqrt(errPow2 - meanVol * meanVol / Vol));
                }
                else
                {
                    return(Math.Sqrt(errPow2));
                }
            }
        }
Пример #9
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="jCell">
        /// Local index of cell which should be recalculated.
        /// </param>
        /// <param name="AcceptedMask">
        /// Bitmask which marks accepted cells - if any neighbor of
        /// <paramref name="jCell"/> is _accepted_, this defines a Dirichlet
        /// boundary; otherwise, the respective cell face is a free boundary.
        /// </param>
        /// <param name="Phi">
        /// Input and output:
        /// - input for _accepted_ cells, i.e. Dirichlet boundary values
        /// - input and output for <paramref name="jCell"/>: an initial value for the iterative procedure, resp. on exit the result of the iteration.
        /// </param>
        /// <param name="gradPhi">
        /// Auxillary variable to store the gradient of the level-set-field.
        /// </param>
        /// <returns></returns>
        public bool LocalSolve(int jCell, BitArray AcceptedMask, SinglePhaseField Phi, VectorField <SinglePhaseField> gradPhi)
        {
            Stpw_tot.Start();

            int    N           = this.LevelSetBasis.GetLength(jCell);
            int    i0G         = this.LevelSetMapping.GlobalUniqueCoordinateIndex(0, jCell, 0);
            int    i0L         = this.LevelSetMapping.LocalUniqueCoordinateIndex(0, jCell, 0);
            double penaltyBase = ((double)(this.LevelSetBasis.Degree + 2)).Pow2();
            double CellVolume  = this.GridDat.Cells.GetCellVolume(jCell);
            int    p           = Phi.Basis.Degree;

            // subgrid on which we are working, consisting only of one cell
            SubGrid jCellGrid = new SubGrid(new CellMask(this.GridDat, Chunk.GetSingleElementChunk(jCell)));
            var     VolScheme = new CellQuadratureScheme(domain: jCellGrid.VolumeMask);
            var     EdgScheme = new EdgeQuadratureScheme(domain: jCellGrid.AllEdgesMask);
            var     VolRule   = VolScheme.SaveCompile(GridDat, 3 * p);
            var     EdgRule   = EdgScheme.SaveCompile(GridDat, 3 * p);

            // parameter list for operator
            DGField[] Params = new DGField[] { Phi };
            gradPhi.ForEach(f => f.AddToArray(ref Params));

            // build operator
            var comp = new EllipticReinitForm(AcceptedMask, jCell, penaltyBase, this.GridDat.Cells.cj);

            comp.LhsSwitch = 1.0;  // matrix is constant -- Lhs Matrix only needs to be computed once
            comp.RhsSwitch = -1.0; //                   Rhs must be updated in every iteration
            var op = comp.Operator();


            // iteration loop:
            MultidimensionalArray Mtx = MultidimensionalArray.Create(N, N);

            double[] Rhs        = new double[N];
            double   ChangeNorm = 0;
            int      iIter;

            for (iIter = 0; iIter < 100; iIter++)
            {
                // update gradient
                gradPhi.Clear(jCellGrid.VolumeMask);
                gradPhi.Gradient(1.0, Phi, jCellGrid.VolumeMask);

                // assemble matrix and rhs
                {
                    // clear
                    for (int n = 0; n < N; n++)
                    {
                        if (iIter == 0)
                        {
                            m_LaplaceMatrix.ClearRow(i0G + n);
                        }
                        this.m_LaplaceAffine[i0L + n] = 0;
                    }

                    // compute matrix (only in iteration 0) and rhs
                    if (iIter == 0)
                    {
                        Stpw_Mtx.Start();
                    }
                    Stpw_Rhs.Start();
                    op.ComputeMatrixEx(this.LevelSetMapping, Params, this.LevelSetMapping,
                                       iIter == 0 ? this.m_LaplaceMatrix : null, this.m_LaplaceAffine,
                                       OnlyAffine: iIter > 0,
                                       //volQrCtx: VolScheme, edgeQrCtx: EdgScheme,
                                       volRule: VolRule, edgeRule: EdgRule,
                                       ParameterMPIExchange: false);
                    //op.Internal_ComputeMatrixEx(this.GridDat,
                    //    this.LevelSetMapping, Params, this.LevelSetMapping,
                    //    iIter == 0 ? this.m_LaplaceMatrix : default(MsrMatrix), this.m_LaplaceAffine, iIter > 0,
                    //    0.0,
                    //    EdgRule, VolRule,
                    //    null, false);


                    if (iIter == 0)
                    {
                        Stpw_Mtx.Stop();
                    }
                    Stpw_Rhs.Stop();

                    // extract matrix for 'jCell'
                    for (int n = 0; n < N; n++)
                    {
#if DEBUG
                        int      Lr;
                        int[]    row_cols = null;
                        double[] row_vals = null;
                        Lr = this.m_LaplaceMatrix.GetRow(i0G + n, ref row_cols, ref row_vals);
                        for (int lr = 0; lr < Lr; lr++)
                        {
                            int    ColIndex = row_cols[lr];
                            double Value    = row_vals[lr];
                            Debug.Assert((ColIndex >= i0G && ColIndex < i0G + N) || (Value == 0.0), "Matrix is expected to be block-diagonal.");
                        }
#endif
                        if (iIter == 0)
                        {
                            for (int m = 0; m < N; m++)
                            {
                                Mtx[n, m] = this.m_LaplaceMatrix[i0G + n, i0G + m];
                            }
                        }
                        else
                        {
#if DEBUG
                            for (int m = 0; m < N; m++)
                            {
                                Debug.Assert(Mtx[n, m] == this.m_LaplaceMatrix[i0G + n, i0G + m]);
                            }
#endif
                        }
                        Rhs[n] = -this.m_LaplaceAffine[i0L + n];
                    }
                }

                // solve
                double[] sol = new double[N];
                Mtx.Solve(sol, Rhs);
                ChangeNorm = GenericBlas.L2Dist(sol, Phi.Coordinates.GetRow(jCell));
                Phi.Coordinates.SetRow(jCell, sol);

                if (ChangeNorm / CellVolume < 1.0e-10)
                {
                    break;
                }
            }


            //Console.WriteLine("Final change norm: {0}, \t iter: {1}", ChangeNorm, iIter );

            if (ChangeNorm > 1.0e-6)
            {
                Console.WriteLine("  local solver funky in cell: " + jCell + ", last iteration change norm = " + ChangeNorm);
            }


            Stpw_tot.Stop();
            return(true);
        }