optimizer

Modules

optimizer module

class Optimizer(simulator)[source]

Bases: object

A class for optimizing building operation in the twin4build framework.

This class optimizes model inputs (variables) (e.g., setpoints) by minimizing a loss function, using gradient-based or other optimization algorithms. The optimizer implements soft constraints on model outputs (embedded in the loss function) and hard constraints on variables.

Parameters:

simulator (Simulator) – The simulator instance for running simulations.

Mathematical Formulation

The general optimization problem is formulated as:

\[\hat{\boldsymbol{U}} = \underset{\boldsymbol{U} \in \mathcal{U}}{\operatorname{argmin}} \; \mathcal{L}(\boldsymbol{U})\]
where:
  • \(\hat{\boldsymbol{U}}\) is the optimal control input matrix

  • \(\boldsymbol{U}\) is the control input matrix

  • \(\mathcal{U} \subseteq \mathbb{R}^{n_u \times n_t}\) is the set of feasible control inputs

  • \(\mathcal{L}(\boldsymbol{U})\) is the loss function

Dimensions

  • \(n_t\): Number of time steps in the simulation period

  • \(n_u\): Number of control inputs (actuators)

  • \(n_d\): Number of disturbance inputs (weather, occupancy, etc.)

  • \(n_y\): Number of system outputs (sensors, performance metrics)

Model Structure

The building model \(\mathcal{M}\) is represented as a directed graph where nodes are dynamic components and edges represent input/output connections as shown in a simple example below.

System overview showing components and their relationships

The model takes control inputs \(\boldsymbol{U} \in \mathbb{R}^{n_u \times n_t}\) (the optimization variables) along with external inputs or disturbances \(\boldsymbol{D} \in \mathbb{R}^{n_d \times n_t}\), and produces system outputs for optimization \(\boldsymbol{\hat{Y}} \in \mathbb{R}^{n_y \times n_t}\) with timesteps \(\boldsymbol{t} \in \mathbb{R}^{n_t}\):

\[\boldsymbol{\hat{Y}} = \mathcal{M}(\boldsymbol{X}, \boldsymbol{t})\]

where:

\[\boldsymbol{X} = [\boldsymbol{U}, \boldsymbol{D}]\]

and \(\mathcal{M}\) represents the complete simulation model. See Simulator for detailed explanation of the simulation process.

Loss Function

The loss function \(\mathcal{L}(\boldsymbol{U})\) is composed of the following terms:

Equality Constraints

\[\mathcal{L}_{eq} = \frac{1}{n_t} \sum_{t=1}^{n_t} \sum_{(j, \boldsymbol{y}) \in \mathcal{C}_{eq}} |\boldsymbol{\hat{Y}}_{j,t} - \boldsymbol{y}_{t}|\]

where \(\mathcal{C}_{eq}\) is the set of equality constraints, each element is (output index \(j\), desired value \(\boldsymbol{y}_{t}\)).

Inequality Constraints

Upper constraints:

\[\mathcal{L}_{ineq}^{upper} = \frac{1}{n_t} \sum_{t=1}^{n_t} \sum_{(j, \boldsymbol{y}) \in \mathcal{C}_{ineq}^{upper}} k \cdot \text{relu}\left(\boldsymbol{\hat{Y}}_{j,t} - \boldsymbol{y}_{t}\right)\]

Lower constraints:

\[\mathcal{L}_{ineq}^{lower} = \frac{1}{n_t} \sum_{t=1}^{n_t} \sum_{(j, \boldsymbol{y}) \in \mathcal{C}_{ineq}^{lower}} k \cdot \text{relu}\left(\boldsymbol{y}_{t} - \boldsymbol{\hat{Y}}_{j,t}\right)\]

where \(\mathcal{C}_{ineq}^{upper}\) and \(\mathcal{C}_{ineq}^{lower}\) are the sets of upper and lower inequality constraints, and \(k\) is a penalty factor.

Combined inequality constraint loss:

\[\mathcal{L}_{ineq} = \mathcal{L}_{ineq}^{upper} + \mathcal{L}_{ineq}^{lower}\]

Objective Terms

\[\mathcal{L}_{obj} = \frac{1}{n_t} \sum_{t=1}^{n_t} \sum_{(j, w) \in \mathcal{O}_{obj}} w \cdot \boldsymbol{\hat{Y}}_{j,t}\]

where \(\mathcal{O}_{obj}\) is the set of outputs to minimize or maximize, and \(w\) is a weight (+1 for minimization, -1 for maximization).

Total Loss

\[\mathcal{L}(\boldsymbol{U}) = \mathcal{L}_{eq} + \mathcal{L}_{ineq} + \mathcal{L}_{obj}\]

