Special Hot Water
- heatpro.special_hot_water.special_hot_water(external_factors: ExternalFactors, total_heating_including_hotwater: Series, monthly_hot_water_profile: DataFrame, temperature_hot_water: float, hourly_hot_water_day_profil: Series, name: str = 'hot_water')[source]
Calculate the hourly energy demand for hot water considering external factors and profiles.
- Parameters:
external_factors (ExternalFactors) – Object containing external factors affecting hot water energy demand.
total_heating_including_hotwater (pd.Series) – Monthly total heat demand including hot water.
monthly_hot_water_profile (pd.DataFrame) – Monthly hot water profile (In term of quantity i.e. L).
temperature_hot_water (float) – The temperature of the hot water.
hourly_hot_water_day_profil (pd.Series) – Hourly profile for hot water demand (In term of quantity i.e. L).
name (str, optional) – Name of the demand. Defaults to “hot_water”.
- Returns:
The hourly demand for hot water.
- Return type:
Explanation
special_hot_water function¶
This note aims to explain how heatpro.special_hot_water.special_hot_water is working
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.io as pio
pio.renderers.default = "notebook"
pd.options.plotting.backend = "plotly"
Prerequisites : External Factors and Induced Factors¶
External Factors are exogenous data.
from heatpro.external_factors import ExternalFactors
df = pd.read_csv("../data/external_factors.csv", parse_dates=True, index_col=0)
df.index = pd.date_range(start="2021", freq="h", periods=8760 * 2)
external_factors = ExternalFactors(
temperature=df["external_temperature"], heating_season=df["heating_season"]
)
external_factors.plot()
Induced factor comes from External Factors processing.
Cold water temperature correspond to temparature in drinking water distribution network.
Closed heating season correspond to a period where months have at least one day in the heating season. Consumption on Closed Heating Season while be used to evaluate a normalized heat consomption for hot water.
from heatpro.external_factors import (
TemperatureThreshold,
Threshold,
closed_heating_season,
burch_cold_water,
basic_temperature_supply,
basic_temperature_return,
kasuda_soil_temperature,
)
induced_factors = pd.concat(
(
closed_heating_season(external_factors),
burch_cold_water(external_factors),
basic_temperature_supply(
external_factors,
TemperatureThreshold(
Threshold(80, 110),
Threshold(70, 90),
outside_mid=15,
outside_min=-15,
),
),
basic_temperature_return(external_factors, 70, 60),
kasuda_soil_temperature(external_factors, d=3, alpha=0.078),
),
axis=1,
)
induced_factors.astype(float).plot().show()
fig = go.Figure(layout_title="Difference between Heating Season and Closed Heating Season")
fig.add_trace(
go.Scatter(
x=external_factors.temperature.index,
y=external_factors.heating_season,
name="Heating Season",
)
)
fig.add_trace(
go.Scatter(
x=induced_factors.index,
y=induced_factors.closed_heating_season,
name="Closed Heating Season",
)
)
fig.show()
Beginning with a monthly energy consumption for heating including hot water consumption.
from heatpro.check import ENERGY_FEATURE_NAME
np.random.seed(22)
total_heating_including_hotwater = pd.Series(
np.random.rand(24) * 10_000 + 60_000, # Heating demand randomly initialized
index=pd.date_range("2021", freq="MS", end="2023", inclusive="left"), # Monthly indexing
name=ENERGY_FEATURE_NAME,
)
total_heating_including_hotwater.plot()
Monthly hot water consumption (In terms of quantity of hot water and not energy) is given. Sum on each year of weights equals 1 as it is a profile (consequently normalized). $$ \forall~year, \int_{year}Q(t)dt = \sum_{month\in year}Q(month)= 1 $$
monthly_hot_water_profile = (
pd.DataFrame(
[1.13, 1.11, 1.04, 1.04, 1.0, 0.93, 0.8, 0.74, 0.98, 1.0, 1.09, 1.14] * 2,
columns=["weight"],
index=total_heating_including_hotwater.index,
)
/ 12
) # To normalize
monthly_hot_water_profile.plot()
monthly_hot_water_profile.resample("YE").sum() # Sum over each year
| weight | |
|---|---|
| 2021-12-31 | 1.0 |
| 2022-12-31 | 1.0 |
Monthly profile is disaggregate into hourly profile taking into account number of day in each month and number of hour in each day
$$ \forall~year, \int_{year}Q(t)dt = \sum_{hour \in month,month\in year}Q(hour,month)= 1 $$
from heatpro.demand_profile import day_length_proportionnal_weight
hourly_hot_water_month_profile = (
(
day_length_proportionnal_weight(monthly_hot_water_profile.index)
/ 24
* monthly_hot_water_profile["weight"]
)
.reindex(induced_factors.index)
.ffill()
)
fig = go.Figure(layout_title="Change to monthly weight to hourly weight (constant on month)")
# Add trace for previous monthly weight
fig.add_trace(
go.Scatter(
x=monthly_hot_water_profile.index,
y=monthly_hot_water_profile.weight,
name="Previous monthly weight",
yaxis="y1", # Assign to y-axis 1
)
)
# Add trace for new hourly weight
fig.add_trace(
go.Scatter(
x=hourly_hot_water_month_profile.index,
y=hourly_hot_water_month_profile,
name="New hourly weight",
yaxis="y2", # Assign to y-axis 2
)
)
# Define y-axis properties
fig.update_layout(
yaxis=dict(
title="Previous monthly weight",
side="left", # Align with left side
),
yaxis2=dict(
title="New hourly weight",
overlaying="y", # Overlay on top of first y-axis
side="right", # Align with right side
),
legend=dict(
orientation="h", # Horizontal orientation
yanchor="top", # Anchor to the top
y=-0.1, # Position below the plot
x=0.5,
xanchor="center", # Anchor to the center
),
)
# Show the figure
fig.show()
Sum of hourly weights over a year equal 1.
hourly_hot_water_month_profile.resample("YE").sum()
2021-12-31 1.0 2022-12-31 1.0 Freq: YE-DEC, Name: weight, dtype: float64
Energy consumption on Closed Non Heating Season (supposedly equals to energy consumption for hot water production)
from heatpro.external_factors import CLOSED_HEATING_SEASON_NAME
non_heating_season_consumption = total_heating_including_hotwater[
~induced_factors[CLOSED_HEATING_SEASON_NAME].reindex(total_heating_including_hotwater.index)
].sum()
df = pd.DataFrame(
{
"Year": total_heating_including_hotwater[
~induced_factors[CLOSED_HEATING_SEASON_NAME].reindex(
total_heating_including_hotwater.index
)
].index.year,
"Month": total_heating_including_hotwater[
~induced_factors[CLOSED_HEATING_SEASON_NAME].reindex(
total_heating_including_hotwater.index
)
].index.month,
}
)
print(f"Non heating season consumption : {non_heating_season_consumption:.0f} kWh")
print(f"Overall consumption : {total_heating_including_hotwater.sum():.0f} kWh")
Non heating season consumption : 522462 kWh Overall consumption : 1559876 kWh
Get hourly energy consumption using gap temperature through time. For now consumption is constant on each day.
$$ \forall~day, E(day) = \frac{\int_{day}Q(t)\cdot(T^{(\text{prod})}-T_t^{(\text{Cold Water})})dt}{\int_{\text{Non Heating Season}}Q(t)\cdot(T^{(\text{prod})}-T_t^{(\text{Cold Water})})dt} \cdot \int_{\text{Non Heating Season}} E(t)dt $$
$$ \forall~hour \in~day, E(hour) = P(hour) \cdot E(day) $$ where, $$ \sum_{hour\in day}P(hour) = 1 $$
Temperature_hot_water = 60
daily_hot_water_energy_consumption = (
non_heating_season_consumption
* (
hourly_hot_water_month_profile
* (Temperature_hot_water - induced_factors["cold_water_temperature"])
)
.groupby(hourly_hot_water_month_profile.index.date)
.transform("sum")
/ (
hourly_hot_water_month_profile
* (Temperature_hot_water - induced_factors["cold_water_temperature"])
)[~induced_factors["closed_heating_season"]].sum()
).rename(ENERGY_FEATURE_NAME)
daily_hot_water_energy_consumption.plot().update_layout(title="Daily consumption").show()
for _, row in df.iterrows():
mask_daily_hot_water_energy_consumption = (
pd.DatetimeIndex(daily_hot_water_energy_consumption.index).to_period("M")
== f"{row.Year}-{row.Month:02d}"
)
mask_total_heating_including_hotwater = (
pd.DatetimeIndex(total_heating_including_hotwater.index).to_period("M")
== f"{row.Year}-{row.Month:02d}"
)
daily_hot_water_energy_consumption.loc[mask_daily_hot_water_energy_consumption] = (
total_heating_including_hotwater.loc[mask_total_heating_including_hotwater].iloc[0]
* (
hourly_hot_water_month_profile
* (Temperature_hot_water - induced_factors["cold_water_temperature"])
)
.groupby(hourly_hot_water_month_profile.index.date)
.transform("sum")
/ (
hourly_hot_water_month_profile
* (Temperature_hot_water - induced_factors["cold_water_temperature"])
)[mask_daily_hot_water_energy_consumption].sum()
).rename(ENERGY_FEATURE_NAME)
daily_hot_water_energy_consumption.plot().update_layout(title="Daily consumption").show()
from heatpro.demand_profile import apply_hourly_pattern
from heatpro.demand_profile.hot_water_profile import basic_hot_water_hourly_profile
hot_water_raw_profil = {
0: 0.01,
1: 0,
2: 0,
3: 0,
4: 0,
5: 4.797,
6: 3.543,
7: 0.86,
8: 0.43,
9: 0.86,
10: 1.3,
11: 0,
12: 0.43,
13: 0.43,
14: 0.43,
15: 0,
16: 1.3,
17: 0.43,
18: 3.01,
19: 4.797,
20: 1.373,
21: 0,
22: 0,
23: 0,
}
hot_water_raw_profil = {key: value / 24 for key, value in hot_water_raw_profil.items()}
hourly_hot_water_raw_day_profil = apply_hourly_pattern(
daily_hot_water_energy_consumption.index, hot_water_raw_profil
)
hourly_hot_water_day_profil = basic_hot_water_hourly_profile(
hourly_hot_water_raw_day_profil["weight"],
0.1,
0.1,
)
final_hourly_hot_water_energy_consumption = pd.DataFrame(
(daily_hot_water_energy_consumption * hourly_hot_water_day_profil).rename(ENERGY_FEATURE_NAME)
)
fig = go.Figure(layout_title="Integrating daily profile")
# Add trace for previous monthly weight
fig.add_trace(
go.Scatter(
x=final_hourly_hot_water_energy_consumption.index,
y=final_hourly_hot_water_energy_consumption[ENERGY_FEATURE_NAME],
name="Hourly consumption",
yaxis="y1", # Assign to y-axis 1
)
)
# Add trace for previous monthly weight
fig.add_trace(
go.Scatter(
x=daily_hot_water_energy_consumption.index,
y=daily_hot_water_energy_consumption / 24,
name="Hourly consumption",
yaxis="y1", # Assign to y-axis 1
)
)
# Add trace for new hourly weight
fig.add_trace(
go.Scatter(
x=induced_factors.index,
y=induced_factors.cold_water_temperature,
name="Cold water temperature",
yaxis="y2", # Assign to y-axis 2
)
)
# Define y-axis properties
fig.update_layout(
yaxis=dict(
title="Hourly consumption",
side="left", # Align with left side
),
yaxis2=dict(
title="Cold water temperature",
overlaying="y", # Overlay on top of first y-axis
side="right", # Align with right side
),
legend=dict(
orientation="h", # Horizontal orientation
yanchor="top", # Anchor to the top
y=-0.1, # Position below the plot
x=0.5,
xanchor="center", # Anchor to the center
),
)
# Show the figure
fig.show()
from heatpro.temporal_demand import HourlyHeatDemand
hot_water_energy_load = HourlyHeatDemand("hot_water", final_hourly_hot_water_energy_consumption)
hot_water_energy_load.plot()
from heatpro.special_hot_water import special_hot_water
direct_hot_water_energy_load = special_hot_water(
external_factors,
total_heating_including_hotwater,
monthly_hot_water_profile,
Temperature_hot_water,
hourly_hot_water_day_profil,
name="hot_water",
)
direct_hot_water_energy_load.plot()