Skip to content

Commit

Permalink
feat(ev): strategic electric vehicle charging
Browse files Browse the repository at this point in the history
  • Loading branch information
sebhoerl committed Dec 7, 2024
1 parent c6e1bf5 commit 699095b
Show file tree
Hide file tree
Showing 99 changed files with 10,675 additions and 4 deletions.
124 changes: 122 additions & 2 deletions contribs/ev/README.md

Large diffs are not rendered by default.

Binary file added contribs/ev/docs/sevc/durations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added contribs/ev/docs/sevc/revenues.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added contribs/ev/docs/sevc/score_distribution.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added contribs/ev/docs/sevc/scores.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,23 @@
* @author Sebastian Hörl (sebhoerl), IRT SystemX
*/
public class ChargerReservationModule extends AbstractModule {
private final boolean bindPriority;

public ChargerReservationModule() {
this(true);
}

public ChargerReservationModule(boolean bindPriority) {
this.bindPriority = bindPriority;
}

@Override
public void install() {
addControlerListenerBinding().to(ChargerReservationManager.class);
bind(ChargingPriority.Factory.class).to(ReservationBasedChargingPriority.Factory.class);

if (bindPriority) {
bind(ChargingPriority.Factory.class).to(ReservationBasedChargingPriority.Factory.class);
}
}

@Provides
Expand All @@ -32,4 +45,4 @@ ReservationBasedChargingPriority.Factory provideReservationBasedChargingPriority
ChargerReservationManager provideChargerReservationManager() {
return new ChargerReservationManager();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package org.matsim.contrib.ev.strategic;

import java.util.Collection;
import java.util.List;

import javax.annotation.Nullable;

import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Leg;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.Plan;
import org.matsim.contrib.ev.charging.BatteryCharging;
import org.matsim.contrib.ev.discharging.DriveEnergyConsumption;
import org.matsim.contrib.ev.fleet.ElectricVehicle;
import org.matsim.contrib.ev.infrastructure.ChargerSpecification;
import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure;
import org.matsim.contrib.ev.strategic.infrastructure.ChargerProvider;
import org.matsim.contrib.ev.strategic.infrastructure.ChargerProvider.ChargerRequest;
import org.matsim.contrib.ev.withinday.ChargingAlternative;
import org.matsim.contrib.ev.withinday.ChargingAlternativeProvider;
import org.matsim.contrib.ev.withinday.ChargingSlot;
import org.matsim.core.mobsim.qsim.QSim;
import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils;
import org.matsim.core.population.routes.NetworkRoute;
import org.matsim.core.router.util.TravelTime;
import org.matsim.vehicles.Vehicle;

import com.google.common.base.Preconditions;

/**
* This class is a special case that is implemented optionally in the
* alternative charger search startegy in strategic electric vehicle charging
* (SEVC). In particular, it treats the case in which an agent, for every leg
* using the car, checks whether he might want to charge along the way. We apply
* a sophisticated logic in which energy consumption along the trip is
* calculated and the agent tries to charge once it gets clear that a
* configurable "critical SoC" will be reached. To find a charger, the
* ChargingProvider logic is used.
*
* The critical SoC for an agent must be defined as an agent attribute.
*
* @author Sebastian Hörl (sebhoerl), IRT SystemX
*/
public class CriticalAlternativeProvider implements ChargingAlternativeProvider {
static public final String CRITICAL_SOC_PERSON_ATTRIBUTE = "sevc:criticalSoc";

private final QSim qsim;
private final Network network;
private final TravelTime travelTime;
private final ChargerProvider chargerProvider;
private final ChargingInfrastructure infrastructure;
private final double minimumDuration;

public CriticalAlternativeProvider(QSim qsim, Network network, TravelTime travelTime,
ChargerProvider chargerProvider, ChargingInfrastructure infrastructure,
StrategicChargingConfigGroup config) {
this.qsim = qsim;
this.network = network;
this.travelTime = travelTime;
this.chargerProvider = chargerProvider;
this.infrastructure = infrastructure;
this.minimumDuration = config.minimumEnrouteChargingDuration;
}

@Override
@Nullable
public ChargingAlternative findAlternative(double now, Person person, Plan plan, ElectricVehicle vehicle,
ChargingSlot slot, List<ChargingAlternative> trace) {
throw new UnsupportedOperationException();
}

@Override
@Nullable
public ChargingAlternative findEnrouteAlternative(double now, Person person, Plan plan,
ElectricVehicle electricVehicle,
@Nullable ChargingSlot slot) {
Preconditions.checkArgument(slot == null);

Double criticalSoc = getCriticalSoc(person);
if (criticalSoc != null) {
// we must be on a leg when finding an enroute alternative
Leg leg = (Leg) WithinDayAgentUtils.getCurrentPlanElement(qsim.getAgents().get(person.getId()));

double initialCharge = electricVehicle.getBattery().getCharge();
double targetCharge = criticalSoc * electricVehicle.getBattery().getCapacity();

// a priori no additional charge needed right now if zero
double deltaCharge = Math.max(0.0, targetCharge - initialCharge);

if (deltaCharge == 0.0) {
// Can we obtain the vehicle parameter from somewhere here? For time
// calculation?
double consumption = calculateConsumption(now, person, leg, electricVehicle, null);
double finalCharge = initialCharge - consumption;

// maybe not enough at the end
deltaCharge = Math.max(0.0, targetCharge - finalCharge);
}

if (deltaCharge > 0.0) {
// we need to charge, otherwise we don't get to the next activity
BatteryCharging calculator = (BatteryCharging) electricVehicle.getChargingPower();

Collection<ChargerSpecification> chargers = chargerProvider.findChargers(person, plan,
new ChargerRequest(leg));

if (chargers.size() > 0) {
final double fixedDeltaCharge = deltaCharge;

ChargerSpecification selected = chargers.stream().sorted((a, b) -> {
double durationA = calculator.calcChargingTime(a, fixedDeltaCharge);
double durationB = calculator.calcChargingTime(b, fixedDeltaCharge);
return Double.compare(durationA, durationB);
}).findFirst().get();

double duration = calculator.calcChargingTime(selected, fixedDeltaCharge);
duration = Math.max(minimumDuration, duration);

return new ChargingAlternative(infrastructure.getChargers().get(selected.getId()), duration);
}
}
}

return null;
}

private double calculateConsumption(double now, Person person, Leg leg, ElectricVehicle electricVehicle,
Vehicle vehicle) {
double consumption = 0.0;

DriveEnergyConsumption calculator = electricVehicle.getDriveEnergyConsumption();
for (Id<Link> linkId : ((NetworkRoute) leg.getRoute()).getLinkIds()) {
Link link = network.getLinks().get(linkId);
double linkTravelTime = travelTime.getLinkTravelTime(link, now, person, vehicle);

consumption += calculator.calcEnergyConsumption(link, linkTravelTime, now);
now += linkTravelTime;
}

return consumption;
}

/**
* Sets the critical SoC for an agent.
*/
static public void setCriticalSoC(Person person, double criticalSoc) {
person.getAttributes().putAttribute(CRITICAL_SOC_PERSON_ATTRIBUTE, criticalSoc);
}

/**
* Retrieves the critical SoC from an agent.
*/
static public Double getCriticalSoc(Person person) {
return (Double) person.getAttributes().getAttribute(CRITICAL_SOC_PERSON_ATTRIBUTE);
}
}
147 changes: 147 additions & 0 deletions contribs/ev/src/main/java/org/matsim/contrib/ev/strategic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Strategic electric vehicle charging (SEVC)

## Configuration parameters

A range of configuration options control the generation of new charging plans:

- `minimumActivityChargingDuration` (*Double*, default `900`, seconds): defines the shortest possible activity or chain of activities during which charging may be planned

- `maximumActivityChargingDuration` (*Double*, default `Inf`, seconds): defines the longest possible activity or chain of activities during which charging may be planned

- `minimumEnrouteDriveTime` (*Double*, default `3600`, seconds): defines the shortest possible drive during which enroute charging may be planned

- `minimumEnrouteChargingDuration` (*Double*, default `900`, seconds): defines the shortest possible charging slot that may be generated enroute

- `maximumEnrouteChargingDuration` (*Double*, default `3600`, seconds): defines the shortest possible charging slot that may be generated enroute

- `chargerSearchRadius` (*Double*, default `1000`, meters): defines the radius within which chargers are searched

The following parmeters control the selection / innovation process for charging plans:

- `selectionProbability` (*Double*, default `0.8`): defines the frequency with which charging plans are switched rather than newly generawted

- `selectionStrategy` (*Exponential|Random|Best*, default `Exponential`): defines the way that plans are selected from the plan memory, based on their charging score

- `maximumChargingPlans` (*Integer*, default `4`): size of the charging plan memory

The following parameters control scoring of charging plans:

- `chargingScoreWeight` (*Double*, default `0.0`): defines with which factor the calculated charging score for a plan is fed back into the score of the regular plan

- `scoreTrackingInterval` (*Integer*, default `0`): defines how often detailed information on the scoring process is written out (`0` means never, any other value produces surely an output in the last iteration)

The following parmeters control the online search process during the simulation:

- `maximumAlternatives` (*Integer*, default `2`): maximum attempts (including the initial one) that an agent tries to find a charger before the search is aborted

- `maximumQueueTime` (*Double*, default `300`): maximum queue time at a charger until an alternative charger is searched

- `useProactiveAlternativeSearch` (*Boolean*, default `true`): defines if agents consider potential alternatives proactively upon starting the leg that leads to a planned charging activity

- `alternativeSearchStrategy`: defines the way agents make decisions for alternative chargers during the simulation

- *Naive*: agents select a random alternative charger
- *OccupancyBased*: agents select a random alternative charger that has a free spot
- *ReservationBased*: agents select a random alternative charger and reserves it for the planned duration

- `alternativeCacheSize` (*Integer*, default `-1`): defines whether to precompute viable charger alternatives for each charging activity planned in an agent's plan. In that case the search process is restricted to one alternative attempt.

## Scoring parameters

- `cost` (*Doule*, default `-1.0`): weight the monetary cost units incurred from charging

- `waitTime_min` (*Double*, default `0.0`): weight the time waited in a queue for charging

- `detourTime_min` (*Double*, default `0.0`): weighs the travel time reduced by the planned travel time of the base plan. Evaluation is mostly relevant compared to the detour component of another charging plan.

- `detourDistance_km` (*Double*, default `0.0`): weighs the travel time reduced by the planned travel time of the base plan. Evaluation is mostly relevant compared to the detour component of another charging plan.

- `zeroSoc` (*Double*, default `-100.0`): penalty added when the SoC of the agent drops to zero

- `failedChargingAttempt` (*Double*, default `-10.0`): penalty added when a charging attempt of the agent fails (probably followed by an alternative attempt)

- `failedChargingProcess` (*Double*, default `-100.0`): penalty added when a charging process fails (no new alternative is found)

- `belowMinimumSoc` (*Double*, default `0.0`): penalty added when the SoC of a person falls below an attributable value (see person attributes)

- `belowMinimumEndSoc` (*Double*, default `0.0`): penalty added when the SoC of the person at the end of the simulation is below an attributable value (see person attributes)

## Cost models

Two cost models can be selected by default.

The *DefaultCostModel* defines one cost parameter:

- `costPerEnergy_kWh` (*Double*, default `0.28`): cost registered for charging energy
- `costPerDuration_min` (*Double*, default `0.0`): cost registered for charging duration

The *AttributeCostModel* defines no parameters, but obtains the cost from the charger (see charger attributes).


## Charger and person attributes

### Chargers

A couple of attributes define the chargers search process. One option would be to save all chargers in a large global spatial index, but then filtering out which chargers are aimed at a specific purpose is costly. Hence, we define a couple of categories in which each charger needs to be sorted in to speed up identification of viable chargers for each desired enroute or activtiy-based charging activity:

- `sevc:facilities` (*String*, optional): Chargers that have this attribute are proposed during charger search when the activity during which an agent wants to charge takes place at one of the listed facilities.

- `sevc:persons` (*String*, optional): Chargers that have this attribute are proposed during charger search when the agent is in the provided list of persons.

- `sevc:public` (*Boolean*, default `false`): By default, all chargers are not public and only found if they fall in one of the previous categories. Only chargers for which is attribute is `true` are provided in a spatial search around the planned enroute or fixed charging activities of an agent, additionally to the categories above.

For public chargers (but, potentially, also for workplace chargers and others) an additional layer of access verification is performed. Each charger can have a list of subscriptions of which a user must have one in order to be allowed to charge:

- `sevc:subscriptions` (*String*, comma-separated, optional): describes the subscriptions that are necessary to use this charger. If no attribute is give, the charger is accessible to everyone.

Additional attributes:

- `sevc:operator` (*String*, optional): describes the name of the operator of the charger. The atrtibutes is used for per-operator analysis.

- `secv:costPerEnergy_kWh` (*Double*, default `0.0`): describes the cost in monetary units (for instance, EUR) for charging one kWh at the charger. Used if the `AttributeBasedChargingCost` is chosen.

- `secv:costPerDuration_min` (*Double*, default `0.0`): describes the cost for the plugging duration at the charger. Used if the `AttributeBasedChargingCost` is chosen.

**Some attributes need clarification: @ Olly**

- `plugType`: What is meant?

### Persons

For a person to be covered by the SEVC contrib, it needs to be activated. Also, access to chargers can be defined:

- `sevc:active` (*Boolean*, default `false`): only if set to true, an agent is simulated in SEVC, otherwise they are ignored

- `sevc:subscriptions` (*String*, comma-separated, optional): a list of subscription that the persons owns. In case a charger requires a subscription, it must be present in the list for the person to be eligible.

- `sevc:activityTypes` (*String*, comma-separated, optional): if given, the agent will only attempt at activities of the listed types

Some parameters relevant for the simulation dynamics:

- `sevc:criticalSoc` (*Double*, default `0.0`): if the agents departs on a leg and detects that the SoC will fall below that value along the way, a spontaneous charging activity will be created. Only used if spontaneous charging has been activated via configuration.

- `sevc:maximumQueueTime` (*Double*, default from config): the time the agent will wait at a charger to be queued before the alternative search process starts. If no value is given, the default value from the main config group is used.

Some parameters are relevant for scoring of the charging plans:

- `sevc:minimumSoc` (*Double*, default `0.2`): if the SoC of the vehicle falls below this threshold, SEVC scoring will add a configurable penalty

- `sevc:minimumEndSoc` (*Double*, default `0.8`): if the SoC of the vehicle is below this threshold at the end of the simulation, SEVC scoring will add a configurable penalty

### Vehicles

The maximum SoC per vehicle should per vehicle:

- `sevc:maximumSoc` (*Double*): defines the maximum SoC until a vehicle will be charged at a charger

**Some things that are not handled by the SEVC contrib: @ Olly**

- Battery capacity is already defined in `VehicleType` > `EngineInformation` attributes in vehicles file
- Initial SoC is already defined in `Vehicle` attributes in vehicles file

**Some things that need clarification / are obsolete @ Olly**

- `simEndSoc` is a result of the simulation and can be handled via scoring (see above)
- `chargingTypeIndex` not sure what is meant
- `chargingType` not sure what is meant
- `chargerIds` is rather handled in the chargers file, to be more consistent across different types
Loading

0 comments on commit 699095b

Please sign in to comment.