Skip to content

Commit

Permalink
feat: require vehicles for IDF by default (#227)
Browse files Browse the repository at this point in the history
* feat: require vehiclces for IDF by default

BREAKING CHANGE: scenarios without vehicles will not work anymore

* add script for retrofitting

* fix retrofit script

* change vehicles path in cutter

* fixed for cutting with vehicles

* chore: moving RunInsertVehicles to core

* fix: adding vehicles if necessary

* chore: running a scenario resulting from the RunScenarioCutter in the tests

* revert auto-generation of vehicles and bugfix in cleaning vehicles

* avoid deprecated call

* update vehicles in test scenario

* fix emission tests

* make cutting vehicles optional

* revert optional vehicles and add verification

* add validator to SMC

* fix corsica tests

---------

Co-authored-by: Tarek Chouaki <[email protected]>
  • Loading branch information
sebhoerl and Tarek Chouaki authored Sep 5, 2024
1 parent ad293ff commit ece4932
Show file tree
Hide file tree
Showing 23 changed files with 209 additions and 146 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package org.eqasim.core.components.config;

import java.util.function.Consumer;

import org.matsim.core.config.CommandLine;
import org.matsim.core.config.CommandLine.ConfigurationException;
import org.matsim.core.config.Config;
Expand All @@ -10,16 +8,19 @@
import org.matsim.core.config.ConfigWriter;

public class ConfigAdapter {
static public void run(String[] args, ConfigGroup[] modules, Consumer<Config> adapter)
static public void run(String[] args, ConfigGroup[] modules, ConfigAdapterConsumer adapter)
throws ConfigurationException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("input-path", "output-path") //
.requireOptions("input-path", "output-path", "prefix") //
.build();

Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("input-path"), modules);
adapter.accept(config);
adapter.accept(config, cmd.getOptionStrict("prefix"));

new ConfigWriter(config).write(cmd.getOptionStrict("output-path"));
}


public interface ConfigAdapterConsumer {
void accept(Config config, String prefix);
}
}
60 changes: 60 additions & 0 deletions core/src/main/java/org/eqasim/core/scenario/RunInsertVehicles.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.eqasim.core.scenario;

import java.io.UncheckedIOException;
import java.util.HashMap;
import java.util.Map;

import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.Scenario;
import org.matsim.api.core.v01.population.Person;
import org.matsim.core.config.CommandLine;
import org.matsim.core.config.CommandLine.ConfigurationException;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.population.io.PopulationReader;
import org.matsim.core.population.io.PopulationWriter;
import org.matsim.core.scenario.ScenarioUtils;
import org.matsim.vehicles.MatsimVehicleWriter;
import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.VehicleUtils;
import org.matsim.vehicles.Vehicles;
import org.matsim.vehicles.VehiclesFactory;

public class RunInsertVehicles {

static public void insertVehicles(Config config, Scenario scenario) {
Vehicles vehicles = scenario.getVehicles();
VehiclesFactory factory = vehicles.getFactory();

vehicles.addVehicleType(VehicleUtils.getDefaultVehicleType());
for (Person person : scenario.getPopulation().getPersons().values()) {
Map<String, Id<Vehicle>> personVehicles = new HashMap<>();

for (String mode : config.routing().getNetworkModes()) {
Vehicle vehicle = factory.createVehicle(Id.createVehicleId(person.getId().toString() + ":" + mode),
VehicleUtils.getDefaultVehicleType());
vehicles.addVehicle(vehicle);

personVehicles.put(mode, vehicle.getId());
}

VehicleUtils.insertVehicleIdsIntoPersonAttributes(person, personVehicles);
}
}

static public void main(String[] args) throws UncheckedIOException, ConfigurationException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path", "input-population-path", "output-vehicles-path",
"output-population-path") //
.build();

Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"));
Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig());
new PopulationReader(scenario).readFile(cmd.getOptionStrict("input-population-path"));

insertVehicles(config, scenario);

