diff --git a/pom.xml b/pom.xml index f8be78d..e6f09d2 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ - 2025.0-PR3429 + 2025.0-PR3486 diff --git a/src/main/R/commuter-analysis.R b/src/main/R/commuter-analysis.R new file mode 100644 index 0000000..08405ff --- /dev/null +++ b/src/main/R/commuter-analysis.R @@ -0,0 +1,52 @@ +library(matsim) +library(tidyverse) +library(readr) +library(sf) + +#FILES +FILE_DIR = "../../shared-svn/projects/DiTriMo/data/commuters-by-town" +SIM <- paste0(FILE_DIR, "lausitz-v1.0-commuter.csv") +GEMEINDE <- paste0(FILE_DIR, "pgemeinden.csv") +COMMUTER <- paste0(FILE_DIR, "commuter.csv") +SHP <- paste0(FILE_DIR, "VG5000_GEM/VG5000_GEM.shp") +LAUSITZ.SHP <- paste0(FILE_DIR, "network-area/network-area.shp") + +shp <- st_read(SHP) +lausitz.shp <- st_read(LAUSITZ.SHP) + +sim <- read_csv(file = SIM) +gemeinden <- read_csv( file = GEMEINDE) %>% + mutate(code = str_remove(string = code, pattern = "P")) + +commuter <- read_csv(file = COMMUTER) %>% + mutate(key = paste0(from, "-", to)) + +sim.1 <- sim %>% + filter(!is.na(from) & !is.na(to)) %>% + left_join(gemeinden, by = c("from" = "code")) %>% + left_join(gemeinden, by = c("from" = "code"), suffix = c("_from", "_to")) %>% + mutate(key = paste0(from, "-", to)) %>% + select(-c(from, to)) %>% + left_join(commuter, by = "key", suffix = c("_sim", "_real")) %>% + filter(!is.na(from)) %>% +# pivot_longer(cols = starts_with("n_"), names_to = "src", values_to = "n", names_prefix = "n_") %>% + arrange(desc(n_real)) + +breaks <- c(-Inf, 0.8, 1.2, Inf) +labels <- c("less", "exakt", "more") + +sim.2 <- sim.1 %>% + filter(n_sim > 10) %>% + select(from, to, starts_with("n_"), starts_with("name_")) %>% + mutate(n_rel = n_sim / n_real, + quality = cut(n_rel, breaks = breaks, labels = labels)) + +ggplot(sim.2, aes(x = n_real, y = n_sim, col = quality)) + + + geom_point() + + + scale_x_log10() + + + scale_y_log10() + + + theme_bw() diff --git a/src/main/R/counts.R b/src/main/R/counts.R index 46adb56..82a1354 100644 --- a/src/main/R/counts.R +++ b/src/main/R/counts.R @@ -4,10 +4,10 @@ devtools::install_github("matsim-vsp/matsim-r",ref="counts") library(matsim) library(tidyverse) -COUNTS <- "Y:/matsim-lausitz/input/v1.0/lausitz-v1.0-counts-car-bast.xml.gz" -NETWORK <- "Y:/matsim-lausitz/input/v1.0/lausitz-v1.0-network-with-pt.xml.gz" +COUNTS <- "../../public-svn/matsim/scenarios/countries/de/lausitz/input/v1.0/lausitz-v1.0-counts-car-bast.xml.gz" +NETWORK <- "../../public-svn/matsim/scenarios/countries/de/lausitz/input/v1.0/lausitz-v1.0-network-with-pt.xml.gz" -linkstats <- readLinkStats(runId = "v1.0-uncalibrated", file = "Y:/matsim-lausitz/qa/output/lausitz-25pct.output_linkstats.csv.gz") +linkstats <- readLinkStats(runId = "v1.0-uncalibrated", file = "Y:/matsim-lausitz/qa/output/lausitz-25pct.output_linkstats.csv.gz", sampleSize = 1) counts <- readCounts(COUNTS) network <- loadNetwork(NETWORK) @@ -17,7 +17,7 @@ join <- mergeCountsAndLinks(counts = counts, linkStats = list(linkstats), netwo #### VIA-styled scatterplot #### -FILE_DIR = "C:/Users/ACER/Desktop/Uni/VSP/Lausitz-Plots/" +FILE_DIR = "../../shared-svn/projects/DiTriMo/data/commuters-by-town" createCountScatterPlot(joinedFrame = join) ggsave(filename = paste0(FILE_DIR, "Traffic_Count_Scatterplot_with_freight.jpg")) @@ -43,7 +43,7 @@ rm(join.dtv.distribution) #### Analysis of Estimation Quality #### -join.est.quality <- processDtvEstimationQuality(joinedFrame = join, aggr = T) %>% +join.est.quality <- processDtvEstimationQuality(joinedFrame = join, aggr = F) %>% filter(!type %in% c("residential", "unclassified", NA)) ggplot(join.est.quality, aes(estimation, share, fill = type)) + @@ -59,4 +59,26 @@ ggplot(join.est.quality, aes(estimation, share, fill = type)) + theme(legend.position = "none", axis.text.x = element_text(angle = 90)) rm(join.est.quality) -ggsave(filename = paste0(FILE_DIR, "Estimation_quality_by_road_type_with_freight.jpg")) \ No newline at end of file +ggsave(filename = paste0(FILE_DIR, "Estimation_quality_by_road_type_with_freight.jpg")) + + +#### network plot #### + +library(tmap) +library(tmaptools) +library(OpenStreetMap) +library(sf) + +link.geom <- join %>% + left_join(network$links, by = c("loc_id" = "id")) %>% + mutate(geom = sprintf("LINESTRING(%s %s, %s %s)", x.from, y.from, x.to, y.to)) %>% + st_as_sf(crs = 25832, wkt = "geom") %>% + transmute(loc_id, type.x, rel_vol = volume / count, geom) + +tmap_mode("view") + +tm_shape(shp = link.geom) + + tm_lines(col = "estimation", style = "cont", lwd = 3.5, palette = c("red", "green", "blue")) + +tm_shape(shp = link.geom) + + tm_lines(col = "rel_vol", style = "cont", lwd = 5, palette = c("red", "yellow", "green"), breaks = c(0, 0.05, 0.8, 2)) diff --git a/src/main/java/org/matsim/dashboards/LausitzDashboardProvider.java b/src/main/java/org/matsim/dashboards/LausitzDashboardProvider.java index 7c2589c..0092383 100644 --- a/src/main/java/org/matsim/dashboards/LausitzDashboardProvider.java +++ b/src/main/java/org/matsim/dashboards/LausitzDashboardProvider.java @@ -1,6 +1,7 @@ package org.matsim.dashboards; import org.matsim.core.config.Config; +import org.matsim.core.population.PersonUtils; import org.matsim.simwrapper.Dashboard; import org.matsim.simwrapper.DashboardProvider; import org.matsim.simwrapper.SimWrapper; @@ -20,8 +21,9 @@ public List getDashboards(Config config, SimWrapper simWrapper) { "lausitz_mode_share.csv", "lausitz_mode_share_per_dist.csv", "lausitz_mode_users.csv") - .withGroupedRefData("lausitz_mode_share_per_group_dist_ref.csv", "age", "economic_status") - .withDistanceDistribution("lausitz_mode_share_distance_distribution.csv"); + .withGroupedRefData("lausitz_mode_share_per_group_dist_ref.csv", "age", "economic_status", "income") + .withDistanceDistribution("lausitz_mode_share_distance_distribution.csv") + .setAnalysisArgs("--person-filter", "subpopulation=person"); return List.of(trips, new EmissionsDashboard() diff --git a/src/main/java/org/matsim/dashboards/LausitzSimWrapperRunner.java b/src/main/java/org/matsim/dashboards/LausitzSimWrapperRunner.java index bb61bda..29e09b6 100644 --- a/src/main/java/org/matsim/dashboards/LausitzSimWrapperRunner.java +++ b/src/main/java/org/matsim/dashboards/LausitzSimWrapperRunner.java @@ -42,7 +42,7 @@ name = "simwrapper", description = "Run additional analysis and create SimWrapper dashboard for existing run output." ) -final class LausitzSimWrapperRunner implements MATSimAppCommand { +public final class LausitzSimWrapperRunner implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(LausitzSimWrapperRunner.class); @@ -56,7 +56,8 @@ final class LausitzSimWrapperRunner implements MATSimAppCommand { private boolean noise; - private LausitzSimWrapperRunner(){ + public LausitzSimWrapperRunner(){ +// public constructor needed for testing purposes. } @Override @@ -81,7 +82,7 @@ public Integer call() throws Exception { simwrapperCfg.defaultDashboards = SimWrapperConfigGroup.Mode.disabled; //add dashboards according to command line parameters -// TODO: if more dashboards are to be added here, we need to check if noise==true before adding noise dashboard here +// if more dashboards are to be added here, we need to check if noise==true before adding noise dashboard here sw.addDashboard(Dashboard.customize(new NoiseDashboard()).context("noise")); diff --git a/src/main/java/org/matsim/run/LausitzScenario.java b/src/main/java/org/matsim/run/LausitzScenario.java index f7fa06b..4ecfbf7 100644 --- a/src/main/java/org/matsim/run/LausitzScenario.java +++ b/src/main/java/org/matsim/run/LausitzScenario.java @@ -2,10 +2,8 @@ import com.google.common.collect.Sets; import org.matsim.analysis.personMoney.PersonMoneyEventsAnalysisModule; -import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.population.Person; import org.matsim.application.MATSimApplication; import org.matsim.application.analysis.CheckPopulation; import org.matsim.application.analysis.traffic.LinkStats; @@ -31,10 +29,11 @@ import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; -import org.matsim.core.population.PopulationUtils; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; import org.matsim.core.scoring.functions.ScoringParametersForPerson; +import org.matsim.run.analysis.CommunityFilter; import org.matsim.run.analysis.CommuterAnalysis; +import org.matsim.run.analysis.DistanceMatrix; import org.matsim.run.prepare.PrepareNetwork; import org.matsim.run.prepare.PreparePopulation; import org.matsim.simwrapper.SimWrapperConfigGroup; @@ -48,9 +47,6 @@ import javax.annotation.Nullable; import java.util.HashSet; import java.util.List; -import java.net.URISyntaxException; -import java.nio.file.Paths; -import java.util.Map; import java.util.Set; @CommandLine.Command(header = ":: Open Lausitz Scenario ::", version = LausitzScenario.VERSION, mixinStandardHelpOptions = true) @@ -61,7 +57,7 @@ SplitActivityTypesDuration.class, CreateCountsFromBAStData.class, PreparePopulation.class, CleanPopulation.class, PrepareNetwork.class }) @MATSimApplication.Analysis({ - LinkStats.class, CheckPopulation.class, CommuterAnalysis.class, + LinkStats.class, CheckPopulation.class, CommuterAnalysis.class, CommunityFilter.class, DistanceMatrix.class }) public class LausitzScenario extends MATSimApplication { @@ -79,6 +75,9 @@ public class LausitzScenario extends MATSimApplication { private static final String HBEFA_FILE_COLD_AVERAGE = HBEFA_2020_PATH + "r9230ru2n209r30u2fn0c9rn20n2rujkhkjhoewt84202.enc" ; private static final String HBEFA_FILE_WARM_AVERAGE = HBEFA_2020_PATH + "7eff8f308633df1b8ac4d06d05180dd0c5fdf577.enc"; + @CommandLine.Option(names = "--alpha", description = "alpha for ride, this is just to get a feeling for the parameters dimension, should never be configurable in release.", defaultValue = "2.") + private double alpha; + @CommandLine.Mixin private final SampleOptions sample = new SampleOptions( 100, 25, 10, 1); @@ -135,7 +134,7 @@ protected Config prepareConfig(Config config) { // 2.0 + 1.0 = alpha + 1 // ride cost = alpha * car cost // ride marg utility of traveling = (alpha + 1) * marg utility travelling car + alpha * beta perf - double alpha = 2; +// double alpha = 2; rideParams.setMarginalUtilityOfTraveling((alpha + 1) * carParams.getMarginalUtilityOfTraveling() - alpha * config.scoring().getPerforming_utils_hr()); rideParams.setDailyMonetaryConstant(0.); rideParams.setMonetaryDistanceRate(carParams.getMonetaryDistanceRate() * 2); @@ -155,13 +154,9 @@ protected Config prepareConfig(Config config) { vvo20.setTransactionPartner("VVO Tarifzone 20"); vvo20.setDescription("VVO Tarifzone 20"); vvo20.setOrder(1); - try { - vvo20.setFareZoneShp(Paths.get(config.getContext().toURI()).getParent().toString() + "/vvo_tarifzone20/vvo_tarifzone20_hoyerswerda_utm32n.shp"); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } + vvo20.setFareZoneShp("./vvo_tarifzone20/vvo_tarifzone20_hoyerswerda_utm32n.shp"); - DistanceBasedPtFareParams germany = DistanceBasedPtFareParams.GERMAN_WIDE_FARE; + DistanceBasedPtFareParams germany = DistanceBasedPtFareParams.GERMAN_WIDE_FARE_2024; germany.setTransactionPartner("Deutschlandtarif"); germany.setDescription("Deutschlandtarif"); germany.setOrder(2); @@ -184,26 +179,6 @@ protected Config prepareConfig(Config config) { @Override protected void prepareScenario(Scenario scenario) { - - - for (Person person : scenario.getPopulation().getPersons().values()) { - - if (PopulationUtils.getSubpopulation(person).contains("commercialPersonTraffic") || - PopulationUtils.getSubpopulation(person).contains("goodsTraffic")) { - - Map> types = VehicleUtils.getVehicleTypes(person); - - for (Map.Entry> entry : types.entrySet()) { - if (Set.of(HEAVY_MODE, MEDIUM_MODE, LIGHT_MODE).contains(entry.getKey())) { - types.put(entry.getKey(), Id.create(entry.getKey(), VehicleType.class)); - } - } - } - - } - - - // add freight and truck as allowed modes together with car PrepareNetwork.prepareFreightNetwork(scenario.getNetwork()); diff --git a/src/main/java/org/matsim/run/RunLausitzPtScenario.java b/src/main/java/org/matsim/run/RunLausitzPtScenario.java index 7dfac1e..f38b3cf 100644 --- a/src/main/java/org/matsim/run/RunLausitzPtScenario.java +++ b/src/main/java/org/matsim/run/RunLausitzPtScenario.java @@ -28,6 +28,10 @@ public class RunLausitzPtScenario extends MATSimApplication { private final LausitzScenario baseScenario = new LausitzScenario(); + public RunLausitzPtScenario(@Nullable Config config) { + super(config); + } + public RunLausitzPtScenario() { super(String.format("input/v%s/lausitz-v%s-10pct.config.xml", LausitzScenario.VERSION, LausitzScenario.VERSION)); } diff --git a/src/main/java/org/matsim/run/analysis/CommunityFilter.java b/src/main/java/org/matsim/run/analysis/CommunityFilter.java new file mode 100644 index 0000000..1bd1b90 --- /dev/null +++ b/src/main/java/org/matsim/run/analysis/CommunityFilter.java @@ -0,0 +1,80 @@ +package org.matsim.run.analysis; + +import org.apache.commons.csv.CSVPrinter; +import org.geotools.api.feature.simple.SimpleFeature; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Point; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.CsvOptions; +import org.matsim.core.utils.collections.Tuple; +import org.matsim.core.utils.gis.GeoFileReader; +import picocli.CommandLine; + +import java.nio.file.Path; +import java.util.*; + +@CommandLine.Command(name = "community-filter", description = "creates a csv with commuity keys within shape") +public class CommunityFilter implements MATSimAppCommand { + + @CommandLine.Option(names = "--community-shp", description = "path to VG5000_GEM.shp", required = true) + private static Path communityShapePath; + + @CommandLine.Option(names = "--dilution-area", description = "path to area-of-interest-shape file", required = true) + private static Path dilutionAreaShapePath; + + @CommandLine.Option(names = "--output", description = "output csv filepath", required = true) + private static Path output; + + @CommandLine.Mixin + CsvOptions csvOptions = new CsvOptions(); + private final Map> filtered = new HashMap<>(); + + public static void main(String[] args) { + new CommunityFilter().execute(args); + } + + @Override + public Integer call() throws Exception { + + Collection communities = readShapeFile(communityShapePath); + Collection dilutionArea = readShapeFile(dilutionAreaShapePath); + + List geometries = dilutionArea.stream().map(feature -> (Geometry) feature.getDefaultGeometry()).toList(); + + for(var community: communities){ + + String attribute = (String) community.getAttribute("ARS"); + + if(geometries.stream() + .anyMatch(geometry -> geometry.covers((Geometry) community.getDefaultGeometry()))){ + + Point centroid = ((Geometry) community.getDefaultGeometry()).getCentroid(); + Tuple coordinates = Tuple.of(centroid.getX(), centroid.getY()); + + filtered.put(attribute, coordinates); + } + + } + + try (CSVPrinter printer = csvOptions.createPrinter(output)) { + printer.print("ars"); + printer.print("x"); + printer.print("y"); + printer.println(); + + for (Map.Entry> entry : filtered.entrySet()) { + printer.print(entry.getKey()); + printer.print(entry.getValue().getFirst()); + printer.print(entry.getValue().getSecond()); + printer.println(); + } + } + + return 0; + } + + private static Collection readShapeFile(Path filepath){ + + return GeoFileReader.getAllFeatures(filepath.toString()); + } +} diff --git a/src/main/java/org/matsim/run/analysis/DistanceMatrix.java b/src/main/java/org/matsim/run/analysis/DistanceMatrix.java new file mode 100644 index 0000000..3be4a0d --- /dev/null +++ b/src/main/java/org/matsim/run/analysis/DistanceMatrix.java @@ -0,0 +1,108 @@ +package org.matsim.run.analysis; + +import org.apache.commons.csv.CSVPrinter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.geotools.api.feature.simple.SimpleFeature; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Point; +import org.matsim.api.core.v01.Coord; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.CsvOptions; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.utils.geometry.CoordUtils; +import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.core.utils.gis.GeoFileReader; +import picocli.CommandLine; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +@CommandLine.Command(name = "distance-matrix", description = "creates a csv with commuity keys within shape") +public class DistanceMatrix implements MATSimAppCommand{ + + @CommandLine.Option(names = "--output", description = "output csv filepath", required = true) + private static Path output; + + @CommandLine.Option(names = "--dilution-area", description = "shape to filter zones", required = false) + private static Path dilutionArea; + + @CommandLine.Mixin + CsvOptions csvOptions = new CsvOptions(); + + @CommandLine.Mixin + ShpOptions shp = new ShpOptions(); + + private final List distances = new ArrayList<>(); + private static final Logger logger = LogManager.getLogger(DistanceMatrix.class); + + public static void main(String[] args) { + new DistanceMatrix().execute(args); + } + + @Override + public Integer call() throws Exception { + + logger.info("Read features."); + List communities = shp.readFeatures(); + + //to prevent RuntimeExceptions + ArrayList copy = new ArrayList<>(communities); + + Predicate filter = getFilter(dilutionArea); + + logger.info("Calculate distance matrix."); + String delimiter = csvOptions.getFormat().getDelimiterString(); + for(var community: communities){ + + if(!filter.test((Geometry) community.getDefaultGeometry())) + continue; + String nameFrom = (String) community.getAttribute("ARS"); + Point centroid = ((Geometry) community.getDefaultGeometry()).getCentroid(); + Coord from = MGC.point2Coord(centroid); + for(var target: copy){ + + String nameTo = (String) target.getAttribute("ARS"); + + Point centroid2 = ((Geometry) target.getDefaultGeometry()).getCentroid(); + Coord to = MGC.point2Coord(centroid2); + double distance = CoordUtils.calcEuclideanDistance(from, to); + + String distanceString = String.valueOf(distance).replace('.', ','); + + distances.add(nameFrom + delimiter + nameTo + delimiter + distanceString); + } + } + + logger.info("Print results to {}", output); + try (CSVPrinter printer = csvOptions.createPrinter(output)) { + printer.print("from"); + printer.print("to"); + printer.print("distance"); + printer.println(); + + for (String entry : distances) { + for (String col : entry.split(csvOptions.getFormat().getDelimiterString())) + printer.print(col); + printer.println(); + } + } + + logger.info("Done!"); + return 0; + } + + private Predicate getFilter(Path path){ + + if(path == null) + return community -> true; + + List geometries = GeoFileReader.getAllFeatures(path.toString()).stream() + .map(feature -> (Geometry) feature.getDefaultGeometry()) + .toList(); + + return community -> geometries.stream().anyMatch(geometry -> geometry.covers(community)); + } +} diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index ade17b5..5ad8bfa 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -91,14 +91,6 @@ public static void prepareFreightNetwork(Network network) { public static void prepareEmissionsAttributes(Network network) { // do not use VspHbefaRoadTypeMapping() as it results in almost every road to mapped to "highway"! HbefaRoadTypeMapping roadTypeMapping = OsmHbefaMapping.build(); -// the type attribute in our network has the prefix "highway" for all links but pt links. -// we need to delete that because OsmHbefaMapping does not handle that. - for (Link link : network.getLinks().values()) { - //pt links can be disregarded - if (!link.getAllowedModes().contains("pt")) { - NetworkUtils.setType(link, NetworkUtils.getType(link).replaceFirst("highway.", "")); - } - } roadTypeMapping.addHbefaMappings(network); } diff --git a/src/test/java/org/matsim/run/EmissionAnalysisOutputTest.java b/src/test/java/org/matsim/run/EmissionAnalysisOutputTest.java deleted file mode 100644 index cfeeab0..0000000 --- a/src/test/java/org/matsim/run/EmissionAnalysisOutputTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.matsim.run; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; -import org.matsim.api.core.v01.Coord; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.population.*; -import org.matsim.application.MATSimApplication; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.population.PersonUtils; -import org.matsim.core.population.PopulationUtils; -import org.matsim.core.utils.io.IOUtils; -import org.matsim.testcases.MatsimTestUtils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; - -import static org.matsim.application.ApplicationUtils.globFile; - -class EmissionAnalysisOutputTest { - - @RegisterExtension - public MatsimTestUtils utils = new MatsimTestUtils(); - - @TempDir - public Path p; - - private final static Id ptPersonId = Id.createPersonId("Hoyerswerda-Cottbus_CAR"); - - @Test - void runEmissionAnalysisOutputTest() throws IOException { - Config config = ConfigUtils.loadConfig(String.format("input/v%s/lausitz-v%s-10pct.config.xml", LausitzScenario.VERSION, LausitzScenario.VERSION)); - - Path inputPath = p.resolve("emissions-test-population.xml.gz"); - - Population population = PopulationUtils.createPopulation(config); - PopulationFactory fac = population.getFactory(); - Person person = fac.createPerson(ptPersonId); - Plan plan = PopulationUtils.createPlan(person); - -// home in hoyerswerda, nearest link 28922425#0 - Activity home = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); - home.setEndTime(8 * 3600); - Activity home2 = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); - home2.setEndTime(19 * 3600); -// work in hoyerswerda, nearest link(s): 686055693#1, -686055693#1 - Activity work = fac.createActivityFromCoord("work_2400", new Coord(863866.47,5710961.86)); - work.setEndTime(17 * 3600 + 25 * 60); - - Leg leg = fac.createLeg(TransportMode.car); - - plan.addActivity(home); - plan.addLeg(leg); - plan.addActivity(work); - plan.addLeg(leg); - plan.addActivity(home2); - - person.addPlan(plan); - PersonUtils.setIncome(person, 1000.); - person.getAttributes().putAttribute("subpopulation", "person"); - population.addPerson(person); - - new PopulationWriter(population).write(inputPath.toString()); - - assert MATSimApplication.execute(LausitzScenario.class, config, - "--1pct", - "--iterations", "0", - "--output", utils.getOutputDirectory(), - "--config:plans.inputPlansFile", inputPath.toString(), - "--config:controller.overwriteFiles=deleteDirectoryIfExists") == 0 : "Must return non error code"; - - - Path csvPath = globFile(Path.of(utils.getOutputDirectory() + "/analysis/emissions"), "*emissions_per_link.csv*"); - - Map nonZeroLinks = new HashMap<>(); - - try { - BufferedReader reader = IOUtils.getBufferedReader(csvPath.toUri().toURL()); - String line; - -// skip header - reader.readLine(); - - while ((line = reader.readLine()) != null) { - String[] parts = line.split(","); - -// if first value (CO) is zero, all others are too - if (Double.parseDouble(parts[1]) == 0.) { - continue; - } - - Double[] values = new Double[23]; - - for (int i = 1; i < parts.length; i++) { - values[i - 1] = Double.parseDouble(parts[i]); - } - - nonZeroLinks.put(parts[0], values); - } - } finally { - - } - - Assertions.assertFalse(nonZeroLinks.isEmpty()); - Assertions.assertTrue(nonZeroLinks.containsKey("28922425#0")); - Assertions.assertTrue(nonZeroLinks.containsKey("-686055693#1")); - } -} diff --git a/src/test/java/org/matsim/run/EmissionAndNoiseAnalysisOutputTest.java b/src/test/java/org/matsim/run/EmissionAndNoiseAnalysisOutputTest.java new file mode 100644 index 0000000..f4cd898 --- /dev/null +++ b/src/test/java/org/matsim/run/EmissionAndNoiseAnalysisOutputTest.java @@ -0,0 +1,186 @@ +package org.matsim.run; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.population.*; +import org.matsim.application.MATSimApplication; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.population.PersonUtils; +import org.matsim.core.population.PopulationUtils; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.dashboards.LausitzSimWrapperRunner; +import org.matsim.simwrapper.SimWrapperConfigGroup; +import org.matsim.testcases.MatsimTestUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.matsim.application.ApplicationUtils.globFile; + +class EmissionAndNoiseAnalysisOutputTest { + + @RegisterExtension + public MatsimTestUtils utils = new MatsimTestUtils(); + + @TempDir + public Path p; + + private final static Id carPersonId = Id.createPersonId("Hoyerswerda-Cottbus_CAR"); + + private static Config config = ConfigUtils.loadConfig(String.format("input/v%s/lausitz-v%s-10pct.config.xml", LausitzScenario.VERSION, LausitzScenario.VERSION)); + + @Disabled("Test is used to secure functionality of emission analysis. As the analysis needs" + + "a lot of RAM, it is disabled and only run manually. -sme0924") + @Test + void runEmissionAnalysisOutputTest() throws IOException { +// Here, we do want dashboards in general (emission dashboard), but do not want to waste computation time on the standard dashboards + ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class).exclude = Set.of("TripDashboard", "OverviewDashboard", "StuckAgentDashboard", + "TrafficCountsDashboard", "TrafficDashboard", "PublicTransitDashboard"); + + Path inputPath = p.resolve("test-population.xml.gz"); + createTestPopulation(inputPath); + + assert MATSimApplication.execute(LausitzScenario.class, config, + "--1pct", + "--iterations", "0", + "--output", utils.getOutputDirectory(), + "--config:plans.inputPlansFile", inputPath.toString(), + "--config:controller.overwriteFiles=deleteDirectoryIfExists") == 0 : "Must return non error code"; + + + Path csvPath = globFile(Path.of(utils.getOutputDirectory() + "/analysis/emissions"), "*emissions_per_link.csv*"); + + Map nonZeroLinks = new HashMap<>(); + + try { + BufferedReader reader = IOUtils.getBufferedReader(csvPath.toUri().toURL()); + String line; + +// skip header + reader.readLine(); + + while ((line = reader.readLine()) != null) { + String[] parts = line.split(","); + +// if first value (CO) is zero, all others are too + if (Double.parseDouble(parts[1]) == 0.) { + continue; + } + + Double[] values = new Double[23]; + + for (int i = 1; i < parts.length; i++) { + values[i - 1] = Double.parseDouble(parts[i]); + } + + nonZeroLinks.put(parts[0], values); + } + } finally { + + } + + Assertions.assertFalse(nonZeroLinks.isEmpty()); + Assertions.assertTrue(nonZeroLinks.containsKey("28922425#0")); + Assertions.assertTrue(nonZeroLinks.containsKey("-686055693#1")); +// CO + Assertions.assertEquals(830.9171, nonZeroLinks.get("28922425#0")[0], 0.0001); + Assertions.assertEquals(700.632, nonZeroLinks.get("-686055693#1")[0], 0.001); +// CO2_TOTAL + Assertions.assertEquals(8029.8788, nonZeroLinks.get("28922425#0")[1], 0.0001); + Assertions.assertEquals(6114.849, nonZeroLinks.get("-686055693#1")[1], 0.001); + } + + @Disabled("Test is used to secure functionality of noise analysis. As the analysis needs" + + "a lot of RAM, it is disabled and only run manually. -sme0924") + @Test + void runNoiseAnalysisOutputTest() throws IOException { +// Here, we do want dashboards in general (emission dashboard), but do not want to waste computation time on the standard dashboards + ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class).defaultDashboards = SimWrapperConfigGroup.Mode.disabled; + + Path inputPath = p.resolve("test-population.xml.gz"); + createTestPopulation(inputPath); + + assert MATSimApplication.execute(LausitzScenario.class, config, + "--1pct", + "--iterations", "0", + "--output", utils.getOutputDirectory(), + "--config:plans.inputPlansFile", inputPath.toString(), + "--config:controller.overwriteFiles=deleteDirectoryIfExists") == 0 : "Must return non error code"; + + new LausitzSimWrapperRunner().execute(utils.getOutputDirectory(), "--noise", "--shp", "../../../../../../../input/shp/lausitz.shp"); + + Path csvPath = globFile(Path.of(utils.getOutputDirectory() + "/analysis/noise-noise"), "*emission_per_day.csv*"); + + Map nonZeroLinks = new HashMap<>(); + + try { + BufferedReader reader = IOUtils.getBufferedReader(csvPath.toUri().toURL()); + String line; + +// skip header + reader.readLine(); + + while ((line = reader.readLine()) != null) { + String[] parts = line.split(","); + + if (Double.parseDouble(parts[1]) == 0.) { + continue; + } + + double value = Double.parseDouble(parts[1]); + + nonZeroLinks.put(parts[0], value); + } + } finally { + + } + + Assertions.assertFalse(nonZeroLinks.isEmpty()); + Assertions.assertTrue(nonZeroLinks.containsKey("28922425#0")); + Assertions.assertTrue(nonZeroLinks.containsKey("-686055693#1")); + Assertions.assertEquals(71.21, Math.round(nonZeroLinks.get("28922425#0") * 100.) / 100. , 0.001); + Assertions.assertEquals(69.72, Math.round(nonZeroLinks.get("-686055693#1") * 100.) / 100., 0.001); + } + + private void createTestPopulation(Path inputPath) { + Population population = PopulationUtils.createPopulation(config); + PopulationFactory fac = population.getFactory(); + Person person = fac.createPerson(carPersonId); + Plan plan = PopulationUtils.createPlan(person); + +// home in hoyerswerda, nearest link 28922425#0 + Activity home = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); + home.setEndTime(8 * 3600); + Activity home2 = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); + home2.setEndTime(19 * 3600); +// work in hoyerswerda, nearest link(s): 686055693#1, -686055693#1 + Activity work = fac.createActivityFromCoord("work_2400", new Coord(863866.47,5710961.86)); + work.setEndTime(17 * 3600 + 25 * 60); + + Leg leg = fac.createLeg(TransportMode.car); + + plan.addActivity(home); + plan.addLeg(leg); + plan.addActivity(work); + plan.addLeg(leg); + plan.addActivity(home2); + + person.addPlan(plan); + PersonUtils.setIncome(person, 1000.); + person.getAttributes().putAttribute("subpopulation", "person"); + population.addPerson(person); + + new PopulationWriter(population).write(inputPath.toString()); + } +} diff --git a/src/test/java/org/matsim/run/RunIntegrationTest.java b/src/test/java/org/matsim/run/RunIntegrationTest.java index 30e2941..085cfaa 100644 --- a/src/test/java/org/matsim/run/RunIntegrationTest.java +++ b/src/test/java/org/matsim/run/RunIntegrationTest.java @@ -1,6 +1,5 @@ package org.matsim.run; -import com.univocity.parsers.common.input.EOFException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -13,9 +12,7 @@ import org.matsim.api.core.v01.events.handler.PersonEntersVehicleEventHandler; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.*; -import org.matsim.application.ApplicationUtils; import org.matsim.application.MATSimApplication; -import org.matsim.application.options.CsvOptions; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; @@ -27,12 +24,8 @@ import org.matsim.pt.transitSchedule.api.*; import org.matsim.simwrapper.SimWrapperConfigGroup; import org.matsim.testcases.MatsimTestUtils; -import tech.tablesaw.api.ColumnType; -import tech.tablesaw.api.DoubleColumn; -import tech.tablesaw.api.Table; -import tech.tablesaw.io.csv.CsvReadOptions; -import java.io.IOException; +import java.io.File; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -59,7 +52,11 @@ void runScenario() { "--iterations", "1", "--config:plans.inputPlansFile", "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/lausitz/input/v1.1/lausitz-v1.1-1pct.plans-initial.xml.gz", "--output", utils.getOutputDirectory(), - "--config:controller.overwriteFiles=deleteDirectoryIfExists") == 0 : "Must return non error code"; + "--config:controller.overwriteFiles=deleteDirectoryIfExists", "--emissions", "DO_NOT_PERFORM_EMISSIONS_ANALYSIS") + == 0 : "Must return non error code"; + + Assertions.assertTrue(new File(utils.getOutputDirectory()).isDirectory()); + Assertions.assertTrue(new File(utils.getOutputDirectory()).exists()); } @Test @@ -67,35 +64,16 @@ void runScenarioIncludingDrt() { Config config = ConfigUtils.loadConfig(String.format("input/v%s/lausitz-v%s-10pct.config.xml", LausitzScenario.VERSION, LausitzScenario.VERSION)); ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class).defaultDashboards = SimWrapperConfigGroup.Mode.disabled; - Path inputPath = p.resolve("drt-test-population.xml.gz"); - - createTestPopulation(config, inputPath, TransportMode.drt, new Coord(838300.95,5711890.36)); - assert MATSimApplication.execute(RunLausitzDrtScenario.class, config, "--1pct", "--iterations", "1", - "--config:plans.inputPlansFile", inputPath.toString(), + "--config:plans.inputPlansFile", "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/lausitz/input/v1.1/lausitz-v1.1-1pct.plans-initial.xml.gz", "--output", utils.getOutputDirectory(), - "--config:controller.overwriteFiles=deleteDirectoryIfExists") == 0 : "Must return non error code"; - - //read customer stats - Path customerStatsPath = ApplicationUtils.matchInput("drt_customer_stats_" + TransportMode.drt + ".csv", Path.of(utils.getOutputDirectory())); - try { - Table customerStats = Table.read() - .csv(CsvReadOptions.builder(customerStatsPath.toFile()) - .columnTypes(columnHeader -> columnHeader.equals("runId") ? ColumnType.STRING : ColumnType.DOUBLE) - .separator(CsvOptions.detectDelimiter(customerStatsPath.toString())) - .build()); - - Assertions.assertEquals(2, customerStats.rowCount()); - - DoubleColumn rideCol = customerStats.doubleColumn("rides"); + "--config:controller.overwriteFiles=deleteDirectoryIfExists", "--emissions", "DO_NOT_PERFORM_EMISSIONS_ANALYSIS") + == 0 : "Must return non error code"; -// there should be exactly 2 drt rides - Assertions.assertEquals(2, rideCol.get(rideCol.size() - 1)); - } catch (IOException e) { - throw new EOFException(); - } + Assertions.assertTrue(new File(utils.getOutputDirectory()).isDirectory()); + Assertions.assertTrue(new File(utils.getOutputDirectory()).exists()); } @Test @@ -105,14 +83,45 @@ void runScenarioIncludingAdditionalPtLine() { Path inputPath = p.resolve("pt-test-population.xml.gz"); - createTestPopulation(config, inputPath, TransportMode.pt, new Coord(867489.48,5746587.47)); + Population population = PopulationUtils.createPopulation(config); + PopulationFactory fac = population.getFactory(); + Person person = fac.createPerson(ptPersonId); + Plan plan = PopulationUtils.createPlan(person); + +// home in hoyerswerda + Activity home = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); + home.setEndTime(8 * 3600); + Activity home2 = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); + home2.setEndTime(19 * 3600); +// work in cottbus + Activity work = fac.createActivityFromCoord("work_2400", new Coord(867489.48,5746587.47)); + work.setEndTime(17 * 3600 + 25 * 60); + + Leg leg = fac.createLeg(TransportMode.pt); + + plan.addActivity(home); + plan.addLeg(leg); + plan.addActivity(work); + plan.addLeg(leg); + plan.addActivity(home2); + + person.addPlan(plan); + PersonUtils.setIncome(person, 1000.); + person.getAttributes().putAttribute("subpopulation", "person"); + population.addPerson(person); + + new PopulationWriter(population).write(inputPath.toString()); assert MATSimApplication.execute(RunLausitzPtScenario.class, config, "--1pct", "--iterations", "1", "--output", utils.getOutputDirectory(), "--config:plans.inputPlansFile", inputPath.toString(), - "--config:controller.overwriteFiles=deleteDirectoryIfExists") == 0 : "Must return non error code"; + "--config:controller.overwriteFiles=deleteDirectoryIfExists", "--emissions", "DO_NOT_PERFORM_EMISSIONS_ANALYSIS") + == 0 : "Must return non error code"; + + Assertions.assertTrue(new File(utils.getOutputDirectory()).isDirectory()); + Assertions.assertTrue(new File(utils.getOutputDirectory()).exists()); Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig()); @@ -140,37 +149,6 @@ void runScenarioIncludingAdditionalPtLine() { } - private static void createTestPopulation(Config config, Path inputPath, String mode, Coord workLocation) { - Population population = PopulationUtils.createPopulation(config); - PopulationFactory fac = population.getFactory(); - Person person = fac.createPerson(ptPersonId); - Plan plan = PopulationUtils.createPlan(person); - -// home in hoyerswerda - Activity home = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); - home.setEndTime(8 * 3600); - Activity home2 = fac.createActivityFromCoord("home_2400", new Coord(863538.13,5711028.24)); - home2.setEndTime(19 * 3600); -// work in given location - Activity work = fac.createActivityFromCoord("work_2400", workLocation); - work.setEndTime(17 * 3600 + 25 * 60); - - Leg leg = fac.createLeg(mode); - - plan.addActivity(home); - plan.addLeg(leg); - plan.addActivity(work); - plan.addLeg(leg); - plan.addActivity(home2); - - person.addPlan(plan); - PersonUtils.setIncome(person, 1000.); - person.getAttributes().putAttribute("subpopulation", "person"); - population.addPerson(person); - - new PopulationWriter(population).write(inputPath.toString()); - } - private static final class PersonEntersPtVehicleEventHandler implements PersonEntersVehicleEventHandler { static List enterEvents = new ArrayList<>();