Skip to content

← Back to subsystem

SubSystemBasis

Source: Distributed_Design_Optimizer/subsystem/SubSystemBasis.py

Subsystem basis module.

This module provides the base class for all subsystems in the distributed design optimization framework.

Classes

SubSystemBasis

Inherits from: SubSystemInterface

A SubSystemBasis object contains a general structure of methods to optimize the subsystem and to couple it to neighboring subsystems.

Methods

init(self, id: str, neighborid: List[str]) → None

Create a new instance of SubSystemBasis with neighbors/controller.

Args:

id: Identifier for the subsystem.
neighborid: Identifiers for the neighbors.

get_SUBSYSTEMID(self) → str

📐 Pseudocode: Determine neighbor subsystem ID (Line 4) → SubSystemBasis.CopyFromMiddleLevel

Get the identifier of the subsystem.

Returns:

The subsystem identifier.

get_NeighborId(self) → List[str]

Get the neighbor IDs.

Returns:

List of neighbor identifiers.

set_Name(self, name: str) → None

Set the use-case name used when saving the subsystem history.

Args:

name: The use-case name identifier.

get_Name(self) → str

Return the use-case name used when saving the subsystem history.

Returns:

The use-case name identifier.

set_HistoryFolderPath(self, history_folder_path: str) → None

Set the folder path used when saving the subsystem history.

Args:

history_folder_path: The absolute path to the historyfiles folder.

get_MiddleLevels(self) → List[MiddleLevelDataStorageInterface] | List[MiddleLevelDataStorageProxy]

📐 Pseudocode: Iterate over all MiddleLevelDataStorage instances (Line 2) → SubSystemBasis.CopyFromMiddleLevel; Iterate over all MiddleLevelDataStorage instances (Line 2) → SubSystemBasis.CopyToMiddleLevel

Return the middle-level data storage interfaces.

Returns:

List of middle-level data storage interfaces or proxies.

set_MiddleLevels(self, middlelevels: List[MiddleLevelDataStorageInterface] | List[MiddleLevelDataStorageProxy]) → None

Set the middle-level data storage interfaces.

Args:

middlelevels: List of middle-level data storage interfaces or proxies.

get_OuterLoop_Itr(self) → int

Return the current outer loop iteration number.

Returns:

The current outer loop iteration number.

get_InnerLoop_Itr(self) → int

Return the current inner loop iteration number.

Returns:

The current inner loop iteration number.

get_InnerLoop_Itr_Runtime(self) → float | None

Return the inner loop iteration runtime.

Returns:

The inner loop iteration runtime.

get_InnerLoop_Itr_NumberOfDesignVariableEvaluations(self) → int | None

Return the number of design variable evaluations.

Returns:

The number of design variable evaluations.

set_OuterLoop_Itr(self, outerloop_itr_in: int) → None

Set the outer loop iteration number.

Args:

outerloop_itr_in: The outer loop iteration number to set.

set_InnerLoop_Itr(self, innerloop_itr_in: int) → None

Set the inner loop iteration number.

Args:

innerloop_itr_in: The inner loop iteration number to set.

set_InnerLoop_Itr_Runtime(self, innerloop_itr_runtime_in: float) → None

Set the inner loop iteration runtime.

Args:

innerloop_itr_runtime_in: The inner loop iteration runtime to set.

set_InnerLoop_Itr_NumberOfDesignVariableEvaluations(self, innerloop_itr_numberofdesignvariableevaluations_in: int) → None

Set the number of design variable evaluations.

Args:

innerloop_itr_numberofdesignvariableevaluations_in: The number of design variable evaluations to set.

get_SubsystemHistory(self) → Deque[SubSystemHistoryEntry]

Return the subsystem history.

Returns:

The subsystem history.

CopyToMiddleLevel(self) → None

📐 Pseudocode: Copy coupling parameters to interface storage (subsystem) (Line 13), Controller: Copy coupling parameters to interface storage (Line 20), Copy coupling parameters to interface storage (after inner update) (Line 25), Copy coupling parameters to interface storage (outer loop prepare update) (Line 34), Copy coupling parameters to interface storage (outer loop update) (Line 39) → Unified Algorithmic Structure; Copy coupling parameters to interface storage (Line 11) → Augmented Lagrangian Coordination; Copy coupling data to interface storage (Line 9) → Consensus Augmented Lagrangian Coordination; Copy gradients, Hessians, coupling to controller (Lines 13, 14, 15), Controller: Copy Δd to interface storage (Line 21), Compute outerloop iterate d_hat and prepare hatted coupling quantities (Lines 27, 28, 29, 30, 31, 32) → Augmented Lagrangian Alternating Direction Inexact Newton; Copy multipliers and coupling data to interface storage (Line 10) → Sensitivity Based Distributed Programming; CopyToMiddleLevel entry point (Line 1) → SubSystemBasis.CopyToMiddleLevel

