Skip to content

← Back to MiddleLevelDataStorageInterface documentation

MiddleLevelDataStorageInterface - Source Code

File: Distributed_Design_Optimizer/middlelevel/MiddleLevelDataStorageInterface.py

# Copyright (C) The DistributedDesignOptimizer Contributors
# Licensed under the GNU General Public License v3.0. See LICENSE file for details.
"""Interface module for middle-level data storage.

This module defines the abstract interface for middle-level data storage
in distributed optimization.
"""

from abc import ABC, abstractmethod
from typing import List, Union, Any
from Distributed_Design_Optimizer.middlelevel import MiddleLevelDataStorageProxy, MiddleLevelCouplingInterface
from Distributed_Design_Optimizer.subsystem.couplingparameters import CouplingParametersInterface


class MiddleLevelDataStorageInterface(ABC):
    """Abstract interface for middle level data storage.

    Defines the contract for classes that store and manage coupling data
    between subsystems in distributed optimization.
    """

    @abstractmethod
    def set_Coupling(self, coupling: CouplingParametersInterface) -> None:
        """Store coupling variables from a subsystem.

        Args:
            coupling: Coupling parameters to store.
        """

    @abstractmethod
    def get_StoredCoupling(self, id: str) -> MiddleLevelCouplingInterface | None:
        """Return the stored coupling data for a given subsystem ID (under lock).

        NOTE ON ASYMMETRY WITH set_Coupling:
            set_Coupling(coupling) takes a CouplingParametersInterface and
            internally calls coupling.CopyToMiddleLevelCoupling(slot) to push
            data INTO the storage. The mirror operation — pulling data OUT —
            cannot use the same pattern (i.e. coupling.CopyFromMiddleLevelCoupling
            called inside this method) because of multiprocessing proxies:
            when called through a BaseProxy, arguments are pickled to the
            manager process. Any mutation of the argument happens on the
            deserialized copy in the manager and is discarded — the caller's
            original object is never modified. Only return values are sent
            back across process boundaries. Therefore this method RETURNS
            the stored data so the caller can apply it locally.

        Args:
            id: Identifier of the neighbor subsystem whose data to retrieve.

        Returns:
            The stored MiddleLevelCouplingInterface for the given subsystem,
            or None if the coupling data for that slot is None.
        """

    @abstractmethod
    def get_ID(self) -> List[str]:
        """Get the identifiers of the connected subsystems.

        Returns:
            List containing parent and child subsystem identifiers.
        """

    @abstractmethod
    def get_CouplingData(self) -> List[MiddleLevelCouplingInterface]:
        """Get all coupling data stored in this middle level.

        Returns:
            List of middle level coupling objects.
        """        

    @abstractmethod
    def createManagedMiddleLevel(self, manager: Any, lock: Any) -> Any:
        """Create a managed proxy copy of this middle level for multiprocessing.

        This method creates a new managed instance of this middle level using the
        CustomManager, copies the current state to it, and returns it.
        For concrete implementation see e.g. MiddleLevelDataStorageALC, MiddleLevelDataStorageLC, ...

        Args:
            manager: CustomManager instance for creating managed objects.
            lock: Multiprocessing lock for the managed object.

        Returns:
            Managed middle level proxy object with state copied from this instance.
        """

    @abstractmethod
    def update_state(self, other_storage: Union['MiddleLevelDataStorageInterface' , MiddleLevelDataStorageProxy]) -> None:
        """Update the state of this data storage from another instance.

        This method is necessary for multiprocessing: when subsystems are executed
        in parallel via Parallel.py using multiprocessing.Pool, each process
        receives a copy of the data. After execution, the original objects must be
        updated with results from the executed copies. This method updates the
        numerical information stored in the class's attributes without changing
        the original memory address location, preserving object identity.

        Args:
            other_storage: Source instance to copy state from. Can be either a
                MiddleLevelDataStorageInterface or MiddleLevelDataStorageProxy.
        """