# Linear Static 3D Euler Beam I/O Definition

## Metadata

- feature_id: `linear-static-3d-euler-beam`
- source_requirement: `docs/requirements/linear-static-3d-euler-beam.md`
- source_formulation: `docs/formulations/3d-isoparametric-euler-beam-formulation.md`
- source_numerical_review: `docs/numerical-reviews/linear-static-3d-euler-beam-review.md`
- source_research: `docs/research/linear-static-3d-euler-beam-research.md`
- approved_design: `docs/superpowers/specs/2026-08-08-linear-static-3d-euler-beam-design.md`
- status: `ready-for-implementation-planning`
- owner_agent: `io-definition-agent`
- date: `2026-08-09`
- authoritative_output: `results.h5`
- reference_baseline: `reference/cantilever beam/` at source commit `2b34d0b`

이 문서는 승인된 V0의 semantic I/O contract만 정의한다. Parser, HDF5 writer,
comparison tooling의 C++ API나 구현 구조는 정의하지 않으며 Abaqus full compatibility를
주장하지 않는다.

## Abaqus Input Scope

- input_format: Abaqus input file (`.inp`)
- supported_analysis: 입력 파일당 하나의 linear-static step과 `TYPE=B33` 2절점 3D
  Euler–Bernoulli beam
- ordinary_unit_system: user-consistent; `.inp`만 보고 SI를 추론하지 않음
- compatibility_disclaimer: 아래 표의 위치, parameter, data grammar만 지원함

`supported`는 해당 행의 정확한 subset만 의미한다. `warning no-op`은 syntax를 소비하고
구조화된 warning을 기록하지만 Domain, AnalysisModel 또는 mandatory HDF5 output을
변경하지 않는다는 뜻이다.

