2-buses UCBlock, NetworkBlock and two ThermalUnitBlocks¶
In the following, we provide a quick example on how to add a simple optimization model with SMS++. The problem below optimizes the dispatch of two thermal generators connected by a transmission line between two buses over 24 hours.
from pysmspp import SMSNetwork, SMSFileType, Variable, Block, SMSConfig
import numpy as np
sn = SMSNetwork(file_type=SMSFileType.eBlockFile) # Empty Block
sn
SMSNetwork Object Block object Attributes (1): SMS++_file_type Dimensions (0): None Variables (0): None Blocks (0): None
Creating an SMS++ Network¶
First, we create an empty SMS++ network with the block file format and import necessary components:
Defining the UCBlock Parameters¶
The network does not contain any block inside, so it has to be populated. The first step is to specify the main parameters of the UCBlock.
1- Parameters for time, number of units, generators, and nodes:
kwargs = {
"TimeHorizon": 24, # number of time steps
"NumberUnits": 2, # number of units
"NumberElectricalGenerators": 2, # number of electrical generators
"NumberNodes": 2, # number of nodes
"NumberLines": 1, # number of lines
}
2- Demand for each node: This has to be defined as a Variable object:
demand_array = np.full((2, 24), 50.0)
demand = {
"ActivePowerDemand": Variable( # active power demand
"ActivePowerDemand",
"float",
("NumberNodes", "TimeHorizon"),
demand_array,
)
} # constant demand of 50kW
kwargs = {**kwargs, **demand}
3- Parameters for the line: A line can be described with a DCNetworkBlock (DC power flow) or a TransportBlock. Here we use a TransportBlock with the following parameters:
line_variables = {
"StartLine": Variable("StartLine", "int", ("NumberLines",), [0]),
"EndLine": Variable("EndLine", "int", ("NumberLines",), [1]),
"MinPowerFlow": Variable("MinPowerFlow", "float", ("NumberLines",), [-50.0]),
"MaxPowerFlow": Variable("MaxPowerFlow", "float", ("NumberLines",), [50.0]),
"LineSusceptance": Variable("LineSusceptance", "float", ("NumberLines",), [0.0]),
}
kwargs = {**kwargs, **line_variables}
4- Generator location: Variable to specify in which bus (node) the generator is attached:
generator_node = {
"GeneratorNode": Variable(
"GeneratorNode", int, ("NumberElectricalGenerators",), [0, 1]
),
}
kwargs = {**kwargs, **generator_node}
Adding the UCBlock to the Network¶
Add everything with the SMSNetwork.add function:
sn.add(
"UCBlock", # block type
"Block_0", # block name
id="0", # block id
**kwargs,
)
sn
SMSNetwork Object Block object Attributes (1): SMS++_file_type Dimensions (0): None Variables (0): None Blocks (1): Block_0
Inspecting the Network Structure¶
Now the SMSNetwork object has a UCBlock called Block_0. Let's see how it is organized:
sn.blocks["Block_0"]
Block object Attributes (2): type, id Dimensions (5): TimeHorizon, NumberUnits, NumberElectricalGenerators, NumberNodes, NumberLines Variables (7): ActivePowerDemand, StartLine, EndLine, MinPowerFlow, MaxPowerFlow, LineSusceptance, GeneratorNode Blocks (0): None
Adding Thermal Unit Blocks¶
Now, the two thermal units have to be added to the UCBlock as ThermalUnitBlocks. First, we create a thermal unit block with the following parameters:
thermal_unit_block = Block().from_kwargs(
block_type="ThermalUnitBlock",
MinPower=Variable("MinPower", "float", (), 0.0),
MaxPower=Variable("MaxPower", "float", (), 70.0),
LinearTerm=Variable("LinearTerm", "float", (), 0.3),
InitUpDownTime=Variable("InitUpDownTime", "int", (), 1),
)
thermal_unit_block
Block object Attributes (1): type Dimensions (0): None Variables (4): MinPower, MaxPower, LinearTerm, InitUpDownTime Blocks (0): None
Then, the unit block is added to the UCBlock:
# Add it to the existing UCBlock (Block_0)
sn.blocks["Block_0"].add_block("UnitBlock_0", block=thermal_unit_block)
sn.blocks["Block_0"]
Block object Attributes (2): type, id Dimensions (5): TimeHorizon, NumberUnits, NumberElectricalGenerators, NumberNodes, NumberLines Variables (7): ActivePowerDemand, StartLine, EndLine, MinPowerFlow, MaxPowerFlow, LineSusceptance, GeneratorNode Blocks (1): UnitBlock_0
Similarly for the second ThermalUnitBlock. The max power is chosen to force the unit to be turned on to supply the demand:
thermal_unit_block = Block().from_kwargs(
block_type="ThermalUnitBlock",
MinPower=Variable("MinPower", "float", (), 0.0),
MaxPower=Variable("MaxPower", "float", (), 90.0),
LinearTerm=Variable("LinearTerm", "float", (), 0.8),
InitUpDownTime=Variable("InitUpDownTime", "int", (), 1),
)
# Add it to the existing UCBlock (Block_0)
sn.blocks["Block_0"].add_block("UnitBlock_1", block=thermal_unit_block)
sn.blocks["Block_0"]
Block object Attributes (2): type, id Dimensions (5): TimeHorizon, NumberUnits, NumberElectricalGenerators, NumberNodes, NumberLines Variables (7): ActivePowerDemand, StartLine, EndLine, MinPowerFlow, MaxPowerFlow, LineSusceptance, GeneratorNode Blocks (2): UnitBlock_0, UnitBlock_1
Optimizing the Network¶
The problem can now be optimized using a solver configuration:
configfile = SMSConfig(
template="UCBlock/uc_solverconfig"
) # path to the template solver config file "uc_solverconfig"
temporary_smspp_file = "./2buses_2thermal.nc" # path to temporary SMS++ file
output_file = "./2buses_2thermal.txt" # path to the output file (optional)
fp_solution = "./fp_solution_2buses_2thermal.nc4" # path to the file where the full problem solution will be saved (optional). When provided, the result.solution object will be populated with the SMS++ solution object
result = sn.optimize(
configfile,
temporary_smspp_file,
output_file,
fp_solution=fp_solution,
)
Executing command:
ucblock_solver 2buses_2thermal.nc -S uc_solverconfig.txt -c /home/docs/checkouts/readthedocs.org/user_builds/pysmspp/checkouts/latest/pysmspp/data/configs/UCBlock/ -p /home/docs/checkouts/readthedocs.org/user_builds/pysmspp/checkouts/latest/docs/examples/ -O /home/docs/checkouts/readthedocs.org/user_builds/pysmspp/checkouts/latest/docs/examples/fp_solution_2buses_2thermal.nc4
Using a default Block configuration
Running HiGHS 1.14.0 (git hash: n/a): Copyright (c) 2026 under MIT licence terms
Solver: HiGHSMILPSolver
MIP has 432 rows; 264 cols; 1004 nonzeros; 144 integer variables (142 binary)
Coefficient ranges:
Matrix [1e+00, 9e+01]
Cost [3e-01, 8e-01]
Bound [1e+00, 5e+01]
RHS [1e+00, 5e+01]
Presolving model
282 rows, 164 cols, 748 nonzeros 0s
0 rows, 0 cols, 0 nonzeros 0s
Presolve reductions: rows 0(-432); columns 0(-264); nonzeros 0(-1004) - Reduced to empty
Presolve: Optimal
Src: B => Branching; C => Central rounding; F => Feasibility pump; H => Heuristic;
I => Shifting; J => Feasibility jump; L => Sub-MIP; P => Empty MIP; R => Randomized rounding;
S => Solve LP; T => Evaluate node; U => Unbounded; X => User solution; Y => HiGHS solution;
Z => ZI Round; l => Trivial lower; p => Trivial point; u => Trivial upper; z => Trivial zero
Nodes | B&B Tree | Objective Bounds | Dynamic Constraints | Work
Src Proc. InQueue | Leaves Expl. | BestBound BestSol Gap | Cuts InLp Confl. | LpIters Time
0 0 0 0.00% 1080 1080 0.00% 0 0 0 0 0.0s
Solving report
Status Optimal
Primal bound 1080
Dual bound 1080
Gap 0% (tolerance: 0.01%)
P-D integral 0
Solution status feasible
1080 (objective)
0 (bound viol.)
0 (int. viol.)
0 (row viol.)
Timing 0.00
Max sub-MIP depth 0
Nodes 0
Repair LPs 0
LP iterations 0
Elapsed time: 2.66157000e-03 s
Status = 10 (Success)
Upper bound = 1.08000000e+03
Lower bound = 1.08000000e+03
----- ThermalUnitBlock 0 -----
Function value = 5.04000000e+02
Commitment = [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ]
Start up = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
Shut down = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
Active power = [ 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 ]
----- ThermalUnitBlock 1 -----
Function value = 5.76000000e+02
Commitment = [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ]
Start up = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
Shut down = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
Active power = [ 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 ]
----- DCNetworkBlock 0 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 1 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 2 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 3 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 4 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 5 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 6 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 7 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 8 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 9 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 10 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 11 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 12 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 13 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 14 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 15 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 16 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 17 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 18 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 19 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 20 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 21 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 22 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
----- DCNetworkBlock 23 -----
Function value = 0.00000000e+00
Power flow = [ 2.00000000e+01 ]
Peak CPU Usage: 0.00 % Peak Memory Usage: 2.48 MB Total Time: 0.21 seconds
Viewing Results¶
The value of the objective function and the complete log can be obtained with:
result.objective_value
1080.0
result.log
'Using a default Block configuration\nRunning HiGHS 1.14.0 (git hash: n/a): Copyright (c) 2026 under MIT licence terms\nSolver: HiGHSMILPSolver\nMIP has 432 rows; 264 cols; 1004 nonzeros; 144 integer variables (142 binary)\nCoefficient ranges:\n Matrix [1e+00, 9e+01]\n Cost [3e-01, 8e-01]\n Bound [1e+00, 5e+01]\n RHS [1e+00, 5e+01]\nPresolving model\n282 rows, 164 cols, 748 nonzeros 0s\n0 rows, 0 cols, 0 nonzeros 0s\nPresolve reductions: rows 0(-432); columns 0(-264); nonzeros 0(-1004) - Reduced to empty\nPresolve: Optimal\n\nSrc: B => Branching; C => Central rounding; F => Feasibility pump; H => Heuristic;\n I => Shifting; J => Feasibility jump; L => Sub-MIP; P => Empty MIP; R => Randomized rounding;\n S => Solve LP; T => Evaluate node; U => Unbounded; X => User solution; Y => HiGHS solution;\n Z => ZI Round; l => Trivial lower; p => Trivial point; u => Trivial upper; z => Trivial zero\n\n Nodes | B&B Tree | Objective Bounds | Dynamic Constraints | Work \nSrc Proc. InQueue | Leaves Expl. | BestBound BestSol Gap | Cuts InLp Confl. | LpIters Time\n\n 0 0 0 0.00% 1080 1080 0.00% 0 0 0 0 0.0s\n\nSolving report\n Status Optimal\n Primal bound 1080\n Dual bound 1080\n Gap 0% (tolerance: 0.01%)\n P-D integral 0\n Solution status feasible\n 1080 (objective)\n 0 (bound viol.)\n 0 (int. viol.)\n 0 (row viol.)\n Timing 0.00\n Max sub-MIP depth 0\n Nodes 0\n Repair LPs 0\n LP iterations 0\nElapsed time: 2.66157000e-03 s\nStatus = 10 (Success)\nUpper bound = 1.08000000e+03\nLower bound = 1.08000000e+03\n\n----- ThermalUnitBlock 0 -----\nFunction value = 5.04000000e+02\nCommitment = [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ]\nStart up = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\nShut down = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\nActive power = [ 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 7.00000000e+01 ]\n\n----- ThermalUnitBlock 1 -----\nFunction value = 5.76000000e+02\nCommitment = [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ]\nStart up = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\nShut down = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\nActive power = [ 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 3.00000000e+01 ]\n\n----- DCNetworkBlock 0 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 1 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 2 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 3 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 4 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 5 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 6 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 7 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 8 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 9 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 10 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 11 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 12 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 13 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 14 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 15 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 16 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 17 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 18 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 19 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 20 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 21 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 22 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\n----- DCNetworkBlock 23 -----\nFunction value = 0.00000000e+00\nPower flow = [ 2.00000000e+01 ]\n\nPeak CPU Usage: 0.00 %\nPeak Memory Usage: 2.48 MB\nTotal Time: 0.21 seconds\n'
result.solution
SMSNetwork Object Block object Attributes (1): SMS++_file_type Dimensions (0): None Variables (0): None Blocks (1): Solution_0