package org.opentrafficsim.demo.steering;
import java.util.ArrayList;
import java.util.List;
import org.djunits.unit.FrequencyUnit;
import org.djunits.unit.MassUnit;
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.Direction;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Mass;
import org.djunits.value.vdouble.scalar.Speed;
import org.djunits.value.vdouble.vector.FrequencyVector;
import org.djunits.value.vdouble.vector.TimeVector;
import org.djunits.value.vdouble.vector.base.DoubleVector;
import org.djutils.cli.CliUtil;
import org.opentrafficsim.base.parameters.ParameterException;
import org.opentrafficsim.base.parameters.ParameterSet;
import org.opentrafficsim.base.parameters.Parameters;
import org.opentrafficsim.core.compatibility.Compatible;
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.GTUException;
import org.opentrafficsim.core.gtu.GTUType;
import org.opentrafficsim.core.network.LinkType;
import org.opentrafficsim.core.network.Node;
import org.opentrafficsim.core.network.OTSNode;
import org.opentrafficsim.core.units.distributions.ContinuousDistMass;
import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD;
import org.opentrafficsim.road.gtu.generator.od.ODApplier;
import org.opentrafficsim.road.gtu.generator.od.ODOptions;
import org.opentrafficsim.road.gtu.generator.od.StrategicalPlannerFactorySupplierOD;
import org.opentrafficsim.road.gtu.generator.od.StrategicalPlannerFactorySupplierOD.TacticalPlannerFactorySupplierOD;
import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
import org.opentrafficsim.road.gtu.lane.VehicleModel;
import org.opentrafficsim.road.gtu.lane.VehicleModelFactory;
import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlannerFactory;
import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory;
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.steering.SteeringLmrs;
import org.opentrafficsim.road.gtu.lane.tactical.util.Steering;
import org.opentrafficsim.road.gtu.lane.tactical.util.Steering.FeedbackTable;
import org.opentrafficsim.road.gtu.lane.tactical.util.Steering.FeedbackTable.FeedbackVector;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Cooperation;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.GapAcceptance;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Synchronization;
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.network.OTSRoadNetwork;
import org.opentrafficsim.road.network.lane.CrossSectionLink;
import org.opentrafficsim.road.network.lane.Lane;
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.swing.script.AbstractSimulationScript;
import nl.tudelft.simulation.jstats.distributions.DistUniform;
import nl.tudelft.simulation.jstats.streams.StreamInterface;
import picocli.CommandLine.Option;
/**
* Simulation script for steering functionality.
*
* Copyright (c) 2013-2020 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 8 jan. 2019
* @author Alexander Verbraeck
* @author Peter Knoppers
* @author Wouter Schakel
*/
public class SteeringSimulation extends AbstractSimulationScript
{
/** Feedback table. */
static final FeedbackTable FEEDBACK_CAR;
/** Number of lanes. */
@Option(names = "--numberOfLanes", description = "Number of lanes", defaultValue = "2")
private int numberOfLanes;
static
{
// TODO: define tables
List list = new ArrayList<>();
list.add(new FeedbackVector(new Speed(25.0, SpeedUnit.KM_PER_HOUR), 0.0, 0.0, 0.0, 0.0));
list.add(new FeedbackVector(new Speed(75.0, SpeedUnit.KM_PER_HOUR), 0.0, 0.0, 0.0, 0.0));
FEEDBACK_CAR = new FeedbackTable(list);
}
/**
* Start a simulation.
* @param args String...; command line arguments
*/
public static void main(final String... args)
{
try
{
SteeringSimulation sim = new SteeringSimulation();
CliUtil.execute(sim, args);
sim.start();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
/**
* Constructor.
*/
protected SteeringSimulation()
{
super("Steering simulation", "Steering simulation");
}
/**
* Sets up the simulation based on provided properties. Properties can be obtained with {@code getProperty()}. Setting up a
* simulation should at least create a network and some demand. Additionally this may setup traffic control, sampling, etc.
* @param sim OTSSimulatorInterface; simulator
* @return OTSRoadNetwork; network
* @throws Exception on any exception
*/
@Override
protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
{
OTSRoadNetwork network = new OTSRoadNetwork("Steering network", true, getSimulator());
Length laneWidth = Length.instantiateSI(3.5);
Length stripeWidth = Length.instantiateSI(0.2);
// points
OTSPoint3D pointA = new OTSPoint3D(0, 0);
OTSPoint3D pointB = new OTSPoint3D(2000, 0);
OTSPoint3D pointC = new OTSPoint3D(2250, 0);
OTSPoint3D pointD = new OTSPoint3D(3250, 0);
OTSPoint3D pointE = new OTSPoint3D(1500, -30);
// 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, network.getLinkType(LinkType.DEFAULTS.FREEWAY),
new OTSLine3D(pointA, pointB), LaneKeepingPolicy.KEEPRIGHT);
CrossSectionLink linkBC =
new CrossSectionLink(network, "BC", nodeB, nodeC, network.getLinkType(LinkType.DEFAULTS.FREEWAY),
new OTSLine3D(pointB, pointC), LaneKeepingPolicy.KEEPRIGHT);
CrossSectionLink linkCD =
new CrossSectionLink(network, "CD", nodeC, nodeD, network.getLinkType(LinkType.DEFAULTS.FREEWAY),
new OTSLine3D(pointC, pointD), LaneKeepingPolicy.KEEPRIGHT);
CrossSectionLink linkEB =
new CrossSectionLink(network, "EB", nodeE, nodeB, network.getLinkType(LinkType.DEFAULTS.FREEWAY),
Bezier.cubic(nodeE.getLocation(), nodeB.getLocation()), LaneKeepingPolicy.KEEPRIGHT);
// lanes and stripes
List originLanes = new ArrayList<>();
for (int i = 0; i < this.numberOfLanes; i++)
{
for (CrossSectionLink link : new CrossSectionLink[] {linkAB, linkBC, linkCD})
{
Lane lane = new Lane(link, "Lane " + (i + 1), laneWidth.times((0.5 + i)), laneWidth,
network.getLaneType(LaneType.DEFAULTS.FREEWAY), new Speed(120, SpeedUnit.KM_PER_HOUR));
Length offset = laneWidth.times(i + 1.0);
Stripe stripe = new Stripe(link, offset, offset, stripeWidth);
if (i < this.numberOfLanes - 1)
{
stripe.addPermeability(network.getGtuType(GTUType.DEFAULTS.VEHICLE), Permeable.BOTH);
}
// sink sensors
if (lane.getParentLink().getId().equals("CD"))
{
new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(100.0)), Compatible.EVERYTHING, sim);
// detectors 100m after on ramp
new Detector(lane.getFullId(), lane, Length.instantiateSI(100.0), sim); // id equal to lane, may be
// different
}
if (lane.getParentLink().getId().equals("AB"))
{
originLanes.add(lane);
}
}
}
new Stripe(linkAB, Length.ZERO, Length.ZERO, stripeWidth);
Stripe stripe = new Stripe(linkBC, Length.ZERO, Length.ZERO, stripeWidth);
stripe.addPermeability(network.getGtuType(GTUType.DEFAULTS.VEHICLE), Permeable.LEFT);
new Stripe(linkCD, Length.ZERO, Length.ZERO, stripeWidth);
new Lane(linkBC, "Acceleration lane", laneWidth.times(-0.5), laneWidth, network.getLaneType(LaneType.DEFAULTS.FREEWAY),
new Speed(120, SpeedUnit.KM_PER_HOUR));
new Lane(linkEB, "Onramp", laneWidth.times(-0.5), laneWidth, network.getLaneType(LaneType.DEFAULTS.FREEWAY),
new Speed(120, 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
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.5, 1.0}, TimeUnit.BASE_HOUR, StorageType.DENSE);
Interpolation interpolation = Interpolation.LINEAR; // or STEPWISE
Categorization categorization = new Categorization("GTU type", GTUType.class);
Category carCategory = new Category(categorization, network.getGtuType(GTUType.DEFAULTS.CAR));
Category truCategory = new Category(categorization, network.getGtuType(GTUType.DEFAULTS.TRUCK));
ODMatrix odMatrix = new ODMatrix("Steering OD", origins, destinations, categorization, timeVector, interpolation);
odMatrix.putDemandVector(nodeA, nodeD, carCategory, freq(new double[] {1000.0, 2000.0, 0.0}));
odMatrix.putDemandVector(nodeA, nodeD, truCategory, freq(new double[] {100.0, 200.0, 0.0}));
odMatrix.putDemandVector(nodeE, nodeD, carCategory, freq(new double[] {500.0, 1000.0, 0.0}));
// anonymous tactical-planner-factory supplier
AbstractLaneBasedTacticalPlannerFactory car = new AbstractLaneBasedTacticalPlannerFactory(
new IDMPlusFactory(sim.getReplication().getStream("generation")), new DefaultLMRSPerceptionFactory())
{
@Override
public SteeringLmrs create(final LaneBasedGTU gtu) throws GTUException
{
return new SteeringLmrs(nextCarFollowingModel(gtu), gtu, getPerceptionFactory().generatePerception(gtu),
Synchronization.PASSIVE, Cooperation.PASSIVE, GapAcceptance.INFORMED, FEEDBACK_CAR);
}
@Override
public Parameters getParameters() throws ParameterException
{
// TODO: add parameters if required (run and wait for ParameterException to find missing parameters)
ParameterSet parameters = new ParameterSet();
getCarFollowingParameters().setAllIn(parameters);
parameters.setDefaultParameters(Steering.class);
return parameters;
}
};
TacticalPlannerFactorySupplierOD tacticalPlannerFactorySupplierOD = new TacticalPlannerFactorySupplierOD()
{
@Override
public LaneBasedTacticalPlannerFactory getFactory(final Node origin, final Node destination,
final Category category, final StreamInterface randomStream)
{
GTUType gtuType = category.get(GTUType.class);
if (gtuType.equals(network.getGtuType(GTUType.DEFAULTS.CAR)))
{
return car;
}
else
{
// TODO: other GTU types
return null;
}
};
};
// anonymous vehicle model factory
// TODO: supply mass and inertia values, possibly randomized, correlated?
ContinuousDistMass massDistCar =
new ContinuousDistMass(new DistUniform(sim.getReplication().getStream("generation"), 600, 1200), MassUnit.SI);
ContinuousDistMass massDistTruck =
new ContinuousDistMass(new DistUniform(sim.getReplication().getStream("generation"), 2000, 10000), MassUnit.SI);
double momentOfInertiaAboutZ = 100; // no idea...
VehicleModelFactory vehicleModelGenerator = new VehicleModelFactory()
{
@Override
public VehicleModel create(final GTUType gtuType)
{
Mass mass =
gtuType.isOfType(network.getGtuType(GTUType.DEFAULTS.CAR)) ? massDistCar.draw() : massDistTruck.draw();
return new VehicleModel.MassBased(mass, momentOfInertiaAboutZ);
}
};
// characteristics generator using OD info and default route based strategical level
DefaultGTUCharacteristicsGeneratorOD characteristicsGenerator = new DefaultGTUCharacteristicsGeneratorOD.Factory()
.setFactorySupplier(StrategicalPlannerFactorySupplierOD.route(tacticalPlannerFactorySupplierOD))
.setVehicleModelGenerator(vehicleModelGenerator).create();
// od options
ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.instantiateSI(300.0)).set(ODOptions.GTU_TYPE,
characteristicsGenerator);
ODApplier.applyOD(network, odMatrix, odOptions);
return network;
}
/**
* 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);
}
}