# Step 4: dof-manager ## 읽어야 할 파일 먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라: - `/AGENTS.md` - `/docs/PRD.md` - `/docs/ARCHITECTURE.md` - `/docs/ADR.md` - `/src/fesa/model/boundary_condition.hpp` - `/src/fesa/analysis/analysis_model.hpp` - `/tests/unit/analysis_model_view_test.cpp` 이전 step에서 만들어진 model object와 AnalysisModel을 꼼꼼히 읽고, equation numbering이 Node/Element로 새지 않도록 유지하라. ## 작업 node별 DOF 정의, constrained/free mapping, equation numbering, sparse pattern ownership의 최소 골격을 `/src/fesa/fem/`에 구현한다. 필수 파일: - `/src/fesa/fem/dof_key.hpp` - `/src/fesa/fem/dof_manager.hpp` - `/tests/unit/dof_manager_numbering_test.cpp` 필수 interface: ```cpp namespace fesa::fem { struct DofKey { core::NodeId node_id; model::DofComponent component; }; bool operator==(const DofKey& lhs, const DofKey& rhs); class DofManager { public: void define_node_dofs(core::NodeId node_id, std::vector components); void apply_boundary_condition(const model::BoundaryCondition& bc); void number_equations(); int total_dof_count() const; int free_dof_count() const; int constrained_dof_count() const; bool is_constrained(DofKey key) const; int equation_id(DofKey key) const; std::optional free_equation_id(DofKey key) const; std::vector expand_free_vector(const std::vector& free_values) const; const std::vector>& sparse_pattern() const; }; } // namespace fesa::fem ``` 구현 규칙: - Equation id는 `DofManager` 내부에만 저장한다. - `equation_id`는 전체 DOF ordering의 dense id를 반환한다. - `free_equation_id`는 constrained DOF면 `std::nullopt`를 반환한다. - `number_equations()`는 deterministic ordering을 보장한다: - node id value 오름차순 - component enum 순서 오름차순 - `expand_free_vector`는 constrained DOF 값을 0.0으로 채우고 free DOF 값만 배치한다. - `sparse_pattern()`은 skeleton 단계에서 free equation ids의 dense full matrix pattern을 deterministic pair list로 보유해도 된다. - missing DOF 조회는 `std::invalid_argument`를 던진다. ## Tests To Write First - `/tests/unit/dof_manager_numbering_test.cpp` - 두 node에 `ux`, `uy`를 정의하고 deterministic equation id ordering을 확인한다. - boundary condition 적용 후 constrained/free count가 맞는지 확인한다. - constrained key의 `free_equation_id`가 `std::nullopt`임을 확인한다. - free vector가 full vector로 재구성될 때 constrained DOF가 0.0으로 채워지는지 확인한다. - sparse pattern pair list가 free equation id 기반 deterministic dense pattern을 가진다. - model::Node나 model::Element를 수정하지 않고도 equation numbering이 가능함을 확인한다. RED 확인: 1. 테스트 파일을 먼저 작성한다. 2. targeted CTest를 실행해 missing `dof_manager.hpp`로 실패함을 확인한다. 3. 그 뒤 production headers를 작성한다. ## Acceptance Criteria ```powershell python -m unittest discover -s scripts -p "test_*.py" python scripts/validate_workspace.py ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R dof_manager_numbering_test ``` ## 검증 절차 1. 위 AC 커맨드를 실행한다. 2. 아키텍처 체크리스트를 확인한다: - DofManager가 equation numbering을 전담하는가? - Node/Element public interface가 equation id로 오염되지 않았는가? - sparse pattern ownership이 DofManager 내부에 있는가? 3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 4를 업데이트한다: - 성공: `"status": "completed"`, `"summary": "DofManager deterministic numbering and constrained/free mapping added"` - 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"` - 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단 ## 금지사항 - Solver backend, assembly matrix, MKL adapter를 구현하지 마라. - Node 또는 Element에 equation id field를 추가하지 마라. - JavaScript/TypeScript/npm fallback을 추가하지 마라.