Beispiel #1
0
        public Func <double[], double> GetF(string species, int d)
        {
            double rho = double.NaN;

            switch (species)
            {
            case "A": rho = rho_A; break;

            case "B": rho = rho_B; break;
                throw new ArgumentException();
            }

            if (mu_A == 0.0 && mu_B == 0)
            {
                return(X => 0.0);
            }
            else
            {
                double   sc    = Math.Min(this.mu_A, this.mu_B);
                double[] Fvec  = new double[] { (1.0) * sc, 0 };
                var      FvecT = ROT.Transform(Fvec);

                return(X => FvecT[d] / rho);
            }
        }
Beispiel #2
0
        public Func <double[], double, double> GetU(string species, int d)
        {
            double a2, a1, a0;

            if (species == "A")
            {
                ParabolaCoeffs_A(out a2, out a1, out a0);
            }
            else if (species == "B")
            {
                ParabolaCoeffs_B(out a2, out a1, out a0);
            }
            else
            {
                throw new ArgumentException();
            }



            return(((_2D)(delegate(double _x, double _y) {
                var Coord = ROTinv.Transform(_x, _y);
                double y = Coord[1];

                Debug.Assert(Coord[0] >= -2);
                Debug.Assert(Coord[0] <= +2);
                Debug.Assert(Coord[1] >= -1);
                Debug.Assert(Coord[1] <= +1);

                double u = (a0 + a1 * y + a2 * y * y);
                //double u = 1 - y*y;
                var UT = ROT.Transform(u, 0.0);

                return UT[d];
            })).Convert_xy2X().Convert_X2Xt());
        }
Beispiel #3
0
        public Vector DistanceBetweenNodes(int jEdge)
        {
            int sourceCell = GridData.iLogicalEdges.CellIndices[jEdge, 0];
            int targetCell = GridData.iLogicalEdges.CellIndices[jEdge, 1];

            if (sourceCell < 0 || targetCell < 0)
            {
                throw new Exception("Boundary edge does not have two connected nodes.");
            }
            Vector sourceNode = new Vector(nodes.Positions[sourceCell, 0], nodes.Positions[sourceCell, 1]);
            Vector targetNode = new Vector(nodes.Positions[targetCell, 0], nodes.Positions[targetCell, 1]);

            //transform if Edge is periodic
            int jGeomEdge = GridData.iLogicalEdges.EdgeToParts[jEdge][0];

            if (this.iGridData.iGeomEdges.EdgeTags[jGeomEdge] >= GridCommons.FIRST_PERIODIC_BC_TAG)
            {
                int         periodicEdgeTag = this.iGridData.iGeomEdges.EdgeTags[jGeomEdge] - GridCommons.FIRST_PERIODIC_BC_TAG;
                AffineTrafo PerT            = ((GridCommons)ParentGrid).PeriodicTrafo[periodicEdgeTag];
                targetNode = PerT.Transform(targetNode);
            }
            ;
            Vector distance = targetNode - sourceNode;

            return(distance);
        }
Beispiel #4
0
        public double NormalEdgeVelocity(int jEdge, double[] x, Vector normal)
        {
            int jCellIn  = this.iGridData.iGeomEdges.CellIndices[jEdge, 1];
            int jCellOut = this.iGridData.iGeomEdges.CellIndices[jEdge, 0];
            int jCell_in = this.iGridData.iGeomCells.GeomCell2LogicalCell[jCellIn];
            int jCell_ot = this.iGridData.iGeomCells.GeomCell2LogicalCell[jCellOut];
            MultidimensionalArray positions  = Nodes.Positions;
            MultidimensionalArray velocities = Nodes.Velocity;

            double[] posOt = positions.GetRow(jCell_ot);
            double[] posIn = positions.GetRow(jCell_in);
            double[] velOt = velocities.GetRow(jCell_ot);
            double[] velIn = velocities.GetRow(jCell_in);

            //transform if Edge is periodic
            if (this.iGridData.iGeomEdges.EdgeTags[jEdge] >= GridCommons.FIRST_PERIODIC_BC_TAG)
            {
                int         periodicEdgeTag = this.iGridData.iGeomEdges.EdgeTags[jEdge] - GridCommons.FIRST_PERIODIC_BC_TAG;
                AffineTrafo PerT            = ((GridCommons)ParentGrid).PeriodicTrafo[periodicEdgeTag];
                posIn = PerT.Transform(posIn);
            }
            ;

            double result = VoronoiEdge.NormalVelocity(posOt, velOt, posIn, velIn, x, normal);

            return(result);
        }
