private MultidimensionalArray GetBlock(BlockMsrMatrix target, bool ignoreVarCoupling, bool ignoreSpecCoupling, int iLoc, int jLoc)
        {
            var _Sblocks = MultidimensionalArray.Create(BMLoc.GetLengthOfCell(iLoc), BMLoc.GetLengthOfCell(jLoc));


            for (int iVar = 0; iVar < StructuredNi0[iLoc].Length; iVar++)     // loop over (row/codomain/test) variables
            {
                for (int jVar = 0; jVar < StructuredNi0[jLoc].Length; jVar++) // loop over (column/domain/trial) variables
                {
                    if (ignoreVarCoupling && jVar != iVar)
                    {
                        continue;
                    }
                    for (int iSpc = 0; iSpc < StructuredNi0[iLoc][iVar].Length; iSpc++)   // loop over species
                    {
                        for (int jSpc = 0; jSpc < StructuredNi0[jLoc][jVar].Length; jSpc++)
                        {
                            if (ignoreSpecCoupling && jSpc != iSpc)
                            {
                                continue;
                            }
                            for (int iMode = 0; iMode < StructuredNi0[iLoc][iVar][iSpc].Length; iMode++)
                            {
                                for (int jMode = 0; jMode < StructuredNi0[jLoc][jVar][jSpc].Length; jMode++)
                                {
                                    extNi0 RowNi0   = StructuredNi0[iLoc][iVar][iSpc][iMode];
                                    extNi0 ColNi0   = StructuredNi0[jLoc][jVar][jSpc][jMode];
                                    int    Targeti0 = RowNi0.Gi0;
                                    int    Targetj0 = ColNi0.Gi0;
                                    int    Subi0    = BMLoc.GetRelativeSubBlockOffset(iLoc, iVar, iSpc, iMode);
                                    int    Subj0    = BMLoc.GetRelativeSubBlockOffset(jLoc, jVar, jSpc, jMode);
                                    int    Subie    = Subi0 + RowNi0.N - 1;
                                    int    Subje    = Subj0 + ColNi0.N - 1;

                                    target.ReadBlock(Targeti0, Targetj0,
                                                     _Sblocks.ExtractSubArrayShallow(new int[] { Subi0, Subj0 }, new int[] { Subie, Subje }));
                                }
                            }
                        }
                    }
                }
            }

            return(_Sblocks);
        }
        /// <summary>
        /// Get the length of a cell within this mask
        /// </summary>
        /// <param name="iCell"></param>
        /// <returns></returns>
        public int GetLengthOfCell(int iCell)
        {
            int len = 0;

            Debug.Assert(iCell < m_StructuredNi0.Length);
            for (int i = 0; i < m_StructuredNi0[iCell].Length; i++)
            {
                for (int j = 0; j < m_StructuredNi0[iCell][i].Length; j++)
                {
                    for (int k = 0; k < m_StructuredNi0[iCell][i][j].Length; k++)
                    {
                        extNi0 block = m_StructuredNi0[iCell][i][j][k];
                        len += block.N;
                    }
                }
            }
            return(len);
        }
        /// <summary>
        /// Get the local index list of a cell within this mask
        /// </summary>
        /// <param name="iCell"></param>
        /// <returns></returns>
        public int[] GetLocalidcOfCell(int iCell)
        {
            List <int> cellidx = new List <int>();

            for (int i = 0; i < m_StructuredNi0[iCell].Length; i++)
            {
                for (int j = 0; j < m_StructuredNi0[iCell][i].Length; j++)
                {
                    for (int k = 0; k < m_StructuredNi0[iCell][i][j].Length; k++)
                    {
                        extNi0 block = m_StructuredNi0[iCell][i][j][k];
                        for (int m = 0; m < block.N; m++)
                        {
                            cellidx.Add(block.Li0 + m);
                        }
                    }
                }
            }
            int[] array = cellidx.ToArray();
            Debug.Assert(array.GroupBy(x => x).Any(g => g.Count() == 1));
            return(array);
        }
        private void AuxGetSubBlockMatrix(BlockMsrMatrix target, BlockMsrMatrix source, BlockMaskBase mask, bool ignoreCellCoupling, bool ignoreVarCoupling, bool ignoreSpecCoupling)
        {
            bool IsLocalMask = mask.GetType() == typeof(BlockMaskLoc);

            extNi0[][][][] RowNi0s = mask.m_StructuredNi0;
            extNi0[][][][] ColNi0s = this.StructuredNi0;

            int auxIdx = 0;

            for (int iLoc = 0; iLoc < RowNi0s.Length; iLoc++)
            {
                for (int jLoc = 0; jLoc < ColNi0s.Length; jLoc++)
                {
                    if (ignoreCellCoupling && jLoc != iLoc)
                    {
                        continue;
                    }
                    for (int iVar = 0; iVar < RowNi0s[iLoc].Length; iVar++)
                    {
                        for (int jVar = 0; jVar < ColNi0s[jLoc].Length; jVar++)
                        {
                            if (ignoreVarCoupling && jVar != iVar)
                            {
                                continue;
                            }
                            for (int iSpc = 0; iSpc < RowNi0s[iLoc][iVar].Length; iSpc++)
                            {
                                for (int jSpc = 0; jSpc < ColNi0s[jLoc][jVar].Length; jSpc++)
                                {
                                    if (ignoreSpecCoupling && jSpc != iSpc)
                                    {
                                        continue;
                                    }
                                    for (int iMode = 0; iMode < RowNi0s[iLoc][iVar][iSpc].Length; iMode++)
                                    {
                                        int Trgi0 = RowNi0s[iLoc][iVar][iSpc][iMode].Si0;
                                        for (int jMode = 0; jMode < ColNi0s[jLoc][jVar][jSpc].Length; jMode++)
                                        {
                                            extNi0 RowNi0 = RowNi0s[iLoc][iVar][iSpc][iMode];
                                            extNi0 ColNi0 = ColNi0s[jLoc][jVar][jSpc][jMode];
                                            int    Srci0  = IsLocalMask? RowNi0.Gi0: RowNi0.Li0 + source._RowPartitioning.i0 - m_map.LocalLength;
                                            int    Srcj0  = ColNi0.Gi0;

                                            var tmpBlock = MultidimensionalArray.Create(RowNi0.N, ColNi0.N);

                                            int Trgj0 = ColNi0s[jLoc][jVar][jSpc][jMode].Si0;
#if Debug
                                            SubMSR.ReadBlock(SubRowIdx, SubColIdx, tmpBlock);
                                            Debug.Assert(tmpBlock.Sum() == 0);
                                            Debug.Assert(tmpBlock.InfNorm() == 0);
#endif

                                            try {
                                                source.ReadBlock(Srci0, Srcj0,
                                                                 tmpBlock);
                                            } catch (Exception e) {
                                                Console.WriteLine("row: " + Srci0);
                                                Console.WriteLine("col: " + Srcj0);
                                                throw new Exception(e.Message);
                                            }
                                            Debug.Assert(Trgi0 < target.RowPartitioning.LocalLength);
                                            Debug.Assert(Trgj0 < target.ColPartition.LocalLength);


                                            target.AccBlock(Trgi0, Trgj0, 1.0, tmpBlock);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    auxIdx++;
                }
                auxIdx++;
            }
        }
        private MultidimensionalArray[] AuxGetSubBlocks(BlockMsrMatrix source, BlockMaskBase mask, bool ignoreCellCoupling, bool ignoreVarCoupling, bool ignoreSpecCoupling)
        {
            bool IsLocMask = mask.GetType() == typeof(BlockMaskLoc); // if external cells are masked, we have to consider other offsets ...

            int NoOfCells = mask.m_StructuredNi0.Length;
            int size      = ignoreCellCoupling ? NoOfCells : NoOfCells * NoOfCells;

            MultidimensionalArray[] Sblocks = new MultidimensionalArray[size];

            int auxIdx = 0;

            for (int iLoc = 0; iLoc < mask.m_StructuredNi0.Length; iLoc++)
            {
                for (int jLoc = 0; jLoc < mask.m_StructuredNi0.Length; jLoc++)
                {
                    if (ignoreCellCoupling && jLoc != iLoc)
                    {
                        continue;
                    }
                    int CellBlockLen = mask.GetLengthOfCell(jLoc);
                    Sblocks[auxIdx] = MultidimensionalArray.Create(CellBlockLen, CellBlockLen);
                    for (int iVar = 0; iVar < mask.m_StructuredNi0[iLoc].Length; iVar++)
                    {
                        for (int jVar = 0; jVar < mask.m_StructuredNi0[jLoc].Length; jVar++)
                        {
                            if (ignoreVarCoupling && jVar != iVar)
                            {
                                continue;
                            }
                            for (int iSpc = 0; iSpc < mask.m_StructuredNi0[iLoc][iVar].Length; iSpc++)
                            {
                                for (int jSpc = 0; jSpc < mask.m_StructuredNi0[jLoc][jVar].Length; jSpc++)
                                {
                                    if (ignoreSpecCoupling && jSpc != iSpc)
                                    {
                                        continue;
                                    }
                                    for (int iMode = 0; iMode < mask.m_StructuredNi0[iLoc][iVar][iSpc].Length; iMode++)
                                    {
                                        for (int jMode = 0; jMode < mask.m_StructuredNi0[jLoc][jVar][jSpc].Length; jMode++)
                                        {
                                            extNi0 RowNi0   = mask.m_StructuredNi0[iLoc][iVar][iSpc][iMode];
                                            extNi0 ColNi0   = mask.m_StructuredNi0[jLoc][jVar][jSpc][jMode];
                                            int    Targeti0 = IsLocMask ? RowNi0.Gi0 : RowNi0.Li0 + source._RowPartitioning.i0 - m_map.LocalLength;
                                            int    Targetj0 = ColNi0.Gi0;
                                            int    Subi0    = mask.GetRelativeSubBlockOffset(iLoc, iVar, iSpc, iMode);
                                            int    Subj0    = mask.GetRelativeSubBlockOffset(jLoc, jVar, jSpc, jMode);
                                            int    Subie    = Subi0 + RowNi0.N - 1;
                                            int    Subje    = Subj0 + ColNi0.N - 1;

                                            var tmp = Sblocks[auxIdx].ExtractSubArrayShallow(new int[] { Subi0, Subj0 }, new int[] { Subie, Subje });

                                            Debug.Assert((m_map.IsInLocalRange(Targeti0) && m_map.IsInLocalRange(Targetj0) && mask.GetType() == typeof(BlockMaskLoc)) || mask.GetType() == typeof(BlockMaskExt));

                                            try {
                                                source.ReadBlock(Targeti0, Targetj0, tmp
                                                                 );
                                            } catch (Exception e) {
                                                Console.WriteLine("row: " + Targeti0);
                                                Console.WriteLine("col: " + Targetj0);
                                                throw new Exception(e.Message);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    auxIdx++;
                }
            }
            return(Sblocks);
        }
        /// <summary>
        /// The core of the masking
        /// Generates index lists and the Ni0-struct-list corresponding to mask
        /// and is called by the child classes: local and external mask
        /// Note: the smallest unit are DG sub blocks!
        /// </summary>
        protected void GenerateAllMasks()
        {
            int NoOfCells     = m_NoOfCells;
            int NoOfVariables = m_NoOfVariables;

            int[][] NoOfSpecies = m_NoOfSpecies;
            int[]   DGdegreeP1  = m_DGdegree.CloneAs();
            for (int iDG = 0; iDG < DGdegreeP1.Length; iDG++)
            {
                DGdegreeP1[iDG] += 1;
            }

            List <extNi0> ListNi0     = new List <extNi0>();
            List <int>    Globalint   = new List <int>();
            List <int>    Localint    = new List <int>();
            List <int>    SubBlockIdx = new List <int>();

            int SubOffset = m_SubBlockOffset; // 0 for local mask and BMLoc.LocalDof for external mask
            int Ni0Length = 0;
            int MaskLen   = 0;
            int prevLocie = m_map.LocalNoOfBlocks;
            var tmpCell   = new List <extNi0[][][]>();

            // local caching of filter functions
            // ensures that functions are not re-allocated during the loops
            var  CellInstruction = m_sbs.CellFilter;
            var  VarInstruction  = m_sbs.VariableFilter;
            var  SpecInstruction = m_sbs.SpeciesFilter;
            var  ModeInstruction = m_sbs.ModeFilter;
            bool emptysel        = true;

            // loop over cells...
            for (int iLoc = 0; iLoc < NoOfCells; iLoc++)
            {
                int jLoc = m_CellOffset + iLoc;     //to address correctly, external cells offset has to be concidered, you know ...
                emptysel &= !CellInstruction(jLoc); //for testing if the entire selection is empty, which hopefully only can happen at the level of cells
                if (!CellInstruction(jLoc))
                {
                    continue;
                }
                var tmpVar = new List <extNi0[][]>();

                // loop over variables...
                for (int iVar = 0; iVar < NoOfVariables; iVar++)
                {
                    if (!VarInstruction(jLoc, iVar))
                    {
                        continue;
                    }
                    var tmpSpc = new List <extNi0[]>();

                    // loop over species...
                    for (int iSpc = 0; iSpc < NoOfSpecies[iLoc][iVar]; iSpc++)
                    {
                        if (!SpecInstruction(jLoc, iVar, iSpc))
                        {
                            continue;
                        }
                        int GlobalOffset = m_map.GlobalUniqueIndex(iVar, jLoc, iSpc, 0);
                        int LocalOffset  = m_map.LocalUniqueIndex(iVar, jLoc, iSpc, 0);
                        var tmpMod       = new List <extNi0>();

                        // loop over polynomial degrees...
                        for (int degree = 0; degree < DGdegreeP1[iVar]; degree++)
                        {
                            if (ModeInstruction(jLoc, iVar, iSpc, degree))
                            {
                                int GlobalModeOffset = m_Ni0[degree].i0 + GlobalOffset;
                                int LocalModeOffset  = m_Ni0[degree].i0 + LocalOffset;
                                int ModeLength       = m_Ni0[degree].N;
                                var newNi0           = new extNi0(LocalModeOffset, GlobalModeOffset, SubOffset, ModeLength);
                                SubOffset += ModeLength;
                                // Fill int lists
                                for (int i = 0; i < newNi0.N; i++)
                                {
                                    Globalint.Add(newNi0.Gi0 + i);
                                    Localint.Add(newNi0.Li0 + i);
                                    SubBlockIdx.Add(newNi0.Si0 + i);
                                    MaskLen++;
                                }
                                // Fill Ni0 Lists
                                tmpMod.Add(newNi0);
                                Ni0Length++;
                                ListNi0.Add(newNi0);
                                Debug.Assert(m_map.LocalUniqueIndex(iVar, jLoc, iSpc, GetNp(degree) - 1) == LocalModeOffset + ModeLength - 1);
                            }
                        }
                        if (tmpMod.Count > 0)
                        {
                            tmpSpc.Add(tmpMod.ToArray());
                        }
                    }
                    if (tmpSpc.Count > 0)
                    {
                        tmpVar.Add(tmpSpc.ToArray());
                    }
                }
                if (tmpVar.Count > 0)
                {
                    tmpCell.Add(tmpVar.ToArray());
                }
            }
            var tmpStructNi0 = tmpCell.ToArray();

            int NumOfNi0 = 0;

#if DEBUG
            for (int iCell = 0; iCell < tmpStructNi0.Length; iCell++)
            {
                for (int iVar = 0; iVar < tmpStructNi0[iCell].Length; iVar++)
                {
                    for (int iSpc = 0; iSpc < tmpStructNi0[iCell][iVar].Length; iSpc++)
                    {
                        NumOfNi0 += tmpStructNi0[iCell][iVar][iSpc].Length;
                    }
                }
            }
#endif
            // an empty selection is allowed,
            // e.g. consider a combination of empty external and non empty local mask
            if (!emptysel)
            {
                Debug.Assert(ListNi0.GroupBy(x => x.Li0).Any(g => g.Count() == 1));
                Debug.Assert(ListNi0.GroupBy(x => x.Gi0).Any(g => g.Count() == 1));
                Debug.Assert(ListNi0.Count() == NumOfNi0);
                Debug.Assert(MaskLen <= m_LocalLength);
                Debug.Assert(Localint.GroupBy(x => x).Any(g => g.Count() == 1));
                Debug.Assert(Globalint.GroupBy(x => x).Any(g => g.Count() == 1));
                Debug.Assert(Localint.Count() == MaskLen);
                Debug.Assert(SubBlockIdx.Count() == MaskLen);
                Debug.Assert(SubBlockIdx.GroupBy(x => x).Any(g => g.Count() == 1));
            }

            m_GlobalMask    = Globalint;
            m_LocalMask     = Localint;
            m_StructuredNi0 = tmpStructNi0;
            m_MaskLen       = MaskLen;
            m_Ni0Len        = Ni0Length;
            m_SubBlockMask  = SubBlockIdx;
        }