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)); }
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; } }
/// <summary> /// See <see cref="IGaussPointExtrapolation2D.ExtrapolateTensorFromGaussPointsToNodes( /// IReadOnlyList{Tensor2D}, IIsoparametricInterpolation2D)"/> /// </summary> public IReadOnlyList <Tensor2D> ExtrapolateTensorFromGaussPointsToNodes(IReadOnlyList <Tensor2D> tensorsAtGaussPoints, IIsoparametricInterpolation2D interpolation) { var nodalTensors = new Tensor2D[interpolation.NumFunctions]; for (int i = 0; i < nodalTensors.Length; ++i) { nodalTensors[i] = tensorsAtGaussPoints[0]; } return(nodalTensors); }
public AuxiliaryStatesTensors(Matrix2by2 displacementGradientMode1, Matrix2by2 displacementGradientMode2, Tensor2D strainTensorMode1, Tensor2D strainTensorMode2, Tensor2D stressTensorMode1, Tensor2D stressTensorMode2) { this.DisplacementGradientMode1 = displacementGradientMode1; this.DisplacementGradientMode2 = displacementGradientMode2; this.StrainTensorMode1 = strainTensorMode1; this.StrainTensorMode2 = strainTensorMode2; this.StressTensorMode1 = stressTensorMode1; this.StressTensorMode2 = stressTensorMode2; }
private IReadOnlyList <Tensor2D> ComputeSmoothedNodalTensors(Vector solution, bool extrapolateFromGPs, IOutputField field) { var tensorsFromAllElements = new Dictionary <XNode, List <Tensor2D> >(); foreach (var node in model.Nodes) { tensorsFromAllElements[node] = new List <Tensor2D>(); } Vector constrainedDisplacements = model.CalculateConstrainedDisplacements(dofOrderer); foreach (var element in model.Elements) { IReadOnlyList <Tensor2D> elementTensors; if (extrapolateFromGPs) { elementTensors = ElementNodalTensorsExtrapolation(element, solution, constrainedDisplacements, field); } else { elementTensors = ElementNodalTensorsDirectly(element, solution, constrainedDisplacements, field); } for (int nodeIdx = 0; nodeIdx < element.Nodes.Count; ++nodeIdx) { tensorsFromAllElements[element.Nodes[nodeIdx]].Add(elementTensors[nodeIdx]); } } // Average with equal weights for all elements. TODO: perhaps vary the weights depending on the element type/area var nodalTensors = new Tensor2D[model.Nodes.Count]; for (int i = 0; i < model.Nodes.Count; ++i) { XNode node = model.Nodes[i]; double tensorXX = 0.0, tensorYY = 0.0, tensorXY = 0.0; foreach (var tensor in tensorsFromAllElements[node]) { tensorXX += tensor.XX; tensorYY += tensor.YY; tensorXY += tensor.XY; } int contributingElementsCount = tensorsFromAllElements[node].Count; tensorXX /= contributingElementsCount; tensorYY /= contributingElementsCount; tensorXY /= contributingElementsCount; nodalTensors[i] = new Tensor2D(tensorXX, tensorYY, tensorXY); } return(nodalTensors); }
private static double ComputeJIntegrand(Vector2 weightGrad, Matrix2by2 displGrad1, Tensor2D stress1, Matrix2by2 displGrad2, Tensor2D strain2, Tensor2D stress2) { // Unrolled to greatly reduce mistakes. Alternatively Einstein notation products could be implementated // in Tensor2D (like the tensor-tensor multiplication is), but still some parts would have to be unrolled. // Perhaps vector (and scalar) gradients should also be accessed by component and derivative variable. double strainEnergy = stress1.MultiplyColon(strain2); double parenthesis0 = stress1.XX * displGrad2[0, 0] + stress1.XY * displGrad2[1, 0] + stress2.XX * displGrad1[0, 0] + stress2.XY * displGrad1[1, 0] - strainEnergy; double parenthesis1 = stress1.XY * displGrad2[0, 0] + stress1.YY * displGrad2[1, 0] + stress2.XY * displGrad1[0, 0] + stress2.YY * displGrad1[1, 0]; return(parenthesis0 * weightGrad[0] + parenthesis1 * weightGrad[1]); }
private static void ComputeStressTensors(CommonValues val, out Tensor2D stressTensorMode1, out Tensor2D stressTensorMode2) { double coeff = 1.0 / (Math.Sqrt(2.0 * Math.PI) * val.sqrtR); double sxxMode1 = coeff * val.cosThetaOver2 * (1.0 - val.sinThetaOver2 * val.sin3ThetaOver2); double syyMode1 = coeff * val.cosThetaOver2 * (1.0 + val.sinThetaOver2 * val.sin3ThetaOver2); double sxyMode1 = coeff * val.sinThetaOver2 * val.cosThetaOver2 * val.cos3ThetaOver2; double sxxMode2 = -coeff * val.sinThetaOver2 * (2.0 + val.cosThetaOver2 * val.cos3ThetaOver2); double syyMode2 = sxyMode1; double sxyMode2 = sxxMode1; stressTensorMode1 = new Tensor2D(sxxMode1, syyMode1, sxyMode1); stressTensorMode2 = new Tensor2D(sxxMode2, syyMode2, sxyMode2); }
//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)); }
// 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); }
public IReadOnlyList <Tensor2D> ComputeSmoothedNodalStresses(Vector solution) { var stressesFromAllElements = new Dictionary <XNode, List <Tensor2D> >(); foreach (var node in model.Nodes) { stressesFromAllElements[node] = new List <Tensor2D>(); } Vector constrainedDisplacements = model.CalculateConstrainedDisplacements(dofOrderer); foreach (var element in model.Elements) { IReadOnlyDictionary <XNode, Tensor2D> elementStresses = ComputeNodalStressesOfElement(element, solution, constrainedDisplacements); foreach (var nodeStressPair in elementStresses) { stressesFromAllElements[nodeStressPair.Key].Add(nodeStressPair.Value); } } // Average with equal weights for all elements. TODO: perhaps vary the weights depending on the element type/area var nodalStresses = new Tensor2D[model.Nodes.Count]; for (int i = 0; i < model.Nodes.Count; ++i) { XNode node = model.Nodes[i]; double stressXX = 0.0, stressYY = 0.0, stressXY = 0.0; foreach (var tensor in stressesFromAllElements[node]) { stressXX += tensor.XX; stressYY += tensor.YY; stressXY += tensor.XY; } int contributingElementsCount = stressesFromAllElements[node].Count; stressXX /= contributingElementsCount; stressYY /= contributingElementsCount; stressXY /= contributingElementsCount; nodalStresses[i] = new Tensor2D(stressXX, stressYY, stressXY); } return(nodalStresses); }
/// <summary> /// See <see cref="IGaussPointExtrapolation2D.ExtrapolateTensorFromGaussPointsToNodes( /// IReadOnlyList{Tensor2D}, IIsoparametricInterpolation2D)"/> /// </summary> public IReadOnlyList <Tensor2D> ExtrapolateTensorFromGaussPointsToNodes(IReadOnlyList <Tensor2D> tensorsAtGaussPoints, IIsoparametricInterpolation2D interpolation) { double[][] nodalExtrapolationFunctions = EvaluateExtrapolationFunctionsAtNodes(interpolation); IReadOnlyList <NaturalPoint> nodes = interpolation.NodalNaturalCoordinates; var nodalTensors = new Tensor2D[nodes.Count]; for (int i = 0; i < nodes.Count; ++i) { //nodalTensors[i] = ExtrapolateVectorFromGaussPoints(tensorsAtGaussPoints, nodes[i]); // for debugging var tensor = new double[3]; for (int gp = 0; gp < Quadrature.IntegrationPoints.Count; ++gp) { tensor[0] += nodalExtrapolationFunctions[i][gp] * tensorsAtGaussPoints[gp].XX; tensor[1] += nodalExtrapolationFunctions[i][gp] * tensorsAtGaussPoints[gp].YY; tensor[2] += nodalExtrapolationFunctions[i][gp] * tensorsAtGaussPoints[gp].XY; } nodalTensors[i] = new Tensor2D(tensor[0], tensor[1], tensor[2]); } return(nodalTensors); }
public void WriteTensor2DField(string fieldName, IReadOnlyDictionary <ICell <TNode>, IReadOnlyList <Tensor2D> > tensorsAtElementNodes) { var nodalTensors = new Tensor2D[mesh.VtkPoints.Count]; for (int e = 0; e < mesh.VtkCells.Count; ++e) { VtkCell cell = mesh.VtkCells[e]; IReadOnlyList <Tensor2D> valuesAtCellVertices = tensorsAtElementNodes[mesh.OriginalElements[e]]; for (int i = 0; i < cell.Vertices.Count; ++i) { nodalTensors[cell.Vertices[i].ID] = valuesAtCellVertices[i]; } } WriteFieldsHeader(nodalTensors.Length); writer.WriteLine(String.Format("TENSORS {0} double", fieldName)); for (int i = 0; i < mesh.VtkPoints.Count; ++i) { writer.WriteLine(String.Format("{0} {1} {2} 0.0 0.0 0.0 0.0 0.0 0.0", nodalTensors[i].XX, nodalTensors[i].YY, nodalTensors[i].XY)); } writer.WriteLine(); }
public void WriteOutputData(IDofOrderer dofOrderer, Vector freeDisplacements, Vector constrainedDisplacements, int step) { // TODO: guess initial capacities from previous steps or from the model var allPoints = new List <VtkPoint>(); var allCells = new List <VtkCell>(); var displacements = new List <double[]>(); var strains = new List <Tensor2D>(); var stresses = new List <Tensor2D>(); int pointCounter = 0; foreach (XContinuumElement2D element in model.Elements) { Vector standardDisplacements = dofOrderer.ExtractDisplacementVectorOfElementFromGlobal(element, freeDisplacements, constrainedDisplacements); Vector enrichedDisplacements = dofOrderer.ExtractEnrichedDisplacementsOfElementFromGlobal(element, freeDisplacements); bool mustTriangulate = MustBeTriangulated(element, out ISingleCrack intersectingCrack); if (!mustTriangulate) { // Mesh var cellPoints = new VtkPoint[element.Nodes.Count]; for (int p = 0; p < cellPoints.Length; ++p) { cellPoints[p] = new VtkPoint(pointCounter++, element.Nodes[p]); allPoints.Add(cellPoints[p]); } allCells.Add(new VtkCell(element.CellType, cellPoints)); // Displacements for (int p = 0; p < cellPoints.Length; ++p) { displacements.Add(new double[] { standardDisplacements[2 * p], standardDisplacements[2 * p + 1] }); } // Strains and stresses at Gauss points of element // WARNING: do not use the quadrature object, since GPs are sorted differently. IReadOnlyList <GaussPoint> gaussPoints = element.GaussPointExtrapolation.Quadrature.IntegrationPoints; var strainsAtGPs = new Tensor2D[gaussPoints.Count]; var stressesAtGPs = new Tensor2D[gaussPoints.Count]; for (int gp = 0; gp < gaussPoints.Count; ++gp) { EvalInterpolation2D evalInterpol = element.Interpolation.EvaluateAllAt(element.Nodes, gaussPoints[gp]); (Tensor2D strain, Tensor2D stress) = ComputeStrainStress(element, gaussPoints[gp], evalInterpol, standardDisplacements, enrichedDisplacements); strainsAtGPs[gp] = strain; stressesAtGPs[gp] = stress; } // Extrapolate strains and stresses to element nodes. This is exact, since the element is not enriched IReadOnlyList <Tensor2D> strainsAtNodes = element.GaussPointExtrapolation. ExtrapolateTensorFromGaussPointsToNodes(strainsAtGPs, element.Interpolation); IReadOnlyList <Tensor2D> stressesAtNodes = element.GaussPointExtrapolation. ExtrapolateTensorFromGaussPointsToNodes(stressesAtGPs, element.Interpolation); for (int p = 0; p < cellPoints.Length; ++p) { strains.Add(strainsAtNodes[p]); stresses.Add(stressesAtNodes[p]); } } else { // Triangulate and then operate on each triangle SortedSet <CartesianPoint> triangleVertices = intersectingCrack.FindTriangleVertices(element); IReadOnlyList <Triangle2D <CartesianPoint> > triangles = triangulator.CreateMesh(triangleVertices); foreach (Triangle2D <CartesianPoint> triangle in triangles) { // Mesh int numTriangleNodes = 3; var cellPoints = new VtkPoint[numTriangleNodes]; for (int p = 0; p < numTriangleNodes; ++p) { CartesianPoint point = triangle.Vertices[p]; cellPoints[p] = new VtkPoint(pointCounter++, point.X, point.Y, point.Z); allPoints.Add(cellPoints[p]); } allCells.Add(new VtkCell(CellType.Tri3, cellPoints)); // Displacements, strains and stresses are not defined on the crack, thus they must be evaluated at GPs // and extrapolated to each point of interest. However how should I choose the Gauss points? Here I take // the Gauss points of the subtriangles. IGaussPointExtrapolation2D extrapolation = ExtrapolationGaussTriangular3Points.UniqueInstance; IIsoparametricInterpolation2D interpolation = InterpolationTri3.UniqueInstance; // Find the Gauss points of the triangle in the natural system of the element IInverseInterpolation2D inverseMapping = element.Interpolation.CreateInverseMappingFor(element.Nodes); var triangleNodesNatural = new NaturalPoint[numTriangleNodes]; for (int p = 0; p < numTriangleNodes; ++p) { triangleNodesNatural[p] = inverseMapping.TransformPointCartesianToNatural(cellPoints[p]); } NaturalPoint[] triangleGPsNatural = FindTriangleGPsNatural(triangleNodesNatural, extrapolation.Quadrature.IntegrationPoints); // Find the field values at the Gauss points of the triangle (their coordinates are in the natural // system of the element) var displacementsAtGPs = new double[triangleGPsNatural.Length][]; var strainsAtGPs = new Tensor2D[triangleGPsNatural.Length]; var stressesAtGPs = new Tensor2D[triangleGPsNatural.Length]; for (int gp = 0; gp < triangleGPsNatural.Length; ++gp) { EvalInterpolation2D evalInterpol = element.Interpolation.EvaluateAllAt(element.Nodes, triangleGPsNatural[gp]); displacementsAtGPs[gp] = element.CalculateDisplacementField(triangleGPsNatural[gp], evalInterpol, standardDisplacements, enrichedDisplacements).CopyToArray(); (Tensor2D strain, Tensor2D stress) = ComputeStrainStress(element, triangleGPsNatural[gp], evalInterpol, standardDisplacements, enrichedDisplacements); strainsAtGPs[gp] = strain; stressesAtGPs[gp] = stress; } // Extrapolate the field values to the triangle nodes. We need their coordinates in the auxiliary // system of the triangle. We could use the inverse interpolation of the triangle to map the natural // (element local) coordinates of the nodes to the auxiliary system of the triangle. Fortunately they // can be accessed by the extrapolation object directly. IReadOnlyList <double[]> displacementsAtTriangleNodes = extrapolation.ExtrapolateVectorFromGaussPointsToNodes(displacementsAtGPs, interpolation); IReadOnlyList <Tensor2D> strainsAtTriangleNodes = extrapolation.ExtrapolateTensorFromGaussPointsToNodes(strainsAtGPs, interpolation); IReadOnlyList <Tensor2D> stressesAtTriangleNodes = extrapolation.ExtrapolateTensorFromGaussPointsToNodes(stressesAtGPs, interpolation); for (int p = 0; p < numTriangleNodes; ++p) { displacements.Add(displacementsAtTriangleNodes[p]); strains.Add(strainsAtTriangleNodes[p]); stresses.Add(stressesAtTriangleNodes[p]); } } } } using (var writer = new VtkFileWriter($"{pathNoExtension}_{step}.vtk")) { writer.WriteMesh(allPoints, allCells); writer.WriteVector2DField("displacement", displacements); writer.WriteTensor2DField("strain", strains); writer.WriteTensor2DField("stress", stresses); } }
public Tensor2D TransformTensorGlobalCartesianToLocalCartesian(Tensor2D tensor) { return(tensor.Rotate(RotationAngle)); }