Input/Output Operations¶
This notebook demonstrates how to perform input/output operations with SMS++ networks using pySMSpp. In particular, it shows how to:
- Load an existing SMS++ network from a NetCDF file
- Create a new SMS++ network programmatically
- Save a network to a NetCDF file
- Reload a saved network and verify its contents
SMS++ stores models in NetCDF4 files (.nc or .nc4),
a self-describing, machine-independent data format designed for array-oriented scientific data.
Setup¶
First, let's import the necessary modules.
import os
import tempfile
import numpy as np
from pysmspp import Block, SMSFileType, SMSNetwork, Variable
Loading a Network from a File¶
An existing SMS++ network stored in a NetCDF file can be loaded by passing the file path to the
SMSNetwork constructor. pySMSpp reads the file and reconstructs the full block hierarchy in memory.
# Load a sample network from a NetCDF file
network_path = "../../test/test_data/microgrid_ALLbutStore_1N.nc4"
net = SMSNetwork(network_path)
print("Network loaded successfully!")
net
Network loaded successfully!
SMSNetwork Object Block object Attributes (1): SMS++_file_type Dimensions (0): None Variables (0): None Blocks (1): Block_0
After loading, you can inspect the network structure with print_tree().
net.print_tree(show_attributes=True)
SMSNetwork
Attributes (1): SMS++_file_type=1
└── Block_0 [UCBlock]
├── UnitBlock_0 [ThermalUnitBlock]
│ Attributes (1): id=0
├── UnitBlock_1 [IntermittentUnitBlock]
│ Attributes (1): id=1
├── UnitBlock_2 [IntermittentUnitBlock]
│ Attributes (1): id=2
├── UnitBlock_3 [BatteryUnitBlock]
│ Attributes (1): id=3
└── UnitBlock_4 [HydroUnitBlock]
Attributes (1): id=4
Creating a Network Programmatically¶
A new SMS++ network can be built from scratch using the SMSNetwork and Block classes.
In this example, we create a simple unit commitment network with a single thermal generator.
# Create an empty network with block-file format
sn = SMSNetwork(file_type=SMSFileType.eBlockFile)
# Add a UCBlock with a 24-hour time horizon and constant demand of 50 kW
sn.add(
"UCBlock",
"Block_0",
id="0",
TimeHorizon=24,
NumberUnits=1,
NumberElectricalGenerators=1,
NumberNodes=1,
ActivePowerDemand=Variable(
"ActivePowerDemand",
"float",
("NumberNodes", "TimeHorizon"),
np.full((1, 24), 50.0),
),
)
# Add a thermal generator to the UCBlock
thermal_unit = 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),
)
sn.blocks["Block_0"].add("ThermalUnitBlock", "UnitBlock_0", block=thermal_unit)
print("Network created successfully!")
sn.print_tree(show_dimensions=True)
Network created successfully!
SMSNetwork
└── Block_0 [UCBlock]
Dimensions (4): TimeHorizon=24, NumberUnits=1, NumberElectricalGenerators=1, NumberNodes=1
└── UnitBlock_0 [ThermalUnitBlock]
Saving a Network to a File¶
Any SMSNetwork (or Block) can be serialized to a NetCDF file using the to_netcdf() method.
By default, to_netcdf() raises an error if the target file already exists; pass force=True to
overwrite it.
# Save the newly created network to a temporary file
with tempfile.TemporaryDirectory() as tmpdir:
output_path = os.path.join(tmpdir, "my_network.nc4")
sn.to_netcdf(output_path)
print(f"Network saved to: {output_path}")
print(f"File size: {os.path.getsize(output_path)} bytes")
# --- Reload and verify ---
reloaded = SMSNetwork(output_path)
print("\nReloaded network:")
reloaded.print_tree(show_dimensions=True)
Network saved to: /tmp/tmp1tfbmft6/my_network.nc4
File size: 10849 bytes
Reloaded network:
SMSNetwork
└── Block_0 [UCBlock]
Dimensions (4): TimeHorizon=24, NumberUnits=1, NumberElectricalGenerators=1, NumberNodes=1
└── UnitBlock_0 [ThermalUnitBlock]
The force=True flag can be used to overwrite an existing file:
with tempfile.TemporaryDirectory() as tmpdir:
output_path = os.path.join(tmpdir, "my_network.nc4")
# First save
sn.to_netcdf(output_path)
# Overwrite with force=True
sn.to_netcdf(output_path, force=True)
print("File overwritten successfully with force=True.")
File overwritten successfully with force=True.
Round-Trip: Save and Reload an Existing Network¶
A common workflow is to load a network, modify it, and save the updated version. The example below loads the sample network, saves it under a new name, and verifies that the reloaded copy has the same structure.
with tempfile.TemporaryDirectory() as tmpdir:
resaved_path = os.path.join(tmpdir, "resaved_network.nc4")
# Load the original network
original = SMSNetwork(network_path)
# Save it to a new file
original.to_netcdf(resaved_path)
# Reload the saved file
reloaded = SMSNetwork(resaved_path)
# Compare top-level block names
original_blocks = list(original.blocks.keys())
reloaded_blocks = list(reloaded.blocks.keys())
print("Original blocks :", original_blocks)
print("Reloaded blocks :", reloaded_blocks)
print("Blocks match :", original_blocks == reloaded_blocks)
Original blocks : ['Block_0'] Reloaded blocks : ['Block_0'] Blocks match : True
Summary¶
This notebook demonstrated:
- Loading an SMS++ network from a NetCDF file with
SMSNetwork(fp) - Creating a network programmatically using
SMSNetwork,Block, andVariable - Saving a network to a NetCDF file with
to_netcdf(fp)(andforce=Trueto overwrite) - Reloading a saved network and verifying its contents
These operations form the foundation of any pySMSpp workflow: you can build models in Python, persist them to disk, and exchange them with the SMS++ solver or other tools.