238 lines
11 KiB
Python
238 lines
11 KiB
Python
"""
|
|
Total operational costs module
|
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|
Copyright © 2024 Project Coder Saeed Ranjbar saeed.ranjbar@mail.concordia.ca
|
|
Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca
|
|
"""
|
|
import math
|
|
import pandas as pd
|
|
|
|
from hub.city_model_structure.building import Building
|
|
import hub.helpers.constants as cte
|
|
|
|
from costing_package.configuration import Configuration
|
|
from costing_package.cost_base import CostBase
|
|
from costing_package.peak_load import PeakLoad
|
|
|
|
|
|
class TotalOperationalCosts(CostBase):
|
|
"""
|
|
Total Operational costs class
|
|
"""
|
|
|
|
def __init__(self, building: Building, configuration: Configuration):
|
|
super().__init__(building, configuration)
|
|
columns_list = self.columns()
|
|
self._yearly_operational_costs = pd.DataFrame(
|
|
index=self._rng,
|
|
columns=columns_list,
|
|
dtype='float'
|
|
)
|
|
|
|
def calculate(self) -> pd.DataFrame:
|
|
"""
|
|
Calculate total operational costs
|
|
:return: pd.DataFrame
|
|
"""
|
|
building = self._building
|
|
fuel_consumption_breakdown = building.energy_consumption_breakdown
|
|
archetype = self._archetype
|
|
total_floor_area = self._total_floor_area
|
|
if archetype.function == 'residential':
|
|
factor = total_floor_area / 80
|
|
else:
|
|
factor = 1
|
|
total_electricity_consumption = sum(self._building.energy_consumption_breakdown[cte.ELECTRICITY].values()) / 3600
|
|
peak_electricity_load = PeakLoad(self._building).electricity_peak_load
|
|
peak_load_value = peak_electricity_load.max(axis=1)
|
|
peak_electricity_demand = peak_load_value[1] / 1000 # self._peak_electricity_demand adapted to kW
|
|
for system_fuel in self._configuration.fuel_type:
|
|
fuel = None
|
|
for fuel_tariff in self._configuration.fuel_tariffs:
|
|
if system_fuel in fuel_tariff:
|
|
fuel = self.search_fuel(system_fuel, fuel_tariff)
|
|
if fuel.type == cte.ELECTRICITY:
|
|
if fuel.variable.rate_type == 'fixed':
|
|
variable_electricity_cost_year_0 = (
|
|
total_electricity_consumption * float(fuel.variable.values[0]) / 1000
|
|
)
|
|
else:
|
|
hourly_electricity_consumption = self.hourly_fuel_consumption_profile(fuel.type)
|
|
hourly_electricity_price_profile = fuel.variable.values * len(hourly_electricity_consumption)
|
|
hourly_electricity_price = [hourly_electricity_consumption[i] / 1000 * hourly_electricity_price_profile[i]
|
|
for i in range(len(hourly_electricity_consumption))]
|
|
variable_electricity_cost_year_0 = sum(hourly_electricity_price)
|
|
peak_electricity_cost_year_0 = peak_electricity_demand * fuel.fixed_power * 12
|
|
monthly_electricity_cost_year_0 = fuel.fixed_monthly * 12 * factor
|
|
for year in range(1, self._configuration.number_of_years + 1):
|
|
price_increase_electricity = math.pow(1 + self._configuration.electricity_price_index, year)
|
|
price_increase_peak_electricity = math.pow(1 + self._configuration.electricity_peak_index, year)
|
|
self._yearly_operational_costs.at[year, 'Fixed Costs Electricity Peak'] = (
|
|
peak_electricity_cost_year_0 * price_increase_peak_electricity
|
|
)
|
|
self._yearly_operational_costs.at[year, 'Fixed Costs Electricity Monthly'] = (
|
|
monthly_electricity_cost_year_0 * price_increase_peak_electricity
|
|
)
|
|
if not isinstance(variable_electricity_cost_year_0, pd.DataFrame):
|
|
variable_costs_electricity = variable_electricity_cost_year_0 * price_increase_electricity
|
|
else:
|
|
variable_costs_electricity = float(variable_electricity_cost_year_0.iloc[0] * price_increase_electricity)
|
|
self._yearly_operational_costs.at[year, 'Variable Costs Electricity'] = (
|
|
variable_costs_electricity
|
|
)
|
|
else:
|
|
fuel_fixed_cost = fuel.fixed_monthly * 12 * factor
|
|
if fuel.type == cte.BIOMASS:
|
|
conversion_factor = 1
|
|
else:
|
|
conversion_factor = fuel.density[0]
|
|
if fuel.variable.rate_type == 'fixed':
|
|
variable_cost_fuel = (
|
|
(sum(fuel_consumption_breakdown[fuel.type].values()) / (
|
|
1e6 * fuel.lower_heating_value[0] * conversion_factor)) * fuel.variable.values[0])
|
|
|
|
else:
|
|
hourly_fuel_consumption = self.hourly_fuel_consumption_profile(fuel.type)
|
|
hourly_fuel_price_profile = fuel.variable.values * len(hourly_fuel_consumption)
|
|
hourly_fuel_price = [hourly_fuel_consumption[i] / (
|
|
1e6 * fuel.lower_heating_value[0] * conversion_factor) * hourly_fuel_price_profile[i]
|
|
for i in range(len(hourly_fuel_consumption))]
|
|
variable_cost_fuel = sum(hourly_fuel_price)
|
|
|
|
for year in range(1, self._configuration.number_of_years + 1):
|
|
price_increase_gas = math.pow(1 + self._configuration.gas_price_index, year)
|
|
self._yearly_operational_costs.at[year, f'Fixed Costs {fuel.type}'] = fuel_fixed_cost * price_increase_gas
|
|
self._yearly_operational_costs.at[year, f'Variable Costs {fuel.type}'] = (
|
|
variable_cost_fuel * price_increase_gas)
|
|
self._yearly_operational_costs.fillna(0, inplace=True)
|
|
|
|
return self._yearly_operational_costs
|
|
|
|
def columns(self):
|
|
columns_list = []
|
|
fuels = [key for key in self._building.energy_consumption_breakdown.keys()]
|
|
for fuel in fuels:
|
|
if fuel == cte.ELECTRICITY:
|
|
columns_list.append('Fixed Costs Electricity Peak')
|
|
columns_list.append('Fixed Costs Electricity Monthly')
|
|
columns_list.append('Variable Costs Electricity')
|
|
else:
|
|
columns_list.append(f'Fixed Costs {fuel}')
|
|
columns_list.append(f'Variable Costs {fuel}')
|
|
|
|
return columns_list
|
|
|
|
def search_fuel(self, system_fuel, tariff):
|
|
fuels = self._archetype.operational_cost.fuels
|
|
for fuel in fuels:
|
|
if system_fuel == fuel.type and tariff == fuel.variable.name:
|
|
return fuel
|
|
raise KeyError(f'fuel {system_fuel} with {tariff} tariff not found')
|
|
|
|
|
|
def hourly_fuel_consumption_profile(self, fuel_type):
|
|
hourly_fuel_consumption = []
|
|
energy_systems = self._building.energy_systems
|
|
if fuel_type == cte.ELECTRICITY:
|
|
appliance = self._building.appliances_electrical_demand[cte.HOUR]
|
|
lighting = self._building.lighting_electrical_demand[cte.HOUR]
|
|
elec_heating = 0
|
|
elec_cooling = 0
|
|
elec_dhw = 0
|
|
if cte.HEATING in self._building.energy_consumption_breakdown[cte.ELECTRICITY]:
|
|
elec_heating = 1
|
|
if cte.COOLING in self._building.energy_consumption_breakdown[cte.ELECTRICITY]:
|
|
elec_cooling = 1
|
|
if cte.DOMESTIC_HOT_WATER in self._building.energy_consumption_breakdown[cte.ELECTRICITY]:
|
|
elec_dhw = 1
|
|
heating = None
|
|
cooling = None
|
|
dhw = None
|
|
|
|
if elec_heating == 1:
|
|
for energy_system in energy_systems:
|
|
if cte.HEATING in energy_system.demand_types:
|
|
for generation_system in energy_system.generation_systems:
|
|
if generation_system.fuel_type == cte.ELECTRICITY:
|
|
if cte.HEATING in generation_system.energy_consumption:
|
|
heating = generation_system.energy_consumption[cte.HEATING][cte.HOUR]
|
|
else:
|
|
if len(energy_system.generation_systems) > 1:
|
|
heating = [x / 2 for x in self._building.heating_consumption[cte.HOUR]]
|
|
else:
|
|
heating = self._building.heating_consumption[cte.HOUR]
|
|
|
|
if elec_dhw == 1:
|
|
for energy_system in energy_systems:
|
|
if cte.DOMESTIC_HOT_WATER in energy_system.demand_types:
|
|
for generation_system in energy_system.generation_systems:
|
|
if generation_system.fuel_type == cte.ELECTRICITY:
|
|
if cte.DOMESTIC_HOT_WATER in generation_system.energy_consumption:
|
|
dhw = generation_system.energy_consumption[cte.DOMESTIC_HOT_WATER][cte.HOUR]
|
|
else:
|
|
if len(energy_system.generation_systems) > 1:
|
|
dhw = [x / 2 for x in self._building.domestic_hot_water_consumption[cte.HOUR]]
|
|
else:
|
|
dhw = self._building.domestic_hot_water_consumption[cte.HOUR]
|
|
|
|
if elec_cooling == 1:
|
|
for energy_system in energy_systems:
|
|
if cte.COOLING in energy_system.demand_types:
|
|
for generation_system in energy_system.generation_systems:
|
|
if cte.COOLING in generation_system.energy_consumption:
|
|
cooling = generation_system.energy_consumption[cte.COOLING][cte.HOUR]
|
|
else:
|
|
if len(energy_system.generation_systems) > 1:
|
|
cooling = [x / 2 for x in self._building.cooling_consumption[cte.HOUR]]
|
|
else:
|
|
cooling = self._building.cooling_consumption[cte.HOUR]
|
|
|
|
for i in range(len(self._building.heating_demand[cte.HOUR])):
|
|
hourly = 0
|
|
hourly += appliance[i] / 3600
|
|
hourly += lighting[i] / 3600
|
|
if heating is not None:
|
|
hourly += heating[i] / 3600
|
|
if cooling is not None:
|
|
hourly += cooling[i] / 3600
|
|
if dhw is not None:
|
|
hourly += dhw[i] / 3600
|
|
hourly_fuel_consumption.append(hourly)
|
|
else:
|
|
heating = None
|
|
dhw = None
|
|
if cte.HEATING in self._building.energy_consumption_breakdown[fuel_type]:
|
|
for energy_system in energy_systems:
|
|
if cte.HEATING in energy_system.demand_types:
|
|
for generation_system in energy_system.generation_systems:
|
|
if cte.HEATING in generation_system.energy_consumption:
|
|
heating = generation_system.energy_consumption[cte.HEATING][cte.HOUR]
|
|
else:
|
|
if len(energy_system.generation_systems) > 1:
|
|
heating = [x / 2 for x in self._building.heating_consumption[cte.HOUR]]
|
|
else:
|
|
heating = self._building.heating_consumption[cte.HOUR]
|
|
if cte.DOMESTIC_HOT_WATER in self._building.energy_consumption_breakdown[fuel_type]:
|
|
for energy_system in energy_systems:
|
|
if cte.DOMESTIC_HOT_WATER in energy_system.demand_types:
|
|
for generation_system in energy_system.generation_systems:
|
|
if cte.DOMESTIC_HOT_WATER in generation_system.energy_consumption:
|
|
dhw = generation_system.energy_consumption[cte.DOMESTIC_HOT_WATER][cte.HOUR]
|
|
else:
|
|
if len(energy_system.generation_systems) > 1:
|
|
dhw = [x / 2 for x in self._building.domestic_hot_water_consumption[cte.HOUR]]
|
|
else:
|
|
dhw = self._building.domestic_hot_water_consumption[cte.HOUR]
|
|
|
|
for i in range(len(self._building.heating_demand[cte.HOUR])):
|
|
hourly = 0
|
|
if heating is not None:
|
|
hourly += heating[i] / 3600
|
|
if dhw is not None:
|
|
hourly += dhw[i] / 3600
|
|
hourly_fuel_consumption.append(hourly)
|
|
return hourly_fuel_consumption
|
|
|
|
|
|
|