"""
AqvaHeat Heat Pump Module
=========================
Vacuum ice slurry generator with heat pump.
:author: Dipl.-Ing. (FH) Jonas Pfeiffer
"""
import CoolProp.CoolProp as CP
import numpy as np
from districtheatingsim.constants import KELVIN_OFFSET
from districtheatingsim.heat_generators.base_heat_pumps import HeatPump
[docs]
class AqvaHeat(HeatPump):
"""
AqvaHeat vacuum ice slurry heat pump system.
:param name: System identifier
:type name: str
:param nominal_power: Nominal power [kW], defaults to 100
:type nominal_power: float
:param temperature_difference: Temperature difference [K], defaults to 0
:type temperature_difference: float
"""
[docs]
def __init__(self, name, nominal_power=100, temperature_difference=0):
self.name = name
self.nominal_power = nominal_power
self.min_partial_load = 1 # no partial load for now (0..1)
self.temperature_difference = 2.5 # difference over heat exchanger
self.Wärmeleistung_FW_WP = nominal_power
[docs]
def calculate(self, economic_parameters, duration, load_profile, **kwargs):
"""
Calculate AqvaHeat system performance.
:param economic_parameters: Economic parameters
:type economic_parameters: dict
:param duration: Simulation duration [h]
:type duration: float
:param load_profile: Load profile [kW]
:type load_profile: numpy.ndarray
:param kwargs: VLT_L (flow temperatures), COP_data (COP interpolation data)
:return: Performance metrics and results
:rtype: dict
"""
VLT_L = kwargs.get("VLT_L")
COP_data = kwargs.get("COP_data")
residual_powers = load_profile
effective_powers = np.zeros_like(residual_powers)
intermediate_temperature = 12 # °C
# calculate power in time steps where operation of aggregate is possible due to minimal partial load
operation_mask = residual_powers >= self.nominal_power * self.min_partial_load
effective_powers[operation_mask] = np.minimum(residual_powers[operation_mask], self.nominal_power)
# HEAT PUMP
# calculate first the heat pump (from 12°C to supply temperature)
COP, effective_output_temperatures = self.calculate_COP(VLT_L, intermediate_temperature, COP_data)
cooling_powers = effective_powers * (1 - (1 / COP))
electrical_powers = effective_powers - cooling_powers
# disable heat pump when not reaching supply temperature
operation_mask = (
effective_output_temperatures >= VLT_L - self.temperature_difference
) # TODO: verify direction of difference
effective_powers[~operation_mask] = 0
cooling_powers[~operation_mask] = 0
electrical_powers[~operation_mask] = 0
# sum energy over whole lifetime
# convert to MWh
heat_supplied = np.sum(effective_powers / 1000) * duration
# VACUUM ICE GENERATOR
# now the vacuum ice generator, needs to supply 12°C from river water to the heatpump
# cooling supplied by heat pump is heat supplied by vacuum ice process
isentropic_efficiency = 0.7 # Adjust this value based on the actual compressor efficiency
fluid = "Water"
# Triple point conditions for water
# temperature_triple_point = 273.16 # Temperature in Kelvin
# pressure_triple_point = 611.657 # Pressure in Pascal
# Define initial conditions
triple_point_pressure = (
CP.PropsSI("ptriple", "T", 0, "P", 0, fluid) + 0.01
) # in Pascal, delta because of validity range
triple_point_temperature = CP.PropsSI(
"T", "Q", 0, "P", triple_point_pressure + 1, fluid
) # Triple point temperature
initial_pressure = triple_point_pressure
initial_temperature = triple_point_temperature
# Define final conditions after first compression
final_temperature = 12 + KELVIN_OFFSET # Convert to Kelvin
final_pressure = CP.PropsSI("P", "T", final_temperature, "Q", 0, fluid)
# mass flow from condensing vapor at 12°C, 14hPa
mass_flows = effective_powers / (
CP.PropsSI("H", "P", 14000, "Q", 1, "Water") - CP.PropsSI("H", "P", 14000, "Q", 0, "Water")
)
# electrical power needed compressing vapor from triple point
energy_compression = (
CP.PropsSI("H", "T", final_temperature, "P", final_pressure, fluid)
- CP.PropsSI("H", "T", initial_temperature, "P", initial_pressure, fluid)
) / isentropic_efficiency
electrical_powers += mass_flows * energy_compression / 1000 # W -> kW
self.Wärmemenge_AqvaHeat = heat_supplied
self.Wärmeleistung_kW = effective_powers
electricity_consumed = np.sum(electrical_powers / 1000) * duration
self.Strombedarf_AqvaHeat = electricity_consumed
self.el_Leistung_kW = electrical_powers
WGK_Abwärme = -1
self.primärenergie = self.Strombedarf_AqvaHeat * self.primärenergiefaktor
self.spec_co2_total = -1
results = {
"tech_name": self.name,
"Wärmemenge": self.Wärmemenge_AqvaHeat, # heat energy for whole duration
"Wärmeleistung_L": self.Wärmeleistung_kW, # vector length time steps with actual power supplied
"Strombedarf": self.Strombedarf_AqvaHeat, # electrical energy consumed during whole duration
"el_Leistung_L": self.el_Leistung_kW, # vector length time steps with actual electrical power consumed
"WGK": WGK_Abwärme,
"spec_co2_total": self.spec_co2_total, # tCO2/MWh_heat
"primärenergie": self.primärenergie,
"color": "blue",
}
return results
[docs]
def set_parameters(self, variables, variables_order, idx):
pass
[docs]
def add_optimization_parameters(self, idx):
"""
Return empty optimization parameters (no optimization for AqvaHeat).
:param idx: Technology index
:type idx: int
:return: Empty lists (initial_values, variables_order, bounds)
:rtype: tuple
"""
return [], [], []
[docs]
def get_display_text(self):
return f"Name: {self.name}, Nennleistung: {self.nominal_power} kW, Temperaturdifferenz: {self.temperature_difference} K"