Пример #1
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);
        }
Пример #2
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);
        }
Пример #3
0
        public IDictionary <string, AppControl.BoundaryValueCollection> GetBoundaryConfig()
        {
            var config = new Dictionary <string, AppControl.BoundaryValueCollection>();

            config.Add("wall_top", new AppControl.BoundaryValueCollection());
            config.Add("wall_bottom", new AppControl.BoundaryValueCollection());

            if (!periodic)
            {
                if (!this.ROT.ApproximateEquals(AffineTrafo.Some2DRotation(0.0)))
                {
                    throw new NotSupportedException();
                }

                double A_a0, A_a1, A_a2, B_a0, B_a1, B_a2;
                this.ParabolaCoeffs_A(out A_a2, out A_a1, out A_a0);
                this.ParabolaCoeffs_B(out B_a2, out B_a1, out B_a0);

                config.Add("velocity_inlet", new AppControl.BoundaryValueCollection());
                config["velocity_inlet"].Evaluators.Add(
                    VariableNames.Velocity_d(0) + "#A",
                    (X, t) => A_a0 + A_a1 * X[1] + A_a2 * X[1] * X[1]);
                config["velocity_inlet"].Evaluators.Add(
                    VariableNames.Velocity_d(0) + "#B",
                    (X, t) => B_a0 + B_a1 * X[1] + B_a2 * X[1] * X[1]);

                config.Add("Pressure_Outlet", new AppControl.BoundaryValueCollection());
            }

            return(config);
        }
Пример #4
0
 public ChannelTest(double angle)
 {
     //double angle = 0.0;
     //double angle = 60.0 * Math.PI / 180.0;
     ROT    = AffineTrafo.Some2DRotation(angle);
     ROTinv = ROT.Invert();
 }
Пример #5
0
        public IDictionary <string, AppControl.BoundaryValueCollection> GetBoundaryConfig()
        {
            var config = new Dictionary <string, AppControl.BoundaryValueCollection>();

            config.Add("wall_top", new AppControl.BoundaryValueCollection());
            config.Add("wall_bottom", new AppControl.BoundaryValueCollection());

            if (!periodic)
            {
                if (!this.ROT.ApproximateEquals(AffineTrafo.Some2DRotation(0.0)))
                {
                    throw new NotSupportedException();
                }

                config.Add("velocity_inlet", new AppControl.BoundaryValueCollection());
                config["velocity_inlet"].Evaluators.Add(
                    VariableNames.Velocity_d(0) + "#A",
                    (X, t) => 1.0 - X[1] * X[1]);
                config["velocity_inlet"].Evaluators.Add(
                    VariableNames.Velocity_d(0) + "#B",
                    (X, t) => 1.0 - X[1] * X[1]);

                config.Add("Pressure_Outlet", new AppControl.BoundaryValueCollection());
            }

            return(config);
        }
Пример #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);
            }
Пример #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]);
        }
Пример #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));
            }
        }
        public void RegisterPeriodicBoundariesTo(GridCommons grid)
        {
            Debug.Assert(boundaryMap.PeriodicTrafos is SortedList <byte, AffineTrafo>);
            var periodicTrafos = boundaryMap.PeriodicTrafos;

            //Sort by edgeTag
            foreach (var pair in periodicTrafos)
            {
                AffineTrafo trafo         = pair.Value;
                byte        edgeTag       = pair.Key;
                byte        edgeTagInGrid = grid.AddPeriodicEdgeTrafo(trafo);
                Debug.Assert(edgeTag == edgeTagInGrid);

                if (!grid.EdgeTagNames.ContainsKey(edgeTagInGrid))
                {
                    grid.EdgeTagNames.Add(edgeTagInGrid, $"periodic corner {edgeTagInGrid}");
                }
            }
        }
Пример #10
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);
        }
        static T ExtractPeriodicTrafos <T>(
            byte[] edgeTags,
            IDictionary <int, bool> periodicInverseMap,
            IDictionary <int, Transformation> periodicTransformations)
            where T : IDictionary <byte, AffineTrafo>, new()
        {
            var periodicTrafos = new T();

            foreach (var indiceInversePair in periodicInverseMap)
            {
                int  boundaryEdgeNumber = indiceInversePair.Key;
                bool isPeriodicInverse  = indiceInversePair.Value;

                if (!isPeriodicInverse)
                {
                    byte        edgeTag = edgeTags[boundaryEdgeNumber];
                    AffineTrafo trafo   = Convert(periodicTransformations[boundaryEdgeNumber]);
                    periodicTrafos.Add(edgeTag, trafo);
                }
            }
            return(periodicTrafos);
        }
