Skip to content

Commit

Permalink
helper class to adapt v2 run output for drtAnalysis
Browse files Browse the repository at this point in the history
  • Loading branch information
simei94 committed Aug 27, 2024
1 parent cde8a20 commit d9c1986
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 2 deletions.
231 changes: 231 additions & 0 deletions src/main/java/org/matsim/dashboard/AdaptV2OutputFiles.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package org.matsim.dashboard;

import com.univocity.parsers.common.input.EOFException;
import org.matsim.application.MATSimAppCommand;
import org.matsim.application.options.CsvOptions;
import org.matsim.vehicles.MatsimVehicleReader;
import org.matsim.vehicles.VehicleUtils;
import org.matsim.vehicles.Vehicles;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import picocli.CommandLine;
import tech.tablesaw.api.DoubleColumn;
import tech.tablesaw.api.Table;
import tech.tablesaw.io.csv.CsvReadOptions;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import static org.matsim.application.ApplicationUtils.globFile;

@CommandLine.Command(
name = "adapt-v2-output",
description = "Adapt run output from a v2 kelheim sim run, such that standard dashboards can be created for the run."
)
public class AdaptV2OutputFiles implements MATSimAppCommand {

@CommandLine.Option(names = "--runDir", description = "Path of V2 run directory with files to adapt.", required = true)
private String dir;

@CommandLine.Option(names = "--drt-modes", description = "Drt modes to be analyzed. Separated by comma.", defaultValue = "drt,av")
private String drtModes;

@CommandLine.Option(names = "--drt-stops-files", description = "File names for drt stops files. This has to be adapted when param --drt-modes is adapted. Needs to be an absolute path Separated by comma.",
defaultValue = "/net/ils/matsim-kelheim/scenarios/input/kelheim-v2.0/kelheim-v2.0-drt-stops.xml,/net/ils/matsim-kelheim/scenarios/input/kelheim-v2.0/av-stops-DP-AS.xml")
private String drtStops;

public static void main(String[] args) {
new AdaptV2OutputFiles().execute(args);
}

@Override
public Integer call() throws Exception {

// copy stops files to run dir
List<String> stopsFiles = Arrays.stream(drtStops.split(",")).toList();

stopsFiles.forEach(path -> {
File oldFile = new File(path);
Path targetPath = Path.of(dir + "/" + oldFile.getName());

copyFile(targetPath, oldFile);
});

// copy original config file
File inputConfigFile = new File(globFile(Path.of(dir), "*output_config.xml").toString());
Path targetConfigPath = Path.of(inputConfigFile.getPath().split(".xml")[0] + "_withZonalSystemParams_withTravelTimeCalculatorParam.xml");
copyFile(targetConfigPath, inputConfigFile);

// comment out config params which produce errors
adaptConfigAndWriteXml(inputConfigFile);

List<String> modes = Arrays.stream(drtModes.split(",")).toList();

Map<String, List<String>> filePaths = new HashMap<>();

// get all relevant filePaths and save to map for iterating over them
modes.forEach(m -> {
String legsPath = globFile(Path.of(dir + "/ITERS/it.999"), "*drt_legs_" + m + ".csv").toString();
String vehicleStatsPath = globFile(Path.of(dir), "*drt_vehicle_stats_" + m + ".csv").toString();
String customerStatsPath = globFile(Path.of(dir), "*drt_customer_stats_" + m + ".csv").toString();

filePaths.put(m, List.of(legsPath, vehicleStatsPath, customerStatsPath));
});


// copy drt and av legs file from iters folder to global output
filePaths.values().forEach(v -> {
File legsFile = new File(v.getFirst());
Path targetLegsPath = Path.of(dir + "/" + legsFile.getName());

copyFile(targetLegsPath, legsFile);
});

// add totalServiceDuration column to vehicle stats file
filePaths.forEach((k, v) -> {
File vehicleStatsFile = new File(v.get(1));

Table vehicleStats;
try {
vehicleStats = Table.read().csv(CsvReadOptions.builder(vehicleStatsFile)
.separator(CsvOptions.detectDelimiter(vehicleStatsFile.getPath()))
.build());
} catch (IOException e) {
throw new EOFException();
}

if (!vehicleStats.containsColumn("totalServiceDuration")) {

Vehicles vehicles = VehicleUtils.createVehiclesContainer();
MatsimVehicleReader.VehicleReader vehicleReader = new MatsimVehicleReader.VehicleReader(vehicles);

vehicleReader.readFile(globFile(Path.of(dir), "*" + k + "_vehicles.xml").toString());

AtomicReference<Double> serviceDuration = new AtomicReference<>(0.);
vehicles.getVehicles().values().forEach(veh -> serviceDuration.updateAndGet(v1 -> v1 + (Double) veh.getAttributes().getAttribute("t_1") - (Double) veh.getAttributes().getAttribute("t_0")));

double[] values = new double[vehicleStats.rowCount()];
Arrays.fill(values, serviceDuration.get());

DoubleColumn totalServiceDuration = DoubleColumn.create("totalServiceDuration", values);
vehicleStats.addColumns(totalServiceDuration);

vehicleStats.write().csv(vehicleStatsFile);
}
});

// add rides_pax and dummy groupSize_mean columns to customer stats file
filePaths.values().forEach(v -> {
File customerStatsFile = new File(v.get(2));

Table customerStats;
try {
customerStats = Table.read().csv(CsvReadOptions.builder(customerStatsFile)
.separator(CsvOptions.detectDelimiter(customerStatsFile.getPath()))
.build());
} catch (IOException e) {
throw new EOFException();
}

if (!customerStats.containsColumn("rides_pax") || !customerStats.containsColumn("groupSize_mean")) {
if (customerStats.containsColumn("rides")) {
DoubleColumn rides = customerStats.doubleColumn("rides");
DoubleColumn ridesPax = rides.copy().setName("rides_pax");
customerStats.addColumns(ridesPax);
}
double[] defaultValues = new double[customerStats.rowCount()];

Arrays.fill(defaultValues, -1);
DoubleColumn groupSizeMean = DoubleColumn.create("groupSize_mean", defaultValues);
customerStats.addColumns(groupSizeMean);

customerStats.write().csv(customerStatsFile);
}
});

return 0;
}

private static void adaptConfigAndWriteXml(File inputConfigFile) throws ParserConfigurationException, SAXException, IOException, TransformerException {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(inputConfigFile);
doc.getDocumentElement().normalize();

NodeList moduleList = doc.getElementsByTagName("module");
for (int i = 0; i < moduleList.getLength(); i++) {
Element moduleElement = (Element) moduleList.item(i);

if (moduleElement.getAttribute("name").equals("travelTimeCalculator")) {
NodeList paramList = moduleElement.getElementsByTagName("param");
for (int j = 0; j < paramList.getLength(); j++) {
Element paramElement = (Element) paramList.item(j);
if (paramElement.getAttribute("name").equals("travelTimeCalculator")) {
// Comment out the param element
Comment comment = doc.createComment(paramElement.getTextContent());
moduleElement.replaceChild(comment, paramElement);
break;
}
}
break;
}

if (moduleElement.getAttribute("name").equals("multiModeDrt")) {
NodeList paramSetList = moduleElement.getElementsByTagName("parameterset");

for (int k = 0; k < paramSetList.getLength(); k++) {
Element paramSetElement = (Element) paramSetList.item(k);

if (paramSetElement.getAttribute("type").equals("drt")) {
NodeList innerParamSetList = paramSetElement.getElementsByTagName("parameterset");

for (int l = 0; l < innerParamSetList.getLength(); l++) {
Element innerParamSetElement = (Element) innerParamSetList.item(l);
if (innerParamSetElement.getAttribute("type").equals("zonalSystem")) {

// Comment out the "parameterset" of type "zonalSystem"
Comment comment = doc.createComment(innerParamSetElement.getTextContent());
paramSetElement.replaceChild(comment, innerParamSetElement);
break;
}
}
}
}
}
}

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(inputConfigFile);
transformer.setOutputProperty(OutputKeys.INDENT, "no");
transformer.transform(source, result);
}

private static void copyFile(Path targetPath, File inputFile) {
if (Files.notExists(targetPath) && inputFile.exists() && inputFile.isFile()) {
try {
Files.copy(inputFile.toPath(), targetPath);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
}
11 changes: 9 additions & 2 deletions src/main/java/org/matsim/dashboard/KelheimSimWrapperRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import org.matsim.core.config.ConfigUtils;
import org.matsim.simwrapper.SimWrapper;
import org.matsim.simwrapper.SimWrapperConfigGroup;
import org.matsim.simwrapper.dashboard.NoiseDashboard;
import org.matsim.simwrapper.dashboard.*;
import picocli.CommandLine;

import java.io.File;
Expand Down Expand Up @@ -97,7 +97,14 @@ public Integer call() throws Exception {
simwrapperCfg.defaultParams().shp = shp.getShapeFile().toString();
}

if (!standard) {
if (standard) {
simwrapperCfg.defaultDashboards = SimWrapperConfigGroup.Mode.enabled;
sw.addDashboard(new OverviewDashboard());
sw.addDashboard(new TripDashboard("kelheim_mode_share.csv", "kelheim_mode_share_per_dist.csv", null));
sw.addDashboard(new TrafficDashboard());
sw.addDashboard(new TrafficCountsDashboard());
sw.addDashboard(new StuckAgentDashboard());
} else {
//skip default dashboards
simwrapperCfg.defaultDashboards = SimWrapperConfigGroup.Mode.disabled;
}
Expand Down

0 comments on commit d9c1986

Please sign in to comment.