mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
added new pricing for air source heat pump
This commit is contained in:
parent
b881c8358e
commit
aa515a9797
2 changed files with 79 additions and 81 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import numpy as np
|
||||
from recommendations.county_to_region import county_to_region_map
|
||||
from utils.logger import setup_logger
|
||||
from backend.ml_models.AnnualBillSavings import AnnualBillSavings
|
||||
|
||||
logger = setup_logger()
|
||||
|
||||
|
|
@ -21,25 +22,6 @@ regional_labour_variations = [
|
|||
{"Region": "Northern Ireland", "Adjustment_Factor": 0.76}
|
||||
]
|
||||
|
||||
# This data is based on the MCS database - taken the figures for June 2024
|
||||
MCS_SOLAR_PV_COST_DATA = {
|
||||
"last_updated": "2024-07-10",
|
||||
"average_cost_per_kwh": 1825,
|
||||
"average_cost_per_kwh-Outer London": 1950,
|
||||
"average_cost_per_kwh-Inner London": 1950,
|
||||
"average_cost_per_kwh-South East England": 1966,
|
||||
"average_cost_per_kwh-South West England": 1864,
|
||||
"average_cost_per_kwh-East of England": 1719,
|
||||
"average_cost_per_kwh-East Midlands": 1730,
|
||||
"average_cost_per_kwh-West Midlands": 1789,
|
||||
"average_cost_per_kwh-North East England": 1872,
|
||||
"average_cost_per_kwh-North West England": 1860,
|
||||
"average_cost_per_kwh-Yorkshire and the Humber": 1789,
|
||||
"average_cost_per_kwh-Wales": 1676,
|
||||
"average_cost_per_kwh-Scotland": 1781,
|
||||
"average_cost_per_kwh-Northern Ireland": 1347,
|
||||
}
|
||||
|
||||
# Installers are now working with 435 watt panels
|
||||
PANEL_SIZE = 0.435
|
||||
|
||||
|
|
@ -61,47 +43,40 @@ INSTALLER_SOLAR_COSTS = [
|
|||
{'n_panels': 18, 'array_kwp': 18 * PANEL_SIZE, 'cost': 6792.57, 'installer': 'CEG'}
|
||||
]
|
||||
|
||||
# These are costs we received from CRG, for pricing up air source heat pumps
|
||||
# These are costs that we have been provided from CRG specifically for air source heat pumps
|
||||
ASHP_SMALL_SYSTEM_COST = 8812.92 # 4.8 to 8.5, based on their pricing
|
||||
ASHP_LARGE_SYSTEM_COST = 11053.25
|
||||
ASHP_SECURITY = 455.00
|
||||
ASHP_WALL_BRACKET = 574.17
|
||||
ASHP_DISTRIBUTION_SYSTEM_COSTS = [
|
||||
{"n_radiators": 4, "cost": 3380.00},
|
||||
{"n_radiators": 5, "cost": 3607.50},
|
||||
{"n_radiators": 6, "cost": 4116.67},
|
||||
{"n_radiators": 7, "cost": 4647.50},
|
||||
{"n_radiators": 8, "cost": 5200.00},
|
||||
{"n_radiators": 9, "cost": 5730.83},
|
||||
{"n_radiators": 10, "cost": 6283.33},
|
||||
{"n_radiators": 11, "cost": 6857.50},
|
||||
{"n_radiators": 12, "cost": 7431.67},
|
||||
{"n_radiators": 13, "cost": 8016.67},
|
||||
{"n_radiators": 14, "cost": 8612.50},
|
||||
{"n_radiators": 15, "cost": 9219.17},
|
||||
{"n_radiators": 16, "cost": 9804.17},
|
||||
{"n_radiators": 17, "cost": 10389.17},
|
||||
]
|
||||
ASHP_CYLINDER_COSTS = [
|
||||
{"capacity_l": 120, "cost": 3318.25},
|
||||
{"capacity_l": 180, "cost": 3480.75},
|
||||
{"capacity_l": 200, "cost": 3853.42},
|
||||
{"capacity_l": 250, "cost": 3961.75},
|
||||
]
|
||||
|
||||
# CEG uses use Solshare as an inverter to provide solar PV to multiple flats. This costs £7500 for the inverter alone
|
||||
# https://midsummerwholesale.co.uk/buy/solshare
|
||||
INSTALLER_SOLAR_PV_INVERTER_COST = 7500
|
||||
INSTALLER_SOLAR_PV_INVERTER_LABOUR_COST = 500 # Just a rough guess to labour costs
|
||||
|
||||
# INSTALLER_SCAFFOLDING_COSTS = [
|
||||
# {'stories': 1, 'description': '1 Story Scaffold', 'cost': 531.00, 'installer': 'CEG'},
|
||||
# {'stories': 2, 'description': '2 Story Scaffold', 'cost': 841.00, 'installer': 'CEG'},
|
||||
# {'stories': 3, 'description': '3 Story Scaffold', 'cost': 1077.00, 'installer': 'CEG'}
|
||||
# ]
|
||||
|
||||
# This data is based on the MCS database, We use the larger figure between the 2023 and 2024 average,
|
||||
# to be conservative
|
||||
MCS_AIR_SOURCE_HEAT_PUMP_COST_DATA = {
|
||||
"Outer London": 13220,
|
||||
"Inner London": 13220,
|
||||
"South East England": 13547,
|
||||
"South West England": 12776,
|
||||
"East of England": 12585,
|
||||
"East Midlands": 12239,
|
||||
"West Midlands": 13182,
|
||||
"North East England": 11829,
|
||||
"North West England": 11714,
|
||||
"Yorkshire and the Humber": 11919,
|
||||
"Wales": 13701,
|
||||
"Scotland": 12586,
|
||||
"Northern Ireland": 12000, # There are hardly any air source heat pump installs going on in Northern Ireland
|
||||
}
|
||||
|
||||
INSTALLER_ASHP_COSTS = [
|
||||
{'capacity_kw': 5.0, 'brand': 'Mitsubishi', 'tank_size_liters': 150, 'cost': 10149.53, 'installer': 'CEG'},
|
||||
{'capacity_kw': 6.0, 'brand': 'Mitsubishi', 'tank_size_liters': 170, 'cost': 10823.48, 'installer': 'CEG'},
|
||||
{'capacity_kw': 8.5, 'brand': 'Mitsubishi', 'tank_size_liters': 200, 'cost': 11312.43, 'installer': 'CEG'},
|
||||
{'capacity_kw': 11.2, 'brand': 'Mitsubishi', 'tank_size_liters': 250, 'cost': 12156.75, 'installer': 'CEG'},
|
||||
{'capacity_kw': 14.0, 'brand': 'Mitsubishi', 'tank_size_liters': 300, 'cost': 14405.54, 'installer': 'CEG'},
|
||||
{'capacity_kw': 14.0, 'brand': 'Mitsubishi', 'tank_size_liters': 300, 'cost': 14405.54, 'installer': 'CEG'},
|
||||
{'capacity_kw': 17.0, 'brand': 'Grant', 'tank_size_liters': 300, 'cost': 14445.00, 'installer': 'CEG'},
|
||||
{'capacity_kw': 20.0, 'brand': 'Ecoforest', 'tank_size_liters': 400, 'cost': 21189.41, 'installer': 'CEG'},
|
||||
{'capacity_kw': None, 'brand': '2 x cascaded ASHPs', 'tank_size_liters': 500, 'cost': 22950.00, 'installer': 'CEG'}
|
||||
]
|
||||
|
||||
INSTALLER_SOLAR_BATTERY_COSTS = [
|
||||
{'capacity_kwh': 5, 'description': 'Battery Add on', 'cost': 3769.89, 'installer': 'JJC'},
|
||||
# {'capacity_kwh': 10, 'description': 'Battery Add on', 'cost': 4300.00, 'installer': 'CEG'},
|
||||
|
|
@ -368,7 +343,7 @@ class Costs:
|
|||
|
||||
total_cost = total_cost + labour_cost
|
||||
|
||||
total_cost = round(total_cost, 2)
|
||||
total_cost = round(total_cost)
|
||||
|
||||
return {
|
||||
"total": total_cost,
|
||||
|
|
@ -853,32 +828,55 @@ class Costs:
|
|||
"labour_days": labour_days,
|
||||
}
|
||||
|
||||
def air_source_heat_pump(self, ashp_size):
|
||||
"""
|
||||
Based on the region and type of property, this function will produce a cost estimation for an air source heat
|
||||
pump. This cost will include the boiler upgrade scheme grant
|
||||
|
||||
"""
|
||||
# This is the average cost of a project, we'll add some additional contingency
|
||||
|
||||
if ashp_size is None:
|
||||
cost = [x for x in INSTALLER_ASHP_COSTS if x["capacity_kw"] is None][0]["cost"]
|
||||
@staticmethod
|
||||
def _select_cylinder_capacity(occupants: float):
|
||||
if occupants <= 2:
|
||||
return 120
|
||||
elif occupants <= 3:
|
||||
return 180
|
||||
elif occupants <= 4:
|
||||
return 200
|
||||
else:
|
||||
cost = [x for x in INSTALLER_ASHP_COSTS if x][0]["cost"]
|
||||
return 250
|
||||
|
||||
# The costs from installers exclude VAT
|
||||
vat = cost * self.VAT_RATE
|
||||
cost = cost + vat
|
||||
def air_source_heat_pump(self, ashp_size: float, number_heated_rooms: int, total_floor_area: float) -> dict:
|
||||
"""
|
||||
We produce a cost estimation for an air source heat pump, based on costs we have received from installers.
|
||||
|
||||
# We assume 5 days installation
|
||||
labour_days = 5
|
||||
labour_hours = labour_days * 8
|
||||
"""
|
||||
|
||||
system_cost = (
|
||||
(ASHP_SMALL_SYSTEM_COST if ashp_size <= 8.5 else ASHP_LARGE_SYSTEM_COST) + ASHP_SECURITY + ASHP_WALL_BRACKET
|
||||
)
|
||||
|
||||
available_n_rads = [x["n_radiators"] for x in ASHP_DISTRIBUTION_SYSTEM_COSTS]
|
||||
if number_heated_rooms < min(available_n_rads):
|
||||
# We use the smallest value
|
||||
rads_to_use = min(available_n_rads)
|
||||
elif number_heated_rooms > max(available_n_rads):
|
||||
# We use the largest value
|
||||
rads_to_use = max(available_n_rads)
|
||||
else:
|
||||
rads_to_use = int(number_heated_rooms)
|
||||
|
||||
distribution_system_cost = [
|
||||
x for x in ASHP_DISTRIBUTION_SYSTEM_COSTS if x["n_radiators"] == rads_to_use
|
||||
][0]["cost"]
|
||||
|
||||
# Cylinder cost
|
||||
est_n_occupants = AnnualBillSavings.calculate_occupants(total_floor_area)
|
||||
cylinder_capacity = self._select_cylinder_capacity(est_n_occupants)
|
||||
cylinder_cost = [
|
||||
x for x in ASHP_CYLINDER_COSTS if x["capacity_l"] == cylinder_capacity
|
||||
][0]["cost"]
|
||||
|
||||
total = system_cost + distribution_system_cost + cylinder_cost
|
||||
|
||||
return {
|
||||
"total": cost,
|
||||
"contingency": cost * self.CONTINGENCIES["air_source_heat_pump"],
|
||||
"total": total,
|
||||
"contingency": total * self.CONTINGENCIES["air_source_heat_pump"],
|
||||
"contingency_rate": self.CONTINGENCIES["air_source_heat_pump"],
|
||||
"vat": vat,
|
||||
"labour_hours": labour_hours,
|
||||
"labour_days": labour_days,
|
||||
"vat": 0,
|
||||
"labour_hours": 80,
|
||||
"labour_days": 10,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -526,14 +526,14 @@ class HeatingRecommender:
|
|||
# 1) Best available path: HLP → direct peak
|
||||
if heat_loss_parameter_W_per_m2K is not None:
|
||||
peak_kw = heat_loss_parameter_W_per_m2K * floor_area_m2 * ΔT / 1000.0
|
||||
return (peak_kw, peak_kw) # no range needed
|
||||
return peak_kw, peak_kw # no range needed
|
||||
|
||||
# 2) Second-best: space-heating demand → HDD method
|
||||
if space_heat_kwh_per_m2_yr is not None:
|
||||
annual_space_kwh = space_heat_kwh_per_m2_yr * floor_area_m2
|
||||
Htot = annual_space_kwh * 1000.0 / (hdd_base_dd * 24.0) # W/K
|
||||
peak_kw = Htot * ΔT / 1000.0
|
||||
return (peak_kw, peak_kw)
|
||||
return peak_kw, peak_kw
|
||||
|
||||
# 3) Minimal inputs: primary energy + assumed fraction → range
|
||||
assert epc_primary_kwh_per_m2_yr is not None
|
||||
|
|
@ -547,7 +547,7 @@ class HeatingRecommender:
|
|||
|
||||
low = to_peak(space_heat_fraction_range[0])
|
||||
high = to_peak(space_heat_fraction_range[1])
|
||||
return (low, high)
|
||||
return low, high
|
||||
|
||||
@staticmethod
|
||||
def pick_model(peak_kw_range, models_kw=(5, 6, 8.5, 11.2, 14, 17, 20)):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue