1-bus UCBlock and ThermalUnitBlock¶
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 a single thermal generator of 100 kW to meet a constant load over 24 hours in one bus.
Creating an SMS++ Network¶
A sample SMS++ network can be created with the following code. After the Python imports, the code creates
a new SMS++ network sn with the block file format. The block file format is a text file that contains
only the model data in a structured way, with no solver information. The solver information is provided
in a separate configuration file.
from pysmspp import SMSConfig, SMSNetwork, Variable, Block, SMSFileType
import numpy as np
# Create an empty SMSNetwork with block file type
sn = SMSNetwork(file_type=SMSFileType.eBlockFile)
# For demonstration, we'll print out the network to confirm it is created.
sn
SMSNetwork Object Block object Attributes (1): SMS++_file_type Dimensions (0): None Variables (0): None Blocks (0): None
Adding a UCBlock¶
After an empty network is created, we can populate it with blocks. In particular, we need to add a first
inner block that describes the type of model to be optimized. Here, we add a UCBlock (Unit Commitment
Block) suitable for unit commitment problems (see UCBlock SMS documentation
for more details). We specify 24 time steps (one day) and a constant demand of 50 kW for each time
step.
The block is added to the network with:
ucb = sn.add(
"UCBlock", # block type
"Block_0", # block name
id="0", # block id
TimeHorizon=24, # number of time steps
NumberUnits=1, # number of units
NumberElectricalGenerators=1, # number of electrical generators
NumberNodes=1, # number of nodes
ActivePowerDemand=Variable( # active power demand
"ActivePowerDemand",
"float",
("NumberNodes", "TimeHorizon"),
np.full((1, 24), 50.0), # constant demand of 50 kW
),
)
print("Added UCBlock with constant demand.")
ucb # print the block to confirm it is created
Added UCBlock with constant demand.
Block object Attributes (2): type, id Dimensions (4): TimeHorizon, NumberUnits, NumberElectricalGenerators, NumberNodes Variables (1): ActivePowerDemand Blocks (0): None
Adding a Thermal Unit¶
In the unit commitment block above, no generator is yet added. To add a generator, we first create a
ThermalUnitBlock. Then we attach it to sn.blocks["Block_0"] using the add method. The snippet
below sets up a thermal generator with 100 kW maximum output.
thermal_unit_block = Block().from_kwargs(
block_type="ThermalUnitBlock",
MinPower=Variable("MinPower", "float", (), 0.0),
MaxPower=Variable("MaxPower", "float", (), 100.0),
LinearTerm=Variable("LinearTerm", "float", (), 0.3),
InitUpDownTime=Variable("InitUpDownTime", "int", (), 1),
)
# Add it to the existing UCBlock (Block_0)
sn.blocks["Block_0"].add("ThermalUnitBlock", "UnitBlock_0", block=thermal_unit_block)
print("ThermalUnitBlock added.")
ThermalUnitBlock added.
Optimizing the Network¶
Finally, we can optimize the network using a solver configuration file and specifying a temporary SMS++ file path. Here's an example invocation:
configfile = SMSConfig(
template="UCBlock/uc_solverconfig"
) # path to the template solver config file "uc_solverconfig"
temporary_smspp_file = "./smspp_temp_file.nc" # path to temporary SMS++ file
output_file = "./smspp_output.txt" # path to the output file (optional)
fp_solution = "./fp_solution.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,
)
print("Optimization finished.")
Executing command:
ucblock_solver smspp_temp_file.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.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 192 rows; 96 cols; 430 nonzeros; 72 integer variables (71 binary)
Coefficient ranges:
Matrix [1e+00, 1e+02]
Cost [3e-01, 3e-01]
Bound [1e+00, 1e+00]
RHS [1e+00, 5e+01]
Presolving model
0 rows, 0 cols, 0 nonzeros 0s
0 rows, 0 cols, 0 nonzeros 0s
Presolve reductions: rows 0(-192); columns 0(-96); nonzeros 0(-430) - 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% 360 360 0.00% 0 0 0 0 0.0s
Solving report
Status Optimal
Primal bound 360
Dual bound 360
Gap 0% (tolerance: 0.01%)
P-D integral 0
Solution status feasible
360 (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: 1.03968200e-03 s
Status = 10 (Success)
Upper bound = 3.60000000e+02
Lower bound = 3.60000000e+02
----- ThermalUnitBlock 0 -----
Function value = 3.60000000e+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 = [ 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01 ]
Peak CPU Usage: 0.00 % Peak Memory Usage: 2.56 MB Total Time: 0.21 seconds Optimization finished.
Viewing Results¶
Basic results are stored in the result object. For instance, you can check the solver status and
the final objective value with:
print("Status:", result.status)
print("Objective value:", result.objective_value)
Status: 10 (Success) Objective value: 360.0
View the solution object saved in fp_solution and automatically read in the result object
result.solution
SMSNetwork Object Block object Attributes (1): SMS++_file_type Dimensions (0): None Variables (0): None Blocks (1): Solution_0