mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
618 lines
26 KiB
Python
618 lines
26 KiB
Python
import math
|
|
from typing import List
|
|
|
|
import pandas as pd
|
|
|
|
from datatypes.enums import QuantityUnits
|
|
from backend.Property import Property
|
|
from BaseUtility import Definitions
|
|
from etl.epc_clean.epc_attributes.WallAttributes import WallAttributes
|
|
from recommendations.recommendation_utils import (
|
|
r_value_per_mm_to_u_value, calculate_u_value_uplift, is_diminishing_returns, update_lowest_selected_u_value,
|
|
get_recommended_part, get_wall_u_value, override_costs, check_simulation_difference
|
|
)
|
|
from recommendations.config import PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION
|
|
from recommendations.Costs import Costs
|
|
from utils.logger import setup_logger
|
|
|
|
logger = setup_logger()
|
|
|
|
|
|
class WallRecommendations(Definitions):
|
|
YEAR_WALLS_BUILT_WITH_INSULATION = 1990
|
|
# After 1930, Solid brick walls became less populate and instead, cavity walls became a
|
|
# more popular choice
|
|
YEARS_CAVITY_WALLS_BEGAN = 1930
|
|
U_VALUE_UNIT = "w/m-¦k"
|
|
|
|
# part L building regulations indicate that any rennovations on an existing property's walls should
|
|
# achieve a U-value of no higher than 0.3
|
|
# This can be seen in table 4.3 in building regulations part L:
|
|
# https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/1133079
|
|
# /Approved_Document_L__Conservation_of_fuel_and_power__Volume_1_Dwellings__2021_edition_incorporating_2023_amendments.pdf
|
|
BUILDING_REGULATIONS_PART_L_MAX_U_VALUE = 0.3
|
|
# We don't recommend measures that are too low because it becomes expensive, therefore we aim to avoid
|
|
# diminishing returns. This value should be verified with Osmosis (TODO)
|
|
DIMINISHING_RETURNS_U_VALUE = 0.25
|
|
|
|
# Building regulations part L also indicates that cavity wall insulation should result in 0.55 u-value
|
|
BUILDING_REGULATIONS_PART_L_CAVITY_WALL_MAX_U_VALUE = 0.55
|
|
|
|
# Part L regulations indicate that any new build should have walls that achieve a u-value of no higher
|
|
# than 0.18.
|
|
BUILDING_REGULATIONS_PART_L_NEW_BUILD_MAX_U_VALUE = 0.18
|
|
# 0.15 is an often cited diminishing returns value for new builds
|
|
NEW_BUILD_DIMINISHING_RETURNS_U_VALUE = 0.15
|
|
|
|
# Add some error so that if, for example, a new part we recommend provides a u-value of 0.19,
|
|
# we still consider it as an option
|
|
U_VALUE_ERROR = 0.01
|
|
|
|
# Typically when the U-value is around 0.75 and below, and the home is a new build, this is a good indication
|
|
# that the home is already insulated with at least some partial insulation. We don't recommend insulation
|
|
# in this case. This estimate was verified with the Warmfront team and 0.75 has been used as a conservative
|
|
# threshold
|
|
NEW_BUILD_INSULATED = 0.75
|
|
|
|
# These are the ending descriptions we consider for walls with external insulation
|
|
# This maps the clean descriptions to the ending descriptions
|
|
EXTERNALLY_INSULATED_WALL_DESCRIPTIONS = {
|
|
"Cavity wall, as built, insulated": "Cavity wall, filled cavity and external insulation",
|
|
"Solid brick, as built, no insulation": "Solid brick, with external insulation",
|
|
"Solid brick, as built, insulated": "Solid brick, with external insulation",
|
|
"Cob, as built": "Cob, with external insulation",
|
|
"System built, as built, no insulation": "System built, with external insulation",
|
|
"Granite or whinstone, as built, no insulation": 'Granite or whinstone, with external insulation',
|
|
"Timber frame, as built, no insulation": "Timber frame, with external insulation",
|
|
}
|
|
|
|
# These are the ending descriptions we consider for walls with internal insulation
|
|
INTERNALLY_INSULATED_WALL_DESCRIPTIONS = {
|
|
"Cavity wall, as built, insulated": "Cavity wall, filled cavity and internal insulation",
|
|
"Solid brick, as built, no insulation": "Solid brick, with internal insulation",
|
|
"Solid brick, as built, insulated": "Solid brick, with internal insulation",
|
|
"Cob, as built": "Cob, with internal insulation",
|
|
"System built, as built, no insulation": "System built, with internal insulation",
|
|
"Granite or whinstone, as built, no insulation": 'Granite or whinstone, with internal insulation',
|
|
"Timber frame, as built, no insulation": "Timber frame, with internal insulation",
|
|
}
|
|
|
|
def __init__(
|
|
self,
|
|
property_instance: Property,
|
|
materials: List
|
|
):
|
|
self.property = property_instance
|
|
self.costs = Costs(self.property)
|
|
# For audit purposes, when estimating u values we'll store it
|
|
self.estimated_u_value = None
|
|
|
|
# Will contains a list of recommended measures
|
|
self.recommendations = []
|
|
|
|
self.cavity_wall_insulation_materials = [
|
|
part for part in materials if part["type"] == "cavity_wall_insulation"
|
|
]
|
|
|
|
self.internal_wall_insulation_materials = [
|
|
part for part in materials if part["type"] == "internal_wall_insulation"
|
|
]
|
|
|
|
self.internal_wall_non_insulation_materials = [
|
|
part
|
|
for part in materials
|
|
if part["type"]
|
|
in ["iwi_wall_demolition", "iwi_vapour_barrier", "iwi_redecoration"]
|
|
]
|
|
|
|
self.external_wall_insulation_materials = [
|
|
part for part in materials if part["type"] == "external_wall_insulation"
|
|
]
|
|
|
|
self.external_wall_non_insulation_materials = [
|
|
part
|
|
for part in materials
|
|
if part["type"] in ["ewi_wall_demolition", "ewi_wall_preparation", "ewi_wall_redecoration"]
|
|
]
|
|
|
|
def ewi_valid(self):
|
|
"""
|
|
This method check available data, to determine if a property is suitable for external wall insulation
|
|
"""
|
|
|
|
# Current logic: If the property is in a conservation area/heritage building/listed building or a flat,
|
|
# it is not suitable for EWI
|
|
if self.property.restricted_measures or (
|
|
self.property.data["property-type"].lower() == "flat"
|
|
) or (
|
|
self.property.walls['is_cob'] or
|
|
self.property.walls['is_sandstone_or_limestone'] or
|
|
self.property.walls["is_cavity_wall"]
|
|
):
|
|
return False
|
|
|
|
return True
|
|
|
|
def is_suitable_for_solid_insulation(self):
|
|
"""
|
|
Checks if the wall is of a suitable type for internal/external wall insulation
|
|
"""
|
|
if self.property.walls["is_cavity_wall"] or self.property.walls["is_cob"]:
|
|
return False
|
|
|
|
return True
|
|
|
|
def mds_recommend_cavity_wall_insulation(self, phase=None):
|
|
# Function specifically for cavity wall insulation, for usage in the mds report
|
|
self.recommendations = []
|
|
insulation_thickness = self.property.walls["insulation_thickness"]
|
|
|
|
u_value = get_wall_u_value(
|
|
clean_description=self.property.walls["clean_description"],
|
|
age_band=self.property.age_band,
|
|
is_granite_or_whinstone=self.property.walls["is_granite_or_whinstone"],
|
|
is_sandstone_or_limestone=self.property.walls["is_sandstone_or_limestone"],
|
|
)
|
|
|
|
# Test filling cavity
|
|
self.find_cavity_insulation(u_value, insulation_thickness, phase)
|
|
|
|
return self.recommendations
|
|
|
|
def mds_recommend_ewi(self, phase=None):
|
|
# Function specifically for external wall insulation, for usage in the mds report
|
|
self.recommendations = []
|
|
|
|
u_value = self.property.walls["thermal_transmittance"]
|
|
|
|
if u_value is None:
|
|
u_value = get_wall_u_value(
|
|
clean_description=self.property.walls["clean_description"],
|
|
age_band=self.property.age_band,
|
|
is_granite_or_whinstone=self.property.walls["is_granite_or_whinstone"],
|
|
is_sandstone_or_limestone=self.property.walls["is_sandstone_or_limestone"],
|
|
)
|
|
|
|
# EWI
|
|
ewi_recommendations = self._find_insulation(
|
|
u_value=u_value,
|
|
insulation_materials=pd.DataFrame(self.external_wall_insulation_materials),
|
|
non_insulation_materials=self.external_wall_non_insulation_materials,
|
|
phase=phase
|
|
)
|
|
|
|
return ewi_recommendations
|
|
|
|
def recommend(self, phase=0):
|
|
# if building built after 1990 + we're able to identify U-value +
|
|
# U-value less than 0.18 and if in or close to a conversation area,
|
|
# recommend internal wall insulation as a possible measure
|
|
|
|
u_value = self.property.walls["thermal_transmittance"]
|
|
u_value = None if math.isnan(u_value) else u_value
|
|
|
|
is_cavity_wall = self.property.walls["is_cavity_wall"]
|
|
insulation_thickness = self.property.walls["insulation_thickness"]
|
|
|
|
# We check if the wall is already insulated and if so, we exit
|
|
if (
|
|
(insulation_thickness in ["average", "above average"])
|
|
or self.property.walls["is_filled_cavity"]
|
|
) and (
|
|
"cavity_extract_and_refill"
|
|
not in self.property.non_invasive_recommendations
|
|
):
|
|
return
|
|
|
|
if u_value:
|
|
|
|
if self.property.walls["thermal_transmittance_unit"] != self.U_VALUE_UNIT:
|
|
raise NotImplementedError(
|
|
"Haven't handled the case of other u value units yet"
|
|
)
|
|
|
|
# If the property is a new build and the U-value is below 0.75, we don't recommend insulation because it's
|
|
# not practical
|
|
if (self.property.data["transaction-type"] == "new dwelling") and (
|
|
u_value <= self.NEW_BUILD_INSULATED
|
|
):
|
|
# Recommend nothing
|
|
return
|
|
|
|
# We can't detect it's a cavity wall, but it was built after 1990 so likely built with insulation already
|
|
# + it already has a U-value WORSE than the building regulations, so we recommend either internal or
|
|
# external wall insulation
|
|
if (
|
|
(not is_cavity_wall)
|
|
and (self.property.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION)
|
|
and (u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE)
|
|
):
|
|
# Recommend insulation
|
|
self.find_insulation(u_value, phase)
|
|
return
|
|
|
|
# We can't detect it's a cavity wall, but it was built after 1990 so likely built with insulation already
|
|
# + it already has a U-value better than the building regulations, so we don't need to recommend anything
|
|
if (
|
|
(not is_cavity_wall)
|
|
and (self.property.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION)
|
|
and (u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE)
|
|
):
|
|
# Recommend nothing
|
|
return
|
|
|
|
raise NotImplementedError("Not implemented yet")
|
|
|
|
u_value = get_wall_u_value(
|
|
clean_description=self.property.walls["clean_description"],
|
|
age_band=self.property.age_band,
|
|
is_granite_or_whinstone=self.property.walls["is_granite_or_whinstone"],
|
|
is_sandstone_or_limestone=self.property.walls["is_sandstone_or_limestone"],
|
|
)
|
|
|
|
self.estimated_u_value = u_value
|
|
|
|
if is_cavity_wall:
|
|
if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
|
# Test filling cavity
|
|
self.find_cavity_insulation(u_value, insulation_thickness, phase)
|
|
|
|
return
|
|
|
|
# Remaining wall types are treated with IWI or EWI
|
|
if (u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE) and self.is_suitable_for_solid_insulation():
|
|
self.find_insulation(u_value, phase)
|
|
return
|
|
|
|
# If the u-value is within regulations, we don't do anything
|
|
return
|
|
|
|
def find_cavity_insulation(self, u_value, insulation_thickness, phase):
|
|
"""
|
|
This method tests different materials to fill the cavity wall, determining which
|
|
material will give us the best U-value.
|
|
|
|
We check for diminishing returns, however this function does not check for meeting building
|
|
Part L regulations right now. This is because Part L is less stringent for cavity walls
|
|
|
|
Width of a cavity:
|
|
There are various sources online that suggest the width of a cavity wall is around 50mm. The retrofit course
|
|
indicates that most cavities are 50-75mm. Many sources online indicate that 50mm is the standard MINIMUM figure
|
|
therefore we'll use 75mm as the base assumption
|
|
|
|
This document:
|
|
https://www.buildingcentre.co.uk/media/_file/pdf/22220_pdf27.pdf
|
|
Indicates that they could be 50-85mm wide
|
|
|
|
:param u_value: u_value of the starting wall
|
|
:param insulation_thickness: describes the insulation level of the wall. If "below average", we have a partially
|
|
filled cavity wall
|
|
"""
|
|
|
|
insulation_materials = pd.DataFrame(self.cavity_wall_insulation_materials)
|
|
cavity_width = 75
|
|
if insulation_thickness == "below average":
|
|
cavity_width = cavity_width * (1 - PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION)
|
|
|
|
# Test the different fill options
|
|
lowest_selected_u_value = None
|
|
recommendations = []
|
|
for _, material in insulation_materials.iterrows():
|
|
|
|
part_u_value = r_value_per_mm_to_u_value(
|
|
cavity_width, material["r_value_per_mm"]
|
|
)
|
|
|
|
_, new_u_value = calculate_u_value_uplift(u_value, part_u_value)
|
|
new_u_value = math.ceil(new_u_value * 100.0) / 100.0
|
|
|
|
if is_diminishing_returns(
|
|
recommendations,
|
|
new_u_value,
|
|
lowest_selected_u_value,
|
|
self.DIMINISHING_RETURNS_U_VALUE,
|
|
):
|
|
continue
|
|
|
|
if new_u_value <= self.BUILDING_REGULATIONS_PART_L_CAVITY_WALL_MAX_U_VALUE:
|
|
lowest_selected_u_value = update_lowest_selected_u_value(
|
|
lowest_selected_u_value, new_u_value
|
|
)
|
|
|
|
is_extraction_and_refill = (
|
|
"cavity_extract_and_refill"
|
|
in self.property.non_invasive_recommendations
|
|
)
|
|
|
|
cost_result = self.costs.cavity_wall_insulation(
|
|
wall_area=self.property.insulation_wall_area,
|
|
material=material.to_dict(),
|
|
is_extraction_and_refill=is_extraction_and_refill,
|
|
)
|
|
|
|
already_installed = (
|
|
"cavity_wall_insulation" in self.property.already_installed
|
|
)
|
|
if already_installed:
|
|
cost_result = override_costs(cost_result)
|
|
|
|
if is_extraction_and_refill:
|
|
description = f"Extract and refill cavity wall insulation with {material['description']}"
|
|
else:
|
|
description = self._make_description(material)
|
|
|
|
# updated the new u-value with the best possible our installers have
|
|
new_u_value = max(0.31, new_u_value)
|
|
|
|
wall_ending_config = WallAttributes("Cavity wall, filled cavity").process()
|
|
|
|
walls_simulation_config = check_simulation_difference(
|
|
new_config=wall_ending_config, old_config=self.property.walls, prefix="walls_"
|
|
)
|
|
|
|
simulation_config = self.set_starting_simulation_config(
|
|
wall_ending_config=wall_ending_config
|
|
)
|
|
|
|
simulation_config = {
|
|
**simulation_config,
|
|
**walls_simulation_config,
|
|
"walls_thermal_transmittance_ending": new_u_value
|
|
}
|
|
|
|
recommendations.append(
|
|
{
|
|
"phase": phase,
|
|
"parts": [
|
|
get_recommended_part(
|
|
part=material.to_dict(),
|
|
quantity=self.property.insulation_wall_area,
|
|
quantity_unit=QuantityUnits.m2.value,
|
|
cost_result=cost_result,
|
|
)
|
|
],
|
|
"type": "cavity_wall_insulation",
|
|
"description": description,
|
|
"starting_u_value": u_value,
|
|
"new_u_value": new_u_value,
|
|
"sap_points": None,
|
|
"already_installed": already_installed,
|
|
"simulation_config": simulation_config,
|
|
**cost_result
|
|
}
|
|
)
|
|
|
|
self.recommendations = recommendations
|
|
|
|
def get_internal_external_wall_description(self, description_map, new_u_value):
|
|
|
|
if "Average thermal transmittance" in self.property.walls["clean_description"]:
|
|
if new_u_value is None:
|
|
raise ValueError("New u value is None")
|
|
return f'Average thermal transmittance {new_u_value} W/m-¦K'
|
|
|
|
return description_map[self.property.walls["clean_description"]]
|
|
|
|
def set_starting_simulation_config(self, wall_ending_config):
|
|
"""
|
|
Helper function to set the starting simulation config
|
|
"""
|
|
|
|
simulation_config = {}
|
|
if self.property.data["walls-energy-eff"] not in ["Good", "Very Good"]:
|
|
simulation_config = {
|
|
"walls_energy_eff_ending": "Good"
|
|
}
|
|
|
|
# We check if we have double insulation in any instances
|
|
double_insulation = (
|
|
(wall_ending_config["is_filled_cavity"] and wall_ending_config["external_insulation"]) or
|
|
(wall_ending_config["is_filled_cavity"] and wall_ending_config["internal_insulation"]) or
|
|
(wall_ending_config["external_insulation"] and wall_ending_config["internal_insulation"])
|
|
)
|
|
if double_insulation:
|
|
simulation_config["walls_energy_eff_ending"] = "Very Good"
|
|
|
|
return simulation_config
|
|
|
|
def _find_insulation(self, u_value, insulation_materials, non_insulation_materials, phase):
|
|
|
|
lowest_selected_u_value = None
|
|
recommendations = []
|
|
for _, insulation_material_group in insulation_materials.groupby("description"):
|
|
|
|
for _, material in insulation_material_group.iterrows():
|
|
|
|
part_u_value = r_value_per_mm_to_u_value(
|
|
material["depth"], material["r_value_per_mm"]
|
|
)
|
|
_, new_u_value = calculate_u_value_uplift(u_value, part_u_value)
|
|
new_u_value = math.ceil(new_u_value * 100.0) / 100.0
|
|
|
|
# If I have a lowest U value and my new u value is higher than that but lower than the
|
|
# diminishing returns threshold, it can be considered
|
|
|
|
# If I have a lowest U value and my new u value is lower than the lowest value, it's
|
|
# further into the diminishing returns threshold and can shouldn't be
|
|
|
|
if is_diminishing_returns(
|
|
recommendations,
|
|
new_u_value,
|
|
lowest_selected_u_value,
|
|
self.DIMINISHING_RETURNS_U_VALUE,
|
|
):
|
|
continue
|
|
|
|
# We allow a small tolerance for error so we don't discount the recommendation entirely
|
|
if new_u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
|
|
|
lowest_selected_u_value = update_lowest_selected_u_value(
|
|
lowest_selected_u_value, new_u_value
|
|
)
|
|
|
|
if material["type"] == "internal_wall_insulation":
|
|
cost_result = self.costs.internal_wall_insulation(
|
|
wall_area=self.property.insulation_wall_area,
|
|
material=material.to_dict(),
|
|
non_insulation_materials=non_insulation_materials,
|
|
)
|
|
already_installed = (
|
|
"internal_wall_insulation"
|
|
in self.property.already_installed
|
|
)
|
|
if already_installed:
|
|
cost_result = override_costs(cost_result)
|
|
|
|
new_description = self.get_internal_external_wall_description(
|
|
self.INTERNALLY_INSULATED_WALL_DESCRIPTIONS, new_u_value
|
|
)
|
|
|
|
elif material["type"] == "external_wall_insulation":
|
|
cost_result = self.costs.external_wall_insulation(
|
|
wall_area=self.property.insulation_wall_area,
|
|
material=material.to_dict(),
|
|
non_insulation_materials=non_insulation_materials,
|
|
)
|
|
already_installed = (
|
|
"external_wall_insulation"
|
|
in self.property.already_installed
|
|
)
|
|
if already_installed:
|
|
cost_result = override_costs(cost_result)
|
|
|
|
new_description = self.get_internal_external_wall_description(
|
|
self.EXTERNALLY_INSULATED_WALL_DESCRIPTIONS, new_u_value
|
|
)
|
|
else:
|
|
raise ValueError("Invalid material type")
|
|
|
|
wall_ending_config = WallAttributes(new_description).process()
|
|
|
|
walls_simulation_config = check_simulation_difference(
|
|
new_config=wall_ending_config, old_config=self.property.walls, prefix="walls_"
|
|
)
|
|
|
|
simulation_config = self.set_starting_simulation_config(
|
|
wall_ending_config=wall_ending_config
|
|
)
|
|
|
|
simulation_config = {
|
|
**walls_simulation_config,
|
|
**simulation_config,
|
|
"walls_thermal_transmittance_ending": new_u_value
|
|
}
|
|
|
|
recommendations.append(
|
|
{
|
|
"phase": phase,
|
|
"parts": [
|
|
get_recommended_part(
|
|
part=material.to_dict(),
|
|
quantity=self.property.insulation_wall_area,
|
|
quantity_unit=QuantityUnits.m2.value,
|
|
cost_result=cost_result,
|
|
)
|
|
],
|
|
"type": material["type"],
|
|
"description": self._make_description(material),
|
|
"starting_u_value": u_value,
|
|
"new_u_value": new_u_value,
|
|
"already_installed": already_installed,
|
|
"sap_points": None,
|
|
"simulation_config": simulation_config,
|
|
**cost_result
|
|
}
|
|
)
|
|
|
|
return recommendations
|
|
|
|
def find_insulation(self, u_value, phase):
|
|
"""
|
|
This function contains the logic for finding potential insulation measures for a property, depending
|
|
on the parts available and whether the property can have external wall insulation installed
|
|
:return:
|
|
"""
|
|
|
|
# Recommend external and internal wall insulation separately
|
|
# Since external and internal wall insulation are sufficiently different,
|
|
# we separate the logic for for recommending them, therefore we don't
|
|
# consider diminishing returns between the two as they are considered to be separate measures
|
|
|
|
ewi_recommendations = []
|
|
if self.ewi_valid():
|
|
ewi_recommendations = self._find_insulation(
|
|
u_value=u_value,
|
|
insulation_materials=pd.DataFrame(
|
|
self.external_wall_insulation_materials
|
|
),
|
|
non_insulation_materials=self.external_wall_non_insulation_materials,
|
|
phase=phase,
|
|
)
|
|
|
|
iwi_recommendations = self._find_insulation(
|
|
u_value=u_value,
|
|
insulation_materials=pd.DataFrame(self.internal_wall_insulation_materials),
|
|
non_insulation_materials=self.internal_wall_non_insulation_materials,
|
|
phase=phase,
|
|
)
|
|
|
|
self.recommendations += ewi_recommendations + iwi_recommendations
|
|
|
|
@staticmethod
|
|
def _make_description(material):
|
|
if material["type"] == "internal_wall_insulation":
|
|
return (
|
|
f"Install {int(material['depth'])}{material['depth_unit']} {material['description']} on internal "
|
|
f"walls"
|
|
)
|
|
|
|
if material["type"] == "external_wall_insulation":
|
|
return (
|
|
f"Install {int(material['depth'])}{material['depth_unit']} {material['description']} on external "
|
|
f"walls"
|
|
)
|
|
|
|
if material["type"] == "cavity_wall_insulation":
|
|
return f"Fill cavity with {material['description']}"
|
|
|
|
raise ValueError("Invalid material type")
|
|
|
|
@staticmethod
|
|
def rvalue_per_mm(total_r_value: float, thickness_mm: float) -> float:
|
|
"""Return R-value per mm.
|
|
|
|
Parameters
|
|
----------
|
|
total_r_value : float
|
|
Total R-value (in m2K/W).
|
|
thickness_mm : float
|
|
Thickness of the material in mm.
|
|
|
|
Returns
|
|
-------
|
|
float
|
|
R-value per mm.
|
|
"""
|
|
return total_r_value / thickness_mm
|
|
|
|
@staticmethod
|
|
def thermal_conductivity_to_r_value_per_mm(thermal_conductivity: float) -> float:
|
|
"""Convert thermal conductivity to R-value per mm.
|
|
|
|
Parameters
|
|
----------
|
|
thermal_conductivity : float
|
|
Thermal conductivity (in W/mK).
|
|
|
|
Returns
|
|
-------
|
|
float
|
|
R-value per mm.
|
|
"""
|
|
# Calculate R-value in m²K/W for 1 meter of the material
|
|
r_value_per_meter = 1 / thermal_conductivity
|
|
|
|
# Convert R-value to R-value per mm
|
|
r_value_per_mm = r_value_per_meter / 1000
|
|
|
|
return r_value_per_mm
|