23 KiB
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.mddocs/PRD.mddocs/ARCHITECTURE.mddocs/ADR.mddocs/CODINGSTYLE.mddocs/TOLERANCE.mddocs/nonlinear-static-newton-raphson/requirements.mddocs/nonlinear-static-newton-raphson/research.mddocs/nonlinear-static-newton-raphson/formulation.md§§11.4, 13.1, 13.4, 14.1docs/nonlinear-static-newton-raphson/numerical-review.md§§3.2, 6.2, 6.3docs/nonlinear-static-newton-raphson/reference-model.md§§3–6docs/nonlinear-static-newton-raphson/io.md§§7.3–7.5.1, 8.1–8.7docs/nonlinear-static-newton-raphson/implementation-plan.mddocs/nonlinear-static-newton-raphson/corrections.md§13phases/nonlinear-static-newton-raphson/index.jsoninclude/fesa/analysis/nonlinear_analysis_state.hinclude/fesa/analysis/nonlinear_static_procedure.hinclude/fesa/analysis/nonlinear_static_control.hsrc/fesa/analysis/nonlinear_static_procedure.cppsrc/fesa/analysis/nonlinear_static_control.cppinclude/fesa/results/nonlinear_results_writer.hinclude/fesa/io/hdf5/nonlinear_hdf5_results_writer.hsrc/fesa/io/hdf5/nonlinear_hdf5_results_writer.cpptests/integration/analysis/nonlinear_static_procedure_test.cpptests/unit/io/hdf5/nonlinear_hdf5_results_writer_test.cpptests/unit/analysis/nonlinear_static_control_test.cpptests/unit/analysis/newton_iteration_log_test.cpptests/reference/reference_tolerance_policy.htests/reference/reference_tolerance_policy.cpptests/reference/nonlinear_static_reference_comparison.htests/reference/nonlinear_static_reference_comparison.cpptests/reference/nonlinear_static_reference_cases_test.cpptests/CMakeLists.txt.harness/config.jsonCMakePresets.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..023comparator/CMake 작업은 이전 blocked Step 11에서 생성된 보존 대상이다. 삭제·재생성·reset하지 말고 현재 내용에서 계속한다. - 현재 comparator의 shared
<=1e-10global 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만 읽는다.
/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:
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:
- Read
Xfrom Domain/model identity andu, phifrom the accepted state. Setx=X+u. - Read generalized applied force/covector from
generalized_external_force. - Build constrained generalized reaction by applying the exact component mask to
generalized_residualbefore any rotational conversion. - 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. - Accumulate, about global origin
[0,0,0]:
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-8gates. 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.mdis 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-011collides with completed Step 10's historicalNSNR-PROC-011increment-loop identity. Preserve that history and use collision-resolved test IDNSNR-PROC-019with exact GTest symbolNonlinearStaticProcedure.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 acceptedX,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-014NonlinearHdf5ResultsWriter.WritesSchemaV1GlobalBalanceErrorLocationchecks existing path/shape, exact component names, schema version 1 andlocation=global-balance-error. Expected RED: current writer emitslocation=global-origin.NSNR-REF-020NonlinearStaticReference.AuditsFinePrimaryAndCoarseCompanionArtifactschecks 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-021NonlinearStaticReference.ReviewOnlyLocalReactionsCannotChangeBlockingVerdictinjects 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 arekBlocking.NSNR-REF-022NonlinearStaticReference.BlocksOnGlobalBalanceAndAcceptedFreeResidualfails 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-10gate, so the approved coarse metric1.413750912531167e-10and fine metric2.7454660159924086e-10are rejected even though both are below1e-8.NSNR-REF-023NonlinearStaticReference.RejectsMissingExtraDuplicateAndNonfiniteRowsBeforeToleranceAndRecordsReviewOnlyFindingscovers 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-024NonlinearStaticReference.GlobalForceAndMomentBalanceUseApprovedInclusiveOneEighthGateuses a test-private balance evaluator for distinct force and moment family identities. It requires an approved between-threshold metric such as5e-9and exact1e-8to pass, andnextafter(1e-8,+infinity)to fail for both identities. Expected decisive RED: the preserved shared<=1e-10comparison rejects5e-9and1e-8.NSNR-REF-025NonlinearStaticReference.GlobalForceAndMomentExactZeroScaleRequiresExactZeroBalanceexercises 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:
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
- Replace the private raw-residual/initial-coordinate global helper in
src/fesa/analysis/nonlinear_static_procedure.cppwith 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. - In
src/fesa/io/hdf5/nonlinear_hdf5_results_writer.cpp, preserve the path, shape, component names and schema version; change the exact/global/equilibriumlocation attribute toglobal-balance-errorand keep writer validation finite/fail-closed. - Extend the existing comparator without deleting its partial work:
- add fine primary inventory and keep coarse companion/linear control independent;
- add
kReviewOnlyand 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/reactionor/global/equilibriumas a substitute for all three; - apply internal gates exactly as specified above, using one test-private/internal shared
1.0e-8constant 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.
- 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. - 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/U3are separate blocking families. - Linear
U1/U2/U3is a blocking regression family. - All
UR1/UR2/UR3families are warning-only. - Fine/coarse local
RF1/RF2,RF3, spatialRM1/RM2are separate review-only families with exact mask-derived keys and complete common-policy error reports. RM3and 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-onlyS=max(abs(reference)),B=0.01*S, near-zero rowabs(error)<=B, other-row relative<=0.05, familyRMS(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:
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:
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/equilibriumremains schema v1[6]with the same component names and stores only the current-arm/spatial-moment applied-plus-reaction balance error withlocation=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-001requires 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/reactionor 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
RM3or stress values, average/collapse rows, match by row order, clamp zero values, addmax(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 the1e-8gate. 이유: 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.