feat: single objective optimization started
This commit is contained in:
parent
20a801626a
commit
21b4fc5202
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
|
@ -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
70
main.py
|
@ -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}'])
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user