implemented further scenarios into solar recommendations

This commit is contained in:
Khalim Conn-Kowlessar 2024-02-14 11:06:10 +00:00
parent 7a219285fc
commit ec80473f3e
6 changed files with 1586 additions and 30 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:
"""

View file

@ -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 = [
{