mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
added in basic process for sloping ceiling
This commit is contained in:
parent
e8b7a569ff
commit
64eb2e2f20
3 changed files with 143 additions and 11 deletions
|
|
@ -9,7 +9,9 @@ TYPICAL_MEASURE_TYPES = [
|
|||
]
|
||||
|
||||
WALL_INSULATION_MEASURES = ["internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"]
|
||||
ROOF_INSULATION_MEASURES = ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"]
|
||||
ROOF_INSULATION_MEASURES = [
|
||||
"loft_insulation", "flat_roof_insulation", "room_roof_insulation", "sloping_ceiling_insulation"
|
||||
]
|
||||
|
||||
# Both all and roof insulaiton measures are eligible for ECO4. These are the remaining fabric and heating measures
|
||||
# This is based on th measures we have recommendations for
|
||||
|
|
@ -31,7 +33,7 @@ SPECIFIC_MEASURES = (
|
|||
|
||||
INSULATION_MEASURES = [
|
||||
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation",
|
||||
"loft_insulation", "flat_roof_insulation", "room_roof_insulation",
|
||||
"loft_insulation", "flat_roof_insulation", "room_roof_insulation", "sloping_ceiling_insulation",
|
||||
"suspended_floor_insulation", "solid_floor_insulation",
|
||||
]
|
||||
|
||||
|
|
@ -46,7 +48,9 @@ MEASURE_MAP = {
|
|||
"wall_insulation": [
|
||||
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation",
|
||||
],
|
||||
"roof_insulation": ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"],
|
||||
"roof_insulation": [
|
||||
"loft_insulation", "flat_roof_insulation", "room_roof_insulation", "sloping_ceiling_insulation"
|
||||
],
|
||||
"floor_insulation": ["suspended_floor_insulation", "solid_floor_insulation"],
|
||||
"heating": ["boiler_upgrade", "high_heat_retention_storage_heaters", "air_source_heat_pump"],
|
||||
"windows": ["double_glazing", "secondary_glazing"],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
from typing import Mapping, Any
|
||||
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
|
||||
|
|
@ -166,7 +168,8 @@ class Costs:
|
|||
"room_roof_insulation": 0.26,
|
||||
"heater_removal": 0.1,
|
||||
"sealing_open_fireplace": 0.1,
|
||||
"mechanical_ventilation": 0.26
|
||||
"mechanical_ventilation": 0.26,
|
||||
"sloping_ceiling_insulation": 0.26 # Similar to IWI so using the same contingency
|
||||
}
|
||||
|
||||
# Preliminaries are a percentage of the total cost of the work and covers the cost of site-specific costs
|
||||
|
|
@ -935,3 +938,66 @@ class Costs:
|
|||
"labour_hours": 80,
|
||||
"labour_days": 10,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _estimate_number_of_days_for_sloping_ceiling(insulation_roof_area: float) -> float:
|
||||
"""
|
||||
Estimate labour days required to insulate an existing sloping ceiling.
|
||||
|
||||
Heuristic model based on retrofit guidance (Checkatrade, The Green Age)
|
||||
and analogy with internal wall insulation.
|
||||
|
||||
Assumptions:
|
||||
- ~30 m² of sloping ceiling takes ~4 working days
|
||||
- Small jobs still require multiple days (setup, stripping, reboarding)
|
||||
- Larger areas benefit from economies of scale, but not linearly
|
||||
|
||||
:param insulation_roof_area: m² of sloping ceiling to be insulated
|
||||
"""
|
||||
|
||||
base_days = 4
|
||||
base_area = 30 # m2 reference case
|
||||
labour_exponent = 0.85
|
||||
min_days = 2
|
||||
|
||||
labour_days = max(
|
||||
min_days,
|
||||
base_days * (insulation_roof_area / base_area) ** labour_exponent
|
||||
)
|
||||
|
||||
return labour_days
|
||||
|
||||
@classmethod
|
||||
def sloping_ceiling_insulation(cls, insulation_roof_area: float) -> Mapping[str, Any]:
|
||||
"""
|
||||
This costing for this is based on Checkatrade desktop research, since we are yet to receive installer quotes.
|
||||
:param insulation_roof_area: Area of the sloping ceiling to be insulated
|
||||
:return:
|
||||
"""
|
||||
################
|
||||
# Assumptions
|
||||
################
|
||||
# Sources:
|
||||
# https://www.checkatrade.com/blog/cost-guides/vaulted-ceiling-cost/
|
||||
# https://www.thegreenage.co.uk/can-i-insulate-my-sloping-ceiling/
|
||||
# These assumptions last updated 21/02/2026
|
||||
insulation_cost_per_m2 = 52 # The actual install process is quite similar to IWI
|
||||
labour_rate = 250 # per day
|
||||
contingency_rate = cls.CONTINGENCIES["sloping_ceiling_insulation"]
|
||||
|
||||
labour_days = cls._estimate_number_of_days_for_sloping_ceiling(insulation_roof_area)
|
||||
labour_hours = labour_days * 8
|
||||
|
||||
total = (insulation_cost_per_m2 * insulation_roof_area) + (labour_rate * labour_days)
|
||||
|
||||
# Assume VAT included in the total => total is 120% of subtotal
|
||||
vat = total - (total / 1.2)
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"contingency": total * contingency_rate,
|
||||
"contingency_rate": contingency_rate,
|
||||
"vat": vat,
|
||||
"labour_hours": labour_hours,
|
||||
"labour_days": labour_days,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,10 +324,11 @@ class RoofRecommendations:
|
|||
)
|
||||
|
||||
self.estimated_u_value = u_value
|
||||
# The Roof is already compliant - in this case, the u-value is beyond the requirements for
|
||||
# Building Regs Part L and so we don't recommend anything
|
||||
if (u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE) or all(
|
||||
m not in measures for m in MEASURE_MAP["roof_insulation"]
|
||||
):
|
||||
# The Roof is already compliant
|
||||
return
|
||||
|
||||
non_invasive_recommendations = self.property.non_invasive_recommendations
|
||||
|
|
@ -381,14 +382,12 @@ class RoofRecommendations:
|
|||
has_room_roof_recommendation=has_room_roof_recommendation
|
||||
)
|
||||
|
||||
##################################################
|
||||
################################################################
|
||||
# ~~~~~ Loft Insulation Recommendation Logic ~~~~~
|
||||
##################################################
|
||||
# We firstly handle non-intrusive recommendations, which may override the normal roof insulation recommendations
|
||||
################################################################
|
||||
if needs_loft_insulation:
|
||||
self.recommend_roof_insulation(
|
||||
u_value=u_value,
|
||||
insulation_thickness=self.insulation_thickness,
|
||||
phase=phase,
|
||||
is_flat=False,
|
||||
is_pitched=True,
|
||||
|
|
@ -396,10 +395,12 @@ class RoofRecommendations:
|
|||
)
|
||||
return
|
||||
|
||||
################################################################
|
||||
# ~~~~~ Flat Roof Insulation Recommendation Logic ~~~~~
|
||||
################################################################
|
||||
if needs_flat_roof_insulation:
|
||||
self.recommend_roof_insulation(
|
||||
u_value=u_value,
|
||||
insulation_thickness=0,
|
||||
phase=phase,
|
||||
is_flat=True,
|
||||
is_pitched=False,
|
||||
|
|
@ -407,12 +408,21 @@ class RoofRecommendations:
|
|||
)
|
||||
return
|
||||
|
||||
################################################################
|
||||
# ~~~~~ Room Roof Insulation Recommendation Logic ~~~~~
|
||||
################################################################
|
||||
# There are cases where the property might have a room roof as the second roof, but we have a recommendation for
|
||||
# it, so we allow this override
|
||||
if needs_rir_insulation:
|
||||
self.recommend_room_roof_insulation(u_value, phase, default_u_values)
|
||||
return
|
||||
|
||||
####################################################################################################
|
||||
# ~~~~~ Sloping Ceiling Insulation Recommendation Logic ~~~~~
|
||||
####################################################################################################
|
||||
if needs_sloping_ceiling:
|
||||
self.recommend_sloping_ceiling()
|
||||
|
||||
raise NotImplementedError("Implement me")
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -432,7 +442,7 @@ class RoofRecommendations:
|
|||
raise ValueError("Invalid material type")
|
||||
|
||||
def recommend_roof_insulation(
|
||||
self, u_value, insulation_thickness, phase, is_pitched, is_flat, default_u_values
|
||||
self, u_value, phase, is_pitched, is_flat, default_u_values
|
||||
):
|
||||
|
||||
"""
|
||||
|
|
@ -773,3 +783,55 @@ class RoofRecommendations:
|
|||
)
|
||||
|
||||
self.recommendations = recommendations
|
||||
|
||||
def recommend_sloping_ceiling(self, phase: int, u_value, sloping_ceiling_recommendation: dict = None):
|
||||
"""
|
||||
Recommend insulation for a sloping ceiling
|
||||
Since we don't have any materials from installers for this specific recommendation, we
|
||||
do not iterate through any materials. Instead, we provide a single recommendation, we estimated
|
||||
prices based on desk research.
|
||||
:return:
|
||||
"""
|
||||
|
||||
new_description = "Pitched, insulated"
|
||||
new_efficiency = "Good"
|
||||
|
||||
roof_ending_config = RoofAttributes(new_description).process()
|
||||
roof_simulation_config = check_simulation_difference(
|
||||
new_config=roof_ending_config, old_config=self.property.roof, prefix="roof_"
|
||||
)
|
||||
|
||||
# We pull out new u-values, based on 75mm of insulation, with u-values defined from Elmhurst
|
||||
new_u_value = 0.5 # This doesn't change, regardless of starting u-value
|
||||
|
||||
simulation_config = {
|
||||
**roof_simulation_config,
|
||||
"roof_thermal_transmittance_ending": new_u_value,
|
||||
"roof_energy_eff_ending": new_efficiency
|
||||
}
|
||||
|
||||
cost_result = self.costs.sloping_ceiling_insulation(
|
||||
roof_area=self.property.roof_area # For a pitched roof, this is the pitched roof area
|
||||
)
|
||||
|
||||
self.recommendations = [
|
||||
{
|
||||
"phase": phase,
|
||||
"parts": [],
|
||||
"type": "sloping_ceiling_insulation",
|
||||
"measure_type": "sloping_ceiling_insulation",
|
||||
"description": "Insulate sloping ceilings at the rafters and re-decorate",
|
||||
"starting_u_value": u_value,
|
||||
"new_u_value": None,
|
||||
"sap_points": sloping_ceiling_recommendation.get("sap_points", None),
|
||||
"simulation_config": simulation_config,
|
||||
"description_simulation": {
|
||||
"roof-description": new_description,
|
||||
"roof-energy-eff": new_efficiency
|
||||
},
|
||||
**cost_result,
|
||||
"already_installed": "sloping_ceiling_insulation" in self.property.already_installed,
|
||||
"survey": sloping_ceiling_recommendation.get("survey", None),
|
||||
"innovation_rate": 0
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue