mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
implementing the solar costing model (incomplete)
This commit is contained in:
parent
83339d2cbe
commit
dd825c73a7
5 changed files with 108 additions and 17 deletions
2
.idea/Model.iml
generated
2
.idea/Model.iml
generated
|
|
@ -7,7 +7,7 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/open_uprn" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (model_data)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (backend)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyNamespacePackagesService">
|
||||
|
|
|
|||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
|
@ -3,7 +3,7 @@
|
|||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.10 (backend)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (model_data)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (backend)" project-jdk-type="Python SDK" />
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ class Property:
|
|||
)
|
||||
self.set_energy_source()
|
||||
self.find_energy_sources()
|
||||
self.set_current_energy_bill()
|
||||
|
||||
def set_current_energy_bill(self):
|
||||
"""
|
||||
|
|
@ -604,6 +605,7 @@ class Property:
|
|||
self.current_adjusted_energy = AnnualBillSavings.adjust_energy_to_metered(
|
||||
epc_energy_consumption=starting_heat_demand,
|
||||
current_epc_rating=self.data["current-energy-rating"],
|
||||
total_floor_area=self.floor_area
|
||||
)
|
||||
|
||||
self.current_energy_bill = AnnualBillSavings.calculate_annual_bill(self.current_adjusted_energy)
|
||||
|
|
|
|||
|
|
@ -135,6 +135,99 @@ class GoogleSolarApi:
|
|||
# We now start finding the solar panel configurations
|
||||
self.optimise_solar_configuration()
|
||||
|
||||
@staticmethod
|
||||
def lifetime_production_ac_kwh(
|
||||
row,
|
||||
efficiency_depreciation_factor,
|
||||
installation_life_span
|
||||
):
|
||||
"""
|
||||
Mimics the function described in the Google Solar API documentation, presenting the lifetime production
|
||||
AC KWH as a geometri sum
|
||||
"""
|
||||
|
||||
return (
|
||||
row["initial_ac_kwh_per_year"] *
|
||||
(1 - pow(
|
||||
efficiency_depreciation_factor,
|
||||
installation_life_span)) /
|
||||
(1 - efficiency_depreciation_factor))
|
||||
|
||||
@staticmethod
|
||||
def annualUtilityBillEstimate(
|
||||
yearlyKWhEnergyConsumption,
|
||||
initialAcKwhPerYear,
|
||||
efficiencyDepreciationFactor,
|
||||
year,
|
||||
costIncreaseFactor,
|
||||
discountRate):
|
||||
"""
|
||||
Implements the bill costing model for esimating annual bill
|
||||
:param yearlyKWhEnergyConsumption:
|
||||
:param initialAcKwhPerYear:
|
||||
:param efficiencyDepreciationFactor:
|
||||
:param year:
|
||||
:param costIncreaseFactor:
|
||||
:param discountRate:
|
||||
:return:
|
||||
"""
|
||||
|
||||
return (
|
||||
billCostModel(
|
||||
yearlyKWhEnergyConsumption -
|
||||
annualProduction(
|
||||
initialAcKwhPerYear,
|
||||
efficiencyDepreciationFactor,
|
||||
year)) *
|
||||
pow(costIncreaseFactor, year) /
|
||||
pow(discountRate, year))
|
||||
|
||||
def lifetimeUtilityBill(
|
||||
yearlyKWhEnergyConsumption,
|
||||
initialAcKwhPerYear,
|
||||
efficiencyDepreciationFactor,
|
||||
installationLifeSpan,
|
||||
costIncreaseFactor,
|
||||
discountRate):
|
||||
bill = [0] * installationLifeSpan
|
||||
for year in range(installationLifeSpan):
|
||||
bill[year] = annualUtilityBillEstimate(
|
||||
yearlyKWhEnergyConsumption,
|
||||
initialAcKwhPerYear,
|
||||
efficiencyDepreciationFactor,
|
||||
year,
|
||||
costIncreaseFactor,
|
||||
discountRate)
|
||||
return bill
|
||||
|
||||
def estimate_solar_costs(self, panel_performance):
|
||||
"""
|
||||
This method implements the recommended costing approach, to estimate the ROI of a solar panel
|
||||
configuration, as described in the Google Solar API documentation
|
||||
:param panel_performance: dataframe containing the solar panel array configuration and energy generation data
|
||||
:return:
|
||||
"""
|
||||
|
||||
# we now estiamte the financial benefits of solar panels for the household, using the framework described
|
||||
# by the Google Solar API
|
||||
# 1) Convert Solar Energy AD production from the DC production
|
||||
panel_performance["initial_ac_kwh_per_year"] = panel_performance["yearly_dc_energy"] * self.dc_to_ac_rate
|
||||
|
||||
# Remove anything where the total ac energy is less than half of the array wattage
|
||||
panel_performance = panel_performance[
|
||||
(panel_performance["initial_ac_kwh_per_year"] / panel_performance["array_warrage"]) >= 0.5
|
||||
]
|
||||
|
||||
# 2) Calculate the liftime solar energy production
|
||||
panel_performance['lifetime_ac_kwh'] = panel_performance.apply(
|
||||
self.lifetime_production_ac_kwh,
|
||||
axis=1,
|
||||
efficiency_depreciation_factor=self.efficiency_depreciation_factor,
|
||||
installation_life_span=self.installation_life_span
|
||||
)
|
||||
|
||||
# TODO: Complete the rest of the solar model
|
||||
|
||||
def optimise_solar_configuration(self):
|
||||
"""
|
||||
Optimise the solar panel configuration for the building.
|
||||
|
|
@ -153,14 +246,14 @@ class GoogleSolarApi:
|
|||
roi_summary = []
|
||||
for segment in roof_segment_summaries:
|
||||
wattage = segment["panelsCount"] * self.insights_data["solarPotential"]["panelCapacityWatts"]
|
||||
generated_energy = segment["yearlyEnergyDcKwh"]
|
||||
ratio = generated_energy / wattage
|
||||
cost = MCS_SOLAR_PV_COST_DATA["average_cost_per_kwh"] * (generated_energy / 1000)
|
||||
generated_dc_energy = segment["yearlyEnergyDcKwh"]
|
||||
ratio = generated_dc_energy / wattage
|
||||
cost = MCS_SOLAR_PV_COST_DATA["average_cost_per_kwh"] * (generated_dc_energy / 1000)
|
||||
roi_summary.append(
|
||||
{
|
||||
"segmentIndex": segment["segmentIndex"],
|
||||
"wattage": wattage,
|
||||
"generatedEnergy": generated_energy,
|
||||
"generated_dc_energy": generated_dc_energy,
|
||||
"ratio": ratio,
|
||||
"n_panels": segment["panelsCount"],
|
||||
"cost": cost,
|
||||
|
|
@ -171,15 +264,15 @@ class GoogleSolarApi:
|
|||
roi_summary = pd.DataFrame(roi_summary)
|
||||
|
||||
weighted_ratio = np.average(
|
||||
roi_summary["ratio"].values, weights=roi_summary["generatedEnergy"].values
|
||||
roi_summary["ratio"].values, weights=roi_summary["generated_dc_energy"].values
|
||||
)
|
||||
total_cost = roi_summary["cost"].sum()
|
||||
total_energy = roi_summary["generatedEnergy"].sum()
|
||||
yearly_dc_energy = roi_summary["generated_dc_energy"].sum()
|
||||
|
||||
panel_performance.append(
|
||||
{
|
||||
"n_panels": roi_summary["n_panels"].sum(),
|
||||
"total_energy": total_energy,
|
||||
"yearly_dc_energy": yearly_dc_energy,
|
||||
"total_cost": total_cost,
|
||||
"weighted_ratio": weighted_ratio,
|
||||
"panneled_roof_area": roi_summary["panneled_roof_area"].sum(),
|
||||
|
|
@ -192,10 +285,8 @@ class GoogleSolarApi:
|
|||
panel_performance = panel_performance.drop_duplicates()
|
||||
# Ensure more than 4 panels
|
||||
panel_performance = panel_performance[panel_performance["n_panels"] >= 4]
|
||||
# Remove anything where the total energy is less than half of the array wattage
|
||||
panel_performance = panel_performance[
|
||||
(panel_performance["total_energy"] / panel_performance["array_warrage"]) >= 0.5
|
||||
]
|
||||
|
||||
self.estimate_solar_costs()
|
||||
|
||||
# This first bracket is the value of the energy bill savings
|
||||
panel_performance["bill_savings"] = (
|
||||
|
|
|
|||
|
|
@ -352,9 +352,10 @@ async def trigger_plan(body: PlanTriggerRequest):
|
|||
|
||||
logger.info("Getting spatial data")
|
||||
for p in input_properties:
|
||||
p.get_components(cleaned, photo_supply_lookup, floor_area_decile_thresholds)
|
||||
p.get_spatial_data(uprn_filenames)
|
||||
# Call Google Solar API
|
||||
solar_api_client.get(longitude=p.spatial["longitude"], latitude=p.spatial["latitude"])
|
||||
solar_performance = solar_api_client.get(longitude=p.spatial["longitude"], latitude=p.spatial["latitude"])
|
||||
|
||||
logger.info("Getting components and epc recommendations")
|
||||
recommendations = {}
|
||||
|
|
@ -362,9 +363,6 @@ async def trigger_plan(body: PlanTriggerRequest):
|
|||
representative_recommendations = {}
|
||||
for p in tqdm(input_properties):
|
||||
|
||||
# Property recommendations
|
||||
p.get_components(cleaned, photo_supply_lookup, floor_area_decile_thresholds)
|
||||
|
||||
recommender = Recommendations(property_instance=p, materials=materials, exclusions=body.exclusions)
|
||||
property_recommendations, property_representative_recommendations = recommender.recommend()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue