diff --git a/recommendations/tests/test_optimisers.py b/recommendations/tests/test_optimisers.py index 394e741d..c265bb99 100644 --- a/recommendations/tests/test_optimisers.py +++ b/recommendations/tests/test_optimisers.py @@ -494,12 +494,65 @@ def _find_measure(input_measures, measure_type): def make_funding_paths(input_measures, tenure): + """ + This function generates funding paths based on the input measures and the tenure of the property. + It checks for the presence of specific measures and creates paths that include necessary insulation measures + to meet minimum insulation requirements, particularly when a heating system is recommended. + + Remaining measures that are not fixed as part of the package are then optimised + :param input_measures: + :param tenure: + :return: + """ + funding_paths = [] + + # We handle the case of minimum insulation requirements. Whenever we have a heating system recommendation, + # we *must* include an additional insulation measure, unless the property already has sufficient insulation. + + # We determine which insulation measures need to be included + wall_insulation_measures = [ + "internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation", + "extension_cavity_wall_insulation" + ] + roof_insulation_measures = [ + "loft_insulation", "flat_roof_insulation", "room_roof_insulation" + ] + # These are the insulation measures that the property still needs and so will be considered for + # filling the minimum insulation requirements + remaining_insulation_type = [] + for insulation_measure in wall_insulation_measures + roof_insulation_measures: + if _find_measure(input_measures, insulation_measure): + remaining_insulation_type.append(insulation_measure) + + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Air source heat pump + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + # For all tenures, if we have an air source heat pump it's a funded measure and so we must consider the minimum + # insulation requirements + if _find_measure(input_measures, "air_source_heat_pump"): + ashp_template = [{"AND": ["air_source_heat_pump"]}] + ashp_paths = [] + for insulation_measure in remaining_insulation_type: + new_ashp_path = deepcopy(ashp_template) + new_ashp_path[0]["AND"].append(insulation_measure) + ashp_paths.append(new_ashp_path) + + if ashp_paths: + # If we have any insulation measures, we add them to the funding paths + funding_paths.extend(ashp_paths) + else: + # If we have no insulation measures, we just add the ASHP path + funding_paths.append(ashp_template) + + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Solar PV with existing eligible heating system + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if tenure == "Social": raise NotImplementedError("Implement me!") - funding_paths = [] - - if tenure == "Private" + if tenure == "Private": # We cover off the main funding paths # 1) The package must include EWI or IWI # We check if we have any EWI or IWI measures available @@ -514,12 +567,6 @@ def make_funding_paths(input_measures, tenure): if ewi_or_iwi[0]["OR"]: funding_paths.append(ewi_or_iwi) - # 2) The package must include a renewable heating system like an ASHP - ashp = [{"OR": []}] - if _find_measure(input_measures, "air_source_heat_pump"): - ashp[0]["OR"].append("air_source_heat_pump") - funding_paths.append(ashp) - # 3) The package must have an existing eligible heating system. We test this with the funding checker # If we have any remaining insulation measure to be applied to the property, we also need to include that in # the package @@ -532,14 +579,6 @@ def make_funding_paths(input_measures, tenure): if has_eligible_heating_system: single_solar_template[0]["AND"].append("solar_pv") # We now look to pair this with any lingering insulation measures - wall_insulation_measures = [ - "internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation", - "extension_cavity_wall_insulation" - ] - roof_insulation_measures = [ - "loft_insulation", "flat_roof_insulation", "room_roof_insulation" - ] - # We search for these solar_paths_with_insulation = [] for insulation_measure in wall_insulation_measures + roof_insulation_measures: if _find_measure(input_measures, insulation_measure): @@ -555,14 +594,16 @@ def make_funding_paths(input_measures, tenure): else: # If we don't have an eligible heating system, we check if we have an eligible heating system # (HHRSH/ASHP/Electric boiler) + solar PV - if _find_measure(input_measures, "solar_pv") and _find_measure(input_measures, - "high_heat_retention_storage_heater"): + if (_find_measure(input_measures, "solar_pv") and + _find_measure(input_measures, "high_heat_retention_storage_heater")): funding_paths.append([{"AND": ["solar_pv", "high_heat_retention_storage_heater"]}]) - if _find_measure(input_measures, "solar_pv") and _find_measure(input_measures, "air_source_heat_pump"): + if (_find_measure(input_measures, "solar_pv") and + _find_measure(input_measures, "air_source_heat_pump")): funding_paths.append([{"AND": ["solar_pv", "air_source_heat_pump"]}]) - if _find_measure(input_measures, "solar_pv") and _find_measure(input_measures, "electric_boiler"): + if (_find_measure(input_measures, "solar_pv") and + _find_measure(input_measures, "electric_boiler")): funding_paths.append([{"AND": ["solar_pv", "electric_boiler"]}]) @@ -791,9 +832,6 @@ def expand_min_insulation_if_needed(input_measures, fixed_selection): return expanded -from copy import deepcopy - - # ---- tiny utilities ---------------------------------------------------------- def parse_types(t):