# 아키텍처

## 목표
FESA의 아키텍처 목표는 Abaqus `.inp` subset을 내부 semantic model로 변환하고, 유한요소 equation system을 구성해 구조해석 결과를 HDF5로 저장하며, reference comparison과 physics sanity가 가능한 C++17/MSVC 솔버 구조를 제공하는 것이다.

FESA의 element formulation과 numerical path는 Abaqus와 독립적이다. Abaqus `.inp`는
승인된 입력 형식이고 Abaqus CSV는 기능별 blocking quantity의 외부 수치 reference다.
Abaqus 내부 적분, stabilization, state 또는 recovery 동작은 FESA architecture contract가
아니다.

핵심 품질 속성:
- FEM formulation traceability
- explicit I/O contracts
- sparse linear algebra backend isolation
- deterministic verification
- incremental feature addition
- Harness 기반 TDD

현재 승인된 V0 end-to-end 기능은 `linear-static-3d-euler-beam`이다. 상세 계약의
source of truth는
`docs/superpowers/specs/2026-08-08-linear-static-3d-euler-beam-design.md`이며, 이 문서는
그 계약을 전체 solver architecture의 모듈과 데이터 흐름에 배치한다. 아래에 나타난
비선형, 동적, thermal 및 다른 element 계층은 장기 확장 경계이며 V0 지원 범위가 아니다.

## 현재 구현된 디렉토리 구조
```text
include/fesa/               # backend-neutral public C++ API
src/
  fesa/
    analysis/             # Analysis lifecycle, V0 LinearStaticAnalysis
    app/                  # CLI application and main
    assembly/             # deterministic stiffness/load assembly, ParallelFor adapter
    constraints/          # essential-constraint elimination and reconstruction
    core/                 # source identity, status, diagnostics
    elements/             # V0 EulerBeam3D kernel and recovery
    fem/                  # DOF/equation numbering and sparse pattern
    io/
      abaqus/             # .inp syntax reader and semantic Domain mapper
      hdf5/               # private HDF5 writer and atomic finalization
    math/                 # owning Vector, row-major Matrix, 0-based CSR SparseMatrix
    model/                # concrete V0 semantic records and immutable Domain
    results/              # recovery records, full residual, ResultsWriter boundary
    solvers/
      linear/             # LinearSolver interface and MKL PARDISO adapter
tests/
  unit/                    # local contracts and failure paths
  integration/             # analysis orchestration and CLI contracts
  reference/               # HDF5-to-Abaqus projection and comparison
reference/
  <case-dir>/              # declared read-only Abaqus input/required CSV pair
.agents/
  skills/                 # Harness and review skills
.codex/
  hooks.json              # PreToolUse/Stop hook registration
  agents/                 # FESA workflow custom agents
  skills/                 # FESA solver workflow skills
docs/                     # Product, architecture, ADR, workflow artifacts
scripts/
  execute.py              # Phase step executor
  hooks/                   # PreToolUse/Stop hook implementations
  msvc_harness/            # MSVC project discovery and validation adapters
phases/                   # Optional generated phase plans
```

`materials/`, nonlinear/dynamic analysis, MPC/penalty policies, general element factories,
history output과 production validation module은 장기 확장 경계이지 현재 구현된 module이
아니다. 새 디렉토리와 추상 계층은 승인된 기능이 실제로 필요로 할 때 추가한다.

## Harness Execution Layer

Harness는 solver core와 분리된 세 계층의 개발 운영 인프라다.

- 계획 계층: `.agents/skills/harness`가 사용자 승인 전 Step 초안을 만들고, 승인 후 `phases/` 파일을 생성한다.
- 실행 계층: `scripts/execute.py`가 `feat-<phase-name>` 브랜치에서 Step마다 독립 Codex 세션을 실행하고 상태와 커밋을 관리한다.
- 검증 계층: `.codex/hooks.json`이 `scripts/hooks/pre_tool_use.py`와 `scripts/hooks/stop_validation.py`를 연결한다. Stop 검증은 `scripts/msvc_harness/`를 통해 MSVC build와 test를 실행한다.

