@geotechcli/core 0.4.119 → 0.4.121

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.
@@ -114,6 +114,21 @@ const DEFAULT_EXTERNAL_BENCHMARK_REFERENCES = [
114
114
  retrievedAt: '2026-06-04',
115
115
  },
116
116
  },
117
+ {
118
+ id: 'opengeosys-consolidation-staggered-benchmark',
119
+ sourceType: 'open-source-solver',
120
+ label: 'OpenGeoSys staggered hydro-mechanics consolidation benchmark',
121
+ citation: 'OpenGeoSys Documentation, Consolidation benchmark with the staggered scheme, hydro-mechanics benchmark with analytical pressure/displacement solution, 1000 Pa top load, t = 10 s, and dt = 0.5 s comparison.',
122
+ referenceSolver: {
123
+ name: 'OpenGeoSys',
124
+ version: 'stable documentation',
125
+ vendor: 'OpenGeoSys project',
126
+ analysisProcedure: 'HYDRO_MECHANICS staggered fixed-stress consolidation benchmark',
127
+ elementType: '2D hydro-mechanics finite elements',
128
+ url: 'https://www.opengeosys.org/docs/benchmarks/hydro-mechanics/consolidationbenchmark/',
129
+ retrievedAt: '2026-06-04',
130
+ },
131
+ },
117
132
  {
118
133
  id: 'opengeosys-hydro-mechanics-benchmarks',
119
134
  sourceType: 'open-source-solver',
@@ -692,7 +707,7 @@ function externalBenchmarkFinalAccepted(input) {
692
707
  ? { unit: input.unit }
693
708
  : { relativeTolerance: input.tolerance, unit: input.unit }).accepted;
694
709
  }
