# FESA 시스템 구조 ## 구성과 의존성 FESA는 로컬 모델 파일을 읽어 해석하고 HDF5 결과를 저장하는 C++ 솔버다. 현재 공개 실행 경로는 하나의 CLI이며, 내부 정적 library와 테스트 target이 같은 core를 사용한다. 개발용 Python 실행기는 솔버 runtime의 의존성이 아니다. ```text fesa_cli └─ app: 경로·출력 쓰기 소유권·실행 조합 ├─ io/abaqus: 입력 읽기 → syntax → semantic mapping │ └─ model: Domain·source identity·재료/단면/step 정의 └─ analysis: 준비된 모델 → procedure별 실행 → 완료 candidate ├─ AnalysisModel: Domain의 non-owning view ├─ fem / constraints: DOF·equation·partition ├─ elements / materials / properties / loads: local 계산 ├─ assembly: 결정적인 global 누적 ├─ math / solvers: storage·PARDISO·FEAST └─ results: 물리 결과 복구·state 반영 └─ io/hdf5: candidate 파일 → self-check·close → 최종 교체 ``` Consumer는 backend-neutral contract를 통해 값을 주고받는다. MKL·TBB·HDF5·Win32 header와 handle은 private implementation에 머문다. 새 물리나 backend를 추가할 때 core가 외부 adapter를 참조하는 방향으로 의존성을 뒤집지 않는다. ## 모듈 경계 | 영역 | 소유하는 책임 | 경계 밖의 책임 | | --- | --- | --- | | `include/fesa` | 공개 선언, 값·실패·수명 계약 | Vendor handle, 실행별 equation 저장 | | `app` | CLI 경로, output lease, 객체 조합과 외부 종료 | 요소 정식화, 수렴 기준 결정 | | `core` | Source identity, ASCII 처리, Status·Diagnostic | 입력 procedure 허용 범위 | | `io/abaqus` | 문법·scope·semantic mapping, Domain 생성 | 해법, 결과 복구 | | `model` | 전체 정의, target 해석, 초기 shell geometry | Mutable solution, global equation 번호 | | `properties` | 재료 연결과 beam/shell 단면 정의 | 물성 평가식, state 수명 | | `materials` | 구성 관계와 필요한 물성 capability | Element topology, analysis state | | `elements` | Local 행렬·힘·관성·회복에 필요한 응답 | Global CSR 갱신, 파일 접근 | | `loads` | 하중 의미·configuration·local contribution | Global 공유 누적, 입력 target 이름 해석 | | `fem` | DOF, scatter, full/free/constrained mapping, pattern | 선형계 factorization | | `constraints` | 지정값 적용, partition·대입 정책 | 물리 결과 재해석 | | `assembly` | Sparse·하중·압력 contribution의 ordered reduction | Equation 번호 발급, 수렴 판정 | | `analysis` | Procedure별 초기화·증분·반복·종료·state lifecycle | Concrete 저장 형식, vendor resource 공개 | | `math` | Vector·Matrix·SparseMatrix, private BLAS adapter | 모델 의미, source target lookup | | `solvers/linear` | PARDISO factorization·solve, resource와 실패 변환 | 경계조건 변경, 모델 수정 | | `solvers/eigen` | FEAST 실행과 수치 제어의 backend 경계 | Source EIGENSOLVER의 재해석, 외부 비교 판정 | | `results` | Location·부호·좌표가 명확한 복구와 candidate commit | HDF5 layout, reference 보정 | | `io/hdf5` | Schema serialization·readback·최종 파일 교체 | 물리량 정의, acceptance 완화 | 같은 module의 public 선언은 `include/fesa/`, implementation은 `src/fesa/`에 나뉜다. 이 물리적 분리가 interface와 implementation의 소유권을 둘로 나누지는 않는다. Parser가 concrete numerical storage를 생성·조작하는 대신 semantic 정의와 검증된 생성 경계로 연결한다. ## 대표 실행 흐름 1. App이 단일 입력 경로에서 lexical sibling 결과·로그 경로를 만들고 output lease를 획득한다. Lease를 얻기 전에 공개 파일을 truncate하지 않는다. 2. 입력을 한 번 읽고 source 위치·raw 표기와 함께 syntax를 해석한다. Mapper가 target, element/section/material 및 procedure 조합을 검증해 Domain을 만든다. 3. 준비 단계가 static, nonlinear static, frequency 또는 HHT executor로 분기한다. Domain을 복사해 별도 모델을 만들지 않는다. 4. 선택된 procedure가 active view, equation과 workspace를 구성하고 local 계산, ordered assembly, constraint, solve 및 필요한 반복을 수행한다. 5. 해와 recovery를 검증해 procedure 전용 state에 반영한다. 비선형·동적 trial은 이전 committed state/history와 독립적이며 실패한 trial은 공개하지 않는다. 6. 완료 outcome을 얻은 뒤 로그 close를 확인하고 해당 writer를 호출한다. Writer는 임시 파일에 기록·검사·close를 마친 뒤 최종 HDF5를 교체한다. State commit과 파일 교체는 별개의 실패 경계다. 일부 frame을 내부에서 accept했더라도 전체 해석이 실패했다면 미완성 이력을 새로운 정상 결과 파일로 공개하지 않는다. 반면 실행 로그는 실패를 설명하기 위해 남을 수 있어 최종 결과와 보존 범위가 다르다. ## 해석 모델과 수명 Domain이 모델 정의를 소유한다. AnalysisModel, DOF 구성에 사용되는 참조와 실행 객체가 Domain보다 오래 살아서는 안 된다. DofManager는 topology에서 물리 DOF와 equation 순서를 구성하며 node/element 내부에 equation 번호를 배포하지 않는다. 현재 procedure마다 static, nonlinear, eigen, dynamic state를 구별한다. 공통된다는 이유로 사용하지 않는 가속도·plastic state·thermal field를 일괄 추가하지 않는다. 새 procedure는 필요한 lifecycle을 정의한 뒤 준비 단계에서 연결한다. 기존 static 반복문에 조건문으로 물리를 누적하지 않는다. Source identity, internal entity index, DOF component, equation index와 결과 위치는 서로 다른 경계의 식별자다. Assembly와 serialization 사이에서 row 순서를 바꾸더라도 source와 결과의 대응은 명시적으로 유지한다. ## 계산과 병렬화 Element/local load 계산은 index-owned buffer에 기록한다. TBB 작업을 join한 뒤 stable COO ordering과 고정된 reduction으로 sparse matrix와 global vector를 만든다. Global storage를 여러 worker가 직접 수정하지 않으므로 thread scheduling이 합산 순서를 정하지 않는다. 진단 선택과 결과 row의 순서도 같은 원칙으로 관리한다. MKL은 private dense 연산과 PARDISO·FEAST backend에서 사용한다. Linear solver와 tangent solver의 matrix 성질을 구별하고, eigen finite-space reduction은 해석 모델의 책임으로 유지한다. Backend 선택이 물성·경계조건·질량을 바꾸는 fallback이 되지 않는다. ## 입력, 결과와 진단 입력 adapter는 syntax뿐 아니라 procedure별 허용 의미까지 검사한다. Source 이름· label·raw spelling은 보존하고 내부 정렬과 결과 schema의 고정 storage 이름을 분리한다. 무시 가능한 출력 요청은 허용된 조합에만 적용한다. Recovery는 물리량의 위치·좌표·단위·부호를 결정하고 writer는 이를 저장한다. Final output은 입력의 lexical sibling인 `_results.h5`다. Log와 debug CSV는 다른 역할이며 공식 HDF5를 대신하지 않는다. HDF5 self-check와 solver의 물리 검증은 서로 다른 책임이다. Expected failure는 Status/Result로 이동하며 source location과 entity를 포함한 Diagnostic으로 변환한다. App이 공개 오류 출력과 종료값을 결정한다. Vendor 코드가 public exit code나 사용자 입력의 의미를 직접 결정하지 않는다. ## 빌드와 개발 실행 영역 CMake는 `fesa_solver` static library, `fesa_cli` 실행 파일과 C++ 테스트를 구성한다. Project-owned dependency target이 MKL·TBB·HDF5의 package target 차이를 감싼다. GoogleTest는 승인된 로컬 source checkout을 사용한다. Windows 실행 target 옆에 필요한 MKL·OpenMP·TBB·HDF5 및 관련 runtime을 준비하며 link 성공만으로 실행 환경이 완성되었다고 판단하지 않는다. Python 실행 경로는 `execute.py → config/discovery/toolchain → adapter → process`다. Adapter는 CMake/CTest 또는 MSBuild 명령 계획을 만들고 process는 argv·cwd·timeout과 결과 검사를 수행한다. FESA 제품의 검증 baseline은 CMake/MSVC x64 Debug/CTest다. Python 실행기 회귀는 별도 suite이며 CTest가 이를 자동 실행하지 않는다. 개발 작업을 조정하는 메인과 전문 agent는 파일·검증 evidence를 다루지만 솔버가 이들을 호출하지 않는다. 실행기는 내부 LLM·phase 상태기계·자동 Git·자동 retry·대화 종료 hook을 소유하지 않는다. 과거 운영 기록은 실행 가능한 현재 pipeline으로 해석하지 않는다.