Пример #12
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]);
            }
        }
Пример #13
0
 /// <summary>
 /// Constructs a subdivision node defined by the given transformation.
 /// </summary>
 /// <param name="transformation">
 /// The transformation that transforms the vertices of the original
 /// simplex to the vertices of the sub-simplex represented by this
 /// node.
 /// </param>
 /// <param name="isCut">
 /// Defines whether this node is considered is cut by the interface.
 /// </param>
 public SubdivisionNode(AffineTrafo transformation, bool isCut)
 {
     Transformation = transformation;
     IsCut          = isCut;
 }
Пример #14
0
 /// <summary>
 /// the Point cannot be subdivided - the subdivision contains only one element;
 /// </summary>
 /// <returns></returns>
 public override AffineTrafo[] GetSubdivision()
 {
     AffineTrafo[] ret = new AffineTrafo[] { AffineTrafo.Identity(1) };
     return(ret);
 }
Пример #15
0
        static MultidimensionalArray NodalPatchRecovery(int jCell, double[] Nodes, DGField Phi, out AffineTrafo Trafilein)
        {
            GridData GridData = (GridData)Phi.GridDat;

            int[] Neighbours, Edges;
            GridData.GetCellNeighbours(jCell, GetCellNeighbours_Mode.ViaVertices, out Neighbours, out Edges);
            if (Neighbours.Length != 8)
            {
                throw new NotSupportedException();
            }

            int[] JS = Neighbours;
            jCell.AddToArray(ref JS);

            MultidimensionalArray CellCoordinates = MultidimensionalArray.Create(JS.Length, 4, 2); // 1st index: jcell, top, bottom, left, right
            var Kref = GridData.iGeomCells.RefElements[0];

            if (GridData.iGeomCells.RefElements.Length != 1 || Kref.GetType() != typeof(BoSSS.Foundation.Grid.RefElements.Square))
            {
                throw new NotImplementedException();
            }
            for (int f = 0; f < JS.Length; f++)
            {
                GridData.TransformLocal2Global(Kref.Vertices, CellCoordinates.ExtractSubArrayShallow(f, -1, -1), JS[f]);
            }

            double xMin = CellCoordinates.ExtractSubArrayShallow(-1, -1, 0).Min();
            double xMax = CellCoordinates.ExtractSubArrayShallow(-1, -1, 0).Max();
            double yMin = CellCoordinates.ExtractSubArrayShallow(-1, -1, 1).Min();
            double yMax = CellCoordinates.ExtractSubArrayShallow(-1, -1, 1).Max();

            int N = Nodes.Length;

            double[] xNodes = new double[N];
            double[] yNodes = new double[N];
            for (int i = 0; i < N; i++)
            {
                xNodes[i] = (xMax - xMin) * 0.5 * (Nodes[i] + 1) + xMin;
                yNodes[i] = (yMax - yMin) * 0.5 * (Nodes[i] + 1) + yMin;
            }
            var TrChey2Glob = new AffineTrafo(2);

            TrChey2Glob.Matrix[0, 0] = (xMax - xMin) * 0.5; TrChey2Glob.Affine[0] = (xMax - xMin) * 0.5 + xMin;
            TrChey2Glob.Matrix[1, 1] = (yMax - yMin) * 0.5; TrChey2Glob.Affine[1] = (yMax - yMin) * 0.5 + yMin;


            MultidimensionalArray[] NodeSets_global = new MultidimensionalArray[JS.Length];
            NodeSet[]     NodeSets   = new NodeSet[JS.Length];
            List <double> xNodesCell = new List <double>();
            List <double> yNodesCell = new List <double>();

            int[][] MapTo_i = new int[JS.Length][];
            int[][] MapTo_j = new int[JS.Length][];


            for (int f = 0; f < JS.Length; f++)
            {
                MultidimensionalArray CellCoords = CellCoordinates.ExtractSubArrayShallow(f, -1, -1);
                double xMinCell = CellCoords.ExtractSubArrayShallow(-1, 0).Min();
                double xMaxCell = CellCoords.ExtractSubArrayShallow(-1, 0).Max();
                double yMinCell = CellCoords.ExtractSubArrayShallow(-1, 1).Min();
                double yMaxCell = CellCoords.ExtractSubArrayShallow(-1, 1).Max();

                int iMin   = xNodes.FirstIndexWhere(x => x >= xMinCell);
                int iMax   = xNodes.LastIndexWhere(x => x < xMaxCell);
                int jMin   = yNodes.FirstIndexWhere(y => y >= yMinCell);
                int jMax   = yNodes.LastIndexWhere(y => y < yMaxCell);
                int NxCell = iMax - iMin + 1;
                int NyCell = jMax - jMin + 1;

                int K = NxCell * NyCell;
                NodeSets_global[f] = MultidimensionalArray.Create(K, 2);

                MapTo_i[f] = new int[K];
                MapTo_j[f] = new int[K];

                int k = 0;
                for (int i = iMin; i <= iMax; i++)
                {
                    for (int j = jMin; j <= jMax; j++)
                    {
                        MapTo_i[f][k]            = i;
                        MapTo_j[f][k]            = j;
                        NodeSets_global[f][k, 0] = xNodes[i];
                        NodeSets_global[f][k, 1] = yNodes[j];

                        Debug.Assert(GridData.Cells.IsInCell(new double[] { xNodes[i], yNodes[j] }, JS[f], null));

                        k++;
                    }
                }

                NodeSets[f] = new NodeSet(GridData.Cells.GetRefElement(jCell), NxCell * NyCell, 2);
                GridData.TransformGlobal2Local(NodeSets_global[f], NodeSets[f], JS[f], null);
                NodeSets[f].LockForever();
            }

            var TrGl2Loc = AffineTrafo.FromPoints(NodeSets_global.Last().ExtractSubArrayShallow(new int[] { 0, 0 }, new int[] { 2, 1 }), NodeSets.Last().ExtractSubArrayShallow(new int[] { 0, 0 }, new int[] { 2, 1 }));

            Trafilein = (TrGl2Loc * TrChey2Glob).Invert();



            var Return = MultidimensionalArray.Create(N, N);

            for (int f = 0; f < JS.Length; f++)
            {
                int K      = NodeSets[f].GetLength(0);
                var Result = MultidimensionalArray.Create(1, K);
                Phi.Evaluate(JS[f], 1, NodeSets[f], Result);

                for (int k = 0; k < K; k++)
                {
                    //double x_i = xNodes[MapTo_i[f][k]];
                    //double y_j = yNodes[MapTo_j[f][k]];
                    //double val = (x_i*0.3).Pow2()  - (y_j*0.3).Pow2();



                    Debug.Assert(Return[MapTo_i[f][k], MapTo_j[f][k]] == 0.0);
                    Return[MapTo_i[f][k], MapTo_j[f][k]] = Result[0, k];
                }
            }

            // ----------------------------------

            return(Return);
        }
