From e1b66fca850b1b77c592371b392132e14c1c73ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:35:57 +0000 Subject: [PATCH 1/4] Initial plan From 42328bcd134908f0d80b7959ff40b5cda21d4910 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:38:41 +0000 Subject: [PATCH 2/4] Add I/O operations example notebook and register in mkdocs nav Co-authored-by: davide-f <67809479+davide-f@users.noreply.github.com> --- docs/examples/io_operations.ipynb | 282 ++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 283 insertions(+) create mode 100644 docs/examples/io_operations.ipynb diff --git a/docs/examples/io_operations.ipynb b/docs/examples/io_operations.ipynb new file mode 100644 index 0000000..260b003 --- /dev/null +++ b/docs/examples/io_operations.ipynb @@ -0,0 +1,282 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "metadata": {}, + "source": [ + "# Input/Output Operations\n", + "\n", + "This notebook demonstrates how to perform input/output operations with SMS++ networks using pySMSpp.\n", + "In particular, it shows how to:\n", + "\n", + "1. Load an existing SMS++ network from a NetCDF file\n", + "2. Create a new SMS++ network programmatically\n", + "3. Save a network to a NetCDF file\n", + "4. Reload a saved network and verify its contents\n", + "\n", + "SMS++ stores models in [NetCDF4](https://www.unidata.ucar.edu/software/netcdf/) files (`.nc` or `.nc4`),\n", + "a self-describing, machine-independent data format designed for array-oriented scientific data." + ] + }, + { + "cell_type": "markdown", + "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First, let's import the necessary modules." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import tempfile\n", + "\n", + "import numpy as np\n", + "\n", + "from pysmspp import Block, SMSFileType, SMSNetwork, Variable" + ] + }, + { + "cell_type": "markdown", + "id": "d4e5f6a7-b8c9-0123-defa-234567890123", + "metadata": {}, + "source": [ + "## Loading a Network from a File\n", + "\n", + "An existing SMS++ network stored in a NetCDF file can be loaded by passing the file path to the\n", + "`SMSNetwork` constructor. pySMSpp reads the file and reconstructs the full block hierarchy in memory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5f6a7b8-c9d0-1234-efab-345678901234", + "metadata": {}, + "outputs": [], + "source": [ + "# Load a sample network from a NetCDF file\n", + "network_path = \"../../test/test_data/microgrid_ALLbutStore_1N.nc4\"\n", + "\n", + "net = SMSNetwork(network_path)\n", + "print(\"Network loaded successfully!\")\n", + "net" + ] + }, + { + "cell_type": "markdown", + "id": "f6a7b8c9-d0e1-2345-fabc-456789012345", + "metadata": {}, + "source": [ + "After loading, you can inspect the network structure with `print_tree()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7b8c9d0-e1f2-3456-abcd-567890123456", + "metadata": {}, + "outputs": [], + "source": [ + "net.print_tree(show_attributes=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b8c9d0e1-f2a3-4567-bcde-678901234567", + "metadata": {}, + "source": [ + "## Creating a Network Programmatically\n", + "\n", + "A new SMS++ network can be built from scratch using the `SMSNetwork` and `Block` classes.\n", + "In this example, we create a simple unit commitment network with a single thermal generator." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9d0e1f2-a3b4-5678-cdef-789012345678", + "metadata": {}, + "outputs": [], + "source": [ + "# Create an empty network with block-file format\n", + "sn = SMSNetwork(file_type=SMSFileType.eBlockFile)\n", + "\n", + "# Add a UCBlock with a 24-hour time horizon and constant demand of 50 kW\n", + "sn.add(\n", + " \"UCBlock\",\n", + " \"Block_0\",\n", + " id=\"0\",\n", + " TimeHorizon=24,\n", + " NumberUnits=1,\n", + " NumberElectricalGenerators=1,\n", + " NumberNodes=1,\n", + " ActivePowerDemand=Variable(\n", + " \"ActivePowerDemand\",\n", + " \"float\",\n", + " (\"NumberNodes\", \"TimeHorizon\"),\n", + " np.full((1, 24), 50.0),\n", + " ),\n", + ")\n", + "\n", + "# Add a thermal generator to the UCBlock\n", + "thermal_unit = Block().from_kwargs(\n", + " block_type=\"ThermalUnitBlock\",\n", + " MinPower=Variable(\"MinPower\", \"float\", (), 0.0),\n", + " MaxPower=Variable(\"MaxPower\", \"float\", (), 100.0),\n", + " LinearTerm=Variable(\"LinearTerm\", \"float\", (), 0.3),\n", + " InitUpDownTime=Variable(\"InitUpDownTime\", \"int\", (), 1),\n", + ")\n", + "sn.blocks[\"Block_0\"].add(\"ThermalUnitBlock\", \"UnitBlock_0\", block=thermal_unit)\n", + "\n", + "print(\"Network created successfully!\")\n", + "sn.print_tree(show_dimensions=True)" + ] + }, + { + "cell_type": "markdown", + "id": "d0e1f2a3-b4c5-6789-defa-890123456789", + "metadata": {}, + "source": [ + "## Saving a Network to a File\n", + "\n", + "Any `SMSNetwork` (or `Block`) can be serialized to a NetCDF file using the `to_netcdf()` method.\n", + "By default, `to_netcdf()` raises an error if the target file already exists; pass `force=True` to\n", + "overwrite it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1f2a3b4-c5d6-7890-efab-901234567890", + "metadata": {}, + "outputs": [], + "source": [ + "# Save the newly created network to a temporary file\n", + "with tempfile.TemporaryDirectory() as tmpdir:\n", + " output_path = os.path.join(tmpdir, \"my_network.nc4\")\n", + "\n", + " sn.to_netcdf(output_path)\n", + " print(f\"Network saved to: {output_path}\")\n", + " print(f\"File size: {os.path.getsize(output_path)} bytes\")\n", + "\n", + " # --- Reload and verify ---\n", + " reloaded = SMSNetwork(output_path)\n", + " print(\"\\nReloaded network:\")\n", + " reloaded.print_tree(show_dimensions=True)" + ] + }, + { + "cell_type": "markdown", + "id": "f2a3b4c5-d6e7-8901-fabc-012345678901", + "metadata": {}, + "source": [ + "The `force=True` flag can be used to overwrite an existing file:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3b4c5d6-e7f8-9012-abcd-123456789012", + "metadata": {}, + "outputs": [], + "source": [ + "with tempfile.TemporaryDirectory() as tmpdir:\n", + " output_path = os.path.join(tmpdir, \"my_network.nc4\")\n", + "\n", + " # First save\n", + " sn.to_netcdf(output_path)\n", + "\n", + " # Overwrite with force=True\n", + " sn.to_netcdf(output_path, force=True)\n", + " print(\"File overwritten successfully with force=True.\")" + ] + }, + { + "cell_type": "markdown", + "id": "b4c5d6e7-f8a9-0123-bcde-234567890123", + "metadata": {}, + "source": [ + "## Round-Trip: Save and Reload an Existing Network\n", + "\n", + "A common workflow is to load a network, modify it, and save the updated version.\n", + "The example below loads the sample network, saves it under a new name, and verifies\n", + "that the reloaded copy has the same structure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5d6e7f8-a9b0-1234-cdef-345678901234", + "metadata": {}, + "outputs": [], + "source": [ + "with tempfile.TemporaryDirectory() as tmpdir:\n", + " resaved_path = os.path.join(tmpdir, \"resaved_network.nc4\")\n", + "\n", + " # Load the original network\n", + " original = SMSNetwork(network_path)\n", + "\n", + " # Save it to a new file\n", + " original.to_netcdf(resaved_path)\n", + "\n", + " # Reload the saved file\n", + " reloaded = SMSNetwork(resaved_path)\n", + "\n", + " # Compare top-level block names\n", + " original_blocks = list(original.blocks.keys())\n", + " reloaded_blocks = list(reloaded.blocks.keys())\n", + "\n", + " print(\"Original blocks :\", original_blocks)\n", + " print(\"Reloaded blocks :\", reloaded_blocks)\n", + " print(\"Blocks match :\", original_blocks == reloaded_blocks)" + ] + }, + { + "cell_type": "markdown", + "id": "d6e7f8a9-b0c1-2345-defa-456789012345", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This notebook demonstrated:\n", + "\n", + "1. **Loading** an SMS++ network from a NetCDF file with `SMSNetwork(fp)`\n", + "2. **Creating** a network programmatically using `SMSNetwork`, `Block`, and `Variable`\n", + "3. **Saving** a network to a NetCDF file with `to_netcdf(fp)` (and `force=True` to overwrite)\n", + "4. **Reloading** a saved network and verifying its contents\n", + "\n", + "These operations form the foundation of any pySMSpp workflow: you can build models in Python,\n", + "persist them to disk, and exchange them with the SMS++ solver or other tools." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "", + "language": "python", + "name": "" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs.yml b/mkdocs.yml index c11b27b..6873306 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -52,6 +52,7 @@ nav: - Unit Commitment (2 thermal, line): examples/ucblock_2thermal_line.ipynb - Navigating SMS Blocks: examples/navigating_sms_blocks.ipynb - Plotting Variables and Blocks: examples/plotting_variables_and_blocks.ipynb + - Input/Output Operations: examples/io_operations.ipynb - User Guide: - API Reference: api_reference.md - Release Notes: release_notes.md From 9661815e8a56c0eb1f56db5b39dbd758e71ed3dc Mon Sep 17 00:00:00 2001 From: Davide Fioriti Date: Wed, 25 Feb 2026 18:57:46 +0100 Subject: [PATCH 3/4] Introduce io operations and solution --- docs/examples/io_operations.ipynb | 32 +++++++++++------------ docs/examples/ucblock_2thermal_line.ipynb | 11 ++++++++ docs/examples/ucblock_thermal_1bus.ipynb | 20 ++++++++++++++ 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/docs/examples/io_operations.ipynb b/docs/examples/io_operations.ipynb index 260b003..b908632 100644 --- a/docs/examples/io_operations.ipynb +++ b/docs/examples/io_operations.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "id": "0", "metadata": {}, "source": [ "# Input/Output Operations\n", @@ -21,7 +21,7 @@ }, { "cell_type": "markdown", - "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "id": "1", "metadata": {}, "source": [ "## Setup\n", @@ -32,7 +32,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -46,7 +46,7 @@ }, { "cell_type": "markdown", - "id": "d4e5f6a7-b8c9-0123-defa-234567890123", + "id": "3", "metadata": {}, "source": [ "## Loading a Network from a File\n", @@ -58,7 +58,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e5f6a7b8-c9d0-1234-efab-345678901234", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -72,7 +72,7 @@ }, { "cell_type": "markdown", - "id": "f6a7b8c9-d0e1-2345-fabc-456789012345", + "id": "5", "metadata": {}, "source": [ "After loading, you can inspect the network structure with `print_tree()`." @@ -81,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a7b8c9d0-e1f2-3456-abcd-567890123456", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "b8c9d0e1-f2a3-4567-bcde-678901234567", + "id": "7", "metadata": {}, "source": [ "## Creating a Network Programmatically\n", @@ -102,7 +102,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c9d0e1f2-a3b4-5678-cdef-789012345678", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -142,7 +142,7 @@ }, { "cell_type": "markdown", - "id": "d0e1f2a3-b4c5-6789-defa-890123456789", + "id": "9", "metadata": {}, "source": [ "## Saving a Network to a File\n", @@ -155,7 +155,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e1f2a3b4-c5d6-7890-efab-901234567890", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -175,7 +175,7 @@ }, { "cell_type": "markdown", - "id": "f2a3b4c5-d6e7-8901-fabc-012345678901", + "id": "11", "metadata": {}, "source": [ "The `force=True` flag can be used to overwrite an existing file:" @@ -184,7 +184,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a3b4c5d6-e7f8-9012-abcd-123456789012", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -201,7 +201,7 @@ }, { "cell_type": "markdown", - "id": "b4c5d6e7-f8a9-0123-bcde-234567890123", + "id": "13", "metadata": {}, "source": [ "## Round-Trip: Save and Reload an Existing Network\n", @@ -214,7 +214,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c5d6e7f8-a9b0-1234-cdef-345678901234", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -241,7 +241,7 @@ }, { "cell_type": "markdown", - "id": "d6e7f8a9-b0c1-2345-defa-456789012345", + "id": "15", "metadata": {}, "source": [ "## Summary\n", diff --git a/docs/examples/ucblock_2thermal_line.ipynb b/docs/examples/ucblock_2thermal_line.ipynb index 644f99a..2a44ac1 100644 --- a/docs/examples/ucblock_2thermal_line.ipynb +++ b/docs/examples/ucblock_2thermal_line.ipynb @@ -272,11 +272,13 @@ ") # path to the template solver config file \"uc_solverconfig\"\n", "temporary_smspp_file = \"./2buses_2thermal.nc\" # path to temporary SMS++ file\n", "output_file = \"./2buses_2thermal.txt\" # path to the output file (optional)\n", + "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\n", "\n", "result = sn.optimize(\n", " configfile,\n", " temporary_smspp_file,\n", " output_file,\n", + " fp_solution=fp_solution,\n", ")" ] }, @@ -306,6 +308,15 @@ "source": [ "result.log" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result.solution" + ] } ], "metadata": { diff --git a/docs/examples/ucblock_thermal_1bus.ipynb b/docs/examples/ucblock_thermal_1bus.ipynb index 217e7a5..50e40d5 100644 --- a/docs/examples/ucblock_thermal_1bus.ipynb +++ b/docs/examples/ucblock_thermal_1bus.ipynb @@ -141,11 +141,13 @@ ") # path to the template solver config file \"uc_solverconfig\"\n", "temporary_smspp_file = \"./smspp_temp_file.nc\" # path to temporary SMS++ file\n", "output_file = \"./smspp_output.txt\" # path to the output file (optional)\n", + "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\n", "\n", "result = sn.optimize(\n", " configfile,\n", " temporary_smspp_file,\n", " output_file,\n", + " fp_solution=fp_solution,\n", ")\n", "\n", "print(\"Optimization finished.\")" @@ -172,6 +174,24 @@ "print(\"Status:\", result.status)\n", "print(\"Objective value:\", result.objective_value)" ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "View the solution object saved in `fp_solution` and automatically read in the `result` object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "result.solution" + ] } ], "metadata": { From b6243cf4813e13e488d5797660ff1b0c3c32cf26 Mon Sep 17 00:00:00 2001 From: Davide Fioriti Date: Wed, 25 Feb 2026 18:58:57 +0100 Subject: [PATCH 4/4] Add release notes --- docs/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release_notes.md b/docs/release_notes.md index 398e640..ef661cf 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -4,6 +4,7 @@ ### New Features and Major Changes +* [Introduce example on input/output operations and introduce solution object into the example #80](https://github.com/SPSUnipi/pySMSpp/pull/80) * [Revise SMSPPSolverTool.is_available to support shell option and move shell option to constructor of SMSPPSolverTool #78](https://github.com/SPSUnipi/pySMSpp/pull/78) * [Enable shell option in SMSNetwork.optimize #77](https://github.com/SPSUnipi/pySMSpp/pull/77) * [Enable shell option in subprocess of tools and generalize options: add explicit solverconfig option and kwargs to generalize options #73](https://github.com/SPSUnipi/pySMSpp/pull/73)