//========================
        // Kinetic energy equation
        //========================
        #region energy

        public static void AddSpeciesKineticEnergyEquation(XSpatialOperatorMk2 XOp, IEnergy_Configuration config, int D, string spcName, SpeciesId spcId,
                                                           IncompressibleMultiphaseBoundaryCondMap BcMap, LevelSetTracker LsTrk)
        {
            // check input
            if (XOp.IsCommited)
            {
                throw new InvalidOperationException("Spatial Operator is already comitted. Adding of new components is not allowed");
            }

            string CodName = EquationNames.KineticEnergyEquation;

            if (!XOp.CodomainVar.Contains(CodName))
            {
                throw new ArgumentException("CoDomain variable \"" + CodName + "\" is not defined in Spatial Operator");
            }

            PhysicalParameters   physParams = config.getPhysParams;
            DoNotTouchParameters dntParams  = config.getDntParams;

            bool laplaceKinE = (config.getKinEviscousDiscretization == KineticEnergyViscousSourceTerms.laplaceKinE);
            bool divergenceP = (config.getKinEpressureDiscretization == KineticEnergyPressureSourceTerms.divergence);

            // set species arguments
            double rhoSpc, LFFSpc, muSpc;

            switch (spcName)
            {
            case "A": { rhoSpc = physParams.rho_A; LFFSpc = dntParams.LFFA; muSpc = physParams.mu_A; break; }

            case "B": { rhoSpc = physParams.rho_B; LFFSpc = dntParams.LFFB; muSpc = physParams.mu_B; break; }

            default: throw new ArgumentException("Unknown species.");
            }

            // set components
            var comps = XOp.EquationComponents[CodName];

            // convective part
            // ================
            if (config.isTransport)
            {
                var convK = new KineticEnergyConvectionInSpeciesBulk(D, BcMap, spcName, spcId, rhoSpc, LFFSpc, LsTrk);
                //var convK = new KineticEnergyConvectionInSpeciesBulk_Upwind(D, BcMap, spcName, spcId, rhoSpc);
                comps.Add(convK);
            }

            // viscous terms
            // =============
            if (config.isViscous && !(muSpc == 0.0))
            {
                double penalty = dntParams.PenaltySafety;

                // Laplace of kinetic energy
                // =========================
                if (laplaceKinE)
                {
                    var kELap = new KineticEnergyLaplaceInSpeciesBulk(
                        dntParams.UseGhostPenalties ? 0.0 : penalty, 1.0,
                        BcMap, spcName, spcId, D, physParams.mu_A, physParams.mu_B);
                    comps.Add(kELap);

                    if (dntParams.UseGhostPenalties)
                    {
                        var kELapPenalty = new KineticEnergyLaplaceInSpeciesBulk(
                            penalty, 0.0,
                            BcMap, spcName, spcId, D, physParams.mu_A, physParams.mu_B);
                        XOp.GhostEdgesOperator.EquationComponents[CodName].Add(kELapPenalty);
                    }
                }

                // Divergence of stress tensor
                // ===========================
                {
                    if (config.getKinEviscousDiscretization == KineticEnergyViscousSourceTerms.fluxFormulation)
                    {
                        comps.Add(new StressDivergenceInSpeciesBulk(D, BcMap, spcName, spcId, muSpc, transposed: !laplaceKinE));
                    }

                    if (config.getKinEviscousDiscretization == KineticEnergyViscousSourceTerms.local)
                    {
                        comps.Add(new StressDivergence_Local(D, muSpc, spcName, spcId, transposed: !laplaceKinE));
                    }
                }

                // Dissipation
                // ===========
                {
                    comps.Add(new Dissipation(D, muSpc, spcName, spcId, _withPressure: config.withPressureDissipation));
                }
            }

            // pressure term
            // =============
            if (config.isPressureGradient)
            {
                if (divergenceP)
                {
                    comps.Add(new DivergencePressureEnergyInSpeciesBulk(D, BcMap, spcName, spcId));
                    //comps.Add(new ConvectivePressureTerm_LLF(D, BcMap, spcName, spcId, LFFSpc, LsTrk));
                }
                else
                {
                    comps.Add(new PressureGradientConvection(D, spcName, spcId));
                }
            }

            // gravity (volume forces)
            // =======================
            {
                comps.Add(new PowerofGravity(D, spcName, spcId, rhoSpc));
            }
        }
        public static void AddInterfaceKineticEnergyEquation(XSpatialOperatorMk2 XOp, IEnergy_Configuration config, int D,
                                                             IncompressibleMultiphaseBoundaryCondMap BcMap, LevelSetTracker LsTrk, int degU)
        {
            // check input
            if (XOp.IsCommited)
            {
                throw new InvalidOperationException("Spatial Operator is already comitted. Adding of new components is not allowed");
            }

            string CodName = EquationNames.KineticEnergyEquation;

            if (!XOp.CodomainVar.Contains(CodName))
            {
                throw new ArgumentException("CoDomain variable \"" + CodName + "\" is not defined in Spatial Operator");
            }

            PhysicalParameters   physParams = config.getPhysParams;
            DoNotTouchParameters dntParams  = config.getDntParams;

            bool laplaceKinE = (config.getKinEviscousDiscretization == KineticEnergyViscousSourceTerms.laplaceKinE);
            bool divergenceP = (config.getKinEpressureDiscretization == KineticEnergyPressureSourceTerms.divergence);

            // set species arguments
            double rhoA  = physParams.rho_A;
            double rhoB  = physParams.rho_B;
            double LFFA  = dntParams.LFFA;
            double LFFB  = dntParams.LFFB;
            double muA   = physParams.mu_A;
            double muB   = physParams.mu_B;
            double sigma = physParams.Sigma;

            double penalty_base = (degU + 1) * (degU + D) / D;
            double penalty      = penalty_base * dntParams.PenaltySafety;

            // set components
            var comps = XOp.EquationComponents[CodName];

            // convective part
            // ================
            if (config.isTransport)
            {
                comps.Add(new KineticEnergyConvectionAtLevelSet(D, LsTrk, rhoA, rhoB, LFFA, LFFB, physParams.Material, BcMap, config.isMovingMesh));
            }

            // viscous terms
            // =============
            if (config.isViscous && (!(muA == 0.0) && !(muB == 0.0)))
            {
                if (laplaceKinE)
                {
                    comps.Add(new KineticEnergyLaplaceAtInterface(LsTrk, muA, muB, penalty * 1.0));
                }

                if (config.getKinEviscousDiscretization == KineticEnergyViscousSourceTerms.fluxFormulation)
                {
                    comps.Add(new StressDivergenceAtLevelSet(LsTrk, muA, muB, transposed: !laplaceKinE));
                }
            }


            // pressure term
            // =============
            if (config.isPressureGradient)
            {
                if (divergenceP)
                {
                    comps.Add(new DivergencePressureEnergyAtLevelSet(LsTrk));
                    //comps.Add(new ConvectivePressureTermAtLevelSet_LLF(D, LsTrk, LFFA, LFFB, physParams.Material, BcMap, config.isMovingMesh));
                }
            }

            // surface energy
            // ==============
            {
                comps.Add(new SurfaceEnergy(D, LsTrk, sigma, rhoA, rhoB));
            }
        }