213 lines
9.2 KiB
Markdown
213 lines
9.2 KiB
Markdown
# Story 3.1: System Graph Structure
|
||
|
||
Status: done
|
||
|
||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||
|
||
## Story
|
||
|
||
As a system modeler,
|
||
I want edges to index the solver's state vector,
|
||
so that P and h unknowns assemble into the Jacobian.
|
||
|
||
## Acceptance Criteria
|
||
|
||
1. **Edges as State Indices** (AC: #1)
|
||
- [x] Given components with ports, when adding to System graph, edges serve as indices into solver's state vector
|
||
- [x] Each flow edge represents P and h unknowns (2 indices per edge)
|
||
- [x] State vector layout: `[P_edge0, h_edge0, P_edge1, h_edge1, ...]` or equivalent documented layout
|
||
|
||
2. **Graph Traversal for Jacobian** (AC: #2)
|
||
- [x] Solver traverses graph to assemble Jacobian
|
||
- [x] Components receive state slice indices via edge→state mapping
|
||
- [x] JacobianBuilder entries use correct (row, col) from graph topology
|
||
|
||
3. **Cycle Detection and Validation** (AC: #3)
|
||
- [x] Cycles are detected (refrigeration circuits form cycles - expected)
|
||
- [x] Topology is validated (no dangling nodes, consistent flow direction)
|
||
- [x] Clear error when topology is invalid
|
||
|
||
## Tasks / Subtasks
|
||
|
||
- [x] Create solver crate (AC: #1)
|
||
- [x] Add `crates/solver` to workspace Cargo.toml
|
||
- [x] Create Cargo.toml with deps: entropyk-core, entropyk-components, petgraph, thiserror
|
||
- [x] Create lib.rs with module structure (system, graph)
|
||
- [x] Implement System graph structure (AC: #1)
|
||
- [x] Use petgraph `Graph` with nodes = components, edges = flow connections
|
||
- [x] Node weight: `Box<dyn Component>` or component handle
|
||
- [x] Edge weight: `FlowEdge { state_index_p: usize, state_index_h: usize }` or similar
|
||
- [x] Build edge→state index mapping when graph is finalized
|
||
- [x] State vector indexing (AC: #1)
|
||
- [x] `state_vector_len() -> usize` = 2 * edge_count (P and h per edge)
|
||
- [x] `edge_state_indices(edge_id) -> (usize, usize)` for P and h columns
|
||
- [x] Document layout in rustdoc
|
||
- [x] Graph traversal for Jacobian assembly (AC: #2)
|
||
- [x] `traverse_for_jacobian()` or iterator over (component, edge_indices)
|
||
- [x] Components receive state and write to JacobianBuilder with correct col indices
|
||
- [x] Integrate with existing `Component::jacobian_entries(state, jacobian)`
|
||
- [x] Cycle detection and validation (AC: #3)
|
||
- [x] Use `petgraph::algo::is_cyclic_directed` for cycle detection
|
||
- [x] Validate: refrigeration cycles are expected; document semantics
|
||
- [x] Validate: no isolated nodes, all ports connected
|
||
- [x] Return `Result<(), TopologyError>` on invalid topology
|
||
- [x] Add tests
|
||
- [x] Test: simple cycle (4 nodes, 4 edges) builds correctly
|
||
- [x] Test: state vector length = 2 * edge_count
|
||
- [x] Test: edge indices are contiguous and unique
|
||
- [x] Test: cycle detection identifies cyclic graph
|
||
- [x] Test: invalid topology (dangling node) returns error
|
||
|
||
## Dev Notes
|
||
|
||
### Epic Context
|
||
|
||
**Epic 3: System Topology (Graph)** - Enable component assembly via Ports and manage multi-circuits with thermal coupling. FR9–FR13 map to `crates/solver/src/system.rs`.
|
||
|
||
**Story Dependencies:**
|
||
- Epic 1 (Component trait, Ports, State machine) - in progress, 1-8 in review
|
||
- No dependency on Epic 2 (fluids) for this story - topology only
|
||
|
||
### Architecture Context
|
||
|
||
**System Topology (FR9–FR13):**
|
||
- Location: `crates/solver/src/system.rs`
|
||
- petgraph for graph topology representation (architecture line 89, 147)
|
||
- `solver → core + fluids + components` (components required)
|
||
- Jacobian entries assembled by solver (architecture line 760)
|
||
|
||
**Workspace:**
|
||
- `crates/solver` is currently commented out in root Cargo.toml: `# "crates/solver", # Will be added in future stories`
|
||
- This story CREATES the solver crate
|
||
|
||
**Component Trait (from components):**
|
||
```rust
|
||
pub trait Component {
|
||
fn compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector) -> Result<(), ComponentError>;
|
||
fn jacobian_entries(&self, state: &SystemState, jacobian: &mut JacobianBuilder) -> Result<(), ComponentError>;
|
||
fn n_equations(&self) -> usize;
|
||
fn get_ports(&self) -> &[ConnectedPort];
|
||
}
|
||
```
|
||
|
||
- `SystemState = Vec<f64>` - state vector
|
||
- `JacobianBuilder` has `add_entry(row, col, value)` - col = state index
|
||
- Components need to know which state indices map to their ports
|
||
|
||
### Technical Requirements
|
||
|
||
**Graph Model:**
|
||
- **Nodes**: Components (or component handles). Each node = one `dyn Component`.
|
||
- **Edges**: Flow connections between component ports. Direction = flow direction (e.g., compressor outlet → condenser inlet).
|
||
- **Edge weight**: Must store `(state_index_p, state_index_h)` so solver can map state vector to edges.
|
||
|
||
**State Vector Layout:**
|
||
- Option A: `[P_0, h_0, P_1, h_1, ...]` - 2 per edge, edge order from graph
|
||
- Option B: Separate P and h vectors - less common for Newton
|
||
- Recommended: Option A for simplicity; document in `System::state_layout()`
|
||
|
||
**Cycle Semantics:**
|
||
- Refrigeration circuits ARE cycles (compressor → condenser → valve → evaporator → compressor)
|
||
- `is_cyclic_directed` returns true for valid refrigeration topology
|
||
- "Validated" = topology is well-formed (connected, no illegal configs), not "no cycles"
|
||
- Port `ConnectionError::CycleDetected` may refer to a different concept (e.g., connection graph cycles during build) - clarify in implementation
|
||
|
||
**petgraph API:**
|
||
- `Graph<N, E, Ty, Ix>` - use `Directed` for flow direction
|
||
- `add_node(weight)`, `add_edge(a, b, weight)`
|
||
- `petgraph::algo::is_cyclic_directed(&graph) -> bool`
|
||
- `NodeIndex`, `EdgeIndex` for stable references
|
||
|
||
### Library/Framework Requirements
|
||
|
||
**petgraph:**
|
||
- Version: 0.6.x (latest stable, check crates.io)
|
||
- Docs: https://docs.rs/petgraph/
|
||
- `use petgraph::graph::Graph` and `petgraph::algo::is_cyclic_directed`
|
||
|
||
**Dependencies (solver Cargo.toml):**
|
||
```toml
|
||
[dependencies]
|
||
entropyk-core = { path = "../core" }
|
||
entropyk-components = { path = "../components" }
|
||
petgraph = "0.6"
|
||
thiserror = "1.0"
|
||
```
|
||
|
||
### File Structure Requirements
|
||
|
||
**New files:**
|
||
- `crates/solver/Cargo.toml`
|
||
- `crates/solver/src/lib.rs` - re-exports
|
||
- `crates/solver/src/system.rs` - System struct, Graph, state indexing
|
||
- `crates/solver/src/graph.rs` (optional) - graph building helpers
|
||
- `crates/solver/src/error.rs` (optional) - TopologyError
|
||
|
||
**Modified files:**
|
||
- Root `Cargo.toml` - uncomment `crates/solver` in workspace members
|
||
|
||
### Testing Requirements
|
||
|
||
**Required Tests:**
|
||
- `test_simple_cycle_builds` - 4 components, 4 edges, graph builds
|
||
- `test_state_vector_length` - len = 2 * edge_count
|
||
- `test_edge_indices_contiguous` - indices 0..2n for n edges
|
||
- `test_cycle_detected` - cyclic graph, is_cyclic_directed true
|
||
- `test_dangling_node_error` - node with no edges returns TopologyError
|
||
- `test_traverse_components` - traversal yields all components with correct edge indices
|
||
|
||
**Integration:**
|
||
- Build a minimal cycle (e.g., 2 components, 2 edges) and verify state layout
|
||
|
||
### Project Structure Notes
|
||
|
||
**Alignment:**
|
||
- Architecture specifies `crates/solver/src/system.rs` for FR9–FR13 - matches
|
||
- petgraph from architecture - matches
|
||
- Component trait from Epic 1 - get_ports() provides port info for graph building
|
||
|
||
**Note:** Story 3.2 (Port Compatibility Validation) will add connection-time checks. This story focuses on graph structure and state indexing once components are added.
|
||
|
||
### References
|
||
|
||
- **Epic 3 Story 3.1:** [Source: planning-artifacts/epics.md#Story 3.1]
|
||
- **Architecture System Topology:** [Source: planning-artifacts/architecture.md - FR9-FR13, system.rs]
|
||
- **Architecture petgraph:** [Source: planning-artifacts/architecture.md - line 89, 147]
|
||
- **Component trait:** [Source: crates/components/src/lib.rs]
|
||
- **Port types:** [Source: crates/components/src/port.rs]
|
||
- **JacobianBuilder:** [Source: crates/components/src/lib.rs - JacobianBuilder]
|
||
- **petgraph is_cyclic_directed:** https://docs.rs/petgraph/latest/petgraph/algo/fn.is_cyclic_directed.html
|
||
|
||
## Change Log
|
||
|
||
- 2026-02-15: Story 3.1 implementation complete. Created crates/solver with System graph (petgraph), FlowEdge, state indexing, traverse_for_jacobian, cycle detection, TopologyError. All ACs satisfied, 7 tests pass.
|
||
- 2026-02-15: Code review fixes. Added state_layout(), compute_residuals bounds check, robust test_dangling_node_error, TopologyError #[allow(dead_code)], removed unused deps (entropyk-core, approx), added test_state_layout_integration and test_compute_residuals_bounds_check. 9 tests pass.
|
||
|
||
## Dev Agent Record
|
||
|
||
### Agent Model Used
|
||
|
||
{{agent_model_name_version}}
|
||
|
||
### Debug Log References
|
||
|
||
### Completion Notes List
|
||
|
||
- Created `crates/solver` with petgraph-based System graph
|
||
- FlowEdge stores (state_index_p, state_index_h) per edge
|
||
- State vector layout: [P_0, h_0, P_1, h_1, ...] documented in rustdoc and state_layout()
|
||
- traverse_for_jacobian() yields (component, edge_indices) for Jacobian assembly
|
||
- TopologyError::IsolatedNode for dangling nodes; refrigeration cycles expected (is_cyclic)
|
||
- All 9 tests pass (core, components, solver); no fluids dependency
|
||
- Code review: state_layout(), bounds check in compute_residuals, robust dangling-node test, integration test
|
||
|
||
### File List
|
||
|
||
- crates/solver/Cargo.toml (new)
|
||
- crates/solver/src/lib.rs (new)
|
||
- crates/solver/src/error.rs (new)
|
||
- crates/solver/src/graph.rs (new)
|
||
- crates/solver/src/system.rs (new)
|
||
- Cargo.toml (modified: uncommented crates/solver in workspace)
|
||
- _bmad-output/implementation-artifacts/sprint-status.yaml (modified: 3-1 in-progress → review)
|