| keyword | status and allowed location | required parameters and data grammar | purpose and semantic mapping | unsupported behavior |
| --- | --- | --- | --- | --- |
| `*HEADING` | supported; optional top-level first keyword | parameters 없음; 다음 keyword 전까지 0개 이상의 text data line | 원문 heading을 source metadata로 보존 | 중첩 위치 또는 parameter는 input error |
| `*PART` / `*END PART` | supported; top-level model block | `NAME=<part-name>` 필수; `*END PART` data 없음 | part-local node, element, set, section 정의 범위 | 닫히지 않음, 중첩 part, duplicate part name은 error |
| `*NODE` | supported; part 내부 | parameter 없음; 각 row `node_label, x, y, z` | positive integer source label과 finite global Cartesian 좌표 | 다른 arity, duplicate label, nonfinite 좌표는 error |
| `*ELEMENT` | supported; part 내부 | `TYPE=B33` 필수; 각 row `element_label, node_1, node_2` | 2절점 straight Euler beam definition | `TYPE=B31`은 `unsupported-element-formulation`; 다른 type/arity와 dangling connectivity는 error |
| `*NSET` | supported; part 또는 assembly 내부 | `NSET=<name>` 필수; optional `GENERATE`; assembly에서는 `INSTANCE=<instance-name>` 필수; explicit row는 하나 이상의 node label, generate row는 `first,last,increment` | part-local 또는 single-instance assembly node set | mixed-instance set, zero/negative increment, duplicate name, dangling member는 error |
| `*ELSET` | supported; part 또는 assembly 내부 | `ELSET=<name>` 필수; optional `GENERATE`; assembly에서는 `INSTANCE=<instance-name>` 필수; explicit/generate grammar는 `*NSET`과 동일 | part-local 또는 single-instance assembly element set | mixed-instance set, invalid range, duplicate name, dangling member는 error |
| `*MATERIAL` | supported; top-level model data | `NAME=<material-name>` 필수; data 없음 | 뒤따르는 단일 isotropic `*ELASTIC`을 소유 | duplicate name, missing/duplicate elasticity, 다른 material model은 error |
| `*ELASTIC` | supported; 현재 material 내부 | parameter 없음; 정확히 한 row `E, nu` | homogeneous isotropic elasticity; `G=E/(2(1+nu))` | extra field/row, nonfinite value 또는 `E<=0`/`G<=0`은 model error |
| `*BEAM GENERAL SECTION` | supported; part 내부 | `ELSET=<name>`, `MATERIAL=<name>`, `SECTION=GENERAL` 필수; 첫 row `A,I11,I12,I22,J`, 둘째 row `n1x,n1y,n1z` | section assignment, properties와 first section axis | 다른 `SECTION`, missing/dangling reference, `I12!=0`, invalid property/guide vector는 error |
| `*SECTION POINTS` | supported; 바로 앞 general beam section에 종속 | parameter 없음; 하나 이상의 row `x1,x2` | 입력 순서의 stress recovery point; `x1=y`, `x2=z` | 선행 section 없음, 다른 arity, nonfinite 또는 duplicate point는 error |
| `*ASSEMBLY` / `*END ASSEMBLY` | supported; 정확히 하나의 top-level assembly block | `NAME=<assembly-name>` 필수; `*END ASSEMBLY` data 없음 | identity instance와 assembly set 범위 | nested/duplicate assembly는 `unsupported-nested-assembly` |
| `*INSTANCE` / `*END INSTANCE` | supported; assembly 내부 | `NAME=<instance-name>`, `PART=<part-name>` 필수; 두 keyword 사이 data 없음 | named identity copy; 같은 part의 여러 identity instance 허용 | translation/rotation data는 `unsupported-instance-transform`; duplicate/dangling instance는 error |
| `*BOUNDARY` | supported; top-level model data 또는 sole step 내부 | parameter 없음; 각 row `target, first_dof, last_dof[, value]`; omitted value는 `0` | sole step의 prescribed displacement | invalid arity/range, unresolved target, conflicting expanded node/DOF value는 error |
| `*CLOAD` | supported; sole step 내부 | parameter 없음; 각 row `target, dof, magnitude` | sole step의 global nodal concentrated load | invalid arity/DOF, nonfinite magnitude 또는 unresolved target은 error |
| `*STEP` / `*END STEP` | supported; exactly one top-level history block | optional `NAME`; optional `NLGEOM=NO`; `*END STEP` data 없음 | canonical result identity `(Step-1, frame 0)` | second step은 `unsupported-multiple-step`; `NLGEOM=YES`는 `unsupported-nonlinear-geometry` |
| `*STATIC` | supported; sole step 내부, exactly once | parameter 없음; exactly one row `initial_increment, time_period, minimum_increment, maximum_increment` | 승인 legacy row를 읽되 V0는 하나의 linear solve와 frame 0만 생성 | missing/extra/nonfinite/nonpositive field 또는 다른 procedure는 error |
| `*PREPRINT` | warning no-op; top-level | 모든 parameter를 보존 후 무시; data 없음 | legacy generator control 수용 | semantic object를 만들지 않음 |
| `*RESTART` | warning no-op; sole step | 모든 parameter를 보존 후 무시; data 없음 | legacy restart request 수용 | restart state를 만들지 않음 |
| `*TRANSVERSE SHEAR STIFFNESS` | warning no-op; beam section context | keyword와 소속 data line을 보존 후 무시 | B33 legacy deck 수용 | Euler formulation/property를 변경하지 않음 |
| `*OUTPUT, FIELD` | warning no-op; sole step | output parameter와 subordinate data를 보존 후 무시 | legacy field request root 수용 | mandatory FESA output을 선택·억제하지 않음 |
| `*OUTPUT, HISTORY` | warning no-op; sole step | output parameter와 subordinate data를 보존 후 무시 | legacy history request root 수용 | history result를 생성하지 않음 |
| `*NODE OUTPUT` | warning no-op; active output request 내부 | 모든 parameter와 다음 keyword 전 variable data를 무시 | legacy nodal variable request 수용 | requested variables로 HDF5 schema를 변경하지 않음 |
| `*ELEMENT OUTPUT` | warning no-op; active output request 내부 | 모든 parameter와 다음 keyword 전 variable data를 무시 | legacy element variable request 수용 | requested variables로 HDF5 schema를 변경하지 않음 |
| `*CONTACT OUTPUT` | warning no-op; active output request 내부 | 모든 parameter와 다음 keyword 전 variable data를 무시 | legacy contact variable request 수용 | contact model/result를 만들지 않음 |

`*DLOAD`, `*INCLUDE` 및 표에 없는 model-affecting keyword는 no-op 대상이 아니며
`unsupported-keyword` input error다. 특히 formulation-only constant local line-load
kernel은 parser나 Domain의 distributed-load 지원을 뜻하지 않는다.