Beispiel #5
0
        public Func <double[], double, double> GetU(string species, int d)
        {
            return(((_2D)(delegate(double _x, double _y) {
                var Coord = ROTinv.Transform(_x, _y);
                double y = Coord[1];

                double u = (1.0 - y * y);
                var UT = ROT.Transform(u, 0.0);

                return UT[d];
            })).Convert_xy2X().Convert_X2Xt());
        }
Beispiel #6
0
            /// <summary>
            /// Subdivides the leaves of thee tree.
            /// </summary>
            /// <param name="leavesLevel">
            /// <see cref="SubdivideCutLeaves"/>.
            /// </param>
            /// <param name="refinedVertexSet">
            /// <see cref="SubdivideCutLeaves"/>.
            /// </param>
            /// <param name="checkIsCut">
            /// If true, leaves will only be subdivided if <see cref="IsCut"/>
            /// returns true.
            /// </param>
            /// <param name="minDistance">
            /// See <see cref="IsCut"/>.
            /// </param>
            /// <returns>
            /// True, if at least one node has been subdivided. Otherwise,
            /// false is returned.
            /// </returns>
            private bool SubdivideLeaves(int leavesLevel, NestedVertexSet refinedVertexSet, bool checkIsCut, double minDistance)
            {
                bool result = false;

                if (level < leavesLevel)
                {
                    if (Children.Count > 0)
                    {
                        foreach (Node child in Children)
                        {
                            result |= child.SubdivideLeaves(leavesLevel, refinedVertexSet, checkIsCut, minDistance);
                        }
                    }
                }
                else if (level == leavesLevel)
                {
                    Debug.Assert(Children.Count == 0, "Can only subdivide leaves");

                    if (!checkIsCut || (checkIsCut && IsPotentiallyCut(minDistance)))
                    {
                        AffineTrafo[] transformations = owner.refElement.GetSubdivision();

                        int N = globalVertexIndices.Length;
                        int D = owner.refElement.SpatialDimension;
                        foreach (AffineTrafo elementaryTransformation in transformations)
                        {
                            AffineTrafo combinedTransformation = Transformation * elementaryTransformation;

                            int[] transfomedVertexIndices = new int[N];
                            for (int i = 0; i < N; i++)
                            {
                                double[] vertex = owner.refElement.Vertices.GetRow(i);
                                vertex = combinedTransformation.Transform(vertex);

                                transfomedVertexIndices[i] = refinedVertexSet.RegisterVertex(vertex);
                            }
                            Children.Add(new Node(
                                             owner, this, level + 1, refinedVertexSet, transfomedVertexIndices, combinedTransformation));
                            result = true;
                        }
                    }
                    else
                    {
                        return(false);
                    }
                }
                else
                {
                    throw new ArgumentException("Given leaves level is higher than the depth of the tree", "leavesLevel");
                }

                return(result);
            }
