Source code for citylearn.electric_vehicle_charger

import inspect
from typing import List, Dict
import numpy as np
from citylearn.base import Environment
from citylearn.electric_vehicle import ElectricVehicle
from citylearn.data import ZERO_DIVISION_PLACEHOLDER
np.seterr(divide='ignore', invalid='ignore')

[docs] class Charger(Environment): def __init__( self, nominal_power: float, efficiency: float = None, charger_id: str = None, charger_type: int = None, max_charging_power: float = None, min_charging_power: float = None, max_discharging_power: float = None, min_discharging_power: float = None, charge_efficiency_curve: Dict[float, float] = None, discharge_efficiency_curve: Dict[float, float] = None, connected_electric_vehicle: ElectricVehicle = None, incoming_electric_vehicle: ElectricVehicle = None, **kwargs ): r"""Initializes the `Electric Vehicle Charger` class with the given attributes. Parameters ---------- charger_id: str Id through which the charger is uniquely identified in the system charger_type: int Either private (0) or public (1) charger max_charging_power : float, default 50 Maximum charging power in kW. min_charging_power : float, default 0 Minimum charging power in kW. max_discharging_power : float, default 50 Maximum discharging power in kW. min_discharging_power : float, default 0 Minimum discharging power in kW. charge_efficiency_curve : dict, default {3.6: 0.95, 7.2: 0.97, 22: 0.98, 50: 0.98} Efficiency curve for charging containing power levels and corresponding efficiency values. discharge_efficiency_curve : dict, default {3.6: 0.95, 7.2: 0.97, 22: 0.98, 50: 0.98} Efficiency curve for discharging containing power levels and corresponding efficiency values. Other Parameters ---------------- **kwargs : dict Other keyword arguments used to initialize super classes. """ self.nominal_power = nominal_power self.efficiency = efficiency self.charger_id = charger_id self.charger_type = charger_type self.max_charging_power = max_charging_power self.min_charging_power = min_charging_power self.max_discharging_power = max_discharging_power self.min_discharging_power = min_discharging_power self.charge_efficiency_curve = charge_efficiency_curve self.discharge_efficiency_curve = discharge_efficiency_curve self.connected_electric_vehicle = connected_electric_vehicle self.incoming_electric_vehicle = incoming_electric_vehicle arg_spec = inspect.getfullargspec(super().__init__) kwargs = { key: value for (key, value) in kwargs.items() if (key in arg_spec.args or (arg_spec.varkw is not None)) } super().__init__(**kwargs) @property def charger_id(self) -> str: """ID of the charger.""" return self.__charger_id @property def charger_type(self) -> int: """Type of the charger.""" return self.__charger_type @property def max_charging_power(self) -> float: """Maximum charging power in kW.""" return self.__max_charging_power @property def min_charging_power(self) -> float: """Minimum charging power in kW.""" return self.__min_charging_power @property def max_discharging_power(self) -> float: """Maximum discharging power in kW.""" return self.__max_discharging_power @property def min_discharging_power(self) -> float: """Minimum discharging power in kW.""" return self.__min_discharging_power @property def charge_efficiency_curve(self) -> dict: """Efficiency curve for charging containing power levels and corresponding efficiency values.""" return self.__charge_efficiency_curve @property def discharge_efficiency_curve(self) -> dict: """Efficiency curve for discharging containing power levels and corresponding efficiency values.""" return self.__discharge_efficiency_curve @property def connected_electric_vehicle(self) -> ElectricVehicle: """Electric_Vehicle currently connected to charger""" return self.__connected_ev @property def incoming_electric_vehicle(self) -> ElectricVehicle: """Electric_Vehicle incoming to charger""" return self.__incoming_ev @property def efficiency(self) -> float: """Technical efficiency.""" return self.__efficiency @property def nominal_power(self) -> float: r"""Nominal power.""" return self.__nominal_power @property def past_connected_evs(self) -> List[ElectricVehicle]: r"""Each timestep with the list of Past connected Evs or None in the case no electric_vehicle was connected """ return self.__past_connected_evs @property def past_charging_action_values(self) -> List[float]: r"""Actions given to charge/discharge in [kWh]. Different from the electricity consumption as in this an action can be given but no electric_vehicle being connect it will not consume such energy""" return self.__past_charging_action_values @property def electricity_consumption(self) -> List[float]: r"""Electricity consumption time series.""" return self.__electricity_consumption @property def available_nominal_power(self) -> float: r"""Difference between `nominal_power` and `electricity_consumption` at current `time_step`.""" return None if self.nominal_power is None else self.nominal_power - self.electricity_consumption[self.time_step] @charger_id.setter def charger_id(self, charger_id: str): self.__charger_id = charger_id @charger_type.setter def charger_type(self, charger_type: str): self.__charger_type = charger_type @max_charging_power.setter def max_charging_power(self, max_charging_power: float): if max_charging_power is None: self.__max_charging_power = 50.0 else: self.__max_charging_power = max_charging_power @min_charging_power.setter def min_charging_power(self, min_charging_power: float): if min_charging_power is None: self.__min_charging_power = 0.0 else: self.__min_charging_power = min_charging_power @max_discharging_power.setter def max_discharging_power(self, max_discharging_power: float): if max_discharging_power is None: self.__max_discharging_power = 50.0 else: self.__max_discharging_power = max_discharging_power @min_discharging_power.setter def min_discharging_power(self, min_discharging_power: float): if min_discharging_power is None: self.__min_discharging_power = 0.0 else: self.__min_discharging_power = min_discharging_power @charge_efficiency_curve.setter def charge_efficiency_curve(self, charge_efficiency_curve: List[List[float]]): if charge_efficiency_curve is None: charge_efficiency_curve = [[3.6, 0.95],[7.2, 0.97],[22, 0.98],[50, 0.98]] else: pass self.__charge_efficiency_curve = np.array(charge_efficiency_curve).T @discharge_efficiency_curve.setter def discharge_efficiency_curve(self, discharge_efficiency_curve: List[List[float]]): if discharge_efficiency_curve is None: discharge_efficiency_curve = [[3.6, 0.95],[7.2, 0.97],[22, 0.98],[50, 0.98]] else: pass self.__discharge_efficiency_curve = np.array(discharge_efficiency_curve).T @connected_electric_vehicle.setter def connected_electric_vehicle(self, electric_vehicle: ElectricVehicle): self.__connected_ev = electric_vehicle if electric_vehicle is None else None @incoming_electric_vehicle.setter def incoming_electric_vehicle(self, electric_vehicle: ElectricVehicle): self.__incoming_ev = electric_vehicle if electric_vehicle is None else None @efficiency.setter def efficiency(self, efficiency: float): if efficiency is None: self.__efficiency = 1.0 else: assert efficiency > 0, 'efficiency must be > 0.' self.__efficiency = efficiency @nominal_power.setter def nominal_power(self, nominal_power: float): if nominal_power is None or nominal_power == 0: self.__nominal_power = ZERO_DIVISION_PLACEHOLDER else: assert nominal_power >= 0, 'nominal_power must be >= 0.' self.__nominal_power = nominal_power
[docs] def plug_car(self, electric_vehicle: ElectricVehicle): """ Connects a electric_vehicle to the charger. Parameters ---------- electric_vehicle : object electric_vehicle instance to be connected to the charger. Raises ------ ValueError If the charger has reached its maximum connected electric_vehicle' capacity. """ self.__past_connected_evs[self.time_step] = electric_vehicle self.connected_electric_vehicle = electric_vehicle
[docs] def unplug_car(self): """ Disconnects a electric_vehicle from the charger. Parameters ---------- electric_vehicle : object electric_vehicle instance to be disconnected from the charger. """ self.connected_electric_vehicle = None
[docs] def associate_incoming_car(self, electric_vehicle: ElectricVehicle): """ Associates incoming electric_vehicle to the charger. Parameters ---------- electric_vehicle : object electric_vehicle instance to be connected to the charger. Raises ------ ValueError If the charger has reached its maximum associated electric_vehicle' capacity. """ self.incoming_electric_vehicle = electric_vehicle
# else: # raise ValueError("Charger has reached its maximum associated electric_vehicle capacity")
[docs] def disassociate_incoming_car(self): """ Disassociates incoming electric_vehicle from the charger. Parameters ---------- electric_vehicle : object electric_vehicle instance to be disconnected from the charger. """ self.incoming_electric_vehicle = None
[docs] def update_connected_electric_vehicle_soc(self, action_value: float): self.__past_charging_action_values[self.time_step] = action_value #ToDo if self.connected_electric_vehicle and action_value != 0: electric_vehicle = self.connected_electric_vehicle if action_value > 0: energy = action_value * self.max_charging_power else: energy = action_value * self.max_discharging_power charging = energy >= 0 if charging: # make sure we do not charge beyond the maximum capacity energy = min(energy, electric_vehicle.battery.capacity - electric_vehicle.battery.soc[self.time_step]) else: # make sure we do not discharge beyond the minimum level (assuming it's zero) max_discharge = - (electric_vehicle.battery.soc[self.time_step] - electric_vehicle.min_battery_soc/100 * electric_vehicle.battery.capacity) energy = max(energy, max_discharge) energy_kwh = energy * self.efficiency # Here we call the electric_vehicle's battery's charge method directly, passing the energy (positive for charging, # negative for discharging) electric_vehicle.battery.charge(energy_kwh) self.__electricity_consumption[self.time_step] = electric_vehicle.battery.electricity_consumption[-1] else: self.__electricity_consumption[self.time_step] = 0
[docs] def next_time_step(self): r"""Advance to next `time_step` and set `electricity_consumption` at new `time_step` to 0.0.""" self.__electricity_consumption.append(0.0) self.__past_connected_evs.append(None) self.__past_charging_action_values.append(0.0) self.connected_electric_vehicle = None self.incoming_electric_vehicle = None super().next_time_step()
[docs] def reset(self): """ Resets the Charger to its initial state by disconnecting all electric_vehicles. """ super().reset() self.connected_electric_vehicle = None self.incoming_electric_vehicle = None self.__electricity_consumption = [0.0] self.__past_connected_evs = [None] self.__past_charging_action_values = [0.0]
def __str__(self): return ( f"Charger ID: {self.charger_id}\n" f"electricity consumption: {self.electricity_consumption} kW\n" f"past_connected_evs: {self.past_connected_evs} kW\n" f"past_charging_action_values: {self.past_charging_action_values} kW\n" f"Currently Connected electric_vehicle: {self.connected_electric_vehicle}\n" f"Incoming electric_vehicle: {self.incoming_electric_vehicle}\n" )