### Nesting grammar

```text
input-file
  := heading? part+ assembly material+ model-boundary* step EOF

part
  := *PART
       node-block+ element-block+ (nset | elset)*
       general-section+ section-points*
     *END PART

assembly
  := *ASSEMBLY
       instance+ assembly-set*
     *END ASSEMBLY

instance
  := *INSTANCE *END INSTANCE

step
  := *STEP *STATIC step-boundary* cload* allowed-no-op* *END STEP
```

Top-level material과 model boundary는 step 전에만 나타난다. Part는 assembly 전에 모두
닫혀야 하고 assembly 안에는 instance와 assembly-level set만 허용한다. Instance 안의
node/element 재정의, nested assembly, dependent/independent mesh option은 지원하지 않는다.
Sole step의 source `NAME` spelling은 metadata에 보존하지만 V0 HDF5와 comparison의
canonical identity는 항상 `Step-1`, `frame 0`이다.

## Syntax Policy

- keyword line은 첫 non-whitespace character가 `*`이고 `**`가 아니어야 한다.
- comment line은 `**`로 시작하며 의미 모델에서는 제외하되 file과 1-based line 위치를
  유지한다.
- keyword, parameter name, enumerated parameter value와 named-reference lookup은 ASCII
  case-insensitive다. Part, instance, material, set의 원래 spelling과 numeric label lexeme는
  외부 identity를 위해 그대로 보존한다.
- keyword line은 comma-separated `KEY[=VALUE]` fields다. Data line도 comma-separated이며
  field 양끝 whitespace를 제거한다. Required empty field, trailing empty optional value,
  잘못된 numeric token은 input error다.
- Keyword/data continuation, quoted/multiline name, include file 및 line continuation은 V0에서
  지원하지 않는다. 한 logical keyword/data row는 한 physical line에 완결되어야 한다.
- Node/element labels와 explicit set members는 positive base-10 integer다. Internal index로
  바꾸어도 raw source label text를 잃지 않는다.
- 모든 numeric value는 finite `double`이어야 한다. `NaN`, positive/negative infinity는
  syntax/semantic validation에서 거부한다.
- 각 parsed keyword, parameter, data row와 semantic entity는 `file`, 1-based `line`, raw
  `keyword`, raw source label을 추적할 수 있어야 한다.
- Output-request subordinate variable data는 직전의 allowlisted output keyword부터 다음
  keyword까지에만 warning no-op이다. 그 밖의 예상되지 않은 data line은 input error다.

### Duplicate and dangling-reference policy

- Part, assembly, instance, material, set 이름은 각 semantic scope의 case-insensitive key로
  unique해야 한다. Node/element numeric source label은 part scope에서 unique해야 한다.
- 같은 part의 여러 identity instance는 duplicate가 아니다. 각 copy는 instance name으로
  구분되는 별도 source identity를 가진다.
- Element connectivity, set membership, instance `PART`, section `ELSET`/`MATERIAL`, assembly
  set `INSTANCE`, boundary/load target은 Domain finalization 전에 모두 resolve되어야 한다.
- Duplicate definition은 `duplicate-entity`; unresolved reference는 `unresolved-reference`로
  진단하고 partial Domain을 해석에 넘기지 않는다.

## Model Data Mapping

### Stable source identity and instances

Part-local node, element, set은 instance expansion 뒤 다음 identity를 갖는다.

```text
SourceEntityId { instance_name, source_label }
```

`instance_name`과 `source_label`은 원래 spelling을 보존한다. Case-insensitive lookup key와
표시용 raw text는 별개다. Stable Domain internal index는 input declaration order,
instance declaration order, part-local entity declaration order의 결정적 순서로 부여하며
thread count나 hash iteration order에 의존하지 않는다. 같은 part의 두 identity instance는
source label이 같아도 instance name이 다르므로 서로 merge하지 않는다.

Assembly-level `*NSET`/`*ELSET`의 `INSTANCE`는 정확히 하나의 identity instance를
지정하고 data의 label을 그 instance가 참조하는 part-local source label로 resolve한다.
따라서 assembly set member도 `(instance_name, source_label)`로 결정된다. V0는 한 set에
여러 instance의 member를 섞는 grammar를 제공하지 않는다.

