Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;

import org.eqasim.core.components.travel_time.RecordedTravelTime;
import org.eqasim.core.misc.InjectorBuilder;
Expand Down Expand Up @@ -40,11 +41,15 @@
import com.google.inject.Injector;

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");

static public void main(String[] args)
throws ConfigurationException, MalformedURLException, IOException, InterruptedException {
throws ConfigurationException, IOException, InterruptedException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path", "output-path", "extent-path") //
.allowOptions("threads", "prefix", "extent-attribute", "extent-value", "plans-path", "events-path") //
.requireOptions(REQUIRED_ARGS) //
.allowOptions(OPTIONAL_ARGS) //
.build();

// Load some configuration
Expand Down Expand Up @@ -160,11 +165,14 @@ static public void main(String[] args)
.addOverridingModule(new TimeInterpretationModule()) //
.build();

PopulationRouter router = routingInjector.getInstance(PopulationRouter.class);
router.run(scenario.getPopulation());
boolean skipRouting = Boolean.parseBoolean(cmd.getOption("skip-routing").orElse("false"));

// Check validity after cutting
scenarioValidator.checkScenario(scenario);
if(!skipRouting) {
PopulationRouter router = routingInjector.getInstance(PopulationRouter.class);
router.run(scenario.getPopulation());
// Check validity after cutting
scenarioValidator.checkScenario(scenario);
}

// Write scenario
ScenarioWriter scenarioWriter = new ScenarioWriter(config, scenario, prefix);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package org.eqasim.core.scenario.cutter;

import org.apache.commons.io.FileUtils;
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
import org.eqasim.core.scenario.cutter.extent.ShapeScenarioExtent;
import org.eqasim.core.simulation.EqasimConfigurator;
import org.eqasim.core.simulation.vdf.VDFConfigGroup;
import org.eqasim.core.simulation.vdf.engine.VDFEngineConfigGroup;
import org.matsim.api.core.v01.IdSet;
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.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.network.NetworkUtils;
import org.matsim.core.network.algorithms.TransportModeNetworkFilter;
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"};

static public void main(String[] args)
throws ConfigurationException, IOException, InterruptedException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path", "output-path", "extent-path", "vdf-travel-times-path") //
.allowOptions("threads", "prefix", "extent-attribute", "extent-value", "plans-path", "events-path") //
.allowOptions("flag-area-link-modes") //
.build();

String outputPath = cmd.getOptionStrict("output-path");

EqasimConfigurator eqasimConfigurator = new EqasimConfigurator();
Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"), eqasimConfigurator.getConfigGroups());
cmd.applyConfiguration(config);
eqasimConfigurator.addOptionalConfigGroups(config);

if(!config.getModules().containsKey(VDFConfigGroup.GROUP_NAME) || !config.getModules().containsKey(VDFEngineConfigGroup.GROUP_NAME)) {
throw new IllegalStateException(String.format("This scenario cutter only works with configs where both '%s' and '%s' modules are used", VDFConfigGroup.GROUP_NAME, VDFEngineConfigGroup.GROUP_NAME));
}

List<String> scenarioCutterArgs = new ArrayList<>();
for(String requiredOption: RunScenarioCutter.REQUIRED_ARGS) {
scenarioCutterArgs.add("--"+requiredOption);
scenarioCutterArgs.add(cmd.getOptionStrict(requiredOption));
}
for(String optionalOption: RunScenarioCutter.OPTIONAL_ARGS) {
if(cmd.hasOption(optionalOption)) {
scenarioCutterArgs.add("--"+optionalOption);
scenarioCutterArgs.add(cmd.getOptionStrict(optionalOption));
}
}
scenarioCutterArgs.add("--skip-routing");
scenarioCutterArgs.add("true");

RunScenarioCutter.main(scenarioCutterArgs.toArray(String[]::new));

String prefix = cmd.getOption("prefix").orElse("");

Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig());
eqasimConfigurator.configureScenario(scenario);
// We first load the population resulting from the legacy cutter and store the person ids
new PopulationReader(scenario).readFile(Paths.get(outputPath, prefix+"population.xml.gz").toString());
IdSet<Person> personIds = new IdSet<>(Person.class);
scenario.getPopulation().getPersons().values().stream().map(Person::getId).forEach(personIds::add);