Beispiel #7
0
        /// <summary>
        /// If this is a node set defined in the edge coordinate system, this method provides
        /// the nodes transformed to the cell coordinate system.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="Edge2CellTrafoIndex">
        /// The transformation index (from edge to cell coordinate system), i.e. an index into <see cref="GridData.EdgeData.Edge2CellTrafos"/>,
        /// see also <see cref="GridData.EdgeData.Edge2CellTrafoIndex"/>.
        /// </param>
        /// <returns></returns>
        public NodeSet GetVolumeNodeSet(IGridData g, int Edge2CellTrafoIndex)
        {
            if (!base.IsLocked)
            {
                throw new NotSupportedException("NodeSet must be locked before first usage.");
            }
            Debug.Assert((base.GetLength(1) == this.RefElement.SpatialDimension) || (base.GetLength(1) == 1 && this.RefElement.SpatialDimension == 0), "Mismatch between number of spatial directions in node set and reference element.");

#if DEBUG
            if (this.GetNodeCoordinateSystem(g) != NodeCoordinateSystem.EdgeCoord)
            {
                throw new NotSupportedException("Operation only supported for edge node sets.");
            }
#endif
            int D   = g.SpatialDimension;
            int NN  = this.NoOfNodes;
            int idx = Edge2CellTrafoIndex + g.iGeomEdges.e2C_offet;

            if (VolumeNodeSets == null)
            {
                // alloc mem, if necessary
                // ++++++++++++++++++++++++
                VolumeNodeSets = new NodeSet[g.iGeomEdges.Edge2CellTrafos.Count + g.iGeomEdges.e2C_offet];
            }
            if (VolumeNodeSets.Length < (g.iGeomEdges.Edge2CellTrafos.Count + g.iGeomEdges.e2C_offet))
            {
                // re-alloc mem, if necessary
                // ++++++++++++++++++++++++++
                var newVNS = new NodeSet[g.iGeomEdges.Edge2CellTrafos.Count + g.iGeomEdges.e2C_offet];
                Array.Copy(VolumeNodeSets, 0, newVNS, 0, VolumeNodeSets.Length);
                VolumeNodeSets = newVNS;
            }
            if (VolumeNodeSets[idx] == null)
            {
                // transform edge-nodes to cell-nodes, if necessary
                // ++++++++++++++++++++++++++++++++++++++++++++++++
                AffineTrafo Trafo = g.iGeomEdges.Edge2CellTrafos[Edge2CellTrafoIndex];
                int         iKref = g.iGeomEdges.Edge2CellTrafosRefElementIndices[Edge2CellTrafoIndex];

                NodeSet volNS = new NodeSet(g.iGeomCells.RefElements[iKref], NN, D);
                Trafo.Transform(this, volNS);
                volNS.LockForever();

                VolumeNodeSets[idx] = volNS;
            }

            return(VolumeNodeSets[idx]);
        }
Beispiel #8
0
        /// <summary>
        /// Evaluates the vector-valued anti-derivatives defining the
        /// moment-fitting basis (cf. <see cref="lambdaBasis"/>) in each node
        /// of all cells in the given range
        /// </summary>
        /// <param name="i0">
        /// First cell in range
        /// </param>
        /// <param name="length">
        /// Number of cells
        /// </param>
        /// <returns>
        /// The values of <see cref="lambdaBasis"/> in each node
        /// <list type="bullet">
        ///     <item>1st index: Node index</item>
        ///     <item>2nd index: Basis function index</item>
        ///     <item>3rd index: Spatial dimension</item>
        /// </list>
        /// </returns>
        /// <remarks>
        /// This method does not evaluate a Lambda which is constant since the
        /// derivative of such function is zero. As a result, the integral over
        /// this function is zero, too, which makes it useless for the
        /// construction of a quadrature rule
        /// </remarks>
        /// <param name="NS">
        /// Nodes at which to evaluate.
        /// </param>
        private MultidimensionalArray EvaluateLambdas(int cell, NodeSet NS)
        {
            int D = LevelSetData.GridDat.SpatialDimension;

            Debug.Assert(
                lambdaBasis.Count % D == 0,
                "Number of polynomials in basis should be divisible by D = " + D);

            int noOfLambdas = lambdaBasis.Count / D;
            int noOfNodes   = NS.NoOfNodes;

            if (RestrictNodes)
            {
                AffineTrafo trafo = trafos[localCellIndex2SubgridIndex[cell]];

                AffineTrafo inverse = trafo.Invert();
                NS = new NodeSet(RefElement, inverse.Transform(NS));
                NS.LockForever();

                MultidimensionalArray lambdaValues = lambdaBasis.Values.GetValues(NS);
                lambdaValues = lambdaValues.ResizeShallow(noOfNodes, noOfLambdas, D);

                for (int i = 0; i < noOfNodes; i++)
                {
                    for (int j = 0; j < noOfLambdas; j++)
                    {
                        for (int d = 0; d < D; d++)
                        {
                            // Bounding box transformation is assumed to just a
                            // stretching, i.e. off-diagonals are zero
                            lambdaValues[i, j, d] *= trafo.Matrix[d, d];
                        }
                    }
                }

                return(lambdaValues);
            }
            else
            {
                MultidimensionalArray lambdaValues = lambdaBasis.Values.GetValues(NS);
                return(lambdaValues.ResizeShallow(noOfNodes, noOfLambdas, D));
            }
        }