Runner는 `git add -A`를 사용하므로 clean worktree 또는 별도 Git worktree가 실행 전제다. 전체 동작 계약은 `docs/HARNESS_WORKFLOW.md`, 설치와 `.harness/config.json` 설정은 `docs/HARNESS.md`를 source of truth로 삼는다.

## CMake target과 dependency graph

Root CMake project는 C++17, extension off, CMake 3.25 이상을 요구하고
`cmake/FesaDependencies.cmake`, `src/fesa`, `tests`를 차례로 구성한다.

```text
MKL CONFIG package  ─> Fesa::MKL  ─┐
TBB CONFIG package  ─> Fesa::TBB  ─┼─> fesa_solver (STATIC) ─> fesa_cli
HDF5 CONFIG package ─> Fesa::HDF5 ─┘             │
                                                  ├─> fesa_unit_tests
approved local GoogleTest source ─> GTest targets ├─> fesa_integration_tests
                                                  └─> fesa_reference_tests
```

- `FESA_GTEST_SOURCE_DIR`는 네트워크 fetch 대신 승인된 local GoogleTest source checkout을 가리키는 필수 cache path다.
- MKL, TBB, HDF5는 CONFIG package로 탐지한다. Package search가 설치를 찾지 못하면 `MKL_DIR`, `TBB_DIR`, `HDF5_DIR`를 configure 때 지정한다.
- Package별 imported target 이름은 `Fesa::MKL`, `Fesa::TBB`, `Fesa::HDF5`로 정규화한다. Product module은 vendor target 이름을 직접 선택하지 않는다.
- HDF5 package가 shared와 static C target을 모두 제공하면 approved Windows environment에서는 shared target을 우선한다. Static archive의 숨은 compiler-runtime 요구가 link interface 밖으로 새는 것을 피하기 위한 결정이다.
- `fesa_solver`는 외부 dependency를 `PRIVATE`으로 link하고 `/W4 /WX`를 사용한다. 따라서 public header는 MKL/TBB/HDF5/Win32 type을 포함하지 않아야 한다.
- Test executable은 unit, integration, reference 경계를 분리하며 `fesa_tests` target은 세 executable을 build하는 aggregate target이다. Reference target에만 source/build root compile definition을 제공한다.

### Windows runtime closure

Configure 성공은 executable이 GoogleTest discovery 또는 CLI 실행 시 필요한 DLL을 찾는다는
뜻이 아니다. `fesa_cli`와 세 test executable의 POST_BUILD 단계는 TBB, MKL thread/core/default
dispatch, OpenMP, `libmmd.dll`, HDF5를 포함한 imported-target runtime DLL을 executable 옆에
복사한다. 새 dynamic backend를 도입할 때는 link 성공뿐 아니라 clean environment에서의
post-build discovery/실행까지 runtime closure로 다뤄야 한다. 개인 설치 absolute path를
CMake source에 기록하지 말고 config package와 imported target metadata를 확장한다.

## 모듈 경계
- `core`는 외부 라이브러리에 의존하지 않는다.
- `io/abaqus`는 syntax와 semantic mapping만 담당하고 해석 알고리즘을 알지 않는다.
- `model`은 Abaqus keyword 문자열이 아니라 solver semantic model을 가진다.
- `fem`의 `DofManager`는 DOF, equation ordering, scatter와 sparse pattern을 소유한다.
- `elements`는 local/global stiffness, transformation, optional load kernel과 recovery를 제공한다. V0 material/section은 concrete Domain record다.
- `assembly`는 element-local contribution과 full nodal load를 stable full-DOF space에 조립한다.
- `constraints`는 V0 essential BC elimination과 full/reduced vector 변환을 담당한다. MPC와 penalty는 현재 범위가 아니다.
- `solvers`는 `LinearSolver` 뒤에 MKL PARDISO 세부 구현을 감춘다. TBB는 `assembly/ParallelFor`, HDF5는 `results/ResultsWriter` 경계 뒤에 각각 격리된다.
- `analysis`는 step/history data를 받아 procedure를 실행하고 solver backend와 result writer를 조율한다.
- `results`는 full residual과 beam rows를 복구하고 backend-neutral writer contract를 제공한다. HDF5 schema 구현은 `io/hdf5`가 담당한다.
- test helper는 production parser/solver 내부 상태를 우회하지 않는다.

