Skip to content
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

Heat pump active power was determined regardless its maximum power. N… #428

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -9,10 +9,6 @@ repos:
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
@@ -22,6 +18,10 @@ repos:
rev: v3.4.0
hooks:
- id: pyupgrade
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
#- repo: https://github.com/pycqa/pylint
# rev: pylint-2.6.0
# hooks:
1 change: 1 addition & 0 deletions doc/whatsnew/v0-3-0.rst
Original file line number Diff line number Diff line change
@@ -28,3 +28,4 @@ Changes
* Move function to assign feeder to Topology class and add methods to the Grid class to get information on the feeders `#360 <https://github.com/openego/eDisGo/pull/360>`_
* Added a storage operation strategy where the storage is charged when PV feed-in is higher than electricity demand of the household and discharged when electricity demand exceeds PV generation `#386 <https://github.com/openego/eDisGo/pull/386>`_
* Added an estimation of the voltage deviation over a cable when selecting a suitable cable to connect a new component `#411 <https://github.com/openego/eDisGo/pull/411>`_
* Added clipping of heat pump electrical power at its maximum value #428 <https://github.com/openego/eDisGo/pull/428>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be correctly formatted

27 changes: 27 additions & 0 deletions edisgo/flex_opt/heat_pump_operation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import logging

