import pandas as pd from BaseUtility import Definitions from backend.Property import Property class VentilationRecommendations(Definitions): """ For properties that do not have ventilation, we recommend installing ventilaion This is particularly important for properties that have insulated walls and is also crucial for prevent overheating risks in warmer months """ def __init__( self, property_instance: Property, materials ): self.property = property_instance self.recommendation = None self.mechanical_ventilation_materials = [part for part in materials if part["type"] == "mechanical_ventilation"] self.trickle_vent_materials = [part for part in materials if part["type"] == "trickle_vent"] def recommend(self, phase): """ If there is no ventilation, we recommend installing ventilation Generally, best practice is to install controlled ventilation for insulated walls so we still recommend ventilation if there is natural ventilation :return: """ self.property.identify_ventilation() if self.property.has_ventilation: return # We recommend installing 2 units n_units = 2 parts = self.mechanical_ventilation_materials.copy() already_installed = "mechanical_ventilation" in self.property.already_installed # TODO: We now have multiple ventilation options - we default to selecting the cheapest option part = min(parts, key=lambda x: x['total_cost']) estimated_cost = n_units * part["total_cost"] if not already_installed else 0 labour_hours = 4 * n_units if not already_installed else 0 labour_days = 4 * n_units / 8.0 if not already_installed else 0 part["total"] = estimated_cost part["quantity"] = n_units part["quantity_unit"] = "part" # We recommend installing two mechanical ventilation systems self.recommendation = [ { "phase": phase, "parts": [part], "type": part["type"], "measure_type": "mechanical_ventilation", "description": f"Install {n_units} {part['description']} units", "starting_u_value": None, "new_u_value": None, "already_installed": already_installed, "sap_points": 0, "heat_demand": 0, "kwh_savings": 0, "co2_equivalent_savings": 0, "energy_cost_savings": 0, "total": estimated_cost, # We use a very simple and rough estimate of 4 hours per unit "labour_hours": labour_hours, "labour_days": labour_days, # Assume 8 hour day "simulation_config": { "mechanical_ventilation_ending": "mechanical, extract only", }, "description_simulation": { "mechanical-ventilation": "mechanical, extract only" }, "innovation_rate": part["innovation_rate"], } ] def recommend_trickle_vents(self): """ This is not something that we can identify completely non-invasively, however a recommendation which may come about as a result of an energy assessment is the installation of trickle vents. This function handles that """ trickle_vents_recommendation_config = next( (r for r in self.property.non_invasive_recommendations if r["type"] == "trickle_vents"), {} ) if not trickle_vents_recommendation_config: return description = ( "Install trickle vents on your windows" if not trickle_vents_recommendation_config.get("description") else trickle_vents_recommendation_config["description"] ) cheapest_trickle_vent = min( self.trickle_vent_materials, key=lambda x: x["total_cost"] ) return [ { "phase": None, "parts": [], "type": "trickle_vents", "measure_type": "trickle_vents", "description": description, "starting_u_value": None, "new_u_value": None, "already_installed": False, "sap_points": 0, "heat_demand": 0, "kwh_savings": 0, "co2_equivalent_savings": 0, "energy_cost_savings": 0, "total": cheapest_trickle_vent["total_cost"] * self.property.number_of_windows, # We use a very simple and rough estimate of 4 hours per unit "labour_hours": trickle_vents_recommendation_config.get("labour_hours", 8), "labour_days": trickle_vents_recommendation_config.get("labour_days", 1), # Assume 8 hour day "survey": True } ]