mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
implemented further scenarios into solar recommendations
This commit is contained in:
parent
7a219285fc
commit
ec80473f3e
6 changed files with 1586 additions and 30 deletions
|
|
@ -118,7 +118,6 @@ class Property:
|
|||
self.number_lighting_outlets = epc_record.prepared_epc.get("fixed_lighting_outlets_count")
|
||||
self.floor_level = None
|
||||
self.number_of_windows = None
|
||||
self.solar_pv_roof_area = None
|
||||
self.solar_pv_percentage = None
|
||||
|
||||
self.current_adjusted_energy = None
|
||||
|
|
@ -185,6 +184,8 @@ class Property:
|
|||
recommendation_record["walls_insulation_thickness_ending"] = "above average"
|
||||
recommendation_record["walls_energy_eff_ending"] = "Good"
|
||||
|
||||
# Note: often when the wall is insulatied, the internal/external insulation is not noted so we should
|
||||
# test the impact of using these booleans
|
||||
if recommendation["type"] == "external_wall_insulation":
|
||||
recommendation_record["external_insulation"] = True
|
||||
recommendation_record["internal_insulation"] = False
|
||||
|
|
@ -238,7 +239,10 @@ class Property:
|
|||
|
||||
recommendation_record["roof_insulation_thickness_ending"] = str(proposed_depth)
|
||||
if recommendation["type"] == "loft_insulation":
|
||||
recommendation_record["roof_energy_eff_ending"] = "Good"
|
||||
if proposed_depth >= 270:
|
||||
recommendation_record["roof_energy_eff_ending"] = "Very Good"
|
||||
else:
|
||||
recommendation_record["roof_energy_eff_ending"] = "Good"
|
||||
else:
|
||||
recommendation_record["roof_energy_eff_ending"] = "Very Good"
|
||||
else:
|
||||
|
|
@ -682,9 +686,16 @@ class Property:
|
|||
percentage_of_roof = photo_supply_matched["photo_supply_median"].mean()
|
||||
percentage_of_roof = percentage_of_roof / 100
|
||||
|
||||
self.solar_pv_roof_area = (
|
||||
self.solar_pv_percentage = percentage_of_roof
|
||||
|
||||
def get_solar_pv_roof_area(self, percentage_of_roof):
|
||||
"""
|
||||
Given a percentage of the roof, this method will return the estimated area of the solar panels
|
||||
:param percentage_of_roof:
|
||||
:return:
|
||||
"""
|
||||
|
||||
return (
|
||||
self.insulation_floor_area * percentage_of_roof if self.roof["is_flat"] else
|
||||
self.pitched_roof_area * percentage_of_roof
|
||||
)
|
||||
|
||||
self.solar_pv_percentage = percentage_of_roof
|
||||
|
|
|
|||
|
|
@ -51,13 +51,14 @@ class PropertyValuation:
|
|||
KNIGHT_FRANK_MAPPING = [
|
||||
{"start": "D", "end": "C", "increase_percentage": 0.03},
|
||||
{"start": "D", "end": "B", "increase_percentage": 0.088},
|
||||
{"start": "D", "end": "A", "increase_percentage": 0.088},
|
||||
]
|
||||
|
||||
NATIONWIDE_MAPPING = [
|
||||
{"start": "G", "end": "D", "increase_percentage": 0.035},
|
||||
{"start": "F", "end": "D", "increase_percentage": 0.035},
|
||||
{"start": "D", "end": "B", "increase_percentage": 0.017},
|
||||
{"start": "D", "end": "A", "increase_percentage": 0.017},
|
||||
# {"start": "G", "end": "D", "increase_percentage": 0.035},
|
||||
# {"start": "F", "end": "D", "increase_percentage": 0.035},
|
||||
# {"start": "D", "end": "B", "increase_percentage": 0.017},
|
||||
# {"start": "D", "end": "A", "increase_percentage": 0.017},
|
||||
]
|
||||
|
||||
EPC_BANDS = ["G", "F", "E", "D", "C", "B", "A"]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -37,6 +37,9 @@ MCS_SOLAR_PV_COST_DATA = {
|
|||
"average_cost_per_kwh-Northern Ireland": 2126.09,
|
||||
}
|
||||
|
||||
# This is based on quotes from installers
|
||||
BATTERY_COST = 3500
|
||||
|
||||
|
||||
class Costs:
|
||||
"""
|
||||
|
|
@ -835,7 +838,7 @@ class Costs:
|
|||
"labour_days": labour_days
|
||||
}
|
||||
|
||||
def solar_pv(self, wattage: float):
|
||||
def solar_pv(self, wattage: float, has_battery: bool = False):
|
||||
|
||||
"""
|
||||
Calculates the total cost for solar PV based data provided by the MCS dashboard, which contains
|
||||
|
|
@ -847,8 +850,8 @@ class Costs:
|
|||
|
||||
Price can also be benchmarked against this checkatrade article:
|
||||
https://www.checkatrade.com/blog/cost-guides/cost-of-solar-panel-installation/
|
||||
:param wattage: Peak wattage of the solar PV system
|
||||
:return:
|
||||
:param wattage: Peak wattage of the solar PV system]
|
||||
:param has_battery: Bool, whether the system includes a battery
|
||||
"""
|
||||
|
||||
# Get the cost data relevant to the region
|
||||
|
|
@ -858,6 +861,11 @@ class Costs:
|
|||
total_cost = kw * regional_cost
|
||||
|
||||
subtotal_before_vat = total_cost / (1 + self.VAT_RATE)
|
||||
|
||||
if has_battery:
|
||||
# The battery cost is based on the £3500 quote, recieved from installers
|
||||
subtotal_before_vat += BATTERY_COST
|
||||
|
||||
vat = total_cost - subtotal_before_vat
|
||||
|
||||
# Labour hours are based on estimates from online research but an average team seems to consist of 3 people
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ class Recommendations:
|
|||
|
||||
"""
|
||||
This method runs the recommendations for the individual measures and then appends them to a list for output
|
||||
|
||||
The recommendations are implemented in order of suggested phase, from fabric first to heating systems, to
|
||||
renewables.
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -39,17 +39,56 @@ class SolarPvRecommendations:
|
|||
if not is_valid_property_type or not is_valid_roof_type or not has_no_existing_solar_pv:
|
||||
return
|
||||
|
||||
# We now have a property which is potentially suitable for solar PV
|
||||
number_solar_panels = np.floor(self.property.solar_pv_roof_area / self.SOLAR_PANEL_AREA)
|
||||
solar_panel_wattage = number_solar_panels * self.SOLAR_PANEL_WATTAGE
|
||||
# For the solar recommendations, we produce the following scenarios:
|
||||
# 1) Solar panels only, we present a high, medium and low coverage
|
||||
# 2) With and without battery
|
||||
roof_coverage_scenarios = [
|
||||
self.property.solar_pv_percentage - 0.1, self.property.solar_pv_percentage,
|
||||
self.property.solar_pv_percentage + 0.1
|
||||
]
|
||||
# We make sure we haven't gone too low or high
|
||||
roof_coverage_scenarios = [v for v in roof_coverage_scenarios if 0 <= v <= 1]
|
||||
battery_scenarios = [False, True]
|
||||
|
||||
roof_coverage_percent = round(self.property.solar_pv_percentage * 100)
|
||||
# I now produce the cross product of the scenarios
|
||||
scenarios = [(roof, battery) for roof in roof_coverage_scenarios for battery in battery_scenarios]
|
||||
|
||||
# Given the wattage, we estimate the cost of the solar PV system. This is based on the MCS database
|
||||
# of solar PV installations
|
||||
cost_result = self.costs.solar_pv(wattage=solar_panel_wattage)
|
||||
for roof_coverage, has_battery in scenarios:
|
||||
# We now have a property which is potentially suitable for solar PV
|
||||
solar_pv_roof_area = self.property.get_solar_pv_roof_area(roof_coverage)
|
||||
|
||||
kw = np.floor(solar_panel_wattage / 100) / 10
|
||||
number_solar_panels = np.floor(solar_pv_roof_area / self.SOLAR_PANEL_AREA)
|
||||
solar_panel_wattage = number_solar_panels * self.SOLAR_PANEL_WATTAGE
|
||||
|
||||
roof_coverage_percent = round(roof_coverage * 100)
|
||||
|
||||
# Given the wattage, we estimate the cost of the solar PV system. This is based on the MCS database
|
||||
# of solar PV installations
|
||||
cost_result = self.costs.solar_pv(wattage=solar_panel_wattage, has_battery=has_battery)
|
||||
|
||||
kw = np.floor(solar_panel_wattage / 100) / 10
|
||||
|
||||
if has_battery:
|
||||
description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) panel system on "
|
||||
f"{round(roof_coverage_percent * 100)}% the roof, with a battery storage system.")
|
||||
else:
|
||||
description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) p"
|
||||
f"anel system on {round(roof_coverage_percent * 100)}% the roof.")
|
||||
|
||||
self.recommendation.append(
|
||||
{
|
||||
"parts": [],
|
||||
"type": "solar_pv",
|
||||
"description": description,
|
||||
"starting_u_value": None,
|
||||
"new_u_value": None,
|
||||
"sap_points": None,
|
||||
**cost_result,
|
||||
# This is required for simulating the SAP impact. solar_pv_percentage is between 0 & 1 so we scale
|
||||
# back up here
|
||||
"photo_supply": 100 * scenario
|
||||
}
|
||||
)
|
||||
|
||||
self.recommendation = [
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue