/// <summary>
        /// Accumulates the DG-projection (with respect to the DG-basis
        /// of this field, <see cref="Basis"/>) of
        /// <paramref name="alpha"/>*<paramref name="f"/>^<paramref name="pow"/> to this field;
        /// </summary>
        /// <param name="alpha">scaling for accumulation</param>
        /// <param name="f">operand</param>
        /// <param name="pow">exponent</param>
        /// <param name="em">
        /// An optional restriction to the domain in which the projection is
        /// computed (it may, e.g. be only required in boundary cells, so a
        /// computation over the whole domain would be a waste of computation
        /// power. A proper execution mask would be see e.g.
        /// <see cref="GridData.BoundaryCells"/>.)
        /// if null, the computation is carried out in the whole domain;
        /// </param>
        virtual public void ProjectPow(double alpha, DGField f, double pow, CellMask em)
        {
            if (!object.ReferenceEquals(f.Basis.GridDat, this.Basis.GridDat))
            {
                throw new ArgumentException("field is associated to another context.", "a");
            }

            SpatialOperator powOp = new SpatialOperator(new string[] { "f" },
                                                        new string[] { "res" },
                                                        QuadOrderFunc.SumOfMaxDegrees());

            powOp.EquationComponents["res"].Add(new PowSource(pow));
            powOp.Commit();

            CoordinateVector coDom = this.CoordinateVector;

            SpatialOperator.Evaluator ev = powOp.GetEvaluatorEx(
                new CoordinateMapping(f), null, coDom.Mapping,
                edgeQrCtx: new EdgeQuadratureScheme(true, EdgeMask.GetEmptyMask(this.Basis.GridDat)),
                volQrCtx: new CellQuadratureScheme(true, em));

            ev.Evaluate <CoordinateVector>(alpha, 1.0, coDom); // only sources, no edge integrals required
        }
        /// <summary>
        /// checks whether the linear and nonlinear implementation of operator evaluation are mathematically equal
        /// </summary>
        void LinearNonlinComparisonTest()
        {
            int L = this.bnd.CoordinateVector.Count();

            // need to assure to use the same quadrature oder on both evaluation variants
            var volQrSch = (new CellQuadratureScheme(false, CellMask.GetFullMask(this.GridData, MaskType.Geometrical)))
                           .AddFixedOrderRules(this.GridData, this.PolynomialDegree * 3);
            var edgQrSch = new EdgeQuadratureScheme(true, EdgeMask.GetFullMask(this.GridData, MaskType.Geometrical))
                           .AddFixedOrderRules(this.GridData, this.PolynomialDegree * 3);

            //var volQrSch = new CellQuadratureScheme(true, CellMask.GetEmptyMask(this.GridData));
            //var edgQrSch = new EdgeQuadratureScheme(true, EdgeMask.GetEmptyMask(this.GridData));
            //var edgQrSch = new EdgeQuadratureScheme(true, this.GridData.BoundaryEdges)
            //                        .AddFixedOrderRules(this.GridData, this.PolynomialDegree * 3);
            //var edgQrSch = new EdgeQuadratureScheme(true, this.GridData.BoundaryEdges.Complement())
            //                          .AddFixedOrderRules(this.GridData, this.PolynomialDegree * 3);



            for (int run = 0; run < 1; run++)
            {
                // setup a random test vector
                Random rnd          = new Random();
                var    TestArgument = this.bnd.CloneAs().CoordinateVector;
                for (int i = 0; i < L; i++)
                {
                    TestArgument[i] = rnd.NextDouble();
                }

                Stopwatch lin = new Stopwatch();
                Stopwatch nol = new Stopwatch();

                // linear evaluation
                CoordinateVector LinResult = this.ViscU_linear.CoordinateVector;
                LinResult.Clear();
                lin.Start();
                {
                    var map             = this.U.Mapping;
                    var tempOperatorMtx = new MsrMatrix(map, map);
                    var tempAffine      = new double[L];
                    Operator.ComputeMatrixEx(map, new DGField[] { this.mu }, map,
                                             tempOperatorMtx, tempAffine,
                                             volQuadScheme: volQrSch, edgeQuadScheme: edgQrSch);


                    tempOperatorMtx.SpMVpara(1.0, TestArgument, 0.0, LinResult);
                    LinResult.AccV(1.0, tempAffine);
                }
                lin.Stop();

                // nonliner evaluation
                CoordinateVector NolResult = this.ViscU_nonlinear.CoordinateVector;
                NolResult.Clear();
                nol.Start();
                {
                    var evaluator = Operator.GetEvaluatorEx(TestArgument.Mapping, new DGField[] { this.mu }, this.Residual.Mapping,
                                                            volQrCtx: volQrSch, edgeQrCtx: edgQrSch);
                    evaluator.Evaluate(1.0, 0.0, NolResult);
                }
                nol.Stop();

                double L2Dist = GenericBlas.L2DistPow2(LinResult, NolResult).MPISum().Sqrt();
                Console.WriteLine("L2 dist of linear/Nonlinear evaluation comparison: {0}", L2Dist);

                LinResult.Acc(-1.0, NolResult);

                foreach (SinglePhaseField DGfield in LinResult.Mapping.Fields)
                {
                    for (int p = 0; p <= DGfield.Basis.Degree; p++)
                    {
                        double L2err_p = DGfield.L2NormPerMode(p);
                        Console.WriteLine("   ERR{2} {1} \t{0}", L2err_p, DGfield.Identification, p);
                    }
                }

                Console.WriteLine("Time linear {0}, time nonlinear: {1}", lin.Elapsed, nol.Elapsed);

                Assert.LessOrEqual(L2Dist, 1.0e-4, "L2 distance between linear and nonlinear evaluation of the same flux.");
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="jCell"></param>
        /// <param name="AcceptedMask"></param>
        /// <param name="Phi"></param>
        /// <param name="gradPhi"></param>
        /// <param name="__DiffusionCoeff">Output: if artificial diffusion is turned</param>
        /// <param name="MaxAllowedPhi">Input: upper threshold for the values of <paramref name="Phi"/> in cell <see cref="jCell"/>.</param>
        /// <param name="MinAllowedPhi">Input: lower threshold for the values of <paramref name="Phi"/> in cell <see cref="jCell"/>.</param>
        /// <returns></returns>
        public bool LocalSolve_Iterative(int jCell, BitArray AcceptedMask, SinglePhaseField Phi, VectorField <SinglePhaseField> gradPhi, SinglePhaseField __DiffusionCoeff, double MaxAllowedPhi, double MinAllowedPhi)
        {
            //this.LocalSolve_Geometric(jCell, AcceptedMask, Phi, +1, out MinAllowedPhi, out MaxAllowedPhi);) {
            int N   = this.LevelSetBasis.GetLength(jCell);
            int i0G = this.LevelSetMapping.GlobalUniqueCoordinateIndex(0, jCell, 0);
            int i0L = this.LevelSetMapping.LocalUniqueCoordinateIndex(0, jCell, 0);

            SinglePhaseField __AcceptedMask = new SinglePhaseField(new Basis(this.GridDat, 0), "accepted");

            for (int j = 0; j < AcceptedMask.Length; j++)
            {
                __AcceptedMask.SetMeanValue(j, AcceptedMask[j] ? 1.0 : 0.0);
            }


            // subgrid on which we are working, consisting only of one cell
            SubGrid jCellGrid = new SubGrid(new CellMask(this.GridDat, Chunk.GetSingleElementChunk(jCell)));

            // create spatial operator
            IEvaluatorNonLin evo;
            {
                SpatialOperator op = new SpatialOperator(1, 2, 1, QuadOrderFunc.NonLinear(2), "Phi", "dPhi_dx0", "dPhi_dx1", "cod1");
                op.EquationComponents["cod1"].Add(new ReinitOperator());
                op.EdgeQuadraturSchemeProvider   = g => (new EdgeQuadratureScheme(domain: EdgeMask.GetEmptyMask(g)));
                op.VolumeQuadraturSchemeProvider = g => (new CellQuadratureScheme(domain: jCellGrid.VolumeMask));
                op.Commit();

                evo = op.GetEvaluatorEx(Phi.Mapping.Fields, gradPhi.Mapping.Fields, Phi.Mapping);
                evo.ActivateSubgridBoundary(jCellGrid.VolumeMask, subGridBoundaryTreatment: SubGridBoundaryModes.InnerEdge);
            }

            // create artificial diffusion operator
            MultidimensionalArray DiffMtx;

            double[]        DiffRhs;
            SpatialOperator dop;
            {
                double penaltyBase = this.LevelSetBasis.Degree + 2;
                penaltyBase = penaltyBase.Pow2();

                dop = (new ArtificialViscosity(AcceptedMask, penaltyBase, GridDat.Cells.h_min, jCell, -1.0)).Operator(1);

                MsrMatrix _DiffMtx = new MsrMatrix(this.LevelSetMapping, this.LevelSetMapping);
                double[]  _DiffRhs = new double[this.LevelSetMapping.LocalLength];

                dop.ComputeMatrixEx(this.LevelSetMapping, new DGField[] { Phi, null, null }, this.LevelSetMapping,
                                    _DiffMtx, _DiffRhs, OnlyAffine: false,
                                    edgeQuadScheme: (new EdgeQuadratureScheme(domain: jCellGrid.AllEdgesMask)),
                                    volQuadScheme: (new CellQuadratureScheme(domain: jCellGrid.VolumeMask)));

                // extract matrix for 'jCell'
                DiffMtx = MultidimensionalArray.Create(N, N);
                DiffRhs = new double[N];
                for (int n = 0; n < N; n++)
                {
#if DEBUG
                    int      Lr;
                    int[]    row_cols = null;
                    double[] row_vals = null;
                    Lr = _DiffMtx.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
                    for (int m = 0; m < N; m++)
                    {
                        DiffMtx[n, m] = _DiffMtx[i0G + n, i0G + m];
                    }
                    DiffRhs[n] = _DiffRhs[i0L + n];
                }

#if DEBUG
                var Test = DiffMtx.Transpose();
                Test.Acc(-1.0, DiffMtx);
                Debug.Assert(Test.InfNorm() <= 1.0e-8);
#endif
            }

            // find 'good' initial value by geometric solve AND
            // thresholds for the maximum an minimal value of Phi
            double Range = MaxAllowedPhi - MinAllowedPhi;
            MinAllowedPhi -= 0.1 * Range;
            MaxAllowedPhi += 0.1 * Range;


            // timestep for pseudo-timestepping
            double dt = 0.5 * this.GridDat.Cells.h_min[jCell] / (((double)(this.LevelSetBasis.Degree)).Pow2());


            DGField[] PlotFields = new DGField[] { Phi, gradPhi[0], gradPhi[1], __DiffusionCoeff, __AcceptedMask };
            //Tecplot.Tecplot.PlotFields(Params, "itt_0", "EllipicReinit", 0, 3);

            double[] PrevVal = new double[N];
            double[] NextVal = new double[N];
            Phi.Coordinates.GetRow(jCell, PrevVal);

            // pseudo-timestepping
            //if(jCell == 80)
            //    Tecplot.Tecplot.PlotFields(PlotFields, "itt_0", "EllipicReinit", 0, 3);
            //Console.Write("  Local solve cell " + jCell + " ... ");

            bool   converged      = false;
            double DiffusionCoeff = 0;
            int    IterGrowCount  = 0; // number of iterations in which the residual grew
            double LastResi       = double.NaN;
            for (int iIter = 0; iIter < 1000; iIter++)
            {
                //Console.Write("  Local solve iteration " + iIter + " ... ");
                PerformRKstep(dt, jCell, AcceptedMask, Phi, gradPhi, evo);

                __DiffusionCoeff.SetMeanValue(jCell, DiffusionCoeff);

                if (jCell == 80)
                {
                    DiffusionCoeff = 0.1;
                }
                if (DiffusionCoeff > 0)
                {
                    //Console.WriteLine(" Diffusion on.");


                    double[] _DiffRhs = new double[this.LevelSetMapping.LocalLength];


                    dop.ComputeMatrixEx(this.LevelSetMapping, new DGField[] { Phi, gradPhi[0], gradPhi[1] }, this.LevelSetMapping,
                                        default(MsrMatrix), _DiffRhs, OnlyAffine: true,
                                        edgeQuadScheme: (new EdgeQuadratureScheme(domain: jCellGrid.AllEdgesMask)),
                                        volQuadScheme: (new CellQuadratureScheme(domain: CellMask.GetEmptyMask(this.GridDat))));

                    // extract matrix for 'jCell'
                    for (int n = 0; n < N; n++)
                    {
                        DiffRhs[n] = _DiffRhs[i0L + n];
                    }

                    PerformArtificialDiffusion(dt * DiffusionCoeff, jCell, Phi, DiffMtx, DiffRhs);
                }
                Phi.Coordinates.GetRow(jCell, NextVal);
                double   resi = Math.Sqrt(GenericBlas.L2DistPow2(NextVal, PrevVal) / GenericBlas.L2NormPow2(PrevVal));
                double[] A    = NextVal;
                NextVal = PrevVal;
                PrevVal = A;
                if (iIter > 0 && resi > LastResi)
                {
                    IterGrowCount++;
                }
                else
                {
                    IterGrowCount = 0;
                }
                LastResi = resi;


                if (resi < 1.0e-10)
                {
                    converged = true;
                    break;
                }

                double maxPhi, minPhi;
                Phi.GetExtremalValuesInCell(out minPhi, out maxPhi, jCell);

                bool MinAlarm  = minPhi < MinAllowedPhi;
                bool Maxalarm  = maxPhi > MaxAllowedPhi;
                bool GrowAlarm = IterGrowCount > 4;
                bool IterAlarm = iIter >= 50;

                if (MinAlarm || Maxalarm || GrowAlarm)
                {
                    // Diffusion coefficient should be increased
                    if (DiffusionCoeff == 0)
                    {
                        DiffusionCoeff = 1.0e-2;
                    }
                    else
                    {
                        if (DiffusionCoeff < 1.0e3)
                        {
                            DiffusionCoeff *= 2;
                        }
                    }
                    //Console.WriteLine("   increasing Diffusion: {0}, Alarms : {1}{2}{3}{4}", DiffusionCoeff, MinAlarm ? 1 : 0, Maxalarm ? 1 : 0, GrowAlarm ? 1 : 0, IterAlarm ? 1 : 0);
                }


                //if(jCell == 80 && iIter < 100)
                //    Tecplot.Tecplot.PlotFields(PlotFields, "itt_" + (iIter + 1), "EllipicReinit", iIter + 1, 3);
            }


            return(converged);
        }
 /// <summary>
 /// Legacy interface
 /// </summary>
 static public IEvaluatorNonLin GetEvaluator(this SpatialOperator op, IList <DGField> DomainVarMap, UnsetteledCoordinateMapping CodomainVarMap)
 {
     return(op.GetEvaluatorEx(DomainVarMap, null, CodomainVarMap));
 }
        /// <summary>
        /// Evaluates this operator for given DG fields;
        /// </summary>
        /// <param name="DomainMapping">
        /// the domain variables, or "input data" for the operator; the number
        /// of elements must be equal to the number of elements in the
        /// <see cref="DomainVar"/>-list;
        /// </param>
        /// <param name="Params">
        /// List of parameter fields; May be null
        /// </param>
        /// <param name="CodomainMapping">
        /// the co-domain variables, or "output" for the evaluation of the
        /// operator; the number of elements must be equal to the number of
        /// elements in the <see cref="CodomainVar"/>-list;
        /// </param>
        /// <param name="alpha">
        /// scaling of the operator
        /// </param>
        /// <param name="beta">
        /// scaling of the accumulator (<paramref name="CodomainMapping"/>);
        /// </param>
        /// <param name="sgrd">
        /// subgrid, for restricted evaluation; null indicates evaluation on
        /// the full grid.
        /// </param>
        /// <param name="bndMode">
        /// Treatment of subgrid boundaries, if <paramref name="sgrd"/> is not
        /// null. See <see cref="Evaluator"/>
        /// </param>
        /// <param name="qInsEdge">
        /// Optional definition of the edge quadrature scheme. Since this
        /// already implies a domain of integration, must be null if
        /// <paramref name="sgrd"/> is not null.
        /// </param>
        /// <param name="qInsVol">
        /// Optional definition of the volume quadrature scheme. Since this
        /// already implies a domain of integration, must be null if
        /// <paramref name="sgrd"/> is not null.
        /// </param>
        /// <remarks>
        /// If some of the input data, <paramref name="DomainMapping"/>, is
        /// contained in the  output data, <paramref name="CodomainMapping"/>,
        /// these DG fields will be cloned to ensure correct operation of the
        /// operator evaluation.<br/>
        /// It is not a good choice to use this function if this operator
        /// should be evaluated multiple times and  contains linear components
        /// (i.e. <see cref="ContainsLinear"/> returns true); If the latter is
        /// the case, the matrix which represents the linear components of the
        /// operator must be computed first, which is computational- and
        /// memory-intensive; After execution of this method, the matrix will
        /// be lost; If multiple evaluation is desired, the
        /// <see cref="Evaluator"/>-class should be used, in which the matrix
        /// of the operator will persist; However, if no linear components are
        /// present, the performance of this function should be almost
        /// comparable to the use of the <see cref="Evaluator"/>-class;
        /// </remarks>
        static public void Evaluate(
            this SpatialOperator op,
            double alpha, double beta,
            CoordinateMapping DomainMapping, IList <DGField> Params, CoordinateMapping CodomainMapping,
            SubGrid sgrd = null,
            EdgeQuadratureScheme qInsEdge = null,
            CellQuadratureScheme qInsVol  = null,
            SpatialOperator.SubGridBoundaryModes bndMode = SubGridBoundaryModes.OpenBoundary,
            double time = double.NaN)                  //
        {
            using (new FuncTrace()) {
                if (sgrd != null && (qInsEdge != null || qInsVol != null))
                {
                    throw new ArgumentException("Specification of Subgrid and quadrature schemes is exclusive: not allowed to specify both at the same time.", "sgrd");
                }
#if DEBUG
                op.Verify(); //this has already been done during this.Commit()
#endif

                IList <DGField> _DomainFields          = DomainMapping.Fields;
                IList <DGField> _CodomainFields        = CodomainMapping.Fields;
                DGField[]       _DomainFieldsRevisited = new DGField[_DomainFields.Count];

                bool a = false;
                for (int i = 0; i < _DomainFields.Count; i++)
                {
                    DGField f = _DomainFields[i];

                    if (_CodomainFields.Contains(f))
                    {
                        // some of the domain variables (input data)
                        // is also a member of the codomain variables (output);
                        // the data need to be cloned to provide correct results
                        a = true;
                        _DomainFieldsRevisited[i] = (DGField)f.Clone();
                    }
                    else
                    {
                        _DomainFieldsRevisited[i] = f;
                    }
                }

                CoordinateMapping domainMappingRevisited;
                if (a)
                {
                    domainMappingRevisited = new CoordinateMapping(_DomainFieldsRevisited);
                }
                else
                {
                    domainMappingRevisited = DomainMapping;
                }

                if (sgrd != null)
                {
                    CellMask cm = (sgrd == null) ? null : sgrd.VolumeMask;
                    EdgeMask em = (sgrd == null) ? null : sgrd.AllEdgesMask;

                    qInsEdge = new EdgeQuadratureScheme(true, em);
                    qInsVol  = new CellQuadratureScheme(true, cm);
                }

                var ev = op.GetEvaluatorEx(
                    domainMappingRevisited, Params, CodomainMapping,
                    qInsEdge,
                    qInsVol);

                if (sgrd != null)
                {
                    ev.ActivateSubgridBoundary(sgrd.VolumeMask, bndMode);
                }
                CoordinateVector outp = new CoordinateVector(CodomainMapping);
                ev.time = time;
                ev.Evaluate <CoordinateVector>(alpha, beta, outp);
            }
        }
        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="VelocityMapping"></param>
        /// <param name="VelocityVectorMapping"></param>
        /// <param name="PressureMapping"></param>
        /// <param name="SolverConf"></param>
        /// <param name="Velocity0"></param>
        /// <param name="Velocity0Mean"></param>
        /// <param name="Phi0"></param>
        /// <param name="Phi0Mean"></param>
        /// <param name="eta">
        /// Dynamic viscosity for viscous (Laplace) operator.
        /// </param>
        public OperatorFactoryFlowFieldVariableDensity(UnsetteledCoordinateMapping VelocityMapping, UnsetteledCoordinateMapping VelocityVectorMapping,
                                                       UnsetteledCoordinateMapping PressureMapping,
                                                       SolverConfiguration SolverConf,
                                                       VectorField <SinglePhaseField> Velocity0, VectorField <SinglePhaseField> Velocity0Mean, SinglePhaseField Phi0, SinglePhaseField Phi0Mean,
                                                       SinglePhaseField eta, SinglePhaseField[] MassFractionsCurrent = null, SinglePhaseField[] MassFractionsMean = null)
        {
            this.Convection       = new SIMPLEOperator[SolverConf.SpatialDimension];
            this.Visc             = new SIMPLEOperator[SolverConf.SpatialDimension];
            this.Swip2            = new SIMPLEOperator[SolverConf.SpatialDimension];
            this.Swip3            = new SIMPLEOperator[SolverConf.SpatialDimension];
            this.PressureGradient = new SIMPLEOperator[SolverConf.SpatialDimension];
            this.DivergenceConti  = new SIMPLEOperator[SolverConf.SpatialDimension];

            for (int d = 0; d < SolverConf.SpatialDimension; d++)
            {
                switch (SolverConf.Control.PhysicsMode)
                {
                case PhysicsMode.Incompressible:
                    throw new ApplicationException("Wrong OperatorFactory");

                case PhysicsMode.LowMach:
                case PhysicsMode.Multiphase:
                    this.Convection[d] = new MomentumConvectionVariableDensityOperator(VelocityMapping, Velocity0, Velocity0Mean, Phi0, Phi0Mean, SolverConf, d);
                    break;

                default:
                    throw new NotImplementedException("PhysicsMode not implemented");
                }
                this.Visc[d]  = new MomentumViscousVariableDensityOperator(VelocityMapping, Phi0, SolverConf, d);
                this.Swip2[d] = new MomentumSwip2(VelocityMapping, VelocityVectorMapping, Phi0, SolverConf, d);
                switch (SolverConf.Control.PhysicsMode)
                {
                case PhysicsMode.LowMach:
                    this.Swip3[d]           = new MomentumSwip3(VelocityMapping, VelocityVectorMapping, Phi0, SolverConf, d);
                    this.DivergenceConti[d] = GetDivergenceContiOperator(PressureMapping, VelocityMapping, Phi0, SolverConf, d);
                    break;

                case PhysicsMode.Multiphase:
                    // Divergence of velocity is zero for smooth interface multiphase flows
                    this.Swip3[d]           = null;
                    this.DivergenceConti[d] = GetDivergenceContiOperator(PressureMapping, VelocityMapping, Phi0, SolverConf, d);
                    break;

                default:
                    throw new ApplicationException("PhysicsMode is not compatible with OperatorFactory.");
                }
                this.PressureGradient[d] = new MomentumPressureGradientOperator(VelocityMapping, PressureMapping, SolverConf, d);
            }

            VariableDensitySIMPLEControl varDensConf = SolverConf.Control as VariableDensitySIMPLEControl;

            if (varDensConf.Froude.HasValue)
            {
                this.BuoyantForce = new IEvaluatorNonLin[SolverConf.SpatialDimension];
                for (int d = 0; d < SolverConf.SpatialDimension; d++)
                {
                    SpatialOperator BuoyancyOperator = (new Buoyancy(varDensConf.GravityDirection,
                                                                     d,
                                                                     varDensConf.Froude.Value, SolverConf.Control.PhysicsMode,
                                                                     varDensConf.EoS)).Operator();

                    this.BuoyantForce[d] = BuoyancyOperator.GetEvaluatorEx(null, new DGField[] { Phi0 }, VelocityMapping);
                }
            }

            //this.PressureStabilization = new DivergencePressureStabilization(ctx, PressureMapping, SolverConf);
        }