// We now read the data from the original scenario
scenario = ScenarioUtils.createScenario(config);
eqasimConfigurator.configureScenario(scenario);
ScenarioUtils.loadScenario(scenario);
eqasimConfigurator.adjustScenario(scenario);

// We remove from the original population, the persons that do not appear in the one resulting from the legacy cutter
IdSet<Person> personsToRemove = new IdSet<>(Person.class);
scenario.getPopulation().getPersons().values().stream().map(Person::getId).filter(personId -> !personIds.contains(personId)).forEach(personsToRemove::add);
personsToRemove.forEach(scenario.getPopulation()::removePerson);

// Now we process the network
File extentPath = new File(cmd.getOptionStrict("extent-path"));
Optional<String> extentAttribute = cmd.getOption("extent-attribute");
Optional<String> extentValue = cmd.getOption("extent-value");
ScenarioExtent extent = new ShapeScenarioExtent.Builder(extentPath, extentAttribute, extentValue).build();

Set<String> insideModes = new HashSet<>();
if(Boolean.parseBoolean(cmd.getOption("flag-area-link-modes").orElse("false"))) {
scenario.getNetwork().getLinks().values()
.stream().filter(link -> extent.isInside(link.getFromNode().getCoord()) && extent.isInside(link.getFromNode().getCoord()))
.forEach(link -> {
Set<String> linkModes = new HashSet<>(link.getAllowedModes());
for(String mode: link.getAllowedModes()) {
String insideMode = "inside_"+mode;
insideModes.add(insideMode);
linkModes.add(insideMode);
}
link.setAllowedModes(linkModes);
});

for(String mode: insideModes) {
findLargestFullyConnectedSubnetwork(scenario.getNetwork(), mode);
}
}

// "Cut" config
// (we need to reload it, because it has become locked at this point)
config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"), eqasimConfigurator.getConfigGroups());
cmd.applyConfiguration(config);
eqasimConfigurator.addOptionalConfigGroups(config);
ConfigCutter configCutter = new ConfigCutter(prefix);
configCutter.run(config);

// Before writing the config, we make sure we configure VDF to update the travel times only in the study area
String extentBasePath = Paths.get(outputPath, "extent").toAbsolutePath().toString();
String copiedExtentPath = Paths.get(extentBasePath, extentPath.getName()).toString();
FileUtils.forceMkdir(new File(extentBasePath));
copyExtentFiles(extentPath.getAbsolutePath(), copiedExtentPath);
VDFConfigGroup vdfConfigGroup = VDFConfigGroup.getOrCreate(config);
vdfConfigGroup.setUpdateAreaShapefile("extent/" + extentPath.getName());
// We also set the VDF config to use the vdf.bin file for initial travel times
vdfConfigGroup.setInputFile("vdf.bin");

new ScenarioWriter(config, scenario, prefix).run(new File(outputPath).getAbsoluteFile());

FileUtils.copyFile(new File(cmd.getOptionStrict("vdf-travel-times-path")), new File(outputPath, "vdf.bin"));
}

public static void findLargestFullyConnectedSubnetwork(Network network, String mode) {
Network subNetwork = NetworkUtils.createNetwork();
new TransportModeNetworkFilter(network).filter(subNetwork, Set.of(mode));

NetworkUtils.runNetworkCleaner(subNetwork);

for(Link link: network.getLinks().values()) {
if(link.getAllowedModes().contains(mode) && !subNetwork.getLinks().containsKey(link.getId())) {
Set<String> modes = new HashSet<>(link.getAllowedModes());
modes.remove(mode);
link.setAllowedModes(modes);
}
}
}

