# Linear Static 3D Euler Beam End-to-End Design

## Metadata

- feature_id: `linear-static-3d-euler-beam`
- phase_name: `linear-static-3d-euler-beam`
- status: `approved`
- approved_by: user
- approved_on: `2026-08-08`
- amended_on: `2026-08-09`
- source_formulation: `docs/formulations/3d-isoparametric-euler-beam-formulation.md`
- reference_baseline: `reference/cantilever beam/` from source commit `2b34d0b`
- implementation_environment: C++17, MSVC, CMake, CTest, GoogleTest, Intel oneMKL, Intel oneTBB, HDF5

## 1. 목적

이 설계는 Abaqus `.inp` keyword subset을 읽어 2절점 3차원
Euler–Bernoulli beam의 단일 선형 정적 step을 해석하고, 변위·반력·요소 내력·축응력을
authoritative HDF5 파일 `results.h5`에 기록하는 FESA V0 파이프라인을 정의한다.

런타임 순서는 다음과 같이 고정한다.

```text
.inp 입력
-> Domain 생성
-> 현재 step의 AnalysisModel 생성
-> DofManager 및 sparse pattern 생성
-> 요소강성 계산과 전역강성 조립
-> free/constrained partition
-> MKL PARDISO factorization
-> 전체 하중벡터 조립과 effective RHS 생성
-> PARDISO substitution
-> 전체 변위벡터 복구
-> 반력·요소 내력·요소 축응력 복구
-> results.h5 기록
-> 정상 종료
```

## 2. 범위

### 2.1 포함 범위

- 입력 파일당 하나의 `*STEP, *STATIC`
- small displacement, small rotation, 선형 탄성 해석
- 2절점 직선 3D Euler–Bernoulli beam
- 절점당 자유도 `[UX, UY, UZ, URX, URY, URZ]`
- 축, 두 방향 굽힘, Saint-Venant 비틀림
- nodal concentrated load와 prescribed displacement
- formulation의 constant local line-load equivalent nodal vector kernel
- nonzero prescribed displacement를 포함한 자유/구속 partition
- deterministic COO-to-CSR sparse assembly
- MKL PARDISO factorization과 substitution의 분리
- oneTBB 기반 element-local 계산
- 변위, 반력, equilibrium end action, section resultant, axial `S11` 출력
- 승인된 `reference/cantilever beam/` B33 bundle의 변위·반력·section resultant comparison

### 2.2 제외 범위

- Abaqus full compatibility
- Abaqus B31/Timoshenko beam
- 다중 analysis step과 step 간 load/BC propagation
- instance translation/rotation, nested assembly, dependent/independent mesh 차이
- `I12 != 0`, taper, offset, release, curved beam, warping
- distributed load용 Abaqus `*DLOAD` 입력과 Domain load object 생성
- 기하·재료비선형, dynamics, contact, thermal effects
- transverse shear stress와 torsional shear stress recovery
- beam stress에 대한 Abaqus reference comparison
- reference artifact의 자동 생성, 수정 또는 Abaqus 실행

## 3. Phase 구성

단일 Harness task `linear-static-3d-euler-beam`은 다음 29개 step으로 구성한다.
각 step은 하나의 gate 또는 하나의 C++ 모듈만 소유한다.

### 3.1 문서 및 계약 gate

| Step | Name | Primary output |
| ---: | --- | --- |
| 0 | `requirements-baseline` | `docs/requirements/linear-static-3d-euler-beam.md` |
| 1 | `research-evidence` | `docs/research/linear-static-3d-euler-beam-research.md` |
| 2 | `formulation-alignment` | `docs/formulations/3d-isoparametric-euler-beam-formulation.md` |
| 3 | `numerical-review` | `docs/numerical-reviews/linear-static-3d-euler-beam-review.md` |
| 4 | `io-contract` | `docs/io-definitions/linear-static-3d-euler-beam-io.md` |
| 5 | `reference-model-contract` | `docs/reference-models/linear-static-3d-euler-beam-reference-models.md` |
| 6 | `implementation-plan` | `docs/implementation-plans/linear-static-3d-euler-beam.md` |

### 3.2 C++ TDD 구현

