fix: single objective optimization algorithm is completed. The cooling system needs to be separated in the archetype

This commit is contained in:
Saeed Ranjbar 2024-09-16 18:51:53 -04:00
parent 59815f4bbf
commit 2ed9555fc8
9 changed files with 86 additions and 65 deletions

View File

@ -58,7 +58,7 @@ class ArchetypeCluster1:
hp = self.building.energy_systems[1].generation_systems[1]
cooling_demand_joules = self.building.cooling_demand[cte.HOUR]
cooling_peak_load = self.building.cooling_peak_load[cte.YEAR][0]
cutoff_temperature = 13
cutoff_temperature = 11
outdoor_temperature = self.building.external_temperature[cte.HOUR]
results = HeatPumpCooling(hp=hp,
hourly_cooling_demand_joules=cooling_demand_joules,

View File

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

View File

@ -38,7 +38,7 @@ class HeatPump:
cop_curve_coefficients[2] * t_inlet_water_fahrenheit ** 2 +
cop_curve_coefficients[3] * t_source_fahrenheit +
cop_curve_coefficients[4] * t_source_fahrenheit ** 2 +
cop_curve_coefficients[5] * t_inlet_water_fahrenheit * t_source_fahrenheit))
cop_curve_coefficients[5] * t_inlet_water_fahrenheit * t_source_fahrenheit)) / 3.41214
hp_efficiency = float(self.hp.cooling_efficiency)
else:
if self.hp.heat_efficiency_curve is not None:

View File

@ -59,12 +59,15 @@ class HeatPumpCooling:
hp_electricity[i] = 0
hp_electricity_j = [(x * cte.WATTS_HOUR_TO_JULES) / number_of_ts for x in hp_electricity]
hp_hourly = []
hp_supply_temperature_hourly = []
hp_sum = 0
for i in range(1, len(demand)):
hp_sum += hp_electricity_j[i]
if (i - 1) % number_of_ts == 0:
hp_hourly.append(hp_sum)
hp_supply_temperature_hourly.append(t_sup_hp[i])
hp_sum = 0
self.hp.cooling_supply_temperature = hp_supply_temperature_hourly
self.hp.energy_consumption[cte.COOLING] = {}
self.hp.energy_consumption[cte.COOLING][cte.HOUR] = hp_hourly
self.hp.energy_consumption[cte.COOLING][cte.MONTH] = MonthlyValues.get_total_month(
@ -73,7 +76,7 @@ class HeatPumpCooling:
sum(self.hp.energy_consumption[cte.COOLING][cte.MONTH])]
self.results['Cooling Demand (W)'] = demand
self.results['HP Cooling Output (W)'] = q_hp
self.results['HP Source Temperature'] = source_temperature
self.results['HP Cooling Source Temperature'] = source_temperature
self.results['HP Cooling Supply Temperature'] = t_sup_hp
self.results['HP Cooling COP'] = hp_cop
self.results['HP Electricity Consumption'] = hp_electricity

View File

