Skip to content

Commit

Permalink
Allow depots and maintenance windows to be at locations where no tran…
Browse files Browse the repository at this point in the history
…sit line passes

- Needs new masking strategy for the network.
- Filter according to network allowed mode or passing transit line.
  • Loading branch information
munterfi committed Oct 30, 2024
1 parent 37d44d6 commit 05a6086
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public Builder addAllowedTypeToDepot(String id, String vehicleTypeId, int capaci

public Builder addShuntingLocation(String locationId) {
if (config.shunting.onRouteLocations.contains(locationId)) {
throw new IllegalArgumentException("Shunting location with id" + locationId + "already exists.");
throw new IllegalArgumentException("Shunting location with id " + locationId + " already exists.");
}
config.shunting.onRouteLocations.add(locationId);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ private static LocalDateTime toLocalDateTime(double secondsAfterMidnight) {
return LocalDateTime.of(LocalDate.now().plusDays(daysToAdd), time);
}

// TODO: Check what happens with non existing types in MATSIM?
private static Id<VehicleType> getVehicleTypeFrom(RsschedRequestConfig config, TransitLine transitLine) {
return Id.create(config.getGlobal().getTransitLineVehicleTypeAllocation().get(transitLine.getId().toString()),
VehicleType.class);
Expand Down Expand Up @@ -154,7 +153,8 @@ private static PassengerResult extractPassengers(List<PassengerCount> passengerC
maxPassengers = Math.max(maxPassengers, leg.count());
maxSeats = Math.max(maxSeats, leg.seats());
}
if (leg.toStop() != null && segment.destination.getStopFacility().getId()
if (leg.toStop() != null && segment.destination.getStopFacility()
.getId()
.equals(leg.toStop().getStopFacility().getId())) {
break;
}
Expand All @@ -179,7 +179,7 @@ public void apply(RequestPipe pipe) {
} else {
// only add specific depots from list if automated depot at terminal creation is deactivated.
// Otherwise, specific depots from config are ignored.
addDepotsFromConfig(builder);
addDepotsFromConfig(builder, scenario);
}
addMaintenanceSlots(builder, scenario);
addDeadHeadTrips(builder, scenario);
Expand All @@ -197,15 +197,17 @@ private void addVehicleTypes(Request.Builder builder, Scenario scenario) {
}

private void addVehicleTypesFromConfig(Request.Builder builder) {
config.getGlobal().getVehicleTypes().forEach(
vehicleType -> builder.addVehicleType(vehicleType.id(), vehicleType.capacity(), vehicleType.seats(),
vehicleType.maximalFormationCount()));
config.getGlobal()
.getVehicleTypes()
.forEach(vehicleType -> builder.addVehicleType(vehicleType.id(), vehicleType.capacity(),
vehicleType.seats(), vehicleType.maximalFormationCount()));

}

private void addVehicleTypesFromScenario(Request.Builder builder, Scenario scenario) {
scenario.getTransitVehicles().getVehicleTypes().forEach(
(vehicleTypeId, vehicleType) -> builder.addVehicleType(vehicleTypeId.toString(),
scenario.getTransitVehicles()
.getVehicleTypes()
.forEach((vehicleTypeId, vehicleType) -> builder.addVehicleType(vehicleTypeId.toString(),
vehicleType.getCapacity().getSeats(),
vehicleType.getCapacity().getSeats() + vehicleType.getCapacity().getStandingRoom(),
config.getShunting().getDefaultMaximalFormationCount()));
Expand All @@ -222,7 +224,9 @@ private void addDepotsToTerminalLocation(Request.Builder builder, Scenario scena
addDepot(builder, origin);
// add upper bound for vehicle types
for (Departure departure : transitRoute.getDepartures().values()) {
VehicleType vehicleType = scenario.getTransitVehicles().getVehicles().get(departure.getVehicleId())
VehicleType vehicleType = scenario.getTransitVehicles()
.getVehicles()
.get(departure.getVehicleId())
.getType();
Set<VehicleType> bounds = depots.computeIfAbsent(origin, k -> new HashSet<>());
if (!bounds.contains(vehicleType)) {
Expand Down Expand Up @@ -315,8 +319,19 @@ private void addDepot(Request.Builder builder, TransitStopFacility facility) {
}
}

private void addDepotsFromConfig(Request.Builder builder) {
private void addDepotsFromConfig(Request.Builder builder, Scenario scenario) {
for (RsschedRequestConfig.Depot.Facility capacity : config.getDepot().getCapacities()) {

// add depot location if not yet existing
Id<TransitStopFacility> facilityId = Id.create(capacity.locationId(), TransitStopFacility.class);
TransitStopFacility facility = scenario.getTransitSchedule().getFacilities().get(facilityId);
if (facility == null) {
throw new IllegalStateException(
"Depot location " + facilityId + " not found in transit schedule facilities.");
}
addLocation(builder, facility);

// add depot with allowed type capacities
builder.addDepot(capacity.id(), capacity.locationId(), capacity.capacity());
for (RsschedRequestConfig.Depot.AllowedType allowedType : capacity.allowedTypes()) {
builder.addVehicleTypeToDepot(capacity.id(), allowedType.vehicleType(), allowedType.capacity());
Expand All @@ -326,13 +341,17 @@ private void addDepotsFromConfig(Request.Builder builder) {

private void addMaintenanceSlots(Request.Builder builder, Scenario scenario) {
for (RsschedRequestConfig.Maintenance.Slot slot : config.getMaintenance().getSlots()) {

// add maintenance location if not yet existing
Id<TransitStopFacility> facilityId = Id.create(slot.locationId(), TransitStopFacility.class);
TransitStopFacility facility = scenario.getTransitSchedule().getFacilities().get(facilityId);
if (facility == null) {
throw new IllegalStateException(
"Maintenance location " + facilityId + " not found in transit schedule facilities.");
}
addLocation(builder, facility);

// add maintenance slot with number of tracks
builder.addMaintenanceSlot(slot.id(), slot.locationId(), slot.start(), slot.end(), slot.trackCount());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import ch.sbb.rssched.client.pipeline.core.Pipeline;
import ch.sbb.rssched.client.pipeline.passenger.PassengerPipeline;
import ch.sbb.rssched.client.pipeline.scenario.ScenarioPipeline;
import org.matsim.api.core.v01.Id;
import org.matsim.pt.transitSchedule.api.TransitStopFacility;

import java.util.Set;
import java.util.stream.Collectors;

/**
* Request export pipeline
Expand All @@ -25,7 +30,8 @@ public RequestPipeline(RsschedRequestConfig config) {
// set source
super(new ScenarioPassengerCollector(config.getRunId(),
new ScenarioPipeline(config.getInstanceId(), config.getRunId(), config.getInputDirectory(),
config.getOutputDirectory(), config.getGlobal().getFilterStrategy()),
config.getOutputDirectory(), config.getGlobal().getFilterStrategy(),
collectTransitStopFacilitiesToKeep(config)),
new PassengerPipeline(config.getInstanceId(), config.getRunId(), config.getInputDirectory(),
config.getOutputDirectory(), config.getGlobal().getFilterStrategy(),
config.getGlobal().getSampleSize(), config.getGlobal().getSeatDurationThreshold())));
Expand All @@ -35,4 +41,25 @@ public RequestPipeline(RsschedRequestConfig config) {
addSink(new RequestConfigWriter(config));
addSink(new RequestJSONWriter(config.getOutputDirectory(), config.getInstanceId()));
}

private static Set<Id<TransitStopFacility>> collectTransitStopFacilitiesToKeep(RsschedRequestConfig config) {
// collect depot locations
Set<Id<TransitStopFacility>> depotFacilities = config.getDepot()
.getCapacities()
.stream()
.map(facility -> Id.create(facility.locationId(), TransitStopFacility.class))
.collect(Collectors.toSet());

// collect maintenance locations
Set<Id<TransitStopFacility>> maintenanceFacilities = config.getMaintenance()
.getSlots()
.stream()
.map(slot -> Id.create(slot.locationId(), TransitStopFacility.class))
.collect(Collectors.toSet());

// merge both sets
depotFacilities.addAll(maintenanceFacilities);

return depotFacilities;
}
}
118 changes: 84 additions & 34 deletions src/main/java/ch/sbb/rssched/client/pipeline/scenario/NetworkMask.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package ch.sbb.rssched.client.pipeline.scenario;

import ch.sbb.rssched.client.pipeline.core.Filter;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.Scenario;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.pt.transitSchedule.api.TransitLine;
import org.matsim.pt.transitSchedule.api.TransitSchedule;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
Expand All @@ -21,48 +19,100 @@
*
* @author munterfi
*/
@RequiredArgsConstructor
@Log4j2
class NetworkMask implements Filter<ScenarioPipe> {

private static List<Id<Link>> maskLinks(Set<Id<TransitLine>> transitLineIds, TransitSchedule transitSchedule, Network network) {
Set<Id<Link>> linkIdsToKeep = transitLineIds.stream().flatMap(
transitLineId -> transitSchedule.getTransitLines().get(transitLineId).getRoutes().values().stream()
.flatMap(transitRoute -> {
Set<Id<Link>> linkIds = new HashSet<>(transitRoute.getRoute().getLinkIds());
linkIds.add(transitRoute.getStops().get(0).getStopFacility().getLinkId());
linkIds.add(
transitRoute.getStops().get(transitRoute.getStops().size() - 1).getStopFacility()
.getLinkId());
return linkIds.stream();
})).collect(Collectors.toSet());
private final Strategy strategy;

List<Id<Link>> linkIdsToRemove = network.getLinks().keySet().stream()
.filter(linkId -> !linkIdsToKeep.contains(linkId)).toList();
private static void maskNodes(Scenario scenario) {
// collect IDs of remaining links
var network = scenario.getNetwork();
List<Id<Link>> remainingLinkIds = network.getLinks().keySet().stream().toList();
var nodeIdsToKeep = remainingLinkIds.stream()
.flatMap(linkId -> Stream.of(network.getLinks().get(linkId).getFromNode().getId(),
network.getLinks().get(linkId).getToNode().getId()))
.distinct()
.toList();

linkIdsToRemove.forEach(network::removeLink);
network.getNodes()
.keySet()
.stream()
.filter(nodeId -> !nodeIdsToKeep.contains(nodeId))
.forEach(network::removeNode);
}

return new ArrayList<>(linkIdsToKeep);
@Override
public void apply(ScenarioPipe pipe) {
log.info("Masking network (nodes: {}, links: {})", pipe.scenario.getNetwork().getNodes().size(),
pipe.scenario.getNetwork().getLinks().size());
strategy.maskLinks(pipe);
maskNodes(pipe.scenario);
log.info("Done (remaining nodes: {}, links: {})", pipe.scenario.getNetwork().getNodes().size(),
pipe.scenario.getNetwork().getLinks().size());
}

private static void maskNodes(Network network, List<Id<Link>> linkIdsToKeep) {
var nodeIdsToKeep = linkIdsToKeep.stream().flatMap(
linkId -> Stream.of(network.getLinks().get(linkId).getFromNode().getId(),
network.getLinks().get(linkId).getToNode().getId())).distinct().toList();
var nodeIdsToRemove = network.getNodes().keySet().stream().filter(nodeId -> !nodeIdsToKeep.contains(nodeId))
.toList();
nodeIdsToRemove.forEach(network::removeNode);
interface Strategy {
void maskLinks(ScenarioPipe pipe);
}

@Override
public void apply(ScenarioPipe pipe) {
maskNetwork(pipe.scenario, pipe.selection.getLineIds());
@RequiredArgsConstructor
static class LinksWithAllowedMode implements Strategy {

private final String allowedMode;

@Override
public void maskLinks(ScenarioPipe pipe) {
var network = pipe.scenario.getNetwork();

Set<Id<Link>> linkIdsToKeep = network.getLinks()
.values()
.stream()
.filter(link -> link.getAllowedModes().contains(allowedMode)) // Check if link's mode is allowed
.map(Link::getId)
.collect(Collectors.toSet());

network.getLinks()
.keySet()
.stream()
.filter(linkId -> !linkIdsToKeep.contains(linkId))
.forEach(network::removeLink);
}
}

private void maskNetwork(Scenario scenario, Set<Id<TransitLine>> transitLineIds) {
var transitSchedule = scenario.getTransitSchedule();
var network = scenario.getNetwork();
log.info("Masking network (nodes: {}, links: {})", network.getNodes().size(), network.getLinks().size());
maskNodes(network, maskLinks(transitLineIds, transitSchedule, network));
log.info("Done (remaining nodes: {}, links: {})", network.getNodes().size(), network.getLinks().size());
static class LinksWithPassingTransitLines implements Strategy {

@Override
public void maskLinks(ScenarioPipe pipe) {
var transitSchedule = pipe.scenario.getTransitSchedule();
var network = pipe.scenario.getNetwork();
var transitLineIds = pipe.selection.getLineIds();

Set<Id<Link>> linkIdsToKeep = transitLineIds.stream()
.flatMap(transitLineId -> transitSchedule.getTransitLines()
.get(transitLineId)
.getRoutes()
.values()
.stream()
.flatMap(transitRoute -> {
Set<Id<Link>> linkIds = new HashSet<>(transitRoute.getRoute().getLinkIds());
linkIds.add(transitRoute.getStops().get(0).getStopFacility().getLinkId());
linkIds.add(transitRoute.getStops()
.get(transitRoute.getStops().size() - 1)
.getStopFacility()
.getLinkId());
return linkIds.stream();
}))
.collect(Collectors.toSet());

List<Id<Link>> linkIdsToRemove = network.getLinks()
.keySet()
.stream()
.filter(linkId -> !linkIdsToKeep.contains(linkId))
.toList();

linkIdsToRemove.forEach(network::removeLink);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import ch.sbb.rssched.client.config.selection.FilterStrategy;
import ch.sbb.rssched.client.pipeline.core.Pipeline;
import org.matsim.api.core.v01.Id;
import org.matsim.pt.transitSchedule.api.TransitStopFacility;

import java.util.Set;

/**
* The ScenarioPipeline processes a MATSim SIMBA MOBi scenario for the Innosuisse project REP of SBB and ETH Zurich.
Expand All @@ -23,15 +27,15 @@ public class ScenarioPipeline extends Pipeline<ScenarioPipe> {
* @param outputDirectory the output directory to export the processed scenario files
* @param filterStrategy the strategy for filtering transit lines
*/
public ScenarioPipeline(String instanceId, String runId, String inputDirectory, String outputDirectory, FilterStrategy filterStrategy) {
public ScenarioPipeline(String instanceId, String runId, String inputDirectory, String outputDirectory, FilterStrategy filterStrategy, Set<Id<TransitStopFacility>> transitStopFacilitiesToKeep) {
// set source
super(new ScenarioSource(runId, inputDirectory));
// filter transit lines
addFilter(new TransitLineFilter(filterStrategy));
// mask scenario
addFilter(new TransitScheduleMask());
addFilter(new TransitScheduleMask(transitStopFacilitiesToKeep));
addFilter(new TransitVehicleMask());
addFilter(new NetworkMask());
addFilter(new NetworkMask(new NetworkMask.LinksWithPassingTransitLines()));
// clear attributes
addFilter(new AttributeRemover());
// add sink
Expand Down
Loading

0 comments on commit 05a6086

Please sign in to comment.