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); }
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); }
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); }
public ChannelTest(double angle) { //double angle = 0.0; //double angle = 60.0 * Math.PI / 180.0; ROT = AffineTrafo.Some2DRotation(angle); ROTinv = ROT.Invert(); }
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); }
/// <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); }
/// <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]); }
/// <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}"); } } }
/// <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); }
/// <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> /// 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; }
/// <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); }
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); }
/// <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); }
/// <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) { }
/// <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]; } } } }
/// <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; }
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 ) } } }