# Step 5: load-boundary-path-and-convergence-gates > 과거 개발 계획이다. 아래 작업·상태·예시는 현재 실행 지시가 아니다. > 현재 구현과 검증 한계는 [기능 요약](../../docs/nonlinear-static-newton-raphson/README.md)을 따른다. ## 읽어야 할 파일 - `AGENTS.md` - `../../docs/solver-core/README.md` - `../../docs/nonlinear-static-newton-raphson/README.md` - `../../docs/nonlinear-static-newton-raphson/formulation.md` - `../../docs/nonlinear-static-newton-raphson/README.md` - `../../docs/nonlinear-static-newton-raphson/README.md` - `include/fesa/core/diagnostic.h` - `include/fesa/fem/dof_manager.h` - `include/fesa/math/sparse_matrix.h` - `include/fesa/math/vector3.h` - Step 1 files: `include/fesa/model/domain.h`, `include/fesa/model/model_types.h` - Step 4 files: `include/fesa/elements/nonlinear_mitc4_shell.h` ## 이전 Step 전제 Step 1 stores raw nonlinear static values in Domain. Step 4 provides internal-only element residual/tangent. This Step owns global-once fixed CLOAD, absolute boundary path assembly, and the external contribution sign contract used by Step 10: `r = f_int - f_ext`, `K_T = K_int - K_ext`. ## 소유 파일 - Create: `include/fesa/analysis/nonlinear_static_control.h` - Create: `src/fesa/analysis/nonlinear_static_control.cpp` - Create: `tests/unit/analysis/nonlinear_static_control_test.cpp` - Modify: `src/fesa/CMakeLists.txt` - Modify: `tests/CMakeLists.txt` ## 인터페이스 ```cpp namespace fesa { struct LoadFactorIncrement { std::uint32_t increment_index; double load_factor; bool is_factor_one; }; enum class NewtonChannelUnit { kForce, kForceLength, kLength, kDimensionless }; inline constexpr double kNewtonRelativeThreshold = 1.0e-2; inline constexpr double kNewtonZeroScaleThreshold = 1.0e-3; struct NewtonResidualChannelInput { std::string identity; Vector free_residual; Vector free_internal; Vector free_external; }; struct NewtonCorrectionChannelInput { std::string identity; Vector fresh_correction; Vector current_increment; }; struct NewtonConvergenceInput { NewtonResidualChannelInput force_residual; NewtonResidualChannelInput moment_residual; NewtonCorrectionChannelInput translation_correction; NewtonCorrectionChannelInput rotation_correction; }; enum class NewtonConvergenceBranch { kRelative, kZeroScale }; struct NewtonChannelDecision { std::string identity; double numerator_norm; double denominator_scale; NewtonConvergenceBranch branch; double threshold; NewtonChannelUnit threshold_unit; bool passed; }; struct NewtonConvergenceResult { NewtonChannelDecision force_residual; NewtonChannelDecision moment_residual; NewtonChannelDecision translation_correction; NewtonChannelDecision rotation_correction; bool combined_passed; }; struct GlobalFixedLoadContribution { Vector generalized_external_force; SparseMatrix generalized_external_tangent; std::vector diagnostics; }; Result> BuildFixedLoadFactorSequence( const StepDefinition& step); Result ApplyAbsolutePrescribedDisplacementTarget( const StepDefinition& step, double load_factor); Result AssembleGlobalFixedLoadContribution( const Domain& domain, const DofManager& dofs, const Vector& trial_generalized_coordinates, double load_factor); Status CheckMomentAdmissibility(const Vector3& director, const Vector3& source_moment); NewtonConvergenceResult EvaluateNewtonConvergence( const NewtonConvergenceInput& input); } // namespace fesa ``` ## RED Add tests first. - `NSNR-CTRL-001`: raw `initial_increment=0.1` and `time_period=1.0` produce factors `0.1..1.0`. - `NSNR-CTRL-002`: final factor is assigned exact `1.0`, not accumulated past one. - `NSNR-CTRL-003`: positive minimum/maximum values are accepted but not used for runtime stepping. - `NSNR-CTRL-004`: nonzero `*BOUNDARY` value is absolute factor-one target from step-start committed state. - `NSNR-CTRL-005`: constrained Newton correction remains exactly zero. - `NSNR-CTRL-006`: `*CLOAD` force and moment directions remain fixed global dead loads and scale by factor. - `NSNR-CTRL-007`: moment admissibility accepts `rho_M <= 1e-12` and rejects larger drilling-aligned moment. - `NSNR-CTRL-008`: `AssembleGlobalFixedLoadContribution` contributes each aggregated source node load exactly once, independent of incident element count. - `NSNR-CTRL-009`: source-order load rows produce deterministic global vector and tangent order. - `NSNR-CTRL-010`: fixed-spatial moment projection and `J_l`/map-Hessian tangent terms pass directional derivative checks and may make `K_ext` nonsymmetric. - `NSNR-CTRL-011`: global fixed-load assembly returns external quantities consumed with approved signs `r = f_int - f_ext` and `K_T = K_int - K_ext`; the fixed-moment tangent is subtracted once and is never duplicated per incident element. - `NSNR-CTRL-012`: `NewtonConvergenceInput` has no caller-supplied threshold or unit fields; `EvaluateNewtonConvergence` owns exact immutable constants `kNewtonRelativeThreshold=1.0e-2` and `kNewtonZeroScaleThreshold=1.0e-3`. - `NSNR-CTRL-013`: four convergence gates expose identity, `numerator_norm`, `denominator_scale`, branch, typed threshold, `threshold_unit`, pass, and combined pass. - `NSNR-CTRL-014`: nonzero-denominator relative branches use threshold `1.0e-2` and threshold unit `kDimensionless` for all four channels. - `NSNR-CTRL-015`: zero-scale branches use threshold `1.0e-3` with units `kForce`, `kForceLength`, `kLength`, and `kDimensionless` for residual force, residual moment, correction translation, and correction rotation respectively. - `NSNR-CTRL-016`: empty free channels evaluate with `numerator_norm=0`, `denominator_scale=0`, branch `kZeroScale`, exact zero-scale threshold/unit, and pass. - `NSNR-CTRL-017`: iteration numbering remains `1..100` with no iteration `101`. ### USER-DECISION-003 option-1 revision addendum Step 5의 completed status, summary, timestamps와 commits는 보존한다. 다음 correction tests와 minimal GREEN은 재실행된 Step 5가 아니라 Executor-selected Step 11 안에서 수행한다. - `NSNR-CTRL-018`: force와 force-length residual channel 각각에서 matching external vector가 finite exact zero이고 internal/residual이 nonzero여도 `kZeroScale`을 선택한다. Numerator exact `1.0e-3`은 pass, `std::nextafter(1.0e-3, +infinity)`는 fail이며 decision denominator는 exact `0`, threshold unit은 각각 `kForce`, `kForceLength`다. Empty free channel pass도 유지한다. - `NSNR-CTRL-019`: matching external scale이 nonzero이면 internal-dominant와 external-dominant case 모두 denominator `max(||f_int||inf,||f_ext||inf)`를 쓴다. Ratio exact `1.0e-2`는 pass, 바로 위는 fail이다. Numerator가 typed `1.0e-3`보다 작아도 relative ratio가 실패하는 case를 포함하여 absolute/relative OR가 없음을 증명한다. - `NSNR-CTRL-020`: residual/internal/external vector 또는 derived norm의 NaN/Inf는 branch/tolerance acceptance 전에 fail하고 combined pass를 만들지 않는다. `std::nextafter(0.0,+infinity)`인 matching external scale은 tolerance band, zero clamp, `max(1,...)` 없이 nonzero relative branch를 선택한다. - `NSNR-CTRL-021`: force residual, moment residual, translation correction, rotation correction을 하나씩 실패시키는 truth table과 네 channel 모두 pass하는 case로 logical AND를 고정한다. NSNR-070 correction selector, denominators, thresholds와 units는 변경하지 않는다. Run: ```powershell cmake --build --preset windows-msvc-ninja-debug ctest --preset windows-msvc-ninja-debug -R "NonlinearStaticControl|NonlinearConvergence|NonlinearLoadBoundary" --output-on-failure ``` Expected: new tests fail. Unexpected pass stops. ## GREEN Implement factor sequence derivation, load/BC scaling, global-once fixed-load external vector `f_ext` and external tangent `K_ext` assembly, moment admissibility, and typed convergence-channel evaluation. For the USER-DECISION-003 correction, validate the residual/internal/external values and norms first, select a residual branch from the matching external scale itself, emit denominator exact `0` and typed absolute `1.0e-3` when that scale is exact zero, and otherwise emit retained `max(internal,external)` with relative `1.0e-2`. Correction denominator selection remains derived from current increment values. Thresholds and threshold units are not caller inputs; `EvaluateNewtonConvergence` assigns the exact branch/channel constants from `requirements.md` and `io.md`. Return structured diagnostics for invalid nonlinear static data, unsupported drilling load, nonfinite Newton data, and nonconvergence readiness. ## VERIFY ```powershell cmake --preset windows-msvc-ninja-debug cmake --build --preset windows-msvc-ninja-debug ctest --preset windows-msvc-ninja-debug -R "NonlinearStaticControl|NonlinearConvergence|NonlinearLoadBoundary" --output-on-failure ctest --preset windows-msvc-ninja-debug --show-only=json-v1 ctest --preset windows-msvc-ninja-debug --output-on-failure ``` Use the ignored 과거 검증 자료(정리됨) preset contract. The Harness process must inherit VsDevCmd x64 and `FESA_GTEST_SOURCE_DIR`, `MKL_DIR`, `TBB_DIR`, and `HDF5_DIR` from its parent launch environment. ## 상태 기록 Update only Step 5 status and one-line payload. Executor owns timestamps, commits, retry, and advancement. ## 금지사항 - Do not put factor sequence storage into Domain. 이유: control layer derives runtime factors. - Do not implement cutback, line search, damping, or automatic growth. 이유: approved Full Newton path excludes them. - Do not mutate source load vectors. 이유: fixed-global source identity must remain immutable. - Do not distribute full source-node CLOAD into every incident element. 이유: this would multiply loads by incident element count and violate formulation §11.2. - Do not emit convergence branch labels outside `relative` and `zero-scale`. 이유: io.md exposes exactly those two branches. - Do not accept per-call convergence thresholds or units. 이유: NSNR-069/070 fix branch thresholds and typed units globally. - Do not use internal scale, tolerance bands, clamps, `max(1,...)`, or absolute/relative OR to select a residual branch. 이유: revised NSNR-069 selects only from the finite matching external scale's exact-zero identity.