feat: single objective optimization started

This commit is contained in:
Saeed Ranjbar 2024-09-11 09:36:30 -04:00
parent 20a801626a
commit 21b4fc5202
12 changed files with 271 additions and 16 deletions

View File

@ -30,11 +30,11 @@ class EnergySystemsSizingFactory:
for building in self._city.buildings:
building.level_of_detail.energy_systems = 1
def _heurisitc_sizing(self):
def _heuristic_sizing(self):
"""
Size Energy Systems using a Single or Multi Objective GA
"""
HeuristicSizing(self._city).enrich_buildings()
HeuristicSizing(self._city).genetic_algorithm()
self._city.level_of_detail.energy_systems = 1
for building in self._city.buildings:
building.level_of_detail.energy_systems = 1

View File

@ -13,17 +13,18 @@ class MontrealEnergySystemArchetypesSimulationFactory:
EnergySystemsFactory class
"""
def __init__(self, handler, building, output_path):
def __init__(self, handler, building, output_path, csv_output=True):
self._output_path = output_path
self._handler = '_' + handler.lower()
self._building = building
self._csv_output = csv_output
def _archetype_cluster_1(self):
"""
Enrich the city by using the sizing and simulation model developed for archetype13 of montreal_future_systems
"""
dt = 900
ArchetypeCluster1(self._building, dt, self._output_path, csv_output=True).enrich_building()
ArchetypeCluster1(self._building, dt, self._output_path, self._csv_output).enrich_building()
self._building.level_of_detail.energy_systems = 2
self._building.level_of_detail.energy_systems = 2

View File

@ -1,6 +1,159 @@
import copy
import random
import hub.helpers.constants as cte
from costing_package.constants import SYSTEM_RETROFIT
from costing_package.cost import Cost
from energy_system_modelling_package.energy_system_modelling_factories.montreal_energy_system_archetype_modelling_factory import \
MontrealEnergySystemArchetypesSimulationFactory
class HeuristicSizing:
def __init__(self, city):
pass
def __init__(self, city, population_size=20, generations=100, crossover_rate=0.8, mutation_rate=0.1,
optimization_scenario='cost', output_path=None):
self.city = city
self.population_size = population_size
self.generations = generations
self.crossover_rate = crossover_rate
self.mutation_rate = mutation_rate
self.optimization_scenario = optimization_scenario
self.output_path = output_path
# Main Genetic Algorithm function
def genetic_algorithm(self):
best_individuals = []
min_fitness_scores = []
for building in self.city.buildings:
population = self.initialize_population(building)
for generation in range(self.generations):
fitness_scores = self.evaluate_population(building, population)
print(f"Generation {generation}, Best Fitness: {min(fitness_scores)}")
selected_population = self.tournament_selection(population, fitness_scores)
next_population = []
for i in range(0, self.population_size, 2):
parent1, parent2 = selected_population[i], selected_population[i + 1]
child1, child2 = self.crossover(parent1, parent2)
next_population.extend([self.mutate(child1), self.mutate(child2)])
population = next_population
# Evaluate final population
fitness_scores = self.evaluate_population(building, population)
best_index = fitness_scores.index(min(fitness_scores))
best_individual = population[best_index]
best_individuals.append(best_individual)
min_fitness_scores.append(min(fitness_scores))
return best_individuals, min_fitness_scores
@staticmethod
def system_components(building):
system_components = {}
energy_systems = building.energy_systems
for energy_system in energy_systems:
if cte.HEATING in energy_system.demand_types:
if cte.DOMESTIC_HOT_WATER in energy_system.demand_types:
break
else:
system_components['heating_system'] = {'generation_components': []}
for generation_system in energy_system.generation_systems:
system_components['heating_system']['generation_components'].append({'type': generation_system.system_type,
'efficiency':
generation_system.heat_efficiency,
'capacity': 0})
if generation_system.energy_storage_systems is not None:
system_components['heating_system']['storage_components'] = []
for storage_system in generation_system.energy_storage_systems:
if storage_system.type_energy_stored == cte.THERMAL:
system_components['heating_system']['storage_components'].append({'storage_type':
storage_system.type_energy_stored,
'volume': 0})
return system_components
def initialize_population(self, building):
system_components = self.system_components(building)
heating_system_population = []
for _ in range(self.population_size):
individual_components = copy.deepcopy(system_components['heating_system'])
for heat_generation_component in individual_components['generation_components']:
heat_generation_component['capacity'] = random.uniform(0, building.heating_peak_load[cte.YEAR][0])
for storage_component in individual_components['storage_components']:
if storage_component['storage_type'] == cte.THERMAL:
max_available_space = 0.01 * building.volume / building.storeys_above_ground
storage_component['volume'] = random.uniform(0, max_available_space)
heating_system_population.append(individual_components)
return heating_system_population
# Evaluate fitness of the population
def evaluate_population(self, building, population):
fitness_scores = None
if self.optimization_scenario == 'cost':
lcc = self.life_cycle_cost(building, population)
fitness_scores = lcc
return fitness_scores
# Selection (Tournament Selection)
@staticmethod
def tournament_selection(population, fitness_scores):
selected = []
for _ in range(len(population)):
i, j = random.sample(range(len(population)), 2)
if fitness_scores[i] < fitness_scores[j]:
selected.append(population[i])
else:
selected.append(population[j])
return selected
# Crossover (Uniform Crossover)
def crossover(self, parent1, parent2):
if random.random() < self.crossover_rate:
child1, child2 = parent1[:], parent2[:]
for i in range(len(parent1)):
if random.random() < 0.5:
child1[i], child2[i] = child2[i], child1[i]
return child1, child2
else:
return parent1, parent2
# Mutation
def mutate(self, individual):
for i in range(len(individual)):
if random.random() < self.mutation_rate:
if i == 3: # cutoff_temp
individual[i] = random.uniform(40, 60)
else:
individual[i] = random.uniform(0.1, 10.0)
return individual
def life_cycle_cost(self, building, population):
costs = []
for individual in population:
self.run_system_archetype_simulation(building, individual)
cost_retrofit_scenario = SYSTEM_RETROFIT
lcc_dataframe = Cost(building=building,
retrofit_scenario=cost_retrofit_scenario,
fuel_tariffs=['Electricity-D', 'Gas-Energir']).life_cycle
total_lcc = (lcc_dataframe.loc['total_capital_costs_systems', f'Scenario {cost_retrofit_scenario}'] +
lcc_dataframe.loc['total_operational_costs', f'Scenario {cost_retrofit_scenario}'] +
lcc_dataframe.loc['total_maintenance_costs', f'Scenario {cost_retrofit_scenario}'] +
lcc_dataframe.loc['end_of_life_costs', f'Scenario {cost_retrofit_scenario}'])
individual['lcc'] = total_lcc
costs.append(total_lcc)
return costs
def run_system_archetype_simulation(self, building, individual):
if building.energy_systems_archetype_cluster_id == '1':
building.energy_systems[1].generation_systems[0].nominal_heat_output = (
individual)['generation_components'][0]['capacity']
building.energy_systems[1].generation_systems[1].nominal_heat_output = (
individual)['generation_components'][1]['capacity']
building.energy_systems[1].generation_systems[0].energy_storage_systems[0].volume = (
individual)['storage_components'][0]['volume']
MontrealEnergySystemArchetypesSimulationFactory(
handler=f'archetype_cluster_{building.energy_systems_archetype_cluster_id}',
building=building,
output_path=self.output_path,
csv_output=False).enrich()
def enrich_buildings(self):
pass
best_individuals, best_fitness_scores = self.genetic_algorithm()
for building in self.city.buildings:
energy_systems = building.energy_systems

View File

@ -22,7 +22,7 @@ class EnergySystemRetrofitReport:
self.retrofit_scenario = retrofit_scenario
self.report = LatexReport('energy_system_retrofit_report',
'Energy System Retrofit Report', self.retrofit_scenario, output_path)
self.system_schemas_path = (Path(__file__).parent.parent / 'hub' / 'data' / 'energy_systems' / 'schemas')
self.system_schemas_path = (Path(__file__).parent.parent.parent / 'hub' / 'data' / 'energy_systems' / 'schemas')
self.charts_path = Path(output_path) / 'charts'
self.charts_path.mkdir(parents=True, exist_ok=True)
files = glob.glob(f'{self.charts_path}/*')
@ -233,7 +233,7 @@ class EnergySystemRetrofitReport:
for archetype, buildings in energy_system_archetypes.items():
buildings_str = ", ".join(buildings)
text += f"Figure 4 shows the schematic of the proposed energy system for buildings {buildings_str}.\n"
if archetype in ['PV+4Pipe+DHW', 'PV+ASHP+GasBoiler+TES']:
if archetype in ['PV+4Pipe+DHW', 'PV+ASHP+GasBoiler+TES', 'Central 4 Pipes Air to Water Heat Pump and Gas Boiler with Independent Water Heating and PV']:
text += "This energy system archetype is formed of the following systems: \par"
items = ['Rooftop Photovoltaic System: The rooftop PV system is tied to the grid and in case there is surplus '
'energy, it is sold to Hydro-Quebec through their Net-Meterin program.',
@ -257,8 +257,7 @@ class EnergySystemRetrofitReport:
for i in range(len(months)):
tilted_radiation = 0
for building in self.city.buildings:
tilted_radiation += (building.roofs[0].global_irradiance_tilted[cte.MONTH][i] /
(cte.WATTS_HOUR_TO_JULES * 1000))
tilted_radiation += (building.roofs[0].global_irradiance_tilted[cte.MONTH][i] / 1000)
monthly_roof_radiation.append(tilted_radiation)
def plot_bar_chart(ax, months, values, color, ylabel, title):
@ -565,7 +564,7 @@ class EnergySystemRetrofitReport:
placement='H')
self.report.add_section(f'{self.retrofit_scenario}')
self.report.add_subsection('Proposed Systems')
# self.energy_system_archetype_schematic()
self.energy_system_archetype_schematic()
if 'PV' in self.retrofit_scenario:
self.report.add_subsection('Rooftop Photovoltaic System Implementation')
self.pv_system()

View File

@ -49,7 +49,7 @@ ExportsFactory('sra', city, sra_output_path).export()
sra_path = (sra_output_path / f'{city.name}_sra.xml').resolve()
subprocess.run(['sra', str(sra_path)])
ResultFactory('sra', city, sra_output_path).enrich()
pv_feasibility(-73.5681295982132, 45.49218262677643, 0.0001, selected_buildings=city.buildings)
# pv_feasibility(-73.5681295982132, 45.49218262677643, 0.0001, selected_buildings=city.buildings)
energy_plus_workflow(city, energy_plus_output_path)
random_assignation.call_random(city.buildings, random_assignation.residential_systems_percentage)
EnergySystemsFactory('montreal_custom', city).enrich()

View File

@ -14,9 +14,10 @@ class EnergyStorageSystem(ABC):
Energy Storage System Abstract Class
"""
def __init__(self, storage_id, model_name=None, manufacturer=None,
def __init__(self, storage_id=None, name=None, model_name=None, manufacturer=None,
nominal_capacity=None, losses_ratio=None):
self._storage_id = storage_id
self._name = name
self._model_name = model_name
self._manufacturer = manufacturer
self._nominal_capacity = nominal_capacity
@ -30,6 +31,14 @@ class EnergyStorageSystem(ABC):
"""
return self._storage_id
@property
def name(self):
"""
Get storage name
:return: string
"""
return self._name
@property
def type_energy_stored(self):
"""

View File

@ -15,11 +15,11 @@ class ThermalStorageSystem(EnergyStorageSystem):
Energy Storage System Class
"""
def __init__(self, storage_id, type_energy_stored=None, model_name=None, manufacturer=None, storage_type=None,
def __init__(self, storage_id=None, name=None, type_energy_stored=None, model_name=None, manufacturer=None, storage_type=None,
nominal_capacity=None, losses_ratio=None, volume=None, height=None, layers=None,
maximum_operating_temperature=None, storage_medium=None, heating_coil_capacity=None):
super().__init__(storage_id, model_name, manufacturer, nominal_capacity, losses_ratio)
super().__init__(storage_id, name, model_name, manufacturer, nominal_capacity, losses_ratio)
self._type_energy_stored = type_energy_stored
self._storage_type = storage_type
self._volume = volume
@ -109,6 +109,7 @@ class ThermalStorageSystem(EnergyStorageSystem):
'Storage component':
{
'storage id': self.id,
'name': self.name,
'type of energy stored': self.type_energy_stored,
'model name': self.model_name,
'manufacturer': self.manufacturer,

View File

@ -278,6 +278,7 @@ class MontrealFutureSystemCatalogue(Catalog):
template_storages = self._archetypes['EnergySystemCatalog']['energy_storage_components']['templateStorages']
for tes in thermal_storages:
storage_id = tes['storage_id']
storage_name = tes['name']
type_energy_stored = tes['type_energy_stored']
model_name = tes['model_name']
manufacturer = tes['manufacturer']
@ -302,6 +303,7 @@ class MontrealFutureSystemCatalogue(Catalog):
losses_ratio = tes['losses_ratio']
heating_coil_capacity = tes['heating_coil_capacity']
storage_component = ThermalStorageSystem(storage_id=storage_id,
name=storage_name,
model_name=model_name,
type_energy_stored=type_energy_stored,
manufacturer=manufacturer,
@ -318,6 +320,7 @@ class MontrealFutureSystemCatalogue(Catalog):
for template in template_storages:
storage_id = template['storage_id']
storage_name = template['name']
storage_type = template['storage_type']
type_energy_stored = template['type_energy_stored']
maximum_operating_temperature = template['maximum_operating_temperature']
@ -342,6 +345,7 @@ class MontrealFutureSystemCatalogue(Catalog):
volume = template['physical_characteristics']['volume']
heating_coil_capacity = template['heating_coil_capacity']
storage_component = ThermalStorageSystem(storage_id=storage_id,
name=storage_name,
model_name=model_name,
type_energy_stored=type_energy_stored,
manufacturer=manufacturer,

View File

@ -14,6 +14,7 @@ class EnergyStorageSystem(ABC):
Energy storage System class
"""
def __init__(self):
self._name = None
self._type_energy_stored = None
self._storage_type = None
self._model_name = None
@ -21,6 +22,22 @@ class EnergyStorageSystem(ABC):
self._nominal_capacity = None
self._losses_ratio = None
@property
def name(self):
"""
Get name of storage system
:return: string
"""
return self._name
@name.setter
def name(self, value):
"""
Set name of storage system
:return: string
"""
self._name = value
@property
def type_energy_stored(self):
"""

View File

@ -163,6 +163,7 @@ class MontrealFutureEnergySystemParameters:
_generic_storage_system.type_energy_stored = 'electrical'
else:
_generic_storage_system = ThermalStorageSystem()
_generic_storage_system.name = storage_system.name
_generic_storage_system.type_energy_stored = storage_system.type_energy_stored
_generic_storage_system.height = storage_system.height
_generic_storage_system.layers = storage_system.layers

70
main.py
View File

@ -0,0 +1,70 @@
from pathlib import Path
import subprocess
from building_modelling.ep_run_enrich import energy_plus_workflow
from energy_system_modelling_package.energy_system_modelling_factories.montreal_energy_system_archetype_modelling_factory import \
MontrealEnergySystemArchetypesSimulationFactory
from hub.imports.geometry_factory import GeometryFactory
from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from hub.imports.results_factory import ResultFactory
from building_modelling.geojson_creator import process_geojson
from energy_system_modelling_package import random_assignation
from hub.imports.energy_systems_factory import EnergySystemsFactory
from energy_system_modelling_package.energy_system_modelling_factories.energy_system_sizing_factory import EnergySystemsSizingFactory
from energy_system_modelling_package.energy_system_retrofit.energy_system_retrofit_results import consumption_data, cost_data
from costing_package.cost import Cost
from costing_package.constants import SYSTEM_RETROFIT_AND_PV, CURRENT_STATUS, SYSTEM_RETROFIT
from hub.exports.exports_factory import ExportsFactory
# Specify the GeoJSON file path
input_files_path = (Path(__file__).parent / 'input_files')
input_files_path.mkdir(parents=True, exist_ok=True)
geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0001)
geojson_file_path = input_files_path / 'output_buildings.geojson'
output_path = (Path(__file__).parent / 'out_files').resolve()
output_path.mkdir(parents=True, exist_ok=True)
energy_plus_output_path = output_path / 'energy_plus_outputs'
energy_plus_output_path.mkdir(parents=True, exist_ok=True)
simulation_results_path = (Path(__file__).parent / 'out_files' / 'simulation_results').resolve()
simulation_results_path.mkdir(parents=True, exist_ok=True)
sra_output_path = output_path / 'sra_outputs'
sra_output_path.mkdir(parents=True, exist_ok=True)
cost_analysis_output_path = output_path / 'cost_analysis'
cost_analysis_output_path.mkdir(parents=True, exist_ok=True)
city = GeometryFactory(file_type='geojson',
path=geojson_file_path,
height_field='height',
year_of_construction_field='year_of_construction',
function_field='function',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city
ConstructionFactory('nrcan', city).enrich()
UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city).enrich()
ExportsFactory('sra', city, sra_output_path).export()
sra_path = (sra_output_path / f'{city.name}_sra.xml').resolve()
subprocess.run(['sra', str(sra_path)])
ResultFactory('sra', city, sra_output_path).enrich()
energy_plus_workflow(city, energy_plus_output_path)
random_assignation.call_random(city.buildings, random_assignation.residential_new_systems_percentage)
EnergySystemsFactory('montreal_future', city).enrich()
EnergySystemsSizingFactory('pv_sizing', city).enrich()
EnergySystemsSizingFactory('peak_load_sizing', city).enrich()
EnergySystemsSizingFactory('heuristic_sizing', city).enrich()
# for building in city.buildings:
# MontrealEnergySystemArchetypesSimulationFactory(f'archetype_cluster_{building.energy_systems_archetype_cluster_id}',
# building,
# simulation_results_path).enrich()
# retrofitted_energy_consumption = consumption_data(city)
# retrofitted_life_cycle_cost = {}
# for building in city.buildings:
# cost_retrofit_scenario = SYSTEM_RETROFIT
# lcc_dataframe = Cost(building=building,
# retrofit_scenario=cost_retrofit_scenario,
# fuel_tariffs=['Electricity-D', 'Gas-Energir']).life_cycle
# print(lcc_dataframe.loc['total_capital_costs_systems', f'Scenario {cost_retrofit_scenario}'])