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

166 lines
7.8 KiB
Markdown

# Step 8: nonlinear-state-commit-rollback
## 읽어야 할 파일
- `AGENTS.md`
- `docs/ARCHITECTURE.md`
- `docs/ADR.md`
- `docs/nonlinear-static-newton-raphson/requirements.md`
- `docs/nonlinear-static-newton-raphson/formulation.md`
- `docs/nonlinear-static-newton-raphson/io.md`
- `include/fesa/analysis/analysis_state.h`
- `src/fesa/analysis/analysis_state.cpp`
- `include/fesa/core/source_identity.h`
- `include/fesa/fem/dof_manager.h`
- `include/fesa/model/domain.h`
- `include/fesa/results/result_records.h`
- Step 3 files: `include/fesa/elements/nonlinear_mitc4_shell.h`
- Step 4 files: `include/fesa/elements/nonlinear_mitc4_shell.h`
- Step 5 files: `include/fesa/analysis/nonlinear_static_control.h`
- Step 6 files: `include/fesa/solvers/linear/tangent_solver.h`
- Step 7 files: `include/fesa/analysis/newton_iteration_log.h`
## 이전 Step 전제
Steps 3-7 provide nonlinear element recovery, residual/tangent, controls, tangent solver, and Newton log record types.
## 소유 파일
- Create: `include/fesa/analysis/nonlinear_analysis_state.h`
- Create: `src/fesa/analysis/nonlinear_analysis_state.cpp`
- Create: `tests/unit/analysis/nonlinear_analysis_state_test.cpp`
- Modify: `src/fesa/CMakeLists.txt`
- Modify: `tests/CMakeLists.txt`
## 인터페이스
```cpp
namespace fesa {
struct NonlinearNodeBaseline {
EntityIndex node_index;
SourceEntityId source_id;
std::array<std::array<double, 3>, 3> triad;
std::array<double, 3> director;
};
struct NonlinearStateBaselineInput {
std::vector<NonlinearNodeBaseline> node_baselines;
};
struct NonlinearTrialState {
std::uint32_t increment_index;
double load_factor;
bool is_factor_zero_baseline;
std::vector<EntityIndex> nodal_internal_order;
std::vector<SourceEntityId> nodal_source_ids;
Vector generalized_coordinates;
Vector displacement;
Vector rotation_vector;
std::vector<std::array<std::array<double, 3>, 3>> nodal_triad;
std::vector<std::array<double, 3>> nodal_director;
Vector internal_force;
Vector external_force;
Vector residual;
Vector reaction;
std::vector<ShellResultRow> shell_results;
double physical_strain_energy;
std::array<double, 6> global_equilibrium;
};
struct AcceptedFrameState {
std::uint32_t frame_index;
std::uint32_t increment_index;
double load_factor;
std::uint32_t iteration_count;
bool is_factor_one;
std::vector<EntityIndex> nodal_internal_order;
std::vector<SourceEntityId> nodal_source_ids;
Vector generalized_coordinates;
Vector displacement;
Vector rotation_vector;
std::vector<std::array<std::array<double, 3>, 3>> nodal_triad;
std::vector<std::array<double, 3>> nodal_director;
Vector internal_force;
Vector external_force;
Vector residual;
Vector reaction;
std::vector<ShellResultRow> shell_results;
double physical_strain_energy;
std::array<double, 6> global_equilibrium;
};
class NonlinearAnalysisState {
public:
static Result<NonlinearAnalysisState> Create(
const Domain& domain,
const DofManager& dofs,
const NonlinearStateBaselineInput& baseline);
Result<NonlinearTrialState> BeginTrialFromCommitted() const;
Status CommitAcceptedFrame(const AcceptedFrameState& frame);
void DiscardTrial() noexcept;
const std::vector<AcceptedFrameState>& AcceptedFrames() const noexcept;
const NonlinearTrialState& CommittedState() const noexcept;
};
} // namespace fesa
```
## RED
Add tests first.
- `NSNR-STATE-000`: `NonlinearAnalysisState::Create` requires exactly one `NonlinearNodeBaseline` per `Domain.Nodes()` entry in stable internal order; each entry has `node_index` equal to its vector offset and `source_id` equal to `Domain.Nodes()[node_index].source_id`.
- `NSNR-STATE-001`: `NonlinearAnalysisState::Create` rejects missing, extra, duplicate, out-of-order, or instance-label mismatched node identities before any mutable state is produced.
- `NSNR-STATE-002`: `NonlinearAnalysisState::Create` validates node/DOF/frame inventory, sizes all vectors, preserves `nodal_internal_order` and `nodal_source_ids`, and creates a committed factor-zero baseline with `increment_index=0`, `load_factor=0.0`, `is_factor_zero_baseline=true`, and no accepted frames.
- `NSNR-STATE-003`: duplicate source labels across different instances remain distinct when `SourceEntityId::instance_name` differs.
- `NSNR-STATE-004`: trial state starts from committed state and can be discarded without mutation.
- `NSNR-STATE-005`: accepted frames are appended in stable accepted-factor order.
- `NSNR-STATE-006`: frame index is zero-based accepted-frame order; the factor-zero committed baseline is metadata/state only and is never appended as an accepted frame.
- `NSNR-STATE-007`: final frame at load factor `1.0` has `is_factor_one=true`.
- `NSNR-STATE-008`: residual and reaction are stored from full residual `r = f_int - f_ext`.
- `NSNR-STATE-009`: accepted frame owns generalized coordinates, displacement, rotation vector, per-node identity/order, per-node triad/director, internal/external/residual/reaction, stable `ShellResultRow` recovery, physical strain energy, and global equilibrium.
- `NSNR-STATE-010`: all accepted-frame vectors match node/DOF counts and all `ShellResultRow` rows are stable element/midsurface order.
- `NSNR-STATE-011`: commit validates finite values for every approved scalar and atomically rejects nonfinite frames with `invalid-accepted-state`.
- `NSNR-STATE-012`: failed trial preserves prior committed state.
- `NSNR-STATE-013`: no unapproved verification data is stored.
- `NSNR-STATE-014`: committed and accepted nodal identity vectors are preserved for Step 9 HDF5 node datasets and Step 11 HDF5-to-reference source mapping; source label alone is never used as identity.
Run:
```powershell
cmake --build --preset windows-msvc-ninja-debug
ctest --preset windows-msvc-ninja-debug -R "NonlinearAnalysisState|CommitRollback" --output-on-failure
```
Expected: new tests fail. Unexpected pass stops.
## GREEN
Implement `NonlinearAnalysisState::Create` as the only construction seam for a valid nonlinear state. It uses `Domain`, `DofManager`, and `NonlinearStateBaselineInput` to require one baseline per Domain node in stable internal order, match both `EntityIndex` and full `SourceEntityId`, allocate zero generalized/displacement/rotation/internal/external/residual/reaction vectors, preserve nodal order/source identity for HDF5/reference mapping, install finite per-node triads/directors, and establish the committed factor-zero baseline without adding an accepted frame. Commit validates inventory, order, shape, identity, and finiteness before mutating committed state.
## VERIFY
```powershell
cmake --preset windows-msvc-ninja-debug
cmake --build --preset windows-msvc-ninja-debug
ctest --preset windows-msvc-ninja-debug -R "NonlinearAnalysisState|CommitRollback" --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 `.harness/config.json` 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 8 status and one-line payload. Executor owns timestamps, commits, retry, and advancement.
## 금지사항
- Do not put equation ids into Node or Element objects. 이유: `DofManager` owns equation numbering.
- Do not identify baseline nodes by source label alone. 이유: labels are not unique across instances and cannot support stable HDF5/reference mapping.
- Do not commit trial data before convergence gates pass. 이유: rollback/preservation contract depends on committed-state isolation.
- Do not expand existing `AnalysisState` for future nonlinear features. 이유: this Step owns only approved NSNR state.
- Do not store stress projections or verification data outside approved `ShellResultRow`, physical energy, and global equilibrium fields. 이유: schema v1 has a bounded accepted-state contract.