Copy coupling parameters to the shared middle-level data storage.

For each MiddleLevelDataStorage, determines which neighbor it connects to, finds the matching local CouplingParameter, and writes it to shared storage (lock-safe via set_Coupling).

CopyFromMiddleLevel(self) → None

📐 Pseudocode: Copy coupling parameters from interface storage (subsystem) (Line 9), Controller: Copy coupling parameters from interface storage (Line 16), Copy coupling parameters from interface storage (inner update) (Line 23), Copy coupling parameters from interface storage (outer loop prepare update) (Line 32), Copy coupling parameters from interface storage (outer loop update) (Line 37) → Unified Algorithmic Structure; Copy coupling parameters from interface storage (Line 9), Dual and Penalty Update loop (Lines 17, 18) → Augmented Lagrangian Coordination; Copy from interface, prepare auxiliary update (Lines 11, 12) → Consensus Augmented Lagrangian Coordination; Local: Copy Δd from controller, compute d_hat (Lines 8, 9, 10, 11), Controller: Copy sensitivity data from locals (Lines 17, 18, 19), Compute outerloop iterate d_hat and prepare hatted coupling quantities (Lines 27, 28, 29, 30, 31, 32) → Augmented Lagrangian Alternating Direction Inexact Newton; Copy multipliers and coupling data from interface storage (Line 6) → Sensitivity Based Distributed Programming; CopyFromMiddleLevel entry point (Line 1), Iterate over all MiddleLevelDataStorage instances (Line 2), Find matching CouplingParameters (Line 6) → SubSystemBasis.CopyFromMiddleLevel

Copy coupling data from the shared middle-level data storage.

For each MiddleLevelDataStorage, determines which neighbor it connects to, retrieves the stored coupling data, and applies it to the matching local CouplingParameter.

NOTE ON ASYMMETRY WITH CopyToMiddleLevel: CopyToMiddleLevel passes the CouplingParameter directly to ml.set_Coupling(cp), which internally calls cp.CopyToMiddleLevelCoupling(slot) — the data flows from the caller's object INTO shared storage via argument.

CopyFromMiddleLevel cannot mirror this pattern because of multiprocessing proxies: arguments sent through a BaseProxy are pickled to the manager process. Mutations of the argument inside the manager are applied to a deserialized copy and discarded — the caller's original is never modified. Only return values travel back across process boundaries.

Therefore we use get_StoredCoupling(neighbor_id) which RETURNS the data, then apply it locally via cp.CopyFromMiddleLevelCoupling(storedcoupling).

run_innerloop_job(self) → None

📐 Pseudocode: For every subsystem i ∈ M (Line 8), For Controller do (Line 15) → Unified Algorithmic Structure; Innerloop repeat (decentralized Primal Update) (Lines 7, 8) → Augmented Lagrangian Coordination; Innerloop decentralized Primal Update optimization (Lines 7, 8) → Consensus Augmented Lagrangian Coordination; Local subsystem NLP optimization (Line 12) → Augmented Lagrangian Alternating Direction Inexact Newton; Decentralized subsystem jobs in parallel (Line 5) → Sensitivity Based Distributed Programming

Execute the inner loop job: copy from middle level, optimize, copy back.

run_updateCouplingParameters_innerLoop_job(self) → None

📐 Pseudocode: Update relevant coupling parameters (inner loop) (Line 24) → Unified Algorithmic Structure

Update coupling parameters in the inner loop after optimization.

savesubsystemhistory(self) → None

Save the subsystem history to a dill file.

The use-case name is read from the subsystem's own state (set via set_Name by the Coordinator). The target folder is the use-case's historyfiles folder (stored as self._historyfolderpath via set_HistoryFolderPath by the Coordinator).

set_LowerBounds_Unscaled(self, lowerbounds: List[float]) → None

Set the lower bounds for the design variables (unscaled values).

Args:

lowerbounds: Lower bound values (unscaled).

