Skip to content

Commit

Permalink
Merge branch 'master' into parkingProxyCleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Royal2Flush authored Jun 7, 2024
2 parents 9d4654b + 9d5f93a commit aa7dc65
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
package ch.sbb.matsim.config;

import com.google.common.base.Verify;

import ch.sbb.matsim.routing.pt.raptor.RaptorStaticConfig.RaptorTransferCalculation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
Expand Down Expand Up @@ -61,6 +64,8 @@ public class SwissRailRaptorConfigGroup extends ReflectiveConfigGroup {
private static final String PARAM_TRANSFER_WALK_MARGIN_DESC = "time deducted from transfer walk leg during transfers between pt legs in order to avoid missing a vehicle by a few seconds due to delays.";
private static final String PARAM_INTERMODAL_LEG_ONLYHANDLING = "intermodalLegOnlyHandling";
private static final String PARAM_INTERMODAL_LEG_ONLYHANDLING_DESC = "Define how routes containing only intermodal legs are handled: Useful options: alllow, avoid, forbid";
private static final String PARAM_TRANSFER_CALCULATION = "transferCalculation";
private static final String PARAM_TRANFER_CALCULATION_DESC = "Defines whether all potential transfers are precomputed at the beginning of the simulation (Initial) or whether they are constructed on-demand (Cached). The former incurs potentially long up-front caclulations, but quicker routing. The latter avoids any initial computation, but may require longer routing time.";

private boolean useRangeQuery = false;
private boolean useIntermodality = false;
Expand All @@ -74,6 +79,7 @@ public class SwissRailRaptorConfigGroup extends ReflectiveConfigGroup {
private double transferPenaltyHourlyCost = 0;
private double transferWalkMargin = 5;
private IntermodalLegOnlyHandling intermodalLegOnlyHandling = IntermodalLegOnlyHandling.forbid;
private RaptorTransferCalculation transferCalculation = RaptorTransferCalculation.Initial;

private ScoringParameters scoringParameters = ScoringParameters.Default;

Expand Down Expand Up @@ -128,9 +134,19 @@ public void setIntermodalLegOnlyHandling(IntermodalLegOnlyHandling intermodalLeg
public String getIntermodalLegOnlyHandlingString() {
return intermodalLegOnlyHandling.toString();
}

public IntermodalLegOnlyHandling getIntermodalLegOnlyHandling() {
return intermodalLegOnlyHandling;
}

@StringSetter(PARAM_TRANSFER_CALCULATION)
public void setTransferCalculation(RaptorTransferCalculation transferCalculation) {
this.transferCalculation = transferCalculation;
}

@StringGetter(PARAM_TRANSFER_CALCULATION)
public RaptorTransferCalculation getTransferCalculation() {
return transferCalculation;
}

@StringGetter(PARAM_USE_RANGE_QUERY)
Expand Down Expand Up @@ -707,6 +723,7 @@ public Map<String, String> getComments() {
comments.put(PARAM_USE_CAPACITY_CONSTRAINTS, PARAM_USE_CAPACITY_CONSTRAINTS_DESC);
comments.put(PARAM_TRANSFER_WALK_MARGIN, PARAM_TRANSFER_WALK_MARGIN_DESC);
comments.put(PARAM_INTERMODAL_ACCESS_EGRESS_MODE_SELECTION,PARAM_INTERMODAL_ACCESS_EGRESS_MODE_SELECTION_DESC);
comments.put(PARAM_TRANSFER_CALCULATION, PARAM_TRANFER_CALCULATION_DESC);
return comments;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ public enum RaptorOptimization {
* (see {@link SwissRailRaptor#calcTree(TransitStopFacility, double, RaptorParameters, Person)} ).
*/
OneToAllRouting }

public enum RaptorTransferCalculation {
/**
* Use this option if you want the algorithm to calculate all possible transfers
* up-front, which will allow for rapid lookup during routing, but may come with
* significant simulation startup time.
*/
Initial,

/**
* Use this option if you want the algorithm to calculate transfers on demand,
* which avoids any simulation start-up time but may increase the routing time
* itself.
*/
Cached
}


/**
Expand All @@ -71,6 +87,7 @@ public enum RaptorOptimization {
private boolean useCapacityConstraints = false;

private RaptorOptimization optimization = RaptorOptimization.OneToOneRouting;
private RaptorTransferCalculation transferCalculation = RaptorTransferCalculation.Initial;

private SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling intermodalLegOnlyHandling = SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling.forbid;

Expand Down Expand Up @@ -167,4 +184,12 @@ public SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling getIntermodalLegOnly
public void setIntermodalLegOnlyHandling(SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling intermodalLegOnlyHandling) {
this.intermodalLegOnlyHandling = intermodalLegOnlyHandling;
}

public RaptorTransferCalculation getTransferCalculation() {
return this.transferCalculation;
}

public void setTransferCalculation(RaptorTransferCalculation transferCalculation) {
this.transferCalculation = transferCalculation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public static RaptorStaticConfig createStaticConfig(Config config) {
staticConfig.setTransferWalkMargin(srrConfig.getTransferWalkMargin());
staticConfig.setIntermodalLegOnlyHandling(srrConfig.getIntermodalLegOnlyHandling());
staticConfig.setMinimalTransferTime(config.transitRouter().getAdditionalTransferTime());
staticConfig.setTransferCalculation(srrConfig.getTransferCalculation());

staticConfig.setUseModeMappingForPassengers(srrConfig.isUseModeMappingForPassengers());
if (srrConfig.isUseModeMappingForPassengers()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -752,10 +752,17 @@ private void handleTransfers(boolean strict, RaptorParameters raptorParams) {
continue;
}
RRouteStop fromRouteStop = fromPE.toRouteStop; // this is the route stop we arrive with least cost at stop
int firstTransferIndex = fromRouteStop.indexFirstTransfer;
int lastTransferIndex = firstTransferIndex + fromRouteStop.countTransfers;

// obtain on-demand transfers if applicable (will return null if transfers are calculated initially)
RTransfer[] transfers = this.data.calculateTransfers(fromRouteStop);

int firstTransferIndex = transfers == null ? fromRouteStop.indexFirstTransfer : 0;
int lastTransferIndex = transfers == null ? firstTransferIndex + fromRouteStop.countTransfers : transfers.length;
transfers = transfers == null ? this.data.transfers : transfers;

for (int transferIndex = firstTransferIndex; transferIndex < lastTransferIndex; transferIndex++) {
RTransfer transfer = this.data.transfers[transferIndex];
RTransfer transfer = transfers[transferIndex];

int toRouteStopIndex = transfer.toRouteStop;
transferProvider.reset(transfer);
int newArrivalTime = arrivalTime + transfer.transferTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,26 @@

package ch.sbb.matsim.routing.pt.raptor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import javax.annotation.Nullable;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.Coord;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.IdMap;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
Expand All @@ -41,18 +57,8 @@
import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.Vehicles;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import ch.sbb.matsim.routing.pt.raptor.RaptorStaticConfig.RaptorOptimization;
import ch.sbb.matsim.routing.pt.raptor.RaptorStaticConfig.RaptorTransferCalculation;

/**
* @author mrieser / SBB
Expand All @@ -75,12 +81,16 @@ public class SwissRailRaptorData {
final QuadTree<TransitStopFacility> stopsQT;
final Map<String, Map<String, QuadTree<TransitStopFacility>>> stopFilterAttribute2Value2StopsQT;
final OccupancyData occupancyData;

// data needed if cached transfer construction is activated
final IdMap<TransitStopFacility, Map<TransitStopFacility, Double>> staticTransferTimes;
final RTransfer[][] transferCache;

private SwissRailRaptorData(RaptorStaticConfig config, int countStops,
RRoute[] routes, int[] departures, Vehicle[] departureVehicles, Id<Departure>[] departureIds, RRouteStop[] routeStops,
RTransfer[] transfers, Map<TransitStopFacility, Integer> stopFacilityIndices,
Map<TransitStopFacility, int[]> routeStopsPerStopFacility, QuadTree<TransitStopFacility> stopsQT,
OccupancyData occupancyData) {
OccupancyData occupancyData, IdMap<TransitStopFacility, Map<TransitStopFacility, Double>> staticTransferTimes) {
this.config = config;
this.countStops = countStops;
this.countRouteStops = routeStops.length;
Expand All @@ -95,6 +105,10 @@ private SwissRailRaptorData(RaptorStaticConfig config, int countStops,
this.stopsQT = stopsQT;
this.stopFilterAttribute2Value2StopsQT = new HashMap<>();
this.occupancyData = occupancyData;

// data needed if cached transfer construction is activated
this.staticTransferTimes = staticTransferTimes;
this.transferCache = new RTransfer[routeStops.length][];
}

public static SwissRailRaptorData create(TransitSchedule schedule, @Nullable Vehicles transitVehicles, RaptorStaticConfig staticConfig, Network network, OccupancyData occupancyData) {
Expand Down Expand Up @@ -200,7 +214,16 @@ public static SwissRailRaptorData create(TransitSchedule schedule, @Nullable Veh
QuadTree<TransitStopFacility> stopsQT = TransitScheduleUtils.createQuadTreeOfTransitStopFacilities(stops);
int countStopFacilities = stops.size();

Map<Integer, RTransfer[]> allTransfers = calculateRouteStopTransfers(schedule, stopsQT, routeStopsPerStopFacility, routeStops, staticConfig);
// if cached transfer calculation is active, don't generate any transfers here
final Map<Integer, RTransfer[]> allTransfers;

if (staticConfig.getTransferCalculation().equals(RaptorTransferCalculation.Initial)) {
allTransfers = calculateRouteStopTransfers(schedule, stopsQT, routeStopsPerStopFacility, routeStops,
staticConfig);
} else {
allTransfers = Collections.emptyMap();
}

long countTransfers = 0;
for (RTransfer[] transfers : allTransfers.values()) {
countTransfers += transfers.length;
Expand All @@ -221,8 +244,22 @@ public static SwissRailRaptorData create(TransitSchedule schedule, @Nullable Veh
indexTransfer += transferCount;
}
}

// if cached transfer calculation is used, build a map for quick lookup of minimal transfer times
IdMap<TransitStopFacility, Map<TransitStopFacility, Double>> staticTransferTimes = null;
if (staticConfig.getTransferCalculation().equals(RaptorTransferCalculation.Cached)) {
staticTransferTimes = new IdMap<>(TransitStopFacility.class);

SwissRailRaptorData data = new SwissRailRaptorData(staticConfig, countStopFacilities, routes, departures, departureVehicles, departureIds, routeStops, transfers, stopFacilityIndices, routeStopsPerStopFacility, stopsQT, occupancyData);
MinimalTransferTimes.MinimalTransferTimesIterator iterator = schedule.getMinimalTransferTimes().iterator();
while (iterator.hasNext()) {
iterator.next();

staticTransferTimes.computeIfAbsent(iterator.getFromStopId(), id -> new HashMap<>())
.put(schedule.getFacilities().get(iterator.getToStopId()), iterator.getSeconds());
}
}

SwissRailRaptorData data = new SwissRailRaptorData(staticConfig, countStopFacilities, routes, departures, departureVehicles, departureIds, routeStops, transfers, stopFacilityIndices, routeStopsPerStopFacility, stopsQT, occupancyData, staticTransferTimes);

long endMillis = System.currentTimeMillis();
log.info("SwissRailRaptor data preparation done. Took " + (endMillis - startMillis) / 1000 + " seconds.");
Expand Down Expand Up @@ -604,4 +641,77 @@ public Transfer get() {
return this.transfer;
}
}

RTransfer[] calculateTransfers(RRouteStop fromRouteStop) {
if (config.getTransferCalculation().equals(RaptorTransferCalculation.Initial)) {
return null;
}

// We tested this in a parallel set-up and things seem to work as they are
// implemented. The routing threads will access the cache as read-only an
// retrieve the cached stop connections. It can happen that two of them try to
// obtain a non-existent entry at the same time. In that case, the calculation
// is performed twice, but this is not critical. Then this function writes the
// connections into the cache. This is a replacement of one address in the array
// from null to a concrete value, and our tests show that this seems to appear
// as atomic to the using threads. However, it is not 100% excluded that there
// is some parallelization issue here and that we rather should shield the cache
// using a lock in some way. But so far, we didn't experience any problem. /sh
// may 2024

RTransfer[] cache = transferCache[fromRouteStop.index];
if (cache != null) return cache; // we had a cache hit

// setting up useful constants
final double minimalTransferTime = config.getMinimalTransferTime();
final double beelineWalkConnectionDistance = config.getBeelineWalkConnectionDistance();
final double beelineDistanceFactor = config.getBeelineWalkDistanceFactor();
final double beelineWalkSpeed = config.getBeelineWalkSpeed();
final RaptorOptimization optimization = config.getOptimization();

// the facility from which we want to transfer
TransitStopFacility fromRouteFacility = fromRouteStop.routeStop.getStopFacility();
Collection<TransitStopFacility> transferCandidates = new LinkedList<>();

// find transfer candidates by distance
transferCandidates.addAll(stopsQT.getDisk(fromRouteFacility.getCoord().getX(), fromRouteFacility.getCoord().getY(), config.getBeelineWalkConnectionDistance()));

// find transfer candidates with predefined transfer time
Map<TransitStopFacility, Double> transferTimes = staticTransferTimes.get(fromRouteFacility.getId());

if (transferTimes != null) { // some transfer times are predefined
transferCandidates.addAll(transferTimes.keySet());
}

// now evaluate whether transfers are useful, distance, and travel time
List<RTransfer> transfers = new LinkedList<>();
for (TransitStopFacility toRouteFacility : transferCandidates) {
for (int toRouteStopIndex : routeStopsPerStopFacility.get(toRouteFacility)) {
RRouteStop toRouteStop = routeStops[toRouteStopIndex];

double beelineDistance = CoordUtils.calcEuclideanDistance(fromRouteFacility.getCoord(), toRouteFacility.getCoord());
double transferTime = beelineDistance / beelineWalkSpeed;

if (transferTime < minimalTransferTime) {
transferTime = minimalTransferTime;
}

if (transferTimes != null) {
// check if we find a predefined transfer time
transferTime = transferTimes.getOrDefault(toRouteFacility, transferTime);
}

if (SwissRailRaptorData.isUsefulTransfer(fromRouteStop, toRouteStop, beelineWalkConnectionDistance, optimization)) {
transfers.add(new RTransfer(fromRouteStop.index, toRouteStop.index, transferTime, beelineDistance * beelineDistanceFactor));
}
}
}

// convert to array
RTransfer[] stopTransfers = transfers.toArray(new RTransfer[transfers.size()]);

// save to cache (no issue regarding parallel execution because we simply set an element)
transferCache[fromRouteStop.index] = stopTransfers;
return stopTransfers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

package org.matsim.pt.transitSchedule;

import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.matsim.api.core.v01.Id;
Expand Down Expand Up @@ -164,4 +166,15 @@ public double getSeconds() {
throw new NoSuchElementException();
}
}

@Override
public Set<Id<TransitStopFacility>> getCandidates(Id<TransitStopFacility> fromStop) {
var inner = minimalTransferTimes.get(fromStop);

if (inner == null) {
return Collections.emptySet();
}

return inner.keySet();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

package org.matsim.pt.transitSchedule.api;

import java.util.Set;

import org.matsim.api.core.v01.Id;

/**
Expand Down Expand Up @@ -66,6 +68,8 @@ public interface MinimalTransferTimes {
* @return the previously set minimal transfer time, or <code>Double.NaN</code> if none was set.
*/
double remove(Id<TransitStopFacility> fromStop, Id<TransitStopFacility> toStop);

Set<Id<TransitStopFacility>> getCandidates(Id<TransitStopFacility> fromStop);

/**
* @return an iterator to iterate over all minimal transfer times set.
Expand Down

0 comments on commit aa7dc65

Please sign in to comment.