695
- function buildDefaultPublishedExternalBenchmarkComparisonResults(input) {
710
+ function buildDefaultExternalBenchmarkComparisonResults(input) {
696
711
  const consolidationRequirement = DEFAULT_EXTERNAL_BENCHMARK_REQUIRED_QUANTITIES
697
712
  .find((requirement) => requirement.id === 'consolidation-settlement-time-curve');
698
713
  const biotRequirement = DEFAULT_EXTERNAL_BENCHMARK_REQUIRED_QUANTITIES
@@ -790,6 +805,69 @@ function buildDefaultPublishedExternalBenchmarkComparisonResults(input) {
790
805
  unit: biotRequirement.unit,
791
806
  quantity: biotRequirement.quantity,
792
807
  }) && seriesSummarySatisfiesRequirementTolerance(biotSeries, biotRequirement);
808
+ const ogsPressureByY = new Map();
809
+ for (const node of input.openGeoSysConsolidation.nodes) {
810
+ const key = round(node.yM, 10);
811
+ const values = ogsPressureByY.get(key) ?? [];
812
+ values.push(node.porePressureKpa);
813
+ ogsPressureByY.set(key, values);
814
+ }
815
+ const ogsPoints = [...ogsPressureByY.entries()]
816
+ .map(([yM, pressures]) => {
817
+ const depthRatioFromTop = 1 - yM;
818
+ const actual = pressures.reduce((sum, value) => sum + value, 0) / pressures.length;
819
+ const expected = input.openGeoSysConsolidationLoadKpa *
820
+ openGeoSysConsolidationPressureRatio(depthRatioFromTop, input.openGeoSysConsolidationDimensionlessTime);
821
+ return {
822
+ x: depthRatioFromTop,
823
+ actual,
824
+ expected,
825
+ };
826
+ })
827
+ .sort((left, right) => right.x - left.x);
828
+ const ogsSeries = buildFemExternalBenchmarkSeriesSummary({
829
+ xQuantity: 'dimensionless depth from drained top',
830
+ xUnit: 'x/H',
831
+ yQuantity: 'load-generated excess pore pressure',
832
+ yUnit: 'kPa',
833
+ points: ogsPoints,
834
+ notes: [
835
+ 'Benchmark-scale Quad4 Biot u-p load-generated pressure profile at t = 10 s compared with the OpenGeoSys staggered consolidation analytical p_D profile.',
836
+ 'OpenGeoSys source parameters are E = 3e4 Pa, nu = 0.2, k = 1e-10 m2, viscosity = 1e-3 Pa s, sigma0 = 1000 Pa, dt = 0.5 s.',
837
+ ],
838
+ });
839
+ const ogsActual = ogsSeries.actual.mean ?? ogsSeries.actual.final;
840
+ const ogsExpected = ogsSeries.expected.mean ?? ogsSeries.expected.final;
841
+ const ogsEvidencePayload = {
842
+ schemaVersion: 'fem-external-benchmark-evidence.v1',
843
+ caseId: 'opengeosys-staggered-consolidation-pressure-profile-t10',
844
+ sourceId: 'opengeosys-consolidation-staggered-benchmark',
845
+ method: input.openGeoSysConsolidation.method,
846
+ pressureEnvelopeMode: input.openGeoSysConsolidation.numericalContract.pressureEnvelopeMode,
847
+ pressureOvershootPolicy: input.openGeoSysConsolidation.numericalContract.pressureOvershootPolicy,
848
+ timeStepCount: input.openGeoSysConsolidation.timeSteps.length,
849
+ finalTimeSeconds: input.openGeoSysConsolidation.timeSteps.at(-1)?.timeSeconds,
850
+ dimensionlessTime: input.openGeoSysConsolidationDimensionlessTime,
851
+ transientAcceptance: input.openGeoSysConsolidation.transientAcceptance,
852
+ seriesHashSha256: ogsSeries.seriesHashSha256,
853
+ };
854
+ const ogsResultPayload = {
855
+ actual: ogsActual,
856
+ expected: ogsExpected,
857
+ maxAbsoluteError: ogsSeries.maxAbsoluteError,
858
+ maxRelativeError: ogsSeries.maxRelativeError,
859
+ resultSeriesHashSha256: ogsSeries.seriesHashSha256,
860
+ };
861
+ const ogsAccepted = input.openGeoSysConsolidation.transientAcceptance.accepted &&
862
+ externalBenchmarkFinalAccepted({
863
+ actual: ogsActual,
864
+ expected: ogsExpected,
865
+ tolerance: biotRequirement.tolerance,
866
+ toleranceType: biotRequirement.toleranceType,
867
+ unit: biotRequirement.unit,
868
+ quantity: biotRequirement.quantity,
869
+ }) &&
870
+ seriesSummarySatisfiesRequirementTolerance(ogsSeries, biotRequirement);
793
871
  return [
794
872
  {
795
873
  id: 'published-terzaghi-1d-consolidation-tv-0-197',
@@ -849,6 +927,45 @@ function buildDefaultPublishedExternalBenchmarkComparisonResults(input) {
849
927
  'Generated published-source comparison record for an alpha-zero Biot pressure-diffusion specialization; commercial solver comparison remains missing.',
850
928
  ],
851
929
  },
930
+ {
931
+ id: 'opengeosys-consolidation-staggered-biot-pressure-profile-t10',
932
+ quantityRequirementId: biotRequirement.id,
933
+ referenceId: 'opengeosys-consolidation-staggered-benchmark',
934
+ caseId: 'opengeosys-staggered-consolidation-pressure-profile-t10',
935
+ comparisonKind: 'series-summary',
936
+ metricName: 'loadGeneratedExcessPorePressureProfileAtT10s',
937
+ quantity: biotRequirement.quantity,
938
+ unit: biotRequirement.unit,
939
+ actual: round(ogsActual, 10),
940
+ expected: round(ogsExpected, 10),
941
+ tolerance: biotRequirement.tolerance,
942
+ toleranceType: biotRequirement.toleranceType,
943
+ accepted: ogsAccepted,
944
+ candidateSolver: {
945
+ name: 'geotechCLI FEM evidence suite',
946
+ version: 'strong-beta',
947
+ solverType: 'geotechcli-kernel',
948
+ analysisProcedure: 'linear-elastic Quad4 Biot u-p load-generated consolidation pressure profile',
949
+ elementType: 'Quad4 plane-strain u-p evidence mesh',
950
+ runId: 'opengeosys-staggered-consolidation-pressure-profile-t10',
951
+ },
952
+ referenceSolver: {
953
+ name: 'OpenGeoSys',
954
+ version: 'stable documentation',
955
+ vendor: 'OpenGeoSys project',
956
+ solverType: 'open-source-solver',
957
+ analysisProcedure: 'HYDRO_MECHANICS staggered fixed-stress consolidation benchmark',
958
+ elementType: '2D hydro-mechanics finite elements',
959
+ runId: 'HydroMechanics/StaggeredScheme/ConsolidationBenchmark/consolidation_benchmark.prj',
960
+ },
961
+ evidenceHashSha256: hashFemBenchmarkPayload(ogsEvidencePayload),
962
+ resultHashSha256: hashFemBenchmarkPayload(ogsResultPayload),
963
+ seriesSummary: ogsSeries,
964
+ notes: [
965
+ 'Generated open-source solver comparison record against the OpenGeoSys staggered consolidation benchmark analytical pressure profile.',
966
+ 'This adds open-source cross-solver evidence but does not satisfy the required commercial-solver production benchmark gate.',
967
+ ],
968
+ },
852
969
  ];
853
970
  }
