/// <summary> /// Constructs the adapted subdivisions for all cells in /// <paramref name="mask"/>. The size of each chunk will be exactly one /// since, in general, we cannot expect subsequent cells to have the /// same subdivisions. /// </summary> /// <param name="mask"> /// <see cref="ISubdivisionStrategy.GetSubdivisionNodes"/> /// </param> /// <returns> /// The subdivisions for all elements in <paramref name="mask"/>, /// </returns> public IEnumerable <KeyValuePair <Chunk, IEnumerable <SubdivisionNode> > > GetSubdivisionNodes(ExecutionMask mask) { using (new FuncTrace()) { foreach (int iElement in mask.ItemEnum) // loop over all cells/edges in mask { this.subdivisionTree.ResetToSavePoint(); double minDistance = EstimateMinDistance(iElement, mask); NestedVertexSet currentSet = baseVertexSet; for (int i = 0; i < maxDivisions; i++) { if (currentSet.NumberOfLocalVertices == 0) { // No new vertices were added during last subdivision break; } int cell; NodeSet vertices = GetVertices(iElement, currentSet, mask, out cell); MultidimensionalArray levelSetValues = LevelSetData.GetLevSetValues(vertices, cell, 1); subdivisionTree.ReadDistances(levelSetValues.ExtractSubArrayShallow(0, -1)); currentSet = new NestedVertexSet(currentSet); subdivisionTree.Subdivide( currentSet, true, minDistance / Math.Pow(2.0, i)); } // Finally, read level set values in leaves (needed by IsCut) { int cell; NodeSet vertices = GetVertices(iElement, currentSet, mask, out cell); if (vertices != null) { MultidimensionalArray levelSetValues = LevelSetData.GetLevSetValues(vertices, cell, 1); subdivisionTree.ReadDistances(levelSetValues.ExtractSubArrayShallow(0, -1)); } } yield return(new KeyValuePair <Chunk, IEnumerable <SubdivisionNode> >( new Chunk() { i0 = iElement, Len = 1 }, subdivisionTree.Leaves)); } } }
/// <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> /// Subdivides all leaves of the current tree (see /// <see cref="Leaves"/>) that can be considered "cut" by the /// interface. A node is considered cut if the distances (provided via /// <see cref="ReadDistances"/>) have different signs at the vertices /// of a leave _or_ if the distance of at least one vertex is /// smaller than <paramref name="minDistance"/>. /// </summary> /// <param name="refinedVertexSet"> /// The vertex set that should hold the vertices of the newly created /// nodes associated with the new leaves of the tree. /// </param> /// <param name="checkIsCut"> /// If true, leaves will only subdivided of it is considered cut. If /// false, all leaves will be subdivided indifferently. /// </param> /// <param name="minDistance"> /// The minimum distance a vertx of a leave may have before the leave /// is considered cut. /// </param> /// <remarks> /// The parameter <paramref name="minDistance"/> can be used to ensure /// that all cut cells are captured in the case of a non-linear /// interface. Here, the simple check if all vertex-distances have the /// same sign is not sufficient since a curved interface can pass /// through a cell without enclosing a vertex. /// </remarks> public void Subdivide(NestedVertexSet refinedVertexSet, bool checkIsCut, double minDistance) { if (checkIsCut) { bool divided = treeRoot.SubdivideCutLeaves(depth, refinedVertexSet, minDistance); if (divided) { depth++; } } else { treeRoot.SubdivideLeaves(depth, refinedVertexSet); depth++; } }
/// <summary> /// Initializes the subdivision of the given simplex. Initially, the /// simplex remains undivided. /// </summary> /// <param name="refElement"> /// The simplex to subdivide. /// </param> /// <param name="tracker"> /// The level set tracker. Allows for the check if a simplex ist cut. /// </param> /// <param name="levSetIndex">Index of the level set</param> /// <param name="maxDivisions"> /// The maximum number of subdivisions of the simplex /// </param> public AdaptiveSubdivisionStrategy(RefElement refElement, LevelSetTracker.LevelSetData levelSetData, int maxDivisions) { this.RefElement = refElement; this.maxDivisions = maxDivisions; this.baseVertexSet = new NestedVertexSet(refElement.SpatialDimension); this.LevelSetData = levelSetData; int verticesPerCell = refElement.Vertices.GetLength(0); int[] simplexVertices = new int[verticesPerCell]; for (int i = 0; i < verticesPerCell; i++) { double[] vertex = refElement.Vertices.GetRow(i); simplexVertices[i] = baseVertexSet.RegisterVertex(vertex); } this.subdivisionTree = new SimplexSubdivisionTree( refElement, baseVertexSet, simplexVertices); this.subdivisionTree.SetSavePoint(); }
/// <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> /// Determines the vertices associated with the given element (either /// a cell or an edge). In case of a cell, they're directly stored in /// the given set <paramref name="set"/>. In case of an edge, the /// stored vertices have to be transformed from the edge coordinate /// system to the volume coordinate system. /// </summary> /// <param name="element"> /// The index of either a cell or an edge /// </param> /// <param name="set"> /// The set containing the vertices /// </param> /// <param name="mask"> /// The mask containing <paramref name="element"/>. /// </param> /// <param name="cell"> /// On exit: The cell the given element <paramref name="element"/> /// belongs to. /// </param> /// <returns> /// The vertices associated with <paramref name="element"/>. /// </returns> private NodeSet GetVertices(int element, NestedVertexSet set, ExecutionMask mask, out int cell) { //NodeSet vertices = new NodeSet(this.RefElement, set.LocalVertices); //cell = element; //if (vertices == null) { // cell = -1; // return null; //} //if (mask is EdgeMask) { // // This might be dangerous if level set is discontinuous // cell = gridData.Edges.CellIndices[element, 0]; // NodeSet volumeVertices = new NodeSet(this.RefElement, vertices.GetLength(0), gridData.SpatialDimension); // int localEdge; // if (gridData.Edges.CellIndices[element, 0] == cell) { // localEdge = gridData.Edges.FaceIndices[element, 0]; // } else { // localEdge = gridData.Edges.FaceIndices[element, 1]; // } // gridData.Grid.RefElements[0].TransformFaceCoordinates(localEdge, vertices, volumeVertices); // volumeVertices.LockForever(); // vertices = volumeVertices; //} //return vertices; MultidimensionalArray vertices = set.LocalVertices; cell = element; if (vertices == null) { cell = -1; return(null); } if (mask is EdgeMask) { // This might be dangerous if level set is discontinuous cell = gridData.Edges.CellIndices[element, 0]; MultidimensionalArray volumeVertices = MultidimensionalArray.Create( vertices.GetLength(0), gridData.SpatialDimension); int localEdge; if (gridData.Edges.CellIndices[element, 0] == cell) { localEdge = gridData.Edges.FaceIndices[element, 0]; } else { localEdge = gridData.Edges.FaceIndices[element, 1]; } gridData.Grid.RefElements[0].TransformFaceCoordinates(localEdge, vertices, volumeVertices); vertices = volumeVertices; } return(new NodeSet(this.gridData.Cells.GetRefElement(cell), vertices)); }
/// <summary> /// Constructs an "empty" set based on <paramref name="parrentSet"/>. /// </summary> /// <param name="parrentSet"> /// A parrent containing vertices that cannot be added to this set /// because they're considered duplicates. /// </param> public NestedVertexSet(NestedVertexSet parrentSet) : this(parrentSet.SpatialDimension) { this.Parrent = parrentSet; }
/// <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; }
/// <summary> /// Tells the node to subdivide itself if it is a leave of the /// tree. If it is not a tree, the command is passed down to all /// children. In contrast to /// <see cref="SubdivideLeaves(int, NestedVertexSet)"/>, nodes /// are only cut if they are (potentially) cut be the interface. /// </summary> /// <param name="leavesLevel"> /// <see cref="SubdivideLeaves(int, NestedVertexSet)"/> /// </param> /// <param name="refinedVertexSet"> /// <see cref="SubdivideLeaves(int, NestedVertexSet)"/> /// </param> /// <param name="minDistance"> /// The minimum distance a node may have from the interface before /// the node is considered cut (even though the signs of the /// distances in all vertices are the same). In the case of curved /// interfaces, this is necessary to fix certain corner cases. /// </param> /// <returns> /// True, if at least one node has been subdivided. Otherwise, /// false is returned. /// </returns> public bool SubdivideCutLeaves(int leavesLevel, NestedVertexSet refinedVertexSet, double minDistance) { return(SubdivideLeaves(leavesLevel, refinedVertexSet, true, minDistance)); }
/// <summary> /// Tells the node to subdivide itself if it is a leave of the /// tree. If it is not a tree, the command is passed down to all /// children. /// </summary> /// <param name="leavesLevel"> /// The current depth of tree. /// </param> /// <param name="refinedVertexSet"> /// The vertex set that, on exit, holds the newly added vertices /// of the new nodes. /// </param> public void SubdivideLeaves(int leavesLevel, NestedVertexSet refinedVertexSet) { SubdivideLeaves(leavesLevel, refinedVertexSet, false, double.NaN); }
/// <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) { }