mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
180 lines
7.2 KiB
Python
180 lines
7.2 KiB
Python
import pandas as pd
|
|
|
|
from backend.Property import Property
|
|
from typing import List
|
|
from recommendations.Costs import Costs
|
|
from recommendations.recommendation_utils import override_costs, check_use_survey
|
|
from backend.ml_models.AnnualBillSavings import AnnualBillSavings
|
|
|
|
|
|
class LightingRecommendations:
|
|
# We introduce a SAP limit to lighting, which is based on empirical findings. We do see cases where lighting is
|
|
# worth more than 2 points, but this is unlikely in the context of other upgrades that can be made to the property
|
|
SAP_LIMIT = 2
|
|
|
|
# If more than 50% of the lighting is LEDs already, the limit is 1 SAP point
|
|
SAP_LOWER_LIMIT = 1
|
|
|
|
def __init__(self, property_instance: Property, materials: List):
|
|
"""
|
|
:param property_instance: Instance of the Property class, for the home associated to property_id
|
|
:param materials: List of materials to be used in the recommendations
|
|
"""
|
|
|
|
self.property = property_instance
|
|
self.costs = Costs(self.property)
|
|
|
|
material = [
|
|
material for material in materials if material["type"] == "low_energy_lighting_installation"
|
|
]
|
|
if len(material) != 1:
|
|
raise ValueError("Incorrect number of low energy lighting materials specified")
|
|
|
|
self.material = material[0]
|
|
self.recommendation = []
|
|
|
|
@classmethod
|
|
def get_sap_limit(cls, lighting_energy_efficiency: str, lighting_proportion: float):
|
|
"""
|
|
Lighting seems to be a more straight forward measure to estimate SAP points for, based on the starting
|
|
energy efficiency rating.
|
|
|
|
We seem to have the following brackes based on % of LEDs in outlets
|
|
Very poor: 0 - 9%
|
|
Poor: 10 - 24%
|
|
Average: 25 - 44%
|
|
Good: 45 - 69%
|
|
Very good: 70 - 100%
|
|
:return:
|
|
"""
|
|
|
|
if lighting_energy_efficiency == "Very Good":
|
|
return 0
|
|
|
|
if lighting_energy_efficiency in ["Good", "Average"]:
|
|
return cls.SAP_LOWER_LIMIT
|
|
|
|
# If lighting_energy_efficiency is missing, we'll use the proportion of low energy lighting
|
|
if not lighting_energy_efficiency or pd.isnull(lighting_energy_efficiency):
|
|
if lighting_proportion >= 0.7:
|
|
return 0
|
|
if lighting_proportion >= 0.25:
|
|
return cls.SAP_LOWER_LIMIT
|
|
return cls.SAP_LIMIT
|
|
|
|
return cls.SAP_LIMIT
|
|
|
|
@staticmethod
|
|
def estimate_lighting_impact(number_of_bulbs: int):
|
|
"""
|
|
Placeholder function to estimate the actual energy savings of LEDs vs traditional lighting
|
|
:return:
|
|
"""
|
|
|
|
wattage_incandescent = 60 # wattage of typical incandescent bulb in watts
|
|
wattage_led = 10 # wattage of typical LED bulb in watts
|
|
hours_per_day = 3 # average usage in hours per day
|
|
days_per_year = 365 # days in a year
|
|
national_grid_carbon_intensity = 162 # gCO2/kWh, average for 2023 in the UK
|
|
|
|
# Energy usage per year for incandescent and LED bulbs (in kWh)
|
|
energy_usage_incandescent_per_year = (wattage_incandescent / 1000) * hours_per_day * days_per_year
|
|
energy_usage_led_per_year = (wattage_led / 1000) * hours_per_day * days_per_year
|
|
|
|
# Energy savings per bulb per year
|
|
energy_savings_per_bulb_per_year = energy_usage_incandescent_per_year - energy_usage_led_per_year
|
|
|
|
# Total energy savings for all bulbs
|
|
total_energy_savings_per_year = energy_savings_per_bulb_per_year * number_of_bulbs
|
|
|
|
carbon_reduction_grams = total_energy_savings_per_year * national_grid_carbon_intensity
|
|
carbon_reduction_tonnes = carbon_reduction_grams / 1_000_000 # converting grams to tonnes
|
|
|
|
return total_energy_savings_per_year, carbon_reduction_tonnes
|
|
|
|
def recommend(self, phase=0):
|
|
"""
|
|
This method will check if there are any lighting fittings that aren't low energy.
|
|
|
|
If there are, the will recommend fitting the rest of the outlets with low energy lighting fittings
|
|
:return:
|
|
"""
|
|
|
|
if "sap05" in self.property.lighting["clean_description"].lower():
|
|
return
|
|
|
|
if self.property.lighting["low_energy_proportion"] >= 1:
|
|
return
|
|
|
|
leds_recommendation_config = next(
|
|
(r for r in self.property.non_invasive_recommendations if r["type"] == "low_energy_lighting"),
|
|
{}
|
|
)
|
|
|
|
number_lighting_outlets = self.property.number_lighting_outlets
|
|
|
|
# Number non lel outlets
|
|
number_non_lel_outlets = number_lighting_outlets - (
|
|
self.property.lighting["low_energy_proportion"] * number_lighting_outlets
|
|
)
|
|
|
|
number_non_lel_outlets = round(number_non_lel_outlets)
|
|
|
|
if number_non_lel_outlets == 0:
|
|
return
|
|
|
|
# Get the cost of the fittings
|
|
if leds_recommendation_config.get("cost"):
|
|
raise NotImplementedError("Costs from for low energy lighting have not been implemented")
|
|
|
|
cost_result = self.costs.low_energy_lighting(
|
|
number_of_lights=number_non_lel_outlets,
|
|
material=self.material
|
|
)
|
|
|
|
if number_non_lel_outlets == 1:
|
|
description = "Install low energy lighting in 1 remaining outlet"
|
|
else:
|
|
description = "Install low energy lighting in %s outlets" % str(number_non_lel_outlets)
|
|
|
|
heat_demand_change, carbon_change = self.estimate_lighting_impact(number_non_lel_outlets)
|
|
|
|
already_installed = "low_energy_lighting" in self.property.already_installed
|
|
if already_installed:
|
|
cost_result = override_costs(cost_result)
|
|
description = "Low energy lighting has already been installed, no further action required"
|
|
|
|
if leds_recommendation_config.get("sap_points") is not None:
|
|
# This could be zero points
|
|
sap_points = leds_recommendation_config["sap_points"]
|
|
else:
|
|
sap_points = round(2 * (number_non_lel_outlets / number_lighting_outlets), 2)
|
|
|
|
self.recommendation = [
|
|
{
|
|
"phase": phase,
|
|
"parts": [],
|
|
"type": "low_energy_lighting",
|
|
"measure_type": "low_energy_lighting",
|
|
"description": description,
|
|
"starting_u_value": None,
|
|
"new_u_value": None,
|
|
"already_installed": already_installed,
|
|
# For SAP points, we use the fact that lighting is usually worth 2 points and we scale this to
|
|
# the proportion of lights that will be set to low energy
|
|
"sap_points": sap_points,
|
|
"kwh_savings": heat_demand_change,
|
|
"energy_cost_savings": heat_demand_change * AnnualBillSavings.ELECTRICITY_PRICE_CAP,
|
|
"co2_equivalent_savings": carbon_change,
|
|
"description_simulation": {
|
|
"lighting-energy-eff": "Very Good",
|
|
"lighting-description": "Low energy lighting in all fixed outlets",
|
|
"low-energy-lighting": 100,
|
|
},
|
|
**cost_result,
|
|
"survey": check_use_survey(
|
|
leds_recommendation_config, self.property.epc_record.has_been_remodelled
|
|
),
|
|
"innovation_rate": self.material["innovation_rate"],
|
|
}
|
|
]
|