diff --git a/.idea/Model.iml b/.idea/Model.iml
index 4413bb06..b0f9c00d 100644
--- a/.idea/Model.iml
+++ b/.idea/Model.iml
@@ -7,7 +7,7 @@
-
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6f308057..1122b380 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/recommendations/Costs.py b/recommendations/Costs.py
index 23edd287..3fe0e560 100644
--- a/recommendations/Costs.py
+++ b/recommendations/Costs.py
@@ -40,6 +40,10 @@ class Costs:
# We assume a conservative 10% contingency for all works which is a rate defined by SPONs
CONTINGENCY = 0.1
+ # We use a higher contingency rate for internal wall insulation because of the potential for issues with moving
+ # fittings and trimming doors, as well as scope for damage to the existing wall during preparation.
+ IWI_CONTINGENCY = 0.15
+
# Where there is more uncertainty, a higher contingency rate is used
HIGH_RISK_CONTINGENCY = 0.2
# When there is less uncertainty, a lower contingency rate is used
@@ -232,7 +236,7 @@ class Costs:
subtotal_before_profit = labour_costs + materials_costs + demolition_plant_costs
- contingency_cost = subtotal_before_profit * self.CONTINGENCY
+ contingency_cost = subtotal_before_profit * self.IWI_CONTINGENCY
preliminaries_cost = subtotal_before_profit * self.PRELIMINARIES
profit_cost = subtotal_before_profit * self.PROFIT_MARGIN
diff --git a/recommendations/tests/test_costs.py b/recommendations/tests/test_costs.py
index 1ba601a8..2854b298 100644
--- a/recommendations/tests/test_costs.py
+++ b/recommendations/tests/test_costs.py
@@ -19,7 +19,7 @@ class TestCosts:
"prime_cost": 5.17,
"material_cost": 5.62,
"labour_cost": 1.125,
- "labour_hours": 0.065
+ "labour_hours_per_unit": 0.065,
}
cwi_results = costs.cavity_wall_insulation(
@@ -27,10 +27,12 @@ class TestCosts:
material=cwi_material,
)
- assert cwi_results == {'total': 1027.0280465530302, 'subtotal': 855.8567054608585, 'vat': 171.1713410921717,
- 'contingency': 63.396792997100626, 'preliminaries': 63.396792997100626,
- 'material': 539.0166061175574, 'profit': 95.09518949565093,
- 'labour_hours': 6.234177828761786, 'labour_cost': 94.95132385344874}
+ assert cwi_results == {
+ 'total': 1065.0661223512907, 'subtotal': 887.5551019594088, 'vat': 177.51102039188177,
+ 'contingency': 63.396792997100626, 'preliminaries': 63.396792997100626, 'material': 539.0166061175574,
+ 'profit': 126.79358599420125, 'labour_hours': 6.234177828761786, 'labour_cost': 94.95132385344874,
+ 'labour_days': 0.38963611429761164
+ }
def test_loft_insulation(self):
mock_property = Mock()
@@ -46,7 +48,7 @@ class TestCosts:
"prime_cost": None,
"material_cost": 5.91938,
"labour_cost": 1.96,
- "labour_hours": 0.11
+ "labour_hours_per_unit": 0.11
}
loft_results = costs.loft_insulation(
@@ -54,10 +56,11 @@ class TestCosts:
material=loft_material,
)
- assert loft_results == {'total': 414.8496486, 'subtotal': 345.70804050000004, 'vat': 69.14160810000001,
- 'contingency': 25.608003000000004, 'preliminaries': 25.608003000000004,
- 'material': 198.29923000000002, 'profit': 38.4120045, 'labour_hours': 3.685,
- 'labour_cost': 57.7808}
+ assert loft_results == {
+ 'total': 430.21445040000003, 'subtotal': 358.512042, 'vat': 71.70240840000001,
+ 'contingency': 25.608003000000004, 'preliminaries': 25.608003000000004, 'material': 198.29923000000002,
+ 'profit': 51.21600600000001, 'labour_hours': 3.685, 'labour_cost': 57.7808, 'labour_days': 0.460625
+ }
def test_internal_wall_insulation(self):
mock_property = Mock()
@@ -171,11 +174,14 @@ class TestCosts:
non_insulation_materials=iwi_non_insulation_materials
)
- assert iwi_results == {'total': 6421.5484411659245, 'subtotal': 5351.29036763827, 'vat': 1070.258073527654,
- 'contingency': 573.3525393898148, 'preliminaries': 382.2350262598765,
- 'material': 1747.488000615996, 'profit': 573.3525393898148,
- 'labour_hours': 88.23759388401297, 'labour_days': 2.757424808875405,
- 'labour_cost': 1927.1602026551818}
+ assert iwi_results == {
+ 'total': 6650.889456921851, 'subtotal': 5542.407880768209, 'vat': 1108.4815761536418,
+ 'contingency': 573.3525393898148, 'preliminaries': 382.2350262598765,
+ 'material': 1747.488000615996,
+ 'profit': 764.470052519753, 'labour_hours': 88.23759388401297,
+ 'labour_days': 2.757424808875405,
+ 'labour_cost': 1927.1602026551818
+ }
def test_suspended_floor_insulation(self):
mock_property = Mock()
@@ -185,16 +191,18 @@ class TestCosts:
costs = Costs(mock_property)
- sus_floor_material = {'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll',
- 'depth': 140.0,
- 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.039,
- 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 0,
- 'material_cost': 11.68, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1,
- 'plant_cost': 0,
- 'total_cost': 13.46, 'link': 'SPONs',
- 'Notes': 'Spons did not contain labour costs so we use values for similar insulations. '
- 'We use the '
- 'same values as in Crown loft roll 44, since it is also an insulation roll'}
+ sus_floor_material = {
+ 'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll',
+ 'depth': 140.0,
+ 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.039,
+ 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 0,
+ 'material_cost': 11.68, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1,
+ 'plant_cost': 0,
+ 'total_cost': 13.46, 'link': 'SPONs',
+ 'Notes': 'Spons did not contain labour costs so we use values for similar insulations. '
+ 'We use the '
+ 'same values as in Crown loft roll 44, since it is also an insulation roll'
+ }
sus_floor_non_insulation_materials = [
{'type': 'suspended_floor_demolition', 'description': 'Removal of carpet and underfelt', 'depth': 0,
@@ -231,9 +239,8 @@ class TestCosts:
)
assert sus_floor_results == {
- 'total': 3003.366924, 'subtotal': 2502.80577, 'vat': 500.561154,
- 'contingency': 185.39302, 'preliminaries': 185.39302, 'material': 483.405,
- 'profit': 278.08952999999997, 'labour_hours': 54.940000000000005,
+ 'total': 3114.6027360000003, 'subtotal': 2595.50228, 'vat': 519.100456, 'contingency': 185.39302,
+ 'preliminaries': 185.39302, 'material': 483.405, 'profit': 370.78604, 'labour_hours': 54.940000000000005,
'labour_days': 2.289166666666667, 'labour_cost': 1370.5252
}
@@ -263,28 +270,29 @@ class TestCosts:
'description': 'clean surface of concrete to receive new damp-proof membrane', 'depth': 0,
'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 4.36, 'labour_hours_per_unit': 0.14,
- 'plant_cost': 0, 'total_cost': 4.36, 'link': 0, 'Notes': 0}, {'type': 'solid_floor_preparation',
- 'description': 'Clean out crack to '
- 'form a 20mm×20mm '
- 'groove and fill with '
- 'cement: mortar mixed '
- 'with bonding agent',
- 'depth': 0, 'depth_unit': 0,
- 'cost_unit': 0,
- 'thermal_conductivity': 0,
- 'thermal_conductivity_unit': 0,
- 'prime_material_cost': 0,
- 'material_cost': 6.91,
- 'labour_cost': 18.99,
- 'labour_hours_per_unit': 0.61,
- 'plant_cost': 0.16,
- 'total_cost': 26.06, 'link': 0,
- 'Notes': 'This step is the '
- 'assessment and repair of '
- 'any damage to the concrete '
- 'floor such as filling '
- 'cracks or levelling uneven '
- 'areas'},
+ 'plant_cost': 0, 'total_cost': 4.36, 'link': 0, 'Notes': 0}, {
+ 'type': 'solid_floor_preparation',
+ 'description': 'Clean out crack to '
+ 'form a 20mm×20mm '
+ 'groove and fill with '
+ 'cement: mortar mixed '
+ 'with bonding agent',
+ 'depth': 0, 'depth_unit': 0,
+ 'cost_unit': 0,
+ 'thermal_conductivity': 0,
+ 'thermal_conductivity_unit': 0,
+ 'prime_material_cost': 0,
+ 'material_cost': 6.91,
+ 'labour_cost': 18.99,
+ 'labour_hours_per_unit': 0.61,
+ 'plant_cost': 0.16,
+ 'total_cost': 26.06, 'link': 0,
+ 'Notes': 'This step is the '
+ 'assessment and repair of '
+ 'any damage to the concrete '
+ 'floor such as filling '
+ 'cracks or levelling uneven '
+ 'areas'},
{'type': 'solid_floor_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier',
'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
'thermal_conductivity_unit': 0, 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48,
@@ -316,8 +324,8 @@ class TestCosts:
)
assert sol_floor_results == {
- 'total': 3962.021952, 'subtotal': 3301.68496, 'vat': 660.336992, 'contingency': 353.75196,
- 'preliminaries': 235.83464, 'material': 1006.3399999999999, 'profit': 353.75196, 'labour_hours': 57.285,
+ 'total': 4245.023520000001, 'subtotal': 3537.5196, 'vat': 707.5039200000001, 'contingency': 471.66928,
+ 'preliminaries': 235.83464, 'material': 1006.3399999999999, 'profit': 471.66928, 'labour_hours': 57.285,
'labour_days': 2.386875, 'labour_cost': 1346.6464
}
@@ -331,11 +339,13 @@ class TestCosts:
costs = Costs(mock_property)
- ewi_material = {'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board',
- 'depth': 150.0, 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.022,
- 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 23.53,
- 'material_cost': 34.62, 'labour_cost': 33.06, 'labour_hours_per_unit': 1.4, 'plant_cost': 0,
- 'total_cost': 67.68, 'link': 'SPONs', 'Notes': 0}
+ ewi_material = {
+ 'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board',
+ 'depth': 150.0, 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.022,
+ 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 23.53,
+ 'material_cost': 34.62, 'labour_cost': 33.06, 'labour_hours_per_unit': 1.4, 'plant_cost': 0,
+ 'total_cost': 67.68, 'link': 'SPONs', 'Notes': 0
+ }
ewi_non_insulation_materials = [
{'type': 'ewi_wall_demolition',
'description': 'Solid & Dry Lined walls: Hack of wall finishes with chipping '
@@ -403,9 +413,8 @@ class TestCosts:
)
assert ewi_results == {
- 'total': 13590.909723215433, 'subtotal': 11325.758102679527, 'vat': 2265.1516205359053,
- 'contingency': 808.9827216199662, 'preliminaries': 1213.4740824299492,
- 'material': 4020.565147410677, 'profit': 1213.4740824299492,
- 'labour_hours': 187.02533486285358, 'labour_days': 5.8445417144641745,
+ 'total': 14561.688989159393, 'subtotal': 12134.740824299493, 'vat': 2426.948164859899,
+ 'contingency': 808.9827216199662, 'preliminaries': 1617.9654432399325, 'material': 4020.565147410677,
+ 'profit': 1617.9654432399325, 'labour_hours': 187.02533486285358, 'labour_days': 5.8445417144641745,
'labour_cost': 3921.5600094613983
}