Beispiel #1
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]);
            }
        }
        /// <summary>
        /// Uses a moment-fitting basis of order <paramref name="order"/> for
        /// determining optimal weights of a quadrature rule for a sub-region
        /// of each edge of each cell in the given <paramref name="mask"/>.
        /// </summary>
        /// <param name="mask">
        /// Cells for which rules shall be created
        /// </param>
        /// <param name="order">
        /// Desired order of the moment-fitting system. Assuming that
        /// <see cref="edgeSurfaceRuleFactory"/> 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>
        private CellBoundaryQuadRule[] GetOptimizedRules(CellMask mask, int order)
        {
            using (var tr = new FuncTrace()) {
                int maxLambdaDegree = order + 1;
                int noOfLambdas     = GetNumberOfLambdas();
                int noOfFaces       = LevelSetData.GridDat.Grid.RefElements[0].NoOfFaces;
                int D = LevelSetData.GridDat.SpatialDimension;
                int noOfNodesPerEdge = baseRule.NumbersOfNodesPerFace[0];
                CellBoundaryQuadRule[] optimizedRules = new CellBoundaryQuadRule[mask.NoOfItemsLocally];

                Debug.Assert(
                    baseRule.NumbersOfNodesPerFace.Distinct().Count() == 1,
                    "Assumption violated: Number of nodes varies from edge to edge.");
                Debug.Assert(noOfLambdas < noOfNodesPerEdge, "Not enough integration points");

                LambdaEdgeBoundaryQuadrature cellBoundaryQuadrature =
                    new LambdaEdgeBoundaryQuadrature(this, CoFaceQuadRuleFactory, maxLambdaDegree, mask);
                cellBoundaryQuadrature.Execute();
                double[, ,] boundaryResults = cellBoundaryQuadrature.Results;

                LambdaLevelSetSurfaceQuadrature surfaceQuadrature =
                    new LambdaLevelSetSurfaceQuadrature(this, edgeSurfaceRuleFactory, maxLambdaDegree, mask);
                surfaceQuadrature.Execute();
                double[, ,] surfaceResults = surfaceQuadrature.Results;

                int noOfRhs = 0;
                int[,] rhsIndexMap = new int[mask.NoOfItemsLocally, noOfFaces];
                foreach (Chunk chunk in mask)
                {
                    MultidimensionalArray levelSetValues = LevelSetData.GetLevSetValues(signTestRule.Nodes, chunk.i0, chunk.Len);

                    for (int i = 0; i < chunk.Len; i++)
                    {
                        int cell     = i + chunk.i0;
                        int iSubGrid = localCellIndex2SubgridIndex[cell];

                        optimizedRules[iSubGrid] = new CellBoundaryQuadRule()
                        {
                            Nodes   = baseRule.Nodes,
                            Weights = MultidimensionalArray.Create(baseRule.NoOfNodes),
                            NumbersOfNodesPerFace = baseRule.NumbersOfNodesPerFace.CloneAs()
                        };

                        int noOfProcessedNodes = 0;
                        for (int e = 0; e < noOfFaces; e++)
                        {
                            int noOfNodesOnEdge = baseRule.NumbersOfNodesPerFace[e];

                            bool faceIsCut = false;
                            for (int k = 0; k < noOfLambdas; k++)
                            {
                                faceIsCut |= surfaceResults[iSubGrid, e, k].Abs() > 1e-9;
                            }

                            //edgeIsCut = tracker.EdgeIsCut[cell, e];

                            if (!faceIsCut)
                            {
                                // Sign is checked in multiple points to avoid
                                // some weird edge cases. Sign with most
                                // occurrences on the test nodes wins
                                int numNeg = 0;
                                int numPos = 0;
                                int offset = signTestRule.NumbersOfNodesPerFace.Take(e).Sum();
                                for (int j = 0; j < signTestRule.NumbersOfNodesPerFace[e]; j++)
                                {
                                    double val = levelSetValues[i, offset + j];
                                    if (val < 0.0)
                                    {
                                        numNeg++;
                                    }
                                    else if (val > 0.0)
                                    {
                                        numPos++;
                                    }
                                }

                                int sign = numPos - numNeg;

                                if (sign == 0)
                                {
                                    throw new Exception(String.Format(
                                                            "Could not determine sign of face {0} of cell {1}", e, cell));
                                }

                                switch (jumpType)
                                {
                                case JumpTypes.Heaviside:
                                    if (sign > 0)
                                    {
                                        CopyWeights(baseRule, optimizedRules[iSubGrid], e, 1.0);
                                    }
                                    break;

                                case JumpTypes.OneMinusHeaviside:
                                    if (sign < 0)
                                    {
                                        CopyWeights(baseRule, optimizedRules[iSubGrid], e, -1.0);
                                    }
                                    break;

                                case JumpTypes.Sign:
                                    CopyWeights(baseRule, optimizedRules[iSubGrid], e, levelSetValues[i, e].Sign());
                                    break;

                                default:
                                    throw new NotImplementedException();
                                }

                                rhsIndexMap[iSubGrid, e] = -1;
                                noOfProcessedNodes      += noOfNodesOnEdge;
                                continue;
                            }

                            rhsIndexMap[iSubGrid, e] = noOfRhs;
                            noOfRhs++;
                            noOfProcessedNodes += noOfNodesOnEdge;
                        }
                    }
                }

                // Leading dimension of B (rhs); required by DGELSY
                int      LDB = Math.Max(noOfLambdas, noOfNodesPerEdge);
                double[] rhs = new double[LDB * noOfRhs];
                foreach (Chunk chunk in mask)
                {
                    for (int i = 0; i < chunk.Len; i++)
                    {
                        int cell     = i + chunk.i0;
                        int iSubGrid = localCellIndex2SubgridIndex[cell];

                        for (int e = 0; e < noOfFaces; e++)
                        {
                            int rhsIndex = rhsIndexMap[iSubGrid, e];
                            if (rhsIndex < 0)
                            {
                                continue;
                            }

                            switch (jumpType)
                            {
                            case JumpTypes.Heaviside:
                                for (int k = 0; k < noOfLambdas; k++)
                                {
                                    rhs[rhsIndex * LDB + k] = boundaryResults[iSubGrid, e, k] - surfaceResults[iSubGrid, e, k];
                                }
                                break;

                            case JumpTypes.OneMinusHeaviside:
                                for (int k = 0; k < noOfLambdas; k++)
                                {
                                    rhs[rhsIndex * LDB + k] = boundaryResults[iSubGrid, e, k] + surfaceResults[iSubGrid, e, k];
                                }
                                break;

                            case JumpTypes.Sign:
                                for (int k = 0; k < noOfLambdas; k++)
                                {
                                    rhs[rhsIndex * LDB + k] = boundaryResults[iSubGrid, e, k] - 2.0 * surfaceResults[iSubGrid, e, k];
                                }
                                break;

                            default:
                                throw new NotImplementedException();
                            }
                        }
                    }
                }

                if (rhs.Length > 0)
                {
                    LAPACK.F77_LAPACK.DGELSY(noOfLambdas, noOfNodesPerEdge, basisValuesEdge.Storage, rhs, noOfRhs, 1e-12);
                }

                foreach (Chunk chunk in mask)
                {
                    for (int i = 0; i < chunk.Len; i++)
                    {
                        int cell     = i + chunk.i0;
                        int iSubGrid = localCellIndex2SubgridIndex[cell];

                        int noOfProcessedNodes = 0;
                        for (int e = 0; e < noOfFaces; e++)
                        {
                            int noOfNodesOnEdge = baseRule.NumbersOfNodesPerFace[e];
                            int rhsIndex        = rhsIndexMap[iSubGrid, e];
                            if (rhsIndex < 0)
                            {
                                noOfProcessedNodes += noOfNodesOnEdge;
                                continue;
                            }

                            for (int j = 0; j < noOfNodesOnEdge; j++)
                            {
                                optimizedRules[iSubGrid].Weights[noOfProcessedNodes + j] = rhs[rhsIndex * LDB + j];
                            }

                            noOfProcessedNodes += noOfNodesOnEdge;
                        }


                        double max = optimizedRules[iSubGrid].Weights.Max(d => d.Abs());
                        if (max > 2.0 * RefElement.Volume)
                        {
                            tr.Info(String.Format(
                                        "Warning: Abnormally large integration weight detected"
                                        + " for level set edge volume integral in cell {0}"
                                        + " (|w| = {1}). This may indicate a loss of"
                                        + " integration accuracy.",
                                        cell,
                                        max));
                        }
                    }
                }

                return(optimizedRules);
            }
        }