Files
FESADev/phases/nonlinear-static-newton-raphson/step11.md
T

406 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Step 11: equilibrium-projection-and-reference-gate
## 목적
USER-DECISION-004 option 1과 `AUDIT-001`을 구현·검증한다. 같은 accepted factor-one state에서
accepted free residual, constrained reaction resultant, global applied-plus-reaction balance error를
서로 다른 quantity로 재구성하고, 기존 schema-v1 `/global/equilibrium`에는 balance error만 저장한다.
Fine nonlinear case는 primary, coarse nonlinear case는 companion으로 검증한다. Local nodal
`RF1/RF2/RF3`와 physical spatial `RM1/RM2`는 deterministic review-only evidence이고 blocking
verdict를 바꾸지 않는다. Global force와 moment balance는 각각 fixed normalized `<=1e-8`
comparator gate를 사용하며 runtime configuration 또는 product solver acceptance로 노출하지 않는다.
## 읽어야 할 파일
- `AGENTS.md`
- `docs/PRD.md`
- `docs/ARCHITECTURE.md`
- `docs/ADR.md`
- `docs/CODINGSTYLE.md`
- `docs/TOLERANCE.md`
- `docs/nonlinear-static-newton-raphson/requirements.md`
- `docs/nonlinear-static-newton-raphson/research.md`
- `docs/nonlinear-static-newton-raphson/formulation.md` §§11.4, 13.1, 13.4, 14.1
- `docs/nonlinear-static-newton-raphson/numerical-review.md` §§3.2, 6.2, 6.3
- `docs/nonlinear-static-newton-raphson/reference-model.md` §§3–6
- `docs/nonlinear-static-newton-raphson/io.md` §§7.3–7.5.1, 8.1–8.7
- `docs/nonlinear-static-newton-raphson/implementation-plan.md`
- `docs/nonlinear-static-newton-raphson/corrections.md` §13
- `phases/nonlinear-static-newton-raphson/index.json`
- `include/fesa/analysis/nonlinear_analysis_state.h`
- `include/fesa/analysis/nonlinear_static_procedure.h`
- `include/fesa/analysis/nonlinear_static_control.h`
- `src/fesa/analysis/nonlinear_static_procedure.cpp`
- `src/fesa/analysis/nonlinear_static_control.cpp`
- `include/fesa/results/nonlinear_results_writer.h`
- `include/fesa/io/hdf5/nonlinear_hdf5_results_writer.h`
- `src/fesa/io/hdf5/nonlinear_hdf5_results_writer.cpp`
- `tests/integration/analysis/nonlinear_static_procedure_test.cpp`
- `tests/unit/io/hdf5/nonlinear_hdf5_results_writer_test.cpp`
- `tests/unit/analysis/nonlinear_static_control_test.cpp`
- `tests/unit/analysis/newton_iteration_log_test.cpp`
- `tests/reference/reference_tolerance_policy.h`
- `tests/reference/reference_tolerance_policy.cpp`
- `tests/reference/nonlinear_static_reference_comparison.h`
- `tests/reference/nonlinear_static_reference_comparison.cpp`
- `tests/reference/nonlinear_static_reference_cases_test.cpp`
- `tests/CMakeLists.txt`
- `.harness/config.json`
- `CMakePresets.json`
## 이전 Step과 보존 전제
- Steps 0–10은 completed다. 그 status, summary, timestamp, commit 또는 구현을 replay, amend,
reset하거나 다시 작성하지 않는다.
- 현재 worktree의 `NSNR-CTRL-018..021`, `NSNR-LOG-012`, `NSNR-PROC-019`,
`NSNR-HDF5-014`, residual-selector GREEN과 `NSNR-REF-001..023` comparator/CMake 작업은
이전 blocked Step 11에서 생성된 보존 대상이다. 삭제·재생성·reset하지 말고 현재 내용에서
계속한다.
- 현재 comparator의 shared `<=1e-10` global force/moment gate는 이전 blocker를 재현하는
보존된 implementation state다. 새 RED가 approved between-threshold metric을 거부하는 것을
증명한 뒤 comparator 내부 gate만 `<=1e-8`로 변경한다.
- Selector RED는 이미 과거 실행 evidence에 속한다. 이 재실행에서 selector RED를 fabricate하거나
다시 실패시키지 않는다. 기존 테스트가 계속 green인지 focused VERIFY에서 확인한다.
- Step 12는 pending이며 이 Step에서 시작하지 않는다.
## 소유 파일
RED test를 먼저 추가한 뒤 필요한 production file만 최소 수정한다.
- Modify test first: `tests/integration/analysis/nonlinear_static_procedure_test.cpp`
- Modify test first: `tests/unit/io/hdf5/nonlinear_hdf5_results_writer_test.cpp`
- Preserve and extend test-private comparator: `tests/reference/nonlinear_static_reference_comparison.h`
- Preserve and extend test-private comparator: `tests/reference/nonlinear_static_reference_comparison.cpp`
- Preserve and extend tests: `tests/reference/nonlinear_static_reference_cases_test.cpp`
- Preserve existing registration; edit only if the current sources are not registered:
`tests/CMakeLists.txt`
- Minimal GREEN: `src/fesa/analysis/nonlinear_static_procedure.cpp`
- Minimal GREEN: `src/fesa/io/hdf5/nonlinear_hdf5_results_writer.cpp`
- Preserve existing partial GREEN/tests without redesign:
`src/fesa/analysis/nonlinear_static_control.cpp`,
`tests/unit/analysis/nonlinear_static_control_test.cpp`,
`tests/unit/analysis/newton_iteration_log_test.cpp`
No public header, new CMake target, new HDF5 dataset, schema-version change or reference artifact is
owned by this Step.
## Authoritative HDF5 입력과 test-private report 경계
Comparator는 아래 existing schema-v1 paths만 읽는다.
```text
/model/nodes [node_count]
/model/nodal_constraint_mask [node_count,6]
/steps/Step-1/accepted_frames unique factor-one row
/steps/Step-1/frames/<frame>/nodal/displacement [node_count,6]
/steps/Step-1/frames/<frame>/nodal/generalized_internal_force [node_count,6]
/steps/Step-1/frames/<frame>/nodal/generalized_external_force [node_count,6]
/steps/Step-1/frames/<frame>/nodal/generalized_residual [node_count,6]
/steps/Step-1/frames/<frame>/nodal/reaction [node_count,6]
/steps/Step-1/frames/<frame>/global/equilibrium [6]
```
`/global/equilibrium` component order is
`FORCE_1,FORCE_2,FORCE_3,MOMENT_1,MOMENT_2,MOMENT_3` and its required
`location` is `global-balance-error`.
Preserve the current test-private types and make only the extensions needed by the approved
dispositions and distinct internal evidence. Add the required standard `<array>` and `<optional>`
includes to the test-private header. Candidate signature-level shape:
```cpp
namespace fesa::test {
enum class NonlinearReferenceDisposition {
kBlocking,
kWarningOnly,
kReviewOnly,
kExcluded,
};
struct NonlinearReferencePrecheckFinding {
std::string family_identity;
NonlinearReferenceDisposition disposition;
std::string diagnostic_code;
std::vector<NonlinearReferenceRowKey> affected_keys;
};
struct NonlinearInternalEquilibriumReport {
std::string family_identity;
std::size_t row_count;
double numerator;
double scale;
std::optional<double> normalized_metric;
bool passed;
std::string diagnostic_code;
};
struct ConstrainedReactionResultantReport {
std::array<double, 3> force;
std::array<double, 3> moment;
};
struct NonlinearReferenceComparisonReport {
std::string case_id;
std::filesystem::path original_input_path;
std::filesystem::path copied_input_path;
std::filesystem::path generated_results_hdf5;
std::filesystem::path generated_log_path;
std::vector<ArtifactSnapshot> pre_run_artifacts;
std::vector<ArtifactSnapshot> post_run_artifacts;
std::vector<NonlinearReferenceFamilyReport> families;
std::vector<NonlinearReferencePrecheckFinding> precheck_findings;
std::vector<NonlinearInternalEquilibriumReport> internal_equilibrium;
ConstrainedReactionResultantReport constrained_reaction_resultant;
bool external_blocking_passed;
bool internal_blocking_passed;
bool blocking_passed;
};
} // namespace fesa::test
```
These are test-support interfaces only. Match the existing aggregate/data style and avoid a public
solver API. For an exact-zero internal scale, leave `normalized_metric` empty and use
`diagnostic_code` plus the exact-zero numerator/pass rule instead of writing NaN or Inf.
## Required physical algorithm
For each node in stable Domain order at the accepted state:
1. Read `X` from Domain/model identity and `u, phi` from the accepted state. Set `x=X+u`.
2. Read generalized applied force/covector from `generalized_external_force`.
3. Build constrained generalized reaction by applying the exact component mask to
`generalized_residual` before any rotational conversion.
4. Convert applied and constrained-reaction rotational covectors independently with
`InverseTransposeLeftJacobianSO3(phi)`. Do not convert their sum as a shortcut and do not mix
free generalized residual into reaction.
5. Accumulate, about global origin `[0,0,0]`:
```text
F_applied += applied spatial force
M_applied += x cross F_applied_node + applied spatial physical moment
F_reaction += constrained reaction spatial force
M_reaction += x cross F_reaction_node + reaction spatial physical moment
global balance error = [F_applied + F_reaction,
M_applied + M_reaction]
```
Store only the six-component balance error in the existing accepted-frame
`global_equilibrium` and existing `/global/equilibrium` dataset. Reconstruct the constrained
reaction resultant and accepted free residual separately in the comparator; do not add summary
datasets or a public header.
For internal blocking gates:
- Force scale is `max(sum(||F_applied_node||2), sum(||F_reaction_node||2))`.
- Moment scale is
`max(sum(||x cross F_applied_node + M_applied_node||2),
sum(||x cross F_reaction_node + M_reaction_node||2))`.
- Force and moment use separate `||balance_error||2/scale <= 1e-8` gates. If scale is exact zero,
only exact-zero balance passes. No floor, `max(1,scale)` or clamp is allowed.
- Accepted free translation-force and rotation-moment rows use the exact free mask and reproduce
NSNR-069: nonzero matching external scale uses
`||residual||inf/max(||internal||inf,||external||inf) <= 1e-2`; exact-zero external scale uses
typed absolute `<=1e-3`. Finite checks occur first.
- `docs/TOLERANCE.md` is not used for these internal gates.
## RED
기존 dirty `NSNR-PROC-019`, `NSNR-HDF5-014`, `NSNR-REF-020..023`은 재작성하지 않는다.
먼저 existing `NSNR-REF-022`를 실행해 old shared gate failure를 다시 관찰·기록한 뒤,
`NSNR-REF-024..025`를 comparator threshold 변경 전에 test-first로 추가한다. Procedure/HDF5
behavior는 threshold rework를 위해 추가 변경하지 않는다.
- Approved draft label `NSNR-PROC-011` collides with completed Step 10's historical
`NSNR-PROC-011` increment-loop identity. Preserve that history and use collision-resolved test ID
`NSNR-PROC-019` with exact GTest symbol
`NonlinearStaticProcedure.NonzeroPrescribedMotionWritesCurrentArmSpatialAppliedPlusReactionBalance`.
Use a supported nonlinear shell input with nonzero prescribed translation and rotation plus a
resolved fixed-global load. Independently reconstruct the six balance components from accepted
`X,u,phi`, external force and masked residual. Expected RED: the current helper uses initial
coordinates, raw full-residual summation and raw rotational residual, so the current-arm/spatial
expected vector or distinct constrained/free projection assertion fails.
- `NSNR-HDF5-014`
`NonlinearHdf5ResultsWriter.WritesSchemaV1GlobalBalanceErrorLocation` checks existing path/shape,
exact component names, schema version 1 and `location=global-balance-error`. Expected RED: current
writer emits `location=global-origin`.
- `NSNR-REF-020`
`NonlinearStaticReference.AuditsFinePrimaryAndCoarseCompanionArtifacts` checks all exact fine,
coarse and linear input/CSV hashes, fine/coarse independent execution directories and case roles.
Expected RED: current case inventory supports only coarse nonlinear and linear cases.
- `NSNR-REF-021`
`NonlinearStaticReference.ReviewOnlyLocalReactionsCannotChangeBlockingVerdict` injects or observes
failing local RF/RM row/RMS decisions while U and internal blocking families pass. It requires
deterministic local error/precheck reporting and an unchanged blocking pass. Expected RED:
current local RF/RM dispositions are `kBlocking`.
- `NSNR-REF-022`
`NonlinearStaticReference.BlocksOnGlobalBalanceAndAcceptedFreeResidual` fails each coarse/fine
global-force, global-moment, free-force-residual and free-moment-residual family one at a time,
then passes all. It also asserts the constrained reaction resultant is a distinct review summary.
Expected RED for this re-execution: the preserved comparator applies the old shared `<=1e-10`
gate, so the approved coarse metric `1.413750912531167e-10` and fine metric
`2.7454660159924086e-10` are rejected even though both are below `1e-8`.
- `NSNR-REF-023`
`NonlinearStaticReference.RejectsMissingExtraDuplicateAndNonfiniteRowsBeforeToleranceAndRecordsReviewOnlyFindings`
covers blocking, warning-only and review-only families. Blocking precheck failure changes the
blocking verdict; warning/review failures remain ordered findings. Expected RED: the current
comparator cannot retain review-only precheck findings independently of the blocking verdict.
- `NSNR-REF-024`
`NonlinearStaticReference.GlobalForceAndMomentBalanceUseApprovedInclusiveOneEighthGate` uses a
test-private balance evaluator for distinct force and moment family identities. It requires an
approved between-threshold metric such as `5e-9` and exact `1e-8` to pass, and
`nextafter(1e-8,+infinity)` to fail for both identities. Expected decisive RED: the preserved
shared `<=1e-10` comparison rejects `5e-9` and `1e-8`.
- `NSNR-REF-025`
`NonlinearStaticReference.GlobalForceAndMomentExactZeroScaleRequiresExactZeroBalance` exercises
force and moment identities independently and requires exact-zero scale plus exact-zero numerator
to pass while any nonzero numerator fails without NaN/Inf or a fabricated normalized metric. This
is an unchanged-contract regression and may already pass in the RED batch; it must be present
before GREEN.
Run and record the observed failures:
```powershell
cmake --build --preset windows-msvc-ninja-debug
ctest --preset windows-msvc-ninja-debug -R "^(NonlinearStaticProcedure|NonlinearHdf5ResultsWriter|NonlinearStaticReference)\." --output-on-failure
```
The new tests must fail for the stated reasons. A compile failure caused only by the test-first
enum/report extension is valid RED when the diagnostic identifies the missing planned production/
test-support behavior. An unexpected pass or unrelated failure stops the Step for classification.
## Minimal GREEN
1. Replace the private raw-residual/initial-coordinate global helper in
`src/fesa/analysis/nonlinear_static_procedure.cpp` with the Required physical algorithm above.
Keep stable node order, mask-before-conversion, independent applied/reaction conversion and
finite failure behavior. Do not change the public procedure header or accepted-state layout.
2. In `src/fesa/io/hdf5/nonlinear_hdf5_results_writer.cpp`, preserve the path, shape, component
names and schema version; change the exact `/global/equilibrium` location attribute to
`global-balance-error` and keep writer validation finite/fail-closed.
3. Extend the existing comparator without deleting its partial work:
- add fine primary inventory and keep coarse companion/linear control independent;
- add `kReviewOnly` and keep local RF/RM key/schema/inventory/finite/row/RMS evidence;
- reconstruct the constrained reaction resultant and accepted free residual from the exact HDF5
paths/mask; never use `/nodal/reaction` or `/global/equilibrium` as a substitute for all three;
- apply internal gates exactly as specified above, using one test-private/internal shared
`1.0e-8` constant for both distinct force and moment families;
- keep U blocking, UR warning-only, local RF/RM review-only, RM3/stress excluded;
- reject blocking missing/extra/duplicate/nonfinite rows before tolerance and retain ordered
nonblocking findings;
- set overall blocking verdict from blocking external U families AND all internal global/free
families only.
4. Preserve existing `NSNR-CTRL-018..021`, `NSNR-LOG-012`, Full Newton, fixed factors, iteration
limit, 39-field log schema and the already corrected residual selector unchanged.
5. Do not change production solver/configuration behavior for the balance threshold. No `fesa.env`,
environment variable, CLI option, public API or HDF5 schema change is part of this GREEN.
## Exact read-only artifact inventory
| case | exact path | rows | SHA-256 |
| --- | --- | ---: | --- |
| fine primary | `reference/shellNL-meshfine/shellNL-meshfine.inp` | input | `40DF1CE77311C8D799564A21A60F3BDEBB8148D52CAE22B7292FDFAD32018220` |
| fine primary | `reference/shellNL-meshfine/shellNL-meshfine displacements.csv` | 729 | `740819FF8AED0E279961581F7CDBFA12784CFBA3A488C35A652E80103F64433F` |
| fine primary | `reference/shellNL-meshfine/shellNL-meshfine reactions.csv` | 729 | `AEB01C98C596792B0E5F2B86A81945CAB0BEB0C6969CFC6738C6F81DF1F94363` |
| fine optional | `reference/shellNL-meshfine/shellNL-meshfine stresses.csv` | 5,408 | `6D6213AB5DB9D6B1F5FBD641367D08BACC805967B6C5A95BCE0E1A5395377782` |
| coarse companion | `reference/shellNL/shellNL.inp` | input | `4BF3ECB0F7F1E8462C573E993DBC97AC5D920C91CF6F23AD4C6891D77FFBADA2` |
| coarse companion | `reference/shellNL/shellNL displacements.csv` | 49 | `4E0F99CA9372B1ADF7FAF102F43BC0B887DC743FDEDA378E4342D2CF8E0C0786` |
| coarse companion | `reference/shellNL/shellNL reactions.csv` | 49 | `001CB1541A9E94424E5E650509D46BC8724970DF4DE47184D9DB09C7938AB069` |
| coarse optional | `reference/shellNL/shellNL stresses.csv` | 288 | `5500E5114B1C44887528E30CEC054A1EB0F68BE3F4B8CE81FA59FC74A881483F` |
| linear control | `reference/shell/shell.inp` | input | `4005851E1AB22FD3A16AC17A8D5DA3E051233F69F37419079F3553AD134ECFCF` |
| linear control | `reference/shell/shell displacements.csv` | 49 | `C81D94E0B4A849F87AA0F79C83A79B94D5661AC79E44ED826919AB432C87746B` |
| linear optional | `reference/shell/shell reactions.csv` | 49 | `0E34EEC27E700C3C0C744C1DAFD8A3B95EFEF34B8616FE96665876586FE82D41` |
| linear optional | `reference/shell/shell stresses.csv` | 288 | `2B75A5B0DEB045A283C9BB7E9B1153B79F7537FF487214BF4A23316B59B660A7` |
For every case, snapshot original inventory, bytes, SHA-256, size and timestamp; copy only the `.inp`
bytes to a unique build-local directory; run only the copy; consume copied-sibling `_results.h5` and
`_log.txt`; compare against original read-only CSVs; then prove the original inventory is unchanged.
## External comparison and classification
- Fine and coarse `U1/U2/U3` are separate blocking families.
- Linear `U1/U2/U3` is a blocking regression family.
- All `UR1/UR2/UR3` families are warning-only.
- Fine/coarse local `RF1/RF2`, `RF3`, spatial `RM1/RM2` are separate review-only families with exact
mask-derived keys and complete common-policy error reports.
- `RM3` and all stress values have no numerical family. Stress files remain hash-audited optional
inventory; the known fine malformed optional stress key is not repaired.
- External families use exactly `docs/TOLERANCE.md`: reference-only `S=max(abs(reference))`,
`B=0.01*S`, near-zero row `abs(error)<=B`, other-row relative `<=0.05`, family
`RMS(error)/S<=0.01`, exact-zero family exact-zero FESA only. No absolute floor, averaging,
collapse, row-order identity, clamp or tolerance relaxation.
The final gate order is literally:
```text
ARTIFACT CHECK -> COMPARE -> CLASSIFY -> REPORT
```
## Focused and full VERIFY
After minimal GREEN, run in this order and retain command, exit code, duration and output tail for
Step 12:
```powershell
cmake --preset windows-msvc-ninja-debug
cmake --build --preset windows-msvc-ninja-debug
ctest --preset windows-msvc-ninja-debug -R "^(NonlinearConvergence|NewtonIterationLog)\." --output-on-failure
ctest --preset windows-msvc-ninja-debug -R "^(NonlinearStaticProcedure|NonlinearHdf5ResultsWriter)\." --output-on-failure
ctest --preset windows-msvc-ninja-debug -R "^NonlinearStaticReference\." --output-on-failure
ctest --preset windows-msvc-ninja-debug -R "^(NonlinearStaticReference|ReferenceTolerance|Mitc4Reference)\." --output-on-failure
ctest --preset windows-msvc-ninja-debug --show-only=json-v1
ctest --preset windows-msvc-ninja-debug --output-on-failure
```
The Harness launch environment must be VsDevCmd x64 and inherit `FESA_GTEST_SOURCE_DIR`, `MKL_DIR`,
`TBB_DIR`, and `HDF5_DIR`. Do not substitute the solution-only MSBuild path.
## Acceptance Criteria
- New RED tests fail first for their declared current behavior, then pass after minimal GREEN.
- `/global/equilibrium` remains schema v1 `[6]` with the same component names and stores only the
current-arm/spatial-moment applied-plus-reaction balance error with
`location=global-balance-error`.
- Fine/coarse global force and moment normalized metrics are each `<=1e-8`; exact-zero scale follows
the exact-zero rule.
- Fine/coarse accepted free force and moment residual gates pass NSNR-069 independently.
- Fine/coarse U blocking and linear U regression pass `docs/TOLERANCE.md`.
- UR differences remain warning-only. Local RF/RM findings remain review-only and cannot fail the
blocking verdict. RM3/stress remain excluded.
- Missing/extra/duplicate/nonfinite blocking rows fail before tolerance; warning/review findings are
deterministic and nonblocking.
- Original reference artifact content, inventory, size, timestamp and hash are unchanged.
- Focused tests, test discovery and full CTest pass without new compiler warnings.
## 상태 기록
On success, update only Step 11 to `completed` and add one concise `summary`. On an implementation,
build, test or reference-comparison failure, classify it and update only the Executor-selected Step's
allowed payload. Executor owns retry, timestamps, commits, advancement and top-level status. Do not
start Step 12.
## 금지사항
- Do not edit any reference artifact. 이유: exact stored files are immutable external evidence.
- Do not run FESA on an original reference `.inp`. 이유: derived sibling outputs would mutate the
reference directory.
- Do not add HDF5 datasets, change schema version/path/shape/components or add public headers. 이유:
Stage 5 requires reconstruction from existing schema-v1 datasets.
- Do not use initial coordinates or raw rotational covectors for global physical moment. 이유:
`AUDIT-001` requires accepted current arms and spatial moment conversion.
- Do not apply the constraint mask after spatial conversion. 이유: partial rotational constraints
would mix free residual into reaction.
- Do not use `/global/equilibrium`, local `/nodal/reaction` or a raw full-residual sum as a substitute
for all three equilibrium quantities. 이유: their physical identities differ.
- Do not make local reaction rows blocking or omit their review evidence. 이유: USER-DECISION-004
fixes review-only with deterministic findings.
- Do not compare `RM3` or stress values, average/collapse rows, match by row order, clamp zero values,
add `max(1,S)`, relax tolerance or repair CSVs. 이유: approved identity/tolerance contracts forbid it.
- Do not add `fesa.env`, runtime configuration, product solver acceptance, CLI or public API behavior
for the `1e-8` gate. 이유: this fixed threshold belongs only to comparator-owned internal verification.
- Do not replay or fabricate selector RED, delete partial Step 11 work, reset history, commit, push,
run a reference solver, start Step 12, or claim Physics/Release readiness.
- Stop and return a classified blocker if the required behavior needs an upstream contract, public
API, HDF5 schema, tolerance or reference-artifact change.