## V0 입력 경계

V0 parser는 keyword와 parameter를 case-insensitive하게 해석하되 source label의 원문을
보존한다. 지원하는 model/procedure keyword는 다음과 같다.

- `*HEADING`
- `*PART`, `*END PART`
- `*NODE`
- `*ELEMENT, TYPE=B33`
- `*NSET`, `*ELSET`, including `GENERATE`
- `*MATERIAL`, `*ELASTIC`
- `*BEAM GENERAL SECTION, SECTION=GENERAL`
- `*SECTION POINTS`
- `*ASSEMBLY`, `*END ASSEMBLY`
- `*INSTANCE`, `*END INSTANCE`
- `*BOUNDARY`, `*CLOAD`
- `*STEP`, `*STATIC`, `*END STEP`

Part 내부 label은 `SourceEntityId { instance_name, source_label }`로 보존하고 Domain은
별도 stable internal index를 부여한다. 같은 part의 여러 identity instance는 허용하지만
translation/rotation data와 nested assembly는 구조화된 unsupported diagnostic으로
거부한다. 입력 파일당 하나의 static step만 허용하고 두 번째 step은 오류다.

`*PREPRINT`, `*RESTART`, `*TRANSVERSE SHEAR STIFFNESS`, `*OUTPUT, FIELD`,
`*OUTPUT, HISTORY`, `*NODE OUTPUT`, `*ELEMENT OUTPUT`, `*CONTACT OUTPUT` 및 그에 속한
미지원 output variable data는 warning 후 no-op 처리한다. 이 allowlist 밖의 미지원
model-affecting keyword는 오류다. FESA output은 Abaqus output request에 좌우되지 않는다.

## V0 beam과 section 계약

`EulerBeam3D`는 2절점과 절점당 `[UX, UY, UZ, URX, URY, URZ]` 6 DOF를 사용하며 축,
두 방향 Euler–Bernoulli 굽힘과 Saint-Venant 비틀림을 제공한다. Beam tangent를 local
`x`, Abaqus first section axis `n1`을 local `y`, `t x n1`을 local `z`로 둔다.
`*BEAM GENERAL SECTION`의 `A, I11, I12, I22, J`는 `Iy=I11`, `Iz=I22`로 매핑하고
V0는 `I12=0`만 허용한다. `E`, `G`, `A`, `Iy`, `Iz`, `J`는 양수여야 하며 zero-length
element와 tangent에 평행한 guide vector를 scale-aware tolerance로 거부한다.

요소 API는 stiffness, transformation, load와 recovery 책임을 분리한다.

```cpp
Matrix localStiffness() const;
Matrix globalStiffness() const;
Vector localEquivalentLoad(const ConstantLocalLineLoad& load) const;
BeamRecovery recover(const Vector& globalElementDisplacement) const;
```

`localEquivalentLoad`는 formulation의 constant local line-load kernel을 unit test하기
위한 계약이다. V0 parser는 `*DLOAD`나 distributed-load Domain object를 생성하지 않으므로
CLI pipeline에서는 이 kernel을 호출하지 않는다. Stiffness와 recovery는 formulation의
2점 Gauss rule과 부호 규약을 따른다.

## 핵심 객체 모델