Beispiel #9
0
        /// <summary>
        /// Non-vectorized reference implementation of
        /// <see cref="GetOptimizedRule(int, AffineTrafo, NodeSet, double[,], int)"/>
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="trafo"></param>
        /// <param name="nodes"></param>
        /// <param name="quadResults"></param>
        /// <param name="order"></param>
        /// <returns></returns>
        private QuadRule GetOptimizedRule(int cell, AffineTrafo trafo, NodeSet nodes, double[,] quadResults, int order)
        {
            int maxLambdaDegree = order + 1;
            int noOfLambdas     = GetNumberOfLambdas(maxLambdaDegree);
            int noOfNodes       = nodes.GetLength(0);

            // Leading dimension of B (rhs); required by DGELSY
            int LDB = Math.Max(noOfLambdas, noOfNodes);

            double[] rhs = new double[LDB];

            AffineTrafo inverseTrafo = trafo.Invert();
            NodeSet     trafoNodes   = new NodeSet(RefElement, inverseTrafo.Transform(nodes));

            trafoNodes.LockForever();

            Basis basis = new Basis(LevelSetData.GridDat, order);
            MultidimensionalArray basisValues = basis.Evaluate(trafoNodes);

            int iSubGrid = localCellIndex2SubgridIndex[cell];

            for (int k = 0; k < noOfLambdas; k++)
            {
                rhs[k] += quadResults[iSubGrid, k];
            }

            LAPACK.F77_LAPACK.DGELSY(noOfLambdas, noOfNodes, basisValues.Storage, rhs, 1, RCOND);

            QuadRule optimizedRule = new QuadRule()
            {
                Nodes            = nodes,
                Weights          = MultidimensionalArray.Create(noOfNodes),
                OrderOfPrecision = order
            };

            for (int j = 0; j < noOfNodes; j++)
            {
                optimizedRule.Weights[j] = rhs[j];
            }

            return(optimizedRule);
        }