get_LowerBounds_Unscaled(self) → List[float] | None

📐 Pseudocode: Retrieve design variable bounds and scaling (Line 3) → SubSystemBasis.run_IterativeOptimization

Get the lower bounds of the design variables.

Returns:

Lower bound values (unscaled).

set_UpperBounds_Unscaled(self, upperbounds: List[float]) → None

Set the upper bounds of the design variables (unscaled values).

Args:

upperbounds: Upper bound values (unscaled).

get_UpperBounds_Unscaled(self) → List[float] | None

📐 Pseudocode: Retrieve design variable bounds and scaling (Line 3) → SubSystemBasis.run_IterativeOptimization

Get the upper bounds of the design variables.

Returns:

Upper bound values (unscaled).

updateSubsystemfromOptimdata(self, optimdata: OptimDataBasis) → None

📐 Pseudocode: Post-process the optimization (subsystem) (Line 12), Controller: Post-process the optimization (Line 19) → Unified Algorithmic Structure; Update subsystem from optimization results (Line 24) → SubSystemBasis.run_IterativeOptimization

Update subsystem state from optimization data.

Updates the subsystem's design variables, objective values, and constraint values from the provided optimization data.

Args:

optimdata: Optimization data containing updated values.

updateOptimdatafromSubsystem(self) → None

Update self._optimdata from the current subsystem information.

Raises:

ValueError: If design variables are incompatible with optimdata.
ValueError: If design variables (unscaled) are incompatible with optimdata.
ValueError: If total objective value is incompatible with optimdata.
ValueError: If total equality constraint values are incompatible with optimdata.
ValueError: If total inequality constraint values are incompatible with optimdata.
ValueError: If coordination objective value is incompatible with optimdata.
ValueError: If coordination equality constraint values are incompatible with optimdata.
ValueError: If lower bounds are incompatible with optimdata.
ValueError: If upper bounds are incompatible with optimdata.

get_DesignVariables(self) → List[float] | None

Get the scaled design variables.

Returns:

Scaled design variable values in [0, 1].

get_DesignVariables_Unscaled(self) → List[float] | None

Get the unscaled design variables.

Returns:

Unscaled design variable values.

set_DesignVariables_Granularity(self, designvariables_granularity: List[float]) → None

Set the granularity of the design variables.

Args:

designvariables_granularity: Granularity values where 0.0 means continuous.

get_DesignVariables_Granularity(self) → List[float] | None

Get the granularity of the design variables.

Returns:

Granularity values where 0.0 means continuous.

set_TotalObjectiveValue(self, totalobjectivevalue: float | None) → None

Store the total objective value after the computation of the TotalObjective Class.

Args:

totalobjectivevalue: The total objective value to store.

get_TotalObjectiveValue(self) → float | None

Return the total objective value after the computation of the TotalObjective Class.

Returns:

The total objective value, or None if not yet computed.

set_CoordinationObjectiveValue(self, coordinationobjectivevalue: float | None) → None

Store the objective inconsistency results.

Args:

coordinationobjectivevalue: The coordination objective value to store.

get_CoordinationObjectiveValue(self) → float | None

Get the objective inconsistency function value.

Returns:

The coordination objective value, or None if not yet computed.

set_TotalConstraintEqValue(self, ceq: List[float] | None) → None

Store the equality constraint value after the computation of the TotalConstraint Class.

Args:

ceq: The equality constraint values to store.

get_TotalConstraintEqValue(self) → List[float] | None

Return the equality constraint value after the computation of the TotalConstraint Class.

Returns:

The equality constraint values, or None if not yet computed.

set_TotalConstraintIneqValue(self, cineq: List[float] | None) → None

Store the inequality constraint value after the computation of the TotalConstraint Class.

Args:

cineq: The inequality constraint values to store.

get_TotalConstraintIneqValue(self) → List[float] | None

Return the inequality constraint value after the computation of the TotalConstraint Class.

Returns:

The inequality constraint values, or None if not yet computed.

set_CoordinationEqualityConstraintValue(self, coordinationequalityconstraintvalue: List[float] | None) → None

Store the constraint inconsistency results.

Args:

coordinationequalityconstraintvalue: The coordination equality constraint values.

get_CoordinationEqualityConstraintValue(self) → List[float] | None

Get the constraint inconsistency function value.

Returns:

The coordination equality constraint values, or None if not yet computed.