### Nodes, elements, sets, material, and section

- Node는 global Cartesian coordinate `[X,Y,Z]`, stable internal node ID와 source identity를
  가진다.
- B33 element는 ordered connectivity `[node_1,node_2]`, stable internal element ID,
  source identity, general-section assignment를 가진다. Connectivity order가 local tangent
  `t=(X2-X1)/L`의 방향을 정한다.
- Explicit set은 data order, generated set은 inclusive `first,last,increment` expansion
  order를 보존한다. Membership은 internal ID에 연결되지만 source identity로 round-trip
  가능해야 한다.
- Material은 정확한 입력 `E,nu`를 보존하고 `G=E/(2(1+nu))`를 유도한다.
- General section 첫 row는 정확히 `[A,I11,I12,I22,J]`다. V0는 `I12`의 exact numeric
  zero만 허용하고 `Iy=I11`, `Iz=I22`로 매핑한다.
- Guide vector `a=n1`을 tangent에 직교 투영·정규화하여 local `y`로, `t x n1`을 local
  `z`로 둔다. `(x,y,z)`는 right-handed다.

### Model validation

모든 값은 finite여야 하고 다음 조건을 element calculation 전에 검사한다.

```text
E > 0
G = E / (2 * (1 + nu)) > 0
A > 0, Iy > 0, Iz > 0, J > 0
L > 1e-12 * max(1, norm(X1), norm(X2))
norm(a_perp) > 1e-12 * max(1, norm(a))
```

`a_perp = a - dot(a,t)*t`다. 길이 조건 실패는 `invalid-beam-length`, zero guide vector나
projection 조건 실패는 `invalid-beam-guide-vector`, nonpositive property는
`invalid-beam-property`, `I12!=0`은 `unsupported-coupled-section` model diagnostic이다.
Fallback axis, absolute-value 보정 또는 B31 재해석은 허용하지 않는다.

## History Data Mapping

V0는 하나의 history object, canonical `Step-1`만 만든다. `*STATIC`의 네 legacy field는
각각 `initial_increment`, `time_period`, `minimum_increment`, `maximum_increment`로
보존한다. 모두 finite positive여야 하고 `minimum_increment <= maximum_increment`여야
한다. 이 값들은 입력 호환성과 provenance를 위한 값이며 V0가 increment loop나 여러
frame을 생성하게 하지 않는다. 결과는 항상 `(Step-1, frame 0)`이다.

`*BOUNDARY`의 target은 node source label 또는 node set이다. Assembly set은 그
`INSTANCE`를 포함해 source node identity로 확장한다. `first_dof <= last_dof`이고 두 값은
1..6이어야 한다. 생략된 value는 exact zero다. Model-level boundary와 step-level boundary는
sole step에 활성화되며 같은 expanded node/DOF에 서로 다른 value가 생기면 input error다.

`*CLOAD` target resolution은 boundary와 같고 각 row는 해당 target의 모든 node에 global
component load를 적용한다. 같은 node/DOF로 resolve되는 여러 load row는 input order로
결정적으로 합산한다.

| DOF | nodal kinematic component | boundary unit dimension | CLOAD component | load unit dimension |
| ---: | --- | --- | --- | --- |
| 1 | `UX` | length | `FX` | force |
| 2 | `UY` | length | `FY` | force |
| 3 | `UZ` | length | `FZ` | force |
| 4 | `URX` | radian | `MX` | force x length |
| 5 | `URY` | radian | `MY` | force x length |
| 6 | `URZ` | radian | `MZ` | force x length |

Abaqus output request는 history activation이나 output selection으로 매핑하지 않는다.
Allowlisted request의 존재 여부와 무관하게 mandatory HDF5 dataset은 모두 생성된다.

## Internal Model Contract

- Domain은 nodes, B33 elements, sets, material, section, boundary, load와 sole static step의
  완전한 semantic definition을 소유하고 mapping 이후 effectively immutable하다.
- AnalysisModel은 sole step에 활성인 Domain IDs/references만 제공하며 Domain object를
  복사하지 않는다.
- Node/element는 equation ID를 소유하지 않는다. DOF numbering과 constrained/free mapping은
  이 I/O 계약 밖의 DofManager 책임이다.
