/// <summary> /// finds the smallest bounding box (in the geometric binary tree) which contains cell <paramref name="jCell"/>; /// </summary> /// <param name="jCell"></param> /// <return> /// the bounding box information (branch and significant bits) for the bounding box of cell <paramref name="jCell"/>; /// </return> public BoundingBoxCode GetCellBoundingBoxCode(int jCell) { BoundingBoxCode ret; int sigbits; ret.Branch = GeomBinTreeBranchCode.Combine(CellMinCode[jCell], CellMaxCode[jCell], out sigbits); ret.SignificantBits = (uint)sigbits; return(ret); }
/// <summary> /// ctor /// </summary> /// <param name="grdDat"></param> public CellLocalization(GridData grdDat) { m_GrdDat = grdDat; int J = grdDat.Cells.NoOfLocalUpdatedCells; int D = grdDat.SpatialDimension; CellMinCode = new GeomBinTreeBranchCode[J]; CellMaxCode = new GeomBinTreeBranchCode[J]; // 1st pass: find bounding box of grid // =================================== GridBB = m_GrdDat.LocalBoundingBox; GridBB.ExtendByFactor(0.01); // 2nd pass: localize all cells // ============================ CellMinCode = new GeomBinTreeBranchCode[J]; CellMaxCode = new GeomBinTreeBranchCode[J]; BoundingBox CellBB = new BoundingBox(D); BoundingBox TreeBB = new BoundingBox(D); var CellBB_points = new double[][] { CellBB.Min, CellBB.Max }; for (int j = 0; j < J; j++) { m_GrdDat.Cells.GetCellBoundingBox(j, CellBB); //grdDat.TransformLocal2Global(verticesLoc, vericesGlob, j, 1, 0); uint mincode = uint.MaxValue; uint maxcode = 0; foreach (var _pt in CellBB_points) { GeomBinTreeBranchCode cd = GeomBinTreeBranchCode.CreateFormPoint(GridBB, _pt); mincode = Math.Min(mincode, cd.Code); maxcode = Math.Max(maxcode, cd.Code); } CellMinCode[j].Code = mincode; CellMaxCode[j].Code = maxcode; // test BoundingBoxCode currentlyGen = GetCellBoundingBoxCode(j); GridBB.SubBoxFromCode(TreeBB, currentlyGen); if (!TreeBB.Contains(CellBB)) { throw new ApplicationException("internal error - should not occur"); } } }
/// <summary> /// Usually used after <see cref="MergeLogically(GridCommons, GridCommons)"/>; this method finds element boundaries /// which intersect geometrically, but not logically and inserts a <see cref="CellFaceTag"/> which connects those cells. /// </summary> /// <param name="g"></param> /// <param name="upsampling"></param> /// <returns></returns> public static GridCommons Seal(GridCommons g, int upsampling = 4) { GridCommons R = g.CloneAs(); g = null; GridData gdat = new GridData(R); int D = gdat.SpatialDimension; int J = gdat.Cells.NoOfLocalUpdatedCells; if (R.CellPartitioning.MpiSize > 1) { throw new NotSupportedException("Not supported in MPI-parallel mode."); } //NodeSet[] TestNodes = gdat.Edges.EdgeRefElements.Select(KrefEdge => KrefEdge.GetSubdivisionTree(upsampling).GlobalVertice).ToArray(); NodeSet[] TestNodes = gdat.Edges.EdgeRefElements.Select(KrefEdge => KrefEdge.GetBruteForceQuadRule(upsampling, 1).Nodes).ToArray(); // Its better to use vertices in the interior of the element; if we use vertices at the corners, we might get // intersection of edges that just share one point. // Define all edges that will be tested (set to boundary edges) // ============================================================ int[] UnknownEdges = gdat.BoundaryEdges.ItemEnum.ToArray(); int L = UnknownEdges.Sum(iEdg => TestNodes[gdat.Edges.GetRefElementIndex(iEdg)].NoOfNodes); // Transform nodes on edges (that should be tested) to global coordinates // ====================================================================== MultidimensionalArray TestNodesGlobal = MultidimensionalArray.Create(L, D); MultidimensionalArray NormalsGlobal = MultidimensionalArray.Create(L, D); int[] NodeToEdge = new int[L]; // pointer l -> Edge index, where l is the first index into 'TestNodesGlobal' & 'NormalsGlobal' int[,] E2C = gdat.Edges.CellIndices; int cnt = 0; foreach (int iEdg in UnknownEdges) { int iKref = gdat.Edges.GetRefElementIndex(iEdg); NodeSet Ns = TestNodes[iKref]; int K = Ns.NoOfNodes; int[] I0 = new int[] { cnt, 0 }; int[] IE = new int[] { cnt + K - 1, D - 1 }; MultidimensionalArray TN = gdat.GlobalNodes.GetValue_EdgeSV(Ns, iEdg, 1); TestNodesGlobal.SetSubArray(TN.ExtractSubArrayShallow(0, -1, -1), I0, IE); MultidimensionalArray N1 = gdat.Edges.NormalsCache.GetNormals_Edge(Ns, iEdg, 1); NormalsGlobal.SetSubArray(N1.ExtractSubArrayShallow(0, -1, -1), I0, IE); for (int i = cnt; i < cnt + K; i++) { NodeToEdge[i] = iEdg; } cnt += K; } // binary tree to speed up point localization int[] pl_Permutation = new int[L]; PointLocalization pl = new PointLocalization(TestNodesGlobal, 0.01, pl_Permutation); Debug.Assert(!object.ReferenceEquals(pl.Points, TestNodesGlobal)); // compare search edges to all other nodes // ======================================= // mapping: cell --> Neighbour cell index, face index // 1st index: Cell index; // 2nd index: enumeration List <Tuple <int, int> >[] FoundPairings = new List <Tuple <int, int> > [gdat.Cells.NoOfCells]; int[][] C2E = gdat.Cells.Cells2Edges; byte[,] E2F = gdat.Edges.FaceIndices; int cnt2 = 0; for (int iEdgC = 0; iEdgC < UnknownEdges.Length; iEdgC++) // loop over edges that may get sealed { int iEdg = UnknownEdges[iEdgC]; int iKref = gdat.Edges.GetRefElementIndex(iEdg); NodeSet Ns = TestNodes[iKref]; int K = Ns.NoOfNodes; int jCell1 = E2C[iEdg, 0]; Debug.Assert(E2C[iEdg, 1] < 0); int iFace1 = E2F[iEdg, 0]; Debug.Assert(E2F[iEdg, 1] == byte.MaxValue); int[] I0 = new int[] { cnt2, 0 }; int[] IE = new int[] { cnt2 + K - 1, D - 1 }; MultidimensionalArray TN = TestNodesGlobal.ExtractSubArrayShallow(I0, IE); //MultidimensionalArray N1 = NormalsGlobal.ExtractSubArrayShallow(I0, IE); // find bounding box for edge BoundingBox bbEdge = new BoundingBox(TN); if (bbEdge.h_min / bbEdge.h_max < 1.0e-5) { // very thin bounding box, thicken slightly double delta = bbEdge.h_max * 1.0e-5; for (int d = 0; d < D; d++) { bbEdge.Min[d] -= delta; bbEdge.Max[d] += delta; } } bbEdge.ExtendByFactor(0.01); // determine binary code for bounding box int bbEdgeSigBits; GeomBinTreeBranchCode bbEdgeCode = GeomBinTreeBranchCode.Combine( GeomBinTreeBranchCode.CreateFormPoint(pl.PointsBB, bbEdge.Min), GeomBinTreeBranchCode.CreateFormPoint(pl.PointsBB, bbEdge.Max), out bbEdgeSigBits); // determine all points in bounding box int iP0, Len; pl.GetPointsInBranch(bbEdgeCode, bbEdgeSigBits, out iP0, out Len); // determine all edged which potentially overlap with edge 'iEdg' HashSet <int> PotOvrlap = new HashSet <int>(); // a set of edge indices for (int n = 0; n < Len; n++) { int l = iP0 + n; int iPt = pl_Permutation[l]; Debug.Assert(GenericBlas.L2DistPow2(pl.Points.GetRow(l), TestNodesGlobal.GetRow(iPt)) <= 0); int iOvlpEdge = NodeToEdge[iPt]; if (iOvlpEdge != iEdg) { PotOvrlap.Add(iOvlpEdge); } } //int[] PotOvrlap = UnknownEdges.CloneAs(); // determine actually overlapping boundary edges: foreach (int iOvrlapEdge in PotOvrlap) { int jCell2 = E2C[iOvrlapEdge, 0]; Debug.Assert(E2C[iOvrlapEdge, 1] < 0); if (jCell2 == jCell1) { continue; } int iFace2 = E2F[iOvrlapEdge, 0]; Debug.Assert(E2F[iOvrlapEdge, 1] == byte.MaxValue); int AllreadyFound = FoundPairings[jCell1] == null ? 0 : FoundPairings[jCell1].Where(tp => tp.Item1 == jCell2 && tp.Item2 == iFace1).Count(); if (AllreadyFound > 1) { throw new ApplicationException("Error in algorithmus."); } if (AllreadyFound > 0) { continue; } var Kref_j2 = gdat.Cells.GetRefElement(jCell2); double h = Kref_j2.GetMaxDiameter(); MultidimensionalArray LocVtx_j2 = MultidimensionalArray.Create(K, D); bool[] NewtonConvervence = new bool[K]; gdat.TransformGlobal2Local(TN, LocVtx_j2, jCell2, NewtonConvervence); for (int k = 0; k < K; k++) // loop over all transformed points { if (!NewtonConvervence[k]) { continue; } double[] pt = LocVtx_j2.GetRow(k); double[] ptClose = new double[D]; double dist = Kref_j2.ClosestPoint(pt, ptClose); if (dist > h * 1.0e-8) { continue; } AffineManifold Face = Kref_j2.GetFacePlane(iFace2); double FaceDist = Face.PointDistance(pt); if (FaceDist.Abs() > 1.0e-8 * h) { continue; } NodeSet Ns2 = new NodeSet(Kref_j2, pt); MultidimensionalArray Normals2 = MultidimensionalArray.Create(1, D); gdat.Edges.GetNormalsForCell(Ns2, jCell2, iFace2, Normals2); double[] N1d = NormalsGlobal.GetRow(cnt2 + k); double[] N2d = Normals2.GetRow(0); //Check if face normals points exactly in the opposite direction, 2 ways: // 1) calculate angle between both normals -> bad choice because of Math.Acos // 2) inner product of two opposite vectors is -1 //if (Math.Abs(Math.Abs(Math.Acos(GenericBlas.InnerProd(N1d, N2d))) - Math.PI) > 1.0e-8) if (Math.Abs(GenericBlas.InnerProd(N1d, N2d) + 1.0) > 1.0e-8) { continue; } // if we reach this point, jCell1 should match with jCell2/iFace2 if (FoundPairings[jCell1] == null) { FoundPairings[jCell1] = new List <Tuple <int, int> >(); } if (FoundPairings[jCell2] == null) { FoundPairings[jCell2] = new List <Tuple <int, int> >(); } FoundPairings[jCell1].Add(new Tuple <int, int>(jCell2, iFace1)); FoundPairings[jCell2].Add(new Tuple <int, int>(jCell1, iFace2)); break; // no need to test jCell1 vs. jCell2 anymore } } cnt2 += K; } // add the newly found pairings to the grid for (int j = 0; j < J; j++) { var fp = FoundPairings[j]; if (fp != null) { foreach (var t in fp) { ArrayTools.AddToArray(new CellFaceTag() { EdgeTag = 0, FaceIndex = t.Item2, ConformalNeighborship = false, NeighCell_GlobalID = gdat.CurrentGlobalIdPermutation.Values[t.Item1] }, ref R.Cells[j].CellFaceTags); } } } return(R); }