Domain Module

Overview

At a Glance

Purpose:

Typed domain objects representing core simulation concepts

Location:

fusion/domain/

Key Files:

config.py, request.py, lightpath.py, network_state.py, results.py

Depends On:

numpy, networkx

Used By:

fusion.core, fusion.pipelines, fusion.interfaces, fusion.rl

The domain module defines the shared vocabulary for the entire FUSION simulator. It contains typed, validated dataclasses that represent fundamental simulation concepts: what a request looks like, how lightpaths track capacity, what network state contains, and what results pipelines return.

Why this module matters:

  • Every other module imports from domain to work with these core concepts

  • Type safety catches bugs at development time, not runtime

  • Immutable result objects prevent accidental state corruption

  • Legacy conversion methods enable gradual migration from dict-based code

In This Section

When you work here:

  • Adding new fields to existing domain objects

  • Creating new result types for pipeline stages

  • Understanding how data flows through the simulator

  • Debugging request processing or allocation issues

How Domain Differs from Configs and CLI

New developers often confuse these three modules. Here’s how they relate:

+------------------+     +------------------+     +------------------+
|      CLI         |     |     Configs      |     |     Domain       |
|   (Entry Point)  |     |  (File Parsing)  |     | (Runtime Objects)|
+------------------+     +------------------+     +------------------+
       |                        |                        ^
       | User runs              | INI files              | Used throughout
       | fusion-sim             | parsed into            | simulation
       v                        v                        |
+------------------+     +------------------+            |
| Parse arguments  |---->| Merge with INI   |            |
| --network NSFNet |     | configuration    |            |
+------------------+     +------------------+            |
                                |                        |
                                v                        |
                         +------------------+            |
                         | Create engine_   |----------->|
                         | props dict       |            |
                         +------------------+            |
                                |                        |
                                v                        |
                         +------------------+     +------+-------+
                         | SimulationConfig |---->| Orchestrator |
                         | (domain object)  |     | uses config  |
                         +------------------+     +--------------+
Module Comparison

Aspect

CLI (fusion/cli/)

Configs (fusion/configs/)

Domain (fusion/domain/)

Purpose

Entry point, argument parsing

INI file parsing, validation

Runtime data structures

When Used

At startup only

At startup only

Throughout simulation

Key Classes

run_sim.py CLI

cli_to_config.py

SimulationConfig, Request, etc.

Data Flow

Args -> engine_props

INI -> engine_props

engine_props -> typed objects

Mutability

N/A (procedural)

Mutable dicts

Immutable dataclasses

In short:

  • CLI: How users invoke the simulator (command-line arguments)

  • Configs: How configuration files are parsed (INI templates, validation)

  • Domain: The typed objects used during simulation (requests, lightpaths, results)

Key Concepts

Understanding these concepts is essential before working with domain objects:

Immutability

Most domain objects use @dataclass(frozen=True) making them immutable after creation. This prevents bugs where objects are accidentally modified and enables safe sharing between components.

Validation on Creation

All domain objects validate their invariants in __post_init__. Invalid data fails fast with clear error messages rather than causing subtle bugs later.

Legacy Conversion

Domain objects provide from_legacy_dict() and to_legacy_dict() methods for interoperability with older code that uses plain dictionaries. These will be removed in v6.1.0 once migration is complete.

Result Objects

Pipeline stages return immutable result objects (RouteResult, SpectrumResult, etc.) that capture the outcome. The success field is the single source of truth for whether an operation succeeded.

State vs. Configuration

SimulationConfig is immutable configuration set once at startup. NetworkState is mutable state that changes as requests are processed. Request and Lightpath track individual connection lifecycle.

Architecture

Module Structure

fusion/domain/
|-- __init__.py          # Public API exports
|-- config.py            # SimulationConfig - immutable configuration
|-- request.py           # Request, RequestType, RequestStatus, BlockReason
|-- lightpath.py         # Lightpath - optical path with capacity tracking
|-- network_state.py     # NetworkState, LinkSpectrum - mutable network state
|-- results.py           # Pipeline result objects (frozen dataclasses)
|-- README.md            # Module documentation
|-- TODO.md              # Development roadmap
`-- tests/               # Unit tests
    |-- test_config.py
    |-- test_request.py
    |-- test_network_state.py
    `-- test_results.py

Object Relationship Diagram

The following diagram shows how domain objects relate to each other:

+-------------------+
| SimulationConfig  |  (immutable, created once at startup)
+-------------------+
         |
         | provides parameters for
         v
+-------------------+         +-------------------+
|   NetworkState    |<------->|     Lightpath     |
| (mutable state)   |  owns   | (capacity mgmt)   |
+-------------------+         +-------------------+
         ^                            ^
         |                            |
         | queries/updates            | creates/uses
         |                            |
+-------------------+         +-------------------+
|      Request      |-------->|  AllocationResult |
| (service request) | yields  | (pipeline output) |
+-------------------+         +-------------------+
                                      |
                                      | contains
                                      v
                              +-------------------+
                              | RouteResult       |
                              | SpectrumResult    |
                              | GroomingResult    |
                              | SNRResult         |
                              | SlicingResult     |
                              | ProtectionResult  |
                              +-------------------+