- Boundary와 load는 expansion 전 target identity와 expansion 후 stable node identity를 모두
  추적할 수 있어야 한다.
- Result identity는 source identity와 stable internal identity를 모두 보유해야 하며
  parser의 raw keyword string을 solver physics object로 사용하지 않는다.
- Endpoint equilibrium action, endpoint section resultant, Gauss generalized result,
  assembled residual reaction은 서로 다른 quantity/location identity다.

## Output HDF5 Schema

Authoritative output은 하나의 HDF5 file `results.h5`, schema version `0`이다. 아래의
`<step-name>`은 V0에서 literal `Step-1`이다. 모든 string은 UTF-8, 모든 물리 실수는
IEEE-754 float64, stable internal ID는 uint64다.

### Metadata and model identity

`/metadata`는 group이며 다음 scalar attributes를 반드시 가진다.

| attribute | required value or meaning |
| --- | --- |
| `schema_version` | `0` |
| `feature_id` | `linear-static-3d-euler-beam` |
| `solver_version` | 실행 solver version string |
| `source_input_identity` | normalized input path와 content identity를 추적 가능한 string |
| `unit_system_label` | ordinary run은 `user-consistent-unspecified` |
| `coordinate_convention` | `global-cartesian; beam-local=(t,n1,t-cross-n1)` |
| `element_formulation` | `B33-3D-Euler-Bernoulli` |
| `step_name` | `Step-1` |
| `frame_index` | integer `0` |

승인 legacy comparison은 `.inp`에서 SI를 추론하지 않는다. Reference Model Contract와
verification report가 model `cantilever-beam-b33`에 외부 `SI` provenance를 적용한다.

| dataset path | shape and dtype | row identity / fields | required attributes and ordering |
| --- | --- | --- | --- |
| `/model/nodes` | `[node_count]` compound | `internal_node_id:uint64`, `instance_name:utf8`, `source_label:utf8`, `coordinates:float64[3]` | internal node ID ascending; `coordinate_system=global-cartesian`, `units_label=length` |
| `/model/elements` | `[element_count]` compound | `internal_element_id:uint64`, `instance_name:utf8`, `source_label:utf8`, `node_internal_ids:uint64[2]`, `local_axes:float64[3,3]` | internal element ID ascending; axes row order `[x,y,z]`, `formulation=B33-3D-Euler-Bernoulli` |

### Step/frame result datasets

| quantity | exact dataset path | shape | row identity and component order | units/coordinates/location |
| --- | --- | --- | --- | --- |
| displacement | `/steps/<step-name>/frames/0/nodal/displacement` | `[node_count,6]` float64 | `/model/nodes` row; `[UX,UY,UZ,URX,URY,URZ]` | `[length,length,length,radian,radian,radian]`; global; nodal |
| reaction | `/steps/<step-name>/frames/0/nodal/reaction` | `[node_count,6]` float64 | `/model/nodes` row; `[RF1,RF2,RF3,RM1,RM2,RM3]` | `[force,force,force,force*length,force*length,force*length]`; global; nodal |
| equilibrium end action | `/steps/<step-name>/frames/0/element/end_force_local` | `[element_count,2,6]` float64 | element row, endpoint `[-1,+1]`; `[FX,FY,FZ,MX,MY,MZ]` | `[force x3, force*length x3]`; beam local; endpoint outward action |
| endpoint section resultant | `/steps/<step-name>/frames/0/element/section_resultant` | `[element_count,2,4]` float64 | element row, endpoint `[-1,+1]`; `[N,T,My,Mz]` | `[force,force*length,force*length,force*length]`; beam local; positive-local-x section cut |
| generalized strain | `/steps/<step-name>/frames/0/element/generalized_strain` | `[element_count,2,4]` float64 | element row, Gauss point `[-1/sqrt(3),+1/sqrt(3)]`; `[epsilon0,kappa_x,kappa_y,kappa_z]` | `[1,1/length,1/length,1/length]`; beam local; integration point |
| generalized resultant | `/steps/<step-name>/frames/0/element/generalized_resultant` | `[element_count,2,4]` float64 | element row, same Gauss points; `[N,T,My,Mz]` | `[force,force*length,force*length,force*length]`; beam local; integration point |
| axial stress | `/steps/<step-name>/frames/0/element/stress_s11` | `[stress_row_count]` compound | `internal_element_id`, `gauss_point_index`, `section_point_index`, `x1`, `x2`, `source`, `S11` | element/Gauss/section order; `units_label=force/length^2`, beam local section point |
| diagnostics | `/diagnostics` | `[diagnostic_count]` compound | `severity`, `code`, `file`, `line`, `keyword`, `entity_identity`, `message` | deterministic diagnostic order; empty dataset is required when count is zero |

