optimizer
Modules
optimizer module
- class Optimizer(simulator)[source]
Bases:
objectA 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.
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
Simulatorfor 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 simulationend_time (
Union[datetime,List[datetime],None]) – End time for simulationstep_size (
Union[float,List[float],None]) – Step size for simulationvariables (
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:
String format (legacy): - “torch”: Uses PyTorch-based gradient optimization - “scipy”: Uses SciPy’s SLSQP solver with automatic differentiation
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