/// <summary> /// WARNING: This doesn't work if a node is enriched with more than 1 Heaviside functions from different cracks. /// </summary> /// <param name="subdomain"></param> /// <returns></returns> private static Dictionary <XNode, HashSet <IEnrichmentItem2D> > FindBoundaryNodesWithSingularHeaviside( ICrackDescription crack, XSubdomain2D_old subdomain) { var singularNodeEnrichments = new Dictionary <XNode, HashSet <IEnrichmentItem2D> >(); foreach (ISingleCrack singleCrack in crack.SingleCracks) { // Build nodal supports of boundary Heaviside nodes. // TODO: if the same node is enriched with more than one cracks, then avoid redoing the following for each crack var boundaryHeavisideNodes = new List <XNode>(); var nodalSupports = new List <ISet <XContinuumElement2D> >(); foreach (var node in subdomain.BoundaryNodes) { // Only process Heaviside nodes bool isHeaviside = false; foreach (var enrichment in node.EnrichmentItems.Keys) { if (enrichment == singleCrack.CrackBodyEnrichment) { boundaryHeavisideNodes.Add(node); isHeaviside = true; break; } } if (isHeaviside) { // Intersection of nodal support and subdomain var support = new HashSet <XContinuumElement2D>(); foreach (var element in singleCrack.Mesh.FindElementsWithNode(node)) { if (subdomain.Elements.Contains(element)) { support.Add(element); } } nodalSupports.Add(support); } } // Find problematic nodes ISet <XNode> singularNodes = singleCrack.SingularityResolver. FindHeavisideNodesToRemove(singleCrack, boundaryHeavisideNodes, nodalSupports); // Register the problematic nodes foreach (var node in singularNodes) { bool nodeExists = singularNodeEnrichments.TryGetValue(node, out HashSet <IEnrichmentItem2D> enrichmentsOnly); if (!nodeExists) { enrichmentsOnly = new HashSet <IEnrichmentItem2D>(); singularNodeEnrichments.Add(node, enrichmentsOnly); } enrichmentsOnly.Add(singleCrack.CrackBodyEnrichment); } } return(singularNodeEnrichments); }
public void UpdateSubdomains(XCluster2D cluster) { //TODO: this can be done without accessing the single crack. We just need to access the same tip element and nodes foreach (ISingleCrack singleCrack in crack.SingleCracks) { // Find the subdomain that contains the crack tip CartesianPoint tip = singleCrack.CrackTips[0]; XContinuumElement2D tipElement = singleCrack.CrackTipElements[tip][0]; // If there are more neighboring ones, we don't need them XSubdomain2D_old tipSubdomain = cluster.FindSubdomainOfElement(tipElement); // Find all elements that have at least one tip enriched node //TODO: this can be merged with the next subtask var tipEnrichedElements = new HashSet <XContinuumElement2D>(); ISet <XNode> tipNodes = singleCrack.CrackTipNodesNew[singleCrack.CrackTipEnrichments]; foreach (XNode node in tipNodes) { tipEnrichedElements.UnionWith(mesh.FindElementsWithNode(node)); } // Find which of these elements must be removed and from which subdomains var removedElements = new Dictionary <XContinuumElement2D, XSubdomain2D_old>(); foreach (var element in tipEnrichedElements) { if (!tipSubdomain.Elements.Contains(element)) { XSubdomain2D_old previousSubdomain = cluster.FindSubdomainOfElement(element); removedElements.Add(element, previousSubdomain); } } if (removedElements.Count == 0) { continue; } // Find the old subdomains of the nodes of the elements to be removed, before moving the elements Dictionary <XNode, HashSet <XSubdomain2D_old> > oldNodeMembership = FindOldNodeMembership(cluster, removedElements); // Move these elements to the subdomain that contains the crack tip //var modifiedSubdomains = new HashSet<XSubdomain2D>(); foreach (var elementSubdomainPair in removedElements) { XContinuumElement2D element = elementSubdomainPair.Key; XSubdomain2D_old oldSubdomain = elementSubdomainPair.Value; oldSubdomain.Elements.Remove(element); tipSubdomain.Elements.Add(element); //modifiedSubdomains.Add(previousSubdomain); //modifiedSubdomains.Add(tipSubdomain); //TODO: this only needs to be added once } // Move their nodes to their new subdomains, which also updates the boundaries. RemoveNodesFromSubdomains(oldNodeMembership); Dictionary <XNode, HashSet <XSubdomain2D_old> > newNodeMembership = FindNewNodeMembership(cluster, oldNodeMembership.Keys); AddNodesToSubdomains(newNodeMembership); } }
public static XSubdomainDofOrderer CreateNodeMajor(ICrackDescription crack, XSubdomain2D_old subdomain) //TODO: also add AMD reordering { // Handle nodes with singular Heaviside enrichment Dictionary <XNode, HashSet <IEnrichmentItem2D> > singularityHeavisideEnrichments = FindBoundaryNodesWithSingularHeaviside(crack, subdomain); if (singularityHeavisideEnrichments.Count > 0) { Console.Write($"WARNING: Subdomain {subdomain.ID} has boundary nodes that are enriched with Heaviside,"); Console.Write(" but that would lead to singular matrices, since the nodal support in this subdomain only"); Console.Write(" contains Gauss points with the same sign. It would be better to use another domain"); Console.Write(" decomposition. The nodes in question are: "); foreach (var node in singularityHeavisideEnrichments.Keys) { Console.Write(node + " "); } Console.WriteLine(); } var subdomainEnrichedDofs = new DofTable <EnrichedDof>(); var boundaryDofs = new Dictionary <XNode, ISet <EnrichedDof> >(); int dofCounter = 0; foreach (XNode node in subdomain.AllNodes) { bool isboundaryNode = subdomain.BoundaryNodes.Contains(node); if (isboundaryNode && node.IsEnriched) { boundaryDofs.Add(node, new HashSet <EnrichedDof>()); } bool isSingularityNode = singularityHeavisideEnrichments.TryGetValue(node, out HashSet <IEnrichmentItem2D> singularEnrichments); foreach (IEnrichmentItem2D enrichment in node.EnrichmentItems.Keys) { if (isSingularityNode && singularEnrichments.Contains(enrichment)) { continue; } foreach (EnrichedDof dof in enrichment.Dofs) { if (isboundaryNode) { boundaryDofs[node].Add(dof); } subdomainEnrichedDofs[node, dof] = dofCounter; ++dofCounter; } } } return(new XSubdomainDofOrderer(dofCounter, subdomainEnrichedDofs, singularityHeavisideEnrichments, boundaryDofs)); }
private XSubdomain2D_old[] AssignNodesToSubdomains(IReadOnlyList <XNode> nodes, IReadOnlyList <IRegion2D> regions) { var subdomains = new XSubdomain2D_old[regions.Count]; for (int s = 0; s < regions.Count; ++s) { subdomains[s] = new XSubdomain2D_old(s); } foreach (var node in nodes) { int multiplicity = 0; for (int s = 0; s < regions.Count; ++s) { NodePosition pos = regions[s].FindRelativePosition(node); if (pos == NodePosition.Internal) { if (multiplicity > 0) { throw new ArgumentException( $"The regions overlap, as {node} is internal to region {s} and boundary to an earlier one."); } subdomains[s].AddInternalNode(node); ++multiplicity; break; // It cannot belong to other regions. TODO: perhaps this should be checked } else if (pos == NodePosition.Boundary) { ++multiplicity; subdomains[s].AddBoundaryNode(node); // It can also belong to other regions. } } if (multiplicity == 0) { throw new IncorrectDecompositionException( $"{node} did not belong to any of the regions provided"); } } return(subdomains); }
/// <summary> /// Index i = element local dof. Dictionary[i] = global dof. /// </summary> /// <param name="element"></param> /// <returns></returns> public IReadOnlyDictionary <int, int> MatchElementToGlobalEnrichedDofsOf(XContinuumElement2D element) //TODO: this can be an array. { XSubdomain2D_old subdomain = cluster.FindSubdomainOfElement(element); return(subdomain.DofOrderer.MatchElementToGlobalEnrichedDofs(element)); }
/// <summary> /// </summary> /// <param name="element"></param> /// <param name="globalFreeVector">Both the free standard and enriched dofs.</param> /// <returns></returns> public Vector ExtractEnrichedDisplacementsOfElementFromGlobal(XContinuumElement2D element, Vector globalFreeVector) { XSubdomain2D_old subdomain = cluster.FindSubdomainOfElement(element); return(subdomain.DofOrderer.ExtractEnrichedDisplacementsOfElementFromGlobal(cluster, element, globalFreeVector)); }