Beispiel #10
0
        /// <summary>
        /// Constructs suitable quadrature rules cells in
        /// <paramref name="mask"/>.
        /// </summary>
        /// <param name="mask">
        /// Cells for which quadrature rules shall be created
        /// </param>
        /// <param name="order">
        /// Desired order of the moment-fitting system. Assuming that
        /// <see cref="surfaceRuleFactory"/> integrates the basis polynomials
        /// exactly over the zero iso-contour (which it usually
        /// doesn't!), the resulting quadrature rules will be exact up to this
        /// order.
        /// </param>
        /// <returns>A set of quadrature rules</returns>
        /// <remarks>
        /// Since the selected level set is generally discontinuous across cell
        /// boundaries, this method does not make use of the fact that
        /// neighboring cells share edges. That is, the optimization will be
        /// performed twice for each inner edge in <paramref name="mask"/>.
        /// </remarks>
        public IEnumerable <IChunkRulePair <QuadRule> > GetQuadRuleSet(ExecutionMask mask, int order)
        {
            using (var tr = new FuncTrace()) {
                CellMask cellMask = mask as CellMask;
                if (cellMask == null)
                {
                    throw new ArgumentException("Mask must be a volume mask", "mask");
                }

                // Note: This is a parallel call, so do this early to avoid parallel confusion
                localCellIndex2SubgridIndex = new SubGrid(cellMask).LocalCellIndex2SubgridIndex;

                int maxLambdaDegree = order + 1;
                int noOfLambdas     = GetNumberOfLambdas(maxLambdaDegree);
                int noOfEdges       = LevelSetData.GridDat.Grid.RefElements[0].NoOfFaces;
                int D = RefElement.SpatialDimension;

                // Get the basis polynomials and integrate them analytically
                Polynomial[] basePolynomials = RefElement.GetOrthonormalPolynomials(order).ToArray();
                Polynomial[] polynomials     = new Polynomial[basePolynomials.Length * D];
                for (int i = 0; i < basePolynomials.Length; i++)
                {
                    Polynomial p = basePolynomials[i];

                    for (int d = 0; d < D; d++)
                    {
                        Polynomial pNew = p.CloneAs();
                        for (int j = 0; j < p.Coeff.Length; j++)
                        {
                            pNew.Exponents[j, d]++;
                            pNew.Coeff[j] /= pNew.Exponents[j, d];
                            pNew.Coeff[j] /= D; // Make sure divergence is Phi again
                        }
                        polynomials[i * D + d] = pNew;
                    }
                }

                // basePolynomials[i] == div(polynomials[i*D], ... , polynomials[i*D + D - 1])
                lambdaBasis = new PolynomialList(polynomials);


                if (RestrictNodes)
                {
                    trafos = new AffineTrafo[mask.NoOfItemsLocally];

                    foreach (Chunk chunk in mask)
                    {
                        foreach (var cell in chunk.Elements.AsSmartEnumerable())
                        {
                            CellMask singleElementMask = new CellMask(
                                LevelSetData.GridDat, Chunk.GetSingleElementChunk(cell.Value));

                            LineAndPointQuadratureFactory.LineQRF lineFactory = this.edgeRuleFactory as LineAndPointQuadratureFactory.LineQRF;
                            if (lineFactory == null)
                            {
                                throw new Exception();
                            }
                            var lineRule  = lineFactory.GetQuadRuleSet(singleElementMask, order).Single().Rule;
                            var pointRule = lineFactory.m_Owner.GetPointFactory().GetQuadRuleSet(singleElementMask, order).Single().Rule;

                            // Also add point rule points since line rule points
                            // are constructed from Gauss rules that do not include
                            // the end points
                            BoundingBox box = new BoundingBox(lineRule.Nodes);
                            box.AddPoints(pointRule.Nodes);

                            int noOfRoots = pointRule.Nodes.GetLength(0);
                            if (noOfRoots <= 1)
                            {
                                // Cell is considered cut because the level set
                                // is very close, but actually isn't. Note that
                                // we can NOT omit the cell (as in the surface
                                // case) as it will be missing in the list of
                                // uncut cells, i.e. this cell would be ignored
                                // completely
                                trafos[localCellIndex2SubgridIndex[cell.Value]] =
                                    AffineTrafo.Identity(RefElement.SpatialDimension);
                                continue;
                            }
                            else if (noOfRoots == 2)
                            {
                                // Go a bit into the direction of the normal
                                // from the center between the nodes in order
                                // not to miss regions with strong curvature
                                double[] center = box.Min.CloneAs();
                                center.AccV(1.0, box.Max);
                                center.ScaleV(0.5);
                                NodeSet centerNode = new NodeSet(RefElement, center);
                                centerNode.LockForever();

                                MultidimensionalArray normal = LevelSetData.GetLevelSetReferenceNormals(centerNode, cell.Value, 1);
                                MultidimensionalArray dist   = LevelSetData.GetLevSetValues(centerNode, cell.Value, 1);

                                double scaling = Math.Sqrt(LevelSetData.GridDat.Cells.JacobiDet[cell.Value]);

                                double[] newPoint = new double[D];
                                for (int d = 0; d < D; d++)
                                {
                                    newPoint[d] = center[d] - normal[0, 0, d] * dist[0, 0] / scaling;
                                }

                                box.AddPoint(newPoint);

                                // Make sure points stay in box
                                for (int d = 0; d < D; d++)
                                {
                                    box.Min[d] = Math.Max(box.Min[d], -1);
                                    box.Max[d] = Math.Min(box.Max[d], 1);
                                }
                            }

                            MultidimensionalArray preImage = RefElement.Vertices.ExtractSubArrayShallow(
                                new int[] { 0, 0 }, new int[] { D, D - 1 });

                            MultidimensionalArray image = MultidimensionalArray.Create(D + 1, D);
                            image[0, 0] = box.Min[0]; // Top left
                            image[0, 1] = box.Max[1];
                            image[1, 0] = box.Max[0]; // Top right
                            image[1, 1] = box.Max[1];
                            image[2, 0] = box.Min[0]; // Bottom left;
                            image[2, 1] = box.Min[1];

                            AffineTrafo trafo = AffineTrafo.FromPoints(preImage, image);
                            trafos[localCellIndex2SubgridIndex[cell.Value]] = trafo;
                        }
                    }
                }

                LambdaCellBoundaryQuadrature cellBoundaryQuadrature =
                    new LambdaCellBoundaryQuadrature(this, edgeRuleFactory, cellMask);
                cellBoundaryQuadrature.Execute();

                LambdaLevelSetSurfaceQuadrature surfaceQuadrature =
                    new LambdaLevelSetSurfaceQuadrature(this, surfaceRuleFactory, cellMask);
                surfaceQuadrature.Execute();

                // Must happen _after_ all parallel calls (e.g., definition of
                // the sub-grid or quadrature) in order to avoid problems in
                // parallel runs
                if (mask.NoOfItemsLocally == 0)
                {
                    var empty = new ChunkRulePair <QuadRule> [0];
                    return(empty);
                }

                if (cachedRules.ContainsKey(order))
                {
                    order = cachedRules.Keys.Where(cachedOrder => cachedOrder >= order).Min();
                    CellMask cachedMask = new CellMask(mask.GridData, cachedRules[order].Select(p => p.Chunk).ToArray());

                    if (cachedMask.Equals(mask))
                    {
                        return(cachedRules[order]);
                    }
                    else
                    {
                        throw new NotImplementedException(
                                  "Case not yet covered yet in combination with caching; deactivate caching to get rid of this message");
                    }
                }

                double[,] quadResults = cellBoundaryQuadrature.Results;
                foreach (Chunk chunk in mask)
                {
                    for (int i = 0; i < chunk.Len; i++)
                    {
                        int iSubGrid = localCellIndex2SubgridIndex[chunk.i0 + i];

                        switch (jumpType)
                        {
                        case JumpTypes.Heaviside:
                            for (int k = 0; k < noOfLambdas; k++)
                            {
                                quadResults[iSubGrid, k] -= surfaceQuadrature.Results[iSubGrid, k];
                            }
                            break;

                        case JumpTypes.OneMinusHeaviside:
                            for (int k = 0; k < noOfLambdas; k++)
                            {
                                quadResults[iSubGrid, k] += surfaceQuadrature.Results[iSubGrid, k];
                            }
                            break;

                        case JumpTypes.Sign:
                            for (int k = 0; k < noOfLambdas; k++)
                            {
                                quadResults[iSubGrid, k] -= 2.0 * surfaceQuadrature.Results[iSubGrid, k];
                            }
                            break;

                        default:
                            throw new NotImplementedException();
                        }
                    }
                }

                BitArray voidCellsArray = new BitArray(LevelSetData.GridDat.Cells.NoOfLocalUpdatedCells);
                BitArray fullCellsArray = new BitArray(LevelSetData.GridDat.Cells.NoOfLocalUpdatedCells);
                foreach (Chunk chunk in cellMask)
                {
                    foreach (var cell in chunk.Elements)
                    {
                        double rhsL2Norm = 0.0;
                        for (int k = 0; k < noOfLambdas; k++)
                        {
                            double entry = quadResults[localCellIndex2SubgridIndex[cell], k];
                            rhsL2Norm += entry * entry;
                        }

                        if (rhsL2Norm < 1e-14)
                        {
                            // All integrals are zero => cell not really cut
                            // (level set is tangent) and fully in void region
                            voidCellsArray[cell] = true;
                            continue;
                        }

                        double l2NormFirstIntegral = quadResults[localCellIndex2SubgridIndex[cell], 0];
                        l2NormFirstIntegral *= l2NormFirstIntegral;
                        double rhsL2NormWithoutFirst = rhsL2Norm - l2NormFirstIntegral;

                        // Beware: This check is only sensible if basis is orthonormal on RefElement!
                        if (rhsL2NormWithoutFirst < 1e-14 &&
                            Math.Abs(l2NormFirstIntegral - RefElement.Volume) < 1e-14)
                        {
                            // All integrals are zero except integral over first integrand
                            // If basis is orthonormal, this implies that cell is uncut and
                            // fully in non-void region since then
                            // \int_K \Phi_i dV = \int_A \Phi_i dV = \delta_{0,i}
                            // However, we have to compare RefElement.Volume since
                            // integration is performed in reference coordinates!
                            fullCellsArray[cell] = true;
                        }
                    }
                }

                var result = new List <ChunkRulePair <QuadRule> >(cellMask.NoOfItemsLocally);

                CellMask emptyCells = new CellMask(LevelSetData.GridDat, voidCellsArray);
                foreach (Chunk chunk in emptyCells)
                {
                    foreach (int cell in chunk.Elements)
                    {
                        QuadRule emptyRule = QuadRule.CreateEmpty(RefElement, 1, RefElement.SpatialDimension);
                        emptyRule.Nodes.LockForever();
                        result.Add(new ChunkRulePair <QuadRule>(
                                       Chunk.GetSingleElementChunk(cell), emptyRule));
                    }
                }

                CellMask fullCells = new CellMask(LevelSetData.GridDat, fullCellsArray);
                foreach (Chunk chunk in fullCells)
                {
                    foreach (int cell in chunk.Elements)
                    {
                        QuadRule fullRule = RefElement.GetQuadratureRule(order);
                        result.Add(new ChunkRulePair <QuadRule>(
                                       Chunk.GetSingleElementChunk(cell), fullRule));
                    }
                }

                CellMask realCutCells = cellMask.Except(emptyCells).Except(fullCells);
                if (RestrictNodes)
                {
                    foreach (Chunk chunk in realCutCells)
                    {
                        foreach (int cell in chunk.Elements)
                        {
                            CellMask singleElementMask = new CellMask(
                                LevelSetData.GridDat, Chunk.GetSingleElementChunk(cell));

                            AffineTrafo trafo = trafos[localCellIndex2SubgridIndex[cell]];
                            Debug.Assert(Math.Abs(trafo.Matrix.Determinant()) > 1e-10);

                            NodeSet nodes       = GetNodes(noOfLambdas).CloneAs();
                            NodeSet mappedNodes = new NodeSet(RefElement, trafo.Transform(nodes));
                            mappedNodes.LockForever();

                            // Remove nodes in negative part
                            MultidimensionalArray levelSetValues  = LevelSetData.GetLevSetValues(mappedNodes, cell, 1);
                            List <int>            nodesToBeCopied = new List <int>(mappedNodes.GetLength(0));
                            for (int n = 0; n < nodes.GetLength(0); n++)
                            {
                                if (levelSetValues[0, n] >= 0.0)
                                {
                                    nodesToBeCopied.Add(n);
                                }
                            }

                            NodeSet reducedNodes = new NodeSet(
                                this.RefElement, nodesToBeCopied.Count, D);
                            for (int n = 0; n < nodesToBeCopied.Count; n++)
                            {
                                for (int d = 0; d < D; d++)
                                {
                                    reducedNodes[n, d] = mappedNodes[nodesToBeCopied[n], d];
                                }
                            }
                            reducedNodes.LockForever();

                            QuadRule optimizedRule = GetOptimizedRule(
                                cell,
                                trafo,
                                reducedNodes,
                                quadResults,
                                order);

                            result.Add(new ChunkRulePair <QuadRule>(
                                           singleElementMask.Single(), optimizedRule));
                        }
                    }
                }
                else
                {
                    // Use same nodes in all cells
                    QuadRule[] optimizedRules = GetOptimizedRules(
                        realCutCells, GetNodes(noOfLambdas), quadResults, order);
                    int ruleIndex = 0;
                    foreach (Chunk chunk in realCutCells)
                    {
                        foreach (var cell in chunk.Elements)
                        {
                            result.Add(new ChunkRulePair <QuadRule>(
                                           Chunk.GetSingleElementChunk(cell), optimizedRules[ruleIndex]));
                            ruleIndex++;
                        }
                    }
                }

                cachedRules[order] = result.OrderBy(p => p.Chunk.i0).ToArray();
                return(cachedRules[order]);
            }
        }