각 numeric result dataset은 `component_names`, `component_unit_dimensions`,
`coordinate_system`, `location`, `step_name=Step-1`, `frame_index=0` attributes를 가진다.
Endpoint axis order는 `xi=-1` then `xi=+1`, Gauss axis order는 negative then positive다.
Stress row는 internal element ID, Gauss index 1..2, input section-point index 순으로 정렬한다.
입력 section point가 없으면 `(x1,x2)=(0,0)`, index `0`, `source=fesa-default`인 centroid
row 하나를 각 Gauss point에 쓴다. 입력 point는 index 1부터 원래 data order를 따른다.

Reaction은 모든 node와 여섯 component에 존재하며 free DOF의 residual도 포함한다.
Transverse/torsional shear stress dataset이나 averaged nodal stress는 만들지 않는다.
`S11`은 mandatory HDF5 output이지만 Abaqus reference comparison은 명시적 N/A다.

### Atomic finalization

Writer는 requested final path와 같은 directory에 temporary HDF5를 만들고 모든 mandatory
group/dataset/attribute 작성, flush, close, schema self-check가 성공한 뒤에만 final path를
원자적으로 교체한다. 실패하면 temporary artifact는 비권위적이며 기존 final file이
있다면 그대로 보존한다. 실패한 새 run은 불완전한 `results.h5`를 남기지 않고 exit code
`6`과 HDF5 diagnostic을 반환한다.

## FESA HDF5 to Reference CSV Comparison Schema

Authoritative 비교는 위 HDF5 rows와 read-only Abaqus CSV rows 사이에서만 수행한다.
HDF5에서 추출한 deterministic CSV view는 debugging/review 보조물이며 solver output이나
reference artifact가 아니다.

### Approved legacy artifact precheck

Comparison 전에 다음 exact files가 존재하고 변경되지 않았음을 확인한다.

- `reference/cantilever beam/cantilever beam.inp`
- `reference/cantilever beam/cantilever beam displacements.csv`
- `reference/cantilever beam/cantilever beam reactions.csv`
- `reference/cantilever beam/cantilever beam elemental forces.csv`

Input은 `TYPE=B33`이어야 하고 trim한 CSV header는 아래 mapping 표와 정확히 일치해야
한다. 각 projected row key는 unique하고 모든 numeric value는 finite여야 한다. 파일
missing/type mismatch는 `needs-reference-artifacts`, header/value/identity mismatch는
`schema-mismatch`이며 comparison은 시작하지 않는다. `metadata.json` 부재는 프로젝트
전역 정책에 따라 허용되고, `README.md`는 이 legacy bundle에서 N/A다. 파일을 rename,
rewrite, zero-clamp 또는 보정하지 않는다.

### Header and component normalization

| legacy file/header | canonical quantity/component | FESA HDF5 source |
| --- | --- | --- |
| `U-U1`, `U-U2`, `U-U3` | displacement `UX`, `UY`, `UZ` | `nodal/displacement` columns 0..2 |
| `UR-UR1`, `UR-UR2`, `UR-UR3` | displacement `URX`, `URY`, `URZ` | `nodal/displacement` columns 3..5 |
| `RF-RF1`, `RF-RF2`, `RF-RF3` | reaction `RF1`, `RF2`, `RF3` | `nodal/reaction` columns 0..2 |
| `RM-RM1`, `RM-RM2`, `RM-RM3` | reaction `RM1`, `RM2`, `RM3` | `nodal/reaction` columns 3..5 |
| `SF-SF1` | section resultant `N` | `element/section_resultant` component `N` |
| `SM-SM1` | section resultant `My` | `element/section_resultant` component `My` |
| `SM-SM2` | section resultant `Mz` | `element/section_resultant` component `Mz` |
| `SM-SM3` | section resultant `T` | `element/section_resultant` component `T` |