set_CoordinationInequalityConstraintValue(self, coordinationinequalityconstraintvalue: List[float] | None) → None

Store the coordination inequality constraint results.

Args:

coordinationinequalityconstraintvalue: The coordination inequality constraint values.

get_CoordinationInequalityConstraintValue(self) → List[float] | None

Get the coordination inequality constraint function value.

Returns:

The coordination inequality constraint values, or None if not yet computed.

copy_Coupling_Past_outerloop_itr(self, outerloop_itr_before: int) → List[CouplingParametersInterface]

Copy the coupling from a past outer loop iteration.

Args:

outerloop_itr_before: Number of iterations to go back.

Returns:

List of coupling parameters from the specified past iteration.

copy_Coupling_Previous_outerloop_itr(self) → List[CouplingParametersInterface]

Copy the coupling from the previous outer loop iteration.

Returns:

List of coupling parameters from the previous iteration.

copy_TotalObjective_Previous_outer_or_innerloop_itr(self) → float | None

Copy the total objective from a previous iteration.

Returns:

Total objective value from the previous iteration, or None if not found.

copy_DesignVariablesUnscaled_Previous_outerloop_itr(self) → List[float] | None

Copy the unscaled design variables from the previous outer loop iteration.

Returns:

Unscaled design variables from the previous iteration, or None if not found.

copy_DesignVariables_Previous_outerloop_itr(self) → List[float] | None

Copy the scaled design variables from the previous outer loop iteration.

Returns:

Scaled design variables from the previous iteration, or None if not found.

copy_DesignVariables_Previous_innerloop_itr(self) → List[float] | None

Copy the scaled design variables from the previous inner loop iteration.

Returns:

Scaled design variables from the previous inner iteration, or None if not found.

copy_DesignVariablesUnscaled_Previous_innerloop_itr(self) → List[float] | None

Copy the unscaled design variables from the previous inner loop iteration.

Returns:

Unscaled design variables from the previous inner iteration, or None if not found.

run_IterativeOptimization(self) → None

📐 Pseudocode: Solve subsystem optimization (argmin) (Line 11), Controller: Solve optimization (argmin) (Line 18) → Unified Algorithmic Structure; Solve ALC augmented Lagrangian optimization (Line 10) → Augmented Lagrangian Coordination; Innerloop decentralized Primal Update optimization (Lines 7, 8) → Consensus Augmented Lagrangian Coordination; Local subsystem NLP optimization (Line 12) → Augmented Lagrangian Alternating Direction Inexact Newton; Solve the local NLP with the linear sensitivity term (Line 8) → Sensitivity Based Distributed Programming; Call the optimizer wrapper (Line 1) → SubSystemBasis.run_IterativeOptimization

Execute the local optimization of the subsystem.

set_OptimData(self, optimdataIn: OptimDataBasis) → None

📐 Pseudocode: Set optimal design variables, objectives, constraints (Line 25) → SubSystemBasis.run_IterativeOptimization

Store the information from the optimization run.

Args:

optimdataIn: The optimization data to store.

get_OptimData(self) → OptimDataBasis

Get information from the last optimization run.

Returns:

The optimization data, or None if not set.

get_Local_ConvergenceIndicator_Innerloop(self) → Local_ConvergenceIndicator_Innerloop_Interface

Get the local convergence indicator for the inner loop.

Returns:

The local inner loop convergence indicator.

get_Local_ConvergenceIndicator_Outerloop(self) → Local_ConvergenceIndicator_Outerloop_Interface

Get the local convergence indicator for the outer loop.

Returns:

The local outer loop convergence indicator.

evaluate_InnerLoopConvergenceIndicator(self) → None

📐 Pseudocode: Compute innerloop convergence criterion (Line 27) → Unified Algorithmic Structure; Innerloop convergence check (Lines 13, 14, 15) → Augmented Lagrangian Coordination; Innerloop convergence check (Lines 16, 17, 18) → Consensus Augmented Lagrangian Coordination; Innerloop convergence check (Lines 23, 24, 25) → Augmented Lagrangian Alternating Direction Inexact Newton

Evaluate the inner loop convergence indicator.

The local convergence indicator retrieves the data it needs from self. This allows different convergence criteria to access different data (e.g., total objective, primal/dual residuals, etc.).

get_ConvInnerLoop(self) → bool

Check if the subsystem is locally converged in the inner loop.

Returns:

True if converged, False otherwise.

