@geotechcli/core 0.4.109 → 0.4.111
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/agents/brain.d.ts.map +1 -1
- package/dist/agents/brain.js +31 -2
- package/dist/agents/brain.js.map +1 -1
- package/dist/agents/fem-tools.js +5 -0
- package/dist/agents/fem-tools.js.map +1 -1
- package/dist/agents/safety.d.ts +1 -0
- package/dist/agents/safety.d.ts.map +1 -1
- package/dist/agents/safety.js +62 -0
- package/dist/agents/safety.js.map +1 -1
- 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 +21 -2
- package/dist/fem/engineering-evidence.d.ts.map +1 -1
- package/dist/fem/engineering-evidence.js +354 -6
- package/dist/fem/engineering-evidence.js.map +1 -1
- package/dist/fem/index.d.ts +4 -2
- package/dist/fem/index.d.ts.map +1 -1
- package/dist/fem/index.js +2 -0
- package/dist/fem/index.js.map +1 -1
- package/dist/fem/nonlinear-plane-strain-solver.d.ts +10 -0
- package/dist/fem/nonlinear-plane-strain-solver.d.ts.map +1 -0
- package/dist/fem/nonlinear-plane-strain-solver.js +358 -0
- package/dist/fem/nonlinear-plane-strain-solver.js.map +1 -0
- package/dist/fem/plane-strain-assembly.d.ts +54 -0
- package/dist/fem/plane-strain-assembly.d.ts.map +1 -1
- package/dist/fem/plane-strain-assembly.js +277 -46
- package/dist/fem/plane-strain-assembly.js.map +1 -1
- package/dist/fem/production-readiness.js +5 -5
- 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 +68 -3
- package/dist/fem/types.d.ts.map +1 -1
- package/dist/fem/validation.d.ts.map +1 -1
- package/dist/fem/validation.js +346 -13
- package/dist/fem/validation.js.map +1 -1
- package/dist/ingest/document-evidence-packet.d.ts +30 -30
- 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
|
@@ -1093,7 +1093,7 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1093
1093
|
pressureOvershootKpa: round(pressureOvershootKpa, 8),
|
|
1094
1094
|
};
|
|
1095
1095
|
const maxVerticalSettlementM = Math.max(0, -Math.min(...model.nodes.map((_, index) => displacement[dofIndex(index, 'uy')])));
|
|
1096
|
-
const
|
|
1096
|
+
const acceptedByPolicy = residualNormRatio <= policy.forceBalanceTolerance &&
|
|
1097
1097
|
massBalanceErrorRatio <= policy.porePressureMassBalanceTolerance;
|
|
1098
1098
|
timeSteps.push({
|
|
1099
1099
|
step: stepIndex + 1,
|
|
@@ -1109,7 +1109,8 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1109
1109
|
minPorePressureKpa: round(minPorePressureKpa, 8),
|
|
1110
1110
|
maxPorePressureKpa: round(maxPorePressureKpa, 8),
|
|
1111
1111
|
maxVerticalSettlementM: round(maxVerticalSettlementM, 12),
|
|
1112
|
-
|
|
1112
|
+
acceptedByPolicy,
|
|
1113
|
+
converged: acceptedByPolicy,
|
|
1113
1114
|
});
|
|
1114
1115
|
previousDisplacement = [...displacement];
|
|
1115
1116
|
previousPorePressure = [...porePressure];
|
|
@@ -1125,6 +1126,69 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1125
1126
|
lastMinPorePressureKpa = minPorePressureKpa;
|
|
1126
1127
|
lastMaxPorePressureKpa = maxPorePressureKpa;
|
|
1127
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
|
+
};
|
|
1128
1192
|
let maxBiotCouplingKpa = 0;
|
|
1129
1193
|
const elementOutputs = elementGaussCache.map((entry) => {
|
|
1130
1194
|
const material = materialById.get(entry.element.materialId);
|
|
@@ -1220,10 +1284,11 @@ export function runPlaneStrainBiotConsolidation(model) {
|
|
|
1220
1284
|
freePorePressureResidualL1M3PerS: Number(lastFreePorePressureResidualL1M3PerS.toExponential(12)),
|
|
1221
1285
|
pressureAudit: lastPressureAudit,
|
|
1222
1286
|
pressureDiagnostics: lastPressureDiagnostics,
|
|
1287
|
+
transientAcceptance,
|
|
1223
1288
|
massBalanceErrorRatio: round(lastMassBalanceErrorRatio, 12),
|
|
1224
1289
|
minPorePressureKpa: round(lastMinPorePressureKpa, 8),
|
|
1225
1290
|
maxPorePressureKpa: round(lastMaxPorePressureKpa, 8),
|
|
1226
|
-
converged:
|
|
1291
|
+
converged: transientAcceptance.accepted,
|
|
1227
1292
|
productionReady: false,
|
|
1228
1293
|
policy,
|
|
1229
1294
|
limitations: [
|
|
@@ -1708,6 +1773,42 @@ function normalizeLoadStepFractions(options) {
|
|
|
1708
1773
|
}
|
|
1709
1774
|
return fractions;
|
|
1710
1775
|
}
|
|
1776
|
+
function normalizeAdaptiveDruckerPragerLoadStepping(value) {
|
|
1777
|
+
if (value == null || value === false) {
|
|
1778
|
+
return {
|
|
1779
|
+
enabled: false,
|
|
1780
|
+
strategy: 'explicit-only',
|
|
1781
|
+
minLoadFactorIncrement: 0,
|
|
1782
|
+
maxCutbacks: 0,
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
const options = value === true ? {} : value;
|
|
1786
|
+
const enabled = options.enabled ?? true;
|
|
1787
|
+
if (!enabled) {
|
|
1788
|
+
return {
|
|
1789
|
+
enabled: false,
|
|
1790
|
+
strategy: 'explicit-only',
|
|
1791
|
+
minLoadFactorIncrement: 0,
|
|
1792
|
+
maxCutbacks: 0,
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
const strategy = options.strategy ?? 'cutback-bisection';
|
|
1796
|
+
if (strategy !== 'cutback-bisection') {
|
|
1797
|
+
throw new Error('adaptiveLoadStepping.strategy must be cutback-bisection.');
|
|
1798
|
+
}
|
|
1799
|
+
const minLoadFactorIncrement = options.minLoadFactorIncrement ?? 1 / 64;
|
|
1800
|
+
if (!Number.isFinite(minLoadFactorIncrement) || minLoadFactorIncrement <= 0 || minLoadFactorIncrement >= 1) {
|
|
1801
|
+
throw new Error('adaptiveLoadStepping.minLoadFactorIncrement must be finite and between 0 and 1.');
|
|
1802
|
+
}
|
|
1803
|
+
const maxCutbacks = Math.floor(options.maxCutbacks ?? 24);
|
|
1804
|
+
assertPositiveInteger(maxCutbacks, 'adaptiveLoadStepping.maxCutbacks');
|
|
1805
|
+
return {
|
|
1806
|
+
enabled: true,
|
|
1807
|
+
strategy,
|
|
1808
|
+
minLoadFactorIncrement,
|
|
1809
|
+
maxCutbacks,
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1711
1812
|
function isDruckerPragerStepConverged(evaluation, policy) {
|
|
1712
1813
|
return evaluation.residualNormRatio <= policy.forceBalanceTolerance &&
|
|
1713
1814
|
evaluation.maxYieldResidualRatio <= policy.residualTolerance;
|
|
@@ -1715,6 +1816,19 @@ function isDruckerPragerStepConverged(evaluation, policy) {
|
|
|
1715
1816
|
function createInitialDruckerPragerStateGrid(system) {
|
|
1716
1817
|
return system.elementGaussCache.map((entry) => entry.gauss.map(() => initialDruckerPragerState()));
|
|
1717
1818
|
}
|
|
1819
|
+
function druckerPragerStateSignature(states) {
|
|
1820
|
+
return states
|
|
1821
|
+
.flatMap((elementStates) => elementStates)
|
|
1822
|
+
.map((state) => [
|
|
1823
|
+
...state.strain,
|
|
1824
|
+
...state.stressKpa,
|
|
1825
|
+
state.sigmaZKpa,
|
|
1826
|
+
state.equivalentPlasticStrain,
|
|
1827
|
+
...state.plasticStrainPrincipal,
|
|
1828
|
+
state.volumetricPlasticStrain,
|
|
1829
|
+
].map((value) => round(value, 12)).join(','))
|
|
1830
|
+
.join('|');
|
|
1831
|
+
}
|
|
1718
1832
|
function druckerPragerResidualHistoryEntry(iteration, evaluation, policy) {
|
|
1719
1833
|
return {
|
|
1720
1834
|
iteration,
|
|
@@ -1830,6 +1944,7 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1830
1944
|
}
|
|
1831
1945
|
const linearSolverMaxIterations = options.linearSolverMaxIterations ?? Math.max(100, system.freeDofs.length * 10);
|
|
1832
1946
|
assertPositiveInteger(linearSolverMaxIterations, 'linearSolverMaxIterations');
|
|
1947
|
+
const adaptiveOptions = normalizeAdaptiveDruckerPragerLoadStepping(options.adaptiveLoadStepping);
|
|
1833
1948
|
const reducedDenseK = linearSolver === 'dense-gaussian'
|
|
1834
1949
|
? system.freeDofs.map((row) => system.freeDofs.map((col) => system.stiffness[row][col]))
|
|
1835
1950
|
: undefined;
|
|
@@ -1843,14 +1958,22 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1843
1958
|
let committedStates = createInitialDruckerPragerStateGrid(system);
|
|
1844
1959
|
let finalEvaluation;
|
|
1845
1960
|
const loadSteps = [];
|
|
1846
|
-
|
|
1961
|
+
let currentLoadFactor = 0;
|
|
1962
|
+
let attemptedStepCount = 0;
|
|
1963
|
+
let cutbackCount = 0;
|
|
1964
|
+
let maxCutbackDepth = 0;
|
|
1965
|
+
const acceptedLoadFactors = [];
|
|
1966
|
+
const adaptiveAttemptAudits = [];
|
|
1967
|
+
const adaptiveBlockerCodes = [];
|
|
1968
|
+
function solveDruckerPragerStepAttempt(input) {
|
|
1969
|
+
const trialDisplacement = [...input.startingDisplacement];
|
|
1847
1970
|
for (const [index, value] of system.prescribed)
|
|
1848
|
-
|
|
1971
|
+
trialDisplacement[index] = value * input.loadFactor;
|
|
1849
1972
|
let evaluation = evaluatePlaneStrainDruckerPragerState({
|
|
1850
1973
|
system,
|
|
1851
|
-
displacement,
|
|
1852
|
-
loadFactor,
|
|
1853
|
-
committedStates,
|
|
1974
|
+
displacement: trialDisplacement,
|
|
1975
|
+
loadFactor: input.loadFactor,
|
|
1976
|
+
committedStates: input.startingCommittedStates,
|
|
1854
1977
|
});
|
|
1855
1978
|
let iterations = 0;
|
|
1856
1979
|
let converged = isDruckerPragerStepConverged(evaluation, system.policy);
|
|
@@ -1864,7 +1987,7 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1864
1987
|
if (system.freeDofs.length === 0)
|
|
1865
1988
|
break;
|
|
1866
1989
|
const correctionRhs = system.freeDofs.map((index) => -evaluation.residual[index]);
|
|
1867
|
-
const currentFreeDisplacement = system.freeDofs.map((index) =>
|
|
1990
|
+
const currentFreeDisplacement = system.freeDofs.map((index) => trialDisplacement[index]);
|
|
1868
1991
|
const solved = linearSolver === 'sparse-csr-cg'
|
|
1869
1992
|
? solveSparseDruckerPragerCorrection({
|
|
1870
1993
|
reducedK: reducedSparseK,
|
|
@@ -1887,51 +2010,155 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1887
2010
|
}
|
|
1888
2011
|
const correction = solved.correction;
|
|
1889
2012
|
for (const [correctionIndex, dof] of system.freeDofs.entries()) {
|
|
1890
|
-
|
|
2013
|
+
trialDisplacement[dof] += correction[correctionIndex];
|
|
1891
2014
|
}
|
|
1892
2015
|
for (const [index, value] of system.prescribed)
|
|
1893
|
-
|
|
2016
|
+
trialDisplacement[index] = value * input.loadFactor;
|
|
1894
2017
|
evaluation = evaluatePlaneStrainDruckerPragerState({
|
|
1895
2018
|
system,
|
|
1896
|
-
displacement,
|
|
1897
|
-
loadFactor,
|
|
1898
|
-
committedStates,
|
|
2019
|
+
displacement: trialDisplacement,
|
|
2020
|
+
loadFactor: input.loadFactor,
|
|
2021
|
+
committedStates: input.startingCommittedStates,
|
|
1899
2022
|
});
|
|
1900
2023
|
converged = isDruckerPragerStepConverged(evaluation, system.policy);
|
|
1901
2024
|
residualHistory.push(druckerPragerResidualHistoryEntry(iterations, evaluation, system.policy));
|
|
1902
2025
|
}
|
|
1903
|
-
finalEvaluation = evaluation;
|
|
1904
|
-
if (converged)
|
|
1905
|
-
committedStates = evaluation.trialStates;
|
|
1906
2026
|
const terminationReason = druckerPragerTerminationReason(evaluation, system.policy, converged, iterations, linearSolverFailure);
|
|
1907
2027
|
const lastLinearAudit = linearSolverAudits.at(-1);
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
iterations,
|
|
1912
|
-
maxFreeResidualKn: round(evaluation.maxFreeResidualKn, 12),
|
|
1913
|
-
residualNormRatio: round(evaluation.residualNormRatio, 12),
|
|
1914
|
-
reactionBalanceRatio: round(evaluation.reactionBalanceRatio, 12),
|
|
1915
|
-
maxYieldResidualRatio: round(evaluation.maxYieldResidualRatio, 12),
|
|
1916
|
-
maxEquivalentPlasticStrain: round(evaluation.maxEquivalentPlasticStrain, 12),
|
|
1917
|
-
maxEquivalentPlasticStrainIncrement: round(evaluation.maxEquivalentPlasticStrainIncrement, 12),
|
|
1918
|
-
plasticGaussPointCount: evaluation.plasticGaussPointCount,
|
|
1919
|
-
linearSolver,
|
|
1920
|
-
linearIterations: linearSolverAudits.reduce((sum, audit) => sum + audit.iterations, 0),
|
|
1921
|
-
linearResidualNormRatio: round(lastLinearAudit?.residualNormRatio ?? 0, 12),
|
|
1922
|
-
correctionNormRatio: round(lastLinearAudit?.correctionNormRatio ?? 0, 12),
|
|
1923
|
-
linearSolverAudits,
|
|
2028
|
+
return {
|
|
2029
|
+
evaluation,
|
|
2030
|
+
displacement: trialDisplacement,
|
|
1924
2031
|
converged,
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2032
|
+
step: {
|
|
2033
|
+
step: input.step,
|
|
2034
|
+
loadFactor: round(input.loadFactor, 8),
|
|
2035
|
+
...(Math.abs(input.requestedLoadFactor - input.loadFactor) > 1e-12
|
|
2036
|
+
? { requestedLoadFactor: round(input.requestedLoadFactor, 8) }
|
|
2037
|
+
: {}),
|
|
2038
|
+
...(input.cutbackDepth > 0 ? { cutbackDepth: input.cutbackDepth, adaptiveCutback: true } : {}),
|
|
2039
|
+
iterations,
|
|
2040
|
+
maxFreeResidualKn: round(evaluation.maxFreeResidualKn, 12),
|
|
2041
|
+
residualNormRatio: round(evaluation.residualNormRatio, 12),
|
|
2042
|
+
reactionBalanceRatio: round(evaluation.reactionBalanceRatio, 12),
|
|
2043
|
+
maxYieldResidualRatio: round(evaluation.maxYieldResidualRatio, 12),
|
|
2044
|
+
maxEquivalentPlasticStrain: round(evaluation.maxEquivalentPlasticStrain, 12),
|
|
2045
|
+
maxEquivalentPlasticStrainIncrement: round(evaluation.maxEquivalentPlasticStrainIncrement, 12),
|
|
2046
|
+
plasticGaussPointCount: evaluation.plasticGaussPointCount,
|
|
2047
|
+
linearSolver,
|
|
2048
|
+
linearIterations: linearSolverAudits.reduce((sum, audit) => sum + audit.iterations, 0),
|
|
2049
|
+
linearResidualNormRatio: round(lastLinearAudit?.residualNormRatio ?? 0, 12),
|
|
2050
|
+
correctionNormRatio: round(lastLinearAudit?.correctionNormRatio ?? 0, 12),
|
|
2051
|
+
linearSolverAudits,
|
|
2052
|
+
converged,
|
|
2053
|
+
terminationReason,
|
|
2054
|
+
...(linearSolverFailure?.failureReason ? { failureReason: linearSolverFailure.failureReason } : {}),
|
|
2055
|
+
residualHistory,
|
|
2056
|
+
},
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function solveToLoadFactor(targetLoadFactor, requestedLoadFactor, cutbackDepth) {
|
|
2060
|
+
const startLoadFactor = currentLoadFactor;
|
|
2061
|
+
const committedStateSignatureBefore = druckerPragerStateSignature(committedStates);
|
|
2062
|
+
const attempt = solveDruckerPragerStepAttempt({
|
|
2063
|
+
step: loadSteps.length + 1,
|
|
2064
|
+
loadFactor: targetLoadFactor,
|
|
2065
|
+
requestedLoadFactor,
|
|
2066
|
+
cutbackDepth,
|
|
2067
|
+
startingDisplacement: displacement,
|
|
2068
|
+
startingCommittedStates: committedStates,
|
|
2069
|
+
});
|
|
2070
|
+
attemptedStepCount += 1;
|
|
2071
|
+
maxCutbackDepth = Math.max(maxCutbackDepth, cutbackDepth);
|
|
2072
|
+
if (attempt.converged) {
|
|
2073
|
+
adaptiveAttemptAudits.push({
|
|
2074
|
+
attempt: adaptiveAttemptAudits.length + 1,
|
|
2075
|
+
startLoadFactor: round(startLoadFactor, 8),
|
|
2076
|
+
targetLoadFactor: round(targetLoadFactor, 8),
|
|
2077
|
+
requestedLoadFactor: round(requestedLoadFactor, 8),
|
|
2078
|
+
cutbackDepth,
|
|
2079
|
+
accepted: true,
|
|
2080
|
+
rollbackApplied: false,
|
|
2081
|
+
terminationReason: attempt.step.terminationReason,
|
|
2082
|
+
committedStateSignatureBefore,
|
|
2083
|
+
committedStateSignatureAfter: druckerPragerStateSignature(attempt.evaluation.trialStates),
|
|
2084
|
+
});
|
|
2085
|
+
displacement.splice(0, displacement.length, ...attempt.displacement);
|
|
2086
|
+
committedStates = attempt.evaluation.trialStates;
|
|
2087
|
+
finalEvaluation = attempt.evaluation;
|
|
2088
|
+
currentLoadFactor = targetLoadFactor;
|
|
2089
|
+
acceptedLoadFactors.push(round(targetLoadFactor, 8));
|
|
2090
|
+
loadSteps.push({
|
|
2091
|
+
...attempt.step,
|
|
2092
|
+
step: loadSteps.length + 1,
|
|
2093
|
+
});
|
|
2094
|
+
return true;
|
|
2095
|
+
}
|
|
2096
|
+
adaptiveAttemptAudits.push({
|
|
2097
|
+
attempt: adaptiveAttemptAudits.length + 1,
|
|
2098
|
+
startLoadFactor: round(startLoadFactor, 8),
|
|
2099
|
+
targetLoadFactor: round(targetLoadFactor, 8),
|
|
2100
|
+
requestedLoadFactor: round(requestedLoadFactor, 8),
|
|
2101
|
+
cutbackDepth,
|
|
2102
|
+
accepted: false,
|
|
2103
|
+
rollbackApplied: adaptiveOptions.enabled,
|
|
2104
|
+
terminationReason: attempt.step.terminationReason,
|
|
2105
|
+
committedStateSignatureBefore,
|
|
2106
|
+
committedStateSignatureAfter: committedStateSignatureBefore,
|
|
1928
2107
|
});
|
|
2108
|
+
if (adaptiveOptions.enabled) {
|
|
2109
|
+
const increment = targetLoadFactor - startLoadFactor;
|
|
2110
|
+
const midpoint = startLoadFactor + increment / 2;
|
|
2111
|
+
if (cutbackCount >= adaptiveOptions.maxCutbacks) {
|
|
2112
|
+
adaptiveBlockerCodes.push('adaptive-load-step-max-cutbacks-exhausted');
|
|
2113
|
+
}
|
|
2114
|
+
else if (Math.abs(midpoint - startLoadFactor) < adaptiveOptions.minLoadFactorIncrement) {
|
|
2115
|
+
adaptiveBlockerCodes.push('adaptive-load-step-min-increment-reached');
|
|
2116
|
+
}
|
|
2117
|
+
else if (Math.abs(midpoint - targetLoadFactor) < 1e-12) {
|
|
2118
|
+
adaptiveBlockerCodes.push('adaptive-load-step-cutback-stalled');
|
|
2119
|
+
}
|
|
2120
|
+
else {
|
|
2121
|
+
cutbackCount += 1;
|
|
2122
|
+
const firstHalfAccepted = solveToLoadFactor(midpoint, requestedLoadFactor, cutbackDepth + 1);
|
|
2123
|
+
if (!firstHalfAccepted)
|
|
2124
|
+
return false;
|
|
2125
|
+
return solveToLoadFactor(targetLoadFactor, requestedLoadFactor, cutbackDepth + 1);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
displacement.splice(0, displacement.length, ...attempt.displacement);
|
|
2129
|
+
finalEvaluation = attempt.evaluation;
|
|
2130
|
+
loadSteps.push({
|
|
2131
|
+
...attempt.step,
|
|
2132
|
+
step: loadSteps.length + 1,
|
|
2133
|
+
});
|
|
2134
|
+
return false;
|
|
2135
|
+
}
|
|
2136
|
+
for (const loadFactor of loadStepFractions) {
|
|
2137
|
+
const accepted = solveToLoadFactor(loadFactor, loadFactor, 0);
|
|
2138
|
+
if (!accepted && adaptiveOptions.enabled)
|
|
2139
|
+
break;
|
|
1929
2140
|
}
|
|
1930
2141
|
if (!finalEvaluation) {
|
|
1931
2142
|
throw new Error('Plane-strain nonlinear load-step solver requires at least one load step.');
|
|
1932
2143
|
}
|
|
2144
|
+
const acceptedFinalEvaluation = finalEvaluation;
|
|
1933
2145
|
const failedStep = loadSteps.find((step) => !step.converged);
|
|
1934
2146
|
const status = failedStep ? 'nonconverged' : 'converged';
|
|
2147
|
+
const adaptiveLoadStepping = {
|
|
2148
|
+
schemaVersion: 'fem-plane-strain-dp-adaptive-load-stepping.v1',
|
|
2149
|
+
enabled: adaptiveOptions.enabled,
|
|
2150
|
+
strategy: adaptiveOptions.strategy,
|
|
2151
|
+
requestedStepCount: loadStepFractions.length,
|
|
2152
|
+
attemptedStepCount,
|
|
2153
|
+
acceptedStepCount: loadSteps.filter((step) => step.converged).length,
|
|
2154
|
+
cutbackCount,
|
|
2155
|
+
maxCutbackDepth,
|
|
2156
|
+
minLoadFactorIncrement: round(adaptiveOptions.minLoadFactorIncrement, 12),
|
|
2157
|
+
requestedLoadFactors: loadStepFractions.map((factor) => round(factor, 8)),
|
|
2158
|
+
acceptedLoadFactors,
|
|
2159
|
+
attempts: adaptiveAttemptAudits,
|
|
2160
|
+
blockerCodes: [...new Set(adaptiveBlockerCodes)],
|
|
2161
|
+
};
|
|
1935
2162
|
return {
|
|
1936
2163
|
schemaVersion: 'fem-plane-strain-drucker-prager-result.v1',
|
|
1937
2164
|
method: 'quad4-plane-strain-drucker-prager-modified-newton',
|
|
@@ -1939,10 +2166,10 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1939
2166
|
...node,
|
|
1940
2167
|
uxM: round(displacement[dofIndex(index, 'ux')], 12),
|
|
1941
2168
|
uyM: round(displacement[dofIndex(index, 'uy')], 12),
|
|
1942
|
-
rxnXKn: round(
|
|
1943
|
-
rxnYKn: round(
|
|
2169
|
+
rxnXKn: round(acceptedFinalEvaluation.residual[dofIndex(index, 'ux')], 8),
|
|
2170
|
+
rxnYKn: round(acceptedFinalEvaluation.residual[dofIndex(index, 'uy')], 8),
|
|
1944
2171
|
})),
|
|
1945
|
-
elements:
|
|
2172
|
+
elements: acceptedFinalEvaluation.elements,
|
|
1946
2173
|
dofCount: system.dofCount,
|
|
1947
2174
|
freeDofCount: system.freeDofs.length,
|
|
1948
2175
|
constrainedDofCount: system.prescribed.size,
|
|
@@ -1951,14 +2178,15 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1951
2178
|
globalTangent: 'elastic',
|
|
1952
2179
|
materialIntegration: 'incremental-committed-drucker-prager-return-mapping',
|
|
1953
2180
|
stateStorage: 'committed-gauss-point-history',
|
|
2181
|
+
adaptiveLoadStepping,
|
|
1954
2182
|
loadSteps,
|
|
1955
|
-
maxFreeResidualKn: round(
|
|
1956
|
-
residualNormRatio: round(
|
|
1957
|
-
reactionBalanceRatio: round(
|
|
1958
|
-
maxYieldResidualRatio: round(
|
|
1959
|
-
maxEquivalentPlasticStrain: round(
|
|
1960
|
-
maxEquivalentPlasticStrainIncrement: round(
|
|
1961
|
-
plasticGaussPointCount:
|
|
2183
|
+
maxFreeResidualKn: round(acceptedFinalEvaluation.maxFreeResidualKn, 12),
|
|
2184
|
+
residualNormRatio: round(acceptedFinalEvaluation.residualNormRatio, 12),
|
|
2185
|
+
reactionBalanceRatio: round(acceptedFinalEvaluation.reactionBalanceRatio, 12),
|
|
2186
|
+
maxYieldResidualRatio: round(acceptedFinalEvaluation.maxYieldResidualRatio, 12),
|
|
2187
|
+
maxEquivalentPlasticStrain: round(acceptedFinalEvaluation.maxEquivalentPlasticStrain, 12),
|
|
2188
|
+
maxEquivalentPlasticStrainIncrement: round(acceptedFinalEvaluation.maxEquivalentPlasticStrainIncrement, 12),
|
|
2189
|
+
plasticGaussPointCount: acceptedFinalEvaluation.plasticGaussPointCount,
|
|
1962
2190
|
converged: status === 'converged',
|
|
1963
2191
|
status,
|
|
1964
2192
|
...(failedStep ? {
|
|
@@ -1975,6 +2203,9 @@ export function runPlaneStrainDruckerPragerLoadSteps(model, options = {}) {
|
|
|
1975
2203
|
limitations: [
|
|
1976
2204
|
'Benchmark-scale modified-Newton plane-strain plasticity evidence kernel only.',
|
|
1977
2205
|
...(failedStep ? ['Nonconverged load-step result is reported fail-closed and must not be treated as an accepted engineering solve.'] : []),
|
|
2206
|
+
...(adaptiveOptions.enabled
|
|
2207
|
+
? ['Adaptive cutback-bisection load stepping can subdivide rejected nonlinear increments with rollback audit, but it is still not an arc-length or production consistent-tangent strategy.']
|
|
2208
|
+
: []),
|
|
1978
2209
|
linearSolver === 'sparse-csr-cg'
|
|
1979
2210
|
? '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.'
|
|
1980
2211
|
: '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.',
|