See method docstrings for details on the specific loss terms and optimization algorithms.

Examples

Basic optimization with PyTorch method:

>>> import twin4build as tb
>>> import datetime
>>> import pytz
>>>
>>> # Create model and simulator
>>> model = tb.SimulationModel(id="my_model")
>>> simulator = tb.Simulator(model)
>>> optimizer = tb.Optimizer(simulator)
>>>
>>> # Define decision variables (actuators to optimize)
>>> variables = [
...     (heater_component, "setpointValue", 18.0, 25.0),  # Temperature setpoint bounds
...     (ventilation_component, "flowRate", 0.1, 1.0)    # Ventilation flow rate bounds
... ]
>>>
>>> # Define objectives (what to optimize)
>>> objectives = [
...     (energy_meter, "powerConsumption", "min"),  # Minimize energy consumption
...     (comfort_sensor, "comfortIndex", "max")     # Maximize comfort
... ]
>>>
>>> # Set time period
>>> start = datetime.datetime(2024, 1, 1, tzinfo=pytz.UTC)
>>> end = datetime.datetime(2024, 1, 2, tzinfo=pytz.UTC)
>>> step = 3600
>>>
>>> # Run optimization with PyTorch (default SGD)
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method="torch",
...     options={"lr": 0.1, "iterations": 100}
... )

Advanced PyTorch optimization with scheduler:

>>> # Use Adam optimizer with cosine annealing scheduler
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method=("torch", "Adam", "ad"),
...     options={
...         "lr": 0.01,
...         "iterations": 200,
...         "scheduler_type": "cosine",
...         "scheduler_params": {"T_max": 200, "eta_min": 1e-6}
...     }
... )

SciPy optimization with constraints:

>>> # Define equality constraints (maintain temperature at specific times)
>>> equality_constraints = [
...     (room_temperature, "temperature", 21.0)  # Maintain 21°C
... ]
>>>
>>> # Define inequality constraints (comfort bounds)
>>> inequality_constraints = [
...     (room_temperature, "temperature", "lower", 20.0),  # Not below 20°C
...     (room_temperature, "temperature", "upper", 24.0),  # Not above 24°C
...     (co2_sensor, "concentration", "upper", 1000.0)     # CO2 limit
... ]
>>>
>>> # Run SciPy optimization with SLSQP (preferred for constrained problems)
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     eq_cons=equality_constraints,
...     ineq_cons=inequality_constraints,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method=("scipy", "SLSQP", "ad"),
...     options={"verbose": 2, "maxiter": 1000}
... )

Alternative SciPy methods:

>>> # Use L-BFGS-B for unconstrained optimization
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method=("scipy", "L-BFGS-B", "ad"),
...     options={"gtol": 1e-8, "maxiter": 500}
... )
>>> # Use trust-region method for difficult constraints
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     eq_cons=equality_constraints,
...     ineq_cons=inequality_constraints,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method=("scipy", "trust-constr", "ad"),
...     options={"verbose": 1, "barrier_tol": 1e-8}
... )

Schedule-based constraints:

>>> # Use schedule systems for time-varying constraints
>>> import twin4build.systems as systems
>>>
>>> # Create temperature schedule
>>> temp_schedule = systems.ScheduleSystem(
...     id="temp_schedule",
...     schedule_filename="temperature_profile.csv"
... )
>>>
>>> # Use schedule as constraint
>>> equality_constraints = [
...     (room_temperature, "temperature", temp_schedule)
... ]
>>>
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     eq_cons=equality_constraints,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method=("scipy", "SLSQP", "ad")
... )

Multi-objective optimization:

>>> # Optimize multiple conflicting objectives
>>> objectives = [
...     (energy_meter, "powerConsumption", "min"),     # Minimize energy
...     (comfort_sensor, "thermalComfort", "max"),     # Maximize comfort
...     (air_quality_sensor, "iaqIndex", "max"),       # Maximize air quality
... ]
>>>
>>> # Use multiple decision variables
>>> variables = [
...     (heater_component, "setpointValue", 18.0, 25.0),
...     (cooler_component, "setpointValue", 22.0, 28.0),
...     (ventilation_component, "flowRate", 0.1, 2.0),
...     (window_actuator, "openingDegree", 0.0, 1.0)
... ]
>>>
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method=("scipy", "SLSQP", "ad"),
...     options={"ftol": 1e-9, "maxiter": 2000}
... )

Legacy string format (still supported):

