Entropyk/_bmad-output/implementation-artifacts/3-1-system-graph-structure.md

9.2 KiB
Raw Permalink Blame History

Story 3.1: System Graph Structure

Status: done

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)

    • Given components with ports, when adding to System graph, edges serve as indices into solver's state vector
    • Each flow edge represents P and h unknowns (2 indices per edge)
    • State vector layout: [P_edge0, h_edge0, P_edge1, h_edge1, ...] or equivalent documented layout
  2. Graph Traversal for Jacobian (AC: #2)

    • Solver traverses graph to assemble Jacobian
    • Components receive state slice indices via edge→state mapping
    • JacobianBuilder entries use correct (row, col) from graph topology
  3. Cycle Detection and Validation (AC: #3)

    • Cycles are detected (refrigeration circuits form cycles - expected)
    • Topology is validated (no dangling nodes, consistent flow direction)
    • Clear error when topology is invalid

Tasks / Subtasks

  • Create solver crate (AC: #1)
    • Add crates/solver to workspace Cargo.toml
    • Create Cargo.toml with deps: entropyk-core, entropyk-components, petgraph, thiserror
    • Create lib.rs with module structure (system, graph)
  • Implement System graph structure (AC: #1)
    • Use petgraph Graph with nodes = components, edges = flow connections
    • Node weight: Box<dyn Component> or component handle
    • Edge weight: FlowEdge { state_index_p: usize, state_index_h: usize } or similar
    • Build edge→state index mapping when graph is finalized
  • State vector indexing (AC: #1)
    • state_vector_len() -> usize = 2 * edge_count (P and h per edge)
    • edge_state_indices(edge_id) -> (usize, usize) for P and h columns
    • Document layout in rustdoc
  • Graph traversal for Jacobian assembly (AC: #2)
    • traverse_for_jacobian() or iterator over (component, edge_indices)
    • Components receive state and write to JacobianBuilder with correct col indices
    • Integrate with existing Component::jacobian_entries(state, jacobian)
  • Cycle detection and validation (AC: #3)
    • Use petgraph::algo::is_cyclic_directed for cycle detection
    • Validate: refrigeration cycles are expected; document semantics
    • Validate: no isolated nodes, all ports connected
    • Return Result<(), TopologyError> on invalid topology
  • Add tests
    • Test: simple cycle (4 nodes, 4 edges) builds correctly
    • Test: state vector length = 2 * edge_count
    • Test: edge indices are contiguous and unique
    • Test: cycle detection identifies cyclic graph
    • 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. FR9FR13 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 (FR9FR13):

  • 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):

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):

[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 FR9FR13 - 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)