@ -8,8 +8,8 @@ from energy_system_modelling_package.energy_system_modelling_factories.montreal_
class HeuristicSizing:
def __init__(self, city, population_size=100, generations=50, crossover_rate=0.8, mutation_rate=0.1,
optimization_scenario='cost', output_path=None):
def __init__(self, city, population_size=100, generations=20, crossover_rate=0.8, mutation_rate=0.1,
optimization_scenario='energy', output_path=None):
self.city = city
self.population_size = population_size
self.generations = generations
@ -20,13 +20,24 @@ class HeuristicSizing:
# Main Genetic Algorithm function
def genetic_algorithm(self):
best_individuals = []
min_fitness_scores = []
city_best_individuals = []
building_best_individuals = []
best_fitness_score = float('inf')
best_solution = None
best_solution_generation = 0
for building in self.city.buildings:
print(building.name)
population = self.initialize_population(building)
for generation in range(self.generations):
print(generation)
fitness_scores = self.evaluate_population(building, population)
population = sorted(population, key=lambda x: x['fitness_score'])
building_best_individuals.append(population[0])
if population[0]['fitness_score'] < best_fitness_score:
best_fitness_score_index = fitness_scores.index(population[0]['fitness_score'])
best_fitness_score = fitness_scores[best_fitness_score_index]
best_solution = population[0]
best_solution_generation = generation
selected_population = self.tournament_selection(population, fitness_scores)
next_population = []
for i in range(0, self.population_size, 2):
@ -34,14 +45,10 @@ class HeuristicSizing:
child1, child2 = self.crossover(parent1, parent2)
next_population.extend([self.mutate(child1, building), self.mutate(child2, building)])
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))
print("best individuals are", best_individuals)
return best_individuals, min_fitness_scores
print(f"best individual of {building.name} is ", best_solution, "It is found in generation:",
best_solution_generation)
city_best_individuals.append(best_solution)
return city_best_individuals
@staticmethod
def system_components(building):
@ -56,14 +63,14 @@ class HeuristicSizing:
for generation_system in energy_system.generation_systems:
system_components[cte.HEATING]['generation_components'].append({'type': generation_system.system_type,
'efficiency':
generation_system.heat_efficiency,
generation_system.heat_efficiency,
'capacity': 0})
if generation_system.energy_storage_systems is not None:
system_components[cte.HEATING]['storage_components'] = []
for storage_system in generation_system.energy_storage_systems:
if storage_system.type_energy_stored == cte.THERMAL:
system_components[cte.HEATING]['storage_components'].append({'storage_type':
storage_system.type_energy_stored,
storage_system.type_energy_stored,
'volume': 0})
if cte.COOLING in energy_system.demand_types:
system_components[cte.COOLING] = {'generation_components': []}
@ -71,14 +78,14 @@ class HeuristicSizing:
if generation_system.fuel_type == cte.ELECTRICITY:
system_components[cte.COOLING]['generation_components'].append({'type': generation_system.system_type,
'efficiency':
generation_system.cooling_efficiency,
generation_system.cooling_efficiency,
'capacity': 0})
if generation_system.energy_storage_systems is not None:
system_components[cte.COOLING]['storage_components'] = []
for storage_system in generation_system.energy_storage_systems:
if storage_system.type_energy_stored == cte.THERMAL:
system_components[cte.COOLING]['storage_components'].append({'storage_type':
storage_system.type_energy_stored,
storage_system.type_energy_stored,
'volume': 0})
if cte.DOMESTIC_HOT_WATER in energy_system.demand_types and cte.HEATING not in energy_system.demand_types:
system_components[cte.DOMESTIC_HOT_WATER] = {'generation_components': []}
@ -93,7 +100,7 @@ class HeuristicSizing:
for storage_system in generation_system.energy_storage_systems:
if storage_system.type_energy_stored == cte.THERMAL:
system_components[cte.DOMESTIC_HOT_WATER]['storage_components'].append({'storage_type':
storage_system.type_energy_stored,
storage_system.type_energy_stored,
'volume': 0})
return system_components
@ -146,42 +153,41 @@ class HeuristicSizing:
if self.optimization_scenario == 'cost':
lcc = self.life_cycle_cost(building, population)
fitness_scores = lcc
elif self.optimization_scenario == 'energy':
energy_consumption = self.energy_consumption(building, population)
fitness_scores = energy_consumption
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)
# Append a deep copy to avoid modifying the original individual
if fitness_scores[i] < fitness_scores[j]:
selected.append(copy.deepcopy(population[i]))
else:
selected.append(copy.deepcopy(population[j]))
return selected
# Crossover (Uniform Crossover)
def crossover(self, parent1, parent2):
if random.random() < self.crossover_rate:
child1, child2 = copy.deepcopy(parent1), copy.deepcopy(parent2)
for key in parent1:
if random.random() < 0.5:
child1[key], child2[key] = child2[key], child1[key]
if key != 'fitness_score':
if random.random() < 0.5:
child1[key], child2[key] = child2[key], child1[key]
return child1, child2
else:
return copy.deepcopy(parent1), copy.deepcopy(parent2)
# Mutation
def mutate(self, individual, building):
"""
Mutates the individual's generation and storage components.
- `individual`: the individual to mutate (contains generation and storage components).
- `building`: building data that contains constraints such as peak heating load and available space.
"""
# Mutate generation components
for key in individual:
if key != 'lcc':
if key != 'fitness_score':
for generation_component in individual[key]['generation_components']:
if random.random() < self.mutation_rate:
if key == cte.HEATING:
@ -197,40 +203,46 @@ class HeuristicSizing:
if 'storage_components' in individual[key]:
for storage_component in individual[key]['storage_components']:
if storage_component['storage_type'] == cte.THERMAL and random.random() < self.mutation_rate:
# Mutate the 'volume' within the available space in the building
max_available_space = 0.01 * building.volume / building.storeys_above_ground
storage_component['volume'] = random.uniform(0, max_available_space)
# for generation_component in individual['generation_components']:
# if random.random() < self.mutation_rate:
# # Mutate the 'capacity' within the range of 0 to the building's peak heating load
# generation_component['capacity'] = random.uniform(0, building.heating_peak_load[cte.YEAR][0])
#
# # Mutate storage components
# for storage_component in individual['storage_components']:
# if storage_component['storage_type'] == cte.THERMAL and random.random() < self.mutation_rate:
# # Mutate the 'volume' within the available space in the building
# max_available_space = 0.01 * building.volume / building.storeys_above_ground
# storage_component['volume'] = random.uniform(0, max_available_space)
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)
individual_feasibility = self.run_system_archetype_simulation(building, individual)
if individual_feasibility:
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['fitness_score'] = total_lcc
costs.append(total_lcc)
else:
individual['fitness_score'] = float('inf')
costs.append(float('inf'))
return costs
def energy_consumption(self, building, population):
total_consumptions = []
for individual in population:
individual_feasibility = self.run_system_archetype_simulation(building, individual)
if individual_feasibility:
total_consumption = (building.heating_consumption[cte.YEAR][0] + building.cooling_consumption[cte.YEAR][0] +
building.domestic_hot_water_consumption[cte.YEAR][0])/ (cte.WATTS_HOUR_TO_JULES * 1000)
individual['fitness_score'] = total_consumption
total_consumptions.append(total_consumption)
else:
individual['fitness_score'] = float('inf')
total_consumptions.append(float('inf'))
return total_consumptions
def run_system_archetype_simulation(self, building, individual):
feasibility = True
if building.energy_systems_archetype_cluster_id == '1':
building.energy_systems[1].generation_systems[0].nominal_heat_output = \
individual[cte.HEATING]['generation_components'][0]['capacity']
@ -249,13 +261,19 @@ class HeuristicSizing:
building=building,
output_path=self.output_path,
csv_output=False).enrich()
if min(building.energy_systems[1].generation_systems[0].energy_storage_systems[0].temperature) < 35:
feasibility = False
if min(building.energy_systems[-1].generation_systems[0].energy_storage_systems[0].temperature) < 55:
feasibility = False
if max(building.energy_systems[1].generation_systems[1].cooling_supply_temperature) > 16:
feasibility = False
return feasibility
def enrich_buildings(self):
best_individuals, best_fitness_scores = self.genetic_algorithm()
best_individuals = self.genetic_algorithm()
for (i, building) in enumerate(self.city.buildings):
energy_systems = building.energy_systems
best_individual = best_individuals[i]
# best_fitness_score = best_fitness_scores[i]
for energy_system in energy_systems:
if cte.HEATING in energy_system.demand_types:
for generation_system in energy_system.generation_systems:
@ -275,7 +293,7 @@ class HeuristicSizing:
for generation_component in best_individual[cte.COOLING]['generation_components']:
if generation_component['type'] == system_type:
generation_system.nominal_cooling_output = generation_component['capacity']
if generation_system.energy_storage_systems is not None:
if generation_system.energy_storage_systems is not None and generation_system.fuel_type == cte.ELECTRICITY:
for storage_system in generation_system.energy_storage_systems:
storage_type = storage_system.type_energy_stored
for storage_component in best_individual[cte.COOLING]['storage_components']:
@ -293,4 +311,3 @@ class HeuristicSizing:
for storage_component in best_individual[cte.DOMESTIC_HOT_WATER]['storage_components']:
if storage_component['storage_type'] == storage_type:
storage_system.volume = storage_component['volume']

