diff --git a/src/pandapipes/multinet/control/multinet_control_power2heat.py b/src/pandapipes/multinet/control/multinet_control_power2heat.py new file mode 100644 index 000000000..bc21fe1c3 --- /dev/null +++ b/src/pandapipes/multinet/control/multinet_control_power2heat.py @@ -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 diff --git a/tutorials/coupled_nets_power_heat.ipynb b/tutorials/coupled_nets_power_heat.ipynb new file mode 100644 index 000000000..ad7b014c7 --- /dev/null +++ b/tutorials/coupled_nets_power_heat.ipynb @@ -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 +}