7.2 KiB
Story 3.6: Hierarchical Subsystems (MacroComponents)
Status: done
Story
As a system designer, I want to encapsulate a complete system (e.g., a Chiller with compressor, condenser, valve, evaporator) into a single reusable block, so that I can compose larger models (like buildings or parallel chiller plants) using these blocks, just like in Modelica.
Acceptance Criteria
-
MacroComponent Trait Implementation (AC: #1)
- Given a fully defined
Systemwith internal components and connections - When I wrap it in a
MacroComponent - Then this
MacroComponentimplements theComponenttrait - And the global solver treats it exactly like a basic Component
- Given a fully defined
-
External Port Mapping (AC: #2)
- Given a
MacroComponentwrapping an internalSystem - When I want to expose specific internal ports (e.g., Evaporator Water In/Out, Condenser Water In/Out)
- Then I can map these to the
MacroComponent's external ports - And external connections to these mapped ports correctly route fluid states to the internal components
- Given a
-
Residual and Jacobian Delegation (AC: #3)
- Given a system solver calling
compute_residualsorjacobian_entrieson aMacroComponent - When the
MacroComponentexecutes these methods - Then it delegates or flattens the computation down to the nested internal
System - And all equations are solved simultaneously globally, avoiding nested numerical solver delays
- Given a system solver calling
-
Serialization and Persistence (AC: #4)
- Given a
Systemthat containsMacroComponents - When serializing the system to JSON
Then the internal topology of the(Moved to Future Scope: Serialization not natively supported yet)MacroComponentis preserved and can be deserialized perfectly
- Given a
Tasks / Subtasks
- Define
MacroComponentstruct incrates/solver/src/macro_component.rs(AC: #1)- Store internal
System - Store
port_mappingdictionary
- Store internal
- Implement
Componenttrait forMacroComponent(AC: #1, #3)- Implement
get_portsreturning mapped external ports - Implement
compute_residualsby delegating to internal components - Implement
jacobian_entriesby offsetting indices and delegating to internal components - Implement
n_equationsreturning the sum of internal equations
- Implement
- Implement external port bounding/mapping logic (AC: #2)
- Create API for
expose_port(internal_edge_pos, name, port)
- Create API for
- Integration Tests (AC: #1-#3)
- Test encapsulating a 4-component cycle into a single
MacroComponent - Test connecting two identical
MacroComponentchillers in parallel inside a higher-levelSystem - Assert global convergence works simultaneously.
- Test encapsulating a 4-component cycle into a single
- Create Action Item: Implement fully recursive
serdeserialization forMacroComponenttopologies.
Dev Notes
Epic Context
Epic 3: System Topology (Graph) — Enable component assembly via Ports and manage multi-circuits with thermal coupling. This story adds the capability to wrap topologies into sub-blocks.
FRs covered: FR48 (Hierarchical Subsystems).
Architecture Context
Technical Stack:
- Rust,
entropyk-components,entropyk-solver - Need to ensure that
SystemStateindices stay aligned when aMacroComponentis placed into a largerSystem.
Relevant Architecture Decisions:
- Wrapper Pattern:
MacroComponentimplementsComponent. - SystemState Flattening: The global solver dictates state vector indices. The
MacroComponentmust know how its internal node IDs map to the globalSystemStateindices, or it must reconstruct an internalSystemStateslice. - Zero-allocation: Port mapping and index offsetting must be pre-calculated during the topology finalization phase.
Code Structure
- Created
crates/solver/src/macro_component.rs(placed in solver crate to avoid circular dependency with components crate). - No structural adjustments to
crates/solver/src/system.rswere required —Systemalready supports complete embedding.
Developer Context
The main complexity of this story lies in index mapping. When the global System builds the solver state vector (P, h for each edge), the MacroComponent must correctly map its internal edges to the global state vector slices provided in compute_residuals(&self, state: &SystemState, ...).
An initialization step via set_global_state_offset() allows the parent system to inform the MacroComponent of its global state offsets before solving begins.
Dev Agent Record
Implementation Plan
- Created
MacroComponentstruct incrates/solver/src/macro_component.rs MacroComponentwraps a finalizedSystemand implements theComponenttrait- Residual computation delegates to the internal
System::compute_residuals()with a state slice extracted viaglobal_state_offset - Jacobian entries are collected from
System::assemble_jacobian()and column indices are offset byglobal_state_offset - External ports are exposed via
expose_port(internal_edge_pos, name, port)API - Port mappings stored as ordered
Vec<PortMapping>, providingget_ports()for the Component trait
Completion Notes
- ✅ AC #1: MacroComponent implements Component trait — verified via
test_macro_component_as_trait_objectandtest_macro_component_creation - ✅ AC #2: External port mapping works — verified via
test_expose_port,test_expose_multiple_ports,test_expose_port_out_of_range - ✅ AC #3: Residual and Jacobian delegation works — verified via
test_compute_residuals_delegation,test_compute_residuals_with_offset,test_jacobian_entries_delegation,test_jacobian_entries_with_offset - ✅ Integration: MacroComponent placed inside parent System — verified via
test_macro_component_in_parent_system - ℹ️ AC #4 (Serialization): Not implemented — requires serde derives on System, which is a separate concern. Story scope focused on runtime behavior.
- ℹ️ MacroComponent placed in
entropyk-solvercrate (notentropyk-components) to avoid circular dependency since it depends onSystem. - ⚠️ Code Review Fix (2026-02-21): Corrected
System::state_vector_lenandSystem::finalizeoffset computation bug where multiple MacroComponents were assigned overlapping indices. - ⚠️ Code Review Fix (2026-02-21): Added
internal_state_lento Component trait. - 12 unit tests pass, 130+ existing solver tests pass, 18 doc-tests pass, 0 regressions.
File List
(Note: During implementation, numerous other files outside the strict scope of this Story (e.g. inverse control, new flow components) were brought in or modified; those are tracked by their respective stories, but are present in the current git diff).
crates/solver/src/macro_component.rs(NEW)crates/solver/tests/macro_component_integration.rs(NEW)crates/components/src/lib.rs(MODIFIED — added internal_state_len)crates/solver/src/system.rs(MODIFIED — fixed global_state_offset logic)crates/solver/src/lib.rs(MODIFIED)
Change Log
- 2026-02-21: Code review conducted. Fixed critical state overlap bug in
system.rsfor multipleMacroComponents. AC #4 deferred to future scope. - 2026-02-20: Implemented MacroComponent struct with Component trait, port mapping API, 12 unit tests. All tests pass.