package org.opentrafficsim.demo.cacc; import java.awt.Color; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.djunits.unit.DurationUnit; import org.djunits.unit.FrequencyUnit; import org.djunits.unit.SpeedUnit; import org.djunits.unit.TimeUnit; import org.djunits.value.ValueRuntimeException; import org.djunits.value.storage.StorageType; import org.djunits.value.vdouble.scalar.Acceleration; import org.djunits.value.vdouble.scalar.Direction; import org.djunits.value.vdouble.scalar.Duration; import org.djunits.value.vdouble.scalar.Length; import org.djunits.value.vdouble.scalar.Speed; import org.djunits.value.vdouble.scalar.Time; import org.djunits.value.vdouble.vector.FrequencyVector; import org.djunits.value.vdouble.vector.TimeVector; import org.djunits.value.vdouble.vector.base.DoubleVector; import org.djunits.value.vfloat.scalar.FloatDuration; import org.djutils.cli.Checkable; import org.djutils.cli.CliException; import org.djutils.cli.CliUtil; import org.djutils.event.EventInterface; import org.djutils.event.EventListenerInterface; import org.opentrafficsim.base.compressedfiles.CompressionType; import org.opentrafficsim.base.compressedfiles.Writer; import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer; import org.opentrafficsim.core.animation.gtu.colorer.IDGTUColorer; import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer; import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer; import org.opentrafficsim.core.distributions.ConstantGenerator; import org.opentrafficsim.core.dsol.OTSSimulatorInterface; import org.opentrafficsim.core.geometry.Bezier; import org.opentrafficsim.core.geometry.OTSLine3D; import org.opentrafficsim.core.geometry.OTSPoint3D; import org.opentrafficsim.core.gtu.GTUDirectionality; import org.opentrafficsim.core.gtu.GTUException; import org.opentrafficsim.core.gtu.GTUType; import org.opentrafficsim.core.gtu.TemplateGTUType; import org.opentrafficsim.core.network.Link; import org.opentrafficsim.core.network.LinkType; import org.opentrafficsim.core.network.NetworkException; import org.opentrafficsim.core.network.Node; import org.opentrafficsim.core.network.OTSNode; import org.opentrafficsim.core.parameters.ParameterFactory; import org.opentrafficsim.core.parameters.ParameterFactoryByType; import org.opentrafficsim.draw.factory.DefaultAnimationFactory; import org.opentrafficsim.draw.graphs.ContourDataSource; import org.opentrafficsim.draw.graphs.ContourPlotSpeed; import org.opentrafficsim.draw.graphs.GraphPath; import org.opentrafficsim.draw.graphs.road.GraphLaneUtil; import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality; import org.opentrafficsim.kpi.sampling.KpiLaneDirection; import org.opentrafficsim.kpi.sampling.SamplingException; import org.opentrafficsim.kpi.sampling.SpaceTimeRegion; import org.opentrafficsim.kpi.sampling.Trajectory; import org.opentrafficsim.kpi.sampling.TrajectoryGroup; import org.opentrafficsim.kpi.sampling.meta.FilterDataGtuType; import org.opentrafficsim.road.gtu.colorer.DesiredSpeedColorer; import org.opentrafficsim.road.gtu.colorer.FixedColor; import org.opentrafficsim.road.gtu.colorer.GTUTypeColorer; import org.opentrafficsim.road.gtu.colorer.SplitColorer; import org.opentrafficsim.road.gtu.colorer.SynchronizationColorer; import org.opentrafficsim.road.gtu.generator.LaneBasedGTUGenerator; import org.opentrafficsim.road.gtu.generator.Platoons; import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD; import org.opentrafficsim.road.gtu.generator.od.ODApplier; import org.opentrafficsim.road.gtu.generator.od.ODApplier.GeneratorObjects; import org.opentrafficsim.road.gtu.generator.od.ODOptions; import org.opentrafficsim.road.gtu.generator.od.StrategicalPlannerFactorySupplierOD; import org.opentrafficsim.road.gtu.lane.LaneBasedGTU; import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccController; import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccControllerFactory; import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccParameters; import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccTacticalPlanner; import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccTacticalPlannerFactory; import org.opentrafficsim.road.gtu.lane.tactical.cacc.LongitudinalControllerFactory; import org.opentrafficsim.road.gtu.lane.tactical.cacc.Platoon; import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusFactory; import org.opentrafficsim.road.gtu.lane.tactical.lmrs.DefaultLMRSPerceptionFactory; import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRSFactory; import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory; import org.opentrafficsim.road.gtu.strategical.od.Categorization; import org.opentrafficsim.road.gtu.strategical.od.Category; import org.opentrafficsim.road.gtu.strategical.od.Interpolation; import org.opentrafficsim.road.gtu.strategical.od.ODMatrix; import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory; import org.opentrafficsim.road.network.OTSRoadNetwork; import org.opentrafficsim.road.network.lane.CrossSectionLink; import org.opentrafficsim.road.network.lane.Lane; import org.opentrafficsim.road.network.lane.LaneDirection; import org.opentrafficsim.road.network.lane.LaneType; import org.opentrafficsim.road.network.lane.OTSRoadNode; import org.opentrafficsim.road.network.lane.Stripe; import org.opentrafficsim.road.network.lane.Stripe.Permeable; import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy; import org.opentrafficsim.road.network.lane.object.sensor.Detector; import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor; import org.opentrafficsim.road.network.sampling.LaneData; import org.opentrafficsim.road.network.sampling.RoadSampler; import org.opentrafficsim.road.network.sampling.data.TimeToCollision; import org.opentrafficsim.swing.graphs.SwingContourPlot; import org.opentrafficsim.swing.gui.OTSSimulationApplication; import org.opentrafficsim.swing.script.AbstractSimulationScript; import nl.tudelft.simulation.dsol.swing.gui.TablePanel; import nl.tudelft.simulation.jstats.streams.StreamInterface; import picocli.CommandLine; import picocli.CommandLine.Option; /** *

* Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 17 okt. 2018
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel */ public class CaccSimulation extends AbstractSimulationScript implements Checkable { /** ... */ private static final long serialVersionUID = 1L; /** Sampler for statistics. */ private RoadSampler sampler; /** Time to collision data type. */ private final TimeToCollision ttc = new TimeToCollision(); /** Meta GTU type. */ private final FilterDataGtuType metaGtu = new FilterDataGtuType(); /** The CACC controller. */ private CaccController caccController; /** The GTU type with CACC. */ private GTUType caccGTUType; /** Space time regions to sample traffic on. */ private final List regions = new ArrayList<>(); /** List of lane for the graphs. */ private List graphLanes = new ArrayList<>(); // Options /** Network name. */ @Option(names = "--nn", description = "Network name", defaultValue = "onramp") private String networkName; /** Intensity factor. */ @Option(names = "--intensity", description = "Intensity factor", defaultValue = "1.00") private double intensity; /** Penetration. */ @Option(names = "--penetration", description = "Fraction of vehicles equipped with CACC", defaultValue = "1.00") private double penetration; /** Platoon size. */ @Option(names = "--platoon", description = "Platoon size", defaultValue = "3") private int platoonSize; /** Headway. */ @Option(names = "--headway", description = "Platoon headway", defaultValue = "0.9") private double headway; /** Synchronization. */ @Option(names = "--synchronization", description = "Synchronization", defaultValue = "0.0") private double synchronization; /** Free flow speed of platoon vehicles. */ @Option(names = "--setspeed", description = "Free flow speed of platoon vehicles", defaultValue = "80 km/h") private Speed setspeed; /** Time step. */ @Option(names = "--sd", description = "Simulation time", defaultValue = "5400s") private Duration simulationDuration; /** Show graphs. */ @Option(names = "--graphs", description = "Show graphs", defaultValue = "true") private boolean graphs; /** Number of lanes. */ @Option(names = "--nol", description = "Number of lanes", defaultValue = "2") private int numberOfLanes; /** Controller. */ @Option(names = "--controller", description = "Controller", defaultValue = "CACC") private String controller; /** Output file for detections (?). */ @Option(names = "--outputFileDets", description = "Output file for detections", defaultValue = "C:\\\\Temp\\\\Journal-Platoon\\\\DetsDefault.csv") private String outputFileDets; /** Output file for trajectories (?). */ @Option(names = "--outputFileTraj", description = "Output file for trajectories", defaultValue = "C:\\\\Temp\\\\Journal-Platoon\\\\GenDefault") private String outputFileTraj; /** Output file for Generators (?). */ @Option(names = "--outputFileGen", description = "Output file for generators", defaultValue = "C:\\Temp\\Journal-Platoon\\GenDefault") private String outputFileGen; /** Equilibrium distance. */ @Option(names = "--startSpacing", description = "Gross equilibrium distance at 25m/s with s0=3m, l=12m and T=0.3s", defaultValue = "0.5") private double startSpacing; /** * @param properties x * @throws CliException x * @throws NoSuchFieldException x */ protected CaccSimulation(final String[] properties) throws NoSuchFieldException, CliException { super("Truck CACC", "Simulation of CACC trucks"); CommandLine cmd = new CommandLine(this); CliUtil.changeOptionDefault(AbstractSimulationScript.class, "seed", "5"); CliUtil.execute(cmd, properties); } /** * Main method that creates and starts a simulation. * @param args String[]; command line arguments * @throws Exception ... */ public static void main(final String[] args) throws Exception { new CaccSimulation(args).start(); } /** {@inheritDoc} */ @Override protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception { OTSRoadNetwork network = new OTSRoadNetwork(this.networkName, true, sim); GTUType carGTUType = network.getGtuType(GTUType.DEFAULTS.CAR); GTUType truckGTUType = network.getGtuType(GTUType.DEFAULTS.TRUCK); this.caccGTUType = new GTUType("CACC", truckGTUType); LinkType freewayLinkType = network.getLinkType(LinkType.DEFAULTS.FREEWAY); LaneType freewayLaneType = network.getLaneType(LaneType.DEFAULTS.FREEWAY); GTUType vehicle = network.getGtuType(GTUType.DEFAULTS.VEHICLE); this.caccController = new CaccController(); this.caccController.setCACCGTUType(this.caccGTUType); setGtuColorer( new SwitchableGTUColorer(0, new FixedColor(Color.BLUE, "Blue"), new GTUTypeColorer().add(carGTUType, Color.BLUE).add(truckGTUType, Color.RED).add(truckGTUType, Color.GREEN), new IDGTUColorer(), new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR)), new DesiredSpeedColorer(new Speed(50, SpeedUnit.KM_PER_HOUR), new Speed(150, SpeedUnit.KM_PER_HOUR)), new AccelerationGTUColorer(Acceleration.instantiateSI(-6.0), Acceleration.instantiateSI(2)), new SplitColorer(), new SynchronizationColorer())); // Factory for settable parameters ParameterFactoryByType parameters = new ParameterFactoryByType(); // Parameters parameters.addParameter(CaccParameters.T_SYSTEM_CACC, new Duration(this.headway, DurationUnit.SI)); parameters.addParameter(CaccParameters.A_REDUCED, Acceleration.instantiateSI(this.synchronization)); parameters.addParameter(CaccParameters.SET_SPEED, this.setspeed); CaccControllerFactory longitudinalControllerFactory; String controllerName = this.controller; if (controllerName.equals("CACC")) { longitudinalControllerFactory = new CaccControllerFactory(); } else { throw new RuntimeException("Controller " + controllerName + " not supported."); } Length laneWidth = Length.instantiateSI(3.5); Length stripeWidth = Length.instantiateSI(0.2); Map odApplierOutput; String platoonDetector1; if (this.networkName.equals("onramp")) { // points OTSPoint3D pointA = new OTSPoint3D(0, 0); OTSPoint3D pointB = new OTSPoint3D(2000, 0); // 700 meters toerit OTSPoint3D pointC = new OTSPoint3D(2330, 0); // 330 meters onramp OTSPoint3D pointD = new OTSPoint3D(3330, 0); OTSPoint3D pointE = new OTSPoint3D(1300, -40); // nodes OTSRoadNode nodeA = new OTSRoadNode(network, "A", pointA, Direction.ZERO); OTSRoadNode nodeB = new OTSRoadNode(network, "B", pointB, Direction.ZERO); OTSRoadNode nodeC = new OTSRoadNode(network, "C", pointC, Direction.ZERO); OTSRoadNode nodeD = new OTSRoadNode(network, "D", pointD, Direction.ZERO); OTSRoadNode nodeE = new OTSRoadNode(network, "E", pointE, Direction.ZERO); // links CrossSectionLink linkAB = new CrossSectionLink(network, "AB", nodeA, nodeB, freewayLinkType, new OTSLine3D(pointA, pointB), LaneKeepingPolicy.KEEPRIGHT); CrossSectionLink linkBC = new CrossSectionLink(network, "BC", nodeB, nodeC, freewayLinkType, new OTSLine3D(pointB, pointC), LaneKeepingPolicy.KEEPRIGHT); CrossSectionLink linkCD = new CrossSectionLink(network, "CD", nodeC, nodeD, freewayLinkType, new OTSLine3D(pointC, pointD), LaneKeepingPolicy.KEEPRIGHT); CrossSectionLink linkEB = new CrossSectionLink(network, "EB", nodeE, nodeB, freewayLinkType, Bezier.cubic(nodeE.getLocation(), nodeB.getLocation()), LaneKeepingPolicy.KEEPRIGHT); // lanes and stripes int n = this.numberOfLanes; // List originLanes = new ArrayList<>(); for (int i = 0; i < n; i++) { for (CrossSectionLink link : new CrossSectionLink[] {linkAB, linkBC, linkCD}) { Lane lane = new Lane(link, "Lane " + (i + 1), laneWidth.times((0.5 + i)), laneWidth, freewayLaneType, new Speed(100, SpeedUnit.KM_PER_HOUR)); Length offset = laneWidth.times(i + 1.0); Stripe stripe = new Stripe(link, offset, offset, stripeWidth); if (i < n - 1) { if (lane.getParentLink().getId().equals("BC")) { // stripe.addPermeability(GTUType.VEHICLE, Permeable.LEFT); stripe.addPermeability(vehicle, Permeable.BOTH); } else { stripe.addPermeability(vehicle, Permeable.BOTH); } } if (lane.getParentLink().getId().equals("BC")) { // new Detector(lane.getFullId(), lane, Length.createSI(150.0), sim); new Detector(lane.getFullId(), lane, Length.instantiateSI(330.0), sim); } // sink sensors if (lane.getParentLink().getId().equals("CD")) { new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(100.0)), GTUDirectionality.DIR_PLUS, sim); // detectors 100m after on ramp // new Detector(lane.getFullId(), lane, Length.createSI(0.0), sim); // id equal to lane, may be // different } if (lane.getParentLink().getId().equals("AB")) { // originLanes.add(lane); this.graphLanes.add(lane); } } } new Stripe(linkAB, Length.ZERO, Length.ZERO, stripeWidth); Stripe stripe = new Stripe(linkBC, Length.ZERO, Length.ZERO, stripeWidth); stripe.addPermeability(vehicle, Permeable.LEFT); new Stripe(linkCD, Length.ZERO, Length.ZERO, stripeWidth); new Lane(linkBC, "Acceleration lane", laneWidth.times(-0.5), laneWidth, freewayLaneType, new Speed(100, SpeedUnit.KM_PER_HOUR)); Lane onramp = new Lane(linkEB, "Onramp", laneWidth.times(-0.5), laneWidth, freewayLaneType, new Speed(100, SpeedUnit.KM_PER_HOUR)); new Stripe(linkEB, Length.ZERO, Length.ZERO, stripeWidth); new Stripe(linkEB, laneWidth.neg(), laneWidth.neg(), stripeWidth); new Stripe(linkBC, laneWidth.neg(), laneWidth.neg(), stripeWidth); // Detector on onramp // new Detector("Acceleration lane", accel, Length.createSI(200.0), sim); // OD without demand List origins = new ArrayList<>(); origins.add(nodeA); origins.add(nodeE); List destinations = new ArrayList<>(); destinations.add(nodeD); TimeVector timeVector = DoubleVector.instantiate(new double[] {0.0, 0.25, 0.50, 0.75, 1.0, 1.25, 1.50, 1.75, 2.0}, TimeUnit.BASE_HOUR, StorageType.DENSE); Interpolation interpolation = Interpolation.LINEAR; // or STEPWISE Categorization categorization = new Categorization("CACC", GTUType.class, Lane.class); // Category carCategory1 = new Category(categorization, carGTUType, originLanes.get(1)); // Category carCategory0 = new Category(categorization, carGTUType, originLanes.get(0)); Category carCategory1 = new Category(categorization, carGTUType, this.graphLanes.get(1)); Category carCategory0 = new Category(categorization, carGTUType, this.graphLanes.get(0)); Category carCategoryR = new Category(categorization, carGTUType, onramp); Category truCategory0 = new Category(categorization, truckGTUType, this.graphLanes.get(0)); Category truCategoryR = new Category(categorization, truckGTUType, onramp); Category caccCategory = new Category(categorization, caccGTUType, this.graphLanes.get(0)); ODMatrix odMatrix = new ODMatrix("CACC OD", origins, destinations, categorization, timeVector, interpolation); double intensityIncrease = this.intensity; double platoonPenetration = this.penetration; // Demand of trucks departing from main road20 List demandList = new ArrayList(); demandList.add((double) 0); demandList.add((double) 0); demandList.add(180 * (1 + intensityIncrease)); demandList.add(180 * (1 + intensityIncrease)); demandList.add(180 * (1 + intensityIncrease)); demandList.add(180 * (1 + intensityIncrease)); demandList.add(180 * (1 + intensityIncrease)); demandList.add(180 * (1 + intensityIncrease)); demandList.add((double) 0); demandList.add((double) 0); List demandPlatoon = new ArrayList(); // List for number of platoons (not number of platoon // vehicles!) List demandPlatoonVehicles = new ArrayList(); // Demand for platoon vehicles for (int k = 0; k < demandList.size(); k++) { double platoonVehicles = (demandList.get(k) * platoonPenetration); double platoonPlatoons = (Math.round(platoonVehicles / this.platoonSize)) / 4; // Actual generated platoons -> divide by 4 to account for 15 minutes double newDemandVal = Math.max(demandList.get(k) - platoonVehicles, 0); demandList.set(k, newDemandVal); demandPlatoon.add(platoonPlatoons); platoonVehicles = platoonPlatoons * this.platoonSize * 4; // Based on actual generated platoons, but used to // compensate per hour demandPlatoonVehicles.add(platoonVehicles); } // Platoon definition (platoons = ... divide into multiple for multiple origins) Set position = new LinkedHashSet<>(); position.add(new LaneDirection(this.graphLanes.get(0), GTUDirectionality.DIR_PLUS)); DefaultGTUCharacteristicsGeneratorOD characteristicsGenerator = getCharacteristicsGenerator(longitudinalControllerFactory, sim, parameters); Platoons platoons = Platoons.ofCategory(characteristicsGenerator, sim, sim.getReplication().getStream("generation"), position); platoons.fixInfo(nodeA, nodeD, caccCategory); double dt = this.startSpacing; // Determine platoon generation times (of leading vehicle only) using pseudorandom generation (exponential // distribution) // Then check if interval times do not overlap with whole platoon generation, adjust accordingly List generationTimes = new ArrayList(); double previous = 0; for (int k = 0; k < demandList.size(); k++) { for (int i = 0; i < demandPlatoon.get(k); i++) { double lambda = (demandPlatoon.get(k) * 4); // Number of platoons to be generated (per hour -> times 4) StreamInterface rand = sim.getReplication().getStream("generation"); // Random rand = new Random(); double arrival = (Math.log(1 - rand.nextDouble()) / (-lambda) * 3600); // Inter arrival time is in the unit of lambda (here per 15 minutes) double startTime = (arrival + previous); // (k * 900) + (arrival + previous); // Interarrival plus 15 // minutes (900 seconds) generationTimes.add(startTime); previous = previous + arrival; } } // Sorting the generation time to check for overlap in generation Collections.sort(generationTimes); for (int i = 0; i < generationTimes.size() - 1; i++) { double diff = generationTimes.get(i + 1) - generationTimes.get(i); double generationDuration = 0.5; double maxDuration = (generationDuration * this.platoonSize) + 1.0; if (diff <= maxDuration) { generationTimes.set(i + 1, generationTimes.get(i) + maxDuration); } // ((platoonSize-1)*((12.0 + 3.0)/22.22) + 0.3) + ( (12.0 + 3.0)/22.22 + 1.0); double endTime = generationTimes.get(i) + (generationDuration * this.platoonSize); platoons.addPlatoon(Time.instantiateSI(generationTimes.get(i)), Time.instantiateSI(endTime)); for (double t = generationTimes.get(i); t < endTime; t += dt) { platoons.addGtu(Time.instantiateSI(t)); } } // OD demand // cars (without compensation we use fraction 0.5 in putDemandVector, otherwise we multiply by laneshare) odMatrix.putDemandVector(nodeA, nodeD, carCategory1, freq(new double[] {0, 0, 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 0, 0}), timeVector, interpolation, 0.5); odMatrix.putDemandVector(nodeA, nodeD, carCategory0, platoons.compensate(carCategory0, freq(new double[] {0, 0, 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 0, 0}), timeVector, interpolation), timeVector, interpolation, 0.5); odMatrix.putDemandVector(nodeE, nodeD, carCategoryR, freq(new double[] {0, 0, 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 0, 0})); // trucks odMatrix.putDemandVector(nodeE, nodeD, truCategoryR, freq(new double[] {0, 0, 45 * (1 + intensityIncrease), 45 * (1 + intensityIncrease), 45 * (1 + intensityIncrease), 45 * (1 + intensityIncrease), 45 * (1 + intensityIncrease), 0, 0})); // cacc trucks & compensated normal trucks - if there are no, or only, cacc vehicles -> remove demand if (this.penetration != 1.0) { odMatrix.putDemandVector(nodeA, nodeD, truCategory0, platoons.compensate(truCategory0, freq(new double[] {demandList.get(0), demandList.get(1), demandList.get(2), demandList.get(3), demandList.get(4), demandList.get(5), demandList.get(6), demandList.get(7), demandList.get(8)}), timeVector, interpolation)); } if (this.penetration != 0.0) { odMatrix.putDemandVector(nodeA, nodeD, caccCategory, platoons.compensate(caccCategory, freq(new double[] {demandPlatoonVehicles.get(0), demandPlatoonVehicles.get(1), demandPlatoonVehicles.get(2), demandPlatoonVehicles.get(3), demandPlatoonVehicles.get(4), demandPlatoonVehicles.get(5), demandPlatoonVehicles.get(6), demandPlatoonVehicles.get(7), demandPlatoonVehicles.get(8)}), timeVector, interpolation)); } // options ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.instantiateSI(300.0)).set(ODOptions.GTU_TYPE, characteristicsGenerator); odApplierOutput = ODApplier.applyOD(network, odMatrix, odOptions); // start platoons platoonDetector1 = "A1"; platoons.start(odApplierOutput.get(platoonDetector1).getGenerator()); // Write platoon generation times to file BufferedWriter bw; bw = new BufferedWriter( new OutputStreamWriter(Writer.createOutputStream(this.outputFileGen, CompressionType.NONE))); bw.write(String.format("Platoon generation times [s]: %s", generationTimes)); bw.close(); } else if (this.networkName.equals("onrampMERGE")) { // points OTSPoint3D pointA = new OTSPoint3D(0, 0); OTSPoint3D pointB = new OTSPoint3D(2000, 0); // 700 meters toerit (1300 - 2000) OTSPoint3D pointC = new OTSPoint3D(2330, 0); // 330 meters onramp OTSPoint3D pointD = new OTSPoint3D(3330, 0); OTSPoint3D pointE = new OTSPoint3D(1300, -40); // nodes OTSRoadNode nodeA = new OTSRoadNode(network, "A", pointA, Direction.ZERO); OTSRoadNode nodeB = new OTSRoadNode(network, "B", pointB, Direction.ZERO); OTSRoadNode nodeC = new OTSRoadNode(network, "C", pointC, Direction.ZERO); OTSRoadNode nodeD = new OTSRoadNode(network, "D", pointD, Direction.ZERO); OTSRoadNode nodeE = new OTSRoadNode(network, "E", pointE, Direction.ZERO); // links CrossSectionLink linkAB = new CrossSectionLink(network, "AB", nodeA, nodeB, freewayLinkType, new OTSLine3D(pointA, pointB), LaneKeepingPolicy.KEEPRIGHT); CrossSectionLink linkBC = new CrossSectionLink(network, "BC", nodeB, nodeC, freewayLinkType, new OTSLine3D(pointB, pointC), LaneKeepingPolicy.KEEPRIGHT); CrossSectionLink linkCD = new CrossSectionLink(network, "CD", nodeC, nodeD, freewayLinkType, new OTSLine3D(pointC, pointD), LaneKeepingPolicy.KEEPRIGHT); CrossSectionLink linkEB = new CrossSectionLink(network, "EB", nodeE, nodeB, freewayLinkType, Bezier.cubic(nodeE.getLocation(), nodeB.getLocation()), LaneKeepingPolicy.KEEPRIGHT); // lanes and stripes int n = this.numberOfLanes; // List originLanes = new ArrayList<>(); for (int i = 0; i < n; i++) { for (CrossSectionLink link : new CrossSectionLink[] {linkAB, linkBC, linkCD}) { Lane lane = new Lane(link, "Lane " + (i + 1), laneWidth.times((0.5 + i)), laneWidth, freewayLaneType, new Speed(100, SpeedUnit.KM_PER_HOUR)); Length offset = laneWidth.times(i + 1.0); Stripe stripe = new Stripe(link, offset, offset, stripeWidth); if (i < n - 1) { if (lane.getParentLink().getId().equals("BC")) { // stripe.addPermeability(GTUType.VEHICLE, Permeable.LEFT); stripe.addPermeability(vehicle, Permeable.BOTH); } else { stripe.addPermeability(vehicle, Permeable.BOTH); } } if (lane.getParentLink().getId().equals("BC")) { new Detector(lane.getFullId(), lane, Length.instantiateSI(330.0), sim); } // sink sensors if (lane.getParentLink().getId().equals("CD")) { new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(100.0)), GTUDirectionality.DIR_PLUS, sim); // detectors 0m after on ramp // new Detector(lane.getFullId(), lane, Length.createSI(0.0), sim); // id equal to lane, may be // different } if (lane.getParentLink().getId().equals("AB")) { // originLanes.add(lane); this.graphLanes.add(lane); } } } new Stripe(linkAB, Length.ZERO, Length.ZERO, stripeWidth); Stripe stripe = new Stripe(linkBC, Length.ZERO, Length.ZERO, stripeWidth); stripe.addPermeability(vehicle, Permeable.LEFT); new Stripe(linkCD, Length.ZERO, Length.ZERO, stripeWidth); new Lane(linkBC, "Acceleration lane", laneWidth.times(-0.5), laneWidth, freewayLaneType, new Speed(100, SpeedUnit.KM_PER_HOUR)); Lane onramp = new Lane(linkEB, "Onramp", laneWidth.times(-0.5), laneWidth, freewayLaneType, new Speed(100, SpeedUnit.KM_PER_HOUR)); new Stripe(linkEB, Length.ZERO, Length.ZERO, stripeWidth); new Stripe(linkEB, laneWidth.neg(), laneWidth.neg(), stripeWidth); new Stripe(linkBC, laneWidth.neg(), laneWidth.neg(), stripeWidth); // OD without demand List origins = new ArrayList<>(); origins.add(nodeA); origins.add(nodeE); List destinations = new ArrayList<>(); destinations.add(nodeD); TimeVector timeVector = DoubleVector.instantiate(new double[] {0.0, 0.25, 0.50, 0.75, 1.0, 1.25, 1.50, 1.75, 2.0}, TimeUnit.BASE_HOUR, StorageType.DENSE); Interpolation interpolation = Interpolation.LINEAR; // or STEPWISE Categorization categorization = new Categorization("CACC", GTUType.class, Lane.class); Category carCategory1 = new Category(categorization, carGTUType, this.graphLanes.get(1)); Category carCategory0 = new Category(categorization, carGTUType, this.graphLanes.get(0)); Category carCategoryR = new Category(categorization, carGTUType, onramp); Category truCategory0 = new Category(categorization, truckGTUType, this.graphLanes.get(0)); Category truCategoryR = new Category(categorization, truckGTUType, onramp); Category caccCategory = new Category(categorization, caccGTUType, onramp); ODMatrix odMatrix = new ODMatrix("CACC OD", origins, destinations, categorization, timeVector, interpolation); double intensityIncrease = this.intensity; double platoonPenetration = this.penetration; // Demand of trucks departing from onramp List demandList = new ArrayList(); demandList.add((double) 0); demandList.add((double) 0); demandList.add(45 * (1 + intensityIncrease)); demandList.add(45 * (1 + intensityIncrease)); demandList.add(45 * (1 + intensityIncrease)); demandList.add(45 * (1 + intensityIncrease)); demandList.add(45 * (1 + intensityIncrease)); demandList.add((double) 0); demandList.add((double) 0); List demandPlatoon = new ArrayList(); // List for number of platoons (not number of platoon // vehicles!) List demandPlatoonVehicles = new ArrayList(); // Demand for platoon vehicles for (int k = 0; k < demandList.size(); k++) { double platoonVehicles = (demandList.get(k) * platoonPenetration); // double platoonPlatoons = Math.round(platoonVehicles/this.platoonSize)/4; // Actual generated platoons -> divide by 4 to account for 15 minutes double platoonPlatoons = Math.ceil(Math.abs(platoonVehicles / this.platoonSize)) / 4; // Rounding up double newDemandVal = Math.max(demandList.get(k) - platoonVehicles, 0); demandList.set(k, newDemandVal); demandPlatoon.add(platoonPlatoons); platoonVehicles = platoonPlatoons * this.platoonSize * 4; // Based on actual generated platoons, but used to // compensate per hour demandPlatoonVehicles.add(platoonVehicles); } // Platoon definition (platoons = ... divide into multiple for multiple origins) Set position = new LinkedHashSet<>(); position.add(new LaneDirection(onramp, GTUDirectionality.DIR_PLUS)); DefaultGTUCharacteristicsGeneratorOD characteristicsGenerator = getCharacteristicsGenerator(longitudinalControllerFactory, sim, parameters); Platoons platoons = Platoons.ofCategory(characteristicsGenerator, sim, sim.getReplication().getStream("generation"), position); platoons.fixInfo(nodeE, nodeD, caccCategory); double dt = this.startSpacing; // Determine platoon generation times (of leading vehicle only) using pseudorandom generation (exponential // distribution) // Then check if interval times do not overlap with whole platoon generation, adjust accordingly List generationTimes = new ArrayList(); double previous = 0; for (int k = 0; k < demandList.size(); k++) { for (int i = 0; i < demandPlatoon.get(k); i++) { double lambda = (demandPlatoon.get(k) * 4); // Number of platoons to be generated (per hour -> times 4) StreamInterface rand = sim.getReplication().getStream("generation"); // Random rand = new Random(); double arrival = (Math.log(1 - rand.nextDouble()) / (-lambda) * 3600); // Inter arrival time is in the unit // of lambda (here per 15 minutes) double startTime = (arrival + previous); // (k * 900) + (arrival + previous); // Interarrival plus 15 // minutes (900 seconds) generationTimes.add(startTime); previous = previous + arrival; } } // Sorting the generation time to check for overlap in generation Collections.sort(generationTimes); for (int i = 0; i < generationTimes.size() - 1; i++) { double diff = generationTimes.get(i + 1) - generationTimes.get(i); double generationDuration = 0.5; double maxDuration = (generationDuration * this.platoonSize) + 1.0; if (diff <= maxDuration) { generationTimes.set(i + 1, generationTimes.get(i) + maxDuration); } // ((this.platoonSize-1)*((12.0 + 3.0)/22.22) + 0.3) + ( (12.0 + 3.0)/22.22 + 1.0); double endTime = generationTimes.get(i) + (generationDuration * this.platoonSize); platoons.addPlatoon(Time.instantiateSI(generationTimes.get(i)), Time.instantiateSI(endTime)); for (double t = generationTimes.get(i); t < endTime; t += dt) { platoons.addGtu(Time.instantiateSI(t)); } } // OD demand // cars (without compensation we use fraction 0.5 in putDemandVector, otherwise we multiply by laneshare) odMatrix.putDemandVector(nodeA, nodeD, carCategory1, freq(new double[] {0, 0, 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 0, 0}), timeVector, interpolation, 0.5); odMatrix.putDemandVector(nodeA, nodeD, carCategory0, freq(new double[] {0, 0, 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 0, 0}), timeVector, interpolation, 0.5); odMatrix.putDemandVector(nodeE, nodeD, carCategoryR, platoons.compensate(carCategoryR, freq(new double[] {0, 0, 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 0, 0}), timeVector, interpolation)); // trucks odMatrix.putDemandVector(nodeA, nodeD, truCategory0, platoons.compensate(truCategory0, freq(new double[] {0, 0, 180 * (1 + intensityIncrease), 180 * (1 + intensityIncrease), 180 * (1 + intensityIncrease), 180 * (1 + intensityIncrease), 180 * (1 + intensityIncrease), 0, 0}), timeVector, interpolation)); // cacc trucks & compensated normal trucks - if there are no, or only, cacc vehicles -> remove demand if (this.penetration != 1.0) { odMatrix.putDemandVector(nodeE, nodeD, truCategoryR, freq(new double[] {demandList.get(0), demandList.get(1), demandList.get(2), demandList.get(3), demandList.get(4), demandList.get(5), demandList.get(6), demandList.get(7), demandList.get(8)}), timeVector, interpolation); } if (this.penetration != 0.0) { odMatrix.putDemandVector(nodeE, nodeD, caccCategory, platoons.compensate(caccCategory, freq(new double[] {demandPlatoonVehicles.get(0), demandPlatoonVehicles.get(1), demandPlatoonVehicles.get(2), demandPlatoonVehicles.get(3), demandPlatoonVehicles.get(4), demandPlatoonVehicles.get(5), demandPlatoonVehicles.get(6), demandPlatoonVehicles.get(7), demandPlatoonVehicles.get(8)}), timeVector, interpolation)); } // options ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.instantiateSI(300.0)).set(ODOptions.GTU_TYPE, characteristicsGenerator); odApplierOutput = ODApplier.applyOD(network, odMatrix, odOptions); // start platoons platoonDetector1 = "E1"; platoons.start(odApplierOutput.get(platoonDetector1).getGenerator()); // Write platoon generation times to file BufferedWriter bw; bw = new BufferedWriter( new OutputStreamWriter(Writer.createOutputStream(this.outputFileGen, CompressionType.NONE))); bw.write(String.format("Platoon generation times [s]: %s", generationTimes)); bw.close(); } else { throw new RuntimeException("Network " + this.networkName + " not supported."); } // listen to generator so we can add platoon object to the tactical planner (for first direction) odApplierOutput.get(platoonDetector1).getGenerator().addListener(new EventListenerInterface() { /** ... */ private static final long serialVersionUID = 1L; /** Current platoon. */ private Platoon platoon = null; /** Last generated CACC GTU. */ private LaneBasedGTU lastGtu; /** {@inheritDoc} */ @Override public void notify(final EventInterface event) throws RemoteException { if (event.getType().equals(LaneBasedGTUGenerator.GTU_GENERATED_EVENT)) { LaneBasedGTU gtu = (LaneBasedGTU) event.getContent(); if (gtu.getGTUType().equals(caccGTUType)) { // If platoon is not within 2 seconds, or platoon is equal or larger than set platoon size -> new // platoon // || this.lastGtu.getLocation().distance(gtu.getLocation()) > 2.0 * gtu.getSpeed().si if (this.lastGtu == null || this.lastGtu.isDestroyed() || this.lastGtu.getLocation().distance(gtu.getLocation()) > 3.0 * gtu.getSpeed().si || this.platoon.size() >= platoonSize) { this.platoon = new Platoon(); } this.platoon.addGtu(gtu.getId()); ((CaccTacticalPlanner) gtu.getTacticalPlanner()).setPlatoon(this.platoon); this.lastGtu = gtu; } } } }, LaneBasedGTUGenerator.GTU_GENERATED_EVENT); /** Sampler for statistics. */ this.sampler = RoadSampler.build(network).registerExtendedDataType(this.ttc).registerFilterDataType(this.metaGtu).create(); // Construct sample time and space window Time start = new Time(0.25, TimeUnit.BASE_HOUR); // TODO set times Time end = new Time(2.00, TimeUnit.BASE_HOUR); for (Link link : network.getLinkMap().values()) { for (Lane lane : ((CrossSectionLink) link).getLanes()) { KpiLaneDirection kpiLane = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS); SpaceTimeRegion region = new SpaceTimeRegion(kpiLane, Length.ZERO, lane.getLength(), start, end); this.regions.add(region); this.sampler.registerSpaceTimeRegion(region); } } return network; } /** {@inheritDoc} */ @Override protected void addTabs(final OTSSimulatorInterface sim, final OTSSimulationApplication animation) { if (this.graphs) { // create tab int h = (int) Math.sqrt(this.graphLanes.size()); int w = (int) Math.ceil(((double) this.graphLanes.size()) / h); TablePanel charts = new TablePanel(w, h); animation.getAnimationPanel().getTabbedPane().addTab(animation.getAnimationPanel().getTabbedPane().getTabCount(), "statistics", charts); // create sampler for the data // RoadSampler graphSampler = new RoadSampler(getNetwork()); RoadSampler graphSampler = RoadSampler.build(getNetwork()).registerExtendedDataType(this.ttc) .registerFilterDataType(this.metaGtu).create(); // create plots per lane int h2 = 0; int w2 = 0; for (int i = 0; i < this.graphLanes.size(); i++) { GraphPath graphPath; try { // starts at the lane and includes downstream lanes graphPath = GraphLaneUtil.createPath("Lane" + (i + 1), new LaneDirection(this.graphLanes.get(i), GTUDirectionality.DIR_PLUS)); GraphPath.initRecording(graphSampler, graphPath); } catch (NetworkException exception) { throw new RuntimeException(exception); } ContourDataSource dataPool = new ContourDataSource<>(graphSampler.getSamplerData(), graphPath); SwingContourPlot plot = new SwingContourPlot(new ContourPlotSpeed("Speed lane " + (i + 1), sim, dataPool)); charts.setCell(plot.getContentPane(), w2, h2); w2++; if (w2 > w) { w = 0; h2++; } } } } /** {@inheritDoc} */ @Override protected void onSimulationEnd() { // periodic = true; periodic data is stored, if false other mesoscopic data would be stored, but that has not been setup Detector.writeToFile(getNetwork(), this.outputFileDets, true); // this.sampler.writeToFile(getProperty("outputFile")); double tts = 0.0; List ttcList = new ArrayList<>(); List ttcListGtuType = new ArrayList<>(); List decList = new ArrayList<>(); List decListGtuType = new ArrayList<>(); List posList = new ArrayList<>(); List timeList = new ArrayList<>(); List postimeList = new ArrayList<>(); List posListGtuType = new ArrayList<>(); List posupstreamList = new ArrayList<>(); List timeupstreamList = new ArrayList<>(); List postimeupstreamList = new ArrayList<>(); List posupstreamListGtuType = new ArrayList<>(); List posdownstreamList = new ArrayList<>(); List timedownstreamList = new ArrayList<>(); List postimedownstreamList = new ArrayList<>(); List posdownstreamListGtuType = new ArrayList<>(); List posrampList = new ArrayList<>(); List timerampList = new ArrayList<>(); List postimerampList = new ArrayList<>(); List posrampListGtuType = new ArrayList<>(); int[] counts = new int[120]; Length detectorPosition = Length.instantiateSI(100.0); for (SpaceTimeRegion region : this.regions) { TrajectoryGroup trajectoryGroup = this.sampler.getSamplerData().getTrajectoryGroup(region.getLaneDirection()).getTrajectoryGroup( region.getStartPosition(), region.getEndPosition(), region.getStartTime(), region.getEndTime()); for (Trajectory trajectory : trajectoryGroup) { try { tts += trajectory.getTotalDuration().si; String gtuTypeId = trajectory.getMetaData(this.metaGtu /* new FilterDataGtuType() */).getId(); for (FloatDuration ttcVal : trajectory.getExtendedData(this.ttc)) { if (!Float.isNaN(ttcVal.si) && ttcVal.si < 20) { ttcList.add(ttcVal.si); ttcListGtuType.add(gtuTypeId); } } for (float decVal : trajectory.getA()) { if (decVal < -2.0) { decList.add(decVal); decListGtuType.add(gtuTypeId); } } // collect data for merging lane and acceleration lane if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("BC") && region.getLaneDirection().getLaneData().getId().equals("Lane 1")) { for (float xPos : trajectory.getX()) { posList.add(xPos); posListGtuType.add(trajectory.getGtuId()); postimeList.add(gtuTypeId); } for (float tPos : trajectory.getT()) { timeList.add(tPos); } } if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("AB") && region.getLaneDirection().getLaneData().getId().equals("Lane 1")) { for (float xPos : trajectory.getX()) { posupstreamList.add(xPos); posupstreamListGtuType.add(trajectory.getGtuId()); postimeupstreamList.add(gtuTypeId); } for (float tPos : trajectory.getT()) { timeupstreamList.add(tPos); } } if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("CD") && region.getLaneDirection().getLaneData().getId().equals("Lane 1")) { for (float xPos : trajectory.getX()) { posdownstreamList.add(xPos); posdownstreamListGtuType.add(trajectory.getGtuId()); postimedownstreamList.add(gtuTypeId); } for (float tPos : trajectory.getT()) { timedownstreamList.add(tPos); } } if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("BC") && region.getLaneDirection().getLaneData().getId().equals("Acceleration lane")) { for (float xPos : trajectory.getX()) { posrampList.add(xPos); posrampListGtuType.add(trajectory.getGtuId()); postimerampList.add(gtuTypeId); } for (float tPos : trajectory.getT()) { timerampList.add(tPos); } } if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("CD") && trajectory.size() > 1 && trajectory.getX(0) < detectorPosition.si && trajectory.getX(trajectory.size() - 1) > detectorPosition.si) { double t = trajectory.getTimeAtPosition(detectorPosition).si - region.getStartTime().si; counts[(int) (t / 60.0)]++; } } catch (SamplingException exception) { throw new RuntimeException("Unexpected exception: TimeToCollission not available or index out of bounds.", exception); } } } int qMax = 0; for (int i = 0; i < counts.length - 4; i++) { int q = 0; for (int j = i; j < i + 5; j++) { q += counts[j]; } qMax = qMax > q ? qMax : q; } BufferedWriter bw; try { bw = new BufferedWriter( new OutputStreamWriter(Writer.createOutputStream(this.outputFileTraj, CompressionType.NONE))); bw.write(String.format("total time spent [s]: %.0f", tts)); bw.newLine(); bw.write(String.format("maximum flow [veh/5min]: %d", qMax)); bw.newLine(); bw.write(String.format("time to collision [s]: %s", ttcList)); bw.newLine(); bw.write(String.format("time to collision GTU type: %s", ttcListGtuType)); bw.newLine(); bw.write(String.format("strong decelerations [m/s^2]: %s", decList)); bw.newLine(); bw.write(String.format("strong decelerations GTU type: %s", decListGtuType)); bw.newLine(); bw.write(String.format("X pos: %s", posList)); bw.newLine(); bw.write(String.format("T pos: %s", timeList)); bw.newLine(); bw.write(String.format("X pos GTU ID: %s", posListGtuType)); bw.newLine(); bw.write(String.format("X pos GTU type: %s", postimeList)); bw.newLine(); bw.write(String.format("X pos ramp: %s", posrampList)); bw.newLine(); bw.write(String.format("T pos ramp: %s", timerampList)); bw.newLine(); bw.write(String.format("X pos ramp GTU ID: %s", posrampListGtuType)); bw.newLine(); bw.write(String.format("X pos ramp GTU type: %s", postimerampList)); bw.newLine(); bw.write(String.format("X pos up: %s", posupstreamList)); bw.newLine(); bw.write(String.format("T pos up: %s", timeupstreamList)); bw.newLine(); bw.write(String.format("X pos up GTU ID: %s", posupstreamListGtuType)); bw.newLine(); bw.write(String.format("X pos up GTU type: %s", postimeupstreamList)); bw.newLine(); bw.write(String.format("X pos down: %s", posdownstreamList)); bw.newLine(); bw.write(String.format("T pos down: %s", timedownstreamList)); bw.newLine(); bw.write(String.format("X pos down GTU ID: %s", posdownstreamListGtuType)); bw.newLine(); bw.write(String.format("X pos down GTU type: %s", postimedownstreamList)); bw.close(); } catch (IOException exception) { throw new RuntimeException(exception); } } /** * Creates a frequency vector. * @param array double[]; array in veh/h * @return FrequencyVector; frequency vector * @throws ValueRuntimeException on problem */ private FrequencyVector freq(final double[] array) throws ValueRuntimeException { return DoubleVector.instantiate(array, FrequencyUnit.PER_HOUR, StorageType.DENSE); } /** * Returns an OD compatible characteristics generator that uses the GTUType to select either regular models or CACC specific * models. * @param longitudinalControllerFactory LongitudinalControllerFactory<? extends CACC>; longitudinal controller factory * @param simulator OTSSimulatorInterface; simulator * @param parameters ParameterFactory parameters * @return OD compatible characteristics generator */ private DefaultGTUCharacteristicsGeneratorOD getCharacteristicsGenerator( final LongitudinalControllerFactory longitudinalControllerFactory, final OTSSimulatorInterface simulator, final ParameterFactory parameters) { // create template for CACC truck properties (this is then used instead of GTUType.defaultCharacteristics(...)) Set templates = new LinkedHashSet<>(); templates.add(new TemplateGTUType(caccGTUType, new ConstantGenerator<>(Length.instantiateSI(12.0)), new ConstantGenerator<>(Length.instantiateSI(2.55)), new ConstantGenerator<>(Speed.instantiateSI(this.setspeed.si + 2.78)))); // anonymous class for factory supplier that overwrites the getFactory() method to return one based on the GTU type return new DefaultGTUCharacteristicsGeneratorOD(templates, new StrategicalPlannerFactorySupplierOD() { /** Strategical planner for regular traffic. */ private LaneBasedStrategicalPlannerFactory lmrsFactory = null; /** Strategical planner for CACC trucks. */ private LaneBasedStrategicalPlannerFactory caccFactory = null; /** {@inheritDoc} */ @Override public LaneBasedStrategicalPlannerFactory getFactory(final Node origin, final Node destination, final Category category, final StreamInterface randomStream) throws GTUException { GTUType gtuType = category.get(GTUType.class); if (!gtuType.equals(caccGTUType)) { if (this.lmrsFactory == null) { this.lmrsFactory = new LaneBasedStrategicalRoutePlannerFactory( new LMRSFactory(new IDMPlusFactory(randomStream), new DefaultLMRSPerceptionFactory()), parameters); } return this.lmrsFactory; } if (this.caccFactory == null) { this.caccFactory = new LaneBasedStrategicalRoutePlannerFactory( new CaccTacticalPlannerFactory(new IDMPlusFactory(randomStream), longitudinalControllerFactory, simulator, caccGTUType), parameters); } return this.caccFactory; } }); } }