@geotechcli/core 0.4.106 → 0.4.108
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 +65 -0
- package/dist/fem/engineering-evidence.d.ts.map +1 -1
- package/dist/fem/engineering-evidence.js +295 -15
- package/dist/fem/engineering-evidence.js.map +1 -1
- package/dist/fem/index.d.ts +3 -2
- package/dist/fem/index.d.ts.map +1 -1
- package/dist/fem/index.js +2 -1
- package/dist/fem/index.js.map +1 -1
- package/dist/fem/nonlinear-column-solver.d.ts.map +1 -1
- package/dist/fem/nonlinear-column-solver.js +137 -6
- package/dist/fem/nonlinear-column-solver.js.map +1 -1
- package/dist/fem/plane-strain-assembly.d.ts +69 -3
- package/dist/fem/plane-strain-assembly.d.ts.map +1 -1
- package/dist/fem/plane-strain-assembly.js +267 -21
- package/dist/fem/plane-strain-assembly.js.map +1 -1
- package/dist/fem/production-readiness.d.ts.map +1 -1
- package/dist/fem/production-readiness.js +8 -2
- package/dist/fem/production-readiness.js.map +1 -1
- package/dist/fem/routing.js +4 -4
- package/dist/fem/routing.js.map +1 -1
- package/dist/fem/sparse-linear-algebra.d.ts +47 -0
- package/dist/fem/sparse-linear-algebra.d.ts.map +1 -0
- package/dist/fem/sparse-linear-algebra.js +290 -0
- package/dist/fem/sparse-linear-algebra.js.map +1 -0
- package/dist/fem/types.d.ts +49 -0
- package/dist/fem/types.d.ts.map +1 -1
- package/dist/fem/validation.d.ts.map +1 -1
- package/dist/fem/validation.js +166 -2
- package/dist/fem/validation.js.map +1 -1
- package/dist/meta/metadata.json +1 -1
- package/package.json +1 -1
|
@@ -8,6 +8,66 @@ export const DEFAULT_FEM_CONVERGENCE_POLICY = {
|
|
|
8
8
|
maxIterations: 40,
|
|
9
9
|
minAcceptedSteps: 3,
|
|
10
10
|
};
|
|
11
|
+
const REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES = [
|
|
12
|
+
'published-source',
|
|
13
|
+
'commercial-solver',
|
|
14
|
+
];
|
|
15
|
+
const DEFAULT_EXTERNAL_BENCHMARK_REQUIRED_QUANTITIES = [
|
|
16
|
+
{
|
|
17
|
+
id: 'nonlinear-plane-strain-displacement-envelope',
|
|
18
|
+
feature: 'coupled-nonlinear-plane-strain',
|
|
19
|
+
quantity: 'nodal displacement envelope',
|
|
20
|
+
unit: 'mm',
|
|
21
|
+
tolerance: 0.05,
|
|
22
|
+
toleranceType: 'relative',
|
|
23
|
+
requiredReferenceSourceTypes: REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'global-reaction-force-balance',
|
|
27
|
+
feature: 'solver-convergence-and-tolerance',
|
|
28
|
+
quantity: 'reaction force and applied load balance',
|
|
29
|
+
unit: 'kN',
|
|
30
|
+
tolerance: 0.02,
|
|
31
|
+
toleranceType: 'relative',
|
|
32
|
+
requiredReferenceSourceTypes: REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'consolidation-settlement-time-curve',
|
|
36
|
+
feature: 'consolidation',
|
|
37
|
+
quantity: 'settlement-time curve and degree of consolidation',
|
|
38
|
+
unit: 'mm, ratio',
|
|
39
|
+
tolerance: 0.02,
|
|
40
|
+
toleranceType: 'absolute-or-relative',
|
|
41
|
+
requiredReferenceSourceTypes: REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'biot-pore-pressure-dissipation',
|
|
45
|
+
feature: 'coupled-biot-plane-strain',
|
|
46
|
+
quantity: 'excess pore-pressure dissipation curve',
|
|
47
|
+
unit: 'kPa',
|
|
48
|
+
tolerance: 0.05,
|
|
49
|
+
toleranceType: 'relative',
|
|
50
|
+
requiredReferenceSourceTypes: REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'seepage-head-flux-gradient',
|
|
54
|
+
feature: 'seepage-pore-pressure-coupling',
|
|
55
|
+
quantity: 'hydraulic head, flux, and gradient checks',
|
|
56
|
+
unit: 'm, m3/s, ratio',
|
|
57
|
+
tolerance: 0.03,
|
|
58
|
+
toleranceType: 'relative',
|
|
59
|
+
requiredReferenceSourceTypes: REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'support-reaction-and-stability-factors',
|
|
63
|
+
feature: 'support-design',
|
|
64
|
+
quantity: 'support reaction and stability safety-factor checks',
|
|
65
|
+
unit: 'kN, ratio',
|
|
66
|
+
tolerance: 0.05,
|
|
67
|
+
toleranceType: 'relative',
|
|
68
|
+
requiredReferenceSourceTypes: REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES,
|
|
69
|
+
},
|
|
70
|
+
];
|
|
11
71
|
function degToRad(degrees) {
|
|
12
72
|
return (degrees * Math.PI) / 180;
|
|
13
73
|
}
|
|
@@ -25,6 +85,144 @@ function round(value, digits = 6) {
|
|
|
25
85
|
const scale = 10 ** digits;
|
|
26
86
|
return Math.round(value * scale) / scale;
|
|
27
87
|
}
|
|
88
|
+
function isNonEmptyString(value) {
|
|
89
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
90
|
+
}
|
|
91
|
+
function hasPublishedCitation(citation) {
|
|
92
|
+
return citation != null &&
|
|
93
|
+
isNonEmptyString(citation.title) &&
|
|
94
|
+
Array.isArray(citation.authors) &&
|
|
95
|
+
citation.authors.some((author) => isNonEmptyString(author)) &&
|
|
96
|
+
Number.isInteger(citation.year) &&
|
|
97
|
+
citation.year >= 1900 &&
|
|
98
|
+
(isNonEmptyString(citation.publication) ||
|
|
99
|
+
isNonEmptyString(citation.doi) ||
|
|
100
|
+
isNonEmptyString(citation.url));
|
|
101
|
+
}
|
|
102
|
+
function hasReferenceSolverCitation(citation) {
|
|
103
|
+
return citation != null &&
|
|
104
|
+
isNonEmptyString(citation.name) &&
|
|
105
|
+
isNonEmptyString(citation.version);
|
|
106
|
+
}
|
|
107
|
+
function copyExternalBenchmarkReference(reference) {
|
|
108
|
+
return {
|
|
109
|
+
...reference,
|
|
110
|
+
...(reference.publishedSource
|
|
111
|
+
? {
|
|
112
|
+
publishedSource: {
|
|
113
|
+
...reference.publishedSource,
|
|
114
|
+
authors: [...reference.publishedSource.authors],
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
: {}),
|
|
118
|
+
...(reference.referenceSolver
|
|
119
|
+
? {
|
|
120
|
+
referenceSolver: { ...reference.referenceSolver },
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function copyExternalBenchmarkQuantityRequirement(requirement) {
|
|
126
|
+
return {
|
|
127
|
+
...requirement,
|
|
128
|
+
requiredReferenceSourceTypes: [...new Set(requirement.requiredReferenceSourceTypes)],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export function buildFemExternalBenchmarkAcceptanceContract(options = {}) {
|
|
132
|
+
const references = (options.references ?? []).map(copyExternalBenchmarkReference);
|
|
133
|
+
const requiredQuantities = (options.requiredQuantities ?? DEFAULT_EXTERNAL_BENCHMARK_REQUIRED_QUANTITIES)
|
|
134
|
+
.map(copyExternalBenchmarkQuantityRequirement);
|
|
135
|
+
const blockerCodes = [];
|
|
136
|
+
if (references.length === 0) {
|
|
137
|
+
blockerCodes.push('external-benchmark-reference-corpus-missing');
|
|
138
|
+
}
|
|
139
|
+
for (const [index, reference] of references.entries()) {
|
|
140
|
+
const referenceCode = isNonEmptyString(reference.id)
|
|
141
|
+
? reference.id
|
|
142
|
+
: String(index);
|
|
143
|
+
if (!isNonEmptyString(reference.id)) {
|
|
144
|
+
blockerCodes.push(`external-benchmark.references.${index}.id-missing`);
|
|
145
|
+
}
|
|
146
|
+
if (!REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES.includes(reference.sourceType) &&
|
|
147
|
+
reference.sourceType !== 'open-source-solver') {
|
|
148
|
+
blockerCodes.push(`external-benchmark.references.${referenceCode}.source-type-invalid`);
|
|
149
|
+
}
|
|
150
|
+
if (!isNonEmptyString(reference.label)) {
|
|
151
|
+
blockerCodes.push(`external-benchmark.references.${referenceCode}.label-missing`);
|
|
152
|
+
}
|
|
153
|
+
if (!isNonEmptyString(reference.citation)) {
|
|
154
|
+
blockerCodes.push(`external-benchmark.references.${referenceCode}.citation-missing`);
|
|
155
|
+
}
|
|
156
|
+
if (reference.sourceType === 'published-source' && !hasPublishedCitation(reference.publishedSource)) {
|
|
157
|
+
blockerCodes.push(`external-benchmark.references.${referenceCode}.published-source-citation-missing`);
|
|
158
|
+
}
|
|
159
|
+
if ((reference.sourceType === 'commercial-solver' || reference.sourceType === 'open-source-solver') &&
|
|
160
|
+
!hasReferenceSolverCitation(reference.referenceSolver)) {
|
|
161
|
+
blockerCodes.push(`external-benchmark.references.${referenceCode}.reference-solver-citation-missing`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const hasPublishedReference = references.some((reference) => reference.sourceType === 'published-source' &&
|
|
165
|
+
isNonEmptyString(reference.citation) &&
|
|
166
|
+
hasPublishedCitation(reference.publishedSource));
|
|
167
|
+
const hasReferenceSolver = references.some((reference) => (reference.sourceType === 'commercial-solver' || reference.sourceType === 'open-source-solver') &&
|
|
168
|
+
isNonEmptyString(reference.citation) &&
|
|
169
|
+
hasReferenceSolverCitation(reference.referenceSolver));
|
|
170
|
+
if (!hasPublishedReference) {
|
|
171
|
+
blockerCodes.push('external-benchmark-published-source-citation-missing');
|
|
172
|
+
}
|
|
173
|
+
if (!hasReferenceSolver) {
|
|
174
|
+
blockerCodes.push('external-benchmark-reference-solver-citation-missing');
|
|
175
|
+
}
|
|
176
|
+
if (requiredQuantities.length === 0) {
|
|
177
|
+
blockerCodes.push('external-benchmark-required-quantities-missing');
|
|
178
|
+
}
|
|
179
|
+
for (const [index, requirement] of requiredQuantities.entries()) {
|
|
180
|
+
const quantityCode = isNonEmptyString(requirement.id)
|
|
181
|
+
? requirement.id
|
|
182
|
+
: String(index);
|
|
183
|
+
if (!isNonEmptyString(requirement.id)) {
|
|
184
|
+
blockerCodes.push(`external-benchmark.required-quantities.${index}.id-missing`);
|
|
185
|
+
}
|
|
186
|
+
if (!isNonEmptyString(requirement.quantity)) {
|
|
187
|
+
blockerCodes.push(`external-benchmark.required-quantities.${quantityCode}.quantity-missing`);
|
|
188
|
+
}
|
|
189
|
+
if (!isNonEmptyString(requirement.unit)) {
|
|
190
|
+
blockerCodes.push(`external-benchmark.required-quantities.${quantityCode}.unit-missing`);
|
|
191
|
+
}
|
|
192
|
+
if (!Number.isFinite(requirement.tolerance) || requirement.tolerance < 0) {
|
|
193
|
+
blockerCodes.push(`external-benchmark.required-quantities.${quantityCode}.tolerance-invalid`);
|
|
194
|
+
}
|
|
195
|
+
if (requirement.toleranceType !== 'absolute' &&
|
|
196
|
+
requirement.toleranceType !== 'relative' &&
|
|
197
|
+
requirement.toleranceType !== 'absolute-or-relative') {
|
|
198
|
+
blockerCodes.push(`external-benchmark.required-quantities.${quantityCode}.tolerance-type-invalid`);
|
|
199
|
+
}
|
|
200
|
+
if (!Array.isArray(requirement.requiredReferenceSourceTypes) ||
|
|
201
|
+
requirement.requiredReferenceSourceTypes.length === 0) {
|
|
202
|
+
blockerCodes.push(`external-benchmark.required-quantities.${quantityCode}.source-types-missing`);
|
|
203
|
+
}
|
|
204
|
+
else if (references.length > 0) {
|
|
205
|
+
const missingSourceTypes = requirement.requiredReferenceSourceTypes.filter((sourceType) => !references.some((reference) => reference.sourceType === sourceType));
|
|
206
|
+
if (missingSourceTypes.length > 0) {
|
|
207
|
+
blockerCodes.push(`external-benchmark.required-quantities.${quantityCode}.reference-source-types-missing`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const uniqueBlockerCodes = [...new Set(blockerCodes)];
|
|
212
|
+
const productionReadinessBlocked = uniqueBlockerCodes.length > 0;
|
|
213
|
+
return {
|
|
214
|
+
schemaVersion: 'fem-external-benchmark-acceptance-metadata.v1',
|
|
215
|
+
status: productionReadinessBlocked ? 'blocked' : 'metadata-ready',
|
|
216
|
+
productionReadinessBlocked,
|
|
217
|
+
requiredSourceTypes: [...REQUIRED_EXTERNAL_BENCHMARK_SOURCE_TYPES],
|
|
218
|
+
references,
|
|
219
|
+
requiredQuantities,
|
|
220
|
+
blockerCodes: uniqueBlockerCodes,
|
|
221
|
+
acceptanceStatement: productionReadinessBlocked
|
|
222
|
+
? 'External benchmark metadata is incomplete; production readiness remains blocked until published source citations, reference-solver citations, and required quantity tolerances are registered.'
|
|
223
|
+
: 'External benchmark metadata is ready for independent result comparison; this does not approve production FEM design use.',
|
|
224
|
+
};
|
|
225
|
+
}
|
|
28
226
|
export function evaluateFemTolerance(quantity, actual, expected, absoluteTolerance, options = {}) {
|
|
29
227
|
const error = Math.abs(actual - expected);
|
|
30
228
|
const relativeError = Math.abs(expected) > 0 ? error / Math.abs(expected) : error;
|
|
@@ -494,13 +692,32 @@ export function runExcavationSupportDesignCheck(input) {
|
|
|
494
692
|
if (!Number.isFinite(input.frictionAngleDeg) || input.frictionAngleDeg <= 0 || input.frictionAngleDeg >= 50) {
|
|
495
693
|
throw new Error('frictionAngleDeg must be finite and between 0 and 50 degrees.');
|
|
496
694
|
}
|
|
695
|
+
if (!Array.isArray(input.supportLevelsM)) {
|
|
696
|
+
throw new Error('supportLevelsM must be an array of finite support depths.');
|
|
697
|
+
}
|
|
698
|
+
for (const [index, level] of input.supportLevelsM.entries()) {
|
|
699
|
+
if (!Number.isFinite(level) || level < 0 || level > input.excavationDepthM) {
|
|
700
|
+
throw new Error(`supportLevelsM.${index} must be finite and between 0 and excavationDepthM.`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const stageDepthsM = input.stageDepthsM ?? [input.excavationDepthM];
|
|
704
|
+
if (!Array.isArray(stageDepthsM) || stageDepthsM.length === 0) {
|
|
705
|
+
throw new Error('stageDepthsM must contain at least one staged excavation depth when provided.');
|
|
706
|
+
}
|
|
707
|
+
let previousStageDepthM = 0;
|
|
708
|
+
for (const [index, stageDepthM] of stageDepthsM.entries()) {
|
|
709
|
+
if (!Number.isFinite(stageDepthM) || stageDepthM <= previousStageDepthM || stageDepthM > input.excavationDepthM) {
|
|
710
|
+
throw new Error(`stageDepthsM.${index} must be finite, increasing, and no deeper than excavationDepthM.`);
|
|
711
|
+
}
|
|
712
|
+
previousStageDepthM = stageDepthM;
|
|
713
|
+
}
|
|
497
714
|
const policy = input.policy ?? DEFAULT_FEM_CONVERGENCE_POLICY;
|
|
498
715
|
const surchargeKpa = input.surchargeKpa ?? 0;
|
|
499
716
|
const waterTableDepthM = input.waterTableDepthM ?? 999;
|
|
500
|
-
const
|
|
501
|
-
wallHeight:
|
|
717
|
+
const activeForceForDepth = (depthM) => calculateLateralEarthPressure({
|
|
718
|
+
wallHeight: depthM,
|
|
502
719
|
soilLayers: [{
|
|
503
|
-
thickness:
|
|
720
|
+
thickness: depthM,
|
|
504
721
|
unitWeight: input.unitWeightKnM3,
|
|
505
722
|
cohesion: input.cohesionKpa,
|
|
506
723
|
frictionAngle: input.frictionAngleDeg,
|
|
@@ -512,7 +729,8 @@ export function runExcavationSupportDesignCheck(input) {
|
|
|
512
729
|
wallInclination: 0,
|
|
513
730
|
waterTableDepth: waterTableDepthM,
|
|
514
731
|
surcharge: surchargeKpa,
|
|
515
|
-
});
|
|
732
|
+
}).totalForce;
|
|
733
|
+
const activeTotalForce = activeForceForDepth(input.excavationDepthM);
|
|
516
734
|
const embedmentM = input.wallToeDepthM - input.excavationDepthM;
|
|
517
735
|
const passive = calculateLateralEarthPressure({
|
|
518
736
|
wallHeight: embedmentM,
|
|
@@ -530,19 +748,49 @@ export function runExcavationSupportDesignCheck(input) {
|
|
|
530
748
|
waterTableDepth: 999,
|
|
531
749
|
surcharge: 0,
|
|
532
750
|
});
|
|
533
|
-
const
|
|
751
|
+
const supportLevelsM = [...new Set(input.supportLevelsM)].sort((a, b) => a - b);
|
|
752
|
+
const supportCount = supportLevelsM.length;
|
|
534
753
|
const supportDemandKnPerM = supportCount > 0
|
|
535
|
-
?
|
|
536
|
-
:
|
|
754
|
+
? activeTotalForce / supportCount
|
|
755
|
+
: activeTotalForce;
|
|
537
756
|
const supportCapacitySafetyFactor = supportCount > 0
|
|
538
757
|
? input.allowableSupportLoadKnPerM / Math.max(supportDemandKnPerM, 1e-9)
|
|
539
758
|
: 0;
|
|
540
|
-
const passiveSafetyFactor = passive.totalForce / Math.max(
|
|
759
|
+
const passiveSafetyFactor = passive.totalForce / Math.max(activeTotalForce, 1e-9);
|
|
541
760
|
const basalHeaveSafetyFactor = input.cohesionKpa > 0
|
|
542
761
|
? (5.14 * input.cohesionKpa) / Math.max(input.unitWeightKnM3 * input.excavationDepthM + surchargeKpa, 1e-9)
|
|
543
762
|
: 0;
|
|
544
763
|
const requiredPassiveSafetyFactor = input.requiredPassiveSafetyFactor ?? 1.5;
|
|
545
764
|
const requiredBasalHeaveSafetyFactor = input.requiredBasalHeaveSafetyFactor ?? 1.5;
|
|
765
|
+
const stagedSequenceCoversFinalDepth = Math.abs(stageDepthsM[stageDepthsM.length - 1] - input.excavationDepthM) <= 1e-9;
|
|
766
|
+
const stageChecks = stageDepthsM.map((stageDepthM, index) => {
|
|
767
|
+
const installedSupportLevelsM = supportLevelsM.filter((level) => level <= stageDepthM);
|
|
768
|
+
const activeEarthPressureKnPerM = activeForceForDepth(stageDepthM);
|
|
769
|
+
const supportReactionDemandKnPerM = installedSupportLevelsM.length > 0
|
|
770
|
+
? activeEarthPressureKnPerM / installedSupportLevelsM.length
|
|
771
|
+
: activeEarthPressureKnPerM;
|
|
772
|
+
const stageSupportCapacitySafetyFactor = installedSupportLevelsM.length > 0
|
|
773
|
+
? input.allowableSupportLoadKnPerM / Math.max(supportReactionDemandKnPerM, 1e-9)
|
|
774
|
+
: 0;
|
|
775
|
+
const blockerCodes = [];
|
|
776
|
+
if (installedSupportLevelsM.length === 0)
|
|
777
|
+
blockerCodes.push('support-level-missing');
|
|
778
|
+
if (stageSupportCapacitySafetyFactor < 1)
|
|
779
|
+
blockerCodes.push('support-reaction-demand-exceeds-allowable');
|
|
780
|
+
if (index === stageDepthsM.length - 1 && !stagedSequenceCoversFinalDepth) {
|
|
781
|
+
blockerCodes.push('final-stage-depth-mismatch');
|
|
782
|
+
}
|
|
783
|
+
return {
|
|
784
|
+
id: `stage-${index + 1}-support-reaction`,
|
|
785
|
+
stageDepthM: round(stageDepthM, 4),
|
|
786
|
+
installedSupportLevelsM: installedSupportLevelsM.map((level) => round(level, 4)),
|
|
787
|
+
activeEarthPressureKnPerM: round(activeEarthPressureKnPerM, 4),
|
|
788
|
+
supportReactionDemandKnPerM: round(supportReactionDemandKnPerM, 4),
|
|
789
|
+
supportCapacitySafetyFactor: round(stageSupportCapacitySafetyFactor, 4),
|
|
790
|
+
blockerCodes,
|
|
791
|
+
status: blockerCodes.length === 0 ? 'accepted' : 'blocked',
|
|
792
|
+
};
|
|
793
|
+
});
|
|
546
794
|
const checks = [
|
|
547
795
|
{
|
|
548
796
|
id: 'support-capacity',
|
|
@@ -562,17 +810,38 @@ export function runExcavationSupportDesignCheck(input) {
|
|
|
562
810
|
required: requiredBasalHeaveSafetyFactor,
|
|
563
811
|
status: basalHeaveSafetyFactor >= requiredBasalHeaveSafetyFactor ? 'accepted' : 'blocked',
|
|
564
812
|
},
|
|
813
|
+
{
|
|
814
|
+
id: 'staged-support-reaction-sequence',
|
|
815
|
+
actual: stageChecks.every((check) => check.status === 'accepted') && stagedSequenceCoversFinalDepth ? 1 : 0,
|
|
816
|
+
required: 1,
|
|
817
|
+
status: stageChecks.every((check) => check.status === 'accepted') && stagedSequenceCoversFinalDepth
|
|
818
|
+
? 'accepted'
|
|
819
|
+
: 'blocked',
|
|
820
|
+
},
|
|
821
|
+
];
|
|
822
|
+
const acceptanceBlockers = [
|
|
823
|
+
...checks
|
|
824
|
+
.filter((check) => check.status === 'blocked')
|
|
825
|
+
.map((check) => `support-design.${check.id}`),
|
|
826
|
+
...stageChecks.flatMap((check) => check.blockerCodes.map((code) => `${check.id}.${code}`)),
|
|
565
827
|
];
|
|
566
828
|
return {
|
|
567
829
|
schemaVersion: 'fem-excavation-support-design-check.v1',
|
|
568
830
|
method: 'rankine-earth-pressure-support-screening',
|
|
569
|
-
|
|
831
|
+
designScope: 'screening-only-not-structural-design',
|
|
832
|
+
activeEarthPressureKnPerM: round(activeTotalForce, 4),
|
|
570
833
|
passiveToeResistanceKnPerM: round(passive.totalForce, 4),
|
|
571
834
|
supportDemandKnPerM: round(supportDemandKnPerM, 4),
|
|
572
835
|
supportCapacitySafetyFactor: round(supportCapacitySafetyFactor, 4),
|
|
573
836
|
passiveSafetyFactor: round(passiveSafetyFactor, 4),
|
|
574
837
|
basalHeaveSafetyFactor: round(basalHeaveSafetyFactor, 4),
|
|
838
|
+
stageChecks,
|
|
575
839
|
checks,
|
|
840
|
+
acceptanceBlockers: [...new Set(acceptanceBlockers)],
|
|
841
|
+
productionBlockers: [
|
|
842
|
+
'jurisdiction-specific-wall-strut-anchor-structural-design-not-implemented',
|
|
843
|
+
'screening-support-reaction-check-is-not-a-design-code-acceptance',
|
|
844
|
+
],
|
|
576
845
|
status: checks.every((check) => check.status === 'accepted') ? 'accepted' : 'blocked',
|
|
577
846
|
policy,
|
|
578
847
|
};
|
|
@@ -606,8 +875,12 @@ export function validateFemReviewerApprovalRecord(record) {
|
|
|
606
875
|
blockers.push('reviewer.license-id.missing');
|
|
607
876
|
if (!record.reviewer?.jurisdiction || record.reviewer.jurisdiction.trim().length < 2)
|
|
608
877
|
blockers.push('reviewer.jurisdiction.missing');
|
|
609
|
-
if (record.scope !== 'experimental-preview' && record.scope !== 'production-design')
|
|
878
|
+
if (record.scope !== 'experimental-preview' && record.scope !== 'production-design') {
|
|
610
879
|
blockers.push('scope.unsupported');
|
|
880
|
+
}
|
|
881
|
+
else if (record.scope === 'production-design') {
|
|
882
|
+
blockers.push('scope.production-design-blocked');
|
|
883
|
+
}
|
|
611
884
|
if (!Array.isArray(record.assumptions) || record.assumptions.length === 0)
|
|
612
885
|
blockers.push('assumptions.missing');
|
|
613
886
|
if (!Array.isArray(record.limitations) || record.limitations.length === 0)
|
|
@@ -622,9 +895,6 @@ export function validateFemReviewerApprovalRecord(record) {
|
|
|
622
895
|
else if (approvedAt > Date.now() + 60_000) {
|
|
623
896
|
blockers.push('approved-at.future');
|
|
624
897
|
}
|
|
625
|
-
if (record.scope === 'production-design') {
|
|
626
|
-
warnings.push('production-design-scope-requires-solver-and-jurisdiction-policy-enforcement');
|
|
627
|
-
}
|
|
628
898
|
return {
|
|
629
899
|
schemaVersion: 'fem-reviewer-approval-validation.v1',
|
|
630
900
|
status: blockers.length === 0 ? 'accepted' : 'blocked',
|
|
@@ -978,7 +1248,7 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
|
|
|
978
1248
|
...biotPatchTopNodes.map((node) => ({ nodeId: node.id, porePressureKpa: 0 })),
|
|
979
1249
|
],
|
|
980
1250
|
initialPorePressureKpa: 100,
|
|
981
|
-
timeStepsSeconds: [1_000],
|
|
1251
|
+
timeStepsSeconds: [1_000, 2_000, 3_000],
|
|
982
1252
|
policy,
|
|
983
1253
|
});
|
|
984
1254
|
const biotPatchGauss = biotPressurePatch.elements[0].gaussPoints[0];
|
|
@@ -1019,7 +1289,7 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
|
|
|
1019
1289
|
],
|
|
1020
1290
|
nodalLoads: biotAlphaZeroLoads,
|
|
1021
1291
|
initialPorePressureKpa: 100,
|
|
1022
|
-
timeStepsSeconds: [3_600, 7_200],
|
|
1292
|
+
timeStepsSeconds: [3_600, 7_200, 14_400],
|
|
1023
1293
|
policy,
|
|
1024
1294
|
});
|
|
1025
1295
|
const alphaZeroDisplacementError = Math.max(...drainedAlphaZero.nodes.map((drainedNode) => {
|
|
@@ -1091,6 +1361,7 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
|
|
|
1091
1361
|
cohesionKpa: 45,
|
|
1092
1362
|
surchargeKpa: 10,
|
|
1093
1363
|
waterTableDepthM: 99,
|
|
1364
|
+
stageDepthsM: [3, 6, 8],
|
|
1094
1365
|
supportLevelsM: [1, 4],
|
|
1095
1366
|
allowableSupportLoadKnPerM: 300,
|
|
1096
1367
|
requiredPassiveSafetyFactor: 1.5,
|
|
@@ -1098,6 +1369,11 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
|
|
|
1098
1369
|
policy,
|
|
1099
1370
|
});
|
|
1100
1371
|
benchmarks.push(benchmark('excavation-support-capacity-screening', 'support-design', 'internal-balance', 'supportStatusAccepted', support.status === 'accepted' ? 1 : 0, 1, 0, 'Support screening must pass support capacity, passive toe resistance, and basal-heave checks for the controlled fixture.'));
|
|
1372
|
+
benchmarks.push(benchmark('excavation-support-staged-reaction-sequence', 'support-design', 'internal-balance', 'stageSupportChecksAccepted', support.stageChecks.every((check) => check.status === 'accepted') &&
|
|
1373
|
+
support.acceptanceBlockers.length === 0 &&
|
|
1374
|
+
support.productionBlockers.includes('jurisdiction-specific-wall-strut-anchor-structural-design-not-implemented')
|
|
1375
|
+
? 1
|
|
1376
|
+
: 0, 1, 0, 'Support screening must tie support reaction demand to staged excavation depths while preserving jurisdiction-specific structural design as a production blocker.'));
|
|
1101
1377
|
const reviewerValidation = validateFemReviewerApprovalRecord({
|
|
1102
1378
|
schemaVersion: 'fem-reviewer-approval.v1',
|
|
1103
1379
|
recordId: 'review-fem-001',
|
|
@@ -1125,12 +1401,14 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
|
|
|
1125
1401
|
.filter((item) => item.status === 'accepted')
|
|
1126
1402
|
.map((item) => item.feature))];
|
|
1127
1403
|
const status = benchmarks.every((item) => item.status === 'accepted') ? 'kernel-verified' : 'blocked';
|
|
1404
|
+
const externalBenchmarkAcceptance = buildFemExternalBenchmarkAcceptanceContract();
|
|
1128
1405
|
return {
|
|
1129
1406
|
schemaVersion: 'fem-engineering-evidence.v1',
|
|
1130
1407
|
status,
|
|
1131
1408
|
productionReady: false,
|
|
1132
1409
|
verifiedFeatures,
|
|
1133
1410
|
benchmarks,
|
|
1411
|
+
externalBenchmarkAcceptance,
|
|
1134
1412
|
convergencePolicy: policy,
|
|
1135
1413
|
remainingProductionBlockers: [
|
|
1136
1414
|
'production-sparse-fem-solver-and-2d-3d-result-route-not-integrated-with-these-kernels',
|
|
@@ -1138,6 +1416,8 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
|
|
|
1138
1416
|
'biot-u-p-route-backed-preview-is-not-production-sparse-solver',
|
|
1139
1417
|
'support-design-is-screening-level-and-not-jurisdiction-specific-structural-design',
|
|
1140
1418
|
'published-commercial-cross-solver-benchmark-corpus-not-approved',
|
|
1419
|
+
...externalBenchmarkAcceptance.blockerCodes,
|
|
1420
|
+
'production-design-approval-scope-fails-closed-until-production-acceptance',
|
|
1141
1421
|
'reviewer-approval-record-validator-exists-but-cli-run-does-not-enforce-persistence-for-every-run',
|
|
1142
1422
|
],
|
|
1143
1423
|
releasePositioning: 'These kernels provide deterministic engineering evidence for strong-beta gating. They do not make geotechCLI a production nonlinear FEM solver until solver integration, external benchmarks, and enforced approval workflows are complete.',
|