public Dictionary <int, int> MatchElementToSubdomainEnrichedDofs(XContinuumElement2D element) { var element2Subdomain = new Dictionary <int, int>(); int elementDof = 0; foreach (XNode node in element.Nodes) { bool isSingularityNode = SingularHeavisideEnrichments.TryGetValue(node, out HashSet <IEnrichmentItem2D> singularEnrichments); // TODO: Perhaps the nodal dof types should be decided by the element type (structural, continuum) // and drawn from XXContinuumElement2D instead of from enrichment items foreach (IEnrichmentItem2D enrichment in node.EnrichmentItems.Keys) { if (isSingularityNode && singularEnrichments.Contains(enrichment)) { ++elementDof; continue; } // TODO: Perhaps I could iterate directly on the dofs, ignoring dof types for performance, if the order is guarranteed foreach (EnrichedDof dofType in enrichment.Dofs) { element2Subdomain[elementDof++] = this.subdomainEnrichedDofs[node, dofType]; } } } return(element2Subdomain); }
public SortedSet <CartesianPoint> FindTriangleVertices(XContinuumElement2D element) { var polygon = ConvexPolygon2D.CreateUnsafe(element.Nodes); var triangleVertices = new SortedSet <CartesianPoint>(element.Nodes, pointComparer); int nodesCount = element.Nodes.Count; foreach (var vertex in Vertices) { PolygonPointPosition position = polygon.FindRelativePositionOfPoint(vertex); if (position == PolygonPointPosition.Inside || position == PolygonPointPosition.OnEdge || position == PolygonPointPosition.OnVertex) { triangleVertices.Add(vertex); } } foreach (var crackSegment in Segments) { var segment = new LineSegment2D(crackSegment.Start, crackSegment.End); IReadOnlyList <CartesianPoint> intersections = segment.IntersectionWith(polygon); foreach (var point in intersections) { triangleVertices.Add(point); } } return(triangleVertices); }
/// <summary> /// If a fixed enrichment area is applied, all nodes inside a circle around the tip are enriched with tip /// functions. They can still be enriched with Heaviside functions, if they do not belong to the tip /// element(s). /// </summary> /// <param name="tipNodes"></param> /// <param name="tipElement"></param> private void ApplyFixedEnrichmentArea(CartesianPoint crackTip, XContinuumElement2D tipElement, HashSet <XNode> tipNodes, IEnrichmentItem2D tipEnrichments) { if (enrichmentRadiusOverElementSize > 0) { var outline = ConvexPolygon2D.CreateUnsafe(tipElement.Nodes); double elementArea = outline.ComputeArea(); double radius = enrichmentRadiusOverElementSize * Math.Sqrt(elementArea); var enrichmentArea = new Circle2D(crackTip, radius); foreach (var element in Mesh.FindElementsInsideCircle(enrichmentArea, tipElement)) { bool completelyInside = true; foreach (var node in element.Nodes) { CirclePointPosition position = enrichmentArea.FindRelativePositionOfPoint(node); if ((position == CirclePointPosition.Inside) || (position == CirclePointPosition.On)) { tipNodes.Add(node); } else { completelyInside = false; } } if (completelyInside) { element.EnrichmentItems.Add(tipEnrichments); } } } }
private void ReportTriangulation(XContinuumElement2D element, SortedSet <CartesianPoint> triangleVertices, IReadOnlyList <Triangle2D <CartesianPoint> > triangles) { if (!reports) { return; } Console.WriteLine("------ DEBUG: TRIANGULATION/ ------"); Console.WriteLine("Element with nodes: "); foreach (var node in element.Nodes) { Console.Write(node); Console.Write(' '); } Console.WriteLine("\nVertices of triangular mesh: "); foreach (var vertex in triangleVertices) { Console.Write(vertex); Console.Write(' '); } Console.WriteLine("\nTriangles: "); foreach (var triangle in triangles) { Console.WriteLine(triangle); } Console.WriteLine("------ /DEBUG: TRIANGULATION ------"); }
private bool IsCutElement(XContinuumElement2D element) { var polygon = ConvexPolygon2D.CreateUnsafe(element.Nodes); // Look at each segment foreach (var crackSegment in Segments) { var segment = new LineSegment2D(crackSegment.Start, crackSegment.End); CartesianPoint intersectionPoint; foreach (var edge in polygon.Edges) { LineSegment2D.SegmentSegmentPosition position = segment.IntersectionWith(edge, out intersectionPoint); if (position == LineSegment2D.SegmentSegmentPosition.Intersecting) { // TODO: Perhaps the element should not be flagged as a Heaviside element, if the segment passes // through 1 node only. To detect this, check if the intersection point coincides with an element // node. If it does store it and go to the next edge. If a second intersection point (that does // not coincide with the stored one) is found then it is a Heaviside element. return(true); } else if (position == LineSegment2D.SegmentSegmentPosition.Overlapping) { return(true); } } } // Look at the vertices // (if a segment is entirely inside an element, it will not be caught by checking the segment itself) bool previousVertexOnEdge = false; LinkedListNode <CartesianPoint> currentNode = Vertices.First.Next; LinkedListNode <CartesianPoint> lastNode = Vertices.Last; while (currentNode != lastNode) { PolygonPointPosition position = polygon.FindRelativePositionOfPoint(currentNode.Value); if (position == PolygonPointPosition.Inside) { return(true); } else if (position == PolygonPointPosition.OnEdge || position == PolygonPointPosition.OnVertex) { if (previousVertexOnEdge) { return(true); } else { previousVertexOnEdge = true; } } else { previousVertexOnEdge = false; } currentNode = currentNode.Next; } return(false); }
public List <int> GetSubdomainEnrichedDofsOf(XContinuumElement2D element) { var dofs = new List <int>(); foreach (XNode node in element.Nodes) { bool isSingularityNode = SingularHeavisideEnrichments.TryGetValue(node, out HashSet <IEnrichmentItem2D> singularEnrichments); // TODO: Perhaps the nodal dof types should be decided by the element type (structural, continuum) // and drawn from XXContinuumElement2D instead of from enrichment items foreach (IEnrichmentItem2D enrichment in node.EnrichmentItems.Keys) { if (isSingularityNode && singularEnrichments.Contains(enrichment)) { continue; } foreach (EnrichedDof dofType in enrichment.Dofs) { dofs.Add(this.subdomainEnrichedDofs[node, dofType]); } } } return(dofs); }
//TODO: I tried an alternative approach, ie elements access their enrichments from their nodes. // My original thought that this approach (storing enrichments in elements, unless they are standard / // blending) wouldn't work for blending elements, was incorrect, as elements with 0 enrichments // were then examined and separated into standard / blending. public void EnrichElement(XContinuumElement2D element) { if (!affectedElements.Contains(element)) { affectedElements.Add(element); element.EnrichmentItems.Add(this); } }
public EvaluatedFunction2D[] EvaluateAllAt(NaturalPoint point, XContinuumElement2D element, EvalInterpolation2D interpolation) { CartesianPoint cartesianPoint = interpolation.TransformPointNaturalToGlobalCartesian(); double signedDistance = crackDescription.SignedDistanceOf(point, element, interpolation); return(new EvaluatedFunction2D[] { enrichmentFunction.EvaluateAllAt(signedDistance) }); }
public void AddElement(XContinuumElement2D element) { if (elements.Contains(element)) { throw new ArgumentException("This element is already inserted"); } elements.Add(element); }
public override EvaluatedFunction2D[] EvaluateAllAt(NaturalPoint point, XContinuumElement2D element, EvalInterpolation2D interpolation) { CartesianPoint cartesianPoint = interpolation.TransformPointNaturalToGlobalCartesian(); double signedDistance = Discontinuity.SignedDistanceOf(cartesianPoint); Vector2 normalVector = Discontinuity.NormalVectorThrough(cartesianPoint); return(new EvaluatedFunction2D[] { enrichmentFunction.EvaluateAllAt(signedDistance, normalVector) }); }
private void ComputeInteractionIntegrals(XContinuumElement2D element, Vector standardNodalDisplacements, Vector enrichedNodalDisplacements, double[] nodalWeights, TipCoordinateSystem tipSystem, out double integralMode1, out double integralMode2) { integralMode1 = 0.0; integralMode2 = 0.0; foreach (GaussPoint naturalGP in element.JintegralStrategy.GenerateIntegrationPoints(element)) { // Nomenclature: global = global cartesian system, natural = element natural system, // local = tip local cartesian system EvalInterpolation2D evaluatedInterpolation = element.Interpolation.EvaluateAllAt(element.Nodes, naturalGP); CartesianPoint globalGP = evaluatedInterpolation.TransformPointNaturalToGlobalCartesian(); Matrix constitutive = element.Material.CalculateConstitutiveMatrixAt(naturalGP, evaluatedInterpolation); // State 1 Matrix2by2 globalDisplacementGradState1 = element.CalculateDisplacementFieldGradient( naturalGP, evaluatedInterpolation, standardNodalDisplacements, enrichedNodalDisplacements); Tensor2D globalStressState1 = element.CalculateStressTensor(globalDisplacementGradState1, constitutive); Matrix2by2 localDisplacementGradState1 = tipSystem. TransformVectorFieldDerivativesGlobalCartesianToLocalCartesian(globalDisplacementGradState1); Tensor2D localStressTensorState1 = tipSystem. TransformTensorGlobalCartesianToLocalCartesian(globalStressState1); // Weight Function // TODO: There should be a method InterpolateScalarGradient(double[] nodalValues) in EvaluatedInterpolation // TODO: Rewrite this as a shapeGradients (matrix) * nodalWeights (vector) operation. var globalWeightGradient = Vector2.CreateZero(); for (int nodeIdx = 0; nodeIdx < element.Nodes.Count; ++nodeIdx) { globalWeightGradient.AxpyIntoThis( evaluatedInterpolation.ShapeGradientsCartesian.GetRow(nodeIdx), // Previously: GetGlobalCartesianDerivativesOf(element.Nodes[nodeIdx]) nodalWeights[nodeIdx]); } Vector2 localWeightGradient = tipSystem. TransformScalarFieldDerivativesGlobalCartesianToLocalCartesian(globalWeightGradient); // State 2 // TODO: XContinuumElement shouldn't have to pass tipCoordinate system to auxiliaryStates. // It would be better to have CrackTip handle this and the coordinate transformations. That would also // obey LoD, but a lot of wrapper methods would be required. AuxiliaryStatesTensors auxiliary = auxiliaryStatesStrategy.ComputeTensorsAt(globalGP, tipSystem); // Interaction integrals double integrandMode1 = ComputeJIntegrand(localWeightGradient, localDisplacementGradState1, localStressTensorState1, auxiliary.DisplacementGradientMode1, auxiliary.StrainTensorMode1, auxiliary.StressTensorMode1); double integrandMode2 = ComputeJIntegrand(localWeightGradient, localDisplacementGradState1, localStressTensorState1, auxiliary.DisplacementGradientMode2, auxiliary.StrainTensorMode2, auxiliary.StressTensorMode2); integralMode1 += integrandMode1 * evaluatedInterpolation.Jacobian.DirectDeterminant * naturalGP.Weight; integralMode2 += integrandMode2 * evaluatedInterpolation.Jacobian.DirectDeterminant * naturalGP.Weight; } }
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 List <int> GetEnrichedDofsOf(XContinuumElement2D element) { var globalDofs = new List <int>(); foreach (var nodeDofLocal in element.GetEnrichedDofs()) { globalDofs.Add(enrichedDofs[nodeDofLocal.row, nodeDofLocal.col]); // It must be included. } return(globalDofs); }
public XSubdomain2D_old FindSubdomainOfElement(XContinuumElement2D element) { foreach (var subdomain in subdomains) { if (subdomain.Elements.Contains(element)) { return(subdomain); } } throw new KeyNotFoundException("This element does not belong to any subdomain."); }
/// <summary> /// Warning: with narrow band this should throw an exception if the element/nodes are not tracked. /// </summary> /// <param name="point"></param> /// <param name="elementNodes"></param> /// <param name="interpolation"></param> /// <returns></returns> public double SignedDistanceOf(NaturalPoint point, XContinuumElement2D element, EvalInterpolation2D interpolation) { double signedDistance = 0.0; for (int nodeIdx = 0; nodeIdx < element.Nodes.Count; ++nodeIdx) { signedDistance += interpolation.ShapeFunctions[nodeIdx] * levelSetsBody[element.Nodes[nodeIdx]]; } return(signedDistance); }
public Tensor2D EvaluateAt(XContinuumElement2D element, NaturalPoint point, Vector standardDisplacements, Vector enrichedDisplacements) { EvalInterpolation2D evaluatedInterpolation = element.Interpolation.EvaluateAllAt(element.Nodes, point); Matrix2by2 displacementGradient = element.CalculateDisplacementFieldGradient( point, evaluatedInterpolation, standardDisplacements, enrichedDisplacements); Matrix constitutive = element.Material.CalculateConstitutiveMatrixAt(point, evaluatedInterpolation); return(element.CalculateStressTensor(displacementGradient, constitutive)); }
/// <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) { DofTable <EnrichedDof> elementDofs = element.GetEnrichedDofs(); double[] elementVector = new double[elementDofs.EntryCount]; foreach ((XNode node, EnrichedDof dofType, int dofIdx) in elementDofs) { int globalEnrichedDof = enrichedDofs[node, dofType]; elementVector[dofIdx] = globalFreeVector[globalEnrichedDof]; } return(Vector.CreateFromArray(elementVector)); }
public static void TestJointStiffnessMatrixOfCrackBodyElement() { double equalityTolerance = 1E-13; Matrix expectedStiffnessNodeMajor = 1E6 * Matrix.CreateFromArray(new double[, ] { { 1.154, 0.481, 0.962, 0.240, -0.769, 0.096, 0.769, 0.144, -0.577, -0.481, 0.577, 0.433, 0.192, -0.096, 0.385, -0.048 }, { 0.481, 1.154, 0.240, 0.481, -0.096, 0.192, 0.337, -0.192, -0.481, -0.577, 0.529, 0.577, 0.096, -0.769, 0.048, -0.096 }, { 0.962, 0.240, 1.923, 0.481, -0.769, 0.337, 0.000, 0.000, -0.577, -0.529, 0.000, 0.000, 0.385, -0.048, 0.769, -0.096 }, { 0.240, 0.481, 0.481, 0.962, 0.144, 0.192, 0.000, 0.000, -0.433, -0.577, 0.000, 0.000, 0.048, -0.096, 0.096, -0.192 }, { -0.769, -0.096, -0.769, 0.144, 1.154, -0.481, -0.962, 0.240, 0.192, 0.096, -0.385, -0.048, -0.577, 0.481, -0.577, 0.433 }, { 0.096, 0.192, 0.337, 0.192, -0.481, 1.154, 0.240, -0.481, -0.096, -0.769, 0.048, 0.096, 0.481, -0.577, 0.529, -0.577 }, { 0.769, 0.337, 0.000, 0.000, -0.962, 0.240, 1.923, -0.481, -0.385, -0.048, 0.769, 0.096, 0.577, -0.529, 0.000, 0.000 }, { 0.144, -0.192, 0.000, 0.000, 0.240, -0.481, -0.481, 0.962, 0.048, 0.096, -0.096, -0.192, -0.433, 0.577, 0.000, 0.000 }, { -0.577, -0.481, -0.577, -0.433, 0.192, -0.096, -0.385, 0.048, 1.154, 0.481, -0.962, -0.240, -0.769, 0.096, -0.769, -0.144 }, { -0.481, -0.577, -0.529, -0.577, 0.096, -0.769, -0.048, 0.096, 0.481, 1.154, -0.240, -0.481, -0.096, 0.192, -0.337, 0.192 }, { 0.577, 0.529, 0.000, 0.000, -0.385, 0.048, 0.769, -0.096, -0.962, -0.240, 1.923, 0.481, 0.769, -0.337, 0.000, 0.000 }, { 0.433, 0.577, 0.000, 0.000, -0.048, 0.096, 0.096, -0.192, -0.240, -0.481, 0.481, 0.962, -0.144, -0.192, 0.000, 0.000 }, { 0.192, 0.096, 0.385, 0.048, -0.577, 0.481, 0.577, -0.433, -0.769, -0.096, 0.769, -0.144, 1.154, -0.481, 0.962, -0.240 }, { -0.096, -0.769, -0.048, -0.096, 0.481, -0.577, -0.529, 0.577, 0.096, 0.192, -0.337, -0.192, -0.481, 1.154, -0.240, 0.481 }, { 0.385, 0.048, 0.769, 0.096, -0.577, 0.529, 0.000, 0.000, -0.769, -0.337, 0.000, 0.000, 0.962, -0.240, 1.923, -0.481 }, { -0.048, -0.096, -0.096, -0.192, 0.433, -0.577, 0.000, 0.000, -0.144, 0.192, 0.000, 0.000, -0.240, 0.481, -0.481, 0.962 } }); Matrix expectedStiffnessStdFirst = 1E6 * Matrix.CreateFromArray(new double[, ] { { 1.154, 0.481, -0.769, 0.096, -0.577, -0.481, 0.192, -0.096, 0.962, 0.240, 0.769, 0.144, 0.577, 0.433, 0.385, -0.048 }, { 0.481, 1.154, -0.096, 0.192, -0.481, -0.577, 0.096, -0.769, 0.240, 0.481, 0.337, -0.192, 0.529, 0.577, 0.048, -0.096 }, { -0.769, -0.096, 1.154, -0.481, 0.192, 0.096, -0.577, 0.481, -0.769, 0.144, -0.962, 0.240, -0.385, -0.048, -0.577, 0.433 }, { 0.096, 0.192, -0.481, 1.154, -0.096, -0.769, 0.481, -0.577, 0.337, 0.192, 0.240, -0.481, 0.048, 0.096, 0.529, -0.577 }, { -0.577, -0.481, 0.192, -0.096, 1.154, 0.481, -0.769, 0.096, -0.577, -0.433, -0.385, 0.048, -0.962, -0.240, -0.769, -0.144 }, { -0.481, -0.577, 0.096, -0.769, 0.481, 1.154, -0.096, 0.192, -0.529, -0.577, -0.048, 0.096, -0.240, -0.481, -0.337, 0.192 }, { 0.192, 0.096, -0.577, 0.481, -0.769, -0.096, 1.154, -0.481, 0.385, 0.048, 0.577, -0.433, 0.769, -0.144, 0.962, -0.240 }, { -0.096, -0.769, 0.481, -0.577, 0.096, 0.192, -0.481, 1.154, -0.048, -0.096, -0.529, 0.577, -0.337, -0.192, -0.240, 0.481 }, { 0.962, 0.240, -0.769, 0.337, -0.577, -0.529, 0.385, -0.048, 1.923, 0.481, 0.000, 0.000, 0.000, 0.000, 0.769, -0.096 }, { 0.240, 0.481, 0.144, 0.192, -0.433, -0.577, 0.048, -0.096, 0.481, 0.962, 0.000, 0.000, 0.000, 0.000, 0.096, -0.192 }, { 0.769, 0.337, -0.962, 0.240, -0.385, -0.048, 0.577, -0.529, 0.000, 0.000, 1.923, -0.481, 0.769, 0.096, 0.000, 0.000 }, { 0.144, -0.192, 0.240, -0.481, 0.048, 0.096, -0.433, 0.577, 0.000, 0.000, -0.481, 0.962, -0.096, -0.192, 0.000, 0.000 }, { 0.577, 0.529, -0.385, 0.048, -0.962, -0.240, 0.769, -0.337, 0.000, 0.000, 0.769, -0.096, 1.923, 0.481, 0.000, 0.000 }, { 0.433, 0.577, -0.048, 0.096, -0.240, -0.481, -0.144, -0.192, 0.000, 0.000, 0.096, -0.192, 0.481, 0.962, 0.000, 0.000 }, { 0.385, 0.048, -0.577, 0.529, -0.769, -0.337, 0.962, -0.240, 0.769, 0.096, 0.000, 0.000, 0.000, 0.000, 1.923, -0.481 }, { -0.048, -0.096, 0.433, -0.577, -0.144, 0.192, -0.240, 0.481, -0.096, -0.192, 0.000, 0.000, 0.000, 0.000, -0.481, 0.962 } }); XContinuumElement2D element = CreateCrackBodyElement(); IMatrix stiffnessNodeMajor = element.JoinStiffnessesNodeMajor(); IMatrix stiffnessStdFirst = element.JoinStiffnessesStandardFirst(); Assert.True(expectedStiffnessNodeMajor.Equals( stiffnessNodeMajor.DoToAllEntries(round), equalityTolerance)); Assert.True(expectedStiffnessStdFirst.Equals( stiffnessStdFirst.DoToAllEntries(round), equalityTolerance)); }
private void FindSignedAreasOfElement(XContinuumElement2D element, out double positiveArea, out double negativeArea) { SortedSet <CartesianPoint> triangleVertices = FindTriangleVertices(element); IReadOnlyList <Triangle2D <CartesianPoint> > triangles = triangulator.CreateMesh(triangleVertices); ReportTriangulation(element, triangleVertices, triangles); positiveArea = 0.0; negativeArea = 0.0; foreach (var triangle in triangles) { CartesianPoint v0 = triangle.Vertices[0]; CartesianPoint v1 = triangle.Vertices[1]; CartesianPoint v2 = triangle.Vertices[2]; double area = 0.5 * Math.Abs(v0.X * (v1.Y - v2.Y) + v1.X * (v2.Y - v0.Y) + v2.X * (v0.Y - v1.Y)); // The sign of the area can be derived from any node with signed distance != 0 int sign = 0; foreach (var vertex in triangle.Vertices) { sign = Math.Sign(SignedDistanceOfPoint(vertex)); if (sign != 0) { break; } } // If no node with non-zero signed distance is found, then find the signed distance of its centroid if (sign == 0) { // Report this instance in DEBUG messages. It should not happen. Console.WriteLine("--- DEBUG: Triangulation resulted in a triangle where all vertices are on the crack. ---"); var centroid = new CartesianPoint((v0.X + v1.X + v2.X) / 3.0, (v0.Y + v1.Y + v2.Y) / 3.0); sign = Math.Sign(SignedDistanceOfPoint(centroid)); } if (sign > 0) { positiveArea += area; } else if (sign < 0) { negativeArea += area; } else { throw new Exception( "Even after finding the signed distance of its centroid, the sign of the area is unidentified"); } } }
/// <summary> /// Checks if the element is internal to this subdomain. If it is, it is added and true is returned. Otherwise false is /// returned. /// </summary> /// <param name="element"></param> /// <returns></returns> public bool AddElementIfInternal(XContinuumElement2D element) { //TODO: check if one node is internal, while another is external. foreach (var node in element.Nodes) { if (!(internalNodes.Contains(node) || boundaryNodes.Contains(node))) { return(false); } } elements.Add(element); return(true); }
public Tensor2D EvaluateAt(XContinuumElement2D element, NaturalPoint point, Vector standardDisplacements, Vector enrichedDisplacements) { EvalInterpolation2D evaluatedInterpolation = element.Interpolation.EvaluateAllAt(element.Nodes, point); Matrix2by2 displacementGradient = element.CalculateDisplacementFieldGradient( point, evaluatedInterpolation, standardDisplacements, enrichedDisplacements); double strainXX = displacementGradient[0, 0]; double strainYY = displacementGradient[1, 1]; double strainXY = 0.5 * (displacementGradient[0, 1] + displacementGradient[1, 0]); return(new Tensor2D(strainXX, strainYY, strainXY)); }
public List <int> GetStandardDofsOf(XContinuumElement2D element) { var globalDofs = new List <int>(2 * element.Nodes.Count); foreach (var nodeDofLocal in element.GetStandardDofs()) { bool isStandard = standardDofs.TryGetValue(nodeDofLocal.row, nodeDofLocal.col, out int globalDof); if (isStandard) { globalDofs.Add(globalDof); } } return(globalDofs); }
public CrackElementPosition FindRelativePositionOf(XContinuumElement2D element) { CartesianPoint crackTip = lsm.GetCrackTip(CrackTipPosition.Single); double minBodyLevelSet = double.MaxValue; double maxBodyLevelSet = double.MinValue; double minTipLevelSet = double.MaxValue; double maxTipLevelSet = double.MinValue; foreach (XNode node in element.Nodes) { double bodyLevelSet = lsm.LevelSetsBody[node]; double tipLevelSet = lsm.LevelSetsTip[node]; if (bodyLevelSet < minBodyLevelSet) { minBodyLevelSet = bodyLevelSet; } if (bodyLevelSet > maxBodyLevelSet) { maxBodyLevelSet = bodyLevelSet; } if (tipLevelSet < minTipLevelSet) { minTipLevelSet = tipLevelSet; } if (tipLevelSet > maxTipLevelSet) { maxTipLevelSet = tipLevelSet; } } //Warning: this might actually be worse than Stolarska's criterion. At least that one enriched the dubious //intersected elements with tip enrichments. This one just ignores them. if (minBodyLevelSet * maxBodyLevelSet <= 0.0) { if (minTipLevelSet * maxTipLevelSet <= 0) { var outline = ConvexPolygon2D.CreateUnsafe(element.Nodes); if (outline.IsPointInsidePolygon(crackTip)) { return(CrackElementPosition.ContainsTip); } } else if (maxTipLevelSet < 0) { return(CrackElementPosition.Intersected); } } return(CrackElementPosition.Irrelevant); }
public EvaluatedFunction2D[] EvaluateAllAt(NaturalPoint point, XContinuumElement2D element, EvalInterpolation2D interpolation) { PolarPoint2D polarPoint = TipSystem.TransformPointGlobalCartesianToLocalPolar( interpolation.TransformPointNaturalToGlobalCartesian()); TipJacobians tipJacobians = TipSystem.CalculateJacobiansAt(polarPoint); var enrichments = new EvaluatedFunction2D[enrichmentFunctions.Count]; for (int i = 0; i < enrichments.Length; ++i) { enrichments[i] = enrichmentFunctions[i].EvaluateAllAt(polarPoint, tipJacobians); } return(enrichments); }
private int DecideElementRegion(XContinuumElement2D element, HashSet <XNode>[] internalNodes, HashSet <XNode>[] boundaryNodes) { foreach (var node in element.Nodes) { for (int i = 0; i < numRegions; ++i) { // The first region that contains part of the element is chosen. //TODO: a more sophisticated criterion, such as looking at the relative areas is needed. if (internalNodes[i].Contains(node)) { return(i); } } } throw new IncorrectDecompositionException("This element's nodes do not belong to any of the regions provided"); }
//TODO: replace this with the Faster~() version public CrackElementPosition FindRelativePositionOf(XContinuumElement2D element) { CartesianPoint crackTip = lsm.GetCrackTip(CrackTipPosition.Single); double minBodyLevelSet = double.MaxValue; double maxBodyLevelSet = double.MinValue; double minTipLevelSet = double.MaxValue; double maxTipLevelSet = double.MinValue; foreach (XNode node in element.Nodes) { double bodyLevelSet = lsm.LevelSetsBody[node]; double tipLevelSet = lsm.LevelSetsTip[node]; if (bodyLevelSet < minBodyLevelSet) { minBodyLevelSet = bodyLevelSet; } if (bodyLevelSet > maxBodyLevelSet) { maxBodyLevelSet = bodyLevelSet; } if (tipLevelSet < minTipLevelSet) { minTipLevelSet = tipLevelSet; } if (tipLevelSet > maxTipLevelSet) { maxTipLevelSet = tipLevelSet; } } // Warning: This criterion might give false positives for tip elements (see Serafeim's thesis for details) if (minBodyLevelSet * maxBodyLevelSet <= 0.0) { var outline = ConvexPolygon2D.CreateUnsafe(element.Nodes); if (outline.IsPointInsidePolygon(crackTip)) { return(CrackElementPosition.ContainsTip); } else if (maxTipLevelSet < 0) { return(CrackElementPosition.Intersected); } } return(CrackElementPosition.Irrelevant); }
public SortedSet <CartesianPoint> FindTriangleVertices(XContinuumElement2D element) { var triangleVertices = new SortedSet <CartesianPoint>(element.Nodes, pointComparer); int nodesCount = element.Nodes.Count; CrackElementPosition relativePosition = meshInteraction.FindRelativePositionOf(element); if (relativePosition != CrackElementPosition.Irrelevant) { // Find the intersections between element edges and the crack. TODO: See Serafeim's Msc Thesis for a correct procedure. for (int i = 0; i < nodesCount; ++i) { XNode node1 = element.Nodes[i]; XNode node2 = element.Nodes[(i + 1) % nodesCount]; double levelSet1 = levelSetsBody[node1]; double levelSet2 = levelSetsBody[node2]; if (levelSet1 * levelSet2 < 0.0) { // The intersection point between these nodes can be found using the linear interpolation, see // Sukumar 2001 double k = -levelSet1 / (levelSet2 - levelSet1); double x = node1.X + k * (node2.X - node1.X); double y = node1.Y + k * (node2.Y - node1.Y); // TODO: For the tip element one intersection point is on the crack extension and does not // need to be added. It is not wrong though. triangleVertices.Add(new CartesianPoint(x, y)); } else if (levelSet1 == 0.0) { triangleVertices.Add(node1); // TODO: perhaps some tolerance is needed. } else if (levelSet2 == 0.0) { triangleVertices.Add(node2); } } if (relativePosition == CrackElementPosition.ContainsTip) { triangleVertices.Add(crackTip); } } return(triangleVertices); }
// Computes stresses directly at the nodes. The other approach is to compute them at Gauss points and then extrapolate private IReadOnlyList <Tensor2D> ElementNodalTensorsDirectly(XContinuumElement2D element, Vector freeDisplacements, Vector constrainedDisplacements, IOutputField field) { Vector standardDisplacements = dofOrderer.ExtractDisplacementVectorOfElementFromGlobal(element, freeDisplacements, constrainedDisplacements); Vector enrichedDisplacements = dofOrderer.ExtractEnrichedDisplacementsOfElementFromGlobal(element, freeDisplacements); IReadOnlyList <NaturalPoint> naturalNodes = element.Interpolation.NodalNaturalCoordinates; var nodalTensors = new Tensor2D[element.Nodes.Count]; for (int i = 0; i < element.Nodes.Count; ++i) { nodalTensors[i] = field.EvaluateAt(element, naturalNodes[i], standardDisplacements, enrichedDisplacements); } return(nodalTensors); }
//TODO: Either work with Tensor class or double[] private IReadOnlyList <Tensor2D> ElementNodalTensorsExtrapolation(XContinuumElement2D element, Vector freeDisplacements, Vector constrainedDisplacements, IOutputField field) { Vector standardDisplacements = dofOrderer.ExtractDisplacementVectorOfElementFromGlobal(element, freeDisplacements, constrainedDisplacements); Vector enrichedDisplacements = dofOrderer.ExtractEnrichedDisplacementsOfElementFromGlobal(element, freeDisplacements); IReadOnlyList <NaturalPoint> gaussPoints = element.GaussPointExtrapolation.Quadrature.IntegrationPoints; var gpTensors = new Tensor2D[gaussPoints.Count]; for (int i = 0; i < gaussPoints.Count; ++i) { gpTensors[i] = field.EvaluateAt(element, gaussPoints[i], standardDisplacements, enrichedDisplacements); } return(element.GaussPointExtrapolation.ExtrapolateTensorFromGaussPointsToNodes(gpTensors, element.Interpolation)); }
private static (XModel model, IMesh2D <XNode, XContinuumElement2D> mesh) CreateModel(string meshPath) { // Mesh generation var reader = new GmshReader <XNode>(meshPath); (IReadOnlyList <XNode> nodes, IReadOnlyList <CellConnectivity <XNode> > elementConnectivities) = reader.CreateMesh( (id, x, y, z) => new XNode(id, x, y, z)); // Nodes var model = new XModel(); foreach (XNode node in nodes) { model.Nodes.Add(node); } // Integration rules var integration = new IntegrationForCrackPropagation2D( new RectangularSubgridIntegration2D <XContinuumElement2D>(8, GaussLegendre2D.GetQuadratureWithOrder(2, 2)), new RectangularSubgridIntegration2D <XContinuumElement2D>(8, GaussLegendre2D.GetQuadratureWithOrder(2, 2))); var jIntegration = new RectangularSubgridIntegration2D <XContinuumElement2D>(8, GaussLegendre2D.GetQuadratureWithOrder(4, 4)); // Elements var material = HomogeneousElasticMaterial2D.CreateMaterialForPlaneStrain(2.1E7, 0.3); var factory = new XContinuumElement2DFactory(integration, jIntegration, material); var cells = new XContinuumElement2D[elementConnectivities.Count]; for (int e = 0; e < cells.Length; ++e) { XContinuumElement2D element = factory.CreateElement(e, CellType.Quad4, elementConnectivities[e].Vertices); cells[e] = element; model.Elements.Add(element); } // Mesh usable for crack-mesh interaction //var boundary = new FilletBoundary(); IDomain2DBoundary boundary = null; model.Boundary = boundary; var mesh = new BidirectionalMesh2D <XNode, XContinuumElement2D>(model.Nodes, cells, boundary); return(model, mesh); }