@geotechcli/core 0.4.108 → 0.4.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fem/demo.d.ts.map +1 -1
- package/dist/fem/demo.js +4 -0
- package/dist/fem/demo.js.map +1 -1
- package/dist/fem/engineering-evidence.d.ts.map +1 -1
- package/dist/fem/engineering-evidence.js +125 -1
- package/dist/fem/engineering-evidence.js.map +1 -1
- package/dist/fem/index.d.ts +2 -1
- package/dist/fem/index.d.ts.map +1 -1
- package/dist/fem/index.js +1 -0
- package/dist/fem/index.js.map +1 -1
- package/dist/fem/plane-strain-assembly.d.ts +27 -1
- package/dist/fem/plane-strain-assembly.d.ts.map +1 -1
- package/dist/fem/plane-strain-assembly.js +245 -48
- package/dist/fem/plane-strain-assembly.js.map +1 -1
- package/dist/fem/production-readiness.js +6 -6
- package/dist/fem/production-readiness.js.map +1 -1
- package/dist/fem/support-design.d.ts +124 -0
- package/dist/fem/support-design.d.ts.map +1 -0
- package/dist/fem/support-design.js +380 -0
- package/dist/fem/support-design.js.map +1 -0
- package/dist/fem/types.d.ts +16 -0
- package/dist/fem/types.d.ts.map +1 -1
- package/dist/fem/validation.d.ts.map +1 -1
- package/dist/fem/validation.js +65 -3
- package/dist/fem/validation.js.map +1 -1
- package/dist/ingest/document-evidence-packet.d.ts +24 -24
- package/dist/ingest/job-worker.d.ts.map +1 -1
- package/dist/ingest/job-worker.js +4 -0
- package/dist/ingest/job-worker.js.map +1 -1
- package/dist/meta/metadata.json +1 -1
- package/package.json +1 -1
|
@@ -180,52 +180,126 @@ function deviatoricNorm(values) {
|
|
|
180
180
|
const mean = (values[0] + values[1] + values[2]) / 3;
|
|
181
181
|
return Math.hypot(values[0] - mean, values[1] - mean, values[2] - mean);
|
|
182
182
|
}
|
|
183
|
-
function
|
|
183
|
+
function principalTrace(values) {
|
|
184
|
+
return values[0] + values[1] + values[2];
|
|
185
|
+
}
|
|
186
|
+
function principalDeviator(values) {
|
|
187
|
+
const mean = principalTrace(values) / 3;
|
|
188
|
+
return [values[0] - mean, values[1] - mean, values[2] - mean];
|
|
189
|
+
}
|
|
190
|
+
function addPrincipal(a, b) {
|
|
191
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
192
|
+
}
|
|
193
|
+
function scalePrincipal(values, scale) {
|
|
194
|
+
return [values[0] * scale, values[1] * scale, values[2] * scale];
|
|
195
|
+
}
|
|
196
|
+
function initialDruckerPragerState() {
|
|
197
|
+
return {
|
|
198
|
+
strain: [0, 0, 0],
|
|
199
|
+
stressKpa: [0, 0, 0],
|
|
200
|
+
sigmaZKpa: 0,
|
|
201
|
+
equivalentPlasticStrain: 0,
|
|
202
|
+
plasticStrainPrincipal: [0, 0, 0],
|
|
203
|
+
volumetricPlasticStrain: 0,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function integrateDruckerPragerStress(input) {
|
|
184
207
|
const { material, strain } = input;
|
|
185
208
|
const params = druckerPragerParameters(material);
|
|
186
|
-
const
|
|
209
|
+
const d = planeStrainD(material);
|
|
210
|
+
const strainIncrement = strain.map((value, index) => value - input.previous.strain[index]);
|
|
211
|
+
const stressIncrement = d.map((row) => row.reduce((sum, value, col) => sum + value * strainIncrement[col], 0));
|
|
212
|
+
const trialStress = [
|
|
213
|
+
input.previous.stressKpa[0] + stressIncrement[0],
|
|
214
|
+
input.previous.stressKpa[1] + stressIncrement[1],
|
|
215
|
+
input.previous.stressKpa[2] + stressIncrement[2],
|
|
216
|
+
];
|
|
217
|
+
const trialSigmaZKpa = input.previous.sigmaZKpa + planeStrainSigmaZ(material, strainIncrement);
|
|
218
|
+
const principal = principalCompressionFromPlaneStress(trialStress, trialSigmaZKpa);
|
|
187
219
|
const principalStress = principal.values;
|
|
188
|
-
const trace = principalStress
|
|
220
|
+
const trace = principalTrace(principalStress);
|
|
189
221
|
const mean = trace / 3;
|
|
190
222
|
const q = deviatoricNorm(principalStress);
|
|
191
|
-
const
|
|
223
|
+
const hardeningModulusKpa = material.hardeningModulusKpa ?? 0;
|
|
224
|
+
const intercept = params.compressionInterceptKpa +
|
|
225
|
+
hardeningModulusKpa * input.previous.equivalentPlasticStrain;
|
|
192
226
|
const yieldValue = q - params.rho * trace - intercept;
|
|
193
227
|
const yieldScale = Math.max(q, Math.abs(params.rho * trace), Math.abs(intercept), 1);
|
|
194
228
|
if (yieldValue <= 0 || Math.abs(yieldValue) / yieldScale <= input.policy.residualTolerance) {
|
|
195
|
-
|
|
229
|
+
const nextState = {
|
|
196
230
|
strain,
|
|
197
|
-
stressKpa:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
231
|
+
stressKpa: trialStress,
|
|
232
|
+
sigmaZKpa: trialSigmaZKpa,
|
|
233
|
+
equivalentPlasticStrain: input.previous.equivalentPlasticStrain,
|
|
234
|
+
plasticStrainPrincipal: input.previous.plasticStrainPrincipal,
|
|
235
|
+
volumetricPlasticStrain: input.previous.volumetricPlasticStrain,
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
nextState,
|
|
239
|
+
result: {
|
|
240
|
+
strain,
|
|
241
|
+
stressKpa: trialStress,
|
|
242
|
+
outOfPlaneStressKpa: trialSigmaZKpa,
|
|
243
|
+
compressionPositivePrincipalStressKpa: principalStress,
|
|
244
|
+
strainIncrement,
|
|
245
|
+
previousEquivalentPlasticStrain: input.previous.equivalentPlasticStrain,
|
|
246
|
+
equivalentPlasticStrainIncrement: 0,
|
|
247
|
+
plasticStrainPrincipal: nextState.plasticStrainPrincipal,
|
|
248
|
+
volumetricPlasticStrain: nextState.volumetricPlasticStrain,
|
|
249
|
+
yieldValueKpa: yieldValue,
|
|
250
|
+
yieldResidualRatio: Math.max(0, yieldValue) / yieldScale,
|
|
251
|
+
plasticMultiplier: 0,
|
|
252
|
+
equivalentPlasticStrain: nextState.equivalentPlasticStrain,
|
|
253
|
+
state: 'elastic',
|
|
254
|
+
},
|
|
205
255
|
};
|
|
206
256
|
}
|
|
207
|
-
const targetQ = Math.max(0, params.rho * trace + intercept);
|
|
208
|
-
const scale = q > 0 ? targetQ / q : 0;
|
|
209
|
-
const correctedPrincipal = principalStress.map((value) => mean + (value - mean) * scale);
|
|
210
|
-
const corrected = planeStressFromPrincipalCompression(correctedPrincipal, principal.angleRad);
|
|
211
|
-
const correctedQ = deviatoricNorm(correctedPrincipal);
|
|
212
|
-
const correctedYieldValue = correctedQ - params.rho * trace - intercept;
|
|
213
|
-
const correctedScale = Math.max(correctedQ, Math.abs(params.rho * trace), Math.abs(intercept), 1);
|
|
214
257
|
const moduli = elasticModuli(material);
|
|
215
258
|
const denominator = 2 * moduli.shearModulusKpa +
|
|
216
259
|
9 * moduli.bulkModulusKpa * params.rho * params.rhoBar +
|
|
217
|
-
|
|
260
|
+
hardeningModulusKpa;
|
|
218
261
|
const plasticMultiplier = Math.max(0, yieldValue / Math.max(denominator, 1e-12));
|
|
219
|
-
|
|
262
|
+
const updatedEquivalentPlasticStrain = input.previous.equivalentPlasticStrain + plasticMultiplier;
|
|
263
|
+
const updatedIntercept = params.compressionInterceptKpa +
|
|
264
|
+
hardeningModulusKpa * updatedEquivalentPlasticStrain;
|
|
265
|
+
const trialDeviator = principalDeviator(principalStress);
|
|
266
|
+
const flowDirection = q > 0 ? scalePrincipal(trialDeviator, 1 / q) : [0, 0, 0];
|
|
267
|
+
const plasticStrainIncrement = addPrincipal(scalePrincipal(flowDirection, plasticMultiplier), [-params.rhoBar * plasticMultiplier, -params.rhoBar * plasticMultiplier, -params.rhoBar * plasticMultiplier]);
|
|
268
|
+
const correctedDeviator = addPrincipal(trialDeviator, scalePrincipal(flowDirection, -2 * moduli.shearModulusKpa * plasticMultiplier));
|
|
269
|
+
const correctedTrace = trace + 9 * moduli.bulkModulusKpa * params.rhoBar * plasticMultiplier;
|
|
270
|
+
const correctedPrincipal = addPrincipal(correctedDeviator, [correctedTrace / 3, correctedTrace / 3, correctedTrace / 3]);
|
|
271
|
+
const corrected = planeStressFromPrincipalCompression(correctedPrincipal, principal.angleRad);
|
|
272
|
+
const correctedQ = deviatoricNorm(correctedPrincipal);
|
|
273
|
+
const correctedYieldValue = correctedQ - params.rho * correctedTrace - updatedIntercept;
|
|
274
|
+
const correctedScale = Math.max(correctedQ, Math.abs(params.rho * correctedTrace), Math.abs(updatedIntercept), 1);
|
|
275
|
+
const plasticStrainPrincipal = addPrincipal(input.previous.plasticStrainPrincipal, plasticStrainIncrement);
|
|
276
|
+
const volumetricPlasticStrain = input.previous.volumetricPlasticStrain + principalTrace(plasticStrainIncrement);
|
|
277
|
+
const nextState = {
|
|
220
278
|
strain,
|
|
221
279
|
stressKpa: corrected.stress,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
280
|
+
sigmaZKpa: corrected.sigmaZKpa,
|
|
281
|
+
equivalentPlasticStrain: updatedEquivalentPlasticStrain,
|
|
282
|
+
plasticStrainPrincipal,
|
|
283
|
+
volumetricPlasticStrain,
|
|
284
|
+
};
|
|
285
|
+
return {
|
|
286
|
+
nextState,
|
|
287
|
+
result: {
|
|
288
|
+
strain,
|
|
289
|
+
stressKpa: corrected.stress,
|
|
290
|
+
outOfPlaneStressKpa: corrected.sigmaZKpa,
|
|
291
|
+
compressionPositivePrincipalStressKpa: correctedPrincipal,
|
|
292
|
+
strainIncrement,
|
|
293
|
+
previousEquivalentPlasticStrain: input.previous.equivalentPlasticStrain,
|
|
294
|
+
equivalentPlasticStrainIncrement: plasticMultiplier,
|
|
295
|
+
plasticStrainPrincipal,
|
|
296
|
+
volumetricPlasticStrain,
|
|
297
|
+
yieldValueKpa: correctedYieldValue,
|
|
298
|
+
yieldResidualRatio: Math.abs(correctedYieldValue) / correctedScale,
|
|
299
|
+
plasticMultiplier,
|
|
300
|
+
equivalentPlasticStrain: updatedEquivalentPlasticStrain,
|
|
301
|
+
state: 'plastic',
|
|
302
|
+
},
|
|
229
303
|
};
|
|
230
304
|
}
|
|
231
305
|
function shapeDerivativesNatural(xi, eta) {
|
|
@@ -1019,7 +1093,7 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1019
1093
|
pressureOvershootKpa: round(pressureOvershootKpa, 8),
|
|
1020
1094
|
};
|
|
1021
1095
|
const maxVerticalSettlementM = Math.max(0, -Math.min(...model.nodes.map((_, index) => displacement[dofIndex(index, 'uy')])));
|
|
1022
|
-
const
|
|
1096
|
+
const acceptedByPolicy = residualNormRatio <= policy.forceBalanceTolerance &&
|
|
1023
1097
|
massBalanceErrorRatio <= policy.porePressureMassBalanceTolerance;
|
|
1024
1098
|
timeSteps.push({
|
|
1025
1099
|
step: stepIndex + 1,
|
|
@@ -1035,7 +1109,8 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1035
1109
|
minPorePressureKpa: round(minPorePressureKpa, 8),
|
|
1036
1110
|
maxPorePressureKpa: round(maxPorePressureKpa, 8),
|
|
1037
1111
|
maxVerticalSettlementM: round(maxVerticalSettlementM, 12),
|
|
1038
|
-
|
|
1112
|
+
acceptedByPolicy,
|
|
1113
|
+
converged: acceptedByPolicy,
|
|
1039
1114
|
});
|
|
1040
1115
|
previousDisplacement = [...displacement];
|
|
1041
1116
|
previousPorePressure = [...porePressure];
|
|
@@ -1051,6 +1126,69 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1051
1126
|
lastMinPorePressureKpa = minPorePressureKpa;
|
|
1052
1127
|
lastMaxPorePressureKpa = maxPorePressureKpa;
|
|
1053
1128
|
}
|
|
1129
|
+
let monotonicAverageFreePressureDissipation = true;
|
|
1130
|
+
let monotonicMaxPressureEnvelope = true;
|
|
1131
|
+
let previousAverageFreePressure = initialAverageFreePorePressureKpa;
|
|
1132
|
+
let previousMaxPressure = pressureUpperBoundKpa;
|
|
1133
|
+
let maxPressureOvershootKpa = 0;
|
|
1134
|
+
let maxResidualNormRatio = 0;
|
|
1135
|
+
let maxMassBalanceErrorRatio = 0;
|
|
1136
|
+
const pressureMonotonicToleranceKpa = 1e-8;
|
|
1137
|
+
for (const step of timeSteps) {
|
|
1138
|
+
maxResidualNormRatio = Math.max(maxResidualNormRatio, step.residualNormRatio);
|
|
1139
|
+
maxMassBalanceErrorRatio = Math.max(maxMassBalanceErrorRatio, step.massBalanceErrorRatio);
|
|
1140
|
+
maxPressureOvershootKpa = Math.max(maxPressureOvershootKpa, step.pressureDiagnostics.pressureOvershootKpa);
|
|
1141
|
+
if (step.pressureDiagnostics.averageFreePorePressureKpa >
|
|
1142
|
+
previousAverageFreePressure + pressureMonotonicToleranceKpa) {
|
|
1143
|
+
monotonicAverageFreePressureDissipation = false;
|
|
1144
|
+
}
|
|
1145
|
+
if (step.maxPorePressureKpa > previousMaxPressure + pressureMonotonicToleranceKpa) {
|
|
1146
|
+
monotonicMaxPressureEnvelope = false;
|
|
1147
|
+
}
|
|
1148
|
+
previousAverageFreePressure = step.pressureDiagnostics.averageFreePorePressureKpa;
|
|
1149
|
+
previousMaxPressure = step.maxPorePressureKpa;
|
|
1150
|
+
}
|
|
1151
|
+
const acceptedStepCount = timeSteps.filter((step) => step.acceptedByPolicy).length;
|
|
1152
|
+
const monotonicAverageFreePressureDissipationRequired = Array.from(prescribedPressures.values()).every((value) => value <= pressureMonotonicToleranceKpa) &&
|
|
1153
|
+
fluxes.every((value) => Math.abs(value) <= 1e-15);
|
|
1154
|
+
const dissipationCheckMode = monotonicAverageFreePressureDissipationRequired
|
|
1155
|
+
? 'drained-dissipation'
|
|
1156
|
+
: 'prescribed-gradient-relaxation';
|
|
1157
|
+
const transientBlockerCodes = [
|
|
1158
|
+
...(acceptedStepCount < policy.minAcceptedSteps
|
|
1159
|
+
? ['accepted-step-count-less-than-policy']
|
|
1160
|
+
: []),
|
|
1161
|
+
...(maxResidualNormRatio > policy.forceBalanceTolerance
|
|
1162
|
+
? ['force-residual-tolerance-exceeded']
|
|
1163
|
+
: []),
|
|
1164
|
+
...(maxMassBalanceErrorRatio > policy.porePressureMassBalanceTolerance
|
|
1165
|
+
? ['pore-pressure-mass-balance-tolerance-exceeded']
|
|
1166
|
+
: []),
|
|
1167
|
+
...(maxPressureOvershootKpa > 1e-6
|
|
1168
|
+
? ['pressure-overshoot-nonzero']
|
|
1169
|
+
: []),
|
|
1170
|
+
...(monotonicAverageFreePressureDissipationRequired && !monotonicAverageFreePressureDissipation
|
|
1171
|
+
? ['average-free-pore-pressure-dissipation-not-monotonic']
|
|
1172
|
+
: []),
|
|
1173
|
+
...(!monotonicMaxPressureEnvelope
|
|
1174
|
+
? ['max-pore-pressure-envelope-not-monotonic']
|
|
1175
|
+
: []),
|
|
1176
|
+
];
|
|
1177
|
+
const transientAcceptance = {
|
|
1178
|
+
schemaVersion: 'fem-plane-strain-biot-transient-acceptance.v1',
|
|
1179
|
+
accepted: transientBlockerCodes.length === 0,
|
|
1180
|
+
dissipationCheckMode,
|
|
1181
|
+
acceptedStepCount,
|
|
1182
|
+
requiredStepCount: policy.minAcceptedSteps,
|
|
1183
|
+
maxResidualNormRatio: round(maxResidualNormRatio, 12),
|
|
1184
|
+
maxMassBalanceErrorRatio: round(maxMassBalanceErrorRatio, 12),
|
|
1185
|
+
maxPressureOvershootKpa: round(maxPressureOvershootKpa, 8),
|
|
1186
|
+
monotonicAverageFreePressureDissipation,
|
|
1187
|
+
monotonicAverageFreePressureDissipationRequired,
|
|
1188
|
+
monotonicMaxPressureEnvelope,
|
|
1189
|
+
finalPorePressureDissipationRatio: round(lastPressureDiagnostics.porePressureDissipationRatio, 12),
|
|
1190
|
+
blockerCodes: transientBlockerCodes,
|
|
1191
|
+
};
|
|
1054
1192
|
let maxBiotCouplingKpa = 0;
|
|
1055
1193
|
const elementOutputs = elementGaussCache.map((entry) => {
|
|
1056
1194
|
const material = materialById.get(entry.element.materialId);
|
|
@@ -1146,10 +1284,11 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1146
1284
|
freePorePressureResidualL1M3PerS: Number(lastFreePorePressureResidualL1M3PerS.toExponential(12)),
|
|
1147
1285
|
pressureAudit: lastPressureAudit,
|
|
1148
1286
|
pressureDiagnostics: lastPressureDiagnostics,
|
|
1287
|
+
transientAcceptance,
|
|
1149
1288
|
massBalanceErrorRatio: round(lastMassBalanceErrorRatio, 12),
|
|
1150
1289
|
minPorePressureKpa: round(lastMinPorePressureKpa, 8),
|
|
1151
1290
|
maxPorePressureKpa: round(lastMaxPorePressureKpa, 8),
|
|
1152
|
-
converged:
|
|
1291
|
+
converged: transientAcceptance.accepted,
|
|
1153
1292
|
productionReady: false,
|
|
1154
1293
|
policy,
|
|
1155
1294
|
limitations: [
|
|
@@ -1489,27 +1628,30 @@ function assemblePlaneStrainSystem(model, options = {}) {
|
|
|
1489
1628
|
};
|
|
1490
1629
|
}
|
|
1491
1630
|
function evaluatePlaneStrainDruckerPragerState(input) {
|
|
1492
|
-
const { system, displacement, loadFactor } = input;
|
|
1631
|
+
const { system, displacement, loadFactor, committedStates } = input;
|
|
1493
1632
|
const internal = new Array(system.dofCount).fill(0);
|
|
1494
1633
|
let maxYieldResidualRatio = 0;
|
|
1495
1634
|
let maxEquivalentPlasticStrain = 0;
|
|
1635
|
+
let maxEquivalentPlasticStrainIncrement = 0;
|
|
1496
1636
|
let plasticGaussPointCount = 0;
|
|
1497
|
-
const
|
|
1637
|
+
const trialStates = [];
|
|
1638
|
+
const elements = system.elementGaussCache.map((entry, elementIndex) => {
|
|
1498
1639
|
const material = system.materialById.get(entry.element.materialId);
|
|
1499
|
-
const d = planeStrainD(material);
|
|
1500
1640
|
const elementDisplacement = entry.globalDofs.map((index) => displacement[index]);
|
|
1501
1641
|
const gaussPoints = entry.gauss.map((point, index) => {
|
|
1502
1642
|
const strain = point.b.map((row) => row.reduce((sum, value, col) => sum + value * elementDisplacement[col], 0));
|
|
1503
|
-
const
|
|
1504
|
-
const projected = projectDruckerPragerStress({
|
|
1643
|
+
const integrated = integrateDruckerPragerStress({
|
|
1505
1644
|
material,
|
|
1506
1645
|
policy: system.policy,
|
|
1507
1646
|
strain,
|
|
1508
|
-
|
|
1509
|
-
sigmaZKpa: planeStrainSigmaZ(material, strain),
|
|
1647
|
+
previous: committedStates[elementIndex]?.[index] ?? initialDruckerPragerState(),
|
|
1510
1648
|
});
|
|
1649
|
+
const projected = integrated.result;
|
|
1650
|
+
trialStates[elementIndex] = trialStates[elementIndex] ?? [];
|
|
1651
|
+
trialStates[elementIndex][index] = integrated.nextState;
|
|
1511
1652
|
maxYieldResidualRatio = Math.max(maxYieldResidualRatio, projected.yieldResidualRatio);
|
|
1512
1653
|
maxEquivalentPlasticStrain = Math.max(maxEquivalentPlasticStrain, projected.equivalentPlasticStrain);
|
|
1654
|
+
maxEquivalentPlasticStrainIncrement = Math.max(maxEquivalentPlasticStrainIncrement, projected.equivalentPlasticStrainIncrement);
|
|
1513
1655
|
if (projected.state === 'plastic')
|
|
1514
1656
|
plasticGaussPointCount += 1;
|
|
1515
1657
|
for (let localDof = 0; localDof < 8; localDof += 1) {
|
|
@@ -1538,6 +1680,19 @@ function evaluatePlaneStrainDruckerPragerState(input) {
|
|
|
1538
1680
|
round(projected.compressionPositivePrincipalStressKpa[1], 8),
|
|
1539
1681
|
round(projected.compressionPositivePrincipalStressKpa[2], 8),
|
|
1540
1682
|
],
|
|
1683
|
+
strainIncrement: [
|
|
1684
|
+
round(projected.strainIncrement[0], 12),
|
|
1685
|
+
round(projected.strainIncrement[1], 12),
|
|
1686
|
+
round(projected.strainIncrement[2], 12),
|
|
1687
|
+
],
|
|
1688
|
+
previousEquivalentPlasticStrain: round(projected.previousEquivalentPlasticStrain, 12),
|
|
1689
|
+
equivalentPlasticStrainIncrement: round(projected.equivalentPlasticStrainIncrement, 12),
|
|
1690
|
+
plasticStrainPrincipal: [
|
|
1691
|
+
round(projected.plasticStrainPrincipal[0], 12),
|
|
1692
|
+
round(projected.plasticStrainPrincipal[1], 12),
|
|
1693
|
+
round(projected.plasticStrainPrincipal[2], 12),
|
|
1694
|
+
],
|
|
1695
|
+
volumetricPlasticStrain: round(projected.volumetricPlasticStrain, 12),
|
|
1541
1696
|
yieldValueKpa: round(projected.yieldValueKpa, 10),
|
|
1542
1697
|
yieldResidualRatio: round(projected.yieldResidualRatio, 12),
|
|
1543
1698
|
plasticMultiplier: round(projected.plasticMultiplier, 12),
|
|
@@ -1578,13 +1733,33 @@ function evaluatePlaneStrainDruckerPragerState(input) {
|
|
|
1578
1733
|
reactionBalanceRatio,
|
|
1579
1734
|
maxYieldResidualRatio,
|
|
1580
1735
|
maxEquivalentPlasticStrain,
|
|
1736
|
+
maxEquivalentPlasticStrainIncrement,
|
|
1581
1737
|
plasticGaussPointCount,
|
|
1738
|
+
trialStates,
|
|
1582
1739
|
elements,
|
|
1583
1740
|
};
|
|
1584
1741
|
}
|
|
1585
|
-
function normalizeLoadStepFractions(
|
|
1586
|
-
|
|
1587
|
-
|
|
1742
|
+
function normalizeLoadStepFractions(options) {
|
|
1743
|
+
if (options.loadStepFractions != null && options.loadHistoryFactors != null) {
|
|
1744
|
+
throw new Error('Specify either loadStepFractions or loadHistoryFactors, not both.');
|
|
1745
|
+
}
|
|
1746
|
+
if (options.loadHistoryFactors != null) {
|
|
1747
|
+
if (!Array.isArray(options.loadHistoryFactors) || options.loadHistoryFactors.length === 0) {
|
|
1748
|
+
throw new Error('loadHistoryFactors must contain at least one load factor when provided.');
|
|
1749
|
+
}
|
|
1750
|
+
const history = [...options.loadHistoryFactors];
|
|
1751
|
+
for (const [index, factor] of history.entries()) {
|
|
1752
|
+
if (!Number.isFinite(factor) || factor < 0 || factor > 1) {
|
|
1753
|
+
throw new Error(`loadHistoryFactors.${index} must be finite and between 0 and 1.`);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
if (history[history.length - 1] !== 1) {
|
|
1757
|
+
throw new Error('loadHistoryFactors must end at 1.');
|
|
1758
|
+
}
|
|
1759
|
+
return history;
|
|
1760
|
+
}
|
|
1761
|
+
const fractions = options.loadStepFractions && options.loadStepFractions.length > 0
|
|
1762
|
+
? [...options.loadStepFractions]
|
|
1588
1763
|
: [0.25, 0.5, 0.75, 1];
|
|
1589
1764
|
let previous = 0;
|
|
1590
1765
|
for (const [index, fraction] of fractions.entries()) {
|
|
@@ -1602,6 +1777,9 @@ function isDruckerPragerStepConverged(evaluation, policy) {
|
|
|
1602
1777
|
return evaluation.residualNormRatio <= policy.forceBalanceTolerance &&
|
|
1603
1778
|
evaluation.maxYieldResidualRatio <= policy.residualTolerance;
|
|
1604
1779
|
}
|
|
1780
|
+
function createInitialDruckerPragerStateGrid(system) {
|
|
1781
|
+
return system.elementGaussCache.map((entry) => entry.gauss.map(() => initialDruckerPragerState()));
|
|
1782
|
+
}
|
|
1605
1783
|
function druckerPragerResidualHistoryEntry(iteration, evaluation, policy) {
|
|
1606
1784
|
return {
|
|
1607
1785
|
iteration,
|
|
@@ -1707,7 +1885,10 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1707
1885
|
const system = assemblePlaneStrainSystem(model, {
|
|
1708
1886
|
storage: linearSolver === 'sparse-csr-cg' ? 'triplets-only' : 'dense-and-triplets',
|
|
1709
1887
|
});
|
|
1710
|
-
const loadStepFractions = normalizeLoadStepFractions(
|
|
1888
|
+
const loadStepFractions = normalizeLoadStepFractions({
|
|
1889
|
+
loadStepFractions: options.loadStepFractions,
|
|
1890
|
+
loadHistoryFactors: options.loadHistoryFactors,
|
|
1891
|
+
});
|
|
1711
1892
|
const linearSolverTolerance = options.linearSolverTolerance ?? Math.min(1e-10, system.policy.forceBalanceTolerance / 10);
|
|
1712
1893
|
if (!Number.isFinite(linearSolverTolerance) || linearSolverTolerance <= 0) {
|
|
1713
1894
|
throw new Error('linearSolverTolerance must be a finite positive number.');
|
|
@@ -1724,12 +1905,18 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1724
1905
|
})
|
|
1725
1906
|
: undefined;
|
|
1726
1907
|
const displacement = new Array(system.dofCount).fill(0);
|
|
1908
|
+
let committedStates = createInitialDruckerPragerStateGrid(system);
|
|
1727
1909
|
let finalEvaluation;
|
|
1728
1910
|
const loadSteps = [];
|
|
1729
1911
|
for (const [stepIndex, loadFactor] of loadStepFractions.entries()) {
|
|
1730
1912
|
for (const [index, value] of system.prescribed)
|
|
1731
1913
|
displacement[index] = value * loadFactor;
|
|
1732
|
-
let evaluation = evaluatePlaneStrainDruckerPragerState({
|
|
1914
|
+
let evaluation = evaluatePlaneStrainDruckerPragerState({
|
|
1915
|
+
system,
|
|
1916
|
+
displacement,
|
|
1917
|
+
loadFactor,
|
|
1918
|
+
committedStates,
|
|
1919
|
+
});
|
|
1733
1920
|
let iterations = 0;
|
|
1734
1921
|
let converged = isDruckerPragerStepConverged(evaluation, system.policy);
|
|
1735
1922
|
const residualHistory = [
|
|
@@ -1769,11 +1956,18 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1769
1956
|
}
|
|
1770
1957
|
for (const [index, value] of system.prescribed)
|
|
1771
1958
|
displacement[index] = value * loadFactor;
|
|
1772
|
-
evaluation = evaluatePlaneStrainDruckerPragerState({
|
|
1959
|
+
evaluation = evaluatePlaneStrainDruckerPragerState({
|
|
1960
|
+
system,
|
|
1961
|
+
displacement,
|
|
1962
|
+
loadFactor,
|
|
1963
|
+
committedStates,
|
|
1964
|
+
});
|
|
1773
1965
|
converged = isDruckerPragerStepConverged(evaluation, system.policy);
|
|
1774
1966
|
residualHistory.push(druckerPragerResidualHistoryEntry(iterations, evaluation, system.policy));
|
|
1775
1967
|
}
|
|
1776
1968
|
finalEvaluation = evaluation;
|
|
1969
|
+
if (converged)
|
|
1970
|
+
committedStates = evaluation.trialStates;
|
|
1777
1971
|
const terminationReason = druckerPragerTerminationReason(evaluation, system.policy, converged, iterations, linearSolverFailure);
|
|
1778
1972
|
const lastLinearAudit = linearSolverAudits.at(-1);
|
|
1779
1973
|
loadSteps.push({
|
|
@@ -1785,6 +1979,7 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1785
1979
|
reactionBalanceRatio: round(evaluation.reactionBalanceRatio, 12),
|
|
1786
1980
|
maxYieldResidualRatio: round(evaluation.maxYieldResidualRatio, 12),
|
|
1787
1981
|
maxEquivalentPlasticStrain: round(evaluation.maxEquivalentPlasticStrain, 12),
|
|
1982
|
+
maxEquivalentPlasticStrainIncrement: round(evaluation.maxEquivalentPlasticStrainIncrement, 12),
|
|
1788
1983
|
plasticGaussPointCount: evaluation.plasticGaussPointCount,
|
|
1789
1984
|
linearSolver,
|
|
1790
1985
|
linearIterations: linearSolverAudits.reduce((sum, audit) => sum + audit.iterations, 0),
|
|
@@ -1819,13 +2014,15 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1819
2014
|
linearSolver,
|
|
1820
2015
|
nonlinearAlgorithm: 'modified-newton',
|
|
1821
2016
|
globalTangent: 'elastic',
|
|
1822
|
-
materialIntegration: '
|
|
2017
|
+
materialIntegration: 'incremental-committed-drucker-prager-return-mapping',
|
|
2018
|
+
stateStorage: 'committed-gauss-point-history',
|
|
1823
2019
|
loadSteps,
|
|
1824
2020
|
maxFreeResidualKn: round(finalEvaluation.maxFreeResidualKn, 12),
|
|
1825
2021
|
residualNormRatio: round(finalEvaluation.residualNormRatio, 12),
|
|
1826
2022
|
reactionBalanceRatio: round(finalEvaluation.reactionBalanceRatio, 12),
|
|
1827
2023
|
maxYieldResidualRatio: round(finalEvaluation.maxYieldResidualRatio, 12),
|
|
1828
2024
|
maxEquivalentPlasticStrain: round(finalEvaluation.maxEquivalentPlasticStrain, 12),
|
|
2025
|
+
maxEquivalentPlasticStrainIncrement: round(finalEvaluation.maxEquivalentPlasticStrainIncrement, 12),
|
|
1829
2026
|
plasticGaussPointCount: finalEvaluation.plasticGaussPointCount,
|
|
1830
2027
|
converged: status === 'converged',
|
|
1831
2028
|
status,
|
|
@@ -1844,8 +2041,8 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1844
2041
|
'Benchmark-scale modified-Newton plane-strain plasticity evidence kernel only.',
|
|
1845
2042
|
...(failedStep ? ['Nonconverged load-step result is reported fail-closed and must not be treated as an accepted engineering solve.'] : []),
|
|
1846
2043
|
linearSolver === 'sparse-csr-cg'
|
|
1847
|
-
? 'Uses an experimental CSR Conjugate Gradient linear solve audit, elastic global tangent, and Gauss-point Drucker-Prager
|
|
1848
|
-
: 'Uses elastic global tangent with Gauss-point Drucker-Prager
|
|
2044
|
+
? 'Uses an experimental CSR Conjugate Gradient linear solve audit, elastic global tangent, and committed Gauss-point Drucker-Prager return mapping; no production consistent tangent, hardening calibration, staged activation, pore-pressure DOF, or route-backed result manifest is provided.'
|
|
2045
|
+
: 'Uses elastic global tangent with committed Gauss-point Drucker-Prager return mapping; no production consistent tangent, production sparse solver, hardening calibration, staged activation, pore-pressure DOF, or route-backed result manifest is provided.',
|
|
1849
2046
|
'Use for deterministic evidence and regression tests only until independent published/commercial benchmark comparison and licensed production approval gates are complete.',
|
|
1850
2047
|
],
|
|
1851
2048
|
};
|