-
Notifications
You must be signed in to change notification settings - Fork 87
developed controllers for power-heat coupled network and plot it #653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sf-snigdha
wants to merge
7
commits into
e2nIEE:develop
Choose a base branch
from
sf-snigdha:develop
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
fd2f9a0
Add files via upload
sf-snigdha dbda747
Merge pull request #1 from sf-snigdha/sf-snigdha-patch-1
sf-snigdha db54904
Merge branch 'develop' into develop
SimonRubenDrauz 462ed2d
Merge branch 'develop' into develop
sf-snigdha 4a64a30
Restructure coupled power-heat tutorial and controller
ecd4bc1
Move coupled controller and plotting code into src package
4f8dae5
Remove coupled network plotting files
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
136 changes: 136 additions & 0 deletions
136
src/pandapipes/multinet/control/multinet_control_power2heat.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| from pandapower.control import ConstControl | ||
| from pandapipes.properties.fluids import get_fluid | ||
| from pandapower.control.basic_controller import Controller | ||
| from pandas.errors import InvalidIndexError | ||
|
|
||
| import pandapower as ppower | ||
| class P2HControlMultiEnergy(Controller): | ||
| def __init__(self, multinet, element_index_power, element_index_heat, efficiency, | ||
| name_power_net='power', name_heat_net='heat', | ||
| in_service=True, order=0, level=0, | ||
| drop_same_existing_ctrl=False, initial_run=True, **kwargs): | ||
| super().__init__(multinet, in_service, order, level, | ||
| drop_same_existing_ctrl=drop_same_existing_ctrl, initial_run=initial_run, | ||
| **kwargs) | ||
|
|
||
| self.elm_idx_power = element_index_power | ||
| self.elm_idx_heat = element_index_heat | ||
| self.name_net_power = name_power_net | ||
| self.name_net_heat = name_heat_net | ||
| self.efficiency = efficiency | ||
| self.qext_w = None | ||
| self.fluid = get_fluid(multinet['nets'][name_heat_net]) | ||
| self.applied = False | ||
|
|
||
| def initialize_control(self, multinet): | ||
| self.applied = False | ||
|
|
||
| def get_all_net_names(self): | ||
| return [self.name_net_power, self.name_net_heat] | ||
|
|
||
| def control_step(self, multinet): | ||
| ppower.runpp(multinet['nets'][self.name_net_power]) | ||
|
|
||
| try: | ||
| power_load = \ | ||
| multinet['nets'][self.name_net_power].res_load.at[self.elm_idx_power, 'p_mw'] | ||
| except (ValueError, TypeError, InvalidIndexError): | ||
| power_load = \ | ||
| multinet['nets'][self.name_net_power].res_load.loc[self.elm_idx_power, 'p_mw'].values | ||
| self.qext_w = - (power_load * self.conversion_factor_mw_to_w() * self.efficiency) | ||
|
|
||
| self.write_to_net(multinet) | ||
| self.applied = True | ||
|
|
||
| def write_to_net(self, multinet): | ||
| try: | ||
| multinet['nets'][self.name_net_heat].heat_exchanger.at[self.elm_idx_heat, 'qext_w'] \ | ||
| = self.qext_w | ||
| except (ValueError, TypeError, InvalidIndexError): | ||
| multinet['nets'][self.name_net_heat].heat_exchanger.loc[self.elm_idx_heat, | ||
| 'qext_w'] = self.qext_w | ||
|
|
||
| def is_converged(self, multinet): | ||
| return self.applied | ||
|
|
||
| def conversion_factor_mw_to_w(self): | ||
| return 1e6 | ||
|
|
||
|
|
||
| class H2PControlMultiEnergy(Controller): | ||
| def __init__(self, multinet, element_index_power, element_index_heat, efficiency, | ||
| name_power_net='power', name_heat_net='heat', element_type_power="sgen", | ||
| in_service=True, order=0, | ||
| level=0, drop_same_existing_ctrl=False, initial_run=True, | ||
| calc_heat_from_power=False, **kwargs): | ||
| super().__init__(multinet, in_service, order, level, | ||
| drop_same_existing_ctrl=drop_same_existing_ctrl, initial_run=initial_run, | ||
| **kwargs) | ||
|
|
||
| self.elm_idx_power = element_index_power | ||
| self.elm_idx_heat = element_index_heat | ||
| self.elm_type_power = element_type_power | ||
| self.name_net_power = name_power_net | ||
| self.name_net_heat = name_heat_net | ||
| self.efficiency = efficiency | ||
| self.qext_w = None | ||
| self.fluid = get_fluid(multinet['nets'][name_heat_net]) | ||
| self.el_power_led = calc_heat_from_power | ||
| self.applied = False | ||
|
|
||
| def initialize_control(self, multinet): | ||
| self.applied = False | ||
|
|
||
| def get_all_net_names(self): | ||
| return [self.name_net_heat, self.name_net_power] | ||
|
|
||
| def control_step(self, multinet): | ||
| if self.el_power_led: | ||
| try: | ||
| power_gen = multinet['nets'][self.name_net_power][self.elm_type_power].at[ | ||
| self.elm_idx_power, 'p_mw'] * multinet['nets'][self.name_net_power][ | ||
| self.elm_type_power].at[self.elm_idx_power, 'scaling'] | ||
|
|
||
| except (ValueError, TypeError, InvalidIndexError): | ||
| power_gen = multinet['nets'][self.name_net_power][self.elm_type_power].loc[ | ||
| self.elm_idx_power, 'p_mw'].values[:] \ | ||
| * multinet['nets'][self.name_net_power][self.elm_type_power].loc[ | ||
| self.elm_idx_power, 'scaling'].values[:] | ||
|
|
||
| self.heat_cons = power_gen / (self.conversion_factor_w_to_mw() * self.efficiency) | ||
|
|
||
| else: | ||
| try: | ||
| heat_heat_exchanger = \ | ||
| multinet['nets'][self.name_net_heat].heat_exchanger.at[self.elm_idx_heat, 'qext_w'] | ||
|
|
||
| except (ValueError, TypeError, InvalidIndexError): | ||
| heat_heat_exchanger = multinet['nets'][self.name_net_heat].heat_exchanger.loc[self.elm_idx_heat, | ||
| 'qext_w'].values[:] | ||
|
|
||
| self.power_gen = heat_heat_exchanger * self.conversion_factor_w_to_mw() * self.efficiency | ||
|
|
||
| self.write_to_net(multinet) | ||
| self.applied = True | ||
|
|
||
| def write_to_net(self, multinet): | ||
| if self.el_power_led: | ||
| try: | ||
| multinet['nets'][self.name_net_heat].heat_exchanger.at[self.elm_idx_heat, | ||
| 'qext_w'] = self.heat_cons | ||
| except (ValueError, TypeError, InvalidIndexError): | ||
| multinet['nets'][self.name_net_heat].heat_exchanger.loc[self.elm_idx_heat, | ||
| 'qext_w'] = self.heat_cons | ||
| else: | ||
| try: | ||
| multinet['nets'][self.name_net_power][self.elm_type_power].at[ | ||
| self.elm_idx_power, 'p_mw'] = self.power_gen | ||
| except (ValueError, TypeError, InvalidIndexError): | ||
| multinet['nets'][self.name_net_power][self.elm_type_power].loc[ | ||
| self.elm_idx_power, 'p_mw'] = self.power_gen | ||
|
|
||
| def is_converged(self, multinet): | ||
| return self.applied | ||
|
|
||
| def conversion_factor_w_to_mw(self): | ||
| return 1 / 1e6 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import pandapipes.plotting as plot | ||
| from itertools import chain | ||
| from pandapower.control import ConstControl | ||
| from pandapipes.properties.fluids import get_fluid | ||
| from pandapower.control.basic_controller import Controller | ||
| from pandas.errors import InvalidIndexError | ||
|
|
||
| import matplotlib.pyplot as plt | ||
| import pandapipes.plotting as pp_plot | ||
|
|
||
| import sys | ||
| try: | ||
| import matplotlib.pyplot as plt | ||
| MATPLOTLIB_INSTALLED = True | ||
| except ImportError: | ||
| MATPLOTLIB_INSTALLED = False | ||
|
|
||
| from pandapower.auxiliary import soft_dependency_error | ||
| from pandapower.plotting.plotting_toolbox import get_collection_sizes | ||
| from pandapower.plotting.collections import create_bus_collection, create_line_collection, \ | ||
| create_trafo_collection, create_trafo3w_collection, \ | ||
| create_line_switch_collection, draw_collections, create_bus_bus_switch_collection, create_ext_grid_collection, create_sgen_collection, \ | ||
| create_gen_collection, create_load_collection, create_dcline_collection | ||
| from pandapower.plotting.generic_geodata import create_generic_coordinates | ||
| from pandapipes.component_models.circulation_pump_mass_component import CirculationPumpMass | ||
| from pandapipes.component_models.circulation_pump_pressure_component import CirculationPumpPressure | ||
| from pandapipes.component_models.pump_component import Pump | ||
| from pandapipes.plotting.collections import create_junction_collection, create_pipe_collection, \ | ||
| create_valve_collection, create_source_collection, create_pressure_control_collection, \ | ||
| create_heat_exchanger_collection, create_sink_collection, create_pump_collection, \ | ||
| create_compressor_collection, create_flow_control_collection | ||
| from pandapipes.plotting.generic_geodata import create_generic_coordinates | ||
| from pandapipes.plotting.plotting_toolbox import get_collection_sizes | ||
|
|
||
| try: | ||
| import pandaplan.core.pplog as logging | ||
| except ImportError: | ||
| import logging | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| import pandapower.plotting as pp_plot | ||
| import pandapipes.plotting as pipes_plot | ||
| import matplotlib.pyplot as plt | ||
|
|
||
|
|
||
| import pandapower.plotting as pp_plot | ||
| import pandapipes.plotting as pipes_plot | ||
| import matplotlib.pyplot as plt | ||
| import sys | ||
| from coupled_nets_power_heat import * | ||
| def plot_coupled_network_with_highlighted_bus(multinet, highlighted_bus): | ||
| fig, ax = plt.subplots() | ||
|
|
||
| # Plot pandapower network | ||
| power_network = multinet["nets"]["power"] | ||
| pp_plot.simple_plot(power_network, ax=ax) | ||
|
|
||
| # Highlight the specific bus in pandapipes network | ||
| heat_network = multinet["nets"]["heat"] | ||
| highlighted_bus_color = 'red' # Choose your desired color | ||
| pipes_plot.simple_plot(heat_network, ax=ax, bus_color=highlighted_bus_color, highlighted_bus=highlighted_bus) | ||
|
|
||
| plt.show() | ||
|
|
||
| plot_coupled_network_with_highlighted_bus(multinet, highlighted_bus=3) | ||
|
|
||
| def plot_coupled_network(multinet): | ||
| fig, ax = plt.subplots() | ||
|
|
||
| # Plot pandapower network | ||
| power_network = multinet["nets"]["power"] | ||
| pp_plot.simple_plot(power_network, ax=ax) | ||
|
|
||
| # Plot pandapipes network | ||
| heat_network = multinet["nets"]["heat"] | ||
| pipes_plot.simple_plot(heat_network, ax=ax) | ||
|
|
||
| plt.show() | ||
|
|
||
| # plotting coupled net | ||
| plot_coupled_network(multinet) | ||
|
|
||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# Coupled Power-Heat Network Example\n", | ||
| "\n", | ||
| "This tutorial demonstrates how to create a coupled power and heat network using pandapipes, pandapower and pandapipes multinet functionality." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Imports\n", | ||
| "\n", | ||
| "Import the required pandapipes, pandapower, plotting and controller modules." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import pandapipes as pp\n", | ||
| "import pandapower as ppower\n", | ||
| "from pandapower.control.basic_controller import Controller\n", | ||
| "from pandapower import networks as pandasnet\n", | ||
| "from pandapipes import networks as pipenet\n", | ||
| "from pandapipes.multinet.create_multinet import create_empty_multinet, add_net_to_multinet\n", | ||
| "from pandapipes.multinet.control.run_control_multinet import run_control\n", | ||
| "import pandapower.plotting as pp_plot\n", | ||
| "import os\n", | ||
| "import matplotlib.pyplot as plt\n", | ||
| "from pandapower.plotting.plotly import simple_plotly\n", | ||
| "import pandas as pd\n", | ||
| "import pandapower.plotting.plotly as pplotly\n", | ||
| "import sys\n", | ||
| "from pandapipes.multinet.control.multinet_control_power2heat import *\n", | ||
| "import matplotlib.pyplot as plt\n", | ||
| "\n", | ||
| "\n", | ||
| "# ---## Create the power network\n", | ||
| "\n", | ||
| "# Step 1: Creating the power network using pandapower's predefined simple network example\n", | ||
| "power_net = pandasnet.example_simple() # Loading a predefined simple electrical network.\n", | ||
| "\n", | ||
| "# ----------------------------------------\n", | ||
| "# Step 2: Creating a heat network using pandapipes\n", | ||
| "# This defines a thermal (fluid) network that models the heat system.\n", | ||
| "\n", | ||
| "heat_net = pp.create_empty_network(fluid=\"water\") # Create an empty heat network with water as the fluid.\n", | ||
| "\n", | ||
| "# Create junctions (nodes) in the heat network:\n", | ||
| "j0 = pp.create_junction(heat_net, pn_bar=5, tfluid_k=293.15, name=\"junction 0\") # Junction 0 at 5 bar pressure and 293.15 K temperature.\n", | ||
| "j1 = pp.create_junction(heat_net, pn_bar=5, tfluid_k=293.15, name=\"junction 1\") # Junction 1 with same pressure and temperature.\n", | ||
| "j2 = pp.create_junction(heat_net, pn_bar=5, tfluid_k=293.15, name=\"junction 2\") # Junction 2 with same pressure and temperature.\n", | ||
| "j3 = pp.create_junction(heat_net, pn_bar=5, tfluid_k=293.15, name=\"junction 3\") # Junction 3 with same pressure and temperature.\n", | ||
| "\n", | ||
| "# Create a pump to circulate fluid between junctions j0 and j3, with a constant mass flow rate.\n", | ||
| "pp.create_circ_pump_const_mass_flow(heat_net, return_junction=j3, flow_junction=j0, p_flow_bar=5,\n", | ||
| " mdot_flow_kg_per_s=20, t_flow_k=273.15+35) # A pump for fluid flow at 20 kg/s with 5 bar pressure difference.\n", | ||
| "\n", | ||
| "# Create a heat exchanger between junctions j1 and j2\n", | ||
| "pp.create_heat_exchanger(heat_net, from_junction=j1, to_junction=j2, diameter_m=200e-3, qext_w=100000) # Heat exchanger between j1 and j2.\n", | ||
| "\n", | ||
| "# Create pipes to connect the junctions and form the network.\n", | ||
| "pp.create_pipe_from_parameters(heat_net, from_junction=j0, to_junction=j1, length_km=1,\n", | ||
| " diameter_m=200e-3, k_mm=.1, alpha_w_per_m2k=10, sections=5, text_k=283) # Pipe from j0 to j1.\n", | ||
| "pp.create_pipe_from_parameters(heat_net, from_junction=j2, to_junction=j3, length_km=1,\n", | ||
| " diameter_m=200e-3, k_mm=.1, alpha_w_per_m2k=10, sections=5, text_k=283) # Pipe from j2 to j3.\n", | ||
| "\n", | ||
| "# ----------------------------------------\n", | ||
| "# Step 3: Create a multinet (multi-network) and add power and heat networks\n", | ||
| "multinet = create_empty_multinet('multinet') # Create an empty multinet system.\n", | ||
| "add_net_to_multinet(multinet, power_net, 'power') # Add the power network to the multinet system.\n", | ||
| "add_net_to_multinet(multinet, heat_net, 'heat') # Add the heat network to the multinet system.\n", | ||
| "\n", | ||
| "# ----------------------------------------\n", | ||
| "# Step 4: Define conversion units (power-to-heat and heat-to-power) within the networks.\n", | ||
| "# These elements will facilitate energy conversions between the power and heat networks.\n", | ||
| "\n", | ||
| "# Power-to-Heat (P2H) conversion: Creating a load in the power network (consuming power) and a heat exchanger in the heat network.\n", | ||
| "p2h_id_el = ppower.create_load(power_net, bus=6, p_mw=.0002, name=\"power to heat consumption\") # Load in the power network at bus 6 (0.2 MW).\n", | ||
| "p2h_id_heat = pp.create_heat_exchanger(heat_net, from_junction=0, to_junction=1, diameter_m=200e-3, qext_w=0, name=\"power to heat feed in\")\n", | ||
| "\n", | ||
| "# Heat-to-Power (H2P) conversion: Creating a heat exchanger in the heat network and a generator in the power network.\n", | ||
| "h2p_id_heat = pp.create_heat_exchanger(heat_net, from_junction=2, to_junction=3, diameter_m=200e-3, qext_w=200000, name=\"power to heat feed in\")\n", | ||
| "h2p_id_el = ppower.create_sgen(power_net, bus=6, p_mw=0, name=\"fuel cell feed in\")\n", | ||
| "\n", | ||
| "# ----------------------------------------\n", | ||
| "# Step 5: Define control objects for the power-to-heat and heat-to-power conversions.\n", | ||
| "\n", | ||
| "# Power-to-Heat control: A control object to manage the energy transfer between power and heat networks.\n", | ||
| "p2h_ctrl = P2HControlMultiEnergy(multinet, p2h_id_el, p2h_id_heat, efficiency=3,\n", | ||
| " name_power_net=\"power\", name_heat_net=\"heat\")\n", | ||
| "\n", | ||
| "# Heat-to-Power control: Another control object for managing the reverse flow (heat to power).\n", | ||
| "h2p_ctrl = H2PControlMultiEnergy(multinet, h2p_id_el, h2p_id_heat, efficiency=2,\n", | ||
| " name_power_net=\"power\", name_heat_net=\"heat\")\n", | ||
| "\n", | ||
| "# ----------------------------------------\n", | ||
| "# Step 6: Print initial values of power-to-heat and heat-to-power elements.\n", | ||
| "print(heat_net.heat_exchanger.loc[p2h_id_heat, 'qext_w']) # Print the initial power-to-heat exchange rate.\n", | ||
| "print(power_net.sgen.loc[h2p_id_el, 'p_mw']) # Print the initial power generation from the fuel cell.\n", | ||
| "\n", | ||
| "# ----------------------------------------\n", | ||
| "# Step 7: Run the control simulation on the multinet system.\n", | ||
| "run_control(multinet) # This runs the control logic for the power and heat networks based on the defined conversions.\n", | ||
| "\n", | ||
| "# ----------------------------------------\n", | ||
| "# Step 8: Print updated values after running the simulation.\n", | ||
| "print(heat_net.heat_exchanger.loc[p2h_id_heat, 'qext_w']) # Print the updated power-to-heat exchange rate after running control.\n", | ||
| "print(power_net.sgen.loc[h2p_id_el, 'p_mw']) # Print the updated power generation after running control.\n" | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3.12.3 64-bit", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
| "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.12.3" | ||
| }, | ||
| "orig_nbformat": 4, | ||
| "vscode": { | ||
| "interpreter": { | ||
| "hash": "9240d949b7e875368571ba59acc67192d2efbcc4561b3c6f94c83d7858e18732" | ||
| } | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 2 | ||
| } |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to have an own file for this? Maybe put this in the tutorial or in the plotting toolbox
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. Since the plotting code was specific to this example and not essential for the controller functionality, I removed the separate plotting file and the associated image entirely. The PR is now focused on the coupled power-heat controller and tutorial.