Commit e8917707 authored by Etienne Cuisinier's avatar Etienne Cuisinier
Browse files

[Adding an example of modelling of an electric vehicle offering flexibility...


[Adding an example of modelling of an electric vehicle offering flexibility when connected to the system]

Signed-off-by: Etienne Cuisinier's avatarEtienne Cuisinier <etienne.cuisinier@grenoble-inp.fr>
parent 55060323
#! usr/bin/env python3
# coding=utf-8 #
"""
** This Module is an example of an electric vehicle (EV) model.**
This example describes a simple microgrid with :
- Two connected nodes: a main node and an EV node with time varying capacity,
the later representing the time during wich the EV is connected or not to the system
- A load profile known in advance, connected to the main node
- An adjustable production unit, with variable production cost, connected to the main node
- A storage system with power in charge and power in discharge, connected to the EV node,
representing the EV battery
- A load profile known in advance, connected to the EV node, representing the EV use
The objective consists on minimizing the production cost.
..
Copyright 2022 G2ELab / MAGE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import os
from pulp import LpStatus
from omegalpes.energy.energy_nodes import EnergyNode
from omegalpes.energy.units.consumption_units import FixedConsumptionUnit
from omegalpes.energy.units.production_units import VariableProductionUnit
from omegalpes.energy.units.storage_units import StorageUnit
from omegalpes.general.optimisation.model import OptimisationModel
from omegalpes.general.utils.plots import plt, plot_quantity, \
plot_node_energetic_flows
from omegalpes.general.utils.output_data import save_energy_flows
from omegalpes.general.time import TimeUnit
import warnings
__docformat__ = "restructuredtext en"
def main(work_path, load_profile, EV_load_profile, EV_disconnection_profile, EV_pcharge_max,
EV_pdischarge_max, EV_capacity, production_cost_profile):
"""
:param work_path: Path to the working directory
:param load_profile: hourly load profile during a day
:type load_profile: list of 24 float or int
:param EV_load_profile: hourly load profile during a day
:type EV_load_profile: list of 24 float or int
:param EV_disconnection_profile: hourly disconnection profile during a day,
1 if the EV is used for a ride, 0 if it stays connected to the system
:type EV_disconnection_profile: list of 24 binaries
:param EV_pcharge_max: Maximal charging power for the storage [kW]
:type EV_pcharge_max: float or int
:param EV_pdischarge_max: Maximal discharging power for the storage
[kW]
:type EV_pdischarge_max: float or int
:param EV_capacity: energy capacity for the EV [kWh]
:type EV_capacity: float or int
:param production_cost_profile: hourly production cost profile during a day
:type production_cost_profile: list of 24 float or int
"""
# Create an empty model
time = TimeUnit(periods=24, dt=1) # Study on a day (24h), delta_t = 1h
model = OptimisationModel(time=time, name='example') # Optimisation model
# Create the load - The load profile is known
load = FixedConsumptionUnit(time, 'load', p=load_profile)
# Create the production unit - The production profile is unknown
production = VariableProductionUnit(time, 'production',
operating_cost=production_cost_profile)
production.minimize_operating_cost()
# Create the EV storage
EV_storage = StorageUnit(time, name='EV_storage', pc_max=EV_pcharge_max,
pd_max=EV_pdischarge_max, capacity=EV_capacity, ef_is_e0=True)
# Create the EV load - The load profile is known
EV_load = FixedConsumptionUnit(time, 'EV_load', p=EV_load_profile)
# Create the energy nodes and connect units
main_node = EnergyNode(time, 'energy_node')
EV_node = EnergyNode(time, 'EV_node')
# Add the energy nodes to the model
main_node.connect_units(load, production)
EV_node.connect_units(EV_load, EV_storage)
# Connect both nodes, including the availability of the EV
EV_node.export_to_node(main_node,
export_max=[EV_pdischarge_max if i == 0 else 0 for i in EV_disconnection_profile])
EV_node.import_from_node(main_node,
import_max=[EV_pcharge_max if i == 0 else 0 for i in EV_disconnection_profile])
# Add nodes to the model
model.add_nodes(main_node, EV_node)
# Optimisation process
model.writeLP(work_path + r'\optim_models\electric_vehicle.lp')
model.solve_and_update()
# Checking the consistency of the EV load and disconnection profiles
unconsistentTimeSteps = []
for t in range(time.LEN):
if EV_disconnection_profile[t] == 0 and EV_load_profile[t] != 0:
unconsistentTimeSteps.append(t)
if EV_load_profile[t] > EV_pdischarge_max:
raise ValueError('The EV load profile exceeds the discharging capacity '
'at time step {}.'.format(t))
if len(unconsistentTimeSteps) > 0:
warnings.warn("The load and the disconnection profiles of the EV may be unconsistent. "
"The vehicle is used while connected at time steps {}.".format(unconsistentTimeSteps))
return model, time, load, production, EV_storage, main_node, EV_node
def print_results():
"""
*** This function print the optimisation result:
Plot the power curves :
Figure 1 :
- Power consumed by the load, labelled 'Load'
- Power imported from the EV , labelled 'Imports from EV'
- Power exported to the EV , labelled 'Exports to EV'
- Power delivered by the production unit, labelled 'Production'
Figure 2 :
- Power imported from the main node , labelled 'Imports from the main node'
- Power exported to the main node , labelled 'Exports to the main node'
- Power consumed by the EV storage, labelled 'EV_storage'
- Power consumed by the EV load, labelled 'EV_load'
Figure 3 :
- The state of charge of the EV storage unit
"""
if LpStatus[MODEL.status] == 'Optimal':
print("\n - - - - - OPTIMISATION RESULTS - - - - - ")
# Show the graph
# Power curves
plot_node_energetic_flows(MAIN_NODE)
plot_node_energetic_flows(EV_NODE)
# SOC curve
plot_quantity(TIME, STORAGE.e)
plt.xlabel('Time (h)')
plt.ylabel('Energy (kWh)')
plt.title('State of charge of the EV')
plt.show()
elif LpStatus[MODEL.status] == 'Infeasible':
print("Sorry, the optimisation problem has no feasible solution !")
elif LpStatus[MODEL.status] == 'Unbounded':
print("The cost function of the optimisation problem is unbounded !")
elif LpStatus[MODEL.status] == 'Undefined':
print("Sorry, a feasible solution has not been found (but may exist). "
"PuLP does not manage to interpret the solver's output, "
"the infeasibility of the MILP problem may have been "
"detected during presolve.")
else:
print("Sorry, the optimisation problem has not been solved.")
if __name__ == '__main__':
# OPTIMIZATION PARAMETERS #
WORK_PATH = os.getcwd()
PRODUCTION_COST_PROFILE = [20, 20, 20, 20, 20, 20, 20, 20, 40, 40, 40, 40, 20, 20, 20, 20, 40,
40, 40, 40, 40, 20, 20, 20]
# Load dynamic profile - one value per hour during a day
LOAD_PROFILE = [4, 5, 6, 2, 3, 4, 7, 8, 13, 24, 18, 16, 17, 12, 20, 15, 17,
21, 25, 23, 18, 16, 13, 4]
# EV load dynamic profile - one value per hour during a day
EV_LOAD_PROFILE = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 0, 10, 10, 0,
0, 0, 0, 0, 0, 0, 0]
EV_DISCONNECTION_PROFILE = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0]
# Storage maximal charging and discharging powers
(EV_PC_MAX, EV_PD_MAX) = 20, 20
# EV maximal energy stored
EV_CAPACITY = 50
# Run main
MODEL, TIME, LOAD, PRODUCTION, STORAGE, MAIN_NODE, EV_NODE = \
main(work_path=WORK_PATH, load_profile=LOAD_PROFILE, EV_load_profile=EV_LOAD_PROFILE,
EV_disconnection_profile=EV_DISCONNECTION_PROFILE,
EV_pcharge_max=EV_PC_MAX, EV_pdischarge_max=EV_PD_MAX,
EV_capacity=EV_CAPACITY, production_cost_profile=PRODUCTION_COST_PROFILE)
# Save energy flows into a CSV file
save_energy_flows(MAIN_NODE, EV_NODE, file_name=WORK_PATH + r'\results\electric_vehicle')
# Show results
print_results()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment