@geotechcli/core 0.4.94 → 0.4.96

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.
Files changed (40) hide show
  1. package/dist/fem/demo.d.ts +2 -0
  2. package/dist/fem/demo.d.ts.map +1 -1
  3. package/dist/fem/demo.js +517 -0
  4. package/dist/fem/demo.js.map +1 -1
  5. package/dist/fem/engineering-evidence.d.ts +60 -0
  6. package/dist/fem/engineering-evidence.d.ts.map +1 -1
  7. package/dist/fem/engineering-evidence.js +189 -1
  8. package/dist/fem/engineering-evidence.js.map +1 -1
  9. package/dist/fem/ground-model-draft.d.ts.map +1 -1
  10. package/dist/fem/ground-model-draft.js +27 -0
  11. package/dist/fem/ground-model-draft.js.map +1 -1
  12. package/dist/fem/index.d.ts +3 -2
  13. package/dist/fem/index.d.ts.map +1 -1
  14. package/dist/fem/index.js +3 -2
  15. package/dist/fem/index.js.map +1 -1
  16. package/dist/fem/nonlinear-column-solver.d.ts +6 -0
  17. package/dist/fem/nonlinear-column-solver.d.ts.map +1 -0
  18. package/dist/fem/nonlinear-column-solver.js +231 -0
  19. package/dist/fem/nonlinear-column-solver.js.map +1 -0
  20. package/dist/fem/production-readiness.js +5 -5
  21. package/dist/fem/production-readiness.js.map +1 -1
  22. package/dist/fem/routing.d.ts +12 -0
  23. package/dist/fem/routing.d.ts.map +1 -1
  24. package/dist/fem/routing.js +149 -12
  25. package/dist/fem/routing.js.map +1 -1
  26. package/dist/fem/types.d.ts +37 -6
  27. package/dist/fem/types.d.ts.map +1 -1
  28. package/dist/fem/validation.d.ts.map +1 -1
  29. package/dist/fem/validation.js +142 -10
  30. package/dist/fem/validation.js.map +1 -1
  31. package/dist/fem/webgl.js +1 -1
  32. package/dist/ingest/document-evidence-packet.d.ts +16 -16
  33. package/dist/ingest/geotech-document-benchmark.d.ts +1 -0
  34. package/dist/ingest/geotech-document-benchmark.d.ts.map +1 -1
  35. package/dist/ingest/geotech-document-benchmark.js +1 -0
  36. package/dist/ingest/geotech-document-benchmark.js.map +1 -1
  37. package/dist/meta/metadata.json +1 -1
  38. package/dist/verifier/findings.js +18 -9
  39. package/dist/verifier/findings.js.map +1 -1
  40. package/package.json +1 -1
@@ -2,7 +2,9 @@ import type { FemAnalysisCase, FemResultManifest } from './types.js';
2
2
  export declare function buildRaftDemoAnalysisCase(now?: Date): FemAnalysisCase;
3
3
  export declare function buildExcavationDemoAnalysisCase(now?: Date): FemAnalysisCase;
4
4
  export declare function buildTunnelVolumeLossDemoAnalysisCase(now?: Date): FemAnalysisCase;
5
+ export declare function buildStagedSettlementConsolidationDemoAnalysisCase(now?: Date): FemAnalysisCase;
5
6
  export declare function runBuiltinElasticRaftDemo(caseFile?: FemAnalysisCase): FemResultManifest;
6
7
  export declare function runBuiltinTunnelVolumeLossDemo(caseFile?: FemAnalysisCase): FemResultManifest;
8
+ export declare function runBuiltinStagedSettlementConsolidationDemo(caseFile?: FemAnalysisCase): FemResultManifest;
7
9
  export declare function runBuiltinElasticExcavationDemo(caseFile?: FemAnalysisCase): FemResultManifest;
8
10
  //# sourceMappingURL=demo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/fem/demo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAIf,iBAAiB,EAIlB,MAAM,YAAY,CAAC;AAuDpB,wBAAgB,yBAAyB,CAAC,GAAG,OAAuC,GAAG,eAAe,CAuHrG;AAED,wBAAgB,+BAA+B,CAAC,GAAG,OAAuC,GAAG,eAAe,CAuI3G;AAED,wBAAgB,qCAAqC,CAAC,GAAG,OAAuC,GAAG,eAAe,CA+GjH;AAyaD,wBAAgB,yBAAyB,CAAC,QAAQ,kBAA8B,GAAG,iBAAiB,CAkEnG;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,kBAA0C,GACjD,iBAAiB,CAiEnB;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,kBAAoC,GAC3C,iBAAiB,CA4EnB"}
