Skip to content

Commit

Permalink
prepare capacity models, use the new predictor interface
Browse files Browse the repository at this point in the history
  • Loading branch information
rakow committed May 9, 2024
1 parent fa11208 commit 51ebdfa
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 61 deletions.
Expand Up @@ -14,6 +14,7 @@
import org.matsim.application.MATSimAppCommand;
import org.matsim.application.options.InputOptions;
import org.matsim.application.options.OutputOptions;
import org.matsim.application.prepare.Predictor;
import org.matsim.application.prepare.network.params.NetworkParamsOpt.Feature;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.utils.io.IOUtils;
Expand Down Expand Up @@ -74,12 +75,12 @@ public static void main(String[] args) {
/**
* Theoretical capacity.
*/
private static double capacityEstimate(double v) {
public static double capacityEstimate(double v) {

// headway
double tT = 1.2;

// car length
// car length + buffer
double lL = 7.0;

double Qc = v / (v * tT + lL);
Expand Down Expand Up @@ -117,9 +118,7 @@ public Integer call() throws Exception {

try {
applyChanges(link, ft);
} catch (UnsupportedOperationException u) {
// This will be ignored silently and should only be thrown if certain changes should not be applied
} catch (IllegalArgumentException e) {
} catch (IllegalArgumentException e) {
warn++;
log.warn("Error processing link {}", link.getId(), e);
}
Expand All @@ -141,26 +140,27 @@ private void applyChanges(Link link, Feature ft) {

if (params.contains(NetworkAttribute.capacity)) {

FeatureRegressor capacity = model.capacity(ft.junctionType(), ft.highwayType());
Predictor capacity = model.capacity(ft.junctionType(), ft.highwayType());
// No operation performed if not supported
if (capacity == null) {
throw new UnsupportedOperationException("Capacity model not available for " + ft.junctionType());
return;
}

double perLane = capacity.predict(ft.features());
double perLane = capacity.predict(ft.features(), ft.categories());

double cap = capacityEstimate(ft.features().getDouble("speed"));

if (perLane < cap * speedFactorBounds[0]) {
if (perLane < cap * capacityBounds[0]) {
log.warn("Increasing capacity per lane on {} ({}, {}) from {} to {}",
link.getId(), ft.highwayType(), ft.junctionType(), perLane, cap * speedFactorBounds[0]);
perLane = cap * speedFactorBounds[0];
link.getId(), ft.highwayType(), ft.junctionType(), perLane, cap * capacityBounds[0]);
perLane = cap * capacityBounds[0];
modified = true;
}

if (perLane > cap * speedFactorBounds[1]) {
if (perLane > cap * capacityBounds[1]) {
log.warn("Reducing capacity per lane on {} ({}, {}) from {} to {}",
link.getId(), ft.highwayType(), ft.junctionType(), perLane, cap * speedFactorBounds[1]);
perLane = cap * speedFactorBounds[1];
link.getId(), ft.highwayType(), ft.junctionType(), perLane, cap * capacityBounds[1]);
perLane = cap * capacityBounds[1];
modified = true;
}

Expand All @@ -170,16 +170,16 @@ private void applyChanges(Link link, Feature ft) {

if (params.contains(NetworkAttribute.freespeed)) {

double speedFactor = 1.0;
FeatureRegressor speedModel = model.speedFactor(ft.junctionType(), ft.highwayType());
Predictor speedModel = model.speedFactor(ft.junctionType(), ft.highwayType());

// No operation performed if not supported
if (speedModel == null) {
throw new UnsupportedOperationException("Speed model not available for " + ft.junctionType());
return;
}

speedFactor = paramsOpt != null ?
speedModel.predict(ft.features(), paramsOpt.getParams(ft.junctionType())) :
speedModel.predict(ft.features());
double speedFactor = paramsOpt != null ?
speedModel.predict(ft.features(), ft.categories(), paramsOpt.getParams(ft.junctionType())) :
speedModel.predict(ft.features(), ft.categories());

if (speedFactor > speedFactorBounds[1]) {
log.warn("Reducing speed factor on {} from {} to {}", link.getId(), speedFactor, speedFactorBounds[1]);
Expand Down
Expand Up @@ -18,6 +18,7 @@
import org.matsim.application.analysis.traffic.traveltime.SampleValidationRoutes;
import org.matsim.application.options.InputOptions;
import org.matsim.application.options.OutputOptions;
import org.matsim.application.prepare.Predictor;
import org.matsim.contrib.osm.networkReader.LinkProperties;
import org.matsim.core.network.NetworkUtils;
import picocli.CommandLine;
Expand Down Expand Up @@ -104,7 +105,7 @@ static Result applyAndEvaluateParams(
continue;
}

FeatureRegressor speedModel = model.speedFactor(ft.junctionType(), ft.highwayType());
Predictor speedModel = model.speedFactor(ft.junctionType(), ft.highwayType());

if (speedModel == null) {
link.setFreespeed(allowedSpeed);
Expand All @@ -115,15 +116,15 @@ static Result applyAndEvaluateParams(

if (request.hasParams()) {
double[] p = request.getParams(ft.junctionType());
speedFactor = speedModel.predict(ft.features(), p);
speedFactor = speedModel.predict(ft.features(), ft.categories(), p);
} else
speedFactor = speedModel.predict(ft.features());
speedFactor = speedModel.predict(ft.features(), ft.categories());

// apply lower and upper bound
speedFactor = Math.max(speedFactorBounds[0], speedFactor);
speedFactor = Math.min(speedFactorBounds[1], speedFactor);

attributes.put(link.getId(), speedModel.getData(ft.features()));
attributes.put(link.getId(), speedModel.getData(ft.features(), ft.categories()));

link.setFreespeed(allowedSpeed * speedFactor);
link.getAttributes().putAttribute("speed_factor", speedFactor);
Expand Down
@@ -1,11 +1,15 @@
package org.matsim.application.prepare.network.params;

import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import org.matsim.application.prepare.Predictor;

/**
* Predictor interface for regression.
* @deprecated Use {@link Predictor} instead.
*/
public interface FeatureRegressor {
@Deprecated
public interface FeatureRegressor extends Predictor {


/**
Expand All @@ -28,4 +32,22 @@ default double[] getData(Object2DoubleMap<String> ft) {
throw new UnsupportedOperationException("Not implemented");
}

default double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
return predict(features);
}

/**
* Predict values with adjusted model params.
*/
default double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories, double[] params) {
return predict(features, params);
}

/**
* Return data that is used for internal prediction function (normalization already applied).
*/
default double[] getData(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
return getData(features);
}

}
@@ -1,22 +1,24 @@
package org.matsim.application.prepare.network.params;

import org.matsim.application.prepare.Predictor;

/**
* A model for estimating network parameters.
*/
public interface NetworkModel {

/**
* Flow Capacity (per lane)
* Flow Capacity (per lane).
*/
default FeatureRegressor capacity(String junctionType, String highwayType) {
return null;
default Predictor capacity(String junctionType, String highwayType) {
throw new UnsupportedOperationException("Capacity model not implemented for class: " + getClass().getName());
}

/**
* Speed factor (relative to free flow speed).
* Speed factor (relative to allowed speed).
*/
default FeatureRegressor speedFactor(String junctionType, String highwayType) {
return null;
default Predictor speedFactor(String junctionType, String highwayType) {
throw new UnsupportedOperationException("Speed factor model not implemented for class: " + getClass().getName());
}

}
Expand Up @@ -4,6 +4,8 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
Expand Down Expand Up @@ -74,6 +76,7 @@ static Map<Id<Link>, Feature> readFeatures(String input, Map<Id<Link>, ? extends

Object2DoubleOpenHashMap<String> ft = new Object2DoubleOpenHashMap<>();
ft.defaultReturnValue(Double.NaN);
Object2ObjectMap<String, String> categories = new Object2ObjectOpenHashMap<>();

for (String column : header) {
String v = row.get(column);
Expand All @@ -82,13 +85,14 @@ static Map<Id<Link>, Feature> readFeatures(String input, Map<Id<Link>, ? extends
} catch (NumberFormatException e) {
// every not equal to True will be false
ft.put(column, Boolean.parseBoolean(v) ? 1 : 0);
categories.put(column, v);
}
}

String highwayType = header.contains(NetworkUtils.TYPE) ? row.get(NetworkUtils.TYPE) :
(link != null ? NetworkUtils.getHighwayType(link) : null);

features.put(id, new Feature(row.get("junction_type"), highwayType, ft));
features.put(id, new Feature(row.get("junction_type").intern(), highwayType, ft, categories));
}
}

Expand Down Expand Up @@ -177,7 +181,7 @@ static Result evaluate(Network network, Object2DoubleMap<SampleValidationRoutes.
return new Result(rmse.getMean(), mse.getMean(), data);
}

record Feature(String junctionType, String highwayType, Object2DoubleMap<String> features) {
record Feature(String junctionType, String highwayType, Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
}

record Result(double rmse, double mae, Map<String, List<Data>> data) {
Expand Down
@@ -0,0 +1,15 @@
package org.matsim.application.prepare.network.params.hbs;

import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import org.matsim.application.prepare.Predictor;

/**
* Capacity for motorways.
*/
public class HBSMotorwayCapacity implements Predictor {
@Override
public double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
return 0;
}
}
@@ -1,6 +1,6 @@
package org.matsim.application.prepare.network.params.hbs;

import org.matsim.application.prepare.network.params.FeatureRegressor;
import org.matsim.application.prepare.Predictor;
import org.matsim.application.prepare.network.params.NetworkModel;

/**
Expand All @@ -9,20 +9,26 @@
*/
public class HBSNetworkParams implements NetworkModel {

private static final FeatureRegressor MOTORWAY = new HSBMotorwayCapacity();
private static final FeatureRegressor ROAD = new HBSRoadCapacity();
private static final Predictor MOTORWAY = new HBSMotorwayCapacity();
private static final Predictor ROAD = new HBSRoadCapacity();
private static final Predictor SIDEROAD = new HBSSideRoadCapacity();

@Override
public FeatureRegressor capacity(String junctionType, String highwayType) {
public Predictor capacity(String junctionType, String highwayType) {

// Traffic lights are not considered
if (junctionType.equals("traffic_light")) {
return null;
}

if (highwayType.startsWith("motorway")) {
return MOTORWAY;
} else if (junctionType.startsWith("priority")) {
// All other roads
} else if (highwayType.startsWith("trunk") || highwayType.startsWith("primary") || highwayType.startsWith("secondary")) {
return ROAD;
}

throw new UnsupportedOperationException("Unknown type: " + junctionType);
// All lower category roads
return SIDEROAD;
}

}
@@ -1,16 +1,15 @@
package org.matsim.application.prepare.network.params.hbs;

import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import org.matsim.application.prepare.network.params.FeatureRegressor;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import org.matsim.application.prepare.Predictor;

/**
* See {@link HBSNetworkParams}.
* Capacity for general roads, that are not motorways or residential roads.
*/
class HBSRoadCapacity implements FeatureRegressor {
public class HBSRoadCapacity implements Predictor {
@Override
public double predict(Object2DoubleMap<String> ft) {

public double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
return 0;

}
}
@@ -0,0 +1,39 @@
package org.matsim.application.prepare.network.params.hbs;

import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import org.matsim.application.prepare.Predictor;

/**
* Road capacity for residential roads or other of similar or lower category.
*/
public class HBSSideRoadCapacity implements Predictor {
@Override
public double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {

return 0;


}

/**
* Capacity of a side road merging into a main road.
*/
public double capacityMerging(Object2DoubleMap<String> features) {

// See HBS page 5-20, eq. S5-12 table S5-5

// mean Folgezeitlücken of the different combinations
double tf = 3.5;

// mean Grenzzeitlücke
double tg = 6.36;

// traffic volume on the main road
// here an assumption needs to be made, normally the capacity of the side roads would not be constant
double qP = 400;

return Math.exp( (-qP/3600) * (tg - tf/2)) * 3600/tf;
}

}

This file was deleted.

0 comments on commit 51ebdfa

Please sign in to comment.