Source code for evnrg.common.evse
import math
from typing import NamedTuple
from evnrg.simulation.vehicle import Vehicle
# from evnrg.simulation.eligibility import EligibilityRules
__all__ = [
'EVSE',
'EVSEType'
]
[docs]class EVSEType(NamedTuple):
max_power: float = 0.
dc: bool = False
max_soc: float = 1.
dc_plugs: tuple = (None,)
v2g_buffer: float = 0.20 # Arbitrage of 20% max SoC
v2g_capable: bool = False
[docs]class EVSE(object):
def __init__(self, model: EVSEType = EVSEType()):
self.model = model
self.power = model.max_power
self.throttle = 1.
self.vehicle = None
self.target_soc = model.max_soc
self.v2g_mode = False
# TODO: Create properties that show the maximum and minimum SoCs and
# energy available for V2G use.
# Can also create methods to describe temporally-based V2G potential
# For the purpose ofsafely bidding into an ISO/utility's grid services,
# we could estimate this as:
# (time_until_departure / 2) * charge_power
@property
def is_connected(self):
return self.vehicle is not None
@property
def is_available(self):
return self.vehicle is None
@property
def demand(self):
out = 0
if self.is_connected:
if self.v2g_mode:
# TODO: If the vehicle is discharging in V2G mode
pass
else:
out = self.power * self.throttle
return out
@property
def charge_completed(self):
out = False
if self.vehicle is not None:
out = self.vehicle.soc >= self.target_soc
return out
[docs] def charge_vehicle(self, minutes: float):
true_power = 0
if self.is_connected:
if not self.charge_completed:
power = self.power * self.throttle
energy_max = self.vehicle.powertrain.energy_at_soc(self.target_soc)
energy_potential = power * (minutes / 60.0)
energy_limit = energy_max - self.vehicle.battery_state
energy_to_add = min(energy_potential, energy_limit)
self.vehicle.charge_battery(energy_to_add)
true_power = energy_to_add / (minutes / 60.0)
return true_power
[docs] def connect_vehicle(self, v: Vehicle):
out = False
target = min(self.target_soc, v.max_soc)
if target > v.soc:
if v.connect_evse(self.power, self.model.dc, self.model.dc_plugs):
self.target_soc = target
power = 0.
if self.model.dc and v.powertrain.dc_power > 0.:
power = min(self.model.max_power, v.powertrain.dc_power)
else:
power = min(self.model.max_power, v.powertrain.ac_power)
self.power = power
self.vehicle = v
out = True
return out
[docs] def intervals_to_target_soc(self, minutes_per_interval: float):
out = 0
if self.vehicle:
current_energy = self.vehicle.battery
target_energy = self.vehicle.energy.energy_at_soc(self.target_soc)
energy_needed = target_energy - current_energy
hours_required = energy_needed / self.power
out = math.ceil(hours_required / minutes_per_interval)
return out
[docs] def connect_vehicle_soc_target(self, v: Vehicle, desired_soc: float):
self.target_soc = desired_soc
return self.connect_vehicle(v)
[docs] def disconnect_vehicle(self):
out = None
if self.is_connected:
v = self.vehicle
self.vehicle.disconnect_evse()
self.vehicle = None
self.target_soc = self.model.max_soc
out = v
return out