Пример #16
0
 /// <summary>
 /// Constructs a root node of a tree.
 /// </summary>
 /// <param name="owner">
 /// The creator of this object.
 /// </param>
 /// <param name="level">
 /// The level of this node in the subdivision tree, i.e. the number
 /// of ancestors.
 /// </param>
 /// <param name="vertexSet">
 /// The set containing all vertices of <b>all</b> nodes on this
 /// level of the subdivision tree (i.e., of this node and its
 /// siblings).
 /// </param>
 /// <param name="vertexIndices">
 /// The indices of the vertices of this node in
 /// <see cref="vertexSet"/>.
 /// </param>
 /// <param name="transformationFromRoot">
 /// The affine transformation that transforms a vertex of the root
 /// simplex to a vertex of this node.
 /// </param>
 public Node(SimplexSubdivisionTree owner, int level, NestedVertexSet vertexSet, int[] vertexIndices, AffineTrafo transformationFromRoot)
     : this(owner, null, level, vertexSet, vertexIndices, transformationFromRoot)
 {
 }
        static AffineTrafo Convert(Transformation transformation)
        {
            var trafo = new AffineTrafo(transformation.Matrix, transformation.AffineTransformation);

            return(trafo);
        }
Пример #18
0
 /// <summary>
 /// Constructs an uncut subdivision node defined by the given
 /// transformation.
 /// </summary>
 /// <param name="transformation">
 /// The transformation that transforms the vertices of the original
 /// simplex to the vertices of the sub-simplex represented by this
 /// node.
 /// </param>
 public SubdivisionNode(AffineTrafo transformation)
     : this(transformation, false)
 {
 }