>>> # Simple usage with default settings
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method="scipy"  # Defaults to ("scipy", "SLSQP", "ad")
... )
>>> # PyTorch method with defaults
>>> optimizer.optimize(
...     variables=variables,
...     objectives=objectives,
...     start_time=start,
...     end_time=end,
...     step_size=step,
...     method="torch"  # Defaults to ("torch", "SGD", "ad")
... )
optimize(start_time=None, end_time=None, step_size=None, variables=None, objectives=None, eq_cons=None, ineq_cons=None, method='scipy', options=None, **kwargs)[source]

Optimize the model using various optimization methods.

Parameters:
  • start_time (Union[datetime, List[datetime], None]) – Start time for simulation

  • end_time (Union[datetime, List[datetime], None]) – End time for simulation

  • step_size (Union[float, List[float], None]) – Step size for simulation

  • variables (Optional[List[Tuple[Any, str, float, float]]]) – List of tuples (component, output_name, lower_bound, upper_bound)

  • objectives (Optional[List[Tuple[Any, str, str]]]) – List of tuples (component, output_name, objective_type) where objective_type is “min” or “max”

  • eq_cons (Optional[List[Tuple[Any, str, Any]]]) – List of tuples (component, output_name, desired_value)

  • ineq_cons (Optional[List[Tuple[Any, str, str, Any]]]) – List of tuples (component, output_name, constraint_type, desired_value) where constraint_type is “upper” or “lower”

  • method (Union[str, Tuple[str, str, str]]) –

    Optimization method specification. Can be specified in two formats:

    1. String format (legacy): - “torch”: Uses PyTorch-based gradient optimization - “scipy”: Uses SciPy’s SLSQP solver with automatic differentiation

    2. Tuple format (recommended): - (library, optimizer, mode) where:

      • library: “torch” or “scipy”

      • optimizer: The specific optimization algorithm

      • mode: “ad” (automatic differentiation) or “fd” (finite difference)

    Supported optimizers by library:

    PyTorch-based methods (library=”torch”):
    • ”SGD”: Stochastic Gradient Descent (default)

    • ”Adam”: Adam optimizer

    • ”LBFGS”: Limited-memory BFGS

    • Mode: Always “ad” (automatic differentiation)

    SciPy-based methods (library=”scipy”):
    • ”SLSQP”: Sequential Least Squares Programming (preferred for most problems)

    • ”L-BFGS-B”: Limited-memory BFGS with bounds

    • ”TNC”: Truncated Newton algorithm with bounds

    • ”trust-constr”: Trust-region constrained optimization

    • ”trf”: Trust Region Reflective (for least-squares problems)

    • ”dogbox”: Dogleg algorithm (for least-squares problems)

    • Mode: “ad” (automatic differentiation) or “fd” (finite difference)

    Method selection guidelines:
    • PyTorch methods: Good for simple optimization problems, easy to configure

    • SciPy SLSQP with AD: Preferred for most constrained optimization problems

    • SciPy with FD: Use for non-PyTorch models or when AD is not available

    Examples

    • (“scipy”, “SLSQP”, “ad”): Preferred for most constrained optimization problems

    • (“torch”, “Adam”, “ad”): Good for simple unconstrained problems

    • (“scipy”, “trf”, “fd”): For non-PyTorch models with least-squares formulation

    • ”scipy”: Legacy format, defaults to (“scipy”, “SLSQP”, “ad”)

  • options (Optional[Dict]) –

    Additional options for the chosen method: For PyTorch methods (library=”torch”):

    • ”lr”: Learning rate for optimizer (default: 1.0)

    • ”iterations”: Number of optimization iterations (default: 100)

    • ”optimizer_type”: Type of PyTorch optimizer (“SGD”, “Adam”, “LBFGS”)

    • ”scheduler_type”: Type of learning rate scheduler (“step”, “exponential”, “cosine”, “reduce_on_plateau”, None)

    • ”scheduler_params”: Parameters for learning rate scheduler
      • For “step”: step_size (default: 30), gamma (default: 0.1)

      • For “exponential”: gamma (default: 0.95)

      • For “cosine”: T_max (default: 100), eta_min (default: 0)

      • For “reduce_on_plateau”: mode (default: “min”), factor (default: 0.9), patience (default: 10), threshold (default: 1e-4)

    For SciPy methods (library=”scipy”):
    • ”verbose”: Verbosity level (0-3)

    • ”maxiter”: Maximum iterations

    • ”gtol”: Gradient tolerance

    • ”xtol”: Parameter tolerance

    • ”barrier_tol”: Barrier tolerance

    • ”initial_tr_radius”: Initial trust region radius

    • ”initial_constr_penalty”: Initial constraint penalty

    • Additional method-specific options as supported by SciPy optimizers