public void UpdateGeometry(double localGrowthAngleStart, double growthLengthStart, double localGrowthAngleEnd, double growthLengthEnd) { // End tip double globalAngleEnd = MathUtilities.WrapAngle(localGrowthAngleEnd + endTipSystem.RotationAngle); double dxEnd = growthLengthEnd * Math.Cos(globalAngleEnd); double dyEnd = growthLengthEnd * Math.Sin(globalAngleEnd); var oldEndTip = Vertices.Last.Value;; var newEndTip = new CartesianPoint(oldEndTip.X + dxEnd, oldEndTip.Y + dyEnd); Vertices.AddLast(newEndTip); Segments.AddLast(new DirectedSegment2D(oldEndTip, newEndTip)); Angles.AddLast(localGrowthAngleEnd); // These are independent of the global coordinate system endTipSystem = new TipCoordinateSystem(newEndTip, globalAngleEnd); //StartTip double globalAngleStart = MathUtilities.WrapAngle(localGrowthAngleStart + startTipSystem.RotationAngle); double dxStart = growthLengthStart * Math.Cos(globalAngleStart); double dyStart = growthLengthStart * Math.Sin(globalAngleStart); var oldStartTip = Vertices.First.Value; var newStartTip = new CartesianPoint(oldStartTip.X + dxEnd, oldStartTip.Y + dyEnd); Vertices.AddFirst(newStartTip); Segments.AddFirst(new DirectedSegment2D(newStartTip, oldStartTip)); Angles.AddFirst(-localGrowthAngleEnd); // From new to old start segment, use the opposite angle endTipSystem = new TipCoordinateSystem(newStartTip, globalAngleStart); }
public AuxiliaryStatesTensors ComputeTensorsAt(CartesianPoint globalIntegrationPoint, TipCoordinateSystem tipCoordinateSystem) { // Common calculations PolarPoint2D polarCoordinates = tipCoordinateSystem.TransformPointGlobalCartesianToLocalPolar(globalIntegrationPoint); var commonValues = new CommonValues(polarCoordinates.R, polarCoordinates.Theta); // Displacement field derivatives Matrix2by2 displacementGradientMode1, displacementGradientMode2; TipJacobians polarJacobians = tipCoordinateSystem.CalculateJacobiansAt(polarCoordinates); ComputeDisplacementDerivatives(polarJacobians, commonValues, out displacementGradientMode1, out displacementGradientMode2); // Strains Tensor2D strainTensorMode1 = ComputeStrainTensor(displacementGradientMode1); Tensor2D strainTensorMode2 = ComputeStrainTensor(displacementGradientMode2); // Stresses Tensor2D stressTensorMode1, stressTensorMode2; ComputeStressTensors(commonValues, out stressTensorMode1, out stressTensorMode2); return(new AuxiliaryStatesTensors(displacementGradientMode1, displacementGradientMode2, strainTensorMode1, strainTensorMode2, stressTensorMode1, stressTensorMode2)); }
public void InitializeGeometry(CartesianPoint crackMouth, CartesianPoint crackTip) { this.crackMouth = crackMouth; crackPath.Add(crackMouth); var segment = new DirectedSegment2D(crackMouth, crackTip); double tangentX = crackTip.X - crackMouth.X; double tangentY = crackTip.Y - crackMouth.Y; double length = Math.Sqrt(tangentX * tangentX + tangentY * tangentY); double tangentSlope = Math.Atan2(tangentY, tangentX); this.crackTip = crackTip; crackPath.Add(crackTip); tipSystem = new TipCoordinateSystem(crackTip, tangentSlope); CrackTipEnrichments.TipSystem = tipSystem; tangentX /= length; tangentY /= length; foreach (XNode node in Mesh.Nodes) { levelSetsBody[node] = segment.SignedDistanceOf(node); levelSetsTip[node] = (node.X - crackTip.X) * tangentX + (node.Y - crackTip.Y) * tangentY; } if (LevelSetLogger != null) { LevelSetLogger.InitialLog(); //TODO: handle this with a NullLogger. } }
//TODO: This should work for any IOpenCurve2D. Same for all ICrackGeometryDescriptions. //TODO: The tangent stuff should be done by the initial curve. public void InitializeGeometry(PolyLine2D initialCrack) { foreach (var vertex in initialCrack.Vertices) { crackPath.Add(vertex); } crackMouth = initialCrack.Start; var lastSegment = initialCrack.Segments[initialCrack.Segments.Count - 1]; double tangentX = lastSegment.End.X - lastSegment.Start.X; double tangentY = lastSegment.End.Y - lastSegment.Start.Y; double length = Math.Sqrt(tangentX * tangentX + tangentY * tangentY); double tangentSlope = Math.Atan2(tangentY, tangentX); this.crackTip = initialCrack.End; tipSystem = new TipCoordinateSystem(crackTip, tangentSlope); CrackTipEnrichments.TipSystem = tipSystem; tangentX /= length; tangentY /= length; foreach (XNode node in Mesh.Nodes) { levelSetsBody[node] = initialCrack.SignedDistanceOf(node); levelSetsTip[node] = (node.X - crackTip.X) * tangentX + (node.Y - crackTip.Y) * tangentY; } if (LevelSetLogger != null) { LevelSetLogger.InitialLog(); //TODO: handle this with a NullLogger. } }
private IReadOnlyDictionary <XContinuumElement2D, double[]> FindJintegralElementsAndNodalWeights( CartesianPoint crackTip, TipCoordinateSystem tipSystem, IReadOnlyList <XContinuumElement2D> tipElements) { Circle2D outerContour = new Circle2D(crackTip, ComputeRadiusOfJintegralOuterContour(tipSystem, tipElements)); IReadOnlyList <XContinuumElement2D> intersectedElements = mesh.FindElementsIntersectedByCircle(outerContour, tipElements[0]); var elementsAndWeights = new Dictionary <XContinuumElement2D, double[]>(); foreach (var element in intersectedElements) { // The relative position of the circle and the nodes was already calculated when checking the // circle-element intersection, but that method should be decoupled from assigning the nodal // weights, even at the cost of some duplicate operations. What could be done more efficiently is // caching the nodes and weights already processed by previous elements, but even then the cost of // processing each node will be increased by the lookup. double[] nodalWeights = new double[element.Nodes.Count]; for (int nodeIdx = 0; nodeIdx < element.Nodes.Count; ++nodeIdx) { CirclePointPosition pos = outerContour.FindRelativePositionOfPoint((CartesianPoint)element.Nodes[nodeIdx]); if (pos == CirclePointPosition.Outside) { nodalWeights[nodeIdx] = 0.0; } else // Node lies inside or exactly on the circle { nodalWeights[nodeIdx] = 1.0; } } elementsAndWeights.Add(element, nodalWeights); } return(elementsAndWeights); }
public void UpdateGeometry(double localGrowthAngle, double growthLength) { double globalGrowthAngle = MathUtilities.WrapAngle(localGrowthAngle + tipSystem.RotationAngle); double dx = growthLength * Math.Cos(globalGrowthAngle); double dy = growthLength * Math.Sin(globalGrowthAngle); double unitDx = dx / growthLength; double unitDy = dy / growthLength; var oldTip = crackTip; var newTip = new CartesianPoint(oldTip.X + dx, oldTip.Y + dy); crackTip = newTip; tipSystem = new TipCoordinateSystem(newTip, globalGrowthAngle); var newSegment = new DirectedSegment2D(oldTip, newTip); foreach (XNode node in Mesh.Nodes) { // Rotate the ALL tip level sets towards the new tip and then advance them double rotatedTipLevelSet = (node.X - crackTip.X) * unitDx + (node.Y - crackTip.Y) * unitDy; levelSetsTip[node] = rotatedTipLevelSet - newSegment.Length; if (rotatedTipLevelSet > 0.0) // Only some body level sets are updated (See Stolarska 2001) { levelSetsBody[node] = newSegment.SignedDistanceOf(node); } } }
public void InitializeGeometry(CartesianPoint startTip, CartesianPoint endTip) { double dx = endTip.X - startTip.X; double dy = endTip.Y - startTip.Y; double tangentSlope = Math.Atan2(dy, dx); endTipSystem = new TipCoordinateSystem(endTip, tangentSlope); startTipSystem = new TipCoordinateSystem(startTip, tangentSlope + Math.PI); Vertices.AddLast(startTip); Vertices.AddLast(endTip); Segments.AddLast(new DirectedSegment2D(startTip, endTip)); }
public void UpdateGeometry(double localGrowthAngle, double growthLength) { double globalGrowthAngle = MathUtilities.WrapAngle(localGrowthAngle + tipSystem.RotationAngle); double dx = growthLength * Math.Cos(globalGrowthAngle); double dy = growthLength * Math.Sin(globalGrowthAngle); var oldTip = Vertices[Vertices.Count - 1]; var newTip = new CartesianPoint(oldTip.X + dx, oldTip.Y + dy); Vertices.Add(newTip); Segments.Add(new DirectedSegment2D(oldTip, newTip)); Angles.Add(localGrowthAngle); // These are independent of the global coordinate system tipSystem = new TipCoordinateSystem(newTip, globalGrowthAngle); CrackTipEnrichments.TipSystem = tipSystem; }
public void InitializeGeometry(CartesianPoint crackMouth, CartesianPoint crackTip) { CrackMouth = crackMouth; double dx = crackTip.X - crackMouth.X; double dy = crackTip.Y - crackMouth.Y; double tangentSlope = Math.Atan2(dy, dx); tipSystem = new TipCoordinateSystem(crackTip, tangentSlope); CrackTipEnrichments.TipSystem = tipSystem; Vertices.Add(crackMouth); Vertices.Add(crackTip); Segments.Add(new DirectedSegment2D(crackMouth, crackTip)); }
// TODO: This method should directly return the elements and take care of cases near the domain boundaries (see Ahmed) // TODO: The J-integral radius should not exceed the last crack segment's length public double ComputeRadiusOfJintegralOuterContour(TipCoordinateSystem tipSystem, IReadOnlyList <XContinuumElement2D> tipElements) { double maxTipElementArea = -1.0; foreach (var element in tipElements) { var outline = ConvexPolygon2D.CreateUnsafe(element.Nodes.Select(node => (CartesianPoint)node).ToArray()); double elementArea = outline.ComputeArea(); if (elementArea > maxTipElementArea) { maxTipElementArea = elementArea; } } return(magnificationOfJintegralRadius * Math.Sqrt(maxTipElementArea)); }
//TODO: make this private public void UpdateGeometry(double localGrowthAngle, double growthLength) { double globalGrowthAngle = MathUtilities.WrapAngle(localGrowthAngle + tipSystem.RotationAngle); double dx = growthLength * Math.Cos(globalGrowthAngle); double dy = growthLength * Math.Sin(globalGrowthAngle); var oldTip = crackTip; var newTip = new CartesianPoint(oldTip.X + dx, oldTip.Y + dy); crackTip = newTip; crackPath.Add(newTip); tipSystem = new TipCoordinateSystem(newTip, globalGrowthAngle); CrackTipEnrichments.TipSystem = tipSystem; //TODO: it is inconsistent that the modified body nodes are updated here, while the other in UpdateEnrichments(); crackBodyNodesModified = levelSetUpdater.Update(oldTip, localGrowthAngle, growthLength, dx, dy, Mesh.Nodes, crackBodyNodesAll, levelSetsBody, levelSetsTip); if (LevelSetLogger != null) { LevelSetLogger.Log(); //TODO: handle this with a NullLogger. } }
public void InitializeGeometry(CartesianPoint crackMouth, CartesianPoint crackTip) { CrackMouth = crackMouth; var segment = new DirectedSegment2D(crackMouth, crackTip); double tangentX = crackTip.X - crackMouth.X; double tangentY = crackTip.Y - crackMouth.Y; double length = Math.Sqrt(tangentX * tangentX + tangentY * tangentY); double tangentSlope = Math.Atan2(tangentY, tangentX); this.crackTip = crackTip; tipSystem = new TipCoordinateSystem(crackTip, tangentSlope); tangentX /= length; tangentY /= length; foreach (XNode node in Mesh.Nodes) { levelSetsBody[node] = segment.SignedDistanceOf(node); levelSetsTip[node] = (node.X - crackTip.X) * tangentX + (node.Y - crackTip.Y) * tangentY; } }
//public FixedPropagator CreateFromPath(IReadOnlyList<CartesianPoint> knownCrackPath, Propagator actualPropagator = null) //{ // int growthSteps = knownCrackPath.Count - 2; // The first 2 vertices are the initial crack. // double[] angles = new double[growthSteps]; // double[] lengths = new double[growthSteps]; // for (int i = 0; i < growthSteps; ++i) // { // CartesianPoint previousTip = knownCrackPath[i]; // CartesianPoint currentTip = knownCrackPath[i+1]; // CartesianPoint nextTip = knownCrackPath[i+2]; // var oldSegment = new DirectedSegment2D(previousTip, currentTip); // var newSegment = new DirectedSegment2D(currentTip, nextTip); // lengths[i] = newSegment.Length; // CartesianPoint local = oldSegment.TransformGlobalToLocalPoint(nextTip); // angles[i] = Math.Atan2(local.Y, local.X); // } // return new FixedPropagator(angles, lengths, actualPropagator); //} public (double growthAngle, double growthLength) Propagate(Dictionary <int, Vector> totalFreeDisplacements, CartesianPoint crackTip, TipCoordinateSystem tipSystem, IReadOnlyList <XContinuumElement2D> tipElements) { if (iteration >= Logger.GrowthLengths.Count) { throw new IndexOutOfRangeException( $"Only {Logger.GrowthLengths.Count} iterations have been recorder."); } double angle = Logger.GrowthAngles[iteration]; double length = Logger.GrowthLengths[iteration]; if (checkPropagation) { actualPropagator.Propagate(totalFreeDisplacements, crackTip, tipSystem, tipElements); Console.Write($"Growth angle: expected = {angle}"); Console.WriteLine($" - computed = {actualPropagator.Logger.GrowthAngles[iteration]}"); Console.Write($"Growth length: expected = {length}"); Console.WriteLine($" - computed = {actualPropagator.Logger.GrowthLengths[iteration]}"); } ++iteration; return(angle, length); }
private (double sifMode1, double sifMode2) ComputeSIFS(Dictionary <int, Vector> totalFreeDisplacements, CartesianPoint crackTip, TipCoordinateSystem tipSystem, IReadOnlyList <XContinuumElement2D> tipElements) { double interactionIntegralMode1 = 0.0, interactionIntegralMode2 = 0.0; IReadOnlyDictionary <XContinuumElement2D, double[]> elementWeights = FindJintegralElementsAndNodalWeights(crackTip, tipSystem, tipElements); foreach (var pair in elementWeights) { XContinuumElement2D element = pair.Key; double[] nodalWeights = pair.Value; //TODO: This needs refactoring ASAP. XSubdomain subdomain = element.Subdomain; double[] elementDisplacements = subdomain.CalculateElementDisplacements(element, totalFreeDisplacements[subdomain.ID]); (double[] standardElementDisplacements, double[] enrichedElementDisplacements) = element.SeparateStdEnrVector(elementDisplacements); //Vector standardElementDisplacements = dofOrderer.ExtractDisplacementVectorOfElementFromGlobal( // element, totalFreeDisplacements, totalConstrainedDisplacements); //Vector enrichedElementDisplacements = dofOrderer.ExtractEnrichedDisplacementsOfElementFromGlobal( // element, totalFreeDisplacements); double partialIntegralMode1, partialIntegralMode2; ComputeInteractionIntegrals(element, Vector.CreateFromArray(standardElementDisplacements), Vector.CreateFromArray(enrichedElementDisplacements), nodalWeights, tipSystem, out partialIntegralMode1, out partialIntegralMode2); interactionIntegralMode1 += partialIntegralMode1; interactionIntegralMode2 += partialIntegralMode2; } double sifMode1 = sifCalculationStrategy.CalculateSIF(interactionIntegralMode1); double sifMode2 = sifCalculationStrategy.CalculateSIF(interactionIntegralMode2); Logger.InteractionIntegralsMode1.Add(interactionIntegralMode1); Logger.InteractionIntegralsMode2.Add(interactionIntegralMode2); Logger.SIFsMode1.Add(sifMode1); Logger.SIFsMode2.Add(sifMode2); return(sifMode1, sifMode2); }
public (double growthAngle, double growthLength) Propagate(Dictionary <int, Vector> totalFreeDisplacements, CartesianPoint crackTip, TipCoordinateSystem tipSystem, IReadOnlyList <XContinuumElement2D> tipElements) { // TODO: Also check if the sifs do not violate the material toughness (double sifMode1, double sifMode2) = ComputeSIFS(totalFreeDisplacements, crackTip, tipSystem, tipElements); double growthAngle = growthDirectionLaw.ComputeGrowthAngle(sifMode1, sifMode2); double growthLength = growthLengthLaw.ComputeGrowthLength(sifMode1, sifMode2); Logger.GrowthAngles.Add(growthAngle); Logger.GrowthLengths.Add(growthLength); return(growthAngle, growthLength); }
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; } }