General Abaqus beam component meaning and FESA local mapping은 다음과 같다.

| Abaqus component | equilibrium end-action component | section-resultant component |
| --- | --- | --- |
| `SF1` | `FX` | `N` |
| `SF3` | `FY` | N/A in `[N,T,My,Mz]` |
| `SF2` | `FZ` | N/A in `[N,T,My,Mz]` |
| `SM3` | `MX` | `T` |
| `SM1` | `MY` | `My` |
| `SM2` | `MZ` | `Mz` |

이 general mapping은 component axis의 의미다. Outward equilibrium end action과
positive-local-x-face section resultant의 sign identity를 합치지 않는다. 승인 legacy
CSV는 `SF1/SM1/SM2/SM3`만 제공하고 section-cut result와 비교하므로 `SF2`/`SF3` row를
0으로 만들거나 reference row로 합성하지 않는다. HDF5의 transverse end action은
unit/analytical test와 physics sanity 대상이다.

### Frame, instance, and node-station normalization

- Legacy `Frame` value `Increment 1: Step Time = 1.000`은 canonical `(Step-1, frame 0)`으로
  변환한다. 승인 bundle의 다른 increment/time string은 `schema-mismatch`다.
- `Part Instance Name`은 HDF5 `/model/nodes` 또는 `/model/elements`의 exact preserved
  `instance_name`과 case-insensitive lookup 후 raw identity 일치 여부를 확인한다.
- `Node Label`은 instance 안의 preserved source node label로 resolve한다. Displacement와
  reaction key는 `(model_id,Step-1,0,instance_name,source_node_label,quantity,component)`다.
- Elemental-force CSV는 element label이 없으므로 HDF5 endpoint section resultants를
  source node station으로 project한다. Boundary station은 유일한 incident endpoint를 쓴다.
- Interior station collapse는 정확히 두 incident B33 endpoints, 동일 section/local-axis
  orientation, 일관된 chain connectivity, 해당 node의 concentrated force/moment가 없는
  경우에만 허용한다. 두 positive-face section-cut 값이 아래 승인 component tolerance
  안에서 먼저 일치해야 한다.
- Interior 값이 일치하면 stable internal element ID가 작은 endpoint를 deterministic
  representative로 선택한다. 두 값을 평균하지 않는다. 불일치는 `tolerance-failure`다.
- Reversed connectivity, local-axis discontinuity, section jump, branch 또는 loaded interior
  station은 element label 없는 legacy schema로 collapse할 수 없다. 이 approved bundle
  밖에서는 element-aware reference row가 필요하며 legacy projection은 `schema-mismatch`로
  중단한다.

Projected canonical comparison row는 다음 fields를 가진다.

```text
model_id, step_name, frame_index, instance_name, source_node_label,
quantity, component, value, unit_dimension, coordinate_system, hdf5_dataset_path
```

Stable ordering은 quantity inventory order, instance declaration order, stable source node
order, component order다. Approved model ID는 `cantilever-beam-b33`; nodal quantities는
global Cartesian, section resultants는 beam local이다.

### Row-set precheck and tolerance

먼저 양쪽을 legacy contract가 실제 제공하는 quantity/component projection으로 제한한다.
그 projection 안에서는 missing, extra, duplicate, nonfinite, schema-mismatched 또는
identity-mismatched row가 하나라도 있으면 tolerance 계산 전에 실패한다. HDF5에만
mandatory인 `SF2/SF3` 대응 end action, generalized results와 `S11`은 계약상 비교 대상이
아니므로 extra reference row가 아니다.

Matched rows는 같은 `model_id`, step/frame, quantity, component로 group한다.

```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
```

즉 exact policy는 `absolute_floor + 1e-6 * reference_scale`이다. `reference_scale`은
read-only Abaqus values만 사용하고 FESA 값으로 조정하지 않는다. Scale이 zero면 relative
term은 zero다. Reference value나 작은 residue를 zero-clamp하지 않고 모든 row를 판정한다.

| approved SI component class | absolute floor |
| --- | ---: |
| displacement and rotation | `1e-9` |
| force and moment | `1e-3` |

