Compare commits

...

56 Commits

Author SHA1 Message Date
KOKO\Mimi c80af74da9 docs: plan implementation agent Terra model 2026-08-16 21:35:54 +09:00
KOKO\Mimi 49918f60b9 docs: specify implementation agent Terra model 2026-08-16 21:21:09 +09:00
KOKO\Mimi 3b30072e00 chore(cpp-object-oriented-modular-refactoring): mark phase completed 2026-08-16 13:54:52 +09:00
KOKO\Mimi 2e634578cc chore(cpp-object-oriented-modular-refactoring): step 24 output 2026-08-16 13:54:51 +09:00
KOKO\Mimi 35866d3d4f feat(cpp-object-oriented-modular-refactoring): step 24 - final-quality-reference-gate 2026-08-16 13:54:51 +09:00
KOKO\Mimi f84ebb541f chore(cpp-object-oriented-modular-refactoring): step 23 output 2026-08-16 13:28:06 +09:00
KOKO\Mimi feaddd9e83 feat(cpp-object-oriented-modular-refactoring): step 23 - hdf5-writer-modules 2026-08-16 13:28:06 +09:00
KOKO\Mimi 5e4e3f2a5b chore(cpp-object-oriented-modular-refactoring): step 22 output 2026-08-16 12:52:11 +09:00
KOKO\Mimi f60d4edc26 feat(cpp-object-oriented-modular-refactoring): step 22 - result-recovery-modules 2026-08-16 12:52:11 +09:00
KOKO\Mimi 2b0f556a79 chore(cpp-object-oriented-modular-refactoring): step 21 output 2026-08-16 11:45:34 +09:00
KOKO\Mimi f2c150b490 feat(cpp-object-oriented-modular-refactoring): step 21 - domain-mapper-modules 2026-08-16 11:45:34 +09:00
KOKO\Mimi f37324eeaf chore(cpp-object-oriented-modular-refactoring): step 20 output 2026-08-16 11:33:08 +09:00
KOKO\Mimi be35d00f49 feat(cpp-object-oriented-modular-refactoring): step 20 - analysis-hierarchy 2026-08-16 11:33:08 +09:00
KOKO\Mimi 95c186b812 chore(cpp-object-oriented-modular-refactoring): step 19 output 2026-08-16 11:23:20 +09:00
KOKO\Mimi 64a43948ef feat(cpp-object-oriented-modular-refactoring): step 19 - boundary-condition-policy 2026-08-16 11:23:20 +09:00
KOKO\Mimi f26e0a61a8 chore(cpp-object-oriented-modular-refactoring): step 18 output 2026-08-16 10:56:57 +09:00
KOKO\Mimi 9ad72e6d21 feat(cpp-object-oriented-modular-refactoring): step 18 - load-hierarchy 2026-08-16 10:56:57 +09:00
KOKO\Mimi 08d352ae46 chore(cpp-object-oriented-modular-refactoring): step 17 output 2026-08-16 10:40:49 +09:00
KOKO\Mimi d711e6d4fd feat(cpp-object-oriented-modular-refactoring): step 17 - generic-result-recovery 2026-08-16 10:40:49 +09:00
KOKO\Mimi 31b6129cf8 chore(cpp-object-oriented-modular-refactoring): step 16 output 2026-08-16 10:24:28 +09:00
KOKO\Mimi 6b10f8a7d2 feat(cpp-object-oriented-modular-refactoring): step 16 - generic-sparse-assembler 2026-08-16 10:24:28 +09:00
KOKO\Mimi a21b991ef9 chore(cpp-object-oriented-modular-refactoring): step 15 output 2026-08-16 10:07:02 +09:00
KOKO\Mimi 9e74398655 feat(cpp-object-oriented-modular-refactoring): step 15 - generic-dof-manager 2026-08-16 10:07:02 +09:00
KOKO\Mimi c8c32236de chore(cpp-object-oriented-modular-refactoring): step 14 output 2026-08-16 09:38:30 +09:00
KOKO\Mimi aaa488211b feat(cpp-object-oriented-modular-refactoring): step 14 - runtime-element-factory 2026-08-16 09:38:30 +09:00
KOKO\Mimi ace493ee57 chore(cpp-object-oriented-modular-refactoring): step 13 output 2026-08-16 09:10:38 +09:00
KOKO\Mimi 19ba02a6a4 feat(cpp-object-oriented-modular-refactoring): step 13 - element-definition-domain 2026-08-16 09:10:38 +09:00
KOKO\Mimi cf6fc6e1d9 chore(cpp-object-oriented-modular-refactoring): step 12 output 2026-08-16 08:48:18 +09:00
KOKO\Mimi 64a7071986 feat(cpp-object-oriented-modular-refactoring): step 12 - material-property-hierarchy 2026-08-16 08:48:18 +09:00
KOKO\Mimi ec9c3e250a chore(cpp-object-oriented-modular-refactoring): step 11 output 2026-08-16 08:34:09 +09:00
KOKO\Mimi a6324a9004 feat(cpp-object-oriented-modular-refactoring): step 11 - source-target-resolver 2026-08-16 08:34:09 +09:00
KOKO\Mimi 5430fffd62 chore(cpp-object-oriented-modular-refactoring): step 10 output 2026-08-16 07:49:57 +09:00
KOKO\Mimi 89fc13c873 feat(cpp-object-oriented-modular-refactoring): step 10 - dense-blas-adapter 2026-08-16 07:49:57 +09:00
KOKO\Mimi 6b0ff31db0 chore(cpp-object-oriented-modular-refactoring): step 9 output 2026-08-16 07:40:16 +09:00
KOKO\Mimi 0be8d1bd89 feat(cpp-object-oriented-modular-refactoring): step 9 - result-io-vector3 2026-08-16 07:40:16 +09:00
KOKO\Mimi 1cb1f26cdc chore(cpp-object-oriented-modular-refactoring): step 8 output 2026-08-16 07:27:51 +09:00
KOKO\Mimi a9ff75b3fa feat(cpp-object-oriented-modular-refactoring): step 8 - element-geometry-vector3 2026-08-16 07:27:51 +09:00
KOKO\Mimi cbad5c3592 chore(cpp-object-oriented-modular-refactoring): step 7 output 2026-08-16 07:08:42 +09:00
KOKO\Mimi 060a41b2b7 feat(cpp-object-oriented-modular-refactoring): step 7 - vector3-value-type 2026-08-16 07:08:42 +09:00
KOKO\Mimi 1e8bf3546a chore(cpp-object-oriented-modular-refactoring): step 6 output 2026-08-16 06:59:50 +09:00
KOKO\Mimi 83fd1d1c7e feat(cpp-object-oriented-modular-refactoring): step 6 - io-application-google-style 2026-08-16 06:59:50 +09:00
KOKO\Mimi bb178c9d3c chore(cpp-object-oriented-modular-refactoring): step 5 output 2026-08-16 06:20:09 +09:00
KOKO\Mimi 24f006fe4a feat(cpp-object-oriented-modular-refactoring): step 5 - solver-workflow-google-style 2026-08-16 06:20:08 +09:00
KOKO\Mimi e1c0e357dd chore(cpp-object-oriented-modular-refactoring): step 4 output 2026-08-16 05:37:40 +09:00
KOKO\Mimi 8bc0ea2f8e feat(cpp-object-oriented-modular-refactoring): step 4 - model-element-google-style 2026-08-16 05:37:40 +09:00
KOKO\Mimi 34ab8b5bf1 chore(cpp-object-oriented-modular-refactoring): step 3 output 2026-08-16 04:26:15 +09:00
KOKO\Mimi 042edadffb feat(cpp-object-oriented-modular-refactoring): step 3 - foundation-google-style 2026-08-16 04:26:14 +09:00
KOKO\Mimi 2628ed3488 chore(cpp-object-oriented-modular-refactoring): step 2 output 2026-08-16 03:27:17 +09:00
KOKO\Mimi f43fbd7dd1 feat(cpp-object-oriented-modular-refactoring): step 2 - architecture-boundaries 2026-08-16 03:27:17 +09:00
KOKO\Mimi f289b437df chore(cpp-object-oriented-modular-refactoring): step 1 output 2026-08-16 03:19:17 +09:00
KOKO\Mimi a94bafbdc6 feat(cpp-object-oriented-modular-refactoring): step 1 - cpp-style-tooling 2026-08-16 03:19:17 +09:00
KOKO\Mimi 0207aa0847 chore(cpp-object-oriented-modular-refactoring): step 0 output 2026-08-16 03:10:54 +09:00
KOKO\Mimi cd2b0afc6d feat(cpp-object-oriented-modular-refactoring): step 0 - coding-style-agent-contract 2026-08-16 03:10:54 +09:00
KOKO\Mimi 2ab2e0c641 docs: add modular refactoring implementation plan 2026-08-16 02:49:35 +09:00
KOKO\Mimi 1e5758f3e4 docs: record refactoring design approval 2026-08-16 02:03:03 +09:00
KOKO\Mimi 0d9ac482ac docs: define C++ modular refactoring design 2026-08-16 01:54:41 +09:00
243 changed files with 34253 additions and 24466 deletions
+3
View File
@@ -0,0 +1,3 @@
BasedOnStyle: Google
IndentWidth: 2
ColumnLimit: 80
+25
View File
@@ -0,0 +1,25 @@
Checks: '-*,bugprone-*,clang-analyzer-*,performance-*,readability-identifier-naming'
HeaderFilterRegex: '^(include|src)/fesa/.*'
FormatStyle: file
ExtraArgs: ['-std=c++17']
CheckOptions:
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.StructCase: CamelCase
readability-identifier-naming.EnumCase: CamelCase
readability-identifier-naming.TypeAliasCase: CamelCase
readability-identifier-naming.FunctionCase: CamelCase
readability-identifier-naming.MethodCase: CamelCase
readability-identifier-naming.VariableCase: lower_case
readability-identifier-naming.ParameterCase: lower_case
readability-identifier-naming.LocalVariableCase: lower_case
readability-identifier-naming.PrivateMemberCase: lower_case
readability-identifier-naming.PrivateMemberSuffix: _
readability-identifier-naming.ProtectedMemberCase: lower_case
readability-identifier-naming.ProtectedMemberSuffix: _
readability-identifier-naming.PublicMemberCase: lower_case
readability-identifier-naming.ConstantCase: CamelCase
readability-identifier-naming.ConstantPrefix: k
readability-identifier-naming.EnumConstantCase: CamelCase
readability-identifier-naming.EnumConstantPrefix: k
readability-identifier-naming.NamespaceCase: lower_case
readability-identifier-naming.MacroDefinitionCase: UPPER_CASE
+5
View File
@@ -21,6 +21,11 @@ Mission:
Skill references:
- Use $fesa-cpp-msvc-tdd when writing C++17/MSVC tests first, verifying RED failures, implementing minimal solver code, registering CMake/CTest targets, running validation, or preparing implementation reports.
Mandatory global input:
- Before every C++ implementation Step, read docs/CODINGSTYLE.md as a mandatory global
input and apply it to production and test code.
- Doxygen coverage applies only to production code.
Mandatory Harness reading:
- Read .agents/skills/harness/SKILL.md, docs/HARNESS.md, docs/HARNESS_WORKFLOW.md, and
.codex/hooks.json before executing a Harness Step; inspect the relevant phase indexes and
+1
View File
@@ -23,6 +23,7 @@ __pycache__/
# local Harness configuration and build outputs
.harness/config.json
.harness/build/
.harness/doxygen/
# phase execution outputs
phases/**/phase*-output.json
+10
View File
@@ -8,6 +8,16 @@ set(CMAKE_CXX_EXTENSIONS OFF)
include(cmake/FesaDependencies.cmake)
find_package(Doxygen QUIET)
if(Doxygen_FOUND)
add_custom_target(fesa_docs
COMMAND "${DOXYGEN_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Generating FESA API documentation"
VERBATIM
)
endif()
enable_testing()
add_subdirectory(src/fesa)
+16
View File
@@ -0,0 +1,16 @@
PROJECT_NAME = FESA
PROJECT_NUMBER = 0.1.0
OUTPUT_DIRECTORY = .harness/doxygen
INPUT = include src
EXCLUDE = tests
RECURSIVE = YES
FILE_PATTERNS = *.h *.cpp
EXTRACT_ALL = NO
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_AS_ERROR = YES
GENERATE_HTML = YES
HTML_OUTPUT = html
GENERATE_LATEX = NO
+81 -5
View File
@@ -35,7 +35,12 @@ solution과 test command를 명시한 직접 MSBuild 프로젝트도 검증할
**트레이드오프**: 사용자는 기존 Abaqus input file을 그대로 사용할 수 없을 수 있다. 대신 지원 범위와 실패 원인이 명확해진다.
### ADR-004: Domain, AnalysisModel, DofManager, AnalysisState를 분리한다
**결정**: `Domain`입력 모델 정의를 소유하고, `AnalysisModel`은 현재 step의 실행 view를 제공하며, `DofManager`는 equation numbering과 constrained/free mapping을 전담하고, `AnalysisState`는 해석 중 변하는 물리량을 소유한다.
**결정**: `Domain``ElementDefinition`, `ElementProperty`, `Material`
`StepDefinition` 입력 모델 정의를 `std::unique_ptr`로 단독 소유하고 const access와
stable collection index를 제공한다. `AnalysisModel`은 Domain 수명 안에서 stable index와
const reference만 사용하는 non-owning current-step view다. `DofManager`는 equation
numbering과 constrained/free mapping을 전담하고, `AnalysisState`는 해석 중 변하는
물리량을 소유한다.
**이유**: 모델 정의, step activation, equation system, transient/nonlinear state가 섞이면 parser, assembler, solver, result writer가 강하게 결합된다. 분리된 상태 모델은 선형 정적 해석에서 시작해 비선형, 동적, thermal coupling으로 확장하기 쉽다.
@@ -57,12 +62,25 @@ solution과 test command를 명시한 직접 MSBuild 프로젝트도 검증할
**트레이드오프**: 단일 기능만 구현할 때는 adapter가 다소 장황해 보일 수 있다. Row-major dense storage와 CSR sparse storage를 따로 유지해야 하지만 backend 의존성과 dense/sparse 의미가 core 모델에 섞이지 않는다.
### ADR-007: Analysis 실행 흐름은 Template Method로 고정한다
**결정**: `Analysis::run()`은 공통 lifecycle을 고정한다. 선형 정적 V0의 순서는 `parse input -> initialize Domain -> build AnalysisModel -> build DOF map/sparse pattern -> assemble stiffness -> partition constraints -> factorize Kff -> assemble load -> form effective RHS -> substitute -> reconstruct displacement -> recover results -> write HDF5`다. 강성행렬 factorization은 하중벡터 조립보다 먼저 수행하고, factorization과 substitution을 하나의 불투명한 solve 호출로 합치지 않는다.
### ADR-007: Analysis base는 최소 실행 계약만 제공한다
**결정**: `Analysis` base는 virtual `Analysis::Run(const AnalysisRequest&)`만 제공하고
linear-static-specific protected hook을 정의하지 않는다. 승인된 선형 정적 순서인
`parse input -> initialize Domain -> build AnalysisModel -> build DOF map/sparse pattern ->
assemble stiffness -> partition constraints -> factorize Kff -> assemble load -> form effective
RHS -> substitute -> reconstruct displacement -> recover results -> write HDF5`
`LinearStaticAnalysis::Run()`의 private lifecycle로 유지한다. 강성행렬 factorization은
하중벡터 조립보다 먼저 수행하고 factorization과 substitution을 하나의 불투명한 solve
호출로 합치지 않는다.
**이유**: 해석 procedure가 늘어나도 공통 실행 순서가 유지되어야 검증, logging, result writing, failure classification이 일관된다. Factorization과 substitution을 분리하면 동일 강성행렬에 여러 RHS를 적용할 수 있고 각 실패 단계를 구조화된 diagnostic으로 분류할 수 있다.
**이유**: 현재 여덟 단계는 linear static equation, state와 failure taxonomy에 특화되어
있다. 최소 base contract는 이 순서의 검증 가능성을 보존하면서 승인되지 않은 dynamic,
eigenvalue 또는 nonlinear procedure에 같은 protected hook과 사용하지 않는 state를
강제하지 않는다. Factorization과 substitution 분리는 동일 강성행렬에 여러 RHS를 적용할
수 있고 각 실패 단계를 구조화된 diagnostic으로 분류하게 한다.
**트레이드오프**: 특수 해석 절차가 공통 흐름에 맞지 않는 경우 hook point가 필요하다. 초기에는 선형 정적 해석을 기준으로 최소 hook만 둔다.
**트레이드오프**: Procedure 사이의 lifecycle code는 base Template Method로 자동 재사용되지
않는다. 두 번째 procedure가 승인되면 실제로 같은 단계만 focused collaborator로 추출하되,
linear-static hook 사이에 조건문으로 새 physics를 삽입하지 않는다.
### ADR-008: Sparse assembly는 deterministic COO-to-CSR 경로로 시작한다
**결정**: 초기 assembly는 element-local contribution을 COO triplet으로 수집한 뒤 CSR로 finalize한다. MKL PARDISO backend는 CSR input contract를 받는다.
@@ -230,3 +248,61 @@ displacement 검증 목적에 맞지 않는다. 고정 절대오차는 현재
**트레이드오프**: Model scale이 크게 달라지면 고정 절대오차의 상대적 엄격도가 달라질 수
있다. 따라서 이 값은 현재 승인된 MITC4 S4 case의 기능 완료 기준이며 개발 완료 후
별도 reference-verification evidence와 함께 재점검한다.
### ADR-021: Semantic definition과 runtime solver contract를 분리한다
**결정**: Domain-owned semantic definition과 analysis-time numerical object를 다음
dependency 방향으로 분리한다.
```text
Domain owns ElementDefinition / ElementProperty / Material / StepDefinition
AnalysisModel is a non-owning stable-index view into Domain
ElementFactory creates runtime Element candidates from compatible definitions
DofManager -> ElementDofLayout
SparseAssembler -> ElementStiffnessContribution
ResultRecovery -> ElementResultBundle
LoadAssembler -> ordered LoadContribution
EssentialConstraintPolicy -> ConstraintDefinition
Analysis <- LinearStaticAnalysis
```
`Domain`의 polymorphic semantic collection은 `std::unique_ptr` 단독 ownership과 stable
vector position을 사용한다. `ElementDefinition`은 source identity, connectivity와
property/material identity를 제공하고 runtime `Element`는 DOF layout, stiffness와 result
recovery를 제공한다. `ElementFactory`가 definition/property/material compatibility를
중앙에서 검사하며 unknown 또는 incompatible 조합은 fail-closed diagnostic으로 거부한다.
Consumer는 B33/MITC4 concrete type branch를 분산시키지 않고 runtime contract를 사용한다.
Linear-static candidate가 runtime `Element``std::unique_ptr`로 소유하고 consumer는 그
수명에 한정된 non-owning view만 사용한다.
`Material` base에는 identity, source location과 lifetime 이외의 future capability를
추가하지 않는다. 현재 isotropic linear elasticity가 실제로 요구하는 data는 concrete
material에 둔다. Density, anisotropy, plastic state, temperature와 rate dependency는
optional field 또는 no-op virtual method로 미리 할당하지 않는다. `ElementProperty`
현재 beam/shell 의미를 각 concrete type에 둔다.
`Load`는 ordered `LoadContribution`을 생성하고 global full-DOF accumulation은
`LoadAssembler`가 단독 소유한다. `BoundaryCondition``ConstraintDefinition`을 생성하고
`EssentialConstraintPolicy`가 prescribed-displacement elimination과 reconstruction을
소유한다. Distributed/body load와 MPC/penalty/Lagrange-multiplier enforcement는 이번
결정으로 구현된 기능이 아니다.
Abaqus Domain mapper, result recovery와 HDF5 writer는 기존 public facade를 유지하면서
각각 topology/material-property/step-final-assembly, equilibrium/beam/shell/atomic-state,
RAII/model-result-dataset/self-check/atomic-finalization 책임으로 private implementation을
나눈다.
**이유**: Semantic identity와 runtime kernel을 같은 concrete record에 두면 DofManager,
SparseAssembler, ResultRecovery, parser와 output이 B33/MITC4 storage를 함께 알아야 한다.
Definition/factory/runtime contract와 contribution/policy 경계를 분리하면 stable source
identity와 deterministic reduction owner를 유지하면서 실제 두 element 구현을 공통
consumer로 연결할 수 있다. Focused facade 분할은 외부 계약을 바꾸지 않고 큰 translation
unit의 서로 다른 failure-atomicity 책임을 검토 가능하게 한다.
**트레이드오프**: Base object, factory와 contribution record가 늘고 checked compatibility에
한 단계의 indirection이 생긴다. 대신 `std::shared_ptr`, speculative `Clone()`, global
registry와 future-only material/analysis capability는 도입하지 않는다. B33/MITC4의 승인된
formulation, 연산·reduction 순서, sign, units, coordinates와 result identity가 이
리팩터링보다 우선하며 HDF5 schema, reference artifact와 ADR-014/ADR-020 tolerance는
변경하지 않는다. MITC3, solid, dynamic과 plastic behavior는 별도 feature gate 전까지
구현된 것으로 간주하지 않는다.
+132 -38
View File
@@ -32,7 +32,7 @@ src/
assembly/ # deterministic stiffness/load assembly, ParallelFor adapter
constraints/ # essential-constraint elimination and reconstruction
core/ # source identity, status, diagnostics
elements/ # V0 EulerBeam3D kernel and recovery
elements/ # current B33/MITC4 kernels and recovery
fem/ # DOF/equation numbering and sparse pattern
io/
abaqus/ # .inp syntax reader and semantic Domain mapper
@@ -62,9 +62,11 @@ scripts/
phases/ # Optional generated phase plans
```
`materials/`, nonlinear/dynamic analysis, MPC/penalty policies, general element factories,
history output과 production validation module은 장기 확장 경계이지 현재 구현된 module
아니다. 새 디렉토리와 추상 계층은 승인된 기능이 실제로 필요로 할 때 추가한다.
`materials/`, `properties/`, `loads/`와 checked `ElementFactory`는 승인된 C++ modular
refactoring의 target boundary이며 후속 implementation Step에서 추가한다.
documentation-only Step 시점에는 현재 구현 디렉토리로 표시하지 않는다. Density,
plasticity, anisotropy, nonlinear/dynamic analysis, MPC/penalty, history output과 production
validation module은 계속 장기 확장 경계이며 별도 승인 기능이 필요하다.
## Harness Execution Layer
@@ -109,16 +111,86 @@ CMake source에 기록하지 말고 config package와 imported target metadata
## 모듈 경계
- `core`는 외부 라이브러리에 의존하지 않는다.
- `io/abaqus`는 syntax와 semantic mapping만 담당하고 해석 알고리즘을 알지 않는다.
- `model`은 Abaqus keyword 문자열이 아니라 solver semantic model을 가진다.
- `model`은 Abaqus keyword 문자열이 아니라 solver semantic model을 가지며 `Domain`
non-owning `AnalysisModel`의 수명 경계를 소유한다.
- `materials``properties`는 Domain이 소유하는 semantic identity와 현재 승인된
isotropic elasticity 및 beam/shell property data만 제공한다.
- `elements`는 Domain-owned `ElementDefinition`, runtime numerical `Element`, checked
`ElementFactory`와 element-local stiffness/recovery contract를 제공한다.
- `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는 현재 범위가 아니다.
- `assembly`는 runtime element contribution과 ordered load contribution을 stable full-DOF
space에 조립한다. Contribution producer는 global storage를 직접 갱신하지 않는다.
- `loads`는 semantic target과 magnitude를 소유하고 ordered `LoadContribution`을 생성한다.
- `constraints``ConstraintDefinition` 생성과 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`가 담당한다.
- `results`는 full residual과 B33/MITC4 rows를 복구하고 backend-neutral writer contract를
제공한다. HDF5 schema 구현은 `io/hdf5`가 담당한다.
- test helper는 production parser/solver 내부 상태를 우회하지 않는다.
## 승인된 리팩터링 dependency와 ownership
다음 graph는 C++ object-oriented modular refactoring의 구현 방향을 고정한다. `owns`
단독 수명 소유권을, 나머지 화살표는 왼쪽 consumer가 오른쪽 contract를 사용한다는
뜻이다. 이 graph는 기존 B33/MITC4 물리 기능을 늘리지 않는다.
```text
Domain owns ElementDefinition / ElementProperty / Material / StepDefinition
AnalysisModel is a non-owning stable-index view into Domain
ElementFactory creates runtime Element candidates from compatible definitions
DofManager -> ElementDofLayout
SparseAssembler -> ElementStiffnessContribution
ResultRecovery -> ElementResultBundle
LoadAssembler -> ordered LoadContribution
EssentialConstraintPolicy -> ConstraintDefinition
Analysis <- LinearStaticAnalysis
```
승인된 B33/MITC4 formulation, operation/reduction order, sign, units, coordinates와 row
identity는 이 ownership 리팩터링보다 우선한다. HDF5 schema, reference artifact와
ADR-014/ADR-020 tolerance도 변경하지 않는다.
`Domain``ElementDefinition`, `ElementProperty`, `Material``StepDefinition` base
object를 `std::unique_ptr`로 단독 소유하고 const access를 제공한다. Collection position은
기존 stable `EntityIndex` 의미를 유지한다. `AnalysisModel`과 solver consumer는 ownership을
가져오지 않고 Domain 수명 안에서 stable index 또는 const reference만 사용한다.
`std::shared_ptr`, speculative `Clone()`과 global registry는 이 계약에 포함되지 않는다.
`ElementDefinition`은 source identity, source element type, connectivity와
property/material identity를 보존하는 semantic object다. Runtime `Element`는 active
`ElementDofLayout`, `ElementStiffnessContribution``ElementResultBundle`을 제공하는
numerical kernel이다. `ElementFactory`만 compatible definition/property/material 조합을
검사해 runtime candidate를 만들며 unknown 또는 incompatible 조합을 기존
`Status`/`Result<T>` diagnostic으로 fail-closed 처리한다. `DofManager`,
`SparseAssembler``ResultRecovery`는 B33/MITC4 concrete storage가 아니라 위 runtime
contract를 소비한다. Linear-static candidate는 runtime `Element`
`std::vector<std::unique_ptr<Element>>`로 소유하고, consumer에는 그 owner 수명 안에서만
유효한 non-owning `ElementView`를 제공한다.
`Material` base는 identity, source location과 수명 의미만 공유한다. 현재 concrete
isotropic linear elasticity에 필요한 capability만 사용하며 density, anisotropy, plastic
state, temperature 또는 rate dependency를 optional field나 no-op method로 미리 추가하지
않는다. `ElementProperty`도 beam/shell이 실제 사용하는 data만 각 concrete type에 둔다.
`Load`는 semantic target과 magnitude를 소유하고 source order가 보존된
`LoadContribution`을 생성한다. Global vector의 deterministic accumulation은
`LoadAssembler`만 수행한다. `BoundaryCondition``ConstraintDefinition`을 생성하고
`EssentialConstraintPolicy`가 prescribed displacement의 stable elimination과
reconstruction을 수행한다. Future distributed/body load와 MPC enforcement는 구현된
기능이 아니며 별도 승인 계약 없이 이 경계에 branch나 optional state를 추가하지 않는다.
책임이 큰 facade는 외부 계약을 유지한 채 private implementation만 다음 owner로 나눈다.
- Abaqus Domain mapping: topology, material/property, step/load/boundary mapping과 final
Domain assembly
- Result recovery: global equilibrium, beam recovery, shell recovery와 atomic state commit
- HDF5 writing: RAII/primitives, model datasets, result datasets, self-check와 atomic
finalization
이 분할은 parser diagnostic, `ResultsWriter` boundary, HDF5 schema 또는 final-file
atomicity를 변경하지 않는다.
## V0 입력 경계
V0 parser는 keyword와 parameter를 case-insensitive하게 해석하되 source label의 원문을
@@ -174,14 +246,23 @@ CLI pipeline에서는 이 kernel을 호출하지 않는다. Stiffness와 recover
```text
Domain
├── owns nodes, B33 elements, materials, beam sections, sets
├── owns boundary conditions, nodal loads, one static step
├── owns nodes, sets and source identity
├── owns unique_ptr<ElementDefinition / ElementProperty / Material>
├── owns StepDefinition with Load / BoundaryCondition definitions
└── owns source path/identity and mapping warnings
AnalysisModel
├── non-owning view into Domain
├── stable active element/BC/load indices
└── reachable material/section indices
├── non-owning stable-index view into Domain
├── stable active element/BC/load definition indices
└── reachable material/property indices
ElementFactory
└── creates checked runtime Element candidates
Element
├── exposes ElementDofLayout
├── produces ElementStiffnessContribution
└── recovers ElementResultBundle
DofManager
├── owns node x [UX,UY,UZ,URX,URY,URZ] full-DOF numbering
@@ -205,12 +286,15 @@ Matrix
SparseMatrix
```
Nonlinear/static, dynamic, frequency, heat-transfer procedure와 general element/material/load
base hierarchy는 이 구조 위의 가능한 확장 방향일 뿐 현재 public API가 아니다. 사용 사례가
승인되기 전에 V0 concrete record를 speculative hierarchy로 감싸지 않는다.
위 abstract boundary는 현재 B33/MITC4, isotropic linear elasticity, beam/shell property,
concentrated nodal load, prescribed displacement와 linear static procedure를 연결하는 데
필요한 최소 계약이다. MITC3, solid, dynamic, frequency, heat-transfer와 plastic behavior는
구현된 기능이 아니며 승인된 사용 사례 전에 future-only method나 state를 base에 추가하지
않는다.
## 상태 관리
- `Domain`은 입력 파일에서 만들어진 전체 모델 정의를 소유한다. 파싱 이후에는 가능한 한 불변으로 취급한다.
- `Domain`은 입력 파일에서 만들어진 전체 모델 정의를 `std::unique_ptr`로 단독 소유한다.
파싱 이후에는 가능한 한 불변으로 취급하고 stable collection index를 바꾸지 않는다.
- `LinearStaticAnalysis``Domain`을 소유하고, 그 뒤에 `AnalysisModel`, `DofManager`, `AnalysisState`, stiffness/RHS를 순서대로 만든다. 재사용 시에는 역순으로 해제하여 이전 Domain을 가리키는 view를 남기지 않는다.
- `AnalysisModel`은 현재 step에서 활성화되는 해석 객체들의 실행 view이다. `Domain`을 복사하지 않으므로 Domain이 반드시 더 오래 살아야 한다.
- `DofManager`는 자유도와 방정식 번호를 전담한다. `Node` 또는 `Element` 내부에 equation id를 분산 저장하지 않는다.
@@ -223,15 +307,16 @@ 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 생성
-> ElementFactory가 compatible definition에서 runtime Element candidate 생성
-> DofManager가 ElementDofLayout으로 DOF/scatter map/sparse pattern 생성
-> ElementStiffnessContribution의 deterministic COO-to-CSR 조립
-> ConstraintDefinition의 stable essential-constraint partition 생성
-> LinearSolver::factorize(Kff)
-> full nodal load vector 조립
-> ordered LoadContribution의 full nodal load vector 조립
-> effective RHS = Ff - Kfc * dc
-> LinearSolver::solve(rhs, df) substitution
-> full displacement 복구
-> full residual/reaction = K*d - F 및 element result 복구
-> full residual/reaction = K*d - F 및 ElementResultBundle 복구
-> ResultsWriter로 results.h5 atomic finalization
```
@@ -240,28 +325,37 @@ Abaqus input file
reaction이고 free component는 equilibrium residual evidence로 full-index vector에 남긴다.
## 해석 실행 흐름
`Analysis::run()`은 Template Method로 다음 여덟 hook의 순서와 fail-fast 경계를 고정한다.
| 순서 | Hook | 주요 작업과 생성되는 소유 객체 | 순서/실패 불변식 |
`Analysis` base는 procedure-specific protected hook을 정의하지 않고 최소 실행 계약인
`Analysis::Run(const AnalysisRequest&)`만 제공한다. 다음 여덟 단계의 순서와 fail-fast
경계는 `LinearStaticAnalysis::Run()`의 private lifecycle이며 다른 procedure에 강제되지
않는다.
| 순서 | Private stage | 주요 작업과 생성되는 소유 객체 | 순서/실패 불변식 |
| --- | --- | --- | --- |
| 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을 교체한다. |
| 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, B33/MITC4 rows, final HDF5 | Recovery candidate를 원자적으로 commit하고 writer 성공 뒤에만 최종 output을 교체한다. |
비선형 정적 및 동적 해석은 V0 범위가 아니며 별도 ADR과 formulation을 승인한 뒤 이
lifecycle과 state/equation 계약을 확장한다. 기존 hook 사이에 조용히 반복·증분·시간 적분
동작을 삽입하지 않는다.
lifecycle과 state/equation 계약을 별도 procedure에 정의한다. LinearStaticAnalysis의
private stage 사이에 조용히 반복·증분·시간 적분 동작을 삽입하지 않는다.
## 설계 패턴
- Strategy/Adapter Pattern: 현재 교체 가능한 public 경계는 `LinearSolver`, `ParallelFor`, `ResultsWriter`다. Vendor API는 concrete adapter implementation 안에만 둔다.
- Template Method Pattern: `Analysis::run()`은 공통 실행 흐름을 고정하고 세부 단계는 procedure별로 재정의한다.
- Strategy/Adapter Pattern: `Analysis::Run(const AnalysisRequest&)`, `LinearSolver`,
`ParallelFor`, `ResultsWriter`가 현재 승인된 public 실행/backend 경계다. Vendor API는
concrete adapter implementation 안에만 둔다.
- Procedure-owned lifecycle: `Analysis`는 protected Template Method hook을 공유하지 않고
`LinearStaticAnalysis`가 승인된 여덟 단계 lifecycle을 private하게 소유한다.
- Syntax/Semantic separation: `AbaqusInputReader`는 syntax record를 만들고 `AbaqusDomainMapper`가 승인된 keyword 의미를 concrete Domain record로 변환한다.
- Runtime Polymorphism: V0에서는 backend 경계에만 사용한다. 요소/재료/하중 base hierarchy와 factory/registry는 두 번째 실제 구현이 필요해질 때 trade-off를 다시 결정한다.
- Runtime Polymorphism: backend와 승인된 analysis/element/material/property/load/boundary
경계에만 사용한다. Factory compatibility는 중앙에서 fail-closed로 검사하며 global
registry 또는 future-only capability를 추가하지 않는다.
- RAII: MKL handle, HDF5 file/dataset, temporary solver workspace의 수명과 오류 처리를 wrapper에 묶는다.
## Sparse Matrix Policy
+256
View File
@@ -0,0 +1,256 @@
# FESA C++ Coding Style
## 목적
이 문서는 FESA production 및 test C++의 코드 스타일, 객체 설계, 문서화와 검증 규칙을
정의하는 project-local source of truth다. 새 C++를 작성하거나 기존 C++를 리팩터링하는
사람과 Implementation Agent는 작업 전에 이 문서를 읽어야 한다.
[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)를 baseline으로
사용한다. 이 문서, `AGENTS.md`, 승인된 feature contract와 architecture/ADR이 Google guide의
일반 규칙보다 우선한다.
## 적용 범위와 우선순위
규칙 충돌 시 다음 순서로 해석한다.
1. 승인된 feature requirements, formulation, numerical-review, I/O와 reference contract
2. `AGENTS.md`, `docs/ARCHITECTURE.md``docs/ADR.md`
3. 이 문서의 FESA-specific rule과 exception
4. Google C++ Style Guide
5. 기존 local style
새 코드는 이 문서를 즉시 준수한다. 기존 코드는 승인된 refactoring plan의 module slice
단위로 전환한다. 요청 범위 밖의 file을 style-only 이유로 함께 수정하지 않는다.
## Language와 Toolchain
- Production language는 C++17 이상이며 MSVC x64를 지원해야 한다.
- 승인된 build 기준은 CMake, Visual Studio generator와 Debug configuration이다.
- C++ compiler extension에 의존하지 않는다.
- MKL, TBB, HDF5와 Win32 type은 public solver core header에 노출하지 않는다.
- Standard library와 RAII를 manual lifetime management보다 우선한다.
- Google guide가 현재 권장하는 C++20 language target은 FESA의 C++17 contract를 바꾸지
않는다.
## File 이름과 Header
- File 이름은 소문자 snake_case를 사용한다.
- Production 및 test source extension은 기존 FESA/CMake 관례인 `.cpp`를 유지한다.
- Header extension은 `.h`를 사용한다. 기존 `.hpp`는 승인된 migration slice에서 `.h`
바꾼다.
- Header는 self-contained여야 하며 include consumer의 transitive include에 의존하지
않는다.
- Header는 `#pragma once` 대신 full repository path 기반 include guard를 사용한다.
예:
```cpp
#ifndef FESA_MATH_VECTOR3_H_
#define FESA_MATH_VECTOR3_H_
namespace fesa {
class Vector3 {};
} // namespace fesa
#endif // FESA_MATH_VECTOR3_H_
```
Include 순서는 다음과 같다.
1. 대응하는 header
2. C system header
3. C++ standard library header
4. Third-party header
5. FESA project header
각 non-empty group 사이에는 빈 줄을 두고 group 안에서는 알파벳순으로 정렬한다. 사용하는
symbol의 declaration을 제공하는 header를 직접 include한다.
## 이름 규칙
| 대상 | 규칙 | 예 |
| --- | --- | --- |
| class, struct, enum, alias | PascalCase | `ElementProperty`, `EntityIndex` |
| function, method, accessor | PascalCase | `ComputeStiffness()`, `NodeCount()` |
| local variable, parameter | snake_case | `element_index`, `source_order` |
| class data member | snake_case + trailing `_` | `youngs_modulus_` |
| struct data member | snake_case | `source_id` |
| compile-time/static constant | `kPascalCase` | `kNodeCount` |
| enumerator | `kPascalCase` | `FailureCategory::kModel` |
| namespace | snake_case | `fesa::hdf5_internal` |
| macro | UPPER_SNAKE_CASE | `FESA_MATH_VECTOR3_H_` |
Google guide는 accessor의 snake_case를 허용하지만 FESA는 사용자 승인에 따라 production
및 test 호출부를 포함한 모든 function name에 PascalCase를 적용한다. Constructor,
destructor와 operator 이름은 C++ language 규칙을 따른다.
이름은 물리 및 수치 의미를 드러내야 한다. `value`, `data`, `handler`, `manager`처럼 문맥이
없는 generic name을 넓은 scope에서 사용하지 않는다. Source label, internal entity index와
equation index를 이름에서 구분한다.
## Formatting
- `.clang-format``BasedOnStyle: Google`을 사용한다.
- 들여쓰기는 space 2개이며 tab을 사용하지 않는다.
- 최대 line length는 80자다. Include, guard, URL과 분할할 수 없는 contract string은 Google
guide의 예외를 따른다.
- Opening brace는 declaration/control statement의 마지막 줄에 둔다.
- Namespace body는 들여쓰지 않고 closing namespace comment를 작성한다.
- 한 statement에 한 declaration만 둔다.
- Variable은 가능한 가장 좁은 scope에서 선언과 동시에 초기화한다.
- `const``constexpr`를 의미가 허용하는 범위에서 사용한다.
- `override`, `final`, `explicit`, `noexcept``[[nodiscard]]`는 실제 contract를 표현할 때
사용한다.
Formatting-only 변경과 behavior/architecture 변경은 같은 commit에 섞지 않는다.
## Class와 Interface 설계
- Class는 하나의 명확한 책임과 invariant를 가져야 한다.
- Polymorphic base는 public virtual destructor를 가져야 한다.
- Abstract interface에는 현재 concrete 구현이 공유하지 않는 future method를 추가하지
않는다.
- 단독 ownership은 `std::unique_ptr`로 표현한다. 실제 shared lifetime이 없는
`std::shared_ptr`는 사용하지 않는다.
- Non-owning pointer/reference의 lifetime은 Doxygen contract에 기록한다.
- Downcast와 type switch를 주요 확장 mechanism으로 사용하지 않는다.
- State가 없는 함수를 묶기 위한 static-only class를 만들지 않는다. Internal namespace와
focused module을 사용한다.
- Base class에 optional field와 no-op method를 누적하지 않는다. Material density, plastic
state와 anisotropic constitutive law처럼 독립적인 의미는 별도 capability 또는 구성
객체로 설계한다.
- Public header가 implementation/vendor dependency를 역으로 끌어오지 않도록 한다.
Data-only record는 struct를 사용할 수 있다. Invariant, encapsulation, lifetime 또는 behavior가
있으면 class를 사용한다.
## FEM Module 책임
- `model`: immutable semantic definition과 stable source/internal identity
- `elements`: element numerical kernel, local contribution과 element recovery
- `properties`: element property identity와 concrete section data
- `materials`: constitutive capability와 concrete material behavior
- `fem`: DOF/equation numbering, scatter와 sparse pattern
- `assembly`: deterministic element/load contribution reduction
- `constraints`: constraint definition 적용과 equation policy
- `analysis`: procedure-specific lifecycle과 backend orchestration
- `results`: backend-neutral recovery record와 physical result identity
- `io`: Abaqus syntax/semantic mapping과 HDF5 schema implementation
- `math`: backend-neutral value/storage type와 private numerical adapter
한 module의 class가 다른 module의 owner 책임을 가져가지 않는다. Element가 global CSR을
직접 쓰거나 Node가 equation ID를 저장하거나 Material이 analysis state를 임의로 소유하면
안 된다.
## 중복과 공통화
같은 의미, units, coordinate, failure policy와 ownership을 가진 logic을 한 번만 구현한다.
현재 승인된 공통화 방향은 다음과 같다.
- 3D coordinate/axis/director 연산은 `Vector3` value class
- source label/set/instance 해석은 `SourceTargetResolver`
- full/free/constrained invariant는 `DofManager` owner validation
- MKL size/copy helper는 private dense-BLAS adapter
- ASCII case-insensitive comparison과 label parsing은 focused core utility
두 코드 block이 비슷해 보여도 formulation sign, result location, source identity 또는
tolerance가 다르면 공통화하지 않는다. 단 한 번 사용하는 logic을 future flexibility만을
위해 framework로 만들지 않는다.
## Error와 Ownership
- Expected failure는 `Status` 또는 `Result<T>`로 반환한다.
- Public solver API를 통해 backend exception이 그대로 새지 않게 한다.
- Unknown type/property/material 조합은 structured diagnostic으로 fail-closed 처리한다.
- Invalid input을 silent default, clamp, average 또는 fallback으로 숨기지 않는다.
- Candidate를 완성하고 검증한 뒤 Domain state, AnalysisState 또는 final HDF5에 commit한다.
- Stable ordering과 failure atomicity는 optimization option이 아니라 correctness contract다.
## Doxygen
Doxygen coverage는 production code에만 요구한다. Test function과 test helper에는 Doxygen를
요구하지 않는다.
Public/protected class와 function declaration은 다음 내용을 필요한 만큼 기록한다.
- `@brief`: 무엇을 하는지 동사형 한 문장
- `@param`: 이름만으로 드러나지 않는 units, coordinates, ownership 또는 valid range
- `@return`: success value와 failure 의미
- `@throws`: 실제로 경계를 넘어가는 exception
- `@pre`: caller가 보장해야 하는 invariant
- `@note`: deterministic order, lifetime 또는 backend constraint
- `@warning`: sign, physical/numerical distinction 또는 destructive side effect
예:
```cpp
/// @brief Computes the element stiffness in stable global DOF order.
/// @return A finite symmetric contribution or a structured model failure.
/// @note The returned matrix does not include nonphysical result terms.
virtual Result<Matrix> ComputeStiffness() const = 0;
```
Private/internal production function은 declaration 또는 definition에 목적을 기록한다. 수식,
sign, coordinate transform, lifetime, ordered reduction이나 failure preservation이
비자명하면 그 이유를 설명한다. Header declaration의 사용법을 `.cpp` definition에서 그대로
반복하지 않는다.
Comment는 코드 한 줄을 한국어 또는 영어로 번역하는 방식으로 작성하지 않는다. Public API
Doxygen는 일관된 tool output을 위해 영어를 기본으로 한다. Diagnostic message와 existing
contract language는 현재 외부 계약을 유지한다.
## Determinism과 수치 코드
- Element contribution은 stable source/internal index 순서로 생성한다.
- Parallel worker는 index-owned output만 수정한다.
- Floating-point reduction 순서는 명시적으로 고정한다.
- Refactoring 중 expression/reduction 순서를 편의상 바꾸지 않는다.
- Arbitrary `max(1, ...)`, zero clamp 또는 missing-row ignore를 추가하지 않는다.
- End action, section resultant, generalized result와 stress의 identity/sign을 구분한다.
- Reference mapping은 row order가 아니라 승인된 source identity와 component를 사용한다.
수치식을 공통화할 때는 현재 formulation과 test가 정의한 operation order 및 tolerance를
먼저 확인한다.
## Test와 변경 관리
- Production C++ 변경은 관련 C++ test와 같은 patch에 있어야 한다.
- Behavior 또는 interface 변경은 `RED -> observed failure -> minimal GREEN -> VERIFY`
따른다.
- Refactoring test는 base interface 사용, ownership/lifetime, invalid combination,
deterministic order와 current numerical result preservation을 검증한다.
- Focused test 뒤에 full MSVC x64 Debug build와 CTest를 실행한다.
- B33/MITC4 output 경계를 건드린 변경은 승인된 reference comparison을 다시 실행한다.
- Reference artifact, input path와 tolerance를 리팩터링에 맞춰 수정하지 않는다.
- Commit은 review 가능한 module slice로 제한하고 Conventional Commits를 사용한다.
## Tooling
Repository가 제공하는 설정을 우선한다.
```powershell
clang-format --dry-run --Werror <changed-cpp-and-header-files>
clang-tidy <changed-cpp-files> -- -std=c++17
doxygen Doxyfile
```
실제 build/test command는 `.harness/config.json`이 있으면 그 설정을 우선하고, 없으면
`AGENTS.md`와 Harness의 MSVC/CMake/CTest entry point를 따른다. 필요한 tool이 설치되지 않아
검증을 실행할 수 없으면 성공으로 간주하지 않고 environment limitation을 보고한다.
Generated Doxygen HTML과 tool cache/build output은 source control에 넣지 않는다.
## Implementation Agent Checklist
Implementation Agent는 C++ Step을 시작하기 전에 다음을 확인한다.
- 이 문서와 feature implementation plan을 읽었다.
- 변경할 base/concrete/module owner가 승인 설계와 일치한다.
- 관련 test file과 RED condition이 Step에 명시되어 있다.
- Public API naming과 production Doxygen가 이 문서에 맞는다.
- Vendor dependency와 ownership direction이 역전되지 않는다.
- Stable identity, numerical order, HDF5와 reference contract가 보존된다.
- Formatting, Doxygen, focused/full MSVC/CTest acceptance command가 계획되어 있다.
@@ -0,0 +1,100 @@
# C++ Object-Oriented Modular Refactoring Build/Test Report
## Metadata
- owner_agent: `implementation-agent`
- feature_id: `cpp-object-oriented-modular-refactoring`
- report_status: `passed`
- date: `2026-08-16`
- workspace: `C:\git\FESADev\.worktrees\cpp-object-oriented-modular-refactoring`
- branch: `feat-cpp-object-oriented-modular-refactoring`
- head: `f84ebb541f4717ab8300cf0d80497e1277d3bb48`
- `.harness/config.json`: absent; Harness/CMake defaults and Step 24 explicit commands used
- build generator: `Visual Studio 18 2026`
- platform/configuration: `x64` / `Debug`
- compiler observed by configure: `MSVC 19.51.36252.0`
- inherited environment note: `FESA_HARNESS_CODEX_SANDBOX=danger-full-access`
## Execution environment
Required dependency paths all existed:
| Path | Status |
| --- | --- |
| `C:/git/googletest` | found |
| `C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl` | found |
| `C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb` | found |
| `C:/Program Files/HDF_Group/HDF5/2.1.1/cmake` | found |
LLVM tools:
- `clang-format version 22.1.8`
- `clang-tidy LLVM version 22.1.8`
- `clang-tidy --verify-config`: `No config errors detected.`
## Command log summary
| Command | Exit | Duration | Output tail / result |
| --- | ---: | ---: | --- |
| `uv run --with pytest python -m pytest -v -rs` | 1 | 2.700s | 20 passed, 1 failed: `test_invoke_codex_uses_utf8_for_unicode_prompt` saw inherited sandbox override `danger-full-access` instead of default `workspace-write`. Classified as environment-specific diagnostic, not product failure. |
| Clean child process without `FESA_HARNESS_CODEX_SANDBOX`; `uv run --with pytest python -m pytest -v -rs` | 0 | 0.751s | `21 passed in 0.13s`. |
| `clang-format --dry-run --Werror` over `@(rg --files include src tests -g "*.h" -g "*.cpp")` | 1 | 0.892s | Reported four formatting findings in `src/fesa/math/sparse_matrix.cpp`, `tests/unit/math/sparse_matrix_test.cpp`, `tests/unit/solvers/linear/linear_solver_test.cpp`, `tests/unit/solvers/linear/mkl_pardiso_solver_test.cpp`. |
| `clang-format -i` on the four reported files | 0 | tool wall 0.5s | Normalized formatting/stat state; `git diff --raw` and `git diff --numstat` remained empty for those files. |
| `clang-format --dry-run --Werror` over 163 C++ files | 0 | 0.888s | `CPP_FILE_COUNT: 163`; no violations. |
| `clang-tidy --config-file=.clang-tidy <publicHeader> -- -x c++ -std=c++17 -Iinclude` | 0 | 41.822s | `PUBLIC_HEADER_COUNT: 46`; 45 diagnostics were required trailing-underscore header guards and two were `const` parameter classifications; zero other naming diagnostics. |
| Read-only production Doxygen/header-guard and test-tag scan | 0 | 0.302s | 63 production headers, 0 missing guards, 0 missing Doxygen-contract headers, and 0 test files with imposed Doxygen tags. |
| `.hpp` scan under `include src tests` | 0 | 0.162s | `LEGACY_HPP_COUNT: 0`; `rg` returned 1 for empty result but count-based AC passed. |
| Required dependency path check | 0 | 0.165s | All four declared dependency paths found. |
| `cmake --fresh -S . -B .harness/build -G "Visual Studio 18 2026" -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"` | 0 | 6.204s | Configure/generate complete; MKL 2026.1.0 found; build files written to `.harness/build`. |
| `cmake --build .harness/build --config Debug --target fesa_tests` | 0 | 9.639s | `fesa_solver.lib`, `fesa_integration_tests.exe`, `fesa_reference_tests.exe`, and `fesa_unit_tests.exe` built. |
| `ctest --test-dir .harness/build -C Debug --show-only=json-v1` | 0 | 0.235s | `DISCOVERED_TEST_COUNT: 206`. |
| `ctest --test-dir .harness/build -C Debug --output-on-failure` | 0 | 9.908s | `100% tests passed out of 206`; labels: unit 182, integration 11, reference 13. |
| `ctest --test-dir .harness/build -C Debug -R "B33ReferenceComparison|Mitc4S4Reference" --output-on-failure` | 0 | 1.260s | `100% tests passed out of 3`. |
| `git diff --check` | 0 | 0.199s | `<no whitespace errors>`. |
## Validation results
| Validation | Result |
| --- | --- |
| Harness Python/policy tests | Pass in clean child process: 21/21. |
| LLVM tool availability/config | Pass. |
| clang-format repository dry-run | Pass: 163 files. |
| clang-tidy selected public-header check | Pass: 46 headers, exit 0, zero naming diagnostics outside the two documented contract exceptions. |
| Production Doxygen/header-guard policy | Pass: 63/63 headers; tests contain no imposed Doxygen boilerplate tags. |
| Header extension policy | Pass: 0 `.hpp` under `include`, `src`, `tests`. |
| Fresh MSVC x64 Debug configure | Pass. |
| MSVC x64 Debug build | Pass: `fesa_tests`. |
| CTest discovery | Pass: 206 tests. |
| Full CTest | Pass: 206/206. |
| Approved reference suites | Pass: 3/3. |
| Reference tree no-change | Pass before and after compare: `git diff --exit-code 1e5758f -- reference` exit 0. |
## Failure classification and failed test inventory
Blocking classification: `none`.
Nonblocking diagnostics:
- `environment`: inherited `FESA_HARNESS_CODEX_SANDBOX=danger-full-access` caused the raw pytest command to fail one default-sandbox test. The same pytest command passed 21/21 in a child process with only that override removed.
- `style`: initial clang-format dry-run reported four whitespace/line-ending findings. Formatting normalization introduced no tracked source-content diff, and the rerun passed.
- `static-policy`: clang-tidy reported header-guard trailing underscores and two
`const` parameters because its generic macro/constant categories differ from the
higher-priority FESA guard and parameter conventions. It reported no other naming
diagnostics, and the required command exited 0.
Failed blocking tests after clean verification: none.
## Handoff recommendation
Proceed to Physics Evaluation Agent. Build/test evidence is sufficient for the final
Implementation-owned gate; no compile, link, test, reference-comparison, or
environment blocker remains.
## No-change assertion
No production behavior, reference artifact, reference tolerance, comparator contract,
or generated Doxygen output was changed in Step 24.
## Open issues
None blocking.
@@ -0,0 +1,410 @@
# C++ Object-Oriented Modular Refactoring Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use
> `superpowers:subagent-driven-development` (recommended) or
> `superpowers:executing-plans` to implement this plan task-by-task. Steps use
> checkbox (`- [ ]`) syntax for tracking.
>
> In FESA, those task-by-task semantics are mediated by the project Harness. Do not
> invoke an implementation skill or select a Step directly; a separate user request
> must start `scripts/execute.py`, which selects exactly one pending Step.
**Goal:** Preserve the current B33, MITC4, and linear-static numerical and external
contracts while converting the FESA C++ production code to explicit object-oriented
boundaries, focused modules, shared utilities, Google C++ style, and production-only
Doxygen documentation.
**Architecture:** Domain owns immutable polymorphic semantic definitions through
`std::unique_ptr` and stable `EntityIndex` positions. `ElementDefinition` remains
separate from runtime numerical `Element`, and load, boundary-condition, analysis,
material, and property abstractions each have independent hierarchies. Existing
deterministic assembly, result identity, HDF5 schema, and reference comparison
contracts remain unchanged.
**Tech Stack:** C++17, MSVC x64 Debug, CMake, CTest, GoogleTest, Intel oneMKL,
Intel oneTBB, HDF5, clang-format, clang-tidy, and optional Doxygen configuration.
## Global Constraints
- Follow `/docs/CODINGSTYLE.md` and the official Google C++ Style Guide baseline.
- Use PascalCase for every C++ function and accessor; use `.h` production headers
with full-path include guards; retain `.cpp` as the FESA source-file exception.
- Add Doxygen comments only to production code. Do not add Doxygen coverage to tests.
- Keep C++17 and MSVC x64 Debug compatibility and add no compiler warnings under
`/W4 /WX`.
- Preserve the approved B33 and MITC4 formulations, signs, units, coordinate systems,
reduction order, result row identity, HDF5 schema, tolerances, and reference files.
- Do not implement MITC3, solid elements, dynamics, eigenvalue analysis, response
spectrum, random vibration, density, plasticity, anisotropy, distributed load, body
force, or MPC behavior.
- Do not expose MKL, TBB, HDF5, Win32, or vendor integer types from public solver-core
headers.
- Every C++ production change requires a related C++ test and an in-Step
`RED -> observed failure -> minimal GREEN -> focused/full VERIFY` cycle.
- Do not run `scripts/execute.py` until the user gives a separate explicit execution
request.
- Doxygen comments and `Doxyfile` configuration are in scope; generated Doxygen
output is deferred and is not a blocking command for this phase.
---
## 1. Metadata
| Field | Value |
| --- | --- |
| `feature_id` | `cpp-object-oriented-modular-refactoring` |
| `source_requirement` | `/docs/superpowers/specs/2026-08-16-cpp-object-oriented-modular-refactoring-design.md` |
| `source_research` | Existing repository duplication and ownership audit captured by the approved design; no new FEM research is required |
| `source_formulation` | `/docs/linear-static-3d-euler-beam/formulation.md`; `/docs/linear-static-mitc4-shell/formulation.md` |
| `source_numerical_review` | `/docs/linear-static-3d-euler-beam/numerical-review.md`; `/docs/linear-static-mitc4-shell/numerical-review.md` |
| `source_io_definition` | `/docs/linear-static-3d-euler-beam/io.md`; `/docs/linear-static-mitc4-shell/io.md` |
| `source_reference_models` | `/docs/linear-static-3d-euler-beam/reference-model.md`; `/docs/linear-static-mitc4-shell/reference-model.md` |
| `status` | `ready-for-implementation` |
| `owner_agent` | `implementation-planning-agent` |
| `date` | `2026-08-16` |
## 2. Readiness Check
- The written refactoring design and the 25-Step draft were explicitly approved on
2026-08-16.
- B33 and MITC4 requirements, formulations, numerical reviews, I/O projections, and
reference contracts already exist and remain upstream read-only inputs.
- Required reference inputs and CSVs are present under
`/reference/cantilever beam/` and `/reference/shell/`.
- `clang-format.exe` and `clang-tidy.exe` are present at
`C:/Program Files/LLVM/bin/`; the current long-lived process PATH need not contain
that directory because the plan uses the absolute paths.
- Doxygen generation is intentionally deferred by user decision. The implementation
still adds production comments and a warning-strict `Doxyfile` for later use.
- No missing formulation, tolerance, HDF5 projection, or artifact decision prevents
implementation planning.
## 3. Implementation Scope
### Included
- Repository policy/tooling and Implementation Agent enforcement.
- Mechanical `.hpp` to `.h`, header guard, PascalCase, formatting, and production
Doxygen conversion in reviewable module slices.
- Shared `Vector3`, dense-BLAS internal adapter, ASCII utilities,
`SourceTargetResolver`, and owner-based DOF invariant validation.
- Independent abstract boundaries for material, element property, semantic element
definition, runtime element, load, boundary condition, and analysis.
- Current concrete B33, MITC4, isotropic linear elasticity, beam/shell property,
concentrated nodal load, prescribed displacement, and linear-static behavior.
- Responsibility-based splits of domain mapping, result recovery, and HDF5 writing.
- Full MSVC/CTest and existing B33/MITC4 external reference verification.
### Excluded and non-goals
- New physics, input keywords, output datasets, tolerances, reference artifacts, or
runtime performance optimization.
- A common root base shared by unrelated element, load, material, and analysis types.
- A giant material interface containing density, plasticity, and anisotropy options.
- Registry/plugin frameworks, global static registration, speculative `Clone()`, or
unnecessary shared ownership.
## 4. Refactoring Requirements
| ID | Requirement |
| --- | --- |
| `R-PRESERVE-001` | Current B33/MITC4/linear-static numerical and external results shall remain unchanged within their approved contracts. |
| `R-STYLE-001` | Production and test C++ shall use approved Google-style naming and formatting; production headers shall use `.h` and header guards. |
| `R-DOC-001` | Production functions and classes shall carry useful Doxygen contracts; tests shall not require Doxygen comments. |
| `R-DUP-001` | Repeated fixed-size 3D vector operations shall be implemented once by `Vector3`. |
| `R-DUP-002` | Repeated dense-BLAS conversion/copy, ASCII/source resolution, and DOF invariant logic shall have one owner. |
| `R-MODEL-001` | Material, element-property, and element-definition semantic objects shall have independent abstractions and Domain-owned stable lifetime. |
| `R-ELEMENT-001` | Semantic `ElementDefinition` and runtime numerical `Element` shall remain separate and be connected by a fail-closed factory. |
| `R-PIPELINE-001` | DofManager, SparseAssembler, and ResultRecovery shall consume runtime `Element` interfaces without scattered B33/MITC4 type branches. |
| `R-LOAD-001` | A `Load` shall emit ordered contributions and only `LoadAssembler` shall accumulate the global vector. |
| `R-BC-001` | A `BoundaryCondition` shall emit definitions and an essential-constraint policy shall enforce prescribed displacement. |
| `R-ANALYSIS-001` | `Analysis` shall expose only `Run()` and `LinearStaticAnalysis` shall own its approved lifecycle. |
| `R-MODULE-001` | Domain mapping, recovery, and HDF5 writing shall be split by their approved responsibilities. |
| `R-AGENT-001` | Implementation Agent shall read `/docs/CODINGSTYLE.md` as a mandatory global input. |
| `R-SCOPE-001` | No excluded future feature or runtime-performance change shall be introduced. |
## 5. Work Breakdown
| Task | Name | Depends on | Deliverable |
| --- | --- | --- | --- |
| `T00` | coding-style-agent-contract | none | Agent profile and Python contract enforce `CODINGSTYLE.md`. |
| `T01` | cpp-style-tooling | `T00` | clang-format/tidy configuration and deferred Doxygen configuration. |
| `T02` | architecture-boundaries | `T00` | Architecture and ADR record the approved responsibility graph. |
| `T03` | foundation-google-style | `T01` | Core/math/linear-solver APIs use the approved style. |
| `T04` | model-element-google-style | `T03` | Model and current element APIs use the approved style. |
| `T05` | solver-workflow-google-style | `T04` | FEM/assembly/constraint/analysis/result APIs use the approved style. |
| `T06` | io-application-google-style | `T05` | I/O, application, and test helper APIs use the approved style. |
| `T07` | vector3-value-type | `T03` | Tested fixed-size vector value type. |
| `T08` | element-geometry-vector3 | `T04`, `T07` | Element/model geometry duplicate helpers removed. |
| `T09` | result-io-vector3 | `T06`, `T08` | Result/I/O vector duplicate helpers removed. |
| `T10` | dense-blas-adapter | `T03` | Matrix/Vector share private MKL conversion and copy helpers. |
| `T11` | source-target-resolver | `T06` | Shared ASCII and source-target resolution module. |
| `T12` | material-property-hierarchy | `T04` | Independent semantic material and property abstractions. |
| `T13` | element-definition-domain | `T11`, `T12` | Domain-owned polymorphic semantic element definitions. |
| `T14` | runtime-element-factory | `T08`, `T13` | Runtime element abstraction and fail-closed factory. |
| `T15` | generic-dof-manager | `T14` | DofManager consumes element DOF layouts and owns invariant checks. |
| `T16` | generic-sparse-assembler | `T15` | SparseAssembler consumes element stiffness contributions. |
| `T17` | generic-result-recovery | `T16` | ResultRecovery consumes element result bundles. |
| `T18` | load-hierarchy | `T11`, `T15` | Ordered load contribution hierarchy. |
| `T19` | boundary-condition-policy | `T15` | Constraint definition hierarchy and essential policy. |
| `T20` | analysis-hierarchy | `T17`, `T18`, `T19` | Minimal Analysis base and unchanged linear-static lifecycle. |
| `T21` | domain-mapper-modules | `T11`, `T13`, `T18`, `T19` | Mapper split by semantic responsibility. |
| `T22` | result-recovery-modules | `T17` | Recovery split into global, beam, shell, and commit responsibilities. |
| `T23` | hdf5-writer-modules | `T17`, `T22` | HDF5 writer split without schema changes. |
| `T24` | final-quality-reference-gate | all prior tasks | Full style, build/test, HDF5, determinism, and reference evidence. |
Each task maps one-to-one to `/phases/cpp-object-oriented-modular-refactoring/stepN.md`.
## 6. TDD Test Plan
| Test ID | First failing evidence | GREEN evidence |
| --- | --- | --- |
| `P-AGENT-001` | Python contract reports missing mandatory `CODINGSTYLE.md` input. | Agent workflow contract passes. |
| `P-STYLE-001` | Policy test reports missing or incorrect clang/Doxygen configuration. | Policy and full Harness Python tests pass. |
| `C-STYLE-001..004` | Test includes/calls use `.h` and PascalCase before production conversion, causing a compile failure. | Focused module suites and full CTest pass. |
| `C-VEC3-001` | `vector3_test.cpp` cannot compile because `Vector3` is absent. | Arithmetic, finite, and normalization-boundary tests pass. |
| `C-DUP-001..004` | Tests reference the new shared seam before it exists. | Shared seam passes and old duplicate helper definitions are absent by `rg` checks. |
| `C-MODEL-001..002` | Polymorphic ownership and const stable-index tests fail before semantic bases exist. | Material/property/definition tests and Domain mapping tests pass. |
| `C-ELEMENT-001` | Base-interface creation and incompatibility tests fail before `ElementFactory`. | B33/MITC4 creation, rejection, stiffness, and recovery tests pass. |
| `C-DOF-001` | Fake runtime element layout is not accepted by DofManager. | Stable scatter/pattern and invariant tests pass. |
| `C-ASSEMBLY-001` | Fake runtime contribution is not assembled. | Serial/TBB/repeated CSR outputs remain byte-identical. |
| `C-RECOVERY-001` | Fake result bundle cannot flow through recovery. | Beam/shell identities, signs, energy, and atomic rollback pass. |
| `C-LOAD-001` | A fake Load cannot emit ordered full-DOF contributions. | Source-order accumulation and current load validation pass. |
| `C-BC-001` | A fake BoundaryCondition cannot resolve constraint definitions. | Nonzero prescribed displacement and reconstruction pass. |
| `C-ANALYSIS-001` | LinearStaticAnalysis cannot be invoked through `Analysis`. | Approved factorization/load/solve/recovery lifecycle passes. |
| `C-MODULE-001..003` | Tests reference extracted mapper/recovery/HDF5 responsibilities before their seams exist. | Existing public behavior and atomicity suites pass after extraction. |
| `C-REF-B33-001` | No new intentional failure; final gate reuses the approved external comparison. | B33 comparison passes under its existing component-scale tolerance. |
| `C-REF-MITC4-001` | No new intentional failure; final gate reuses the approved external comparison. | MITC4 translations pass at fixed `1.0e-5`; rotations remain warning-only. |
RED and GREEN evidence, command, exit code, duration, output tail, and failed test names
must be recorded during execution in the Implementation-owned reports. A final reference
gate does not manufacture an artificial RED because it verifies an unchanged approved
external contract after all refactoring tasks.
## 7. CMake/CTest Plan
- Keep the existing `fesa_solver`, `fesa_cli`, `fesa_unit_tests`,
`fesa_integration_tests`, `fesa_reference_tests`, and `fesa_tests` targets.
- Register new production/test files in `/src/fesa/CMakeLists.txt` and
`/tests/CMakeLists.txt` in their owning task.
- Do not create a new test executable or change existing test labels.
- `.harness/config.json` is absent, so use `.harness/build`, MSVC x64, Debug, and the
explicit local dependency paths recorded in each Step.
- Every C++ task runs a focused CTest regular expression and the full CTest discovery
and execution sequence.
- Step `T24` performs a fresh configure and the final B33/MITC4 reference tests.
## 8. Candidate Files and Ownership
| Responsibility | Candidate files |
| --- | --- |
| Policy/tooling | `.codex/agents/implementation-agent.toml`, `.clang-format`, `.clang-tidy`, `Doxyfile`, `tests/test_agent_skill_workflow_contract.py`, `tests/test_cpp_policy_contract.py` |
| Fixed/dynamic math | `include/fesa/math/vector3.h`, `include/fesa/math/vector.h`, `include/fesa/math/matrix.h`, `src/fesa/math/dense_blas_internal.h`, matching `.cpp` and unit tests |
| Semantic material/property | `include/fesa/materials/*.h`, `include/fesa/properties/*.h`, `src/fesa/materials/*.cpp`, `src/fesa/properties/*.cpp`, matching unit tests |
| Semantic element definitions | `include/fesa/elements/element_definition.h`, concrete definition headers, `include/fesa/model/domain.h`, `src/fesa/model/domain.cpp` |
| Runtime elements | `include/fesa/elements/element.h`, `element_factory.h`, existing B33/MITC4 kernels and new factory implementation/tests |
| Source resolution | `include/fesa/model/source_target_resolver.h`, `src/fesa/model/source_target_resolver.cpp`, focused tests |
| Solver consumers | DofManager, SparseAssembler, ResultRecovery headers/sources/tests |
| Loads | `include/fesa/loads/load.h`, `concentrated_nodal_load.h`, sources, LoadAssembler and tests |
| Constraints | `boundary_condition.h`, `prescribed_displacement.h`, `essential_constraint_policy.h`, sources and tests |
| Analysis | `analysis.h`, `linear_static_analysis.h`, sources and integration tests |
| Mapper split | focused private mapper modules under `src/fesa/io/abaqus/` with one public `domain_mapper.h` facade |
| Recovery split | focused modules under `src/fesa/results/` with one public `result_recovery.h` facade |
| HDF5 split | private modules under `src/fesa/io/hdf5/` with one public `hdf5_results_writer.h` facade |
These are implementation candidates, not permission to introduce extra public API. Each Step
must choose the minimum files consistent with the approved boundaries.
## 9. Candidate Interface Contracts
The implementation may refine parameter carrier names while preserving these semantic contracts:
```cpp
struct AnalysisRequest {
std::filesystem::path input_path;
std::filesystem::path output_path;
};
class Analysis {
public:
virtual ~Analysis() = default;
virtual Status Run(const AnalysisRequest& request) = 0;
};
class ElementDefinition {
public:
virtual ~ElementDefinition() = default;
virtual ElementDefinitionKind Kind() const noexcept = 0;
virtual const SourceEntityId& SourceId() const noexcept = 0;
virtual const std::vector<EntityIndex>& NodeIndices() const noexcept = 0;
virtual EntityIndex PropertyIndex() const noexcept = 0;
};
class Element {
public:
virtual ~Element() = default;
virtual const ElementDofLayout& DofLayout() const noexcept = 0;
virtual Result<ElementStiffnessContribution> ComputeStiffness() const = 0;
virtual Result<ElementResultBundle> Recover(
const Vector& full_displacement) const = 0;
};
class Load {
public:
virtual ~Load() = default;
virtual Result<std::vector<LoadContribution>> ComputeContributions(
const LoadContext& context) const = 0;
};
class BoundaryCondition {
public:
virtual ~BoundaryCondition() = default;
virtual Result<std::vector<ConstraintDefinition>> ResolveConstraints(
const BoundaryConditionContext& context) const = 0;
};
```
Do not add future-only methods to these bases. Factory compatibility may use a centralized,
explicit kind discriminator followed by a checked concrete access; consumers must not scatter
`dynamic_cast` or B33/MITC4 switches.
## 10. Data Flow Contract
```text
existing Abaqus .inp
-> syntax reader
-> responsibility-split semantic mappers
-> immutable Domain-owned definitions
-> AnalysisModel non-owning active view
-> ElementFactory runtime elements
-> DofManager / deterministic assembly / constraints
-> LinearStaticAnalysis
-> result recovery candidate and validation
-> authoritative results.h5 atomic commit
-> test-only deterministic projection
-> existing Abaqus CSV comparison by source identity and component
```
- B33 input and CSVs remain under `/reference/cantilever beam/` with their current
names and component-scale tolerance.
- Blocking MITC4 S4 input/displacement CSV remains under `/reference/shell/` with
fixed absolute tolerance `1.0e-5` for U1/U2/U3 and warning-only UR1/UR2/UR3.
- `/reference/shellR/` is not promoted into a blocking comparison.
- No reference artifact is renamed, rewritten, regenerated, or normalized.
## 11. Acceptance Traceability Matrix
| Requirement | Tasks | Tests/evidence | Acceptance |
| --- | --- | --- | --- |
| `R-PRESERVE-001` | `T03..T24` | all current suites, `C-REF-B33-001`, `C-REF-MITC4-001` | Full CTest and blocking references pass. |
| `R-STYLE-001` | `T01`, `T03..T06`, `T24` | `P-STYLE-001`, clang-format, clang-tidy config, legacy-header scan | Style commands and full build pass. |
| `R-DOC-001` | `T03..T24` | policy scan and configured warning-strict Doxyfile | Production comments exist; tests are excluded. |
| `R-DUP-001` | `T07..T09` | `C-VEC3-001`, element/result/I/O suites, duplicate scan | One Vector3 implementation remains. |
| `R-DUP-002` | `T10`, `T11`, `T15` | `C-DUP-001..004` | Shared owners pass focused tests. |
| `R-MODEL-001` | `T12`, `T13` | `C-MODEL-001..002` | Polymorphic stable ownership passes. |
| `R-ELEMENT-001` | `T13`, `T14` | `C-ELEMENT-001` | Factory creates current kinds and rejects incompatible combinations. |
| `R-PIPELINE-001` | `T15..T17` | `C-DOF-001`, `C-ASSEMBLY-001`, `C-RECOVERY-001` | Generic consumer and deterministic tests pass. |
| `R-LOAD-001` | `T18` | `C-LOAD-001` | Ordered accumulation and current validations pass. |
| `R-BC-001` | `T19` | `C-BC-001` | Prescribed displacement partition/reconstruction passes. |
| `R-ANALYSIS-001` | `T20` | `C-ANALYSIS-001` | Lifecycle and factorization count pass. |
| `R-MODULE-001` | `T21..T23` | `C-MODULE-001..003` | Facade behavior and atomicity suites pass. |
| `R-AGENT-001` | `T00` | `P-AGENT-001` | Python workflow contract passes. |
| `R-SCOPE-001` | every task | diff review and final reference/artifact checks | No excluded behavior or artifact change appears. |
## 12. Validation Commands
Harness Python and policy validation:
```powershell
uv run --with pytest python -m pytest -v -rs
& "C:/Program Files/LLVM/bin/clang-format.exe" --version
& "C:/Program Files/LLVM/bin/clang-tidy.exe" --version
& "C:/Program Files/LLVM/bin/clang-tidy.exe" --verify-config
```
MSVC clean configure and full verification:
```powershell
$requiredBuildPaths = @(
"C:/git/googletest",
"C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl",
"C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb",
"C:/Program Files/HDF_Group/HDF5/2.1.1/cmake"
)
foreach ($requiredBuildPath in $requiredBuildPaths) {
if (-not (Test-Path -LiteralPath $requiredBuildPath)) {
throw "Missing $requiredBuildPath"
}
}
cmake --fresh -S . -B .harness/build -G "Visual Studio 18 2026" -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 --target fesa_tests
ctest --test-dir .harness/build -C Debug --show-only=json-v1
ctest --test-dir .harness/build -C Debug --output-on-failure
ctest --test-dir .harness/build -C Debug `
-R "B33ReferenceComparison|Mitc4S4Reference" --output-on-failure
```
Repository style and artifact checks:
```powershell
$cppFiles = @(rg --files include src tests -g "*.h" -g "*.cpp")
& "C:/Program Files/LLVM/bin/clang-format.exe" --dry-run --Werror $cppFiles
$publicHeaders = @(rg --files include/fesa -g "*.h")
foreach ($publicHeader in $publicHeaders) {
& "C:/Program Files/LLVM/bin/clang-tidy.exe" --config-file=.clang-tidy `
$publicHeader -- -x c++ -std=c++17 -Iinclude
if ($LASTEXITCODE -ne 0) {
throw "clang-tidy failed for $publicHeader"
}
}
$legacyHeaders = @(rg --files include tests -g "*.hpp")
if ($legacyHeaders.Count -ne 0) {
$legacyHeaders
throw "Legacy .hpp headers remain"
}
git diff --exit-code 1e5758f -- reference
```
Doxygen generation is deliberately absent from the blocking commands. When the user
requests documentation generation later, execute `doxygen Doxyfile` and treat warnings
as failures without committing generated HTML.
## 13. Risks and Downstream Handoff
- Global API/header renaming has a wide compile blast radius. Mechanical style Steps
are isolated from semantic restructuring to keep failures attributable.
- Domain polymorphism can accidentally destabilize vector indices or lifetimes. Tests
must prove insertion order, const access, and AnalysisModel non-owning lifetime.
- Virtual element recovery can tempt a giant result record. Preserve distinct beam and
shell rows in a backend-neutral bundle rather than adding meaningless common fields.
- Moving vector helpers can change floating-point operation order. Preserve each
formulation expression order and use exact regression where no approved tolerance
applies.
- File splits can leak vendor dependencies through public headers. Keep all HDF5/MKL/TBB
types in private implementation modules.
Downstream handoff is one bounded handoff to `implementation-agent` through the
Coordinator: execute only the Executor-selected `stepN.md`, read `/docs/CODINGSTYLE.md`
before C++ work, record RED/GREEN/VERIFY evidence, and do not advance another Step.
## 14. Harness Step Draft
- Task name: `cpp-object-oriented-modular-refactoring`
- Steps: `step0.md` through `step24.md` in dependency order shown in Work Breakdown.
- Every Step contains its own prerequisite files, test-first failure, candidate
interfaces, exact focused/full commands, and prohibitions.
- Stop conditions are an upstream contract conflict, a missing declared artifact at
final comparison, an unresolved environment dependency, or repeated build/test
failure. In each case only the current Step status payload is changed.
- Planning approval materializes these files but does not authorize
`python scripts/execute.py cpp-object-oriented-modular-refactoring`.
## 15. Open Issues
- No blocking architecture, formulation, I/O, reference, or tolerance issue remains.
- Doxygen executable use and generated documentation are deferred by explicit user
decision; this does not waive production Doxygen comments or `Doxyfile` configuration.
@@ -0,0 +1,120 @@
# C++ Object-Oriented Modular Refactoring Implementation Report
## Metadata
- feature_id: `cpp-object-oriented-modular-refactoring`
- owner_agent: `implementation-agent`
- final_step: `24 final-quality-reference-gate`
- source_plan: `docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- phase_index: `phases/cpp-object-oriented-modular-refactoring/index.json`
- evidence_date: `2026-08-16`
- head: `f84ebb541f4717ab8300cf0d80497e1277d3bb48`
- reference_baseline: `1e5758f3e482fae4c3d58cac680abe0aac02e549`
- classification: `pass-for-physics-evaluation`
## Scope and no-change assertion
Step 24 added no production behavior, no comparator/tolerance changes, and no
reference artifact changes. The only implementation-owned source action during this
step was running `clang-format -i` on four files that the dry-run style gate reported;
`git diff --raw`, `git diff --numstat`, and `git diff --check` for those files were
empty after the run, so no tracked source-content diff was introduced.
No Doxygen executable, hook entry point, `scripts/execute.py`, Abaqus, Nastran,
reference solver, commit, or push was run.
## Prior Step RED/GREEN/VERIFY evidence
The final gate reviewed the Executor-recorded Step 0-23 summaries. Each prior step is
already `completed` in the phase index and retains its Executor-owned timestamps.
| Step | Task | Evidence summary |
| ---: | --- | --- |
| 0 | `coding-style-agent-contract` | RED missing mandatory `CODINGSTYLE.md`; GREEN/VERIFY pytest 13/13, clean-env pytest 20/20, VS18 Debug build and CTest 144/144. |
| 1 | `cpp-style-tooling` | RED missing style/Doxygen config; GREEN/VERIFY policy pytest, clean-env pytest 21/21, LLVM 22.1.8 config checks, VS18 Debug build, CTest 144/144. |
| 2 | `architecture-boundaries` | Documentation-only architecture/ADR update; contract grep, diff check, MSVC Debug build, CTest discovery and 144/144 passed. |
| 3 | `foundation-google-style` | RED missing `.h` header; GREEN/VERIFY focused build/CTest 22/22, format, full build, CTest 144/144. |
| 4 | `model-element-google-style` | RED missing model `.h`; GREEN/VERIFY focused build/CTest 37/37, format 14 files, full build, CTest 144/144. |
| 5 | `solver-workflow-google-style` | RED missing analysis model `.h`; GREEN/VERIFY focused build/CTest 57/57, format 31 files, full build, CTest 144/144. |
| 6 | `io-application-google-style` | RED missing input reader `.h`; GREEN/VERIFY focused I/O/HDF5/app/reference CTest 36/36, format 23 files, full build, CTest 144/144. |
| 7 | `vector3-value-type` | RED missing `vector3.h`; GREEN/VERIFY Vector3 tests 7/7, format/tidy, full build, CTest 151/151. |
| 8 | `element-geometry-vector3` | RED typed Vector3 seam compile failure; GREEN/VERIFY focused CTest 44/44, duplicate scan 0, format, full build, CTest 155/155. |
| 9 | `result-io-vector3` | RED duplicate helpers; GREEN/VERIFY ResultRecovery/InpDomainMapping/HDF5 33/33, duplicate scan 0, format, full build, CTest 156/156. |
| 10 | `dense-blas-adapter` | RED missing dense BLAS internal header; GREEN/VERIFY focused CTest 4/4, public vendor scan 0, format, full build, CTest 158/158. |
| 11 | `source-target-resolver` | RED missing ASCII/source-target modules and plus-label failure; GREEN/VERIFY targeted/focused tests 44/44, helper scan, format, full build, CTest 164/164. |
| 12 | `material-property-hierarchy` | RED missing material/property bases; GREEN/VERIFY focused CTest 11/11, format, full build, CTest 170/170. |
| 13 | `element-definition-domain` | RED missing element definition/ownership APIs; GREEN/VERIFY focused CTest 20/20, format, full build, CTest 172/172. |
| 14 | `runtime-element-factory` | RED missing runtime element API; GREEN/VERIFY focused CTest 35/35, dynamic_cast scan 0, format, full build, CTest 176/176. |
| 15 | `generic-dof-manager` | RED fake element/layout seam failures; GREEN/VERIFY focused CTest 21/21, concrete/helper branches 0, format, full build, CTest 178/178. |
| 16 | `generic-sparse-assembler` | RED fake runtime contribution seam missing; GREEN/VERIFY SparseAssembly 9/9, concrete branch count 0, format, full build, CTest 179/179. |
| 17 | `generic-result-recovery` | RED missing generic recovery seam; GREEN/VERIFY focused CTest 53/53, concrete branch count 0, format, full build, CTest 182/182. |
| 18 | `load-hierarchy` | RED missing Load APIs; GREEN/VERIFY focused CTest 29/29, format, full build, CTest 186/186. |
| 19 | `boundary-condition-policy` | RED missing BoundaryCondition APIs; GREEN/VERIFY focused CTest 29/29, format, full build, discovery/full CTest 191/191. |
| 20 | `analysis-hierarchy` | RED missing `analysis.h`; GREEN/VERIFY focused CTest 12/12, scans, full build, discovery/full CTest 193/193. |
| 21 | `domain-mapper-modules` | RED missing private mapper seam; GREEN/VERIFY focused CTest 17/17, format, full build, CTest 197/197. |
| 22 | `result-recovery-modules` | RED missing recovery component seam; GREEN/VERIFY focused CTest 28/28, format, full build, CTest 203/203. |
| 23 | `hdf5-writer-modules` | RED missing HDF5 component seam; GREEN/VERIFY focused schema/atomicity CTest 13/13, scans, format, full build, CTest 206/206. |
Step 24 is a final verification gate and did not manufacture a new RED condition;
it reused the approved B33 and MITC4 reference comparisons after style and full
build/test verification.
## Step 24 command evidence
| Stage | Command | Exit | Duration | Result |
| --- | --- | ---: | ---: | --- |
| Environment diagnostic | `uv run --with pytest python -m pytest -v -rs` with inherited `FESA_HARNESS_CODEX_SANDBOX=danger-full-access` | 1 | 2.700s | Environment-specific failure: default-sandbox test observed the explicit override. |
| Policy verify | `Remove FESA_HARNESS_CODEX_SANDBOX` in child process; `uv run --with pytest python -m pytest -v -rs` | 0 | 0.751s | 21/21 passed. |
| Tool verify | `& "C:/Program Files/LLVM/bin/clang-format.exe" --version` | 0 | 0.026s | clang-format 22.1.8. |
| Tool verify | `& "C:/Program Files/LLVM/bin/clang-tidy.exe" --version` | 0 | 0.030s | clang-tidy 22.1.8. |
| Tool verify | `& "C:/Program Files/LLVM/bin/clang-tidy.exe" --verify-config` | 0 | 0.028s | No config errors. |
| Style RED | `clang-format --dry-run --Werror` over 163 files | 1 | 0.892s | Four whitespace/line-ending format findings. |
| Style normalization | `clang-format -i` on the four reported files | 0 | tool wall 0.5s | No tracked content diff after formatting normalization. |
| Style verify | `clang-format --dry-run --Werror` over 163 files | 0 | 0.888s | Passed. |
| Public-header policy | `clang-tidy --config-file=.clang-tidy <header> -- -x c++ -std=c++17 -Iinclude` over 46 public headers | 0 | 41.822s | Passed. The 47 naming diagnostics were 45 required trailing-underscore header guards and two `const` parameter classifications; there were zero other naming diagnostics. |
| Doxygen/header-guard policy | Read-only scan of production headers and test Doxygen tags | 0 | 0.302s | 63/63 production headers had guards and Doxygen contracts; 0 test files contained imposed Doxygen tags. |
| Header extension | `.hpp` scan under `include src tests` | 0 | 0.162s | 0 legacy `.hpp` files. |
| Dependency precheck | Test declared GoogleTest/MKL/TBB/HDF5 paths | 0 | 0.165s | All paths found. |
| Fresh configure | `cmake --fresh -S . -B .harness/build -G "Visual Studio 18 2026" -A x64 ...` | 0 | 6.204s | MSVC 19.51, VS18, build files generated. |
| Build | `cmake --build .harness/build --config Debug --target fesa_tests` | 0 | 9.639s | Debug aggregate test target built. |
| Discovery | `ctest --test-dir .harness/build -C Debug --show-only=json-v1` | 0 | 0.235s | 206 tests discovered. |
| Full test | `ctest --test-dir .harness/build -C Debug --output-on-failure` | 0 | 9.908s | 206/206 passed. |
| Artifact check | Exact declared artifact existence, SHA-256, line/row inventory | 0 | 0.218s | Six declared files present. |
| Artifact no-change | `git diff --exit-code 1e5758f -- reference` | 0 | 0.165s | No reference diff. |
| Artifact schema | Read-only type/header/key/finite precheck | 0 | 0.266s | B33/S4 type present; CSV headers/keys/finite checks passed. |
| Compare | `ctest --test-dir .harness/build -C Debug -R "B33ReferenceComparison|Mitc4S4Reference" --output-on-failure` | 0 | 1.260s | 3/3 approved reference tests passed. |
| Generated result check | Required `results.h5` existence and hashes | 0 | 0.218s | B33 and MITC4 comparison `results.h5` present. |
| Post no-change | `git diff --exit-code 1e5758f -- reference` | 0 | 0.179s | No reference diff after compare. |
| Whitespace check | `git diff --check` | 0 | 0.199s | No whitespace errors. |
## Requirement traceability
| Requirement | Final-gate evidence |
| --- | --- |
| `R-PRESERVE-001` | Full CTest 206/206, B33 comparison 176/176 rows passed, MITC4 S4 comparison 147/147 blocking U rows passed; no tolerance/comparator/reference diff. |
| `R-STYLE-001` | `.h` header scan passed with zero `.hpp`; clang-format passed over 163 files; clang-tidy selected public-header check passed over 46 headers. |
| `R-DOC-001` | Policy pytest passed; 63/63 production headers had header guards and Doxygen contracts, while 0 test files contained imposed Doxygen tags. Doxygen generation was intentionally not run. |
| `R-DUP-001` | Step 7-9 summaries record the shared `Vector3`, element/result/I/O preservation tests, and duplicate-definition scans with zero remaining local definition families; full CTest 206/206 passed. |
| `R-DUP-002` | Steps 10, 11, and 15 record the single dense-BLAS adapter, shared ASCII/source resolver, and DofManager-owned invariant logic with focused tests and helper/branch scans; full CTest 206/206 passed. |
| `R-MODEL-001` | Steps 12-13 record polymorphic Material/ElementProperty/ElementDefinition ownership and stable Domain views; focused ownership tests and full CTest passed. |
| `R-ELEMENT-001` | Step 14 records semantic/runtime separation, fail-closed factory compatibility checks, virtual destruction, owner-bounded views, and zero `dynamic_cast` uses; full CTest passed. |
| `R-PIPELINE-001` | Steps 15-17 record generic DofManager, SparseAssembler, and ResultRecovery seams with fake runtime elements, deterministic/atomic tests, and zero concrete B33/MITC4 consumer branches; full CTest passed. |
| `R-LOAD-001` | Step 18 records Domain-owned Load objects, ordered contributions, validation-before-candidate accumulation, and focused LoadAssembler tests; full CTest passed. |
| `R-BC-001` | Step 19 records BoundaryCondition definitions and stable essential-constraint partition/reconstruction, including nonzero and `0 x 0 Kff` cases; full CTest passed. |
| `R-ANALYSIS-001` | Step 20 records minimal base `Run()` dispatch and procedure-owned lifecycle tests, including factorize-before-load, exactly-one factorization, all-constrained solve, and writer suppression on recovery failure; full CTest passed. |
| `R-MODULE-001` | Steps 21-23 record the approved Domain-mapper, result-recovery, and HDF5 private component splits with facade, diagnostic, identity, rollback, self-check, and atomic-finalization tests; full CTest passed. |
| `R-AGENT-001` | Step 0 records the mandatory `docs/CODINGSTYLE.md` implementation-agent contract; the final clean-environment policy suite passed 21/21. |
| `R-SCOPE-001` | No production behavior or future-feature changes in Step 24; reference tree diff against `1e5758f` is empty. |
All acceptance traceability rows from the approved implementation plan are listed
above. Their owning task rows `T00..T23` retain the recorded RED/GREEN/VERIFY
summaries, and `T24` supplies the final style, build, CTest, artifact, HDF5,
comparison, and no-change evidence.
## Handoff
Implementation gate verdict: `pass-for-physics-evaluation`.
Open issues: none blocking. The inherited `FESA_HARNESS_CODEX_SANDBOX` override is an
environment note only; the clean child-process policy command passed without code
changes.
@@ -0,0 +1,165 @@
# C++ Object-Oriented Modular Refactoring Reference Comparison Report
## Metadata
- owner_agent: `implementation-agent`
- feature_id: `cpp-object-oriented-modular-refactoring`
- report_status: `passed`
- date: `2026-08-16`
- reference_baseline: `1e5758f3e482fae4c3d58cac680abe0aac02e549`
- command_order: `ARTIFACT CHECK -> COMPARE -> CLASSIFY -> REPORT`
- authoritative FESA output: generated `results.h5`
- reference artifacts: read-only existing files under `reference/`
## ARTIFACT CHECK
Exact declared artifact inventory:
| Artifact | Bytes | Lines | Data rows | SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `reference/cantilever beam/cantilever beam.inp` | 2330 | 106 | N/A | `E406EA9560321B791DBDB829E03BD24593B9875E0195D35B86BD931EDA122EF3` |
| `reference/cantilever beam/cantilever beam displacements.csv` | 1790 | 12 | 11 | `7B3312FBC8848E81D9A0FD4FF2B56BC1954636A2C14B5C1CBB269CB9477D3C31` |
| `reference/cantilever beam/cantilever beam elemental forces.csv` | 1396 | 12 | 11 | `E5E77FEC0FA9482AE018DBF296E74D396335C7C711BD2E9AA2315247A34290BA` |
| `reference/cantilever beam/cantilever beam reactions.csv` | 1780 | 12 | 11 | `BF30CDB0CD50106885DE14D63492737736C587426EBD787DE4F7EE6AA86DAA23` |
| `reference/shell/shell.inp` | 4770 | 164 | N/A | `4005851E1AB22FD3A16AC17A8D5DA3E051233F69F37419079F3553AD134ECFCF` |
| `reference/shell/shell displacements.csv` | 5592 | 50 | 49 | `C81D94E0B4A849F87AA0F79C83A79B94D5661AC79E44ED826919AB432C87746B` |
Read-only schema precheck:
| Check | Result |
| --- | --- |
| B33 input contains `TYPE=B33` | Pass |
| MITC4 S4 input contains `TYPE=S4` | Pass |
| B33 displacement CSV header/key/finite precheck | Pass: 11 rows, 11 unique keys, 0 duplicate keys, 0 nonfinite values |
| B33 reaction CSV header/key/finite precheck | Pass: 11 rows, 11 unique keys, 0 duplicate keys, 0 nonfinite values |
| B33 elemental-force CSV header/key/finite precheck | Pass: 11 rows, 11 unique keys, 0 duplicate keys, 0 nonfinite values |
| MITC4 S4 displacement CSV header/key/finite precheck | Pass: 49 rows, 49 unique keys, 0 duplicate keys, 0 nonfinite values |
| Reference tree diff before compare | Pass: `git diff --exit-code 1e5758f -- reference`, exit 0 |
## COMPARE
Command:
```powershell
ctest --test-dir .harness/build -C Debug `
-R "B33ReferenceComparison|Mitc4S4Reference" --output-on-failure
```
Result: exit 0 in 1.260s, 3/3 tests passed.
Generated artifacts:
| Artifact | Bytes | SHA-256 |
| --- | ---: | --- |
| `.harness/build/reference/cantilever-beam-b33/results.h5` | 25336 | `58CD358F68D8094079E9E525C35EE88AE0575A2962D93472BF0678D78A785247` |
| `.harness/build/reference/cantilever-beam-b33/comparison.json` | 128118 | `258347AEA791D981AEA9B2BCAD85DE5344D4859ECA3692DC5E7AA01A848F8E0D` |
| `.harness/build/reference/mitc4-shell-s4-comparison/results.h5` | 95024 | `A8D2E12886E87BAA5D895B7E96278EA4B462718D13B985CA2688B505480F0195` |
| `.harness/build/reference/mitc4-shell-s4-comparison/comparison.json` | 94349 | `8E8DEA51B6F7C663BACC41FDA6103A4596DB26E02F1EAD6069D458F51E0102E6` |
| `.harness/build/reference/mitc4-shell-s4-metadata/results.h5` | 95024 | `8FD6609A2D3758365A2AC2E34692CC0EC982D8E67396BDFF03FAAF2539EF483C` |
| `.harness/build/reference/mitc4-shell-s4-metadata/comparison.json` | 94349 | `8E8DEA51B6F7C663BACC41FDA6103A4596DB26E02F1EAD6069D458F51E0102E6` |
The generated `comparison.json` files are the deterministic machine-readable
per-row decision records. The summaries below preserve row counts, worst rows,
precheck/tolerance decisions, and artifact hashes for audit.
## HDF5-to-CSV projection and tolerance contracts
### B33
- Model: `cantilever-beam-b33`
- HDF5 datasets:
- `/steps/Step-1/frames/0/nodal/displacement`
- `/steps/Step-1/frames/0/nodal/reaction`
- `/steps/Step-1/frames/0/element/section_resultant`
- Row identity: model, `Step-1`, frame `0`, instance `PART-1_1-1`, source node label, quantity, component.
- Components:
- displacement `UX/UY/UZ/URX/URY/URZ`
- reaction `RF1/RF2/RF3/RM1/RM2/RM3`
- section resultant `N/T/My/Mz`
- Tolerance: `absolute_floor + 1.0e-6 * reference_scale`, with reference scale from read-only Abaqus rows only; displacement/rotation floor `1.0e-9`, force/moment floor `1.0e-3`.
- Pre-tolerance policy: missing, extra, duplicate, nonfinite, schema-mismatched, or identity-mismatched rows fail before tolerance.
### MITC4 S4
- Case: `shell-s4`
- Source element type: `S4`
- Internal formulation: `FESA-MITC4`
- Integration rule: `2x2x2-gauss; mitc4-edge-midpoint-shear`
- HDF5 dataset: `/steps/Step-1/frames/0/nodal/displacement`
- Row identity: case, instance, source node label, component.
- Components: `U1/U2/U3` blocking; `UR1/UR2/UR3` warning-only.
- Tolerance: fixed absolute `1.0e-5` for every U/UR row; no component scale, row denominator, zero clamp, omission, or averaging affects the decision.
- Pre-tolerance policy: missing, extra, duplicate, nonfinite, header-mismatched, or identity-mismatched projected rows fail before tolerance.
## CLASSIFY
Blocking classification: `pass`.
No missing, extra, duplicate, nonfinite, schema-mismatched, identity-mismatched, or
tolerance-failed blocking row was reported by either generated comparison.
### B33 row and metric decisions
Overall: `passed=true`; row decisions: 176/176 passed; failed rows: 0; nonfinite row
metrics: 0; stress comparison applicable: `false` with N/A reason
`Abaqus beam stress comparison is N/A; analytical/unit and HDF5 schema tests provide stress evidence.`
Physics evidence: endpoint consistency passed; free residual norm
`9.356339321107032e-07`.
| Quantity | Component | Rows | Reference scale | Max abs error | Max normalized error | RMS error | Norm error | Worst row decision |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| displacement | UX | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `1e-09`, pass |
| displacement | UY | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `1e-09`, pass |
| displacement | UZ | 11 | 0.0190476272 | 5.33322917078971e-10 | 0.026602795021995 | 2.79095304685767e-10 | 9.25654406392607e-10 | node 11, FESA -0.019047626666677083, reference -0.0190476272, tol `2.00476272e-08`, pass |
| displacement | URX | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `1e-09`, pass |
| displacement | URY | 11 | 0.00285714399 | 1.00001394318094e-10 | 0.0259262798011579 | 5.80165854051178e-11 | 1.92419245406385e-10 | node 9, FESA 0.002742858240001394, reference 0.00274285814, tol `3.85714399e-09`, pass |
| displacement | URZ | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `1e-09`, pass |
| reaction | RF1 | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `0.001`, pass |
| reaction | RF2 | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `0.001`, pass |
| reaction | RF3 | 11 | 1000000 | 8.19563865661621e-07 | 8.1874512054108e-07 | 3.62393833938394e-07 | 1.20192437351202e-06 | node 1, FESA 1000000.0000008196, reference 1000000, tol `1.001`, pass |
| reaction | RM1 | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `0.001`, pass |
| reaction | RM2 | 11 | 10000000 | 5.05149364471436e-06 | 5.05098854585977e-07 | 1.52613777609191e-06 | 5.06162638168429e-06 | node 1, FESA -10000000.000005051, reference -10000000, tol `10.001`, pass |
| reaction | RM3 | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `0.001`, pass |
| section_resultant | N | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `0.001`, pass |
| section_resultant | T | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `0.001`, pass |
| section_resultant | My | 11 | 10000000 | 0.0156002428611895 | 0.00155986829928902 | 0.00470365086790684 | 0.0156002450736765 | node 11, FESA 2.4286118949223834e-07, reference -0.0156, tol `10.001`, pass |
| section_resultant | Mz | 11 | 0 | 0 | 0 | 0 | 0 | node 1, FESA 0, reference 0, tol `0.001`, pass |
### MITC4 S4 row and metric decisions
Overall: `passed=true`; rows: 294/294 within tolerance; blocking U rows: 147/147
passed; warning-only UR rows: 147/147 within tolerance; warning count: 0; vector
metrics: 49.
| Component | Rows | Blocking rows | Reference scale | Tolerance | Max abs error | Max normalized error | RMS error | Vector norm error | Worst row decision |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| U1 | 49 | 49 | 3.11730945e-23 | 1e-05 | 3.11730945e-23 | 3.11730945e-18 | 1.13587076092006e-23 | 7.95109532644045e-23 | node 12, FESA 0, reference 3.11730945e-23, pass |
| U2 | 49 | 49 | 3.11730945e-23 | 1e-05 | 3.11730945e-23 | 3.11730945e-18 | 1.13587076092006e-23 | 7.95109532644045e-23 | node 11, FESA 0, reference 3.11730945e-23, pass |
| U3 | 49 | 49 | 2.37408203e-05 | 1e-05 | 1.90378534915144e-07 | 0.0190378534915144 | 4.24886125341252e-08 | 2.97420287738877e-07 | node 2, FESA -2.3550441765084857e-05, reference -2.37408203e-05, pass |
| UR1 | 49 | 0 | 7.60725743e-06 | 1e-05 | 6.88285496274043e-08 | 0.00688285496274043 | 2.7737837921291e-08 | 1.94164865449037e-07 | node 44, FESA 1.3985063203725957e-06, reference 1.46733487e-06, pass warning-only |
| UR2 | 49 | 0 | 7.60725743e-06 | 1e-05 | 6.8828549627399e-08 | 0.0068828549627399 | 2.77378379212909e-08 | 1.94164865449036e-07 | node 34, FESA 1.398506320372601e-06, reference 1.46733487e-06, pass warning-only |
| UR3 | 49 | 0 | 5.27113701e-25 | 1e-05 | 5.27113701e-25 | 5.27113701e-20 | 2.12986098533393e-25 | 1.49090268973375e-24 | node 34, FESA 0, reference -5.27113701e-25, pass warning-only |
Overall worst MITC4 row: node 2, component U3, FESA
`-2.3550441765084857e-05`, reference `-2.37408203e-05`, absolute error
`1.903785349151439e-07`, tolerance `1e-05`, normalized error
`0.01903785349151439`, blocking pass.
## Reference no-change assertion
Post-compare command:
```powershell
git diff --exit-code 1e5758f -- reference
```
Result: exit 0, `<no reference diff>`.
No reference input, CSV, tolerance, comparator contract, or generated reference
artifact was modified. Generated FESA outputs are confined to `.harness/build/`.
## Open issues
None blocking. Passing comparison is only an implementation handoff to physics
evaluation; it is not release readiness or physics approval.
@@ -0,0 +1,70 @@
# Implementation Agent Terra Model Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Configure the project-local `implementation-agent` to use `gpt-5.6-terra` while preserving its existing reasoning effort.
**Architecture:** Add one explicit model override to the existing Implementation Agent TOML profile. Do not introduce shared model policy, modify other profiles, or change the agent's instructions.
**Tech Stack:** TOML, Python 3 `tomllib`, Git
## Global Constraints
- Set `model` to exactly `gpt-5.6-terra` only in `.codex/agents/implementation-agent.toml`.
- Preserve `model_reasoning_effort = "extra high"`.
- Do not modify other custom agents, user-global Codex configuration, solver production code, or agent instructions.
- Do not add or modify contract tests or other test files.
- Verify the change only through TOML parsing, exact-value assertions, and Git diff inspection.
---
### Task 1: Add the Implementation Agent model override
**Files:**
- Modify: `.codex/agents/implementation-agent.toml`
- Test: none, per the approved design
**Interfaces:**
- Consumes: the existing project-local `implementation-agent` TOML profile
- Produces: `model = "gpt-5.6-terra"` with the existing `model_reasoning_effort = "extra high"`
- [ ] **Step 1: Add the model setting**
Insert the model key between `sandbox_mode` and `model_reasoning_effort` so the profile header is:
```toml
name = "implementation-agent"
description = "Implements FESA solver features in C++17/MSVC by following approved TDD-first implementation plans."
sandbox_mode = "workspace-write"
model = "gpt-5.6-terra"
model_reasoning_effort = "extra high"
```
- [ ] **Step 2: Parse the profile and verify the exact values**
Run:
```powershell
python -c "import pathlib, tomllib; p = tomllib.loads(pathlib.Path('.codex/agents/implementation-agent.toml').read_text(encoding='utf-8')); assert p['model'] == 'gpt-5.6-terra'; assert p['model_reasoning_effort'] == 'extra high'"
```
Expected: exit code `0` with no output.
- [ ] **Step 3: Verify the change is surgical**
Run:
```powershell
git diff --check
git diff -- .codex/agents/implementation-agent.toml
```
Expected: no whitespace errors, and the profile diff contains only the added
`model = "gpt-5.6-terra"` line.
- [ ] **Step 4: Commit the configuration change**
```powershell
git add -- .codex/agents/implementation-agent.toml
git commit -m "chore: use Terra for implementation agent"
```
@@ -0,0 +1,355 @@
# FESA C++ Object-Oriented Modular Refactoring Design
## 상태
- 설계 대화 승인: 2026-08-16
- 서면 spec 리뷰: 승인 완료 (2026-08-16)
- 구현 상태: not-started
## 목적
현재 B33 Euler beam, MITC4 shell과 linear static solver의 수치 및 외부 동작을
유지하면서 C++ production code를 객체 책임 중심으로 재구성한다. 이번 리팩터링은
Google C++ Style Guide 기반의 일관된 코드 스타일, 중복 제거, production Doxygen 문서화,
명시적인 추상 경계와 응집된 모듈을 제공해야 한다.
효율성은 runtime 성능 향상이 아니라 다음 유지보수 특성을 의미한다.
- 새 element, element property, material, analysis, load 또는 boundary condition을 추가할
때 기존 concrete 구현을 수정하는 범위를 줄인다.
- 하나의 의미를 여러 translation unit에서 다시 구현하지 않는다.
- parser semantic data, numerical kernel, assembly, recovery와 output 책임을 구분한다.
- 수치식과 I/O 계약을 사람이 직접 대조할 수 있는 단순한 dependency direction을 유지한다.
## 범위
### 포함
- production 및 test C++ 전체의 Google-style naming과 formatting 전환
- production header의 `.hpp`에서 `.h`로 전환과 header guard 적용
- production 함수와 class의 Doxygen 문서화
- `ElementDefinition`, `Element`, `ElementProperty`, `Material`, `Analysis`, `Load`,
`BoundaryCondition` 추상 경계
- B33, MITC4, isotropic linear elasticity, beam/shell property, linear static analysis,
concentrated nodal load와 prescribed displacement의 concrete 구현 연결
- `Vector3`, source-target resolution, DOF invariant validation과 dense-BLAS adapter의
중복 제거
- `domain_mapper.cpp`, `hdf5_results_writer.cpp`, `result_recovery.cpp`의 책임별 분할
- style, Doxygen, MSVC/CTest와 reference comparison 검증
- `docs/CODINGSTYLE.md` 유지관리 문서와 Implementation Agent 필수 참조 연결
### 제외
- MITC3, solid hexa/tetra 또는 다른 element 구현
- density, plasticity, anisotropic material 동작 구현
- dynamic, eigenvalue, response spectrum 또는 random vibration analysis 구현
- distributed load, body force 또는 MPC 구현
- 승인된 formulation, sign, units, coordinate, HDF5 schema, reference artifact 또는
tolerance 변경
- runtime 성능 최적화 또는 parallel reduction policy 변경
- general plugin registry, global static registration 또는 shared ownership framework
## 근거와 제약
현재 production code는 semantic record와 numerical kernel을 이미 구분하지만 Domain은
element, material, property, load와 boundary를 concrete vector로 각각 소유한다.
`SparseAssembler`, `DofManager``ResultRecovery`는 B33/MITC4 concrete storage를 직접
알아야 한다. B33과 MITC4라는 두 실제 element가 있으므로 element abstraction은 현재
구현으로 검증할 수 있다.
반면 아직 구현되지 않은 plastic integration, dynamic state, MPC enforcement의 메서드를
base class에 미리 추가할 근거는 없다. 추상 class는 현재 concrete 구현이 실제로 공유하는
계약만 제공하고 future capability는 해당 기능의 requirements/formulation/ADR이 승인될 때
추가한다.
다음 기존 계약은 리팩터링보다 우선한다.
- Domain은 semantic definition을 단독 소유하고 parsing 이후 불변으로 취급한다.
- AnalysisModel은 Domain을 복사하지 않는 non-owning stable-index view다.
- DofManager는 DOF와 equation numbering 및 sparse pattern을 단독 소유한다.
- assembly worker는 global CSR storage를 직접 수정하지 않는다.
- stiffness partition과 factorization은 load assembly보다 먼저 수행한다.
- reaction과 free-equilibrium evidence는 full residual `K*d-F`에서 구한다.
- result recovery와 final HDF5는 candidate validation 후 commit한다.
- B33 및 MITC4 reference identity와 tolerance는 변경하지 않는다.
## 추상 계층과 소유권
```text
Domain
├─ ElementDefinition*
│ ├─ EulerBeam3DDefinition
│ └─ Mitc4ShellDefinition
├─ ElementProperty*
│ ├─ GeneralBeamSection
│ └─ ShellSection
├─ Material*
│ └─ IsotropicLinearElasticMaterial
└─ StepDefinition
├─ Load*
│ └─ ConcentratedNodalLoad
└─ BoundaryCondition*
└─ PrescribedDisplacementBoundaryCondition
Analysis
└─ LinearStaticAnalysis
Element
├─ EulerBeam3D
└─ Mitc4Shell
```
Domain은 각 base type을 `std::unique_ptr`로 단독 소유한다. Public access는 const이며
collection의 vector position은 기존 stable `EntityIndex` 의미를 유지한다. AnalysisModel과
후속 solver object는 raw ownership을 획득하지 않고 Domain 수명 안에서 index 또는 const
reference만 사용한다. Copy를 지원하기 위한 speculative `Clone()``std::shared_ptr`
추가하지 않는다.
`ElementDefinition``Element`는 서로 다른 책임이다.
- `ElementDefinition`은 source identity, source element type, node connectivity와
property/material identity를 제공하는 semantic model이다.
- `Element`는 active DOF layout, stiffness contribution, transformation과 result recovery를
제공하는 numerical kernel이다.
- `ElementFactory`는 definition, property와 material compatibility를 검증한 뒤 concrete
kernel을 만든다.
- 잘못된 조합은 `dynamic_cast` 실패나 undefined behavior로 넘기지 않고 기존
`Status`/`Result<T>` diagnostic으로 fail-closed 처리한다.
Element result는 모든 element에 의미 없는 field를 추가한 비대한 base record가 아니다.
공통 step/frame/source identity를 가진 backend-neutral result bundle이 beam 및 shell row를
각자의 명확한 record로 보관한다. ResultRecovery와 ResultsWriter는 stable row identity를
보존하며 서로 다른 result location을 평균하거나 합치지 않는다.
## Material과 Element Property
`Material` base는 identity, source location과 수명 계약만 제공한다. 현재 concrete type은
물리 의미를 드러내도록 `IsotropicLinearElasticMaterial`로 명명한다. 현재 element factory가
필요로 하는 isotropic elastic capability만 노출한다.
다음 future concern은 이번 interface에 빈 메서드나 optional field로 미리 넣지 않는다.
- density와 inertia contribution
- anisotropic elastic constitutive data
- plastic history state와 return mapping
- temperature 또는 rate dependency
이 concern은 각 기능이 승인될 때 별도의 capability 또는 구성 객체로 추가한다. 같은
원칙으로 `ElementProperty`는 identity를 제공하고 `GeneralBeamSection``ShellSection`
각자 필요한 기하 property를 소유한다. Solid property를 예상해 비어 있는 thickness/area
accessor를 base에 추가하지 않는다.
## Analysis 계층
현재 `Analysis` base의 8개 protected hook은 linear-static lifecycle에 특화되어 있다.
이를 모든 future procedure에 강제하지 않는다.
```cpp
class Analysis {
public:
virtual ~Analysis() = default;
virtual Status Run(const AnalysisRequest& request) = 0;
};
```
현재 승인 순서는 `LinearStaticAnalysis::Run()`의 private 단계로 유지한다.
```text
initialize
-> build analysis model
-> build DOF map and sparse pattern
-> assemble and partition stiffness
-> factorize Kff
-> assemble loads and effective RHS
-> substitute and reconstruct
-> recover and write results
```
Dynamic, eigenvalue와 stochastic procedure는 추가될 때 별도 state, equation, solver와 output
lifecycle을 정의한다. 기존 linear-static hook 사이에 condition이나 unused future state를
추가하지 않는다. 이 책임 변경은 구현 전에 ADR-007을 대체하거나 개정하는 ADR로 기록한다.
## Load와 Boundary Condition
`Load` concrete object는 자신의 semantic target과 magnitude를 소유하고 ordered full-DOF
contribution을 생성한다. `LoadAssembler`는 active source order로 contribution을 모아 기존
fixed accumulation order로 global vector에 반영한다. Polymorphic load가 global vector를
직접 병렬 갱신하지 않는다.
현재 concrete load는 `ConcentratedNodalLoad`다. Future distributed load와 body force는
element-local contribution을 생성할 수 있지만 stable global reduction은 계속 assembler가
소유한다.
`BoundaryCondition`은 enforcement algorithm을 직접 수행하지 않고 constraint definition을
생성한다. 현재 concrete type은 nonzero 값을 포함하는
`PrescribedDisplacementBoundaryCondition`이다. `EssentialConstraintPolicy`가 기존 stable
elimination과 full/reduced reconstruction을 수행한다.
Future MPC는 별도 constraint equation과 enforcement policy를 요구한다. Prescribed
displacement, MPC, penalty와 Lagrange multiplier를 하나의 bool/enum branch가 누적된 class로
합치지 않는다.
## 공통 수학과 중복 제거
### Vector3
좌표, local axis, shell director와 cross-product는 고정 크기 `Vector3` 값 class를 사용한다.
동적 크기와 MKL-backed storage를 소유하는 기존 `Vector`와 역할을 섞지 않는다.
`Vector3`는 현재 반복되는 다음 연산을 한 번만 정의한다.
- component access
- addition, subtraction과 scalar multiplication
- `Dot()`
- `Cross()`
- `Norm()`
- `Normalized()`
- `IsFinite()`
Normalization failure policy는 호출 위치에서 기존 scale-aware diagnostic을 유지한다.
`Vector3`가 임의 tolerance, zero clamp 또는 solver diagnostic을 소유하지 않는다.
### 다른 공통 책임
- `SourceTargetResolver`: source label, instance와 set target을 stable identity로 해석한다.
- `DofManager::ValidateInvariants()`: full/free/constrained ordering과 equation mapping을 owner가
한 번 검증한다.
- private dense-BLAS adapter: Matrix와 Vector의 MKL integer conversion 및 copy operation을
공유한다. Vendor type은 public header에 노출하지 않는다.
- ASCII utility: case-insensitive name comparison과 positive source-label parsing을 공유한다.
중복 제거는 같은 의미와 failure policy가 반복될 때만 적용한다. 이름만 비슷하지만 units,
identity 또는 diagnostic owner가 다른 계산을 하나로 합치지 않는다. State가 없는 helper를
static-only class로 포장하지 않고 internal namespace/module을 사용한다.
## 모듈 구조
```text
include/fesa/
├─ analysis/
│ ├─ analysis.h
│ └─ linear_static_analysis.h
├─ elements/
│ ├─ element.h
│ ├─ element_definition.h
│ ├─ element_factory.h
│ ├─ euler_beam_3d.h
│ └─ mitc4_shell.h
├─ properties/
│ ├─ element_property.h
│ ├─ general_beam_section.h
│ └─ shell_section.h
├─ materials/
│ ├─ material.h
│ └─ isotropic_linear_elastic_material.h
├─ loads/
│ ├─ load.h
│ └─ concentrated_nodal_load.h
├─ constraints/
│ ├─ boundary_condition.h
│ ├─ prescribed_displacement.h
│ └─ essential_constraint_policy.h
├─ math/
│ ├─ vector.h
│ ├─ vector3.h
│ ├─ matrix.h
│ └─ sparse_matrix.h
└─ model/
├─ domain.h
├─ analysis_model.h
└─ source_target_resolver.h
```
`model_types.hpp`의 unrelated record는 각 owner module로 이동한다. Top-level orchestration
file은 다음과 같이 분리한다.
- Abaqus mapping: topology, material/property, step/load/BC와 final Domain assembly
- HDF5 output: RAII/primitives, model dataset, result dataset, self-check와 atomic finalization
- Result recovery: global equilibrium, beam recovery, shell recovery와 atomic state commit
Public header와 implementation dependency direction을 역전하지 않는다. MKL, TBB, HDF5와
Win32 type은 기존 adapter/private implementation 경계 안에 남는다.
## 코드 스타일과 문서화
`docs/CODINGSTYLE.md`를 FESA C++ style의 project-local source of truth로 사용한다. Google
C++ Style Guide가 baseline이고 FESA 계약이 우선한다.
주요 결정은 다음과 같다.
- C++17/MSVC 호환을 유지한다. Google guide의 현재 C++20 language target은 적용하지 않는다.
- 함수와 accessor를 포함한 production API는 PascalCase로 전면 전환한다.
- type은 PascalCase, 변수는 snake_case, constant/enumerator는 `kPascalCase`, class member는
trailing underscore를 사용한다.
- Header는 `.h`와 full-path Google header guard를 사용한다.
- Source는 기존 FESA/CMake 관례인 `.cpp`를 유지하는 project exception으로 둔다.
- Formatting은 `BasedOnStyle: Google`, 2-space indentation과 80-column limit를 사용한다.
- Production public/protected declaration에는 Doxygen contract를 기록한다.
- Production internal function은 definition에 목적과 비자명한 수치/순서 의미를 기록한다.
- Test code에는 Doxygen coverage를 요구하지 않는다.
Repository는 `.clang-format`, selected C++17-compatible `.clang-tidy`, `Doxyfile`과 optional
CMake docs target을 제공한다. Generated HTML은 source control에 넣지 않는다.
Implementation Agent의 profile은 구현 전에 `docs/CODINGSTYLE.md`를 mandatory global input으로
읽도록 변경한다. Agent workflow contract test는 해당 profile이 문서를 직접 참조하는지
검증한다.
## 오류 처리
- 모든 polymorphic base는 public virtual destructor를 갖는다.
- Factory는 null object를 성공 결과로 반환하지 않는다.
- Element/property/material incompatibility는 structured model diagnostic으로 거부한다.
- Public solver 경계는 기존 `Status`/`Result<T>`를 사용한다.
- Backend exception은 현재 failure category와 atomicity contract를 유지해 번역한다.
- Unknown future kind를 silent fallback이나 default concrete type으로 바꾸지 않는다.
- Failed candidate는 Domain, AnalysisState 또는 final HDF5를 부분 변경하지 않는다.
## 단계적 마이그레이션
1. 기존 unit/integration/reference 및 HDF5 contract baseline을 기록하고 architecture ADR을
갱신한다.
2. `.clang-format`, header rename/guard와 PascalCase를 module slice별 mechanical change로
적용한다.
3. `Vector3`, ASCII utility, SourceTargetResolver, DOF invariant validation과 private BLAS
adapter를 도입한다.
4. Domain semantic hierarchy와 current concrete material/property/load/boundary type을
연결한다.
5. Element runtime hierarchy와 factory를 DofManager, SparseAssembler와 ResultRecovery에
연결한다.
6. Ordered load contribution과 essential constraint policy를 연결한다.
7. Minimal Analysis base와 LinearStaticAnalysis-owned lifecycle로 전환한다.
8. Mapper, HDF5 writer와 recovery를 책임별로 분할하고 Doxygen/style coverage를 완료한다.
Mechanical formatting, API rename와 semantic restructuring을 같은 review unit에 섞지 않는다.
각 slice는 buildable하고 독립 검증 가능해야 한다.
## TDD와 검증
각 production change는 관련 C++ test와 같은 Step에서 `RED -> GREEN -> VERIFY`를 수행한다.
- abstract base와 concrete polymorphic use를 검증하는 compile-time/unit test
- factory success와 incompatible property/material rejection test
- base interface를 통한 B33/MITC4 stiffness 및 recovery test
- stable element, load와 boundary source-order test
- Domain ownership, AnalysisModel lifetime와 stable identity test
- Vector3 arithmetic, finite and normalization-boundary test
- 기존 parser/I/O, HDF5 schema와 atomicity test
- B33 및 MITC4 integration/reference comparison
- repeated execution의 sparse structure, result row와 diagnostic order test
- Doxygen warning, formatting과 selected lint check
- full MSVC x64 Debug `/W4 /WX` build와 CTest
수치 산술 순서를 의도적으로 변경하지 않은 slice는 가능한 한 exact equality를 요구한다.
Feature-approved reference tolerance는 최종 external comparison에만 그대로 적용한다.
## 완료 기준
- 승인된 abstraction과 current concrete implementation이 base interface를 통해 연결된다.
- DofManager, assembler와 recovery에 B33/MITC4 type branch 또는 duplicate geometry helper가
남지 않는다.
- Production 및 test C++가 `docs/CODINGSTYLE.md`의 naming/formatting 규칙을 만족한다.
- Production API와 non-obvious internal function에 요구된 Doxygen가 존재한다.
- Implementation Agent profile이 `docs/CODINGSTYLE.md`를 mandatory input으로 참조한다.
- 전체 MSVC x64 Debug build/CTest와 B33/MITC4 reference comparison이 통과한다.
- HDF5 schema, stable identity, diagnostic, tolerance와 reference artifact에 변경이 없다.
@@ -0,0 +1,38 @@
# Implementation Agent Terra 모델 지정 설계
## Metadata
- date: 2026-08-16
- status: approved-design
- scope: project-local `implementation-agent` 모델 선택
## 목표
FESA의 `implementation-agent`가 명시적으로 `gpt-5.6-terra`를 사용하도록 설정한다.
기존 `model_reasoning_effort = "extra high"` 설정은 그대로 유지한다.
## 설계
`.codex/agents/implementation-agent.toml`에 다음 모델 설정만 추가한다.
```toml
model = "gpt-5.6-terra"
```
이 설정은 프로젝트 로컬 `implementation-agent` 프로필에만 적용된다. 다른 custom agent,
사용자 전역 Codex 설정, agent 지시문과 solver production 코드는 변경하지 않는다.
## 검증
새 계약 테스트나 테스트 파일 변경은 추가하지 않는다. 변경 후 다음 항목만 확인한다.
1. `implementation-agent.toml`이 유효한 TOML로 파싱된다.
2. `model` 값이 정확히 `gpt-5.6-terra`이다.
3. `model_reasoning_effort` 값이 기존의 `extra high`로 유지된다.
4. Git diff에 설계된 설정 외의 구현 변경이 없다.
## 완료 조건
- `implementation-agent`에만 `gpt-5.6-terra` 모델 override가 존재한다.
- 기존 reasoning effort와 agent 동작 계약은 변경되지 않는다.
- 새 계약 테스트는 추가되지 않는다.
+30
View File
@@ -0,0 +1,30 @@
#ifndef FESA_ANALYSIS_ANALYSIS_H_
#define FESA_ANALYSIS_ANALYSIS_H_
#include <filesystem>
#include "fesa/core/status.h"
namespace fesa {
/// @brief Carries input and authoritative output paths for one analysis run.
struct AnalysisRequest {
std::filesystem::path input_path;
std::filesystem::path output_path;
};
/// @brief Defines the minimal execution contract shared by analysis procedures.
class Analysis {
public:
virtual ~Analysis() = default;
/// @brief Executes one procedure for the supplied input and output paths.
/// @param request Input and authoritative output paths for this run.
/// @return The concrete procedure result without changing its failure
/// category.
virtual Status Run(const AnalysisRequest& request) = 0;
};
} // namespace fesa
#endif // FESA_ANALYSIS_ANALYSIS_H_
+63
View File
@@ -0,0 +1,63 @@
#ifndef FESA_ANALYSIS_ANALYSIS_MODEL_H_
#define FESA_ANALYSIS_ANALYSIS_MODEL_H_
#include <vector>
#include "fesa/model/domain.h"
namespace fesa {
/// @brief Provides the active-step view into a non-owned Domain.
/// @note The referenced Domain must outlive this object and retains all
/// semantic ownership.
class AnalysisModel {
public:
/// @brief Creates the sole active-step view for a valid Domain.
/// @param domain Domain that remains alive for the returned view's lifetime.
/// @return A stable view or an input-cardinality failure.
static Result<AnalysisModel> Create(const Domain& domain);
/// @brief Returns the non-owned Domain backing this view.
const Domain& GetDomain() const noexcept;
/// @brief Returns the sole active static step.
const StepDefinition& Step() const noexcept;
/// @brief Returns active element-definition indices in stable Domain order.
const std::vector<EntityIndex>& ActiveElements() const noexcept;
/// @brief Returns active B33 indices in their concrete compatibility view.
const std::vector<EntityIndex>& ActiveBeamElements() const noexcept;
/// @brief Returns reachable material indices in stable internal order.
const std::vector<EntityIndex>& ActiveMaterials() const noexcept;
/// @brief Returns reachable property indices in stable internal order.
const std::vector<EntityIndex>& ActiveProperties() const noexcept;
/// @brief Returns reachable beam-section indices in stable internal order.
const std::vector<EntityIndex>& ActiveSections() const noexcept;
/// @brief Returns boundary-condition indices in source order.
const std::vector<EntityIndex>& ActiveBoundaryConditions() const noexcept;
/// @brief Returns concentrated-load indices in source order.
const std::vector<EntityIndex>& ActiveLoads() const noexcept;
private:
/// @brief Builds stable indices without copying the referenced Domain.
explicit AnalysisModel(const Domain& domain);
const Domain* domain_;
std::vector<EntityIndex> active_elements_;
std::vector<EntityIndex> active_beam_elements_;
std::vector<EntityIndex> active_materials_;
std::vector<EntityIndex> active_properties_;
std::vector<EntityIndex> active_sections_;
std::vector<EntityIndex> active_boundary_conditions_;
std::vector<EntityIndex> active_loads_;
};
} // namespace fesa
#endif // FESA_ANALYSIS_ANALYSIS_MODEL_H_
-34
View File
@@ -1,34 +0,0 @@
#pragma once
#include "fesa/model/domain.hpp"
#include <vector>
namespace fesa {
// Provides the sole active-step view while the referenced Domain retains all
// semantic ownership and must outlive this object.
class AnalysisModel {
public:
static Result<AnalysisModel> create(const Domain& domain);
const Domain& domain() const noexcept;
const StaticStepDefinition& step() const noexcept;
const std::vector<EntityIndex>& activeElements() const noexcept;
const std::vector<EntityIndex>& activeMaterials() const noexcept;
const std::vector<EntityIndex>& activeSections() const noexcept;
const std::vector<EntityIndex>& activeBoundaryConditions() const noexcept;
const std::vector<EntityIndex>& activeLoads() const noexcept;
private:
explicit AnalysisModel(const Domain& domain);
const Domain* domain_;
std::vector<EntityIndex> activeElements_;
std::vector<EntityIndex> activeMaterials_;
std::vector<EntityIndex> activeSections_;
std::vector<EntityIndex> activeBoundaryConditions_;
std::vector<EntityIndex> activeLoads_;
};
} // namespace fesa
+102
View File
@@ -0,0 +1,102 @@
#ifndef FESA_ANALYSIS_ANALYSIS_STATE_H_
#define FESA_ANALYSIS_ANALYSIS_STATE_H_
#include <array>
#include <cstddef>
#include <vector>
#include "fesa/core/status.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/math/vector.h"
#include "fesa/results/result_records.h"
namespace fesa {
/// @brief Owns mutable quantities required by the V0 linear-static frame.
class AnalysisState {
public:
/// @brief Allocates zeroed full-DOF vectors for a DOF manager.
/// @param dofs Owner of the full-DOF dimension used by every state vector.
/// @param identity Stable step and frame identity for this state.
static AnalysisState Create(const DofManager& dofs,
StepFrameIdentity identity);
/// @brief Returns mutable full-space displacement.
Vector& Displacement() noexcept;
/// @brief Returns full-space displacement.
const Vector& Displacement() const noexcept;
/// @brief Returns mutable full-space external force.
Vector& ExternalForce() noexcept;
/// @brief Returns full-space external force.
const Vector& ExternalForce() const noexcept;
/// @brief Returns mutable full-space internal force.
Vector& InternalForce() noexcept;
/// @brief Returns full-space internal force.
const Vector& InternalForce() const noexcept;
/// @brief Returns mutable full residual K*d-F.
Vector& Residual() noexcept;
/// @brief Returns full residual K*d-F.
const Vector& Residual() const noexcept;
/// @brief Returns mutable full-index reaction and free residual evidence.
Vector& Reaction() noexcept;
/// @brief Returns full-index reaction and free residual evidence.
const Vector& Reaction() const noexcept;
/// @brief Returns the stable step and frame identity.
const StepFrameIdentity& Identity() const noexcept;
/// @brief Returns mutable beam endpoint result rows.
std::vector<EndpointResultRow>& EndpointResults() noexcept;
/// @brief Returns beam endpoint result rows.
const std::vector<EndpointResultRow>& EndpointResults() const noexcept;
/// @brief Returns mutable beam Gauss result rows.
std::vector<GaussResultRow>& GaussResults() noexcept;
/// @brief Returns beam Gauss result rows.
const std::vector<GaussResultRow>& GaussResults() const noexcept;
/// @brief Returns mutable beam axial-stress rows.
std::vector<StressS11Row>& StressResults() noexcept;
/// @brief Returns beam axial-stress rows.
const std::vector<StressS11Row>& StressResults() const noexcept;
/// @brief Validates and atomically replaces all shell recovery evidence.
/// @param expected_element_order Unique shell indices in stable order.
/// @param candidate Complete shell rows, energy, and equilibrium evidence.
/// @return Success only after the complete candidate is validated and
/// committed; failure preserves the prior shell state.
Status CommitShellResults(
const std::vector<EntityIndex>& expected_element_order,
ShellStateCandidate candidate);
/// @brief Returns shell rows in stable element and location order.
const std::vector<ShellResultRow>& ShellResults() const noexcept;
/// @brief Returns physical shell strain energy without drilling energy.
double PhysicalStrainEnergy() const noexcept;
/// @brief Returns global force and moment equilibrium components.
const std::array<double, 6>& Equilibrium() const noexcept;
/// @brief Returns normalized shell verification metrics.
const std::array<double, 3>& VerificationMetrics() const noexcept;
private:
/// @brief Allocates state storage for one stable full-DOF dimension.
AnalysisState(std::size_t full_dof_count, StepFrameIdentity identity);
StepFrameIdentity identity_;
Vector displacement_;
Vector external_force_;
Vector internal_force_;
Vector residual_;
// Reactions retain full-index space so free residual components remain
// visible.
Vector reaction_;
// Recovery appends rows in stable element/location order.
std::vector<EndpointResultRow> endpoint_results_;
std::vector<GaussResultRow> gauss_results_;
std::vector<StressS11Row> stress_results_;
// Shell recovery is replaced only through validated candidate commit.
std::vector<ShellResultRow> shell_results_;
double physical_strain_energy_{0.0};
std::array<double, 6> equilibrium_{};
std::array<double, 3> verification_metrics_{};
};
} // namespace fesa
#endif // FESA_ANALYSIS_ANALYSIS_STATE_H_
-66
View File
@@ -1,66 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/fem/dof_manager.hpp"
#include "fesa/math/vector.hpp"
#include "fesa/results/result_records.hpp"
#include <array>
#include <cstddef>
#include <vector>
namespace fesa {
// Owns only the mutable quantities required by the V0 linear-static frame.
class AnalysisState {
public:
static AnalysisState create(
const DofManager& dofs, StepFrameIdentity identity);
Vector& displacement() noexcept;
const Vector& displacement() const noexcept;
Vector& externalForce() noexcept;
const Vector& externalForce() const noexcept;
Vector& internalForce() noexcept;
const Vector& internalForce() const noexcept;
Vector& residual() noexcept;
const Vector& residual() const noexcept;
Vector& reaction() noexcept;
const Vector& reaction() const noexcept;
const StepFrameIdentity& identity() const noexcept;
std::vector<EndpointResultRow>& endpointResults() noexcept;
const std::vector<EndpointResultRow>& endpointResults() const noexcept;
std::vector<GaussResultRow>& gaussResults() noexcept;
const std::vector<GaussResultRow>& gaussResults() const noexcept;
std::vector<StressS11Row>& stressResults() noexcept;
const std::vector<StressS11Row>& stressResults() const noexcept;
Status commitShellResults(
const std::vector<EntityIndex>& expectedElementOrder,
ShellStateCandidate candidate);
const std::vector<ShellResultRow>& shellResults() const noexcept;
double physicalStrainEnergy() const noexcept;
const std::array<double, 6>& equilibrium() const noexcept;
const std::array<double, 3>& verificationMetrics() const noexcept;
private:
AnalysisState(std::size_t fullDofCount, StepFrameIdentity identity);
StepFrameIdentity identity_;
Vector displacement_;
Vector externalForce_;
Vector internalForce_;
Vector residual_;
// Reactions retain full-index space so free residual components remain visible.
Vector reaction_;
// Recovery appends rows in stable element/location order.
std::vector<EndpointResultRow> endpointResults_;
std::vector<GaussResultRow> gaussResults_;
std::vector<StressS11Row> stressResults_;
// Shell recovery is replaced only through validated candidate commit.
std::vector<ShellResultRow> shellResults_;
double physicalStrainEnergy_{0.0};
std::array<double, 6> equilibrium_{};
std::array<double, 3> verificationMetrics_{};
};
} // namespace fesa
@@ -0,0 +1,74 @@
#ifndef FESA_ANALYSIS_LINEAR_STATIC_ANALYSIS_H_
#define FESA_ANALYSIS_LINEAR_STATIC_ANALYSIS_H_
#include <memory>
#include <vector>
#include "fesa/analysis/analysis.h"
#include "fesa/analysis/analysis_model.h"
#include "fesa/analysis/analysis_state.h"
#include "fesa/constraints/essential_constraint_policy.h"
#include "fesa/elements/element.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/math/sparse_matrix.h"
#include "fesa/math/vector.h"
#include "fesa/model/domain.h"
namespace fesa {
class LinearSolver;
class ParallelFor;
class ResultsWriter;
/// @brief Orchestrates the single-step linear-static procedure.
/// @note Factorization, substitution, recovery, and writing remain separately
/// observable through injected backend boundaries.
class LinearStaticAnalysis final : public Analysis {
public:
/// @brief Creates a procedure using non-owned backend adapters.
/// @note All three adapters must outlive this analysis object.
LinearStaticAnalysis(const ParallelFor& parallel_for,
LinearSolver& linear_solver,
ResultsWriter& results_writer);
/// @brief Runs the approved eight-stage linear-static lifecycle.
/// @return The first stage failure or successful result finalization.
Status Run(const AnalysisRequest& request) override;
private:
/// @brief Initializes owned input and Domain state for a run candidate.
Status InitializeCandidate(const AnalysisRequest& request);
/// @brief Builds the non-owning active-model view.
Status BuildAnalysisModel();
/// @brief Creates runtime elements, stable DOFs, and the sparse pattern.
Status BuildDofMapAndSparsePattern();
/// @brief Assembles full stiffness and stable constraint partitions.
Status AssembleAndPartitionStiffness();
/// @brief Factorizes Kff before any load assembly.
Status FactorizeFreeSystem();
/// @brief Assembles loads and forms Ff-Kfc*dc without solving.
Status AssembleLoadsAndEffectiveRhs();
/// @brief Substitutes the retained factorization and reconstructs full d.
Status SubstituteAndReconstruct();
/// @brief Recovers a complete candidate before writing authoritative output.
Status RecoverAndWrite();
const ParallelFor& parallel_for_;
LinearSolver& linear_solver_;
ResultsWriter& results_writer_;
AnalysisRequest request_;
std::unique_ptr<Domain> domain_;
std::unique_ptr<AnalysisModel> model_;
std::vector<std::unique_ptr<Element>> elements_;
ElementView element_view_;
std::unique_ptr<DofManager> dofs_;
std::unique_ptr<AnalysisState> state_;
std::unique_ptr<SparseMatrix> full_stiffness_;
std::unique_ptr<PartitionedStiffness> partitioned_stiffness_;
std::unique_ptr<Vector> effective_rhs_;
std::vector<Diagnostic> diagnostics_;
};
} // namespace fesa
#endif // FESA_ANALYSIS_LINEAR_STATIC_ANALYSIS_H_
@@ -1,78 +0,0 @@
#pragma once
#include "fesa/analysis/analysis_model.hpp"
#include "fesa/analysis/analysis_state.hpp"
#include "fesa/constraints/essential_constraints.hpp"
#include "fesa/core/status.hpp"
#include "fesa/fem/dof_manager.hpp"
#include "fesa/math/sparse_matrix.hpp"
#include "fesa/math/vector.hpp"
#include "fesa/model/domain.hpp"
#include <filesystem>
#include <memory>
#include <vector>
namespace fesa {
class LinearSolver;
class ParallelFor;
class ResultsWriter;
struct AnalysisRequest {
std::filesystem::path inputPath;
std::filesystem::path outputPath;
};
// Fixes the public V0 lifecycle while leaving each analysis procedure to
// implement its approved stages.
class Analysis {
public:
virtual ~Analysis() = default;
Status run(const AnalysisRequest& request);
protected:
virtual Status initialize(const AnalysisRequest& request) = 0;
virtual Status buildAnalysisModel() = 0;
virtual Status buildDofMapAndSparsePattern() = 0;
virtual Status assembleAndPartitionStiffness() = 0;
virtual Status factorize() = 0;
virtual Status assembleLoadsAndEffectiveRhs() = 0;
virtual Status substituteAndReconstruct() = 0;
virtual Status recoverAndWriteResults() = 0;
};
// Orchestrates the single-step B33 procedure through injected backend
// boundaries so factorization and substitution remain independently visible.
class LinearStaticAnalysis final : public Analysis {
public:
LinearStaticAnalysis(const ParallelFor& parallelFor,
LinearSolver& linearSolver,
ResultsWriter& resultsWriter);
protected:
Status initialize(const AnalysisRequest& request) override;
Status buildAnalysisModel() override;
Status buildDofMapAndSparsePattern() override;
Status assembleAndPartitionStiffness() override;
Status factorize() override;
Status assembleLoadsAndEffectiveRhs() override;
Status substituteAndReconstruct() override;
Status recoverAndWriteResults() override;
private:
const ParallelFor& parallelFor_;
LinearSolver& linearSolver_;
ResultsWriter& resultsWriter_;
AnalysisRequest request_;
std::unique_ptr<Domain> domain_;
std::unique_ptr<AnalysisModel> model_;
std::unique_ptr<DofManager> dofs_;
std::unique_ptr<AnalysisState> state_;
std::unique_ptr<SparseMatrix> fullStiffness_;
std::unique_ptr<PartitionedStiffness> partitionedStiffness_;
std::unique_ptr<Vector> effectiveRhs_;
std::vector<Diagnostic> diagnostics_;
};
} // namespace fesa
+21
View File
@@ -0,0 +1,21 @@
#ifndef FESA_APP_FESA_APPLICATION_H_
#define FESA_APP_FESA_APPLICATION_H_
#include <string>
#include <vector>
namespace fesa {
/// @brief Owns the argv-independent CLI contract and stable process exit codes.
class FesaApplication {
public:
/// @brief Runs one solver invocation from operands and options after argv[0].
/// @param arguments Input path and optional `--output` pair.
/// @return The stable CLI exit code for usage, input, model, solver, or
/// output status.
int Run(const std::vector<std::string>& arguments);
};
} // namespace fesa
#endif // FESA_APP_FESA_APPLICATION_H_
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include <string>
#include <vector>
namespace fesa {
// Owns the argv-independent command-line contract and stable process codes.
class FesaApplication {
public:
int run(const std::vector<std::string>& arguments);
};
} // namespace fesa
+38
View File
@@ -0,0 +1,38 @@
#ifndef FESA_ASSEMBLY_LOAD_ASSEMBLER_H_
#define FESA_ASSEMBLY_LOAD_ASSEMBLER_H_
#include "fesa/analysis/analysis_model.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/loads/load.h"
#include "fesa/math/sparse_matrix.h"
#include "fesa/math/vector.h"
namespace fesa {
/// @brief Assembles semantic nodal loads in stable source order.
class LoadAssembler {
public:
/// @brief Accumulates all active CLOAD rows in full-DOF space.
/// @return A finite full load vector or a structured model failure.
static Result<Vector> AssembleFullNodalLoad(const AnalysisModel& model,
const DofManager& dofs);
/// @brief Accumulates explicitly supplied loads in their view order.
/// @param loads Non-owning loads whose contribution source orders must match
/// their view positions.
/// @return A candidate committed only after all contributions validate.
static Result<Vector> AssembleFullNodalLoad(const AnalysisModel& model,
const DofManager& dofs,
const LoadView& loads);
/// @brief Forms Ff-Kfc*dc in stable free/constrained order.
/// @note This operation neither factorizes nor invokes a solver.
static Result<Vector> EffectiveFreeRhs(const Vector& full_load,
const SparseMatrix& kfc,
const Vector& prescribed_values,
const DofManager& dofs);
};
} // namespace fesa
#endif // FESA_ASSEMBLY_LOAD_ASSEMBLER_H_
-24
View File
@@ -1,24 +0,0 @@
#pragma once
#include "fesa/analysis/analysis_model.hpp"
#include "fesa/fem/dof_manager.hpp"
#include "fesa/math/sparse_matrix.hpp"
#include "fesa/math/vector.hpp"
namespace fesa {
// Assembles only semantic nodal CLOAD records and forms the eliminated free
// right-hand side; stiffness factorization remains an analysis responsibility.
class LoadAssembler {
public:
static Result<Vector> assembleFullNodalLoad(
const AnalysisModel& model,
const DofManager& dofs);
static Result<Vector> effectiveFreeRhs(
const Vector& fullLoad,
const SparseMatrix& kfc,
const Vector& prescribedValues,
const DofManager& dofs);
};
} // namespace fesa
+39
View File
@@ -0,0 +1,39 @@
#ifndef FESA_ASSEMBLY_PARALLEL_FOR_H_
#define FESA_ASSEMBLY_PARALLEL_FOR_H_
#include <cstddef>
#include <functional>
namespace fesa {
/// @brief Executes independent index-addressed work behind a backend boundary.
/// @note Callers own output storage and each invocation may write only its
/// index-owned slot.
class ParallelFor {
public:
virtual ~ParallelFor() = default;
/// @brief Invokes body once for every index in [0, count).
virtual void Execute(std::size_t count,
const std::function<void(std::size_t)>& body) const = 0;
};
/// @brief Executes index-addressed work serially.
class SerialParallelFor final : public ParallelFor {
public:
/// @copydoc ParallelFor::Execute
void Execute(std::size_t count,
const std::function<void(std::size_t)>& body) const override;
};
/// @brief Executes index-addressed work through oneTBB.
class TbbParallelFor final : public ParallelFor {
public:
/// @copydoc ParallelFor::Execute
void Execute(std::size_t count,
const std::function<void(std::size_t)>& body) const override;
};
} // namespace fesa
#endif // FESA_ASSEMBLY_PARALLEL_FOR_H_
-32
View File
@@ -1,32 +0,0 @@
#pragma once
#include <cstddef>
#include <functional>
namespace fesa {
// Executes independent index-addressed work without exposing the backend.
// Callers own output storage and must confine each invocation to its index.
class ParallelFor {
public:
virtual ~ParallelFor() = default;
virtual void execute(
std::size_t count,
const std::function<void(std::size_t)>& body) const = 0;
};
class SerialParallelFor final : public ParallelFor {
public:
void execute(
std::size_t count,
const std::function<void(std::size_t)>& body) const override;
};
class TbbParallelFor final : public ParallelFor {
public:
void execute(
std::size_t count,
const std::function<void(std::size_t)>& body) const override;
};
} // namespace fesa
+40
View File
@@ -0,0 +1,40 @@
#ifndef FESA_ASSEMBLY_SPARSE_ASSEMBLER_H_
#define FESA_ASSEMBLY_SPARSE_ASSEMBLER_H_
#include "fesa/core/status.h"
#include "fesa/elements/element.h"
#include "fesa/math/sparse_matrix.h"
namespace fesa {
class AnalysisModel;
class DofManager;
class ParallelFor;
/// @brief Owns deterministic element-contribution reduction into global CSR.
class SparseAssembler {
public:
/// @brief Assembles runtime element stiffness into validated full-DOF CSR.
/// @param elements Non-owning elements in stable active source order.
/// @param dofs Owner of the matching scatter and structural pattern.
/// @param parallel_for Backend for index-owned element-local computation.
/// @return A validated matrix or a structured model failure.
/// @note Workers write only their element-owned COO buffers; flattening and
/// duplicate reduction retain fixed element and local-entry order.
static Result<SparseMatrix> Assemble(const ElementView& elements,
const DofManager& dofs,
const ParallelFor& parallel_for);
/// @brief Assembles stiffness in stable source-element and local-entry order.
/// @return A validated full-DOF CSR matrix or structured model failure.
/// @note Parallel workers produce index-owned local buffers; serial reduction
/// remains the sole global CSR writer. This compatibility facade creates
/// runtime candidates until LinearStaticAnalysis owns them directly.
static Result<SparseMatrix> AssembleStiffness(
const AnalysisModel& model, const DofManager& dofs,
const ParallelFor& parallel_for);
};
} // namespace fesa
#endif // FESA_ASSEMBLY_SPARSE_ASSEMBLER_H_
@@ -1,20 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/math/sparse_matrix.hpp"
namespace fesa {
class AnalysisModel;
class DofManager;
class ParallelFor;
class SparseAssembler {
public:
static Result<SparseMatrix> assembleStiffness(
const AnalysisModel& model,
const DofManager& dofs,
const ParallelFor& parallelFor);
};
} // namespace fesa
+13
View File
@@ -0,0 +1,13 @@
#ifndef FESA_BUILD_INFO_H_
#define FESA_BUILD_INFO_H_
#include <string_view>
namespace fesa {
/// @brief Returns the stable solver version written to result metadata.
std::string_view SolverVersion() noexcept;
} // namespace fesa
#endif // FESA_BUILD_INFO_H_
-10
View File
@@ -1,10 +0,0 @@
#pragma once
#include <string_view>
namespace fesa {
// Returns the stable solver version written to externally visible result metadata.
std::string_view solverVersion() noexcept;
} // namespace fesa
@@ -0,0 +1,71 @@
#ifndef FESA_CONSTRAINTS_BOUNDARY_CONDITION_H_
#define FESA_CONSTRAINTS_BOUNDARY_CONDITION_H_
#include <cstddef>
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "fesa/core/diagnostic.h"
#include "fesa/core/status.h"
namespace fesa {
class DofManager;
class Domain;
class SourceTargetResolver;
/// @brief Describes one prescribed value in stable full-DOF order.
struct ConstraintDefinition {
std::size_t source_order;
std::size_t full_dof_index;
double prescribed_value;
};
/// @brief Provides immutable semantic and equation context to a boundary.
/// @note Every referenced object must outlive a constraint request.
struct BoundaryConditionContext {
const Domain& domain;
const DofManager& dof_manager;
const SourceTargetResolver& target_resolver;
};
/// @brief Produces ordered constraint definitions without equation mutation.
class BoundaryCondition {
public:
virtual ~BoundaryCondition() = default;
/// @brief Resolves finite full-DOF definitions in stable target order.
/// @param context Non-owning semantic and equation context for this call.
/// @return Ordered definitions or a structured model failure.
virtual Result<std::vector<ConstraintDefinition>> ResolveConstraints(
const BoundaryConditionContext& context) const = 0;
/// @brief Returns the source location used by policy diagnostics.
const SourceLocation& Location() const noexcept { return location_; }
/// @brief Returns the source target identity used by policy diagnostics.
const std::string& TargetIdentity() const noexcept {
return target_identity_;
}
protected:
/// @brief Creates a boundary with optional shared diagnostic provenance.
BoundaryCondition(std::string target_identity = {},
SourceLocation location = {})
: target_identity_{std::move(target_identity)},
location_{std::move(location)} {}
private:
std::string target_identity_;
SourceLocation location_;
};
/// @brief Holds non-owning boundaries in an explicitly supplied source order.
using BoundaryConditionView =
std::vector<std::reference_wrapper<const BoundaryCondition>>;
} // namespace fesa
#endif // FESA_CONSTRAINTS_BOUNDARY_CONDITION_H_
@@ -0,0 +1,43 @@
#ifndef FESA_CONSTRAINTS_ESSENTIAL_CONSTRAINT_POLICY_H_
#define FESA_CONSTRAINTS_ESSENTIAL_CONSTRAINT_POLICY_H_
#include "fesa/core/status.h"
#include "fesa/math/sparse_matrix.h"
#include "fesa/math/vector.h"
namespace fesa {
class DofManager;
/// @brief Stores full-stiffness blocks in stable free/constrained order.
struct PartitionedStiffness {
SparseMatrix k_ff;
SparseMatrix k_fc;
SparseMatrix k_cf;
SparseMatrix k_cc;
};
/// @brief Applies stable prescribed-displacement elimination.
class EssentialConstraintPolicy {
public:
/// @brief Partitions full stiffness into Kff, Kfc, Kcf, and Kcc.
Result<PartitionedStiffness> Partition(const SparseMatrix& full_stiffness,
const DofManager& dof_manager) const;
/// @brief Gathers a full vector in stable free-equation order.
Vector GatherFree(const Vector& full_values,
const DofManager& dof_manager) const;
/// @brief Gathers a full vector in stable constrained-DOF order.
Vector GatherConstrained(const Vector& full_values,
const DofManager& dof_manager) const;
/// @brief Reconstructs full d from stable df and exact prescribed dc.
Vector ReconstructFull(const Vector& free_values,
const Vector& constrained_values,
const DofManager& dof_manager) const;
};
} // namespace fesa
#endif // FESA_CONSTRAINTS_ESSENTIAL_CONSTRAINT_POLICY_H_
@@ -1,35 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/math/sparse_matrix.hpp"
#include "fesa/math/vector.hpp"
namespace fesa {
class DofManager;
struct PartitionedStiffness {
SparseMatrix kff;
SparseMatrix kfc;
SparseMatrix kcf;
SparseMatrix kcc;
};
// Applies the DofManager's stable elimination order without owning equation
// numbering, load assembly, or a solver policy.
class EssentialConstraints {
public:
static Result<PartitionedStiffness> partition(
const SparseMatrix& full,
const DofManager& dofs);
static Vector gatherFree(const Vector& full, const DofManager& dofs);
static Vector gatherConstrained(
const Vector& full,
const DofManager& dofs);
static Vector reconstructFull(
const Vector& freeValues,
const Vector& constrainedValues,
const DofManager& dofs);
};
} // namespace fesa
@@ -0,0 +1,48 @@
#ifndef FESA_CONSTRAINTS_PRESCRIBED_DISPLACEMENT_H_
#define FESA_CONSTRAINTS_PRESCRIBED_DISPLACEMENT_H_
#include <cstddef>
#include "fesa/constraints/boundary_condition.h"
#include "fesa/core/diagnostic.h"
#include "fesa/elements/element.h"
#include "fesa/model/source_target_resolver.h"
namespace fesa {
/// @brief Emits one prescribed nodal displacement component for a target.
class PrescribedDisplacementBoundaryCondition final : public BoundaryCondition {
public:
/// @brief Creates one prescribed component in stable source order.
PrescribedDisplacementBoundaryCondition(SourceTargetQuery target,
DofComponent component,
double prescribed_value,
std::size_t source_order,
SourceLocation location = {});
/// @brief Resolves target-major full-DOF constraint definitions.
Result<std::vector<ConstraintDefinition>> ResolveConstraints(
const BoundaryConditionContext& context) const override;
/// @brief Returns the immutable source target query.
const SourceTargetQuery& Target() const noexcept;
/// @brief Returns the prescribed global DOF component.
DofComponent Component() const noexcept;
/// @brief Returns the exact prescribed displacement value.
double PrescribedValue() const noexcept;
/// @brief Returns the stable boundary-definition order.
std::size_t SourceOrder() const noexcept;
private:
SourceTargetQuery target_;
DofComponent component_;
double prescribed_value_;
std::size_t source_order_;
};
} // namespace fesa
#endif // FESA_CONSTRAINTS_PRESCRIBED_DISPLACEMENT_H_
+28
View File
@@ -0,0 +1,28 @@
#ifndef FESA_CORE_ASCII_H_
#define FESA_CORE_ASCII_H_
#include <cstdint>
#include <string_view>
#include "fesa/core/status.h"
namespace fesa {
/// @brief Converts one ASCII uppercase byte to lowercase.
/// @return The lowercase ASCII byte, or the input byte when it is not A-Z.
char AsciiLower(char value) noexcept;
/// @brief Compares two byte strings with ASCII-only case folding.
/// @return True when the strings have equal length and equal ASCII-folded
/// bytes.
bool AsciiCaseInsensitiveEquals(std::string_view lhs,
std::string_view rhs) noexcept;
/// @brief Parses a complete positive base-10 source label.
/// @return The positive label or an input failure for malformed, nonpositive,
/// or out-of-range text.
Result<std::int64_t> ParsePositiveSourceLabel(std::string_view text);
} // namespace fesa
#endif // FESA_CORE_ASCII_H_
+31
View File
@@ -0,0 +1,31 @@
#ifndef FESA_CORE_DIAGNOSTIC_H_
#define FESA_CORE_DIAGNOSTIC_H_
#include <string>
#include <vector>
#include "fesa/core/source_identity.h"
namespace fesa {
/// @brief Distinguishes recoverable warnings from operation-stopping errors.
enum class Severity { kWarning, kError };
/// @brief Carries a structured, backend-independent diagnostic record.
struct Diagnostic {
Severity severity;
std::string code;
SourceLocation location;
std::string keyword;
std::string entity_identity;
std::string message;
};
/// @brief Orders diagnostics by their externally visible source tuple.
/// @param diagnostics Records to reorder in place.
/// @note Records with identical keys retain their discovery order.
void SortDiagnostics(std::vector<Diagnostic>& diagnostics);
} // namespace fesa
#endif // FESA_CORE_DIAGNOSTIC_H_
-30
View File
@@ -1,30 +0,0 @@
#pragma once
#include "fesa/core/source_identity.hpp"
#include <string>
#include <vector>
namespace fesa {
// Distinguishes recoverable warnings from errors that stop the current operation.
enum class Severity {
warning,
error
};
// Carries a structured, backend-independent diagnostic record.
struct Diagnostic {
Severity severity;
std::string code;
SourceLocation location;
std::string keyword;
std::string entityIdentity;
std::string message;
};
// Orders diagnostics by their externally visible source tuple while retaining
// discovery order for records with identical keys.
void sortDiagnostics(std::vector<Diagnostic>& diagnostics);
} // namespace fesa
+29
View File
@@ -0,0 +1,29 @@
#ifndef FESA_CORE_SOURCE_IDENTITY_H_
#define FESA_CORE_SOURCE_IDENTITY_H_
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <string>
namespace fesa {
/// @brief Identifies a semantic entity by its stable collection position.
using EntityIndex = std::uint32_t;
/// @brief Identifies the input location that produced an item or diagnostic.
struct SourceLocation {
std::filesystem::path file;
std::size_t line;
};
/// @brief Preserves semantic and raw-text forms of a source entity identity.
struct SourceEntityId {
std::string instance_name;
std::int64_t source_label;
std::string source_label_text;
};
} // namespace fesa
#endif // FESA_CORE_SOURCE_IDENTITY_H_
-23
View File
@@ -1,23 +0,0 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <string>
namespace fesa {
// Identifies the physical input location that produced a model item or diagnostic.
struct SourceLocation {
std::filesystem::path file;
std::size_t line;
};
// Preserves both semantic and raw-text forms of an input entity identity.
struct SourceEntityId {
std::string instanceName;
std::int64_t sourceLabel;
std::string sourceLabelText;
};
} // namespace fesa
+114
View File
@@ -0,0 +1,114 @@
#ifndef FESA_CORE_STATUS_H_
#define FESA_CORE_STATUS_H_
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
#include "fesa/core/diagnostic.h"
namespace fesa {
/// @brief Maps failures to the stable command-line exit-code categories.
enum class FailureCategory { kInput, kModel, kSolver, kOutput };
/// @brief Transports success or structured failure diagnostics.
class Status {
public:
/// @brief Creates a successful status.
/// @return A status with no failure category or diagnostics.
static Status Ok();
/// @brief Creates an uncategorized failed status.
/// @param diagnostics Structured diagnostics owned by the returned status.
/// @return A failed status with diagnostics in deterministic source order.
static Status Failure(std::vector<Diagnostic> diagnostics);
/// @brief Creates a categorized failed status.
/// @param category Stable external failure category.
/// @param diagnostics Structured diagnostics owned by the returned status.
/// @return A failed status with diagnostics in deterministic source order.
static Status Failure(FailureCategory category,
std::vector<Diagnostic> diagnostics);
/// @brief Reports whether the operation succeeded.
bool IsOk() const noexcept;
/// @brief Returns the optional stable failure category.
std::optional<FailureCategory> Category() const noexcept;
/// @brief Returns the deterministically ordered diagnostic records.
const std::vector<Diagnostic>& Diagnostics() const noexcept;
private:
/// @brief Constructs a status from its validated invariant fields.
Status(bool is_ok, std::optional<FailureCategory> category,
std::vector<Diagnostic> diagnostics);
bool is_ok_;
std::optional<FailureCategory> category_;
std::vector<Diagnostic> diagnostics_;
};
/// @brief Owns exactly one successful value or one failed Status.
template <class T>
class Result {
public:
/// @brief Creates a successful result that owns the supplied value.
static Result Success(T value) {
return Result{SuccessTag{}, std::move(value)};
}
/// @brief Creates a failed result that owns a failed status.
/// @throws std::invalid_argument if status represents success.
static Result Failure(Status status) {
if (status.IsOk()) {
throw std::invalid_argument{"A failed Result requires a failed Status."};
}
return Result{FailureTag{}, std::move(status)};
}
/// @brief Reports whether this result owns a successful value.
bool HasValue() const noexcept { return value_.has_value(); }
/// @brief Returns the owned successful value.
/// @throws std::logic_error if this result represents failure.
T& Value() {
if (!value_) {
throw std::logic_error{"Result has no value."};
}
return *value_;
}
/// @brief Returns the owned successful value.
/// @throws std::logic_error if this result represents failure.
const T& Value() const {
if (!value_) {
throw std::logic_error{"Result has no value."};
}
return *value_;
}
/// @brief Returns the success or failure status.
const Status& GetStatus() const noexcept { return status_; }
private:
struct SuccessTag {};
struct FailureTag {};
/// @brief Constructs the successful value alternative.
Result(SuccessTag, T value)
: value_{std::move(value)}, status_{Status::Ok()} {}
/// @brief Constructs the failed status alternative.
Result(FailureTag, Status status)
: value_{std::nullopt}, status_{std::move(status)} {}
std::optional<T> value_;
Status status_;
};
} // namespace fesa
#endif // FESA_CORE_STATUS_H_
-94
View File
@@ -1,94 +0,0 @@
#pragma once
#include "fesa/core/diagnostic.hpp"
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
namespace fesa {
// Maps a failure to the stable command-line exit-code classes defined by V0.
enum class FailureCategory {
input,
model,
solver,
output
};
// Transports success or structured diagnostics without exposing backend errors.
class Status {
public:
static Status ok();
static Status failure(std::vector<Diagnostic> diagnostics);
static Status failure(
FailureCategory category, std::vector<Diagnostic> diagnostics);
bool isOk() const noexcept;
std::optional<FailureCategory> failureCategory() const noexcept;
const std::vector<Diagnostic>& diagnostics() const noexcept;
private:
Status(
bool isOk,
std::optional<FailureCategory> category,
std::vector<Diagnostic> diagnostics);
bool isOk_;
std::optional<FailureCategory> category_;
std::vector<Diagnostic> diagnostics_;
};
// Owns exactly one successful value or one failed Status.
template<class T>
class Result {
public:
static Result success(T value) {
return Result{SuccessTag{}, std::move(value)};
}
static Result failure(Status status) {
if (status.isOk()) {
throw std::invalid_argument{"A failed Result requires a failed Status."};
}
return Result{FailureTag{}, std::move(status)};
}
bool hasValue() const noexcept {
return value_.has_value();
}
T& value() {
if (!value_) {
throw std::logic_error{"Result has no value."};
}
return *value_;
}
const T& value() const {
if (!value_) {
throw std::logic_error{"Result has no value."};
}
return *value_;
}
const Status& status() const noexcept {
return status_;
}
private:
struct SuccessTag {};
struct FailureTag {};
Result(SuccessTag, T value)
: value_{std::move(value)}, status_{Status::ok()} {}
Result(FailureTag, Status status)
: value_{std::nullopt}, status_{std::move(status)} {}
std::optional<T> value_;
Status status_;
};
} // namespace fesa
+85
View File
@@ -0,0 +1,85 @@
#ifndef FESA_ELEMENTS_ELEMENT_H_
#define FESA_ELEMENTS_ELEMENT_H_
#include <cstdint>
#include <functional>
#include <variant>
#include <vector>
#include "fesa/core/source_identity.h"
#include "fesa/core/status.h"
#include "fesa/math/matrix.h"
#include "fesa/math/vector.h"
#include "fesa/results/result_records.h"
namespace fesa {
/// @brief Identifies one component in the stable six-DOF node layout.
enum class DofComponent : std::uint8_t {
kUx,
kUy,
kUz,
kUrx,
kUry,
kUrz,
};
/// @brief Describes one runtime element's stable node and component order.
struct ElementDofLayout {
SourceEntityId source_id;
std::vector<EntityIndex> node_indices;
std::vector<DofComponent> components_per_node;
};
/// @brief Carries one element-local stiffness in its declared DOF order.
struct ElementStiffnessContribution {
ElementDofLayout layout;
Matrix values;
};
/// @brief Keeps beam recovery locations in their distinct row collections.
struct BeamElementResultRows {
std::vector<EndpointResultRow> endpoint_rows;
std::vector<GaussResultRow> gauss_rows;
std::vector<StressS11Row> stress_rows;
};
/// @brief Keeps physical shell rows separate from numerical drilling data.
struct ShellElementResultRows {
std::vector<ShellResultRow> rows;
double physical_strain_energy{0.0};
};
/// @brief Selects the physical recovery shape of one runtime element.
using ElementResultPayload =
std::variant<BeamElementResultRows, ShellElementResultRows>;
/// @brief Carries stable source identity with one typed recovery payload.
struct ElementResultBundle {
SourceEntityId source_id;
ElementResultPayload payload;
};
/// @brief Defines the numerical contract consumed by solver pipeline owners.
class Element {
public:
virtual ~Element() = default;
/// @brief Returns the stable element-local DOF ordering.
virtual const ElementDofLayout& DofLayout() const noexcept = 0;
/// @brief Computes the finite stiffness in the declared local ordering.
virtual Result<ElementStiffnessContribution> ComputeStiffness() const = 0;
/// @brief Recovers typed physical rows from element-local global DOFs.
virtual Result<ElementResultBundle> Recover(
const Vector& element_displacement) const = 0;
};
/// @brief Provides non-owning runtime elements in stable owner order.
/// @note Every referenced element must outlive this view.
using ElementView = std::vector<std::reference_wrapper<const Element>>;
} // namespace fesa
#endif // FESA_ELEMENTS_ELEMENT_H_
@@ -0,0 +1,42 @@
#ifndef FESA_ELEMENTS_ELEMENT_DEFINITION_H_
#define FESA_ELEMENTS_ELEMENT_DEFINITION_H_
#include <string_view>
#include <vector>
#include "fesa/core/source_identity.h"
namespace fesa {
/// @brief Identifies the supported semantic element-definition kinds.
enum class ElementDefinitionKind { kEulerBeam3D, kMitc4Shell };
/// @brief Provides immutable source identity and topology for one element.
/// @note Numerical stiffness, recovery, and equation ids are intentionally
/// excluded from this semantic interface.
class ElementDefinition {
public:
virtual ~ElementDefinition() = default;
/// @brief Returns the concrete semantic definition kind.
virtual ElementDefinitionKind Kind() const noexcept = 0;
/// @brief Returns the stable external source identity.
virtual const SourceEntityId& SourceId() const noexcept = 0;
/// @brief Returns the preserved source element type such as B33 or S4R.
virtual std::string_view SourceElementType() const noexcept = 0;
/// @brief Returns stable Domain node collection positions.
virtual const std::vector<EntityIndex>& NodeIndices() const noexcept = 0;
/// @brief Returns the stable Domain material collection position.
virtual EntityIndex MaterialIndex() const noexcept = 0;
/// @brief Returns the stable Domain property collection position.
virtual EntityIndex PropertyIndex() const noexcept = 0;
};
} // namespace fesa
#endif // FESA_ELEMENTS_ELEMENT_DEFINITION_H_
+36
View File
@@ -0,0 +1,36 @@
#ifndef FESA_ELEMENTS_ELEMENT_FACTORY_H_
#define FESA_ELEMENTS_ELEMENT_FACTORY_H_
#include <memory>
#include "fesa/core/status.h"
#include "fesa/elements/element.h"
namespace fesa {
class Domain;
class ElementDefinition;
/// @brief Creates checked numerical elements from Domain-owned definitions.
class ElementFactory {
public:
/// @brief Creates the supported runtime kernel for one semantic definition.
/// @param definition Definition owned by domain for the returned operation.
/// @param domain Immutable owner of referenced nodes, property, and material.
/// @return A non-null element or a structured model failure.
Result<std::unique_ptr<Element>> Create(const ElementDefinition& definition,
const Domain& domain) const;
private:
/// @brief Creates one checked B33 runtime candidate.
Result<std::unique_ptr<Element>> CreateBeam(
const ElementDefinition& definition, const Domain& domain) const;
/// @brief Creates one checked MITC4 runtime candidate.
Result<std::unique_ptr<Element>> CreateShell(
const ElementDefinition& definition, const Domain& domain) const;
};
} // namespace fesa
#endif // FESA_ELEMENTS_ELEMENT_FACTORY_H_
+154
View File
@@ -0,0 +1,154 @@
#ifndef FESA_ELEMENTS_EULER_BEAM_3D_H_
#define FESA_ELEMENTS_EULER_BEAM_3D_H_
#include <array>
#include <cstddef>
#include <string>
#include <string_view>
#include <vector>
#include "fesa/core/status.h"
#include "fesa/elements/element.h"
#include "fesa/elements/element_definition.h"
#include "fesa/materials/isotropic_linear_elastic_material.h"
#include "fesa/math/matrix.h"
#include "fesa/math/vector.h"
#include "fesa/properties/general_beam_section.h"
namespace fesa {
class Domain;
struct Node;
/// @brief Defines one two-node B33 semantic element.
class EulerBeam3DDefinition final : public ElementDefinition {
public:
/// @brief Constructs a parser-validated semantic definition.
EulerBeam3DDefinition(SourceEntityId source_id,
std::array<EntityIndex, 2> node_indices,
EntityIndex material_index, EntityIndex section_index,
SourceLocation location);
ElementDefinitionKind Kind() const noexcept override;
const SourceEntityId& SourceId() const noexcept override;
std::string_view SourceElementType() const noexcept override;
const std::vector<EntityIndex>& NodeIndices() const noexcept override;
EntityIndex MaterialIndex() const noexcept override;
EntityIndex PropertyIndex() const noexcept override;
SourceEntityId source_id;
std::array<EntityIndex, 2> node_indices;
EntityIndex material_index;
EntityIndex section_index;
SourceLocation location;
private:
friend class Domain;
/// @brief Synchronizes the base view after parser-candidate construction.
void SynchronizeNodeIndices();
std::vector<EntityIndex> node_indices_view_;
};
/// @brief Stores constant line-load components in the beam local frame.
struct ConstantLocalLineLoad {
double px;
double py;
double pz;
double mx;
};
/// @brief Stores one axial stress at a Gauss and section-point identity.
struct BeamStressPoint {
int gauss_point;
std::size_t section_point;
double x1;
double x2;
double s11;
std::string source;
};
/// @brief Stores distinct beam end-action, section, Gauss, and stress results.
struct BeamRecovery {
std::array<std::array<double, 6>, 2> equilibrium_end_actions;
std::array<std::array<double, 4>, 2> endpoint_section_resultants;
std::array<std::array<double, 4>, 2> gauss_generalized_strains;
std::array<std::array<double, 4>, 2> gauss_generalized_resultants;
std::vector<BeamStressPoint> stress_points;
};
/// @brief Implements the approved two-node prismatic B33 Euler beam kernel.
/// @note Equation numbering and semantic element identity remain external.
class EulerBeam3D final : public Element {
public:
/// @brief Creates a validated beam kernel and right-handed local frame.
/// @param first_node First source node in the element connectivity.
/// @param second_node Second source node in the element connectivity.
/// @param section Supported general beam section and local first axis.
/// @param material Supported isotropic elastic material.
/// @return A validated beam or a structured model failure.
static Result<EulerBeam3D> Create(const Node& first_node,
const Node& second_node,
const GeneralBeamSection& section,
const LinearElasticMaterial& material);
/// @brief Returns the factory-bound B33 DOF order.
const ElementDofLayout& DofLayout() const noexcept override;
/// @brief Computes global B33 stiffness through the runtime contract.
Result<ElementStiffnessContribution> ComputeStiffness() const override;
/// @brief Recovers typed B33 rows through the runtime contract.
Result<ElementResultBundle> Recover(
const Vector& element_displacement) const override;
/// @brief Computes the 12-by-12 stiffness in local DOF order.
/// @note Uses the approved two-point Gauss operation order.
Matrix LocalStiffness() const;
/// @brief Computes the stiffness in stable global element DOF order.
Matrix GlobalStiffness() const;
/// @brief Computes the formulation-only constant local line-load vector.
/// @warning This kernel does not expose distributed loads through parser
/// input.
Vector LocalEquivalentLoad(const ConstantLocalLineLoad& load) const;
/// @brief Recovers signed physical quantities at their distinct locations.
/// @param global_element_displacement Twelve global element DOF values.
/// @return Beam recovery rows in deterministic location order.
BeamRecovery RecoverBeam(const Vector& global_element_displacement) const;
private:
friend class ElementFactory;
/// @brief Binds Domain identity after the numerical candidate is valid.
void BindRuntime(ElementDofLayout layout, EntityIndex element_index,
std::vector<SourceEntityId> node_source_ids,
SourceLocation location);
/// @brief Stores already validated geometry, material, and section state.
EulerBeam3D(double length, double youngs_modulus, double shear_modulus,
double area, double iy, double iz, double torsional_constant,
std::array<double, 9> rotation,
std::vector<std::array<double, 2>> section_points);
double length_;
double youngs_modulus_;
double shear_modulus_;
double area_;
double iy_;
double iz_;
double torsional_constant_;
std::array<double, 9> rotation_;
std::vector<std::array<double, 2>> section_points_;
ElementDofLayout dof_layout_;
EntityIndex element_index_{0U};
std::vector<SourceEntityId> node_source_ids_;
SourceLocation runtime_location_;
};
} // namespace fesa
#endif // FESA_ELEMENTS_EULER_BEAM_3D_H_
-74
View File
@@ -1,74 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/math/matrix.hpp"
#include "fesa/math/vector.hpp"
#include "fesa/model/model_types.hpp"
#include <array>
#include <cstddef>
#include <string>
#include <vector>
namespace fesa {
struct ConstantLocalLineLoad {
double px;
double py;
double pz;
double mx;
};
struct BeamStressPoint {
int gaussPoint;
std::size_t sectionPoint;
double x1;
double x2;
double s11;
std::string source;
};
struct BeamRecovery {
std::array<std::array<double, 6>, 2> equilibriumEndActions;
std::array<std::array<double, 4>, 2> endpointSectionResultants;
std::array<std::array<double, 4>, 2> gaussGeneralizedStrains;
std::array<std::array<double, 4>, 2> gaussGeneralizedResultants;
std::vector<BeamStressPoint> stressPoints;
};
// Implements the approved two-node straight prismatic B33 Euler-Bernoulli
// kernel. Equation numbering and element identity remain outside this type.
class EulerBeam3D {
public:
static Result<EulerBeam3D> create(const Node& firstNode,
const Node& secondNode,
const GeneralBeamSection& section,
const LinearElasticMaterial& material);
Matrix localStiffness() const;
Matrix globalStiffness() const;
Vector localEquivalentLoad(const ConstantLocalLineLoad& load) const;
BeamRecovery recover(const Vector& globalElementDisplacement) const;
private:
EulerBeam3D(double length,
double youngsModulus,
double shearModulus,
double area,
double iy,
double iz,
double torsionalConstant,
std::array<double, 9> rotation,
std::vector<std::array<double, 2>> sectionPoints);
double length_;
double youngsModulus_;
double shearModulus_;
double area_;
double iy_;
double iz_;
double torsionalConstant_;
std::array<double, 9> rotation_;
std::vector<std::array<double, 2>> sectionPoints_;
};
} // namespace fesa
+248
View File
@@ -0,0 +1,248 @@
#ifndef FESA_ELEMENTS_MITC4_SHELL_H_
#define FESA_ELEMENTS_MITC4_SHELL_H_
#include <array>
#include <string>
#include <string_view>
#include <vector>
#include "fesa/core/status.h"
#include "fesa/elements/element.h"
#include "fesa/elements/element_definition.h"
#include "fesa/materials/isotropic_linear_elastic_material.h"
#include "fesa/math/matrix.h"
#include "fesa/math/vector.h"
#include "fesa/math/vector3.h"
#include "fesa/properties/shell_section.h"
namespace fesa {
class Domain;
struct Node;
/// @brief Preserves the source shell type independently of formulation.
enum class ShellSourceElementType { kS4, kS4r };
/// @brief Names the internal shell formulation selected by S4 and S4R.
inline constexpr std::string_view kMitc4InternalFormulation{"FESA-MITC4"};
/// @brief Defines one four-node S4/S4R semantic element.
class Mitc4ShellDefinition final : public ElementDefinition {
public:
/// @brief Constructs a parser-validated semantic definition.
Mitc4ShellDefinition(SourceEntityId source_id,
ShellSourceElementType source_type,
std::array<EntityIndex, 4> node_indices,
EntityIndex material_index, EntityIndex section_index,
SourceLocation location);
ElementDefinitionKind Kind() const noexcept override;
const SourceEntityId& SourceId() const noexcept override;
std::string_view SourceElementType() const noexcept override;
const std::vector<EntityIndex>& NodeIndices() const noexcept override;
EntityIndex MaterialIndex() const noexcept override;
EntityIndex PropertyIndex() const noexcept override;
SourceEntityId source_id;
ShellSourceElementType source_type;
std::array<EntityIndex, 4> node_indices;
EntityIndex material_index;
EntityIndex section_index;
SourceLocation location;
private:
friend class Domain;
/// @brief Rebinds a shell-local section index to the unified property view.
void SetPropertyIndex(EntityIndex property_index) noexcept;
/// @brief Synchronizes the base view after parser-candidate construction.
void SynchronizeNodeIndices();
EntityIndex property_index_;
std::vector<EntityIndex> node_indices_view_;
};
/// @brief Stores bilinear shape values and natural-coordinate derivatives.
struct Mitc4ShapeFunctions {
std::array<double, 4> values;
std::array<double, 4> xi_derivatives;
std::array<double, 4> eta_derivatives;
};
/// @brief Stores a right-handed local shell frame at one location.
struct Mitc4LocalFrame {
std::array<double, 3> e1;
std::array<double, 3> e2;
std::array<double, 3> e3;
};
/// @brief Stores canonical MITC4 covariant shear interpolation weights.
struct Mitc4TyingWeights {
std::array<double, 2> xi_zeta;
std::array<double, 2> eta_zeta;
};
/// @brief Stores one fixed 2-by-2-by-2 integration point and weight.
struct Mitc4QuadraturePoint {
std::array<double, 3> natural_coordinates;
double weight;
};
/// @brief Separates physical, drilling, and stabilized stiffness matrices.
struct Mitc4Stiffness {
Matrix physical_local20;
Matrix physical_global24;
Matrix drilling_global24;
Matrix stabilized_global24;
double drilling_stiffness;
};
/// @brief Stores physical shell recovery at one midsurface Gauss location.
struct Mitc4PhysicalRecoveryPoint {
std::array<double, 2> natural_coordinates;
Mitc4LocalFrame local_frame;
std::array<double, 8> generalized_strain;
std::array<double, 8> section_resultant;
std::array<std::array<double, 3>, 3> in_plane_stress;
};
/// @brief Stores physical-only recovery rows and strain energy.
struct Mitc4PhysicalRecovery {
std::array<Mitc4PhysicalRecoveryPoint, 4> points;
double strain_energy;
};
/// @brief Implements the approved small-rotation FESA-MITC4 shell kernel.
/// @note Physical and numerical drilling contributions remain separate.
class Mitc4Shell final : public Element {
public:
/// @brief Creates a validated shell kernel from four non-owning node
/// pointers.
/// @param nodes Node pointers valid for the duration of this call.
/// @param initial_directors Validated unit initial directors in node order.
/// @param section Centered constant-thickness shell section.
/// @param material Supported isotropic elastic material.
/// @return A validated shell or a structured model failure.
static Result<Mitc4Shell> Create(
std::array<const Node*, 4> nodes,
std::array<std::array<double, 3>, 4> initial_directors,
const ShellSection& section, const LinearElasticMaterial& material);
/// @brief Returns the factory-bound MITC4 DOF order.
const ElementDofLayout& DofLayout() const noexcept override;
/// @brief Computes stabilized MITC4 stiffness through the runtime contract.
Result<ElementStiffnessContribution> ComputeStiffness() const override;
/// @brief Recovers physical MITC4 rows through the runtime contract.
Result<ElementResultBundle> Recover(
const Vector& element_displacement) const override;
/// @brief Evaluates bilinear shape functions and derivatives.
static Mitc4ShapeFunctions ShapeFunctions(double xi, double eta) noexcept;
/// @brief Evaluates the canonical edge-midpoint tying weights.
static Mitc4TyingWeights TyingWeights(double xi, double eta) noexcept;
/// @brief Returns the fixed 2-by-2-by-2 quadrature inventory.
static const std::array<Mitc4QuadraturePoint, 8>& VolumeQuadrature() noexcept;
/// @brief Evaluates the right-handed local frame at a midsurface location.
[[nodiscard]] Mitc4LocalFrame LocalFrame(double xi, double eta) const;
/// @brief Returns the physical 24-to-20 transformation.
[[nodiscard]] Matrix PhysicalTransformation20() const;
/// @brief Returns the numerical drilling 24-to-4 transformation.
[[nodiscard]] Matrix DrillingTransformation4() const;
/// @brief Evaluates the direct five-component physical strain operator.
[[nodiscard]] Matrix DirectStrainDisplacement20(double xi, double eta,
double zeta) const;
/// @brief Evaluates all four canonical covariant tying shear samples.
[[nodiscard]] Matrix CovariantTyingShearSamples20() const;
/// @brief Evaluates the MITC-projected five-component strain operator.
[[nodiscard]] Matrix StrainDisplacement20(double xi, double eta,
double zeta) const;
/// @brief Returns the isotropic in-plane plane-stress matrix.
[[nodiscard]] Matrix PlaneStressConstitutive() const;
/// @brief Returns the five-component plane-stress and shear matrix.
[[nodiscard]] Matrix MaterialConstitutive5() const;
/// @brief Returns the centered membrane section matrix.
[[nodiscard]] Matrix MembraneSectionMatrix() const;
/// @brief Returns the centered bending section matrix.
[[nodiscard]] Matrix BendingSectionMatrix() const;
/// @brief Returns the corrected transverse-shear section matrix.
[[nodiscard]] Matrix TransverseShearSectionMatrix() const;
/// @brief Computes physical, drilling, and stabilized stiffness matrices.
/// @return Finite stiffness matrices or a structured model failure.
[[nodiscard]] Result<Mitc4Stiffness> Stiffness() const;
/// @brief Recovers physical shell quantities without drilling results.
/// @param global_element_displacement24 Global element DOFs in node order.
/// @return Physical recovery rows or a structured model failure.
[[nodiscard]] Result<Mitc4PhysicalRecovery> RecoverPhysical(
const Vector& global_element_displacement24) const;
private:
friend class ElementFactory;
/// @brief Binds Domain identity after the numerical candidate is valid.
void BindRuntime(ElementDofLayout layout, EntityIndex element_index,
SourceLocation location);
/// @brief Stores covariant, reciprocal, frame, and Jacobian data at one
/// point.
struct GeometryData {
std::array<Vector3, 3> covariant;
std::array<Vector3, 3> reciprocal;
Mitc4LocalFrame frame;
double jacobian;
};
/// @brief Stores validated shell geometry and constitutive state.
Mitc4Shell(std::array<Vector3, 4> coordinates,
std::array<Vector3, 4> directors, std::array<Vector3, 4> tangent_a,
std::array<Vector3, 4> tangent_b, Vector3 normal_candidate,
double thickness, double youngs_modulus, double poisson_ratio,
SourceLocation source_location, std::string identity);
/// @brief Evaluates a finite positive Jacobian and right-handed frame.
bool EvaluateGeometry(double xi, double eta, double zeta,
GeometryData& result) const noexcept;
/// @brief Evaluates displacement-basis derivatives in covariant directions.
std::array<std::array<Vector3, 3>, 20> BasisDerivatives(
double xi, double eta, double zeta) const noexcept;
/// @brief Builds direct or tied strain without changing projection order.
Matrix StrainDisplacement(double xi, double eta, double zeta,
const Matrix* tying_samples) const;
std::array<Vector3, 4> coordinates_;
std::array<Vector3, 4> directors_;
std::array<Vector3, 4> tangent_a_;
std::array<Vector3, 4> tangent_b_;
Vector3 normal_candidate_;
double thickness_;
double youngs_modulus_;
double poisson_ratio_;
SourceLocation source_location_;
std::string identity_;
ElementDofLayout dof_layout_;
EntityIndex element_index_{0U};
SourceLocation runtime_location_;
};
} // namespace fesa
#endif // FESA_ELEMENTS_MITC4_SHELL_H_
-142
View File
@@ -1,142 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/math/matrix.hpp"
#include "fesa/math/vector.hpp"
#include "fesa/model/model_types.hpp"
#include <array>
#include <string>
namespace fesa {
struct Mitc4ShapeFunctions {
std::array<double, 4> values;
std::array<double, 4> xiDerivatives;
std::array<double, 4> etaDerivatives;
};
struct Mitc4LocalFrame {
std::array<double, 3> e1;
std::array<double, 3> e2;
std::array<double, 3> e3;
};
struct Mitc4TyingWeights {
std::array<double, 2> xiZeta;
std::array<double, 2> etaZeta;
};
struct Mitc4QuadraturePoint {
std::array<double, 3> naturalCoordinates;
double weight;
};
struct Mitc4Stiffness {
Matrix physicalLocal20;
Matrix physicalGlobal24;
Matrix drillingGlobal24;
Matrix stabilizedGlobal24;
double drillingStiffness;
};
struct Mitc4PhysicalRecoveryPoint {
std::array<double, 2> naturalCoordinates;
Mitc4LocalFrame localFrame;
std::array<double, 8> generalizedStrain;
std::array<double, 8> sectionResultant;
std::array<std::array<double, 3>, 3> inPlaneStress;
};
struct Mitc4PhysicalRecovery {
std::array<Mitc4PhysicalRecoveryPoint, 4> points;
double strainEnergy;
};
// Concrete small-rotation MITC4 kinematics, constitutive, stiffness, and
// physical-only recovery kernel. Global equation/result ownership remains outside.
class Mitc4Shell {
public:
static Result<Mitc4Shell> create(
std::array<const Node*, 4> nodes,
std::array<std::array<double, 3>, 4> initialDirectors,
const ShellSection& section,
const LinearElasticMaterial& material);
static Mitc4ShapeFunctions shapeFunctions(double xi, double eta) noexcept;
static Mitc4TyingWeights tyingWeights(double xi, double eta) noexcept;
static const std::array<Mitc4QuadraturePoint, 8>&
volumeQuadrature() noexcept;
[[nodiscard]] Mitc4LocalFrame localFrame(double xi, double eta) const;
[[nodiscard]] Matrix physicalTransformation20() const;
[[nodiscard]] Matrix drillingTransformation4() const;
[[nodiscard]] Matrix directStrainDisplacement20(
double xi,
double eta,
double zeta) const;
[[nodiscard]] Matrix covariantTyingShearSamples20() const;
[[nodiscard]] Matrix strainDisplacement20(
double xi,
double eta,
double zeta) const;
[[nodiscard]] Matrix planeStressConstitutive() const;
[[nodiscard]] Matrix materialConstitutive5() const;
[[nodiscard]] Matrix membraneSectionMatrix() const;
[[nodiscard]] Matrix bendingSectionMatrix() const;
[[nodiscard]] Matrix transverseShearSectionMatrix() const;
[[nodiscard]] Result<Mitc4Stiffness> stiffness() const;
[[nodiscard]] Result<Mitc4PhysicalRecovery> recoverPhysical(
const Vector& globalElementDisplacement24) const;
private:
using Vector3 = std::array<double, 3>;
struct GeometryData {
std::array<Vector3, 3> covariant;
std::array<Vector3, 3> reciprocal;
Mitc4LocalFrame frame;
double jacobian;
};
Mitc4Shell(
std::array<Vector3, 4> coordinates,
std::array<Vector3, 4> directors,
std::array<Vector3, 4> tangentA,
std::array<Vector3, 4> tangentB,
Vector3 normalCandidate,
double thickness,
double youngsModulus,
double poissonRatio,
SourceLocation sourceLocation,
std::string identity);
bool evaluateGeometry(
double xi,
double eta,
double zeta,
GeometryData& result) const noexcept;
std::array<std::array<Vector3, 3>, 20> basisDerivatives(
double xi,
double eta,
double zeta) const noexcept;
Matrix strainDisplacement(
double xi,
double eta,
double zeta,
const Matrix* tyingSamples) const;
std::array<Vector3, 4> coordinates_;
std::array<Vector3, 4> directors_;
std::array<Vector3, 4> tangentA_;
std::array<Vector3, 4> tangentB_;
Vector3 normalCandidate_;
double thickness_;
double youngsModulus_;
double poissonRatio_;
SourceLocation sourceLocation_;
std::string identity_;
};
} // namespace fesa
+107
View File
@@ -0,0 +1,107 @@
#ifndef FESA_FEM_DOF_MANAGER_H_
#define FESA_FEM_DOF_MANAGER_H_
#include <array>
#include <cstddef>
#include <optional>
#include <vector>
#include "fesa/analysis/analysis_model.h"
#include "fesa/constraints/boundary_condition.h"
#include "fesa/elements/element.h"
#include "fesa/math/vector.h"
namespace fesa {
class DofManagerTestAccess;
/// @brief Stores the stable structural CSR pattern.
struct SparsePattern {
std::vector<std::size_t> row_offsets;
std::vector<std::size_t> column_indices;
};
/// @brief Owns full/free/constrained numbering, scatter maps, and CSR pattern.
class DofManager {
public:
/// @brief Creates an empty candidate for atomic Build replacement.
DofManager() = default;
/// @brief Creates every equation-space mapping for an active model.
/// @note This compatibility entry point derives temporary semantic layouts;
/// the procedure-owned runtime element view supersedes it in Step 20.
static Result<DofManager> Create(const AnalysisModel& model);
/// @brief Builds mappings from runtime element layouts in supplied order.
/// @param analysis_model Non-owning active model view that outlives this
/// call.
/// @param elements Runtime elements in stable active source order.
/// @return Success after atomic replacement or a structured model failure.
Status Build(const AnalysisModel& analysis_model,
const ElementView& elements);
/// @brief Builds mappings from explicit runtime elements and boundaries.
/// @param boundaries Non-owning definitions in stable source order.
/// @return Success after atomic replacement or a structured failure.
Status Build(const AnalysisModel& analysis_model, const ElementView& elements,
const BoundaryConditionView& boundaries);
/// @brief Returns the full node-by-component DOF count.
std::size_t FullDofCount() const noexcept;
/// @brief Returns the free-equation count.
std::size_t FreeDofCount() const noexcept;
/// @brief Returns the prescribed-DOF count.
std::size_t ConstrainedDofCount() const noexcept;
/// @brief Maps a stable node index and component to a full DOF.
std::size_t FullDof(EntityIndex node, DofComponent component) const;
/// @brief Returns the free equation for a full DOF when unconstrained.
std::optional<std::size_t> FreeEquation(std::size_t full_dof) const;
/// @brief Maps one declared runtime layout to stable full DOFs.
/// @return The declared node/component scatter or a layout failure.
Result<std::vector<std::size_t>> ElementScatter(
const ElementDofLayout& layout) const;
/// @brief Returns a beam scatter in endpoint/component order.
/// @note This compatibility wrapper delegates to the generic stored layout.
std::array<std::size_t, 12> ElementScatter(EntityIndex element) const;
/// @brief Returns a shell scatter in node/component order.
/// @note This compatibility wrapper delegates to the generic stored layout.
std::array<std::size_t, 24> ShellElementScatter(EntityIndex element) const;
/// @brief Returns free full DOFs in stable increasing order.
const std::vector<std::size_t>& FreeDofs() const noexcept;
/// @brief Returns constrained full DOFs in stable increasing order.
const std::vector<std::size_t>& ConstrainedDofs() const noexcept;
/// @brief Returns dc in constrained-DOF order.
const Vector& PrescribedValues() const noexcept;
/// @brief Returns the full-space structural CSR pattern.
const SparsePattern& GetSparsePattern() const noexcept;
/// @brief Validates the complete owner-issued equation and pattern mapping.
Status ValidateInvariants() const;
private:
friend class DofManagerTestAccess;
/// @brief Builds from copied layouts after the caller fixes their order.
Status BuildLayouts(const AnalysisModel& analysis_model,
const std::vector<ElementDofLayout>& layouts,
const BoundaryConditionView& boundaries);
/// @brief Takes ownership of fully validated stable equation mappings.
DofManager(std::size_t full_dof_count,
std::vector<std::optional<std::size_t>> free_equations,
std::vector<std::vector<std::size_t>> element_scatters,
std::vector<std::size_t> free_dofs,
std::vector<std::size_t> constrained_dofs,
Vector prescribed_values, SparsePattern sparse_pattern);
std::size_t full_dof_count_{0U};
std::vector<std::optional<std::size_t>> free_equations_;
std::vector<std::vector<std::size_t>> element_scatters_;
std::vector<std::size_t> free_dofs_;
std::vector<std::size_t> constrained_dofs_;
Vector prescribed_values_{0U};
SparsePattern sparse_pattern_;
};
} // namespace fesa
#endif // FESA_FEM_DOF_MANAGER_H_
-69
View File
@@ -1,69 +0,0 @@
#pragma once
#include "fesa/analysis/analysis_model.hpp"
#include "fesa/math/vector.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>
namespace fesa {
enum class DofComponent : std::uint8_t {
ux,
uy,
uz,
urx,
ury,
urz
};
struct SparsePattern {
std::vector<std::size_t> rowOffsets;
std::vector<std::size_t> columnIndices;
};
// Owns every equation-space mapping so semantic model objects remain free of
// analysis-specific equation IDs.
class DofManager {
public:
static Result<DofManager> create(const AnalysisModel& model);
std::size_t fullDofCount() const noexcept;
std::size_t freeDofCount() const noexcept;
std::size_t constrainedDofCount() const noexcept;
std::size_t fullDof(EntityIndex node, DofComponent component) const;
std::optional<std::size_t> freeEquation(std::size_t fullDof) const;
const std::array<std::size_t, 12>& elementScatter(
EntityIndex element) const;
const std::array<std::size_t, 24>& shellElementScatter(
EntityIndex element) const;
const std::vector<std::size_t>& freeDofs() const noexcept;
const std::vector<std::size_t>& constrainedDofs() const noexcept;
const Vector& prescribedValues() const noexcept;
const SparsePattern& sparsePattern() const noexcept;
private:
DofManager(
std::size_t fullDofCount,
std::vector<std::optional<std::size_t>> freeEquations,
std::vector<std::array<std::size_t, 12>> elementScatters,
std::vector<std::array<std::size_t, 24>> shellElementScatters,
std::vector<std::size_t> freeDofs,
std::vector<std::size_t> constrainedDofs,
Vector prescribedValues,
SparsePattern sparsePattern);
std::size_t fullDofCount_;
std::vector<std::optional<std::size_t>> freeEquations_;
std::vector<std::array<std::size_t, 12>> elementScatters_;
std::vector<std::array<std::size_t, 24>> shellElementScatters_;
std::vector<std::size_t> freeDofs_;
std::vector<std::size_t> constrainedDofs_;
Vector prescribedValues_;
SparsePattern sparsePattern_;
};
} // namespace fesa
+24
View File
@@ -0,0 +1,24 @@
#ifndef FESA_IO_ABAQUS_DOMAIN_MAPPER_H_
#define FESA_IO_ABAQUS_DOMAIN_MAPPER_H_
#include "fesa/core/status.h"
#include "fesa/io/abaqus/input_syntax.h"
#include "fesa/model/domain.h"
namespace fesa {
/// @brief Maps syntax-only blocks into an approved immutable semantic model.
/// @note Source identity and declaration order are preserved through mapping.
class AbaqusDomainMapper {
public:
/// @brief Resolves supported Abaqus syntax into a complete Domain candidate.
/// @param input Parsed syntax whose source locations remain valid for
/// mapping.
/// @return A committed Domain or structured input/model diagnostics; partial
/// domains are never returned.
Result<Domain> Map(const ParsedInput& input) const;
};
} // namespace fesa
#endif // FESA_IO_ABAQUS_DOMAIN_MAPPER_H_
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/io/abaqus/input_syntax.hpp"
#include "fesa/model/domain.hpp"
namespace fesa {
// Converts syntax-only blocks into the approved immutable B33 semantic model.
class AbaqusDomainMapper {
public:
Result<Domain> map(const ParsedInput& input) const;
};
} // namespace fesa
+24
View File
@@ -0,0 +1,24 @@
#ifndef FESA_IO_ABAQUS_INPUT_READER_H_
#define FESA_IO_ABAQUS_INPUT_READER_H_
#include <filesystem>
#include "fesa/core/status.h"
#include "fesa/io/abaqus/input_syntax.h"
namespace fesa {
/// @brief Reads Abaqus physical keyword, data, and comment syntax.
/// @note Semantic policy is applied later by AbaqusDomainMapper.
class AbaqusInputReader {
public:
/// @brief Parses one input file without applying semantic mapping policy.
/// @param input_path Path to the exact source bytes whose identity is
/// retained.
/// @return Parsed syntax or a structured input diagnostic.
Result<ParsedInput> Read(const std::filesystem::path& input_path) const;
};
} // namespace fesa
#endif // FESA_IO_ABAQUS_INPUT_READER_H_
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/io/abaqus/input_syntax.hpp"
#include <filesystem>
namespace fesa {
// Reads only physical keyword/data/comment syntax; semantic policy is applied
// later by AbaqusDomainMapper.
class AbaqusInputReader {
public:
Result<ParsedInput> read(const std::filesystem::path& inputPath) const;
};
} // namespace fesa
+46
View File
@@ -0,0 +1,46 @@
#ifndef FESA_IO_ABAQUS_INPUT_SYNTAX_H_
#define FESA_IO_ABAQUS_INPUT_SYNTAX_H_
#include <filesystem>
#include <optional>
#include <string>
#include <vector>
#include "fesa/core/source_identity.h"
namespace fesa {
/// @brief Stores a canonical parameter name and its optional source value.
/// @note Parameter names are canonicalized for lookup while values remain
/// source text.
struct KeywordParameter {
std::string name;
std::optional<std::string> value;
};
/// @brief Stores one parsed data row with its source location.
struct DataLine {
std::vector<std::string> fields;
SourceLocation location;
};
/// @brief Stores one syntax-only keyword block and its following data rows.
struct KeywordBlock {
std::string canonical_name;
std::string original_line;
std::vector<KeywordParameter> parameters;
std::vector<DataLine> data;
SourceLocation location;
};
/// @brief Stores the parsed syntax and stable identity of one Abaqus input
/// file.
struct ParsedInput {
std::filesystem::path source_path;
std::string source_content_identity;
std::vector<KeywordBlock> blocks;
};
} // namespace fesa
#endif // FESA_IO_ABAQUS_INPUT_SYNTAX_H_
-37
View File
@@ -1,37 +0,0 @@
#pragma once
#include "fesa/core/source_identity.hpp"
#include <filesystem>
#include <optional>
#include <string>
#include <vector>
namespace fesa {
// Names are canonicalized for syntax lookup while values remain source text.
struct KeywordParameter {
std::string name;
std::optional<std::string> value;
};
struct DataLine {
std::vector<std::string> fields;
SourceLocation location;
};
struct KeywordBlock {
std::string canonicalName;
std::string originalLine;
std::vector<KeywordParameter> parameters;
std::vector<DataLine> data;
SourceLocation location;
};
struct ParsedInput {
std::filesystem::path sourcePath;
std::string sourceContentIdentity;
std::vector<KeywordBlock> blocks;
};
} // namespace fesa
@@ -0,0 +1,28 @@
#ifndef FESA_IO_HDF5_HDF5_RESULTS_WRITER_H_
#define FESA_IO_HDF5_HDF5_RESULTS_WRITER_H_
#include "fesa/results/results_writer.h"
namespace fesa {
/// @brief Writes authoritative schema-v0 HDF5 results atomically.
/// @note HDF5 and platform types remain private to the implementation.
class Hdf5ResultsWriter final : public ResultsWriter {
public:
/// @brief Writes and self-checks a complete candidate before finalization.
/// @param output_path Final authoritative path; the candidate is created in
/// the same directory.
/// @param domain Immutable source and model identity.
/// @param state Fully recovered analysis state.
/// @param diagnostics Deterministically ordered run diagnostics.
/// @return Success only after atomic replacement or a structured output
/// failure.
/// @note A failed candidate does not replace an existing valid final file.
Status Write(const std::filesystem::path& output_path, const Domain& domain,
const AnalysisState& state,
const std::vector<Diagnostic>& diagnostics) override;
};
} // namespace fesa
#endif // FESA_IO_HDF5_HDF5_RESULTS_WRITER_H_
@@ -1,17 +0,0 @@
#pragma once
#include "fesa/results/results_writer.hpp"
namespace fesa {
// Writes schema-v0 output while keeping backend and platform types private.
class Hdf5ResultsWriter final : public ResultsWriter {
public:
Status write(
const std::filesystem::path& outputPath,
const Domain& domain,
const AnalysisState& state,
const std::vector<Diagnostic>& diagnostics) override;
};
} // namespace fesa
@@ -0,0 +1,52 @@
#ifndef FESA_LOADS_CONCENTRATED_NODAL_LOAD_H_
#define FESA_LOADS_CONCENTRATED_NODAL_LOAD_H_
#include <array>
#include <cstddef>
#include "fesa/core/diagnostic.h"
#include "fesa/loads/load.h"
#include "fesa/model/source_target_resolver.h"
namespace fesa {
/// @brief Emits global concentrated nodal components for one source target.
class ConcentratedNodalLoad final : public Load {
public:
/// @brief Creates a six-component global concentrated nodal load.
ConcentratedNodalLoad(SourceTargetQuery target,
std::array<double, 6> global_components,
std::size_t source_order);
/// @brief Creates one parsed CLOAD component while preserving diagnostics.
ConcentratedNodalLoad(SourceTargetQuery target, int source_dof,
double magnitude, std::size_t source_order,
SourceLocation location);
/// @brief Computes target-major, component-minor full-DOF contributions.
Result<std::vector<LoadContribution>> ComputeContributions(
const LoadContext& context) const override;
/// @brief Returns the immutable source target query.
const SourceTargetQuery& Target() const noexcept;
/// @brief Returns six global force/moment components without reordering.
const std::array<double, 6>& GlobalComponents() const noexcept;
/// @brief Returns the stable CLOAD declaration order.
std::size_t SourceOrder() const noexcept;
/// @brief Returns the source location used by structured diagnostics.
const SourceLocation& Location() const noexcept;
private:
SourceTargetQuery target_;
std::array<double, 6> global_components_{};
std::size_t source_order_;
SourceLocation location_{};
int source_dof_{0};
};
} // namespace fesa
#endif // FESA_LOADS_CONCENTRATED_NODAL_LOAD_H_
+48
View File
@@ -0,0 +1,48 @@
#ifndef FESA_LOADS_LOAD_H_
#define FESA_LOADS_LOAD_H_
#include <cstddef>
#include <functional>
#include <vector>
#include "fesa/core/status.h"
namespace fesa {
class DofManager;
class Domain;
class SourceTargetResolver;
/// @brief Describes one ordered contribution to the full load vector.
struct LoadContribution {
std::size_t source_order;
std::size_t full_dof_index;
double value;
};
/// @brief Provides immutable semantic and equation context to a Load.
/// @note Every referenced object must outlive a contribution request.
struct LoadContext {
const Domain& domain;
const DofManager& dof_manager;
const SourceTargetResolver& target_resolver;
};
/// @brief Produces local ordered load contributions without global mutation.
class Load {
public:
virtual ~Load() = default;
/// @brief Computes finite full-DOF contributions in stable target order.
/// @param context Non-owning semantic and equation context for this call.
/// @return Ordered contributions or a structured model failure.
virtual Result<std::vector<LoadContribution>> ComputeContributions(
const LoadContext& context) const = 0;
};
/// @brief Holds non-owning loads in an explicitly supplied source order.
using LoadView = std::vector<std::reference_wrapper<const Load>>;
} // namespace fesa
#endif // FESA_LOADS_LOAD_H_
@@ -0,0 +1,71 @@
#ifndef FESA_MATERIALS_ISOTROPIC_LINEAR_ELASTIC_MATERIAL_H_
#define FESA_MATERIALS_ISOTROPIC_LINEAR_ELASTIC_MATERIAL_H_
#include <string>
#include "fesa/core/status.h"
#include "fesa/materials/material.h"
namespace fesa {
/// @brief Stores homogeneous isotropic linear-elastic material data.
/// @note Poisson ratios above 0.5 remain valid for the approved beam subset;
/// shell compatibility is checked by the shell kernel.
class IsotropicLinearElasticMaterial final : public Material {
public:
/// @brief Creates a material after validating the current elastic fields.
/// @param source_id Stable semantic identity supplied by the mapper.
/// @param name Source material name.
/// @param youngs_modulus Young's modulus in the active consistent unit
/// system.
/// @param poissons_ratio Dimensionless Poisson ratio.
/// @param location Source MATERIAL keyword location.
/// @return A material or a structured model failure.
static Result<IsotropicLinearElasticMaterial> Create(SourceEntityId source_id,
std::string name,
double youngs_modulus,
double poissons_ratio,
SourceLocation location);
/// @brief Constructs an already validated parser-owned material record.
/// @note This compatibility seam preserves existing semantic mapping until
/// Domain polymorphic ownership is migrated.
IsotropicLinearElasticMaterial(std::string name, double youngs_modulus,
double poissons_ratio,
SourceLocation location);
MaterialKind Kind() const noexcept override;
const SourceEntityId& SourceId() const noexcept override;
const SourceLocation& Location() const noexcept override;
/// @brief Returns the source material name.
const std::string& Name() const noexcept;
/// @brief Returns Young's modulus in the active consistent unit system.
double YoungsModulus() const noexcept;
/// @brief Returns the dimensionless Poisson ratio.
double PoissonsRatio() const noexcept;
// Public storage preserves the current semantic-record API until Domain
// ownership migrates in the next approved Step.
std::string name;
double youngs_modulus;
double poisson_ratio;
SourceLocation location;
private:
/// @brief Constructs a candidate whose fields have already been checked.
IsotropicLinearElasticMaterial(SourceEntityId source_id, std::string name,
double youngs_modulus, double poissons_ratio,
SourceLocation location);
SourceEntityId source_id_;
};
/// @brief Preserves the approved V0 material spelling for current consumers.
using LinearElasticMaterial = IsotropicLinearElasticMaterial;
} // namespace fesa
#endif // FESA_MATERIALS_ISOTROPIC_LINEAR_ELASTIC_MATERIAL_H_
+28
View File
@@ -0,0 +1,28 @@
#ifndef FESA_MATERIALS_MATERIAL_H_
#define FESA_MATERIALS_MATERIAL_H_
#include "fesa/core/source_identity.h"
namespace fesa {
/// @brief Identifies the supported concrete material semantics.
enum class MaterialKind { kIsotropicLinearElastic };
/// @brief Provides stable identity for a Domain-owned material definition.
class Material {
public:
virtual ~Material() = default;
/// @brief Returns the concrete material kind.
virtual MaterialKind Kind() const noexcept = 0;
/// @brief Returns the stable source identity preserved for diagnostics.
virtual const SourceEntityId& SourceId() const noexcept = 0;
/// @brief Returns the input location that defined the material.
virtual const SourceLocation& Location() const noexcept = 0;
};
} // namespace fesa
#endif // FESA_MATERIALS_MATERIAL_H_
+59
View File
@@ -0,0 +1,59 @@
#ifndef FESA_MATH_MATRIX_H_
#define FESA_MATH_MATRIX_H_
#include <cstddef>
#include <vector>
#include "fesa/math/vector.h"
namespace fesa {
/// @brief Owns row-major contiguous storage independently of sparse matrices.
class Matrix {
public:
/// @brief Constructs a row-major matrix initialized to one value.
Matrix(std::size_t rows, std::size_t columns, double value = 0.0);
/// @brief Copies matrix values into independent contiguous storage.
Matrix(const Matrix& other);
/// @brief Moves matrix storage and resets other to a zero-by-zero shape.
Matrix(Matrix&& other) noexcept;
/// @brief Copies matrix values into independent contiguous storage.
Matrix& operator=(const Matrix& other);
/// @brief Moves matrix storage and resets other to a zero-by-zero shape.
Matrix& operator=(Matrix&& other) noexcept;
/// @brief Returns the row count.
std::size_t Rows() const noexcept;
/// @brief Returns the column count.
std::size_t Columns() const noexcept;
/// @brief Returns a bounds-checked mutable entry.
/// @throws std::out_of_range if the index is outside the matrix.
double& operator()(std::size_t row, std::size_t column);
/// @brief Returns a bounds-checked immutable entry.
/// @throws std::out_of_range if the index is outside the matrix.
const double& operator()(std::size_t row, std::size_t column) const;
/// @brief Multiplies this row-major matrix by a dense vector.
/// @throws std::invalid_argument if the dimensions are incompatible.
Vector Multiply(const Vector& rhs) const;
/// @brief Multiplies this row-major matrix by another dense matrix.
/// @throws std::invalid_argument if the dimensions are incompatible.
Matrix Multiply(const Matrix& rhs) const;
private:
std::size_t rows_;
std::size_t columns_;
std::vector<double> values_;
};
} // namespace fesa
#endif // FESA_MATH_MATRIX_H_
-32
View File
@@ -1,32 +0,0 @@
#pragma once
#include "fesa/math/vector.hpp"
#include <cstddef>
#include <vector>
namespace fesa {
// Owns row-major contiguous dense storage independently of sparse matrices.
class Matrix {
public:
Matrix(std::size_t rows, std::size_t columns, double value = 0.0);
Matrix(const Matrix& other);
Matrix(Matrix&& other) noexcept;
Matrix& operator=(const Matrix& other);
Matrix& operator=(Matrix&& other) noexcept;
std::size_t rows() const noexcept;
std::size_t columns() const noexcept;
double& operator()(std::size_t row, std::size_t column);
const double& operator()(std::size_t row, std::size_t column) const;
Vector multiply(const Vector& rhs) const;
Matrix multiply(const Matrix& rhs) const;
private:
std::size_t rows_;
std::size_t columns_;
std::vector<double> values_;
};
} // namespace fesa
+73
View File
@@ -0,0 +1,73 @@
#ifndef FESA_MATH_SPARSE_MATRIX_H_
#define FESA_MATH_SPARSE_MATRIX_H_
#include <cstddef>
#include <vector>
#include "fesa/core/status.h"
#include "fesa/math/vector.h"
namespace fesa {
struct SparsePattern;
/// @brief Carries one deterministic element-local COO contribution.
struct CooContribution {
std::size_t row;
std::size_t column;
double value;
std::size_t element_order;
std::size_t local_order;
};
/// @brief Owns canonical 0-based CSR independently of the dense Matrix type.
class SparseMatrix {
public:
/// @brief Reduces ordered COO contributions into an expected CSR pattern.
/// @return A validated matrix or a structured model failure.
/// @note Duplicate sums use stable element and local contribution order.
static Result<SparseMatrix> FromCoo(
std::size_t rows, std::size_t columns,
std::vector<CooContribution> contributions,
const SparsePattern& expected_pattern);
/// @brief Returns the row count.
std::size_t Rows() const noexcept;
/// @brief Returns the column count.
std::size_t Columns() const noexcept;
/// @brief Returns the canonical 0-based CSR row offsets.
const std::vector<std::size_t>& RowOffsets() const noexcept;
/// @brief Returns sorted unique 0-based CSR column indices.
const std::vector<std::size_t>& ColumnIndices() const noexcept;
/// @brief Returns CSR values including preserved structural zeros.
const std::vector<double>& Values() const noexcept;
/// @brief Multiplies this matrix by a dense vector in stable CSR order.
/// @throws std::invalid_argument if the dimensions are incompatible.
Vector Multiply(const Vector& rhs) const;
/// @brief Validates shape, indices, ordering, and finite CSR values.
/// @return Success or a structured model failure.
Status Validate() const;
private:
/// @brief Constructs CSR storage after boundary validation.
SparseMatrix(std::size_t rows, std::size_t columns,
std::vector<std::size_t> row_offsets,
std::vector<std::size_t> column_indices,
std::vector<double> values);
std::size_t rows_;
std::size_t columns_;
std::vector<std::size_t> row_offsets_;
std::vector<std::size_t> column_indices_;
std::vector<double> values_;
};
} // namespace fesa
#endif // FESA_MATH_SPARSE_MATRIX_H_
-53
View File
@@ -1,53 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/math/vector.hpp"
#include <cstddef>
#include <vector>
namespace fesa {
struct SparsePattern;
struct CooContribution {
std::size_t row;
std::size_t column;
double value;
std::size_t elementOrder;
std::size_t localOrder;
};
// Owns canonical 0-based CSR data independently of the dense Matrix adapter.
class SparseMatrix {
public:
static Result<SparseMatrix> fromCoo(
std::size_t rows,
std::size_t columns,
std::vector<CooContribution> contributions,
const SparsePattern& expectedPattern);
std::size_t rows() const noexcept;
std::size_t columns() const noexcept;
const std::vector<std::size_t>& rowOffsets() const noexcept;
const std::vector<std::size_t>& columnIndices() const noexcept;
const std::vector<double>& values() const noexcept;
Vector multiply(const Vector& rhs) const;
Status validate() const;
private:
SparseMatrix(
std::size_t rows,
std::size_t columns,
std::vector<std::size_t> rowOffsets,
std::vector<std::size_t> columnIndices,
std::vector<double> values);
std::size_t rows_;
std::size_t columns_;
std::vector<std::size_t> rowOffsets_;
std::vector<std::size_t> columnIndices_;
std::vector<double> values_;
};
} // namespace fesa
+64
View File
@@ -0,0 +1,64 @@
#ifndef FESA_MATH_VECTOR_H_
#define FESA_MATH_VECTOR_H_
#include <cstddef>
#include <vector>
namespace fesa {
/// @brief Owns a contiguous dense vector while keeping MKL private.
class Vector {
public:
/// @brief Constructs a vector with all entries initialized to one value.
explicit Vector(std::size_t size, double value = 0.0);
/// @brief Copies vector values into independent contiguous storage.
Vector(const Vector& other);
/// @brief Moves vector storage and leaves other empty.
Vector(Vector&& other) noexcept;
/// @brief Copies vector values into independent contiguous storage.
Vector& operator=(const Vector& other);
/// @brief Moves vector storage and leaves other empty.
Vector& operator=(Vector&& other) noexcept;
/// @brief Returns the number of entries.
std::size_t Size() const noexcept;
/// @brief Returns mutable contiguous storage.
double* Data() noexcept;
/// @brief Returns immutable contiguous storage.
const double* Data() const noexcept;
/// @brief Returns a bounds-checked mutable entry.
/// @throws std::out_of_range if index is outside the vector.
double& operator[](std::size_t index);
/// @brief Returns a bounds-checked immutable entry.
/// @throws std::out_of_range if index is outside the vector.
const double& operator[](std::size_t index) const;
/// @brief Computes the Euclidean dot product with rhs.
/// @throws std::invalid_argument if the vector sizes differ.
double Dot(const Vector& rhs) const;
/// @brief Computes the Euclidean norm.
double Norm() const;
/// @brief Scales each entry by alpha through the dense backend.
void Scale(double alpha);
/// @brief Accumulates alpha times x into this vector.
/// @throws std::invalid_argument if the vector sizes differ.
void Axpy(double alpha, const Vector& x);
private:
std::vector<double> values_;
};
} // namespace fesa
#endif // FESA_MATH_VECTOR_H_
-31
View File
@@ -1,31 +0,0 @@
#pragma once
#include <cstddef>
#include <vector>
namespace fesa {
// Owns a contiguous dense vector while keeping the MKL backend private.
class Vector {
public:
explicit Vector(std::size_t size, double value = 0.0);
Vector(const Vector& other);
Vector(Vector&& other) noexcept;
Vector& operator=(const Vector& other);
Vector& operator=(Vector&& other) noexcept;
std::size_t size() const noexcept;
double* data() noexcept;
const double* data() const noexcept;
double& operator[](std::size_t index);
const double& operator[](std::size_t index) const;
double dot(const Vector& rhs) const;
double norm() const;
void scale(double alpha);
void axpy(double alpha, const Vector& x);
private:
std::vector<double> values_;
};
} // namespace fesa
+114
View File
@@ -0,0 +1,114 @@
#ifndef FESA_MATH_VECTOR3_H_
#define FESA_MATH_VECTOR3_H_ // NOLINT(readability-identifier-naming)
#include <array>
#include <cmath>
#include <cstddef>
#include <optional>
namespace fesa {
/// @brief Represents an owning fixed-size three-dimensional value vector.
class Vector3 {
public:
/// @brief Constructs the zero vector.
constexpr Vector3() noexcept = default;
/// @brief Constructs a vector from three Cartesian components.
/// @param x First component in the caller-defined coordinate system.
/// @param y Second component in the caller-defined coordinate system.
/// @param z Third component in the caller-defined coordinate system.
constexpr Vector3(double x, double y, double z) noexcept
: components_{{x, y, z}} {}
/// @brief Copies components from an existing array-backed carrier.
explicit constexpr Vector3(const std::array<double, 3>& components) noexcept
: components_{components} {}
/// @brief Returns the first component.
constexpr double X() const noexcept { return components_[0]; }
/// @brief Returns the second component.
constexpr double Y() const noexcept { return components_[1]; }
/// @brief Returns the third component.
constexpr double Z() const noexcept { return components_[2]; }
/// @brief Returns a component by zero-based index.
/// @pre index is less than three.
constexpr double operator[](std::size_t index) const noexcept {
return components_[index];
}
/// @brief Returns the immutable array-backed component carrier.
constexpr const std::array<double, 3>& Components() const noexcept {
return components_;
}
/// @brief Adds corresponding vector components.
constexpr Vector3 operator+(const Vector3& rhs) const noexcept {
return Vector3{X() + rhs.X(), Y() + rhs.Y(), Z() + rhs.Z()};
}
/// @brief Subtracts corresponding vector components.
constexpr Vector3 operator-(const Vector3& rhs) const noexcept {
return Vector3{X() - rhs.X(), Y() - rhs.Y(), Z() - rhs.Z()};
}
/// @brief Multiplies every component by a scalar.
constexpr Vector3 operator*(double scalar) const noexcept {
return Vector3{X() * scalar, Y() * scalar, Z() * scalar};
}
/// @brief Divides every component by a scalar.
constexpr Vector3 operator/(double scalar) const noexcept {
return Vector3{X() / scalar, Y() / scalar, Z() / scalar};
}
/// @brief Multiplies every component with the scalar as the left operand.
friend constexpr Vector3 operator*(double scalar,
const Vector3& rhs) noexcept {
return Vector3{scalar * rhs.X(), scalar * rhs.Y(), scalar * rhs.Z()};
}
/// @brief Compares every component exactly.
constexpr bool operator==(const Vector3& rhs) const noexcept {
return X() == rhs.X() && Y() == rhs.Y() && Z() == rhs.Z();
}
/// @brief Computes the Euclidean dot product with rhs.
double Dot(const Vector3& rhs) const noexcept {
return X() * rhs.X() + Y() * rhs.Y() + Z() * rhs.Z();
}
/// @brief Computes the right-handed cross product with rhs.
Vector3 Cross(const Vector3& rhs) const noexcept {
return Vector3{Y() * rhs.Z() - Z() * rhs.Y(), Z() * rhs.X() - X() * rhs.Z(),
X() * rhs.Y() - Y() * rhs.X()};
}
/// @brief Computes the Euclidean norm.
double Norm() const noexcept { return std::hypot(X(), Y(), Z()); }
/// @brief Returns a unit vector when the norm is usable.
/// @return Empty when the norm is exactly zero or nonfinite.
std::optional<Vector3> Normalized() const noexcept {
const double norm = Norm(); // NOLINT(readability-identifier-naming)
if (norm == 0.0 || !std::isfinite(norm)) {
return std::nullopt;
}
return Vector3{X() / norm, Y() / norm, Z() / norm};
}
/// @brief Reports whether all components are finite.
bool IsFinite() const noexcept {
return std::isfinite(X()) && std::isfinite(Y()) && std::isfinite(Z());
}
private:
std::array<double, 3> components_{};
};
} // namespace fesa
#endif // FESA_MATH_VECTOR3_H_
+202
View File
@@ -0,0 +1,202 @@
#ifndef FESA_MODEL_DOMAIN_H_
#define FESA_MODEL_DOMAIN_H_
#include <cstddef>
#include <filesystem>
#include <memory>
#include <string>
#include <vector>
#include "fesa/constraints/boundary_condition.h"
#include "fesa/constraints/prescribed_displacement.h"
#include "fesa/core/status.h"
#include "fesa/loads/concentrated_nodal_load.h"
#include "fesa/loads/load.h"
#include "fesa/model/model_types.h"
namespace fesa {
class StepDefinition;
/// @brief Exposes immutable references without transferring Domain ownership.
/// @tparam T Base or concrete semantic type stored by the Domain.
template <class T>
class DomainCollectionView {
public:
/// @brief Returns the number of stable collection positions.
std::size_t Size() const noexcept { return entries_.size(); }
/// @brief Reports whether the collection has no entries.
bool Empty() const noexcept { return entries_.empty(); }
/// @brief Returns one immutable entry without bounds checking.
const T& operator[](const std::size_t index) const noexcept {
return *entries_[index];
}
/// @brief Returns one immutable entry with bounds checking.
const T& At(const std::size_t index) const { return *entries_.at(index); }
private:
friend class Domain;
friend class StepDefinition;
/// @brief Adds one reference while the owning Domain candidate is built.
void Add(const T& entry) { entries_.push_back(&entry); }
std::vector<const T*> entries_;
};
/// @brief Owns one immutable static-step semantic definition.
/// @note Loads retain source order and are owned polymorphically by unique
/// pointers.
class StepDefinition {
public:
StepDefinition(const StepDefinition&) = delete;
StepDefinition& operator=(const StepDefinition&) = delete;
StepDefinition(StepDefinition&&) noexcept = default;
StepDefinition& operator=(StepDefinition&&) noexcept = default;
/// @brief Returns the source step name.
const std::string& Name() const noexcept;
/// @brief Returns polymorphic boundaries in stable source/component order.
const BoundaryConditionView& BoundaryConditions() const noexcept;
/// @brief Returns prescribed displacements in stable source/component order.
const DomainCollectionView<PrescribedDisplacementBoundaryCondition>&
PrescribedDisplacements() const noexcept;
/// @brief Returns polymorphic loads in stable source order.
const LoadView& Loads() const noexcept;
/// @brief Returns current concentrated loads in stable source order.
const DomainCollectionView<ConcentratedNodalLoad>& ConcentratedLoads()
const noexcept;
/// @brief Returns the static initial increment provenance value.
double InitialIncrement() const noexcept;
/// @brief Returns the static time-period provenance value.
double TimePeriod() const noexcept;
/// @brief Returns the static minimum-increment provenance value.
double MinimumIncrement() const noexcept;
/// @brief Returns the static maximum-increment provenance value.
double MaximumIncrement() const noexcept;
/// @brief Returns the source location of the step declaration.
const SourceLocation& Location() const noexcept;
private:
friend class Domain;
/// @brief Converts one parsed static-step record to owned semantic objects.
explicit StepDefinition(StaticStepDefinition definition);
std::string name_;
std::vector<std::unique_ptr<BoundaryCondition>> boundary_conditions_;
BoundaryConditionView boundary_conditions_view_;
DomainCollectionView<PrescribedDisplacementBoundaryCondition>
prescribed_displacements_view_;
std::vector<std::unique_ptr<Load>> loads_;
LoadView loads_view_;
DomainCollectionView<ConcentratedNodalLoad> concentrated_loads_view_;
double initial_increment_;
double time_period_;
double minimum_increment_;
double maximum_increment_;
SourceLocation location_;
};
/// @brief Owns the complete immutable semantic model definition.
/// @note Collection positions remain stable internal indices after
/// construction.
class Domain {
public:
/// @brief Creates a Domain that owns a copy or moved model definition.
/// @param definition Complete parsed semantic records in declaration order.
/// @return A successful owning Domain.
static Result<Domain> Create(ModelDefinition definition);
Domain(const Domain&) = delete;
Domain& operator=(const Domain&) = delete;
Domain(Domain&&) noexcept = default;
Domain& operator=(Domain&&) noexcept = default;
/// @brief Returns nodes in stable declaration order.
const std::vector<Node>& Nodes() const noexcept;
/// @brief Returns all element definitions in stable Domain index order.
const DomainCollectionView<ElementDefinition>& Elements() const noexcept;
/// @brief Returns B33 definitions in their stable concrete order.
const DomainCollectionView<EulerBeam3DDefinition>& BeamElements()
const noexcept;
/// @brief Returns MITC4 shell definitions in stable declaration order.
const DomainCollectionView<Mitc4ShellDefinition>& ShellElements()
const noexcept;
/// @brief Returns all materials in stable Domain index order.
const DomainCollectionView<Material>& Materials() const noexcept;
/// @brief Returns current isotropic materials in stable concrete order.
const DomainCollectionView<LinearElasticMaterial>& LinearElasticMaterials()
const noexcept;
/// @brief Returns all properties in stable Domain index order.
const DomainCollectionView<ElementProperty>& Properties() const noexcept;
/// @brief Returns beam sections in stable declaration order.
const DomainCollectionView<GeneralBeamSection>& Sections() const noexcept;
/// @brief Returns shell sections in stable declaration order.
const DomainCollectionView<ShellSection>& ShellSections() const noexcept;
/// @brief Returns preprocessed shell-node frames in stable node order.
const std::vector<ShellNodeInitialFrame>& ShellNodeInitialFrames()
const noexcept;
/// @brief Returns node sets in stable declaration order.
const std::vector<NodeSet>& NodeSets() const noexcept;
/// @brief Returns element sets in stable declaration order.
const std::vector<ElementSet>& ElementSets() const noexcept;
/// @brief Returns static steps in stable declaration order.
const DomainCollectionView<StepDefinition>& Steps() const noexcept;
/// @brief Returns sorted nonfatal mapping diagnostics.
const std::vector<Diagnostic>& Warnings() const noexcept;
/// @brief Returns the source input path associated with this model.
const std::filesystem::path& SourcePath() const noexcept;
/// @brief Returns the deterministic source-content identity.
const std::string& SourceContentIdentity() const noexcept;
private:
/// @brief Takes ownership of an already constructed model definition.
explicit Domain(ModelDefinition definition);
ModelDefinition definition_;
std::vector<std::unique_ptr<ElementDefinition>> element_definitions_;
std::vector<std::unique_ptr<ElementProperty>> element_properties_;
std::vector<std::unique_ptr<Material>> materials_;
std::vector<std::unique_ptr<StepDefinition>> step_definitions_;
DomainCollectionView<ElementDefinition> elements_view_;
DomainCollectionView<EulerBeam3DDefinition> beam_elements_view_;
DomainCollectionView<Mitc4ShellDefinition> shell_elements_view_;
DomainCollectionView<ElementProperty> properties_view_;
DomainCollectionView<GeneralBeamSection> sections_view_;
DomainCollectionView<ShellSection> shell_sections_view_;
DomainCollectionView<Material> materials_view_;
DomainCollectionView<LinearElasticMaterial> linear_materials_view_;
DomainCollectionView<StepDefinition> steps_view_;
};
} // namespace fesa
#endif // FESA_MODEL_DOMAIN_H_
-38
View File
@@ -1,38 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/model/model_types.hpp"
#include <filesystem>
#include <string>
#include <vector>
namespace fesa {
// Owns the complete semantic definition. Public access remains const so a
// vector position can serve as a stable internal index after construction.
class Domain {
public:
static Result<Domain> create(ModelDefinition definition);
const std::vector<Node>& nodes() const noexcept;
const std::vector<EulerBeam3DDefinition>& elements() const noexcept;
const std::vector<Mitc4ShellDefinition>& shellElements() const noexcept;
const std::vector<LinearElasticMaterial>& materials() const noexcept;
const std::vector<GeneralBeamSection>& sections() const noexcept;
const std::vector<ShellSection>& shellSections() const noexcept;
const std::vector<ShellNodeInitialFrame>& shellNodeInitialFrames() const noexcept;
const std::vector<NodeSet>& nodeSets() const noexcept;
const std::vector<ElementSet>& elementSets() const noexcept;
const std::vector<StaticStepDefinition>& steps() const noexcept;
const std::vector<Diagnostic>& warnings() const noexcept;
const std::filesystem::path& sourcePath() const noexcept;
const std::string& sourceContentIdentity() const noexcept;
private:
explicit Domain(ModelDefinition definition);
ModelDefinition definition_;
};
} // namespace fesa
+132
View File
@@ -0,0 +1,132 @@
#ifndef FESA_MODEL_MODEL_TYPES_H_
#define FESA_MODEL_MODEL_TYPES_H_
#include <array>
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "fesa/core/diagnostic.h"
#include "fesa/core/source_identity.h"
#include "fesa/elements/euler_beam_3d.h"
#include "fesa/elements/mitc4_shell.h"
#include "fesa/materials/isotropic_linear_elastic_material.h"
#include "fesa/properties/general_beam_section.h"
#include "fesa/properties/shell_section.h"
namespace fesa {
/// @brief Stores one source node and its global coordinates.
struct Node {
SourceEntityId source_id;
std::array<double, 3> coordinates;
SourceLocation location;
};
/// @brief Stores the deterministic initial director and tangent frame at a
/// node.
struct ShellNodeInitialFrame {
EntityIndex node_index;
std::array<double, 3> director;
std::array<double, 3> tangent_a;
std::array<double, 3> tangent_b;
};
/// @brief Stores one prescribed nodal degree-of-freedom range.
struct PrescribedDisplacementDefinition {
std::string target;
int first_dof;
int last_dof;
double value;
SourceLocation location;
};
/// @brief Stores one concentrated nodal load component.
struct NodalLoad {
std::string target;
int dof;
double magnitude;
SourceLocation location;
};
/// @brief Stores the approved single linear-static step definition.
struct StaticStepDefinition {
std::string name;
std::vector<PrescribedDisplacementDefinition> boundaries;
std::vector<NodalLoad> loads;
double initial_increment;
double time_period;
double minimum_increment;
double maximum_increment;
SourceLocation location;
};
/// @brief Stores a stable resolved node-set membership list.
struct NodeSet {
std::string name;
std::optional<std::string> instance_name;
std::vector<EntityIndex> node_indices;
SourceLocation location;
};
/// @brief Stores a stable resolved element-set membership list.
struct ElementSet {
std::string name;
std::optional<std::string> instance_name;
std::vector<EntityIndex> element_indices;
SourceLocation location;
};
/// @brief Preserves source identities declared inside one part.
struct PartDefinition {
std::string name;
std::vector<std::int64_t> node_source_labels;
std::vector<std::int64_t> element_source_labels;
std::vector<std::string> node_set_names;
std::vector<std::string> element_set_names;
SourceLocation location;
};
/// @brief Maps one source label to a stable internal entity index.
struct SourceIndexMapping {
std::int64_t source_label;
EntityIndex internal_index;
};
/// @brief Preserves one identity instance and its deterministic source
/// mappings.
struct InstanceDefinition {
std::string name;
std::string part_name;
std::vector<SourceIndexMapping> node_mappings;
std::vector<SourceIndexMapping> element_mappings;
SourceLocation location;
};
/// @brief Owns every parsed semantic record before immutable Domain
/// construction.
struct ModelDefinition {
std::filesystem::path source_path;
std::string source_content_identity;
std::string heading;
std::vector<Node> nodes;
std::vector<EulerBeam3DDefinition> elements;
std::vector<Mitc4ShellDefinition> shell_elements;
std::vector<LinearElasticMaterial> materials;
std::vector<GeneralBeamSection> sections;
std::vector<ShellSection> shell_sections;
std::vector<ShellNodeInitialFrame> shell_node_initial_frames;
std::vector<NodeSet> node_sets;
std::vector<ElementSet> element_sets;
std::vector<PartDefinition> parts;
std::vector<InstanceDefinition> instances;
std::vector<StaticStepDefinition> steps;
std::vector<Diagnostic> warnings;
};
} // namespace fesa
#endif // FESA_MODEL_MODEL_TYPES_H_
-165
View File
@@ -1,165 +0,0 @@
#pragma once
#include "fesa/core/diagnostic.hpp"
#include "fesa/core/source_identity.hpp"
#include <array>
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace fesa {
// Stable internal identities are vector positions assigned in declaration order.
using EntityIndex = std::uint32_t;
struct Node {
SourceEntityId sourceId;
std::array<double, 3> coordinates;
SourceLocation location;
};
struct LinearElasticMaterial {
std::string name;
double youngsModulus;
double poissonRatio;
SourceLocation location;
};
struct GeneralBeamSection {
std::string name;
double area;
double i11;
double i12;
double i22;
double torsionalConstant;
std::array<double, 3> firstAxis;
std::vector<std::array<double, 2>> sectionPoints;
SourceLocation location;
};
enum class ShellSourceElementType {
s4,
s4r
};
inline constexpr std::string_view kMitc4InternalFormulation{"FESA-MITC4"};
struct ShellSection {
std::string name;
double thickness;
EntityIndex materialIndex;
SourceLocation location;
};
struct Mitc4ShellDefinition {
SourceEntityId sourceId;
ShellSourceElementType sourceType;
std::array<EntityIndex, 4> nodeIndices;
EntityIndex materialIndex;
EntityIndex sectionIndex;
SourceLocation location;
};
struct ShellNodeInitialFrame {
EntityIndex nodeIndex;
std::array<double, 3> director;
std::array<double, 3> tangentA;
std::array<double, 3> tangentB;
};
struct EulerBeam3DDefinition {
SourceEntityId sourceId;
std::array<EntityIndex, 2> nodeIndices;
EntityIndex materialIndex;
EntityIndex sectionIndex;
SourceLocation location;
};
struct BoundaryCondition {
std::string target;
int firstDof;
int lastDof;
double value;
SourceLocation location;
};
struct NodalLoad {
std::string target;
int dof;
double magnitude;
SourceLocation location;
};
struct StaticStepDefinition {
std::string name;
std::vector<BoundaryCondition> boundaries;
std::vector<NodalLoad> loads;
double initialIncrement;
double timePeriod;
double minimumIncrement;
double maximumIncrement;
SourceLocation location;
};
struct NodeSet {
std::string name;
std::optional<std::string> instanceName;
std::vector<EntityIndex> nodeIndices;
SourceLocation location;
};
struct ElementSet {
std::string name;
std::optional<std::string> instanceName;
std::vector<EntityIndex> elementIndices;
SourceLocation location;
};
struct PartDefinition {
std::string name;
std::vector<std::int64_t> nodeSourceLabels;
std::vector<std::int64_t> elementSourceLabels;
std::vector<std::string> nodeSetNames;
std::vector<std::string> elementSetNames;
SourceLocation location;
};
struct SourceIndexMapping {
std::int64_t sourceLabel;
EntityIndex internalIndex;
};
struct InstanceDefinition {
std::string name;
std::string partName;
std::vector<SourceIndexMapping> nodeMappings;
std::vector<SourceIndexMapping> elementMappings;
SourceLocation location;
};
// This construction-boundary value owns every parsed semantic record before
// it is finalized into an immutable Domain.
struct ModelDefinition {
std::filesystem::path sourcePath;
std::string sourceContentIdentity;
std::string heading;
std::vector<Node> nodes;
std::vector<EulerBeam3DDefinition> elements;
std::vector<Mitc4ShellDefinition> shellElements;
std::vector<LinearElasticMaterial> materials;
std::vector<GeneralBeamSection> sections;
std::vector<ShellSection> shellSections;
std::vector<ShellNodeInitialFrame> shellNodeInitialFrames;
std::vector<NodeSet> nodeSets;
std::vector<ElementSet> elementSets;
std::vector<PartDefinition> parts;
std::vector<InstanceDefinition> instances;
std::vector<StaticStepDefinition> steps;
std::vector<Diagnostic> warnings;
};
} // namespace fesa
+53
View File
@@ -0,0 +1,53 @@
#ifndef FESA_MODEL_SHELL_GEOMETRY_H_
#define FESA_MODEL_SHELL_GEOMETRY_H_
#include <array>
#include <cstddef>
#include <vector>
#include "fesa/core/status.h"
#include "fesa/model/model_types.h"
namespace fesa {
/// @brief Classifies a mandatory shell-geometry validation location.
enum class ShellGeometryPointKind { kCenter, kStiffness, kTying, kRecovery };
/// @brief Identifies one deterministic shell-geometry validation point.
struct ShellGeometryValidationPoint {
ShellGeometryPointKind kind;
std::size_t location_index;
std::array<double, 3> natural_coordinates;
};
/// @brief Stores deterministic preprocessing data for one shell element.
struct ShellElementGeometryData {
EntityIndex element_index;
std::array<double, 3> normal_candidate;
double surface_area_weight;
};
/// @brief Owns preprocessed shell node frames and element geometry data.
struct ShellGeometry {
std::vector<ShellNodeInitialFrame> nodal_frames;
std::vector<ShellElementGeometryData> element_data;
};
/// @brief Returns the complete fixed validation-point inventory.
/// @note Ordering is center, stiffness, tying, then recovery identity.
const std::array<ShellGeometryValidationPoint, 17>&
ShellGeometryValidationPoints() noexcept;
/// @brief Builds deterministic nodal frames and validates shell geometry.
/// @param nodes Source nodes indexed by stable EntityIndex.
/// @param elements Shell definitions in stable source order.
/// @param sections Shell sections used for thickness validation.
/// @return Validated geometry or a structured model failure.
Result<ShellGeometry> PreprocessShellGeometry(
const std::vector<Node>& nodes,
const std::vector<Mitc4ShellDefinition>& elements,
const std::vector<ShellSection>& sections);
} // namespace fesa
#endif // FESA_MODEL_SHELL_GEOMETRY_H_
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
#include "fesa/model/model_types.hpp"
#include <array>
#include <cstddef>
#include <vector>
namespace fesa {
enum class ShellGeometryPointKind {
center,
stiffness,
tying,
recovery
};
struct ShellGeometryValidationPoint {
ShellGeometryPointKind kind;
std::size_t locationIndex;
std::array<double, 3> naturalCoordinates;
};
struct ShellElementGeometryData {
EntityIndex elementIndex;
std::array<double, 3> normalCandidate;
double surfaceAreaWeight;
};
struct ShellGeometry {
std::vector<ShellNodeInitialFrame> nodalFrames;
std::vector<ShellElementGeometryData> elementData;
};
const std::array<ShellGeometryValidationPoint, 17>&
shellGeometryValidationPoints() noexcept;
Result<ShellGeometry> preprocessShellGeometry(
const std::vector<Node>& nodes,
const std::vector<Mitc4ShellDefinition>& elements,
const std::vector<ShellSection>& sections);
} // namespace fesa
@@ -0,0 +1,81 @@
#ifndef FESA_MODEL_SOURCE_TARGET_RESOLVER_H_
#define FESA_MODEL_SOURCE_TARGET_RESOLVER_H_
#include <cstddef>
#include <string>
#include <vector>
#include "fesa/core/source_identity.h"
#include "fesa/core/status.h"
#include "fesa/model/model_types.h"
namespace fesa {
class Domain;
/// @brief Selects the independent source node or element namespace.
enum class SourceEntityKind { kNode, kElement };
/// @brief Maps one direct label or named-target membership to stable identity.
/// @note An empty target_name denotes a direct source-label entry.
struct SourceTargetIndexEntry {
SourceEntityKind entity_kind;
std::string instance_name;
std::string target_name;
SourceEntityId source_id;
EntityIndex entity_index;
std::size_t declaration_order;
};
/// @brief Owns immutable compact source-target lookup entries.
class SourceTargetIndex {
public:
/// @brief Takes ownership of compact entries from a validated model
/// candidate.
explicit SourceTargetIndex(std::vector<SourceTargetIndexEntry> entries);
/// @brief Builds compact entries from an immutable semantic Domain.
/// @return An owning index that preserves Domain declaration order.
static SourceTargetIndex FromDomain(const Domain& domain);
/// @brief Returns owned entries without exposing mutable index state.
const std::vector<SourceTargetIndexEntry>& Entries() const noexcept;
private:
std::vector<SourceTargetIndexEntry> entries_;
};
/// @brief Describes one source target lookup.
struct SourceTargetQuery {
SourceEntityKind entity_kind;
std::string instance_name;
std::string target_name_or_label;
};
/// @brief Preserves both external source identity and stable internal index.
struct ResolvedSourceTarget {
SourceEntityId source_id;
EntityIndex entity_index;
};
/// @brief Resolves source labels and named targets without owning model state.
/// @note The referenced SourceTargetIndex must outlive this resolver.
class SourceTargetResolver {
public:
/// @brief Creates a non-owning resolver over an immutable index.
/// @param index Index whose lifetime must exceed the resolver lifetime.
explicit SourceTargetResolver(const SourceTargetIndex& index) noexcept;
/// @brief Resolves one query in stable declaration order.
/// @return Stable source targets, or a deterministic input diagnostic for an
/// invalid, missing, duplicate, or ambiguous target.
Result<std::vector<ResolvedSourceTarget>> Resolve(
const SourceTargetQuery& query) const;
private:
const SourceTargetIndex* index_;
};
} // namespace fesa
#endif // FESA_MODEL_SOURCE_TARGET_RESOLVER_H_
@@ -0,0 +1,28 @@
#ifndef FESA_PROPERTIES_ELEMENT_PROPERTY_H_
#define FESA_PROPERTIES_ELEMENT_PROPERTY_H_
#include "fesa/core/source_identity.h"
namespace fesa {
/// @brief Identifies the supported concrete element-property semantics.
enum class ElementPropertyKind { kGeneralBeamSection, kShellSection };
/// @brief Provides stable identity for a Domain-owned element property.
class ElementProperty {
public:
virtual ~ElementProperty() = default;
/// @brief Returns the concrete property kind.
virtual ElementPropertyKind Kind() const noexcept = 0;
/// @brief Returns the stable source identity preserved for diagnostics.
virtual const SourceEntityId& SourceId() const noexcept = 0;
/// @brief Returns the input location that defined the property.
virtual const SourceLocation& Location() const noexcept = 0;
};
} // namespace fesa
#endif // FESA_PROPERTIES_ELEMENT_PROPERTY_H_
@@ -0,0 +1,83 @@
#ifndef FESA_PROPERTIES_GENERAL_BEAM_SECTION_H_
#define FESA_PROPERTIES_GENERAL_BEAM_SECTION_H_
#include <array>
#include <string>
#include <vector>
#include "fesa/core/status.h"
#include "fesa/properties/element_property.h"
namespace fesa {
/// @brief Stores the approved general Euler-beam section properties.
class GeneralBeamSection final : public ElementProperty {
public:
/// @brief Creates a validated general beam section.
/// @param first_axis Abaqus first section axis in global coordinates.
/// @param section_points Optional section-point coordinates `(x1,x2)`.
/// @return A section or a structured model failure.
static Result<GeneralBeamSection> Create(
SourceEntityId source_id, std::string name, double area, double i11,
double i12, double i22, double torsional_constant,
std::array<double, 3> first_axis,
std::vector<std::array<double, 2>> section_points,
SourceLocation location);
/// @brief Constructs an already validated parser-owned section record.
/// @note This compatibility seam preserves current consumers until Domain
/// polymorphic ownership is migrated.
GeneralBeamSection(std::string name, double area, double i11, double i12,
double i22, double torsional_constant,
std::array<double, 3> first_axis,
std::vector<std::array<double, 2>> section_points,
SourceLocation location);
ElementPropertyKind Kind() const noexcept override;
const SourceEntityId& SourceId() const noexcept override;
const SourceLocation& Location() const noexcept override;
/// @brief Returns the source section name.
const std::string& Name() const noexcept;
/// @brief Returns cross-sectional area.
double Area() const noexcept;
/// @brief Returns the first principal section inertia input.
double I11() const noexcept;
/// @brief Returns the cross-bending inertia, which is exactly zero in V0.
double I12() const noexcept;
/// @brief Returns the second principal section inertia input.
double I22() const noexcept;
/// @brief Returns the Saint-Venant torsional constant.
double TorsionalConstant() const noexcept;
/// @brief Returns the Abaqus first section axis in global coordinates.
const std::array<double, 3>& FirstAxis() const noexcept;
/// @brief Returns optional section points in source order.
const std::vector<std::array<double, 2>>& SectionPoints() const noexcept;
// Public storage preserves the current semantic-record API until Domain
// ownership migrates in the next approved Step.
std::string name;
double area;
double i11;
double i12;
double i22;
double torsional_constant;
std::array<double, 3> first_axis;
std::vector<std::array<double, 2>> section_points;
SourceLocation location;
private:
/// @brief Constructs a candidate whose fields have already been checked.
GeneralBeamSection(SourceEntityId source_id, std::string name, double area,
double i11, double i12, double i22,
double torsional_constant,
std::array<double, 3> first_axis,
std::vector<std::array<double, 2>> section_points,
SourceLocation location);
SourceEntityId source_id_;
};
} // namespace fesa
#endif // FESA_PROPERTIES_GENERAL_BEAM_SECTION_H_
+57
View File
@@ -0,0 +1,57 @@
#ifndef FESA_PROPERTIES_SHELL_SECTION_H_
#define FESA_PROPERTIES_SHELL_SECTION_H_
#include <string>
#include "fesa/core/status.h"
#include "fesa/properties/element_property.h"
namespace fesa {
/// @brief Stores a centered constant-thickness shell section assignment.
class ShellSection final : public ElementProperty {
public:
/// @brief Creates a validated shell section.
/// @param thickness Positive thickness in the active consistent unit system.
/// @param material_index Stable Domain material collection position.
/// @return A section or a structured model failure.
static Result<ShellSection> Create(SourceEntityId source_id, std::string name,
double thickness,
EntityIndex material_index,
SourceLocation location);
/// @brief Constructs an already validated parser-owned shell record.
/// @note This compatibility seam preserves current consumers until Domain
/// polymorphic ownership is migrated.
ShellSection(std::string name, double thickness, EntityIndex material_index,
SourceLocation location);
ElementPropertyKind Kind() const noexcept override;
const SourceEntityId& SourceId() const noexcept override;
const SourceLocation& Location() const noexcept override;
/// @brief Returns the source shell-section name.
const std::string& Name() const noexcept;
/// @brief Returns centered shell thickness.
double Thickness() const noexcept;
/// @brief Returns the stable Domain material collection position.
EntityIndex MaterialIndex() const noexcept;
// Public storage preserves the current semantic-record API until Domain
// ownership migrates in the next approved Step.
std::string name;
double thickness;
EntityIndex material_index;
SourceLocation location;
private:
/// @brief Constructs a candidate whose fields have already been checked.
ShellSection(SourceEntityId source_id, std::string name, double thickness,
EntityIndex material_index, SourceLocation location);
SourceEntityId source_id_;
};
} // namespace fesa
#endif // FESA_PROPERTIES_SHELL_SECTION_H_
+86
View File
@@ -0,0 +1,86 @@
#ifndef FESA_RESULTS_RESULT_RECORDS_H_
#define FESA_RESULTS_RESULT_RECORDS_H_
#include <array>
#include <cstddef>
#include <string>
#include <vector>
#include "fesa/core/source_identity.h"
namespace fesa {
/// @brief Identifies one deterministic result frame.
struct StepFrameIdentity {
std::string step_name;
std::size_t frame_index;
};
/// @brief Stores one beam endpoint action and section-resultant row.
struct EndpointResultRow {
EntityIndex element;
int endpoint;
SourceEntityId node;
std::array<double, 6> end_action;
std::array<double, 4> section_resultant;
};
/// @brief Stores one beam Gauss generalized result row.
struct GaussResultRow {
EntityIndex element;
int gauss_point;
std::array<double, 4> generalized_strain;
std::array<double, 4> generalized_resultant;
};
/// @brief Stores one beam axial-stress section-point row.
struct StressS11Row {
EntityIndex element;
int gauss_point;
std::size_t section_point;
double x1;
double x2;
double s11;
std::string source;
};
/// @brief Identifies one MITC4 midsurface integration location.
enum class ShellMidsurfaceLocation { kGp1, kGp2, kGp3, kGp4 };
/// @brief Identifies one through-thickness shell recovery position.
enum class ShellSectionPosition { kBottom, kMiddle, kTop };
/// @brief Stores one through-thickness shell stress row.
struct ShellSectionStressRow {
ShellSectionPosition position;
double zeta;
std::array<double, 3> components;
};
/// @brief Stores one MITC4 physical recovery row.
struct ShellResultRow {
EntityIndex element;
ShellMidsurfaceLocation location;
std::array<double, 2> natural_coordinates;
// Axis rows [e1,e2,e3], global-component columns.
std::array<std::array<double, 3>, 3> local_frame;
std::array<double, 8> generalized_strain;
std::array<double, 8> section_resultant;
// Fixed BOTTOM, MIDDLE, TOP order; components are [S11,S22,S12].
std::array<ShellSectionStressRow, 3> stress;
};
/// @brief Carries a complete shell result candidate for atomic validation.
struct ShellStateCandidate {
std::vector<ShellResultRow> rows;
double physical_strain_energy{0.0};
// [FORCE_1,FORCE_2,FORCE_3,MOMENT_1,MOMENT_2,MOMENT_3].
std::array<double, 6> equilibrium{};
// [FREE_RESIDUAL_NORMALIZED,FORCE_BALANCE_NORMALIZED,
// MOMENT_BALANCE_NORMALIZED].
std::array<double, 3> verification_metrics{};
};
} // namespace fesa
#endif // FESA_RESULTS_RESULT_RECORDS_H_
-83
View File
@@ -1,83 +0,0 @@
#pragma once
#include "fesa/model/model_types.hpp"
#include <array>
#include <cstddef>
#include <string>
#include <vector>
namespace fesa {
struct StepFrameIdentity {
std::string stepName;
std::size_t frameIndex;
};
struct EndpointResultRow {
EntityIndex element;
int endpoint;
SourceEntityId node;
std::array<double, 6> endAction;
std::array<double, 4> sectionResultant;
};
struct GaussResultRow {
EntityIndex element;
int gaussPoint;
std::array<double, 4> generalizedStrain;
std::array<double, 4> generalizedResultant;
};
struct StressS11Row {
EntityIndex element;
int gaussPoint;
std::size_t sectionPoint;
double x1;
double x2;
double s11;
std::string source;
};
enum class ShellMidsurfaceLocation {
gp1,
gp2,
gp3,
gp4
};
enum class ShellSectionPosition {
bottom,
middle,
top
};
struct ShellSectionStressRow {
ShellSectionPosition position;
double zeta;
std::array<double, 3> components;
};
struct ShellResultRow {
EntityIndex element;
ShellMidsurfaceLocation location;
std::array<double, 2> naturalCoordinates;
// Axis rows [e1,e2,e3], global-component columns.
std::array<std::array<double, 3>, 3> localFrame;
std::array<double, 8> generalizedStrain;
std::array<double, 8> sectionResultant;
// Fixed BOTTOM, MIDDLE, TOP order; components are [S11,S22,S12].
std::array<ShellSectionStressRow, 3> stress;
};
struct ShellStateCandidate {
std::vector<ShellResultRow> rows;
double physicalStrainEnergy{0.0};
// [FORCE_1,FORCE_2,FORCE_3,MOMENT_1,MOMENT_2,MOMENT_3].
std::array<double, 6> equilibrium{};
// [FREE_RESIDUAL_NORMALIZED,FORCE_BALANCE_NORMALIZED,
// MOMENT_BALANCE_NORMALIZED].
std::array<double, 3> verificationMetrics{};
};
} // namespace fesa
+63
View File
@@ -0,0 +1,63 @@
#ifndef FESA_RESULTS_RESULT_RECOVERY_H_
#define FESA_RESULTS_RESULT_RECOVERY_H_
#include <array>
#include <vector>
#include "fesa/analysis/analysis_model.h"
#include "fesa/analysis/analysis_state.h"
#include "fesa/core/status.h"
#include "fesa/elements/element.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/math/sparse_matrix.h"
namespace fesa {
/// @brief Stores one normalized positive-local-x node-station row.
struct NodeStationResultRow {
SourceEntityId node;
EntityIndex representative_element;
std::array<double, 4> section_resultant;
};
/// @brief Recovers full equilibrium and typed runtime element rows.
class ResultRecovery {
public:
/// @brief Aggregates runtime element bundles into one atomic state candidate.
/// @param model Non-owning active semantic model view.
/// @param elements Runtime elements in stable active source order.
/// @param dofs Owner of the matching full-space scatter and partition.
/// @param full_stiffness Assembled full-space stiffness matrix.
/// @param full_displacement Reconstructed full-space displacement candidate.
/// @param full_external_force Assembled full-space external-force candidate.
/// @param state Prior state replaced only after every bundle and global
/// evidence validates.
/// @return Success after atomic commit or a structured model failure.
static Status Recover(const AnalysisModel& model, const ElementView& elements,
const DofManager& dofs,
const SparseMatrix& full_stiffness,
const Vector& full_displacement,
const Vector& full_external_force,
AnalysisState& state);
/// @brief Builds and atomically commits a complete recovery candidate.
/// @note This compatibility facade creates runtime elements until the
/// procedure owns their lifetime directly.
/// @return Success after full residual K*d-F and all result rows validate.
static Status Recover(const AnalysisModel& model, const DofManager& dofs,
const SparseMatrix& full_stiffness,
AnalysisState& state);
/// @brief Normalizes eligible endpoint rows to source node stations.
/// @param component_tolerances Per-component interior-station tolerances.
/// @return Stable source-node rows or a structured identity/value failure.
static Result<std::vector<NodeStationResultRow>>
NormalizeSectionResultantsToNodeStations(
const AnalysisModel& model,
const std::vector<EndpointResultRow>& endpoint_rows,
const std::array<double, 4>& component_tolerances);
};
} // namespace fesa
#endif // FESA_RESULTS_RESULT_RECOVERY_H_
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#include "fesa/analysis/analysis_model.hpp"
#include "fesa/analysis/analysis_state.hpp"
#include "fesa/core/status.hpp"
#include "fesa/fem/dof_manager.hpp"
#include "fesa/math/sparse_matrix.hpp"
#include <array>
#include <vector>
namespace fesa {
struct NodeStationResultRow {
SourceEntityId node;
EntityIndex representativeElement;
std::array<double, 4> sectionResultant;
};
// Recovers full-space equilibrium and the active concrete element rows without
// exposing element or sparse-backend details to result consumers.
class ResultRecovery {
public:
static Status recover(const AnalysisModel& model,
const DofManager& dofs,
const SparseMatrix& fullStiffness,
AnalysisState& state);
static Result<std::vector<NodeStationResultRow>>
normalizeSectionResultantsToNodeStations(
const AnalysisModel& model,
const std::vector<EndpointResultRow>& endpointRows,
const std::array<double, 4>& componentTolerances);
};
} // namespace fesa
+28
View File
@@ -0,0 +1,28 @@
#ifndef FESA_RESULTS_RESULTS_WRITER_H_
#define FESA_RESULTS_RESULTS_WRITER_H_
#include <filesystem>
#include <vector>
#include "fesa/analysis/analysis_state.h"
#include "fesa/core/diagnostic.h"
#include "fesa/core/status.h"
#include "fesa/model/domain.h"
namespace fesa {
/// @brief Isolates authoritative result storage from the solver core.
class ResultsWriter {
public:
virtual ~ResultsWriter() = default;
/// @brief Writes one complete validated analysis state.
/// @return Success only after backend-specific finalization completes.
virtual Status Write(const std::filesystem::path& output_path,
const Domain& domain, const AnalysisState& state,
const std::vector<Diagnostic>& diagnostics) = 0;
};
} // namespace fesa
#endif // FESA_RESULTS_RESULTS_WRITER_H_
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include "fesa/analysis/analysis_state.hpp"
#include "fesa/core/diagnostic.hpp"
#include "fesa/core/status.hpp"
#include "fesa/model/domain.hpp"
#include <filesystem>
#include <vector>
namespace fesa {
// Keeps the solver core independent of the authoritative result-storage backend.
class ResultsWriter {
public:
virtual ~ResultsWriter() = default;
virtual Status write(
const std::filesystem::path& outputPath,
const Domain& domain,
const AnalysisState& state,
const std::vector<Diagnostic>& diagnostics) = 0;
};
} // namespace fesa
@@ -0,0 +1,30 @@
#ifndef FESA_SOLVERS_LINEAR_LINEAR_SOLVER_H_
#define FESA_SOLVERS_LINEAR_LINEAR_SOLVER_H_
#include "fesa/core/status.h"
namespace fesa {
class SparseMatrix;
class Vector;
/// @brief Separates reusable factorization from RHS substitution.
class LinearSolver {
public:
/// @brief Destroys a backend-neutral linear solver.
virtual ~LinearSolver() = default;
/// @brief Factorizes a validated free-equation matrix for reuse.
/// @return Success or a structured solver failure.
virtual Status Factorize(const SparseMatrix& matrix) = 0;
/// @brief Substitutes one right-hand side using retained factorization.
/// @param rhs Immutable right-hand side in free-equation order.
/// @param solution Updated only after successful finite substitution.
/// @return Success or a structured solver failure.
virtual Status Solve(const Vector& rhs, Vector& solution) const = 0;
};
} // namespace fesa
#endif // FESA_SOLVERS_LINEAR_LINEAR_SOLVER_H_
@@ -1,18 +0,0 @@
#pragma once
#include "fesa/core/status.hpp"
namespace fesa {
class SparseMatrix;
class Vector;
// Separates reusable matrix factorization from right-hand-side substitution.
class LinearSolver {
public:
virtual ~LinearSolver() = default;
virtual Status factorize(const SparseMatrix& matrix) = 0;
virtual Status solve(const Vector& rhs, Vector& solution) const = 0;
};
} // namespace fesa
@@ -0,0 +1,32 @@
#ifndef FESA_SOLVERS_LINEAR_MKL_PARDISO_SOLVER_H_
#define FESA_SOLVERS_LINEAR_MKL_PARDISO_SOLVER_H_
#include <memory>
#include "fesa/solvers/linear/linear_solver.h"
namespace fesa {
/// @brief Adapts retained oneMKL PARDISO state behind LinearSolver.
class MklPardisoSolver final : public LinearSolver {
public:
/// @brief Constructs an empty, unfactorized PARDISO adapter.
MklPardisoSolver();
/// @brief Releases retained PARDISO backend state.
~MklPardisoSolver() override;
/// @brief Validates and factorizes a symmetric free-equation matrix.
Status Factorize(const SparseMatrix& matrix) override;
/// @brief Substitutes one right-hand side without refactorization.
Status Solve(const Vector& rhs, Vector& solution) const override;
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace fesa
#endif // FESA_SOLVERS_LINEAR_MKL_PARDISO_SOLVER_H_
@@ -1,23 +0,0 @@
#pragma once
#include "fesa/solvers/linear/linear_solver.hpp"
#include <memory>
namespace fesa {
// Keeps every oneMKL type and the retained factorization in the private Impl.
class MklPardisoSolver final : public LinearSolver {
public:
MklPardisoSolver();
~MklPardisoSolver() override;
Status factorize(const SparseMatrix& matrix) override;
Status solve(const Vector& rhs, Vector& solution) const override;
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace fesa
@@ -0,0 +1,208 @@
{
"project": "FESA Structural Solver",
"phase": "cpp-object-oriented-modular-refactoring",
"steps": [
{
"step": 0,
"name": "coding-style-agent-contract",
"status": "completed",
"summary": "RED: P-AGENT-001 missing mandatory input assertion failed; GREEN/VERIFY: targeted pytest 13/13, clean-env full pytest 20/20, VS18 Debug build and CTest 144/144 passed; implementation-agent now requires docs/CODINGSTYLE.md before C++ Steps and production-only Doxygen.",
"started_at": "2026-08-16T02:57:41+0900",
"completed_at": "2026-08-16T03:10:54+0900"
},
{
"step": 1,
"name": "cpp-style-tooling",
"status": "completed",
"summary": "RED: P-STYLE-001 failed on missing .clang-format/.clang-tidy/Doxyfile; GREEN/VERIFY: targeted pytest 1/1, clean-env full pytest 21/21, clang-format/clang-tidy 22.1.8 and tidy config validation, VS18 Debug build, and CTest 144/144 passed; added style/lint/Doxygen configs, optional non-default fesa_docs target, and ignored .harness/doxygen output.",
"started_at": "2026-08-16T03:10:54+0900",
"completed_at": "2026-08-16T03:19:17+0900"
},
{
"step": 2,
"name": "architecture-boundaries",
"status": "completed",
"summary": "Documentation-only: ARCHITECTURE/ADR now fix Domain unique_ptr/stable-index ownership, ElementDefinition/runtime ElementFactory separation, generic element/load/constraint pipeline boundaries, minimal Analysis::Run with a private LinearStaticAnalysis lifecycle, and facade module splits while preserving B33/MITC4/HDF5/reference contracts; contract rg, git diff --check, MSVC Debug build, CTest discovery, and full CTest 144/144 passed.",
"started_at": "2026-08-16T03:19:17+0900",
"completed_at": "2026-08-16T03:27:17+0900"
},
{
"step": 3,
"name": "foundation-google-style",
"status": "completed",
"summary": "RED: test-first status.h/PascalCase contract failed with expected MSVC C1083 missing-header error; GREEN/VERIFY: VS18 x64 Debug focused build and CTest 22/22, mandatory clang-format, full build, CTest discovery 144, and full CTest 144/144 passed; foundation core/math/linear-solver/build-info APIs now use .h guards, PascalCase names, approved identifiers, production-only Doxygen, and mechanically updated consumers without numerical/reference changes.",
"started_at": "2026-08-16T03:27:17+0900",
"completed_at": "2026-08-16T04:26:14+0900"
},
{
"step": 4,
"name": "model-element-google-style",
"status": "completed",
"summary": "RED: MSVC direct /c of tests/unit/model/domain_test.cpp against the Step 3 HEAD include tree exited 2 with expected C1083 missing fesa/model/domain.h; GREEN/VERIFY: fesa_unit_tests build, focused CTest 37/37, clang-format 14 files, full MSVC Debug build, CTest discovery 144, and full CTest 144/144 passed; migrated the five model/element headers, APIs, members, enums, helpers, and repository callsites to .h guards and Google naming with production-only Doxygen while preserving B33/MITC4 numerical and reference contracts.",
"started_at": "2026-08-16T04:26:15+0900",
"completed_at": "2026-08-16T05:37:40+0900"
},
{
"step": 5,
"name": "solver-workflow-google-style",
"status": "completed",
"summary": "RED: current analysis_model_test.cpp against Step 4 HEAD headers failed with expected MSVC C1083 missing fesa/analysis/analysis_model.h; GREEN/VERIFY: VS18 x64 Debug focused build, focused CTest 57/57, clang-format 31/31, full build, CTest discovery 144, and full CTest 144/144 passed; solver workflow headers/APIs/callsites now use .h guards, PascalCase, and production-only Doxygen while preserving lifecycle, deterministic assembly, full residual, 0x0 Kff, and atomic state contracts.",
"started_at": "2026-08-16T05:37:40+0900",
"completed_at": "2026-08-16T06:20:08+0900"
},
{
"step": 6,
"name": "io-application-google-style",
"status": "completed",
"summary": "RED: current input_syntax_test.cpp against the Step 5 HEAD include tree failed with expected MSVC C1083 missing fesa/io/abaqus/input_reader.h; GREEN/VERIFY: focused targets built, I/O/HDF5/app/reference CTest 36/36 (Hdf5ResultsWriter 9/9, B33 reference 1/1, MITC4 S4 reference 2/2), clang-format 23/23, full MSVC Debug build, CTest discovery 144, and full CTest 144/144 passed; I/O, application, and test-only reference helpers now use .h guards, PascalCase, approved identifiers, and production-only Doxygen without schema, tolerance, or artifact changes.",
"started_at": "2026-08-16T06:20:09+0900",
"completed_at": "2026-08-16T06:59:50+0900"
},
{
"step": 7,
"name": "vector3-value-type",
"status": "completed",
"summary": "RED: fesa_unit_tests build exited 1 with expected MSVC C1083 missing fesa/math/vector3.h; GREEN/VERIFY: header-only Vector3 arithmetic, right-handed cross product, finite checks, and exact zero/nonfinite normalization rejection passed focused CTest 7/7, clang-format and clang-tidy exited 0, full VS18 MSVC Debug build succeeded, CTest discovered 151 tests, and full CTest 151/151 passed without migrating existing consumers.",
"started_at": "2026-08-16T06:59:50+0900",
"completed_at": "2026-08-16T07:08:42+0900"
},
{
"step": 8,
"name": "element-geometry-vector3",
"status": "completed",
"summary": "RED: C-DUP-001 typed array-to-Vector3 seam failed with expected MSVC C2440/C2676/C2039 errors; GREEN/VERIFY: exact rotated B33, MITC4 stiffness/recovery/patch, and warped-geometry regressions passed in focused CTest 44/44, the duplicate primitive scan found 0 definitions, clang-format passed, and the full MSVC Debug build and CTest 155/155 passed; element/model geometry now uses the shared Vector3 without changing public array or result contracts.",
"started_at": "2026-08-16T07:08:42+0900",
"completed_at": "2026-08-16T07:27:50+0900"
},
{
"step": 9,
"name": "result-io-vector3",
"status": "completed",
"summary": "RED: C-DUP-002 Vector3 characterizations fixed global-origin moment, large-finite mapper geometry, shell director/frame scalar serialization, finite rejection, stable dataset identity, and atomic replacement while the duplicate scan failed on the old local Dot/Norm helpers; GREEN/VERIFY: ResultRecovery/InpDomainMapping/Hdf5ResultsWriter passed 33/33, duplicate definitions were 0, clang-format passed, and the full VS18 MSVC Debug build and CTest 156/156 including B33/MITC4 references passed without schema, tolerance, or artifact changes.",
"started_at": "2026-08-16T07:27:51+0900",
"completed_at": "2026-08-16T07:40:16+0900"
},
{
"step": 10,
"name": "dense-blas-adapter",
"status": "completed",
"summary": "RED: fesa_unit_tests build exited 1 with expected C1083 missing math/dense_blas_internal.h; GREEN/VERIFY: private Result<MKL_INT> conversion and BLAS copy adapter passed focused CTest 4/4, helper scan showed one definition family with Vector/Matrix consumers, public vendor scan returned 0 matches, clang-format passed, full VS18 MSVC Debug build succeeded, and CTest passed 158/158.",
"started_at": "2026-08-16T07:40:16+0900",
"completed_at": "2026-08-16T07:49:57+0900"
},
{
"step": 11,
"name": "source-target-resolver",
"status": "completed",
"summary": "RED: temp HEAD+test-only fesa_unit_tests build exited 1 with expected C1083 missing fesa/core/ascii.h and fesa/model/source_target_resolver.h; corrective RED: after moving +1 to invalid labels, rebuilt Ascii.ParsesOnlyCompletePositiveBaseTenSourceLabels failed 1/1 because +1 still parsed; GREEN/VERIFY: removed plus-skip behavior, targeted Ascii 1/1 and focused CTest 44/44 passed, helper scan showed only shared ascii.cpp definition/internal call, clang-format passed, full Debug build passed, and CTest discovery/full 164/164 passed.",
"started_at": "2026-08-16T07:49:57+0900",
"completed_at": "2026-08-16T08:34:09+0900"
},
{
"step": 12,
"name": "material-property-hierarchy",
"status": "completed",
"summary": "RED: fesa_unit_tests failed with expected C1083 missing material/property base headers; GREEN/VERIFY: checked Material/ElementProperty hierarchies, virtual base ownership, stable source/internal identity, exact current fields, and invalid rejection passed focused CTest 11/11, clang-format, VS18 Debug full build, CTest discovery 170, and full CTest 170/170 without Domain ownership or numerical/reference changes.",
"started_at": "2026-08-16T08:34:09+0900",
"completed_at": "2026-08-16T08:48:18+0900"
},
{
"step": 13,
"name": "element-definition-domain",
"status": "completed",
"summary": "RED: fesa_unit_tests failed with the expected missing element_definition.h and ownership APIs; GREEN/VERIFY: Domain now uniquely owns mixed ElementDefinition, ElementProperty, and Material bases with stable const views and move-only lifetime, focused ownership/AnalysisModel/mapper CTest passed 20/20, clang-format passed, full VS18 Debug build passed, CTest discovered 172 tests, and full CTest passed 172/172 including B33/MITC4 references.",
"started_at": "2026-08-16T08:48:18+0900",
"completed_at": "2026-08-16T09:10:38+0900"
},
{
"step": 14,
"name": "runtime-element-factory",
"status": "completed",
"summary": "RED: fesa_unit_tests failed with expected MSVC C1083 missing element.h; GREEN/VERIFY: direct B33/MITC4 Element implementations and the fail-closed factory passed success, incompatible/unknown/missing-frame rejection, virtual destruction, owner-bounded stable ElementView, and base stiffness/recovery tests in focused CTest 35/35; dynamic_cast scan found 0 matches, clang-format passed, full VS18 Debug build passed, CTest discovered 176 tests, and full CTest passed 176/176 including B33/MITC4 references.",
"started_at": "2026-08-16T09:10:38+0900",
"completed_at": "2026-08-16T09:38:30+0900"
},
{
"step": 15,
"name": "generic-dof-manager",
"status": "completed",
"summary": "RED: fake Element/layout and invariant-owner tests exposed missing generic APIs and CSR missing/extra acceptance; GREEN/VERIFY: source-ordered generic scatter, atomic owner validation, exact scatter-derived CSR, focused CTest 21/21, zero concrete/helper branches, clang-format, full VS18 Debug build, discovery 178, and full CTest 178/178 passed.",
"started_at": "2026-08-16T09:38:30+0900",
"completed_at": "2026-08-16T10:07:01+0900"
},
{
"step": 16,
"name": "generic-sparse-assembler",
"status": "completed",
"summary": "RED: fake 2x2/3x3 runtime contribution build failed with the expected missing generic Assemble seam; GREEN/VERIFY: exact nonconsecutive-DOF CSR and repeated serial/TBB/reverse byte identity passed SparseAssembly 9/9, concrete branch count was 0, clang-format and the full VS18 Debug build passed, and CTest discovery/full passed 179/179 including B33/MITC4 references.",
"started_at": "2026-08-16T10:07:02+0900",
"completed_at": "2026-08-16T10:24:28+0900"
},
{
"step": 17,
"name": "generic-result-recovery",
"status": "completed",
"summary": "RED: C-RECOVERY-001 fake bundles failed with expected MSVC C2660 for the missing seven-argument generic Recover seam; GREEN/VERIFY: fake beam/shell bundles preserved stable row identity, end-action versus section-resultant signs, physical-only shell energy, and whole-state rollback, focused CTest passed 53/53, concrete branch count was 0, clang-format and the full VS18 Debug build passed, and CTest discovery/full passed 182/182 including B33/MITC4 references.",
"started_at": "2026-08-16T10:25:16+0900",
"completed_at": "2026-08-16T10:40:49+0900"
},
{
"step": 18,
"name": "load-hierarchy",
"status": "completed",
"summary": "RED: fake Load, generic assembler, and StepDefinition ownership tests failed with missing Load headers/APIs; GREEN/VERIFY: Domain-owned unique_ptr<Load> hierarchy and validation-before-candidate deterministic accumulation passed focused CTest 29/29, clang-format, full VS18 Debug build, discovery 186, and full CTest 186/186 including B33/MITC4 references.",
"started_at": "2026-08-16T10:40:49+0900",
"completed_at": "2026-08-16T10:56:57+0900"
},
{
"step": 19,
"name": "boundary-condition-policy",
"status": "completed",
"summary": "RED: fake BoundaryCondition and policy tests failed with expected C1083 missing boundary_condition.h; GREEN/VERIFY: Domain-owned prescribed definitions, stable partition/reconstruction including nonzero and 0x0 Kff, duplicate/conflict/nonfinite atomic rejection, focused CTest 29/29, clang-format, full MSVC Debug build, discovery, and full CTest 191/191 passed.",
"started_at": "2026-08-16T10:56:57+0900",
"completed_at": "2026-08-16T11:23:20+0900"
},
{
"step": 20,
"name": "analysis-hierarchy",
"status": "completed",
"summary": "RED: the new minimal Analysis contract test failed with expected MSVC C1083 missing fesa/analysis/analysis.h; GREEN/VERIFY: base-only Run dispatch, virtual destruction, Status propagation, procedure-owned generic runtime elements, factorize-before-invalid-load, exactly one factorization, 0x0 all-constrained solve, and invalid-recovery writer suppression passed focused CTest 12/12, format/boundary scans, full VS18 Debug build, CTest discovery 193, and full CTest 193/193 including B33/MITC4 references.",
"started_at": "2026-08-16T11:23:20+0900",
"completed_at": "2026-08-16T11:33:07+0900"
},
{
"step": 21,
"name": "domain-mapper-modules",
"status": "completed",
"summary": "RED: fesa_unit_tests failed with expected MSVC C1083 missing private domain_builder.h; GREEN/VERIFY: topology, material/property, step/load/BC candidate seams and exact diagnostics passed focused CTest 17/17, domain_mapper.cpp now orchestrates private Status stages with atomic DomainBuilder commit, clang-format and full VS18 Debug build passed, and CTest discovery/full passed 197/197 including B33/MITC4 references.",
"started_at": "2026-08-16T11:33:08+0900",
"completed_at": "2026-08-16T11:45:33+0900"
},
{
"step": 22,
"name": "result-recovery-modules",
"status": "completed",
"summary": "RED: temporary HEAD+test-only fesa_unit_tests build failed with expected MSVC C1083 for missing results/analysis_state_commit.h; GREEN/VERIFY: focused recovery/AnalysisState CTest passed 28/28, all five component files, facade wiring, and clang-format passed, and the full VS18 Debug build, discovery, and CTest 203/203 preserved B33/MITC4 identity, signs, order, global-origin equilibrium, physical-only energy, and later-invalid rollback.",
"started_at": "2026-08-16T11:45:34+0900",
"completed_at": "2026-08-16T12:52:10+0900"
},
{
"step": 23,
"name": "hdf5-writer-modules",
"status": "completed",
"summary": "RED: fesa_unit_tests failed with expected MSVC C1083 for missing private io/hdf5/hdf5_atomic_file.h; GREEN/VERIFY: private RAII, primitives, model, result, reopen self-check, and atomic-finalization components passed focused schema/atomicity CTest 13/13, component/facade scans and zero public HDF5 vendor leaks passed with clang-format, and full VS18 Debug build, discovery, and CTest passed 206/206 while preserving schema-v0 and existing-final behavior.",
"started_at": "2026-08-16T12:52:11+0900",
"completed_at": "2026-08-16T13:28:06+0900"
},
{
"step": 24,
"name": "final-quality-reference-gate",
"status": "completed",
"started_at": "2026-08-16T13:28:07+0900",
"summary": "Style gate passed with clean-env pytest 21/21, clang-format 163 files, clang-tidy 46 public headers, and 0 .hpp files; fresh VS18 Debug build passed, CTest discovery/full passed 206/206, approved B33/MITC4 reference suites passed 3/3 with generated results.h5 for cantilever-beam-b33 and mitc4-shell-s4-comparison, blocking comparisons passed with reference tree unchanged against 1e5758f, and reports were written to docs/cpp-object-oriented-modular-refactoring/{implementation-report.md,build-test.md,reference-comparison.md}.",
"completed_at": "2026-08-16T13:54:51+0900"
}
],
"created_at": "2026-08-16T02:57:41+0900",
"completed_at": "2026-08-16T13:54:51+0900"
}
@@ -0,0 +1,81 @@
# Step 0: Coding Style Agent Contract
## 담당 역할과 필수 스킬
- 담당 역할: `implementation-agent`
- 필수 스킬: `harness`, `fesa-cpp-msvc-tdd`
- 이 Step만 `RED -> observed failure -> minimal GREEN -> focused/full VERIFY`로 수행한다.
## 읽어야 할 파일
- `/.agents/skills/harness/SKILL.md`
- `/.codex/skills/fesa-cpp-msvc-tdd/SKILL.md`
- `/AGENTS.md`
- `/docs/CODINGSTYLE.md`
- `/docs/SOLVER_AGENT_DESIGN.md`
- `/docs/HARNESS.md`
- `/docs/HARNESS_WORKFLOW.md`
- `/.codex/hooks.json`
- `/.codex/agents/implementation-agent.toml`
- `/tests/test_agent_skill_workflow_contract.py`
- `/docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- `/phases/cpp-object-oriented-modular-refactoring/index.json`
- `/phases/cpp-object-oriented-modular-refactoring/step0.md`
필수 파일이 없거나 승인 설계와 충돌하면 현재 Step을 `blocked`로 기록하고 중단한다.
## 작업
Requirement `R-AGENT-001`만 구현한다.
1. `tests/test_agent_skill_workflow_contract.py``P-AGENT-001`을 먼저 추가한다.
2. Test는 `implementation-agent.toml`의 mandatory global input 구간이 literal path
`docs/CODINGSTYLE.md`를 직접 포함하고, C++ Step 시작 전에 읽도록 지시하는지 검증한다.
3. RED에서 현재 profile이 이 contract를 만족하지 않아 assertion failure가 발생함을 기록한다.
4. `/.codex/agents/implementation-agent.toml`에 다음 의미의 지시를 최소 추가한다.
```text
Before every C++ implementation Step, read docs/CODINGSTYLE.md as a mandatory global
input and apply it to production and test code. Doxygen coverage applies only to
production code.
```
5. 다른 agent profile, solver 문서, production C++, CMake는 수정하지 않는다.
## Acceptance Criteria
RED와 GREEN을 다음 명령으로 구분해 기록한다.
```powershell
uv run --with pytest python -m pytest -v -rs `
tests/test_agent_skill_workflow_contract.py
uv run --with pytest python -m pytest -v -rs
```
Repository Stop 검증과 동일한 C++ 회귀 확인:
```powershell
cmake -S . -B .harness/build -G "Visual Studio 18 2026" -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
```
## 검증 및 상태 갱신
- RED assertion, GREEN targeted/full pytest, C++ regression 결과를 summary에 남긴다.
- 성공 시 현재 Step만 `completed`와 한 줄 `summary`로 갱신한다.
- 실패면 `error`/`error_message`, 사용자 개입이 필요하면
`blocked`/`blocked_reason`을 기록한다.
- timestamp, retry, commit, 다음 Step 선택은 Executor 소유다.
## 금지사항
- C++ 또는 CMake를 수정하지 마라. 이유: 이 Step은 agent contract만 소유한다.
- 다른 agent profile에 동일 문구를 일괄 추가하지 마라. 이유: 승인 범위를 넓힌다.
- 직접 commit하거나 hook script를 수동 실행하지 마라. 이유: Executor와 hook의 소유권이다.
- `scripts/execute.py`를 호출하지 마라. 이유: 현재 독립 Step을 재귀 실행하면 안 된다.
@@ -0,0 +1,76 @@
# Step 1: C++ Style Tooling
## 담당 역할과 필수 스킬
- 담당 역할: `implementation-agent`
- 필수 스킬: `harness`, `fesa-cpp-msvc-tdd`
- 이 Step만 `RED -> observed failure -> minimal GREEN -> focused/full VERIFY`로 수행한다.
## 읽어야 할 파일
- `/.agents/skills/harness/SKILL.md`
- `/.codex/skills/fesa-cpp-msvc-tdd/SKILL.md`
- `/AGENTS.md`
- `/docs/CODINGSTYLE.md`
- `/docs/superpowers/specs/2026-08-16-cpp-object-oriented-modular-refactoring-design.md`
- `/docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- `/CMakeLists.txt`
- `/.gitignore`
- `/tests/test_agent_skill_workflow_contract.py`
- `/phases/cpp-object-oriented-modular-refactoring/index.json`
- `/phases/cpp-object-oriented-modular-refactoring/step1.md`
- Step 0이 수정한 `/.codex/agents/implementation-agent.toml`
## 작업
Requirements `R-STYLE-001``R-DOC-001`의 repository tooling만 구현한다.
1. `/tests/test_cpp_policy_contract.py``P-STYLE-001`을 먼저 작성한다. 다음 literal
contract를 검사한다.
- `.clang-format`: `BasedOnStyle: Google`, `IndentWidth: 2`, `ColumnLimit: 80`.
- `.clang-tidy`: C++17-compatible checks와 FESA PascalCase/snake_case identifier rules.
- `Doxyfile`: `INPUT = include src`, tests 제외, `WARN_AS_ERROR = YES`, generated HTML은
source control 밖의 build 경로.
- Root CMake의 optional `fesa_docs` target은 Doxygen가 발견될 때만 등록되고 default
configure에는 Doxygen를 요구하지 않는다.
2. RED에서 설정 파일 부재로 test failure를 확인한다.
3. `/.clang-format`, `/.clang-tidy`, `/Doxyfile`을 추가한다.
4. `/CMakeLists.txt``find_package(Doxygen QUIET)`와 발견 시에만 등록되는
`fesa_docs` custom target을 추가한다. Default build dependency에 넣지 않는다.
5. Generated Doxygen HTML 경로가 ignore되지 않았다면 `/.gitignore`에 정확한 output
directory만 추가한다.
6. Doxygen executable은 실행하지 않는다. 사용자가 문서 생성을 추후 수행하기로 했다.
## Acceptance Criteria
```powershell
uv run --with pytest python -m pytest -v -rs tests/test_cpp_policy_contract.py
uv run --with pytest python -m pytest -v -rs
& "C:/Program Files/LLVM/bin/clang-format.exe" --version
& "C:/Program Files/LLVM/bin/clang-tidy.exe" --version
& "C:/Program Files/LLVM/bin/clang-tidy.exe" --verify-config
cmake -S . -B .harness/build -G "Visual Studio 18 2026" -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
```
Expected GREEN에는 Doxygen 실행이나 generated HTML이 포함되지 않는다.
## 검증 및 상태 갱신
- RED policy failure와 GREEN pytest/tool version/config/full CTest를 summary에 남긴다.
- 성공 시 현재 Step만 `completed`로 갱신하고 생성한 설정 파일을 summary에 기록한다.
- 환경에 두 LLVM executable이 없으면 `blocked`와 정확한 경로를 기록한다.
- timestamp, retry, commit, advancement는 Executor 소유다.
## 금지사항
- 기존 C++를 format하지 마라. 이유: 기계적 migration은 Step 36 소유다.
- Doxygen를 실행하거나 generated HTML을 commit하지 마라. 이유: 사용자 결정으로 생성은 연기됐다.
- Doxygen를 default build 필수 dependency로 만들지 마라. 이유: 현재 blocking gate가 아니다.
- 직접 commit하거나 hook script를 수동 실행하지 마라.
@@ -0,0 +1,82 @@
# Step 10: Dense BLAS Adapter
## 담당 역할과 필수 스킬
- 담당 역할: `implementation-agent`
- 필수 스킬: `harness`, `fesa-cpp-msvc-tdd`
- 이 Step만 `RED -> observed failure -> minimal GREEN -> focused/full VERIFY`로 수행한다.
## 읽어야 할 파일
- `/AGENTS.md`
- `/docs/CODINGSTYLE.md`
- `/docs/ARCHITECTURE.md`
- `/docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- `/include/fesa/math/vector.h`, `/src/fesa/math/vector.cpp`
- `/include/fesa/math/matrix.h`, `/src/fesa/math/matrix.cpp`
- `/tests/unit/math/vector_test.cpp`, `/tests/unit/math/matrix_test.cpp`
- `/src/fesa/CMakeLists.txt`, `/tests/CMakeLists.txt`
- `/phases/cpp-object-oriented-modular-refactoring/index.json`
- `/phases/cpp-object-oriented-modular-refactoring/step10.md`
## 작업
Requirement `R-DUP-002`의 dense-BLAS duplication만 제거한다.
1. `/tests/unit/math/dense_blas_internal_test.cpp`를 먼저 추가하고 `C-DUP-003`을 작성한다.
Test는 zero/normal/overflow length conversion과 zero/nonzero contiguous copy behavior를
검증한다.
2. Missing internal adapter include/symbol로 RED compile failure를 기록한다.
3. Private candidate module은 `/src/fesa/math/dense_blas_internal.h`
`/src/fesa/math/dense_blas_internal.cpp`다.
Tests에 private include directory가 필요하면 `fesa_unit_tests`에만
`${PROJECT_SOURCE_DIR}/src/fesa`를 PRIVATE로 추가한다.
4. Candidate functions:
```cpp
namespace fesa::dense_blas_internal {
Result<MKL_INT> ToMklSize(std::size_t size);
void CopyValues(const double* source, std::size_t size, double* destination);
} // namespace fesa::dense_blas_internal
```
5. `MKL_INT` and MKL includes are permitted only in this private implementation boundary and
existing backend `.cpp`; no file under `/include/fesa/` may expose them.
6. Matrix/Vector의 duplicated conversion/copy helpers를 adapter call로 교체한다. Exception,
Status/failure meaning, row-major layout and BLAS call order stay unchanged.
## Acceptance Criteria
```powershell
cmake --build .harness/build --config Debug --target fesa_unit_tests
ctest --test-dir .harness/build -C Debug `
-R "DenseMath|DenseBlasInternal" --output-on-failure
rg -n "ToMklSize|CopyValues" src/fesa/math
rg -n "MKL_INT|mkl\.h" include/fesa
& "C:/Program Files/LLVM/bin/clang-format.exe" --dry-run --Werror `
src/fesa/math/dense_blas_internal.h src/fesa/math/dense_blas_internal.cpp `
src/fesa/math/vector.cpp src/fesa/math/matrix.cpp `
tests/unit/math/dense_blas_internal_test.cpp
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
```
The public-header vendor scan must return no matches. The private helper scan must show one
definition family and the two intended consumers only.
## 검증 및 상태 갱신
- RED missing seam, focused conversion/copy behavior, scans and full CTest를 summary에 기록한다.
- Public vendor type leak or behavior regression이면 `error`로 기록한다.
- 성공 시 현재 Step만 `completed`로 갱신한다.
- timestamp, retry, commit, advancement는 Executor 소유다.
## 금지사항
- Public math API에 MKL type을 추가하지 마라.
- Matrix layout 또는 BLAS operation order를 바꾸지 마라.
- SIMD, allocation or runtime performance optimization을 하지 마라.
- 직접 commit하거나 hook script를 수동 실행하지 마라.
@@ -0,0 +1,131 @@
# Step 11: Source Target Resolver
## 담당 역할과 필수 스킬
- 담당 역할: `implementation-agent`
- 필수 스킬: `harness`, `fesa-cpp-msvc-tdd`
- 이 Step만 `RED -> observed failure -> minimal GREEN -> focused/full VERIFY`로 수행한다.
## 읽어야 할 파일
- `/AGENTS.md`
- `/docs/CODINGSTYLE.md`
- `/docs/ARCHITECTURE.md`
- `/docs/ADR.md`
- `/docs/linear-static-3d-euler-beam/io.md`
- `/docs/linear-static-mitc4-shell/io.md`
- `/docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- `/include/fesa/core/source_identity.h`
- `/include/fesa/model/domain.h`, `/src/fesa/model/domain.cpp`
- `/src/fesa/io/abaqus/domain_mapper.cpp`
- `/src/fesa/fem/dof_manager.cpp`
- `/src/fesa/assembly/load_assembler.cpp`
- `/src/fesa/results/result_recovery.cpp`
- matching mapper/DOF/load/recovery tests
- `/src/fesa/CMakeLists.txt`, `/tests/CMakeLists.txt`
- `/phases/cpp-object-oriented-modular-refactoring/index.json`
- `/phases/cpp-object-oriented-modular-refactoring/step11.md`
## 작업
Requirement `R-DUP-002`의 ASCII/source-target owner를 구현한다.
1. `/tests/unit/core/ascii_test.cpp`
`/tests/unit/model/source_target_resolver_test.cpp`를 먼저 추가한다.
2. `C-DUP-004` tests cover ASCII-only lower/equality, positive source-label parsing,
separate node/element set namespaces, instance identity, declaration-order expansion,
duplicate/missing/ambiguous/nonpositive rejection and deterministic diagnostic order.
3. Missing headers/symbols로 RED compile failure를 기록한다.
4. Create `/include/fesa/core/ascii.h`, `/src/fesa/core/ascii.cpp`,
`/include/fesa/model/source_target_resolver.h`, and
`/src/fesa/model/source_target_resolver.cpp`; register both sources and both tests in CMake.
5. Candidate core functions:
```cpp
char AsciiLower(char value) noexcept;
bool AsciiCaseInsensitiveEquals(std::string_view lhs,
std::string_view rhs) noexcept;
Result<std::int64_t> ParsePositiveSourceLabel(std::string_view text);
```
6. Candidate model interface:
```cpp
enum class SourceEntityKind { kNode, kElement };
struct SourceTargetIndexEntry {
SourceEntityKind entity_kind;
std::string instance_name;
std::string target_name;
SourceEntityId source_id;
EntityIndex entity_index;
std::size_t declaration_order;
};
class SourceTargetIndex {
public:
explicit SourceTargetIndex(std::vector<SourceTargetIndexEntry> entries);
const std::vector<SourceTargetIndexEntry>& Entries() const noexcept;
};
struct SourceTargetQuery {
SourceEntityKind entity_kind;
std::string instance_name;
std::string target_name_or_label;
};
struct ResolvedSourceTarget {
SourceEntityId source_id;
EntityIndex entity_index;
};
class SourceTargetResolver {
public:
explicit SourceTargetResolver(const SourceTargetIndex& index) noexcept;
Result<std::vector<ResolvedSourceTarget>> Resolve(
const SourceTargetQuery& query) const;
};
```
7. `SourceTargetIndex` is an immutable index built from validated candidate or Domain semantic
records. It owns its compact entries; the resolver stores a non-owning reference, so the index
lifetime must outlive the resolver and Doxygen must state it.
8. Replace repeated `AsciiLower`, equal-name, positive-integer, and same-meaning source resolution
helpers only. Preserve each owner-specific diagnostic category/source location.
## Acceptance Criteria
```powershell
cmake --build .harness/build --config Debug --target fesa_unit_tests
ctest --test-dir .harness/build -C Debug `
-R "Ascii|SourceTargetResolver|InpDomainMapping|DofManager|LoadAssembly|ResultRecovery" `
--output-on-failure
rg -n "AsciiLower|EqualName|TryPositiveInteger" src/fesa
& "C:/Program Files/LLVM/bin/clang-format.exe" --dry-run --Werror `
include/fesa/core/ascii.h src/fesa/core/ascii.cpp `
include/fesa/model/source_target_resolver.h `
src/fesa/model/source_target_resolver.cpp `
tests/unit/core/ascii_test.cpp `
tests/unit/model/source_target_resolver_test.cpp
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
```
The helper scan may show only the shared definitions and intentional calls, not repeated local
definitions. Full diagnostics and stable order tests must pass.
## 검증 및 상태 갱신
- RED, focused resolution cases, duplicate scan and full CTest를 summary에 기록한다.
- Identity/diagnostic order regression이면 `error`; missing upstream identity contract이면
`blocked`로 기록한다.
- 성공 시 현재 Step만 `completed`로 갱신한다.
- timestamp, retry, commit, advancement는 Executor 소유다.
## 금지사항
- Unicode case folding이나 locale behavior를 추가하지 마라. 이유: input contract is ASCII.
- Node와 element set namespace를 합치지 마라.
- Domain mapper를 responsibility files로 split하지 마라. 이유: Step 21 소유다.
- 직접 commit하거나 hook script를 수동 실행하지 마라.
@@ -0,0 +1,106 @@
# Step 12: Material and Element Property Hierarchy
## 담당 역할과 필수 스킬
- 담당 역할: `implementation-agent`
- 필수 스킬: `harness`, `fesa-cpp-msvc-tdd`
- 이 Step만 `RED -> observed failure -> minimal GREEN -> focused/full VERIFY`로 수행한다.
## 읽어야 할 파일
- `/AGENTS.md`
- `/docs/CODINGSTYLE.md`
- `/docs/ARCHITECTURE.md`
- `/docs/ADR.md`
- `/docs/superpowers/specs/2026-08-16-cpp-object-oriented-modular-refactoring-design.md`
- `/docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- `/include/fesa/model/model_types.h`
- `/include/fesa/model/domain.h`, `/src/fesa/model/domain.cpp`
- `/tests/unit/model/model_types_test.cpp`, `/tests/unit/model/domain_test.cpp`
- `/src/fesa/CMakeLists.txt`, `/tests/CMakeLists.txt`
- `/phases/cpp-object-oriented-modular-refactoring/index.json`
- `/phases/cpp-object-oriented-modular-refactoring/step12.md`
## 작업
Requirement `R-MODEL-001`의 material/property type system만 구현한다. Domain polymorphic
ownership migration은 Step 13에서 수행한다.
1. `/tests/unit/materials/material_test.cpp`
`/tests/unit/properties/element_property_test.cpp`를 먼저 만든다.
2. `C-MODEL-001` tests create each concrete through `std::unique_ptr<Base>`, verify virtual
destruction, source/internal identity, concrete kind, exact current fields and invalid input
rejection. Missing base headers cause RED compile failure.
3. Candidate interfaces:
```cpp
enum class MaterialKind { kIsotropicLinearElastic };
class Material {
public:
virtual ~Material() = default;
virtual MaterialKind Kind() const noexcept = 0;
virtual const SourceEntityId& SourceId() const noexcept = 0;
};
class IsotropicLinearElasticMaterial final : public Material {
public:
double YoungsModulus() const noexcept;
double PoissonsRatio() const noexcept;
};
enum class ElementPropertyKind { kGeneralBeamSection, kShellSection };
class ElementProperty {
public:
virtual ~ElementProperty() = default;
virtual ElementPropertyKind Kind() const noexcept = 0;
virtual const SourceEntityId& SourceId() const noexcept = 0;
};
```
4. Create these exact production files and register their `.cpp` sources in CMake:
- `/include/fesa/materials/material.h`
- `/include/fesa/materials/isotropic_linear_elastic_material.h`
- `/src/fesa/materials/isotropic_linear_elastic_material.cpp`
- `/include/fesa/properties/element_property.h`
- `/include/fesa/properties/general_beam_section.h`
- `/include/fesa/properties/shell_section.h`
- `/src/fesa/properties/general_beam_section.cpp`
- `/src/fesa/properties/shell_section.cpp`
5. Add concrete `GeneralBeamSection` and `ShellSection` classes preserving their current validated
fields, units, source identity and optional frame meaning.
6. Move concrete record responsibility out of unrelated `model_types.h` only as required to avoid
duplicate definitions; update current compile consumers minimally.
7. No base contains density, plastic state, anisotropic tensor, thickness, area or no-op virtual
methods that are not common to every concrete.
## Acceptance Criteria
```powershell
cmake --build .harness/build --config Debug --target fesa_unit_tests
ctest --test-dir .harness/build -C Debug `
-R "Material|ElementProperty|DomainModel" --output-on-failure
& "C:/Program Files/LLVM/bin/clang-format.exe" --dry-run --Werror `
(rg --files include/fesa/materials include/fesa/properties -g "*.h") `
(rg --files src/fesa/materials src/fesa/properties -g "*.cpp") `
tests/unit/materials/material_test.cpp `
tests/unit/properties/element_property_test.cpp
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
```
## 검증 및 상태 갱신
- RED missing interfaces, concrete validation, virtual ownership and full CTest를 summary에 기록한다.
- Any future-only field/interface or current property regression is an `error`.
- 성공 시 현재 Step만 `completed`로 갱신한다.
- timestamp, retry, commit, advancement는 Executor 소유다.
## 금지사항
- Density/plasticity/anisotropy APIs를 추가하지 마라. 이유: 승인된 현재 behavior가 아니다.
- Domain을 `shared_ptr` repository로 바꾸지 마라.
- ElementFactory 또는 numerical kernel을 수정하지 마라. 이유: Step 14 소유다.
- 직접 commit하거나 hook script를 수동 실행하지 마라.
@@ -0,0 +1,93 @@
# Step 13: Element Definition and Domain Ownership
## 담당 역할과 필수 스킬
- 담당 역할: `implementation-agent`
- 필수 스킬: `harness`, `fesa-cpp-msvc-tdd`
- 이 Step만 `RED -> observed failure -> minimal GREEN -> focused/full VERIFY`로 수행한다.
## 읽어야 할 파일
- `/AGENTS.md`
- `/docs/CODINGSTYLE.md`
- `/docs/ARCHITECTURE.md`
- `/docs/ADR.md`
- `/docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- Step 11 `source_target_resolver` files
- Step 12 material/property headers and tests
- `/include/fesa/model/model_types.h`
- `/include/fesa/model/domain.h`, `/src/fesa/model/domain.cpp`
- `/include/fesa/analysis/analysis_model.h`, `/src/fesa/analysis/analysis_model.cpp`
- `/src/fesa/io/abaqus/domain_mapper.cpp`
- matching Domain, AnalysisModel and DomainMapper tests
- `/src/fesa/CMakeLists.txt`, `/tests/CMakeLists.txt`
- `/phases/cpp-object-oriented-modular-refactoring/index.json`
- `/phases/cpp-object-oriented-modular-refactoring/step13.md`
## 작업
Requirements `R-MODEL-001` and `R-ELEMENT-001`의 semantic ownership을 구현한다.
1. Domain/AnalysisModel/mapper tests에 `C-MODEL-002`를 먼저 추가한다. Tests verify mixed
B33/MITC4 definition ownership through base references, vector-position `EntityIndex`, insertion
order, const access, move-only Domain, and Domain-outlives-AnalysisModel contract.
2. Missing base/ownership API로 RED compile failure를 기록한다.
3. Candidate interface:
```cpp
enum class ElementDefinitionKind { kEulerBeam3D, kMitc4Shell };
class ElementDefinition {
public:
virtual ~ElementDefinition() = default;
virtual ElementDefinitionKind Kind() const noexcept = 0;
virtual const SourceEntityId& SourceId() const noexcept = 0;
virtual std::string_view SourceElementType() const noexcept = 0;
virtual const std::vector<EntityIndex>& NodeIndices() const noexcept = 0;
virtual EntityIndex PropertyIndex() const noexcept = 0;
};
```
4. Create `/include/fesa/elements/element_definition.h`. Keep concrete definitions in the
current `/include/fesa/elements/euler_beam_3d.h` and
`/include/fesa/elements/mitc4_shell.h` ownership modules rather than creating a second record
location.
5. Existing `EulerBeam3DDefinition` and `Mitc4ShellDefinition` become final concrete definitions;
preserve B33 vs S4/S4R source type and FESA internal identity.
6. Domain owns `std::vector<std::unique_ptr<ElementDefinition>>`,
`std::vector<std::unique_ptr<ElementProperty>>`, and
`std::vector<std::unique_ptr<Material>>`. Accessors return const base references or const
collection views; Domain copy is disabled and move is allowed if existing construction needs it.
7. Mapper builds a complete validated candidate before moving it into Domain. Failed mapping must
not leave a partially visible Domain.
8. AnalysisModel remains a non-owning active index/reference view and never copies Domain.
## Acceptance Criteria
```powershell
cmake --build .harness/build --config Debug --target fesa_unit_tests
ctest --test-dir .harness/build -C Debug `
-R "ElementDefinition|DomainModel|AnalysisModel|InpDomainMapping" --output-on-failure
& "C:/Program Files/LLVM/bin/clang-format.exe" --dry-run --Werror `
include/fesa/elements/element_definition.h `
include/fesa/model/domain.h src/fesa/model/domain.cpp `
tests/unit/model/domain_test.cpp tests/unit/analysis/analysis_model_test.cpp `
tests/unit/io/abaqus/domain_mapper_test.cpp
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
```
## 검증 및 상태 갱신
- RED, stable ownership/order/lifetime focused tests, mapper and full CTest를 summary에 기록한다.
- Index/order/lifetime regression is `error`; missing ownership decision is `blocked`.
- 성공 시 현재 Step만 `completed`로 갱신한다.
- timestamp, retry, commit, advancement는 Executor 소유다.
## 금지사항
- `Clone()` or `shared_ptr`를 추가하지 마라. 이유: Domain has unique immutable ownership.
- Node/Element에 equation id를 저장하지 마라.
- Runtime Element methods를 semantic definition에 넣지 마라.
- 직접 commit하거나 hook script를 수동 실행하지 마라.
@@ -0,0 +1,130 @@
# Step 14: Runtime Element and Factory
## 담당 역할과 필수 스킬
- 담당 역할: `implementation-agent`
- 필수 스킬: `harness`, `fesa-cpp-msvc-tdd`
- 이 Step만 `RED -> observed failure -> minimal GREEN -> focused/full VERIFY`로 수행한다.
## 읽어야 할 파일
- `/AGENTS.md`
- `/docs/CODINGSTYLE.md`
- `/docs/ARCHITECTURE.md`
- `/docs/ADR.md`
- `/docs/linear-static-3d-euler-beam/formulation.md`
- `/docs/linear-static-mitc4-shell/formulation.md`
- `/docs/cpp-object-oriented-modular-refactoring/implementation-plan.md`
- Step 12 material/property hierarchy files
- Step 13 element definition and Domain files
- `/include/fesa/elements/euler_beam_3d.h`, `/src/fesa/elements/euler_beam_3d.cpp`
- `/include/fesa/elements/mitc4_shell.h`, `/src/fesa/elements/mitc4_shell.cpp`
- element/model/result record tests
- `/src/fesa/CMakeLists.txt`, `/tests/CMakeLists.txt`
- `/phases/cpp-object-oriented-modular-refactoring/index.json`
- `/phases/cpp-object-oriented-modular-refactoring/step14.md`
## 작업
Requirement `R-ELEMENT-001`의 numerical runtime boundary를 구현한다.
1. `/tests/unit/elements/element_factory_test.cpp`를 먼저 추가한다. `C-ELEMENT-001` covers
base-pointer virtual destruction, B33 and MITC4 factory success, stable DOF layout, stiffness
and recovery through base, unknown/mismatched property/material/null rejection.
2. Missing `element.h` and `element_factory.h` cause RED compile failure.
3. Create `/include/fesa/elements/element.h`,
`/include/fesa/elements/element_factory.h`, and
`/src/fesa/elements/element_factory.cpp`; register the source and factory test in CMake.
4. Candidate interfaces:
```cpp
struct ElementDofLayout {
SourceEntityId source_id;
std::vector<EntityIndex> node_indices;
std::vector<DofComponent> components_per_node;
};
struct ElementStiffnessContribution {
ElementDofLayout layout;
Matrix values;
};
struct BeamElementResultRows {
std::vector<EndpointResultRow> endpoint_rows;
std::vector<GaussResultRow> gauss_rows;
std::vector<StressS11Row> stress_rows;
};
struct ShellElementResultRows {
std::vector<ShellResultRow> rows;
double physical_strain_energy;
};
using ElementResultPayload =
std::variant<BeamElementResultRows, ShellElementResultRows>;
struct ElementResultBundle {
SourceEntityId source_id;
ElementResultPayload payload;
};
class Element {
public:
virtual ~Element() = default;
virtual const ElementDofLayout& DofLayout() const noexcept = 0;
virtual Result<ElementStiffnessContribution> ComputeStiffness() const = 0;
virtual Result<ElementResultBundle> Recover(
const Vector& full_displacement) const = 0;
};
using ElementView = std::vector<std::reference_wrapper<const Element>>;
class ElementFactory {
public:
Result<std::unique_ptr<Element>> Create(
const ElementDefinition& definition,
const Domain& domain) const;
};
```
5. The linear-static candidate owns `std::vector<std::unique_ptr<Element>>` and builds an
`ElementView` whose lifetime is bounded by that owner. Carrier type locations may be adjusted
to avoid circular public dependencies, but semantics and names must stay consistent for
Steps 1517.
6. Factory centralizes explicit definition/property/material kind compatibility, bounds checks,
and structured diagnostic. It may use a checked kind discriminator and concrete access after
validation; downstream consumers must not downcast.
7. Existing B33/MITC4 numerical kernels implement `Element` without changing formulation or adding
fields meaningless to the other element.
## Acceptance Criteria
```powershell
cmake --build .harness/build --config Debug --target fesa_unit_tests
ctest --test-dir .harness/build -C Debug `
-R "ElementFactory|EulerBeam3D|Mitc4Shell" --output-on-failure
rg -n "dynamic_cast" src/fesa include/fesa
& "C:/Program Files/LLVM/bin/clang-format.exe" --dry-run --Werror `
include/fesa/elements/element.h include/fesa/elements/element_factory.h `
src/fesa/elements/element_factory.cpp tests/unit/elements/element_factory_test.cpp
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
```
The downcast scan must show no newly scattered consumer downcasts. If the centralized factory uses
a checked cast, document the preceding kind validation and keep it in the factory only.
## 검증 및 상태 갱신
- RED, success/rejection cases, base stiffness/recovery, downcast scan and full CTest를 summary에 기록한다.
- Formulation or result identity changes are `error`.
- 성공 시 현재 Step만 `completed`로 갱신한다.
- timestamp, retry, commit, advancement는 Executor 소유다.
## 금지사항
- Semantic definition and runtime kernel을 one class로 합치지 마라.
- Future element registry/global static registration을 추가하지 마라.
- Giant common result record with meaningless optional fields를 만들지 마라.
- 직접 commit하거나 hook script를 수동 실행하지 마라.

Some files were not shown because too many files have changed in this diff Show More