| Step | Name | Module responsibility |
| ---: | --- | --- |
| 7 | `cmake-test-foundation` | CMake, GoogleTest, CTest, dependency discovery |
| 8 | `core-diagnostics` | IDs, source locations, diagnostics, status |
| 9 | `dense-math-adapters` | MKL BLAS `Matrix` and `Vector` |
| 10 | `domain-model` | Immutable model definitions and source identities |
| 11 | `inp-syntax-parser` | Keyword/data/comment lexical and syntax parsing |
| 12 | `inp-domain-mapping` | Part/Assembly/Instance semantic mapping |
| 13 | `analysis-model` | Active single-step views into Domain |
| 14 | `dof-manager` | DOFs, constraints, numbering, sparse pattern |
| 15 | `analysis-state` | Displacement, forces, residual, step/frame state |
| 16 | `euler-beam-element` | B33 element stiffness, transformation, load/recovery kernels |
| 17 | `parallel-for-tbb` | oneTBB adapter for deterministic element-local work |
| 18 | `sparse-assembly` | COO contribution collection and CSR finalization |
| 19 | `essential-constraints` | Matrix partition and full/reduced vector mapping |
| 20 | `mkl-pardiso-solver` | PARDISO adapter, factorization, substitution, diagnostics |
| 21 | `load-assembly` | Nodal load vector and prescribed-displacement RHS |
| 22 | `result-recovery` | Reactions, end actions, resultants, axial stress |
| 23 | `hdf5-results-writer` | Versioned HDF5 schema and atomic finalization |
| 24 | `linear-static-cli` | Analysis orchestration and `fesa.exe` entry point |

### 3.3 최종 gate

| Step | Name | Primary output |
| ---: | --- | --- |
| 25 | `build-test-verification` | `docs/build-test-reports/linear-static-3d-euler-beam.md` |
| 26 | `reference-verification` | `docs/reference-verifications/linear-static-3d-euler-beam-reference-verification.md` |
| 27 | `physics-sanity` | `docs/physics-evaluations/linear-static-3d-euler-beam-physics-evaluation.md` |
| 28 | `release-readiness` | `docs/releases/linear-static-3d-euler-beam-release.md` |

## 4. 아키텍처와 소유권

### 4.1 Domain 계층

`Domain`은 parser가 만든 전체 모델을 소유하며 parsing 이후 가능한 한 불변으로
취급한다. 최소 semantic object는 다음과 같다.

- `Node`
- `EulerBeam3DDefinition`
- `LinearElasticMaterial`
- `GeneralBeamSection`
- `NodeSet`, `ElementSet`
- `BoundaryCondition`, `NodalLoad`
- `StaticStepDefinition`

Part 내부 source label은 변경하지 않는다. 외부 identity는
`SourceEntityId { instance_name, source_label }`로 유지하고, Domain은 별도의 stable
internal index를 부여한다. 같은 part의 여러 identity instance는 허용하지만 instance
transform data가 있으면 입력 오류로 처리한다.

### 4.2 AnalysisModel

`AnalysisModel`은 단일 static step에 활성화된 element, load, boundary condition,
material, section의 ID/reference view를 제공한다. Domain 객체를 복사하거나 수정하지
않는다. 두 번째 `*STEP`은 V0 unsupported diagnostic을 발생시킨다.

### 4.3 DofManager

`DofManager`만 다음 정보를 소유한다.

- node별 6개 DOF 정의
- full DOF index
- constrained/free mapping
- free equation numbering
- element scatter map
- sparse matrix pattern
- full/reduced vector reconstruction

Node와 Element에는 equation ID를 저장하지 않는다.

### 4.4 AnalysisState

`AnalysisState`는 다음 해석 중 상태를 소유한다.

- full displacement
- external force
- internal force
- residual
- constrained reaction
- current step/frame identity
- element recovery rows

V0에서는 velocity, acceleration, temperature, iteration history를 할당하지 않는다.

### 4.5 요소 계약

`EulerBeam3D`는
`docs/formulations/3d-isoparametric-euler-beam-formulation.md`의 부호, DOF 순서,
2점 Gauss rule, transformation을 따른다. 구현 API는 다음 책임을 분리한다.

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

요소는 equation ID를 소유하지 않는다. `BeamRecovery`는 endpoint equilibrium end
action, endpoint section resultant, Gauss-point generalized strain/resultant, section-point
`S11`을 구분한다. `localEquivalentLoad`는 정식화의 constant local line-load vector를
검증하기 위한 element kernel이지만 V0 `.inp` parser는 `*DLOAD`를 생성하지 않으므로
CLI 해석에서는 호출되지 않는다.

