diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java index 83e96c5694d..dcea329962a 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java @@ -10,6 +10,35 @@ public class HBSMotorwayCapacity implements Predictor { @Override public double predict(Object2DoubleMap features, Object2ObjectMap categories) { - return 0; + + // speed in km/h + int speed = (int) Math.round(features.getDouble("speed") * 3.6); + int lanes = (int) features.getOrDefault("lanes", 1); + + // Capacity for 1 lane motorways is not defined in HBS + double capacity = 2000; + if (lanes == 2) { + if (speed >= 130) + capacity = 3700.0; + else if (speed >= 120) + capacity = 3800; + else + capacity = 3750; + } else if (lanes == 3) { + if (speed >= 130) + capacity = 5300; + else if (speed >= 120) + capacity = 5400; + else + capacity = 5350; + } else if (lanes >= 4) { + if (speed >= 130) + capacity = 7300; + else + capacity = 7400; + } + + // Return capacity per lane + return capacity / lanes; } } diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java index 8d3e01ad4b7..faff8379a3c 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java @@ -11,7 +11,6 @@ public class HBSNetworkParams implements NetworkModel { private static final Predictor MOTORWAY = new HBSMotorwayCapacity(); private static final Predictor ROAD = new HBSRoadCapacity(); - private static final Predictor SIDEROAD = new HBSSideRoadCapacity(); @Override public Predictor capacity(String junctionType, String highwayType) { @@ -23,12 +22,10 @@ public Predictor capacity(String junctionType, String highwayType) { if (highwayType.startsWith("motorway")) { return MOTORWAY; - } else if (highwayType.startsWith("trunk") || highwayType.startsWith("primary") || highwayType.startsWith("secondary")) { - return ROAD; } // All lower category roads - return SIDEROAD; + return ROAD; } } diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java index 614d9200f99..d91e85f9fbb 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java @@ -8,8 +8,87 @@ * Capacity for general roads, that are not motorways or residential roads. */ public class HBSRoadCapacity implements Predictor { + /** + * Capacity on primary roads (Landstraße) + */ + private static double capacityHighway(int lanes) { + + // There is currently no way to differentiate Landstraße and Stadtstraße which can both have up to 70km/h + + if (lanes == 1) + return 2846.990116534646; + + if (lanes == 2) + return 3913.3439999999996 / 2; + + // Own assumption of increasing capacity with more lanes + // This is not covered by the HBS and is a very rare case + return (3913.3439999999996) * 1.3 / lanes; + } + + /** + * Capacity on a side road merging into a main road at a junction. + * + * @param qP the vehicle volume of the main road + */ + private static double capacityMerging(double qP) { + + // 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; + + + return Math.exp((-qP / 3600) * (tg - tf / 2)) * 3600 / tf; + } + + /** + * Capacity of a side road of type merging into a main road. + * + * @param roadType type of the target road + * @param mainType type of the higher priority road + */ + private static double capacityMerging(String roadType, String mainType) { + + if (mainType.equals("primary")) + return capacityMerging(600); + + return capacityMerging(400); + } + @Override public double predict(Object2DoubleMap features, Object2ObjectMap categories) { - return 0; + + // Speed in km/h + int speed = (int) Math.round(features.getDouble("speed") * 3.6); + int lanes = (int) features.getOrDefault("lanes", 1); + + if (speed >= 70) { + return capacityHighway(lanes); + } + + String merging = categories.get("is_merging_into"); + + // Only merging with a single lane road is considered + if (!merging.isEmpty() && lanes == 1) { + return capacityMerging(categories.get("highway_type"), merging); + } + + // Capacity for city roads + if (speed >= 40 || lanes >= 2) { + return switch (lanes) { + case 1 -> 1139.0625; + case 2 -> 2263.438914027149 / 2; + // Own assumption, rare edge-case + default -> 2263.438914027149 * 1.3 / lanes; + }; + } + + // Remaining are residential which are assumed to have high urbanisation + return 800.0/lanes; } + } diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSSideRoadCapacity.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSSideRoadCapacity.java deleted file mode 100644 index aa9db16ca78..00000000000 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSSideRoadCapacity.java +++ /dev/null @@ -1,39 +0,0 @@ -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 features, Object2ObjectMap categories) { - - return 0; - - - } - - /** - * Capacity of a side road merging into a main road. - */ - public double capacityMerging(Object2DoubleMap 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; - } - -} diff --git a/contribs/application/src/main/python/capacity/hbs.py b/contribs/application/src/main/python/capacity/hbs.py index bc7a3377f1a..a842e1cbe8c 100644 --- a/contribs/application/src/main/python/capacity/hbs.py +++ b/contribs/application/src/main/python/capacity/hbs.py @@ -29,7 +29,12 @@ def calc_capacity_stadtstrasse(street): if street.lanes == 1: f = 1.0 - if street.speed == 50: + # Assume middle-high "Erschließungsintensität" + if street.speed == 30: + k = 45 + a = 38 + b = 0.715 + elif street.speed == 50: k = 45 a = 54 b = 0.850 @@ -106,7 +111,7 @@ def calc_capacity_autobahn(street): for lanes in range(1, 5): capacities[osm_type][lanes] = {} for speed in speeds: - if (osm_type == 'residential' and speed > 70) or (osm_type == 'residential' and speed < 50) or (osm_type == 'residential' and lanes > 2): + if (osm_type == 'residential' and speed > 70) or (osm_type == 'residential' and speed < 30) or (osm_type == 'residential' and lanes > 2): continue if (osm_type == 'unclassified' and speed > 100) or (osm_type == 'unclassified' and speed <= 50) or (osm_type == 'unclassified' and lanes > 2): continue @@ -166,7 +171,7 @@ def calc_capacity_autobahn(street): capacity_compare.append(capacity_estimate(v)) fig.add_trace(go.Scatter(x=speeds_compare, y=capacity_compare, mode='lines', showlegend=True, name="REFERENZ")) -for qp in (0, 100, 400, 800): +for qp in (0, 200, 400, 600): kontenpunkt_y = [] kontenpunkt_y.append(merge(qp)) kontenpunkt_y.append(merge(qp)) diff --git a/contribs/application/src/test/java/org/matsim/application/prepare/network/params/HBSNetworkParamsTest.java b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/HBSNetworkParamsTest.java new file mode 100644 index 00000000000..ab6172db208 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/HBSNetworkParamsTest.java @@ -0,0 +1,44 @@ +package org.matsim.application.prepare.network.params; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.contrib.sumo.SumoNetworkConverter; +import org.matsim.testcases.MatsimTestUtils; + +import java.nio.file.Path; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class HBSNetworkParamsTest { + + @RegisterExtension + MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + void apply() throws Exception { + + Path networkPath = Path.of(utils.getPackageInputDirectory()).resolve("osm.net.xml"); + + Path output = Path.of(utils.getOutputDirectory()); + + SumoNetworkConverter converter = SumoNetworkConverter.newInstance(List.of(networkPath), + output.resolve("network.xml"), + "EPSG:4326", "EPSG:4326"); + + converter.call(); + + assertThat(output.resolve("network.xml")).exists(); + assertThat(output.resolve("network-ft.csv")).exists(); + + new ApplyNetworkParams().execute( + "capacity", + "--network", output.resolve("network.xml").toString(), + "--input-features", output.resolve("network-ft.csv").toString(), + "--output", output.resolve("network-hbs.xml").toString(), + "--model", "org.matsim.application.prepare.network.params.hbs.HBSNetworkParams" + ); + + assertThat(output.resolve("network-hbs.xml")).exists(); + } +}