Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create Kelheim-specific emission dashboard #70

Merged
merged 9 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions src/main/java/org/matsim/analysis/CreateEmissionDashboard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* *********************************************************************** *
* project: org.matsim.*
* Controler.java
* *
* *********************************************************************** *
* *
* copyright : (C) 2007 by the members listed in the COPYING, *
* LICENSE and WARRANTY file. *
* email : info at matsim dot org *
* *
* *********************************************************************** *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* See also COPYING, LICENSE and WARRANTY file *
* *
* *********************************************************************** */

package org.matsim.analysis;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.analysis.emissions.KelheimEmissionsDashboard;
import org.matsim.application.ApplicationUtils;
import org.matsim.application.MATSimAppCommand;
import org.matsim.application.options.ShpOptions;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.simwrapper.SimWrapper;
import org.matsim.simwrapper.SimWrapperConfigGroup;
import picocli.CommandLine;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;

@CommandLine.Command(
name = "emissions",
description = "Run emission analysis and create SimWrapper dashboard for existing run output."
)
final class CreateEmissionDashboard implements MATSimAppCommand {

private static final Logger log = LogManager.getLogger(CreateEmissionDashboard.class);

@CommandLine.Parameters(arity = "1..*", description = "Path to run output directories for which emission dashboards are to be generated.")
private List<Path> inputPaths;

@CommandLine.Mixin
private final ShpOptions shp = new ShpOptions();

@CommandLine.Option(names = "--base", description = "Optional. " +
"Relative path (from each! output directory provided) to main output folder for the base MATSim run. " +
"Will be used to compare emissions per link.", required = false)
private String baseRun;

private CreateEmissionDashboard(){
}

@Override
public Integer call() throws Exception {

for (Path runDirectory : inputPaths) {
log.info("Running on {}", runDirectory);

//this is to avoid overriding
renameExistingDashboardYAMLs(runDirectory);

Path configPath = ApplicationUtils.matchInput("config.xml", runDirectory);
Config config = ConfigUtils.loadConfig(configPath.toString());
SimWrapper sw = SimWrapper.create(config);

SimWrapperConfigGroup simwrapperCfg = ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class);
if (shp.isDefined()){
//not sure if this is the best way to go, might be that the shape file would be automatically read by providing the --shp command line option
simwrapperCfg.defaultParams().shp = shp.getShapeFile().toString();
}
//skip default dashboards
simwrapperCfg.defaultDashboards = SimWrapperConfigGroup.Mode.disabled;
simwrapperCfg.defaultParams().mapCenter = "48.91265,11.89223";

if (baseRun != null){
sw.addDashboard(new KelheimEmissionsDashboard(baseRun));
} else {
sw.addDashboard(new KelheimEmissionsDashboard());
}

try {
sw.generate(runDirectory);
sw.run(runDirectory);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

return 0;
}

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

}

private static void renameExistingDashboardYAMLs(Path runDirectory) {
// List of files in the folder
File folder = new File(runDirectory.toString());
File[] files = folder.listFiles();

// Loop through all files in the folder
if (files != null) {
for (File file : files) {
if (file.isFile()) {
// Check if the file name starts with "dashboard-" and ends with ".yaml"
if (file.getName().startsWith("dashboard-") && file.getName().endsWith(".yaml")) {
// Get the current file name
String oldName = file.getName();

// Extract the number from the file name
String numberPart = oldName.substring(oldName.indexOf('-') + 1, oldName.lastIndexOf('.'));

// Increment the number by ten
int number = Integer.parseInt(numberPart) + 10;

// Create the new file name
String newName = "dashboard-" + number + ".yaml";

// Create the new File object with the new file name
File newFile = new File(file.getParent(), newName);

// Rename the file
if (file.renameTo(newFile)) {
log.info("File successfully renamed: " + newName);
} else {
log.info("Error renaming file: " + file.getName());
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.matsim.dashboards;
package org.matsim.analysis;

import org.matsim.analysis.emissions.KelheimEmissionsDashboard;
import org.matsim.core.config.Config;
import org.matsim.core.utils.io.IOUtils;
import org.matsim.simwrapper.Dashboard;
Expand All @@ -24,7 +25,8 @@ public List<Dashboard> getDashboards(Config config, SimWrapper simWrapper) {
trips.setAnalysisArgs("--dist-groups", "0,1000,2000,5000,10000,20000");
return List.of(
trips,
new TravelTimeComparisonDashboard(IOUtils.resolveFileOrResource( "kelheim-v3.0-routes-ref.csv.gz").toString())
new TravelTimeComparisonDashboard(IOUtils.resolveFileOrResource( "kelheim-v3.0-routes-ref.csv.gz").toString()),
new KelheimEmissionsDashboard()
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* *********************************************************************** *
* project: org.matsim.*
* Controler.java
* *
* *********************************************************************** *
* *
* copyright : (C) 2007 by the members listed in the COPYING, *
* LICENSE and WARRANTY file. *
* email : info at matsim dot org *
* *
* *********************************************************************** *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* See also COPYING, LICENSE and WARRANTY file *
* *
* *********************************************************************** */

package org.matsim.analysis.emissions;

import org.matsim.application.prepare.network.CreateGeoJsonNetwork;
import org.matsim.simwrapper.Dashboard;
import org.matsim.simwrapper.Header;
import org.matsim.simwrapper.Layout;
import org.matsim.simwrapper.viz.Links;
import org.matsim.simwrapper.viz.Table;
import org.matsim.simwrapper.viz.XYTime;

/**
* this is basically equivalent to the standard emissions dashboard
* but calls the matsim-kelheim-specific emissions analysis class
* {@code KelheimOfflineAirPollutionAnalysisByEngineInformation}
* which has specific network and vehicle type attributes.
*/
public class KelheimEmissionsDashboard implements Dashboard{
private final String pathToCsvBase;

public KelheimEmissionsDashboard() {
this.pathToCsvBase = null;
}

public KelheimEmissionsDashboard(String pathToBaseRun) {
if (!pathToBaseRun.endsWith("/")) {
pathToBaseRun += "/";
}
this.pathToCsvBase = pathToBaseRun + "analysis/emissions/emissions_per_link_per_m.csv";
}

/**
* Produces the dashboard.
*/
public void configure(Header header, Layout layout) {

header.title = "Emissions";
header.description = "Shows the emissions footprint and spatial distribution.";

String linkDescription = "Displays the emissions for each link per meter. Be aware that emission values are provided in the simulation sample size!";
if (pathToCsvBase != null){
linkDescription += String.format("\n Base is %s", pathToCsvBase);
}
String finalLinkDescription = linkDescription;

layout.row("links")
.el(Table.class, (viz, data) -> {
viz.title = "Emissions";
viz.description = "by pollutant. Total values are scaled from the simulation sample size to 100%.";
viz.dataset = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_total.csv", new String[0]);
viz.enableFilter = false;
viz.showAllRows = true;
viz.width = 1.0;
})
.el(Links.class, (viz, data) -> {
viz.title = "Emissions per Link per Meter";
viz.description = finalLinkDescription;
viz.height = 12.0;
viz.datasets.csvFile = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_per_link_per_m.csv", new String[0]);
viz.datasets.csvBase = this.pathToCsvBase;
viz.network = data.compute(CreateGeoJsonNetwork.class, "network.geojson", new String[0]);
viz.display.color.columnName = "CO2_TOTAL [g/m]";
viz.display.color.dataset = "csvFile";
//TODO how to set color ramp??
// viz.display.color.setColorRamp(Plotly.ColorScheme.RdBu, 5, true);
viz.display.width.scaleFactor = 100;
viz.display.width.columnName = "CO2_TOTAL [g/m]";
viz.display.width.dataset = "csvFile";
viz.center = data.context().getCenter();
viz.width = 3.0;
});
layout.row("second").el(XYTime.class, (viz, data) -> {
viz.title = "CO₂ Emissions";
viz.description = "per day. Be aware that CO2 values are provided in the simulation sample size!";
viz.height = 12.0;
viz.file = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_grid_per_day.xyt.csv", new String[0]);
});
layout.row("third")
.el(XYTime.class, (viz, data) -> {
viz.title = "CO₂ Emissions";
viz.description = "per hour. Be aware that CO2 values are provided in the simulation sample size!";
viz.height = 12.;
viz.file = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_grid_per_hour.csv");
});
}
}
Loading
Loading