### 4.6 LinearSolver 계약

Solver core에는 MKL 타입을 노출하지 않는다.

```cpp
class LinearSolver {
public:
    virtual ~LinearSolver() = default;
    virtual Status factorize(const SparseMatrix& matrix) = 0;
    virtual Status solve(const Vector& rhs, Vector& solution) const = 0;
};
```

`MklPardisoSolver`는 factorization 상태를 소유하고 같은 matrix에 여러 RHS
substitution을 수행할 수 있다. singularity와 invalid CSR은 구조화된 diagnostic으로
반환한다. V0는 constrained `Kff`를 real symmetric positive definite matrix로
factorize하며, positive-definite factorization 실패를 unconstrained, singular 또는
invalid model diagnostic으로 분류한다.

## 5. Matrix, Vector 및 SparseMatrix

### 5.1 Vector

`Vector`는 연속된 `double` 데이터와 크기를 소유한다. copy/move semantics와
bounds-checked access를 제공한다. 다음 연산은 MKL CBLAS를 사용한다.

- copy
- `dot`
- Euclidean norm
- `scale`
- `axpy`

### 5.2 Matrix

`Matrix`는 연속된 row-major `double` 데이터, row count, column count를 소유한다.
copy/move semantics와 bounds-checked access를 제공한다. matrix-vector와
matrix-matrix 연산은 `CBLAS_ROW_MAJOR`의 MKL CBLAS를 사용한다. beam의 3x3, 4x12,
12x12 dense 연산도 이 타입을 사용한다.

MKL header와 MKL-specific type은 `.cpp` adapter 경계 밖으로 노출하지 않는다.

### 5.3 SparseMatrix

`SparseMatrix`는 CSR 전용 별도 타입이며 `Matrix`를 상속하지 않는다. CSR row pointer,
column index, values를 소유하고 0-based indexing을 사용한다. PARDISO adapter가
필요한 indexing과 descriptor 변환은 adapter 내부에서 수행한다.

## 6. 입력 계약

### 6.1 지원 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`

Keyword와 parameter는 case-insensitive이고 label identity는 원문 값을 보존한다.
Comment line은 `**`로 시작한다.

`*BOUNDARY`는 `target, first_dof, last_dof[, value]` 형식만 지원하고 DOF 범위는
1부터 6까지다. value가 없으면 0으로 해석한다. `*CLOAD`는
`target, dof, magnitude` 형식만 지원하고 DOF 범위는 1부터 6까지다. target은 source
node label 또는 node set이며 assembly-level set의 `INSTANCE` parameter를 해석한다.

### 6.2 General beam section mapping

`*BEAM GENERAL SECTION, SECTION=GENERAL`의 data는
`A, I11, I12, I22, J` 순서로 읽는다.

- `MATERIAL` parameter는 필수이며 해당 `*MATERIAL`의 isotropic `*ELASTIC` data는
  정확히 `E, nu`를 제공해야 한다.
- `I12`는 0이어야 한다.
- Abaqus first beam section axis `n1`을 FESA local `y`로 둔다.
- `t x n1`을 FESA local `z`로 둔다.
- `Iy = I11`, `Iz = I22`로 매핑한다.
- `E`와 Poisson ratio `nu`로 `G = E / (2(1 + nu))`를 계산한다.
- `E`, `G`, `A`, `Iy`, `Iz`, `J`는 양수여야 한다.
- `coordinate_scale = max(1, norm(X1), norm(X2))`에 대해
  `L > 1e-12 * coordinate_scale`이어야 한다.
- guide vector는 0이 아니어야 하며
  `norm(a_perp) > 1e-12 * max(1, norm(a))`이어야 한다.

이 축 계약은 Abaqus의 right-handed `(t, n1, n2)` section system 및 현재 formulation의
`(x, y, z)` system과 일치한다.

### 6.3 B33 의미 보존

Abaqus B31은 transverse shear deformation을 포함하는 Timoshenko beam이고 B33은
2절점 cubic Euler–Bernoulli beam이다. FESA V0는 `TYPE=B33`만 Euler 요소로 매핑한다.
`TYPE=B31`은 `unsupported-element-formulation` 오류로 거부한다.
`reference/cantilever beam/cantilever beam.inp`는 source commit `2b34d0b`에서
`TYPE=B33`으로 생성된 승인 reference input이다. Reference artifact check는 비교 전에
이 element type을 다시 확인하며 B31로 되돌아간 입력이나 결과를 허용하지 않는다.