```text
Domain
├── owns nodes, B33 elements, materials, beam sections, sets
├── owns boundary conditions, nodal loads, one static step
└── owns source path/identity and mapping warnings

AnalysisModel
├── non-owning view into Domain
├── stable active element/BC/load indices
└── reachable material/section indices

DofManager
├── owns node x [UX,UY,UZ,URX,URY,URZ] full-DOF numbering
├── owns stable free/constrained order and prescribed values
├── owns element scatter maps
└── owns full-space CSR structural pattern

AnalysisState
├── displacement U
├── external force Fext
├── internal force Fint
├── residual R and full-index reaction
├── StepFrameIdentity
└── endpoint, Gauss and S11 recovery rows

Analysis
└── LinearStaticAnalysis

Vector
Matrix
SparseMatrix
```

Nonlinear/static, dynamic, frequency, heat-transfer procedure와 general element/material/load
base hierarchy는 이 구조 위의 가능한 확장 방향일 뿐 현재 public API가 아니다. 사용 사례가
승인되기 전에 V0 concrete record를 speculative hierarchy로 감싸지 않는다.

## 상태 관리
- `Domain`은 입력 파일에서 만들어진 전체 모델 정의를 소유한다. 파싱 이후에는 가능한 한 불변으로 취급한다.
- `LinearStaticAnalysis`가 `Domain`을 소유하고, 그 뒤에 `AnalysisModel`, `DofManager`, `AnalysisState`, stiffness/RHS를 순서대로 만든다. 재사용 시에는 역순으로 해제하여 이전 Domain을 가리키는 view를 남기지 않는다.
- `AnalysisModel`은 현재 step에서 활성화되는 해석 객체들의 실행 view이다. `Domain`을 복사하지 않으므로 Domain이 반드시 더 오래 살아야 한다.
- `DofManager`는 자유도와 방정식 번호를 전담한다. `Node` 또는 `Element` 내부에 equation id를 분산 저장하지 않는다.
- `AnalysisState`는 V0 frame에 필요한 다섯 full-DOF vector와 recovery rows만 소유한다. Velocity, acceleration, temperature, iteration history, nonlinear element state는 해당 procedure가 승인될 때 별도 계약으로 추가한다.
- Result recovery는 모든 candidate vector/row를 검증한 뒤 state에 반영한다. 실패한 recovery가 앞선 유효 state를 부분적으로 덮어쓰지 않아야 한다.

## 데이터 흐름
```text
Abaqus input file
-> syntax parse and semantic mapping
-> immutable Domain 생성
-> 단일 step AnalysisModel view 생성
-> DofManager DOF/scatter map/sparse pattern 생성
-> element stiffness 계산과 deterministic COO-to-CSR 조립
-> free/constrained partition 생성
-> LinearSolver::factorize(Kff)
-> full nodal load vector 조립
-> effective RHS = Ff - Kfc * dc
-> LinearSolver::solve(rhs, df) substitution
-> full displacement 복구
-> full residual/reaction = K*d - F 및 element result 복구
-> ResultsWriter로 results.h5 atomic finalization
```

강성행렬 factorization은 하중벡터 조립보다 먼저 수행한다. 반력은 element end action의
별도 합이 아니라 조립된 전체 residual에서 구한다. Constrained component는 physical
reaction이고 free component는 equilibrium residual evidence로 full-index vector에 남긴다.

## 해석 실행 흐름
`Analysis::run()`은 Template Method로 다음 여덟 hook의 순서와 fail-fast 경계를 고정한다.