Пример #19
0
            /// <summary>
            /// Creates a new child of <paramref name="parrent"/>.
            /// </summary>
            /// <param name="owner">
            /// <see cref="Node.Node(SimplexSubdivisionTree, Node, int, NestedVertexSet, int[], AffineTrafo)"/>
            /// </param>
            /// <param name="parrent">
            /// The parrent of this node.
            /// </param>
            /// <param name="level">
            /// <see cref="Node.Node(SimplexSubdivisionTree, Node, int, NestedVertexSet, int[], AffineTrafo)"/>
            /// </param>
            /// <param name="vertexSet">
            /// <see cref="Node.Node(SimplexSubdivisionTree, Node, int, NestedVertexSet, int[], AffineTrafo)"/>
            /// </param>
            /// <param name="vertexIndices">
            /// <see cref="Node.Node(SimplexSubdivisionTree, Node, int, NestedVertexSet, int[], AffineTrafo)"/>
            /// </param>
            /// <param name="transformationFromRoot">
            /// <see cref="Node.Node(SimplexSubdivisionTree, Node, int, NestedVertexSet, int[], AffineTrafo)"/>
            /// </param>
            private Node(SimplexSubdivisionTree owner, Node parrent, int level, NestedVertexSet vertexSet, int[] vertexIndices, AffineTrafo transformationFromRoot)
                : base(transformationFromRoot)
            {
                Debug.Assert(vertexIndices.Length == owner.refElement.NoOfVertices, "Wrong number of vertices");
                Debug.Assert(vertexIndices.All((i) => i >= 0), "All vertex indices must be positive");
                Debug.Assert(vertexIndices.Distinct().Count() == vertexIndices.Length, "Vertex indices must be unique");

                this.owner               = owner;
                this.level               = level;
                this.vertexSet           = vertexSet;
                this.globalVertexIndices = vertexIndices;

                Children  = new List <Node>();
                distances = new double[globalVertexIndices.Length];
                ArrayTools.SetAll(distances, double.NaN);

                if (parrent != null)
                {
                    for (int i = 0; i < globalVertexIndices.Length; i++)
                    {
                        int parrentIndex = -1;
                        for (int j = 0; j < parrent.globalVertexIndices.Length; j++)
                        {
                            if (parrent.globalVertexIndices[j] == globalVertexIndices[i])
                            {
                                parrentIndex = j;
                                break;
                            }
                        }

                        if (parrentIndex >= 0)
                        {
                            distances[i] = parrent.distances[parrentIndex];
                        }
                    }
                }
            }
Пример #20
0
 /// <summary>
 /// Constructs a new tree with depth zero.
 /// </summary>
 /// <param name="refElement">
 /// The simplex to be subdivided
 /// </param>
 /// <param name="vertexSet">
 /// A container for the vertices of the subdivisions simplices. Must
 /// already contain the vertices of the root simplex.
 /// </param>
 /// <param name="rootVertexIndices">
 /// Indices of the vertices of the root simplex in
 /// <paramref name="vertexSet"/>.
 /// </param>
 public SimplexSubdivisionTree(RefElement refElement, NestedVertexSet vertexSet, int[] rootVertexIndices)
 {
     this.refElement = refElement;
     this.treeRoot   = new Node(this, 0, vertexSet, rootVertexIndices, AffineTrafo.Identity(refElement.SpatialDimension));
     this.depth      = 0;
 }
