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

7.8 KiB

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

인터페이스

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:

cmake --build .harness/build --config Debug
ctest --test-dir .harness/build -C 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

cmake --fresh -S . -B .harness/build -G "Visual Studio 18 2026" -A x64 `
  "-DFESA_GTEST_SOURCE_DIR=$gtestSource" `
  "-DMKL_DIR=$mklDir" `
  "-DTBB_DIR=$tbbDir" `
  "-DHDF5_DIR=$hdf5Dir"
cmake --build .harness/build --config Debug
ctest --test-dir .harness/build -C Debug -R "NonlinearAnalysisState|CommitRollback" --output-on-failure
ctest --test-dir .harness/build -C Debug --show-only=json-v1
ctest --test-dir .harness/build -C Debug --output-on-failure

Recheck .harness/config.json before default commands.

상태 기록

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.