| 순서 | Hook | 주요 작업과 생성되는 소유 객체 | 순서/실패 불변식 |
| --- | --- | --- | --- |
| 1 | `initialize(request)` | `.inp` syntax read, semantic map, owned immutable `Domain`, sorted warnings | 이전 run의 dependent object를 역순으로 제거하고 parse/map 실패를 input category로 반환한다. |
| 2 | `buildAnalysisModel()` | non-owning `AnalysisModel` view | Domain을 복사하지 않으며 Domain lifetime 안에서만 사용한다. |
| 3 | `buildDofMapAndSparsePattern()` | `DofManager`, zero-initialized `AnalysisState` | Stable full/free/constrained numbering과 structural pattern을 한 소유자에게 둔다. |
| 4 | `assembleAndPartitionStiffness()` | full CSR K와 `Kff/Kfc/Kcf/Kcc` | Element-local buffer를 deterministic하게 reduce하고 structural zero와 stable order를 보존한다. |
| 5 | `factorize()` | retained `Kff` factorization | 모든 load assembly보다 먼저 호출한다. Valid fully constrained model의 `0 x 0 Kff`는 trivial success다. |
| 6 | `assembleLoadsAndEffectiveRhs()` | full F와 `Ff-Kfc*dc` | Semantic load source order와 finite sum을 보존하며 solver를 호출하지 않는다. |
| 7 | `substituteAndReconstruct()` | free solution과 full displacement | Factorization을 재수행하지 않고 substitution한 뒤 prescribed value를 stable order로 복구한다. |
| 8 | `recoverAndWriteResults()` | full residual/reaction, beam rows, final HDF5 | Recovery candidate를 원자적으로 commit하고 writer 성공 뒤에만 최종 output을 교체한다. |

비선형 정적 및 동적 해석은 V0 범위가 아니며 별도 ADR과 formulation을 승인한 뒤 이
lifecycle과 state/equation 계약을 확장한다. 기존 hook 사이에 조용히 반복·증분·시간 적분
동작을 삽입하지 않는다.

## 설계 패턴
- Strategy/Adapter Pattern: 현재 교체 가능한 public 경계는 `LinearSolver`, `ParallelFor`, `ResultsWriter`다. Vendor API는 concrete adapter implementation 안에만 둔다.
- Template Method Pattern: `Analysis::run()`은 공통 실행 흐름을 고정하고 세부 단계는 procedure별로 재정의한다.
- Syntax/Semantic separation: `AbaqusInputReader`는 syntax record를 만들고 `AbaqusDomainMapper`가 승인된 keyword 의미를 concrete Domain record로 변환한다.
- Runtime Polymorphism: V0에서는 backend 경계에만 사용한다. 요소/재료/하중 base hierarchy와 factory/registry는 두 번째 실제 구현이 필요해질 때 trade-off를 다시 결정한다.
- RAII: MKL handle, HDF5 file/dataset, temporary solver workspace의 수명과 오류 처리를 wrapper에 묶는다.

## Sparse Matrix Policy
- Assembly는 element마다 index-addressed contribution buffer를 만들고, join 뒤 COO tuple을 stable order로 정렬해 한 thread에서 순서대로 합산한 후 CSR로 finalize한다.
- `SparseMatrix`는 0-based CSR 데이터를 소유하는 별도 타입이며 dense `Matrix`를
  상속하지 않는다. MKL PARDISO backend는 adapter 경계에서 필요한 descriptor와 indexing을
  변환한다.
- CSR row offset, sorted-unique column, dimensions, finite values를 construction boundary에서 검증하며 structural zero를 삭제하지 않는다.
- Parallel worker는 global sparse matrix나 shared reduction accumulator를 직접 갱신하지 않는다. Worker별 failure slot과 contribution만 쓰고, deterministic reduction은 join 뒤에 수행한다.
- Matrix symmetry와 factorization/substitution failure는 solver adapter가 구조화된 solver diagnostic으로 번역한다. 승인되지 않은 regularization이나 fallback으로 singularity를 숨기지 않는다.

## Dense Math Policy

- `Vector`는 contiguous `double` 데이터와 크기를 소유하고 copy, dot, Euclidean norm,
  scale, axpy를 MKL CBLAS adapter로 수행한다.
- `Matrix`는 row-major contiguous `double` 데이터와 dimensions를 소유하고
  matrix-vector 및 matrix-matrix 연산에 `CBLAS_ROW_MAJOR`를 사용한다.
- 두 타입은 copy/move semantics와 bounds-checked access를 제공한다.
- MKL header와 MKL-specific type은 adapter implementation 밖으로 노출하지 않는다.