evaluate_OuterLoopConvergenceIndicator(self) → None

📐 Pseudocode: Compute outerloop convergence criterion (Line 41) → Unified Algorithmic Structure; Outerloop convergence and return (Lines 22, 23, 24, 25) → Augmented Lagrangian Coordination; Outerloop convergence and return (Lines 24, 25, 26, 27) → Consensus Augmented Lagrangian Coordination; Outerloop convergence and return (Lines 40, 41, 42, 43) → Augmented Lagrangian Alternating Direction Inexact Newton; Outerloop convergence check (Lines 11, 12, 13, 14) → Sensitivity Based Distributed Programming

Evaluate the outer loop convergence indicator.

Same design rationale as evaluate_InnerLoopConvergenceIndicator: the local convergence indicator retrieves the data it needs from self.

get_ConvOuterLoop(self) → bool

Get the outer loop convergence indicator.

Returns:

True if converged, False otherwise.

CopyFromMiddleLevelCoupling(self, idIn: str, couplingIn: MiddleLevelCouplingInterface) → None

Apply stored middle-level coupling data to the matching local CouplingParameter.

Args:

idIn: Identifier of the neighbor subsystem.
couplingIn: The MiddleLevelCouplingInterface data retrieved from storage.

get_CouplingParameters(self) → List[CouplingParametersInterface]

📐 Pseudocode: Iterate over all CouplingParameters (Line 3) → SubSystemBasis.CopyToMiddleLevel

Get the coupling parameters.

Returns:

List of coupling parameters for all neighbors.

get_CouplingParameter_with_ID(self, id: str) → CouplingParametersInterface

Return the coupling parameter from self._couplingparameters with the provided ID.

Args:

id: Identifier of the neighbor subsystem.

Raises:

ValueError: If no coupling parameter with the given ID exists.

Returns:

The coupling parameter matching the provided ID.

compute_KKT_multipliers(self) → None

📐 Pseudocode: Recover and distribute the coordination-equality multipliers (Line 9) → Sensitivity Based Distributed Programming

If a coordination method needs multipliers of local constraints (incl. bound constraints), but the used solver does not provide such, then a globally defined procedure is called to set them in the OptimDataBasis object.

The KKT multipliers are computed with respect to the current state of the subsystem, i.e. based on the Jacobians/gradients and design variable values currently stored in self._optimdata; updates of these quantities have to be done separately. Therefore, before calling this method, one should typically call evaluateAllJacobians() and updateSubsystemfromOptimdata() to ensure that the subsystem state and the stored Jacobians/gradients are consistent and up to date.

compute_ApproximateKKT_multipliers(self, matrix_KKT_system: np.typing.ArrayLike, negative_gradient_totalobjective: List[float], bounds: List[Tuple[float | None, float | None]]) → List[float]

Compute approximate KKT multipliers when the solver does not provide them.

If a coordination method needs multipliers of local constraints (incl. bound constraints), but the used solver does not provide such, then a globally defined procedure is called to set them in the OptimDataBasis object.

This function should only be called if the solver does not obtain a solution for the exact KKT-system; then, as a mitigation for algorithms that need multipliers to work, we compute the 'multipliers' that minimize the violation of the KKT system w.r.t. the squared l2-error.

Args:

matrix_KKT_system: The KKT system matrix.
negative_gradient_totalobjective: Negative gradient of the total objective.
bounds: List of (lower, upper) bound tuples for each dual variable.

Returns:

The computed approximate KKT multipliers as a list.

check_MultipliersNotSet(self) → bool

📐 Pseudocode: Recover and distribute the coordination-equality multipliers (Line 9) → Sensitivity Based Distributed Programming

Return True if any existing constraint is missing its corresponding multiplier in optimdata.

Returns:

True if at least one constraint has no corresponding multiplier set.

update_state(self, other_subsystem: SubSystemBasis) → None

Update the state of this SubSystemBasis instance with values from another instance.

This method is necessary for multiprocessing. When subsystems are executed in parallel using multiprocessing.Pool, they are serialized and deserialized, creating new objects in separate memory spaces. After parallel execution completes, this method updates the original object's attribute values while preserving their memory addresses.

The update preserves memory addresses by modifying attribute contents in-place where possible, rather than reassigning references. This is essential for maintaining object identity across the multiprocessing boundary.

Args:

other_subsystem: The source SubSystemBasis containing updated values
from parallel execution.