# 아키텍처 ## 목표 FESA의 아키텍처 목표는 Abaqus `.inp` subset을 내부 semantic model로 변환하고, 유한요소 equation system을 구성해 구조해석 결과를 HDF5로 저장하며, reference comparison과 physics sanity가 가능한 C++17/MSVC 솔버 구조를 제공하는 것이다. 핵심 품질 속성: - 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 src/ fesa/ core/ # ids, status, diagnostics, units, small value types io/ abaqus/ # .inp lexer/parser, keyword subset, include policy hdf5/ # HDF5 result writer/reader, schema versioning model/ # semantic model: nodes, elements, sets, materials, sections, steps fem/ # DOF space, equation numbering, quadrature, shape functions elements/ # truss/bar, beam, plane, solid, shell element routines materials/ # elastic/plastic material contracts and state variables assembly/ # local-to-global mapping, sparse pattern, COO/CSR assembly constraints/ # essential BC, MPC, penalty or elimination policies solvers/ linear/ # MKL PARDISO backend, iterative backend boundary nonlinear/ # Newton control, residual/tangent norms, increments analysis/ # static, modal, dynamic, nonlinear procedure drivers results/ # recovery, field/history output, diagnostics validation/ # comparison metrics and tolerance helpers math/ # owning Vector, row-major Matrix, CSR SparseMatrix adapters tests/ unit/ integration/ reference/ reference/ / model.inp metadata.json _displacements.csv _reactions.csv _internalforces.csv _stresses.csv .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 ``` ## Harness Execution Layer Harness는 solver core와 분리된 세 계층의 개발 운영 인프라다. - 계획 계층: `.agents/skills/harness`가 사용자 승인 전 Step 초안을 만들고, 승인 후 `phases/` 파일을 생성한다. - 실행 계층: `scripts/execute.py`가 `feat-` 브랜치에서 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로 삼는다. ## 모듈 경계 - `core`는 외부 라이브러리에 의존하지 않는다. - `io/abaqus`는 syntax와 semantic mapping만 담당하고 해석 알고리즘을 알지 않는다. - `model`은 Abaqus keyword 문자열이 아니라 solver semantic model을 가진다. - `fem`은 DOF, interpolation, quadrature, local/global mapping을 제공하되 특정 analysis procedure에 종속되지 않는다. - `elements`와 `materials`는 local residual/tangent/stress recovery 계약을 제공한다. - `assembly`는 sparse pattern 생성과 local contribution 조립을 담당한다. - `constraints`는 essential BC, MPC, penalty/elimination 정책을 분리한다. - `solvers`는 MKL/TBB 세부 구현을 감추는 backend boundary를 가진다. - `analysis`는 step/history data를 받아 procedure를 실행하고 solver backend와 result writer를 조율한다. - `results`는 HDF5 schema를 통해 nodal, element, integration-point, diagnostic output을 분리한다. - 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 ├── Node ├── Element ├── Material ├── Property ├── NodeSet ├── ElementSet ├── BoundaryCondition ├── Load └── StepDefinition AnalysisModel ├── active elements ├── active loads ├── active boundary conditions ├── active properties/materials └── equation system view AnalysisState ├── displacement U ├── velocity V ├── acceleration A ├── temperature T ├── external force Fext ├── internal force Fint ├── residual R ├── current time / increment / iteration └── element state / integration point state DofManager ├── node dof definitions ├── constrained/free dof mapping ├── equation numbering ├── sparse matrix pattern ownership └── full/reduced vector reconstruction Analysis ├── LinearStaticAnalysis ├── NonlinearStaticAnalysis ├── DynamicAnalysis ├── FrequencyAnalysis └── HeatTransferAnalysis Element ├── Element1D │ ├── Truss │ └── Beam ├── Element2D │ ├── MITC3 │ └── MITC4 └── Element3D ├── Hexahedral ├── Tetrahedral ├── Wedge └── Pyramid BoundaryCondition ├── Fix ├── RBE2 └── RBE3 Load ├── NodalLoad ├── PressureLoad └── BodyForce Results ├── ResultStep ├── ResultFrame ├── FieldOutput └── HistoryOutput Vector Matrix SparseMatrix ``` 이 객체 모델 중 V0 `Analysis` 구현은 `LinearStaticAnalysis` 하나이며 `AnalysisState`는 full displacement, external/internal force, residual, constrained reaction, step/frame identity와 element recovery rows만 할당한다. Velocity, acceleration, temperature, iteration history와 nonlinear element state는 V0에서 할당하지 않는다. ## 상태 관리 - `Domain`은 입력 파일에서 만들어진 전체 모델 정의를 소유한다. 파싱 이후에는 가능한 한 불변으로 취급한다. - `AnalysisModel`은 현재 step에서 활성화되는 해석 객체들의 실행 view이다. `Domain`을 복사하지 않고 참조 또는 id 기반 view로 구성한다. - `DofManager`는 자유도와 방정식 번호를 전담한다. `Node` 또는 `Element` 내부에 equation id를 분산 저장하지 않는다. - `AnalysisState`는 해석 중 변하는 물리량과 반복 상태를 소유한다. Phase 1에서는 displacement 중심으로 최소 구현하되, 기하비선형과 thermal-stress coupling을 위해 element/internal state 확장 지점을 유지한다. - 결과는 `ResultStep` -> `ResultFrame` -> `FieldOutput`/`HistoryOutput` 구조로 관리한다. ## 데이터 흐름 ```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 복구 -> reaction = K*d - F 및 element result 복구 -> ResultsWriter로 results.h5 atomic finalization ``` 강성행렬 factorization은 하중벡터 조립보다 먼저 수행한다. 반력은 element end action의 별도 합이 아니라 조립된 전체 residual에서 구한다. ## 해석 실행 흐름 `Analysis::run()`은 Template Method로 다음 V0 흐름을 고정한다. 해석 종류별 class는 승인된 procedure contract 안에서 필요한 단계만 재정의한다. ```text initialize buildAnalysisModel buildDofMap buildSparsePattern assembleStiffness partitionConstraints factorize assembleLoads formEffectiveRhs substitute reconstructDisplacement recoverResults writeResults ``` 비선형 정적 및 동적 해석은 V0 범위가 아니며 별도 ADR과 formulation을 승인한 뒤 이 lifecycle의 확장 지점을 사용한다. ## 설계 패턴 - Strategy Pattern: `Analysis`, `LinearSolver`, `TimeIntegrator`, `ConvergenceCriteria`를 교체 가능한 전략으로 둔다. - Template Method Pattern: `Analysis::run()`은 공통 실행 흐름을 고정하고 세부 단계는 procedure별로 재정의한다. - Factory + Registry Pattern: Abaqus keyword와 내부 객체 생성을 분리한다. 예: `*Element, type=S4` -> `MITC4ElementFactory`. - Adapter Pattern: MKL, TBB, HDF5 API는 solver core에 직접 노출하지 않는다. - Runtime Polymorphism: 요소, 재료, 하중, 경계조건은 base interface를 통해 다룬다. 대규모 모델 성능 최적화가 필요하면 assembly 내부에서 타입별 batch 처리 또는 kernel 분리를 추가한다. - RAII: MKL handle, HDF5 file/dataset, temporary solver workspace의 수명과 오류 처리를 wrapper에 묶는다. ## Sparse Matrix Policy - assembly는 초기에는 COO triplet 수집 후 CSR finalize를 기준으로 한다. - `SparseMatrix`는 0-based CSR 데이터를 소유하는 별도 타입이며 dense `Matrix`를 상속하지 않는다. MKL PARDISO backend는 adapter 경계에서 필요한 descriptor와 indexing을 변환한다. - matrix symmetry, definiteness, singularity diagnostic을 구조화된 diagnostic으로 남긴다. - deterministic assembly를 위해 TBB element loop는 thread-local contribution buffer 또는 two-pass sparse assembly를 사용한다. ## 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 - 첫 번째 oneTBB 적용 지점은 element-local matrix/residual 계산이다. - 전역 sparse write는 thread-local buffer 또는 deterministic reduction으로 제한한다. - MKL 내부 thread와 TBB element loop가 oversubscription을 만들지 않도록 thread count와 task arena 정책을 명시한다. ## HDF5 Result Schema ```text /metadata /model/nodes /model/elements /steps//frames/0/nodal/displacement /steps//frames/0/nodal/reaction /steps//frames/0/element/end_force_local /steps//frames/0/element/section_resultant /steps//frames/0/element/generalized_strain /steps//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는 `reference//` 아래 CSV 파일이다. - Verification은 documented IDs, components, units, coordinate system, step/frame identity, tolerance 기준으로 FESA HDF5 rows와 Abaqus reference CSV rows를 비교한다. - FESA HDF5에서 추출한 deterministic CSV view는 optional debugging/review artifact이며 공식 solver output 또는 reference artifact가 아니다. - Writer는 임시 HDF5 파일을 완성한 뒤 최종 경로로 교체하며 실패 시 불완전한 `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 --output ``` `--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한 순서로 출력한다.