/// <summary>
        /// Matrix/Affine assembly in the case of an implicit RK stage.
        /// </summary>
        protected override void AssembleMatrixCallback(out BlockMsrMatrix System, out double[] Affine, out BlockMsrMatrix PcMassMatrix, DGField[] argCurSt, bool Linearization)
        {
            if (Linearization == false)
            {
                throw new NotImplementedException("todo");
            }

            int Ndof = m_CurrentState.Count;

            // copy data from 'argCurSt' to 'CurrentStateMapping', if necessary
            // -----------------------------------------------------------
            DGField[] locCurSt = CurrentStateMapping.Fields.ToArray();
            if (locCurSt.Length != argCurSt.Length)
            {
                throw new ApplicationException();
            }
            int NF = locCurSt.Length;

            for (int iF = 0; iF < NF; iF++)
            {
                if (object.ReferenceEquals(locCurSt[iF], argCurSt[iF]))
                {
                    // nothing to do
                }
                else
                {
                    locCurSt[iF].Clear();
                    locCurSt[iF].Acc(1.0, argCurSt[iF]);
                }
            }

            // update of level-set
            // ----------------------

            bool updateAgglom = false;

            if (this.Config_LevelSetHandling == LevelSetHandling.Coupled_Once && m_ImplStParams.m_IterationCounter == 0 ||
                this.Config_LevelSetHandling == LevelSetHandling.Coupled_Iterative)
            {
                //MoveLevelSetAndRelatedStuff(locCurSt, m_CurrentPhystime, m_CurrentDt, 1.0);
                if (Math.Abs(m_ImplStParams.m_ActualLevSetRelTime - m_ImplStParams.m_RelTime) > 1.0e-14)
                {
                    if (m_ImplStParams.m_IterationCounter <= 0)// only push tracker in the first iter
                    {
                        m_LsTrk.PushStacks();
                    }
                    MoveLevelSetAndRelatedStuff(locCurSt,
                                                m_ImplStParams.m_CurrentPhystime, m_ImplStParams.m_CurrentDt * m_ImplStParams.m_RelTime, IterUnderrelax,
                                                m_ImplStParams.m_Mass, m_ImplStParams.m_k);

                    // note that we need to update the agglomeration
                    updateAgglom = true;
                }
            }

            // update agglomeration
            // --------------------
#if DEBUG
            if (this.Config_LevelSetHandling == LevelSetHandling.LieSplitting || this.Config_LevelSetHandling == LevelSetHandling.StrangSplitting)
            {
                Debug.Assert(m_CurrentAgglomeration != null);
                Debug.Assert(m_PrecondMassMatrix != null);
                // ensure, that, when splitting is used we update the agglomerator in the very first iteration.
            }
#endif
            if (updateAgglom || m_CurrentAgglomeration == null)
            {
                this.UpdateAgglom(m_ImplStParams.m_IterationCounter > 0);



                // update Multigrid-XDG basis
                base.MultigridBasis.UpdateXdgAggregationBasis(m_CurrentAgglomeration);
            }


            // mass matrix update
            // ------------------

            if (this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsIdentity)
            {
                // may occur e.g. if one runs the FSI solver as a pure single-phase solver,
                // i.e. if the Level-Set is outside the domain.

                PcMassMatrix = null;
            }
            else
            {
                // checks:
                Debug.Assert(this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsNonIdentity ||
                             this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsTimeDependent ||
                             this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsTimeAndSolutionDependent,
                             "Something is not implemented here.");
                if (this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsNonIdentity &&
                    Config_LevelSetHandling != LevelSetHandling.None)
                {
                    throw new NotSupportedException("Illegal configuration;");
                }

                if (this.Config_LevelSetHandling == LevelSetHandling.Coupled_Once || this.Config_LevelSetHandling == LevelSetHandling.Coupled_Iterative)
                {
                    if ((this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsNonIdentity && m_PrecondMassMatrix == null) || // compute mass matrix (only once in application lifetime)
                        (this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsTimeDependent && m_ImplStParams.m_IterationCounter == 0) || // compute mass matrix once per timestep
                        (this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsTimeAndSolutionDependent) // re-compute mass matrix in every iteration
                        )
                    {
                        BlockMsrMatrix PM, SM;
                        UpdateMassMatrix(out PM, out SM, m_ImplStParams.m_CurrentPhystime + m_ImplStParams.m_CurrentDt * m_ImplStParams.m_RelTime);
                        m_ImplStParams.m_Mass[1] = SM;
                        m_PrecondMassMatrix      = PM;
                    }
                }

                PcMassMatrix = m_PrecondMassMatrix;
            }

            // operator matrix update
            // ----------------------

            // we perform the extrapolation to have valid parameters if
            // - the operator matrix depends on these values
            this.m_CurrentAgglomeration.Extrapolate(CurrentStateMapping);

            var OpMatrix = new BlockMsrMatrix(CurrentStateMapping);
            var OpAffine = new double[Ndof];

            this.ComputeOperatorMatrix(OpMatrix, OpAffine, CurrentStateMapping, locCurSt, base.GetAgglomeratedLengthScales(), m_ImplStParams.m_CurrentPhystime + m_ImplStParams.m_CurrentDt * m_ImplStParams.m_RelTime);


            // assemble system
            // ---------------

            double dt = m_ImplStParams.m_CurrentDt;

            // select mass matrix (and some checks)
            double[]       RHS = new double[Ndof];
            BlockMsrMatrix MamaRHS, MamaLHS;
            if (this.Config_LevelSetHandling == LevelSetHandling.Coupled_Once ||
                this.Config_LevelSetHandling == LevelSetHandling.Coupled_Iterative)
            {
                Debug.Assert(m_ImplStParams.m_Mass.Length == 2);
                MamaRHS = m_ImplStParams.m_Mass[0];
                MamaLHS = m_ImplStParams.m_Mass[1];
            }
            else if (this.Config_LevelSetHandling == LevelSetHandling.LieSplitting ||
                     this.Config_LevelSetHandling == LevelSetHandling.StrangSplitting ||
                     this.Config_LevelSetHandling == LevelSetHandling.None)
            {
                Debug.Assert(m_ImplStParams.m_Mass.Length == 1);
                MamaRHS = m_ImplStParams.m_Mass[0];
                MamaLHS = m_ImplStParams.m_Mass[0];
            }
            else
            {
                throw new NotImplementedException();
            }

            // right-hand-side, resp. affine vector
            if (MamaRHS != null)
            {
                MamaRHS.SpMV(1.0 / dt, m_ImplStParams.m_u0, 0.0, RHS);
            }
            else
            {
                Debug.Assert(this.Config_MassMatrixShapeandDependence == MassMatrixShapeandDependence.IsIdentity);
                RHS.SetV(m_ImplStParams.m_u0, 1.0 / dt);
            }
            for (int l = 0; l < m_ImplStParams.m_s; l++)
            {
                if (m_ImplStParams.m_RK_as[l] != 0.0)
                {
                    RHS.AccV(-m_ImplStParams.m_RK_as[l], m_ImplStParams.m_k[l]);
                }
            }

            Affine = RHS;
            Affine.ScaleV(-1.0);
            Affine.AccV(m_ImplStParams.m_RK_as[m_ImplStParams.m_s], OpAffine);

            // left-hand-side
            System = OpMatrix.CloneAs();
            System.Scale(m_ImplStParams.m_RK_as[m_ImplStParams.m_s]);
            if (MamaLHS != null)
            {
                System.Acc(1.0 / dt, MamaLHS);
            }
            else
            {
                System.AccEyeSp(1.0 / dt);
            }

            // perform agglomeration
            // ---------------------
            Debug.Assert(object.ReferenceEquals(m_CurrentAgglomeration.Tracker, m_LsTrk));
            m_CurrentAgglomeration.ManipulateMatrixAndRHS(System, Affine, CurrentStateMapping, CurrentStateMapping);

            // increase iteration counter
            // --------------------------
            m_ImplStParams.m_IterationCounter++;
        }