Compare commits
56 Commits
405dc2a47d
...
c80af74da9
| Author | SHA1 | Date | |
|---|---|---|---|
| c80af74da9 | |||
| 49918f60b9 | |||
| 3b30072e00 | |||
| 2e634578cc | |||
| 35866d3d4f | |||
| f84ebb541f | |||
| feaddd9e83 | |||
| 5e4e3f2a5b | |||
| f60d4edc26 | |||
| 2b0f556a79 | |||
| f2c150b490 | |||
| f37324eeaf | |||
| be35d00f49 | |||
| 95c186b812 | |||
| 64a43948ef | |||
| f26e0a61a8 | |||
| 9ad72e6d21 | |||
| 08d352ae46 | |||
| d711e6d4fd | |||
| 31b6129cf8 | |||
| 6b10f8a7d2 | |||
| a21b991ef9 | |||
| 9e74398655 | |||
| c8c32236de | |||
| aaa488211b | |||
| ace493ee57 | |||
| 19ba02a6a4 | |||
| cf6fc6e1d9 | |||
| 64a7071986 | |||
| ec9c3e250a | |||
| a6324a9004 | |||
| 5430fffd62 | |||
| 89fc13c873 | |||
| 6b0ff31db0 | |||
| 0be8d1bd89 | |||
| 1cb1f26cdc | |||
| a9ff75b3fa | |||
| cbad5c3592 | |||
| 060a41b2b7 | |||
| 1e8bf3546a | |||
| 83fd1d1c7e | |||
| bb178c9d3c | |||
| 24f006fe4a | |||
| e1c0e357dd | |||
| 8bc0ea2f8e | |||
| 34ab8b5bf1 | |||
| 042edadffb | |||
| 2628ed3488 | |||
| f43fbd7dd1 | |||
| f289b437df | |||
| a94bafbdc6 | |||
| 0207aa0847 | |||
| cd2b0afc6d | |||
| 2ab2e0c641 | |||
| 1e5758f3e4 | |||
| 0d9ac482ac |
@@ -0,0 +1,3 @@
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 2
|
||||
ColumnLimit: 80
|
||||
+25
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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 동작 계약은 변경되지 않는다.
|
||||
- 새 계약 테스트는 추가되지 않는다.
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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 3–6 소유다.
|
||||
- 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 15–17.
|
||||
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
Reference in New Issue
Block a user