new MatsimVehicleWriter(scenario.getVehicles()).writeFile(cmd.getOptionStrict("output-vehicles-path"));
new PopulationWriter(scenario.getPopulation()).write(cmd.getOptionStrict("output-population-path"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ public void run(Config config) {
config.households().setInputFile(prefix + "households.xml.gz");
config.transit().setTransitScheduleFile(prefix + "transit_schedule.xml.gz");
config.transit().setVehiclesFile(prefix + "transit_vehicles.xml.gz");
config.vehicles().setVehiclesFile(prefix + "vehicles.xml.gz");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.eqasim.core.scenario.cutter.network.RoadNetwork;
import org.eqasim.core.scenario.cutter.outside.OutsideActivityAdapter;
import org.eqasim.core.scenario.cutter.population.CleanHouseholds;
import org.eqasim.core.scenario.cutter.population.CleanVehicles;
import org.eqasim.core.scenario.cutter.population.PopulationCutter;
import org.eqasim.core.scenario.cutter.population.PopulationCutterModule;
import org.eqasim.core.scenario.cutter.population.RemoveEmptyPlans;
Expand All @@ -27,6 +28,7 @@
import org.eqasim.core.scenario.routing.PopulationRouter;
import org.eqasim.core.scenario.routing.PopulationRouterModule;
import org.eqasim.core.scenario.validation.ScenarioValidator;
import org.eqasim.core.scenario.validation.VehiclesValidator;
import org.eqasim.core.simulation.EqasimConfigurator;
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension;
import org.eqasim.core.simulation.termination.EqasimTerminationModule;
Expand All @@ -43,10 +45,10 @@
public class RunScenarioCutter {

public static final Collection<String> REQUIRED_ARGS = Set.of("config-path", "output-path", "extent-path");
public static final Collection<String> OPTIONAL_ARGS = Set.of("threads", "prefix", "extent-attribute", "extent-value", "plans-path", "events-path", "skip-routing");
public static final Collection<String> OPTIONAL_ARGS = Set.of("threads", "prefix", "extent-attribute",
"extent-value", "plans-path", "events-path", "skip-routing");

static public void main(String[] args)
throws ConfigurationException, IOException, InterruptedException {
static public void main(String[] args) throws ConfigurationException, IOException, InterruptedException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions(REQUIRED_ARGS) //
.allowOptions(OPTIONAL_ARGS) //
Expand All @@ -69,10 +71,12 @@ static public void main(String[] args)
// Load scenario
EqasimConfigurator configurator = new EqasimConfigurator();
configurator.getModules().removeIf(m -> m instanceof EqasimTerminationModule);

Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"), configurator.getConfigGroups());
cmd.applyConfiguration(config);

VehiclesValidator.validate(config);

Optional<String> plansPath = cmd.getOption("plans-path");

if (plansPath.isPresent()) {
Expand Down Expand Up @@ -106,7 +110,8 @@ static public void main(String[] args)

// Cut population
Injector populationCutterInjector = new InjectorBuilder(scenario) //
.addOverridingModules(configurator.getModules().stream().filter(module -> !(module instanceof AbstractEqasimExtension)).toList()) //
.addOverridingModules(configurator.getModules().stream()
.filter(module -> !(module instanceof AbstractEqasimExtension)).toList()) //
.addOverridingModule(
new PopulationCutterModule(extent, numberOfThreads, 40, cmd.getOption("events-path"))) //
.addOverridingModule(new CutterTravelTimeModule(travelTime)) //
Expand All @@ -128,6 +133,10 @@ static public void main(String[] args)
CleanHouseholds cleanHouseholds = new CleanHouseholds(scenario.getPopulation());
cleanHouseholds.run(scenario.getHouseholds());

// .. and make vehicles consistent
CleanVehicles cleanVehicles = new CleanVehicles(scenario.getPopulation());
cleanVehicles.run(scenario.getVehicles());

// Cut transit
StopSequenceCrossingPointFinder stopSequenceCrossingPointFinder = new DefaultStopSequenceCrossingPointFinder(
extent);
Expand Down Expand Up @@ -159,15 +168,16 @@ static public void main(String[] args)

// Final routing
Injector routingInjector = new InjectorBuilder(scenario) //
.addOverridingModules(configurator.getModules().stream().filter(module -> !(module instanceof AbstractEqasimExtension)).toList()) //
.addOverridingModules(configurator.getModules().stream()
.filter(module -> !(module instanceof AbstractEqasimExtension)).toList()) //
.addOverridingModule(new PopulationRouterModule(numberOfThreads, 100, false)) //
.addOverridingModule(new CutterTravelTimeModule(travelTime)) //
.addOverridingModule(new TimeInterpretationModule()) //
.build();

boolean skipRouting = Boolean.parseBoolean(cmd.getOption("skip-routing").orElse("false"));

if(!skipRouting) {
if (!skipRouting) {
PopulationRouter router = routingInjector.getInstance(PopulationRouter.class);
router.run(scenario.getPopulation());
// Check validity after cutting
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package org.eqasim.core.scenario.cutter;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
import org.eqasim.core.scenario.cutter.extent.ShapeScenarioExtent;
Expand All @@ -20,11 +29,6 @@
import org.matsim.core.population.io.PopulationReader;
import org.matsim.core.scenario.ScenarioUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;

public class RunScenarioCutterV2 {

public static final String[] SHAPEFILE_EXTENSIONS = new String[]{".shp", ".cpg", ".dbf", ".qmd", ".shx", ".prj"};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,8 @@ public void run(File outputDirectory) {
.writeFile(new File(outputDirectory, prefix + "transit_schedule.xml.gz").toString());
new MatsimVehicleWriter(scenario.getTransitVehicles())
.writeFile(new File(outputDirectory, prefix + "transit_vehicles.xml.gz").toString());

if (config.vehicles().getVehiclesFile() != null) {
new MatsimVehicleWriter(scenario.getVehicles())
.writeFile(new File(outputDirectory, prefix + "vehicles.xml.gz").toString());
}

new MatsimVehicleWriter(scenario.getVehicles())
.writeFile(new File(outputDirectory, prefix + "vehicles.xml.gz").toString());
}

static void checkOutputDirectory(File outputDirectory) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.eqasim.core.scenario.cutter.population;

import org.matsim.api.core.v01.IdSet;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.Population;
import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.VehicleUtils;
import org.matsim.vehicles.Vehicles;

public class CleanVehicles {
private final IdSet<Vehicle> retainedIds = new IdSet<>(Vehicle.class);

public CleanVehicles(Population population) {
for (Person person : population.getPersons().values()) {
retainedIds.addAll(VehicleUtils.getVehicleIds(person).values());
}
}

public void run(Vehicles vehicles) {
IdSet<Vehicle> removeIds = new IdSet<>(Vehicle.class);
removeIds.addAll(vehicles.getVehicles().keySet());
removeIds.removeAll(retainedIds);

removeIds.forEach(vehicles::removeVehicle);
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
package org.eqasim.core.scenario.routing;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eqasim.core.misc.InjectorBuilder;
import org.eqasim.core.scenario.validation.VehiclesValidator;
import org.eqasim.core.simulation.EqasimConfigurator;
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension;
import org.eqasim.core.simulation.termination.EqasimTerminationConfigGroup;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.Scenario;
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.core.config.CommandLine;
import org.matsim.core.config.CommandLine.ConfigurationException;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource;
import org.matsim.core.population.io.PopulationWriter;
import org.matsim.core.population.routes.NetworkRoute;
import org.matsim.core.router.TripStructureUtils;
import org.matsim.core.scenario.ScenarioUtils;
import org.matsim.core.utils.timing.TimeInterpretationModule;
import org.matsim.facilities.ActivityFacility;
import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.VehicleUtils;
import org.matsim.vehicles.Vehicles;
import org.matsim.vehicles.VehiclesFactory;

import com.google.inject.Injector;

Expand All @@ -41,17 +29,17 @@ static public void main(String[] args) throws ConfigurationException, Interrupte

EqasimConfigurator configurator = new EqasimConfigurator();
Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"), configurator.getConfigGroups());
config.getModules().remove(EqasimTerminationConfigGroup.GROUP_NAME);
config.getModules().remove(EqasimTerminationConfigGroup.GROUP_NAME);
configurator.addOptionalConfigGroups(config);
cmd.applyConfiguration(config);
config.replanning().clearStrategySettings();
VehiclesValidator.validate(config);

int batchSize = cmd.getOption("batch-size").map(Integer::parseInt).orElse(100);
int numberOfThreads = cmd.getOption("threads").map(Integer::parseInt)
.orElse(Runtime.getRuntime().availableProcessors());

Scenario scenario = ScenarioUtils.loadScenario(config);
insertVehicles(config, scenario);

if (scenario.getActivityFacilities() != null) {
for (ActivityFacility facility : scenario.getActivityFacilities().getFacilities().values()) {
Expand All @@ -70,54 +58,14 @@ static public void main(String[] args) throws ConfigurationException, Interrupte
}

Injector injector = new InjectorBuilder(scenario) //
.addOverridingModules(configurator.getModules().stream().filter(module -> !(module instanceof AbstractEqasimExtension)).toList()) //
.addOverridingModules(configurator.getModules().stream()
.filter(module -> !(module instanceof AbstractEqasimExtension)).toList()) //
.addOverridingModule(new PopulationRouterModule(numberOfThreads, batchSize, true, modes)) //
.addOverridingModule(new TimeInterpretationModule()).build();

PopulationRouter populationRouter = injector.getInstance(PopulationRouter.class);
populationRouter.run(scenario.getPopulation());

clearVehicles(config, scenario);
new PopulationWriter(scenario.getPopulation()).write(cmd.getOptionStrict("output-path"));
}

static public void insertVehicles(Config config, Scenario scenario) {
if (config.qsim().getVehiclesSource().equals(VehiclesSource.defaultVehicle)) {
Vehicles vehicles = scenario.getVehicles();
VehiclesFactory factory = vehicles.getFactory();

vehicles.addVehicleType(VehicleUtils.getDefaultVehicleType());

for (Person person : scenario.getPopulation().getPersons().values()) {
Map<String, Id<Vehicle>> personVehicles = new HashMap<>();

for (String mode : config.routing().getNetworkModes()) {
Vehicle vehicle = factory.createVehicle(Id.createVehicleId(person.getId().toString() + ":" + mode),
VehicleUtils.getDefaultVehicleType());
vehicles.addVehicle(vehicle);

personVehicles.put(mode, vehicle.getId());
}

VehicleUtils.insertVehicleIdsIntoAttributes(person, personVehicles);
}
}
}

static public void clearVehicles(Config config, Scenario scenario) {
if (config.qsim().getVehiclesSource().equals(VehiclesSource.defaultVehicle)) {
for (Person person : scenario.getPopulation().getPersons().values()) {
person.getAttributes().removeAttribute("vehicles");

for (Plan plan : person.getPlans()) {
for (Leg leg : TripStructureUtils.getLegs(plan)) {
if (leg.getRoute() instanceof NetworkRoute) {
NetworkRoute route = (NetworkRoute) leg.getRoute();
route.setVehicleId(null);
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.eqasim.core.scenario.validation;

import org.matsim.core.config.Config;
import org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource;

public class VehiclesValidator {
static public void validate(Config config) {
boolean missingVehicles = config.vehicles().getVehiclesFile() == null;
boolean wrongVehicleSource = !config.qsim().getVehiclesSource().equals(VehiclesSource.fromVehiclesData);

if (missingVehicles || wrongVehicleSource) {
throw new IllegalStateException(
"Eqasim now requires every scenario to provide a vehicles file and to use fromVehiclesData in qsim.vehiclesSource. See RunInsertVehicles to retrofit existing scenarios.");
}
}
}
Loading

0 comments on commit ece4932

Please sign in to comment.