Source code for src.datafev.routines.charging_control.decentralized_llf

# The datafev framework

# Copyright (C) 2022,
# Institute for Automation of Complex Power Systems (ACS),
# E.ON Energy Research Center (E.ON ERC),
# RWTH Aachen University

# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
# Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from datafev.algorithms.cluster.prioritization_llf import leastlaxityfirst


[docs]def charging_routine(ts, t_delta, system): """ This routine is executed periodically during operation of charger clusters. It addresses the scenarios where each cluster has a local power consumption constraint and therefore has to control the power distribution to the chargers. The control architecture is decentralized; therefore, each cluster applies its own control. The applied control is based on "least-laxity-first" logic. Parameters ---------- ts : datetime Current time. t_delta : timedelta Control horizon. system : data_handling.multi_cluster Multi-cluster system object. Returns ------- None. """ step = t_delta.seconds # Loop through the clusters for cc_id in system.clusters.keys(): cluster = system.clusters[cc_id] if cluster.query_actual_occupation(ts) > 0: # The cluster includes connected EVs ################################################################################################ # Step 1: Identification of charging demand inisoc = {} # Will contain the current SOC values of EV batteries tarsoc = ( {} ) # Will contain the target SOC values of EV batteries (at estimate departure time) bcap = {} # Will contain the EV battery capacities eff = {} # Will contain the power conversion efficiencies during p_socdep = {} # Will contain the data of SOC dependency of charge power p_chmax = ( {} ) # Will contain the maximum charge power that can be handled by EV-charger pair p_re = {} # Will contain the charge powers that EVs request leadtime = ( {} ) # Will contain the lead time for charging from now arrial until estimate departure) # Loop through the chargers for cu_id, cu in cluster.chargers.items(): ev = cu.connected_ev if ev != None: # There is an EV connected in this charger ev_id = ev.vehicle_id # Current SOC of EV ev_soc = ev.soc[ts] inisoc[ev_id] = ev_soc # Target SOC of EV (for estimated departure time) ev_tarsoc = ev.soc_tar_at_t_dep_est tarsoc[ev_id] = ev_tarsoc # Energy capactiy of the EV battery ev_bcap = ev.bCapacity bcap[ev_id] = ev_bcap # Power conversion efficiency of charger eff[ev_id] = cu.eff # Maximum charge power that can be handled by EV-charger pair (for the whole SOC curve) p_chmax[ev_id] = min(ev.p_max_ch, cu.p_max_ch) # How long EV will stay connected to the charger (seconds) leadtime[ev_id] = ( (ev.t_dep_est - ts).seconds if ts < ev.t_dep_est else 0.001 ) if ev_soc >= ev_tarsoc: # The EV connected here has already reached its target SOC p_re[ev_id] = 0.0 else: # The EV connected here wants to keep charging # Calculation of the amount of energy that can be supplied to the EV lim_ev_batcap = ( 1 - ev_soc ) * ev_bcap # Limit due to the battery capacity of EV lim_ch_pow = ( cu.p_max_ch * step ) # Limit due to the charger power capability if ev.pow_soc_table != None: # The EV battery has a specific charger power-SOC dependency limiting the power transfer table = ev.pow_soc_table soc_range = ( table[ (table["SOC_LB"] <= ev_soc) & (ev_soc < table["SOC_UB"]) ] ).index[0] p_max = table.loc[soc_range, "P_UB"] lim_ev_socdep = ( p_max * step ) # Limit due to the SOC dependency of charge power e_max = min(lim_ev_batcap, lim_ch_pow, lim_ev_socdep) p_socdep[ev_id] = table.to_dict() else: # The power transfer is only limited by the charger's power and battery capacity e_max = min(lim_ev_batcap, lim_ch_pow) p_socdep[ev_id] = None # Charge powers requested by EVs during the control horizon p_re[ev_id] = e_max / step ################################################################################################ ################################################################################################ # Step 2: Power distribution based on first-come-first-serve algorithm upperlimit = cluster.upper_limit[ts] # Cluster level constraint p_charge = leastlaxityfirst( inisoc, tarsoc, bcap, eff, p_socdep, p_chmax, p_re, leadtime, upperlimit ) ################################################################################################ ################################################################################################ # Step 3: Charging for cu_id in system.clusters[cc_id].chargers.keys(): cu = system.clusters[cc_id].chargers[cu_id] if cu.connected_ev != None: ev_id = cu.connected_ev.vehicle_id cu.supply(ts, t_delta, p_charge[ev_id])
################################################################################################