이 결정은 [[Abaqus Structural Element Families]], [[Beam and Frame Finite Elements]],
[[Abaqus-Analysis-User-s-Guide-Volume-IV|Abaqus Analysis User's Guide Volume IV]]의
beam-family 구분을 따른다.

### 6.4 Instance 제한

- 같은 part의 여러 identity instance를 허용한다.
- instance name과 source node/element/set label을 보존한다.
- source identity를 stable Domain index로 deterministic하게 매핑한다.
- translation 또는 rotation data가 있으면 `unsupported-instance-transform` 오류를 낸다.
- nested assembly와 dependent/independent mesh semantics는 지원하지 않는다.

### 6.5 No-op allowlist

다음 keyword와 관련 data line은 구조화된 warning을 기록하고 해석에서는 사용하지
않는다.

- `*PREPRINT`
- `*RESTART`
- `*TRANSVERSE SHEAR STIFFNESS`
- `*OUTPUT, FIELD`
- `*OUTPUT, HISTORY`
- `*NODE OUTPUT`
- `*ELEMENT OUTPUT`
- `*CONTACT OUTPUT`
- 위 output request에 속한 미지원 variable data

이 allowlist 밖의 미지원 keyword는 오류다. FESA의 기본 출력은 Abaqus output request와
무관하게 항상 생성된다.

## 7. 선형 정적 알고리즘

1. `AbaqusInputReader`가 syntax model과 source location을 만든다.
2. semantic mapper가 immutable `Domain`을 만든다.
3. `AnalysisModel`이 단일 static step의 active view를 만든다.
4. `DofManager`가 DOF, constraints, scatter map, sparse pattern을 만든다.
5. `ParallelFor`가 element-local global stiffness를 계산한다.
6. element internal index 순서로 COO contribution을 deterministic하게 reduce하고 CSR로
   finalize한다.
7. free/constrained partition으로 `Kff`, `Kfc`, `Kcf`, `Kcc`를 만든다.
8. `MklPardisoSolver::factorize(Kff)`를 호출한다.
9. nodal load를 full `F`에 조립한다.
10. `rhs = Ff - Kfc * dc`를 만든다.
11. `solve(rhs, df)`로 substitution하고 full `d`를 복구한다.
12. `Rc = Kcf * df + Kcc * dc - Fc`로 reaction을 복구한다.
13. element displacement를 gather하여 end action, section resultant, strain, `S11`을
    복구한다.
14. `ResultsWriter`가 임시 HDF5 파일을 완성한 뒤 최종 `results.h5`로 교체한다.

반력은 요소 끝력의 별도 합산 경로가 아니라 전체 조립 residual `K*d-F`에서 구한다.

## 8. 결과 복구

### 8.1 변위와 반력

모든 node에 전역 성분 `[UX, UY, UZ, URX, URY, URZ]`를 기록한다. 반력은 동일한
6성분 순서로 기록하고 free DOF의 값은 수치 residual 검사에 사용한다.

### 8.2 요소 내력

다음 데이터를 서로 다른 dataset으로 유지한다.

- equilibrium end action: `Kl*dl-fl_dist`, local `[FX,FY,FZ,MX,MY,MZ]`
- section resultant: endpoint `xi=-1,+1`의 `[N,T,My,Mz]`
- generalized strain/resultant: 두 Gauss point의 formulation 값

Abaqus internal-force CSV는 equilibrium end action이 아니라 section resultant와
비교한다. 일반 component mapping은 다음과 같다.

| Abaqus | FESA local component |
| --- | --- |
| `SF1` | `FX` / `N` |
| `SF3` | `FY` |
| `SF2` | `FZ` |
| `SM3` | `MX` / `T` |
| `SM1` | `MY` |
| `SM2` | `MZ` |

승인된 B33 reference의 `cantilever beam elemental forces.csv`는 element label 없이
`Frame`, `Part Instance Name`, `Node Label`, `SF1`, `SM1`, `SM2`, `SM3`을 기록한다.
따라서 이 파일의 comparison은 다음 규칙을 사용한다.

- Reference row key는 `(Step-1, frame 0, instance name, source node label, component)`다.
  단일 step invariant에 따라 CSV의 `Increment 1: Step Time = 1.000`을
  `(Step-1, frame 0)`으로 정규화한다.
- FESA endpoint section resultant를 동일한 section-cut 부호로 변환한 뒤 source node
  station으로 정규화한다. Interior node의 두 인접 element endpoint 값은 먼저 승인된
  tolerance 안에서 서로 일치해야 한다. 불일치는 평균으로 숨기지 않고
  `tolerance-failure`로 보고한다.
- Interior endpoint가 일치하면 stable internal element ID가 작은 endpoint를 대표 row로
  선택한다. Boundary node는 하나의 endpoint를 사용한다.
- 이 bundle에서 비교하는 mapping은 `SF1 -> N`, `SM1 -> My`, `SM2 -> Mz`,
  `SM3 -> T`다. CSV에 없는 `SF2`와 `SF3`는 reference comparison 대상이 아니며,
  FESA equilibrium end action의 transverse force는 unit/analytical test와 physics sanity로
  검증한다.

### 8.3 Axial stress

General beam section은 section point에서 axial stress만 복구한다.

```text
S11 = E * (epsilon0 + x2*kappa1 - x1*kappa2)
```

FESA 좌표로는 `x1=y`, `x2=z`, `kappa1=kappa_y`, `kappa2=kappa_z`다.
`*SECTION POINTS`가 있으면 지정 순서대로 출력한다. 없으면 FESA-specific centroid
point `(0,0)`을 만들고 `source=fesa-default`로 기록한다. Stress는 두 Gauss point에서
평가한다. Transverse shear stress와 torsional shear stress는 출력하지 않는다.

이 계약은 [[Abaqus Beam and Shell Section Definitions]]과
[[Abaqus-Analysis-User-s-Guide-Volume-IV|Abaqus Analysis User's Guide Volume IV]]의
general beam section output 제한을 따른다.

## 9. HDF5 schema v0

Authoritative output은 `results.h5` 하나다. 최소 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
```

`/metadata`에는 schema version, solver version, source input identity, unit-system label,
coordinate convention, element formulation을 기록한다. Node/element group은 stable internal
ID와 instance/source label mapping을 함께 저장한다.

Abaqus `.inp` 자체에는 단위 선언이 없으므로 일반 solver run의 unit-system label은
`user-consistent-unspecified`로 기록하고 solver가 입력 파일만 보고 SI를 추론하지
않는다. 승인된 cantilever B33 reference에 한해서는 이 설계와 후속 Reference Model
Contract가 입력 수치의 단위계를 SI로 선언하고, reference verification이 동일한 입력으로
생성된 FESA 결과에 그 외부 단위 계약을 적용한다. 새 reference model도 승인된
Reference Model Contract에 단위계를 선언한다. 선택 `metadata.json`이 존재하면 그
단위 선언을 read-only 보조 자료로 계약과 대조한다.

- displacement: `[node_count, 6]`
- reaction: `[node_count, 6]`
- end force: `[element_count, 2, 6]`
- section resultant: `[element_count, 2, 4]`, `xi={-1,+1}`
- generalized strain: `[element_count, 2, 4]`, two Gauss points
- stress: flat row identity with element, Gauss point, section point, `(x1,x2)`, `S11`

HDF5 API는 `Hdf5ResultsWriter` implementation 밖으로 노출하지 않는다. Writer failure 시
불완전한 최종 `results.h5`를 남기지 않는다.

## 10. CLI와 diagnostics

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

`--output`을 생략하면 현재 작업 디렉터리에 `results.h5`를 생성한다.

| Exit code | Meaning |
| ---: | --- |
| 0 | success |
| 2 | CLI usage error |
| 3 | input syntax or semantic mapping error |
| 4 | model validation error |
| 5 | factorization or substitution error |
| 6 | HDF5 output error |

각 diagnostic은 `severity`, `code`, `file`, `line`, `keyword`, `entity_identity`,
`message`를 가진다. CLI는 오류 diagnostic을 stderr에 deterministic한 순서로 출력한다.

## 11. TDD와 검증

### 11.1 C++ step 규칙

각 production C++ step은 다음 순서를 같은 step 안에서 증명한다.

1. GoogleTest 작성
2. 관련 test target 실행과 요구사항을 재현하는 RED 실패 확인
3. 최소 production 구현
4. 관련 CTest GREEN 확인
5. 전체 MSVC x64 Debug build와 CTest VERIFY

새 경고를 허용하지 않으며 FESA target에만 MSVC `/W4 /WX`를 적용한다. Third-party
GoogleTest target에는 FESA warning policy를 강제하지 않는다.

### 11.2 테스트 포트폴리오

- Matrix/Vector ownership, bounds, dimension mismatch, BLAS result checks
- parser case-insensitivity, comments, set generation, wrapper nesting, allowlist diagnostics
- B31 rejection, B33 acceptance, instance transform rejection, unknown keyword rejection
- Domain duplicate label와 dangling reference validation
- AnalysisModel active view와 Domain non-copy/non-mutation 검사
- DofManager numbering, constraints, scatter map, full/reduced reconstruction
- Hermite interpolation, B matrix, 2-point Gauss vs closed-form stiffness
- symmetry, rank 6, six rigid modes, positive deformation energy
- local/global transform orthogonality와 energy invariance
- deterministic parallel COO reduction과 CSR structure
- nonzero prescribed displacement와 reaction recovery
- PARDISO factorization/substitution 분리, repeated RHS, singular diagnostic
- axial, torsion, y/z bending cantilever analytical cases
- HDF5 schema, identity, component, metadata, atomic finalization
- CLI `.inp -> results.h5` integration
- 승인된 B33 CSV header/Frame 정규화, node-station matching, interior endpoint 일치 검사
- zero 및 near-zero reference row를 포함한 component-scale 혼합 허용오차 검사

### 11.3 수치 tolerance

| Check | Criterion |
| --- | --- |
| matrix symmetry and Gauss/closed-form comparison | normalized `1e-12` |
| rigid-mode and linear-system residual | normalized `1e-10` |
| analytical solution tests | relative `1e-9` |
| Abaqus B33 reference comparison | component-scale relative `1e-6` |
| SI displacement and rotation absolute floor | `1e-9` |
| SI force and moment absolute floor | `1e-3` |

Reference row 판정은 zero-reference에서도 의미가 있도록 component-scale 혼합
허용오차를 사용한다. 같은 model, step/frame, quantity, component의 Abaqus reference
rows에 대해 다음 값을 계산한다.

```text
reference_scale = max(abs(reference_value_i))
row_tolerance = absolute_floor + 1e-6 * reference_scale
row_pass = abs(fesa_value_i - reference_value_i) <= row_tolerance
```

- Scale은 displacement, rotation, force, moment 및 각 component를 섞지 않는다.
- Scale은 read-only Abaqus reference 값만 사용하며 FESA 결과로 조정하지 않는다.
- Scale이 0이면 absolute floor만 적용한다.
- 모든 row를 개별 판정하며 reference 값을 zero-clamp하거나 row를 제거하지 않는다.
- Missing/extra row, nonfinite value, schema 또는 identity mismatch는 tolerance 계산 전에
  실패한다.
- Report는 max absolute error, component-scale normalized error, RMS error, norm error,
  worst row/component를 기록한다.

현재 B33 `SM1`의 reference scale은 `1.0e7`이고 moment row tolerance는
`1.0e-3 + 1.0e-6 * 1.0e7 = 10.001`이다. 자유단 reference residue `-1.56e-2`와
FESA의 이론적 zero 사이 오차 `1.56e-2`는 이 기준을 통과하지만, 예를 들어 `100`의
자유단 moment 오차는 실패한다.

승인된 cantilever bundle은 SI absolute floor를 사용한다. 이후 SI가 아닌 reference를
추가하면 quantity별 absolute floor를 해당 단위로 변환하고 변환 근거를 metadata와
verification report에 기록해야 한다.

### 11.4 공통 build/test command

GoogleTest checkout은 `C:/git/googletest`에 있고 승인 당시 revision은
`04ee1b4f2aefdffb0135d7cf2a2c519fe50dabe4`다. 절대경로를 production CMake에
하드코딩하지 않고 cache variable로 전달한다.

```powershell
cmake -S . -B .harness/build -A x64 `
  -DFESA_GTEST_SOURCE_DIR=C:/git/googletest `
  "-DMKL_DIR=C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl" `
  "-DTBB_DIR=C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb" `
  "-DHDF5_DIR=C:/Program Files/HDF_Group/HDF5/2.1.1/cmake"

cmake --build .harness/build --config Debug
ctest --test-dir .harness/build -C Debug --show-only=json-v1
ctest --test-dir .harness/build -C Debug --output-on-failure
```

CTest discovery 결과는 한 개 이상의 test를 포함해야 한다.

## 12. Reference 및 release gate

`reference/cantilever beam/`을 V0의 승인된 Abaqus B33 reference baseline으로 사용한다.
논리 model ID는 `cantilever-beam-b33`, reference schema ID는
`abaqus-cae-report-csv-v0`이며 source baseline은 commit `2b34d0b`이다. Input header가
기록한 generator는 `Abaqus/CAE Learning Edition 2024`다. 모델은 SI 단위계, global
Cartesian nodal output, beam local section-force output, `Step-1`, increment 1,
step time 1.0 계약으로 해석한다.

| Purpose | Approved artifact |
| --- | --- |
| analysis input | `reference/cantilever beam/cantilever beam.inp` |
| displacement | `reference/cantilever beam/cantilever beam displacements.csv` |
| reaction | `reference/cantilever beam/cantilever beam reactions.csv` |
| section resultant | `reference/cantilever beam/cantilever beam elemental forces.csv` |
| stress | N/A: beam stress reference comparison은 승인 범위에서 제외 |

이 기존 bundle의 space-containing filename과 CAE report header는 승인된 legacy alias로
그대로 지원하며 reference artifact를 rename하거나 rewrite하지 않는다. `README.md`는 이
baseline에 한해서 N/A다. `metadata.json`은 프로젝트 전역에서 선택 artifact이며 이
bundle에는 존재하지 않는다. 이 설계와 Step 5의 Reference Model Contract가 model ID,
provenance, units, coordinate system, schema, file inventory, tolerance와 stress N/A 사유를
기록한다. 이후 추가하는 reference model에는 canonical artifact 이름과 `README.md`를
요구하며, 선택 `metadata.json`이 존재하면 계약과 대조한다.

Reference verification은 build/test gate 통과 후 승인 input으로 FESA `results.h5`를
생성하고 다음을 직접 비교한다.

- nodal displacement: CSV `U1/U2/U3/UR1/UR2/UR3`과 HDF5 displacement
- nodal reaction: CSV `RF1/RF2/RF3/RM1/RM2/RM3`과 HDF5 reaction
- section resultant: CSV `SF1/SM1/SM2/SM3`과 node station으로 정규화한 HDF5
  section resultant
- stress: 명시적 N/A

Artifact check는 네 파일의 존재, B33 element type, expected header, 유일한 row key,
finite value를 확인한다. 하나라도 충족하지 않으면 `needs-reference-artifacts` 또는
`schema-mismatch`로 분류하고 comparison을 시작하지 않는다. `metadata.json` 부재는 모든
bundle에서 허용되며, 현재 baseline의 `README.md` 부재도 승인된 legacy 예외로 차단하지
않는다.

Agent와 Harness는 Abaqus를 실행하거나 reference artifact를 생성·수정하지 않는다.

Reference comparison이 pass한 뒤에만 physics sanity를 수행한다. Physics sanity는
global force/moment equilibrium, reaction sign, displacement direction, symmetry, element
section-force consistency, normalized residual을 검사한다. Release readiness는 모든 이전
gate evidence와 known limitations를 확인한다.

## 13. 완료 조건

다음 조건을 모두 만족해야 phase를 완료할 수 있다.

1. 요구조건, 연구, 정식화, 수치 검토, I/O, reference model, 구현 계획 문서가 승인
   상태다.
2. 모든 production C++ 변경에 대응 테스트와 RED/GREEN/VERIFY evidence가 있다.
3. MSVC x64 Debug configure, build, CTest discovery, 전체 CTest가 성공한다.
4. `fesa.exe`가 승인된 B33 single-step input에서 `results.h5`를 생성한다.
5. HDF5 변위·반력·내력 rows가 승인된 cantilever B33 reference CSV와 component-scale
   혼합 tolerance 안에서 일치한다.
6. Beam stress reference comparison은 명시적 N/A이고 stress output schema/test는
   통과한다.
7. Physics sanity가 pass한다.
8. Release report에 지원 subset과 모든 V0 제한사항이 기록된다.
