Compare commits
1 Commits
dev
...
4e7fd1087d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e7fd1087d |
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"phases": [
|
||||
{
|
||||
"dir": "solver-core-skeleton",
|
||||
"status": "pending"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"project": "FESA Structural Solver",
|
||||
"phase": "solver-core-skeleton",
|
||||
"steps": [
|
||||
{
|
||||
"step": 0,
|
||||
"name": "cmake-ctest-bootstrap",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"CMakeLists.txt",
|
||||
"tests/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 1,
|
||||
"name": "core-primitives",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"src/fesa/core/",
|
||||
"tests/unit/core_*_test.cpp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 2,
|
||||
"name": "domain-model-entities",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"src/fesa/model/",
|
||||
"tests/unit/model_*_test.cpp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 3,
|
||||
"name": "analysis-model-view",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"src/fesa/analysis/",
|
||||
"tests/unit/analysis_model_*_test.cpp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 4,
|
||||
"name": "dof-manager",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"src/fesa/fem/",
|
||||
"tests/unit/dof_manager_*_test.cpp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 5,
|
||||
"name": "analysis-state",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"src/fesa/analysis/",
|
||||
"tests/unit/analysis_state_*_test.cpp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 6,
|
||||
"name": "analysis-template-flow",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"src/fesa/analysis/",
|
||||
"tests/unit/analysis_flow_*_test.cpp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 7,
|
||||
"name": "results-containers",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"src/fesa/results/",
|
||||
"tests/unit/results_*_test.cpp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": 8,
|
||||
"name": "solver-skeleton-integration-report",
|
||||
"status": "pending",
|
||||
"allowed_paths": [
|
||||
"tests/integration/",
|
||||
"docs/build-test-reports/"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# Step 0: cmake-ctest-bootstrap
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 검증 규칙을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/scripts/validate_workspace.py`
|
||||
|
||||
## 작업
|
||||
|
||||
C++ solver skeleton을 구현할 수 있도록 최소 CMake/CTest 부트스트랩을 만든다.
|
||||
|
||||
요구사항:
|
||||
|
||||
- 루트 `/CMakeLists.txt`를 생성한다.
|
||||
- C++ 표준은 C++17 이상으로 고정한다.
|
||||
- MSVC에서 warning-as-error를 강제하지 않는다.
|
||||
- 아직 production source가 없으므로 solver target은 `INTERFACE` library `fesa_core`로 시작한다.
|
||||
- `fesa_core`는 repository root의 `src`를 include directory로 노출한다.
|
||||
- `enable_testing()`과 `tests/` 하위 CMake 구성을 연결한다.
|
||||
- `/tests/CMakeLists.txt`, `/tests/unit/CMakeLists.txt`, `/tests/integration/CMakeLists.txt`를 생성한다.
|
||||
- `tests/unit/*_test.cpp`와 `tests/integration/*_test.cpp` 파일을 각각 독립 test executable로 등록한다.
|
||||
- 각 test executable은 `fesa_core`에 link한다.
|
||||
- `/tests/unit/harness_smoke_test.cpp`를 생성하고, 표준 라이브러리만 사용해 CTest가 동작함을 확인하는 최소 `main()`을 둔다.
|
||||
- npm, JavaScript, TypeScript fallback은 추가하지 않는다.
|
||||
|
||||
권장 CMake 구조:
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(FESA LANGUAGES CXX)
|
||||
|
||||
add_library(fesa_core INTERFACE)
|
||||
target_compile_features(fesa_core INTERFACE cxx_std_17)
|
||||
target_include_directories(fesa_core INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
```
|
||||
|
||||
`tests/unit/CMakeLists.txt`와 `tests/integration/CMakeLists.txt`는 `file(GLOB CONFIGURE_DEPENDS ...)`와 `foreach`를 사용해 새 `*_test.cpp`가 자동으로 CTest에 등록되도록 만든다.
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/unit/harness_smoke_test.cpp`
|
||||
- `main()`이 `std::string{"fesa"}.size() == 4` 같은 deterministic smoke assertion을 검증한다.
|
||||
- 실패 시 non-zero를 반환한다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. `tests/unit/harness_smoke_test.cpp`를 먼저 만든다.
|
||||
2. `ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R harness_smoke_test`를 실행해 아직 CMake 구성이 없어 실패함을 확인한다.
|
||||
3. 그 뒤 CMake 파일을 작성한다.
|
||||
|
||||
## 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 harness_smoke_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- ARCHITECTURE.md의 `src/`, `tests/unit/` 구조를 따르는가?
|
||||
- ADR-002의 C++17/MSVC/CMake/CTest 기본값을 벗어나지 않았는가?
|
||||
- AGENTS.md의 `python scripts/validate_workspace.py` 기본 검증 경로를 유지하는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 0을 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "CMake/CTest bootstrap with fesa_core interface target and smoke test"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- C++ production solver class를 이 step에서 만들지 마라.
|
||||
- 외부 test framework를 추가하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
@@ -0,0 +1,105 @@
|
||||
# Step 1: core-primitives
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/CMakeLists.txt`
|
||||
- `/tests/unit/CMakeLists.txt`
|
||||
|
||||
이전 step에서 만들어진 CMake/CTest 구성을 꼼꼼히 읽고, 새 unit test가 자동 등록되는 방식과 일관성을 유지하라.
|
||||
|
||||
## 작업
|
||||
|
||||
solver skeleton의 공통 primitive를 `/src/fesa/core/` 아래 header-only로 구현한다.
|
||||
|
||||
필수 파일:
|
||||
|
||||
- `/src/fesa/core/ids.hpp`
|
||||
- `/src/fesa/core/diagnostic.hpp`
|
||||
- `/src/fesa/core/status.hpp`
|
||||
- `/tests/unit/core_primitives_test.cpp`
|
||||
|
||||
필수 interface:
|
||||
|
||||
```cpp
|
||||
namespace fesa::core {
|
||||
|
||||
struct NodeId { int value; };
|
||||
struct ElementId { int value; };
|
||||
struct MaterialId { int value; };
|
||||
struct PropertyId { int value; };
|
||||
struct StepId { int value; };
|
||||
|
||||
enum class Severity { info, warning, error };
|
||||
|
||||
struct Diagnostic {
|
||||
Severity severity;
|
||||
std::string code;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class Status {
|
||||
public:
|
||||
static Status ok();
|
||||
static Status failure(Diagnostic diagnostic);
|
||||
|
||||
bool is_ok() const;
|
||||
const std::vector<Diagnostic>& diagnostics() const;
|
||||
void add(Diagnostic diagnostic);
|
||||
};
|
||||
|
||||
} // namespace fesa::core
|
||||
```
|
||||
|
||||
구현 규칙:
|
||||
|
||||
- 모든 ID type은 strong typedef 역할을 하며 서로 암시적으로 대체되지 않아야 한다.
|
||||
- ID에는 equation id 또는 DOF numbering 정보를 넣지 않는다.
|
||||
- `Status::ok()`는 diagnostics가 비어 있고 `is_ok() == true`여야 한다.
|
||||
- `Status::failure(...)`는 최소 하나의 diagnostic을 포함하고 `is_ok() == false`여야 한다.
|
||||
- 외부 라이브러리에 의존하지 않는다.
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/unit/core_primitives_test.cpp`
|
||||
- `NodeId{1}`과 `ElementId{1}`이 서로 다른 타입임을 compile-time으로 확인한다.
|
||||
- `Status::ok().is_ok()`가 true임을 확인한다.
|
||||
- `Status::failure(...)`가 false이고 diagnostic code/message를 보존함을 확인한다.
|
||||
- `Status::add(...)`로 warning을 추가해도 diagnostic 순서가 보존됨을 확인한다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. 테스트 파일을 먼저 작성한다.
|
||||
2. `python scripts/validate_workspace.py` 또는 targeted CTest를 실행해 `fesa/core/ids.hpp` 등 missing include로 실패함을 확인한다.
|
||||
3. 그 뒤 production header를 작성한다.
|
||||
|
||||
## 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 core_primitives_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- `core`가 외부 라이브러리에 의존하지 않는가?
|
||||
- ID type에 equation numbering을 저장하지 않는가?
|
||||
- C++ production header 변경에 대응하는 test file이 있는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 1을 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "Core ID, diagnostic, and status primitives added with tests"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- Node, Element, Domain 같은 model class를 이 step에서 만들지 마라.
|
||||
- MKL, TBB, HDF5 API를 include하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
@@ -0,0 +1,175 @@
|
||||
# Step 2: domain-model-entities
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/src/fesa/core/ids.hpp`
|
||||
- `/src/fesa/core/status.hpp`
|
||||
- `/tests/unit/core_primitives_test.cpp`
|
||||
|
||||
이전 step에서 만들어진 core primitive를 꼼꼼히 읽고, ID ownership과 diagnostic convention을 유지하라.
|
||||
|
||||
## 작업
|
||||
|
||||
입력 파일에서 생성된 전체 semantic model을 소유하는 최소 model layer를 `/src/fesa/model/`에 구현한다.
|
||||
|
||||
필수 파일:
|
||||
|
||||
- `/src/fesa/model/node.hpp`
|
||||
- `/src/fesa/model/element.hpp`
|
||||
- `/src/fesa/model/material.hpp`
|
||||
- `/src/fesa/model/property.hpp`
|
||||
- `/src/fesa/model/boundary_condition.hpp`
|
||||
- `/src/fesa/model/load.hpp`
|
||||
- `/src/fesa/model/analysis_step.hpp`
|
||||
- `/src/fesa/model/domain.hpp`
|
||||
- `/tests/unit/model_domain_test.cpp`
|
||||
|
||||
필수 interface:
|
||||
|
||||
```cpp
|
||||
namespace fesa::model {
|
||||
|
||||
class Node {
|
||||
public:
|
||||
Node(core::NodeId id, std::array<double, 3> coordinates);
|
||||
core::NodeId id() const;
|
||||
const std::array<double, 3>& coordinates() const;
|
||||
};
|
||||
|
||||
enum class ElementTopology { truss2, bar2, unknown };
|
||||
|
||||
class Element {
|
||||
public:
|
||||
Element(core::ElementId id, ElementTopology topology,
|
||||
std::vector<core::NodeId> node_ids, core::PropertyId property_id);
|
||||
core::ElementId id() const;
|
||||
ElementTopology topology() const;
|
||||
const std::vector<core::NodeId>& node_ids() const;
|
||||
core::PropertyId property_id() const;
|
||||
};
|
||||
|
||||
class Material {
|
||||
public:
|
||||
Material(core::MaterialId id, std::string name);
|
||||
core::MaterialId id() const;
|
||||
const std::string& name() const;
|
||||
};
|
||||
|
||||
class Property {
|
||||
public:
|
||||
Property(core::PropertyId id, std::string name, core::MaterialId material_id);
|
||||
core::PropertyId id() const;
|
||||
const std::string& name() const;
|
||||
core::MaterialId material_id() const;
|
||||
};
|
||||
|
||||
enum class DofComponent { ux, uy, uz, rx, ry, rz, temperature };
|
||||
|
||||
class BoundaryCondition {
|
||||
public:
|
||||
BoundaryCondition(core::NodeId node_id, DofComponent component, double value);
|
||||
core::NodeId node_id() const;
|
||||
DofComponent component() const;
|
||||
double value() const;
|
||||
};
|
||||
|
||||
class Load {
|
||||
public:
|
||||
Load(core::NodeId node_id, DofComponent component, double value);
|
||||
core::NodeId node_id() const;
|
||||
DofComponent component() const;
|
||||
double value() const;
|
||||
};
|
||||
|
||||
class AnalysisStep {
|
||||
public:
|
||||
AnalysisStep(core::StepId id, std::string name);
|
||||
core::StepId id() const;
|
||||
const std::string& name() const;
|
||||
void add_boundary_condition(BoundaryCondition bc);
|
||||
void add_load(Load load);
|
||||
const std::vector<BoundaryCondition>& boundary_conditions() const;
|
||||
const std::vector<Load>& loads() const;
|
||||
};
|
||||
|
||||
class Domain {
|
||||
public:
|
||||
void add_node(Node node);
|
||||
void add_element(Element element);
|
||||
void add_material(Material material);
|
||||
void add_property(Property property);
|
||||
void add_step(AnalysisStep step);
|
||||
|
||||
const std::vector<Node>& nodes() const;
|
||||
const std::vector<Element>& elements() const;
|
||||
const std::vector<Material>& materials() const;
|
||||
const std::vector<Property>& properties() const;
|
||||
const std::vector<AnalysisStep>& steps() const;
|
||||
|
||||
const Node* find_node(core::NodeId id) const;
|
||||
const Element* find_element(core::ElementId id) const;
|
||||
const Material* find_material(core::MaterialId id) const;
|
||||
const Property* find_property(core::PropertyId id) const;
|
||||
const AnalysisStep* find_step(core::StepId id) const;
|
||||
};
|
||||
|
||||
} // namespace fesa::model
|
||||
```
|
||||
|
||||
구현 규칙:
|
||||
|
||||
- Domain은 semantic model 객체를 소유한다.
|
||||
- `nodes()`, `elements()`, `materials()`, `properties()`, `steps()`는 const reference를 반환한다.
|
||||
- `find_*`는 없으면 `nullptr`을 반환한다.
|
||||
- Node와 Element 내부에 equation id, constrained/free equation mapping, sparse pattern 정보를 저장하지 않는다.
|
||||
- `DofComponent`는 아직 equation number가 아니라 물리 DOF component만 표현한다.
|
||||
- Abaqus keyword 문자열이나 parser detail을 model object에 저장하지 않는다.
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/unit/model_domain_test.cpp`
|
||||
- Node가 id와 3D coordinates를 보존한다.
|
||||
- Element가 topology, connectivity, property id를 보존한다.
|
||||
- Material과 Property가 id/name/material link를 보존한다.
|
||||
- AnalysisStep이 boundary condition과 load를 저장한다.
|
||||
- Domain이 add/find를 통해 각 객체를 조회한다.
|
||||
- 없는 id는 `nullptr`을 반환한다.
|
||||
- Node/Element public interface에 equation id를 노출하지 않는다는 점을 테스트 코드 사용 방식으로 확인한다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. 테스트 파일을 먼저 작성한다.
|
||||
2. targeted CTest를 실행해 missing model headers로 실패함을 확인한다.
|
||||
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 model_domain_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- Domain이 전체 모델 정의를 소유하는가?
|
||||
- model layer가 parser keyword 문자열이나 analysis state를 소유하지 않는가?
|
||||
- Node/Element에 equation id가 분산 저장되지 않는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 2를 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "Model entities and Domain ownership API added with tests"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- Parser, assembler, solver backend를 만들지 마라.
|
||||
- `Domain`을 실행 중 state container로 사용하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
@@ -0,0 +1,97 @@
|
||||
# Step 3: analysis-model-view
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/src/fesa/model/domain.hpp`
|
||||
- `/src/fesa/model/analysis_step.hpp`
|
||||
- `/tests/unit/model_domain_test.cpp`
|
||||
|
||||
이전 step에서 만들어진 Domain과 AnalysisStep을 꼼꼼히 읽고, Domain 복사 없는 step view를 유지하라.
|
||||
|
||||
## 작업
|
||||
|
||||
현재 step에서 활성화되는 해석 객체 view를 제공하는 `AnalysisModel`을 `/src/fesa/analysis/`에 구현한다.
|
||||
|
||||
필수 파일:
|
||||
|
||||
- `/src/fesa/analysis/analysis_model.hpp`
|
||||
- `/tests/unit/analysis_model_view_test.cpp`
|
||||
|
||||
필수 interface:
|
||||
|
||||
```cpp
|
||||
namespace fesa::analysis {
|
||||
|
||||
class AnalysisModel {
|
||||
public:
|
||||
AnalysisModel(const model::Domain& domain, core::StepId step_id);
|
||||
|
||||
const model::Domain& domain() const;
|
||||
const model::AnalysisStep& step() const;
|
||||
const std::vector<const model::Element*>& active_elements() const;
|
||||
const std::vector<const model::BoundaryCondition*>& active_boundary_conditions() const;
|
||||
const std::vector<const model::Load*>& active_loads() const;
|
||||
|
||||
const model::Property* property_for(const model::Element& element) const;
|
||||
const model::Material* material_for(const model::Property& property) const;
|
||||
};
|
||||
|
||||
} // namespace fesa::analysis
|
||||
```
|
||||
|
||||
구현 규칙:
|
||||
|
||||
- `AnalysisModel`은 `Domain`을 복사하지 않고 const reference 또는 pointer view만 보유한다.
|
||||
- Phase skeleton에서는 모든 Domain elements가 active라고 간주한다.
|
||||
- Boundary condition과 load는 선택된 `AnalysisStep`에서 가져온다.
|
||||
- `property_for`와 `material_for`는 Domain lookup을 사용한다.
|
||||
- 없는 step id는 구조화된 exception 대신 `std::invalid_argument`로 실패해도 된다. 이 skeleton 단계에서는 별도 error hierarchy를 만들지 않는다.
|
||||
- `AnalysisModel`은 displacement, residual, equation number를 소유하지 않는다.
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/unit/analysis_model_view_test.cpp`
|
||||
- Domain에 node/element/material/property/step을 만든다.
|
||||
- `AnalysisModel(domain, step_id)`가 원본 Domain reference를 유지함을 pointer identity로 확인한다.
|
||||
- 모든 Domain element가 `active_elements()`에 const pointer로 나타난다.
|
||||
- step의 boundary condition과 load가 active view에 const pointer로 나타난다.
|
||||
- `property_for(element)`와 `material_for(property)`가 Domain 소유 객체를 반환한다.
|
||||
- 없는 step id는 `std::invalid_argument`를 던진다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. 테스트 파일을 먼저 작성한다.
|
||||
2. targeted CTest를 실행해 missing `analysis_model.hpp`로 실패함을 확인한다.
|
||||
3. 그 뒤 production header를 작성한다.
|
||||
|
||||
## 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 analysis_model_view_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- AnalysisModel이 Domain을 복사하지 않는가?
|
||||
- AnalysisModel이 현재 step view만 제공하고 mutable state를 소유하지 않는가?
|
||||
- active BC/load가 AnalysisStep에서 온 것임이 테스트되는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 3을 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "AnalysisModel step view added without Domain copies"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- DofManager나 equation numbering을 이 step에서 구현하지 마라.
|
||||
- AnalysisState를 이 step에서 구현하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
@@ -0,0 +1,112 @@
|
||||
# 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<model::DofComponent> 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<int> free_equation_id(DofKey key) const;
|
||||
|
||||
std::vector<double> expand_free_vector(const std::vector<double>& free_values) const;
|
||||
const std::vector<std::pair<int, int>>& 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을 추가하지 마라.
|
||||
@@ -0,0 +1,112 @@
|
||||
# Step 5: analysis-state
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/src/fesa/fem/dof_manager.hpp`
|
||||
- `/tests/unit/dof_manager_numbering_test.cpp`
|
||||
|
||||
이전 step에서 만들어진 DofManager를 꼼꼼히 읽고, 해석 중 변하는 물리량은 AnalysisState가 소유하도록 유지하라.
|
||||
|
||||
## 작업
|
||||
|
||||
displacement 중심의 최소 `AnalysisState`를 `/src/fesa/analysis/`에 구현한다.
|
||||
|
||||
필수 파일:
|
||||
|
||||
- `/src/fesa/analysis/analysis_state.hpp`
|
||||
- `/tests/unit/analysis_state_vectors_test.cpp`
|
||||
|
||||
필수 interface:
|
||||
|
||||
```cpp
|
||||
namespace fesa::analysis {
|
||||
|
||||
struct IterationState {
|
||||
double time = 0.0;
|
||||
int increment = 0;
|
||||
int iteration = 0;
|
||||
};
|
||||
|
||||
class AnalysisState {
|
||||
public:
|
||||
explicit AnalysisState(int total_dof_count);
|
||||
|
||||
const std::vector<double>& displacement() const;
|
||||
const std::vector<double>& velocity() const;
|
||||
const std::vector<double>& acceleration() const;
|
||||
const std::vector<double>& temperature() const;
|
||||
const std::vector<double>& external_force() const;
|
||||
const std::vector<double>& internal_force() const;
|
||||
const std::vector<double>& residual() const;
|
||||
|
||||
void set_displacement(std::vector<double> values);
|
||||
void set_external_force(std::vector<double> values);
|
||||
void set_internal_force(std::vector<double> values);
|
||||
void update_residual();
|
||||
|
||||
IterationState& iteration_state();
|
||||
const IterationState& iteration_state() const;
|
||||
|
||||
void set_element_state(core::ElementId element_id, std::vector<double> state);
|
||||
const std::vector<double>* element_state(core::ElementId element_id) const;
|
||||
};
|
||||
|
||||
} // namespace fesa::analysis
|
||||
```
|
||||
|
||||
구현 규칙:
|
||||
|
||||
- 모든 vector는 `total_dof_count` 크기로 초기화한다.
|
||||
- `update_residual()`은 `external_force - internal_force`를 component-wise로 계산한다.
|
||||
- setter는 입력 vector 크기가 맞지 않으면 `std::invalid_argument`를 던진다.
|
||||
- temperature는 Phase skeleton에서 0.0 vector로 둔다.
|
||||
- element state는 향후 integration point state 확장을 위한 최소 map으로 둔다.
|
||||
- AnalysisState는 Domain, AnalysisModel, DofManager를 소유하지 않는다.
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/unit/analysis_state_vectors_test.cpp`
|
||||
- 생성 시 displacement/velocity/acceleration/temperature/force/residual vector 크기와 0.0 초기값을 확인한다.
|
||||
- displacement setter가 값을 보존한다.
|
||||
- external/internal force 설정 후 residual이 `Fext - Fint`가 되는지 확인한다.
|
||||
- 크기가 맞지 않는 vector setter가 `std::invalid_argument`를 던진다.
|
||||
- time/increment/iteration 값이 저장된다.
|
||||
- element state를 element id로 저장/조회한다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. 테스트 파일을 먼저 작성한다.
|
||||
2. targeted CTest를 실행해 missing `analysis_state.hpp`로 실패함을 확인한다.
|
||||
3. 그 뒤 production header를 작성한다.
|
||||
|
||||
## 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 analysis_state_vectors_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- AnalysisState가 해석 중 변하는 물리량을 소유하는가?
|
||||
- Domain이나 AnalysisModel을 복사/소유하지 않는가?
|
||||
- displacement 중심이되 velocity/acceleration/temperature 확장 지점을 유지하는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 5를 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "AnalysisState vector ownership and residual update added"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- Solver backend나 numerical integration loop를 구현하지 마라.
|
||||
- HDF5 writer를 이 step에서 구현하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
@@ -0,0 +1,114 @@
|
||||
# Step 6: analysis-template-flow
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/src/fesa/analysis/analysis_model.hpp`
|
||||
- `/src/fesa/analysis/analysis_state.hpp`
|
||||
- `/src/fesa/fem/dof_manager.hpp`
|
||||
- `/tests/unit/analysis_state_vectors_test.cpp`
|
||||
|
||||
이전 step에서 만들어진 AnalysisModel, DofManager, AnalysisState를 꼼꼼히 읽고, ARCHITECTURE.md의 Template Method 실행 흐름과 일관성을 유지하라.
|
||||
|
||||
## 작업
|
||||
|
||||
공통 해석 실행 흐름을 고정하는 `Analysis` base class와 선형 정적 해석 skeleton을 `/src/fesa/analysis/`에 구현한다.
|
||||
|
||||
필수 파일:
|
||||
|
||||
- `/src/fesa/analysis/analysis.hpp`
|
||||
- `/src/fesa/analysis/linear_static_analysis.hpp`
|
||||
- `/tests/unit/analysis_flow_template_test.cpp`
|
||||
|
||||
필수 interface:
|
||||
|
||||
```cpp
|
||||
namespace fesa::analysis {
|
||||
|
||||
class Analysis {
|
||||
public:
|
||||
virtual ~Analysis() = default;
|
||||
void run();
|
||||
|
||||
protected:
|
||||
virtual void initialize() {}
|
||||
virtual void build_analysis_model() {}
|
||||
virtual void build_dof_map() {}
|
||||
virtual void build_sparse_pattern() {}
|
||||
virtual void assemble() {}
|
||||
virtual void apply_boundary_conditions() {}
|
||||
virtual void solve() {}
|
||||
virtual void update_state() {}
|
||||
virtual void write_results() {}
|
||||
};
|
||||
|
||||
class LinearStaticAnalysis : public Analysis {
|
||||
public:
|
||||
LinearStaticAnalysis(const model::Domain& domain, core::StepId step_id);
|
||||
|
||||
const AnalysisModel* analysis_model() const;
|
||||
const AnalysisState* state() const;
|
||||
|
||||
protected:
|
||||
void build_analysis_model() override;
|
||||
void build_dof_map() override;
|
||||
void update_state() override;
|
||||
};
|
||||
|
||||
} // namespace fesa::analysis
|
||||
```
|
||||
|
||||
구현 규칙:
|
||||
|
||||
- `Analysis::run()`은 반드시 다음 순서로 hook을 호출한다:
|
||||
`initialize -> build_analysis_model -> build_dof_map -> build_sparse_pattern -> assemble -> apply_boundary_conditions -> solve -> update_state -> write_results`
|
||||
- `LinearStaticAnalysis`는 skeleton 단계에서 실제 stiffness assembly나 solve를 하지 않는다.
|
||||
- `LinearStaticAnalysis::build_analysis_model()`은 `AnalysisModel`을 생성한다.
|
||||
- `LinearStaticAnalysis::build_dof_map()`은 active element connectivity에서 등장한 node에 `ux`, `uy`, `uz`를 정의하고 active BC를 적용한 뒤 equation numbering을 수행한다.
|
||||
- `LinearStaticAnalysis::update_state()`는 total DOF count 크기의 `AnalysisState`를 준비한다.
|
||||
- MKL, TBB, HDF5 adapter는 만들지 않는다.
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/unit/analysis_flow_template_test.cpp`
|
||||
- test-only derived `RecordingAnalysis`가 hook 호출 순서를 vector에 기록한다.
|
||||
- `run()` 호출 후 ARCHITECTURE.md 순서와 정확히 일치하는지 확인한다.
|
||||
- 최소 Domain과 Step으로 `LinearStaticAnalysis`를 실행하면 `analysis_model()`과 `state()`가 null이 아니게 되는지 확인한다.
|
||||
- `LinearStaticAnalysis`가 실제 solver 결과를 계산한다고 주장하지 않음을 테스트 이름과 assertion 범위에 반영한다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. 테스트 파일을 먼저 작성한다.
|
||||
2. targeted CTest를 실행해 missing `analysis.hpp` 또는 `linear_static_analysis.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 analysis_flow_template_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- Analysis::run()이 Template Method 흐름을 고정하는가?
|
||||
- LinearStaticAnalysis가 skeleton 범위를 넘어 solver backend를 구현하지 않는가?
|
||||
- 외부 라이브러리 API가 solver core에 노출되지 않는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 6을 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "Analysis template method and LinearStaticAnalysis skeleton added"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 실제 stiffness matrix assembly, linear solve, MKL PARDISO adapter를 구현하지 마라.
|
||||
- HDF5 writer를 구현하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
@@ -0,0 +1,118 @@
|
||||
# Step 7: results-containers
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/src/fesa/analysis/analysis_state.hpp`
|
||||
- `/src/fesa/analysis/analysis.hpp`
|
||||
- `/tests/unit/analysis_flow_template_test.cpp`
|
||||
|
||||
이전 step에서 만들어진 AnalysisState와 Analysis flow를 꼼꼼히 읽고, 결과 container가 HDF5 API에 직접 의존하지 않도록 유지하라.
|
||||
|
||||
## 작업
|
||||
|
||||
HDF5 writer 구현 전 단계의 results data model skeleton을 `/src/fesa/results/`에 구현한다.
|
||||
|
||||
필수 파일:
|
||||
|
||||
- `/src/fesa/results/results.hpp`
|
||||
- `/tests/unit/results_containers_test.cpp`
|
||||
|
||||
필수 interface:
|
||||
|
||||
```cpp
|
||||
namespace fesa::results {
|
||||
|
||||
enum class FieldLocation { nodal, element, integration_point };
|
||||
|
||||
struct FieldOutput {
|
||||
std::string name;
|
||||
FieldLocation location;
|
||||
std::vector<std::string> components;
|
||||
std::vector<int> entity_ids;
|
||||
std::vector<double> values;
|
||||
};
|
||||
|
||||
struct HistoryOutput {
|
||||
std::string name;
|
||||
std::vector<double> time;
|
||||
std::vector<double> values;
|
||||
};
|
||||
|
||||
class ResultFrame {
|
||||
public:
|
||||
ResultFrame(int frame_id, double time);
|
||||
int frame_id() const;
|
||||
double time() const;
|
||||
void add_field_output(FieldOutput output);
|
||||
void add_history_output(HistoryOutput output);
|
||||
const std::vector<FieldOutput>& field_outputs() const;
|
||||
const std::vector<HistoryOutput>& history_outputs() const;
|
||||
};
|
||||
|
||||
class ResultStep {
|
||||
public:
|
||||
explicit ResultStep(std::string name);
|
||||
const std::string& name() const;
|
||||
ResultFrame& add_frame(int frame_id, double time);
|
||||
const std::vector<ResultFrame>& frames() const;
|
||||
};
|
||||
|
||||
} // namespace fesa::results
|
||||
```
|
||||
|
||||
구현 규칙:
|
||||
|
||||
- Result hierarchy는 `ResultStep -> ResultFrame -> FieldOutput/HistoryOutput` 구조를 따른다.
|
||||
- Field output은 row identity를 위해 `entity_ids`를 보존한다.
|
||||
- Field output values layout은 skeleton 단계에서 row-major flat vector로 둔다.
|
||||
- HDF5 file/dataset, schema writer, CSV export는 구현하지 않는다.
|
||||
- validation은 크기 consistency만 최소로 확인한다:
|
||||
- `components`가 비어 있으면 `std::invalid_argument`
|
||||
- `entity_ids.size() * components.size() == values.size()`가 아니면 `std::invalid_argument`
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/unit/results_containers_test.cpp`
|
||||
- ResultStep이 이름과 frame 목록을 보존한다.
|
||||
- ResultFrame이 frame id/time을 보존한다.
|
||||
- nodal displacement FieldOutput을 추가하면 components/entity ids/values가 보존된다.
|
||||
- invalid FieldOutput shape가 `std::invalid_argument`를 던진다.
|
||||
- HistoryOutput이 time/value series를 보존한다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. 테스트 파일을 먼저 작성한다.
|
||||
2. targeted CTest를 실행해 missing `results.hpp`로 실패함을 확인한다.
|
||||
3. 그 뒤 production header를 작성한다.
|
||||
|
||||
## 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 results_containers_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- Results hierarchy가 ARCHITECTURE.md와 일치하는가?
|
||||
- HDF5 API가 results container에 직접 노출되지 않는가?
|
||||
- reference comparison을 위한 entity row identity가 보존되는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 7을 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "ResultStep, ResultFrame, FieldOutput, and HistoryOutput containers added"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- HDF5 writer/reader를 구현하지 마라.
|
||||
- deterministic CSV export를 구현하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
@@ -0,0 +1,89 @@
|
||||
# Step 8: solver-skeleton-integration-report
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- `/src/fesa/model/domain.hpp`
|
||||
- `/src/fesa/analysis/linear_static_analysis.hpp`
|
||||
- `/src/fesa/results/results.hpp`
|
||||
- `/tests/unit/results_containers_test.cpp`
|
||||
- `/docs/build-test-reports/README.md`
|
||||
|
||||
이전 step에서 만들어진 전체 skeleton API를 꼼꼼히 읽고, integration smoke test가 실제 solver 수치해석 완료를 주장하지 않도록 범위를 제한하라.
|
||||
|
||||
## 작업
|
||||
|
||||
solver skeleton의 주요 class가 함께 컴파일되고 기본 data flow를 구성할 수 있음을 통합 테스트와 build/test report로 남긴다.
|
||||
|
||||
필수 파일:
|
||||
|
||||
- `/tests/integration/solver_core_skeleton_integration_test.cpp`
|
||||
- `/docs/build-test-reports/solver-core-skeleton.md`
|
||||
|
||||
통합 테스트 요구사항:
|
||||
|
||||
- Domain에 두 개 Node, 하나 Element, Material, Property, AnalysisStep을 구성한다.
|
||||
- AnalysisStep에는 최소 하나의 BoundaryCondition과 Load를 추가한다.
|
||||
- `LinearStaticAnalysis`를 생성하고 `run()`을 호출한다.
|
||||
- `analysis_model()`과 `state()`가 생성되는지 확인한다.
|
||||
- `ResultStep`과 `ResultFrame`을 만들고 nodal displacement `FieldOutput`을 추가한다.
|
||||
- 이 테스트는 stiffness assembly, linear solve, HDF5 write, reference comparison을 검증하지 않는다.
|
||||
|
||||
보고서 요구사항:
|
||||
|
||||
- `docs/build-test-reports/solver-core-skeleton.md`에 아래 항목을 기록한다.
|
||||
- phase: solver-core-skeleton
|
||||
- scope: C++ skeleton classes only
|
||||
- commands run
|
||||
- exit code summary
|
||||
- CTest tests added
|
||||
- known limitations
|
||||
- known limitations에는 최소한 다음을 명시한다:
|
||||
- 실제 element stiffness/residual/tangent 계산 없음
|
||||
- 실제 linear solver backend 없음
|
||||
- HDF5 writer 없음
|
||||
- Abaqus parser 및 reference comparison 없음
|
||||
|
||||
## Tests To Write First
|
||||
|
||||
- `/tests/integration/solver_core_skeleton_integration_test.cpp`
|
||||
- 전체 skeleton header를 include한다.
|
||||
- 위 통합 테스트 요구사항을 `main()` assertion으로 검증한다.
|
||||
|
||||
RED 확인:
|
||||
|
||||
1. 통합 테스트 파일을 먼저 작성한다.
|
||||
2. targeted CTest를 실행해 아직 integration test registration 또는 required API 문제로 실패하면 실패 내용을 확인한다.
|
||||
3. Step 0의 CMake glob이 `tests/integration/*_test.cpp`를 자동 등록해야 한다. 등록되지 않는다면 현재 step에서 CMake 파일을 수정하지 말고 `blocked`로 표시하고 사용자에게 allowed_paths 확장을 요청한다.
|
||||
|
||||
## 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 solver_core_skeleton_integration_test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- Domain -> AnalysisModel -> DofManager/AnalysisState -> Results data flow가 컴파일되는가?
|
||||
- 통합 테스트가 skeleton 범위를 넘어 수치 정확성을 주장하지 않는가?
|
||||
- build/test report가 실제 실행 evidence와 known limitations를 기록하는가?
|
||||
3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 8을 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "Solver skeleton integration test and build/test report added"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 실제 FEM stiffness, residual, tangent, material law 계산을 구현하지 마라.
|
||||
- Abaqus reference artifact를 생성, 수정, 복원하지 마라.
|
||||
- HDF5 writer를 구현하지 마라.
|
||||
- JavaScript/TypeScript/npm fallback을 추가하지 마라.
|
||||
Reference in New Issue
Block a user