diff --git a/backend/engine/engine.py b/backend/engine/engine.py index 4a503a08..18b93ec7 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -1065,21 +1065,8 @@ async def model_engine(body: PlanTriggerRequest): ) continue - fixed_gain = optimiser_functions.calculate_fixed_gain( - property_required_measures, recommendations, p, needs_ventilation - ) - gain = optimiser_functions.calculate_gain(body=body, p=p, fixed_gain=fixed_gain, eco_packages=eco_packages) - - # We insert the innovation uplift - measures_to_optimise_with_uplift = deepcopy(measures_to_optimise) - - for group in measures_to_optimise_with_uplift: - for r in group: - (r["partial_project_score"], r["partial_project_funding"], r["innovation_uplift"], - r["uplift_project_score"]) = (0, 0, 0, 0) - already_installed_measures = [] - for measures in measures_to_optimise_with_uplift: + for measures in measures_to_optimise: for m in measures: # A) We're going to make the already installed measures default # B) We need to SAP points for all already installed measures to avoid double counting @@ -1096,6 +1083,22 @@ async def model_engine(body: PlanTriggerRequest): default_already_installed = keep_max_sap_per_measure_type(already_installed_measures) already_installed_sap = float(sum(d["sap_points"] for d in default_already_installed)) + fixed_gain = optimiser_functions.calculate_fixed_gain( + property_required_measures, recommendations, p, needs_ventilation + ) + gain = optimiser_functions.calculate_gain( + body=body, p=p, fixed_gain=fixed_gain, eco_packages=eco_packages, + already_installed_gain=already_installed_sap + ) + + # We insert the innovation uplift + measures_to_optimise_with_uplift = deepcopy(measures_to_optimise) + + for group in measures_to_optimise_with_uplift: + for r in group: + (r["partial_project_score"], r["partial_project_funding"], r["innovation_uplift"], + r["uplift_project_score"]) = (0, 0, 0, 0) + # Remove them from the optimisation pool finalised_measures_to_optimise = [] for m in measures_to_optimise_with_uplift: diff --git a/etl/customers/peabody/Nov 2025 Consulting Project/k_deck_stats.py b/etl/customers/peabody/Nov 2025 Consulting Project/k_deck_stats.py index 5200c34d..c641da06 100644 --- a/etl/customers/peabody/Nov 2025 Consulting Project/k_deck_stats.py +++ b/etl/customers/peabody/Nov 2025 Consulting Project/k_deck_stats.py @@ -231,6 +231,49 @@ properties_data, plans_data, recommendations_data = get_data( ) recommendations_df = pd.DataFrame(recommendations_data) +properties_df = pd.DataFrame(properties_data) solar_pv_recommendations = recommendations_df[recommendations_df["measure_type"] == "solar_pv"] average_savings = solar_pv_recommendations.groupby("scenario_id")["energy_cost_savings"].mean().reset_index() + +# Check tenures +initial_asset_data = pd.read_excel( + "/Users/khalimconn-kowlessar/Documents/hestia/Customers/Peabody/Nov 2025 Consulting Project/2025_11_11 - Peabody " + "- Data Extracts for Domna.xlsx", + sheet_name="Properties" +) +sustainability_data = pd.read_excel( + "/Users/khalimconn-kowlessar/Documents/hestia/Customers/Peabody/Nov 2025 Consulting Project/2025_11_11 - Peabody " + "- Data Extracts for Domna.xlsx", + sheet_name="Sustainability" +) + +sustainability_sample = sustainability_data[ + sustainability_data["UPRN"].isin(properties_df["uprn"].astype(int).astype(str).values) +] + +sustainability_sample = sustainability_sample.merge( + initial_asset_data, left_on="Org Ref", right_on="UPRN", suffixes=("_sustainability", "_initial_asset") +) + +block_sizes = initial_asset_data["BlockCode"].value_counts().reset_index().sort_values("count", ascending=False) +block_sizes.to_excel("/Users/khalimconn-kowlessar/Downloads/peabody_block_sizes.xlsx", index=False) + +initial_asset_data.columns +initial_asset_data["LeaseType"].value_counts() + +# sustainability_sample["Tenure Group"].value_counts() +# Tenure Group +# General Needs 57787 +# Home Ownership 25471 +# Care & Supported Housing 4239 +# Rental 2677 +# Other 188 + +df = sustainability_sample["Ownership Type"].value_counts().to_frame().reset_index() +df.to_excel("/Users/khalimconn-kowlessar/Downloads/sustainability_tenures.xlsx", index=False) + +tenure_groups = sustainability_sample["Tenure Group"].value_counts().to_frame().reset_index() +tenure_groups.to_excel("/Users/khalimconn-kowlessar/Downloads/sustainability_tenure_groups.xlsx", index=False) + +initial_asset_data[~pd.isnull(initial_asset_data["BlockCode"])]["Tenure Group"].value_counts() diff --git a/recommendations/optimiser/optimiser_functions.py b/recommendations/optimiser/optimiser_functions.py index 0eec35dc..ca48d26d 100644 --- a/recommendations/optimiser/optimiser_functions.py +++ b/recommendations/optimiser/optimiser_functions.py @@ -202,8 +202,13 @@ def calculate_fixed_gain(property_required_measures, recommendations, p, needs_v return fixed_gain -def calculate_gain(body: PlanTriggerRequest, p: Property, fixed_gain: float, - eco_packages: None | dict = None) -> float | None: +def calculate_gain( + body: PlanTriggerRequest, + p: Property, + fixed_gain: float, + eco_packages: None | dict = None, + already_installed_gain: float = 0, +) -> float | None: """ Calculates the target gain value for optimisation based on the goal. @@ -221,6 +226,7 @@ def calculate_gain(body: PlanTriggerRequest, p: Property, fixed_gain: float, fixed_gain : float Total fixed gain from required measures (returned by calculate_fixed_gain). eco_packages : dict, optional + already_installed_gain: float, optional Returns ------- @@ -228,13 +234,16 @@ def calculate_gain(body: PlanTriggerRequest, p: Property, fixed_gain: float, Required SAP gain for EPC, or None for non-EPC goals. """ if body.goal == "Increasing EPC": - current_sap = int(p.data["current-energy-efficiency"]) + current_sap = int(p.data["current-energy-efficiency"]) + already_installed_gain target_sap = ( eco_packages.get(p.id)[1] if eco_packages.get(p.id)[1] is not None else epc_to_sap_lower_bound(body.goal_value) ) + if target_sap == current_sap: + return 0 + gain = CostOptimiser.calculate_sap_gain_with_slack( target_sap - current_sap ) - fixed_gain diff --git a/recommendations/tests/test_optimiser_functions.py b/recommendations/tests/test_optimiser_functions.py index 865e3398..ea0b5d94 100644 --- a/recommendations/tests/test_optimiser_functions.py +++ b/recommendations/tests/test_optimiser_functions.py @@ -85,6 +85,22 @@ class TestCalculateGain: gain = optimiser_functions.calculate_gain(body, prop, fixed_gain=0) assert gain is None + def test_returns_zero_for_already_installed_getting_to_target(self): + body = SimpleNamespace(goal="Increasing EPC", goal_value="C") + p = SimpleNamespace(data={"current-energy-efficiency": "67"}, id=1) + fixed_gain = 0 + eco_packages = {1: (None, None, None, [])} + already_installed_sap = 2 + gain = optimiser_functions.calculate_gain( + body=body, + p=p, + fixed_gain=fixed_gain, + eco_packages=eco_packages, + already_installed_gain=already_installed_sap + ) + + assert gain == 0 + def test_calculates_gain_for_epc(self, monkeypatch): # patch cost optimiser calculation monkeypatch.setattr(optimiser_functions, "epc_to_sap_lower_bound", lambda goal_value: 69)