private static void copyExtentFiles(String sourcePath, String destPath) throws IOException {
if(sourcePath.endsWith(".shp")) {
sourcePath = sourcePath.substring(0, sourcePath.length()-4);
destPath = destPath.substring(0, destPath.length()-4);
for(String extension: SHAPEFILE_EXTENSIONS) {
FileUtils.copyFile(new File(sourcePath + extension), new File(destPath + extension));
}
} else {
FileUtils.copyFile(new File(sourcePath), new File(destPath));
}
}
}
38 changes: 36 additions & 2 deletions core/src/test/java/org/eqasim/TestSimulationPipeline.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.eqasim.core.analysis.run.RunPublicTransportLegAnalysis;
import org.eqasim.core.analysis.run.RunTripAnalysis;
import org.eqasim.core.scenario.cutter.RunScenarioCutter;
import org.eqasim.core.scenario.cutter.RunScenarioCutterV2;
import org.eqasim.core.simulation.EqasimConfigurator;
import org.eqasim.core.simulation.analysis.EqasimAnalysisModule;
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension;
Expand Down Expand Up @@ -213,6 +214,36 @@ private void runCutter() throws Exception {
});
}

public void runCutterV2() throws CommandLine.ConfigurationException, IOException, InterruptedException {
RunScenarioCutterV2.main(new String[] {
"--config-path", "melun_test/input/config_vdf.xml",
"--events-path", "melun_test/output_vdf/output_events.xml.gz",
"--vdf-travel-times-path", "melun_test/output_vdf/vdf.bin",
"--output-path", "melun_test/cutter_v2",
"--prefix", "center_",
"--extent-path", "melun_test/input/center.shp",
"--flag-area-link-modes", "true"
});

CreateDrtVehicles.main(new String[]{
"--network-path", "melun_test/cutter_v2/center_network.xml.gz",
"--output-vehicles-path", "melun_test/cutter_v2/drt_vehicles.xml",
"--vehicles-number", "25",
"--network-modes", "inside_car"
});

AdaptConfigForDrt.main(new String[]{
"--input-config-path", "melun_test/cutter_v2/center_config.xml",
"--output-config-path", "melun_test/cutter_v2/center_config_drt.xml",
"--vehicles-paths", "melun_test/cutter_v2/drt_vehicles.xml",
"--operational-schemes", "serviceAreaBased",
"--config:multiModeDrt.drt[mode=drt].drtServiceAreaShapeFile", "extent/center.shp",
"--config:dvrp.networkModes", "inside_car"
});

runMelunSimulation("melun_test/cutter_v2/center_config_drt.xml", "melun_test/output_cutter_v2_drt");
}

@Test
public void testDrt() throws IOException, CommandLine.ConfigurationException {
CreateDrtVehicles.main(new String[]{
Expand Down Expand Up @@ -335,8 +366,7 @@ public void testTransitWithAbstractAccess() throws CommandLine.ConfigurationExce
runMelunSimulation("melun_test/input/config_abstract_access.xml", "melun_test/output_abstract_access");
}

@Test
public void testVDF() throws CommandLine.ConfigurationException, IOException {
public void runVdf() throws CommandLine.ConfigurationException, IOException {
AdaptConfigForVDF.main(new String[] {
"--input-config-path", "melun_test/input/config.xml",
"--output-config-path", "melun_test/input/config_vdf.xml",
Expand All @@ -345,6 +375,8 @@ public void testVDF() throws CommandLine.ConfigurationException, IOException {
"--config:eqasim:vdf_engine.generateNetworkEvents", "true"
});

runMelunSimulation("melun_test/input/config_vdf.xml", "melun_test/output_vdf");

CreateDrtVehicles.main(new String[]{
"--network-path", "melun_test/input/network.xml.gz",
"--output-vehicles-path", "melun_test/input/drt_vehicles.xml.gz",
Expand All @@ -365,9 +397,11 @@ public void testVDF() throws CommandLine.ConfigurationException, IOException {
public void testPipeline() throws Exception {
runMelunSimulation("melun_test/input/config.xml", "melun_test/output");
runStandaloneModeChoice();
runVdf();
runAnalyses();
runExports();
runCutter();
runCutterV2();
}


Expand Down