854
971
  export function evaluateFemTolerance(quantity, actual, expected, absoluteTolerance, options = {}) {
@@ -1115,6 +1232,21 @@ export function terzaghiAverageConsolidation(timeFactor, terms = 80) {
1115
1232
  const degree = 1 - (8 / (Math.PI * Math.PI)) * remaining;
1116
1233
  return Math.min(1, Math.max(0, degree));
1117
1234
  }
1235
+ function openGeoSysConsolidationPressureRatio(depthRatioFromTop, dimensionlessTime, terms = 120) {
1236
+ if (!Number.isFinite(depthRatioFromTop) || depthRatioFromTop < -1e-12 || depthRatioFromTop > 1 + 1e-12) {
1237
+ throw new Error('depthRatioFromTop must be finite and within [0, 1].');
1238
+ }
1239
+ if (!Number.isFinite(dimensionlessTime) || dimensionlessTime < 0) {
1240
+ throw new Error('dimensionlessTime must be finite and non-negative.');
1241
+ }
1242
+ const xD = Math.min(1, Math.max(0, depthRatioFromTop));
1243
+ let pressureRatio = 0;
1244
+ for (let n = 0; n < terms; n++) {
1245
+ const m = 0.5 * Math.PI * (2 * n + 1);
1246
+ pressureRatio += (2 / m) * Math.sin(m * xD) * Math.exp(-(m * m * dimensionlessTime));
1247
+ }
1248
+ return Math.max(0, pressureRatio);
1249
+ }
1118
1250
  function solveTridiagonal(lower, diagonal, upper, rhs) {
1119
1251
  const n = diagonal.length;
1120
1252
  const cPrime = new Array(n).fill(0);
@@ -2035,6 +2167,59 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
2035
2167
  biotLoadGenerated.massBalanceErrorRatio <= policy.porePressureMassBalanceTolerance
2036
2168
  ? 1
2037
2169
  : 0, 1, 0, 'Mechanically loaded Biot consolidation must require explicit load-generated pressure mode and audit positive excess pore-pressure generation without weakening the default envelope guard.'));