## Parallel Policy
- `ParallelFor`는 `[0,count)` index-addressed 독립 작업만 노출하며 `SerialParallelFor`와 `TbbParallelFor`가 같은 observable contract를 가진다.
- 첫 oneTBB 적용 지점은 element-local stiffness 계산이다. 각 callback은 자기 index의 output slot만 쓴다.
- 전역 sparse write와 부동소수 reduction은 parallel callback 밖의 deterministic 단계로 제한한다.
- MKL 내부 thread와 TBB element loop가 oversubscription을 만들지 않도록 thread count와 task arena 정책을 명시한다.

## HDF5 Result Schema
```text
/metadata
/model/nodes
/model/elements
/steps/<step-name>/frames/0/nodal/displacement
/steps/<step-name>/frames/0/nodal/reaction
/steps/<step-name>/frames/0/element/end_force_local
/steps/<step-name>/frames/0/element/section_resultant
/steps/<step-name>/frames/0/element/generalized_strain
/steps/<step-name>/frames/0/element/stress_s11
/diagnostics
```

Schema requirements:
- schema version, units, coordinate system, solver version, source input identity를 metadata에 기록한다.
- field output과 history output을 구분한다.
- reference comparison을 위한 row identity는 node id, element id, integration point id, step/frame id를 포함한다.
- FESA solver는 `results.h5`를 authoritative output으로 쓴다.
- Abaqus reference results는 기능 문서가 선언한 기존 CSV 파일이다. Directory/file
  naming, README, metadata 또는 provenance는 architecture readiness 조건이 아니다.
- Verification은 기능이 요구하는 source identity와 component를 결정적으로 대응시키고
  승인 tolerance를 적용한다. Missing/extra/duplicate/nonfinite required row는 숫자 비교
  전에 실패한다. 단일 step/final-frame 기능은 별도 CSV step/frame 열을 요구하지 않는다.
- FESA HDF5에서 추출한 deterministic CSV view는 optional debugging/review artifact이며 공식 solver output 또는 reference artifact가 아니다.
- Writer는 final과 같은 directory의 임시 HDF5에 전체 schema를 쓴 뒤 flush, checked close,
  read-only reopen/self-check를 수행한다. Existing final은 `ReplaceFileW`, 새 final은
  `MoveFileExW(..., MOVEFILE_WRITE_THROUGH)`로 완료하며 실패 시 temporary artifact를
  정리하고 불완전한 파일을 정상 `results.h5`로 노출하지 않는다.

## V0 결과 복구와 reference normalization

- Nodal displacement와 reaction은 global `[UX, UY, UZ, URX, URY, URZ]` 순서다.
- Equilibrium end action은 local `[FX,FY,FZ,MX,MY,MZ]`, endpoint section resultant는
  `[N,T,My,Mz]`, generalized strain/resultant는 두 Gauss point에 기록한다.
- General beam section stress는 section point의 axial `S11`만 복구한다. Section point가
  없으면 centroid `(0,0)`을 `source=fesa-default`로 기록한다.
- 승인된 `reference/cantilever beam/cantilever beam elemental forces.csv`는 node station
  기준 `SF1/SM1/SM2/SM3`을 제공한다. FESA endpoint를 동일한 section-cut 부호로
  정규화하고 interior node의 두 endpoint가 tolerance 안에서 일치하는지 먼저 확인한 뒤
  `SF1 -> N`, `SM1 -> My`, `SM2 -> Mz`, `SM3 -> T`로 비교한다.
- Reference tolerance는 같은 model, step/frame, quantity, component의 Abaqus rows에서
  `reference_scale = max(abs(reference_value))`를 구하고 각 row에
  `absolute_floor + 1e-6 * reference_scale`을 적용한다. SI displacement/rotation floor는
  `1e-9`, force/moment floor는 `1e-3`이다.
- Beam stress는 HDF5 schema와 unit/analytical test로 검증하지만 Abaqus reference
  comparison은 N/A다.

## CLI와 diagnostics

```powershell
fesa.exe <model.inp> --output <results.h5>
```