Data Flow Summary

  1. Configuration: SimulationConfig is created from engine_props at startup

  2. State Initialization: NetworkState is created with topology and empty spectrum

  3. Request Arrival: Request objects are generated with source, destination, bandwidth

  4. Pipeline Processing: Orchestrator processes request through pipeline stages

  5. Result Objects: Each stage returns an immutable result (RouteResult, etc.)

  6. State Updates: NetworkState is updated with new lightpaths and spectrum allocations

  7. Final Result: AllocationResult captures whether request was served or blocked

Components

config.py

Purpose:

Immutable simulation configuration

Key Classes:

SimulationConfig

Replaces the mutable engine_props dictionary with a typed, validated, frozen dataclass. Provides type safety, IDE autocompletion, and clear defaults.

from fusion.domain import SimulationConfig

# Create from legacy dict
config = SimulationConfig.from_engine_props(engine_props)

# Access with type safety
print(config.network_name)      # IDE knows this is str
print(config.cores_per_link)    # IDE knows this is int

request.py

Purpose:

Network service request with lifecycle tracking

Key Classes:

Request, RequestType, RequestStatus, BlockReason, ProtectionStatus

Represents a connection request from source to destination with bandwidth requirement. Tracks lifecycle state from PENDING through ALLOCATED or BLOCKED.

from fusion.domain import Request, RequestType, RequestStatus

request = Request(
    request_id=1,
    source="0",
    destination="5",
    bandwidth_gbps=100,
    arrive=0.5,
    depart=1.5,
)

# Lifecycle tracking
print(request.status)  # RequestStatus.PENDING

lightpath.py

Purpose:

Allocated optical path with capacity management

Key Classes:

Lightpath

Represents an established optical connection with spectrum assignment. Supports traffic grooming by tracking capacity and per-request bandwidth allocations.

from fusion.domain import Lightpath

lp = Lightpath(
    lightpath_id=1,
    path=["0", "2", "5"],
    start_slot=10,
    end_slot=18,
    core=0,
    band="c",
    modulation="QPSK",
    total_bandwidth_gbps=100,
    remaining_bandwidth_gbps=100,
)

# Capacity management
if lp.can_accommodate(50):
    lp.allocate_bandwidth(request_id=42, bandwidth_gbps=50)

network_state.py

Purpose:

Mutable network state container

Key Classes:

NetworkState, LinkSpectrum

The single source of truth for network state during simulation. Owns all LinkSpectrum objects and the lightpath registry.

from fusion.domain import NetworkState

# Check spectrum availability
if state.is_spectrum_available(path, start, end, core, band):
    lightpath_id = state.create_lightpath(...)

results.py

Purpose:

Immutable pipeline stage outputs

Key Classes:

RouteResult, SpectrumResult, GroomingResult, SlicingResult, SNRResult, ProtectionResult, AllocationResult

Each pipeline stage returns a frozen result object. AllocationResult is the final output indicating success or failure with detailed tracking.

from fusion.domain import AllocationResult, BlockReason

# Successful allocation
result = AllocationResult(
    success=True,
    lightpaths_created=(1,),
    total_bandwidth_allocated_gbps=100,
)

# Blocked request
result = AllocationResult(
    success=False,
    block_reason=BlockReason.CONGESTION,
)

Dependencies

This Module Depends On

  • numpy - Spectrum array storage in LinkSpectrum

  • networkx - Topology queries in NetworkState

  • Standard Library: dataclasses, enum, typing

Modules That Depend On This

  • fusion.core - SimulationEngine, SDNOrchestrator, SDNController

  • fusion.pipelines - All pipeline implementations

  • fusion.interfaces - Pipeline protocols reference domain types

  • fusion.rl - RL adapters work with Request and NetworkState

  • fusion.modules - Routing, spectrum, SNR modules

Testing

Test Location:

fusion/domain/tests/

Run Tests:

pytest fusion/domain/tests/ -v

Coverage Target:

80%+

Test files:

  • test_config.py - SimulationConfig creation and conversion

  • test_request.py - Request lifecycle and enums

  • test_network_state.py - NetworkState and LinkSpectrum operations

  • test_results.py - All result dataclasses

Adding new tests:

def test_lightpath_when_capacity_exceeded_then_returns_false():
    """Test that allocate_bandwidth returns False when insufficient capacity."""
    # Arrange
    lp = Lightpath(
        lightpath_id=1,
        path=["0", "5"],
        start_slot=0,
        end_slot=8,
        core=0,
        band="c",
        modulation="QPSK",
        total_bandwidth_gbps=100,
        remaining_bandwidth_gbps=50,
    )

    # Act
    result = lp.allocate_bandwidth(request_id=1, bandwidth_gbps=100)

    # Assert
    assert result is False
    assert lp.remaining_bandwidth_gbps == 50  # Unchanged