Compare commits
60 Commits
405dc2a47d
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| a6fadcd8bb | |||
| 624ea4872c | |||
| 123a1b6ccc | |||
| 84bd0e671a | |||
| 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
|
||||
@@ -1,6 +1,7 @@
|
||||
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"
|
||||
|
||||
developer_instructions = """
|
||||
@@ -21,6 +22,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
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
- 프로젝트의 배경, 목적, 사용자, 범위는 `docs/PRD.md`에서 확인한다.
|
||||
- 전체 아키텍처와 모듈별 책임은 `docs/ARCHITECTURE.md`에서 확인한다.
|
||||
- 주요 아키텍처 결정과 그 이유 및 트레이드오프는 `docs/ADR.md`에서 확인한다.
|
||||
- 외부 reference tolerance 값, 판정 과정, 보고서와 변경 관리는
|
||||
`docs/TOLERANCE.md`를 따른다.
|
||||
- AI 에이전트는 설계나 구현 결정을 내리기 전에 PRD에서 제품 범위를, ARCHITECTURE에서 소유권과 데이터 흐름을, ADR에서 이미 결정된 트레이드오프를 먼저 확인한다. 기능별 요구조건, 연구, 정식화, 수치/reference 검토, I/O, 구현, 검증, 물리 및 release 의미는 `docs/<feature-id>/`의 승인 문서를 source of truth로 삼는다.
|
||||
- Harness는 솔버 자체가 아니라 요구조건, TDD, phase 실행, 검증을 통제하는 개발 운영 인프라이다. 전체 실행 흐름은 `docs/HARNESS_WORKFLOW.md`, 설치와 설정은 `docs/HARNESS.md`를 따른다.
|
||||
- FESA는 Abaqus와 독립적인 솔버다. 문서와 구현은 full Abaqus compatibility뿐 아니라
|
||||
@@ -32,6 +34,18 @@
|
||||
- 비교기는 필요한 source row identity와 component를 결정적으로 대응시키고 누락, 추가,
|
||||
중복, nonfinite 값을 tolerance 전에 거부한다. Reference artifact는 rename, rewrite 또는
|
||||
보정하지 않는다.
|
||||
- 모든 외부 reference comparison은 `docs/TOLERANCE.md`의 공통 정책을 사용한다.
|
||||
설계 근거는 `docs/superpowers/specs/2026-08-17-common-reference-tolerance-design.md`와
|
||||
ADR-022에 보존한다. 동일 logical quantity, unit dimension, coordinate system과 blocking behavior의
|
||||
component family에서 Abaqus-only scale `S=max(abs(reference))`를 구한다. `abs(reference)`가
|
||||
`0.01*S` 이하인 행은 `abs(fesa-reference) <= 0.01*S`, 그 외 행은 상대오차 `<=0.05`로
|
||||
판정하고, family scale-relative RMS `RMS(error)/S <= 0.01`도 함께 통과해야 한다. 별도
|
||||
absolute-error gate는 사용하지 않으며 zero-scale family는 FESA도 exact zero일 때만
|
||||
통과한다.
|
||||
- B33 section-resultant reference는 `(instance, element label, endpoint node label,
|
||||
component)`로 HDF5 element endpoint와 직접 대응한다. Node-station collapse 또는 평균을
|
||||
사용하지 않는다. MITC4 `U1/U2/U3`는 blocking, `UR1/UR2/UR3`는 warning-only 분류를
|
||||
유지하면서 같은 공통 수치 정책을 사용한다.
|
||||
- CSV는 FESA 공식 output이 아니며, FESA HDF5에서 추출한 deterministic CSV view는 비교 디버깅/검토용 보조 artifact로만 둔다.
|
||||
|
||||
## FESA 개발의 핵심 원칙
|
||||
|
||||
@@ -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
|
||||
+121
-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를 받는다.
|
||||
@@ -111,6 +129,9 @@ solution과 test command를 명시한 직접 MSBuild 프로젝트도 검증할
|
||||
|
||||
### ADR-014: B33 cantilever baseline은 component-scale 혼합 tolerance로 비교한다
|
||||
|
||||
**상태**: Artifact inventory와 component mapping의 역사적 결정은 유지하며 tolerance와
|
||||
node-station projection 결정은 ADR-022로 대체됨.
|
||||
|
||||
**결정**: `reference/cantilever beam/`의 B33 input, displacement CSV, reaction CSV, elemental-force CSV를 V0 reference baseline으로 사용한다. Displacement와 reaction은 node identity로, section resultant는 CSV node station과 정규화된 FESA element endpoint로 비교한다. 같은 model, step/frame, quantity, component에 대해 `reference_scale = max(abs(reference rows))`를 계산하고 모든 row에 `absolute_floor + 1e-6 * reference_scale`을 적용한다. SI displacement/rotation absolute floor는 `1e-9`, force/moment floor는 `1e-3`이다. Beam stress는 출력하되 Abaqus stress reference comparison은 N/A다.
|
||||
|
||||
**이유**: 자유단 moment처럼 이론적으로 0인 값에는 행별 상대오차가 정의되지 않으며 Abaqus 결과에 작은 수치 잔차가 남을 수 있다. Component scale을 사용하면 전체 물리량 크기에 비해 작은 잔차를 허용하면서 각 row를 결정적으로 판정할 수 있다.
|
||||
@@ -214,6 +235,9 @@ false match와 결과 보정을 방지한다. 더 강한 provenance가 필요한
|
||||
|
||||
### ADR-020: MITC4 displacement reference는 고정 절대오차로 판정한다
|
||||
|
||||
**상태**: S4 artifact 선택과 U blocking/UR warning-only 분류는 유지하며 고정 절대오차
|
||||
결정은 ADR-022로 대체됨.
|
||||
|
||||
**결정**: Full-integration FESA-MITC4의 sole S4 reference comparison은 matched global
|
||||
`U1/U2/U3` row에 고정 절대오차 `1.0e-5`를 적용해 pass/fail을 판정한다.
|
||||
`UR1/UR2/UR3`도 고정 절대오차 `1.0e-5`로 비교하지만 초과는 deterministic warning만
|
||||
@@ -230,3 +254,95 @@ 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 전까지
|
||||
구현된 것으로 간주하지 않는다.
|
||||
|
||||
### ADR-022: 외부 reference comparison은 공통 family-scale 정책을 사용한다
|
||||
|
||||
운영 상수, 검증 순서, report schema와 변경 관리는 `docs/TOLERANCE.md`를 따른다.
|
||||
|
||||
**결정**: B33, MITC4와 이후 기능의 외부 reference comparison은 동일한 무차원 수치
|
||||
정책을 사용한다. 같은 model/case, step/frame, logical quantity, unit dimension, coordinate
|
||||
system과 blocking behavior의 component family에 대해 reference-only scale
|
||||
`S=max(abs(reference))`를 계산한다. `abs(reference)<=0.01*S`인 행은
|
||||
`abs(fesa-reference)<=0.01*S`, 나머지 행은 `abs(fesa-reference)/abs(reference)<=0.05`를
|
||||
만족해야 한다. 모든 행과 함께 family scale-relative RMS `RMS(error)/S<=0.01`도
|
||||
통과해야 한다. 독립 absolute-error gate는 두지 않으며 `S=0`이면 모든 FESA 값도 exact
|
||||
zero일 때만 통과한다.
|
||||
|
||||
B33 family는 translation, rotation, reaction force, reaction moment, section force와
|
||||
section moment로 구분한다. 2026-08-18에 다시 생성된 elemental-force CSV의
|
||||
`Element Label`과 `Node Label`을 사용해 `(instance, element, endpoint node, component)`를
|
||||
HDF5 endpoint에 직접 대응하고 node-station collapse나 평균을 사용하지 않는다. MITC4는
|
||||
translation `U1/U2/U3` family를 blocking으로, rotation `UR1/UR2/UR3` family를
|
||||
warning-only로 유지한다.
|
||||
|
||||
**이유**: Feature별 absolute floor와 fixed absolute tolerance는 모델 단위와 크기에 따라
|
||||
서로 다른 엄격도를 만들었다. 개별 component scale은 물리적으로 zero-like인 MITC4
|
||||
`U1/U2` residue를 자체 scale로 만들어 잘못 실패시킨다. 동일 차원의 family scale,
|
||||
near-zero 대체 분기와 relative RMS를 결합하면 zero row의 불안정한 raw relative error를
|
||||
피하면서 모든 행과 전체 오차 수준을 함께 검사할 수 있다. B33의 element-endpoint identity는
|
||||
reference와 HDF5가 제공하는 실제 source identity를 보존한다.
|
||||
|
||||
**트레이드오프**: Family 최대값이 작은 component의 허용폭을 결정하므로 행별 relative
|
||||
gate와 family RMS gate를 모두 유지해야 한다. Reference family 전체가 zero이면 별도
|
||||
절대 scale이 없으므로 exact-zero만 허용하는 엄격한 fail-closed 정책이 된다. 기존 B33과
|
||||
MITC4 verification report 및 comparator schema는 새 정책에 맞춰 다시 생성해야 하지만
|
||||
reference artifact, HDF5 schema, element formulation과 blocking/warning 분류는 변경하지
|
||||
않는다.
|
||||
|
||||
+148
-50
@@ -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
|
||||
@@ -325,14 +419,19 @@ Schema requirements:
|
||||
`[N,T,My,Mz]`, generalized strain/resultant는 두 Gauss point에 기록한다.
|
||||
- General beam section stress는 section point의 axial `S11`만 복구한다. Section point가
|
||||
없으면 centroid `(0,0)`을 `source=fesa-default`로 기록한다.
|
||||
- 승인된 `reference/cantilever beam/cantilever beam elemental forces.csv`는 node station
|
||||
기준 `SF1/SM1/SM2/SM3`을 제공한다. FESA endpoint를 동일한 section-cut 부호로
|
||||
정규화하고 interior node의 두 endpoint가 tolerance 안에서 일치하는지 먼저 확인한 뒤
|
||||
`SF1 -> N`, `SM1 -> My`, `SM2 -> Mz`, `SM3 -> T`로 비교한다.
|
||||
- Reference tolerance는 같은 model, step/frame, quantity, component의 Abaqus rows에서
|
||||
`reference_scale = max(abs(reference_value))`를 구하고 각 row에
|
||||
`absolute_floor + 1e-6 * reference_scale`을 적용한다. SI displacement/rotation floor는
|
||||
`1e-9`, force/moment floor는 `1e-3`이다.
|
||||
- 승인된 `reference/cantilever beam/cantilever beam elemental forces.csv`는 각 B33
|
||||
element의 두 endpoint에 `Element Label`, `Node Label`과 `SF1/SM1/SM2/SM3`을 제공한다.
|
||||
`(instance, element label, endpoint node label, component)` identity로 HDF5
|
||||
`[element,endpoint,N/T/My/Mz]`에 직접 대응하고 `SF1 -> N`, `SM1 -> My`, `SM2 -> Mz`,
|
||||
`SM3 -> T`로 비교한다. External reference comparison은 node-station collapse나 평균을
|
||||
사용하지 않는다.
|
||||
- Reference tolerance는 같은 model/case, step/frame, logical quantity, unit dimension,
|
||||
coordinate system과 blocking behavior의 component family에서 Abaqus-only scale
|
||||
`S=max(abs(reference))`를 구한다. `abs(reference)<=0.01*S`인 행은 absolute error
|
||||
`<=0.01*S`, 그 외 행은 relative error `<=0.05`로 판정하며 family scale-relative RMS
|
||||
`RMS(error)/S<=0.01`도 통과해야 한다. 독립 absolute-error gate는 사용하지 않고
|
||||
zero-scale family는 FESA도 exact zero일 때만 통과한다. 전체 판정 과정과 report/change
|
||||
management contract는 `docs/TOLERANCE.md`를 따른다.
|
||||
- Beam stress는 HDF5 schema와 unit/analytical test로 검증하지만 Abaqus reference
|
||||
comparison은 N/A다.
|
||||
|
||||
@@ -375,9 +474,8 @@ stabilization만 둔다. Drilling calibration, artificial-energy policy와 별
|
||||
dataset은 이 기능 범위가 아니다.
|
||||
|
||||
Full-integration FESA-MITC4의 reference comparison은 `reference/shell/` S4의 기존 input 및
|
||||
displacement CSV만 사용한다. Global `U1/U2/U3`만 blocking이고 모든 matched row에 고정
|
||||
절대오차 `1.0e-5`를 적용한다. `UR1/UR2/UR3`도 고정 절대오차 `1.0e-5`로 비교하되
|
||||
warning-only evidence다. MITC4 판정에는 component scale을 사용하지 않으며 B33의 기존
|
||||
혼합 tolerance는 변경하지 않는다. S4R은
|
||||
displacement CSV만 사용한다. Global `U1/U2/U3` translation family만 blocking이고
|
||||
`UR1/UR2/UR3` rotation family는 warning-only evidence다. 두 family 모두 ADR-022의 공통
|
||||
family-scale row/RMS 정책을 사용한다. S4R은
|
||||
같은 kernel을 선택하는 source mapping과 metadata를 unit/integration tests로 검증하며
|
||||
`reference/shellR/` artifact는 acceptance comparison에 포함하지 않는다.
|
||||
|
||||
@@ -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가 계획되어 있다.
|
||||
+7
-7
@@ -113,7 +113,7 @@ Parser keyword, element kernel, solver backend 또는 output dataset 중 하나
|
||||
| FESA-PRD-014 | 선형 정적 pipeline은 stiffness factorization과 load substitution을 분리해야 한다. | `Kff` factorization이 load vector assembly보다 먼저 수행되고, `rhs=Ff-Kfc*dc` substitution으로 full displacement를 복구한다. | orchestration test, solver-adapter test |
|
||||
| FESA-PRD-015 | FESA는 V0 결과와 diagnostic을 안정된 외부 계약으로 출력해야 한다. | CLI가 `0=success`, `2=usage`, `3=input`, `4=model`, `5=solver`, `6=HDF5` exit code를 사용하고 diagnostic field가 계약과 일치하며 HDF5가 displacement, reaction, end force, section resultant, generalized strain/resultant, axial `S11`을 포함하고 실패 시 불완전한 최종 파일을 남기지 않는다. | CLI integration test, HDF5 schema/atomicity test |
|
||||
| FESA-PRD-016 | Dense와 sparse math storage 및 backend 경계를 분리해야 한다. | `Vector`는 contiguous, `Matrix`는 row-major contiguous storage와 MKL CBLAS를 사용하고 `SparseMatrix`는 별도 0-based CSR 타입이며 MKL 타입이 public core API에 노출되지 않는다. | math unit test, dependency review |
|
||||
| FESA-PRD-017 | B33 reference comparison은 component-scale 혼합 tolerance를 사용해야 한다. | 모든 matched row가 `abs_error <= absolute_floor + 1e-6 * reference_scale`을 만족하고 missing/extra/nonfinite row는 comparison 전에 실패한다. | reference comparison unit/integration test, verification report |
|
||||
| FESA-PRD-017 | 모든 외부 reference comparison은 승인된 공통 family-scale tolerance를 사용해야 한다. | 각 matched row가 near-zero 대체 조건 또는 상대오차 `0.05`를 통과하고 각 family의 scale-relative RMS가 `0.01` 이하이며 missing/extra/duplicate/nonfinite row는 comparison 전에 실패한다. | reference comparison unit/integration test, verification report |
|
||||
| FESA-PRD-018 | 승인된 B33 reference artifact는 현재 경로의 read-only baseline으로 유지해야 한다. | `reference/cantilever beam/` 파일을 rename, rewrite 또는 보정하지 않고 기능이 선언한 exact path에서 읽는다. | artifact inventory, Git diff review |
|
||||
| FESA-PRD-019 | B33 beam section과 local axis를 Abaqus 의미에 맞게 매핑해야 한다. | `n1 -> local y`, `t x n1 -> local z`, `Iy=I11`, `Iz=I22`, `I12=0`을 적용하고 nonpositive property, zero-length element, tangent-parallel guide vector를 구조화된 model diagnostic으로 거부한다. | section-mapping unit test, element geometry test |
|
||||
|
||||
@@ -121,12 +121,12 @@ Parser keyword, element kernel, solver backend 또는 output dataset 중 하나
|
||||
- MSVC x64 Debug 환경에서 configure, build, CTest를 검증한다.
|
||||
- reference test 결과는 deterministic해야 한다.
|
||||
- HDF5 schema는 versioned contract로 관리한다.
|
||||
- B33 reference tolerance는 model, step/frame, quantity, component별 Abaqus scale만 사용한다.
|
||||
- 승인된 SI B33 bundle의 absolute floor는 displacement/rotation `1e-9`, force/moment
|
||||
`1e-3`이며 relative coefficient는 `1e-6`이다.
|
||||
- MITC4 U/UR comparison은 고정 절대오차 `1.0e-5`를 사용한다. `U1/U2/U3`만
|
||||
blocking이고 `UR1/UR2/UR3` 초과는 warning-only다. B33의 component-scale 혼합
|
||||
tolerance는 별도 기존 계약으로 유지한다.
|
||||
- 모든 외부 reference comparison은 동일 logical quantity와 unit dimension의 component
|
||||
family별 Abaqus-only maximum scale을 사용한다. Near-zero ratio는 `0.01`, 일반 행
|
||||
상대오차는 `0.05`, family scale-relative RMS는 `0.01`이며 독립 absolute-error gate는
|
||||
사용하지 않는다.
|
||||
- B33 section resultant는 source element label과 endpoint node label로 직접 대응한다.
|
||||
MITC4 `U1/U2/U3`만 blocking이고 `UR1/UR2/UR3` 초과는 warning-only다.
|
||||
- parser, solver, HDF5 writer는 실패 원인을 구조화된 diagnostic으로 보고한다.
|
||||
- oneMKL, oneTBB, HDF5는 CMake에서 명시 탐지하고 실패 원인을 분류한다.
|
||||
- 대규모 모델 성능 최적화보다 V0의 명확성, 테스트 가능성, 검증 traceability를 우선한다.
|
||||
|
||||
@@ -148,7 +148,8 @@ gap은 Coordinator가 해당 owner 단계로 되돌린다.
|
||||
|
||||
Reference comparison은 source identity와 component로 행을 결정적으로 대응시키며 missing,
|
||||
extra, duplicate 또는 nonfinite required row를 tolerance 전에 거부한다. CSV는 외부 reference이고
|
||||
FESA의 authoritative output은 `results.h5`다.
|
||||
FESA의 authoritative output은 `results.h5`다. 공통 tolerance 값, family 구성, row/RMS
|
||||
판정, report evidence와 변경 관리는 `docs/TOLERANCE.md`를 따른다.
|
||||
|
||||
## 요구사항 단위 산출물 구조
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
# FESA Reference Tolerance Policy
|
||||
|
||||
## 문서 정보
|
||||
|
||||
- policy_id: `common-reference-tolerance`
|
||||
- status: `approved-and-implemented`
|
||||
- effective_date: `2026-08-18`
|
||||
- decision_record: `docs/ADR.md`의 ADR-022
|
||||
|
||||
## 목적
|
||||
|
||||
이 문서는 FESA 결과와 외부 reference 결과를 비교할 때 사용하는 공통 tolerance 값과
|
||||
검증 방법을 정의한다. 현재 기능뿐 아니라 앞으로 추가되는 요소, 재료, 해석, 하중 및
|
||||
경계조건의 reference comparison에도 같은 규칙을 적용한다.
|
||||
|
||||
이 문서는 공통 수치 판정만 정의한다. 각 기능의 비교 대상, 단위, 좌표계, row identity,
|
||||
component 구성과 최종 판정 영향은 해당 기능의 `requirements.md`, `reference-model.md`와
|
||||
`io.md`에서 정의한다.
|
||||
|
||||
## 적용 범위
|
||||
|
||||
이 정책은 승인된 외부 reference 값과 FESA 공식 결과인 `results.h5`의 값을 비교하는 데
|
||||
사용한다. Parser schema, row identity, 물리 평형, 수렴성 및 정식화 검증에는 각각의 별도
|
||||
계약을 적용한다. 이러한 검증 실패를 수치 tolerance로 완화해서는 안 된다.
|
||||
|
||||
## 공통 tolerance 값
|
||||
|
||||
| 항목 | 값 | 의미 |
|
||||
| --- | ---: | --- |
|
||||
| Near-zero 비율 | `0.01` | Reference family 최대값의 1% 이하를 near-zero로 분류 |
|
||||
| 상대오차 tolerance | `0.05` | 일반 행의 상대오차를 5% 이하로 제한 |
|
||||
| Relative RMS tolerance | `0.01` | Family 전체 RMS 오차를 reference scale의 1% 이하로 제한 |
|
||||
|
||||
독립적인 absolute-error tolerance는 사용하지 않는다. Absolute error는 near-zero 행을
|
||||
판정하고 결과를 진단하기 위해서만 사용한다.
|
||||
|
||||
## Comparison family
|
||||
|
||||
수치 scale은 개별 행이나 component마다 만들지 않고 comparison family마다 계산한다.
|
||||
하나의 family에는 다음 조건이 같은 값만 포함한다.
|
||||
|
||||
- 같은 model 또는 reference case
|
||||
- 같은 step과 frame
|
||||
- 같은 logical quantity
|
||||
- 같은 단위 차원
|
||||
- 같은 좌표계
|
||||
- 같은 최종 판정 영향(`blocking` 또는 `warning-only`)
|
||||
|
||||
각 기능 문서는 family 이름, 포함 component와 위 항목을 명시해야 한다. 서로 다른 단위,
|
||||
좌표계 또는 판정 영향을 가진 값은 같은 family에 포함할 수 없다.
|
||||
|
||||
## 검증 방법
|
||||
|
||||
### 1. 비교 입력 확정
|
||||
|
||||
기능 문서가 승인한 reference artifact와 FESA `results.h5`를 사용한다. Reference artifact는
|
||||
비교를 통과시키기 위해 이름을 바꾸거나 값을 수정, 보정 또는 zero-clamp하지 않는다.
|
||||
|
||||
### 2. Row 대응 및 사전검사
|
||||
|
||||
Reference와 FESA 값을 기능 문서가 정의한 stable source identity와 component로 일대일
|
||||
대응시킨다. 다음 오류는 tolerance 계산 전에 comparison을 실패시킨다.
|
||||
|
||||
- 필요한 파일, dataset 또는 component 누락
|
||||
- missing, extra 또는 duplicate row
|
||||
- source identity 불일치
|
||||
- 비유한 값(`NaN`, `Inf`)
|
||||
|
||||
Tolerance는 schema 또는 identity 오류를 허용하는 수단이 아니다.
|
||||
|
||||
### 3. Reference scale 계산
|
||||
|
||||
Family의 reference 값 `r_i`만 사용해 scale `S`와 near-zero band `B`를 계산한다.
|
||||
|
||||
\[
|
||||
S = \max_i |r_i|
|
||||
\]
|
||||
|
||||
\[
|
||||
B = 0.01S
|
||||
\]
|
||||
|
||||
FESA 값은 scale 계산에 사용하지 않는다. 임의의 absolute floor나 `max(1, S)`도 추가하지
|
||||
않는다.
|
||||
|
||||
### 4. 행별 오차 판정
|
||||
|
||||
FESA 값 `f_i`와 reference 값 `r_i`의 absolute error를 계산한다.
|
||||
|
||||
\[
|
||||
e_i = |f_i-r_i|
|
||||
\]
|
||||
|
||||
Reference 값이 near-zero band 안에 있으면 absolute error로 판정한다.
|
||||
|
||||
\[
|
||||
|r_i| \le B \quad\Rightarrow\quad e_i \le B
|
||||
\]
|
||||
|
||||
그 외 행은 상대오차로 판정한다.
|
||||
|
||||
\[
|
||||
|r_i| > B \quad\Rightarrow\quad \frac{e_i}{|r_i|} \le 0.05
|
||||
\]
|
||||
|
||||
경계값은 통과에 포함하며 모든 대응 행을 검사한다.
|
||||
|
||||
### 5. Family Relative RMS 판정
|
||||
|
||||
Family의 모든 absolute error로 RMS를 계산하고 reference scale로 정규화한다.
|
||||
|
||||
\[
|
||||
\operatorname{relative\_rms} =
|
||||
\frac{\sqrt{\frac{1}{n}\sum_i e_i^2}}{S}
|
||||
\]
|
||||
|
||||
다음을 만족해야 RMS 판정을 통과한다.
|
||||
|
||||
\[
|
||||
\operatorname{relative\_rms} \le 0.01
|
||||
\]
|
||||
|
||||
Family가 통과하려면 모든 행과 Relative RMS가 모두 통과해야 한다.
|
||||
|
||||
### 6. Reference scale이 0인 경우
|
||||
|
||||
`S = 0`이면 family의 모든 reference 값이 정확히 0이다.
|
||||
|
||||
- 모든 FESA 값도 정확히 0이면 통과한다.
|
||||
- 하나라도 0이 아니면 실패한다.
|
||||
- 결과에는 비유한 metric 대신 `zero-reference-scale-nonzero-error`를 기록한다.
|
||||
|
||||
### 7. 최종 판정
|
||||
|
||||
- 사전검사 실패는 항상 전체 comparison을 실패시킨다.
|
||||
- `blocking` family의 행 또는 RMS 실패는 전체 comparison을 실패시킨다.
|
||||
- `warning-only` family의 실패는 warning을 기록하되 전체 blocking 판정은 변경하지 않는다.
|
||||
- Warning은 행 실패와 RMS 실패를 구분해 결정적인 순서로 기록한다.
|
||||
|
||||
## 결과 기록
|
||||
|
||||
Comparison 결과는 원본 값에서 판정을 재현할 수 있어야 한다. 최소한 다음 정보를
|
||||
기록한다.
|
||||
|
||||
- 사용한 input, reference artifact와 FESA 결과 identity
|
||||
- 사전검사 결과
|
||||
- Family identity, component, row 수와 reference scale
|
||||
- 각 행의 FESA 값, reference 값, error, 적용 판정과 통과 여부
|
||||
- Family Relative RMS와 통과 여부
|
||||
- Blocking failure, warning과 전체 verdict
|
||||
|
||||
동일한 입력을 반복 비교하면 row, family, warning과 결과 출력 순서가 같아야 한다.
|
||||
|
||||
## 새 기능에 적용하는 방법
|
||||
|
||||
새 기능의 comparator를 구현하기 전에 기능 문서에서 다음 항목을 승인한다.
|
||||
|
||||
1. 사용할 input, reference artifact와 FESA HDF5 위치
|
||||
2. 비교할 quantity와 component
|
||||
3. 단위와 좌표계
|
||||
4. Stable source row identity와 일대일 mapping
|
||||
5. Comparison family 구성
|
||||
6. `blocking` 또는 `warning-only` 판정 영향
|
||||
|
||||
구현 시에는 사전검사, tolerance 경계값, zero-scale, 행별 판정, Relative RMS와 결과 기록을
|
||||
테스트한다. 이후 기능별 reference comparison을 다시 실행해 evidence를 남긴다.
|
||||
|
||||
## 변경 관리
|
||||
|
||||
Tolerance 값, 계산식 또는 family 구성 규칙을 변경하려면 다음 절차를 따른다.
|
||||
|
||||
1. 변경 이유와 영향을 검토하고 사용자 승인을 받는다.
|
||||
2. 이 문서와 ADR을 갱신한다.
|
||||
3. 경계값과 실패 동작을 테스트로 먼저 고정한다.
|
||||
4. 영향받는 comparator와 기능 문서를 수정한다.
|
||||
5. 전체 테스트와 영향받는 reference comparison을 다시 실행한다.
|
||||
|
||||
기존 comparison report의 과거 수치를 소급 수정하지 않는다. 변경된 정책으로 새 evidence를
|
||||
생성하며 reference artifact 자체는 변경하지 않는다.
|
||||
@@ -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.
|
||||
@@ -1311,9 +1311,10 @@ $$
|
||||
|
||||
가 일치해야 한다. 따라서 이 V0 조건의 equilibrium end action은 요소 끝에 작용하는
|
||||
outward action이고, endpoint section resultant는 동일 section을 양의 local $x$ 면으로
|
||||
자른 값이다. Abaqus node-station force row에는 equilibrium end action이 아니라 section
|
||||
resultant를 동일한 section-cut 부호로 정규화해 비교하며, interior node의 두 endpoint가
|
||||
승인 tolerance 안에서 먼저 일치해야 한다. 승인된 component mapping은
|
||||
자른 값이다. Abaqus element-endpoint force row에는 equilibrium end action이 아니라
|
||||
section resultant를 동일한 section-cut 부호로 정규화해 비교한다. Reference의 element
|
||||
label과 endpoint node label을 HDF5 row에 직접 대응하며 node station collapse, 대표 endpoint
|
||||
선택 또는 평균은 하지 않는다. 승인된 component mapping은
|
||||
`SF1 -> N`, `SM1 -> My`, `SM2 -> Mz`, `SM3 -> T`이고, CSV에 없는 transverse
|
||||
`SF2`, `SF3`는 reference comparison 대상이 아니다.
|
||||
|
||||
@@ -1529,19 +1530,25 @@ $$
|
||||
\sum\mathbf M_{\mathrm{reaction}}\right\|\le\epsilon_M.
|
||||
$$
|
||||
|
||||
승인된 B33 reference row는 같은 model, step/frame, quantity, component별로 Abaqus
|
||||
reference 값만 사용해
|
||||
승인된 B33 reference row는 같은 model, step/frame, quantity, component로 family를 만들고
|
||||
Abaqus reference 값만 사용해
|
||||
|
||||
$$
|
||||
\operatorname{reference\_scale}=\max_i|r_i|,\qquad
|
||||
\operatorname{row\_tolerance}=\operatorname{absolute\_floor}
|
||||
+10^{-6}\operatorname{reference\_scale}
|
||||
S=\max_i|r_i|
|
||||
$$
|
||||
|
||||
를 적용한다. 승인된 SI bundle의 displacement/rotation floor는 $10^{-9}$,
|
||||
force/moment floor는 $10^{-3}$이다. Reference 값을 zero-clamp하거나 row를 제거하지
|
||||
않으며 missing, extra, duplicate, nonfinite, schema/identity mismatch는 수치 판정 전에
|
||||
실패한다. 이 reference policy는 위 formulation/analytical tolerance를 대체하지 않는다.
|
||||
를 계산한다. $|r_i|\le0.01S$인 near-zero row는 $|f_i-r_i|\le0.01S$로,
|
||||
나머지 row는 $|f_i-r_i|/|r_i|\le0.05$로 판정한다. 또한
|
||||
|
||||
$$
|
||||
\frac{\sqrt{n^{-1}\sum_i(f_i-r_i)^2}}{S}\le0.01
|
||||
$$
|
||||
|
||||
을 family 전체에서 만족해야 한다. $S=0$이면 모든 FESA 값이 정확히 0일 때만 통과하고,
|
||||
그렇지 않으면 zero-reference-scale-nonzero-error로 실패한다. 독립적인 절대오차 gate는
|
||||
사용하지 않는다. Reference 값을 zero-clamp하거나 row를 제거하지 않으며 missing, extra,
|
||||
duplicate, nonfinite, schema/identity mismatch는 수치 판정 전에 실패한다. 이 reference
|
||||
policy는 위 formulation/analytical tolerance를 대체하지 않는다.
|
||||
|
||||
## 19. Numerical Risks
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
- owner_agent: `io-definition-agent`
|
||||
- date: `2026-08-09`
|
||||
- authoritative_output: `results.h5`
|
||||
- reference_baseline: `reference/cantilever beam/` at source commit `2b34d0b`
|
||||
- reference_baseline: exact read-only artifacts under `reference/cantilever beam/`
|
||||
|
||||
이 문서는 승인된 V0의 semantic I/O contract만 정의한다. Parser, HDF5 writer,
|
||||
comparison tooling의 C++ API나 구현 구조는 정의하지 않으며 Abaqus full compatibility를
|
||||
@@ -336,7 +336,7 @@ CSV는 `SF1/SM1/SM2/SM3`만 제공하고 section-cut result와 비교하므로 `
|
||||
0으로 만들거나 reference row로 합성하지 않는다. HDF5의 transverse end action은
|
||||
unit/analytical test와 physics sanity 대상이다.
|
||||
|
||||
### Frame, instance, and node-station normalization
|
||||
### Frame, instance, and element-endpoint normalization
|
||||
|
||||
- Legacy `Frame` value `Increment 1: Step Time = 1.000`은 canonical `(Step-1, frame 0)`으로
|
||||
변환한다. 승인 bundle의 다른 increment/time string은 `schema-mismatch`다.
|
||||
@@ -344,29 +344,27 @@ unit/analytical test와 physics sanity 대상이다.
|
||||
`instance_name`과 case-insensitive lookup 후 raw identity 일치 여부를 확인한다.
|
||||
- `Node Label`은 instance 안의 preserved source node label로 resolve한다. Displacement와
|
||||
reaction key는 `(model_id,Step-1,0,instance_name,source_node_label,quantity,component)`다.
|
||||
- Elemental-force CSV는 element label이 없으므로 HDF5 endpoint section resultants를
|
||||
source node station으로 project한다. Boundary station은 유일한 incident endpoint를 쓴다.
|
||||
- Interior station collapse는 정확히 두 incident B33 endpoints, 동일 section/local-axis
|
||||
orientation, 일관된 chain connectivity, 해당 node의 concentrated force/moment가 없는
|
||||
경우에만 허용한다. 두 positive-face section-cut 값이 아래 승인 component tolerance
|
||||
안에서 먼저 일치해야 한다.
|
||||
- Interior 값이 일치하면 stable internal element ID가 작은 endpoint를 deterministic
|
||||
representative로 선택한다. 두 값을 평균하지 않는다. 불일치는 `tolerance-failure`다.
|
||||
- Reversed connectivity, local-axis discontinuity, section jump, branch 또는 loaded interior
|
||||
station은 element label 없는 legacy schema로 collapse할 수 없다. 이 approved bundle
|
||||
밖에서는 element-aware reference row가 필요하며 legacy projection은 `schema-mismatch`로
|
||||
중단한다.
|
||||
- Elemental-force CSV의 `Element Label`은 instance 안의 preserved source element label로,
|
||||
`Node Label`은 해당 B33 connectivity의 정확한 endpoint source node로 resolve한다.
|
||||
- Section-resultant key는
|
||||
`(model_id,Step-1,0,instance_name,source_element_label,source_node_label,quantity,component)`다.
|
||||
한 element에는 connectivity와 일치하는 두 endpoint 행이 정확히 존재해야 한다.
|
||||
- Missing, extra, duplicate 또는 element-connectivity-mismatched endpoint row는
|
||||
`schema-mismatch`로 tolerance 전에 실패한다. Source node station collapse,
|
||||
deterministic representative 선택 또는 endpoint 평균은 사용하지 않는다.
|
||||
|
||||
Projected canonical comparison row는 다음 fields를 가진다.
|
||||
|
||||
```text
|
||||
model_id, step_name, frame_index, instance_name, source_node_label,
|
||||
quantity, component, value, unit_dimension, coordinate_system, hdf5_dataset_path
|
||||
model_id, step_name, frame_index, instance_name,
|
||||
[source_element_label], source_node_label, quantity, component, value,
|
||||
unit_dimension, coordinate_system, hdf5_dataset_path
|
||||
```
|
||||
|
||||
Stable ordering은 quantity inventory order, instance declaration order, stable source node
|
||||
order, component order다. Approved model ID는 `cantilever-beam-b33`; nodal quantities는
|
||||
global Cartesian, section resultants는 beam local이다.
|
||||
`source_element_label`은 section-resultant row에 필수이고 nodal row에는 적용하지 않는다.
|
||||
Stable ordering은 quantity inventory order, instance declaration order, stable source
|
||||
node/element-endpoint order, component order다. Approved model ID는 `cantilever-beam-b33`;
|
||||
nodal quantities는 global Cartesian, section resultants는 beam local이다.
|
||||
|
||||
### Row-set precheck and tolerance
|
||||
|
||||
@@ -376,27 +374,32 @@ identity-mismatched row가 하나라도 있으면 tolerance 계산 전에 실패
|
||||
mandatory인 `SF2/SF3` 대응 end action, generalized results와 `S11`은 계약상 비교 대상이
|
||||
아니므로 extra reference row가 아니다.
|
||||
|
||||
Matched rows는 같은 `model_id`, step/frame, quantity, component로 group한다.
|
||||
Matched rows는 같은 model/case, step/frame, logical quantity, unit dimension, coordinate
|
||||
system과 blocking behavior의 component family로 group한다. B33 family는 translation
|
||||
`UX/UY/UZ`, rotation `URX/URY/URZ`, reaction force `RF1/RF2/RF3`, reaction moment
|
||||
`RM1/RM2/RM3`, section force `N`, section moment `T/My/Mz`다.
|
||||
|
||||
```text
|
||||
reference_scale = max(abs(reference_value_i))
|
||||
row_tolerance = absolute_floor + 1e-6 * reference_scale
|
||||
row_pass = abs(fesa_value_i - reference_value_i) <= row_tolerance
|
||||
S = max(abs(reference_value_i))
|
||||
error_i = abs(fesa_value_i-reference_value_i)
|
||||
near_zero_band = 0.01*S
|
||||
|
||||
if abs(reference_value_i) <= near_zero_band:
|
||||
row_pass = error_i <= near_zero_band
|
||||
else:
|
||||
row_pass = error_i/abs(reference_value_i) <= 0.05
|
||||
|
||||
relative_rms = sqrt(mean(error_i^2))/S
|
||||
family_pass = all(row_pass) and relative_rms <= 0.01
|
||||
```
|
||||
|
||||
즉 exact policy는 `absolute_floor + 1e-6 * reference_scale`이다. `reference_scale`은
|
||||
read-only Abaqus values만 사용하고 FESA 값으로 조정하지 않는다. Scale이 zero면 relative
|
||||
term은 zero다. Reference value나 작은 residue를 zero-clamp하지 않고 모든 row를 판정한다.
|
||||
|
||||
| approved SI component class | absolute floor |
|
||||
| --- | ---: |
|
||||
| displacement and rotation | `1e-9` |
|
||||
| force and moment | `1e-3` |
|
||||
|
||||
Interior endpoint consistency도 해당 CSV quantity/component의 same Abaqus-only scale과
|
||||
floor를 사용한다. Verification report는 모든 row pass/fail과 quantity별 max absolute
|
||||
error, component-scale normalized error, RMS error, norm error, worst row/component를
|
||||
기록한다. 이 reference tolerance는 analytical/formulation tolerance를 대체하지 않는다.
|
||||
`S`는 read-only Abaqus values만 사용하고 FESA 값으로 조정하지 않는다. 독립적인
|
||||
absolute-error gate와 zero clamp는 사용하지 않는다. `S=0`이면 모든 FESA 값도 exact
|
||||
zero일 때 relative RMS를 0으로 기록하고 통과하며, 하나라도 nonzero이면 NaN/Inf 대신
|
||||
`zero-reference-scale-nonzero-error`로 실패한다. Verification report는 모든 row의 적용
|
||||
branch와 pass/fail, family identity/components, scale, near-zero band/count, max absolute
|
||||
error, scale-relative RMS와 worst row/component를 기록한다. 이 reference tolerance는
|
||||
analytical/formulation tolerance를 대체하지 않는다.
|
||||
|
||||
## CLI and Diagnostics Contract
|
||||
|
||||
@@ -445,17 +448,15 @@ release approval을 이 status가 의미하지 않는다.
|
||||
|
||||
### Resolved numerical-review handoff
|
||||
|
||||
`NR-O03-STATION-NORMALIZATION`은 approved legacy bundle에 대해 unloaded, consistently
|
||||
oriented two-endpoint interior station만 collapse하고, tolerance check 후 smaller stable
|
||||
element ID를 선택하는 규칙으로 구체화했다. Reversed/branched/loaded/jumped station은
|
||||
element-aware reference가 없는 한 비교하지 않는다.
|
||||
`NR-O03-STATION-NORMALIZATION`은 2026-08-18 regenerated elemental-force CSV의
|
||||
`Element Label`로 해소됐다. Comparator는 `(instance, element label, endpoint node label,
|
||||
component)`를 직접 대응하며 station collapse, 대표 endpoint 선택 또는 평균을 하지 않는다.
|
||||
|
||||
### Reference Model Agent
|
||||
|
||||
- Exact legacy inventory, generator `Abaqus/CAE Learning Edition 2024`, source commit
|
||||
`2b34d0b`, external SI provenance와 stress comparison N/A를 계약에 고정한다.
|
||||
- 추가 reference model은 canonical filenames와 metadata를 사용하며 이 legacy file을
|
||||
변경하지 않는다.
|
||||
- Exact legacy inventory, current row schema와 stress comparison N/A를 계약에 고정한다.
|
||||
- 추가 reference case의 exact paths와 수치 비교 계약은 해당 feature requirement가 정하며,
|
||||
canonical naming이나 optional metadata를 readiness 조건으로 추가하지 않는다.
|
||||
|
||||
### Implementation Planning Agent
|
||||
|
||||
@@ -467,7 +468,7 @@ element-aware reference가 없는 한 비교하지 않는다.
|
||||
|
||||
### Reference Verification Agent
|
||||
|
||||
- Artifact precheck 뒤 HDF5-to-legacy projection, node-station eligibility, row-set equality,
|
||||
component-scale comparison 순서를 유지한다.
|
||||
- Artifact precheck 뒤 HDF5-to-CSV source identity projection, direct element-endpoint row-set
|
||||
equality, common family-scale comparison 순서를 유지한다.
|
||||
- Missing/extra/nonfinite row를 무시하거나 `SF2/SF3`/stress reference row를 합성하지 않는다.
|
||||
|
||||
|
||||
@@ -191,8 +191,13 @@ Confirmed defects, risks, and open issues are separated.
|
||||
2. `NR-O02-DETERMINISTIC-REDUCTION`: stable COO sort and duplicate-summation rules are project policy and must be made explicit before NR-T11.
|
||||
3. `NR-O03-STATION-NORMALIZATION`: reversed connectivity/local-axis orientation and legitimate jumps at loaded interior nodes need an explicit downstream row-normalization/eligibility rule. The legacy baseline may use its documented stable orientation and unloaded interior stations, but mismatch must never be averaged. NR-T07 covers element signs.
|
||||
|
||||
2026-08-18 amendment: `NR-O03-STATION-NORMALIZATION` is resolved and superseded for the
|
||||
approved case. The regenerated elemental-force CSV supplies `Element Label`, so comparison uses
|
||||
direct `(instance, element label, endpoint node label, component)` identity and never collapses,
|
||||
selects or averages adjacent endpoints.
|
||||
|
||||
No open issue requires formulation revision. NR-O01/NR-O02 are implementation-planning
|
||||
handoffs; NR-O03 belongs to I/O and reference-model contracts.
|
||||
handoffs; the resolved NR-O03 identity is fixed by the I/O and reference-model contracts.
|
||||
|
||||
## Required Revisions
|
||||
|
||||
@@ -206,7 +211,7 @@ handoffs; NR-O03 belongs to I/O and reference-model contracts.
|
||||
|
||||
### Reference Model Agent
|
||||
|
||||
- Make NR-O03 orientation and unloaded-interior assumptions explicit without modifying the approved legacy artifacts.
|
||||
- Enforce the resolved NR-O03 direct element-endpoint identity without modifying reference artifacts.
|
||||
|
||||
## Downstream Handoff
|
||||
|
||||
|
||||
@@ -10,11 +10,45 @@
|
||||
- source_io_definition: `docs/linear-static-3d-euler-beam/io.md`
|
||||
- source_implementation_plan: `docs/linear-static-3d-euler-beam/implementation-plan.md`
|
||||
- source_implementation_report: `docs/linear-static-3d-euler-beam/implementation-report.md`
|
||||
- status: `pass-for-physics-evaluation`
|
||||
- historical_status: `pass-for-physics-evaluation`
|
||||
- current_status: `pass-for-physics-evaluation`
|
||||
- superseded_on: `2026-08-18`
|
||||
- revalidated_on: `2026-08-18`
|
||||
- owner_agent: `reference-verification-agent`
|
||||
- date: `2026-08-09`
|
||||
- review_fix_date: `2026-08-10`
|
||||
|
||||
The 2026-08-09 evidence below is preserved as a historical record of the former 11-station,
|
||||
component-scale comparison. ADR-022 and the regenerated 20-row element-endpoint CSV supersede
|
||||
that identity and tolerance. The following revalidation is the current reference-gate evidence.
|
||||
|
||||
## 2026-08-18 Common-policy Revalidation
|
||||
|
||||
`cmake --build .harness/build --config Debug` passed, followed by
|
||||
`ctest --test-dir .harness/build -C Debug --output-on-failure`: 214/214 tests passed.
|
||||
The B33 reference test generated
|
||||
`.harness/build/reference/cantilever-beam-b33/comparison.json` from the exact read-only
|
||||
reference paths.
|
||||
|
||||
- exact canonical rows: 212 = 66 displacement + 66 reaction + 80 direct element-endpoint
|
||||
section-resultant rows
|
||||
- exact families: 6; all row gates and family RMS gates passed
|
||||
- direct section identity: 20 endpoint rows, 10 B33 elements x 2 connectivity-matched endpoints
|
||||
- warnings, identity/schema/nonfinite failures: 0
|
||||
- overall verdict: `passed=true`
|
||||
|
||||
| family | rows | scale | near-zero rows | max absolute error | scale-relative RMS |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| translation `UX/UY/UZ` | 33 | `1.90476272e-2` | 23 | `5.333229170789711e-10` | `8.459623217000381e-9` |
|
||||
| rotation `URX/URY/URZ` | 33 | `2.85714399e-3` | 23 | `1.000013943180944e-10` | `1.1723557271997219e-8` |
|
||||
| reaction force `RF1/RF2/RF3` | 33 | `1.0e6` | 32 | `8.195638656616211e-7` | `2.0922817757699256e-13` |
|
||||
| reaction moment `RM1/RM2/RM3` | 33 | `1.0e7` | 32 | `5.0514936447143555e-6` | `8.811160558471221e-14` |
|
||||
| section force `N` | 20 | `0` | 20 | `0` | `0` |
|
||||
| section moment `T/My/Mz` | 60 | `1.0e7` | 41 | `1.2499958951957524e-1` | `2.2910664449287597e-9` |
|
||||
|
||||
All values above come from the generated JSON ledger. The historical report body starts below
|
||||
and must not be read as the current identity/tolerance evidence.
|
||||
|
||||
The prerequisite build/test report has status
|
||||
`pass-for-reference-verification`. This report applies only the approved Abaqus
|
||||
B33 reference tolerance. It does not approve physics sanity or release readiness.
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
- status: `ready-for-implementation-planning`
|
||||
- owner_agent: `reference-model-agent`
|
||||
- date: `2026-08-09`
|
||||
- amended_on: `2026-08-18`
|
||||
- approved_reference_model: `cantilever-beam-b33`
|
||||
- approved_reference_schema: `abaqus-cae-report-csv-v0`
|
||||
- reference_baseline: `reference/cantilever beam/` at source commit `2b34d0b`
|
||||
- approved_reference_identity: direct source-node and element-endpoint rows
|
||||
- reference_baseline: exact current files under `reference/cantilever beam/`
|
||||
|
||||
이 문서는 구현 전에 필요한 code verification, analytical solution verification 및
|
||||
approved B33 reference comparison의 모델·artifact 계약을 정의한다. 이 status는 모델과
|
||||
@@ -45,7 +46,7 @@ test fixtures이며 reference artifact bundle로 가장하지 않는다.
|
||||
논리 모델 `cantilever-beam-b33`의 exact read-only legacy bundle만 사용하여 FESA
|
||||
`results.h5`의 displacement, reaction 및 endpoint section resultant를 Abaqus/CAE report
|
||||
CSV row와 비교한다. Artifact precheck와 exact row-set matching이 먼저 통과해야 하며,
|
||||
수치 비교는 component-scale mixed tolerance를 사용한다. Axial `S11` output은 필수지만
|
||||
수치 비교는 ADR-022의 common family-scale tolerance를 사용한다. Axial `S11` output은 필수지만
|
||||
Abaqus beam stress comparison은 명시적 N/A다.
|
||||
|
||||
### Excluded validation scope
|
||||
@@ -136,8 +137,8 @@ single-step deck and changes only the named condition.
|
||||
- boundary_conditions: source node 1, DOFs 1 through 6 fixed
|
||||
- load: source node 11, global DOF 3, magnitude `-1e6` N
|
||||
- model_id: `cantilever-beam-b33`
|
||||
- logical_schema: `abaqus-cae-report-csv-v0`
|
||||
- source_commit: `2b34d0b`
|
||||
- historical_schema_record: `abaqus-cae-report-csv-v0` for the 2026-08-09 inventory only
|
||||
- historical_source_commit: `2b34d0b` for the 2026-08-09 inventory only
|
||||
- generator: `Abaqus/CAE Learning Edition 2024`
|
||||
- units: SI
|
||||
- nodal_coordinate_system: global Cartesian
|
||||
@@ -145,7 +146,7 @@ single-step deck and changes only the named condition.
|
||||
- step_name: `Step-1`
|
||||
- increment: `1`
|
||||
- step_time: `1.0`
|
||||
- artifact_status: all four exact paths present; structural precheck observed; FESA comparison not run
|
||||
- artifact_status: all four exact paths present; 20-row element-endpoint comparison passed on 2026-08-18
|
||||
- stress: N/A for Abaqus reference comparison; mandatory FESA `S11` remains covered by unit/analytical and HDF5 schema tests
|
||||
|
||||
The input and CSV numeric reference values are not recalculated, repaired, rounded, clamped or
|
||||
@@ -193,93 +194,44 @@ For this approved legacy bundle:
|
||||
- `README.md`: N/A
|
||||
- stress CSV: N/A because beam stress reference comparison is outside the approved V0 scope
|
||||
|
||||
The approved design and this contract record model ID, provenance, generator, source commit,
|
||||
units, coordinate systems, step/frame identity, logical CSV schema, exact inventory, tolerance
|
||||
policy and the stress N/A reason. The optional metadata file's absence and the approved legacy
|
||||
README/stress exclusions therefore do not change the record to `needs-reference-artifacts`.
|
||||
This contract records the exact required paths, row identities, comparison quantities, tolerance
|
||||
policy and the stress N/A reason. Historical schema/provenance fields are retained as dated
|
||||
inventory only and are not readiness gates. The optional metadata file's absence and the approved
|
||||
legacy README/stress exclusions do not change the record to `needs-reference-artifacts`.
|
||||
|
||||
### Future reference bundles
|
||||
### Later reference cases and optional metadata
|
||||
|
||||
Every later reference model shall use this structure unless its approved requirement explicitly
|
||||
marks a quantity N/A:
|
||||
|
||||
```text
|
||||
reference/
|
||||
<model-id>/
|
||||
model.inp
|
||||
metadata.json # optional
|
||||
<model-id>_displacements.csv
|
||||
<model-id>_reactions.csv
|
||||
<model-id>_internalforces.csv
|
||||
<model-id>_stresses.csv
|
||||
README.md
|
||||
```
|
||||
|
||||
CSV names are canonical `<model-id>_*.csv` names. `README.md` is mandatory for later bundles;
|
||||
`metadata.json` is optional. A quantity CSV may be omitted only when the upstream acceptance
|
||||
contract explicitly records N/A and gives its verification replacement. Missing required files
|
||||
or required Reference Model Contract provenance keep that model at `needs-reference-artifacts`.
|
||||
|
||||
## Reference Metadata Contract
|
||||
|
||||
This document is the required source of truth for the following metadata. A later bundle may
|
||||
optionally duplicate it in `metadata.json` using at least this schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"feature_id": "linear-static-3d-euler-beam",
|
||||
"model_id": "<model-id>",
|
||||
"artifact_status": "needs-reference-artifacts | ready-for-verification",
|
||||
"input_file": "model.inp",
|
||||
"abaqus_version": "<exact generator/version>",
|
||||
"generation_owner": "<person or approved procedure>",
|
||||
"generation_date": "<YYYY-MM-DD>",
|
||||
"source_commit": "<commit>",
|
||||
"units": "<consistent unit system>",
|
||||
"coordinate_system": "<nodal and element result systems>",
|
||||
"analysis_type": "single linear static",
|
||||
"element_types": ["B33"],
|
||||
"step_name": "Step-1",
|
||||
"increment": 1,
|
||||
"step_time": 1.0,
|
||||
"output_requests": ["U", "RF", "SF"],
|
||||
"reference_csv_schema_version": "<approved schema>",
|
||||
"reference_csv_files": ["<canonical filenames>"],
|
||||
"tolerance_policy": "<approved quantity/component policy>",
|
||||
"limitations": ["<known limitations and explicit N/A quantities>"]
|
||||
}
|
||||
```
|
||||
|
||||
No agent may invent unknown provenance fields or mark a bundle ready merely because filenames
|
||||
exist. An absent `metadata.json` is allowed. If the file exists, inventory it read-only and report
|
||||
any disagreement with this contract or stored artifacts as an upstream contract/provenance issue.
|
||||
Each later feature requirement names its exact input and comparison CSV paths, required row
|
||||
identity/components and N/A quantities. Canonical filenames, `README.md`, `metadata.json`,
|
||||
generator/version, provenance, duplicated unit/schema fields and a portfolio-wide directory layout
|
||||
are not readiness gates unless that feature explicitly makes one part of its numerical comparison
|
||||
contract. No agent may invent unknown provenance. If optional metadata exists, inspect it read-only
|
||||
and report disagreement without rewriting the reference artifacts.
|
||||
|
||||
## Abaqus Reference CSV Requirements
|
||||
|
||||
Header comparison trims whitespace around each comma-separated field but does not rename fields.
|
||||
For every file, `Frame` must normalize exactly from
|
||||
`Increment 1: Step Time = 1.000` to `(Step-1, frame 0)`, `Part Instance Name` must resolve to the
|
||||
preserved instance identity, `Node Label` must be a unique source-node station, and all projected
|
||||
preserved instance identity, every declared source identity must be unique, and all projected
|
||||
numeric values must be finite.
|
||||
|
||||
| exact legacy path | expected trimmed header | unique row key | observed inventory |
|
||||
| --- | --- | --- | --- |
|
||||
| `reference/cantilever beam/cantilever beam displacements.csv` | `Frame, Part Instance Name, Node Label, U-U1, U-U2, U-U3, UR-UR1, UR-UR2, UR-UR3` | `(Frame, Part Instance Name, Node Label)` | 11 rows; header/key/finite/arity checks observed |
|
||||
| `reference/cantilever beam/cantilever beam reactions.csv` | `Frame, Part Instance Name, Node Label, RF-RF1, RF-RF2, RF-RF3, RM-RM1, RM-RM2, RM-RM3` | `(Frame, Part Instance Name, Node Label)` | 11 rows; header/key/finite/arity checks observed |
|
||||
| `reference/cantilever beam/cantilever beam elemental forces.csv` | `Frame, Part Instance Name, Node Label, SF-SF1, SM-SM1, SM-SM2, SM-SM3` | `(Frame, Part Instance Name, Node Label)` | 11 rows; header/key/finite/arity checks observed |
|
||||
| `reference/cantilever beam/cantilever beam elemental forces.csv` | `Frame, Part Instance Name, Element Label, Node Label, SF-SF1, SM-SM1, SM-SM2, SM-SM3` | `(Frame, Part Instance Name, Element Label, Node Label)` | 20 rows; 10 B33 elements x 2 connectivity-matched endpoints; header/key/finite/arity checks observed |
|
||||
|
||||
The wide-row key becomes unique canonical component rows after adding `quantity` and `component`.
|
||||
Missing, extra, duplicate, nonfinite, header/schema or identity mismatch stops comparison as
|
||||
`needs-reference-artifacts` or `schema-mismatch`. No bad or near-zero row may be silently dropped.
|
||||
|
||||
For the elemental-force CSV, source node station is not an element-end identity. A boundary
|
||||
station uses its only incident endpoint. An interior station may collapse exactly two endpoints
|
||||
only when chain connectivity, section and local axes are consistent and the station has no
|
||||
concentrated force/moment. The two positive-local-x section-cut values must first agree within the
|
||||
approved component tolerance. If they agree, choose the endpoint with smaller stable internal
|
||||
element ID; never average. Reversed orientation, branch, section jump, local-axis discontinuity or
|
||||
loaded interior station requires an element-aware future schema and is a `schema-mismatch` under
|
||||
this legacy schema.
|
||||
For the elemental-force CSV, `(Part Instance Name, Element Label, Node Label)` is the source
|
||||
element-endpoint identity. Each B33 element must have exactly two rows whose node labels match its
|
||||
ordered input connectivity. Every row maps directly to the corresponding HDF5
|
||||
`[element,endpoint,N/T/My/Mz]` row. Missing, extra, duplicate or connectivity-mismatched endpoint
|
||||
identity fails as `schema-mismatch` before tolerance. The comparator does not collapse endpoints to
|
||||
a node station, choose a representative or average values.
|
||||
|
||||
## Coverage Matrix
|
||||
|
||||
@@ -288,20 +240,20 @@ the canonical V0 step identity.
|
||||
|
||||
| verification quantity | requirement ids | model_id | FESA HDF5 dataset | legacy CSV and components | row identity/location | tolerance | verification method | status |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| nodal displacement/rotation | 003, 029-031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/nodal/displacement` | `reference/cantilever beam/cantilever beam displacements.csv`: `U-U1/U-U2/U-U3 -> UX/UY/UZ`, `UR-UR1/UR-UR2/UR-UR3 -> URX/URY/URZ` | preserved instance + source node; global nodal | `1e-9 + 1e-6*reference_scale` per displacement/rotation component | HDF5-to-read-only CSV after artifact/row-set precheck | ready for implementation planning; comparison not run |
|
||||
| nodal reaction force | 007, 027, 029-031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/nodal/reaction` | `reference/cantilever beam/cantilever beam reactions.csv`: `RF-RF1/RF-RF2/RF-RF3 -> RF1/RF2/RF3` | preserved instance + source node; global nodal | `1e-3 + 1e-6*reference_scale` per force component | HDF5-to-read-only CSV plus global equilibrium | ready for implementation planning; comparison not run |
|
||||
| nodal reaction moment | 007, 027, 029-031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/nodal/reaction` | `reference/cantilever beam/cantilever beam reactions.csv`: `RM-RM1/RM-RM2/RM-RM3 -> RM1/RM2/RM3` | preserved instance + source node; global nodal | `1e-3 + 1e-6*reference_scale` per moment component | HDF5-to-read-only CSV plus moment equilibrium | ready for implementation planning; comparison not run |
|
||||
| section axial force | 031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/element/section_resultant` | `reference/cantilever beam/cantilever beam elemental forces.csv`: `SF-SF1 -> N` | positive-local-x endpoint projected to eligible source node station | `1e-3 + 1e-6*reference_scale` for `N` | endpoint consistency, deterministic station selection, HDF5-to-CSV | ready for implementation planning; comparison not run |
|
||||
| section moments/torsion | 031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/element/section_resultant` | `reference/cantilever beam/cantilever beam elemental forces.csv`: `SM-SM1 -> My`, `SM-SM2 -> Mz`, `SM-SM3 -> T` | positive-local-x endpoint projected to eligible source node station; beam local | `1e-3 + 1e-6*reference_scale` separately for `My`, `Mz`, `T` | endpoint consistency, deterministic station selection, HDF5-to-CSV | ready for implementation planning; comparison not run |
|
||||
| nodal displacement/rotation | 003, 029-031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/nodal/displacement` | `reference/cantilever beam/cantilever beam displacements.csv`: `U-U1/U-U2/U-U3 -> UX/UY/UZ`, `UR-UR1/UR-UR2/UR-UR3 -> URX/URY/URZ` | preserved instance + source node; global nodal | common translation/rotation family policy | HDF5-to-read-only CSV after artifact/row-set precheck | passed 2026-08-18 |
|
||||
| nodal reaction force | 007, 027, 029-031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/nodal/reaction` | `reference/cantilever beam/cantilever beam reactions.csv`: `RF-RF1/RF-RF2/RF-RF3 -> RF1/RF2/RF3` | preserved instance + source node; global nodal | common reaction-force family policy | HDF5-to-read-only CSV plus global equilibrium | passed 2026-08-18 |
|
||||
| nodal reaction moment | 007, 027, 029-031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/nodal/reaction` | `reference/cantilever beam/cantilever beam reactions.csv`: `RM-RM1/RM-RM2/RM-RM3 -> RM1/RM2/RM3` | preserved instance + source node; global nodal | common reaction-moment family policy | HDF5-to-read-only CSV plus moment equilibrium | passed 2026-08-18 |
|
||||
| section axial force | 031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/element/section_resultant` | `reference/cantilever beam/cantilever beam elemental forces.csv`: `SF-SF1 -> N` | direct instance + source element + endpoint node; beam local | common section-force family policy | direct HDF5-to-endpoint CSV comparison | passed 2026-08-18 |
|
||||
| section moments/torsion | 031, 036-042 | `cantilever-beam-b33` | `/steps/Step-1/frames/0/element/section_resultant` | `reference/cantilever beam/cantilever beam elemental forces.csv`: `SM-SM1 -> My`, `SM-SM2 -> Mz`, `SM-SM3 -> T` | direct instance + source element + endpoint node; beam local | common section-moment family policy | direct HDF5-to-endpoint CSV comparison | passed 2026-08-18 |
|
||||
| equilibrium end action | 031, 035, 043 | analytical models and physics portfolio | `/steps/Step-1/frames/0/element/end_force_local` | Abaqus CSV N/A for direct outward-action comparison | element endpoint `xi=-1,+1`; local outward action `[FX,FY,FZ,MX,MY,MZ]` | analytical normalized `1e-12`; residual `1e-10` | unit/analytical end-sign tests and later physics sanity | planned |
|
||||
| generalized strain/resultant | 029, 031, 035 | code and analytical models | `/steps/Step-1/frames/0/element/generalized_strain` and `/steps/Step-1/frames/0/element/generalized_resultant` | Abaqus CSV N/A | two Gauss points; beam local | matrix/formulation normalized `1e-12`, analytical relative `1e-9` | formulation/unit/HDF5 schema tests | planned |
|
||||
| axial stress | 029, 032, 035 | axial/local-z analytical models | `/steps/Step-1/frames/0/element/stress_s11` | stress CSV N/A; Abaqus beam stress reference comparison N/A | element, Gauss point, input section point or `fesa-default` centroid | analytical relative `1e-9`; exact unit/row schema | unit/analytical recovery and HDF5 schema tests | planned; reference N/A |
|
||||
|
||||
For every matched reference group,
|
||||
`reference_scale=max(abs(Abaqus reference rows))` for the same model, step/frame, quantity and
|
||||
component, and `row_tolerance=absolute_floor+1e-6*reference_scale`. Abaqus values alone set the
|
||||
scale. A zero scale uses only the floor. Every row decision and max absolute, component-scale
|
||||
normalized, RMS, norm and worst-row/component metrics must be reported.
|
||||
For every matched family, `S=max(abs(Abaqus reference rows))` uses only read-only reference values.
|
||||
Rows with `abs(reference)<=0.01*S` use `error<=0.01*S`; other rows use relative error `<=0.05`;
|
||||
the family also requires `RMS(error)/S<=0.01`. A zero-scale family requires exact-zero FESA values.
|
||||
Every row branch/decision and family scale, near-zero band/count, max absolute error,
|
||||
scale-relative RMS, worst row/component and zero-scale diagnostic must be reported.
|
||||
|
||||
### Complete must-requirement coverage
|
||||
|
||||
@@ -346,19 +298,20 @@ it does not waive the requirement.
|
||||
| `FESA-REQ-LS3DEB-033` | `smoke-b33-cli`, diagnostic negative cases | CLI exit-code/field/order integration tests; CSV N/A |
|
||||
| `FESA-REQ-LS3DEB-034` | every implementation model/test | per-step RED/GREEN/VERIFY and full MSVC/CTest evidence; model CSV N/A |
|
||||
| `FESA-REQ-LS3DEB-035` | `NR-T01` through `NR-T11` and analytical inventory | exact numerical criteria in model records |
|
||||
| `FESA-REQ-LS3DEB-036` | `cantilever-beam-b33` and comparison-policy unit fixtures | Abaqus-only component scale and exact formula tests |
|
||||
| `FESA-REQ-LS3DEB-037` | same comparison fixtures, including zero-scale groups | exact SI floors and zero-scale tests |
|
||||
| `FESA-REQ-LS3DEB-036` | `cantilever-beam-b33` and comparison-policy unit fixtures | exact family membership and Abaqus-only scale tests |
|
||||
| `FESA-REQ-LS3DEB-037` | same comparison fixtures, including boundary and zero-scale families | near-zero, relative-row, scale-relative RMS and zero-scale tests |
|
||||
| `FESA-REQ-LS3DEB-038` | malformed reference cases in `neg-b33-input-contract` | pre-tolerance fail-fast and no-clamp/no-drop tests |
|
||||
| `FESA-REQ-LS3DEB-039` | verification-report schema fixture | every row decision and aggregate/worst metrics test |
|
||||
| `FESA-REQ-LS3DEB-039` | verification-report schema fixture | every row branch/decision and family aggregate/worst metrics test |
|
||||
| `FESA-REQ-LS3DEB-040` | exact legacy Artifact Bundle Contract | inventory plus `git diff --exit-code -- reference/` process check |
|
||||
| `FESA-REQ-LS3DEB-041` | approved artifact precheck | four files, B33, exact headers, unique keys, finite values |
|
||||
| `FESA-REQ-LS3DEB-042` | three comparison quantities and station normalization | exact component mapping, endpoint consistency and no-average tests |
|
||||
| `FESA-REQ-LS3DEB-042` | three comparison quantities and direct endpoint projection | exact component mapping plus missing/extra/duplicate/connectivity-mismatch endpoint tests |
|
||||
| `FESA-REQ-LS3DEB-043` | gate audit; later physics portfolio | reference execution N/A at this step; enforce build/test -> comparison -> physics -> release order |
|
||||
| `FESA-REQ-LS3DEB-044` | process/Git diff audit and limitations review | no reference execution/mutation and no out-of-scope support claim |
|
||||
|
||||
## Artifact Acceptance Checklist
|
||||
|
||||
Read-only inventory inspection on `2026-08-09` established the following pre-implementation facts:
|
||||
The following is historical 2026-08-09 pre-implementation inventory evidence. Its 11-row
|
||||
elemental-force observation is superseded by the 2026-08-18 regenerated 20-row endpoint file:
|
||||
|
||||
- all four exact legacy paths exist;
|
||||
- the input declares `TYPE=B33`;
|
||||
@@ -370,6 +323,11 @@ Read-only inventory inspection on `2026-08-09` established the following pre-imp
|
||||
- absent `metadata.json` is allowed by project-wide policy; legacy `README.md` and stress CSV are accepted N/A exceptions;
|
||||
- no reference value was recalculated and no comparison was performed.
|
||||
|
||||
Read-only inspection on `2026-08-18` confirms that the elemental-force CSV has 20 rows,
|
||||
exactly two connectivity-matched endpoint rows for each of the 10 B33 elements. Displacement and
|
||||
reaction CSVs remain 11-row source-node tables. The fresh comparison passed on 2026-08-18;
|
||||
current metrics are recorded in `reference-comparison.md`.
|
||||
|
||||
Before an actual comparison, tooling must repeat all artifact checks, verify the exact Frame and
|
||||
instance identities, compare the complete projected row sets, and stop on any failure. The Step AC
|
||||
must also show no working-tree diff under `reference/`. Passing this checklist is not a reference
|
||||
@@ -383,8 +341,8 @@ comparison pass.
|
||||
model.
|
||||
- `NR-O01` (official oneMKL PARDISO contract) and `NR-O02` (deterministic duplicate-reduction
|
||||
algorithm) remain implementation-planning inputs, not reference artifact defects.
|
||||
- Future reversed, branched, loaded-interior or section-jump reference models require an
|
||||
element-aware canonical CSV schema; the legacy node-station schema must not be generalized.
|
||||
- Future reversed, branched, loaded-interior or section-jump reference models use the same
|
||||
element-endpoint identity and require their own approved reference coverage.
|
||||
|
||||
### Implementation Planning Agent
|
||||
|
||||
@@ -403,8 +361,8 @@ artifacts for code/analytical fixtures.
|
||||
|
||||
### Reference Verification Agent
|
||||
|
||||
Run `ARTIFACT CHECK -> HDF5 ROW PROJECTION -> EXACT ROW-SET CHECK -> ENDPOINT CONSISTENCY ->
|
||||
COMPONENT-SCALE COMPARE -> REPORT`. Use only the exact legacy files and the HDF5 paths/component
|
||||
Run `ARTIFACT CHECK -> HDF5 ROW PROJECTION -> EXACT ROW-SET CHECK -> DIRECT ENDPOINT MATCH ->
|
||||
COMMON FAMILY-SCALE COMPARE -> REPORT`. Use only the exact legacy files and the HDF5 paths/component
|
||||
mappings in the Coverage Matrix. Do not synthesize `SF2/SF3` or stress rows, clamp values, omit
|
||||
rows, average interior endpoints, or change the approved tolerance.
|
||||
|
||||
|
||||
@@ -19,11 +19,17 @@
|
||||
- reference_model_id: `cantilever-beam-b33`
|
||||
- reference_schema: `abaqus-cae-report-csv-v0`
|
||||
- reference_baseline: `reference/cantilever beam/` at source commit `2b34d0b`
|
||||
- status: `ready-for-release`
|
||||
- historical_status: `ready-for-release`
|
||||
- current_status: `superseded-pending-revalidation`
|
||||
- superseded_on: `2026-08-18`
|
||||
- owner_agent: `release-agent`
|
||||
- date: `2026-08-10`
|
||||
- final_review_source_head: `b7a1258ce0f36a85b888e23470cf9d936a7595cd`
|
||||
|
||||
The readiness evidence below is historical. The 2026-08-18 build/reference revalidation passed,
|
||||
but fresh physics and release audits are still required before a current `ready-for-release`
|
||||
verdict may be issued.
|
||||
|
||||
This is an internal feature-readiness verdict. It authorizes no publish, deploy, package, tag,
|
||||
push, external release, or reference-artifact operation.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- approved_design: `docs/superpowers/specs/2026-08-08-linear-static-3d-euler-beam-design.md`
|
||||
- approval_basis: design `status: approved`, user approval on `2026-08-08`, and amendment on `2026-08-09`
|
||||
- source_formulation: `docs/linear-static-3d-euler-beam/formulation.md`
|
||||
- reference_baseline: `reference/cantilever beam/` at source commit `2b34d0b`
|
||||
- reference_baseline: exact read-only artifacts under `reference/cantilever beam/`
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -113,7 +113,7 @@ compatibility, 새로운 수학 계약 또는 새로운 구현 정책을 추가
|
||||
- nodal_displacement: required, global six components, HDF5-to-Abaqus CSV comparison
|
||||
- reaction: required, global six components plus global force/moment equilibrium
|
||||
- equilibrium_end_action: required, local endpoint six components; unit/analytical and physics tests
|
||||
- section_resultant: required, endpoint `[N,T,My,Mz]`, node-station-normalized reference comparison
|
||||
- section_resultant: required, endpoint `[N,T,My,Mz]`, element-endpoint reference comparison
|
||||
- generalized_strain_and_resultant: required at two Gauss points; formulation and schema tests
|
||||
- stress: axial `S11` required; Abaqus reference comparison N/A
|
||||
- residual: required, free-DOF and normalized global equilibrium checks
|
||||
@@ -122,21 +122,22 @@ compatibility, 새로운 수학 계약 또는 새로운 구현 정책을 추가
|
||||
|
||||
## Tolerance Policy
|
||||
|
||||
- **FESA-REQ-LS3DEB-036** — The approved B33 reference comparison shall group rows by the same model, step/frame, quantity, and component, compute `reference_scale` only from read-only Abaqus values, and apply `absolute_floor + 1e-6 * reference_scale` to every matched row.
|
||||
- **FESA-REQ-LS3DEB-037** — For the approved SI bundle, displacement and rotation shall use `absolute_floor=1e-9`, force and moment shall use `absolute_floor=1e-3`, and a zero component scale shall use the applicable absolute floor alone.
|
||||
- **FESA-REQ-LS3DEB-036** — The approved B33 reference comparison shall group rows into translation, rotation, reaction-force, reaction-moment, section-force and section-moment families and compute each family scale `S=max(abs(reference))` only from read-only Abaqus values.
|
||||
- **FESA-REQ-LS3DEB-037** — A matched row with `abs(reference)<=0.01*S` shall pass when `abs(fesa-reference)<=0.01*S`; every other row shall pass when its relative error is at most `0.05`; each family shall also satisfy `RMS(error)/S<=0.01`. No independent absolute-error gate is used, and a zero-scale family passes only when every FESA value is exactly zero.
|
||||
- **FESA-REQ-LS3DEB-038** — Reference values shall not be zero-clamped and rows shall not be dropped; missing, extra, duplicate, nonfinite, schema-mismatched, or identity-mismatched rows shall fail before tolerance evaluation.
|
||||
- **FESA-REQ-LS3DEB-039** — The verification report shall record every row decision and maximum absolute error, component-scale normalized error, RMS error, norm error, and worst row/component for each compared quantity.
|
||||
- **FESA-REQ-LS3DEB-039** — The verification report shall record every row decision and comparison branch, family identity/components, reference scale, near-zero band/count, maximum absolute error, scale-relative RMS, worst row/component and zero-scale diagnostic for each compared family.
|
||||
|
||||
## Reference Artifact Requirements
|
||||
|
||||
The approved logical model is `cantilever-beam-b33`, schema is
|
||||
`abaqus-cae-report-csv-v0`, source commit is `2b34d0b`, generator is
|
||||
The approved logical model is `cantilever-beam-b33`. The input header records
|
||||
`Abaqus/CAE Learning Edition 2024`, and the coordinate/output contract is global Cartesian
|
||||
nodal output plus beam-local section-force output at `Step-1`, increment 1, step time 1.0.
|
||||
The historical `abaqus-cae-report-csv-v0` and source commit `2b34d0b` inventory is not a
|
||||
readiness requirement for the regenerated elemental-force CSV.
|
||||
|
||||
- **FESA-REQ-LS3DEB-040** — The V0 reference baseline shall use the exact read-only files `reference/cantilever beam/cantilever beam.inp`, `reference/cantilever beam/cantilever beam displacements.csv`, `reference/cantilever beam/cantilever beam reactions.csv`, and `reference/cantilever beam/cantilever beam elemental forces.csv` without rename, rewrite, correction, or restoration; absent `metadata.json` is allowed by project-wide policy and `README.md` is N/A for this approved legacy bundle.
|
||||
- **FESA-REQ-LS3DEB-041** — Before comparison, artifact validation shall confirm all four files, `TYPE=B33`, expected CAE report headers, unique row keys, and finite values; failure shall be classified as `needs-reference-artifacts` or `schema-mismatch` and comparison shall not start.
|
||||
- **FESA-REQ-LS3DEB-042** — Reference verification shall compare displacement by source-node identity (`U1/U2/U3/UR1/UR2/UR3`), reaction by source-node identity (`RF1/RF2/RF3/RM1/RM2/RM3`), and node-station-normalized section resultant by `SF1->N`, `SM1->My`, `SM2->Mz`, `SM3->T`; adjacent interior endpoints shall first agree within approved tolerance and shall not be averaged to hide a mismatch.
|
||||
- **FESA-REQ-LS3DEB-042** — Reference verification shall compare displacement by source-node identity (`U1/U2/U3/UR1/UR2/UR3`), reaction by source-node identity (`RF1/RF2/RF3/RM1/RM2/RM3`), and section resultants by direct `(instance, element label, endpoint node label, component)` identity using `SF1->N`, `SM1->My`, `SM2->Mz`, `SM3->T`; missing, extra, duplicate or connectivity-mismatched endpoint rows shall fail before tolerance and shall not be collapsed or averaged.
|
||||
- **FESA-REQ-LS3DEB-043** — Reference comparison shall run only after build/test passes; physics sanity shall run only after reference comparison passes and shall check global force/moment equilibrium, reaction sign, displacement direction, symmetry, element section-force consistency, and normalized residual; release readiness shall require all prior gate evidence and known limitations.
|
||||
- **FESA-REQ-LS3DEB-044** — FESA agents and Harness shall not execute Abaqus, Nastran, or another reference solver and shall not create, modify, rename, or restore reference artifacts; release documentation shall not claim support for any Out Of Scope behavior.
|
||||
|
||||
@@ -180,13 +181,13 @@ nodal output plus beam-local section-force output at `Step-1`, increment 1, step
|
||||
| FESA-REQ-LS3DEB-033 | The CLI shall support `fesa.exe <model.inp> --output <results.h5>`, default output to the current directory's `results.h5`, use exit codes `0=success`, `2=usage`, `3=input`, `4=model`, `5=solver`, `6=HDF5`, and emit `severity`, `code`, `file`, `line`, `keyword`, `entity_identity`, and `message` diagnostics to stderr in deterministic order. | output | Stabilize automation and failure classification. | Approved design §10 | must | CLI integration and diagnostic ordering tests | Default/explicit output works and every failure class returns its exact code and complete ordered fields. | Exact codes, fields, and order | io-definition-agent; implementation-planning-agent | approved |
|
||||
| FESA-REQ-LS3DEB-034 | Every production C++ behavior shall be developed in one step as GoogleTest `RED -> GREEN -> VERIFY`, with a related C++ test file, focused CTest evidence, full MSVC x64 Debug build/CTest evidence, at least one discovered test, and no new warning under the FESA target's `/W4 /WX` policy. | verification | Enforce project TDD and warning policy. | Approved design §§11.1, 11.4; ADR-012 | must | Implementation report; build/CTest logs | The related test fails first, then focused/full tests pass, discovery finds tests, and FESA emits no warning. | Zero test failures and new warnings | implementation-planning-agent; implementation-agent; build-test-executor-agent | approved |
|
||||
| FESA-REQ-LS3DEB-035 | Numerical tests shall satisfy normalized `1e-12` for stiffness symmetry and two-point-Gauss/closed-form agreement, normalized `1e-10` for rigid-mode and linear-system residual, and relative `1e-9` for analytical solutions, while checking six rigid modes, rank 6, positive deformation energy, transformation orthogonality/energy invariance, prescribed-displacement recovery, and axial/torsion/two-plane bending benchmarks. | verification | Detect sign, integration, rank, and transform defects. | Approved design §§11.2, 11.3; formulation §18 | must | Unit, analytical, and orchestration tests | Every listed invariant and analytical case passes at its stated threshold. | `1e-12` matrix; `1e-10` residual; `1e-9` analytical | formulation-agent; numerical-review-agent; implementation-planning-agent | approved |
|
||||
| FESA-REQ-LS3DEB-036 | The approved B33 reference comparison shall group rows by the same model, step/frame, quantity, and component, compute `reference_scale` only from read-only Abaqus values, and apply `absolute_floor + 1e-6 * reference_scale` to every matched row. | tolerance | Give zero and nonzero rows one deterministic rule. | Approved design §11.3; ADR-014 | must | Comparison unit/integration test; report review | Every group uses the Abaqus-only maximum absolute scale and every matched row uses the exact formula. | Relative coefficient `1e-6` | reference-model-agent; reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-037 | For the approved SI bundle, displacement and rotation shall use `absolute_floor=1e-9`, force and moment shall use `absolute_floor=1e-3`, and a zero component scale shall use the applicable absolute floor alone. | tolerance | Preserve dimensional meaning near zero. | Approved design §11.3; ADR-014 | must | Comparison tests with zero/near-zero rows | Each quantity uses its exact SI floor and zero-scale groups use no relative contribution. | SI `1e-9` displacement/rotation; `1e-3` force/moment | reference-model-agent; reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-036 | The approved B33 reference comparison shall group rows into translation, rotation, reaction-force, reaction-moment, section-force and section-moment families and compute `S=max(abs(reference))` from read-only Abaqus values. | tolerance | Give zero-like components a dimensionally compatible reference scale. | Common tolerance design; ADR-022 | must | Comparison unit/integration test; report review | Every family uses the exact declared components and Abaqus-only maximum scale. | Family scale | reference-model-agent; reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-037 | Near-zero rows shall use the `0.01*S` fallback, other rows relative error `0.05`, and every family scale-relative RMS `0.01`; zero-scale families require exact-zero FESA values and no independent absolute gate is used. | tolerance | Stabilize zero-like rows while retaining row and aggregate checks. | Common tolerance design; ADR-022 | must | Boundary, zero-scale and RMS comparison tests | Every row and family uses the exact common constants and branch rules without NaN/Inf. | `0.01`, `0.05`, `0.01` | reference-model-agent; reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-038 | Reference values shall not be zero-clamped and rows shall not be dropped; missing, extra, duplicate, nonfinite, schema-mismatched, or identity-mismatched rows shall fail before tolerance evaluation. | tolerance | Prevent false passes through omission or clamping. | Approved design §§11.3, 12; ADR-014 | must | Negative comparison tests | Every listed invalid case fails before numeric comparison and zero values remain unchanged. | No ignored invalid rows | reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-039 | The verification report shall record every row decision and maximum absolute error, component-scale normalized error, RMS error, norm error, and worst row/component for each compared quantity. | tolerance | Make the pass/fail decision auditable. | Approved design §11.3; ADR-014 | must | Verification report schema/review | Per-row decisions and all required aggregate/worst metrics are present for every quantity. | Report completeness | reference-verification-agent; release-agent | approved |
|
||||
| FESA-REQ-LS3DEB-039 | The verification report shall record every row branch/decision, family identity/components, scale, near-zero band/count, maximum absolute error, scale-relative RMS, worst row/component and zero-scale diagnostic. | tolerance | Make the pass/fail decision auditable. | Common tolerance design; ADR-022 | must | Verification report schema/review | Per-row decisions and all required family metrics are present without nonfinite report values. | Report completeness | reference-verification-agent; release-agent | approved |
|
||||
| FESA-REQ-LS3DEB-040 | The V0 reference baseline shall use the exact read-only files `reference/cantilever beam/cantilever beam.inp`, `reference/cantilever beam/cantilever beam displacements.csv`, `reference/cantilever beam/cantilever beam reactions.csv`, and `reference/cantilever beam/cantilever beam elemental forces.csv` without rename, rewrite, correction, or restoration; absent `metadata.json` is allowed by project-wide policy and `README.md` is N/A for this approved legacy bundle. | reference | Protect the approved correctness baseline. | Approved design §12; ADR-010, ADR-014 | must | Artifact inventory; Git diff review | Exact filenames exist and no reference file is added, removed, renamed, or content-modified. | Exact path/content identity | reference-model-agent; reference-verification-agent; release-agent | approved |
|
||||
| FESA-REQ-LS3DEB-041 | Before comparison, artifact validation shall confirm all four files, `TYPE=B33`, expected CAE report headers, unique row keys, and finite values; failure shall be classified as `needs-reference-artifacts` or `schema-mismatch` and comparison shall not start. | reference | Detect stale B31 or malformed evidence. | Approved design §§6.3, 12 | must | Artifact-check integration test | All checks pass before comparison and every failure uses an approved classification. | Exact inventory and schema | reference-model-agent; reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-042 | Reference verification shall compare displacement by source-node identity (`U1/U2/U3/UR1/UR2/UR3`), reaction by source-node identity (`RF1/RF2/RF3/RM1/RM2/RM3`), and node-station-normalized section resultant by `SF1->N`, `SM1->My`, `SM2->Mz`, `SM3->T`; adjacent interior endpoints shall first agree within approved tolerance and shall not be averaged to hide a mismatch. | reference | Compare equivalent quantities despite legacy station rows. | Approved design §§8.2, 12 | must | Reference comparison test/report | All components match by source identity and interior endpoints pass before deterministic representative selection. | Requirements 036 and 037 policy | io-definition-agent; reference-model-agent; reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-042 | Reference verification shall compare displacement and reaction by source-node identity and section resultants by direct `(instance, element label, endpoint node label, component)` identity using `SF1->N`, `SM1->My`, `SM2->Mz`, `SM3->T`; endpoint rows shall not be collapsed or averaged. | reference | Preserve the source identity supplied by the element-endpoint CSV and HDF5. | Common tolerance design; ADR-022 | must | Reference comparison test/report | All 20 B33 endpoint rows map one-to-one and malformed endpoint identity fails before tolerance. | Requirements 036 and 037 policy | io-definition-agent; reference-model-agent; reference-verification-agent | approved |
|
||||
| FESA-REQ-LS3DEB-043 | Reference comparison shall run only after build/test passes; physics sanity shall run only after reference comparison passes and shall check global force/moment equilibrium, reaction sign, displacement direction, symmetry, element section-force consistency, and normalized residual; release readiness shall require all prior gate evidence and known limitations. | governance | Keep numerical similarity distinct from physical/release approval. | Approved design §§11, 12, 13 | must | Gate evidence audit | Each downstream report cites the preceding pass and physics evidence covers all six checks before release review. | Relevant upstream tolerances | coordinator-agent; physics-evaluation-agent; release-agent | approved |
|
||||
| FESA-REQ-LS3DEB-044 | FESA agents and Harness shall not execute Abaqus, Nastran, or another reference solver and shall not create, modify, rename, or restore reference artifacts; release documentation shall not claim support for any Out Of Scope behavior. | governance | Prevent baseline contamination and scope inflation. | Approved design §§2.2, 12; ADR-010 | must | Process audit; Git diff; release documentation review | No reference execution or artifact mutation occurs and every exclusion is recorded without a support claim. | N/A | coordinator-agent; reference-model-agent; release-agent | approved |
|
||||
|
||||
|
||||
@@ -1540,11 +1540,11 @@ or drilling-energy warning is part of this check.
|
||||
|
||||
### 17.5 Reference-comparison boundary
|
||||
|
||||
Abaqus comparison uses only the declared full-integration S4 case and blocks only
|
||||
on matched global `U1/U2/U3` rows under the fixed absolute criterion
|
||||
`abs(fesa-reference) <= 1.0e-5`. `UR1/UR2/UR3` uses the same fixed absolute value but
|
||||
an exceedance emits only a deterministic nonblocking warning. A reported reference
|
||||
scale is diagnostic only and does not enter the MITC4 decision or normalization.
|
||||
Abaqus comparison uses only the declared full-integration S4 case. Matched global
|
||||
`U1/U2/U3` families are blocking and `UR1/UR2/UR3` families are warning-only. Both use
|
||||
the common family-scale row rule (`S=max(abs(reference))`, near-zero ratio `0.01`, relative
|
||||
tolerance `0.05`) and scale-relative RMS tolerance `0.01`. `S=0` requires exact-zero FESA
|
||||
values and there is no independent absolute-error gate.
|
||||
FESA `S4` and `S4R` inputs must produce the same internal numerical rows for identical
|
||||
supported models while preserving distinct source metadata. This common-path property
|
||||
is verified without consuming an S4R Abaqus artifact; Abaqus S4R is not an acceptance
|
||||
@@ -1631,7 +1631,7 @@ research brief remain the project source of truth.
|
||||
| `024-029` | deterministic element buffers, partitioned linear lifecycle, full-residual reaction | planning |
|
||||
| `039-048` | nodal/global and shell/local recovery inventory, units, identities, physical shell energy | I/O schema |
|
||||
| `049-057` | normalized invariants, patches, fixed drilling, declared S4 reference and equilibrium | Numerical Review/reference/physics |
|
||||
| `058-064` | fixed absolute `1.0e-5`; U blocking and UR warning-only | reference verification |
|
||||
| `058-064` | common family-scale row/RMS policy; U blocking and UR warning-only | reference verification |
|
||||
| `065-072` | exact existing S4 paths, S4R reference non-consumption, immutability and displacement-only boundary | reference model |
|
||||
|
||||
### 20.1 Numerical Review revision traceability
|
||||
@@ -1685,7 +1685,8 @@ items or an expanded reference portfolio as missing evidence.
|
||||
without claiming element equivalence.
|
||||
- Do not consume `reference/shellR/` in acceptance comparison; preserve S4R support
|
||||
through source-mapping/common-kernel/metadata tests.
|
||||
- Use the fixed absolute MITC4 tolerance `1.0e-5`; do not alter the separate B33 tolerance or add administrative metadata or portfolio gates.
|
||||
- Use the project-wide common family-scale row/RMS tolerance; do not add an independent
|
||||
absolute-error gate, administrative metadata or portfolio gates.
|
||||
- Do not create, repair, rename, or run reference artifacts during this formulation
|
||||
gate.
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ The user approved the following I/O-specific decisions on `2026-08-12`:
|
||||
retaining the existing common metadata, nodal-result, diagnostic, CLI, and
|
||||
failure-atomicity conventions.
|
||||
|
||||
The approved requirements fix the drilling rule and the MITC4 fixed absolute
|
||||
displacement tolerance `1.0e-5`.
|
||||
The approved requirements fix the drilling rule and the project-wide common
|
||||
family-scale reference tolerance.
|
||||
Drilling calibration/output, `NR-O03`, `NR-O04`, bundle-administration metadata, and
|
||||
an expanded reference portfolio are outside this contract.
|
||||
|
||||
@@ -515,22 +515,31 @@ Before tolerance evaluation:
|
||||
Only `U1/U2/U3` affect pass/fail. `UR1/UR2/UR3` are always compared and reported but
|
||||
can emit only an approved deterministic nonblocking warning.
|
||||
|
||||
For every matched displacement row:
|
||||
Matched U rows form one blocking translation family and matched UR rows form one warning-only
|
||||
rotation family. For each family:
|
||||
|
||||
```text
|
||||
row_tolerance = 1.0e-5
|
||||
row_pass = abs(fesa_value-reference_value) <= row_tolerance
|
||||
S = max(abs(reference_value_i))
|
||||
error_i = abs(fesa_value_i-reference_value_i)
|
||||
near_zero_band = 0.01*S
|
||||
|
||||
if abs(reference_value_i) <= near_zero_band:
|
||||
row_pass = error_i <= near_zero_band
|
||||
else:
|
||||
row_pass = error_i/abs(reference_value_i) <= 0.05
|
||||
|
||||
relative_rms = sqrt(mean(error_i^2))/S
|
||||
family_pass = all(row_pass) and relative_rms <= 0.01
|
||||
```
|
||||
|
||||
The `1.0e-5` value is in the user-consistent length unit for U and dimensionless for
|
||||
UR. No reference or result value is zero-clamped and neither component scale nor a
|
||||
row-specific denominator changes the fixed value. A reference scale may remain in the
|
||||
report as diagnostic information only. U exceedance fails; UR exceedance emits a
|
||||
deterministic warning only. The separate B33 mixed tolerance is unchanged.
|
||||
Reference values alone define `S`; values are not zero-clamped or omitted and there is no
|
||||
independent absolute-error gate. A zero-scale family passes only if every FESA value is exactly
|
||||
zero, otherwise it fails without emitting NaN/Inf. U row or RMS exceedance fails; the same UR
|
||||
exceedance emits a deterministic warning only.
|
||||
|
||||
The report records every U/UR row, blocking/nonblocking decision, absolute error,
|
||||
fixed-tolerance-normalized error, RMS error, displacement/rotation vector-norm
|
||||
error, worst source row/component, and every UR warning.
|
||||
The report records every U/UR row, blocking/nonblocking decision, family scale,
|
||||
near-zero branch, absolute and applicable row-relative error, scale-relative RMS,
|
||||
worst source row/component, and every UR warning.
|
||||
|
||||
## 8. CLI and Diagnostics Contract
|
||||
|
||||
@@ -615,7 +624,7 @@ physics review, or release status follows from approval of this document alone.
|
||||
| `031-038` | source-independent MITC4 identity, fixed drilling stabilization and full-integration policy; no drilling output | Implementation Planning tests |
|
||||
| `039-048` | additive HDF5 v0 paths, mandatory quantities, location identity, atomic output | Reference Model and Implementation Planning |
|
||||
| `049-057` | diagnostic/schema hooks and required verification-metric/physical-energy evidence | Numerical Review and planning |
|
||||
| `058-064` | normalized U/UR rows, fixed absolute `1.0e-5`, blocking/warning behavior, report inventory | Reference Verification |
|
||||
| `058-064` | common-policy U/UR families, blocking/warning behavior, report inventory | Reference Verification |
|
||||
| `065-072` | exact current S4 paths, S4R reference non-consumption, immutability and displacement-only gate | Reference Model |
|
||||
|
||||
## 11. Open Issues and Downstream Handoff
|
||||
@@ -623,8 +632,8 @@ physics review, or release status follows from approval of this document alone.
|
||||
### 11.1 Numerical Review boundary
|
||||
|
||||
No I/O-owned calibration value remains open. Numerical Review shall verify the exact
|
||||
fixed drilling rule, basic geometry predicates, required HDF5 inventory, and fixed
|
||||
absolute MITC4 tolerance mapping. Drilling calibration/energy output, `NR-O03`, `NR-O04`, bundle
|
||||
fixed drilling rule, basic geometry predicates, required HDF5 inventory, and common
|
||||
family-scale row/RMS tolerance mapping. Drilling calibration/energy output, `NR-O03`, `NR-O04`, bundle
|
||||
administration and reference-portfolio expansion are removed scope.
|
||||
|
||||
### 11.2 Reference Model Agent
|
||||
@@ -632,7 +641,7 @@ administration and reference-portfolio expansion are removed scope.
|
||||
- Write `docs/linear-static-mitc4-shell/reference-model.md` using
|
||||
this exact keyword/HDF5/reference-row contract.
|
||||
- Record only the two exact existing input/displacement pairs, comparison components,
|
||||
HDF5 projection, source-row identity, fixed absolute MITC4 tolerance and immutability rule.
|
||||
HDF5 projection, source-row identity, common family-scale tolerance and immutability rule.
|
||||
- Treat reaction/stress artifacts as nonblocking review evidence and do not create
|
||||
location-equivalence claims absent from this contract.
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ confirmed mathematical defect는 없다.
|
||||
| `NR-O02` drilling-energy ratio | `removed from scope` | Drilling energy는 내부 quadratic identity일 뿐 physical energy나 mandatory output이 아니며 ratio/warning threshold도 요구하지 않는다. |
|
||||
| `NR-O03` smooth-director calibration | `removed from scope` | Pairwise positive incident-normal orientation, finite/nonzero averaging 및 duplicate-node fold modeling이 승인된 exact predicate다. 별도 angle calibration은 gate가 아니다. |
|
||||
| `NR-O04` distortion/warp calibration | `removed from scope` | Basic topology, finite/nonzero surface measure 및 required-point `J>0`가 승인된 predicate다. Quality sweep이나 cutoff는 gate가 아니다. |
|
||||
| `NR-O05` U/UR tolerance | `resolved` | 모든 관련 문서가 sole S4 reference에 고정 절대오차 `1.0e-5`, U blocking, UR warning-only를 동일하게 정의한다. Reference scale은 판정에 사용하지 않으며 S4R은 reference gate가 아닌 common-path evidence다. |
|
||||
| `NR-O05` U/UR tolerance | `resolved` | 모든 관련 문서가 sole S4 reference에 공통 family-scale row/RMS 규칙, U blocking, UR warning-only를 동일하게 정의한다. S4R은 reference gate가 아닌 common-path evidence다. |
|
||||
|
||||
이전의 `needs-reference-model` 판정에 포함됐던 canonical naming, README,
|
||||
`metadata.json`, provenance, expanded portfolio 및 아직 없는 comparison result는 현재
|
||||
@@ -223,9 +223,10 @@ confirmed mathematical defect는 없다.
|
||||
not emitted, and `S13/S23` point stress is not synthesized. Different natural or
|
||||
section locations are never averaged.
|
||||
- Reference comparison first rejects missing, extra, duplicate, nonfinite or
|
||||
identity-mismatched rows. Every U/UR row then uses fixed absolute tolerance
|
||||
`1.0e-5`; no reference-scale decision term, zero clamp or row denominator is
|
||||
introduced. U1/U2/U3 is blocking and UR1/UR2/UR3 is warning-only.
|
||||
identity-mismatched rows. Every U/UR family then uses the common reference-only scale,
|
||||
near-zero ratio `0.01`, row relative tolerance `0.05` and scale-relative RMS tolerance
|
||||
`0.01`. `S=0` requires exact-zero FESA values; there is no zero clamp or independent
|
||||
absolute-error gate. U1/U2/U3 is blocking and UR1/UR2/UR3 is warning-only.
|
||||
- Source S4 and S4R select the same FESA MITC4 kernel/quadrature/recovery path while
|
||||
preserving source type. This is an input mapping, not an Abaqus formulation,
|
||||
integration, stabilization or recovery equivalence claim.
|
||||
|
||||
@@ -11,10 +11,39 @@
|
||||
`docs/linear-static-mitc4-shell/reference-model.md`
|
||||
- source_io_definition: `docs/linear-static-mitc4-shell/io.md`
|
||||
- source_requirements: `docs/linear-static-mitc4-shell/requirements.md`
|
||||
- status: `pass-for-physics-evaluation`
|
||||
- historical_status: `pass-for-physics-evaluation`
|
||||
- current_status: `pass-for-physics-evaluation`
|
||||
- superseded_on: `2026-08-18`
|
||||
- revalidated_on: `2026-08-18`
|
||||
- owner_agent: `reference-verification-agent`
|
||||
- date: `2026-08-13`
|
||||
|
||||
The fixed-absolute-tolerance evidence below is preserved as a historical record. ADR-022
|
||||
supersedes that decision with the common family-scale row/RMS policy. The following revalidation
|
||||
is the current reference-gate evidence.
|
||||
|
||||
## 2026-08-18 Common-policy Revalidation
|
||||
|
||||
`cmake --build .harness/build --config Debug` passed, followed by
|
||||
`ctest --test-dir .harness/build -C Debug --output-on-failure`: 214/214 tests passed.
|
||||
The S4 reference test generated
|
||||
`.harness/build/reference/mitc4-shell-s4-comparison/comparison.json` from the declared
|
||||
read-only input/CSV pair.
|
||||
|
||||
- exact canonical rows: 294
|
||||
- blocking translation family: 147 rows, all row/RMS gates passed
|
||||
- warning-only rotation family: 147 rows, all row/RMS gates passed; warnings 0
|
||||
- identity/schema/nonfinite failures: 0
|
||||
- overall verdict: `passed=true`
|
||||
|
||||
| family | rows | scale | near-zero rows | max absolute error | scale-relative RMS |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| translation `U1/U2/U3` | 147 | `2.37408203e-5` | 122 | `1.903785349151439e-7` | `1.0332756650397166e-3` |
|
||||
| rotation `UR1/UR2/UR3` | 147 | `7.60725743e-6` | 107 | `6.882854962740428e-8` | `2.9771372973060382e-3` |
|
||||
|
||||
All values above come from the generated JSON ledger. The historical report body starts below
|
||||
and must not be read as the current identity/tolerance evidence.
|
||||
|
||||
The prerequisite build/test report has status `pass-for-reference-verification`.
|
||||
This report verifies only the approved full-integration S4 displacement case. It
|
||||
does not compare S4R artifacts, claim Abaqus formulation equivalence, approve
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
- status: `approved-for-implementation-planning`
|
||||
- owner_agent: `reference-model-agent`
|
||||
- date: `2026-08-13`
|
||||
- comparison_revalidated_on: `2026-08-18`
|
||||
- artifact_policy: `read-only-existing-files`
|
||||
- authoritative_fesa_output: `results.h5`
|
||||
|
||||
@@ -80,26 +81,31 @@ zero-clamped and mismatched rows are not omitted, averaged, or synthesized.
|
||||
|
||||
## 5. Tolerance and decision rule
|
||||
|
||||
For every matched row:
|
||||
Matched `U1/U2/U3` rows form one blocking translation family and matched
|
||||
`UR1/UR2/UR3` rows form one warning-only rotation family. For each family:
|
||||
|
||||
```text
|
||||
tolerance = 1.0e-5
|
||||
absolute_error_i = abs(fesa_value_i - abaqus_value_i)
|
||||
S = max(abs(abaqus_value_i))
|
||||
absolute_error_i = abs(fesa_value_i-abaqus_value_i)
|
||||
near_zero_band = 0.01*S
|
||||
|
||||
near-zero row: absolute_error_i <= near_zero_band
|
||||
other row: absolute_error_i/abs(abaqus_value_i) <= 0.05
|
||||
relative_rms = sqrt(mean(absolute_error_i^2))/S <= 0.01
|
||||
```
|
||||
|
||||
The fixed `1.0e-5` value is expressed in the model's user-consistent length unit for
|
||||
`U1/U2/U3` and is dimensionless for `UR1/UR2/UR3`. Neither a component reference scale
|
||||
nor a row-specific denominator changes the value. Reference scale may be reported as
|
||||
non-decision diagnostic information. The separate B33 mixed tolerance is unchanged.
|
||||
The scale uses read-only Abaqus values only. No independent absolute-error gate, zero clamp or
|
||||
row omission is permitted. A zero-scale family passes only when all FESA values are exactly zero;
|
||||
otherwise it reports `zero-reference-scale-nonzero-error` without NaN/Inf. The U family controls
|
||||
the verdict and the UR family uses the same numeric rule for deterministic warning-only evidence.
|
||||
|
||||
- Every matched `U1/U2/U3` row must satisfy `absolute_error_i <= tolerance`.
|
||||
Any U exceedance fails that case and the feature reference comparison.
|
||||
- `UR1/UR2/UR3` uses the same fixed value. Every exceedance produces a deterministic
|
||||
warning containing the case, source row, component, error, and tolerance, but does
|
||||
not change pass/fail.
|
||||
- Every matched `U1/U2/U3` row and the translation family RMS must satisfy the common
|
||||
row/RMS rule. Any U row or family-RMS exceedance fails the feature reference comparison.
|
||||
- `UR1/UR2/UR3` uses the same family-scale row/RMS rule. Every row or family-RMS
|
||||
exceedance produces deterministic warning evidence but does not change pass/fail.
|
||||
|
||||
The comparison report records every U/UR row decision, maximum absolute error,
|
||||
fixed-tolerance-normalized error, RMS error, vector-norm error, worst source
|
||||
The comparison report records every U/UR row decision, family scale, near-zero branch,
|
||||
maximum absolute error, row relative error, scale-relative RMS, worst source
|
||||
row/component, and every UR warning.
|
||||
|
||||
## 6. Coverage and handoff
|
||||
|
||||
@@ -18,11 +18,17 @@
|
||||
- source_physics_evaluation_report: `docs/linear-static-mitc4-shell/physics-evaluation.md`
|
||||
- audited_source_head: `820ba30c717b3d0e113775608e20dfd5fbc05d53`
|
||||
- audited_branch: `feat-linear-static-mitc4-shell`
|
||||
- status: `ready-for-release`
|
||||
- historical_status: `ready-for-release`
|
||||
- current_status: `superseded-pending-revalidation`
|
||||
- superseded_on: `2026-08-18`
|
||||
- owner_agent: `release-agent`
|
||||
- date: `2026-08-13`
|
||||
- release_boundary: internal FESA feature release readiness only; no publish, deploy, package, tag, commit, or external release was performed
|
||||
|
||||
The readiness evidence below is historical. The 2026-08-18 common-policy reference revalidation
|
||||
passed, but fresh physics and release audits are still required before a current
|
||||
`ready-for-release` verdict may be issued.
|
||||
|
||||
## Release Scope
|
||||
|
||||
| item | included | excluded | notes |
|
||||
|
||||
@@ -184,13 +184,13 @@ Formulation, Numerical Review, I/O, Reference Model, Implementation Planning 및
|
||||
|
||||
## Tolerance Policy
|
||||
|
||||
- **FESA-REQ-LSMITC4-058** — Abaqus reference pass/fail shall apply only to matched global `U1/U2/U3` rows using the fixed absolute tolerance `abs(fesa-reference) <= 1.0e-5` for every row.
|
||||
- **FESA-REQ-LSMITC4-059** — MITC4 row tolerance and tolerance-normalized error shall not depend on `reference_scale`, a row-specific denominator, zero clamp or component magnitude; a reference scale may be reported only as non-decision diagnostic information.
|
||||
- **FESA-REQ-LSMITC4-060** — The fixed `1.0e-5` U tolerance is expressed in the model's user-consistent length unit and is independent of the approved B33 component-scale mixed tolerance.
|
||||
- **FESA-REQ-LSMITC4-061** — Global `UR1/UR2/UR3` rows shall use the same fixed absolute value `1.0e-5` and shall be fully reported; an exceedance emits a deterministic nonblocking warning and never changes pass/fail.
|
||||
- **FESA-REQ-LSMITC4-062** — The fixed `1.0e-5` UR tolerance is dimensionless. No separate UR large-error or drilling-energy threshold is required.
|
||||
- **FESA-REQ-LSMITC4-058** — Abaqus reference pass/fail shall apply only to matched global `U1/U2/U3` rows grouped as one blocking translation family with Abaqus-only scale `S=max(abs(reference))`.
|
||||
- **FESA-REQ-LSMITC4-059** — A U row with `abs(reference)<=0.01*S` shall pass when `abs(fesa-reference)<=0.01*S`; every other U row shall pass when its relative error is at most `0.05`; the U family shall also satisfy `RMS(error)/S<=0.01`.
|
||||
- **FESA-REQ-LSMITC4-060** — MITC4 shall use the project-wide family-scale policy without an independent absolute-error gate, zero clamp or row omission; a zero-scale family passes only when every FESA value is exactly zero.
|
||||
- **FESA-REQ-LSMITC4-061** — Global `UR1/UR2/UR3` rows shall form one warning-only rotation family using the same near-zero, relative-row and scale-relative RMS constants; an exceedance emits a deterministic nonblocking warning and never changes pass/fail.
|
||||
- **FESA-REQ-LSMITC4-062** — No separate UR large-error, fixed absolute or drilling-energy threshold is required; every row decision and family metric shall remain finite and auditable.
|
||||
- **FESA-REQ-LSMITC4-063** — Missing, extra, duplicate, nonfinite, schema-mismatched or source-identity-mismatched rows shall fail artifact/schema validation before numeric tolerance evaluation for both U and UR inventories.
|
||||
- **FESA-REQ-LSMITC4-064** — The comparison report shall record each U/UR row decision, maximum absolute error, fixed-tolerance-normalized error, RMS error, vector-norm error and worst source row/component; nonblocking UR warnings shall not be omitted from an otherwise passing report.
|
||||
- **FESA-REQ-LSMITC4-064** — The comparison report shall record each U/UR row branch/decision, family identity/components, scale, near-zero band/count, maximum absolute error, scale-relative RMS, vector-norm diagnostic, worst source row/component and zero-scale diagnostic; nonblocking UR warnings shall not be omitted from an otherwise passing report.
|
||||
|
||||
## Reference Artifact Requirements
|
||||
|
||||
@@ -226,8 +226,8 @@ without gaps or overlap.
|
||||
| `031-038` | 5-DOF physics embedded in 6-DOF with fixed drilling stabilization | numerical boundary | User approval; MITC literature and thesis 6-DOF discussion | must | Formulation review, invariant and rank tests | Exact `10^-3` positive rotational-diagonal rule; physical outputs exclude drilling | Fixed by Requirements 033-036 | Formulation; Numerical Review; Implementation Planning | approved |
|
||||
| `039-048` | Mandatory HDF5 output and failure atomicity | output | User approval; ADR-005/016/018 | must | Recovery, schema, identity, nonfinite and atomicity tests | Every quantity/location/unit/identity exists; failure commits no partial success | Exact component/location inventory; I/O Definition owns schema | Formulation; I/O Definition; Implementation Planning | approved |
|
||||
| `049-057` | TDD, invariants, patch, declared reference and physics | verification | User approval; shell formulation evidence; project process | must | CTest evidence, analytical/patch tests, the S4 reference case and physics review | Required tests pass; removed calibration/portfolio checks are not reintroduced | `1e-12` symmetry/frame; `1e-10` rigid/residual | Numerical Review; Implementation Planning | approved |
|
||||
| `058-060` | Translational displacement pass/fail tolerance | tolerance | User approval; ADR-020 | must | Comparator unit/integration tests and report review | Every matched U row uses fixed absolute `1.0e-5` without scale, clamp or omission | Fixed by Requirements 058-060 | Reference Verification | approved |
|
||||
| `061-062` | Rotational warning-only comparison | tolerance/warning | User approval; ADR-020 | must | Comparator/diagnostic tests and report review | UR never changes pass/fail; fixed absolute `1.0e-5` exceedance emits a deterministic warning | Fixed by Requirements 061-062 | Reference Verification | approved |
|
||||
| `058-060` | Translational displacement pass/fail tolerance | tolerance | User approval; ADR-022 | must | Comparator unit/integration tests and report review | Every matched U row and U-family RMS use the common family-scale policy without clamp or omission | `0.01`, `0.05`, `0.01` | Reference Verification | approved |
|
||||
| `061-062` | Rotational warning-only comparison | tolerance/warning | User approval; ADR-022 | must | Comparator/diagnostic tests and report review | UR never changes pass/fail; common-policy exceedance emits a deterministic warning | `0.01`, `0.05`, `0.01` | Reference Verification | approved |
|
||||
| `063-064` | Row/schema failure and report completeness | reference verification | User approval; ADR-005/014/018 | must | Negative comparator and report-schema tests | Invalid inventory fails before numeric comparison; all U/UR metrics remain visible | No ignored invalid rows | I/O Definition; Reference Verification | approved |
|
||||
| `065-068` | Exact S4 reference-case inventory, S4R exclusion and row validity | reference | User declaration; ADR-019 | must | Read-only S4 inventory, source-row/component precheck and S4R non-consumption test | Two declared S4 paths exist; required rows are unique, finite and deterministically mapped; S4R artifacts are not required or consumed | Requirements `058-063` | Reference Model; Reference Verification | approved |
|
||||
| `069-071` | S4 reference coverage and displacement-only comparison | reference | User approval | must | HDF5-to-CSV comparison | Declared S4 case only; U blocks and UR only warns; S4R mapping remains independently tested | Requirements `058-064` | Reference Verification; Physics Evaluation | approved |
|
||||
@@ -270,7 +270,7 @@ and tangent derivation may remain in the formulation document.
|
||||
|
||||
- Record only the exact existing S4 input/displacement CSV paths from Requirement 065 as acceptance artifacts and keep every existing reference artifact read-only.
|
||||
- Do not consume the S4R bundle in reference verification; route S4R source support to parser/common-kernel/HDF5 tests from Requirement 066.
|
||||
- Define only the HDF5-to-CSV source-node/component projection and the approved fixed absolute MITC4 tolerance `1.0e-5`; do not add bundle administration or portfolio gates.
|
||||
- Define only the HDF5-to-CSV source-node/component projection and the approved common family-scale tolerance; do not add bundle administration or portfolio gates.
|
||||
|
||||
### Implementation Planning Agent
|
||||
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
@@ -7,11 +7,15 @@
|
||||
- status: `approved`
|
||||
- approved_by: user
|
||||
- approved_on: `2026-08-08`
|
||||
- amended_on: `2026-08-09`
|
||||
- amended_on: `2026-08-18`
|
||||
- source_formulation: `docs/linear-static-3d-euler-beam/formulation.md`
|
||||
- reference_baseline: `reference/cantilever beam/` from source commit `2b34d0b`
|
||||
- reference_baseline: exact read-only artifacts under `reference/cantilever beam/`
|
||||
- implementation_environment: C++17, MSVC, CMake, CTest, GoogleTest, Intel oneMKL, Intel oneTBB, HDF5
|
||||
|
||||
2026-08-18 amendment: the former node-station projection and mixed absolute-floor tolerance are
|
||||
superseded by the direct element-endpoint identity and common family-scale row/RMS policy in
|
||||
`2026-08-17-common-reference-tolerance-design.md` and ADR-022.
|
||||
|
||||
## 1. 목적
|
||||
|
||||
이 설계는 Abaqus `.inp` keyword subset을 읽어 2절점 3차원
|
||||
@@ -288,8 +292,8 @@ node label 또는 node set이며 assembly-level set의 `INSTANCE` parameter를
|
||||
Abaqus B31은 transverse shear deformation을 포함하는 Timoshenko beam이고 B33은
|
||||
2절점 cubic Euler–Bernoulli beam이다. FESA V0는 `TYPE=B33`만 Euler 요소로 매핑한다.
|
||||
`TYPE=B31`은 `unsupported-element-formulation` 오류로 거부한다.
|
||||
`reference/cantilever beam/cantilever beam.inp`는 source commit `2b34d0b`에서
|
||||
`TYPE=B33`으로 생성된 승인 reference input이다. Reference artifact check는 비교 전에
|
||||
`reference/cantilever beam/cantilever beam.inp`는 `TYPE=B33`으로 생성된 승인 reference
|
||||
input이다. Reference artifact check는 비교 전에
|
||||
이 element type을 다시 확인하며 B31로 되돌아간 입력이나 결과를 허용하지 않는다.
|
||||
|
||||
이 결정은 [[Abaqus Structural Element Families]], [[Beam and Frame Finite Elements]],
|
||||
@@ -370,19 +374,20 @@ Abaqus internal-force CSV는 equilibrium end action이 아니라 section resulta
|
||||
| `SM1` | `MY` |
|
||||
| `SM2` | `MZ` |
|
||||
|
||||
승인된 B33 reference의 `cantilever beam elemental forces.csv`는 element label 없이
|
||||
`Frame`, `Part Instance Name`, `Node Label`, `SF1`, `SM1`, `SM2`, `SM3`을 기록한다.
|
||||
승인된 B33 reference의 `cantilever beam elemental forces.csv`는 `Frame`,
|
||||
`Part Instance Name`, `Element Label`, `Node Label`, `SF1`, `SM1`, `SM2`, `SM3`을
|
||||
기록한다.
|
||||
따라서 이 파일의 comparison은 다음 규칙을 사용한다.
|
||||
|
||||
- Reference row key는 `(Step-1, frame 0, instance name, source node label, component)`다.
|
||||
- Reference row key는 `(Step-1, frame 0, instance name, source element label,
|
||||
endpoint source node label, component)`다.
|
||||
단일 step invariant에 따라 CSV의 `Increment 1: Step Time = 1.000`을
|
||||
`(Step-1, frame 0)`으로 정규화한다.
|
||||
- FESA endpoint section resultant를 동일한 section-cut 부호로 변환한 뒤 source node
|
||||
station으로 정규화한다. Interior node의 두 인접 element endpoint 값은 먼저 승인된
|
||||
tolerance 안에서 서로 일치해야 한다. 불일치는 평균으로 숨기지 않고
|
||||
`tolerance-failure`로 보고한다.
|
||||
- Interior endpoint가 일치하면 stable internal element ID가 작은 endpoint를 대표 row로
|
||||
선택한다. Boundary node는 하나의 endpoint를 사용한다.
|
||||
- 각 B33 element에는 input connectivity의 두 source node label과 일치하는 endpoint row가
|
||||
정확히 두 개 있어야 하며, 각 row를 해당 HDF5 element endpoint에 직접 대응한다.
|
||||
Missing, extra, duplicate 또는 connectivity mismatch는 tolerance 전에 실패한다.
|
||||
- Comparator는 같은 source node의 인접 endpoint를 node station으로 축약하거나 대표값을
|
||||
선택하거나 평균하지 않는다.
|
||||
- 이 bundle에서 비교하는 mapping은 `SF1 -> N`, `SM1 -> My`, `SM2 -> Mz`,
|
||||
`SM3 -> T`다. CSV에 없는 `SF2`와 `SF3`는 reference comparison 대상이 아니며,
|
||||
FESA equilibrium end action의 transverse force는 unit/analytical test와 physics sanity로
|
||||
@@ -496,8 +501,8 @@ GoogleTest target에는 FESA warning policy를 강제하지 않는다.
|
||||
- axial, torsion, y/z bending cantilever analytical cases
|
||||
- HDF5 schema, identity, component, metadata, atomic finalization
|
||||
- CLI `.inp -> results.h5` integration
|
||||
- 승인된 B33 CSV header/Frame 정규화, node-station matching, interior endpoint 일치 검사
|
||||
- zero 및 near-zero reference row를 포함한 component-scale 혼합 허용오차 검사
|
||||
- 승인된 B33 CSV header/Frame 정규화와 element-endpoint direct identity 검사
|
||||
- zero 및 near-zero reference row를 포함한 공통 family-scale 상대오차/RMS 검사
|
||||
|
||||
### 11.3 수치 tolerance
|
||||
|
||||
@@ -506,37 +511,33 @@ GoogleTest target에는 FESA warning policy를 강제하지 않는다.
|
||||
| matrix symmetry and Gauss/closed-form comparison | normalized `1e-12` |
|
||||
| rigid-mode and linear-system residual | normalized `1e-10` |
|
||||
| analytical solution tests | relative `1e-9` |
|
||||
| Abaqus B33 reference comparison | component-scale relative `1e-6` |
|
||||
| SI displacement and rotation absolute floor | `1e-9` |
|
||||
| SI force and moment absolute floor | `1e-3` |
|
||||
| Abaqus reference row relative error | `0.05` |
|
||||
| near-zero threshold ratio | reference family scale의 `0.01` |
|
||||
| reference family scale-relative RMS | `0.01` |
|
||||
|
||||
Reference row 판정은 zero-reference에서도 의미가 있도록 component-scale 혼합
|
||||
허용오차를 사용한다. 같은 model, step/frame, quantity, component의 Abaqus reference
|
||||
rows에 대해 다음 값을 계산한다.
|
||||
Reference row 판정은 같은 model, step/frame, quantity, component로 구성한 reference
|
||||
family별 공통 규칙을 사용한다. Reference 값으로만 family scale을 계산한다.
|
||||
|
||||
```text
|
||||
reference_scale = max(abs(reference_value_i))
|
||||
row_tolerance = absolute_floor + 1e-6 * reference_scale
|
||||
row_pass = abs(fesa_value_i - reference_value_i) <= row_tolerance
|
||||
S = max(abs(reference_value_i))
|
||||
near_zero_i = abs(reference_value_i) <= 0.01 * S
|
||||
row_pass_i = abs(fesa_value_i - reference_value_i) / abs(reference_value_i) <= 0.05
|
||||
if not near_zero_i
|
||||
row_pass_i = abs(fesa_value_i - reference_value_i) <= 0.01 * S
|
||||
if near_zero_i
|
||||
relative_rms = sqrt(mean((fesa_value_i - reference_value_i)^2)) / S
|
||||
family_pass = all(row_pass_i) and relative_rms <= 0.01
|
||||
```
|
||||
|
||||
- Scale은 displacement, rotation, force, moment 및 각 component를 섞지 않는다.
|
||||
- Family는 displacement, rotation, force, moment 및 각 component를 섞지 않는다.
|
||||
- Scale은 read-only Abaqus reference 값만 사용하며 FESA 결과로 조정하지 않는다.
|
||||
- Scale이 0이면 absolute floor만 적용한다.
|
||||
- `S == 0`이면 모든 FESA 값이 정확히 0일 때만 통과한다. 하나라도 0이 아니면
|
||||
`zero-reference-scale-nonzero-error`로 실패한다.
|
||||
- 모든 row를 개별 판정하며 reference 값을 zero-clamp하거나 row를 제거하지 않는다.
|
||||
- Missing/extra row, nonfinite value, schema 또는 identity mismatch는 tolerance 계산 전에
|
||||
실패한다.
|
||||
- Report는 max absolute error, component-scale normalized error, RMS error, norm error,
|
||||
worst row/component를 기록한다.
|
||||
|
||||
현재 B33 `SM1`의 reference scale은 `1.0e7`이고 moment row tolerance는
|
||||
`1.0e-3 + 1.0e-6 * 1.0e7 = 10.001`이다. 자유단 reference residue `-1.56e-2`와
|
||||
FESA의 이론적 zero 사이 오차 `1.56e-2`는 이 기준을 통과하지만, 예를 들어 `100`의
|
||||
자유단 moment 오차는 실패한다.
|
||||
|
||||
승인된 cantilever bundle은 SI absolute floor를 사용한다. 이후 SI가 아닌 reference를
|
||||
추가하면 quantity별 absolute floor를 해당 단위로 변환하고 변환 근거를 metadata와
|
||||
verification report에 기록해야 한다.
|
||||
- Report는 family scale, max absolute error, max row relative error, scale-relative RMS,
|
||||
worst row/component와 판정 reason을 기록한다. 독립적인 absolute-error gate는 없다.
|
||||
|
||||
### 11.4 공통 build/test command
|
||||
|
||||
@@ -561,9 +562,10 @@ CTest discovery 결과는 한 개 이상의 test를 포함해야 한다.
|
||||
## 12. Reference 및 release gate
|
||||
|
||||
`reference/cantilever beam/`을 V0의 승인된 Abaqus B33 reference baseline으로 사용한다.
|
||||
논리 model ID는 `cantilever-beam-b33`, reference schema ID는
|
||||
`abaqus-cae-report-csv-v0`이며 source baseline은 commit `2b34d0b`이다. Input header가
|
||||
기록한 generator는 `Abaqus/CAE Learning Edition 2024`다. 모델은 SI 단위계, global
|
||||
논리 model ID는 `cantilever-beam-b33`이다. 기존 `abaqus-cae-report-csv-v0`과 source
|
||||
commit `2b34d0b` 기록은 2026-08-09 당시 inventory의 역사적 provenance이며, 2026-08-18
|
||||
regenerated elemental-force CSV의 현재 readiness gate가 아니다. Input header가 기록한
|
||||
generator는 `Abaqus/CAE Learning Edition 2024`다. 모델은 SI 단위계, global
|
||||
Cartesian nodal output, beam local section-force output, `Step-1`, increment 1,
|
||||
step time 1.0 계약으로 해석한다.
|
||||
|
||||
@@ -588,8 +590,8 @@ Reference verification은 build/test gate 통과 후 승인 input으로 FESA `re
|
||||
|
||||
- nodal displacement: CSV `U1/U2/U3/UR1/UR2/UR3`과 HDF5 displacement
|
||||
- nodal reaction: CSV `RF1/RF2/RF3/RM1/RM2/RM3`과 HDF5 reaction
|
||||
- section resultant: CSV `SF1/SM1/SM2/SM3`과 node station으로 정규화한 HDF5
|
||||
section resultant
|
||||
- section resultant: CSV `SF1/SM1/SM2/SM3`과 element label 및 endpoint node label로
|
||||
직접 대응한 HDF5 section resultant
|
||||
- stress: 명시적 N/A
|
||||
|
||||
Artifact check는 네 파일의 존재, B33 element type, expected header, 유일한 row key,
|
||||
|
||||
+19
-10
@@ -3,11 +3,16 @@
|
||||
## Status
|
||||
|
||||
- date: `2026-08-12`
|
||||
- amended_on: `2026-08-18`
|
||||
- status: `approved`
|
||||
- scope: FESA project policy, agents, skills, and `linear-static-mitc4-shell` upstream contracts
|
||||
- implementation_code: out of scope
|
||||
- reference_artifact_mutation: prohibited
|
||||
|
||||
2026-08-18 amendment: the former MITC4 fixed absolute tolerance is superseded by the common
|
||||
family-scale row/RMS policy in `2026-08-17-common-reference-tolerance-design.md` and ADR-022.
|
||||
The independent-solver, minimal-reference-case and drilling decisions remain unchanged.
|
||||
|
||||
## Decision
|
||||
|
||||
FESA is an independent finite element solver. It accepts an approved subset of the
|
||||
@@ -84,19 +89,23 @@ Only matched global `U1/U2/U3` rows are blocking. `UR1/UR2/UR3` rows are compare
|
||||
reported as warning-only evidence. Reactions and stresses may be inspected but are not
|
||||
reference pass/fail quantities.
|
||||
|
||||
For every matched U/UR row:
|
||||
The project-wide common policy now evaluates each matched U/UR component family:
|
||||
|
||||
```text
|
||||
tolerance = 1.0e-5
|
||||
S = max(abs(reference_value_i))
|
||||
near_zero_i = abs(reference_value_i) <= 0.01 * S
|
||||
ordinary_row_pass_i = abs(error_i) / abs(reference_value_i) <= 0.05
|
||||
near_zero_row_pass_i = abs(error_i) <= 0.01 * S
|
||||
relative_rms = sqrt(mean(error_i^2)) / S <= 0.01
|
||||
```
|
||||
|
||||
The fixed value is expressed in the model's user-consistent length unit for translation
|
||||
and as a dimensionless rotation value for warning-only UR. Reference scale may remain
|
||||
diagnostic report data but does not enter the tolerance or normalized-error denominator.
|
||||
Values are not zero-clamped. Missing, duplicate, unmatched, or nonfinite required rows
|
||||
fail before numeric tolerance evaluation. U tolerance failure fails reference
|
||||
verification; UR tolerance exceedance emits a deterministic warning only. The B33 mixed
|
||||
tolerance remains unchanged.
|
||||
The scale is computed from read-only reference values in the same model, step/frame,
|
||||
quantity and component family. If `S == 0`, only exact-zero FESA values pass; otherwise
|
||||
the reason is `zero-reference-scale-nonzero-error`. There is no independent absolute-error
|
||||
gate. Values are not zero-clamped. Missing, duplicate, unmatched, or nonfinite required
|
||||
rows fail before numeric tolerance evaluation. U tolerance failure fails reference
|
||||
verification; UR tolerance exceedance emits a deterministic warning only. B33 uses the
|
||||
same row and family-RMS thresholds.
|
||||
|
||||
No additional locking, convergence, distorted, curved, director-angle, or invalid
|
||||
geometry reference portfolio is required for this MITC4 implementation acceptance.
|
||||
@@ -147,7 +156,7 @@ For MITC4:
|
||||
|
||||
- fixed drilling stabilization closes `NR-O01` and removes `NR-O02` by scope;
|
||||
- `NR-O03` and `NR-O04` are removed from the approved verification scope;
|
||||
- the fixed absolute MITC4 tolerance `1.0e-5` closes the blocking part of `NR-O05`;
|
||||
- the common family-scale row/RMS policy closes the blocking part of `NR-O05`;
|
||||
- missing heavyweight reference metadata is `not-required-by-policy`;
|
||||
- confirmed formulation defects remain blocking.
|
||||
|
||||
|
||||
@@ -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,227 @@
|
||||
# Common Reference Tolerance Policy Design
|
||||
|
||||
## Status
|
||||
|
||||
- date: `2026-08-17`
|
||||
- status: `implemented-and-verified`
|
||||
- verified_on: `2026-08-18`
|
||||
- scope: 모든 FESA 외부 reference comparison
|
||||
- production_solver_behavior: 변경 없음
|
||||
- reference_artifact_mutation: 금지
|
||||
|
||||
현재 운영 상수, 검증 순서, report schema와 변경 관리는 `docs/TOLERANCE.md`를 source of
|
||||
truth로 사용한다. 이 문서는 승인 결정의 설계 근거와 대안 검토를 보존한다.
|
||||
|
||||
## 목적
|
||||
|
||||
B33, MITC4 및 이후 추가되는 기능이 서로 다른 수치 tolerance 식을 만들지 않도록 공통
|
||||
reference comparison 규칙을 정의한다. 이 정책은 Abaqus CSV와 FESA HDF5에서 결정적으로
|
||||
대응된 행을 비교하는 외부 reference gate에만 적용한다. Element formulation, analytical
|
||||
solution, matrix symmetry, residual 및 physics sanity tolerance는 이 정책의 범위가 아니다.
|
||||
|
||||
## 결정
|
||||
|
||||
모든 reference comparison은 다음 세 개의 무차원 상수를 사용한다.
|
||||
|
||||
```text
|
||||
near_zero_ratio = 0.01
|
||||
relative_tolerance = 0.05
|
||||
relative_rms_tolerance = 0.01
|
||||
```
|
||||
|
||||
독립적인 absolute-error gate는 두지 않는다. Absolute error는 reference 값이 0에 가까워
|
||||
행별 상대오차를 안정적으로 계산할 수 없는 경우의 대체 판정과 진단에만 사용한다.
|
||||
|
||||
## Comparison Family와 Scale
|
||||
|
||||
Tolerance scale은 개별 component가 아니라 동일한 물리 차원의 component family마다
|
||||
계산한다. 하나의 family는 최소한 다음 identity가 모두 같은 행으로 제한한다.
|
||||
|
||||
- model/reference case
|
||||
- step 및 frame
|
||||
- logical quantity
|
||||
- unit dimension
|
||||
- coordinate system
|
||||
- blocking 또는 warning-only behavior
|
||||
|
||||
서로 다른 quantity나 단위 차원의 값은 scale을 공유하지 않는다. Family scale은 대응된
|
||||
reference 값만 사용해 계산한다.
|
||||
|
||||
\[
|
||||
S_g = \max_{i \in g}|R_i|
|
||||
\]
|
||||
|
||||
여기서 \(g\)는 comparison family, \(R_i\)는 원본 reference 값이다. Reference 값은 scale
|
||||
계산 전후에 clamp, rewrite 또는 생략하지 않는다.
|
||||
|
||||
현재 기능의 family mapping은 다음과 같다.
|
||||
|
||||
| feature | logical quantity / dimension | components | behavior |
|
||||
| --- | --- | --- | --- |
|
||||
| B33 | displacement / length | `UX`, `UY`, `UZ` | blocking |
|
||||
| B33 | displacement / rotation | `URX`, `URY`, `URZ` | blocking |
|
||||
| B33 | reaction / force | `RF1`, `RF2`, `RF3` | blocking |
|
||||
| B33 | reaction / moment | `RM1`, `RM2`, `RM3` | blocking |
|
||||
| B33 | section resultant / force | `N` | blocking |
|
||||
| B33 | section resultant / moment | `T`, `My`, `Mz` | blocking |
|
||||
| MITC4 | displacement / length | `U1`, `U2`, `U3` | blocking |
|
||||
| MITC4 | displacement / rotation | `UR1`, `UR2`, `UR3` | warning-only |
|
||||
|
||||
새 quantity는 해당 기능의 requirements와 reference-model 계약에서 logical quantity,
|
||||
dimension, components 및 behavior를 명시해 family에 배치한다. 물리 차원이 같다는 이유만으로
|
||||
서로 다른 logical quantity를 자동으로 합치지 않는다.
|
||||
|
||||
### B33 element-endpoint identity amendment
|
||||
|
||||
2026-08-18에 사용자가 다시 생성한
|
||||
`reference/cantilever beam/cantilever beam elemental forces.csv`는 `Element Label`과
|
||||
`Node Label`을 모두 포함하고 B33 요소마다 두 endpoint 행을 제공한다. Section-resultant
|
||||
comparison은 다음 key로 각 CSV 행을 HDF5 `[element, endpoint, component]` 행에 직접
|
||||
대응시킨다.
|
||||
|
||||
```text
|
||||
(model_id, step/frame, instance_name, source_element_label,
|
||||
source_node_label, quantity, component)
|
||||
```
|
||||
|
||||
Component mapping은 `SF-SF1 -> N`, `SM-SM1 -> My`, `SM-SM2 -> Mz`,
|
||||
`SM-SM3 -> T`다. Comparator는 이 CSV를 source-node station으로 collapse하거나 인접
|
||||
endpoint를 평균하지 않는다. Missing, extra, duplicate 또는 connectivity-mismatched
|
||||
element-endpoint identity는 tolerance 전에 실패한다. Production
|
||||
`ResultRecovery::NormalizeSectionResultantsToNodeStations` 계약과 그 단위 테스트는 이 외부
|
||||
reference identity 변경의 범위가 아니다.
|
||||
|
||||
## 행별 판정
|
||||
|
||||
각 대응 행에 대해 다음 absolute error를 계산한다.
|
||||
|
||||
\[
|
||||
E_i = |F_i-R_i|
|
||||
\]
|
||||
|
||||
Family scale이 양수이면 near-zero band는 다음과 같다.
|
||||
|
||||
\[
|
||||
Z_g = 0.01S_g
|
||||
\]
|
||||
|
||||
행은 다음 두 분기 중 정확히 하나로 판정한다.
|
||||
|
||||
1. \(|R_i| \le Z_g\)이면 near-zero 행이다. 이 행은 \(E_i \le Z_g\)일 때 통과한다.
|
||||
2. \(|R_i| > Z_g\)이면 일반 상대오차 행이다. 이 행은
|
||||
\(E_i/|R_i| \le 0.05\)일 때 통과한다.
|
||||
|
||||
따라서 absolute error는 모든 행에 적용되는 별도 acceptance gate가 아니다. Near-zero
|
||||
분기는 0 또는 0에 가까운 reference 행에서 정의되지 않거나 과도하게 증폭되는 raw relative
|
||||
error를 대체한다.
|
||||
|
||||
### Zero-scale family
|
||||
|
||||
\(S_g=0\)이면 해당 family의 모든 reference 값이 정확히 0이다. 사용자가 독립 absolute
|
||||
floor를 제외했으므로 이 경우에는 다음 fail-closed 규칙을 사용한다.
|
||||
|
||||
- 모든 FESA 값도 정확히 0이면 모든 행과 family relative RMS가 통과한다.
|
||||
- 하나라도 0이 아닌 FESA 값이 있으면 해당 행과 family가 실패한다.
|
||||
|
||||
Comparator는 이 경우 infinity 또는 NaN을 report에 기록하지 않고
|
||||
`zero-reference-scale-nonzero-error` 진단을 기록한다.
|
||||
|
||||
## Scale-relative RMS
|
||||
|
||||
Family 전체의 평균적인 오차 수준은 scale-relative RMS로 판정한다.
|
||||
|
||||
\[
|
||||
\operatorname{relative\_rms}_g =
|
||||
\frac{\sqrt{\frac{1}{N_g}\sum_{i \in g} E_i^2}}{S_g}
|
||||
\]
|
||||
|
||||
\[
|
||||
\operatorname{relative\_rms}_g \le 0.01
|
||||
\]
|
||||
|
||||
이 값은 raw row-relative error의 RMS가 아니다. Error RMS를 같은 family의 reference 최대
|
||||
scale로 정규화한 무차원 값이다. 행별 gate가 국부적인 큰 오차를 검출하고, relative RMS
|
||||
gate가 family 전체의 평균 오차를 검출한다. 두 gate를 모두 통과해야 한다.
|
||||
|
||||
## 판정 순서
|
||||
|
||||
1. 필요한 artifact와 schema를 확인한다.
|
||||
2. Source identity 및 component를 결정적으로 대응시킨다.
|
||||
3. Missing, extra, duplicate, identity-mismatched 또는 nonfinite required row를 tolerance 전에
|
||||
실패시킨다.
|
||||
4. Comparison family와 reference-only scale을 계산한다.
|
||||
5. 모든 행에 near-zero 또는 일반 상대오차 판정을 적용한다.
|
||||
6. Family scale-relative RMS를 계산한다.
|
||||
7. Blocking family는 모든 행과 RMS가 통과해야 reference gate를 통과한다.
|
||||
8. Warning-only family의 동일한 실패는 deterministic warning을 생성하지만 전체 reference
|
||||
verdict를 실패시키지 않는다.
|
||||
|
||||
Tolerance 정책은 feature별 blocking/warning-only quantity 선택을 변경하지 않는다.
|
||||
|
||||
## Report Contract
|
||||
|
||||
Comparator report는 최소한 다음 항목을 보존한다.
|
||||
|
||||
- family identity와 component 목록
|
||||
- reference scale와 near-zero band
|
||||
- 전체 행 수와 near-zero 행 수
|
||||
- 각 행의 원본 FESA/reference 값, absolute error, 적용된 분기 및 판정
|
||||
- 일반 상대오차 행의 relative error
|
||||
- family scale-relative RMS와 판정
|
||||
- maximum absolute error와 worst row를 진단 정보로 기록하되 독립 gate로 사용하지 않음
|
||||
- blocking 실패와 warning-only exceedance의 구분
|
||||
|
||||
## 현재 B33/MITC4 Evidence
|
||||
|
||||
MSVC Debug build와 214개 CTest를 통과한 현재 comparator가 다음 ledger를 생성했다.
|
||||
Reference artifact는 수정하지 않았으며 B33은 regenerated 20-row element-endpoint identity를
|
||||
직접 비교한다.
|
||||
|
||||
- `.harness/build/reference/cantilever-beam-b33/comparison.json`
|
||||
- `.harness/build/reference/mitc4-shell-s4-comparison/comparison.json`
|
||||
|
||||
| case | row gate | maximum family relative RMS | RMS gate | verdict |
|
||||
| --- | --- | ---: | --- | --- |
|
||||
| B33 direct endpoint | 모든 212 blocking 행 통과 | 약 `1.17e-8` | 통과 | 통과 |
|
||||
| MITC4 U | 모든 blocking 행 통과 | 약 `1.03e-3` | 통과 | 통과 |
|
||||
| MITC4 UR | 모든 warning-only 행 통과 | 약 `2.98e-3` | 통과 | warning 없음 |
|
||||
|
||||
개별 component scale을 사용하면 MITC4 `U1/U2`의 최대 reference가 약 `3.12e-23`이어서
|
||||
near-zero 행 판정과 relative RMS가 실패한다. Translation family의 `U1/U2/U3`가 공통
|
||||
scale을 사용하면 물리적으로 zero-like인 in-plane residue를 실제 translational response와
|
||||
같은 차원에서 판정할 수 있다.
|
||||
|
||||
## 검토한 대안
|
||||
|
||||
### Component별 scale
|
||||
|
||||
규칙은 단순하지만 component 전체가 zero-like이면 수치 residue 자체가 scale이 된다. 현재
|
||||
MITC4 `U1/U2`가 실패하므로 채택하지 않는다.
|
||||
|
||||
### Reference RMS를 분모로 한 relative L2 norm
|
||||
|
||||
`sqrt(sum(error^2)/sum(reference^2))`는 일반적인 상대 norm이지만 zero-dominant component나
|
||||
family에서 분모가 불안정하거나 정의되지 않는다. Near-zero 행 규칙과 동일한 scale 의미를
|
||||
공유하지 않으므로 공통 정책으로 채택하지 않는다.
|
||||
|
||||
### Near-zero 보정 row-relative error의 RMS
|
||||
|
||||
각 행의 분모를 `max(abs(reference), near_zero_band)`로 바꿔 RMS를 계산할 수 있다. 그러나
|
||||
행별 near-zero 판정을 다시 집계해 같은 오차를 중복 평가하고 해석이 복잡해지므로 채택하지
|
||||
않는다.
|
||||
|
||||
## 구현 및 계약 정렬 범위
|
||||
|
||||
이 문서의 승인은 목표 tolerance 정책을 고정하지만 기존 comparator와 기능 문서를 즉시
|
||||
변경하지 않는다. 후속 implementation plan은 TDD로 다음을 함께 정렬해야 한다.
|
||||
|
||||
- 공통 comparison policy와 metric 구현
|
||||
- B33 및 MITC4 comparator의 공통 정책 사용
|
||||
- positive, boundary, zero-scale, near-zero, nonfinite 및 row-identity 테스트
|
||||
- B33와 MITC4 requirements, numerical review, reference model, I/O 및 reference comparison
|
||||
문서의 tolerance 부분
|
||||
- `AGENTS.md`와 `docs/ADR.md`의 기존 B33/MITC4 tolerance 결정을 새 공통 정책으로 대체
|
||||
|
||||
기존 reference input과 CSV는 생성, rename, rewrite 또는 보정하지 않는다. Production solver
|
||||
formulation, HDF5 output schema, component mapping, blocking/warning-only 분류 및 physics gate도
|
||||
이 tolerance 변경으로 수정하지 않는다.
|
||||
@@ -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_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user