chore: remove retired workflow and historical material

This commit is contained in:
KOKO\Mimi
2026-10-17 22:39:47 +09:00
parent 4bd19f5bf0
commit 774b80099c
768 changed files with 0 additions and 54164 deletions
-158
View File
@@ -1,158 +0,0 @@
# Shell Transient Heat Transfer 결정 기록
- Feature ID: `shell-transient-heat-transfer`
- Owner: 메인 assistant
- 상태: Accepted (구현 검증 완료와 구별)
- 승인 근거: ready 인터뷰의 개별 선택, 최종 “네 승인합니다”, 후속 `dryforge:go` 요청.
기준: [프로젝트 지침](../../AGENTS.md), [프로젝트 PRD](../PRD.md),
[공통 ADR](../ADR.md#adr-028). 요구사항과 현재 checklist는 [PRD](PRD.md),
지역 소유권은 [FEATURED_AGENTS](FEATURED_AGENTS.md)를 따른다.
아래 결정은 이전 Proposed 상태를 대체하며 실제 계산 결과의 정확도 승인을 의미하지 않는다.
수식 원본은 [formulation](formulation.md), 독립 판정은 [numerical-review](numerical-review.md)다.
## STH-ADR-001: Uncoupled transient 범위
- 상태: Accepted
- 결정: 구조해석과 분리된 단일 Shell 과도 열전달 procedure를 구현한다.
- 이유: 사용자가 “지금은 uncoupled heat transfer를 구현합니다”라고 명시했다.
- 영향: 열응력·변형 피드백·양방향 연성은 장기 목표이며 이번 acceptance에서 제외한다.
## STH-ADR-002: 다섯 두께 온도점
- 상태: Accepted; 초기 두께 등온 선택을 대체
- 결정: 균질 centered section의 온도점은 항상 다섯 개다. 면내 및 두께 전도를 계산한다.
Source DOF 11/13/15는 bottom/midsurface/top이다.
- 이유: 기존 DS4 reference와 사용자가 선택한 두께 방향 범위를 유지한다.
- 영향: 적층·사용자 선택 온도점 수·삼각형은 제외하며 Abaqus 내부 정식화 동등성을 주장하지 않는다.
## STH-ADR-003: 상수 물성과 제한된 열입력
- 상태: Accepted; edge 부분은 STH-ADR-012로 대체
- 결정: 양의 유한 상수 k/rho/c와 요소 집합별 재료·일정 두께를 사용한다.
초기 온도는 node/Nset별로 지정하되 한 절점의 두께 전체에 동일하다.
지정 온도와 집중 열입력은 source DOF 11/15에만 허용하며 step 중 일정하다.
- 이유: 사용자 선택 범위에서 독립 검증 가능한 첫 열해석 기능을 구성한다.
- 영향: 온도 의존·이방성·표면 분포 flux·대류·복사·내부 발열·내부 station 직접 입력은 제외한다.
Concentrated P는 signed power이며 h나 면적을 곱하지 않는다. 중복 하중은 source 순서로 합한다.
## STH-ADR-004: 모든 완료 증분과 초기 온도 저장
- 상태: Accepted
- 결정: 초기 raw 온도와 모든 완료 증분의 온도·flux·반력 열률을 공식 HDF5에 저장한다.
- 이유: 한 시점의 reference 외에도 전체 과도응답을 검증해야 한다.
- 영향: Output no-op 요청이 필수 저장량을 줄이지 않는다. 초기 flux/reaction dataset은 없다.
종료 규칙은 009, 영속 schema는 013을 따른다.
## STH-ADR-005: NT11 외부 비교와 독립 물리 검증
- 상태: Superseded by STH-ADR-015 (원본 비교의 blocking 분류에 한함)
- 결정: 기존 CSV의 Step-1, t=37, NT11을 필수 비교하고 나머지는 해석해·수지·수렴으로 검증한다.
- 이유: Reference의 실제 coverage는 단일 시점의 아랫면 온도 729행이다.
- 영향: 공통 TOLERANCE의 row/family 기준을 유지한다. 추가 Abaqus CSV나 Abaqus 실행은 필수가 아니다.
원본 음수·단위·시간을 보정하지 않는다. Source identity 오류는 tolerance 전에 거부한다.
## STH-ADR-006: 단일 step
- 상태: Accepted
- 결정: 초기 온도에서 일정한 지정 온도·열입력으로 하나의 transient step을 수행한다.
- 이유: 사용자의 명시 범위 선택이다.
- 영향: 여러 step, restart, 시간 변화 amplitude와 자동 시간 증분을 제외한다.
## STH-ADR-007: Q4×P2 두 구간과 consistent K/C
- 상태: Accepted; 이전 수치 방법 Proposed 상태를 대체
- 결정: 요소당 20 DOF, Q4 면내 보간과 C0 두 구간 P2 두께 보간을 사용한다.
K=integral(B^T k B), C=integral(rho c H^T H), 면내 3×3 Gauss와 두께 exact Z를 채택한다.
- 이유: 전도·열저장·반력·에너지를 같은 Galerkin 보간으로 연결하며 직접 54점 적분으로 검산할 수 있다.
- 대안·영향: Lumped C, reduced integration, hourglass, 인공 확산과 온도 clamp는 추가하지 않는다.
Consistent C는 급변 경계에서 positivity를 보장하지 않으며 일반 warped patch의 3×3은 exact가 아니다.
Reference 실패에 맞춰 방법을 변경하지 않는다. 수치 상세 기준은 구현 전 독립 검토한다.
## STH-ADR-008: 실제 midsurface metric과 normalized station 접속
- 상태: Accepted; 이전 물리 범위 검토 대기를 대체
- 결정: 실제 bilinear 3D midsurface의 metric/법선을 사용하고 두께 offset 곡률 보정을 생략한다.
공유 source node의 같은 normalized station을 접속하며 fold와 두께 단차도 같은 규칙을 사용한다.
- 이유: 승인한 4절점 Shell과 다섯 온도점을 일관된 product-domain으로 표현한다.
- 영향: 접합부의 상세 3D 전도·접촉 저항·추가 부피/용량을 해상하지 않는다.
방향이 일관된 manifold mesh만 허용하며 T-edge/vertex-only 접합을 거부한다.
전체 patch validity를 검증하고 수치적으로 증명할 수 없는 geometry는 진단한다.
임의 warp/aspect cutoff나 자동 normal flip·node weld로 지원 의미를 바꾸지 않는다.
## STH-ADR-009: Backward Euler와 마지막 짧은 증분
- 상태: Accepted; 이전 정수 배수 종료 제한 제안을 대체
- 결정: A=C/delta+K를 사용하고 동일 계수 factorization을 재사용한다.
기본 dt로 진행하고 종료까지 남은 구간만 단축한다. dt>T_end이면 한 증분이다.
- 이유: 사용자의 시간 제어 선택을 유지하며 정확한 종료 시점을 저장한다.
- 영향: 마지막 delta가 실제로 달라지면 재분해하고 RHS·반력·수지 모두 같은 delta를 사용한다.
n*dt 기반 시간격자의 표현오차와 INC 사전검사를 고정한다. 무구속 transient도 유효하다.
Generic all-constrained 처리는 algebra 검증이며 공개 내부 station BC 지원이 아니다.
## STH-ADR-010: Raw 초기값과 step BC override
- 상태: Accepted; 이전 초기 상태·default 검토 대기를 대체
- 결정: t=0은 raw 초기 온도이며 미지정 값은 0이다. 첫 양의 증분에서 g를 적용한다.
첫 RHS의 C_fc*T_c_old/delta를 보존한다. Model BC를 상속하고 같은 target의 step BC가 덮어쓴다.
- 이유: 원래 초기 상태와 첫 증분의 열저장·반력·에너지 변화를 보존한다.
- 영향: 각 단계 안의 동일 BC 중복은 허용하고 상충은 거부한다. Thermal override를 구조 절차에 전파하지 않는다.
생략 BC 값은 0이며 초기값을 g로 projection하지 않는다. 순간 impulse를 별도 해상하지 않는다.
## STH-ADR-011: 직접 flux와 full residual 반력
- 상태: Accepted; 이전 recovery 검토 대기를 대체
- 결정: q_global=-k B T를 면내 네 점×두께 여섯 trace에서 직접 복구한다.
Global q와 local frame을 저장하며 local q를 중복 필수 저장하지 않는다.
반력 열률은 C*DeltaT/delta+K*T-Q의 constrained 성분이고 양수는 모델로 공급되는 열이다.
- 이유: 온도 기울기와 열저장을 포함한 반력을 서로 다른 observable로 검증한다.
- 영향: 0-/0+와 요소 경계 값을 평균하지 않는다. Free residual·열에너지는 내부 검증 evidence다.
Fourier flux 경계 적분으로 FE 반력을 대체하지 않는다. 초기 반력을 0이나 NaN으로 저장하지 않는다.
## STH-ADR-012: Edge 분포 열입력 연기
- 상태: Deferred; 이전 edge kernel·syntax 제안을 대체
- 결정: FEM-REQ-SHELL-THERMAL-011을 이번 구현에서 제외한다. Edge kernel·keyword·공개 테스트를 추가하지 않는다.
- 이유: Ready에서 사용자가 집중 열입력만 먼저 구현하는 범위를 승인했다.
- 영향: 이전 q_in*h*L와 edge weighting 유도는 이번 제품 계약이 아니다.
후속 지원에는 별도 단위·부호·대상·중복 의미 및 수치 검증이 필요하다.
## STH-ADR-013: 입력 subset과 영속 thermal schema
- 상태: Accepted
- 결정: [PRD STH-C02](PRD.md#sth-c02--공개-입력-충돌과-부가-keyword)의 입력과
[STH-C05](PRD.md#sth-c05--hdf5-외부-계약)의 schema version 1을 구현한다.
- 이유: Domain/source identity에서 결과 row까지 재현 가능한 외부 계약을 고정한다.
- 영향: Temperature N×5, global flux E×4×6×3, constrained reaction P를 저장한다.
P=0도 빈 dataset을 유지하며 t=0 flux/reaction은 dataset 자체가 없다.
지정된 no-op만 warning과 함께 허용하고 unknown keyword는 오류다.
초기/완료 frame, mapping·units·finite 값을 atomic finalize 전에 self-check한다.
## STH-ADR-014: Candidate 보존과 검증 evidence
- 상태: Accepted
- 결정: Domain 정의, non-owning AnalysisModel, DofManager numbering, thermal mutable state를 분리한다.
Solve/recovery/검증을 마친 candidate만 반영하고 전체 성공 후 최종 HDF5를 원자 교체한다.
- 이유: 실패가 이전 accepted state와 기존 최종 결과를 오염시키지 않도록 한다.
- 영향: Deterministic COO reduction과 기존 CLI/lease/exit 분류를 유지한다.
전체 baseline을 먼저 재현하고 완료 시 전체 검사를 다시 실행한다.
재현된 무관한 HHT temporal 실패만 별도 미해결로 둘 수 있으며 새 실패·악화를 허용하지 않는다.
이를 전체 CTest green으로 보고하지 않는다. 열해석 필수 검증 실패는 면제되지 않는다.
## STH-ADR-015: 원본 reference 진단 전환과 새 모델 비교
- 상태: Accepted; 사용자 선택 “A, reference 모델을 변경하겠습니다.”
- 결정: 승인된 Q4×P2, consistent C, raw 초기값 및 Backward Euler 정식화를 유지한다.
기존 `reference/shell-thermal` Step-1/time 37/NT11 family는 진단으로 분류하고
공통 tolerance로 계산한 row/family 실패 수치를 그대로 남긴다. 사용자는 새 reference
모델과 CSV를 기존 원본과 분리된 경로에 두기로 결정했다. 새 파일이 확정되면 별도의
필수 family로 비교한다.
- 이유: 원본 729행 중 87행 실패와 RMS/S=0.04967408461567323 초과가 있었고,
FESA와 독립적으로 승인 식을 계산한 oracle도 같은 차이를 재현했다.
[충돌 evidence](reference-comparison-conflict.md)는 원본의 blocking 조건과 승인 방법을
동시에 만족하지 못함을 보여준다.
- 영향: 원본 artifact와 공통 tolerance는 수정하지 않는다. 진단 실패를 PASS로 바꾸지
않고 row identity·오차를 보고한다. 새 모델의 경로, 원본 hash, 비교 시점·성분·identity가
아직 제공되지 않았으므로 새 필수 비교의 실행·통과는 미완료다. 독립 해석해·수지·수렴성,
전체 회귀 및 physics 검토를 완료 조건으로 유지한다. 이는 Abaqus 내부 알고리즘의
동등성이나 기존 reference가 잘못되었다는 판정이 아니다.
@@ -1,24 +0,0 @@
# shell-transient-heat-transfer 문서·작업 지침
- Feature ID: `shell-transient-heat-transfer`
- 문서 진입점·목적·사용법·Handoff: [README](README.md).
- 현재 범위·checklist: [PRD](PRD.md); 결정·승인·대체 이력: [ADR](ADR.md).
- 적용 범위: 해당 기능의 문서. 자동 로드되는 코드 영역 `AGENTS.md`가 아니다.
기준: [프로젝트 지침](../../AGENTS.md), [ARCHITECTURE](../ARCHITECTURE.md),
[Feature 문서 관리](../FEATURE_DOCUMENTATION.md), [공통 tolerance](../TOLERANCE.md).
## 보존할 계약과 인계
수식·I/O·수치 기준의 원본은 README에서 연결한 전문 문서를 따른다. 기능을 재개할 때
관련 계약과 실제 실행 revision·미해결 항목을 함께 읽는다. 과거 stage pass나 현재 문서
정리를 solver 검증 통과로 계산하지 않는다. 요구사항·ADR ID와 승인 이력을 유지한다.
메인은 PRD·ADR·진입점·문서 참조를 관리하고 전문 작업자는 할당한 본문과 관련 범위만 수정한다.
기존 개인 설정·reference·다른 작업자의 변경을 보존하며 실패를 tolerance 완화로 숨기지 않는다.
문서만 수정하면 정적 검토를 수행하고, 동작 변경에는 관련 검사와 필요한 독립 검토를 수행한다.
Uncoupled Q4×two-P2·consistent C·Backward Euler가 현재 범위다. DOF11/13/15는 bottom/mid/top이고
공개 지정온도·열률은11/15만 지원한다. 중립면의 양쪽 flux trace를 평균하지 않는다.
원본 NT11은 수치 FAIL을 보존한 diagnostic이며 새 필수 reference는 준비 중이다.
Edge 열입력·내부 station BC·열-구조 연성을 확대하지 않는다. Private full-end fixture를
공개 입력 지원이나 접합부의 실제3D 전도 검증으로 해석하지 않는다.
-458
View File
@@ -1,458 +0,0 @@
# Shell Transient Heat Transfer 요구사항과 승인 계약
- Feature ID: `shell-transient-heat-transfer`
- Owner: 메인 assistant
- 상태: 사용자 승인 계약; 구현·검증 진행 중
- 승인 근거: ready 인터뷰의 명시 선택, 최종 “네 승인합니다”, 후속 `dryforge:go` 실행 요청.
기준: [프로젝트 지침](../../AGENTS.md), [프로젝트 PRD](../PRD.md),
[공통 ADR](../ADR.md#adr-028), [ARCHITECTURE](../ARCHITECTURE.md),
[CODINGSTYLE](../CODINGSTYLE.md), [TOLERANCE](../TOLERANCE.md).
지역 지침은 [FEATURED_AGENTS](FEATURED_AGENTS.md), 결정 근거는 [ADR](ADR.md),
수식은 [formulation](formulation.md), 구현 순서는 [design-plan](design.md#design-plan),
독립 수치 판정은 [numerical-review](numerical-review.md)를 따른다.
개인 연구용 솔버가 일차 목적이며 외부 배포와 양방향 열–구조 연성은 장기 목표다.
현재 목표는 3차원 공간의 4절점 shell에 대한 단일 step uncoupled transient heat transfer다.
입력 의미, 열 방정식, 시간 적분, 결과 복구, HDF5 저장과 독립 검증을 함께 구현한다.
채택 승인은 정확도 통과 evidence가 아니다. 아래는 승인된 실행 명세를 영속 문서에 반영한 계약이다.
초기 제안 중 edge 입력 지원과 정수 배수 종료 제한은 아래 계약으로 대체한다.
## 요구사항과 경계
`FEM-REQ-SHELL-THERMAL-`를 아래 표의 ID prefix로 사용한다. 기존 ID를 재번호화하지 않는다.
| ID | 구현할 동작 |
| --- | --- |
| 001 | 구조해석과 분리된 과도 열전달 procedure |
| 002 | 3차원 공간의 DS4 4절점 shell; 평판·유효한 warped patch·곡면 mesh·접힌 판 |
| 003 | 절점마다 두께 온도점 5개, 면내와 두께 방향 전도 |
| 004 | 단면 전체가 같은 재료인 centered homogeneous shell |
| 005 | 상수 등방성 양의 유한 열전도율 k, 밀도 rho, 비열 c |
| 006 | 요소 집합별 서로 다른 재료 |
| 007 | 요소 집합별 양의 유한 두께 h; 요소 내부에서는 일정 |
| 008 | 절점/절점 집합의 초기 온도; 동일 절점의 5점에는 같은 값 |
| 009 | 아랫면·윗면의 독립적인 지정 온도 |
| 010 | 아랫면·윗면의 독립적인 절점 집중 열입력 |
| 011 | **이번 구현에서 연기:** 모서리 분포 열입력. Kernel·keyword·공개 테스트 지원을 추가하지 않음 |
| 012 | 지정 온도와 열입력은 step 중 일정 |
| 013 | 사용자 지정 총 시간과 기본 시간 간격; 마지막 증분만 필요 시 단축 |
| 014 | 온도·요소 전역 열유속·지정 온도점의 반력 열률 |
| 015 | 초기 온도와 모든 완료 증분을 공식 HDF5에 저장 |
| 016 | 원본 reference Step-1, t=37, NT11의 진단 외부 비교와 새 reference 모델의 필수 비교 |
| 017 | 전체 온도·flux·반력에 대한 해석해·수지·시간 및 공간 수렴 검증 |
| 018 | 하나의 transient step만 지원 |
추가 상세 계약은 이 문서의 `STH-C01`–`STH-C08`이다. 위 요구사항의 구체화이며 별도 기능을
추가하지 않는다. 제외: 열–구조 연성, 삼각형, 적층, 이방성·온도 의존 물성, 잠열,
대류·복사·내부 발열, 윗면/아랫면 분포 열유속, 모서리 분포 입력, 두께 비균일 초기 온도,
내부 온도점 직접 BC/CFLUX, 절점별 두께, 사용자 선택 온도점 수, 시간 이력·amplitude,
적응 시간 간격, 여러 step, restart, Abaqus 내부 정식화 동등성.
## STH-C01 — 모델, geometry와 자유도
### Source 의미
- Shell geometry 절점은 midsurface에 있다. Source DOF `[11,12,13,14,15]`는 각각
`zeta=[-1,-1/2,0,1/2,1]`이며 11은 bottom, 13은 midsurface, 15는 top이다.
- 요소 절점 순서는 자연좌표 `(-1,-1),(1,-1),(1,1),(-1,1)`이다. 요소 로컬 DOF는
node-major `p=5*(i-1)+a`, i=1..4, a=1..5의 20개다.
- Source instance/label, Domain index, full DOF, equation index는 서로 다른 identity다.
집합을 전개해도 source row와 target identity를 진단까지 보존한다.
- 모든 열 방정식은 활성 DS4에 연결된 온도 DOF에 대해 구성한다. 비활성/미연결 절점을
열 방정식에 넣어 0 용량 행을 만들지 않는다. 그런 절점을 명시적으로 대상으로 하는 열
initial/BC/load는 유효한 열 target이 아니므로 모델 오류다. 일반 source model의 미사용
절점은 보존할 수 있으며 결과의 활성 절점 mapping으로 구분한다.
- 한 열해석 모델에 structural B33/S4/S4R 또는 coupled 요소를 혼합하지 않는다.
각 DS4에는 정확히 하나의 shell section과 thermal material이 연결되어야 한다.
Section 중복 지정은 값이 같아도 오류이며, set 내부의 동일 member는 한 번만 전개한다.
### Geometry와 접속
`r=sum(N_i*x_i)`, `a1=r_,xi`, `a2=r_,eta`, `g=A^T A`, `A=[a1,a2]`,
`Js=|a1 cross a2|`, `n=(a1 cross a2)/Js`를 평가점별로 사용한다.
Warped 요소를 중심 평면에 투영하지 않는다.
`grad_s(N_i)=A*g^-1*[N_i,xi,N_i,eta]^T`,
`grad_*T=grad_s(T)+n*(2/h)*T_,zeta`, `dV*=Js*(h/2)*dxi*deta*dzeta`다.
실제 offset surface의 곡률에 따른 metric·면적 변화는 포함하지 않는다.
이는 두께 방향 등온 가정이 아니다. 얇은 shell product-domain 모델의 한계를 문서화하며,
큰 h/곡률 또는 초기 두께 경계층에 대한 정량 정확도를 검증 없이 보장하지 않는다.
공유 source node의 같은 normalized zeta 온도는 공유한다. 서로 다른 두께에서도 같으며,
실제 물리 z 좌표 일치, 접촉 열저항, 접합부의 유한 부피·추가 용량을 의미하지 않는다.
접힌 판·두께 단차의 국소 3D 전도장은 이번 모델의 해상 대상이 아니다.
Topology는 방향이 일관된 manifold shell 표면이다. 하나의 edge에 최대 두 요소가 접속하고
공유 edge를 따라 절점 순서는 반대여야 한다. 불일치는 오류이며 자동으로 뒤집지 않는다.
T자 분기 edge, vertex-only 접합과 nonmanifold vertex fan은 거부한다.
분리된 여러 shell 영역은 허용하며 좌표만 같은 서로 다른 source node를 자동 병합하지 않는다.
서로 다른 instance도 명시적인 공유 source identity가 없으면 연결되지 않는다.
유한 좌표, 서로 다른 4절점, 양의 Js와 비특이 metric, 자기 교차 없는 patch를 요구한다.
적분점 몇 곳의 검사만으로 전체 patch 유효성을 증명했다고 하지 않는다.
Roundoff를 고려한 전체 patch의 유효성 판정과 geometry 오차 분리는 STH-C07의 선행
수치 계약 작업에서 고정한다. 양의 작은 Jacobian을 clamp하거나 구조 shell의 drilling/
director/두께 비율 제한을 검토 없이 전용하지 않는다.
## STH-C02 — 공개 입력, 충돌과 부가 keyword
공통 ASCII·대소문자·source ordering·single physical file·part/identity-instance·set 규칙은
[공통 입력 계약](../contracts.md)의 기존 계약을 따른다. 열 입력의 추가 subset은 다음과 같다.
| 입력 | 허용 형식과 의미 |
| --- | --- |
| `*ELEMENT, TYPE=DS4` | 4절점; 기존 선택적 ELSET 및 topology 구문 사용 |
| `*SHELL SECTION, ELSET=..., MATERIAL=...` | 단일 row `h` 또는 `h,5`; 온도점 수 생략은 5. 명시 값은 5만 허용; offset/composite/nodal thickness 제외 |
| `*CONDUCTIVITY` | parameter 없는 상수 k 한 row; 선택적 마지막 빈 field 하나 허용 |
| `*DENSITY` | 기존 상수 rho 구문; 양의 유한 값 |
| `*SPECIFIC HEAT` | parameter 없는 상수 c 한 row; 선택적 마지막 빈 field 하나 허용 |
| `*INITIAL CONDITIONS, TYPE=TEMPERATURE` | model-level, `node-or-nset,T`; 각 target의 5점에 같은 유한 T |
| `*BOUNDARY` | model 또는 step, `target,dof,dof[,T]`; dof는 11 또는 15, T 생략은 0 |
| `*CFLUX` | step-level, parameter 없이 `target,dof,P`; dof 11 또는 15, 유한한 signed P |
| `*STEP` | 기존 NAME, INC; NLGEOM은 생략 또는 NO, PERTURBATION 제외 |
| `*HEAT TRANSFER` | END 생략 또는 PERIOD; 한 row `dt,T_end` 뒤 최대 3개의 빈 field 허용 |
| `*END STEP` | 단일 step 종료 |
추가 material temperature/field columns, ANISOTROPIC/ORTHOTROPIC, STEADY STATE,
DELTMX, 시간증분 최소/최대의 명시 숫자, OP/AMPLITUDE 등 미지원 option은 거부한다.
`*BOUNDARY`의 11..15 범위 표기는 내부 station을 포함하므로 거부한다.
물성·두께·시간은 양의 유한 값이며 온도는 signed finite scalar다. 섭씨/절대온도 단위를
숫자로 추정하지 않는다. Reference 파일의 상수 값을 변환하지 않는다.
미지정 초기 온도는 0이다. 같은 초기 선언에서 집합이 겹치면 동일 값은 허용하고 다른
값은 오류다. BC는 model 단계와 step 단계 각각에서 target을 완전히 전개하여 충돌을
확인한 뒤 step의 값이 동일 node/DOF에 대한 model 값을 덮어쓴다. 각 단계 안의 동일
값 중복은 허용하며 다른 값은 오류다. 이 override는 thermal에만 적용한다.
집중 입력은 source row 순서로 더한다. 양수 P는 모델 안으로 공급되는 열률이며 음수는
열 제거다. P에 h·면적·shape weight를 곱하지 않는다. 지정 온도와 겹치는 입력도 full Q에
유지하여 반력에 반영한다. 온도 또는 열입력이 지정되지 않은 외부 경계는 자연 단열이다.
Reference에 있는 다음 부가 구문만 열 procedure의 no-op으로 허용한다. Data row가
필요 없는 구문에 data를 추가하거나 미지원 option을 붙이면 오류다. 허용한 각 no-op
block에 source 위치를 갖는 warning을 하나씩 남기며 필수 결과를 줄이지 않는다.
- `*PREPRINT, ECHO=NO, MODEL=NO, HISTORY=NO, CONTACT=NO`.
- `*RESTART, WRITE, FREQUENCY=0`: restart 파일을 생성하지 않는다.
- `*OUTPUT, FIELD, VARIABLE=PRESELECT` 및 `*OUTPUT, HISTORY, FREQUENCY=0`.
Child NODE/ELEMENT OUTPUT 및 그 밖의 output 설정은 제외한다.
- 하중이 참조하지 않는 assembly `*SURFACE, TYPE=ELEMENT, NAME=...`:
기존 target 및 SPOS/SNEG facet 의미를 검증하고 보존하되 열하중으로 사용하지 않는다.
- Model-level `*PHYSICAL CONSTANTS, ABSOLUTE ZERO=<finite>`:
이번 상수 물성·비복사 모델에서 온도 변환이나 에너지 영점 이동에 쓰지 않는다.
Unknown/unsupported keyword를 일반적으로 무시하는 fallback은 없다.
## STH-C03 — 행렬과 시간 적분
두께는 `[-1,0]`, `[0,1]`의 C0 quadratic 구간 둘로 구성한다. 각 구간에서
`s=2*zeta+1` 또는 `s=2*zeta-1`로 두고
`l=[s(s-1)/2, 1-s*s, s(s+1)/2]`를 각각 station `(1,2,3)`과 `(3,4,5)`에 놓는다.
`dL/dzeta=2*dl/ds`이며 중립면의 온도는 하나지만 미분의 두 trace는 별도다.
`H_ia=N_i*L_a`, `B_ia=L_a*grad_s(N_i)+n*N_i*(2/h)*L_a,zeta`다.
```text
M_ij = integral_surface N_i N_j dA
S_ij = integral_surface grad_s(N_i) dot grad_s(N_j) dA
Z0_ab = integral_-1^1 L_a L_b dzeta
Z1_ab = integral_-1^1 L_a,zeta L_b,zeta dzeta
C_e = rho*c*(h/2) * (M tensor Z0)
K_e = k * [(h/2)*(S tensor Z0) + (2/h)*(M tensor Z1)]
Z0_segment = [[4,2,-1],[2,16,2],[-1,2,4]] / 30
Z1_segment = [[7,-8,1],[-8,16,-8],[1,-8,7]] / 3
```
각 segment의 3×3 행렬을 공유 station 3에 조립한다. 면내 production 적분은 3×3
Gauss, 두께는 위 exact Z다. 동일 보간의 직접 54점 적분과 교차 검산한다.
Affine 평판의 exact 면내 2×2 검산과 production 규칙을 혼동하지 않는다.
Structural 질량을 C로 재사용하지 않으며 lumping, reduced integration, hourglass,
인공 확산·온도 clamp를 추가하지 않는다. Consistent C는 급변 BC에서 undershoot를
만들 수 있으므로 온도 범위 초과만으로 성공/실패를 결정하지 않는다.
기본 dt>0, 총 시간 T_end>0에서 종료까지의 증분을 구성한다. 마지막 구간이 남으면 그
길이 delta로 단축한다. dt>T_end이면 delta=T_end인 한 증분이다. 적응 제어는 없다.
일반 시점은 누적 덧셈 대신 n*dt로 만들고 마지막 저장 시점은 입력 T_end다.
정수 배수의 부동소수점 표현 오차 때문에 거의 0인 가짜 증분을 만들지 않는다.
시간 비교의 roundoff bound는 입력·곱셈·나눗셈 연산을 근거로 STH-C07에서 고정한다.
정상 간격과 표현 오차 수준에서 같은 마지막 간격에는 같은 계수를 사용하며, 실제로
단축된 마지막 간격은 모든 방정식·반력·에너지에 동일한 delta를 사용한다.
시점이 유한하고 엄격히 증가해야 하며 표현할 수 없는 격자는 진단으로 거부한다.
INC는 기존 STEP 기본 100 또는 명시 양의 uint32다. 필요한 완료 증분 수가 INC를
넘으면 실행 전에 오류로 처리하며 중간 결과를 성공 결과로 저장하지 않는다.
각 증분의 Backward Euler 식은 다음과 같다.
```text
A = C/delta + K
A_ff*T_f_new = Q_f + C_ff*T_f_old/delta + C_fc*T_c_old/delta - A_fc*g_new
T_c_new = g_new
```
입력 초기 T_raw를 t=0에 보존하며 첫 old state로 그대로 쓴다. t=0에 g로 projection하지
않는다. 따라서 첫 증분의 `C_fc*(T_c_old-g_new)/delta`는 생략할 수 없다.
이 정책은 초기 불연속을 첫 유한 증분에 반영하며 순간 impulse를 해상한다는 뜻은 아니다.
동일 계수의 A_ff factorization은 재사용하고 마지막 delta가 바뀌면 다시 구성·분해한다.
무구속 단열 모델도 C가 양정치여서 transient A는 양정치다. 연결 component별 K의 상수
null mode를 과도계의 singular error로 오인하지 않는다. Generic 0×0 free partition은
solve 없이 처리하되 공개 입력은 내부 station을 구속하지 못하므로 all-constrained는
algebra fixture로만 검증한다.
## STH-C04 — 열유속, 반력과 수지
각 완료 온도장으로 `q_global=-k*B*T_e`를 직접 평가한다. 단위 차원은 power/area다.
`e1=a1/|a1|`, `e2=n cross e1`, `R=[e1,e2,n]`이고 후처리 local 성분은 `R^T*q_global`다.
Local q3는 bottom에서 top으로 흐를 때 양수다. Local 성분을 중복 필수 저장하지 않는다.
면내 점 순서는 `(-g,-g),(g,-g),(g,g),(-g,g)`, g=1/sqrt(3)이다.
각 점에서 두께 순서는 `(-1,+),(-1/2,interior),(0,-),(0,+),(1/2,interior),(1,-)`로
고정한다. 총 24개/요소다. 부호는 해당 두께 위치로 접근하는 쪽이며 바깥 법선 부호가 아니다.
중립면 양쪽, 재료 경계와 인접 요소의 값을 평균하지 않는다. Nodal smoothing/extrapolation을
공식 flux로 추가하지 않는다. Point source 부근의 mesh 독립적인 유한 flux를 보장하지 않는다.
```text
r = C*(T_new-T_old)/delta + K*T_new - Q
reaction = r의 constrained 성분 [power, 양수: 모델에 공급]
free residual = r의 free 성분 [내부 검증 evidence]
DeltaE = 1^T*C*(T_new-T_old) [energy]
DeltaE = delta*(sum(Q)+sum(reaction)+sum(free residual))
```
물리 수지 defect와 free residual 크기를 별도로 검사한다. 위 등식이 대수적으로 맞는다는
사실만으로 free equation 정확도를 주장하지 않는다. 에너지 영점을 자동 추정하지 않으며
온도 기준 이동에 대한 에너지 변화의 불변성을 검사한다. Fourier flux의 경계 적분은
일반적으로 FE 반력과 같지 않으며 반력 대신 사용하지 않는다. BE의 C-norm 소산도
실제 열에너지 보존과 별도 검증한다. t=0 반력은 미정의다.
## STH-C05 — HDF5 외부 계약
CLI는 기존 `fesa.exe <model.inp>`이고 output은 lexical sibling `<stem>_results.h5`다.
새 thermal 형식은 기존 structural 형식을 변경하지 않는다. 아래 모든 필드는 mandatory다.
F64/U64/U8/I8은 고정 폭 little-endian, 문자열은 UTF-8다. Compound는 field/type/extent로
읽으며 메모리 padding을 계약으로 삼지 않는다. Float 결과는 모두 finite다.
### Metadata와 model
`/metadata` scalar attributes:
| 이름 | 값/형식 |
| --- | --- |
| schema_version | U64, 1 |
| feature_id | `shell-transient-heat-transfer` |
| analysis_procedure | `uncoupled-transient-heat-transfer` |
| source_step_name | 입력의 원문 step 이름, 생략 시 Step-1 |
| solver_version | 기존 build version |
| source_input_identity | 실제 읽은 입력 byte의 기존 identity 계약 |
| unit_system_label | `user-consistent-unspecified` |
| internal_formulation | `FESA-Q4-P2x2-thermal-shell` |
| integration_rule | `surface-3x3-gauss; thickness-exact-P2x2` |
| time_integration | `backward-euler` |
| nominal_time_increment | F64, 입력 dt |
| time_period | F64, 입력 T_end |
Model의 기본 row 순서는 기존 Domain의 stable source declaration/instance expansion 순서다.
| Dataset | Shape/field와 의미 |
| --- | --- |
| `/model/nodes` | 기존 node compound: internal_node_id U64, instance_name/string, source_label/string, coordinates F64[3] |
| `/model/elements` | thermal DS4 row: internal_element_id U64, instance_name/string, source_label/string, source_element_type=`DS4`, internal_formulation/string, node_internal_ids U64[4], shell_section_internal_id U64, material_internal_id U64 |
| `/model/thermal_materials` | internal_material_id U64, name/string, conductivity F64, density F64, specific_heat F64 |
| `/model/shell_sections` | internal_section_id U64, source_file/string, source_line U64, source_elset/string, material_internal_id U64, thickness F64 |
| `/model/thermal_node_ids` | U64[N], 활성 온도 절점의 internal_node_id, 원래 node 순서를 유지 |
| `/model/thermal_element_ids` | U64[E], flux 요소 행에 대응하는 internal_element_id, 원래 element 순서 유지 |
| `/model/temperature_dofs` | U64[5]=[11,12,13,14,15] |
| `/model/temperature_zeta` | F64[5]=[-1,-0.5,0,0.5,1] |
| `/model/thermal_constraint_mask` | U8[N,5], free=0/constrained=1 |
| `/model/thermal_reaction_dofs` | compound[P]: internal_node_id U64, source_dof U64; mask의 1인 항목을 thermal node 행/DOF 순서로 나열 |
| `/model/thermal_flux_inplane_coordinates` | F64[4,2], 위 xi/eta 순서 |
| `/model/thermal_flux_zeta` | F64[6]=[-1,-0.5,0,0,0.5,1] |
| `/model/thermal_flux_trace_side` | I8[6]=[1,0,-1,1,0,-1]; 0=interior, +/-1=위의 접근 방향 |
| `/model/thermal_flux_local_frames` | F64[E,4,3,3], 마지막 두 축은 global component와 local axis; R의 열이 e1,e2,n |
ID는 해당 model table에 존재해야 하며 중복·누락 mapping은 self-check 실패다.
Reaction mapping은 source DOF 11/15만 포함한다. P=0이면 길이 0인 dataset을 유지한다.
Material/section의 source provenance는 기존 모델 writer convention을 보존한다.
Structural elastic E/nu나 displacement mask를 thermal material/DOF 대신 기록하지 않는다.
### Frames
Storage slot은 `/steps/Step-1`로 고정하며 실제 step 이름은 metadata로 구별한다.
`/steps/Step-1/accepted_frames`는 compound[N_step+1]이고 field는
`frame_index U64`, `increment_index U64`, `time F64`, `time_increment F64`, `is_initial U8`다.
Frame/index는 0..N_step이고 서로 같다. 초기 행은 time=0, time_increment=0, is_initial=1,
나머지는 완료 시간과 해당 방정식에 사용한 양의 delta, is_initial=0이다.
각 `/steps/Step-1/frames/<decimal-index>` group에도 위 다섯 scalar attribute를 저장한다.
| Frame 상대 경로 | Shape | 초기 frame | 완료 frame |
| --- | --- | --- | --- |
| `nodal/temperature` | F64[N,5] | raw 초기 온도 | 완료 온도 |
| `nodal/reaction_heat_rate` | F64[P] | **dataset 없음** | constrained full residual, reaction mapping 순서 |
| `element/heat_flux` | F64[E,4,6,3] | **dataset 없음** | global qx,qy,qz |
Temperature attributes는 `component_order=NT11,NT12,NT13,NT14,NT15`,
`units_label=temperature`, `coordinate_system=scalar`, `location=nodal-thickness-stations`다.
Reaction은 `component_order=RFL`, `units_label=power`, `coordinate_system=scalar`,
`location=prescribed-temperature-dofs`, `positive_direction=into-model`이다.
Flux는 `component_order=QX,QY,QZ`, `units_label=power/area`,
`coordinate_system=global-cartesian`, `location=element-inplane-thickness-traces`다.
Model geometry의 좌표·두께는 length, normalized coordinates/frames/masks/IDs는
dimensionless다. `/model/nodes`는 `units_label=length`,
`/model/shell_sections`는 `thickness_units_label=length`를 가진다.
`/model/thermal_materials`의 세 attribute는
`conductivity_units_label=power/(length*temperature)`,
`density_units_label=mass/volume`, `specific_heat_units_label=energy/(mass*temperature)`다.
`/steps/Step-1/accepted_frames`에는 `time_units_label=time`을 저장하며 time과
time_increment 모두에 적용한다. 서로 다른 단위의 compound field를 하나의
모호한 units_label로 합치지 않는다.
초기 frame의 누락 flux/reaction은 정해진 schema이며 NaN이나 계산된 0으로 대체하지 않는다.
Free residual·열에너지는 검증 보고서의 evidence로 유지하고 필수 공개 dataset을 추가하지 않는다.
Warnings는 기존 `/diagnostics` compound convention의 source/severity/code/message를 유지한다.
형식 discriminator, schema, mandatory field·shape·identity·finite value·frame 완전성을
atomic finalization 이전에 self-check한다. Unknown optional additive field를 무시할 수
있으나 잘못된 mandatory schema를 허용하지 않는다.
## STH-C06 — 런타임 소유권과 실패
Domain은 thermal material/section/initial/BC/load/step 정의를 소유하며 parsing 이후
불변이다. AnalysisModel은 non-owning active view, DofManager는 열 5개와 구조 6개의
layout, numbering, scatter, constraint 및 sparse pattern을 전담한다. Equation ID를
Node/Element에 분산시키지 않는다. Thermal kernel/procedure/state/recovery를 구분하고
구조 displacement API에 temperature를 위장하거나 structural mass로 C를 취급하지 않는다.
미래 coupling state를 미리 할당하지 않는다. Vendor type은 기존 adapter 경계 안에 둔다.
Assembly는 element-local contribution, stable COO ordering/reduction을 사용한다.
병렬 worker가 global CSR를 직접 갱신하지 않는다. 같은 입력은 같은 sparse structure,
축약 순서, diagnostic order와 result row order를 만든다.
증분마다 solve, full temperature 복구, residual/flux/finite/수지 검증을 끝낸 candidate만
accepted state에 반영한다. 실패 시 마지막 accepted state를 오염시키지 않는다.
최종 output은 전체 해석 성공과 writer self-check·close 후 원자적으로 교체한다.
실패 시 기존 최종 HDF5를 보존하며 로그는 실패 진단으로 갱신될 수 있다.
Output lease와 CLI exit 2/3/4/5/6의 기존 분류를 유지한다. 초기 해석 성공이나 이전
파일 존재만으로 이번 실행 성공을 표시하지 않는다. Failed trial을 accepted_frames에 넣지 않는다.
## STH-C07 — 독립 검증과 수치 계약의 선행 조건
채택된 수식의 일관성에 관한 기존 독립 검토와 실제 solver 정확도 검증을 구별한다.
수치 전문 작업은 kernel 구현 전에 [정식화](formulation.md)와 [독립 수치 검토](numerical-review.md)에 아래 세부 계약을 문서화하고 독립 numerical review를
통과해야 한다. 이는 기존 PRD가 numerical owner에게 맡긴 상세 설계 작업이다.
1. 전체 bilinear patch 유효성·orientation 판정, 비다양체 판정, scale-aware degeneracy
bound와 검증할 geometry군. 전역 자기 교차 검출이나 contact 기능을 추가하지 않는다.
2. Roundoff metric: 행렬 norm·연산 수·machine epsilon·conditioning을 근거로 exact
fixture 및 free residual의 bound를 도출한다. 예: backward error의 분모는
`||A_ff||*||T_f||+||b_f||`; 분모 0은 별도 처리한다. `max(1,scale)` floor 금지.
3. 에너지 defect는 저장·외력·반력 항의 절댓값 합을 기준으로 정의하고 free residual
평가와 분리한다. 이산 수지와 BE 소산 각각의 roundoff 전파를 문서화한다.
4. 3×3/5×5/7×7 quadrature 비교, 시간/공간 convergence의 norm·mesh·시간·series
절단 오차·판정 구간을 독립 oracle와 예상 asymptotic order에서 도출한다.
5. 고정 5점의 slab 오차 plateau와 초기 열침투 깊이 sqrt(alpha*t)를 보고하는 기준,
비교 시점과 각 물리량의 허용 이산 오차를 테스트 실행 전에 고정한다.
6. 시간격자 roundoff 동등성·increment count·overflow와 representability 판정을 고정한다.
이 수치 기준을 구현 결과에 맞춰 완화하거나 외부 reference 5%/1%를 내부 검사에
전용하지 않는다. 검토에서 3×3 방법 변경, 지원 geometry 축소, 추가 물리 또는 accuracy
acceptance 변경이 필요하면 구체적 근거를 제시하고 사용자 결정으로 돌아간다.
수치 상세 기준의 독립 검토가 끝나기 전 production kernel 착수를 통과로 표시하지 않는다.
필수 검증은 다음과 같다. 별도 허용을 뜻하는 fixture와 공개 CLI 사례를 구별한다.
| 검증 | 독립 oracle와 실패 검출 |
| --- | --- |
| Shape/K/C | sum(H)=1, sum(B)=0, 정확 M/S/Z, C SPD, K rank19/상수 null, symmetry, 양의 에너지, h 및 1/h scaling, 분리식/직접54점 일치 |
| 방향/flux | constant/면내선형/두께선형 patch, global rotation covariance, normal 반전과 station 재매핑, R 직교성 |
| 중립면 trace | kernel state (0,0,0,1,2)에서 q3(0-)=0, q3(0+)=-4k/h; 공개 초기조건 사례로 오인하지 않음 |
| 초기 jump | C=[[2,1],[1,2]], K=[[1,-1],[-1,1]], delta=1/2, old=(0,0), 두 번째 g=1: 첫 free=-1/5, 다음=1/25, 반력24/5와36/25 |
| 시간격자 | 정수배, 10/3 마지막1, dt>T, decimal roundoff, INC 초과, 비표현 가능 시간, 마지막 factorization 변경 및 실제delta 반력/수지 |
| 열저장/하중 | 단열 균일 온도 유지, no-BC transient, P 및 -P, overlapping source load 합산, constrained DOF에 P 유지, disconnected component별 보존 |
| Partition | 0×0 all-constrained generic algebra; 공개 내부 station BC 금지 유지 |
| 면내 해석해 | 단열 직사각형 T=T0+A*cos(pi*x/L)*exp(-alpha*(pi/L)^2*t), alpha=k/(rho*c); 온도와 미분 flux, 면내 L2 온도2차/gradient1차 |
| 시간 해석해 | 같은 고정 spatial discretization의 독립 eigenpair Kv=lambda Cv 및 exp(-lambda*t), BE 증폭률; dt refinement의 1차 오차 |
| 두께 slab | 균일 초기온도, bottom step 온도/top 단열 Fourier series; 5점 온도·flux·열에너지·반력, series 절단 bound 및 두께 오차 plateau |
| 재료/두께/접속 | full-end prescribed strip의 sum L/(k*h*w) 저항은 assembly fixture; 실제 bottom/top-only CLI는 해당 조건의 독립 oracle 사용. Fold 등온 및 조립 열률 수지 |
| Geometry | 왜곡/warped patch 적분 비교, scaling/translation, zero/negative/uncertain metric, 자기 교차, 방향 충돌, T edge와 vertex-only junction 음성 사례 |
| IO/실패 | 모든 양성/음성 subset, source identity, HDF5 round trip, 초기 dataset 부재, P=0 반력, 마지막delta, no-op warnings, candidate/lease/write/self-check/close 실패 시 기존결과 보존 |
구현 단계에는 열해석 관련 검사를 먼저 수행하고 전체 MSVC x64 Debug build/CTest와
확정된 새 reference의 필수 비교를 수행한다. 기존 reference의 진단 비교도 실패 수치까지
보고한다. 새 compiler warning을 추가하지 않는다.
구현 전 전체 검사를 실행하여 기존 HHT 시간수렴 실패 2건의 실제 재현·이름·metric을
기록한다. 기록만으로 현재 실패를 가정하지 않는다. 구현 후 전체 검사에서 제외하지 않고,
새 실패·기존 실패 악화를 허용하지 않는다. 재현된 기존의 무관한 HHT 실패만 별도
미해결로 유지할 수 있으며 전체 CTest green이라고 보고하지 않는다.
예상하지 못한 baseline failure는 자동 면제하지 않고 원인과 영향 범위를 확인한다.
열해석의 필수 검증 실패는 이 예외에 포함되지 않는다.
## STH-C08 — Reference 원본과 완료 evidence
대상 경로는 다음 두 개다.
- `reference/shell-thermal/shell-thermal.inp`
- `reference/shell-thermal/shell-thermal NodalTemperature.csv`
구현 preflight에서 동일 저장소의 기존 dev worktree에 있는
동일 상대 경로에서 byte 그대로 복사한다. `git worktree list --porcelain`으로 dev
worktree를 찾고 파일별 SHA256을 아래와 대조한다. 이 방식은 머신 절대경로를 계약에
고정하지 않는다. 원본이 달라졌거나 없으면 임의 생성·보정·대체하지 않는다.
| 파일 | 확인한 SHA256 |
| --- | --- |
| shell-thermal.inp | 955C8BEEC35CE53DA5F5DD48785CF8C12FCE726EBFDE7EAE40CF69DBF00D6EA4 |
| shell-thermal NodalTemperature.csv | 765B0D0F64C24CDE6D033D6EF80E0267275A496C97E28D932466EB877810D3E5 |
목적 파일이 이미 있으면 hash 일치를 확인해 재사용하고 다르면 덮어쓰지 않는다.
복사 전후 hash와 사용한 경로를 evidence에 남긴다. Abaqus를 실행하거나 원본 파일명,
내용, metadata를 수정하는 권한은 포함하지 않는다. FESA HDF5/log는 복사한 입력 옆에
생성한다. 보조 deterministic CSV view는 공식 결과가 아니다.
기존 외부 family는 `(shell-thermal, source step Step-1, time 37, bottom temperature NT11,
scalar, user-consistent temperature units, diagnostic)`이다. 원본은 단일 Part-1-1,
676 DS4, 729 node이며 CSV의 `Step,Frame,Node Label,NT11`에서 instance 열 부재는
입력의 단일 instance 및 label 집합 검증으로만 복원한다. HDF5의 source_step_name,
정확한 t=37 accepted frame, source DOF11을 일대일 대응한다. Nearest-time matching,
station 평균, 행 순서 추정, 잘못된 instance 추정은 금지한다.
Missing/extra/duplicate/nonfinite/identity 오류는 tolerance 전에 실패다.
[공통 tolerance](../TOLERANCE.md)의 S=max|reference|, B=0.01S, near-zero absolute<=B,
그 밖의 relative<=0.05, family RMS/S<=0.01, S=0 exact-zero 규칙을 그대로 적용한다.
관찰된 min=-15.5742216, max=1000은 정적 관찰이며 solver 통과 evidence가 아니다.
음수·미소값을 clamp하거나 온도 기준·단위를 조정해 비교를 통과시키지 않는다.
이 원본 비교의 tolerance 초과는 진단 실패로 남긴다. Row identity 오류와 metric을
숨기거나 원본 비교를 통과로 표시하지 않는다. 독립 계산과 실제 실패 evidence는
[충돌 보고서](reference-comparison-conflict.md)에 보존한다. 사용자가 새 reference 모델과
CSV를 기존 `reference/shell-thermal`과 분리된 경로에 두기로 결정했다. 해당 입력·CSV
경로, source row identity, 비교 시점·성분과 새 파일의 hash가 확정되면
이를 별도의 필수 family로 지정하여 실제 비교한다. 새 artifact가 아직 제공되지 않았으므로
새 family의 통과를 주장하지 않는다. 공통 tolerance 수치는 변경하지 않는다.
완료 보고서는 구현 diff, 실제 검증 명령/환경·결과, 독립 numerical/physics 검토,
외부 비교 row/family 판정, 기존 HHT 실패, 남은 물리 한계를 구분한다.
새 reference 모델과 그 결과 CSV가 확정되어야 새 필수 family를 실행할 수 있다.
이는 다른 물리량의 해석해·수지·수렴성 검증을 생략하는 근거가 아니다.
## 현재 checklist
- [x] 요구사항 인터뷰와 단일 uncoupled step 범위 확정
- [x] Q4×P2 두 구간, consistent C, 3×3/exact Z, BE 채택 승인
- [x] Geometry 접속 이상화, raw 초기값, 마지막 짧은 증분, 입력·HDF5 계약 승인
- [x] 모서리 분포 입력 011 연기 및 reference 복사 승인
- [x] 실행 계획 승인 및 구현 요청
- [x] 정식화 정합과 상세 수치 기준의 [독립 검토](numerical-review.md) 완료
- [x] Production 변경 전 전체 baseline과 reference hash evidence 기록
- [x] Thermal semantic model·DOF·geometry·K/C·flux 구현 및 [독립·통합 검증](implementation-verification.md#요소-kernel과-형상-검증)
- [x] Thermal 입력 subset·진단·결정적 조립·시간 적분 구현 및 [범위별 검증](implementation-verification.md#backward-euler-procedure와-candidate-검증)
- [x] HDF5·CLI·candidate와 기존 결과 보존 [검증](implementation-verification.md#application과-실제-cli)
- [x] 승인 정식화와 원본 reference 통과 조건의 [실측 충돌](reference-comparison-conflict.md)에 대한 사용자 결정: 기존 비교는 진단, 정식화 유지, 새 reference 모델 사용
- [x] [해석해·수지·수렴성 검증](implementation-verification.md#t10-해석해수지수렴성과-원본-진단-비교)과 원본 NT11 진단 비교의 실패 metric 보존
- [ ] 새 reference 모델·CSV·identity 확정 및 필수 외부 비교 통과
- [x] 전체 MSVC Debug build/CTest 및 [기존 실패 대비 확인](implementation-verification.md#통합-전체-회귀와-남은-완료-조건): 614 PASS·기존 2 FAIL·1 SKIP, 추가 139개 모두 PASS
- [x] [독립 physics 검토](physics-evaluation.md)와 [구현·검증 evidence 기록](implementation-verification.md#통합-전체-회귀와-남은-완료-조건)
새 필수 reference 모델·CSV는 사용자 답변에 따라 아직 준비 중이다. 위 실행·검토 완료는
그 외부 비교의 통과를 뜻하지 않으며, 파일·identity 확정과 실제 필수 비교가 완료되어야
기능 검증 완료로 판단한다.
미실행 검사를 통과로 표시하지 않는다. 전문 문서의 수치 유도·검산, 실제 solver 검사,
외부 reference 비교와 physics 검토는 서로 다른 evidence다.
@@ -1,252 +0,0 @@
# 구현 방법과 재사용 Handoff
현재 구현·검증 상태는 [PRD](PRD.md)와 [구현 검증](implementation-verification.md)을 따른다. 아래는 채택 방법론이며 새 실행 계획이 아니다.
<a id="design-plan"></a>
<a id="design-plan--shell-transient-heat-transfer-상세-설계구현-계획안"></a>
## Shell Transient Heat Transfer 상세 설계·구현 계획안
- 상태: 사용자 승인 실행 계획. 구현·검증 진행 중이며 수치 통과 evidence와 구별한다.
- 기준: [Feature PRD](PRD.md), [프로젝트 지침](../../AGENTS.md), [ARCHITECTURE](../ARCHITECTURE.md),
[CODINGSTYLE](../CODINGSTYLE.md), [TOLERANCE](../TOLERANCE.md)
- 수식의 상세 원본: [정식화](formulation.md). 현재 checklist는 [PRD](PRD.md#현재-checklist)에만 둔다.
- 독립 검토: [numerical-review.md](numerical-review.md). 수식의 일관성 검토이며 구현·정확도 통과 판정은 아니다.
<a id="design-plan--1-구현할-해석과-채택-방법"></a>
### 1. 구현할 해석과 채택 방법
입력의 4절점 Shell mesh에서 온도만 풀며, 한 절점에 두께 방향 온도 5개를 둔다.
요소당 20개 온도 자유도로 면내·두께 방향 전도와 열저장을 함께 계산한다.
기존 MITC4의 변위·회전 강성행렬을 열행렬로 전용하지 않는다.
| 항목 | 채택 방법 | 선택 이유와 한계 |
| --- | --- | --- |
| 면내 보간 | Bilinear Q4 | 승인된 4절점 topology와 연결되며 상수 온도·평판 선형 온도장을 재현 |
| 두께 보간 | 2개 구간의 C0 2차 Lagrange 보간 | 5개 온도점을 사용하고 중앙 온도점을 공유. 전체 두께 단일 4차 보간보다 구간별 gradient와 적분을 분리하기 쉬움 |
| geometry | 실제 3차원 midsurface의 metric과 법선을 이용하는 thin-shell product-domain | 접힌 판·곡면을 같은 scalar 열전도 문제로 구성. 두께 offset에 따른 곡률 보정은 생략하는 모델임을 명시 |
| 열용량 | Consistent heat-capacity matrix | Galerkin 약형에서 직접 유도하고 열저장·반력·에너지 계산에 같은 행렬 사용. Lumping·인공 확산·온도 clamp는 승인 범위 밖 |
| 적분 | 면내 3×3 Gauss, 두께의 두 P2 구간은 정확 적분한 Z 행렬 사용 | 직접 54점 적분과 대조. Affine 평판은 2×2 면내 적분도 정확하며 곡면·왜곡은 고차 적분 대조로 오차 확인 |
| 시간 적분 | Backward Euler, 고정 Δt | 일정 계수 선형 문제에서 factorization을 재사용할 수 있고 첫 구현의 시간 수렴 검증이 명확 |
| 열유속 | 보간 온도의 기울기에서 직접 복구 | 요소별·위치별 Fourier 열유속과 전체 residual 반력을 구별하여 검증 |
이 표는 사용자가 채택한 FESA 방법이며 Abaqus 내부 알고리즘 동등성의 약속이 아니다.
특히 consistent C가 현재 reference의 과도응답을 공통 tolerance 안에서 재현하는지는
아직 검증하지 않았다. 시간 적분의 안정성이 양의 온도 보존이나 정확도를 보장하지도 않는다.
<a id="design-plan--2-온도-자유도와-보간"></a>
### 2. 온도 자유도와 보간
온도점의 무차원 두께 좌표는 `ζ = [-1, -1/2, 0, 1/2, 1]`, 실제 좌표는 `z=hζ/2`다.
요소 vector는 node-major 순서로 정의한다.
\[
\mathbf T_e=[T_{1,1},\ldots,T_{1,5},T_{2,1},\ldots,T_{4,5}]^T.
\]
Source 자유도 11~15와 위 온도점 1~5를 명시적으로 대응시킨다. 내부 equation 번호와
source 자유도를 같은 정수로 취급하지 않는다. 현재 reference의 NT11은 아랫면 값이다.
\[
T_h(\xi,\eta,\zeta)=\sum_{a=1}^4\sum_{s=1}^5
N_a(\xi,\eta)L_s(\zeta)T_{a,s}=\mathbf H\mathbf T_e.
\]
Q4의 `N_a`와 두께의 `L_s` 및 도함수는 [정식화](formulation.md)에 명시한다.
5개의 서로 독립적인 2D 판을 만드는 것이 아니라, 두께 도함수 항으로 온도점 사이를 결합한다.
<a id="design-plan--3-3차원-geometry와-열전도행렬-k"></a>
### 3. 3차원 geometry와 열전도행렬 K
Midsurface를 `r(ξ,η)=Σ N_a x_a`로 나타낸다. 접선 행렬 `A=[r_,ξ r_,η]`, metric
`G=AᵀA`, 면적 Jacobian `J_s=|r_,ξ×r_,η|`, 단위 법선 `n`을 계산한다.
면내 gradient와 전체 gradient의 각 열은 다음과 같다.
\[
\nabla_sN_a=A G^{-1}\begin{bmatrix}N_{a,\xi}\\N_{a,\eta}\end{bmatrix},
\qquad
\mathbf B_{a,s}=L_s\nabla_sN_a+
\mathbf n N_a\frac{2}{h}L'_s.
\]
`B`는 3×20이며 `∇T=B T_e`다. 등방성 전도율 `k>0`에 대해
\[
\boxed{\mathbf K_e=\frac h2\int_{-1}^1\!\int_{-1}^1\!\int_{-1}^1
\mathbf B^T(k\mathbf I)\mathbf B\,J_s\,d\zeta\,d\eta\,d\xi}
\]
를 20×20 행렬로 계산한다. `L_s L_t ∇_sN_a·∇_sN_b`는 면내 전도,
`N_aN_b(2/h)^2 L'_sL'_t`는 두께 전도다. 법선과 면내 접선의 직교성 때문에 이
product-domain 모델의 교차항은 0이다. 평판에서 각각 대략 `kh`, `k/h`의 두께 scaling을 갖는다.
`G`의 퇴화, 뒤집히거나 자기 교차하는 요소, 불일치하는 surface side는 입력 geometry
진단으로 처리한다. `|det J|`로 invalid 요소를 정상화하거나 임의의 최소 두께를 대입하지 않는다.
중앙면 접속은 같은 normalized thickness station을 공유하는 이상화다.
두께가 다른 요소의 단차·급한 fold의 실제 3D 접합부 열장은 이 모델의 해상 범위 밖이다.
이 접속 의미와 얇은 Shell 곡률 근사는 사용자가 채택한 물리 모델이며 상세 3D 전도를 보장하지 않는다.
<a id="design-plan--4-열용량행렬-c와-적분-절차"></a>
### 4. 열용량행렬 C와 적분 절차
밀도 `ρ>0`, 비열 `c>0`에 대해
\[
\boxed{\mathbf C_e=\frac h2\int\rho c\,\mathbf H^T\mathbf H\,J_s
\,d\zeta\,d\eta\,d\xi}
\]
를 계산한다. K의 단위는 `power/temperature`, C는 `energy/temperature`다.
사용자가 일관된 단위계를 제공하며 코드가 입력 숫자를 보고 SI 단위로 추정하지 않는다.
계산 순서는 다음과 같다.
1. 면내 9개 Gauss 위치에서 `N`, 도함수, `J_s`, `G`, 법선을 계산한다.
2. `M_ij=∫N_iN_j dA`, `S_ij=∫∇_sN_i·∇_sN_j dA`의 4×4 행렬을 누적한다.
3. 정확히 적분한 `Z0_ab=∫L_aL_b dζ`, `Z1_ab=∫L'_aL'_b dζ`의 5×5 행렬을 사용한다.
4. Node-major 순서에서 `C=ρc(h/2)(M⊗Z0)`, `K=k[(h/2)(S⊗Z0)+(2/h)(M⊗Z1)]`를 구성한다.
5. finite·대칭성·차원과 상수 온도 null mode를 확인한 element candidate를 반환한다.
Z의 명시 행렬은 [정식화](formulation.md)에 있다. 별도의 직접 적분 검산은 면내 9점 ×
두께 2구간 × 구간당 3점, 총 54개 위치에서 H/B를 구성해 같은 K/C를 얻는지 확인한다.
Affine 평판은 면내 2×2 × 두께 6점, 총 24점으로도 정확하다. 5개 온도점, 6개 두께 적분
위치, 아래의 flux trace 위치는 서로 다른 개념이다. 곡면은 면내 3×3→5×5→7×7 대조로
적분 오차를 평가하며 reduced integration이나 hourglass 안정화항을 추가하지 않는다.
<a id="design-plan--5-열입력과-지정-온도"></a>
### 5. 열입력과 지정 온도
공개 입력은 source DOF 11/15에 대한 일정한 절점 집중 power P만 지원한다.
양수는 모델로 공급되는 열이며 h·면적·shape weight를 곱하지 않는다. Source row 순서로
더하고 constrained DOF의 입력도 full Q에 유지한다. Edge 분포 입력 011은 연기되었으며
이번에 kernel이나 keyword를 추가하지 않는다. 지정되지 않은 외부 경계는 자연 단열이다.
초기 온도는 node/Nset별 signed finite 값으로 5점에 동일 적용하고 미지정 값은 0이다.
BC 생략값은 0이며 model 단계와 step 단계 각각에서 중복 충돌을 검사한 뒤 step이
동일 target의 model 값을 덮어쓴다. 동일 값 중복은 허용하고 다른 값은 오류다.
정확한 subset·미지원 옵션·no-op warning은 [PRD STH-C02](PRD.md#sth-c02--공개-입력-충돌과-부가-keyword)를 따른다.
<a id="design-plan--6-transient-equation과-해법"></a>
### 6. Transient equation과 해법
\[
\mathbf C\dot{\mathbf T}+\mathbf K\mathbf T=\mathbf Q+\mathbf R.
\]
Backward Euler로 다음을 구성한다.
\[
\mathbf A=\mathbf C/\Delta t+\mathbf K,\qquad
\mathbf b^{n+1}=\mathbf Q^{n+1}+\mathbf C\mathbf T^n/\Delta t.
\]
free/constrained partition에서
\[
\boxed{\mathbf A_{ff}\mathbf T_f^{n+1}=
\mathbf Q_f^{n+1}+\mathbf C_{ff}\mathbf T_f^n/\Delta t+
\mathbf C_{fc}\mathbf T_c^n/\Delta t-\mathbf A_{fc}\mathbf g^{n+1}}
\]
를 푼다. 초기 구속점 온도와 step 지정 온도가 다르면 첫 증분의
`C_fc(T_c^0−g)/Δt` 항을 빠뜨리면 안 된다. 입력 초기 상태를 `t=0`에 보존하고
첫 양의 시점부터 step 지정 온도를 부과하는 것이다. 초기 값을 덮어쓴 다른 해석과
에너지·reference 결과가 달라지므로 raw 초기값을 조용히 projection하지 않는다.
고정 물성·geometry·구속 집합·Δt에서는 A_ff를 한 번 factorize하고 매 증분 RHS만 갱신한다.
전체 시간이 Δt로 나누어떨어지지 않으면 마지막 증분만 종료까지 단축한다. Δt가 전체 시간보다
크면 한 증분이다. 시간은 n*Δt로 생성하고 마지막 저장 시점은 입력 종료 시간이다. 표현 오차
수준으로 같은 마지막 간격은 같은 계수를 사용하고 실제로 짧은 간격만 재분해한다.
시간격자·INC·표현 가능성의 상세 판정은 정식화와 독립 numerical review에서 고정한다.
절연체의 K는 상수 온도 null mode를 가지지만 양의 C 때문에 A는 양정치다.
정적 solver처럼 “온도 구속이 없으므로 singular”로 판정하지 않는다. 일반 partition 연산은
전체 구속 시 0×0 free solve를 건너뛰도록 검산한다. 다만 이번 공개 입력은 bottom/top만
구속하므로 내부 3개 온도점까지 모두 구속한 사례는 CLI 지원 모델이 아닌 algebra fixture다.
<a id="design-plan--7-heat-flux와-reaction-heat-rate-복구"></a>
### 7. Heat flux와 reaction heat rate 복구
각 완료 증분에 element scatter로 `T_e`를 모아, 동일한 B로
\[
\boxed{\mathbf q^{global}=-k\mathbf B\mathbf T_e}
\]
를 계산한다. 단위는 `power/area`이며 고온에서 저온 방향이 양의 흐름이다.
local 직교 frame `R=[e_1,e_2,n]`이면 `q_local=Rᵀq_global`이고,
앞 두 성분은 면내, 세 번째는 두께 방향 열유속이다.
- 기본 authoritative flux는 면내 2×2 Gauss 위치와 두께 trace
`[-1+, -1/2, 0−, 0+, 1/2, 1−]`의 곱인 24개 위치에서 복구한다. 두께 trace는 구간
안쪽에서 평가하는 위치/접근 방향이다. Global 성분과 local frame을 저장하고 local 성분은 Rᵀq로 후처리한다.
- 위 trace 위치에서 B를 직접 평가한다. C0 보간의 중앙면에서 두께 도함수는 한쪽 극한
두 개일 수 있으므로 `0−`와 `0+`를 평균으로 합치지 않는다. 행렬의 적분 위치와 복구 위치는 독립이다.
- 재료 경계와 요소 경계를 넘어 nodal averaging한 값을 공식 heat flux로 사용하지 않는다.
- 부호를 비교할 때 아랫면의 외향 법선은 `−n`, 윗면은 `+n`임을 구별한다.
반력 열률은 gradient flux를 절점으로 평균하여 만들지 않고 full residual에서 계산한다.
\[
\boxed{\mathbf r^{n+1}=\mathbf C(\mathbf T^{n+1}-\mathbf T^n)/\Delta t
+\mathbf K\mathbf T^{n+1}-\mathbf Q^{n+1}}
\]
free 성분은 평형 잔차, constrained 성분은 지정 온도 유지에 필요한 반력 열률이다.
양의 반력은 모델에 추가로 공급한 열이다. transient 반력에는 열저장 항이 포함되어야 한다.
초기 온도는 저장하지만 `t=0` flux와 reaction dataset은 생성하지 않는다. 반력을 계산된 0이나 NaN으로 대체하지 않는다.
<a id="design-plan--8-기존-코드와-연결할-책임파일"></a>
### 8. 기존 코드와 연결할 책임·파일
현재 `Element::Recover`는 displacement를 소비하고 `DofManager`는 `kDofsPerNode=6`을
전제로 한다. 따라서 parser에 DS4만 추가해서 완료할 수 없다. 다음은 실제 저장소 경로와
신규 파일 배치다. 세부 C++ signature는 승인된 I/O·정식화 계약과 기존 객체 책임을 따른다.
| 작업 경계 | 기존 수정 대상 | 신규 파일과 책임 |
| --- | --- | --- |
| Semantic model | `include/fesa/model/domain.h`, `src/fesa/model/domain.cpp`, `include/fesa/model/model_types.h`, `include/fesa/materials/material.h` | `include/fesa/materials/isotropic_thermal_material.h`, `src/fesa/materials/isotropic_thermal_material.cpp`: 양의 k·ρ·c와 source identity |
| Thermal element | `include/fesa/elements/element_definition.h`, `src/fesa/elements/element_factory.cpp`의 definition/runtime 생성 경계 | `include/fesa/elements/thermal_shell.h`, `src/fesa/elements/thermal_shell.cpp`: 20×20 K/C와 직접 flux 복구. 열 kernel을 구조 Element의 displacement 계약에 억지로 넣지 않음 |
| Geometry | 기존 `model/shell_geometry`의 MITC4 definition/director 의존성 확인 | `src/fesa/elements/thermal_shell_geometry.h`, `.cpp`: Q4 metric·법선·geometry 검증. 구조 shear tying·drilling 정보 불필요 |
| DOF | `include/fesa/elements/element.h`, `include/fesa/fem/dof_manager.h`, `src/fesa/fem/dof_manager.cpp` | Explicit layout/constraint 입력 경계를 추가하여 DofManager 안에서 구조 6개·열 5개 profile을 구별. 기존 구조 numbering/scatter 계약 보존 |
| Thermal assembly | `SparseMatrix::FromCoo`, `ParallelFor` 재사용 | `include/fesa/assembly/thermal_assembler.h`, `src/fesa/assembly/thermal_assembler.cpp`: 동일 scatter의 K/C, stable COO reduction과 source-ordered Q |
| Procedure/state | `analysis_model.h/.cpp`, `prepared_analysis.h/.cpp`, `src/fesa/app/fesa_application.cpp` | `include/fesa/analysis/transient_heat_procedure.h`, `src/fesa/analysis/transient_heat_procedure.cpp`, `include/fesa/analysis/thermal_analysis_state.h`: factorize/step/recover/candidate commit |
| Abaqus mapping | `src/fesa/io/abaqus/domain_mapping_context.cpp`, `domain_builder.cpp`, `topology_mapper.cpp`, `material_property_mapper.cpp`, `step_definition_mapper.cpp` | 책임별 thermal block mapping 추가. 기존 structural validation의 제한을 온도에 그대로 적용하지 않음 |
| Results | 기존 HDF5 atomic file·primitives와 prepared outcome dispatch | `include/fesa/results/thermal_results_writer.h`, `include/fesa/io/hdf5/thermal_hdf5_results_writer.h`, `src/fesa/io/hdf5/thermal_hdf5_results_writer.cpp`: 온도/flux/reaction/history identity와 atomic finalize |
Domain이 정의를 소유하고 AnalysisModel이 non-owning view를 제공하는 기존 책임을 유지한다.
새 thermal state만 temperature/history를 소유한다. Node/Element에 equation 번호를 저장하지 않는다.
열용량 C를 structural mass field에 저장하지 않으며 vendor API는 기존 backend 경계에 둔다.
<a id="design-plan--10-방법론을-반증할-검증-사례"></a>
### 10. 방법론을 반증할 검증 사례
필수 사례의 원본은 [PRD STH-C07](PRD.md#sth-c07--독립-검증과-수치-계약의-선행-조건)이다.
정확 fixture·norm·시간/mesh 목록·oracle 절단 및 order 판정은 [독립 수치 검토](numerical-review.md)가 소유한다.
- Shape partition, affine exact K/C, constant null mode, C SPD, 양의 에너지와 회전 변환.
- 일정 온도·면내 선형·두께 선형 flux 및 중앙면 0-/0+를 구별하는 직접 미분.
- 두 자유도 C=[[2,1],[1,2]], K=[[1,-1],[-1,1]], delta=.5, old=0, 두 번째 g=1에서
첫 free=-.2, 두 번째 .04, reaction=4.8와1.44. 첫 C_fc 이전 상태 항을 검출한다.
- No-BC 균일 보존, P/-P·constrained P·source 중복과 component별 에너지 수지.
- Semidiscrete 독립 eigenmode로 시간1차, Fourier 직사각형의 L2 온도2차·gradient1차.
- Bottom step/top 단열 slab series의 온도·flux·에너지·반력; 고정5점 두께 오차 plateau와 초기 침투 깊이 보고.
- 다중 재료/두께 strip 저항은 full-end assembly fixture로 확인한다. 내부 station BC를 CLI로 확대하지 않는다.
- 원본 Step-1 t37 NT11 729행의 identity precheck와 공통 tolerance 진단 비교;
새 reference 모델이 확정되면 별도 필수 비교. [STH-ADR-015](ADR.md#sth-adr-015-원본-reference-진단-전환과-새-모델-비교)를 따른다.
에너지 defect의 scale은 저장·외력·반력 항의 절댓값 합이며 free residual을 별도로 검사한다.
E=1ᵀCT의 기준 온도 이동에 대한 DeltaE 불변성과 BE C-norm 소산은 서로 다른 검사다.
외부 5%/1% 기준으로 내부 수지를 판정하거나 max(1,scale)로 작은 모델의 오류를 숨기지 않는다.
<a id="design-plan--12-채택-모델의-한계"></a>
### 12. 채택 모델의 한계
Product-domain은 실제 offset 곡률과 상세 접합부를 해상하지 않는다. 고정5점은 초기 두께
경계층을 충분히 해상하지 못할 수 있고 면내 refinement가 두께 오차를 제거하지 않는다.
Consistent C/BE의 안정성은 positivity나 정확도를 보장하지 않는다. Point source 부근의
mesh 독립적인 유한 flux는 보장하지 않는다. 이 한계를 임의 clamp나 tolerance 완화로 숨기지 않는다.
Reference의 NT11 최소값 -15.5742216은 S=1000, B=10의 near-zero 범위 밖이다.
공통 상대오차 검사를 유지하고 초기 jump·공간 이산·열용량의 영향을 독립적으로 평가한다.
현재 채택 계약을 바꾸어야 하는 충돌이 확인되면 근거를 제시하고 사용자 결정으로 돌린다.
@@ -1,513 +0,0 @@
# Shell 과도 열전달 구현·검증 기록
- Feature ID: `shell-transient-heat-transfer`
- Owner: 메인 assistant
- 상태: 열해석 구현·전체 회귀·독립 검토 완료. 새 필수 reference 비교는 미실행이며 제품 검증 완료가 아니다.
- 기준: [PRD](PRD.md), [ADR](ADR.md), [정식화](formulation.md),
[독립 수치 검토](numerical-review.md), [공통 tolerance](../TOLERANCE.md).
## Production 변경 전 baseline
검사 대상 source revision은 `a3502c9b974bc17698d57438274408f29317ced4`다.
검사 도중 reference 두 파일만 `e6fd9e9`에 추가했으며 production source와 테스트는 변경하지 않았다.
정식화·계약 문서 편집은 별도 task checkout에서 수행했다.
Windows MSVC x64 Debug, CMake/Ninja와 기존 설치된 의존성을 사용했다.
MSVC toolset 14.51.36231, oneAPI MKL 2026.1, TBB 2023.1, HDF5 2.1.1 및
승인 local GoogleTest checkout을 사용했다. 머신별 절대 경로는 개인 실행 환경에만 둔다.
저장소에 `.harness/config.json`이 없어 직접 preset을 실행했다.
| 실행 | Captured exit | 관찰 |
| --- | --- | --- |
| `cmake --preset windows-msvc-ninja-debug -DCMAKE_PROJECT_FESA_INCLUDE=` | 0 | Configure 완료; 추가 task source overlay 없음 |
| `cmake --build --preset windows-msvc-ninja-debug --parallel 4` | 0 | 전체 baseline build 완료; compiler warning 검색 결과 없음 |
| `ctest --preset windows-msvc-ninja-debug --show-only=json-v1` | 0 | 478개 검사 발견 |
| `ctest --preset windows-msvc-ninja-debug --output-on-failure` | 8 | 478개: 475 PASS, 2 FAIL, 1 SKIP. 3480.67초; 전체 검사 제외 없음 |
실제 로그·argv·revision·exit·완료 시각은 로컬 `.dryforge/evidence/baseline-*.log`와
각 `.log.json`에 보존한다. 전체 CTest 로그도 `.dryforge/evidence/baseline-LastTest.log`에
복사해 후속 실행으로 덮어쓰이지 않도록 보존했다. Native CTest exit은 8이고 외부
PowerShell 실행 도구는 실패 exit 1을 반환했다. 아래 표는 실제 로그의 값을 요약한다.
### 기존 HHT 실패
`HhtShellPhysics.C08aRigidManufacturedOrbitFullCoordinateTimeConvergence`는
828.34초 실행 후 실패했다. 여섯 channel의 정의와 원래 판정은
`tests/unit/analysis/hht_shell_physics_test.cpp`의 해당 검사 그대로다.
| 실패 metric | 실제 값 | 기존 기준 |
| --- | --- | --- |
| Channel 3 최종 refinement error / noise floor | `1.178006582488537e-6` / `1.664587376115457e-6` | error > noise floor |
| Channel 5 마지막 시간격자의 최대 error | `5.675659240050177e-4` | <= `1e-4` |
| Channel 5 두 refinement order | `1.0286686694389584`, `1.0289692381730642` | 각각 >= `1.8` |
| Channel 5 error / noise floor | `5.675659240050177e-4` / `8.1556816896757325e-4` | error > noise floor |
`HhtShellPhysics.C08bFreeFiniteRotationPlateSelfConvergenceAndPhysicalInvariants`는
2382.81초 실행 후 실패했다. 마지막 공통시간 차이는 다음과 같다.
| Channel | 마지막 self-difference | 기존 기준 |
| --- | --- | --- |
| q_u | `5.5474274071377627e-6` | <= `1e-4` (이 항목은 통과) |
| q_phi | `1.3870090193204238e-4` | <= `1e-4` |
| v_u | `1.7761250930811503e-4` | <= `1e-4` |
| v_phi | `1.15434079805253e-2` | <= `1e-4` |
| a_u | `1.1979645042237048e-2` | <= `1e-4` |
| a_phi | `8.6893881162834119e-1` | <= `1e-4` |
q_phi/v_phi/a_u/a_phi의 마지막 두 관측 차수는 각각
`(-.1538334889569796,.17869938751647654)`,
`(-.084636748464689021,.057860368459572473)`,
`(.4062904963410236,.20093674620022031)`,
`(-.15578672026532631,.17674898422982255)`로 기존 `>=1.8`에 미달했다.
v_u의 마지막 차수도 `.85658192310858094`로 미달했다.
Linear momentum drift의 마지막 세 값은
`3.8504721819452428e-14`, `4.8249991985258491e-14`, `2.004031863681472e-13`으로,
원래 strict decrease와 마지막 비율 `<=.6` 검사도 실패했다. 작은 값이라는 이유로 면제하지 않는다.
최종 energy/L drift는 `9.5590394502167011e-7` / `4.2974921804532845e-7`이었다.
이 실패 양상은 [기존 HHT physics 검토](../hht-implicit-dynamics/physics-evaluation.md)의
미해결 C08 범위와 일치하며 열해석 변경 이전에 재현했다.
### 기존 HHT N7 inconclusive
`HhtTemporalVerification.C04aFinestReductionComparisonIsNumericallyResolved`는
기존 테스트의 명시 `GTEST_SKIP()` 분기로 SKIP이었다. Full/reduced acceleration defect
`3.0790209097630861e-10`이 기존 비교 기준 `1e-10`보다 크지만 arithmetic/solve resolution
`6.2961354117696263e-10` 이하인 경우를 N7 INCONCLUSIVE로 표시하는 원래 검사다.
원인과 범위를 `tests/unit/analysis/hht_temporal_verification_test.cpp:478` 및 실제 출력으로
대조했다. 환경 누락이나 열 기능의 미실행이 아니며, 이 항목을 PASS로 세지 않는다.
최종 전체 검사에서도 이 분류와 metric을 대조하고 새 skip으로 검증을 회피하지 않는다.
승인된 예외는 재현된 무관한 기존 HHT 실패의 별도 보존뿐이다. 마지막 전체 검사에서
동일 metric을 대조하여 새 실패·악화 여부를 확인한다. 기존 실패를 PASS/SKIP으로 바꾸거나
전체 CTest green으로 보고하지 않는다. 예상하지 못한 실패와 열해석 필수 실패는 자동 면제하지 않는다.
## Reference 복사와 원본 보존
`git worktree list --porcelain`으로 확인한 동일 저장소 `dev` checkout의
`reference/shell-thermal/`에서 다음 두 파일을 같은 상대 경로로 byte 그대로 복사했다.
원본과 복사본의 SHA256이 각각 일치했고, 복사본을 `e6fd9e9`에 기록했다.
| 파일 | 원본과 복사본의 SHA256 |
| --- | --- |
| `shell-thermal.inp` | `955C8BEEC35CE53DA5F5DD48785CF8C12FCE726EBFDE7EAE40CF69DBF00D6EA4` |
| `shell-thermal NodalTemperature.csv` | `765B0D0F64C24CDE6D033D6EF80E0267275A496C97E28D932466EB877810D3E5` |
Abaqus 실행·reference 재생성·이름 변경·내용 보정은 수행하지 않았다.
이 hash 확인은 FESA/reference 수치 비교 통과 evidence가 아니다.
## 구현 및 최종 검증
### 구현 전 수치 계약
정식화와 승인 범위의 정합, 전체 patch certificate, source 접속, 시간격자·오차·수렴·slab
검증 기준을 작성하고 독립 수치 검토를 마쳤다. 검토 대상 정식화의 마지막 수식 변경은
`dd409b5`, 독립 보고서의 최종 보완은 `6df5a17`이다. 설계상 blocking 지적은 해결했다.
소스 구현의 정확도 통과 판정은 아니다.
- 전역 T-edge 좌표 검사를 source endpoint를 공유하는 후보로 제한하여 disconnected 접촉을 구별했다.
- 입력 rounding-cell을 놓치는 시간격자 반례를 발견하고 정확한 cell overlap으로 보완했다.
Production은 최대 122비트의 고정 폭 연산이며 새 임의정밀도 의존성을 요구하지 않는다.
- 공간수렴 검증은 8/16/32/64 정사각 mesh, `T=.001`, 32증분으로 고정했다.
기존 관찰 차수 기준을 유지하고 시간오차/공간오차 비율의 최대값 `.006864`를 확인했다.
가장 큰 mesh의 온도·flux 원시 이력은 약 77.3MiB다.
- 고정5점의 초기 slab 오차·warped 3×3 적분 오차는 별도로 정량화했다.
통합된 문서의 [독립 oracle 부록](verification-oracles.md#독립-oracle와-재현)의 Python block 5개를
메인이 다시 추출 실행했다. Captured Python exit 0,
`ALL SCRATCH BLOCKS COMPLETED`를 확인했고 출력은
`.dryforge/evidence/t1-oracle-replay.log`에 보존했다.
기능 문서의 링크·anchor·요구사항 ID 및 `git diff --check`도 통과했다.
문서 변경을 이유로 solver build/CTest를 반복하지 않았다.
### Thermal semantic model
열물성·Domain·AnalysisModel semantic 구현을 `254247e`, `283628e`로 작성하고,
독립 코드 검토에서 해당 범위의 잔여 차단 항목이 없음을 확인했다.
BC/CFLUX는 source DOF 11·15만 허용한다. 열 재료·단면의 local index와 전체 Domain
material index를 구분하며, 구조 step 접근을 열 모델에 잘못 호출하면 명시적으로 실패한다.
두 단계 BC는 원본 수준별 정의를 보존하며 최종 상속·override는 후속 입력/실행 연결의 책임이다.
Endpoint 제한의 행동 재현 검사에서 기존 구현이 12·13·14를 수용하여 CTest exit 8을
확인했고 수정 후 통과했다. 이전 API 누락에 의한 compile 실패는 행동 재현 evidence로
계산하지 않는다. 잘못된 물성·두께, 비활성 target, 초기값·BC 충돌, constrained CFlux
중복·순서 보존, 이동 후 소유 데이터 접근과 다중 재료 인덱스를 검사했다.
작업 checkout의 clean MSVC x64 Debug build와 최종 fixture 추가 후 incremental build는
각각 exit 0이다. 최종 관련 CTest는 **21/21 PASS**, exit 0이며 compiler warning은
검색되지 않았다. 실제 evidence는 `t2-correction-clean-build-build.log`,
`t2-correction-final-build-build.log`, `t2-correction-final-regression-test.log` 및 각
명령·cwd·native exit JSON에 보존했다. 실행 당시 HEAD는 commit 전 `254247e`이고,
최종 검사 09:47:06 UTC 후 동일 수정분을 `283628e`로 기록했다.
검증 중 한국어 MSVC `/showIncludes` prefix가 CMake에서 잘못 decode되어 Ninja가
header dependency를 0개로 기록하는 환경 문제를 발견했다. Header 변경 후 이전 객체를
재사용하여 AnalysisModel 접근 오류가 발생했다. 개인 검증 환경에서 실제 UTF-8 prefix를
지정하고 clean build한 뒤 같은 객체의 dependency가 47개이며 관련 header를 포함함을
확인했다. 잘못된 추적 상태의 중간 incremental 결과는 최종 evidence로 사용하지 않는다.
Production 변경 전 baseline은 175개 build action을 처음부터 실행한 결과로 유지한다.
공통 CMake source/test 등록 후 통합 checkout에서도 clean build exit 0, CTest inventory
**487개**, 관련 semantic·기존 structural DOF 검사를 포함한 **29/29 PASS**를 확인했다.
Evidence는 `.dryforge/evidence/semantic-integrated-{configure,build,test}.log`와
`semantic-integrated-serial-inventory.log`다. 최초 inventory와 test를 동시에 시작했을 때
GoogleTest discovery의 공용 JSON 생성이 경합하여 inventory exit 8이 발생했다.
직렬 inventory 재실행은 exit 0이며, 이후 같은 build tree의 discovery/test는 직렬 실행한다.
### Thermal DOF 매핑
`e71f3ad`, `17a2355`의 열 5성분 profile, 활성 절점 번호, 20-entry scatter, partition과
CSR 매핑을 독립 검토했다. Canonical 11..15 순서, 비연속 반복 절점, 잘못된 enum 범위,
구조/열 profile 오용을 거부한다. 미사용 절점이 중간에 있는 경우와 공유 edge·분리 영역,
BC 상속·override, 실패 후 전체 mapping 보존을 확인했다.
`t4-final3-build.log`는 native exit 0, `t4-final3-test.log`는 **13/13 PASS**, exit 0이다.
이 중 기존 structural DofManager 검사는 8개다. `t4-final2-inventory.log`의 491개는
discovery 수이며 전체 실행 수가 아니다. 검사 후 마지막 수정은 줄바꿈·들여쓰기뿐임을
독립 reviewer가 확인했고, 범위 내 잔여 차단 항목 없이 `7b9a36c`로 통합했다.
요소 kernel과 결합한 통합 검사는 kernel 완료 후 수행한다.
PATH 밖의 설치된 clang-format을 확인한 뒤 새 semantic 파일의 서식을 적용했다.
`8ba4d18`은 세 신규 파일의 서식 수정이며 관련 신규 header/source/test의 dry-run 검사는
exit 0이다. 앞선 수동 서식 확인을 formatter 실행으로 대신 주장하지 않는다.
### 시간격자 독립 실행 확인
요소 kernel과 분리된 시간격자 checkpoint `e33ee6a`를 독립 검토자가 별도 실행기로
컴파일했다. Production의 binary64 입력 cell 판정을 exact Fraction oracle의
**11,043개 사례**에 대조했고, 유효 11,030개와 입력·INC 거부 13개에서 불일치가 없었다.
**66,023개 Frame 표본**의 저장 시각·실제 delta가 비트 단위로 일치했다.
기본 INC=100, 범위 밖 Frame 거부와 진단도 확인했다.
MSVC `/Od /W4 /WX /fp:precise` standalone build, runner 및 비교기는 각각 native exit 0이다.
원래 subnormal 간격, open/closed midpoint, 최대 finite, 실제 shortened 마지막 증분과
거대한 count의 O(1) 사전검사를 포함한다. Evidence는 작업 checkout의
`.dryforge/review/time_grid_e33ee6a/`에 committed source manifest, 실제 출력,
`comparison.json` 및 실행 명령으로 보존했다. 이는 시간격자에 한정한 evidence이며
kernel·procedure·physics의 최종 판정을 대신하지 않는다.
### 요소 kernel과 형상 검증
요소 구현 checkpoint `32f0c16`의 독립 행동 계약 검토에서 잔여 차단 항목이 없었다.
Domain의 node/section 범위 검사 뒤 전체 patch와 source 접속을 검사하며, 분리 영역의
좌표 접촉을 자동 연결하지 않는다. Recovery는 4×6 위치, 중립면의 별도 두 trace와
열벡터 `e1/e2/n`을 유지한다. 생성·복구 실패는 기존 kernel을 변경하지 않는다.
작업 checkout의 `t3-ready-build.log`, `t3-ready-test.log`는 native exit 0이며
**32/32 PASS**다: kernel 10, geometry 9, time 7, semantic 6개다.
`t3-ready-inventory.log`의 513개는 discovery 수이며 전체 실행 수가 아니다.
실행 metadata의 HEAD는 commit 전 `e33ee6a`이고, 검사한 수정분을 이후 `32f0c16`에
기록했다. Build warning은 검색되지 않았고 formatter와 `git diff --check`도 통과했다.
`32f0c16`의 독립 수치 검토도 잔여 차단 항목 없이 통과했다. Commit에서 추출한 실제
kernel/geometry를 MSVC로 별도 컴파일하여 고정밀 oracle와 대조했다.
- 유효 fixture 22개의 C/K 계수 17,600개와 평가점 198개의 면적·gradient enclosure
2,574개에서 production 반올림 오차 경계 위반이 없었다.
- 직접 54점 적분과 절댓값 contribution 기준, C 양정치·K rank 19·상수 모드,
두께 변화·별도 중립면 trace·source topology 14개 사례를 확인했다.
- 고정 affine/distorted/saddle 사례의 3/5/7 적분 진단과 production C/K를 이용한
독립 BE 대수 응답, 실제 `Recover`의 온도 응답 5개를 확인했다. 전역 procedure를
실행한 결과는 아니다.
실제 명령·native exit와 계산 결과는 작업 checkout의
`.dryforge/review/kernel_32f0c16/`에 보존했다. 최종 compile/runner/checker는 모두 exit 0이다.
최초 surface oracle는 이동된 평판의 정확한 영 성분에 남은 고정밀 cancellation 폭으로
exit 1이었다. 입력 좌표 차를 exact Fraction으로 계산하여 oracle의 translation을
제거한 뒤 같은 포함 검사가 통과했다. Production 변경이나 tolerance 완화는 없었다.
공통 CMake 등록 commit `85b5daa`에서 MSVC x64 Debug clean build exit 0,
inventory **517개**, 열·기존 Domain·자유도 관련 **89/89 PASS**, exit 0을 확인했다.
Evidence는 `.dryforge/evidence/thermal-kernel-final-{configure,build,inventory,test}.log`와
각 native exit JSON이다. 전체 CTest 실행은 아니며 최종 회귀는 별도로 남는다.
최초 통합 build는 exit 0이었지만 Windows PowerShell 5가 UTF-8 BOM 없는 개인 helper의
한국어 prefix를 잘못 읽어 header 의존성 추적이 손상됐다. Helper에 BOM을 추가하고
`pwsh`로 configure 및 clean build를 다시 수행했다. 최종 Ninja prefix와 실제 header
dependency를 확인했으며, 중간 `thermal-kernel-integrated-build.log`는 최종 근거를
대신하지 않는다. 이 보완은 개인 검증 환경에 한정하며 제품 소스 변경은 없다.
### 결정적 조립과 accepted state
`54154bee`의 full K/C/Q 조립과 결과 state를 독립 검토하여 잔여 차단 항목이 없음을
확인했다. Local kernel 오차와 순서가 고정된 합산 오차를 포함한 경계를 제공하며,
constrained Q도 보존한다. State는 생성 뒤 Domain 수명에 의존하지 않고 raw 초기
프레임, immutable row mapping, 정확한 schedule 및 완료 프레임을 소유한다.
작업 checkout의 `t6-final-{build,inventory,test}.log`는 각각 native exit 0이다.
Inventory 528개 중 관련 **11/11 PASS**이며 24종류의 candidate 손상에 대한 전체
이력 보존과 UINT32_MAX schedule의 선할당 방지도 검사했다. 실행 당시 HEAD는
commit 전 `85b5daa`였고, 같은 수정분을 `54154bee`에 기록했다.
독립 실행기는 committed assembler/state를 별도 컴파일하여 4요소·9활성절점의
서로 다른 물성·두께 및 global material offset 모델을 검산했다. Exact Fraction
oracle의 C/K **4,050항**과 **40개 하중 사례**를 대조했고, 유효 39개와 overflow
거부 1개가 기대와 일치했다. Serial/actual TBB의 값·오차 경계·flux는 비트 단위로
일치했다. State의 초기값·완료 여부·잘못된 uint64 frame 거부·복사 독립성도 확인했다.
Evidence는 작업 checkout의 `.dryforge/review/t6_54154bee/`에 보존했다.
최초 reviewer runner의 정수 narrowing compile 실패를 구별해 기록했으며,
수정 후 compile/runner/Fraction checker/source audit는 모두 실제 exit 0이다.
입력 파서와 합친 통합 검사 결과는 아래에 기록한다.
### Thermal 입력 subset과 진단
입력 구현 `203d068`을 독립 검토하여 T5 범위의 잔여 차단 항목이 없음을 확인했다.
DS4 inline ELSET, 상수 물성·5점 단면, raw 초기값, model/step 경계조건,
source 순서 CFLUX와 정확한 시간격자를 연결한다. 부가 keyword는 승인된 형식만
허용하며 source warning을 남긴다. 미사용 surface도 target을 검증하고 보존한다.
검토에서 strict output/restart, surface resolution, source 진단 및 mixed procedure
문제를 보완했다. 마지막 DS4 inline ELSET 거부와 beam section 무시 문제는 실제
반례 4개의 실패(native exit 8)를 확인한 뒤 수정했다. 같은 집합 내부의 member는
STH-C01에 따라 한 번 전개하고, 별도 CFLUX source row는 가산 대상으로 유지한다.
작업 checkout의 `t5-inline-green-{build,test,inventory}.log`와 JSON에서 각각
native exit 0을 확인했다. 관련 **82/82 PASS**는 thermal 입력 32개와 기존 structural
입력 50개이며 inventory **549개**는 전체 실행 수가 아니다. 검사 당시 HEAD는
수정 전 `360f8df`였고, `t5-inline-manifest.json`의 소스·테스트 hash가 최종 commit과
일치함을 독립 확인했다. 원본 reference 두 파일의 hash는 변경되지 않았다.
공통 CMake 등록 commit `983e2fe`에서 configure/build/inventory/test가 각각
native exit 0이었다. 실제 inventory는 **560개**, 열·기존 입력·Domain·자유도 관련
**152/152 PASS**다. 새 compiler warning이 없고 Ninja의 실제 header dependency도
정상임을 확인했다. Evidence는
`.dryforge/evidence/thermal-input-state-integrated-{configure,build,inventory,test}.log`
및 native exit JSON이다. 전체 회귀 검사는 아니며 시간 적분·HDF5·CLI 이후에 수행한다.
### 시간 적분 구현 전 residual 해석 확인
정식화의 reduced 분모 `D_f=||A_ff||∞||T_f||∞+||b_f||∞`를 reduced 및
full-free residual certificate 모두에 유지한다. Reduced 검사는 승인된 식 그대로 수행한다.
Full residual은 직접 평가 interval과 reduced residual에 독립적인 signed construction
interval을 더한 interval의 교집합으로 인증할 수 있다. 두 interval은 같은 stored-data
정확 잔차를 포함해야 하며 빈 교집합은 실패다. 이는 허용치를 늘리지 않고 포함 구간을
좁히는 방법이다. 방정식 구성 오차를 residual 허용치나 에너지 bound에 더하지 않는다.
독립 Fraction 반례에서 2-DOF fixture의 큰 constrained 온도에 의해 계산된 우변이
0으로 소실되지만 정확 우변은 4인 경우를 확인했다. Full BE scale로 정규화하면
이 오류가 통과할 수 있으므로 이를 blocking 분모로 대체하지 않는다.
`.dryforge/review/t7_residual_normalization/`의 script·실제 출력·실행 JSON은
native exit 0이며, 이는 승인 계약 해석의 evidence다. 최초 검토에서 direct-full의
느슨한 상계를 그대로 별도 gate로 요구한 해석은 필수 첫 jump에서 `29.4u > 20u`로
충돌하여 정정했다. `refinement-interpretation.txt`와 실제 Fraction 검산은 교집합
인증에서 같은 사례가 약 `4u`로 통과하고, 앞의 우변 소실 반례는 여전히 실패함을
보인다. `D_f=0`은 물리 잔차의 exact zero 증명이 필요하며 단순히 interval이 0을
포함한다고 통과시키지 않는다. Procedure 구현의 실제 interval 검증은 별도로 남는다.
### Backward Euler procedure와 candidate 검증
`ef8ec473`의 시간 적분 구현은 독립 행동 계약 검토와 별도 exact 산술 검산에서
잔여 차단 항목이 없었다. Raw 초기값, 첫 증분의 old constrained 용량 항,
실제 delta가 같을 때의 factorization 재사용, full 반력·flux 복구와 실패 전 commit
방지를 구현했다. 공개 API는 `Result<ThermalExecutionResult>`이며 내부 검산용
partition이나 recovery 주입을 공개 경계조건 기능으로 노출하지 않는다.
작업 checkout의 `t7-final-{build,inventory,test}.log` 및 native exit JSON은 모두 0이다.
Inventory **570개**, 관련 **91/91 PASS**이며 새 10개 tests는 두 증분 exact fixture,
all-constrained, 상쇄와 잘못된 solve 거부, 실제 시간격자·factorization 횟수,
실제 MKL affine mode/flux, 이력 보존, signed power·분리 영역, subnormal 경계,
잘못된 입력과 Serial/TBB 이력 일치를 검사한다. `t7-tested-source-hashes.json`은
실행한 6개 소스·테스트가 최종 commit과 동일함을 식별한다.
메인의 독립 실행기는 committed algebra와 bounds를 별도 MSVC target으로 호출하고
Python Fraction oracle와 비교했다. **31,668개 산술 enclosure**, 인증된 **502개 증분**의
**737개 free residual**, **502개 열수지**, **108개 단열 소산**에서 경계 위반이 없었다.
추가 무작위 algebra 사례 1개는 full-free 인증 불가로 거부됐으며 통과 수에 포함하지
않았다. 허용치나 production을 결과에 맞춰 변경하지 않았다. Evidence와 적용 한계는
작업 checkout의 `.dryforge/review/t7_ef8ec473/review.md`, `comparison.json`, 실제
runner/checker 출력·native exit 기록에 보존했다. 최종 독립 build/runner/checker는 exit 0이다.
HDF5 구현과 합친 통합 검사는 writer의 독립 검토 후 수행한다.
### Thermal HDF5 저장과 publication
`7f11d533`의 writer와 self-check를 STH-C05/C06에 대조한 독립 검토에서 차단 항목이
없었다. 모든 필수 metadata·model·mapping·frame·diagnostic의 field/type/extent/value를
검사하며, compound의 순서·padding에 의존하지 않고 fixed/variable UTF-8을 허용한다.
Global material ID, 정규화한 section provenance, raw initial, 실제 delta와 회전된 R 및
global flux의 trace/component 순서를 직접 읽어 검증했다.
Candidate close 이후 파일을 다시 열어 검사하고 reader close 성공 뒤에만 최종 파일을
교체한다. 쓰기·candidate close·self-check·reader close·finalize의 다섯 실패 경계에서
기존 결과 byte 보존과 candidate 정리를 확인했다. Domain과 이력의 활성 identity,
시간격자, raw 초기값, constraint mask와 완료된 prescribed 온도도 대조한다.
작업 checkout의 `t8-final-{build,inventory,test}.log` 및 native exit JSON은 모두 0이다.
Inventory **573개**, 관련 **31/31 PASS**이며 이 중 thermal writer 검사는 **13개**다.
실행 당시 HEAD는 commit 전 `983e2fe`였고 `t8-tested-source-hashes.json`의 8개 파일 hash가
최종 commit과 정확히 일치함을 독립 확인했다. 이 결과는 writer 범위이며 CLI·물리·외부
reference 검증을 대신하지 않는다.
시간 적분과 writer를 실제 CMake에 등록한 `1b82707`에서 configure/build/inventory/test는
모두 native exit 0이다. Inventory **583개** 중 `Thermal|TransientHeat|Hdf5`에 해당하는
**134/134 PASS**, 새 compiler warning 없음이다. Evidence는 메인 checkout의
`.dryforge/evidence/thermal-procedure-output-integrated-{configure,build,inventory,test}.log`
및 exit JSON에 보존했다. 이는 관련 통합 검사이며 전체 회귀는 아니다.
### Application과 실제 CLI
`7cccfcd`의 독립 검토에서 application 연결 범위의 차단 항목이 없었다. Thermal step을
구조 step 개수 검사보다 먼저 선택하며, typed outcome과 thermal writer를 연결했다.
기존 호출의 optional adapter, output lease·log close 순서와 오류 분류를 보존한다.
실제 executable을 사용하는 통합 테스트의 CMake 의존성도 명시했다.
작업 checkout의 `t9-verified-{build,test}.log`, `t9-complete-inventory.log`와 exit JSON은
native exit 0이다. Inventory **592개**, application 관련 **28/28 PASS**이며 새 thermal
검사 9개와 기존 static/nonlinear/eigen, 실제 HHT 경로를 포함한다. 최초 정상 thermal
입력이 `invalid-analysis-step-count`로 거부된 실패(native test exit 8)를 기록한 후 수정했다.
실행 시점 HEAD는 `14160dd`이고 `t9-tested-source-sha256.json`의 4개 파일은 최종 commit과
일치한다. Lease 경합, solver overflow와 실제 Windows final-file 교체 거부에서도 기존
결과가 보존되고 candidate가 정리됐다.
메인의 독립 probe는 실제 `fesa.exe`와 별도 `h5dump`를 subprocess로 실행했다.
**29개 명령**에서 기대 exit가 일치했고, raw 초기 온도·양면 지정 온도·모든 frame의
시간/실제 delta·source step·반력/flux shape·finite 값·초기 recovery 부재를 확인했다.
미지원 source DOF12와 usage 오류 이후에도 기존 최종 파일 SHA256이 유지됐다.
`.dryforge/review/t9_cli/t9-7cccfcd/`에 stdout/stderr, `execution.json`, `result.json`과
script/binary hash를 포함한 `review-execution.json`을 보존했다. Probe 자체도 native exit 0이다.
이는 실행 경로와 저장 계약 검증이며 실제 열해의 정확도 판단은 다음 물리 검증에 속한다.
### 원본 reference 비교의 실패와 진단 전환
NT11 외부 비교를 실제 실행했으나 **실패**했다. 원본 Step-1/time37/DOF11의 729행 중
87행이 기준을 벗어났고 RMS/S=0.04967408461567323으로 승인 0.01을 초과한다.
별도 C++ 공통 정책 비교도 같은 실패를 재현했다. 승인 식을 FESA 코드 없이 조립·계산한
독립 oracle는 FESA와 최대 7.673861546209082e-12 차이로 같은 reference 불일치를 보였다.
수치·원본 hash·재현 명령·의사결정 범위는 [충돌 보고서](reference-comparison-conflict.md)에
기록했다. 사용자 선택 A에 따라 승인 정식화와 공통 tolerance를 유지하고 원본 비교를
진단으로 분류했다([STH-ADR-015](ADR.md#sth-adr-015-원본-reference-진단-전환과-새-모델-비교)).
실패 행과 family metric은 변경하지 않는다. 새 reference 모델·CSV는 아직 제공되지 않아
그 필수 비교를 실행하거나 기능 완료로 표시하지 않는다.
결정 전 T10 comparator checkpoint `95ba2c5`는 별도 작업 branch에 보존하고 필수 검사가 실패한
상태로 구현 branch에 병합하지 않았다. Configure/build/inventory는 native 0,
inventory 599개이며, 최종 schema/identity 음성검사 **6/6 PASS**(native 0)다.
원본 application/reference 실행의 CTest는 native 8이고, 최종 comparator 소스로 같은
생성 HDF5를 다시 비교한 standalone 실행은 native 1로 같은 실패를 확인했다.
`.dryforge/evidence/t10-checkpoint-{source,artifact}-sha256.json`과 실제 실행/row 보고서를
보존했고, 4개 tested-source hash와 clean checkpoint의 일치를 메인이 확인했다.
이 checkpoint에는 물리·수렴성 검사 구현이나 production 변경이 없다.
### T10 해석해·수지·수렴성과 원본 진단 비교
T10 고정 소스 `9e8afb9`의 8개 파일은
`T2/.dryforge/evidence/t10-final-eigen-source-sha256.json`의 SHA256과 모두 일치한다.
실행 JSON의 revision은 commit 전 `95ba2c5`지만 실행한 작업트리의 8개 파일 hash가
최종 `9e8afb9`와 일치한다. 메인 checkout은 Git의 CRLF checkout 변환으로 raw byte hash가
일부 다르며, 8개 파일의 LF 정규화 내용이 모두 일치함을 별도로 확인했다.
작업 checkout의 MSVC Debug build·inventory는 native exit 0, inventory **617개**다.
`t10-final-focused-test.log`와 즉시 보존한 `t10-final-focused-LastTest.log`에서
**26/26 PASS**, native exit 0, 실제 2901.03초를 확인했다. 146개 수치 기록은
`t10-final-metrics.json`에 있다. Test 코드의 slab 에너지/반력 인덱스 오류는 최초
실패 로그를 보존한 뒤 수정했고, 위 최종 검사는 수정된 소스 전체를 다시 실행한 결과다.
단열 균일장, signed·중첩·구속점 열입력의 component별 수지, full-end 검산 fixture의
재료/두께 단차와 90° fold 저항, 회전·법선 반전·중립면 양쪽 flux trace를 확인했다.
Full-end fixture는 공개 입력의 내부 station BC 지원을 뜻하지 않는다. 독립 rational
Q1×P2 조립에서 8×8 고유모드 `K v−λ_h C v`의 405행과 C-노름을 검산했다.
실제 시간 수렴차수는 **0.981712, 0.990734, 0.995336, 0.997660**으로 승인 구간에 있다.
전체 이력을 저장한 8/16/32/64의 2D 공간 수렴은 온도 차수
**1.996702, 2.000769, 2.006577**, gradient 차수
**0.997220, 0.999305, 0.999826**이다. 64 mesh의 시간/공간 오차 비는
**0.00686327**이고 온도·flux payload는 **81,074,472 byte**였다.
Slab는 `t=.001,.01,.05,.1,.5,1`에서 N=4096/8192, 각 1×1/2×2 mesh의
**24개 실제 해석·147,456증분**을 실행했다. 모든 완료 프레임의 온도·flux와
에너지·반력을 독립 two-P2/Fourier interval 및 실제 residual 기반 오차 경계로
판정했다. 최대 `(solver+oracle bound)/(D_space/1000)`은 **0.163432**다.
초기 시점의 두께 오차 plateau는 관측됐으나 후기 `.5/1`의 T/E/R은 도달하지
않은 것으로 분리해 보고한다. 이는 고정 5점의 두께 해상도 한계다.
Warped patch의 production 3점 대 독립 3점 K/C·BE 온도/flux 검산과 3/5/7점
진단은 기존 고정 [kernel 검토](#요소-kernel과-형상-검증)의 실제 evidence를 재사용했다.
7점을 exact해로 취급하지 않으며 이 검토만으로 전역 warped procedure 전체를
검증했다고 주장하지 않는다.
독립 수치 검토자는 Gauss header 112개 값과 slab header 654개 값을 generator로
재생성하여 일치함을 확인했다. 수정된 에너지/반력 index, 고유모드 405행,
C-노름 및 승인 오차 예산에서도 차단 finding이 없었다. 별도 비교기 검토는 원본
진단 분류에도 row identity/schema·공통 tolerance 실패가 그대로 드러남을 확인했다.
원본 CLI/reference 검사는 **테스트로는 PASS**지만 실제 비교는 **87/729행 FAIL**,
RMS/S=**0.049674084615673214**, max abs=**245.26294650091936**,
`blocking=false`다. 원본 INP/CSV SHA256은 실행 전후 승인 값과 같다.
새 필수 reference는 별도 경로의 모델·CSV를 아직 받지 못하여 미실행이다.
메인에서 CMake 등록을 반영한 작업트리의 configure/build/inventory는 native 0이다.
실행 JSON의 revision은 commit 전 `69fa4ca`이며 등록 변경은 `9f7fb2e`로 확정했다.
inventory **617개**, 새 compiler warning 없음이다. 짧은 물리·비교기 통합 검사
**10/10 PASS**도 native 0이다. 관련 로그는 메인의
`.dryforge/evidence/t10-integrated-*`와 작업 checkout의 위 `t10-final-*`에 보존한다.
전체 회귀의 최종 결과는 아래에 별도로 기록했다. 독립 physics 평가는
[물리 검토 보고서](physics-evaluation.md)에 기록했다.
### 독립 최종 구현 검토
독립 검토자가 `a3502c9..926e97e`의 열해석 구현과 Feature 계약을 읽기 전용으로
대조했다. 수정이 필요한 finding은 발견하지 못했다. Q4×P2 K/C·24개 Fourier flux trace,
raw 초기값을 사용하는 BE RHS·실제 delta·full residual 반력, 결정적 조립과 accepted
history의 소유권, 입력의 fail-closed 처리, candidate close/readback/atomic finalize,
reference identity와 공통 tolerance를 확인했다. 검토자는 파일을 변경하거나 검사를
실행하지 않았으며, 이 검토를 build/CTest의 실행 evidence로 취급하지 않는다.
새 필수 reference의 미제공과 physics 보고서의 두께 경계층·곡면·접합부 검증 한계는
그대로 남는다.
### 통합 전체 회귀와 남은 완료 조건
Production/test/CMake 소스가 `9f7fb2e`인 통합 구현에서
`ctest --preset windows-msvc-ninja-debug --output-on-failure`를 필터 없이 실행했다.
후속 commit은 문서만 변경했으며 최종 검토 revision `926e97e`와 해당 소스의 diff가 없다.
전체 **617개: 614 PASS, 2 FAIL, 1 SKIP**, native CTest exit **8**이다.
Baseline의 478개 검사 이름이 모두 포함되며 추가된 **139개는 모두 PASS**다.
전체 CTest green이라고 보고하지 않는다.
| 기존 비통과 항목 | 최종 판정 | 실행 시간 | Baseline과 수치 기록 대조 |
| --- | --- | --- | --- |
| `HhtShellPhysics.C08aRigidManufacturedOrbitFullCoordinateTimeConvergence` | FAIL | 928.48초 | status와 GTest metric body 정확히 일치 |
| `HhtShellPhysics.C08bFreeFiniteRotationPlateSelfConvergenceAndPhysicalInvariants` | FAIL | 2478.99초 | status와 GTest metric body 정확히 일치 |
| `HhtTemporalVerification.C04aFinestReductionComparisonIsNumericallyResolved` | SKIP | 0.07초 | status와 판정 불가 사유 정확히 일치 |
세 항목은 기존의 무관한 HHT 미해결로 유지한다. 시간 정보만 제외한 assertion·metric
본문을 `compare_baseline_metrics.py`로 대조한 native exit은 0이며, 실패 기준을
완화하거나 검사를 제외하지 않았다. 독립 최종 검토에서도 새로운 코드 finding이 없다.
검토자가 종료 후 전체 617개 test block·집계·native exit 복구 기록·baseline 대조를
읽기 전용으로 확인했고 기존 검토 결론을 유지했다.
실행 중 세션 재시작으로 stdout 수집 wrapper가 종료되어
`t11-full-test.log`는 547번 시작까지만 기록됐다. 살아 있던 CTest PID 67836의 Windows
process handle을 유지하고 `GetExitCodeProcess`로 최종 native exit 8을 직접 캡처했다.
모든 617개 verdict와 `End testing`이 있는 CTest 원본 `LastTest.log`를 종료 직후 복사해
보존했다. Wrapper의 불완전한 stdout를 전체 결과 근거로 사용하지 않는다.
최종 evidence는 메인 `.dryforge/evidence/`의 다음 파일이다.
- `t11-full-test-recovered-exit.json`: native exit·argv·대상 source revision·복구 방법.
- `t11-full-LastTest.log`: 전체 검사별 실제 결과와 수치 출력.
- `t11-full-outcomes.json`: 617개 최종 verdict, baseline 포함 여부와 추가 139개 판정.
- `t11-hht-baseline-comparison.json`: 기존 2 FAIL·1 SKIP의 정확한 metric body 대조.
최종 원본 NT11 비교는 **87/729행 FAIL**, RMS/S=**0.049674084615673214**,
max abs=**245.26294650091845**, `numerical_passed=0`, `blocking=0`이다.
진단 분류 검사는 PASS지만 외부 수치 일치가 PASS라는 뜻이 아니다. 원본 INP/CSV의
SHA256은 전체 실행 후에도 STH-C08 값과 일치한다.
사용자가 새 reference 모델·NT11 CSV는 **아직 준비 중**이라고 답했다. 별도 경로의
파일·identity가 제공되면 승인 공통 tolerance로 새 필수 family를 실제 실행해야 한다.
그 전까지 기능의 외부 검증 완료·release 준비를 주장하지 않는다.
### 사용자 요청에 따른 dev 병합과 worktree 정리
2026-10-04 사용자가 작업 내용을 dev에 병합하고 나머지 worktree를 정리하도록
명시적으로 요청했다. 이는 구현 통합·작업 공간 정리 승인이고 새 필수 reference의
검증 통과 승인이 아니다. 해당 미실행 상태와 기존 HHT 미해결은 유지한다.
작업 checkout을 제거하기 전에 dev checkout 기준
`.dryforge/retained/shell-transient-heat-transfer-20261004/`에 검증 자료를 보존한다.
원래 경로와 보존 경로의 대응은 다음과 같다.
| 원래 작업 checkout의 상대 경로 | 보존 디렉터리의 상대 경로 |
| --- | --- |
| 메인 `.dryforge/`의 실행 계획·evidence·review·개인 실행 script | `root-dryforge/` |
| T1 `.dryforge/`의 kernel·procedure 검산과 실행 evidence | `T1-dryforge/` |
| T2 `.dryforge/`의 T10 oracle·검사·검토 evidence | `T2-dryforge/` |
| 메인 reference 옆 생성 HDF5 | `generated-results/` |
| 병합 전 dev의 미추적 원본 INP/CSV | `dev-reference-before-merge/` |
`manifest.json`에 보존한 각 파일의 source·destination·길이·SHA256을 기록하고
복사본을 source hash와 대조한다. 빌드 cache는 검증 evidence로 취급하지 않는다.
기존 reference 두 파일의 byte는 유지하고, 기능 기록과 구현 이력은 dev에서 보존한다.
새 필수 reference를 위한 활성 계획도 위 보존 디렉터리에 남긴다.
@@ -1,589 +0,0 @@
# Shell 과도 열전달 독립 수치 검토
- 검토일: 2026-09-29. **정식화 일관성 및 구현 전 수치 검증 설계 PASS /
solver 구현·정확도·reference·physics 검증 미실행**.
- 독립 검토 대상: formulation commit e052b6e, source 접속 범위 보완 efa5403,
입력 rounding-cell 보완 dd409b5의 실제 diff 및 §7.1–7.2.
[PRD STH-C07](PRD.md#sth-c07--독립-검증과-수치-계약의-선행-조건)의 위임 범위에서
아래 식·상수·fixture·판정 기준을 production kernel 구현 전에 고정한다.
- 상태: **DONE_WITH_CONCERNS**. 고정 5점의 초기 침투층 및 warped 3×3 적분 오차를
정량 기록했다. 지원 방법·geometry·외부 tolerance 변경을 제안하거나 승인하지 않는다.
- 기준: [정식화](formulation.md), [승인 ADR](ADR.md), [공통 수치 기준](../TOLERANCE.md),
[개발 규칙](../standards.md), [수치 주의점](../engineering-notes.md).
재현 계산은 [scratch appendix](verification-oracles.md#독립-oracle와-재현)에 있다.
Reviewer는 정식화 작성자가 아니며 production·test·reference를 수정하지 않았다.
## 1. 판정과 이전 검토의 정리
2026-09-28 Proposed 검토는 당시 evidence다. NR-01의 product-domain 및 normalized
station 접속, NR-02의 raw 초기값과 BC 상속/override는 현재 승인 계약으로 해결되었다.
NR-03의 edge 입력 제안은 **011 전체 연기**로 대체된다. 과거 edge weight는 이번
구현·검증 요구가 아니다. 종료 시간의 정수배 제한 제안도 폐기되었다.
| 항목 | 독립 판단 / 현재 경계 |
| --- | --- |
| 약형·분리 행렬·부호 | Q4×두 C0 P2, 실제 midsurface metric, exact Z, consistent C와 BE는 일관된다. 단위·node-major 순서·법선 방향을 확인했다. |
| Whole patch | affine normal image의 separating vector certificate 및 bilinear midpoint identity의 injectivity 유도가 성립한다. 적분점만 검사하는 것은 불충분하다. |
| Source 접속 | 전역 node-edge scan은 disconnected/contact 범위를 침범할 수 있었다. efa5403의 source endpoint를 공유하는 coarse/partial edge 후보 제한을 독립 확인했다. |
| 초기 jump | raw t=0 보존 및 첫 RHS의 C_fc T_c_old 항이 필요하다. Slab의 δ→0 자유 초기 극한은 분석용 oracle이며 raw 초기 dataset을 바꾸는 projection 절차가 아니다. |
| 시간 | 표현오차 수준 endpoint-close와 실제 shortened δ를 구별하는 predicate를 검산했다. 저장 시각의 subtraction과 연산 δ를 혼동하지 않는다. |
| Recovery | 4 면내점×6 두께 trace의 global q와 R=[e1,n×e1,n], 0−/0+ 분리, full residual 반력 부호가 일관된다. t=0 flux/reaction은 없다. |
| 남는 위험 | consistent C의 초기 undershoot, 고정 두께 해상도, metric conditioning, 3×3 유한 적분 오차. Clamp/lumping/차수 변경/지원 축소로 숨길 수 없다. |
PASS는 **설계의 수학적 일관성과 독립 사전 oracle**에 한정한다.
실제 MSVC solver·HDF5·외부 reference 결과가 통과했다는 뜻은 아니다.
## 2. Geometry와 mesh 판정
### 2.1 Whole-patch certificate와 uncertainty
정식화 §2.1의 r=a+bξ+cη+dξη, w=w0+uξ+vη를 사용한다.
Convex set W={w(ξ,η)}의 원점에 가장 가까운 w*는 w*·w≥||w*||²를 만족한다.
따라서 네 corner에 대한 **같은** 고정 double 벡터 s의 양의 interval dot product가
전체 patch를 증명한다. 후보 최소점 계산이 부정확해도 최종 certificate가 양수이면
안전하다. 후보 탐색 실패를 수학적 퇴화의 증명으로 보고하면 안 된다.
r(p)−r(q)=Dr((p+q)/2)(p−q)이므로 전체 patch에서 rank(Dr)=2이면 단사다.
이 증명은 bilinear map 전용이며 일반 고차 curved element로 확대하지 않는다.
**고정 규칙:** binary64, u=2^-53, epsilon=2^-52, round-to-nearest,
gradual underflow; 입력 double은 singleton interval이다. 각 사칙연산 endpoint를
양쪽 nextafter로 한 번씩 넓힌다. 0을 포함한 divisor는 사용하지 않는다.
sqrt는 비음수 endpoint를 계산한 뒤 같은 방식으로 넓힌다. 재척도는 exact
power-of-two이며 손실된 subnormal도 enclosure에 포함한다. Fast-math/FTZ와
증명되지 않은 재결합은 이 bound의 전제가 아니다.
모든 lower(s·w_corner)>0이면
J_lower=min lower(s·w_corner)/upper(sqrt(s·s))>0를 보존한다.
증명되지 않으면 다른 유한 candidate를 시도하고 모두 실패하면 uncertain geometry로
fail closed한다. 정확히 중복된 좌표·source node, 비유한 값, 증명된 w=0은 invalid다.
고정 Jacobian epsilon·warp/aspect cutoff는 **0개**다. Certificate 뒤에도 각 사용점의
metric inverse/gradient가 유한하고 interval에서 0 divisor가 배제되는지 확인한다.
κ(g)는 진단과 오차 전파에 쓰며 지원 cutoff가 아니다.
### 2.2 Source topology와 T-edge uncertainty
Edge incidence는 1 또는 반대 순서의 2; 3 이상·같은 방향·중복 patch는 거부한다.
각 vertex link는 하나의 path 또는 cycle이며 끊어진 fan·branch·중복 link는 거부한다.
T-edge 후보는 **같은 instance에서 source endpoint를 공유한**
(p,q)와 (p,v) 또는 (v,q)이고 p,q,v는 서로 다른 node다. Coarse 방향을 양쪽으로
시도한다. a=q−p, b=v−p의 interval에서 |a_j| lower가 가장 큰 성분 j를 택하고
λ=b_j/a_j, 잔여 성분 b_i−λa_i를 계산한다.
- 잔여 성분 interval 하나가 0을 제외하거나 λ가 [0,1] 밖이면 비접합이다.
- 공선 및 0<λ<1이 증명되면 nonconforming T-edge다.
- 공선/열린 구간 포함이 불확실하면 uncertain 접속으로 실패한다. 정확한 끝점 일치는
별도 중복 좌표 진단이며 열린 구간으로 clamp하지 않는다.
- Interval이 0을 포함한다고 exact 공선을 주장하지 않는다. Exact double-rational
검산은 선택 사항이며 새 arbitrary-precision runtime dependency를 요구하지 않는다.
Interval fail-closed만으로 계약을 충족한다.
모든 node 대 모든 edge scan, 좌표 welding, source 후보가 없는 component 접촉 검출은
하지 않는다. 같은 component의 멀리 떨어진 부분끼리의 접촉도 대상이 아니다.
이는 global contact 검증을 완료했다는 뜻이 아니다.
### 2.3 고정 geometry fixture군
| 군 | 좌표/변환과 필수 판정 |
| --- | --- |
| Affine | (0,0,0),(1,0,0),(1,1,0),(0,1,0); 평행사변형 (0,0,0),(2,0,0),(3,1,0),(1,1,0). Certificate PASS. |
| 평면 왜곡 | (0,0,0),(2,0,0),(1.5,1,0),(0,1,0). J>0; M exact, S의 3×3 exactness 주장 금지. |
| Saddle | r=(ξ,η,aξη), a=.1,.5,2; 자연 corner를 node로 사용. w=(-aη,-aξ,1), s=(0,0,1), J_lower=1의 exact 근거. 모두 지원. |
| 긴 aspect | rectangle 1×2^-40; 유효 certificate 필요. 조건수와 metric 계산을 별도 기록하며 aspect만으로 거부 금지. |
| Scale/이동/회전 | 유효군의 2^-200,1,2^200배; unit patch에 (2^20,-2^20,2^20) 이동. Signed axis permutation 및 z축 3-4-5 회전. 입력 반올림을 oracle interval에 포함. |
| 적분점 검사의 반례 | r=(ξ,(ξ−1/4)η,0). 3×3 모든 점의 J는 비영이지만 ξ=1/4에서 J=0. Whole-patch 거부. |
| Invalid | bow-tie 순서, 반복 source/좌표, collinear 네 node, NaN/Inf. 자동 재순서/normal flip 금지. |
| Uncertain | affine b=(1,1,0),c=(1,1+2^-52,0): cancellation interval이 0을 포함할 수 있다. 증명된 certificate 또는 uncertain 실패만 허용; 임의 epsilon/형상 보정 금지. |
| 접속 양성 | 반대 순서 edge 공유 Q4 두 개, 90도 fold, h=1/2 두께 단차, k=1/10 재료 접속; disconnected 두 patch. |
| 음성/대조 | edge 3 incidence, 동일 directed edge, node 하나만 공유한 두 fan; coarse(p,q)/partial(p,v),v=(p+q)/2. 각 음성의 별도 source identity/instance 좌표 접촉 대조군은 자동접속·T-edge 거부가 없어야 함. |
## 3. 내부 roundoff, 행렬 및 solve 기준
### 3.1 공통 오차 언어
외부 5%/1%와 무관하게 γ_m=mu/(1−mu),u=2^-53이며 mu<1을 요구한다.
행렬/shape는 Frobenius norm, solve는 ∞ norm, 물리 integral은 아래 L2 norm이다.
분모가 0이면 numerator도 정확히 0일 때만 통과한다. max(1,scale)은 금지다.
P_ij는 계산한 signed 합의 절댓값이 아니라 **각 quadrature/분리 항의 절댓값 합**이다.
Cancellation 때문에 P와 |X|를 바꿔 쓰지 않는다. 단위 affine fixture의 closed-form
rational oracle에 대한 고정 roundoff bound는 다음과 같다.
| 검사 | 고정 bound / 연산 수 근거 |
| --- | --- |
| M,S,Z 및 분리 C,K | \|\|X_hat−X_exact\|\|F ≤γ_4096\|\|P_X\|\|F. 점당 shape·metric·gradient·weight·entry scalar path128 이하×9+분리/scatter64 이하를 포괄한다. |
| 직접54점 대 분리 | \|\|X_54−X_sep\|\|F ≤γ_8192\|\|P_54\|\|F+γ_4096\|\|P_sep\|\|F. 54×128+reduction<8192. |
| Affine gradient·H·R | component 절댓값 전파 scale P에 error norm≤γ_2048\|\|P\|\|. Partition sum은 P=Σ\|H\|, gradient sum은 P=Σ\|\|B_p\|\|; 비영 patch는 exact gradient 크기도 포함. |
| Symmetry | \|\|X−Xᵀ\|\|F≤2E_X. E_X는 위 bound. 강제 대칭화로 검사를 통과시키지 않는다. |
| 상수 null | \|\|K1\|\|2≤sqrt(20)E_K; \|1ᵀC1−2\|≤20E_C. Fraction 검산은 exact equality. |
| h scaling | h=1/2,1,2, k/rho/c=1; C와 면내 K는 h, normal K는 1/h. 두 계산의 E를 해당 배율로 더한다. |
Operation budget은 단순 loop의 최장 path 상한이다. 실제 경로가 이보다 길면 결과에 맞춰
상수를 늘리지 않고 경로/interval을 다시 검토한다. 일반 warped/translated geometry에는
affine bound를 그대로 쓰지 않는다. §2 interval을 같은 quadrature에 전파하여 X^I를
얻고 E_X=||max(|lower(X^I)−X_mid|,|upper(X^I)−X_mid|)||F를 사용한다.
각 point의 κ2(g)와 κ2(g)γ_64를 기록한다. Inverse perturbation 상대 증폭은
κ2(g)γ_64/(1−κ2(g)γ_64)이고 분모가 양수가 아니면 이 상계는 불확실하다.
직접 interval inverse가 유한 enclosure를 증명하면 사용하고 아니면 uncertain이다.
κ를 geometry 지원 조건으로 바꾸지 않는다. Roundoff와 quadrature 절단 차이는 별개다.
### 3.2 Exact fixture와 rank
단위 square,h=2,k=rho=c=1:
M=1/36 [[4,2,1,2],[2,4,2,1],[1,2,4,2],[2,1,2,4]],
S=1/6 [[4,-1,-2,-1],[-1,4,-1,-2],[-2,-1,4,-1],[-1,-2,-1,4]].
길이1 ζ 구간의 Z0=1/30 [[4,2,-1],[2,16,2],[-1,2,4]],
Z1=1/3 [[7,-8,1],[-8,16,-8],[1,-8,7]]을 station(1,2,3),(3,4,5)에
조립한다. C=M⊗Z0,K=S⊗Z0+M⊗Z1, node-major 순서다.
정확 유리수 LDL에서 C의20 pivot>0,K의 leading19 pivot>0,K1=0,1ᵀC1=2를
확인했다. C SPD,K PSD/rank19가 독립 증명된다.
Floating 결과는 matrix bound 외에 C의 λ_min>E_C, K를 1⊥에 제한한
λ_min>E_K와 null eigenvalue 크기≤E_K를 확인한다. Eigen 연산 자체의 backward
error enclosure도 더한다. Separation이 불가능하면 rank는 inconclusive이며
clamp로 PASS를 만들지 않는다. 전역 K는 component마다 null mode 하나다.
Gradient fixture는 단위 affine 평판의 접선 b에 대해 T=A+b·r+cz,
A=2,b=(3,-2,0),c=4로 q=−k(b+cn)이다. Warped patch의 임의 global b에
같은 식을 강제하지 않으며 그 경우 실제 reciprocal tangent의 projection을 사용한다.
Constant T=2, 면내/두께선형도 각각 검사한다. 회전 후 q는 같은 회전,
normal 반전 후 station a↔6−a와 조건까지 재매핑한다. RᵀR=I,detR=+1.
Trace state(0,0,0,1,2)는 q3(0−)=0,q3(0+)=−4k/h이며 평균하지 않는다.
### 3.3 Free solve, certified residual과 forward 전파
n=free DOF 수,A=A_ff,b=같은 δ와 full old state로 만든 RHS다.
Full residual의 free 성분 및 독립 A T_f−b를 확인한다.
β_limit=nγ_(n+1)+γ_(2n+4)+γ_(4n+8).
첫 항은 Cholesky의 |L||L|ᵀ bound와 || |L||L|ᵀ ||∞≤n||A||∞에서,
나머지는 triangular solves 및 residual 평가의 dot-product bound에서 나온 보수적인
**허용 상한**이다. Backend가 전제를 충족하지 못하면 더 엄밀한 backward-error
evidence가 필요하며 이 상한을 늘리지 않는다. n=0은 free 검사 대신 full reaction·수지를
검사한다. 큰 n의 상한을 실제 오차로 가정하여 forward budget에 넣지 않는다.
실제 sparse 행 i의 nonzero 수 ν_i로 독립 r_tilde=fl(A T_f−b)를 평가하고
E_r,i=γ_(2ν_i+2)(Σ_j|A_ij||T_f,j|+|b_i|),
R_cert=upper(|| |r_tilde|+E_r ||∞),
D_lower=lower(||A||∞||T_f||∞+||b||∞),
β_cert=upper(R_cert/D_lower).
모든 scale sum/norm은 outward interval로 계산하며 underflow 항도 §2 enclosure로
보존한다. D_lower=0은 exact-zero numerator/denominator를 별도 확인한다.
**β_cert≤β_limit**가 residual 판정이다. Full C dT/δ+KT−Q 평가에도 자신의 실제
row count·signed term absolute scale로 같은 의미를 검산한다. 구체적으로
E_r,full,i=γ_(2ν_C,i+2ν_K,i+6)(Σ|C_ij||dT_j|/δ+Σ|K_ij||Tnew_j|+|Q_i|)
+γ_1Σ|C_ij|(|Tnew_j|+|Told_j|)/δ로 둔다.
관측 residual에 roundoff enclosure를 더하므로 우연히 작은 computed residual을 그대로
정확한 잔차라고 가정하지 않는다.
n≤100의 소형 fixture는 독립 dense inverse로 Z≈A^-1를 구하고
θ=upper(||I−ZA||∞)<1을 확인한다. 그러면
upper(||A^-1||∞)≤upper(||Z||∞)/(1−θ)가 rigorous inverse-norm bound다.
κ∞(A)β_cert<1일 때 relative forward bound는
2κ∞(A)β_cert/(1−κ∞(A)β_cert).
정확 coefficient의 E_A/E_b도 numerator에 E_A||T_f||∞+E_b로 더한다.
이때 stored A의 inverse 상계를 J라 하면 J E_A<1을 확인하고 정확 A*의 inverse는
J/(1−J E_A)로 넓힌다. 따라서 absolute forward bound는
`[J/(1−J E_A)](R_cert+E_A||T_f||∞+E_b)`다(E_A/E_b는 ∞ norm bound).
이 denominator의 조건을 증명하지 못하면 inconclusive이며 clamp/상한 확대는 금지다.
큰 **구조화된 convergence fixture**는 inverse나 최악 β_limit 없이 다음 C-norm
전파를 사용한다. 정확 oracle C*,K*에 대한 증분 residual의 component enclosure를
R_n=|r_tilde|+E_r+E_C|dT|/δ+E_K|Tnew|+E_Q
로 둔다(E_C/E_K는 성분별 절댓값 오차 상계). 단열 homogeneous error의
(C*+δK*)^-1C*는 C* norm에서 contraction이므로
B_T^n=B_T^(n−1)+δ||R_n||2/sqrt(λ_lower(C*))
가 온도 C-norm error의 사전 고정된 누적 상계다. 초기 nodal cos의 interval 오차를
B_T^0에 포함한다. Normalized k=rho=c=h=1 fixture에서는 이 C-norm이 실제
product-volume temperature L2다. Sampling/오차 norm 적분의 interval도 더한다.
n×n unit square의 tensor mass는 Mx⊗My⊗Cz다.
Uniform Q1 1D mass의 Gershgorin lower는 1/(6n), 두 길이1/2 P2 segment의
Cz lower는 1/60이므로 **λ_lower(C*)=1/(2160n²)**.
K generalized upper는 면내 12n²+12n² 및 thickness
||Kz||∞/λ_lower(Cz)=(64/3)/(1/60)=1280으로
**λ_upper(K*,C*)=24n²+1280**.
따라서 gradient L2 solver budget은 sqrt(24n²+1280) B_T^n이다.
이 analytic bounds는 §6의 unit affine tensor fixture 전용이며 일반 mesh의 condition
보증으로 확대하지 않는다. 큰 fixture에 production sparse inverse를 요구하지 않는다.
§4 에너지 roundoff 상계의 β_limit는 보수적인 acceptance로 유지할 수 있지만,
§6–7의 oracle 차이/관찰 order budget에는 β_cert 또는 위 R_n 전파를 사용한다.
## 4. 에너지, 반력과 BE 소산
C=[[2,1],[1,2]],K=[[1,-1],[-1,1]],δ=1/2,Q=0,rawT0=(0,0),g2=1에서
T_f=(-1/5,1/25),Rc=(24/5,36/25)를 exact 확인했다. Raw constrained 항을 빼거나
먼저 g로 projection하면 실패한다. Signed undershoot를 삭제하면 안 된다.
이 fixture와 all-constrained 검사는 algebra다. Full-end strip ΣL/(khw)도 assembly
전용이며 공개 내부 station BC 지원을 뜻하지 않는다.
dT=Tnew−Told,ΔE=1ᵀC dT,Rc=(C dT/δ+K Tnew−Q)_c.
온도 영점을 뺀 두 큰 U를 차분하는 대신 ΔE를 직접 계산해도 같은 물리량이다.
**물리 수지 defect**
D_E=|ΔE−δ(ΣQ+ΣRc)|,
S_E=Σ_ij|C_ij dT_j|+δΣ_i|Q_i|+δΣ_c|Rc_i|.
m=전체 DOF 수,D_f=||A_ff||∞||T_f||∞+||b_f||∞라 하면
B_E=δnβ_limit D_f+δ||1ᵀK||1||Tnew||∞+γ_(4m+12)S_E
+δγ_(2m+6)(|| |C||dT| ||1/δ+|| |K||Tnew| ||1+||Q||1)
+γ_1Σ_ij|C_ij|(|Tnew_j|+|Told_j|).
두 번째 줄은 full residual 평가의 dot-product/division/subtraction 오차이며,
마지막 항은 온도 subtraction 오차다. D_E≤B_E와 free β≤β_limit를 **각각**
만족해야 한다. K 상수 mode 항은 §3 null bound도 만족해야 하므로 K 오류를 면제하지
않는다. 보고 metric D_E/S_E에서 S_E=0이면 exact-zero 분기다. Actual free residual을
수지에 다시 넣어 0을 만드는 대수 항등식은 물리 평형 판정을 대신하지 않는다.
단열·무입력·무구속의 별도 C-norm 소산 defect는
D_C=|0.5(TnewᵀCTnew−ToldᵀCTold+dTᵀCdT)+δTnewᵀKTnew|.
S_C는 앞의 세 C quadratic form의 **성분별 절댓값 합**에 각각 0.5를 곱하고,
마지막 K quadratic form의 성분별 절댓값 합에 δ를 곱하여 모두 더한 값이다.
B_C=δ||Tnew||1β_limit D_f+γ_(6m+20)S_C
+0.5||dT||2||C−Cᵀ||2||Told||2
+γ_1(|Tnew|+|Told|)ᵀ|C||dT|.
Residual work, 합/subtraction, symmetry defect, dT 오차 전파를 구분한 식이다.
D_C≤B_C,||Tnew||C²≤||Told||C²+2B_C를 검사하고 S_C=0은 exact-zero다.
이 제곱 norm은 실제 열에너지와 단위가 다르다.
P/−P,constrained DOF의 P,disconnected component를 별도 실행한다. 집중 입력은
signed power 그대로 Q에 가산한다. 단열 균일 T=2 보존,no-BC transient,component별
수지,정상상태 ΣQ+ΣRc=0을 검사한다. Raw flux 경계 적분과 FE 반력의 exact equality를
강제하지 않는다.
## 5. Warped quadrature의 사전 진단
§2 affine/distorted/saddle a=.1,.5,2에서 k=rho=c=h=1.
각 3/5/7 surface rule과 exact Z로 C_r,K_r를 독립 계산한다.
대표 응답은 old=0,Q=e_(node1,station11),δ=.1,noBC의 한 BE 증분이다.
d_X(r,s)=||X_r−X_s||F/||X_s||F(X=C,K),
d_T=||T_r−T_s||2/||T_s||2,
d_q=max_trace||q_r−q_s||2/max_trace||q_s||2.
| fixture | d_C(3,5)/(5,7) | d_K(3,5)/(5,7) | d_T(3,5)/(5,7) | d_q(3,5)/(5,7) |
| --- | --- | --- | --- | --- |
| distorted | roundoff/roundoff | 2.189065e-7/5.970737e-12 | 2.385149e-6/6.505495e-11 | 1.070060e-6/2.918586e-11 |
| saddle .1 | 3.944257e-7/7.758135e-13 | 3.862043e-7/7.500898e-13 | 7.916236e-7/1.286406e-12 | 9.283663e-7/1.601583e-12 |
| saddle .5 | 1.667355e-4/1.431780e-7 | 1.643476e-4/1.408553e-7 | 3.301794e-4/2.150240e-7 | 3.703751e-4/2.649276e-7 |
| saddle 2 | 5.631484e-3/2.213607e-4 | 5.618988e-3/2.217451e-4 | 6.690109e-3/1.033767e-4 | 5.483294e-3/8.641481e-5 |
표의 반올림 값을 tolerance로 쓰지 않는다. 판정은 (1) production3점이 독립3점
oracle와 §3 bound로 일치,(2) 각 fixture d(5,7)≤d(3,5)+양쪽 roundoff bound,
(3) affine 모든 차이는 roundoff 이내다. Appendix에서 재계산한 metric과 interval을
함께 보고한다. d가 roundoff 이하이면 ratio/order를 만들지 않는다. 독립54점/분리
C,K의 scratch 상대 norm 차이는 최대7.73e-16이었다.
이는 **선정 fixture의 적분 수렴 진단**이다. 7점을 exact해로 보거나 모든 허용
geometry에 같은 d 상한을 요구하지 않는다. 일반 모델의 진단이 비단조이면 조사·보고하며
지원 geometry cutoff,production 차수 변경/adaptive quadrature로 자동 교체하지 않는다.
## 6. 시간·면내 수렴
### 6.1 고정 spatial eigen oracle의 시간1차
Affine 단열 unit square,균일8×8 mesh,h=1,k=rho=c=1,
station 동일 v_ij=cos(πi/8)를 쓴다. Consistent Q1의 정확한 discrete eigenvector이며
λ_h=6(1−cos(π/8))/[(1/8)²(2+cos(π/8))].
독립 assembly로 Kv−λCv 및 C normalization을 §3 bound로 확인한다.
τ=1/λ_h,δ=τ/N,N=16,32,64,128,256,INC≥256.
T_exact=e^-1v,T_BE=(1+1/N)^-Nv,
e_N=||T_BE−T_exact||C/||v||C,p=log2(e_N/e_2N).
모든 pair의 p 허용구간은 **[.97,1.02]**.
독립 기대값은 .9817124,.9907338,.9953356,.9976599.
log(1+x)의 전개에서 e_N=e^-1/(2N)+O(N^-2)이며 N≥16의 독립 값으로 구간을
고정했다. Roundoff propagation이 e_N/1000을 넘으면 order는 inconclusive다.
공간오차와 시간오차를 분리하는 고정 spatial oracle다.
### 6.2 Fourier rectangle의 면내2차/gradient1차
Unit square,h=1,k=rho=c=1,noBC/noQ,T0=cos(πx)를 모든 station에 동일하게 둔다.
**τ=.001,N=32,δ=τ/32,INC=32**로 고정한다. 연속해는
T=cos(πx)exp(−π²t),∇T=(-πsin(πx)exp(−π²t),0,0).
Affine n×n,n=8,16,32,64를 모두 유지한다. Norm은 product-volume temperature와
global gradient L2다. 평가 적분은 cell별12×12 Gauss×exact thickness이며
12→16점 oracle 변화≤해당 오차의1e-8을 확인한다.
독립 exp(-λ_hτ) spatial oracle와 finite-N modal BE oracle를 동시에 사용한다.
BE-exponential 온도 L2 차이는 continuum semidiscrete 오차의 **1/32 이하**여야 한다.
작은 시간과 bounded N은 이 시간오차 분리를 유지하면서 모든 accepted frame을 저장한다.
별도 시간1차 검사는 §6.1에 그대로 남긴다.
| n | Semidiscrete T L2 | BE T L2 | BE gradient L2 | Time/spatial ratio |
| ---: | ---: | ---: | ---: | ---: |
| 8 | 9.903629946e-3 | 9.902647628e-3 | 2.487150504e-1 | .0001089543 |
| 16 | 2.482303232e-3 | 2.481327028e-3 | 1.245973612e-1 | .0004305809 |
| 32 | 6.209759342e-4 | 6.200013808e-4 | 6.232870148e-2 | .0017171164 |
| 64 | 1.552689976e-4 | 1.542952827e-4 | 3.116810477e-2 | .0068632659 |
모든 pair의 기존 허용구간을 유지한다: T p **[1.95,2.05]**,gradient p **[.98,1.03]**.
독립 finite-N 기대값은 T=(1.9967024,2.0007686,2.0065775),
gradient=(.9972203,.9993050,.9998262).
Q1 smooth interpolation O(h²)/O(h),λ_h=π²+O(h²)와 위 독립 계산에 근거한다.
12/16점 변화 최대1.30e-13이며 criterion1e-8보다 작다.
§3의 실제 residual 기반 B_T 및 gradient budget이 측정 오차의1/1000 이하여야
order 판정을 사용한다. 집중 source의 특이 해에 이 smooth order를 요구하지 않는다.
**실행 예산:** n=64에서도 sparse solve32회다. Mandatory flux와 initial 포함
temperature raw doubles는
4096×24×3×32×8 + 4225×5×33×8 = **81,074,472 bytes(약77.3MiB)**.
모든4개 mesh의 같은 payload 합은107,772,960 bytes(약102.8MiB), solve총128회다.
Model의 local frames는 시간마다 중복하지 않는 승인 schema이므로 largest mesh에서
추가4096×4×9×8=1,179,648 bytes다. Identity/HDF5 overhead와 실제 peak memory를
evidence에 별도 기록한다. 이 예산은 history/flux 생략이나 제품 출력 변경을 전제하지 않는다.
초기 문서의 τ=.1,N=16n²는 n64에서 flux만 약154.6GB인 과도한 fixture였으므로
**kernel 구현 전** 독립 계산으로 위 설계로 대체했다. Production 결과에 맞춘 tolerance
완화가 아니며 기존 공간 order·시간오차 분리·모든 frame 계약을 그대로 유지한다.
## 7. 고정5점 slab: 허용 이산 오차와 plateau
Unit area,h=1,k=rho=c=1,Ti=0,Ts=1,bottom step/top 단열이며 면내 node 조건은 같다.
x=(0,1/4,1/2,3/4,1) T,six trace x=(0,1/4,1/2−,1/2+,3/4,1)의 q3,
E=1ᵀCT,bottom 전체 Rc를 비교한다. Unit square1요소 및2×2 mesh다.
t=0은 rawTi이며 flux/reaction을 생성하지 않는다.
무차원 시간 t*=αt/h²,T/ΔT,q/(kΔT/h),E/(ρc area hΔT),R/(k areaΔT/h).
이 fixture의 척도는 모두1이다. T/flux는 위 위치의 max absolute error,
E/R은 absolute error다. Station 상대오차나 near-zero floor를 만들지 않는다.
### 7.1 Fourier 및 독립 P2 spectral oracle
정식화 §10 series에서 M=80,최소 t*=.001.
β=π²t*/4,r=exp[-8β(M+1)]이면 온도 tail≤
4exp[-β(2M+1)²]/[π(2M+1)(1-r)],flux/reaction tail≤
2exp[-β(2M+1)²]/(1-r),energy tail≤temperature tail.
최소 시간에서도 모든 무차원 tail은 **1e-27 미만**이다.
1−r는 -expm1 표현과 수학적으로 동등한 exact interval subtraction으로 평가한다.
**Oracle 산술은 다음 방식으로 고정**하며 C++17 libm의 막연한 1ULP 보장을 가정하지 않는다.
Appendix E가 실제 실행 가능한 표준 라이브러리 generator다.
1. π=16 atan(1/5)−4 atan(1/239), 각 alternating arctan을 j=0…63까지 exact
유리수로 합한다. 첫 생략항 q^129/129로 양쪽 enclosure를 만든다.
2. 이후 모든 interval endpoint는 격자 2^-320으로 floor/ceil하여 outward 보존한다.
원래 π enclosure 폭은2^-295 미만이다.
3. Slab station의 phase는 정수 kπ/8이다. 정수 quadrant reduction으로 |r|≤π/4에
옮긴 뒤 sin/cos Taylor 각각32항을 쓴다. Remainder는 보수적으로1/65!,1/64!다.
4. exp(-x),0≤x≤512는 y=x/1024의64차 Taylor,
remainder≤(1/2)^65/65!를 더하고 interval squaring10회로 복구한다.
x≥512이면 0≤exp(-x)≤2^-512(e>2)을 사용한다.
5. M=80 series를 위 interval로 합한다. Tail을 더하기 전 각 quantity enclosure 폭은
**2^-270 미만**이어야 한다. 여기에 앞의 analytic Fourier tail을 더한다.
필요한 binary64 fixture는 lower를 float 변환 후 nextafter(-∞),upper를 변환 후
nextafter(+∞)하여 export한다. 이 endpoint 차이가 B_oracle이며 별도 floor가 없다.
실제 generator에서 최소 시간의 tailT=1.658661e-30,tailq=4.194724e-28,
최종 enclosure 최대 폭8.389448e-28이었다. 나머지 시각의 폭은8.93e-89 이하였다.
이 방식은 테스트 oracle generator 전용이며 production arbitrary-precision dependency가
아니다. Spectral oracle의 exp도 동일 enclosure를 적용하고 eigenpair/coefficients의
backward-error interval을 함께 전파한다. Appendix A의 통상 math 계산은 표 수치의
간단한 재현용이며 interval E가 transcendental correctness의 근거다.
T10은 generator의 T/q/E/R interval을 outward binary64 fixture로 고정하여 사용할 수
있다. 또는 binary64 outward Taylor가 동일한 최종 B_oracle 및 §6–7의 오차 budget을
증명하면 그 경로도 가능하다. 2^-270은 고정 고정밀 generator의 내부 검사이며 production
정밀도 요구가 아니다. 무차원 interval error를 실제 단위로 옮길 때 T는 |ΔT|,
q는 k|ΔT|/h,E는 rho c area h|ΔT|,R은 k area|ΔT|/h를 곱한다.
온도 기준 이동은 interval 폭을 바꾸지 않는다. 선형 sampling/trace/energy/reaction row L은
성분별 |L| times input error로 전파하며 signed 값 자체를 절댓값으로 바꾸지 않는다.
독립1D P2 FE는 길이1/2인 두 segment를 조립한다.
C_seg=(1/2)/30 [[4,2,-1],[2,16,2],[-1,2,4]],
K_seg=2/3 [[7,-8,1],[-8,16,-8],[1,-8,7]].
Bottom elimination 뒤4×4 generalized eigenproblem의 Cholesky+Jacobi 계산은
3D product kernel을 호출하지 않는다.
Raw 초기 정책에서 δ→0 자유 초기 극한은
T_f(0+)=-C_ff^-1C_fc=(-5/34,3/17,-1/34,1/17).
y0=T_f(0+)−1,C-orthonormal v_j의 a_j=v_jᵀC_ff y0로
T_f(t)=1+Σa_jv_j exp(-λ_jt).
이는 첫 finite-step RHS의 극한이며 FESA t=0 state/dataset을 변경하지 않는다.
λ=(2.4686647564,22.9461660098,77.0631371712,198.6985026508).
Scratch eigen residual3.994e-16,C-orthogonality8.069e-16.
Solver 허용치는 이 숫자 자체가 아니라 §3의 coefficient/eigen/residual enclosure다.
BE oracle는 exp(-λt)를 (1+λt/N)^-N으로 대체한다.
N>1의 마지막 반력은 modal derivative -λ×final amplitude로 구할 수 있어
작은 δ의 subtraction cancellation을 피한다.
### 7.2 사전 고정 물리 오차와 허용치
각 t*마다 별도 N=4096,8192,δ=t*/N,INC=N run.
아래 첫 값은 **δ→0 P2 공간오차**, 괄호는 위로 반올림한 **D_space**다.
초기의 큰 허용치는 method의 제한이며 제품 전 범위 정확도 보증이 아니다.
| t*/sqrt(t*) | T∞ | q∞ | E | Rc |
| --- | --- | --- | --- | --- |
| .001/.03162 | .1399996(.141) | 10.64818(10.7) | .03031345(.0304) | 10.86927(10.9) |
| .01/.10000 | .01524952(.0153) | 1.274464(1.28) | .00467600(.00468) | .823687(.824) |
| .05/.22361 | .00597851(.00598) | .252470(.253) | .00129690(.00130) | .00631488(.00632) |
| .1/.31623 | .001387325(.00139) | .135427(.136) | .000665986(.000666) | .009524106(.00953) |
| .5/.70711 | .000141091(.000142) | .0283969(.0284) | .0000944866(.0000945) | .000525742(.000526) |
| 1/1 | .0000413942(.0000414) | .00815759(.00816) | .0000158234(.0000159) | .0000478014(.0000479) |
Continuum 오차 허용치는 **D_space+D_time(N)+B_oracle+B_solver**다.
D_time은 독립 modal 차이의 max/absolute norm이다. 온도/flux sampling L,
energy row1ᵀC,reaction row K_cf−C_cf C_ff^-1K_ff 각각에 대해
LΣa_jv_j[(1+λ_jt/N)^-N−exp(-λ_jt)]를 계산한다.
Cancellation이 불확실하면 Σ|La_jv_j|×|amplitude차이|로 enclosure한다.
B_oracle은 series tail+summation/eigen interval,B_solver는 §3 행렬/solve 오차를
L에 전파한 값이다. **B_oracle+B_solver>D_space/1000이면 plateau 판정은
inconclusive**다. 이 조건은 geometry 지원 cutoff가 아니다.
N=4096의 독립 D_time은 아래와 같다. N=8192에서는 대략 절반이며 appendix가 재현한다.
| t* | T∞ | q∞ | E | Rc |
| ---: | ---: | ---: | ---: | ---: |
| .001 | 7.5820e-7 | 8.7770e-6 | 4.6839e-8 | 6.0181e-6 |
| .01 | 1.6648e-5 | 1.9198e-4 | 1.8013e-6 | 1.5383e-4 |
| .05 | 2.9817e-5 | 2.2173e-4 | 7.3403e-6 | 1.9484e-4 |
| .1 | 3.0985e-5 | 1.9790e-4 | 1.1177e-5 | 1.6441e-4 |
| .5 | 6.8903e-5 | 1.1406e-4 | 4.3931e-5 | 1.0879e-4 |
| 1 | 8.0305e-5 | 1.3224e-4 | 5.1125e-5 | 1.2621e-4 |
물리 오차 허용치 외에 **동시에** FESA와 finite-N BE oracle가
B_oracle+B_solver 내에서 일치해야 한다. 큰 초기 공간오차가 잘못 구현된 방법까지
허용하지 않도록 분리했다. Table rounding은 production 결과와 무관하게 고정했다.
### 7.3 Plateau 정량 보고
E_space=||oracle_P2−Fourier||와 FESA error e_N에 대해
|e_N−E_space|≤D_time+B_oracle+B_solver를 확인한다.
면내1×1과2×2의 slab 차이는 §3 roundoff 이내여야 한다.
두께 지배 해가 면내 refinement로 좋아진다고 판정하면 실패다.
관측 plateau의 기준은 D_time+B_oracle+B_solver≤E_space/10.
N=8192에서 .001/.01/.05/.1의 네 quantity는 충족한다. 뒤 시각에서 미충족하는
T/E/R은 **미도달**로 보고하고 δ→0 spectral 공간오차를 따로 제시한다.
예컨대 t*=1의 E는 시간오차가 공간오차보다 커서 두 N만으로 plateau가 관측되었다고
할 수 없다. 침투 깊이 sqrt(αt),station 간격 h/4,segment 길이 h/2,E_space,D_time을
함께 보고한다. 초기 큰 flux/reaction 오차는 음수 온도 제거로 고칠 수 없다.
## 8. 시간격자 predicate, count와 재분해
### 8.1 독립 반증과 수정 범위
Arithmetic-only D≤d rad(q)+rad(p)는 입력 표현오차를 모두 포함하지 않았다.
τ=5.351455173733616,d=.17262758624947144,m=31에서
exact |τ−md|=8.326672684688674e-16 > 기존 Bhat=7.507374033534679e-16인데,
입력 rounding-cell I(τ)와 mI(d)는 겹친다. 이 반례를 blocking으로 보고했고
승인된 입력 표현오차 의미에 맞춰 **input-cell overlap**으로 보완했다.
이전 predicate의 실행 성공을 의미 정합 승인으로 대체하지 않았다.
Appendix D는 이 실패 이력을 보존하며 현재 판정은 C를 따른다.
### 8.2 현재 고정 predicate
입력 double x의 원래 rounding-cell은
L_x=(prev(x)+x)/2,U_x=(x+next(x))/2다. Significand LSB가 짝수면 두 endpoint
포함, 홀수면 두 endpoint 제외(nearest-even). 최대 finite의 U_x는 next=Inf를
평균하지 않고 x+(x−prev(x))/2라는 유한 real dyadic threshold다.
최소 subnormal의 half-ULP도 binary64로 먼저 반올림하지 않는다.
τ,d를 공통 power-of-two로 τ∈[1/2,1)에 옮겨 q=fl(τ/d),
m=nearest-even integer(q)를 만든다. **원래 입력의 cell도 동일 scale로 옮긴다.**
Normal화된 값의 이웃으로 cell을 다시 만들면 subnormal 입력의 표현오차를 축소하므로
잘못이다. Endpoint-close는 I(τ)와 mI(d)의 exact 교집합이 비어 있지 않은 경우다.
두 구간이 한 점에서만 닿으면 그 점이 양쪽에 모두 포함되는지도 확인한다.
INC≤2^32−1,q≤INC+1 및 위 scale에서 cell endpoint는 고정 dyadic 격자 2^-88에
정확히 놓인다. Endpoint 정수와 m곱의 보수적 최대122bit는 두 uint64 또는 네 uint32
limb 안에 들어간다. Carry/shift를 검사하는 bounded 정수 산술이며 새 arbitrary-precision
runtime dependency가 아니다. Reviewer의 Fraction은 이 bounded 비교의 독립 oracle다.
나눗셈·곱셈 roundoff는 후보/연산 bound 설명에 쓰고 cell을 더 넓히는 별도 epsilon으로
중복 가산하지 않는다.
q overflow 또는 INC+1 초과는 preflight 실패; close면 N=m이다. Nonclose에서는
X=τs2^88,Y=ds2^88,f=floor(q)로 fY≤X<(f+1)Y를 exact 비교하고 필요하면 f를
한 번 ±1 교정한다. X=fY이면 N=f, 아니면 N=f+1이다. Division 오차를 count에
그대로 전달하지 않는다. d≥τ는 한 증분 δ=τ의 선행 분기다.
INC 기본100,양의 uint32,N≤INC. 정상 stored time=fl(jd),마지막=입력τ,
strict increase/finite를 검사한다. Close의 마지막 δ는 **d 그대로**다.
실제 shortened만 fma(-(N−1),d,τ)를 쓰며 A/RHS/reaction/energy/dissipation은 같은 δ다.
| fixture | 필수 결과 |
| --- | --- |
| τ=10,d=3 | N=4,stored3,6,9,10;δ=3,3,3,1;마지막 refactor |
| τ=2,d=3 및 d=τ | 한 증분 δ=τ |
| τ=.9,d=.3 | N=3,stored 마지막 .9;세 δ=.3,factorization 재사용 |
| τ=1,d=.1 | N=10;stored .30000000000000004 등을 보존,δ=.1 유지 |
| nextafter(.9,-∞),d=.3 | .8999999999999999:close,N=3,δ=.3 |
| 두 ULP 아래 τ,d=.3 | .8999999999999998:nonclose,N=3,finalδ=.2999999999999998 |
| nextafter(.9,+∞),d=.3 | .9000000000000001:nonclose,N=4,finalδ=1.6653345369377348e-16 |
| 기존 반례 τ=5.351455173733616,d=.17262758624947144 | input cells overlap,N=31,모든 δ=d;가짜 tiny 증분 제거 |
| Subnormal midpoint | a=최소 positive subnormal:τ=20a,d=6a는 even/even 닫힌 접촉,N=3;τ=17a,d=5a는 odd/odd 열린 접촉,N=4,마지막δ=2a |
| Scale/최대값 | (.9,.3)×2^-1000 및×2^1000은 같은 count/close;τ=1e-323,d=5e-324는 N=2;τ=maxfinite,d=fl(maxfinite/3)는 N=3 |
| INC | N/N−1 경계,생략100;0,-1,1.5,4294967296 실패;τ=2^32,d=1,INC=2^32−1은 사전 count 실패 |
| 비유한/비표현 | Inf/NaN/0/음수,binary64에서0으로 underflow하는 token 거부;거대한 τ/d는 cast/allocation 전 실패 |
| 저장 가능성 | finite-positive/uint32 count 제한 아래 정상 간격 d는 ulp(jd)보다 충분히 크다. Generator의 finite/strict-increase 보호 분기는 중복/Inf 시간을 주입한 내부 fixture로 검사;불가능한 정상 공개 입력을 만들지 않음. |
특히 nextafter(.9,+∞)/.3의 tiny remainder를 유지하는 근거는 단순 predicate 결과가
아니다. exact |τ−3d|=1.6653345369377348e-16이고 두 입력 cell 반폭 합은
1.3877787807814457e-16이므로 **실제 rounding cells가 분리된다**. 이를 가짜 표현오차
증분으로 흡수하면 입력 표현오차 범위를 넘는다. 반면 위 m=31 반례는 cells가 겹치므로
추가 tiny 증분을 만들면 안 된다. 작은 실제 δ의 나쁜 conditioning은 §3과 candidate
보존으로 처리하며 nominal δ로 바꾸지 않는다.
현재 predicate의 independent seed901 10,000쌍은 원래 cell Fraction overlap과
2^-88 정수 비교가 모두 일치했고 최대122bit를 넘지 않았다. 원래 subnormal,maxfinite,
열린/닫힌 접촉,반례 및 scale 경계를 따로 검산했다. FMA oracle는 exact Fraction
product-sum을 한 번 float로 반올림한다. Production dependency 요구가 아니다.
## 9. 외부 reference와 evidence 경계
필수 원본은 reference/shell-thermal/shell-thermal.inp와
reference/shell-thermal/shell-thermal NodalTemperature.csv다.
이 검토는 원본 복사·수정·생성 또는 Abaqus 실행을 하지 않았다.
2026-09-28 read-only snapshot(729row,min=-15.5742216,max=1000)은 과거 관찰이며
이번 solver comparison을 대신하지 않는다.
Family=(shell-thermal,source Step-1,exact t=37,NT11 bottom,scalar,
사용자 일관 온도 단위,blocking),identity=(Part-1-1,source node label,DOF11).
CSV instance 생략은 입력 단일 instance와 label 집합 검증으로만 복원한다.
Missing/extra/duplicate/nonfinite/frame/instance/station 오류는 tolerance 전에 실패한다.
공통 TOLERANCE 그대로 S=max|reference|=1000,B=.01S=10;
|r|≤B이면 |f−r|≤B,그 밖에는 |f−r|/|r|≤.05,
모든729row RMS/S≤.01을 동시에 만족한다. S=0이면 모든 값 exact0이다.
−15.5742216은 relative 대상이며 clamp/온도 영점 이동 금지.
추가 Abaqus CSV는 필수가 아니다. 한 frame으로 내부 station/flux/reaction/시간 이력을
검증했다고 할 수 없다.
## 10. 실행 기록과 완료 경계
git rev-parse --show-toplevel로 T1 worktree를 확인했다(exit0).
NumPy/SciPy import 환경 probe는 NumPy 부재로 exit1;설치 없이 표준 라이브러리
scratch로 전환했다. [Appendix](verification-oracles.md#독립-oracle와-재현)의 명령은 Python blocks를
지정 순서로 메모리에서 실행한다. 실제 scratch exit0의 핵심 출력:
- Fraction:C positive pivot20,K positive leading pivot19/K1=0,1ᵀC1=2;
jump−1/5,1/25 및 reaction24/5,36/25.
- 직접54점/분리식:다섯 geometry 상대 norm 차이 최대7.73e-16.
- Slab eigen residual3.994e-16/C-orthogonality8.069e-16,§7 공간/시간 오차.
- §5 quadrature,§6 예상 차수,§8 time predicate/INC assertions.
최종 재현 명령은 appendix 첫 PowerShell block이며 exit 0,
마지막 출력은 ALL SCRATCH BLOCKS COMPLETED였다. Input-cell 독립 비교는
10,000/10,000 및 CURRENT TIME GRID ASSERTIONS PASSED를 출력했다.
실행 예산 보완 뒤 five-block 재검산도 exit 0이었다. Bounded-space order/time ratio,
FOURIER INTERVAL ASSERTIONS PASSED 및 최종 완료 출력을 확인했다.
상대 링크 파일 존재/Markdown 표 열 수 정적 검사도 exit 0(8 links,2 files),
두 소유 파일만 대상으로 한 git diff --check는 exit 0이었다.
수학 scratch는 production correctness를 증명하지 않는다. **미실행:**MSVC build/CTest,
구현 단위·통합 검사,FESA 결과 생성,NT11 comparison,실제 solver convergence/energy/
physics 검토. 필요한 수치 기준은 고정되었으며 구현 결과 뒤에 선택할 미정 tolerance는
없다. 필수 실패 시 구현·conditioning·oracle 전제를 조사하고 physics/method/acceptance
충돌은 메인에 보고한다.
@@ -1,55 +0,0 @@
# Shell transient heat transfer — T10 물리 결과 평가
평가 대상은 T10 소스 `9e8afb9`의 실제 MSVC focused 실행이다. 메인 병합 후 HEAD는
`9f7fb2e`다. [승인 계약](PRD.md) STH-C03/C04/C07/C08,
[수치 기준](numerical-review.md), [reference 결정](ADR.md#sth-adr-015-원본-reference-진단-전환과-새-모델-비교)을
적용했다. 이 평가는 T10 결과의 물리 개연성 검토이며 기능 완료 또는 release 승인이 아니다.
실행 근거는 작업 checkout의
`.dryforge/worktrees/T2/.dryforge/evidence/t10-final-focused-test.log`,
`t10-final-focused-LastTest.log`, `t10-final-metrics.json`,
`t10-final-eigen-source-sha256.json`, `t10-final-reference-after-sha256.json`이다.
소스 manifest는 실행한 physics/oracle/comparator 파일 8개를 식별하고 metrics의
`source_hashes_match=true`다. Focused CTest는 **26/26 PASS, native exit 0**이다.
실행 JSON은 commit 전 revision `95ba2c5`를 기록하지만 T2 작업트리의 8개 raw hash는
최종 commit `9e8afb9`와 일치한다. 메인 checkout의 일부 raw byte 차이는 CRLF 변환이며
LF 정규화 내용은 8개 모두 같다.
이 숫자에는 원본 reference가 수치 실패로 남았는지를 확인하는 진단 테스트의 PASS가
포함된다. 따라서 26/26을 원본 수치 비교의 통과로 읽을 수 없다.
| 물리 항목 | 실제 결과와 계산 | 판정 범위 |
| --- | --- | --- |
| 단열·잔차·에너지 | 0, +2, -2 균일 온도 8증분에서 시작값 유지; 0장은 수지/소산 defect가 정확히 0. 실제 38개 `THERMAL_PHYSICS` run의 최대 free `beta=1.0613e-14`, full `physical_beta=1.1041e-14`, 최대 `energy_defect=7.2165e-16`; 각 run의 최대 `energy_defect/energy_bound=0.001161`. 단열에서 최대 `dissipation_defect/dissipation_bound=6.194e-5`. | 기록된 승인 roundoff bound 안. `beta`·수지·소산 defect 자체는 production 증분 evidence이며, 테스트는 이를 bound와 대조한다. 독립적인 40-DOF 두 component 수지 계산은 아래 항목이다. C-norm 소산은 실제 열에너지 수지와 구별한다. |
| 부호·component 수지·반력 | 두 분리 영역의 signed 입력에서 첫 영역 `+3`의 constrained 입력을 제거해도 온도 이력은 동일하고 반력은 `-3`만큼 변한다: `R_with-R_without=-3` [power]. 두 번째 영역의 `+2+3-5=0`이며 독립 용량 적분에서 `ΔE₂≈0`; 첫 영역은 `ΔE₁-dt·R≈0`을 승인 bound로 확인한다. | 외력 `Q`가 full residual에 남고 reaction 양수가 모델 공급이라는 STH-C04 부호와 일치한다. 검사는 상태/반력 벡터를 읽는다. |
| 두께·재료·접힘 | 길이 1씩, `k₁h₁=1`, `k₂h₂=5`, 폭 1의 전단면 strip 저항 `R=1+1/5=1.2`, `ΔT=1`이므로 `P=1/R=5/6=0.8333333333333333`. 평판·90° 접힘 모두 출력 반력 `0.8333333333333877`, 차이 약 `5.44e-14`, 서로 같은 반력. Flux는 첫 요소 `-5/6` x, 두 번째 요소 평판 `-5/3` x / 접힘 `-5/3` z 방향이다. | 길이·두께·물성에 맞는 열률 및 전역 방향이다. **모든 두께 station을 양 끝에서 구속하고 C=0으로 둔 내부 assembly fixture**이므로 공개 bottom/top-only CLI 사례의 검증으로 확대할 수 없다. 접힘의 실제 3D 접합장도 해상하지 않는다. |
| Flux 방향·trace | `T=2+3x-2y+4(hζ/2)`의 직접 복구에서 `q=(-3k,+2k,-4k)` [power/area]. 회전 후 벡터도 함께 회전하고 법선 반전에는 station이 재매핑된다. `T_station=(0,0,0,1,2)`에서는 중립면 `q₃(0-)=0`, `q₃(0+)=-4k/h`; 평균하지 않는다. Frame은 직교·right-handed 조건을 검사한다. | Fourier 부호와 한쪽 trace 의미에 부합한다. 이 affine/trace 검사는 내부 all-constrained partition fixture이며 공개 BC 허용 범위를 넓히지 않는다. |
| 시간·면내 수렴 | 고정 8×8 eigenmode의 N=16→256에서 N=32/64/128/256 오차 `0.00567442, 0.00285549, 0.00143237, 0.000717347`; 관측 차수 `0.981712, 0.990734, 0.995336, 0.997660`, 승인 `[.97,1.02]`. 8/16/32/64 면내 mesh의 T L2 차수 `1.99670, 2.00077, 2.00658` 및 gradient L2 차수 `0.997220, 0.999305, 0.999826`, 승인 `[1.95,2.05]`/`[.98,1.03]`. 최대 time/spatial ratio `0.0068633<1/32`. | 실제 전체 node×5 station 온도 벡터와 독립 eigen/Fourier oracle를 비교한다. 단위 k=1 fixture에서는 gradient 차수가 flux의 공간차수를 대표한다. 일반 점열원·곡면에 이 smooth 수렴차수를 주장하지 않는다. |
| 두께 slab 응답 | 단위 면적, `h=k=rho=c=1`, 초기 0, bottom 1, top 단열의 1×1/2×2 mesh, 시간 `.001,.01,.05,.1,.5,1`, N=4096/8192에서 실제 온도·global q₃·용량 적분 E·반력 합을 각각 독립 finite-N P2 및 Fourier oracle와 대조한다. 96개 quantity-group 결과 중 최대 finite-BE gap/bound `0.01871`, 최대 물리오차/(D_space+D_time+B_oracle+B_solver) `0.999979`; 1×1/2×2 차이는 각 승인 roundoff 안이다. | 두꺼운 layer의 같은 두께 오차가 면내 refinement로 사라지지 않는 결과와 일치. `.001≤t*≤.1`, N=8192의 네 물리량은 모두 plateau 조건 성립. `.5,1`에서는 flux만 plateau이고 T/E/R은 미도달로 표기한다. |
초기 slab에서 `sqrt(alpha·t)=0.03162h` (`t*=.001`)는 station 간격 `0.25h`,
P2 segment 길이 `0.5h`보다 훨씬 작다. 따라서 N=8192에서도 Fourier 대비 최대
무차원 오차는 T `0.13999998`, q `10.64818068`, E `0.03031342`, bottom 반력
`10.86926883`이다. 각각 승인된 `D_space=0.141,10.7,0.0304,10.9`와
시간/roundoff 예산 안이지만, **초기 경계층의 국소 flux 및 반력 정확도는 낮다**.
이 수치 일치는 5점 모델의 의도한 제한을 드러내며 일반 shell의 정확도 보증은 아니다.
`t*=1`의 E는 `E_space=1.58234e-5`, `D_time=2.55629e-5`로 시간오차가 더 커서
plateau 관측을 주장하지 않는다. Slab의 q, E, 반력 계산은 solver 결과를 별도로
집계하지만 Fourier/P2 목표값과 bound는 테스트 oracle가 제공한다.
원본 `reference/shell-thermal/` 입력/CSV SHA256은 계약의
`955C8BEE…00D6EA4` / `765B0D0F…10D3E5`와 일치한다. 실제 HDF5와 비교한
Step-1, `t=37`, NT11 729행에서 **87행 실패**, `RMS/S=0.049674084615673214`
(`S=1000`, `B=10`, 승인 family 한계 `0.01`), 최대 절대차 `245.26294650091936`이다.
STH-ADR-015에 따라 이는 **수치 FAIL인 진단 family**로 유지한다. 독립 승인식 oracle도
같은 불일치를 보였다는 [충돌 기록](reference-comparison-conflict.md)은 원인 범위를
좁히지만 Abaqus 내부 정식화의 옳고 그름을 판정하지 않는다. 분리 경로의 새 필수
reference 모델/CSV가 없어 해당 비교는 **미실행이며 완료 차단**이다.
검증 범위의 경계: T10 물리 검사는 `Domain → Assembly → MKL 증분 → accepted state`
결과를 주로 평가하며 공개 CLI/HDF5의 모든 물리량을 각 증분마다 별도 재적분하지는
않는다. 원본 reference는 실제 HDF5 경로를 사용하나 NT11 한 성분·한 시점뿐이다.
Warped/곡면 전도 응답, 접힘·두께 단차의 국소 3D 전도, 큰 `h/곡률`, 초기 두께
경계층, 점열원 근처 mesh-independent flux는 이 evidence로 검증되지 않는다.
`STH-C01`의 normalized station 공유도 실제 물리 z 일치 또는 접촉 전도를 뜻하지
않는다. T10 focused 26개는 전체 회귀 결과가 아니다. 물리적으로 확인된 범위는
위 균일·affine·strip·smooth 면내·slab fixture에 한정한다.
@@ -1,113 +0,0 @@
# 승인 정식화와 원본 NT11 reference 비교의 충돌
상태: **비교 분류 결정 완료; 원본 수치 불일치 유지**. 작성일: 2026-09-29.
사용자는 정식화를 유지하고 기존 비교를 진단으로 전환하며 reference 모델을 변경하는
선택 A를 승인했다([STH-ADR-015](ADR.md#sth-adr-015-원본-reference-진단-전환과-새-모델-비교)).
이 문서는 결정 전의 실패 evidence를 그대로 보존한다. 정식화·초기화·공통 허용치는 변경하지 않는다.
기준은 [PRD STH-C03/C04/C08](PRD.md), [formulation](formulation.md),
[numerical review](numerical-review.md), [공통 tolerance](../TOLERANCE.md)다.
## 1. 실제 실행과 비교
구현 `7cccfcd`의 실제 CLI로 원본 `reference/shell-thermal/shell-thermal.inp`를 실행했다.
100개 증분과 초기 프레임을 저장하고 exit 0으로 종료했다(153.5초).
원본 INP와 CSV의 SHA256은 실행 전후 PRD의 승인 값과 일치했다.
FESA가 생성한 HDF5는 입력의 lexical sibling이다.
별도 `h5dump`로 source step, 정확한 time=37/increment=37, DOF11,
`Part-1-1`/node label과 thermal row mapping을 확인했다. 729행 모두 중복·누락 없이
대응했고 값은 finite였다. Source DOF13이나 nearest frame으로 대체하지 않았다.
| 판정량 | 실제 결과 | 승인 기준 |
| --- | ---: | ---: |
| 비교 행 | 729 | 모든 source 행의 일대일 대응 |
| Reference scale S / near-zero B | 1000 / 10 | 공통 정책 |
| 행별 실패 수 | 87 | 0 |
| RMS/S | 0.04967408461567323 | ≤0.01 |
| 최대 절대 차이 | 245.26294650091884 | 해당 행 기준으로 판정 |
| FESA 최솟값 / 최댓값 | -258.91081180091885 / 1000 | clamp 금지 |
대표 source node 22에서 reference=-13.6478653, FESA=-258.91081180091885이며,
행별 5% 기준은 0.682393265다. 근소한 반올림 차이가 아니다.
별도 C++ comparator도 기존 `ReferenceTolerancePolicy::Evaluate`를 그대로 사용하여
동일 실패를 확인했다. `ThermalReference.OriginalAbaqusNt11AtExactTime37`의 실제 결과는
729행, 87개 실패, RMS/S=0.049674084615673214, max_abs=245.26294650091862다.
결정 전 해당 CTest 실행은 native exit 8이었고, 비교를 통과로 표시하지 않았다.
## 2. FESA 코드를 사용하지 않은 독립 계산
독립 Python/SciPy 실행기는 원본 텍스트에서 729개 좌표, 676개 사각형 connectivity,
Set-10의 196개 node를 읽었다. 모든 요소가 평면 직사각형임을 확인한 뒤,
직사각형 Q1의 닫힌형 M/S와 두 P2 구간의 정확한 Z0/Z1을 조립했다.
FESA parser·kernel·assembler·시간 적분 코드는 호출하지 않았다.
입력 물성 k=54, rho=7850, c=460, h=0.5를 사용하고 승인된 식
`C=rho*c*(h/2)*M⊗Z0`, `K=k*((h/2)*S⊗Z0+(2/h)*M⊗Z1)`을 적용했다.
전체 raw T0=0, Set-10의 source DOF11만 g=1000, dt=1인 BE를 37회 계산했다.
3449 free / 196 constrained DOF를 별도 SciPy sparse LU로 풀었다.
| 독립 계산량 | 결과 |
| --- | ---: |
| FESA NT11과 최대 절대 차이 | 7.673861546209082e-12 |
| 최대 정규화 선형 잔차 | 3.599964225668404e-16 |
| Reference 행별 실패 수 | 87 |
| Reference RMS/S | 0.04967408461567341 |
| 독립 NT11 최솟값 | -258.9108118009203 |
이 수치는 새 tolerance가 아니다. 승인 식의 독립 계산도 같은 큰 reference 차이를
재현한다는 관측이다. 이 사례의 차이를 단순한 source mapping이나 production 행렬
계산 버그로 설명할 근거는 발견하지 못했다. 전체 물리 검증의 통과를 뜻하지는 않는다.
별도 read-only 검토자는 원본 hash·수치 상수·경계조건, oracle의 tensor 식/BE 우변,
HDF/CSV identity join과 공통 tolerance 계산을 대조하여 이 충돌 evidence의 차단 오류를
발견하지 못했다. Oracle는 범용 입력기가 아니며 물성·두께·시간·g는 이 사례의 확인된
상수를 사용한다. 다른 입력에 대한 정확도나 Abaqus의 내부 동작을 입증하는 검사는 아니다.
## 3. 공개 문서 확인과 해석 한계
공개 [Choosing a Shell Element](https://docs.software.vt.edu/abaqusv2025/English/SIMACAEELMRefMap/simaelm-c-shellelem.htm)의
Heat Transfer Shell Elements 절은 다중 온도점을 갖는 shell의 DOF11을 아랫면 온도로
설명한다. [Uncoupled Heat Transfer Analysis](https://docs.software.vt.edu/abaqusv2025/English/SIMACAEANLRefMap/simaanl-c-heattransfer.htm)의
Initial Conditions 절은 생략된 초기 온도의 기본값을 0으로 설명한다.
이 관측 가능한 의미는 현재 reference mapping을 DOF13으로 바꿀 근거를 주지 않는다.
확인 가능한 문서는 2025판이며 2024 URL은 접근 거부였다. 공개 문서와 한 시점의 CSV만으로
reference 생성기의 내부 행렬·적분·초기 처리 방식이나 그 동등성을 단정하지 않는다.
다른 방법의 수치 결과가 비슷하다는 이유만으로 FESA의 승인 방법을 교체하지 않는다.
## 4. 승인된 결정과 남은 비교
원본 모델에서는 다음 두 조건을 동시에 충족하지 못했다.
1. 승인된 consistent C, Q4×P2 두 구간, exact thickness 적분과 raw 초기값 BE를 유지한다.
2. 이 원본 NT11 비교를 현재 공통 tolerance의 필수 blocking 검사로 통과한다.
사용자는 첫 조건을 유지하고 기존 원본 비교를 **진단**으로 전환하는 선택 A를 승인했다.
공통 tolerance 계산과 87행 실패, RMS/S 초과를 그대로 기록한다. 비교기의 새 진단
테스트가 통과하더라도 이는 열해가 원본 CSV tolerance를 만족한다는 뜻이 아니다.
독립 해석해·수지·수렴성 검증과 새 reference 모델의 필수 비교가 완료 조건이다.
새 모델과 결과 CSV는 기존 원본과 분리된 경로에 두기로 했다. 구체적인 경로·hash·비교
시점·성분·row identity는 아직 확정되지 않았다.
원본을 변경하거나 새 모델의 결과를 추정하여 통과로 표시하지 않는다.
T1–T9 구현·검증은 유지한다. T10은 원본 비교의 실제 FAIL metric을 보존한 진단
분류와 독립 slab/시간·공간 수렴 검증을 마쳤다. 자세한 실제 실행은
[구현 검증 보고서](implementation-verification.md#t10-해석해수지수렴성과-원본-진단-비교)에
기록했다. 전체 회귀와 독립 physics·최종 구현 검토도 완료했고 그 결과는
[구현 검증 보고서](implementation-verification.md#통합-전체-회귀와-남은-완료-조건)에
기록했다. 새 필수 reference 비교는 별도 모델·CSV 제공 전까지 미실행이다.
## 5. 보존 evidence
- 메인 `.dryforge/review/t10_reference/`: `run.py`, `execution.json`, stdout/stderr,
생성 log, `compare.py`, 실제 dump/exit 기록, `comparison.json`, `comparison-rows.json`.
- 같은 경로의 `independent.py`, `independent-execution.json`,
`independent-comparison.json`, `independent-rows.json`: native exit 0,
NumPy 2.4.6 / SciPy 1.17.1, 사용 script hash 포함.
- 작업 checkout T2 `.dryforge/evidence/t10-reference-test.log` 및 native exit JSON:
공통 정책을 사용한 C++ 비교의 실제 실패. Checkpoint `95ba2c5`의 최종 schema/identity
음성검사는 6/6 PASS이며, 동일 출력의 최종 comparator는 native 1로 비교 실패를 유지했다.
세부 source/artifact hash와 보존 상태는 [구현 검증 보고서](implementation-verification.md)에 기록했다.
- 원본 reference INP/CSV는 변경하지 않았다. 생성 HDF5와 CSV row view는 외부 reference
artifact를 교체하는 자료가 아니다.
@@ -1,508 +0,0 @@
# 독립 oracle와 재현
수치 검토에 사용한 exact/rational·spectral·Gauss 검산 코드를 보존한다. 코드의 결과는 당시 정식화 검산이며 실제 solver의 전체 검증을 대신하지 않는다. 실제 실행·소스·한계는 [구현 검증](implementation-verification.md)과 [수치 검토](numerical-review.md)를 따른다.
# 독립 numerical oracle scratch
이 파일은 numerical-review.md의 사전 계산을 재현하는 문서 부록이다. Solver 또는
production test가 아니며 외부 package·reference·출력 파일을 사용하지 않는다.
Python 표준 라이브러리만 사용한다. Fraction은 oracle에서 입력 double의 exact 산술을
확인하는 용도이며 production arbitrary-precision dependency를 요구하지 않는다.
T1 root에서 다음 명령은 각 Python block을 순서대로 메모리에서 실행한다.
```powershell
@'
from pathlib import Path
import re
p = Path('docs/shell-transient-heat-transfer/numerical-oracle-scratch.md')
blocks = re.findall(r'```python\n(.*?)```', p.read_text(encoding='utf-8-sig'), re.S)
assert len(blocks) == 5
scope = {}
for block in blocks:
exec(compile(block, str(p), 'exec'), scope)
print('ALL SCRATCH BLOCKS COMPLETED')
'@ | python -
```
실행 exit는 0이어야 한다. Assertion 범위는 exact algebra와 독립 oracle 자체에 한정된다.
수렴/이산 오차의 출력 숫자 자체를 production tolerance로 사용하지 않고 본 보고서의
roundoff·conditioning·오차 전파 기준을 적용한다. NumPy 부재 probe exit1은 실행 실패
이력으로 보고서에 남겼으며 아래 코드에는 NumPy를 쓰지 않는다.
## A. Slab spectral·quadrature·공간/시간 oracle
```python
import math,json
def zeros(n,m=None): return [[0.0]*(n if m is None else m) for _ in range(n)]
def tr(A):return list(map(list,zip(*A)))
def mm(A,B):return [[sum(a*b for a,b in zip(row,col)) for col in zip(*B)]for row in A]
def mv(A,x):return [sum(a*b for a,b in zip(row,x)) for row in A]
def solve(A,b):
a=[list(r)+[v]for r,v in zip(A,b)];n=len(b)
for k in range(n):
p=max(range(k,n),key=lambda i:abs(a[i][k]));a[k],a[p]=a[p],a[k]
d=a[k][k]
for j in range(k,n+1):a[k][j]/=d
for i in range(n):
if i!=k:
d=a[i][k]
for j in range(k,n+1):a[i][j]-=d*a[k][j]
return [r[-1]for r in a]
def chol(A):
n=len(A);L=zeros(n)
for i in range(n):
for j in range(i+1):
v=A[i][j]-sum(L[i][k]*L[j][k]for k in range(j))
L[i][j]=math.sqrt(v)if i==j else v/L[j][j]
return L
def eig(A):
a=[r[:]for r in A];n=len(a);v=[[float(i==j)for j in range(n)]for i in range(n)]
for z in range(100*n*n):
p,q=max(((i,j)for i in range(n)for j in range(i+1,n)),key=lambda ij:abs(a[ij[0]][ij[1]]))
if abs(a[p][q])<1e-15*sum(abs(a[i][i])for i in range(n)):break
phi=.5*math.atan2(2*a[p][q],a[q][q]-a[p][p]);c=math.cos(phi);s=math.sin(phi)
J=[[float(i==j)for j in range(n)]for i in range(n)]
J[p][p]=J[q][q]=c;J[p][q]=s;J[q][p]=-s
a=mm(mm(tr(J),a),J);v=mm(v,J)
ix=sorted(range(n),key=lambda i:a[i][i])
return [a[i][i]for i in ix],[[v[j][i]for i in ix]for j in range(n)]
def slabmat(ne):
n=2*ne+1;C=zeros(n);K=zeros(n);l=1/ne
for e in range(ne):
for i in range(3):
for j in range(3):
C[2*e+i][2*e+j]+=l/30*[[4,2,-1],[2,16,2],[-1,2,4]][i][j]
K[2*e+i][2*e+j]+=[[7,-8,1],[-8,16,-8],[1,-8,7]][i][j]/(3*l)
return C,K
C,K=slabmat(2);Cf=[r[1:]for r in C[1:]];Kf=[r[1:]for r in K[1:]]
L=chol(Cf);Li=tr([solve(L,[float(i==j)for i in range(4)])for j in range(4)])
lam,V=eig(mm(mm(Li,Kf),tr(Li)));W=mm(tr(Li),V)
yp=[-1-x for x in solve(Cf,[r[0]for r in C[1:]])]
coef=mv(tr(W),mv(Cf,yp))
def spectral(t,n=None):
amp=[a*(math.exp(-l*t)if n is None else math.exp(-n*math.log1p(l*t/n)))for a,l in zip(coef,lam)]
T=[1]+[1+x for x in mv(W,amp)]
if n is None:dT=[0]+mv(W,[-l*x for l,x in zip(lam,amp)])
else:
oldamp=[x*(1+l*t/n)for l,x in zip(lam,amp)]
old=[1]+[1+x for x in mv(W,oldamp)]
dT=[(a-b)/(t/n)for a,b in zip(T,old)]
E=sum(mv(C,T));R=mv(C,dT)[0]+mv(K,T)[0]
q=[]
for e,ss in [(0,-1),(0,0),(0,1),(1,-1),(1,0),(1,1)]:
q.append(-4*sum(a*b for a,b in zip([ss-.5,-2*ss,ss+.5],T[2*e:2*e+3])))
return T,q,E,R
def exact(t):
M=80
T=[1-sum(4/((2*m+1)*math.pi)*math.sin((2*m+1)*math.pi*x/2)*math.exp(-((2*m+1)*math.pi/2)**2*t)for m in range(M))for x in [0,.25,.5,.75,1]]
q=[sum(2*math.cos((2*m+1)*math.pi*x/2)*math.exp(-((2*m+1)*math.pi/2)**2*t)for m in range(M))for x in [0,.25,.5,.5,.75,1]]
E=1-sum(8/((2*m+1)*math.pi)**2*math.exp(-((2*m+1)*math.pi/2)**2*t)for m in range(M))
return T,q,E,q[0]
def errors(a,b):return [max(abs(x-y)for x,y in zip(a[0],b[0])),max(abs(x-y)for x,y in zip(a[1],b[1])),abs(a[2]-b[2]),abs(a[3]-b[3])]
out={"lambda":lam,"projected_initial_free":[1+x for x in yp],"slab":[]}
for t in [.001,.01,.05,.1,.5,1]:
s=spectral(t);x=exact(t)
out["slab"].append({"t":t,"penetration":math.sqrt(t),"spatial_error":errors(s,x),"BE4096_temporal":errors(spectral(t,4096),s),"BE8192_temporal":errors(spectral(t,8192),s),"spectral":s})
print(json.dumps(out,indent=2))
def gauss(n):
x=[];w=[]
for i in range(1,n+1):
z=math.cos(math.pi*(i-.25)/(n+.5))
for _ in range(30):
p0,p1=1.,z
for j in range(2,n+1):p0,p1=p1,((2*j-1)*z*p1-(j-1)*p0)/j
dp=n*(z*p1-p0)/(z*z-1);nz=z-p1/dp
if abs(nz-z)<2e-16: z=nz;break
z=nz
x.append(z);w.append(2/((1-z*z)*dp*dp))
return list(zip(x,w))
def dot(x,y):return sum(a*b for a,b in zip(x,y))
def cross(a,b):return [a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]
def geometry(X,xi,eta):
signs=[(-1,-1),(1,-1),(1,1),(-1,1)]
N=[(1+s*xi)*(1+t*eta)/4 for s,t in signs]
D=[[s*(1+t*eta)/4,t*(1+s*xi)/4]for s,t in signs]
a=[sum(D[i][0]*X[i][j]for i in range(4))for j in range(3)]
b=[sum(D[i][1]*X[i][j]for i in range(4))for j in range(3)]
W=cross(a,b);J=math.sqrt(dot(W,W));norm=[z/J for z in W]
aa,ab,bb=dot(a,a),dot(a,b),dot(b,b);det=J*J
ra=[(bb*x-ab*y)/det for x,y in zip(a,b)];rb=[(aa*y-ab*x)/det for x,y in zip(a,b)]
G=[[D[i][0]*ra[j]+D[i][1]*rb[j]for j in range(3)]for i in range(4)]
return N,G,J,norm
def surface(X,n):
M=zeros(4);S=zeros(4)
for xi,wx in gauss(n):
for eta,wy in gauss(n):
N,G,J,no=geometry(X,xi,eta)
for i in range(4):
for j in range(4):
M[i][j]+=N[i]*N[j]*J*wx*wy;S[i][j]+=dot(G[i],G[j])*J*wx*wy
return M,S
Z0,Z1=slabmat(2)
# h=1: Z0 physical mass already; Z1 physical normal stiffness already.
def mats(X,n):
M,S=surface(X,n);C=zeros(20);K=zeros(20)
for i in range(4):
for j in range(4):
for a in range(5):
for b in range(5):
C[5*i+a][5*j+b]=M[i][j]*Z0[a][b]
K[5*i+a][5*j+b]=S[i][j]*Z0[a][b]+M[i][j]*Z1[a][b]
return C,K
def fn(A):return math.sqrt(sum(x*x for r in A for x in r))
def rel(A,B):return fn([[a-b for a,b in zip(r,s)]for r,s in zip(A,B)])/fn(B)
def response(X,C,K):
A=[[c/.1+k for c,k in zip(cr,kr)]for cr,kr in zip(C,K)]
T=solve(A,[float(i==0)for i in range(20)])
flux=[]
for xi,eta in [(-1/math.sqrt(3),-1/math.sqrt(3)),(1/math.sqrt(3),-1/math.sqrt(3)),(1/math.sqrt(3),1/math.sqrt(3)),(-1/math.sqrt(3),1/math.sqrt(3))]:
N,G,J,no=geometry(X,xi,eta)
for off,s in [(0,-1),(0,0),(0,1),(2,-1),(2,0),(2,1)]:
Ls=[s*(s-1)/2,1-s*s,s*(s+1)/2];Ds=[4*(s-.5),-8*s,4*(s+.5)]
flux.append([-sum((G[i][j]*Ls[a]+no[j]*N[i]*Ds[a])*T[5*i+off+a]for i in range(4)for a in range(3))for j in range(3)])
return T,flux
def rv(a,b):return math.sqrt(sum((x-y)**2 for x,y in zip(a,b)))/math.sqrt(sum(x*x for x in b))
cases={"affine":[[0,0,0],[1,0,0],[1,1,0],[0,1,0]],"distorted":[[0,0,0],[2,0,0],[1.5,1,0],[0,1,0]]}
for w in [.1,.5,2]:cases["saddle"+str(w)]=[[x,y,w*x*y]for x,y in [(-1,-1),(1,-1),(1,1),(-1,1)]]
ans={}
for name,X in cases.items():
ds=[]
for n in [3,5,7]:
C,K=mats(X,n);T,q=response(X,C,K);ds.append((C,K,T,q))
rows=[]
for a,b in [(ds[0],ds[1]),(ds[1],ds[2])]:
rows.append([rel(a[0],b[0]),rel(a[1],b[1]),rv(a[2],b[2]),max(math.sqrt(dot([x-y for x,y in zip(q,r)],[x-y for x,y in zip(q,r)]))for q,r in zip(a[3],b[3]))/max(math.sqrt(dot(r,r))for r in b[3])])
ans[name]=rows
print(json.dumps({"quadrature":ans},indent=2))
def direct54(X):
C=zeros(20);K=zeros(20)
for xi,wx in gauss(3):
for eta,wy in gauss(3):
N,G,J,no=geometry(X,xi,eta)
for off in [0,2]:
for s,ws in gauss(3):
ls=[s*(s-1)/2,1-s*s,s*(s+1)/2]
ds=[4*(s-.5),-8*s,4*(s+.5)]
H=[0.]*20;B=zeros(20,3)
for i in range(4):
for a in range(3):
p=5*i+off+a;H[p]=N[i]*ls[a]
B[p]=[G[i][j]*ls[a]+no[j]*N[i]*ds[a]for j in range(3)]
weight=wx*wy*ws*J/4
for i in range(20):
for j in range(20):
C[i][j]+=weight*H[i]*H[j];K[i][j]+=weight*dot(B[i],B[j])
return C,K
for name,X in cases.items():
C,K=mats(X,3);Cd,Kd=direct54(X)
print("54point",name,rel(C,Cd),rel(K,Kd))
assert rel(C,Cd)<1e-12 and rel(K,Kd)<1e-12
# Generalized eigen residual and C orthogonality, independently reassemble.
C,K=slabmat(2);Cf=[r[1:]for r in C[1:]];Kf=[r[1:]for r in K[1:]]
res=fn([[a-b*l for a,b,l in zip(kr,cr,lam)]for kr,cr in zip(mm(Kf,W),mm(Cf,W))])/fn(mm(Kf,W))
orth=fn([[v-float(i==j)for j,v in enumerate(row)]for i,row in enumerate(mm(mm(tr(W),Cf),W))])
print("slab eigen residual",res,"C orthogonality",orth)
assert res<1e-12 and orth<1e-12
def spatial_bounded(n,steps=32,t=.001,ng=12):
h=1/n;l=6*(1-math.cos(math.pi/n))/(h*h*(2+math.cos(math.pi/n)))
af=math.exp(-steps*math.log1p(l*t/steps));ase=math.exp(-l*t);ae=math.exp(-math.pi**2*t)
et=eg=etime=ese=0.
for i in range(n):
for s,w in gauss(ng):
x=(i+(s+1)/2)*h;measure=w*h/2
v=((1-s)*math.cos(math.pi*i/n)+(1+s)*math.cos(math.pi*(i+1)/n))/2
g=(math.cos(math.pi*(i+1)/n)-math.cos(math.pi*i/n))/h
et+=(af*v-ae*math.cos(math.pi*x))**2*measure
eg+=(af*g+math.pi*ae*math.sin(math.pi*x))**2*measure
etime+=((af-ase)*v)**2*measure
ese+=(ase*v-ae*math.cos(math.pi*x))**2*measure
return math.sqrt(et),math.sqrt(eg),math.sqrt(etime),math.sqrt(ese)
rows=[]
for n in [8,16,32,64]:
a=spatial_bounded(n);b=spatial_bounded(n,ng=16)
ratio=a[2]/a[3];quad=max(abs(x-y)/abs(x)for x,y in zip(a,b))
assert ratio<1/32 and quad<1e-8
rows.append(a)
fluxbytes=n*n*24*3*32*8
tempbytes=(n+1)**2*5*33*8
print("bounded space",n,"T,grad,time,semi",a,"time/space",ratio,"quad",quad,"historybytes",fluxbytes+tempbytes)
for a,b in zip(rows,rows[1:]):
pt=math.log(a[0]/b[0],2);pg=math.log(a[1]/b[1],2)
assert 1.95<=pt<=2.05 and .98<=pg<=1.03
print("bounded order",pt,pg)
time_orders=[]
for n in [16,32,64,128]:
e1=abs(math.exp(-n*math.log1p(1/n))-math.exp(-1))
e2=abs(math.exp(-2*n*math.log1p(1/(2*n)))-math.exp(-1))
order=math.log(e1/e2,2)
assert .97<=order<=1.02
time_orders.append(order)
print("fixed spatial time orders",time_orders)
```
## B. Exact Fraction integration·rank·jump
```python
from fractions import Fraction as F
def fmat(A,d):return [[F(v,d)for v in r]for r in A]
def mul(a,b):
c=[F(0)]*(len(a)+len(b)-1)
for i,x in enumerate(a):
for j,y in enumerate(b):c[i+j]+=x*y
return c
def der(a):return [i*a[i] for i in range(1,len(a))]
def integ(a):return sum(F(x,i+1)for i,x in enumerate(a))
sh=[[1,-3,2],[0,4,-4],[0,-1,2]]
assert [[integ(mul(a,b))for b in sh]for a in sh]==fmat([[4,2,-1],[2,16,2],[-1,2,4]],30)
assert [[integ(mul(der(a),der(b)))for b in sh]for a in sh]==fmat([[7,-8,1],[-8,16,-8],[1,-8,7]],3)
M=fmat([[4,2,1,2],[2,4,2,1],[1,2,4,2],[2,1,2,4]],36)
S=fmat([[4,-1,-2,-1],[-1,4,-1,-2],[-2,-1,4,-1],[-1,-2,-1,4]],6)
fac=[([1,-1],[1,-1]),([0,1],[1,-1]),([0,1],[0,1]),([1,-1],[0,1])]
assert [[integ(mul(a,c))*integ(mul(b,d))for c,d in fac]for a,b in fac]==M
assert [[integ(mul(der(a),der(c)))*integ(mul(b,d))+integ(mul(a,c))*integ(mul(der(b),der(d)))for c,d in fac]for a,b in fac]==S
Z0=[[F(0)for _ in range(5)]for _ in range(5)]
Z1=[[F(0)for _ in range(5)]for _ in range(5)]
for off in [0,2]:
for i in range(3):
for j in range(3):
Z0[i+off][j+off]+=F([[4,2,-1],[2,16,2],[-1,2,4]][i][j],30)
Z1[i+off][j+off]+=F([[7,-8,1],[-8,16,-8],[1,-8,7]][i][j],3)
C=[[M[i//5][j//5]*Z0[i%5][j%5]for j in range(20)]for i in range(20)]
K=[[S[i//5][j//5]*Z0[i%5][j%5]+M[i//5][j//5]*Z1[i%5][j%5]for j in range(20)]for i in range(20)]
def pivots(A):
a=[r[:]for r in A];p=[]
for k in range(len(a)):
d=a[k][k];p.append(d)
for i in range(k+1,len(a)):
for j in range(k+1,len(a)):a[i][j]-=a[i][k]*a[k][j]/d
return p
assert all(x>0 for x in pivots(C))
assert all(x>0 for x in pivots([r[:19]for r in K[:19]]))
assert all(sum(r)==0 for r in K)
assert sum(map(sum,C))==2
cc=[[F(2),F(1)],[F(1),F(2)]];kk=[[F(1),F(-1)],[F(-1),F(1)]]
old=[F(0),F(0)]
for expected,react in [(F(-1,5),F(24,5)),(F(1,25),F(36,25))]:
new=[(2*(2*old[0]+old[1])-1)/5,F(1)]
r=[2*sum(cc[i][j]*(new[j]-old[j])for j in range(2))+sum(kk[i][j]*new[j]for j in range(2))for i in range(2)]
assert new[0]==expected and r==[0,react]
assert 3*sum(new[j]-old[j]for j in range(2))==react/2
old=new
print("exact: C SPD 20/20; K rank19; K1=0; 1TC1=2; jump -1/5,1/25; Rc 24/5,36/25")
```
## C. Time predicate와 경계
```python
import math,random,struct
from fractions import Fraction as F
def input_cell(x):
v=F(x);prev=F(math.nextafter(x,-math.inf))
nxt=math.nextafter(x,math.inf)
hi=(v+F(nxt))/2 if math.isfinite(nxt) else v+(v-prev)/2
even=not(struct.unpack('>Q',struct.pack('>d',x))[0]&1)
return (prev+v)/2,hi,even
def cell_overlap(t,d,m):
a,b,ca=input_cell(t);c,e,cb=input_cell(d);c*=m;e*=m
lo=max(a,c);hi=min(b,e)
return lo<hi or (lo==hi and (ca or (a<lo<b)) and (cb or (c<lo<e)))
def dyadic_overlap(t,d,m):
_,ex=math.frexp(t)
factor=F(2)**(-ex)
aa,bb,ca=input_cell(t);cc,ee,cb=input_cell(d)
ints=[]
for x in [aa,bb,cc,ee]:
z=x*factor*2**88
assert z.denominator==1
ints.append(z.numerator)
a,b,c,e=ints;c*=m;e*=m
assert max(z.bit_length()for z in [a,b,c,e])<=122
lo=max(a,c);hi=min(b,e)
return lo<hi or (lo==hi and (ca or (a<lo<b)) and (cb or(c<lo<e)))
def fm(a,b,c):return float(F(a)*F(b)+F(c))
def grid_cell(t,d,inc=100):
if not(math.isfinite(t)and math.isfinite(d)and t>0 and d>0 and type(inc)is int and 1<=inc<=2**32-1):return "input-error"
if d>=t:return 1,False,t
if F(t)/F(d)>inc+1:return "INC"
_,e=math.frexp(t);q=math.ldexp(t,-e)/math.ldexp(d,-e);m=round(q)
close=cell_overlap(t,d,m)
assert close==dyadic_overlap(t,d,m)
if close:n=m
else:
factor=F(2)**(-e+88);X=F(t)*factor;Y=F(d)*factor
assert X.denominator==Y.denominator==1
X=int(X);Y=int(Y);f=math.floor(q)
if f*Y>X:f-=1
elif (f+1)*Y<=X:f+=1
assert f*Y<=X<(f+1)*Y
n=f if X==f*Y else f+1
if n>inc:return "INC"
if not close:assert n==-(-F(t)//F(d))
delta=d if close else fm(-(n-1),d,t)
assert 0<delta<=d and math.isfinite(delta)
assert (n-1)*d<t
return n,close,delta
pairs=[(10.,3.),(2.,3.),(.9,.3),(math.nextafter(.9,math.inf),.3),
(5.351455173733616,.17262758624947144),
(20*math.ulp(0.),6*math.ulp(0.)),(17*math.ulp(0.),5*math.ulp(0.)),
(math.nextafter(math.inf,0.),math.nextafter(math.inf,0.)/3)]
for t,d in pairs:print("input-cell grid",repr(t),repr(d),grid_cell(t,d))
rng=random.Random(901)
count=0
for i in range(10000):
m=rng.randint(2,2**32-1);d=math.ldexp(rng.uniform(.5,1),rng.randint(-1073,990))
t=m*d
if not(math.isfinite(t)and d>0):continue
t=math.nextafter(t,math.inf if i%2 else -math.inf)
if not math.isfinite(t):continue
q=t/d
if not math.isfinite(q)or q>2**32:continue
mm=round(q)
assert cell_overlap(t,d,mm)==dyadic_overlap(t,d,mm)
count+=1
print("input-cell/fixed-dyadic independent comparisons",count)
assert grid_cell(5.351455173733616,.17262758624947144)[0]==31
assert grid_cell(math.nextafter(.9,math.inf),.3)[0]==4
assert grid_cell(20*math.ulp(0.),6*math.ulp(0.))[0]==3
assert grid_cell(17*math.ulp(0.),5*math.ulp(0.))[0]==4
print("INPUT-CELL ASSERTIONS PASSED")
for t,d in [(1.,.1),(math.ldexp(.9,-1000),math.ldexp(.3,-1000)),(math.ldexp(.9,1000),math.ldexp(.3,1000)),(1e-323,5e-324)]:
print("input-cell scale",repr(t),repr(d),grid_cell(t,d))
assert grid_cell(10.,3.,3)=="INC" and grid_cell(10.,3.,4)[0]==4
assert grid_cell(float(2**32),1.,2**32-1)=="INC"
for bad in [0,-1,1.5,2**32]:assert grid_cell(1.,.1,bad)=="input-error"
for bad in [0.,-1.,math.inf,math.nan]:assert grid_cell(bad,.1)=="input-error"
print("CURRENT TIME GRID ASSERTIONS PASSED")
```
## D. 입력 rounding-cell의 독립 반증 검색
고정 seed의 검색은 폐기된 arithmetic-only predicate와 입력 rounding-cell의 차이를
검출한다. 이 출력은 predicate 실행 성공과 승인 시간 의미의 정합을 구별하기 위한
반증 evidence다. 상세 판정은 numerical-review.md §8을 따른다.
```python
import math,random
from fractions import Fraction as F
def rad(x):return max(x-math.nextafter(x,-math.inf),math.nextafter(x,math.inf)-x)/2
def pred(t,d,m):
q=t/d;p=m*d
B=math.nextafter(d*rad(q)+rad(p),math.inf)
return abs(float(F(t)-m*F(d)))<=B,B
rng=random.Random(41);found=[]
for z in range(20000):
d=rng.uniform(.125,.25);m=rng.randint(2,100);t=m*d
for dr in [-1,1]:
tt=math.nextafter(t,math.inf if dr>0 else -math.inf)
loT=(F(tt)+F(math.nextafter(tt,-math.inf)))/2
hiT=(F(tt)+F(math.nextafter(tt,math.inf)))/2
loD=(F(d)+F(math.nextafter(d,-math.inf)))/2
hiD=(F(d)+F(math.nextafter(d,math.inf)))/2
overlap=max(loT,m*loD)<=min(hiT,m*hiD)
ok,B=pred(tt,d,m)
if overlap and not ok:
found.append((tt,d,m,float(abs(F(tt)-m*F(d))),B));break
if found:break
print("input-cell overlap rejected by retired arithmetic predicate:",found)
t=math.nextafter(.9,math.inf);d=.3;m=3
print("specific .9 upper:",float(F(t)-m*F(d)),rad(t)+m*rad(d),"input-cell overlap",abs(F(t)-m*F(d))<=F(rad(t))+m*F(rad(d)))
```
## E. Library ULP 가정 없는 Fourier interval oracle
고정 dyadic 320-bit outward enclosure, Machin π, Taylor sin/cos/exp와 analytic
remainder를 사용한다. Python Fraction은 검증 generator 전용이다. 출력 interval의
outward binary64 hex endpoints를 C++ fixture에 고정할 수 있다. FESA solver와
C++ std::sin/std::exp의 ULP 보장을 가정하지 않는다.
```python
from fractions import Fraction as F
import math
BITS=320;SCALE=1<<BITS
def down(x):return F((x*SCALE).numerator//(x*SCALE).denominator,SCALE)
def up(x):return -down(-x)
def interval(a,b=None):return (down(F(a)),up(F(a if b is None else b)))
def add(a,b):return down(a[0]+b[0]),up(a[1]+b[1])
def neg(a):return -a[1],-a[0]
def sub(a,b):return add(a,neg(b))
def mul(a,b):
v=[x*y for x in a for y in b];return down(min(v)),up(max(v))
def div(a,b):
assert b[0]>0 or b[1]<0
return mul(a,(F(1)/b[1],F(1)/b[0]))
def scale(a,c):return mul(a,interval(c))
def atan_bounds(q):
a=sum((F(-1)**j)*q**(2*j+1)/(2*j+1) for j in range(64))
tail=q**129/129
return a,a+tail
a,b=atan_bounds(F(1,5)),atan_bounds(F(1,239))
PI=interval(16*a[0]-4*b[1],16*a[1]-4*b[0])
assert PI[1]-PI[0]<F(2)**-295
def exneg(x):
assert x[0]>=0
if x[0]>=512:return interval(0,F(2)**-512)
assert x[1]<=512
y=scale(x,F(1,1024));term=interval(1);s=term
for j in range(1,65):
term=scale(mul(term,neg(y)),F(1,j));s=add(s,term)
tail=F(1,2)**65/math.factorial(65)
s=add(s,interval(-tail,tail))
for _ in range(10):s=mul(s,s)
return s
def sincos_pi8(k):
k%=16;quad=(k+2)//4;rem=k-4*quad
x=scale(PI,F(rem,8));x2=mul(x,x)
st=x;ss=x;ct=interval(1);cs=ct
for j in range(1,32):
st=scale(mul(st,neg(x2)),F(1,2*j*(2*j+1)));ss=add(ss,st)
ct=scale(mul(ct,neg(x2)),F(1,(2*j-1)*2*j));cs=add(cs,ct)
ss=add(ss,interval(-F(1,math.factorial(65)),F(1,math.factorial(65))))
cs=add(cs,interval(-F(1,math.factorial(64)),F(1,math.factorial(64))))
q=quad%4
return [(ss,cs),(cs,neg(ss)),(neg(ss),neg(cs)),(neg(cs),ss)][q]
def series_interval(t):
Ts=[interval(1)for _ in range(5)];qs=[interval(0)for _ in range(5)];E=interval(1)
for m in range(80):
odd=2*m+1
x=scale(mul(PI,PI),F(t)*odd*odd/4);dec=exneg(x)
for j in range(5):
si,co=sincos_pi8(odd*j)
Ts[j]=sub(Ts[j],scale(div(mul(si,dec),PI),F(4,odd)))
qs[j]=add(qs[j],scale(mul(co,dec),2))
E=sub(E,scale(div(dec,mul(PI,PI)),F(8,odd*odd)))
assert max(hi-lo for lo,hi in Ts+qs+[E])<F(2)**-270
# Uniform analytic tail bound, using safe r upper at minimum time.
beta=scale(mul(PI,PI),F(t)/4)
first=exneg(scale(beta,161*161))
rr=exneg(scale(beta,8*81))
tailq=div(scale(first,2),sub(interval(1),rr))[1]
tailt=div(scale(first,4),scale(mul(PI,sub(interval(1),rr)),161))[1]
Ts=[add(v,interval(-tailt,tailt))for v in Ts]
qs=[add(v,interval(-tailq,tailq))for v in qs]
E=add(E,interval(-tailt,tailt))
return Ts,qs,E,tailt,tailq
for t in [.001,.01,.05,.1,.5,1.]:
Ts,qs,ee,tt,tq=series_interval(t)
assert tt<F(1,10**27) and tq<F(1,10**27)
# Certified brackets can be exported as outward binary64 hex endpoints for C++ fixtures.
width=max(float(hi-lo)for lo,hi in Ts+qs+[ee])
assert width<1e-26
print("Fourier certified",t,"max-width",width,"tailT",float(tt),"tailq",float(tq),
"E bracket",math.nextafter(float(ee[0]),-math.inf).hex(),math.nextafter(float(ee[1]),math.inf).hex())
print("FOURIER INTERVAL ASSERTIONS PASSED")
```