Merge pull request #666 from Hestia-Homes/portfolio-diagnostics

Added change to pass an already installed sap value, which rebaseline…
This commit is contained in:
KhalimCK 2026-01-12 17:34:56 +00:00 committed by GitHub
commit 43b327e31d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 88 additions and 17 deletions

View file

@ -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:

View file

@ -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()

View file

@ -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

View file

@ -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)