Interior endpoint consistency도 해당 CSV quantity/component의 same Abaqus-only scale과
floor를 사용한다. Verification report는 모든 row pass/fail과 quantity별 max absolute
error, component-scale normalized error, RMS error, norm error, worst row/component를
기록한다. 이 reference tolerance는 analytical/formulation tolerance를 대체하지 않는다.

## CLI and Diagnostics Contract

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

`--output`을 생략하면 현재 작업 directory의 `results.h5`가 final path다. Input path는
정확히 하나이며 unknown option, missing argument, repeated `--output`은 usage error다.

| exit code | meaning |
| ---: | --- |
| `0` | success; authoritative HDF5 finalization complete |
| `2` | CLI usage error |
| `3` | input syntax or semantic mapping error, including unsupported keyword/formulation/wrapper |
| `4` | model validation error, including geometry, section or property invalidity |
| `5` | factorization or substitution error |
| `6` | HDF5 write, validation or atomic finalization error |

각 stderr diagnostic record는 exact field order `severity`, `code`, `file`, `line`,
`keyword`, `entity_identity`, `message`를 사용한다. Source-backed diagnostics는 normalized
file path와 1-based line을 가지며, source가 없는 solver/output error는 빈 source field를
명시한다. 출력 순서는 source file declaration order, line, entity stable internal ID,
diagnostic code의 tuple로 결정하고 같은 tuple에서는 discovery order를 보존한다. Warning은
성공 run에서도 `/diagnostics`에 기록되며 error가 있으면 해당 exit class를 반환한다.

## Validation Rules

| validation area | required pass condition | failure class |
| --- | --- | --- |
| keyword inventory | supported 또는 exact warning no-op allowlist에 속함 | input, exit 3 |
| nesting/cardinality | closed part/assembly/instance/step; one assembly and one static step | input, exit 3 |
| source identity | duplicate 없음; all references resolve; multi-instance identity unique | input, exit 3 |
| B33 semantics | only `TYPE=B33`; two distinct connected nodes | input/model, exit 3 or 4 |
| section/material | exact data arity/mapping, `I12=0`, all required positive properties | model, exit 4 |
| geometry/local axes | approved length/projection thresholds and right-handed axes | model, exit 4 |
| history | DOF 1..6, resolved targets, finite values, sole canonical frame | input, exit 3 |
| HDF5 | all exact paths, shapes, attributes, identities and finite result values | output, exit 6 |
| legacy artifacts | exact four files, B33, exact headers, unique finite rows | reference precheck |
| comparison | exact projected row-set, endpoint eligibility/consistency, approved tolerance | reference verification |

No parser/HDF5 implementation, reference artifact mutation, Abaqus execution, physics pass 또는
release approval을 이 status가 의미하지 않는다.

## Open Issues and Downstream Handoff

### Resolved numerical-review handoff

`NR-O03-STATION-NORMALIZATION`은 approved legacy bundle에 대해 unloaded, consistently
oriented two-endpoint interior station만 collapse하고, tolerance check 후 smaller stable
element ID를 선택하는 규칙으로 구체화했다. Reversed/branched/loaded/jumped station은
element-aware reference가 없는 한 비교하지 않는다.

### Reference Model Agent

- Exact legacy inventory, generator `Abaqus/CAE Learning Edition 2024`, source commit
  `2b34d0b`, external SI provenance와 stress comparison N/A를 계약에 고정한다.
- 추가 reference model은 canonical filenames와 metadata를 사용하며 이 legacy file을
  변경하지 않는다.

### Implementation Planning Agent

- Keyword별 positive/negative grammar, source location, duplicates/dangling references,
  B31/transform/nested/multiple-step/NLGEOM diagnostics를 TDD cases로 만든다.
- Exact HDF5 path/shape/component/identity, mandatory-output independence와 atomic failure를
  검증한다.
- C++ API는 이 semantic contract에서 파생하되 여기에 소급해 API 설계를 추가하지 않는다.

### Reference Verification Agent

- Artifact precheck 뒤 HDF5-to-legacy projection, node-station eligibility, row-set equality,
  component-scale comparison 순서를 유지한다.
- Missing/extra/nonfinite row를 무시하거나 `SF2/SF3`/stress reference row를 합성하지 않는다.

