222 lines
37 KiB
Markdown
222 lines
37 KiB
Markdown
# HHT 관성 구현 독립 수치 검토
|
||
|
||
- Owner: `numerical-review-agent`; date: `2026-09-22`.
|
||
- 상태: `source-delta-reviewed; runtime-verification-pending`. 수식/API 중간 검토이며 전체 구현 gate 통과 판정이 아니다.
|
||
- 범위: [관성 API](../../include/fesa/elements/nonlinear_mitc4_inertia.h), [관성 구현](../../src/fesa/elements/nonlinear_mitc4_inertia.cpp), [관성 tests](../../tests/unit/elements/nonlinear_mitc4_inertia_test.cpp), [비선형 내력](../../src/fesa/elements/nonlinear_mitc4_shell.cpp)의 directional third-derivative, [초기화·null algebra](../../src/fesa/analysis/hht_dynamic_algebra.cpp), [동적 조립](../../src/fesa/analysis/hht_dynamic_assembly.cpp), [HHT procedure](../../src/fesa/analysis/hht_dynamic_procedure.cpp).
|
||
- 기준: [정식화](formulation.md), [관성 상세](formulation-inertia.md), [N1–N7](numerical-controls.md), [C01–C12](verification-cases.md). Source/tests/상위 계약은 읽기 전용이다.
|
||
|
||
## 첫 검토 findings
|
||
|
||
아래는 작업 중 최초로 관찰한 내용이다. Owner 수정 뒤의 대조·실행 evidence는 별도로 갱신한다.
|
||
|
||
| ID | 심각도·상태 | 관찰·수정과 필요한 검증 |
|
||
| --- | --- | --- |
|
||
| HHT-IMP-NR-001 | **P1; source corrected, execution pending** | `Inverse(JetMatrix)`의 cyclic minor에 이미 포함된 cofactor 부호를 다시 적용하던 오류가 제거됐다. 새 test가 독립 `Jl^-T`와 현재 null, finite rotation의 `M n=0`을 대조한다. 실행 evidence 확인 전이다. |
|
||
| HHT-IMP-NR-002 | **P2; corrected, focused regression passed** | `convective_force=force-M*a` cancellation을 직접 `∫rho Hᵀ B_x[v,v]dV0` 누적으로 수정했다. 같은 q/v에서 a를 `1e15`배 변경해도 c가 동일한지 검사하는 test의 통과 로그를 확인했다. |
|
||
| HHT-IMP-NR-003 | **P2; corrected, focused sweep passed** | 최초 raw mixed-unit direction/`1e-5` test를 D scaling/단위 direction, N5 `1e-6`·연속한 두 내부 sweep 점으로 수정했다. Position/velocity/acceleration partial 및 두 h의 dependent-rate effective inertia tangent, pure translation/rotation/mixed 방향과 해당 실행 통과를 확인했다. |
|
||
| HHT-IMP-NR-004 | **미완료 coverage; 실행 대기** | 새 N4 guard는 analytic null의 20차원 직교 여공간에서 scaled symmetry/null, positive Cholesky와 `rcond_inf>=2^-32`를 검사한다. 물리 DOF를 numeric rank cutoff로 삭제하지 않는다. C05/C06의 finite rotation·warped/oracle·D3·단위변환 test와 실행 evidence는 owner 완료 후 확인한다. |
|
||
| HHT-IMP-NR-005 | **P2; corrected, threshold regression passed** | 작은 각에서 기존 직접 삼각함수 D3의 cancellation 위험을 안정 series로 수정했다. `|phi|<.5`의 squared-norm 9차 coefficient recurrence와 최종 회전행렬 extras 복사를 대조했으며 작은각 경계 양쪽을 포함한 4개 C++ sweep의 통과를 확인했다. |
|
||
| HHT-IMP-NR-006 | **P2; closed, exact scale restored** | 기존 `Σe |D²f_e[v,v]|`는 element 내부 coordinate-pair 상쇄를 잃어 N3와 달랐다. 수정본은 아래 exact scalar ψ Hessian을 global `(q_j,q_k)` identity로 합친 **뒤** `Σjk |ψ_qq,jk v_j v_k|`를 누적하고 algebra가 이를 소비한다. N3/[I/O 계약](io-contract.md) 의미와 일치한다. Finite/warped ψ·gradient·Hessian 독립 test 통과도 확인했다. |
|
||
| HHT-IMP-NR-007 | **P2; source corrected, execution pending** | 최초 `BuildPhysicalSpace`에 없던 N4 `PᵀZ` 및 `[P Z]` Gram guard가 추가됐다. Nodal sparse block의 실제 free dimension과 global Frobenius 오차, normalized null Gram을 검사하며 `1e-12` 기준을 유지한다. 수정본 실행 evidence는 대기다. |
|
||
| HHT-IMP-NR-008 | **수치 절차 변경; 문서 정합성 필요** | Reference의 큰 DIRECT step에서 constant-A 초기 추정이 geometry를 벗어나, Newton 시작 좌표를 `q_n`으로 변경했다. HHT predictor/종속 rates/endpoint 식은 그대로이며 cutback/retry도 없어 수식 변경은 아니다. 다만 N2가 기존 시작값을 명시하므로 새 deterministic 시작값과 실패 동작을 N2에 명시해야 한다. 기존 문구 상태로 계약 일치를 주장하지 않는다. |
|
||
|
||
NR-001의 독립 scalar 반례: `phi=(.5,0,0)`, `d0=eZ`, Jl의 yz block은 `[[sin(.5)/.5,−(1−cos(.5))/.5],[(1−cos(.5))/.5,sin(.5)/.5]]`다. 관찰한 inverse 식을 적용하면 unit null의 yz 성분이 `(-.6816387600,.7316888689)`이고 `||B n||=||(Jl n)×d||=.4744471057`이다. Python 표준 산술로 실제 계산했다. 이는 zero rotation에서 숨는 부호 오류이며 같은 잘못된 n을 미분한 finite-difference test만으로는 검출되지 않는다.
|
||
|
||
## 일치한 수식과 남은 대조
|
||
|
||
최초 snapshot의 `H`, `B_x`, `C_x` 조립과 `I=∫rho Hᵀ(Ha+B_x[v,v])`, `DqI`, `DvI=2∫rho HᵀB_x[v,p]`, `M`의 표시된 index contraction은 정식화와 일치했다. `P/L` 및 `Pdot/Ldot`는 같은 현재 x와 물리적 acceleration을 사용하고 `Ldot=∫rho x×xddot`의 `v×v=0` 항등식과 맞는다. Referenced density/measure, 3³ mass quadrature, current geometry 검사와 fixed-global rate 의미도 대조했다. 이것은 runtime 통과 판정이 아니다.
|
||
|
||
Internal Jet의 product 6항 및 Compose의 `f''' x_i x_v²+f''(2x_iv x_v+x_i x_vv)+f' x_ivv`를 독립 대조했다. Atan2의 bounded-ratio 미분은 quadrant 상수의 미분이 0인 점과 맞으며 기존 first/second reduction을 유지한다. 반환 `force_directional_second=D²f_int[v,v]`, `drilling_metric=Σkd gradγ⊗gradγ` 및 physical/drilling force·energy 분리는 정식화와 일치한다. 실제 procedure의 hidden-constraint 연결은 이 kernel 범위 밖이며 별도 검증이 필요하다.
|
||
|
||
NR-005의 독립 scalar 검산은 `phi=(θ,0,0), d0=eZ`에 대해 `D³d_x[eX,eX,eZ]=cosθ/θ−2sinθ/θ²+2(1−cosθ)/θ³`와 안정 전개 `−θ/4+θ³/36−θ⁵/960+θ⁷/50400`를 Python binary64로 비교했다. `θ=1e-4`에서 각각 `−7.7441382018e-5`, `−2.4999999972e-5`, 대칭 상대오차 `0.512`이고 `θ=1e-4*(1+2^-10)`에서 `0.339`다. 이는 algebraic scalar cancellation의 재현이며 수정 전 C++ Jet 출력 자체를 실행했다고 주장하지 않는다. 관성 rotation kernel의 32항 matrix exponential series에는 같은 직접 뺄셈이 없다. `|phi|<pi`에서 그 D3 truncation tail의 exact-arithmetic 상계는 `pi^30/30!/(1−pi/31)<3.45e-18`이며 binary64 roundoff 검증은 별개다.
|
||
|
||
NR-006의 최소 exact 구현 경로는 공개 moment-load가 0이고 동일 nodal initial director를 공유하는 현 범위에서 `κ_i=||Jl^-1d_i||`, `σ_i=1/κ_i`, `ψ_i=σ_i Σ_(e∋i)kd_e γ_ei`를 2차 미분하는 것이다. `δphi_i=n_i`는 `δtheta_i=σ_i d_i`를 만들어 모든 director와 element polar rotation을 고정하고 nodal R을 오른쪽 drilling 회전시킨다. 따라서 `Dγ_ej[n_i]=δij σ_i`, `n_iᵀf_phys=0`이다. σ는 현재 phi의 함수이므로 Hessian에서 동결하지 않는다. `Jl^-1d=Jr^-1d0=d0+.5 phi×d0+a(phi²)phi×(phi×d0)`로 σ를 안정 계산할 수 있다. 이 scalar ψ의 element-local Hessian을 source identity로 합친 뒤 `Σjk|ψ_i,qjqk||v_j||v_k|`를 계산하면 full energy D3 tensor 없이 N3 분모를 얻는다. 이는 승인 정식화의 항등식이며 scale 계약 변경이 아니다. 실제 code에서는 finite/warped 상태의 `nᵀf` 및 ψ gradient/Hessian과 독립 대조하고 수치 polar가 proper rotation으로 수렴하는 범위를 확인해야 한다.
|
||
|
||
## 초기화·procedure delta와 실행 evidence
|
||
|
||
`a_P=D P (Pᵀ DᵀM D P)^-1 PᵀDᵀ(F−f−c)` 뒤 `W^-1 ψ_q N W^-1` solve와 `N W^-1` 복원으로 null acceleration을 구하는 구현은 부호·단위와 맞는다. `ψ_q=DNᵀg+NᵀK`, raw hidden2의 `D²N[v,v]ᵀg+2DN[v]ᵀKv+NᵀD²f[v,v]+ψ_q a`도 일치한다. Null은 현재 `Jl^-1d`와 실제 free BC의 exact intersection으로 결정하며 작은 비영 constrained 성분을 버리지 않는다. Accepted null inventory 변경은 실패한다. Scaled physical mass factor와 W-scaled algebraic rcond 검사에는 epsilon mass/numeric rank deletion이 없다.
|
||
|
||
Newmark의 q/v는 A를 사용하고 물리 a는 `(A+alpha*a_old)/(1+alpha)`로 회복한다. Effective tangent의 rate 계수, force/moment 별 uncancelled Newton scale, 네 correction gate, 100번째 correction 뒤 재평가, fixed DIRECT `n*h`, endpoint reaction 및 현재 arm/`Jl^-T` spatial moment balance를 대조했다. Constant STEP load의 work `F·q`와 `T+Uphys+Udrill−E0−W`는 현 하중 범위와 맞으며 energy 감소를 강제하지 않는다. 이것은 실제 moving-shell 시간 정확도 인증이 아니다.
|
||
|
||
메인 실행 로그를 독립 열람했다. focused.log(당시 경로: `../../.harness/build/hht-numerical/focused.log`, 현재 checkout에 없음)는 관성 8 tests와 일반 finite-rotation internal D3 sweep, HHT helper 3 tests의 통과를 보인다. 같은 실행 전체는 당시 mapper/writer 7 failures가 있어 전체 PASS가 아니다. `DynamicRotation.*`의 inverse/null/D3 oracle suite는 이 로그에 없어 별도 실행 확인이 필요하다. 당시 없었던 internal 작은각 threshold fixture는 아래 후속 로그에서 통과했다.
|
||
|
||
focused-procedure.log(당시 경로: `../../.harness/build/hht-numerical/focused-procedure.log`, 현재 checkout에 없음)는 algebra 7/8, all-fixed procedure, state/writer/log 통과와 총 37개 중 2개 실패를 보인다. Algebra 경계 fixture는 이후 `W=I`, `diag(1,2^-k)`로 binary-exact threshold를 검사하도록 고쳤으나 이 보고서 시점 재실행 evidence는 대기다. 나머지 실패는 mapper diagnostic fixture다. 기존 로그를 새 소스 전체 통과로 소급하지 않는다.
|
||
|
||
후속 focused-exact-constraint.log(당시 경로: `../../.harness/build/hht-numerical/focused-exact-constraint.log`, 현재 checkout에 없음)는 internal residual 7 tests의 통과를 보이며, NR-005의 작은각 4 fixtures와 NR-006의 finite/warped exact ψ·gradient·Hessian 검사를 포함한다. zero-trial-red.log(당시 경로: `../../.harness/build/hht-numerical/zero-trial-red.log`, 현재 checkout에 없음)의 기존 영변형 spurious force 실패도 확인했다. 수정 `CovariantIncrement`의 `2E=G·δg+δg·G+δg·δg`, drilling `F=I+δg⊗Gdual`는 큰 기준값의 뺄셈을 피하는 동일 수식이다. `q=0`의 exact-zero 회복은 force clamp나 tolerance 완화가 아니다. 새 zero-trial test의 통과를 확인했다. Recovery·기존 정적 영향은 전체 회귀 검사 대상이다.
|
||
|
||
C04a는 판정을 분리한다. 위 로그에서 finest binary64 full/reduced acceleration 차이 약 `3.079e-10`이 `1e-10`을 넘었지만 N7 해상도 약 `6.296e-10` 이하다. q/v/physical-a의 시간차수·오차/noise 검사와 coarse reduction은 별개다. N7에 따라 finest reduction은 **INCONCLUSIVE**로 남길 수 있으며 threshold를 바꾸지 않는다. decimal80 독립 검산(당시 경로: `../../.harness/build/hht-numerical/c04a-decimal-reduction.json`, 현재 checkout에 없음)의 full 2×2/scalar recurrence 차이 `<=1.70e-73`은 수학적 reduction을 지지하지만 FESA binary64의 `1e-10` 통과를 대신하지 않는다. 새 test는 시간수렴/coarse gate와 finest `GTEST_SKIP`을 분리했으며 이 보고서에서는 새 바이너리 재실행을 아직 확인하지 않았다. C04a 전체를 무조건 PASS로 합치지 않는다.
|
||
|
||
Build/CTest는 메인/구현 owner가 담당하며 reviewer가 별도 C++ 실행을 했다고 주장하지 않는다. 문서 `git diff --check`는 exit 0이다. NR-008 문서 정합성, 새 algebra/rotation oracle 실행, 실제 shell의 시간수렴·energy·moving-null·reference 검증이 남는다. 본 보고서는 전체 기능/물리 gate 통과 판정이 아니다.
|
||
|
||
## 2026-09-26 재검토: startup와 C08
|
||
|
||
**HHT-IMP-NR-009 — blocking formulation/acceptance conflict.** 승인된 `A0=a0`와 전체 좌표 Newmark/HHT recurrence는 일반 regular mass-null DAE의 전체 시점 physical acceleration 2차 정확도를 보장하지 않는다. 아래 Decimal80 반례는 Newton·roundoff 없이 이를 재현한다. 기존 C04의 제한된 성공 evidence를 일반 shell 보장으로 확장할 수 없다. 적용 기준은 [formulation](formulation.md), [N7](numerical-controls.md), [C08](verification-cases.md)이며 [공통 tolerance](../TOLERANCE.md)는 변경하지 않았다.
|
||
|
||
### 독립 재현 및 startup 수정의 한계
|
||
|
||
무차원 `q=(x,z)`, `M=diag(1,0)`, `U=(z-x³)²/2`, `F=0`이면 endpoint 식은 `x''-3x²(z-x³)=0`, `z-x³=0`이다. 초기 `x=z=0`, `vx=1,vz=0`, `a0=A0=0`의 정확해는 `x=t,z=t³`다. Null regularity `∂ψ/∂z=1`이고 초기 ψ/ψdot/ψddot도 0이다. 이산 x와 endpoint z가 정확하므로 승인 predictor/rate 식만으로 독립 재현할 수 있다.
|
||
|
||
Python 표준 `decimal`, precision80, `python -B -`로 실제 계산했다. `alpha=-.05,beta=.275625,gamma=.55,T=1`; 아래는 전체 endpoint maximum absolute error다. Position error는 정확히 0이다.
|
||
|
||
| h | velocity error | acceleration error |
|
||
| --- | ---: | ---: |
|
||
| 1/16 | 3.923965419501134e-3 | 5.154933875921670e-1 |
|
||
| 1/32 | 9.809913548752835e-4 | 2.577466937960835e-1 |
|
||
| 1/64 | 2.452478387188209e-4 | 1.288733468980418e-1 |
|
||
| 1/128 | 6.131195967970522e-5 | 6.443667344902088e-2 |
|
||
| 1/256 | 1.532798991992631e-5 | 3.221833672451044e-2 |
|
||
|
||
모든 refinement 차수는 velocity **2**, acceleration **1**이다. Finest acceleration 최대는 `n=10,t=10/256`이므로 첫 frame 하나의 문제도 아니다. Smooth prescribed null coordinate의 Taylor 전개는
|
||
|
||
`a1-z''(h)=h*[1/(6*beta*(1+alpha))-1]*z'''(0)+O(h²)`
|
||
|
||
를 주며 대괄호 계수는 `-.36348808529259657`이다. Cubic recurrence의 particular solution은 `a_n=6t_n`, `A_n=6t_n+6alpha*h`, `v_n=3t_n²+(1-3alpha-6beta)h²`; 마지막 계수는 `-.50375`다. Exact physical v0와 discrete particular velocity의 O(h²) 차이가 null acceleration의 O(h) transient를 만든다. 동차 `(delta v/h,delta A)` 행렬은 `[[1-gamma/beta,1-gamma/(2beta)],[-1/beta,1-1/(2beta)]]`이며 중근은 `-19/21`, physical-a filter root는 `-1/19`다. 고정 양의 시간에서 transient가 줄어도 all-time maximum은 1차다.
|
||
|
||
**A0만의 변경은 충분하지 않다.** `A0=h*z'''(0)*(1/6-beta*(1+alpha))/(1/2-beta)`로 a1을 exact하게 만든 경우에도 max acceleration은 `h=1/16,1/256`에서 `.5183366687687775 / .03239604179804859`로 1차다. `A0=alpha*h*z'''(0)`도 `.5154649974736597 / .03221656234210373`으로 1차다. Exact v0를 유지한 채 A0 하나만 조정하여 해결됐다고 주장할 수 없다. Velocity의 discrete branch와 auxiliary history를 함께 다루는 별도 startup 또는 rate recovery가 필요하다.
|
||
|
||
다음 코드는 세 경우와 각 refinement order를 JSON stdout으로 재현한다. Main이 필요 시 `.harness/build/hht-numerical/startup-counterexample-20260926.py`로 추출하고 `python -B <script> > <result.json>`으로 보존할 수 있다. Reviewer 수정 경계상 scratch 파일은 생성하지 않았다. 이 수식 probe는 FESA binary build/test evidence가 아니다.
|
||
|
||
```python
|
||
from decimal import Decimal as D, getcontext
|
||
import json
|
||
getcontext().prec = 80
|
||
alpha = D('-.05')
|
||
beta = (1-alpha)**2/4
|
||
gamma = D('.5')-alpha
|
||
result = {}
|
||
for mode in ('approved', 'first_a_exact', 'filtered_A0'):
|
||
rows = []
|
||
for n in (16, 32, 64, 128, 256):
|
||
h = 1/D(n)
|
||
q = v = a = D(0)
|
||
A = (D(0) if mode == 'approved' else
|
||
(D(1)/6-beta*(1+alpha))/(D('.5')-beta)*6*h
|
||
if mode == 'first_a_exact' else alpha*6*h)
|
||
ev = ea = first = D(0)
|
||
peak = 0
|
||
for i in range(1, n+1):
|
||
t = i*h
|
||
qp = q+h*v+h*h*(D('.5')-beta)*A
|
||
An = (t**3-qp)/(beta*h*h)
|
||
vn = v+h*((1-gamma)*A+gamma*An)
|
||
an = (An+alpha*a)/(1+alpha)
|
||
ev = max(ev, abs(vn-3*t*t))
|
||
if abs(an-6*t) > ea:
|
||
ea, peak = abs(an-6*t), i
|
||
if i == 1:
|
||
first = abs(an-6*t)
|
||
q, v, a, A = t**3, vn, an, An
|
||
row = dict(n=n, v=str(ev), a=str(ea), first_a=str(first), peak=peak)
|
||
if rows:
|
||
for key in ('v', 'a'):
|
||
row['order_'+key] = str((D(rows[-1][key])/D(row[key])).ln()/D(2).ln())
|
||
rows.append(row)
|
||
result[mode] = rows
|
||
print(json.dumps(result, indent=2))
|
||
```
|
||
|
||
### C08 driver 대조 및 현재 evidence
|
||
|
||
[C08 source](../../tests/unit/analysis/hht_shell_physics_test.cpp)의 `RunPlate`, `InitializeNullVelocity`, `Differences`, `RecordNoise`를 대조했다. q0=0에서 null=e_phi3, f=0이므로 stiffness phi3 행으로 초기 ψ_q를 구성하는 식은 맞다. Initial physical director/null velocity, physical/hidden2 a0, A0=a0, old physical-a history, correction length scaling, `fine.history[2*frame]` 시간 대응과 T/T² normalization도 일치한다. `RecordNoise`의 free-vector nodal indexing은 이 all-free fixture에서는 유효하며 partial BC로 일반화하면 안 된다. **큰 rotational-rate 비수렴을 설명하는 구체적인 driver 인덱스·산술 오류는 찾지 못했다.**
|
||
|
||
최신 optimized 진단(당시 경로: `../../.harness/build/hht-numerical/c08-diagnostic-20260926.log`, 현재 checkout에 없음)의 C08a finest angular-a 최대는 `n=10,t=.0390625`로 scalar transient peak와 일치한다. Total error `5.67565924e-4` 중 current-null 성분은 `5.58582318e-4`, physical complement는 `1.00582661e-4`다. Startup 가설을 지지하지만 physical 성분도 해당 node/time에서 이미 `1e-4`를 넘는다. 전체 node/time physical maximum 및 recovery 결과가 없으므로 null-only 회복으로 C08a가 통과한다고 예측할 수 없다.
|
||
|
||
같은 로그의 C08b finest rotational q/v/a self difference는 `1.38700902e-4 / 1.15434080e-2 / .868938812`다. 마지막 acceleration noise 합은 약 `5.426e-6`이므로 큰 차이를 N7 산술/solve 해상도로 설명할 수 없다. Cubic 반례는 q가 exact하므로 C08b q plateau까지 증명한 원인은 아니다. 다음 진단은 처음/최대/후반/최종 오차의 node·시간과 analytic null/physical complement별 q/v/a 차이, 초기값 grid independence, 대표 frame의 residual/effective tangent 대조다. Alpha·grid·fixture를 바꿔 기존 FAIL을 지워서는 안 된다.
|
||
|
||
**HHT-IMP-NR-010 — 별도 test 판정 보정 필요.** C08b energy/P/L 공통 loop는 roundoff 수준의 P에도 strict decrease와 마지막 ratio<=.6을 무조건 요구한다. Free centered shell의 총 linear P는 일정 translational mass와 총 내력 0으로 이산 recurrence에서도 보존된다. 마지막 세 P error `3.85047e-14 / 4.82500e-14 / 2.00403e-13`은 temporal order를 판정할 근거가 아니다. C08의 “변화가 존재하는 각 quantity”와 N7 unresolved/noise 구분을 적용해 P convergence를 별도 표시하고 raw drift와 기존 finest bound는 유지해야 한다. 이 판정 보정으로 rotational q/v/a FAIL이 해소되는 것은 아니다. C++ `long double` type 이름만으로 MSVC oracle를 Decimal80과 같은 고정밀 근거로 간주해서도 안 된다.
|
||
|
||
### 권고와 사용자 결정 경계
|
||
|
||
기존 FAIL과 recurrence를 보존하고 아래 **null recovery 후보를 별도 검증한 뒤** formulation/I/O owner가 계약 변경안을 제시하는 경로를 우선 권고한다. 현재 검토는 후보 채택·구현 승인이 아니다.
|
||
|
||
| 선택 | 수학적 효과·필수 검토 |
|
||
| --- | --- |
|
||
| q/v integration 유지, physical null a 별도 recovery | 현재 q/v에서 `r2=psi_q*a_h+psi_qq[v,v]+2psi_qt*v+psi_tt`, `(psi_q*N)*eta=-r2`, `a_rec=a_h+N*eta`. `M*N=0`이므로 endpoint physical equilibrium은 바뀌지 않는다. Cubic a_rec는 exact다. 일반 shell은 q/v/physical a의 2차성과 bounded regularity가 추가로 필요하며 현재 C08b evidence는 이를 충족하지 못한다. Internal history `a_h`와 recovered output `a_rec`를 구별하는 state/I/O 계약 변경이 필요하다. a_rec를 next history에 넣으면 원 적분법을 유지한 것이 아니다. |
|
||
| Constraint-consistent startup/적분법 변경 | Exact frame0 q/v/a, auxiliary history, velocity의 discrete bias와 null derivative constraints를 함께 설계해야 한다. 단순 A0/a1 수정은 부족하다. Filter, tangent, state update와 C01–C08/reference를 재검증한다. |
|
||
| 현재 알고리즘의 정확도 범위 제한 | 사용자가 기존 all-time full-a C08/N7 acceptance를 명시적으로 변경하는 선택이다. 기존 FAIL은 유지하고 검증 범위만 주장한다. 외부 UR warning-only 분류나 reference tolerance로 내부 a 실패를 면제할 수 없다. |
|
||
|
||
Null acceleration correction은 `B*N=0`이므로 현재 q/v의 director acceleration을 바꾸지 않지만 chart a와 spatial axial spin acceleration 값은 바꾼다. Recovered v까지 바꾸면 convective inertia도 다시 평가해야 하므로 a-only 회복과 구별한다. Hidden2 satisfaction은 일반 2차 적분 증명을 대신하지 않는다. 공개 입력의 zero initial velocity만으로 nonzero-jerk 내부 C08 acceptance가 충족됐다고 주장할 수 없다.
|
||
|
||
실행 범위: reviewer는 독립 Decimal80 probe와 source/log 정적 대조를 수행했다. C++ build/test는 실행하지 않았다. Tolerance·계약·source·tests·reference는 수정하지 않았다. C08b 원인 확정, 선택된 수정의 독립 검토 및 전체 solver 검증은 미완료다.
|
||
|
||
### 별도 recovery 오류: 두께 moment 계수
|
||
|
||
**HHT-IMP-NR-011 — source corrected; optimized focused regression passed.** Main이 식별한 [RecoverState](../../src/fesa/elements/nonlinear_mitc4_shell.cpp)의 bending resultant 계수를 독립 검산했다. `z=t*zeta/2`, `dz=t/2*d(zeta)`이므로 `M=∫z*S*dz=(t²/4)*Σw*zeta*S`다. 기존 `t/4`는 두께 1개가 누락되어 moment per length의 단위 `[force]` 대신 membrane resultant와 같은 `[force/length]`가 된다. 현재 소스의 `0.25*thickness_*thickness_*thickness_sign*gauss*stress` 수정은 부호·단위·두께 적분과 맞는다.
|
||
|
||
[새 test](../../tests/unit/elements/nonlinear_mitc4_shell_test.cpp)의 flat reference, quadratic-through-thickness stress에서 `t²/12*(S_top-S_bottom)`은 odd first moment의 정확한 별도 샘플 oracle다. `t=.2,.5,2` 선택은 t=1이 누락 계수를 숨기는 문제를 피한다. 수정 전 로그(당시 경로: `../../.harness/build/hht-numerical/bending-moment-red.log`, 현재 checkout에 없음)의 t=2에서 계산값 `4.36084804185815`, oracle `8.72169608371628`의 factor2 실패도 식과 일치한다. 수정 후 실행 및 기존 static recovery 영향은 owner 검증 대기다. 이 변경은 recovery의 section resultant에 한정되며 internal force/tangent 식이나 HHT recurrence를 변경하지 않는다. C08 시간수렴 FAIL의 해결로 분류하면 안 된다.
|
||
|
||
후속 optimized recovery 실행(당시 경로: `../../.harness/build/hht-numerical/recovery-green-optimized.log`, 현재 checkout에 없음)을 독립 열람하여 **13 tests PASS**를 확인했다. 새 thickness moment test와 기존 small-rotation/objectivity/recovery/patch가 포함된다. 위 “검증 대기”는 최초 검토 시점 기록이며, 현재 확인한 범위는 이 focused optimized 실행이다. 전체 Debug 및 다른 reference 검증의 대체 evidence는 아니다.
|
||
|
||
## 가속도 복구 후보 독립 검토 — HHT-ADR-011
|
||
|
||
**HHT-IMP-NR-012 — candidate mathematics consistent; verification/adoption pending.** [별도 정식화](formulation-acceleration-recovery.md)를 검토했다. 사용자 선택은 기존 정확도 유지와 별도 검증이며 production/state/schema 채택 승인이 아니다. 핵심 `G*eta=-r2_h`, `a_rec=a_h+N*eta`는 지정된 shell·고정 chart·일정 null rank에서 타당하다. 다음 일관성을 확인했다.
|
||
|
||
| 경계 | 독립 대조 결과 |
|
||
| --- | --- |
|
||
| 좌표·단위 | `q=D_f*x`, `N=D_f*Z`, `C_x=C_q*D_f`이므로 `C_q*N=C_x*Z`, `a_h=D_f*b_h`, `r2_q=r2_x`. Shell null의 병진 성분은 0이어도 일반 scaled mapping을 생략하면 안 된다. W-scaled solve는 `B=W^-1*G*W^-1`, `B*y=-W^-1*r2`, `eta=W^-1*y`다. |
|
||
| Partial BC | Admissible free null의 zero-constrained lift만 사용한다. Positive Gram mass와 analytic `H*N=0`는 full mass action도 0으로 만들어 constrained-row reaction까지 보존한다. 잘린 nodal null이나 `M_ff*N≈0`만으로 full 보존을 인증할 수 없다. Empty null/all-fixed는 identity recovery다. |
|
||
| Explicit time | q-independent F(t)에서 `psi_t=-N^T*Fdot`, `psi_qt[p]=-DN[p]^T*Fdot`, `psi_tt=-N^T*Fddot`; mixed 항의 계수 2와 부호가 맞다. AR-02의 추가 `psi=z-t*x`는 현재 문서가 별도 `U(q,t)=(z-t*x)²/2`로 명시하여 synthetic time-dependent-potential oracle임을 구분했다. 이 경우 일반 ψ 미분을 쓰며 F-only 단축식을 적용하지 않는다. |
|
||
| 보존과 출력 | 고정 q/v에서 `delta I=M*N*eta=0`, full E/reaction/material-point acceleration/Pdot/Ldot 불변. q/v 기반 energy/P/L도 불변. `delta omega_dot=d*lambda`이므로 chart a 및 axial spin acceleration은 변한다. 기존 `a_h,A`와 모든 integration history는 별개로 그대로 보존해야 한다. |
|
||
| 차수 | Smooth bounded recovery map은 q/v와 non-null a의 O(h²) 오차를 전달할 수 있지만 그 전제는 아직 C08에서 검증되지 않았다. 동일 endpoint에서 physical complement 오류는 그대로다. `r2_rec≈0`만으로 정확도·평형·hidden1 충족을 주장할 수 없다. |
|
||
|
||
Python 표준 `fractions.Fraction`의 exact arithmetic으로 `D=diag(7,1)`, cubic constraint의 `x=2,vx=3,a_h=(1/5,-4)`에서 scaled/unscaled r2·G 동일성, physical a 보존, recovered r2=0을 실행 확인했다. Constant basis scale `s=-2,3`에서 `N'=sN,psi'=spsi,G'=s²G`의 correction도 exact 동일했다. 별도 `psi=z-t*x`, `t=2/5,ax=3/7,vx=5/6`에서 `az=t*ax+2vx`가 mixed-time r2를 exact 0으로 만들었다. 이 제한 scalar 대조는 shell probe 실행 evidence가 아니다.
|
||
|
||
**Off-hidden1 basis 의존성은 실제다.** `psi=z-x²`, `q=(1,1)`, `v=(1,2+delta)`, `a_h=0`, `N=e_z`이면 `r1=delta`, 원 회복 `a_rec,z=2`다. 같은 null을 `N'=xN`, `psi'=xpsi`로 표현하면 q=1에서 basis 값은 동일하지만 그 미분 때문에 `a_rec,z=2-2delta`다. `delta=1/100`의 exact 계산은 `2` 대 `1.98`을 주었다. 따라서 current normalized basis와 그 미분을 고정해야 한다. Constant basis 재표현·고정 global frame의 covariance와 arbitrary state-dependent renormalization의 exact invariance를 혼동하면 안 된다. Nonlinear chart 변경도 가속도의 비선형 변환항과 basis 선택을 함께 다뤄야 하며 현 fixed-chart 검토 밖이다.
|
||
|
||
### Probe 보정과 검증 준비도
|
||
|
||
**HHT-IMP-NR-013 — in-progress test-probe corrections required.** 검토 당시 [RecoverAcceleration](../../tests/unit/analysis/hht_shell_physics_test.cpp)는 올바른 explicit-time `frame.hidden_acceleration_defect`를 RHS로 쓰지만 post-check는 `assembly.hidden_second+C*a_rec`만 다시 계산했다. C08a에서는 `-D²N[v,v]^T*F-2DN[v]^T*Fdot-N^T*Fddot`가 빠진다. 같은 q/v/time/F의 별도 frame에 recovered a를 넣고 `ManufacturedHidden`을 재평가하거나 정확히 같은 항을 포함해야 한다. 이 오류는 post-check의 문제이며 현재 RHS solve 자체가 잘못됐다는 판정은 아니다. Main에 즉시 전달했으며 수정 후 대조·실행은 대기다.
|
||
|
||
또한 post-scale의 `sum abs(DN_component*g_component*a)+sum abs(N_component*K_entry*a)`는 N3의 `abs(psi_q)*abs(a)`와 일반적으로 다르다. 먼저 source/global coordinate별 `C_j`를 합친 다음 `sum_j abs(C_j)*abs(a_j)`로 계산해야 한다. Hessian 및 explicit-time 항도 정해진 derivative-entry contraction scale와 대조해야 한다. `ManufacturedHidden`의 기존 확장항 scale에도 같은 점검이 필요하다. 더 큰 분모로 hidden2 통과를 만들지 않는다. Raw r2/G/eta 진단은 유지할 수 있지만 scale 검증 전 N3-certified 판정으로 보고하면 안 된다.
|
||
|
||
검증 적용은 기존 N1 finite/zero-scale branch, N2-L linear backward error `1e-12`, N4 current-null/regularity, N5 derivative sweep, N6 보존 기준을 유지한다. 후보 post-hidden2는 corrected a로 **기존 N3의 정확한 uncancelled derivative scale를 재평가하여 `1e-10`**과 대조한다. 이 판정은 consistency일 뿐 C08/N7의 full-a 정확도 판정과 별개다. Exact-zero scale에서 nonzero residual을 absolute ratio로 바꾸지 않는다.
|
||
|
||
회복 가속도의 noise에는 `delta eta=-G^-1*(delta r2+delta G*eta)`, `delta a_rec=delta a_h+delta N*eta+N*delta eta`가 필요하다. 기존 a_h의 cancellation estimate만 복사하면 충분하지 않으며 basis/derivative/linear solve 오차를 구별해야 한다. 전후 history의 exact 동일성, common-projector error decomposition, full I/E/reaction과 director/axial-spin 불변식, partial BC/empty null, AR-01–08의 실행 evidence가 남는다. 이 검토는 추가 numerical tolerance나 production adoption을 승인하지 않는다.
|
||
|
||
### 2026-09-27 probe 수정 재대조
|
||
|
||
NR-013의 **explicit-time post-check 수정은 source에서 확인**했다. Manufactured branch는 q/v/time/F를 그대로 복사한 frame에 a_rec만 넣고 `ManufacturedHidden`을 재평가한다. Autonomous C08b branch도 `C_j`를 먼저 global coordinate별로 합친 뒤 `sum abs(C_j*a_rec,j)`를 구하므로 지적한 gradient-scale 문제를 수정했다. 다만 C08a `ManufacturedHidden`의 component별 확장항 scale 및 explicit-time Hessian 합산은 아직 정확한 N3 derivative-entry scale로 인증되지 않았다. 해당 ratio는 diagnostic이며 raw residual도 함께 남겨야 한다. Zero scale이면 raw residual의 exact-zero 판정이 별도로 필요하다.
|
||
|
||
별도 `RecoveredAcceleration` 저장, const assembly 입력과 copied frame 사용을 대조했으며 q/v/a_h/A에 recovered 값을 대입하는 경로는 없다. 기존 acceptance assertions는 원 a_h 결과를 계속 검사한다. `CheckLinearSolve`는 recovery solve의 N2-L backward check를 수행하지만 이것만으로 N7 forward noise/시간차수를 인증할 수 없다. `Mdeltaa=||M*delta a||/(||M||*||delta a||)`는 null-action 진단이며 large correction을 고려한 실제 full E/reaction의 전후 오차 평가를 대신하지 않는다.
|
||
|
||
최초 recovery-probe 로그(당시 경로: `../../.harness/build/hht-numerical/c08-recovery-probe-20260926.log`, 현재 checkout에 없음)의 C08a 전체 recovered angular-a maximum은 h=1/16..1/256에서 `.01351694529 / .004558583885 / .001373166739 / .0004000031671 / .0001005829141`이다. 이 값으로 계산한 마지막 두 raw order는 **1.77942349 / 1.99162617**, finest는 **1.005829141e-4**다. 따라서 기존 `>=1.8`, `<=1e-4`를 충족하지 않으며 후보 PASS가 아니다. 이 숫자는 아직 N7로 인증된 차수 evidence도 아니다.
|
||
|
||
같은 로그의 C08a `angular_a_null=0,angular_a_physical=0`는 **무효 diagnostic**이다. Analytic frame의 비어 있는 `nodal_internal_order`로 loop가 0회 실행됐기 때문이다. Full recovered error를 계산하는 `FrameError`는 vector size를 사용하므로 위 maximum 숫자에는 이 문제가 없다. 수정 source는 `generalized_coordinates.Size()/6`와 vector-size guard를 사용한다. 수정 후 C08a split 실행 evidence는 별도 확인해야 하며 이전 0을 exact branch로 해석하면 안 된다. C08b의 실제 accepted frame metadata는 채워져 있어 이 특정 zero-loop 문제와 구별한다.
|
||
|
||
현재 N7 미충족 사항은 recovery의 별도 linear-defect solve와 그 null correction 전파, C/k/N 계산 오차와 q/v/a_h perturbation의 recovery-map 전파, 각 grid의 전체시점 maximum noise 산정이다. Finest C08a recovered error는 기존 a_h noise `8.15568169e-6`의 약 **12.33배**다. 이 a_h 지표를 후보에 그대로 적용해도 100배 분리에 못 미치지만, null projection 때문에 a_rec에 동일 지표를 복사하는 것 자체가 올바른 증명은 아니다. 반대로 회복이 noise를 제거했다고 가정해 지표를 버릴 수도 없다. 확정된 recovery noise와 비교하기 전 상태는 **INCONCLUSIVE for recovered-a order**, 실제 finest/ratio 미달 기록은 그대로 유지한다. 정해진 grid·threshold 또는 첫 시점을 바꾸지 않는다.
|
||
|
||
완료된 같은 probe 로그는 **3 tests / 1 PASS / 2 FAIL**이다. C08b의 recovered angular-a 공통시간 차이는 h=1/64..1/512의 네 비교쌍에서 `.846306569 / .881654308 / .982191000 / .868938661`로 수렴하지 않는다. Finest는 원 a_h 차이 `.868938812`와 사실상 같은 크기이며, fine-state common projector의 physical maximum `.868938153`, null maximum `.00286332558`을 확인했다. 각 maximum은 서로 다른 node/time일 수 있으므로 두 maximum의 제곱합을 full maximum으로 해석하지 않는다. **주요 C08b 잔여 오차는 physical complement에 있고 null-only recovery로 제거되지 않는다.** q/v difference 역시 그대로다.
|
||
|
||
C08b post-hidden2 ratio는 네 pair에서 `6.06e-17..1.45e-16`, normalized Mdeltaa는 약 `5e-20`이다. 수정된 autonomous scale와 null correction의 제한 consistency evidence를 지지하지만 full N7 검증이나 a 정확도 PASS가 아니다. 회복 후보 자체의 후속 검증과 별도로 C08b의 physical dynamics/rate 비수렴 원인을 조사해야 한다. 이 결론은 production recovery 채택 또는 새 integrator 선택을 승인하지 않는다.
|
||
|
||
후속 C08a split 수정 실행(당시 경로: `../../.harness/build/hht-numerical/c08-recovery-split-spectrum-20260927.log`, 현재 checkout에 없음)은 **2 tests / 1 PASS / 1 FAIL**로 완료됐고 C08a temporal FAIL을 유지했다. Recovered full maximum은 이전 실행과 같으며, finest exact-state common projector의 angular-a null/physical maximum은 각각 **`4.64641213e-7 / 1.00582661e-4`**다. False-zero split 문제는 이 실행으로 수정 확인했다. 남은 C08a recovered maximum도 physical complement가 지배하며 finest `1e-4` 초과는 그대로다. 별도 matrix dump test의 PASS는 spectral/시간해상도 결론을 뜻하지 않으며 이 검토에서 spectrum 계산을 수행하지 않았다.
|
||
|
||
### C08b 초기 spectrum 독립 재현
|
||
|
||
후속 할당에 따라 위 로그의 `C08bInitialPhysicalSpectrumProbe`가 출력한 **M/K 각각 54×54 raw matrix**를 독립 계산했다. 이전 문단의 미실행 범위와 구별한다. Row 0..53의 완전성·중복·54 components·finite를 먼저 확인했다. q0=0의 알려진 nodal UR3 index `5,11,..,53`만 z로 지정하고 나머지 45개를 p로 지정했다. 수치 고유치 cutoff로 null을 선택하거나 작은 양의 질량을 삭제하지 않았다.
|
||
|
||
`C=-solve(Kzz,Kzp)`, `T[p,:]=I`, `T[z,:]=C`, `Kc=Kpp+Kpz*C`, `Mp=Mpp=L*L^T`로 응축했다. Python/NumPy를 `uv run --with numpy python -B -`로 실행하여 `A=L^-1*Kc*L^-T`의 raw skew를 확인한 후 `(A+A^T)/2`의 `eigh`로 eigenvectors까지 구했다. `Phi=T*L^-T*Y`를 원 54차원으로 복원하여 **원 K/M에 대한** residual도 검증했다. Eigenvalue를 clamp하거나 reference JSON을 수정하지 않았다.
|
||
|
||
| 항목 | 독립 계산 |
|
||
| --- | ---: |
|
||
| M의 지정 null columns norm | exact 0 |
|
||
| Raw M/K/Kc relative Frobenius skew | 각각 exact 0 |
|
||
| `cond_inf(Kzz)` / `cond_inf(Mp)` | 4 / 7500 |
|
||
| Mp Cholesky / minimum diagonal L | 성공 / `7.37711114e-3` |
|
||
| Whitened A raw relative skew | `1.15170851e-16` |
|
||
| `lambda_max` / `omega_max` | `102297.7057360391 time^-2` / `319.84012527517416 rad/time` |
|
||
| Full generalized residual maximum | `2.62506622e-15` |
|
||
| `||Phi^T*M*Phi-I||F` | `8.78577073e-15` |
|
||
|
||
Full residual metric은 각 mode의 `||K*phi-lambda*M*phi||2 / [(||K||2+abs(lambda)*||M||2)*||phi||2]`다. 독립 `eigh`의 최저 여섯 raw eigenvalue는 `[-1.63581e-11,-7.39169e-12,-7.29645e-12,2.01889e-12,3.26844e-12,8.09326e-12]`, 그 다음은 `44.22815124448042`였다. Main eigvalsh JSON(당시 경로: `../../.harness/build/hht-numerical/c08b-initial-spectrum-20260927.json`, 현재 checkout에 없음)의 raw 값과 전체 최대 차이는 `7.276e-11`이며 최대 ω는 표시 precision에서 동일하다. 음수 값을 0으로 바꾸거나 그 제곱근을 계산하지 않았다. 별도로 3 translations와 `u=Omega×X,phi=Omega`의 3 analytic rigid fields는 `||K*R||F/(||K||F*||R||F)=2.27995067e-17`이었다. 이는 여섯 near-zero 값의 rigid-mode 해석을 지지하며 nonlinear stability 인증은 아니다.
|
||
|
||
| h | omega_max*h |
|
||
| --- | ---: |
|
||
| 1/64 | 4.9975019574 |
|
||
| 1/128 | 2.4987509787 |
|
||
| 1/256 | 1.2493754894 |
|
||
| 1/512 | .6246877447 |
|
||
| 1/1024 | .3123438723 |
|
||
|
||
**한정 결론:** coarse grids가 초기 고주파 탄성 모드를 잘 분해하지 못할 가능성은 추가 조사할 만하다. 그러나 최대 eigenfrequency만으로 C08b의 실패 원인을 확정하거나 finest의 asymptotic accuracy를 보증할 수 없다. Dump의 `EmptyFrame`은 v=0이므로 실제 C08b의 nonzero initial velocity에서 생기는 convective tangent/velocity coupling을 포함한 dynamic Jacobian spectrum이 아니다. 해당 모드의 초기 가진 크기, 비선형 진행 중의 frequency/phase 변화, 오차 history와의 대응 및 실제 rate convergence가 남는다. 기존 grid·C08/N7 threshold·실패 판정은 유지하며, 이 진단은 grid 변경이나 production recovery 채택의 근거가 아니다.
|