import pandas as pd
@@ -31,11 +32,37 @@ def operating_strategy(
if heat_pump_names is None:
heat_pump_names = edisgo_obj.heat_pump.cop_df.columns

missing = set(heat_pump_names) - set(edisgo_obj.topology.loads_df.index)
if missing:
logger.warning(
f"The following heat pumps are are in the heat pump class but not yet "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove double "are"

f"integrated into the topology class. Therefore, their maximum capacity "
f"cannot be considered in the operating strategies. {missing=}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a note similar to "For better results, please integrate these heat pumps into the topology before running this function.".

)

if strategy == "uncontrolled":
ts = (
edisgo_obj.heat_pump.heat_demand_df.loc[:, heat_pump_names]
/ edisgo_obj.heat_pump.cop_df.loc[:, heat_pump_names]
)

ts_prev = copy.deepcopy(ts)

# clips heat pump load at maximum level
in_topology = list(set(heat_pump_names) - set(missing))

ts_clipped = ts[in_topology].clip(
upper=edisgo_obj.topology.loads_df.p_set[in_topology].values
)
ts[in_topology] = ts_clipped[in_topology]

clipped = ts.eq(ts_prev).all()[lambda x: ~x].index
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be more readable py using pandas.series.ne

logger.warning(
f"Heat pump power at {clipped} was "
f"clipped at its maximum capacity. The heat demand at this bus "
f"should be covered by additional heat sources."
)

edisgo_obj.timeseries.add_component_time_series(
"loads_active_power",
ts,
1 change: 1 addition & 0 deletions edisgo/io/heat_pump_import.py
Original file line number Diff line number Diff line change
@@ -149,6 +149,7 @@ def _get_central_heat_pump_or_resistive_heaters(carrier):
<= edisgo_object.config["grid_connection"]["upper_limit_voltage_level_4"],
)
df = pd.read_sql(query.statement, engine, index_col=None)
# Alternativ Code einfügen, falls Tabelle leer ist
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this temp comment

if not df.empty:
# get geom of heat bus, weather_cell_id, district_heating_id and area_id
srid_etrago_bus = db.get_srid_of_db_table(session, egon_etrago_bus.geom)
1 change: 0 additions & 1 deletion edisgo/network/timeseries.py
Original file line number Diff line number Diff line change
@@ -2120,7 +2120,6 @@ def add_component_time_series(self, df_name, ts_new):
:attr:`~.network.timeseries.TimeSeries.generators_active_power`.
ts_new : :pandas:`pandas.DataFrame<DataFrame>`
Dataframe with new time series to add to existing time series dataframe.

"""
# drop possibly already existing time series
self.drop_component_time_series(df_name, ts_new.columns)
2 changes: 1 addition & 1 deletion examples/edisgo_simple_example.ipynb
Original file line number Diff line number Diff line change
@@ -892,7 +892,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
"version": "3.10.16"
},
"toc": {
"base_numbering": 1,
26 changes: 24 additions & 2 deletions examples/electromobility_example.ipynb
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@
"outputs": [],
"source": [
"# interactive matplotlib\n",
"%matplotlib notebook"
"%matplotlib ipympl"
]
},
{
@@ -407,6 +407,28 @@
" * If the power rating of the charging point is <= 4.5 MVA, it is connected to the nearest grid connection point or cable. If a cable is selected, the line is cut at the point closest to the charging station and a new branch tee is added to which the charging station is connected."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5997b187-e1dd-4a55-a13b-f665f5fa5921",
"metadata": {},
"outputs": [],
"source": [
"simbev_example_data_path = os.path.join(\n",
" os.path.expanduser(\"~\"), \".edisgo\", \"simbev_example_data\"\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "feb0cc6b-45ce-46b1-ad7e-ff1095aad480",
"metadata": {},
"outputs": [],
"source": [
"simbev_example_data_path"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -774,7 +796,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.18"
"version": "3.10.16"
},
"toc": {
"base_numbering": 1,
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ def read(fname):
"geoalchemy2 < 0.7.0",
"geopandas >= 0.12.0",
"geopy >= 2.0.0",
"ipympl",
"jupyterlab",
"jupyter_dash",
"matplotlib >= 3.3.0",
63 changes: 47 additions & 16 deletions tests/flex_opt/test_heat_pump_operation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pandas as pd
import pytest

from numpy.random import default_rng

from edisgo import EDisGo
from edisgo.flex_opt.heat_pump_operation import operating_strategy

@@ -9,23 +11,45 @@ class TestHeatPumpOperation:
@classmethod
def setup_class(self):
self.timeindex = pd.date_range("1/1/2011 12:00", periods=2, freq="H")

self.edisgo = EDisGo(
ding0_grid=pytest.ding0_test_network_path, timeindex=self.timeindex
)

# insert heat pumps into topology
rng = default_rng(1)
number_of_heat_pumps = 2
self.name = []
for i in range(number_of_heat_pumps):
self.name.append(
self.edisgo.add_component(
comp_type="load",
bus=rng.choice(self.edisgo.topology.buses_df.index, size=1)[0],
p_set=0.43,
type="heat_pump",
)
)

# add one addtional hp that is not in the topology
self.name.append("hp" + str(len(self.name) + 1))

self.cop = pd.DataFrame(
data={
"hp1": [5.0, 6.0],
"hp2": [7.0, 8.0],
self.name[0]: [5.0, 6.0],
self.name[1]: [7.0, 8.0],
self.name[2]: [7.2, 6.7],
},
index=self.timeindex,
)
self.heat_demand = pd.DataFrame(
data={
"hp1": [1.0, 2.0],
"hp2": [3.0, 4.0],
self.name[0]: [1.0, 4.0],
self.name[1]: [3.0, 4.0],
self.name[2]: [2.0, 3.0],
},
index=self.timeindex,
)
self.edisgo = EDisGo(
ding0_grid=pytest.ding0_test_network_path, timeindex=self.timeindex
)

self.edisgo.heat_pump.cop_df = self.cop
self.edisgo.heat_pump.heat_demand_df = self.heat_demand

@@ -35,8 +59,9 @@ def test_operating_strategy(self):

hp_ts = pd.DataFrame(
data={
"hp1": [0.2, 1 / 3],
"hp2": [3 / 7, 0.5],
self.name[0]: [0.2, 0.43],
self.name[1]: [3 / 7, 0.43],
self.name[2]: [2 / 7.2, 3 / 6.7],
},
index=self.timeindex,
)
@@ -46,8 +71,9 @@ def test_operating_strategy(self):
)
hp_ts = pd.DataFrame(
data={
"hp1": [0.0, 0.0],
"hp2": [0.0, 0.0],
self.name[0]: [0.0, 0.0],
self.name[1]: [0.0, 0.0],
self.name[2]: [0.0, 0.0],
},
index=self.timeindex,
)
@@ -58,13 +84,18 @@ def test_operating_strategy(self):

# test with providing heat pump names
timestep = self.timeindex[0]
self.edisgo.heat_pump.heat_demand_df.at[timestep, "hp1"] = 0.0
self.edisgo.heat_pump.heat_demand_df.at[timestep, "hp2"] = 0.0
self.edisgo.heat_pump.heat_demand_df.at[timestep, self.name[0]] = 0.0
self.edisgo.heat_pump.heat_demand_df.at[timestep, self.name[1]] = 0.0

operating_strategy(self.edisgo, heat_pump_names=["hp1"])
operating_strategy(self.edisgo, heat_pump_names=[self.name[0]])

assert self.edisgo.timeseries.loads_active_power.at[timestep, "hp1"] == 0.0
assert self.edisgo.timeseries.loads_active_power.at[timestep, "hp2"] == 3 / 7
assert (
self.edisgo.timeseries.loads_active_power.at[timestep, self.name[0]] == 0.0
)
assert (
self.edisgo.timeseries.loads_active_power.at[timestep, self.name[1]]
== 3 / 7
)

# test error raising
msg = "Heat pump operating strategy dummy is not a valid option."