diff --git a/src/MGroup.Constitutive.Structural/Continuum/VonMisesMaterial3D.cs b/src/MGroup.Constitutive.Structural/Continuum/VonMisesMaterial3D.cs index 0ad5be4..6a4006d 100644 --- a/src/MGroup.Constitutive.Structural/Continuum/VonMisesMaterial3D.cs +++ b/src/MGroup.Constitutive.Structural/Continuum/VonMisesMaterial3D.cs @@ -20,7 +20,12 @@ namespace MGroup.Constitutive.Structural.Continuum /// Wikipedia -Von Mises yield criterion public class VonMisesMaterial3D : IIsotropicContinuumMaterial3D { - private const string PLASTIC_STRAIN = "Plastic strain"; + /// + /// Identity second order tensor written in vector form. + /// + private static readonly double[] IdentityVector = new[] { 1.0, 1.0, 1.0, 0, 0, 0 }; + private const string EQUIVALENT_PLASTIC_STRAIN = "Equivalent plastic strain"; + private const string YIELD_STRESS = "Yield stress"; private const string STRESS_X = "Stress X"; private const string STRESS_Y = "Stress Y"; private const string STRESS_Z = "Stress Z"; @@ -45,18 +50,31 @@ public class VonMisesMaterial3D : IIsotropicContinuumMaterial3D private const int TotalStresses = TotalStrains; /// - /// An array needed for the formulation of the consistent constitutive matrix. + /// Array for projecting a tensor on its deviatoric part. /// - private static readonly double[,] SupportiveMatrixForConsistentConstitutiveMatrix = new[,] + private static readonly double[,] DeviatoricProjection = new[,] { - { 0, -1, -1, 0, 0, 0 }, - { -1, 0, -1, 0, 0, 0 }, - { -1, -1, 0, 0, 0, 0 }, - { 0, 0, 0, 0.5, 0, 0 }, - { 0, 0, 0, 0, 0.5, 0 }, - { 0, 0, 0, 0, 0, 0.5 } + { 2.0/3.0, -1.0/3.0, -1.0/3.0, 0, 0, 0 }, + { -1.0/3.0, 2.0/3.0, -1.0/3.0, 0, 0, 0 }, + { -1.0/3.0, -1.0/3.0, 2.0/3.0, 0, 0, 0 }, + { 0, 0, 0, 1.0, 0, 0 }, + { 0, 0, 0, 0, 1.0, 0 }, + { 0, 0, 0, 0, 0, 1.0 } }; + /// + /// Array for projecting a tensor on its volumetric part. + /// + private static readonly double[,] VolumetricProjection = new[,] + { + { 1.0, 1.0, 1.0, 0, 0, 0 }, + { 1.0, 1.0, 1.0, 0, 0, 0 }, + { 1.0, 1.0, 1.0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0} + }; + /// /// The constitutive matrix of the material while still in the elastic region. /// @@ -84,13 +102,30 @@ public class VonMisesMaterial3D : IIsotropicContinuumMaterial3D private readonly double shearModulus; /// - /// The yields stress. + /// The bulk modulus. + /// + /// + /// Wikipedia - Bulk Modulus + /// + private readonly double bulkModulus; + + /// + /// The initial yield stress. /// /// /// Yield (engineering) /// The yield strength or yield point of a material is defined in engineering and materials science as the stress at which a material begins to deform plastically. /// - private readonly double yieldStress; + private readonly double initialYieldStress; + + /// + /// The current yield stress. + /// + /// + /// Yield (engineering) + /// The yield strength or yield point of a material is defined in engineering and materials science as the stress at which a material begins to deform plastically. + /// + private double yieldStress; /// /// The young modulus. @@ -119,25 +154,52 @@ public class VonMisesMaterial3D : IIsotropicContinuumMaterial3D private bool modified; /// - /// The current plastic strain. + /// The current strain vector. + /// + private double[] strains = new double[6]; + + /// + /// The previously converged elastic strain vector. + /// + private double[] strainsElasticPrev; + + /// + /// The current elastic strain vector. + /// + private double[] strainsElastic = new double[6]; + + /// + /// The previously converged plastic strain vector. /// - private double plasticStrain; + private double[] strainsPlasticPrev; /// - /// The new plastic strain. + /// The current plastic strain vector. /// - private double plasticStrainNew; + private double[] strainsPlastic = new double[6]; /// - /// The array of stresses. + /// The previously converged equivalent/accumulated plastic strain vector. + /// + private double strainsEquivalentPrev; + + /// + /// The current equivalent/accumulated plastic strain vector. + /// + private double strainsEquivalent; + + /// + /// The current increment stress vector. /// private double[] stresses = new double[6]; /// - /// The array of new stresses. + /// The current iteration stress vector. /// private double[] stressesNew = new double[6]; + private bool matrices_not_initialized = true; + /// /// Initializes a new instance of the class. /// @@ -165,38 +227,25 @@ public VonMisesMaterial3D(double youngModulus, double poissonRatio, double yield } this.poissonRatio = poissonRatio; - this.yieldStress = yieldStress; + this.initialYieldStress = yieldStress; this.hardeningRatio = hardeningRatio; this.shearModulus = this.YoungModulus / (2 * (1 + this.PoissonRatio)); + this.bulkModulus = this.YoungModulus / (3 * (1 - 2 * this.PoissonRatio)); double lamda = (youngModulus * poissonRatio) / ((1 + poissonRatio) * (1 - (2 * poissonRatio))); double mi = youngModulus / (2 * (1 + poissonRatio)); double value1 = (2 * mi) + lamda; - this.elasticConstitutiveMatrix = Matrix.CreateZero(6, 6); - this.elasticConstitutiveMatrix[0, 0] = value1; - this.elasticConstitutiveMatrix[0, 1] = lamda; - this.elasticConstitutiveMatrix[0, 2] = lamda; - this.elasticConstitutiveMatrix[1, 0] = lamda; - this.elasticConstitutiveMatrix[1, 1] = value1; - this.elasticConstitutiveMatrix[1, 2] = lamda; - this.elasticConstitutiveMatrix[2, 0] = lamda; - this.elasticConstitutiveMatrix[2, 1] = lamda; - this.elasticConstitutiveMatrix[2, 2] = value1; - this.elasticConstitutiveMatrix[3, 3] = mi; - this.elasticConstitutiveMatrix[4, 4] = mi; - this.elasticConstitutiveMatrix[5, 5] = mi; + elasticConstitutiveMatrix = GetConstitutiveMatrix(); + InitializeMatrices(); + } - currentState = new GenericConstitutiveLawState(this, new[] - { - (PLASTIC_STRAIN, 0d), - (STRESS_X, 0d), - (STRESS_Y, 0d), - (STRESS_Z, 0d), - (STRESS_XY, 0d), - (STRESS_XZ, 0d), - (STRESS_YZ, 0d), - }); + public void InitializeMatrices() + { + strainsElasticPrev = new double[6]; + strainsPlasticPrev = new double[6]; + strainsEquivalentPrev = 0; + matrices_not_initialized = false; } public double[] Coordinates { get; set; } @@ -211,12 +260,7 @@ public IMatrixView ConstitutiveMatrix { get { - if (this.constitutiveMatrix == null) - { - UpdateConstitutiveMatrixAndEvaluateResponse(new double[6]); - this.constitutiveMatrix.MatrixSymmetry = LinearAlgebra.Providers.MatrixSymmetry.Symmetric; - } - + if (this.constitutiveMatrix == null) UpdateConstitutiveMatrixAndEvaluateResponse(new double[6]); return constitutiveMatrix; } } @@ -241,7 +285,7 @@ public IMatrixView ConstitutiveMatrix public double[] IncrementalStrains => this.incrementalStrains; /// - /// Gets a value indicating whether this is modified. + /// Gets a value indicating whether this is modified. /// /// /// true if modified; otherwise, false. @@ -252,9 +296,9 @@ public IMatrixView ConstitutiveMatrix /// Gets the plastic strain. /// /// - /// The plastic strain. + /// The plastic strain vector. /// - public double PlasticStrain => this.plasticStrain; + public double[] StrainsPlastic => this.strainsPlastic; /// /// Gets the Poisson ratio. @@ -311,20 +355,16 @@ public double YoungModulus /// public object Clone() { - return new VonMisesMaterial3D(this.youngModulus, this.poissonRatio, this.yieldStress, this.hardeningRatio) + return new VonMisesMaterial3D(this.youngModulus, this.poissonRatio, this.initialYieldStress, this.hardeningRatio) { modified = this.IsCurrentStateDifferent(), - plasticStrain = this.plasticStrain, + strainsEquivalent = this.strainsEquivalent, + strainsEquivalentPrev = this.strainsEquivalentPrev, incrementalStrains = incrementalStrains.Copy(), stresses = stresses.Copy() }; } - /// - /// Resets the indicator of whether the material is modified. - /// - public void ResetModified() => this.modified = false; - /// /// Clears the stresses of the element's material. /// @@ -337,24 +377,33 @@ public void ClearStresses() public void ClearState() { modified = false; - constitutiveMatrix.Clear(); incrementalStrains.Clear(); stresses.Clear(); stressesNew.Clear(); - plasticStrain = 0; - plasticStrainNew = 0; + strains.Clear(); + incrementalStrains.Clear(); + strainsElasticPrev.Clear(); + strainsElastic.Clear(); + strainsPlasticPrev.Clear(); + strainsPlastic.Clear(); + strainsEquivalent = 0; + strainsEquivalentPrev = 0; + constitutiveMatrix = GetConstitutiveMatrix(); } /// - /// Saves the state of the element's material. + /// Saves the state of the element's material. /// public GenericConstitutiveLawState CreateState() { - this.plasticStrain = this.plasticStrainNew; stresses.CopyFrom(stressesNew); + strainsElasticPrev.CopyFrom(strainsElastic); + strainsPlasticPrev.CopyFrom(strainsPlastic); + strainsEquivalentPrev = strainsEquivalent; currentState = new GenericConstitutiveLawState(this, new[] { - (PLASTIC_STRAIN, plasticStrain), + (EQUIVALENT_PLASTIC_STRAIN, strainsEquivalent), + (YIELD_STRESS, yieldStress), (STRESS_X, stresses[0]), (STRESS_Y, stresses[1]), (STRESS_Z, stresses[2]), @@ -372,7 +421,8 @@ public GenericConstitutiveLawState CurrentState set { currentState = value; - plasticStrain = currentState.StateValues[PLASTIC_STRAIN]; + strainsEquivalent = currentState.StateValues[EQUIVALENT_PLASTIC_STRAIN]; + yieldStress = currentState.StateValues[YIELD_STRESS]; stresses[0] = currentState.StateValues[STRESS_X]; stresses[1] = currentState.StateValues[STRESS_Y]; stresses[2] = currentState.StateValues[STRESS_Z]; @@ -382,15 +432,35 @@ public GenericConstitutiveLawState CurrentState } } + /// + /// Resets the indicator of whether the material is modified. + /// + public void ResetModified() => this.modified = false; + + /// /// Updates the element's material with the provided incremental strains. /// /// The incremental strains to use for the next step. public double[] UpdateConstitutiveMatrixAndEvaluateResponse(double[] strainsIncrement) { + if (matrices_not_initialized) + { this.InitializeMatrices(); } incrementalStrains.CopyFrom(strainsIncrement); this.CalculateNextStressStrainPoint(); + currentState = new GenericConstitutiveLawState(this, new[] +{ + (EQUIVALENT_PLASTIC_STRAIN, strainsEquivalent), + (YIELD_STRESS, yieldStress), + (STRESS_X, stresses[0]), + (STRESS_Y, stresses[1]), + (STRESS_Z, stresses[2]), + (STRESS_XY, stresses[3]), + (STRESS_XZ, stresses[4]), + (STRESS_YZ, stresses[5]), + }); + return stressesNew; } @@ -399,32 +469,45 @@ public double[] UpdateConstitutiveMatrixAndEvaluateResponse(double[] strainsIncr /// /// This is a constant already calculated in the calling method. /// - /// We need an additional constant here equal to: 2*G*G*deltaPlasticStrain*sqrt(3/J2elastic) - /// Since value1 is already calculated, the additional constant can be calculated through it: - /// value3 = 2 * G * value1; + /// Refer to chapter 7.6.6 page 262 in Souza Neto. /// - private void BuildConsistentTangentialConstitutiveMatrix(double value1) + private Matrix BuildConsistentTangentialConstitutiveMatrix(double vonMisesStress, double[] unityvector) { - this.constitutiveMatrix = Matrix.CreateZero(TotalStresses, TotalStrains); - this.constitutiveMatrix.MatrixSymmetry = LinearAlgebra.Providers.MatrixSymmetry.Symmetric; - double invariantJ2New = this.GetDeviatorSecondStressInvariant(stressesNew); - - double value2 = (3 * this.shearModulus * this.shearModulus) / - ((this.hardeningRatio + (3 * this.shearModulus)) * invariantJ2New); - - double value3 = 2 * this.shearModulus * value1; - double value4 = 0.5 / invariantJ2New; + Matrix consistenttangent = Matrix.CreateZero(TotalStresses, TotalStrains); + double dgamma = this.strainsEquivalent - this.strainsEquivalentPrev; + double v1 = -dgamma * 6 * Math.Pow(this.shearModulus, 2) / vonMisesStress; + double Hk = 0; + double Hi = this.hardeningRatio; + double v2 = (dgamma / vonMisesStress - 1 / (3 * this.shearModulus + Hk + Hi)) * 6 * Math.Pow(this.shearModulus, 2); + for (int i = 0; i < 6; i++) + { + for (int j = 0; j < 6; j++) + { + consistenttangent[i, j] = this.elasticConstitutiveMatrix[i, j] + v1 * DeviatoricProjection[i, j] + v2 * unityvector[i] * unityvector[j]; + } + } + return consistenttangent; + } - var stressDeviator = this.GetStressDeviator(stressesNew); - for (int k1 = 0; k1 < TotalStresses; k1++) + /// + /// Builds the consistent tangential constitutive matrix. + /// + /// + /// Refer to chapter 7.6.6 page 262 in Souza Neto. + /// + private void BuildConsistentTangentialConstitutiveMatrix(double[] strainDeviatoricTrial, double normStrainDeviatoricTrial, double deltastrainsEquivalent, double vonMisesStress) + { + var N = new double[incrementalStrains.Length]; + for (int i = 0; i < incrementalStrains.Length; i++) { - for (int k2 = 0; k2 < TotalStresses; k2++) + N[i] = strainDeviatoricTrial[i] / normStrainDeviatoricTrial; + } + for (int i = 0; i < incrementalStrains.Length; i++) + { + for (int j = 0; j < incrementalStrains.Length; j++) { - this.constitutiveMatrix[k2, k1] = this.elasticConstitutiveMatrix[k2, k1] - - (value2 * stressDeviator[k2] * stressDeviator[k1]) - - (value3 * - (SupportiveMatrixForConsistentConstitutiveMatrix[k2, k1] - - (value4 * stressDeviator[k2] * stressDeviator[k1]))); + constitutiveMatrix[i, j] = 2 * shearModulus * (1 - (3 * shearModulus * deltastrainsEquivalent) / vonMisesStress) * DeviatoricProjection[i, j] + + 6 * shearModulus * shearModulus * (deltastrainsEquivalent / vonMisesStress - 1 / (3 * shearModulus + hardeningRatio)) * (N[i] * N[j]) + bulkModulus * VolumetricProjection[i, j]; } } } @@ -457,49 +540,87 @@ private void BuildTangentialConstitutiveMatrix() /// When the new plastic strain is less than the previous one. private void CalculateNextStressStrainPoint() { - var stressesElastic = new double[6]; - for (int i = 0; i < 6; i++) + //calculate trial variables + var strainTrial = new double[incrementalStrains.Length]; + var stressTrial = new double[incrementalStrains.Length]; + var strainDeviatoricTrial = new double[incrementalStrains.Length]; + var stressVolumetricTrial = new double[incrementalStrains.Length]; + var stressDeviatoricTrial = new double[incrementalStrains.Length]; + var normStrainDeviatoricTrial = new double(); + for (int i = 0; i < incrementalStrains.Length; i++) + strainTrial[i] = strainsElasticPrev[i] + incrementalStrains[i]; + for (int i = 0; i < incrementalStrains.Length; i++) { - stressesElastic[i] = this.stresses[i]; - for (int j = 0; j < 6; j++) - stressesElastic[i] += this.elasticConstitutiveMatrix[i, j] * this.incrementalStrains[j]; + for (int j = 0; j < incrementalStrains.Length; j++) + { + strainDeviatoricTrial[i] = strainDeviatoricTrial[i] + DeviatoricProjection[i, j] * strainTrial[j]; + if (i >= 0 && i <= 2) + stressTrial[i] += (2 * shearModulus * DeviatoricProjection[i, j] * strainTrial[j] + bulkModulus * VolumetricProjection[i, j] * strainTrial[j]); + else + stressTrial[i] += 0.5 * (2 * shearModulus * DeviatoricProjection[i, j] * strainTrial[j] + bulkModulus * VolumetricProjection[i, j] * strainTrial[j]); + } } - - double invariantJ2Elastic = this.GetDeviatorSecondStressInvariant(stressesElastic); - double vonMisesStress = Math.Sqrt(3 * invariantJ2Elastic); + for (int i = 0; i < incrementalStrains.Length; i++) + { + for (int j = 0; j < incrementalStrains.Length; j++) + { + stressVolumetricTrial[i] += (double)1 / 3 * VolumetricProjection[i, j] * stressTrial[j]; + stressDeviatoricTrial[i] += DeviatoricProjection[i, j] * stressTrial[j]; + } + normStrainDeviatoricTrial += strainDeviatoricTrial[i] * strainDeviatoricTrial[i]; + } + normStrainDeviatoricTrial = Math.Sqrt(normStrainDeviatoricTrial); + var J2 = GetDeviatorSecondStressInvariant(stressTrial); + double vonMisesStress = Math.Sqrt(3 * J2); double vonMisesStressMinusYieldStress = vonMisesStress - - (this.yieldStress + (this.hardeningRatio * this.plasticStrain)); + (this.initialYieldStress + (this.hardeningRatio * this.strainsEquivalentPrev)); + yieldStress = this.initialYieldStress + (this.hardeningRatio * this.strainsEquivalentPrev); bool materialIsInElasticRegion = vonMisesStressMinusYieldStress <= 0; + var stressVolumetric = new double[incrementalStrains.Length]; + var stressDeviatoric = new double[incrementalStrains.Length]; if (materialIsInElasticRegion) { - this.stressesNew = stressesElastic; - this.constitutiveMatrix = this.elasticConstitutiveMatrix; - this.plasticStrainNew = this.plasticStrain; + stressesNew.CopyFrom(stressTrial); + stressVolumetric.CopyFrom(stressVolumetricTrial); + stressDeviatoric.CopyFrom(stressDeviatoricTrial); + strainsEquivalent = strainsEquivalentPrev; + constitutiveMatrix = elasticConstitutiveMatrix.CopyToFullMatrix(); } else { - double deltaPlasticStrain = vonMisesStressMinusYieldStress / + double deltastrainsEquivalent = vonMisesStressMinusYieldStress / ((3 * this.shearModulus) + this.hardeningRatio); - this.plasticStrainNew = this.plasticStrain + deltaPlasticStrain; + this.strainsEquivalent = this.strainsEquivalentPrev + deltastrainsEquivalent; - // 2.0 and 1/2 cancel out - double value1 = this.shearModulus * deltaPlasticStrain * Math.Sqrt(3 / invariantJ2Elastic); - var stressDeviatorElastic = this.GetStressDeviator(stressesElastic); - for (int i = 0; i < 6; i++) - this.stressesNew[i] = stressesElastic[i] - (value1 * stressDeviatorElastic[i]); - - //this.BuildConsistentTangentialConstitutiveMatrix(value1); - this.BuildTangentialConstitutiveMatrix(); + for (int i = 0; i < incrementalStrains.Length; i++) + { + stressVolumetric[i] = stressVolumetricTrial[i]; + stressDeviatoric[i] = (1 - (3 * shearModulus * deltastrainsEquivalent) / vonMisesStress) * stressDeviatoricTrial[i]; + stressesNew[i] = stressVolumetric[i] + stressDeviatoric[i]; + } + BuildConsistentTangentialConstitutiveMatrix(strainDeviatoricTrial, normStrainDeviatoricTrial, deltastrainsEquivalent, vonMisesStress); + //this.BuildTangentialConstitutiveMatrix(); } - if (Math.Abs(this.plasticStrainNew) < Math.Abs(this.plasticStrain)) + if (Math.Abs(this.strainsEquivalent) < Math.Abs(this.strainsEquivalentPrev)) { throw new InvalidOperationException("Plastic strain cannot decrease."); } - this.modified = this.plasticStrainNew != this.plasticStrain; + this.modified = this.strainsEquivalent != this.strainsEquivalentPrev; + + for (int i = 0; i < incrementalStrains.Length; i++) + { + if (i >= 0 && i <= 2) + strainsElastic[i] = 1 / (2 * shearModulus) * stressDeviatoric[i] + 1 / (3 * bulkModulus) * stressVolumetric[i]; + else + strainsElastic[i] = 2 * (1 / (2 * shearModulus) * stressDeviatoric[i] + 1 / (3 * bulkModulus) * stressVolumetric[i]); + if (vonMisesStress > 0) + strainsPlastic[i] = strainsPlasticPrev[i] + strainTrial[i] - strainsElastic[i]; + strains[i] = strainsElastic[i] + strainsPlastic[i]; + } } /// @@ -518,8 +639,8 @@ private void CalculateNextStressStrainPoint() /// Calculates and returns the second stress invariant (I2). /// /// The second stress invariant (I2). - public double GetSecondStressInvariant(double[] stresses) - => (stresses[0] * stresses[1]) + (stresses[1] * stresses[2]) + (stresses[0] * stresses[2]) + public double GetSecondStressInvariant(double[] stresses) + => (stresses[0] * stresses[1]) + (stresses[1] * stresses[2]) + (stresses[0] * stresses[2]) - Math.Pow(stresses[5], 2) - Math.Pow(stresses[3], 2) - Math.Pow(stresses[4], 2); /// @@ -546,9 +667,9 @@ public double[] GetStressDeviator(double[] stresses) /// Calculates and returns the third stress invariant (I3). /// /// The third stress invariant (I3). - public double GetThirdStressInvariant(double[] stresses) - => (stresses[0] * stresses[1] * stresses[2]) + (2 * stresses[5] * stresses[3] * stresses[4]) - - (Math.Pow(stresses[5], 2) * stresses[2]) -(Math.Pow(stresses[3], 2) * stresses[0]) + public double GetThirdStressInvariant(double[] stresses) + => (stresses[0] * stresses[1] * stresses[2]) + (2 * stresses[5] * stresses[3] * stresses[4]) + - (Math.Pow(stresses[5], 2) * stresses[2]) - (Math.Pow(stresses[3], 2) * stresses[0]) - (Math.Pow(stresses[4], 2) * stresses[1]); /// @@ -584,5 +705,28 @@ public double GetDeviatorThirdStressInvariant(double[] stresses) return j3; } + private Matrix GetConstitutiveMatrix() + { + double fE1 = YoungModulus / (double)(1 + PoissonRatio); + double fE2 = fE1 * PoissonRatio / (double)(1 - 2 * PoissonRatio); + double fE3 = fE1 + fE2; + double fE4 = fE1 * 0.5; + var afE = Matrix.CreateZero(6, 6); + afE[0, 0] = fE3; + afE[0, 1] = fE2; + afE[0, 2] = fE2; + afE[1, 0] = fE2; + afE[1, 1] = fE3; + afE[1, 2] = fE2; + afE[2, 0] = fE2; + afE[2, 1] = fE2; + afE[2, 2] = fE3; + afE[3, 3] = fE4; + afE[4, 4] = fE4; + afE[5, 5] = fE4; + + return afE; + } + } } diff --git a/src/MGroup.Constitutive.Structural/Continuum/VonMisesMaterial3DFunctionalHardening.cs b/src/MGroup.Constitutive.Structural/Continuum/VonMisesMaterial3DFunctionalHardening.cs new file mode 100644 index 0000000..15e7f9c --- /dev/null +++ b/src/MGroup.Constitutive.Structural/Continuum/VonMisesMaterial3DFunctionalHardening.cs @@ -0,0 +1,733 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// To be decided +// +// +// Class for 3D Von Mises materials. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using MGroup.LinearAlgebra.Matrices; +using MGroup.MSolve.Constitutive; +using MGroup.MSolve.DataStructures; + +namespace MGroup.Constitutive.Structural.Continuum +{ + /// + /// Class for 3D Von Mises materials. + /// + /// Wikipedia -Von Mises yield criterion + public class VonMisesMaterial3DFunctionalHardening : IIsotropicContinuumMaterial3D + { + /// + /// Identity second order tensor written in vector form. + /// + private static readonly double[] IdentityVector = new[] { 1.0, 1.0, 1.0, 0, 0, 0 }; + private const string EQUIVALENT_PLASTIC_STRAIN = "Equivalent plastic strain"; + private const string STRESS_X = "Stress X"; + private const string STRESS_Y = "Stress Y"; + private const string STRESS_Z = "Stress Z"; + private const string STRESS_XY = "Stress XY"; + private const string STRESS_XZ = "Stress XZ"; + private const string STRESS_YZ = "Stress YZ"; + private GenericConstitutiveLawState currentState; + + /// + /// The Poisson ratio value of an incompressible solid. + /// + private const double PoissonRatioForIncompressibleSolid = 0.5; + + /// + /// The total number of strains. + /// + private const int TotalStrains = 6; + + /// + /// The total number of stresses. + /// + private const int TotalStresses = TotalStrains; + + /// + /// Array for projecting a tensor on its deviatoric part. + /// + private static readonly double[,] DeviatoricProjection = new[,] + { + { 2.0/3.0, -1.0/3.0, -1.0/3.0, 0, 0, 0 }, + { -1.0/3.0, 2.0/3.0, -1.0/3.0, 0, 0, 0 }, + { -1.0/3.0, -1.0/3.0, 2.0/3.0, 0, 0, 0 }, + { 0, 0, 0, 1.0, 0, 0 }, + { 0, 0, 0, 0, 1.0, 0 }, + { 0, 0, 0, 0, 0, 1.0 } + }; + + /// + /// Array for projecting a tensor on its volumetric part. + /// + private static readonly double[,] VolumetricProjection = new[,] + { + { 1.0, 1.0, 1.0, 0, 0, 0 }, + { 1.0, 1.0, 1.0, 0, 0, 0 }, + { 1.0, 1.0, 1.0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0} + }; + + /// + /// The constitutive matrix of the material while still in the elastic region. + /// + private readonly Matrix elasticConstitutiveMatrix; + + /// + /// Hardening modulus for linear hardening. + /// + private readonly Func hardeningFunc; + + private double hardeningGrad; + + /// + /// Hardening modulus for linear hardening. + /// + private double hardeningModulus; + + /// + /// The Poisson ratio. + /// + /// + /// Wikipedia - Poisson's Ratio + /// + private double poissonRatio; + + /// + /// The shear modulus. + /// + /// + /// Wikipedia - Shear Modulus + /// + private readonly double shearModulus; + + /// + /// The bulk modulus. + /// + /// + /// Wikipedia - Bulk Modulus + /// + private readonly double bulkModulus; + + /// + /// The yields stress. + /// + /// + /// Yield (engineering) + /// The yield strength or yield point of a material is defined in engineering and materials science as the stress at which a material begins to deform plastically. + /// + private readonly double yieldStress; + + /// + /// The young modulus. + /// + /// + /// Wikipedia - Young's Modulus + /// + private double youngModulus; + + /// + /// The constitutive matrix of the material. + /// + private Matrix constitutiveMatrix; + + /// + /// The array of incremental strains. + /// + /// + /// Deformation (engineering) + /// + private double[] incrementalStrains = new double[6]; + + /// + /// Indicates whether this is modified. + /// + private bool modified; + + /// + /// The current strain vector. + /// + private double[] strains = new double[6]; + + /// + /// The previously converged elastic strain vector. + /// + private double[] strainsElasticPrev; + + /// + /// The current elastic strain vector. + /// + private double[] strainsElastic = new double[6]; + + /// + /// The previously converged plastic strain vector. + /// + private double[] strainsPlasticPrev; + + /// + /// The current plastic strain vector. + /// + private double[] strainsPlastic = new double[6]; + + /// + /// The previously converged equivalent/accumulated plastic strain vector. + /// + private double strainsEquivalentPrev; + + /// + /// The current equivalent/accumulated plastic strain vector. + /// + private double strainsEquivalent; + + /// + /// The current increment stress vector. + /// + private double[] stresses = new double[6]; + + /// + /// The current iteration stress vector. + /// + private double[] stressesNew = new double[6]; + + private bool matrices_not_initialized = true; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The young modulus. + /// + /// + /// The Poisson ratio. + /// + /// + /// The yield stress. + /// + /// + /// The hardening function. + /// + /// When Poisson ratio is equal to 0.5. + public VonMisesMaterial3DFunctionalHardening(double youngModulus, double poissonRatio, double yieldStress, Func hardeningFunc) + { + this.youngModulus = youngModulus; + + if (poissonRatio == PoissonRatioForIncompressibleSolid) + { + throw new ArgumentException( + "Poisson ratio cannot be" + PoissonRatioForIncompressibleSolid + "(incompressible solid)"); + } + + this.poissonRatio = poissonRatio; + this.yieldStress = yieldStress; + this.hardeningFunc = hardeningFunc; + + this.shearModulus = this.YoungModulus / (2 * (1 + this.PoissonRatio)); + this.bulkModulus = this.YoungModulus / (3 * (1 - 2 * this.PoissonRatio)); + double lamda = (youngModulus * poissonRatio) / ((1 + poissonRatio) * (1 - (2 * poissonRatio))); + double mi = youngModulus / (2 * (1 + poissonRatio)); + double value1 = (2 * mi) + lamda; + + elasticConstitutiveMatrix = GetConstitutiveMatrix(); + InitializeMatrices(); + } + + public void InitializeMatrices() + { + strainsElasticPrev = new double[6]; + strainsPlasticPrev = new double[6]; + strainsEquivalentPrev = 0; + matrices_not_initialized = false; + } + + public double[] Coordinates { get; set; } + + /// + /// Gets the constitutive matrix. + /// + /// + /// The constitutive matrix. + /// + public IMatrixView ConstitutiveMatrix + { + get + { + if (this.constitutiveMatrix == null) UpdateConstitutiveMatrixAndEvaluateResponse(new double[6]); + return constitutiveMatrix; + } + } + + /// + /// Gets the ID of the material. + /// + /// + /// The id. + /// + public int ID => 1; + + /// + /// Gets the incremental strains of the finite element's material. + /// + /// + /// The incremental strains. + /// + /// + /// Deformation (engineering) + /// + public double[] IncrementalStrains => this.incrementalStrains; + + /// + /// Gets a value indicating whether this is modified. + /// + /// + /// true if modified; otherwise, false. + /// + public bool IsCurrentStateDifferent() => modified; + + /// + /// Gets the plastic strain. + /// + /// + /// The plastic strain vector. + /// + public double[] StrainsPlastic => this.strainsPlastic; + + /// + /// Gets the Poisson ratio. + /// + /// + /// The Poisson ratio. + /// + /// + /// Wikipedia - Poisson's Ratio + /// + public double PoissonRatio + { + get + { + return this.poissonRatio; + } + set + { + this.poissonRatio = value; + } + } + + /// + /// Gets the stresses of the finite element's material. + /// + /// + /// The stresses. + /// + /// + /// Stress (mechanics) + /// + public double[] Stresses => this.stressesNew; + + /// + /// Gets the Young's Modulus. + /// + /// + /// The young modulus. + /// + /// + /// Wikipedia - Young's Modulus + /// + public double YoungModulus + { + get => this.youngModulus; + set => this.youngModulus = value; + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + /// + /// A new object that is a copy of this instance. + /// + public object Clone() + { + return new VonMisesMaterial3DFunctionalHardening(this.youngModulus, this.poissonRatio, this.yieldStress, this.hardeningFunc) + { + modified = this.IsCurrentStateDifferent(), + strainsEquivalent = this.strainsEquivalent, + strainsEquivalentPrev = this.strainsEquivalentPrev, + incrementalStrains = incrementalStrains.Copy(), + stresses = stresses.Copy() + }; + } + + /// + /// Clears the stresses of the element's material. + /// + public void ClearStresses() + { + stresses.Clear(); + stressesNew.Clear(); + } + + public void ClearState() + { + modified = false; + incrementalStrains.Clear(); + stresses.Clear(); + stressesNew.Clear(); + strains.Clear(); + incrementalStrains.Clear(); + strainsElasticPrev.Clear(); + strainsElastic.Clear(); + strainsPlasticPrev.Clear(); + strainsPlastic.Clear(); + strainsEquivalent = 0; + strainsEquivalentPrev = 0; + constitutiveMatrix = GetConstitutiveMatrix(); + } + + /// + /// Saves the state of the element's material. + /// + public GenericConstitutiveLawState CreateState() + { + stresses.CopyFrom(stressesNew); + strainsElasticPrev.CopyFrom(strainsElastic); + strainsPlasticPrev.CopyFrom(strainsPlastic); + strainsEquivalentPrev = strainsEquivalent; + currentState = new GenericConstitutiveLawState(this, new[] + { + (EQUIVALENT_PLASTIC_STRAIN, strainsEquivalent), + (STRESS_X, stresses[0]), + (STRESS_Y, stresses[1]), + (STRESS_Z, stresses[2]), + (STRESS_XY, stresses[3]), + (STRESS_XZ, stresses[4]), + (STRESS_YZ, stresses[5]), + }); + + return currentState; + } + IHaveState ICreateState.CreateState() => CreateState(); + public GenericConstitutiveLawState CurrentState + { + get => currentState; + set + { + currentState = value; + strainsEquivalent = currentState.StateValues[EQUIVALENT_PLASTIC_STRAIN]; + stresses[0] = currentState.StateValues[STRESS_X]; + stresses[1] = currentState.StateValues[STRESS_Y]; + stresses[2] = currentState.StateValues[STRESS_Z]; + stresses[3] = currentState.StateValues[STRESS_XY]; + stresses[4] = currentState.StateValues[STRESS_XZ]; + stresses[5] = currentState.StateValues[STRESS_YZ]; + } + } + + /// + /// Resets the indicator of whether the material is modified. + /// + public void ResetModified() => this.modified = false; + + + /// + /// Updates the element's material with the provided incremental strains. + /// + /// The incremental strains to use for the next step. + public double[] UpdateConstitutiveMatrixAndEvaluateResponse(double[] strainsIncrement) + { + if (matrices_not_initialized) + { this.InitializeMatrices(); } + incrementalStrains.CopyFrom(strainsIncrement); + this.CalculateNextStressStrainPoint(); + + currentState = new GenericConstitutiveLawState(this, new[] +{ + (EQUIVALENT_PLASTIC_STRAIN, strainsEquivalent), + (STRESS_X, stresses[0]), + (STRESS_Y, stresses[1]), + (STRESS_Z, stresses[2]), + (STRESS_XY, stresses[3]), + (STRESS_XZ, stresses[4]), + (STRESS_YZ, stresses[5]), + }); + + return stressesNew; + } + + ///// + ///// Builds the consistent tangential constitutive matrix. + ///// + ///// This is a constant already calculated in the calling method. + ///// + ///// Refer to chapter 7.6.6 page 262 in Souza Neto. + ///// + //private Matrix BuildConsistentTangentialConstitutiveMatrix(double vonMisesStress, double[] unityvector) + //{ + // Matrix consistenttangent = Matrix.CreateZero(TotalStresses, TotalStrains); + // double dgamma = this.strainsEquivalent - this.strainsEquivalentPrev; + // double v1 = -dgamma * 6 * Math.Pow(this.shearModulus, 2) / vonMisesStress; + // double Hk = 0; + // double Hi = this.hardeningRatio; + // double v2 = (dgamma / vonMisesStress - 1 / (3 * this.shearModulus + Hk + Hi)) * 6 * Math.Pow(this.shearModulus, 2); + // for (int i = 0; i < 6; i++) + // { + // for (int j = 0; j < 6; j++) + // { + // consistenttangent[i, j] = this.elasticConstitutiveMatrix[i, j] + v1 * DeviatoricProjection[i, j] + v2 * unityvector[i] * unityvector[j]; + // } + // } + // return consistenttangent; + //} + + /// + /// Builds the consistent tangential constitutive matrix. + /// + /// + /// Refer to chapter 7.6.6 page 262 in Souza Neto. + /// + private void BuildConsistentTangentialConstitutiveMatrix(double[] strainDeviatoricTrial, double normStrainDeviatoricTrial, double vonMisesStress, double deltastrainsEquivalent) + { + var N = new double[incrementalStrains.Length]; + for (int i = 0; i < incrementalStrains.Length; i++) + { + N[i] = strainDeviatoricTrial[i] / normStrainDeviatoricTrial; + } + for (int i = 0; i < incrementalStrains.Length; i++) + { + for (int j = 0; j < incrementalStrains.Length; j++) + { + constitutiveMatrix[i, j] = 2 * shearModulus * (1 - (3 * shearModulus * vonMisesStress) / deltastrainsEquivalent) * DeviatoricProjection[i, j] + + 6 * shearModulus * shearModulus * (vonMisesStress / deltastrainsEquivalent - 1 / (3 * shearModulus + hardeningModulus)) * (N[i] * N[j]) + bulkModulus * VolumetricProjection[i, j]; + } + } + } + + ///// + ///// Builds the tangential constitutive matrix. + ///// + //private void BuildTangentialConstitutiveMatrix() + //{ + // this.constitutiveMatrix = Matrix.CreateZero(TotalStresses, TotalStrains); + // double invariantJ2New = this.GetDeviatorSecondStressInvariant(stressesNew); + + // double value2 = (3 * this.shearModulus * this.shearModulus) / + // ((this.hardeningRatio + (3 * this.shearModulus)) * invariantJ2New); + + // var stressDeviator = this.GetStressDeviator(stressesNew); + // for (int k1 = 0; k1 < TotalStresses; k1++) + // { + // for (int k2 = 0; k2 < TotalStresses; k2++) + // { + // this.constitutiveMatrix[k2, k1] = this.elasticConstitutiveMatrix[k2, k1] - + // (value2 * stressDeviator[k2] * stressDeviator[k1]); + // } + // } + //} + + /// + /// Calculates the next stress-strain point. + /// + /// When the new plastic strain is less than the previous one. + private void CalculateNextStressStrainPoint() + { + //calculate trial variables + var strainTrial = new double[incrementalStrains.Length]; + var stressTrial = new double[incrementalStrains.Length]; + var strainDeviatoricTrial = new double[incrementalStrains.Length]; + var stressVolumetricTrial = new double[incrementalStrains.Length]; + var stressDeviatoricTrial = new double[incrementalStrains.Length]; + var normStrainDeviatoricTrial = new double(); + for (int i = 0; i < incrementalStrains.Length; i++) + strainTrial[i] = strainsElasticPrev[i] + incrementalStrains[i]; + for (int i = 0; i < incrementalStrains.Length; i++) + { + for (int j = 0; j < incrementalStrains.Length; j++) + { + strainDeviatoricTrial[i] = strainDeviatoricTrial[i] + DeviatoricProjection[i, j] * strainTrial[j]; + if (i >= 0 && i <= 2) + stressTrial[i] += (2 * shearModulus * DeviatoricProjection[i, j] * strainTrial[j] + bulkModulus * VolumetricProjection[i, j] * strainTrial[j]); + else + stressTrial[i] += 0.5 * (2 * shearModulus * DeviatoricProjection[i, j] * strainTrial[j] + bulkModulus * VolumetricProjection[i, j] * strainTrial[j]); + } + } + for (int i = 0; i < incrementalStrains.Length; i++) + { + for (int j = 0; j < incrementalStrains.Length; j++) + { + stressVolumetricTrial[i] += (double)1 / 3 * VolumetricProjection[i, j] * stressTrial[j]; + stressDeviatoricTrial[i] += DeviatoricProjection[i, j] * stressTrial[j]; + } + normStrainDeviatoricTrial += strainDeviatoricTrial[i] * strainDeviatoricTrial[i]; + } + normStrainDeviatoricTrial = Math.Sqrt(normStrainDeviatoricTrial); + var J2 = GetDeviatorSecondStressInvariant(stressTrial); + double vonMisesStress = Math.Sqrt(3 * J2); + double vonMisesStressMinusYieldStress = vonMisesStress - hardeningFunc(strainsEquivalentPrev); + + bool materialIsInElasticRegion = vonMisesStressMinusYieldStress <= 0; + var stressVolumetric = new double[incrementalStrains.Length]; + var stressDeviatoric = new double[incrementalStrains.Length]; + double deltastrainsEquivalent = 0; + if (materialIsInElasticRegion) + { + stressesNew.CopyFrom(stressTrial); + stressVolumetric.CopyFrom(stressVolumetricTrial); + stressDeviatoric.CopyFrom(stressDeviatoricTrial); + strainsEquivalent = strainsEquivalentPrev; + constitutiveMatrix = elasticConstitutiveMatrix.CopyToFullMatrix(); + } + else + { + strainsEquivalent = strainsEquivalentPrev; + var yieldFunc = Math.Sqrt(3*J2) - hardeningFunc(strainsEquivalent); + hardeningModulus = CalculateHardeningGradient(strainsEquivalent); + while (Math.Abs(yieldFunc) >= 1e-6) + { + deltastrainsEquivalent += yieldFunc / (3 * shearModulus + hardeningModulus); + strainsEquivalent = strainsEquivalentPrev + deltastrainsEquivalent; + yieldFunc = Math.Sqrt(3 * J2) - 3 * shearModulus * deltastrainsEquivalent - hardeningFunc(strainsEquivalent); + hardeningModulus = CalculateHardeningGradient(strainsEquivalent); + } + + for (int i = 0; i < incrementalStrains.Length; i++) + { + stressVolumetric[i] = stressVolumetricTrial[i]; + stressDeviatoric[i] = (1 - (3 * shearModulus * deltastrainsEquivalent) / vonMisesStress) * stressDeviatoricTrial[i]; + stressesNew[i] = stressVolumetric[i] + stressDeviatoric[i]; + } + BuildConsistentTangentialConstitutiveMatrix(strainDeviatoricTrial, normStrainDeviatoricTrial, deltastrainsEquivalent, vonMisesStress); + //this.BuildTangentialConstitutiveMatrix(); + } + + if (Math.Abs(this.strainsEquivalent) < Math.Abs(this.strainsEquivalentPrev)) + { + throw new InvalidOperationException("Plastic strain cannot decrease."); + } + + this.modified = this.strainsEquivalent != this.strainsEquivalentPrev; + + for (int i = 0; i < incrementalStrains.Length; i++) + { + if (i >= 0 && i <= 2) + strainsElastic[i] = 1 / (2 * shearModulus) * stressDeviatoric[i] + 1 / (3 * bulkModulus) * stressVolumetric[i]; + else + strainsElastic[i] = 2 * (1 / (2 * shearModulus) * stressDeviatoric[i] + 1 / (3 * bulkModulus) * stressVolumetric[i]); + if (vonMisesStress > 0) + strainsPlastic[i] = strainsPlasticPrev[i] + strainTrial[i] - strainsElastic[i]; + strains[i] = strainsElastic[i] + strainsPlastic[i]; + } + } + + private double CalculateHardeningGradient(double strain_eq) => hardeningGrad = (hardeningFunc(strain_eq + 1e-8) - hardeningFunc(strain_eq)) / 1e-8; + + /// + /// Calculates and returns the first stress invariant (I1). + /// + /// The first stress invariant (I1). + public double GetFirstStressInvariant(double[] stresses) => stresses[0] + stresses[1] + stresses[2]; + + /// + /// Calculates and returns the mean hydrostatic stress. + /// + /// The mean hydrostatic stress. + public double GetMeanStress(double[] stresses) => GetFirstStressInvariant(stresses) / 3.0; + + /// + /// Calculates and returns the second stress invariant (I2). + /// + /// The second stress invariant (I2). + public double GetSecondStressInvariant(double[] stresses) + => (stresses[0] * stresses[1]) + (stresses[1] * stresses[2]) + (stresses[0] * stresses[2]) + - Math.Pow(stresses[5], 2) - Math.Pow(stresses[3], 2) - Math.Pow(stresses[4], 2); + + /// + /// Calculates and returns the stress deviator tensor in vector form. + /// + /// The stress deviator tensor in vector form. + public double[] GetStressDeviator(double[] stresses) + { + var hydrostaticStress = this.GetMeanStress(stresses); + var stressDeviator = new double[] + { + stresses[0] - hydrostaticStress, + stresses[1] - hydrostaticStress, + stresses[2] - hydrostaticStress, + stresses[3], + stresses[4], + stresses[5] + }; + + return stressDeviator; + } + + /// + /// Calculates and returns the third stress invariant (I3). + /// + /// The third stress invariant (I3). + public double GetThirdStressInvariant(double[] stresses) + => (stresses[0] * stresses[1] * stresses[2]) + (2 * stresses[5] * stresses[3] * stresses[4]) + - (Math.Pow(stresses[5], 2) * stresses[2]) - (Math.Pow(stresses[3], 2) * stresses[0]) + - (Math.Pow(stresses[4], 2) * stresses[1]); + + /// + /// Returns the first stress invariant of the stress deviator tensor (J1), which is zero. + /// + /// The first stress invariant of the stress deviator tensor (J1). + public double GetDeviatorFirstStressInvariant(double[] stresses) => 0; + + /// + /// Calculates and returns the second stress invariant of the stress deviator tensor (J2). + /// + /// The second stress invariant of the stress deviator tensor (J2). + public double GetDeviatorSecondStressInvariant(double[] stresses) + { + double i1 = this.GetFirstStressInvariant(stresses); + double i2 = this.GetSecondStressInvariant(stresses); + + double j2 = (1 / 3d * Math.Pow(i1, 2)) - i2; + return j2; + } + + /// + /// Calculates and returns the third stress invariant of the stress deviator tensor (J3). + /// + /// The third deviator stress invariant (J3). + public double GetDeviatorThirdStressInvariant(double[] stresses) + { + double i1 = this.GetFirstStressInvariant(stresses); + double i2 = this.GetSecondStressInvariant(stresses); + double i3 = this.GetThirdStressInvariant(stresses); + + double j3 = (2 / 27 * Math.Pow(i1, 3)) - (1 / 3 * i1 * i2) + i3; + return j3; + } + + private Matrix GetConstitutiveMatrix() + { + double fE1 = YoungModulus / (double)(1 + PoissonRatio); + double fE2 = fE1 * PoissonRatio / (double)(1 - 2 * PoissonRatio); + double fE3 = fE1 + fE2; + double fE4 = fE1 * 0.5; + var afE = Matrix.CreateZero(6, 6); + afE[0, 0] = fE3; + afE[0, 1] = fE2; + afE[0, 2] = fE2; + afE[1, 0] = fE2; + afE[1, 1] = fE3; + afE[1, 2] = fE2; + afE[2, 0] = fE2; + afE[2, 1] = fE2; + afE[2, 2] = fE3; + afE[3, 3] = fE4; + afE[4, 4] = fE4; + afE[5, 5] = fE4; + + return afE; + } + + } +}