1
+ {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/fem/demo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAIf,iBAAiB,EAIlB,MAAM,YAAY,CAAC;AA2DpB,wBAAgB,yBAAyB,CAAC,GAAG,OAAuC,GAAG,eAAe,CAuHrG;AAED,wBAAgB,+BAA+B,CAAC,GAAG,OAAuC,GAAG,eAAe,CAuI3G;AAED,wBAAgB,qCAAqC,CAAC,GAAG,OAAuC,GAAG,eAAe,CA+GjH;AAED,wBAAgB,kDAAkD,CAChE,GAAG,OAAuC,GACzC,eAAe,CA4KjB;AA2pBD,wBAAgB,yBAAyB,CAAC,QAAQ,kBAA8B,GAAG,iBAAiB,CAkEnG;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,kBAA0C,GACjD,iBAAiB,CAiEnB;AAED,wBAAgB,2CAA2C,CACzD,QAAQ,kBAAuD,GAC9D,iBAAiB,CAuJnB;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,kBAAoC,GAC3C,iBAAiB,CA4EnB"}
package/dist/fem/demo.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { runMohrCoulombMaterialPoint, runTerzaghiConsolidationTimeStepper, } from './engineering-evidence.js';
1
2
  import { validateFemAnalysisCase } from './validation.js';
2
3
  const DEFAULT_UNITS = {
3
4
  length: 'm',
@@ -412,6 +413,178 @@ export function buildTunnelVolumeLossDemoAnalysisCase(now = new Date('2026-05-18
412
413
  ],
413
414
  };
414
415
  }
416
+ export function buildStagedSettlementConsolidationDemoAnalysisCase(now = new Date('2026-06-03T00:00:00.000Z')) {
417
+ const assumptions = [
418
+ {
419
+ id: 'one-dimensional-consolidation',
420
+ parameter: 'consolidation model',
421
+ value: '1D staged Terzaghi consolidation with Mohr-Coulomb material-point screening',
422
+ basis: 'Production-candidate deterministic slice for staged settlement routing, not a full 2D/3D coupled FEM solver.',
423
+ confidence: 'review',
424
+ reviewRequired: true,
425
+ },
426
+ {
427
+ id: 'drainage-boundary',
428
+ parameter: 'drainage',
429
+ value: 'double drainage',
430
+ basis: 'Representative preliminary assumption; project drainage boundaries must be confirmed from ground model and construction details.',
431
+ confidence: 'review',
432
+ reviewRequired: true,
433
+ },
434
+ {
435
+ id: 'mc-strength-screening',
436
+ parameter: 'strength model',
437
+ value: 'Mohr-Coulomb triaxial material-point cap',
438
+ basis: 'Mobilized strength ratio and plastic settlement increment are used as review gates, not as a production plastic zone calculation.',
439
+ confidence: 'review',
440
+ reviewRequired: true,
441
+ },
442
+ {
443
+ id: 'groundwater-saturated-column',
444
+ parameter: 'groundwater',
445
+ value: 'saturated column assumed for consolidation time-rate preview',
446
+ basis: 'Terzaghi consolidation requires saturated low-permeability soil and reviewed drainage assumptions.',
447
+ confidence: 'review',
448
+ reviewRequired: true,
449
+ },
450
+ ];
451
+ return {
452
+ schemaVersion: 'fem-analysis-case.v0',
453
+ caseId: 'staged-settlement-consolidation-demo',
454
+ title: 'Experimental 1D staged settlement consolidation preview',
455
+ createdBy: 'geotechcli-fem-demo',
456
+ createdAt: now.toISOString(),
457
+ experimental: true,
458
+ objective: 'staged_settlement_consolidation',
459
+ analysisType: 'time_dependent_1d_consolidation',
460
+ units: DEFAULT_UNITS,
461
+ geometry: {
462
+ domain: {
463
+ type: 'box',
464
+ lengthM: 24,
465
+ widthM: 12,
466
+ depthM: 12,
467
+ },
468
+ consolidation: {
469
+ type: 'soil_column',
470
+ layerThicknessM: 10,
471
+ surfaceAreaM2: 200,
472
+ drainage: 'double',
473
+ stages: [
474
+ { id: 'stage-1', label: 'Stage 1 - preload fill', loadKpa: 45, durationYears: 0.5 },
475
+ { id: 'stage-2', label: 'Stage 2 - embankment raise', loadKpa: 35, durationYears: 1 },
476
+ { id: 'stage-3', label: 'Stage 3 - service surcharge hold', loadKpa: 20, durationYears: 2 },
477
+ ],
478
+ },
479
+ },
480
+ materials: [
481
+ {
482
+ id: 'soft-clay-1d',
483
+ name: 'Representative saturated clay consolidation layer',
484
+ model: 'mohr_coulomb',
485
+ elasticModulusKpa: 30_000,
486
+ poissonRatio: 0.32,
487
+ unitWeightKnM3: 18.5,
488
+ constrainedModulusKpa: 8_000,
489
+ frictionAngleDeg: 28,
490
+ cohesionKpa: 12,
491
+ coefficientOfConsolidationM2PerYear: 0.8,
492
+ hydraulicConductivityMPerS: 1e-9,
493
+ evidenceRefs: [],
494
+ assumptions,
495
+ },
496
+ ],
497
+ loads: [
498
+ {
499
+ id: 'stage-1-load',
500
+ type: 'uniform_pressure',
501
+ target: 'ground_surface',
502
+ pressureKpa: 45,
503
+ evidenceRefs: [],
504
+ assumptions: [
505
+ {
506
+ id: 'stage-1-load-assumption',
507
+ parameter: 'stage 1 surface pressure',
508
+ value: 45,
509
+ unit: 'kPa',
510
+ basis: 'Representative preload fill pressure for the experimental consolidation demo.',
511
+ confidence: 'review',
512
+ reviewRequired: true,
513
+ },
514
+ ],
515
+ },
516
+ {
517
+ id: 'stage-2-load',
518
+ type: 'uniform_pressure',
519
+ target: 'ground_surface',
520
+ pressureKpa: 35,
521
+ evidenceRefs: [],
522
+ assumptions: [
523
+ {
524
+ id: 'stage-2-load-assumption',
525
+ parameter: 'stage 2 surface pressure',
526
+ value: 35,
527
+ unit: 'kPa',
528
+ basis: 'Representative embankment raise pressure for the experimental consolidation demo.',
529
+ confidence: 'review',
530
+ reviewRequired: true,
531
+ },
532
+ ],
533
+ },
534
+ {
535
+ id: 'stage-3-load',
536
+ type: 'uniform_pressure',
537
+ target: 'ground_surface',
538
+ pressureKpa: 20,
539
+ evidenceRefs: [],
540
+ assumptions: [
541
+ {
542
+ id: 'stage-3-load-assumption',
543
+ parameter: 'stage 3 surface pressure',
544
+ value: 20,
545
+ unit: 'kPa',
546
+ basis: 'Representative service surcharge hold pressure for the experimental consolidation demo.',
547
+ confidence: 'review',
548
+ reviewRequired: true,
549
+ },
550
+ ],
551
+ },
552
+ ],
553
+ boundaryConditions: [
554
+ {
555
+ id: 'base-fixed',
556
+ type: 'fixed_base',
557
+ description: 'Column base fixed in vertical displacement for 1D settlement preview.',
558
+ },
559
+ {
560
+ id: 'side-rollers',
561
+ type: 'side_rollers',
562
+ description: 'Column sides constrained laterally to approximate one-dimensional strain.',
563
+ },
564
+ ],
565
+ mesh: {
566
+ elementType: 'hex8',
567
+ divisionsX: 12,
568
+ divisionsY: 4,
569
+ divisionsZ: 10,
570
+ },
571
+ groundwater: {
572
+ condition: 'specified',
573
+ depthM: 0,
574
+ note: 'Saturated drainage condition assumed for 1D consolidation; pore-pressure coupling is limited to the staged Terzaghi column preview.',
575
+ reviewRequired: true,
576
+ },
577
+ assumptions,
578
+ evidenceRefs: [],
579
+ limitations: [
580
+ 'Experimental 1D staged consolidation preview only; not a production nonlinear geotechnical FEM solver.',
581
+ 'Uses Terzaghi average consolidation time stepping with reviewed drainage assumptions.',
582
+ 'Mohr-Coulomb behavior is a material-point strength cap and plastic-settlement review gate; no 2D/3D plastic zone is solved.',
583
+ 'No 2D/3D seepage field, embankment geometry, creep, secondary compression, monitoring calibration, or support/structure interaction is modelled.',
584
+ 'Independent published/commercial solver benchmarks are still required before production design use.',
585
+ ],
586
+ };
587
+ }
415
588
  function buildVisualizationMesh(caseFile, maxSettlementMm) {
416
589
  const { domain, raft } = caseFile.geometry;
417
590
  if (!raft) {
@@ -659,6 +832,100 @@ function buildTunnelVisualizationMesh(caseFile, maxSettlementMm) {
659
832
  outlineIdx: [0, 1, 1, 2, 2, 3, 3, 0, 4, 5],
660
833
  };
661
834
  }
835
+ function buildConsolidationVisualizationMesh(caseFile, stageResults, finalSettlementMm) {
836
+ const { domain, consolidation } = caseFile.geometry;
837
+ if (!consolidation) {
838
+ throw new Error('Consolidation visualization mesh requires consolidation geometry.');
839
+ }
840
+ const nx = caseFile.mesh.divisionsX;
841
+ const ny = caseFile.mesh.divisionsY;
842
+ const base = [];
843
+ const tri = [];
844
+ const edge = [];
845
+ const totalTimeYears = Math.max(stageResults[stageResults.length - 1]?.cumulativeTimeYears ?? 1, 1e-6);
846
+ const halfWidth = domain.widthM / 2;
847
+ const idx = (ix, iy) => iy * (nx + 1) + ix;
848
+ for (let iy = 0; iy <= ny; iy += 1) {
849
+ const y = -halfWidth + (domain.widthM * iy) / ny;
850
+ for (let ix = 0; ix <= nx; ix += 1) {
851
+ const x = -domain.lengthM / 2 + (domain.lengthM * ix) / nx;
852
+ base.push(round(x), round(y), 0);
853
+ }
854
+ }
855
+ for (let iy = 0; iy < ny; iy += 1) {
856
+ for (let ix = 0; ix < nx; ix += 1) {
857
+ const a = idx(ix, iy);
858
+ const b = idx(ix + 1, iy);
859
+ const c = idx(ix + 1, iy + 1);
860
+ const d = idx(ix, iy + 1);
861
+ tri.push(a, b, c, a, c, d);
862
+ }
863
+ }
864
+ for (let iy = 0; iy <= ny; iy += 1) {
865
+ for (let ix = 0; ix < nx; ix += 1) {
866
+ edge.push(idx(ix, iy), idx(ix + 1, iy));
867
+ }
868
+ }
869
+ for (let ix = 0; ix <= nx; ix += 1) {
870
+ for (let iy = 0; iy < ny; iy += 1) {
871
+ edge.push(idx(ix, iy), idx(ix, iy + 1));
872
+ }
873
+ }
874
+ function settlementAtTime(timeYears, activeStage) {
875
+ let previous;
876
+ for (const stage of stageResults) {
877
+ if (stage.cumulativeTimeYears <= timeYears) {
878
+ previous = stage;
879
+ }
880
+ }
881
+ if (previous)
882
+ return previous.settlementMm;
883
+ const activeTimeRatio = Math.max(0, Math.min(1, timeYears / Math.max(activeStage.cumulativeTimeYears, 1e-6)));
884
+ return activeStage.settlementMm * activeTimeRatio;
885
+ }
886
+ function buildFrame(stageResult) {
887
+ const disp = [];
888
+ const color = [];
889
+ for (let index = 0; index < base.length; index += 3) {
890
+ const x = base[index];
891
+ const y = base[index + 1];
892
+ const timeRatio = (x + domain.lengthM / 2) / domain.lengthM;
893
+ const timeYears = totalTimeYears * Math.max(0, Math.min(1, timeRatio));
894
+ const crossSectionTaper = 0.94 + 0.06 * Math.cos((Math.PI * y) / Math.max(halfWidth, 1e-6));
895
+ const settlementMm = Math.min(stageResult.settlementMm, settlementAtTime(timeYears, stageResult)) * crossSectionTaper;
896
+ const normalized = settlementMm / Math.max(finalSettlementMm, 1e-6);
897
+ disp.push(0, 0, round(-settlementMm / 1000, 6));
898
+ color.push(...settlementColor(normalized));
899
+ }
900
+ return {
901
+ field: 'vertical_settlement',
902
+ fieldLabel: 'Vertical settlement',
903
+ stageIndex: stageResult.stageIndex,
904
+ stageLabel: stageResult.stageLabel,
905
+ disp,
906
+ color,
907
+ };
908
+ }
909
+ const frames = stageResults.map(buildFrame);
910
+ const primary = frames[frames.length - 1];
911
+ const z = 0.05;
912
+ return {
913
+ base,
914
+ disp: primary.disp,
915
+ color: primary.color,
916
+ tri,
917
+ edge,
918
+ outlineBase: [
919
+ -domain.lengthM / 2, -halfWidth, z,
920
+ domain.lengthM / 2, -halfWidth, z,
921
+ domain.lengthM / 2, halfWidth, z,
922
+ -domain.lengthM / 2, halfWidth, z,
923
+ ],
924
+ outlineDisp: new Array(12).fill(0),
925
+ outlineIdx: [0, 1, 1, 2, 2, 3, 3, 0],
926
+ frames,
927
+ };
928
+ }
662
929
  function buildRaftResultFields() {
663
930
  return [
664
931
  {
@@ -672,6 +939,83 @@ function buildRaftResultFields() {
672
939
  },
673
940
  ];
674
941
  }
942
+ function buildConsolidationResultFields() {
943
+ return [
944
+ {
945
+ id: 'vertical_settlement',
946
+ label: 'Vertical settlement',
947
+ unit: 'mm',
948
+ location: 'surface_nodes',
949
+ quantity: 'displacement',
950
+ component: 'z',
951
+ signConvention: 'Positive values represent downward settlement magnitude in the staged consolidation viewer.',
952
+ },
953
+ {
954
+ id: 'final_settlement',
955
+ label: 'Final settlement',
956
+ unit: 'mm',
957
+ location: 'envelope',
958
+ quantity: 'displacement',
959
+ signConvention: 'Final downward surface settlement at the end of the staged consolidation preview.',
960
+ },
961
+ {
962
+ id: 'plastic_settlement',
963
+ label: 'Plastic settlement increment',
964
+ unit: 'mm',
965
+ location: 'envelope',
966
+ quantity: 'displacement',
967
+ signConvention: 'Material-point plastic settlement increment from the Mohr-Coulomb review gate.',
968
+ },
969
+ {
970
+ id: 'final_degree_of_consolidation',
971
+ label: 'Final degree of consolidation',
972
+ unit: 'ratio',
973
+ location: 'envelope',
974
+ quantity: 'degree_of_consolidation',
975
+ signConvention: 'Average degree of consolidation at the final staged time.',
976
+ },
977
+ {
978
+ id: 'max_excess_pore_pressure',
979
+ label: 'Maximum excess pore pressure',
980
+ unit: 'kPa',
981
+ location: 'envelope',
982
+ quantity: 'pore_pressure',
983
+ signConvention: 'Maximum average excess pore pressure carried by a staged load increment.',
984
+ },
985
+ {
986
+ id: 'max_mobilized_strength_ratio',
987
+ label: 'Maximum mobilized strength ratio',
988
+ unit: 'ratio',
989
+ location: 'envelope',
990
+ quantity: 'strength_ratio',
991
+ signConvention: 'Maximum Mohr-Coulomb material-point mobilized strength ratio across stages.',
992
+ },
993
+ {
994
+ id: 'stage_count',
995
+ label: 'Load stages',
996
+ unit: 'count',
997
+ location: 'envelope',
998
+ quantity: 'stage_count',
999
+ signConvention: 'Number of staged loading steps in the consolidation preview.',
1000
+ },
1001
+ {
1002
+ id: 'total_load',
1003
+ label: 'Total applied load',
1004
+ unit: 'kN',
1005
+ location: 'envelope',
1006
+ quantity: 'load',
1007
+ signConvention: 'Applied staged surface pressure multiplied by the reviewed tributary surface area.',
1008
+ },
1009
+ {
1010
+ id: 'reaction',
1011
+ label: 'Boundary reaction',
1012
+ unit: 'kN',
1013
+ location: 'envelope',
1014
+ quantity: 'reaction',
1015
+ signConvention: 'Deterministic preview reaction balancing the applied surface load.',
1016
+ },
1017
+ ];
1018
+ }
675
1019
  function buildRaftResultSteps() {
676
1020
  return [
677
1021
  {
@@ -681,6 +1025,13 @@ function buildRaftResultSteps() {
681
1025
  },
682
1026
  ];
683
1027
  }
1028
+ function buildConsolidationResultSteps(stageResults) {
1029
+ return stageResults.map((stageResult) => ({
1030
+ id: stageResult.stageId,
1031
+ label: stageResult.stageLabel,
1032
+ index: stageResult.stageIndex,
1033
+ }));
1034
+ }
684
1035
  function buildRaftResultDatasets(visualization) {
685
1036
  return [
686
1037
  {
@@ -693,6 +1044,28 @@ function buildRaftResultDatasets(visualization) {
693
1044
  },
694
1045
  ];
695
1046
  }
1047
+ function buildConsolidationResultDatasets(visualization, steps, envelope) {
1048
+ const stepByIndex = new Map(steps.map((step) => [step.index, step.id]));
1049
+ const frameDatasets = (visualization.frames ?? []).map((frame) => ({
1050
+ id: `vertical_settlement-${stepByIndex.get(frame.stageIndex ?? 0) ?? 'final'}`,
1051
+ fieldId: 'vertical_settlement',
1052
+ stepId: stepByIndex.get(frame.stageIndex ?? 0),
1053
+ values: frame.disp,
1054
+ stride: 3,
1055
+ source: 'visualization.frame',
1056
+ }));
1057
+ return [
1058
+ ...frameDatasets,
1059
+ { id: 'final_settlement-final', fieldId: 'final_settlement', values: [envelope.finalSettlementMm], stride: 1, source: 'envelope' },
1060
+ { id: 'plastic_settlement-final', fieldId: 'plastic_settlement', values: [envelope.plasticSettlementMm], stride: 1, source: 'envelope' },
1061
+ { id: 'final_degree_of_consolidation-final', fieldId: 'final_degree_of_consolidation', values: [envelope.finalDegreeOfConsolidation], stride: 1, source: 'envelope' },
1062
+ { id: 'max_excess_pore_pressure-final', fieldId: 'max_excess_pore_pressure', values: [envelope.maxExcessPorePressureKpa], stride: 1, source: 'envelope' },
1063
+ { id: 'max_mobilized_strength_ratio-final', fieldId: 'max_mobilized_strength_ratio', values: [envelope.maxMobilizedStrengthRatio], stride: 1, source: 'envelope' },
1064
+ { id: 'stage_count-final', fieldId: 'stage_count', values: [envelope.stageCount], stride: 1, source: 'envelope' },
1065
+ { id: 'total_load-final', fieldId: 'total_load', values: [envelope.totalLoadKn], stride: 1, source: 'envelope' },
1066
+ { id: 'reaction-final', fieldId: 'reaction', values: [envelope.reactionKn], stride: 1, source: 'envelope' },
1067
+ ];
1068
+ }
696
1069
  function buildExcavationResultFields() {
697
1070
  return [
698
1071
  {
@@ -925,6 +1298,150 @@ export function runBuiltinTunnelVolumeLossDemo(caseFile = buildTunnelVolumeLossD
925
1298
  limitations: caseFile.limitations,
926
1299
  };
927
1300
  }
1301
+ export function runBuiltinStagedSettlementConsolidationDemo(caseFile = buildStagedSettlementConsolidationDemoAnalysisCase()) {
1302
+ const validation = validateFemAnalysisCase(caseFile);
1303
+ const consolidation = caseFile.geometry.consolidation;
1304
+ const material = caseFile.materials[0];
1305
+ if (!consolidation || !material) {
1306
+ throw new Error('Staged consolidation demo requires consolidation geometry and one material.');
1307
+ }
1308
+ if (validation.status === 'blocked') {
1309
+ throw new Error(`FEM case is blocked: ${validation.findings.map((item) => item.message).join('; ')}`);
1310
+ }
1311
+ const constrainedModulusKpa = material.constrainedModulusKpa ?? material.elasticModulusKpa;
1312
+ const coefficientOfConsolidationM2PerYear = material.coefficientOfConsolidationM2PerYear ?? 0.5;
1313
+ const frictionAngleDeg = material.frictionAngleDeg ?? 30;
1314
+ const cohesionKpa = material.cohesionKpa ?? 0;
1315
+ const k0 = Math.max(0.25, Math.min(0.75, 1 - Math.sin((frictionAngleDeg * Math.PI) / 180)));
1316
+ const initialVerticalEffectiveStressKpa = Math.max(25, material.unitWeightKnM3 * consolidation.layerThicknessM * 0.5);
1317
+ const stageResults = [];
1318
+ let cumulativeLoadKpa = 0;
1319
+ let cumulativeTimeYears = 0;
1320
+ let cumulativeElasticSettlementMm = 0;
1321
+ let cumulativePlasticSettlementMm = 0;
1322
+ let maxReferenceError = 0;
1323
+ for (const [index, stage] of consolidation.stages.entries()) {
1324
+ cumulativeLoadKpa += stage.loadKpa;
1325
+ cumulativeTimeYears += stage.durationYears;
1326
+ const primarySettlementMm = (stage.loadKpa / constrainedModulusKpa) * consolidation.layerThicknessM * 1000;
1327
+ const timeStepsYears = [0.2, 0.4, 0.6, 0.8, 1].map((ratio) => round(stage.durationYears * ratio, 8));
1328
+ const consolidationResult = runTerzaghiConsolidationTimeStepper({
1329
+ layerThicknessM: consolidation.layerThicknessM,
1330
+ drainage: consolidation.drainage,
1331
+ coefficientOfConsolidationM2PerYear,
1332
+ initialExcessPorePressureKpa: stage.loadKpa,
1333
+ primarySettlementMm,
1334
+ timeStepsYears,
1335
+ nodeCount: Math.max(21, caseFile.mesh.divisionsZ * 8 + 1),
1336
+ });
1337
+ maxReferenceError = Math.max(maxReferenceError, consolidationResult.maxReferenceError);
1338
+ cumulativeElasticSettlementMm += consolidationResult.finalStep.settlementMm;
1339
+ const materialPoint = runMohrCoulombMaterialPoint({
1340
+ confiningEffectiveStressKpa: Math.max(10, k0 * (initialVerticalEffectiveStressKpa + cumulativeLoadKpa * 0.5)),
1341
+ axialStrain: cumulativeLoadKpa / constrainedModulusKpa,
1342
+ elasticModulusKpa: material.elasticModulusKpa,
1343
+ poissonRatio: material.poissonRatio,
1344
+ frictionAngleDeg,
1345
+ cohesionKpa,
1346
+ increments: 24,
1347
+ });
1348
+ const plasticSettlementAtStageMm = materialPoint.finalStep.plasticAxialStrain * consolidation.layerThicknessM * 1000 * 0.35;
1349
+ cumulativePlasticSettlementMm = Math.max(cumulativePlasticSettlementMm, plasticSettlementAtStageMm);
1350
+ const settlementMm = cumulativeElasticSettlementMm + cumulativePlasticSettlementMm;
1351
+ stageResults.push({
1352
+ stageId: stage.id,
1353
+ stageLabel: stage.label,
1354
+ stageIndex: index,
1355
+ cumulativeTimeYears: round(cumulativeTimeYears, 6),
1356
+ stageLoadKpa: round(stage.loadKpa, 4),
1357
+ cumulativeLoadKpa: round(cumulativeLoadKpa, 4),
1358
+ elasticSettlementMm: round(cumulativeElasticSettlementMm, 4),
1359
+ plasticSettlementMm: round(cumulativePlasticSettlementMm, 4),
1360
+ settlementMm: round(settlementMm, 4),
1361
+ degreeOfConsolidation: consolidationResult.finalStep.degreeOfConsolidation,
1362
+ averageExcessPorePressureKpa: consolidationResult.finalStep.averageExcessPorePressureKpa,
1363
+ mobilizedStrengthRatio: materialPoint.finalStep.mobilizedStrengthRatio,
1364
+ state: materialPoint.finalStep.state,
1365
+ maxReferenceError: consolidationResult.maxReferenceError,
1366
+ });
1367
+ }
1368
+ const finalStage = stageResults[stageResults.length - 1];
1369
+ const finalSettlementMm = finalStage.settlementMm;
1370
+ const totalLoadKn = consolidation.stages.reduce((total, stage) => total + stage.loadKpa * consolidation.surfaceAreaM2, 0);
1371
+ const maxExcessPorePressureKpa = Math.max(...stageResults.map((stage) => stage.stageLoadKpa));
1372
+ const maxMobilizedStrengthRatio = Math.max(...stageResults.map((stage) => stage.mobilizedStrengthRatio));
1373
+ const visualization = buildConsolidationVisualizationMesh(caseFile, stageResults, finalSettlementMm);
1374
+ const steps = buildConsolidationResultSteps(stageResults);
1375
+ const drainagePathM = consolidation.drainage === 'double'
1376
+ ? consolidation.layerThicknessM / 2
1377
+ : consolidation.layerThicknessM;
1378
+ const envelope = {
1379
+ maxSettlementMm: round(finalSettlementMm, 3),
1380
+ minSettlementMm: 0,
1381
+ totalLoadKn: round(totalLoadKn, 3),
1382
+ reactionKn: round(totalLoadKn, 3),
1383
+ reactionBalanceRatio: 1,
1384
+ stageCount: consolidation.stages.length,
1385
+ finalSettlementMm: round(finalSettlementMm, 3),
1386
+ plasticSettlementMm: round(cumulativePlasticSettlementMm, 3),
1387
+ finalDegreeOfConsolidation: round(finalStage.degreeOfConsolidation, 6),
1388
+ maxExcessPorePressureKpa: round(maxExcessPorePressureKpa, 4),
1389
+ maxMobilizedStrengthRatio: round(maxMobilizedStrengthRatio, 6),
1390
+ drainagePathM: round(drainagePathM, 6),
1391
+ consolidationDurationYears: round(cumulativeTimeYears, 6),
1392
+ };
1393
+ return {
1394
+ schemaVersion: 'fem-result-manifest.v0',
1395
+ caseId: caseFile.caseId,
1396
+ title: caseFile.title,
1397
+ generatedAt: new Date().toISOString(),
1398
+ backend: {
1399
+ id: 'builtin-staged-consolidation-1d',
1400
+ label: 'Built-in experimental staged 1D consolidation preview',
1401
+ deterministic: true,
1402
+ version: '0.1.0',
1403
+ },
1404
+ analysisCase: caseFile,
1405
+ validation,
1406
+ mesh: {
1407
+ nodes: (caseFile.mesh.divisionsX + 1) * (caseFile.mesh.divisionsY + 1) * (caseFile.mesh.divisionsZ + 1),
1408
+ elements: caseFile.mesh.divisionsX * caseFile.mesh.divisionsY * caseFile.mesh.divisionsZ,
1409
+ elementType: caseFile.mesh.elementType,
1410
+ divisions: [caseFile.mesh.divisionsX, caseFile.mesh.divisionsY, caseFile.mesh.divisionsZ],
1411
+ visualizationNodes: visualization.base.length / 3,
1412
+ visualizationTriangles: visualization.tri.length / 3,
1413
+ visualizationEdges: visualization.edge.length / 2,
1414
+ },
1415
+ envelope,
1416
+ visualization,
1417
+ resultFields: buildConsolidationResultFields(),
1418
+ steps,
1419
+ datasets: buildConsolidationResultDatasets(visualization, steps, {
1420
+ finalSettlementMm: envelope.finalSettlementMm,
1421
+ plasticSettlementMm: envelope.plasticSettlementMm,
1422
+ finalDegreeOfConsolidation: envelope.finalDegreeOfConsolidation,
1423
+ maxExcessPorePressureKpa: envelope.maxExcessPorePressureKpa,
1424
+ maxMobilizedStrengthRatio: envelope.maxMobilizedStrengthRatio,
1425
+ stageCount: envelope.stageCount,
1426
+ totalLoadKn: envelope.totalLoadKn,
1427
+ reactionKn: envelope.reactionKn,
1428
+ }),
1429
+ assumptions: [
1430
+ ...caseFile.assumptions,
1431
+ ...caseFile.loads.flatMap((item) => item.assumptions),
1432
+ {
1433
+ id: 'consolidation-reference-error',
1434
+ parameter: 'maximum Terzaghi reference error',
1435
+ value: round(maxReferenceError, 8),
1436
+ unit: 'ratio',
1437
+ basis: 'Backward-Euler 1D consolidation stepper compared with analytical average consolidation for each stage.',
1438
+ confidence: 'measured',
1439
+ reviewRequired: true,
1440
+ },
1441
+ ],
1442
+ limitations: caseFile.limitations,
1443
+ };
1444
+ }
928
1445
  export function runBuiltinElasticExcavationDemo(caseFile = buildExcavationDemoAnalysisCase()) {
929
1446
  const validation = validateFemAnalysisCase(caseFile);
930
1447
  const excavation = caseFile.geometry.excavation;