`--output`을 생략하면 현재 작업 디렉터리의 `results.h5`를 사용한다. Exit code는
`0=success`, `2=usage`, `3=input syntax/semantic mapping`, `4=model validation`,
`5=factorization/substitution`, `6=HDF5 output`으로 고정한다. Diagnostic은 `severity`,
`code`, `file`, `line`, `keyword`, `entity_identity`, `message`를 가지며 stderr에
deterministic한 순서로 출력한다.

## 기능 확장 플레이북

다음 표는 코드 위치만이 아니라 함께 바뀌어야 하는 계약 경계를 나타낸다. 한 열만
구현하고 다른 열을 생략하면 internal utility 또는 실험 kernel일 수는 있어도 제품 기능은
아니다.

| 기능 유형 | 시작 전에 고정할 것 | 주요 구현 경계 | 함께 검증할 것 | 피해야 할 shortcut |
| --- | --- | --- | --- | --- |
| 새 element/material | DOF, interpolation, constitutive law, integration, local axes/sign, feature-approved validity boundary | Domain record와 mapper, element kernel, DofManager scatter/pattern, SparseAssembler, ResultRecovery | feature-required invariants/tests and blocking reference quantities | 서로 다른 물리를 같다고 주장, source ID와 internal index 혼용, 검증 전 범용 hierarchy 추가 |
| 새 load/constraint | Abaqus target grammar, application order, units, follower 여부, prescribed-value 의미 | Syntax/mapper, Domain target, full-space LoadAssembler 또는 constraint partition, diagnostics | set/direct target ambiguity, multi-instance identity, nonfinite sum, `Ff-Kfc*dc`, reaction | Element load kernel 존재를 parser 지원으로 간주, penalty를 elimination에 몰래 혼합 |
| 새 analysis procedure | governing equation, state variables, increment/time lifecycle, tangent/residual, convergence와 output frame | 별도 Analysis implementation, procedure-specific state/equation owner, solver interface extension | orchestration order, failure atomicity, restart/frame identity, numerical benchmark | V0 hook 사이에 조건문으로 반복/시간 적분 삽입, 사용하지 않는 future state 선할당 |
| 새 numerical backend | matrix/index contract, lifecycle, reusable state, failure taxonomy, thread/runtime policy | 기존 `LinearSolver` 또는 `ParallelFor` interface의 concrete adapter, CMake normalized target | empty/dimension/extreme-scale input, repeated call, failed-output preservation, clean runtime discovery | Vendor type을 public header에 노출, silent fallback/regularization, absolute install path 고정 |
| 새 output/reference quantity | 물리 정의, location, sign, units, coordinates, stable row identity, mandatory 여부, tolerance | Result record/recovery, AnalysisState, ResultsWriter/HDF5 schema, comparator projection | schema dtype/shape, ordering, nonfinite rejection, identity inventory, reference N/A 대체 evidence | 서로 다른 result identity 혼합, station mismatch 평균, output request로 mandatory result 제거 |

모든 확장은 PRD의 제품 완료 정의와 요구조건→정식화→I/O→구현→reference→physics gate를
따른다. 기존 feature contract에 없는 범위를 편의상 “Abaqus compatible”이라고 넓히지 않는다.

## MITC4 확장 경계

MITC4가 구현될 때 Abaqus `S4`와 `S4R` source type은 같은 FESA formulation을 선택한다.
Source type은 metadata/diagnostic identity로 보존하지만 FESA integration 또는 hourglass
경로를 선택하지 않는다. 6-DOF embedding의 비물리 drilling coordinate에는 physical
rotational stiffness block의 positive minimum diagonal에 `1e-3`을 곱한 고정 numerical
stabilization만 둔다. Drilling calibration, artificial-energy policy와 별도 drilling result
dataset은 이 기능 범위가 아니다.

Reference comparison은 `reference/shell/` S4와 `reference/shellR/` S4R의 기존 input 및
displacement CSV를 사용한다. Global `U1/U2/U3`만 blocking이고 B33과 같은 component-scale
mixed tolerance를 적용한다. `UR1/UR2/UR3`은 같은 기준의 warning-only evidence다.