Пример #21
0
        public void CreateWithMatlab()
        {
            // ================================
            // generate voronoi graph in matlab
            // ================================


            int J = this.DelaunayVertices.NoOfRows;
            int D = this.DelaunayVertices.NoOfCols;

            if (D != 2)
            {
                throw new NotSupportedException("todo");
            }


            int[][] OutputVertexIndex = new int[J * 5][];
            MultidimensionalArray VertexCoordinates;
            {
                var Matlab = new BatchmodeConnector();

                Matlab.PutMatrix(this.DelaunayVertices, "X");

                // create mirror points
                Matlab.Cmd("[J, D] = size(X);");
                Matlab.Cmd("Xneg = [-X(:, 1), X(:, 2)];");
                Matlab.Cmd("Yneg = [X(:, 1), -X(:, 2)];");
                Matlab.Cmd("X2 = [ones(J, 1) * 2, zeros(J, 1)];");
                Matlab.Cmd("Y2 = [zeros(J, 1), ones(J, 1) * 2];");

                Matlab.Cmd("Xm = X;");
                Matlab.Cmd("Xm = [Xm; Xneg];    % mirror at x = 0");
                Matlab.Cmd("Xm = [Xm; X2 + Xneg]; % mirror at x = 1");
                Matlab.Cmd("Xm = [Xm; Yneg];    % mirror at x = 0");
                Matlab.Cmd("Xm = [Xm; Y2 + Yneg]; % mirror at x = 1");

                // compute Voronoi diagramm
                Matlab.Cmd("[V, C] = voronoin(Xm);");

                // output (export from matlab)
                Matlab.GetStaggeredIntArray(OutputVertexIndex, "C");
                Matlab.GetMatrix(null, "V");

                // run matlab
                Matlab.Execute(false);

                // import here
                VertexCoordinates = (MultidimensionalArray)(Matlab.OutputObjects["V"]);

                // correct indices (1-based index to 0-based index)
                foreach (int[] cell in OutputVertexIndex)
                {
                    int K = cell.Length;
                    for (int k = 0; k < K; k++)
                    {
                        cell[k]--;
                    }
                }
            }

            // ===============
            // record internal
            // ===============

            {
                // define Cell data
                {
                    this.m_CellData         = new CellData();
                    this.m_CellData.m_Owner = this;
                    this.m_VertexData       = new VertexData();
                    this.m_LogEdges         = new LogEdgeData();

                    this.m_VertexData.Coordinates = VertexCoordinates;
                    this.m_CellData.CellVertices  = OutputVertexIndex.GetSubVector(0, J);

                    this.m_CellData.InfoFlags = new CellInfo[J];
                    ArrayTools.SetAll(this.m_CellData.InfoFlags, CellInfo.CellIsAffineLinear | CellInfo.IsAggregate);
                }

                // decomposition of Voronoi cells to triangles/tetrahedrons
                {
                    m_CellData.AggregateCellToParts = new int[J][];
                    if (D == 2)
                    {
                        var Tri = RefElements.Triangle.Instance;
                        m_CellData.RefElements = new RefElement[] { Tri };

                        int cnt = 0;
                        for (int j = 0; j < J; j++)
                        {
                            int[] VtxIndices = m_CellData.CellVertices[j];

                            int[] PartIdx = new int[VtxIndices.Length - 2];
                            for (int i = 0; i < PartIdx.Length; i++)
                            {
                                PartIdx[i] = cnt;
                                cnt++;
                            }
                            m_CellData.AggregateCellToParts[j] = PartIdx;
                        }

                        int NoOfParts = cnt;
                        m_CellData.PartTransformation = MultidimensionalArray.Create(NoOfParts, D, D);
                        m_CellData.PartCenter         = MultidimensionalArray.Create(NoOfParts, D);

                        MultidimensionalArray TriangleVtx = MultidimensionalArray.Create(3, D);
                        for (int j = 0; j < J; j++)
                        {
                            int[] VtxIndices = m_CellData.CellVertices[j];
                            int[] PartIdx    = m_CellData.AggregateCellToParts[j];

                            for (int i = 0; i < PartIdx.Length; i++)
                            {
                                int iV0 = VtxIndices[0];
                                int iV1 = VtxIndices[i + 1];
                                int iV2 = VtxIndices[i + 2];

                                TriangleVtx[0, 0] = m_VertexData.Coordinates[iV0, 0];
                                TriangleVtx[0, 1] = m_VertexData.Coordinates[iV0, 1];
                                TriangleVtx[1, 0] = m_VertexData.Coordinates[iV1, 0];
                                TriangleVtx[1, 1] = m_VertexData.Coordinates[iV1, 1];
                                TriangleVtx[2, 0] = m_VertexData.Coordinates[iV2, 0];
                                TriangleVtx[2, 1] = m_VertexData.Coordinates[iV2, 1];

                                var TR = AffineTrafo.FromPoints(Tri.Vertices, TriangleVtx);

                                m_CellData.PartTransformation.ExtractSubArrayShallow(PartIdx[i], -1, -1).Set(TR.Matrix);
                                m_CellData.PartCenter.ExtractSubArrayShallow(PartIdx[i], -1).SetVector(TR.Affine);
                            }
                        }
                    }
                    else if (D == 3)
                    {
                        throw new NotImplementedException("todo");
                    }
                    else
                    {
                        throw new NotSupportedException("Unknown spatial dimension.");
                    }
                }

                // bounding boxes, transformations
                {
                    BoundingBox BB = new BoundingBox(D);
                    m_CellData.BoundingBoxTransformation = MultidimensionalArray.Create(J, D, D);
                    m_CellData.BoundingBoxCenter         = MultidimensionalArray.Create(J, D);
                    for (int j = 0; j < J; j++)
                    {
                        m_CellData.GetCellBoundingBox(j, BB);
                        for (int d = 0; d < D; d++)
                        {
                            double lo = BB.Min[d];
                            double hi = BB.Max[d];
                            m_CellData.BoundingBoxCenter[j, d]            = 0.5 * (lo + hi);
                            m_CellData.BoundingBoxTransformation[j, d, d] = 0.5 * (hi - lo);
                        }
                    }
                }

                // mapping: vertex to cell
                {
                    List <int>[] VertexToCell = new List <int> [VertexCoordinates.Length];
                    for (int j = 0; j < J; j++)
                    {
                        foreach (int iVtx in OutputVertexIndex[j])
                        {
                            if (VertexToCell[iVtx] == null)
                            {
                                VertexToCell[iVtx] = new List <int>();
                            }

                            if (!VertexToCell[iVtx].Contains(j))
                            {
                                VertexToCell[iVtx].Add(j);
                            }
                        }
                    }

                    m_VertexData.VerticeToCell = new int[VertexToCell.Length][];
                    for (int i = 0; i < VertexToCell.Length; i++)
                    {
                        if (VertexToCell[i] == null)
                        {
                            m_VertexData.VerticeToCell[i] = new int[0];
                        }
                        else
                        {
                            m_VertexData.VerticeToCell[i] = VertexToCell[i].ToArray();
                        }
                    }
                    VertexToCell = null;
                }

                // cell neighbors, edges
                {
                    m_CellData.CellNeighbours = new int[J][];
                    var tmpCells2Edges = new List <int> [J];

                    Dictionary <int, int> ShareCount = new Dictionary <int, int>(); // key: cell index; value: number of vertices shared with this cell
                    var        EdgesTemp             = new List <EdgeTemp>();
                    List <int> Neighs            = new List <int>();
                    List <int> EdgeVtx           = new List <int>();
                    List <int> IdedEdgsAtOneCell = new List <int>();
                    for (int jCell = 0; jCell < J; jCell++)   // loop over cells
                    {
                        ShareCount.Clear();
                        Neighs.Clear();
                        IdedEdgsAtOneCell.Clear();

                        // determine how many vertices 'jCell' shares with other cells
                        foreach (int iVtx in m_CellData.CellVertices[jCell])
                        {
                            foreach (int jOtherCell in m_VertexData.VerticeToCell[iVtx])
                            {
                                if (jOtherCell != jCell)
                                {
                                    if (!ShareCount.ContainsKey(jOtherCell))
                                    {
                                        ShareCount.Add(jOtherCell, 1);
                                    }
                                    else
                                    {
                                        ShareCount[jOtherCell]++;
                                    }
                                }
                            }
                        }

                        // find faces
                        int[][] FaceIdx = ConvexHullFaces(m_VertexData.Coordinates, m_CellData.CellVertices[jCell]);

                        // determine cell neighbors and edges
                        int NoOfFacesFound = 0;
                        foreach (var kv in ShareCount)
                        {
                            int jCellNeigh    = kv.Key;
                            int NoOfSharedVtx = kv.Value;
                            if (NoOfSharedVtx >= D)
                            {
                                // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                // cells 'jCell' and 'jCellNeigh' share more than 'D' vertices - this is an edge to another cell,
                                // resp. a face of 'jCell'.
                                // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                                Debug.Assert(jCellNeigh != jCell);
                                Debug.Assert(!Neighs.Contains(jCellNeigh));
                                Neighs.Add(jCellNeigh);
                                NoOfFacesFound++;

                                EdgeVtx.Clear();
                                foreach (int iVtx in m_CellData.CellVertices[jCell])
                                {
                                    if (Array.IndexOf(m_VertexData.VerticeToCell[iVtx], jCellNeigh) >= 0)
                                    {
                                        EdgeVtx.Add(iVtx);
                                    }
                                }


                                if (jCell < jCellNeigh)
                                {
                                    // the pairing 'jCell'/'jCellNeigh' will be discovered twice;
                                    // we only want to record once

                                    var Etmp = new EdgeTemp()
                                    {
                                        jCell1   = jCell,
                                        jCell2   = jCellNeigh,
                                        Vertices = EdgeVtx.ToArray()
                                    };
                                    EdgesTemp.Add(Etmp);
                                    IdedEdgsAtOneCell.Add(EdgesTemp.Count - 1);
                                    if (tmpCells2Edges[jCell] == null)
                                    {
                                        tmpCells2Edges[jCell] = new List <int>();
                                    }
                                    if (tmpCells2Edges[jCellNeigh] == null)
                                    {
                                        tmpCells2Edges[jCellNeigh] = new List <int>();
                                    }
                                    tmpCells2Edges[jCell].Add(EdgesTemp.Count);       // the funky convention for edges-to-cell: the index is
                                    tmpCells2Edges[jCellNeigh].Add(-EdgesTemp.Count); // shifted by 1, out-cell is negative
                                }
                                else
                                {
                                    Debug.Assert(jCellNeigh < jCell);

                                    int MatchCount = 0;
                                    foreach (int i in tmpCells2Edges[jCellNeigh])
                                    {
                                        int iEdge = Math.Abs(i) - 1;

                                        if (EdgesTemp[iEdge].jCell1 == jCellNeigh && EdgesTemp[iEdge].jCell2 == jCell)
                                        {
                                            MatchCount++;
                                            IdedEdgsAtOneCell.Add(iEdge);
                                        }
                                    }
                                    Debug.Assert(MatchCount == 1);
                                }
#if DEBUG
                                if (D == 2)
                                {
                                    Debug.Assert(EdgeVtx.Count == 2);
                                }
                                else if (D == 3)
                                {
                                    // verify that all vertices of the edge are geometrically in one plane

                                    Debug.Assert(EdgeVtx.Count >= 3);
                                    var FacePlane = AffineManifold.FromPoints(
                                        m_VertexData.Coordinates.GetRowPt(EdgeVtx[0]),
                                        m_VertexData.Coordinates.GetRowPt(EdgeVtx[1]),
                                        m_VertexData.Coordinates.GetRowPt(EdgeVtx[2])
                                        );

                                    BoundingBox BB = new BoundingBox(D);
                                    m_CellData.GetCellBoundingBox(jCell, BB);
                                    double h = BB.Diameter;

                                    foreach (int iVtx in EdgeVtx)
                                    {
                                        double dist = Math.Abs(FacePlane.PointDistance(m_VertexData.Coordinates.GetRow(iVtx)));
                                        Debug.Assert(dist < h * 1e-8);
                                    }
                                }
                                else
                                {
                                    throw new NotSupportedException("Unknown spatial dimension.");
                                }
#endif
                            }
                        }
                        m_CellData.CellNeighbours[jCell] = Neighs.ToArray();
                        Debug.Assert(NoOfFacesFound <= FaceIdx.Length);
                        Debug.Assert(NoOfFacesFound == IdedEdgsAtOneCell.Count);

                        // boundary edges
                        if (NoOfFacesFound == FaceIdx.Length)
                        {
                            // nothing to do - all faces/edges identified
#if DEBUG
                            for (int i = 0; i < NoOfFacesFound; i++)
                            {
                                int Matches = 0;
                                for (int l = 0; l < NoOfFacesFound; l++)
                                {
                                    if (FaceIdx[i].SetEquals(EdgesTemp[IdedEdgsAtOneCell[l]].Vertices))
                                    {
                                        Matches++;
                                    }
                                }
                                Debug.Assert(Matches == 1);
                            }
#endif
                        }
                        else
                        {
                            // missing boundary

                            for (int i = 0; i < FaceIdx.Length; i++)
                            {
                                int Matches = 0;
                                for (int l = 0; l < NoOfFacesFound; l++)
                                {
                                    if (FaceIdx[i].SetEquals(EdgesTemp[IdedEdgsAtOneCell[l]].Vertices))
                                    {
                                        Matches++;
                                    }
                                }
                                Debug.Assert(Matches <= 1);

                                if (Matches == 0)
                                {
                                    // boundary edge found
                                    var Etmp = new EdgeTemp()
                                    {
                                        jCell1   = jCell,
                                        jCell2   = int.MinValue,
                                        Vertices = EdgeVtx.ToArray()
                                    };
                                    EdgesTemp.Add(Etmp);
                                    tmpCells2Edges[jCell].Add(EdgesTemp.Count); // index shifted by 1
                                }
                            }
                        }
                    }

                    // convert temporary data structures to the final ones
                    m_CellData.Cells2Edges = new int[J][];
                    var C2E = m_CellData.Cells2Edges;
                    for (int j = 0; j < J; j++)
                    {
                        C2E[j] = tmpCells2Edges[j] != null ? tmpCells2Edges[j].ToArray() : new int[0];
                    }


                    m_GeomEdges = new GeomEdgeData();
                    int NoOfEdges = EdgesTemp.Count;
                    m_GeomEdges.Info          = new EdgeInfo[NoOfEdges];
                    m_LogEdges.CellIndices    = new int[NoOfEdges, 2];
                    m_GeomEdges.VertexIndices = new int[NoOfEdges][];
                    var Evtx = m_GeomEdges.VertexIndices;
                    var E2C  = m_LogEdges.CellIndices;
                    var Einf = m_GeomEdges.Info;
                    for (int iEdge = 0; iEdge < NoOfEdges; iEdge++)
                    {
                        var Etmp = EdgesTemp[iEdge];
                        E2C[iEdge, 0] = Etmp.jCell1;
                        E2C[iEdge, 1] = Etmp.jCell2;
                        Einf[iEdge]   = EdgeInfo.EdgeIsAffineLinear | EdgeInfo.IsAggregate;
                        if (Etmp.jCell2 < 0)
                        {
                            Einf[iEdge] |= EdgeInfo.Boundary;
                        }
                        Evtx[iEdge] = Etmp.Vertices;
                    }
                }


                // edge metrics
                {
                    if (D == 2)
                    {
                        m_GeomEdges.EdgeRefElements = new RefElement[] { Line.Instance };
                    }
                    else if (D == 3)
                    {
                        m_GeomEdges.EdgeRefElements = new RefElement[] { Triangle.Instance };
                    }
                    else
                    {
                        throw new NotSupportedException("Unknown spatial dimension.");
                    }

                    int[][] Evtx = m_GeomEdges.VertexIndices;

                    int NoOfParts = 0;
                    int NoOfEdges = m_GeomEdges.Count;
                    for (int iEdge = 0; iEdge < NoOfEdges; iEdge++)
                    {
                        NoOfParts += Evtx[iEdge].Length - D + 1;
                    }
                    Debug.Assert(D != 2 || NoOfParts == NoOfEdges);

                    m_GeomEdges.Edge2CellTrafoIndex = new int[NoOfParts, 2];
                    var tmpEdg2CellTrafo = new Dictionary <int, Tuple <int, AffineTrafo> >();

                    // unsolved problem:
                    // (D-1) -- dimensional tesselation of edge is given by D -- dimensional tesselation of adjacent cells
                    // * case D == 2: trivial
                    // * case D == 3: the problem is that two adjacent cells will induce _two different_ tesselations, which
                    //                wont match in the general case.


                    MultidimensionalArray PreImage = m_GeomEdges.EdgeRefElements[0].Vertices;
                    MultidimensionalArray Image    = MultidimensionalArray.Create(PreImage.NoOfRows, D);

                    //for(int iEdge )
                }
            }
        }