feat: single objective optimization started

This commit is contained in:
Saeed Ranjbar 2024-09-26 17:44:52 -04:00
parent 4c3b28a088
commit 07cdfc4bf7
4 changed files with 142 additions and 93 deletions

View File

@ -10,9 +10,8 @@ from energy_system_modelling_package.energy_system_modelling_factories.system_si
class GeneticAlgorithm:
def __init__(self, city, population_size=100, generations=50, crossover_rate=0.8, mutation_rate=0.1,
def __init__(self, population_size=100, generations=50, 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
@ -24,8 +23,11 @@ class GeneticAlgorithm:
def energy_system_archetype_optimal_sizing(self):
pass
# Initialize Population
def initialize_population(self, energy_system):
# Initialize Population
def initialize_population(self, building, energy_system, heating_design_load=None, cooling_design_load=None,
available_space=None, dt=1800, fuel_price_index=0.05, electricity_tariff_type='fixed',
consumer_price_index=0.04, interest_rate=0.04, discount_rate=0.03, percentage_credit=0,
credit_years=15):
"""
The population to optimize the sizes of generation and storage components of any energy system are optimized.
The initial population will be a list of individuals where each of them represent a possible solution to the
@ -35,21 +37,28 @@ class GeneticAlgorithm:
"""
population = []
for i in range(self.population_size):
population.append(Individual(energy_system, self.optimization_scenario))
population.append(Individual(building, energy_system, self.optimization_scenario,
heating_design_load=heating_design_load, cooling_design_load=cooling_design_load,
available_space=available_space, dt=dt, fuel_price_index=fuel_price_index,
electricity_tariff_type=electricity_tariff_type,
consumer_price_index=consumer_price_index, interest_rate=interest_rate,
discount_rate=discount_rate, percentage_credit=percentage_credit,
credit_years=credit_years))
return population
def solve_ga(self, energy_system):
def solve_ga(self, building, energy_system):
"""
Solving GA for a single energy system. Here are the steps:
1- Population is initialized using the "initialize_population" method in this class.
:param building:
:param energy_system:
:return:
"""
energy_system = energy_system
best_individuals = []
best_solution = None
population = self.initialize_population(energy_system)
population = self.initialize_population(building, energy_system)
for individual in population:
individual.score_evaluation()
print(individual.individual['feasible'])
print(individual.individual['fitness_score'])

View File

@ -12,9 +12,9 @@ import numpy_financial as npf
class Individual:
def __init__(self, building, energy_system, optimization_scenario, heating_design_load=None,
cooling_design_load=None, available_space=None, dt=1800, fuel_price_index=0.05,
electricity_tariff_type='fixed', consumer_price_index=0.04, interest_rate=0.04,
discount_rate=0.03, percentage_credit=0, credit_years=15):
cooling_design_load=None, available_space=None, dt=None, fuel_price_index=None,
electricity_tariff_type='fixed', consumer_price_index=None, interest_rate=None,
discount_rate=None, percentage_credit=None, credit_years=None):
"""
:param building: building object
:param energy_system: energy system to be optimized
@ -95,10 +95,12 @@ class Individual:
f'{energy_storage_system.type_energy_stored}_storage'))
if energy_storage_system.type_energy_stored == cte.THERMAL:
heating_coil_capacity = energy_storage_system.heating_coil_capacity
heating_coil_fuel_cost = self.fuel_cost_per_kwh(f'{cte.ELECTRICITY}', 'fixed')
volume = 0
capacity = None
else:
heating_coil_capacity = None
heating_coil_fuel_cost = None
volume = None
capacity = 0
self.individual['Energy Storage Components'].append(
@ -108,6 +110,7 @@ class Individual:
'heating_coil_capacity': heating_coil_capacity,
'unit_investment_cost': investment_cost,
'unit_reposition_cost': reposition_cost,
'heating_coil_fuel_cost': heating_coil_fuel_cost,
'unit_maintenance_cost': 0,
'lifetime': lifetime
})
@ -122,12 +125,18 @@ class Individual:
generation_components = self.individual['Generation Components']
storage_components = self.individual['Energy Storage Components']
for generation_component in generation_components:
if generation_component['nominal_heating_efficiency'] is not None and self.heating_design_load is not None:
generation_component['heating_capacity'] = random.uniform(0, self.heating_design_load)
if generation_component['nominal_heating_efficiency'] is not None:
if self.heating_design_load is not None:
generation_component['heating_capacity'] = random.uniform(0, self.heating_design_load)
else:
generation_component['heating_capacity'] = random.uniform(0, self.building.heating_peak_load[cte.YEAR][0])
else:
generation_component['heating_capacity'] = None
if generation_component['nominal_cooling_efficiency'] is not None and self.cooling_design_load is not None:
generation_component['cooling_capacity'] = random.uniform(0, self.cooling_design_load)
if generation_component['nominal_cooling_efficiency'] is not None:
if self.cooling_design_load is not None:
generation_component['cooling_capacity'] = random.uniform(0, self.cooling_design_load)
else:
generation_component['cooling_capacity'] = random.uniform(0, self.building.cooling_peak_load[cte.YEAR][0])
else:
generation_component['cooling_capacity'] = None
if generation_component['nominal_electricity_efficiency'] is None:
@ -135,12 +144,15 @@ class Individual:
for storage_component in storage_components:
if storage_component['type'] == f'{cte.THERMAL}_storage':
storage_component['volume'] = random.uniform(0, 0.01 * self.available_space)
if storage_component['heating_coil_capacity'] is not None:
storage_component['heating_coil_capacity'] = random.uniform(0, self.heating_design_load)
def score_evaluation(self):
self.initialization()
self.system_simulation()
self.individual['feasible'] = self.feasibility
if self.feasibility:
if self.optimization_scenario == 'cost':
if 'cost' in self.optimization_scenario:
investment_cost = 0
operation_cost_year_0 = 0
maintenance_cost_year_0 = 0
@ -157,17 +169,27 @@ class Individual:
for storage_system in self.individual['Energy Storage Components']:
if cte.THERMAL in storage_system['type']:
investment_cost += storage_system['volume'] * storage_system['unit_investment_cost']
if storage_system['heating_coil_capacity'] is not None:
operation_cost_year_0 += (storage_system['yearly_energy_consumption(kWh)'] *
storage_system['heating_coil_fuel_cost'])
lcc = self.life_cycle_cost_calculation(investment_cost=investment_cost,
operation_cost_year_0=operation_cost_year_0,
maintenance_cost_year_0=maintenance_cost_year_0)
self.individual['fitness score'] = lcc
elif self.optimization_scenario == 'energy_consumption':
pass
self.individual['lcc'] = lcc
self.individual['fitness_score'] = lcc
elif 'energy_consumption' in self.optimization_scenario:
total_energy_consumption = 0
for generation_system in self.individual['Generation Components']:
total_energy_consumption += generation_system['yearly_energy_consumption(kWh)']
for storage_system in self.individual['Energy Storage Components']:
if cte.THERMAL in storage_system['type'] and storage_system['heating_coil_capacity'] is not None:
total_energy_consumption += storage_system['yearly_energy_consumption(kWh)']
self.individual['total_energy_consumption'] = total_energy_consumption
self.individual['fitness_score'] = total_energy_consumption
elif self.optimization_scenario == 'cost_energy_consumption':
pass
else:
self.individual['fitness score'] = float('inf')
print(self.individual)
self.individual['fitness_score'] = float('inf')
def system_simulation(self):
"""
@ -183,9 +205,12 @@ class Individual:
hp.nominal_heat_output = self.individual['Generation Components'][1]['heating_capacity']
tes = self.energy_system.generation_systems[0].energy_storage_systems[0]
tes.volume = self.individual['Energy Storage Components'][0]['volume']
tes.height = self.building.average_storey_height - 1.5
tes.height = self.building.average_storey_height - 1
tes.heating_coil_capacity = self.individual['Energy Storage Components'][0]['heating_coil_capacity'] \
if self.individual['Energy Storage Components'][0]['heating_coil_capacity'] is not None else 0
heating_demand_joules = self.building.heating_demand[cte.HOUR]
heating_peak_load_watts = self.heating_design_load
heating_peak_load_watts = self.heating_design_load if (self.heating_design_load is not
None) else self.building.heating_peak_load[cte.YEAR][0]
upper_limit_tes_heating = 55
outdoor_temperature = self.building.external_temperature[cte.HOUR]
results = HeatPumpBoilerTesHeating(hp=hp,
@ -200,7 +225,12 @@ class Individual:
self.feasibility = False
elif cte.DOMESTIC_HOT_WATER in self.demand_types:
hp = self.energy_system.generation_systems[0]
hp.nominal_heat_output = self.individual['Generation Components'][0]['heating_capacity']
tes = self.energy_system.generation_systems[0].energy_storage_systems[0]
tes.volume = self.individual['Energy Storage Components'][0]['volume']
tes.height = self.building.average_storey_height - 1
tes.heating_coil_capacity = self.individual['Energy Storage Components'][0]['heating_coil_capacity'] \
if self.individual['Energy Storage Components'][0]['heating_coil_capacity'] is not None else 0
dhw_demand_joules = self.building.domestic_hot_water_heat_demand[cte.HOUR]
upper_limit_tes = 65
outdoor_temperature = self.building.external_temperature[cte.HOUR]
@ -218,8 +248,80 @@ class Individual:
for generation_component in self.individual['Generation Components']:
if generation_component['type'] in generation_system_types:
index = generation_system_types.index(generation_component['type'])
generation_component['yearly_energy_consumption(kWh)'] = (
self.energy_system.generation_systems[index].energy_consumption[cte.HEATING][cte.YEAR][0] / 3.6e6)
for demand_type in self.demand_types:
if demand_type in self.energy_system.generation_systems[index].energy_consumption:
generation_component['yearly_energy_consumption(kWh)'] = (
self.energy_system.generation_systems[index].energy_consumption[demand_type][cte.YEAR][0] / 3.6e6)
for storage_component in self.individual['Energy Storage Components']:
if storage_component['type'] == f'{cte.THERMAL}_storage' and storage_component[
'heating_coil_capacity'] is not None:
for generation_system in self.energy_system.generation_systems:
if generation_system.energy_storage_systems is not None:
for storage_system in generation_system.energy_storage_systems:
if storage_system.type_energy_stored == cte.THERMAL:
for demand_type in self.demand_types:
if demand_type in storage_system.heating_coil_energy_consumption:
storage_component['yearly_energy_consumption(kWh)'] = (
storage_system.heating_coil_energy_consumption[demand_type][cte.YEAR][0] / 3.6e6)
def life_cycle_cost_calculation(self, investment_cost, operation_cost_year_0, maintenance_cost_year_0,
life_cycle_duration=41):
"""
Calculating the life cycle cost of the energy system. The original cost workflow in hub is not used to reduce
computation time.Here are the steps:
1- Costs catalog and different components are imported.
2- Capital costs (investment and reposition) are calculated and appended to a list to have the capital cash
flow.
3-
:param maintenance_cost_year_0:
:param operation_cost_year_0:
:param investment_cost:
:param life_cycle_duration:
:return:
"""
capital_costs_cash_flow = [investment_cost]
operational_costs_cash_flow = [0]
maintenance_costs_cash_flow = [0]
end_of_life_costs = [0] * (life_cycle_duration + 1)
for i in range(1, life_cycle_duration + 1):
yearly_operational_cost = math.pow(1 + self.fuel_price_index, i) * operation_cost_year_0
yearly_maintenance_cost = math.pow(1 + self.consumer_price_index, i) * maintenance_cost_year_0
yearly_capital_cost = 0
for generation_system in self.individual['Generation Components']:
if (i % generation_system['lifetime']) == 0 and i != (life_cycle_duration - 1):
cost_increase = math.pow(1 + self.consumer_price_index, i)
heating_capacity = 0 if generation_system['heating_capacity'] is None else generation_system[
'heating_capacity']
cooling_capacity = 0 if generation_system['cooling_capacity'] is None else generation_system[
'cooling_capacity']
capacity = max(heating_capacity, cooling_capacity)
yearly_capital_cost += generation_system['unit_reposition_cost'] * capacity * cost_increase / 1000
yearly_capital_cost += -npf.pmt(self.interest_rate, self.credit_years,
investment_cost * self.percentage_credit)
for storage_system in self.individual['Energy Storage Components']:
if (i % storage_system['lifetime']) == 0 and i != (life_cycle_duration - 1):
cost_increase = math.pow(1 + self.consumer_price_index, i)
capacity = storage_system['volume'] if cte.THERMAL in storage_system['type'] else storage_system['capacity']
yearly_capital_cost += storage_system['unit_reposition_cost'] * capacity * cost_increase
yearly_capital_cost += -npf.pmt(self.interest_rate, self.credit_years,
investment_cost * self.percentage_credit)
capital_costs_cash_flow.append(yearly_capital_cost)
operational_costs_cash_flow.append(yearly_operational_cost)
maintenance_costs_cash_flow.append(yearly_maintenance_cost)
for year in range(1, life_cycle_duration + 1):
price_increase = math.pow(1 + self.consumer_price_index, year)
if year == life_cycle_duration:
end_of_life_costs[year] = (
self.building.thermal_zones_from_internal_zones[0].total_floor_area *
self.individual['End of Life Cost'] * price_increase
)
life_cycle_capital_cost = npf.npv(self.discount_rate, capital_costs_cash_flow)
life_cycle_operational_cost = npf.npv(self.discount_rate, operational_costs_cash_flow)
life_cycle_maintenance_cost = npf.npv(self.discount_rate, maintenance_costs_cash_flow)
life_cycle_end_of_life_cost = npf.npv(self.discount_rate, end_of_life_costs)
total_life_cycle_cost = life_cycle_capital_cost + life_cycle_operational_cost + life_cycle_maintenance_cost + life_cycle_end_of_life_cost
return total_life_cycle_cost
def costs_archetype(self):
costs_catalogue = CostsCatalogFactory('montreal_new').catalog.entries().archetypes
@ -328,62 +430,3 @@ class Individual:
conversion_factor = fuel.density[0]
fuel_cost = fuel.variable.values[0] / (conversion_factor * fuel.lower_heating_value[0] * 0.277)
return fuel_cost
def life_cycle_cost_calculation(self, investment_cost, operation_cost_year_0, maintenance_cost_year_0,
life_cycle_duration=41):
"""
Calculating the life cycle cost of the energy system. The original cost workflow in hub is not used to reduce
computation time.Here are the steps:
1- Costs catalog and different components are imported.
2- Capital costs (investment and reposition) are calculated and appended to a list to have the capital cash
flow.
3-
:param maintenance_cost_year_0:
:param operation_cost_year_0:
:param investment_cost:
:param life_cycle_duration:
:return:
"""
capital_costs_cash_flow = [investment_cost]
operational_costs_cash_flow = [0]
maintenance_costs_cash_flow = [0]
end_of_life_costs = [0] * (life_cycle_duration + 1)
for i in range(1, life_cycle_duration + 1):
yearly_operational_cost = math.pow(1 + self.fuel_price_index, i) * operation_cost_year_0
yearly_maintenance_cost = math.pow(1 + self.consumer_price_index, i) * maintenance_cost_year_0
yearly_capital_cost = 0
for generation_system in self.individual['Generation Components']:
if (i % generation_system['lifetime']) == 0 and i != (life_cycle_duration - 1):
cost_increase = math.pow(1 + self.consumer_price_index, i)
heating_capacity = 0 if generation_system['heating_capacity'] is None else generation_system[
'heating_capacity']
cooling_capacity = 0 if generation_system['cooling_capacity'] is None else generation_system[
'cooling_capacity']
capacity = max(heating_capacity, cooling_capacity)
yearly_capital_cost += generation_system['unit_reposition_cost'] * capacity * cost_increase / 1000
yearly_capital_cost += -npf.pmt(self.interest_rate, self.credit_years,
investment_cost * self.percentage_credit)
for storage_system in self.individual['Energy Storage Components']:
if (i % storage_system['lifetime']) == 0 and i != (life_cycle_duration - 1):
cost_increase = math.pow(1 + self.consumer_price_index, i)
capacity = storage_system['volume'] if cte.THERMAL in storage_system['type'] else storage_system['capacity']
yearly_capital_cost += storage_system['unit_reposition_cost'] * capacity * cost_increase
yearly_capital_cost += -npf.pmt(self.interest_rate, self.credit_years,
investment_cost * self.percentage_credit)
capital_costs_cash_flow.append(yearly_capital_cost)
operational_costs_cash_flow.append(yearly_operational_cost)
maintenance_costs_cash_flow.append(yearly_maintenance_cost)
for year in range(1, life_cycle_duration + 1):
price_increase = math.pow(1 + self.consumer_price_index, year)
if year == life_cycle_duration:
end_of_life_costs[year] = (
self.building.thermal_zones_from_internal_zones[0].total_floor_area *
self.individual['End of Life Cost'] * price_increase
)
life_cycle_capital_cost = npf.npv(self.discount_rate, capital_costs_cash_flow)
life_cycle_operational_cost = npf.npv(self.discount_rate, operational_costs_cash_flow)
life_cycle_maintenance_cost = npf.npv(self.discount_rate, maintenance_costs_cash_flow)
life_cycle_end_of_life_cost = npf.npv(self.discount_rate, end_of_life_costs)
total_life_cycle_cost = life_cycle_capital_cost + life_cycle_operational_cost + life_cycle_maintenance_cost + life_cycle_end_of_life_cost
return total_life_cycle_cost

View File

@ -1274,7 +1274,7 @@
<storage_type>sensible</storage_type>
<nominal_capacity/>
<losses_ratio/>
<heating_coil_capacity>5000</heating_coil_capacity>
<heating_coil_capacity>0</heating_coil_capacity>
</templateStorages>
</energy_storage_components>
<materials>

View File

@ -3,8 +3,7 @@ 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 energy_system_modelling_package.energy_system_modelling_factories.system_sizing_methods.genetic_algorithm.individual import \
Individual
from energy_system_modelling_package.energy_system_modelling_factories.system_sizing_methods.genetic_algorithm.genetic_algorithm import GeneticAlgorithm
from hub.imports.geometry_factory import GeometryFactory
from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction_factory import ConstructionFactory
@ -58,9 +57,7 @@ for building in city.buildings:
heating_design_load = building.heating_peak_load[cte.YEAR][0]
cooling_design_load = building.cooling_peak_load[cte.YEAR][0]
available_space = building.volume / building.storeys_above_ground
Individual(building, building.energy_systems[1], optimization_scenario='cost',
heating_design_load=heating_design_load, cooling_design_load=cooling_design_load,
available_space=available_space, dt=3600, electricity_tariff_type='fixed').score_evaluation()
GeneticAlgorithm(optimization_scenario='cost').solve_ga(building=building, energy_system=building.energy_systems[1])
# EnergySystemsSizingFactory('pv_sizing', city).enrich()
# EnergySystemsSizingFactory('peak_load_sizing', city).enrich()
# EnergySystemsSizingFactory('heuristic_sizing', city).enrich()