2170
+ const openGeoSysConsolidationMesh = buildPlaneStrainRectangularMesh({
2171
+ widthM: 1,
2172
+ heightM: 1,
2173
+ divisionsX: 1,
2174
+ divisionsY: 16,
2175
+ materialId: 'soil',
2176
+ });
2177
+ const openGeoSysConsolidationBottomNodes = openGeoSysConsolidationMesh.nodes.filter((node) => node.yM === 0);
2178
+ const openGeoSysConsolidationTopNodes = openGeoSysConsolidationMesh.nodes.filter((node) => node.yM === 1);
2179
+ const openGeoSysConsolidationLeftNodes = openGeoSysConsolidationMesh.nodes.filter((node) => node.xM === 0);
2180
+ const openGeoSysConsolidationRightNodes = openGeoSysConsolidationMesh.nodes.filter((node) => node.xM === 1);
2181
+ const openGeoSysConsolidationYoungModulusKpa = 30;
2182
+ const openGeoSysConsolidationPoissonRatio = 0.2;
2183
+ const openGeoSysConsolidationHydraulicConductivityMPerS = 9.81e-4;
2184
+ const openGeoSysConsolidationLoadKpa = 1;
2185
+ const openGeoSysConsolidationFinalTimeSeconds = 10;
2186
+ const openGeoSysConsolidationLambdaPlus2MuKpa = openGeoSysConsolidationYoungModulusKpa *
2187
+ (1 - openGeoSysConsolidationPoissonRatio) /
2188
+ ((1 + openGeoSysConsolidationPoissonRatio) * (1 - 2 * openGeoSysConsolidationPoissonRatio));
2189
+ const openGeoSysConsolidationDimensionlessTime = openGeoSysConsolidationLambdaPlus2MuKpa *
2190
+ (openGeoSysConsolidationHydraulicConductivityMPerS / 9.81) *
2191
+ openGeoSysConsolidationFinalTimeSeconds;
2192
+ const openGeoSysConsolidation = runPlaneStrainBiotConsolidation({
2193
+ schemaVersion: 'fem-plane-strain-biot-consolidation-model.v1',
2194
+ nodes: openGeoSysConsolidationMesh.nodes,
2195
+ elements: openGeoSysConsolidationMesh.elements,
2196
+ materials: [{
2197
+ id: 'soil',
2198
+ elasticModulusKpa: openGeoSysConsolidationYoungModulusKpa,
2199
+ poissonRatio: openGeoSysConsolidationPoissonRatio,
2200
+ hydraulicConductivityXMPerS: openGeoSysConsolidationHydraulicConductivityMPerS,
2201
+ hydraulicConductivityYMPerS: openGeoSysConsolidationHydraulicConductivityMPerS,
2202
+ biotCoefficient: 1,
2203
+ specificStorage1PerM: 1e-9,
2204
+ }],
2205
+ boundaryConditions: [
2206
+ ...openGeoSysConsolidationBottomNodes.map((node) => ({ nodeId: node.id, dof: 'uy' })),
2207
+ ...openGeoSysConsolidationLeftNodes.map((node) => ({ nodeId: node.id, dof: 'ux' })),
2208
+ ...openGeoSysConsolidationRightNodes.map((node) => ({ nodeId: node.id, dof: 'ux' })),
2209
+ ],
2210
+ porePressureBoundaryConditions: openGeoSysConsolidationTopNodes.map((node) => ({
2211
+ nodeId: node.id,
2212
+ porePressureKpa: 0,
2213
+ })),
2214
+ nodalLoads: openGeoSysConsolidationTopNodes.map((node) => ({
2215
+ nodeId: node.id,
2216
+ fyKn: -openGeoSysConsolidationLoadKpa / openGeoSysConsolidationTopNodes.length,
2217
+ })),
2218
+ initialPorePressureKpa: 0,
2219
+ pressureEnvelopeMode: 'load-generated-positive-pressure',
2220
+ timeStepsSeconds: Array.from({ length: 20 }, (_, index) => (index + 1) * 0.5),
2221
+ policy,
2222
+ });
2038
2223
  const biotPatchMesh = buildPlaneStrainRectangularMesh({
2039
2224
  widthM: 2,
2040
2225
  heightM: 1,
@@ -2338,12 +2523,15 @@ export function runFemEngineeringEvidenceSuite(policy = DEFAULT_FEM_CONVERGENCE_
2338
2523
  .map((item) => item.feature))];
2339
2524
  const status = benchmarks.every((item) => item.status === 'accepted') ? 'kernel-verified' : 'blocked';
2340
2525
  const externalBenchmarkAcceptance = buildFemExternalBenchmarkAcceptanceContract({
2341
- comparisonResults: buildDefaultPublishedExternalBenchmarkComparisonResults({
2526
+ comparisonResults: buildDefaultExternalBenchmarkComparisonResults({
2342
2527
  consolidation,
2343
2528
  biotTerzaghi,
2344
2529
  biotTerzaghiInitialPressureKpa,
2345
2530
  biotTerzaghiHydraulicConductivityMPerS,
2346
2531
  biotTerzaghiSpecificStorage1PerM,
2532
+ openGeoSysConsolidation,
2533
+ openGeoSysConsolidationDimensionlessTime,
2534
+ openGeoSysConsolidationLoadKpa,
2347
2535
  }),
2348
2536
  });
2349
2537
  return {