View File

@ -474,7 +474,7 @@ class NonPvGenerationSystem(GenerationSystem):
Get the hourly cooling supply temperature
:return: list
"""
return self._heat_supply_temperature
return self._cooling_supply_temperature
@cooling_supply_temperature.setter
def cooling_supply_temperature(self, value):

View File

@ -109,16 +109,16 @@ class ThermalStorageSystem(EnergyStorageSystem):
@property
def temperature(self) -> dict:
"""
Get fuel consumption in W, m3, or kg
:return: dict{[float]}
Get hourly storage temperature in degree Celsius
:return: list
"""
return self._temperature
@temperature.setter
def temperature(self, value):
"""
Set fuel consumption in W, m3, or kg
:param value: dict{[float]}
Set hourly storage temperature in degree Celsius
:param value: list
"""
self._temperature = value

View File

@ -912,7 +912,7 @@
<nominal_cooling_output/>
<minimum_cooling_output/>
<maximum_cooling_output/>
<cooling_efficiency>4</cooling_efficiency>
<cooling_efficiency>4.5</cooling_efficiency>
<electricity_efficiency/>
<source_temperature/>
<source_mass_flow/>

View File

@ -51,12 +51,13 @@ 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()
# 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()
print('test')
# retrofitted_energy_consumption = consumption_data(city)
# retrofitted_life_cycle_cost = {}
# for building in city.buildings: