package org.opentrafficsim.demo.ntm;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.djunits.unit.DurationUnit;
import org.djunits.unit.FrequencyUnit;
import org.djunits.unit.LengthUnit;
import org.djunits.unit.SpeedUnit;
import org.djunits.unit.TimeUnit;
import org.djunits.value.vdouble.scalar.Frequency;
import org.djunits.value.vdouble.scalar.Speed;
import org.djunits.value.vdouble.scalar.Time;
import org.jgrapht.graph.SimpleDirectedWeightedGraph;
import org.opentrafficsim.core.network.LinkEdge;
import org.opentrafficsim.demo.ntm.NTMNode.TrafficBehaviourType;
import org.opentrafficsim.demo.ntm.trafficdemand.TripDemand;
import org.opentrafficsim.demo.ntm.trafficdemand.TripInfoTimeDynamic;
/**
*
* 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.
*
* $LastChangedDate$, @version $Revision$, by $Author$,
* initial version 29 Oct 2014
* @author Alexander Verbraeck
* @author Hans van Lint
* @author Peter Knoppers
* @author Guus Tamminga
* @author Yufei Yuan
*/
public class NTMsimulation
{
// variables for data output during the simulation
/** */
static int steps = 0;
/** For reporting */
static int MAXSTEPS = 1080;
/** */
static LinkedHashMap> fluxAreaToNeighbours = new LinkedHashMap<>();
/**
* @param model NTMModel;
* @throws Exception
*/
public static void simulate(final NTMModel model) throws Exception
{
@SuppressWarnings("unchecked")
Time currentTime = null;
// debug for fastest path and write data??
steps++;
currentTime = new Time(
model.getSettingsNTM().getDurationSinceMidnight().getSI() + model.getSimulator().getSimulatorTime().getSI(),
TimeUnit.BASE_SECOND);
// retrieve information from the Area Graph containing the NTM areas and the selected highways
// The AreaGraph contains EDGES with differing characteristics:
// - "NTM"-links between NTM areas
// - "Cordon" links at the border of the study area that act as feeders and sinks of traffic to the "real" Areas
// - "Flow" links representing higher order roads with behaviour that deviates from the NTM area links
// (only for specific "main" roads)
// The VERTICES (type: BoundedNode) represent the NTM areas, or the entrance / exit of a Flow link.
// NTM nodes and FlowLink entrances/exits are connected by a "Transfer" link
//
// The "NTM" links between a pair of nodes, visualise the connection of that node to its "neighbours" with NTM
// characteristics
// The "Flow" links represent a connection where traffic behaves differently than in the NTM areas:
// once traffic is on a homogeneous link, the capacity remains stable
//
// The simulation of Traffic becomes a nested process:
// - the NTM process is the parent of the simulation
// - the Flow process acts as a "child" process
// ********************************************************************************************************
// STEP 1 of the simulation describes the initialisation of Demand from the Traffic Demand file:
// - this is generated by the OD matrix from the model
// first loop through the network nodes and select the NTM and Cordon "Area nodes"
// These nodes generate traffic from the trip demand file (feeders)
// There are two ESSENTIAL variables / class types:
// - the Node.ClassBehaviour represents the aggregated traffic flow dynamics between nodes/cells
// - the TripInfoTimeDynamic of an Area, showing for every OD-pair:
// . - number of trips between origin (or intermediate areas on a path from O to D) - destination pairs (from
// . . demand file)
// . - neighbour area on path form Origin to Destination
// . - time profile curve of trip departures from this origin (only used for the real origins!)
// . - accumulated cars in this area/node/cell, heading on the way to a certain destination
// . - flow in this time-step to neighbour from this area to destination
if (model.getSettingsNTM().isReRoute())
{
if (model.getSettingsNTM().getTimeStepDurationNTM().getInUnit(DurationUnit.SECOND) * steps
% model.getSettingsNTM().getReRouteTimeInterval().getInUnit(DurationUnit.SECOND) == 0)
{
System.out.println("reroute");
// new K-shortest paths creation
Routes.createRoutes(model, model.getSettingsNTM().getNumberOfRoutes(),
model.getSettingsNTM().getWeightNewRoutes(), model.getSettingsNTM().getVarianceRoutes(), false, steps,
MAXSTEPS);
}
}
int found = 1;
// initial settings of SUPPLY at first step
if (steps == 1)
{
NTMTestApplication.textArea.append("The simulation has started right now, \n" + "Wait for the next message... \n"
+ "This may take a while! \n" + " \n");
for (NTMNode node : model.getAreaGraph().vertexSet())
{
BoundedNode origin = (BoundedNode) node;
// **** RELEVANT: set SUPPLY of all areas at maximum in first step
if (origin.getBehaviourType() == TrafficBehaviourType.NTM)
{
CellBehaviourNTM cellBehaviourNTM = (CellBehaviourNTM) origin.getCellBehaviour();
double tripByTimeStep = model.getSettingsNTM().getTimeStepDurationNTM().getSI()
* cellBehaviourNTM.getMaxCapacityNTMArea().getSI();
cellBehaviourNTM.setSupply(tripByTimeStep);
// initial speed (no accumulation yet)
cellBehaviourNTM.setCurrentSpeed(cellBehaviourNTM.getParametersNTM().getFreeSpeed());
}
else if (origin.getBehaviourType() == TrafficBehaviourType.CORDON)
{
// Supply of Cordon nodes/areas always infinite!
origin.getCellBehaviour().setSupply(java.lang.Double.POSITIVE_INFINITY);
}
}
}
// STEP 1: Loop through all area-nodes (called BoundedNode)to detect the trips (demand) to the destination area
// The FLOW nodes are intermediate destinations, and dealt with in the next loop!!!
// the variable CellBehaviour(NTM) defines the traffic process within an area (the area is
// represented by the "BoundedNode"). This can be NTM behaviour, Cell transmission or other.
// during the simulation traffic enters and leaves the NTM areas. The number of "accumulated cars"
// represents the net balance of cars within the Nodes/areas. The new demand will be added!
// The variable TripsFrom contains information on trips from an origin/node to ALL other
// destinations.
// This origin can be the real origin or an intermediate area on the path to destination
// (neighbours).
// The structure (or Class in Java) named TripInfoDynamic is stored in a LinkedHashMap (lookup array) that
// contains this information for all destinations separately.
for (NTMNode node : model.getAreaGraph().vertexSet())
{
BoundedNode origin = (BoundedNode) node;
try
{
if (origin.getBehaviourType() == TrafficBehaviourType.NTM
|| origin.getBehaviourType() == TrafficBehaviourType.CORDON)
{
// only production, if there are accumulated cars!!
if (origin.getCellBehaviour().getAccumulatedCars() > 0.0)
{
// The object CellBehaviour contains simulation data, i.e. the number of accumulated cars in
// this cell, the demand/supply and others
// First update the demand per Area, as a function of the ACCUMULATION
if (origin.getBehaviourType() == TrafficBehaviourType.NTM)
{
// CellBehaviourNTM extends CellBehaviour and contains additional behaviour
CellBehaviourNTM cellBehaviourNTM = (CellBehaviourNTM) origin.getCellBehaviour();
// compute the total Demand (production) from an Area to all other Destinations (the level
// is based on the accumulation, the capacity of an area and the NFD algorithm).
// The new demand of this area is derived via the method RetrieveDemand that is based on the
// network fundamental diagram (see there for further details)
// **** RELEVANT: set DEMAND
double tripByHourPerLengthUnit = cellBehaviourNTM
.retrieveDemandPerLengthUnit(origin.getCellBehaviour().getAccumulatedCars(),
cellBehaviourNTM.getArea().getRoadLength(), cellBehaviourNTM.getParametersNTM())
.getInUnit(FrequencyUnit.PER_HOUR);
double tripByHour = tripByHourPerLengthUnit
* cellBehaviourNTM.getArea().getRoadLength().getInUnit(LengthUnit.KILOMETER);
double tripByTimeStep =
model.getSettingsNTM().getTimeStepDurationNTM().getInUnit(DurationUnit.HOUR) * tripByHour;
cellBehaviourNTM.setDemand(tripByTimeStep);
// compute the total supply (maximum) from neighbours to this Area (again based on the
// accumulation and NFD/area characteristics)
// TODO implement roadLength and length simulation step !!!!!!!!!!!!!!!!!!!!!!!!
// **** RELEVANT: set SUPPLY
double tripByHourSupply = cellBehaviourNTM
.retrieveSupplyPerLengthUnit(origin.getCellBehaviour().getAccumulatedCars(),
cellBehaviourNTM.getArea().getRoadLength(), cellBehaviourNTM.getParametersNTM())
.getInUnit(FrequencyUnit.PER_HOUR)
* cellBehaviourNTM.getArea().getRoadLength().getInUnit(LengthUnit.KILOMETER);
tripByTimeStep = model.getSettingsNTM().getTimeStepDurationNTM().getInUnit(DurationUnit.HOUR)
* tripByHourSupply;
cellBehaviourNTM.setSupply(tripByTimeStep);
Speed speed = cellBehaviourNTM.retrieveCurrentSpeed(cellBehaviourNTM.getAccumulatedCars(),
cellBehaviourNTM.getArea().getRoadLength());
if (speed == null)
{
speed = new Speed(0, SpeedUnit.KM_PER_HOUR);
}
origin.getArea().setCurrentSpeed(cellBehaviourNTM.retrieveCurrentSpeed(
cellBehaviourNTM.getAccumulatedCars(), cellBehaviourNTM.getArea().getRoadLength()));
}
// the border, or CORDON areas, act as sink/source for traffic
// The flow nodes act as entrances, intermediate or exits of the cell transmission model
// For this last category, we assume that the links are putting a restriction on capacity
else if (origin.getBehaviourType() == TrafficBehaviourType.CORDON)
{
// demand is the sum of new demand and accumulated traffic from previous time steps
// (possibly if the neighbour area does not accept all traffic)
// Within cordon areas all traffic can directly move to another area or Node!
// **** RELEVANT
origin.getCellBehaviour().setDemand(origin.getCellBehaviour().getAccumulatedCars());
// the total supply is infinite for Cordon and Flow nodes (sinks with no limit on in-flow)
// TODO can go to the initiation of this cellBehaviour
}
if (origin.getArea() != null)
{
origin.getArea().setAccumulatedCars(origin.getCellBehaviour().getAccumulatedCars());
}
else
{
System.out.println("test");
}
}
else
// if NO ACCUMULATION in a node, supply needs to be set, else it may become too low!!!!
// TODO: is this necessary?????????
{
if (origin.getBehaviourType() == TrafficBehaviourType.NTM)
{
// CellBehaviourNTM extends CellBehaviour (additional or different behaviour)
CellBehaviourNTM cellBehaviourNTM = (CellBehaviourNTM) origin.getCellBehaviour();
double tripByHour = cellBehaviourNTM
.retrieveSupplyPerLengthUnit(origin.getCellBehaviour().getAccumulatedCars(),
cellBehaviourNTM.getArea().getRoadLength(), cellBehaviourNTM.getParametersNTM())
.getInUnit(FrequencyUnit.PER_HOUR)
* cellBehaviourNTM.getArea().getRoadLength().getInUnit(LengthUnit.KILOMETER);
double tripByTimeStep =
model.getSettingsNTM().getTimeStepDurationNTM().getInUnit(DurationUnit.HOUR) * tripByHour;
cellBehaviourNTM.setSupply(tripByTimeStep);
}
}
// STEP 2
// After determining the initial demand from an area, we measure the potential demand to specific
// neighbour areas, and compare this to the capacity.
// When there are any capacity restrictions between two areas, this may restrict demand!
// TODO: also for other types of areas????
if (origin.getBehaviourType() == TrafficBehaviourType.NTM)
{
CellBehaviourNTM cellBehaviour = (CellBehaviourNTM) origin.getCellBehaviour();
LinkedHashMap borderDemand = new LinkedHashMap();
cellBehaviour.setBorderDemand(borderDemand);
for (TripInfoByDestination tripInfoByDestination : cellBehaviour.getTripInfoByDestinationMap().values())
{
if (tripInfoByDestination.getAccumulatedCarsToDestination() > 0)
{
// Compute the share of the accumulated trips to a certain destination as part of the
// total accumulation
Set neighbours = tripInfoByDestination.getRouteFractionToNeighbours().keySet();
for (BoundedNode neighbour : neighbours)
{
if (neighbour != null)
{
double demandToNeighbour =
(tripInfoByDestination.getAccumulatedCarsToNeighbour().get(neighbour)
/ cellBehaviour.getAccumulatedCars()) * cellBehaviour.getDemand();
double demandToNeighbourPerHour = demandToNeighbour * 3600 / model.getSettingsNTM()
.getTimeStepDurationNTM().getInUnit(DurationUnit.SECOND);
Frequency demand = new Frequency(demandToNeighbourPerHour, FrequencyUnit.PER_HOUR);
cellBehaviour.addBorderDemand(neighbour, demand);
}
}
}
}
}
// STEP 3
// after determining potential demand, we compute the demand for traffic to other nodes or areas
// only if we have demand of traffic for this area!!
if (origin.getCellBehaviour().getDemand() > 0)
{
for (TripInfoByDestination tripInfoByDestination : origin.getCellBehaviour()
.getTripInfoByDestinationMap().values())
{
// The tripInfoByNode includes information about the trips, specified by all destination
// zones
// In this step we are interested in the first zone we encounter ("neighbour") of the
// cars on their path to a certain destination Area.
// We determine the total DEMAND that wants to "enter" a neighbour
Set neighbours = tripInfoByDestination.getAccumulatedCarsToNeighbour().keySet();
for (BoundedNode neighbour : neighbours)
{
// Compute the share of the accumulated trips to a certain destination as part of the
// total accumulation
if (tripInfoByDestination.getAccumulatedCarsToNeighbour().get(neighbour) > 0)
{
// this potential out-flow is heading to the neighbour that is on its path to
// destination
// **** RELEVANT
double demandToNeighbourByDestination = 0;
demandToNeighbourByDestination =
tripInfoByDestination.getAccumulatedCarsToNeighbour().get(neighbour)
/ origin.getCellBehaviour().getAccumulatedCars()
* origin.getCellBehaviour().getDemand();
// in case of CAPACITY RESTRAINTS at borders, the actual demand could be lower
if (origin.getBehaviourType() == TrafficBehaviourType.NTM
|| origin.getBehaviourType() == TrafficBehaviourType.CORDON)
{
double ratioCapacityVersusDemand = 1.0;
CellBehaviour cellBehaviour = origin.getCellBehaviour();
if (cellBehaviour.getBorderDemand() != null)
{
if (cellBehaviour.getBorderDemand().get(neighbour) != null)
{
if (cellBehaviour.getBorderDemand().get(neighbour).getSI() > 0)
{
if (cellBehaviour.getBorderCapacity().get(neighbour) != null)
{
ratioCapacityVersusDemand = cellBehaviour.getBorderCapacity()
.get(neighbour).getSI()
/ cellBehaviour.getBorderDemand().get(neighbour).getSI();
}
else
{
ratioCapacityVersusDemand = 1;
}
}
}
}
demandToNeighbourByDestination =
Math.min(ratioCapacityVersusDemand, 1) * demandToNeighbourByDestination;
}
// **** RELEVANT
tripInfoByDestination.getDemandToNeighbour().put(neighbour, demandToNeighbourByDestination);
tripInfoByDestination.addDemandToDestination(demandToNeighbourByDestination);
// first add these trips to the number of trips that want to transfer to their
// neighbour
if (neighbour.getBehaviourType() == TrafficBehaviourType.NTM
|| neighbour.getBehaviourType() == TrafficBehaviourType.CORDON)
{
// this flow is also added to the total sum of traffic that wants to enter this
// neighbour Area.
// **** RELEVANT
neighbour.getCellBehaviour().addDemandToEnter(demandToNeighbourByDestination);
}
// the trips that enter the flow Links, are processed immediately (no timestep
// delay) to the first cell of the succeeding transmission link
else if (neighbour.getBehaviourType() == TrafficBehaviourType.FLOW)
{
Set nextNeighbours = neighbour.getCellBehaviour()
.getTripInfoByDestinationMap().get(tripInfoByDestination.getDestination())
.getDemandToNeighbour().keySet();
for (BoundedNode nextNeighbour : nextNeighbours)
{
double shareOfNeighbour = neighbour.getCellBehaviour().getTripInfoByDestinationMap()
.get(tripInfoByDestination.getDestination()).getRouteFractionToNeighbours()
.get(nextNeighbour);
// when entering a flow link, loop through all succeeding flow links until
// reaching
// a NTM or CORODN node :
if (nextNeighbour.getBehaviourType() == TrafficBehaviourType.FLOW)
{
// Retrieve the cell transmission link
// all cells (should) have identical characteristics
LinkCellTransmission ctmLink = (LinkCellTransmission) model.getAreaGraph()
.getEdge(neighbour, nextNeighbour).getLink();
// add the demand of trips that want to enter the first cell
// **** RELEVANT
ctmLink.getCells().get(0).getCellBehaviourFlow()
.addDemandToEnter(shareOfNeighbour * demandToNeighbourByDestination);
}
else if (neighbour.getBehaviourType() == TrafficBehaviourType.NTM
|| neighbour.getBehaviourType() == TrafficBehaviourType.CORDON)
{
nextNeighbour.getCellBehaviour()
.addDemandToEnter(shareOfNeighbour * demandToNeighbourByDestination);
}
}
}
}
else
{
// System.out.println("NTMSimulation line 379: Strange: no neighbour");
}
}
// In the next step, see whether this demand from nodes is able to enter completely or
// just partly (when supply is restricted)
}
}
}
}
// in the next steps, the dynamics of demand and supply create a certain flow between areas
catch (Exception e)
{
e.printStackTrace();
}
}
// Step 4: FLOW links
// Here we regard the generation of demand as a result of propagation of traffic on FLOW links
for (LinkEdge link : model.getAreaGraph().edgeSet())
{
{
if (link.getLink().getBehaviourType() == TrafficBehaviourType.FLOW)
{
LinkCellTransmission ctmLink = (LinkCellTransmission) link.getLink();
// Loop through the cells and do transmission
for (FlowCell cell : ctmLink.getCells())
{
double accumulationCell = cell.getCellBehaviourFlow().getAccumulatedCars();
// at the first cell, demand that wants to enter comes from the FlowNode!!
// maximum to enter this cell
// **** RELEVANT
// SUPPLY
Frequency tripByHour = cell.getCellBehaviourFlow().retrieveSupply(accumulationCell,
cell.getCellBehaviourFlow().getParametersFundamentalDiagram());
double tripByStep = model.getSettingsNTM().getTimeStepDurationNTM().getInUnit(DurationUnit.SECOND)
* tripByHour.getInUnit(FrequencyUnit.PER_SECOND);
cell.getCellBehaviourFlow().setSupply(tripByStep);
// DEMAND
if (accumulationCell > 0)
{
tripByHour = cell.getCellBehaviourFlow().retrieveDemand(accumulationCell,
cell.getCellBehaviourFlow().getParametersFundamentalDiagram());
tripByStep = model.getSettingsNTM().getTimeStepDurationNTM().getInUnit(DurationUnit.SECOND)
* tripByHour.getInUnit(FrequencyUnit.PER_SECOND);
cell.getCellBehaviourFlow().setDemand(tripByStep);
}
}
}
}
}
// STEP 5; after determining the total demand on FLOW links, here the demand by destination is determined
for (NTMNode node : model.getAreaGraph().vertexSet())
{
BoundedNode origin = (BoundedNode) node;
try
{
if (origin.getBehaviourType() == TrafficBehaviourType.FLOW)
{
// Update Demand and supply of the flow links
// Every node has "packages" of information for trips to a certain destination: the
// TripInfoByDestination
// Loop through this info to retrieve the demand by destination
for (TripInfoByDestination tripInfoByDestination : origin.getCellBehaviour().getTripInfoByDestinationMap()
.values())
{
// Every trips to a destination may have more than one route (and therefore have trips via
// different neighbours)
Set neighbours = tripInfoByDestination.getRouteFractionToNeighbours().keySet();
for (BoundedNode neighbour : neighbours)
{
if (neighbour.getId() != null)
{
// Only the flow links are considered in the CTM model, so we need a Flow node at
// the end of the link
if (neighbour.getBehaviourType() == TrafficBehaviourType.FLOW)
{
// In case of Cell Transmission Links, there is a process of
// traffic moving over a link. The demand via CELLS is computed here.
// In Step 6 see if this demand is below the capacity of the CELLS (supply)
try
{
LinkCellTransmission ctmLink = (LinkCellTransmission) model.getAreaGraph()
.getEdge(origin, neighbour).getLink();
// Loop through the cells and do transmission
for (FlowCell cell : ctmLink.getCells())
{
double accumulationCell = cell.getCellBehaviourFlow().getAccumulatedCars();
// retrieve the current info of trips in this cell
// at the first cell, demand comes from the FlowNode!!
// maximum to enter this cell
if (accumulationCell > 0)
{
// **** RELEVANT
// Distribute demand over destinations
TripInfoByDestination cellInfoByDestinationMap =
cell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.get(tripInfoByDestination.getDestination());
// we don't have to iterate over multiple neighbours: there is no
// specific one...
if (cellInfoByDestinationMap.getAccumulatedCarsToNeighbour()
.get(neighbour) != null)
{
double demandToDestination = cellInfoByDestinationMap
.getAccumulatedCarsToNeighbour().get(neighbour) / accumulationCell
* cell.getCellBehaviourFlow().getDemand();
// **** RELEVANT
cellInfoByDestinationMap.getDemandToNeighbour().put(neighbour,
demandToDestination);
}
}
}
// demand from last Cell to the first cell of the down stream flow link
FlowCell lastCell = ctmLink.getCells().get(ctmLink.getCells().size() - 1);
if (lastCell.getCellBehaviourFlow().getDemand() > 0)
{
Set nextNeighbours = neighbour.getCellBehaviour()
.getTripInfoByDestinationMap().get(tripInfoByDestination.getDestination())
.getRouteFractionToNeighbours().keySet();
for (BoundedNode nextNeighbour : nextNeighbours)
{
// double shareOfNeighbour =
// neighbour.getCellBehaviour().getTripInfoByDestinationMap()
// .get(tripInfoByDestination.getDestination())
// .getAccumulatedCarsToNeighbour().get(nextNeighbour);
if (lastCell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.get(tripInfoByDestination.getDestination()) == null)
{
if (model.getInputNTM().isDEBUG())
{
System.out.println("Stop");
}
}
if (lastCell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.get(tripInfoByDestination.getDestination()).getDemandToNeighbour()
.get(neighbour) != null)
{
double addDemandToDestination =
lastCell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.get(tripInfoByDestination.getDestination())
.getDemandToNeighbour().get(neighbour);
// lastCell.getCellBehaviourFlow().getTripInfoByDestinationMap()
// .get(tripInfoByDestination.getDestination())
// .getDemandToDestination();
// * tripInfoByDestination.getNeighbourAndRouteShare()
// .get(neighbour);
if (nextNeighbour.getBehaviourType() == TrafficBehaviourType.FLOW)
{
LinkCellTransmission ctmLinkNext = (LinkCellTransmission) model
.getAreaGraph().getEdge(neighbour, nextNeighbour).getLink();
ctmLinkNext.getCells().get(0).getCellBehaviourFlow()
.addDemandToEnter(addDemandToDestination); // shareOfNeighbour
// *
}
else if (nextNeighbour.getBehaviourType() == TrafficBehaviourType.NTM
|| nextNeighbour.getBehaviourType() == TrafficBehaviourType.CORDON)
{
nextNeighbour.getCellBehaviour()
.addDemandToEnter(addDemandToDestination); // shareOfNeighbour *
}
}
else
{
System.out.println("No demand to destination");
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
else if (neighbour.getBehaviourType() == TrafficBehaviourType.NTM
|| neighbour.getBehaviourType() == TrafficBehaviourType.CORDON)
{
System.out.println("CTMsimulation line 595: FLOW node has neighbour of type NTM");
}
else if (neighbour.getBehaviourType() != TrafficBehaviourType.NTM
&& neighbour.getBehaviourType() != TrafficBehaviourType.CORDON)
{
if (model.getInputNTM().isDEBUG())
{
System.out.println(
"CTMsimulation line 560: FLOW node has neighbour of type NTM or Cordon");
}
}
}
else
{
System.out.println("NTMSimulation line 565: Strange and to repair: nodeTo equals null");
}
}
}
}
}
// in the next steps, the dynamics of demand and supply create a certain flow between areas
catch (Exception e)
{
e.printStackTrace();
}
}
// ********************************************************************************************************
// STEP 6: FLUXES!!!!!
// Monitor whether the demand of traffic from outside areas is able to enter a certain Area
// Perhaps SUPPLY poses an upper bound on the Demand!
// First, NTM and Cordon nodes are being passed
for (NTMNode node : model.getAreaGraph().vertexSet())
{
BoundedNode origin = (BoundedNode) node;
try
{
if (origin.getCellBehaviour().getAccumulatedCars() < 0)
{
System.out.println("Negative accumulation: step " + steps);
}
else if (origin.getCellBehaviour().getAccumulatedCars() >= 0.0)
{
// Trips always start from an NTM or Cordon Node. If they pass a Flow Node they by definition enter
// a flow Link directly. The FLOW node is rather artificial: putting through but not storing
// anything
// The simulation of these flow links is carried out sequentially
if (origin.getBehaviourType() == TrafficBehaviourType.NTM
|| origin.getBehaviourType() == TrafficBehaviourType.CORDON)
{
for (TripInfoByDestination tripInfoByDestination : origin.getCellBehaviour()
.getTripInfoByDestinationMap().values())
{
if (tripInfoByDestination.getDemandToDestination() > 0)
{
Set neighbours = tripInfoByDestination.getDemandToNeighbour().keySet();
for (BoundedNode neighbour : neighbours)
{
BoundedNode destination = (BoundedNode) tripInfoByDestination.getDestination();
if (neighbour.getBehaviourType() == TrafficBehaviourType.NTM
|| neighbour.getBehaviourType() == TrafficBehaviourType.CORDON)
{
// retrieve the neighbour area on the path to a certain destination
if (neighbour != null
&& tripInfoByDestination.getDemandToNeighbour().get(neighbour) > 0.0)
{
double demandToNeighbour =
tripInfoByDestination.getDemandToNeighbour().get(neighbour);
// compute the share of traffic that wants to enter this Neighbour area from
// a certain origin - destination pair as part of the total demand that
// wants to enter the neighbour cell. The total supply to the neighbour may
// be restricted (by calling getSupply that provides the maximum Supply).
double totalDemandThatCanEnter =
Math.min(neighbour.getCellBehaviour().getDemandToEnter(),
neighbour.getCellBehaviour().getSupply());
double flowToNeighbour = 0;
if (neighbour.getCellBehaviour().getDemandToEnter() > 0 && demandToNeighbour > 0)
{
flowToNeighbour =
(demandToNeighbour / neighbour.getCellBehaviour().getDemandToEnter())
* totalDemandThatCanEnter;
}
// Compute the final flow based on the share of Trips
// to a certain destination and this maximum supply of the Cell.
// set the final flow to the neighbour
// **** RELEVANT
if (!neighbour.getId().equals(destination.getId()))
{
if (neighbour.getCellBehaviour().getTripInfoByDestinationMap()
.get(destination) == null)
{
if (model.getInputNTM().isDEBUG())
{
System.out.println(
found + " Step " + steps + ": Neighbour: " + neighbour.getId()
+ " has no destination " + destination.getId());
}
}
else
{
neighbour.getCellBehaviour().addAccumulatedCars(flowToNeighbour);
neighbour.getCellBehaviour().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(flowToNeighbour);
// TODO check this
Set nextNeighbours =
neighbour.getCellBehaviour().getTripInfoByDestinationMap()
.get(destination).getRouteFractionToNeighbours().keySet();
for (BoundedNode nextNeighbour : nextNeighbours)
{
double oldAccumulated = neighbour.getCellBehaviour()
.getTripInfoByDestinationMap().get(destination)
.getAccumulatedCarsToNeighbour().get(nextNeighbour);
double routeFraction = neighbour.getCellBehaviour()
.getTripInfoByDestinationMap().get(destination)
.getRouteFractionToNeighbours().get(nextNeighbour);
neighbour.getCellBehaviour().getTripInfoByDestinationMap()
.get(destination).getAccumulatedCarsToNeighbour()
.put(nextNeighbour,
routeFraction * flowToNeighbour + oldAccumulated);
}
}
}
else
{
if (flowToNeighbour > 0)
{
// TODO (what to do here??)
destination.getCellBehaviour().addArrivals(flowToNeighbour);
tripInfoByDestination.addArrivedTrips(flowToNeighbour);
}
}
double oldAccumulated =
tripInfoByDestination.getAccumulatedCarsToNeighbour().get(neighbour);
tripInfoByDestination.getAccumulatedCarsToNeighbour().put(neighbour,
oldAccumulated - flowToNeighbour);
tripInfoByDestination.addAccumulatedCarsToDestination(-flowToNeighbour);
tripInfoByDestination.addFluxToNeighbour(flowToNeighbour);
origin.getCellBehaviour().addAccumulatedCars(-flowToNeighbour);
}
else if (tripInfoByDestination.getDemandToDestination() == 0.0)
{
// System.out.println("NTMSimulation line 471: no demand to neighbour");
}
else
{
if (model.getInputNTM().isDEBUG())
{
System.out.println("NTMSimulation line 475: no neighbour");
}
}
}
// else flux to the first cell of the flow link: Do the simulation of flow links
else if (neighbour.getBehaviourType() == TrafficBehaviourType.FLOW)
{
// determine the next movement: a flow link
double demandToNeighbour = tripInfoByDestination.getDemandToNeighbour().get(neighbour);
// tripInfoByDestination.getDemandToDestination()
// * tripInfoByDestination.getRouteFractionToNeighbours().get(
// neighbour);
if (demandToNeighbour > 0)
{
// retrieve the downstream Flow node opportunities
Set nextNeighbours =
neighbour.getCellBehaviour().getTripInfoByDestinationMap().get(destination)
.getDemandToNeighbour().keySet();
// tripInfoByDestination.getNeighbourAndRouteShare().keySet();
for (BoundedNode nextNeighbour : nextNeighbours)
{
if (nextNeighbour.getBehaviourType() == TrafficBehaviourType.FLOW)
{
LinkCellTransmission ctmLink = (LinkCellTransmission) model.getAreaGraph()
.getEdge(neighbour, nextNeighbour).getLink();
TripInfoByDestination neighbourTripInfoByDestination = neighbour
.getCellBehaviour().getTripInfoByDestinationMap().get(destination);
double demandCell;
double supplyCell;
// at the first cell after the NTM/Cordon Node, demand comes from
// the FlowNode!!
FlowCell cell = ctmLink.getCells().get(0);
double share = neighbourTripInfoByDestination.getRouteFractionToNeighbours()
.get(nextNeighbour);
// * tripInfoByDestination.getNeighbourAndRouteShare().get(
// nextNeighbour);
double demandToNextNeighbour = demandToNeighbour * share;
if (demandToNextNeighbour > 0)
{
// maximum to enter this cell
supplyCell = cell.getCellBehaviourFlow().getSupply();
demandCell = cell.getCellBehaviourFlow().getDemandToEnter();
// tripInfoByDestination.getDemandToNeighbour();
double flowToDestination = 0;
if (demandCell > 0)
{
flowToDestination = Math.min(supplyCell / demandCell, 1.0)
* demandToNextNeighbour;
}
// **** RELEVANT
cell.getCellBehaviourFlow().addAccumulatedCars(flowToDestination);
if (cell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.get(destination) == null)
{
if (model.getInputNTM().isDEBUG())
{
System.out.println("NTM-Flow-NTM");
}
}
cell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.get(destination)
.addAccumulatedCarsToDestination(flowToDestination);
tripInfoByDestination
.addAccumulatedCarsToDestination(-flowToDestination);
origin.getCellBehaviour().addAccumulatedCars(-flowToDestination);
}
}
else
{
if (model.getInputNTM().isDEBUG())
{
System.out.println("NTM-Flow-NTM");
}
}
}
}
}
}
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
// STEP 6b: FLUXES of flow links!!!!!
for (LinkEdge link : model.getAreaGraph().edgeSet())
{
{
if (link.getLink().getBehaviourType() == TrafficBehaviourType.FLOW)
{
LinkCellTransmission ctmLink = (LinkCellTransmission) link.getLink();
simulateFlowLink(model.getAreaGraph(), model.getNodeAreaGraphMap(), ctmLink);
}
}
}
// FIRST: Loop through all nodes and reset the relevant variables such as DemandToEnter (for nodes with more
// than one entrance) to zero
for (LinkEdge link : model.getAreaGraph().edgeSet())
{
{
// only the feeding areas of the type NTM and Cordon can generate new traffic from the trip demand
// matrix
if (link.getLink().getBehaviourType() == TrafficBehaviourType.NTM
|| link.getLink().getBehaviourType() == TrafficBehaviourType.CORDON)
{
BoundedNode node = (BoundedNode) link.getLink().getStartNode();
BoundedNode nodeGraph = (BoundedNode) model.getNodeAreaGraphMap().get(node.getId());
nodeGraph.getCellBehaviour().setDepartures(0);
// set all fluxes from node to neighbour by destination to zero
for (TripInfoByDestination tripInfoByDestination : nodeGraph.getCellBehaviour()
.getTripInfoByDestinationMap().values())
{
tripInfoByDestination.setDepartedTrips(0);
}
}
}
}
// STEP 7: Add new Demand from the trip matrix to prepare for the next simulation cycle
// The variable TripsFrom contains information on trips from an origin/node to ALL other
// destinations.
for (NTMNode node : model.getAreaGraph().vertexSet())
{
BoundedNode origin = (BoundedNode) node;
try
{
// only the feeding areas of the type NTM and Cordon can generate new traffic from the trip demand
// matrix
if (origin.getBehaviourType() == TrafficBehaviourType.NTM
|| origin.getBehaviourType() == TrafficBehaviourType.CORDON)
{
Double maximumNumberOfTripsToAdd = Double.POSITIVE_INFINITY;
if (origin.getBehaviourType() == TrafficBehaviourType.NTM)
{
CellBehaviourNTM celBehaviourNTM = (CellBehaviourNTM) origin.getCellBehaviour();
int crit = celBehaviourNTM.getParametersNTM().getAccCritical().size();
double critDensityPerHour = celBehaviourNTM.getParametersNTM().getAccCritical().get(crit - 1);
double roadLength = 0.0;
if (origin.getArea() != null)
{
roadLength = origin.getArea().getRoadLength().getInUnit(LengthUnit.KILOMETER);
}
else
{
if (model.getInputNTM().isDEBUG())
{
System.out.println("no area connected to: " + origin.getId());
}
}
// double share =
// model.getSettingsNTM().getTimeStepDurationNTM().getInUnit(DurationUnit.SECOND) / 3600;
Double maxAccumulationThisArea = roadLength * critDensityPerHour;
maximumNumberOfTripsToAdd = maxAccumulationThisArea - origin.getCellBehaviour().getAccumulatedCars();
}
// total departures during this timestep:
double tripsInReservoir = 0;
// loop through all destinations to get total demand and supply per origin and add the new trips
for (TripInfoByDestination tripInfoByDestination : origin.getCellBehaviour().getTripInfoByDestinationMap()
.values())
// only select the final destinations: where Trips are heading to
{
tripsInReservoir += tripInfoByDestination.getTripsInReservoir();
}
Double totalTrips = TripDemand.getTotalNumberOfTripsFromOrigin(model.tripDemandToUse, origin.getId(),
currentTime, model.getSettingsNTM().getTimeStepDurationNTM()) + tripsInReservoir;
Double shareToAdd = Math.min(1, maximumNumberOfTripsToAdd / totalTrips);
if (shareToAdd < 1.0)
{
System.out.println("full");
}
Map tripsFrom = model.tripDemandToUse.getTripInfo().get(origin.getId());
// loop through all destinations to get total demand and supply per origin and add the new trips
for (TripInfoByDestination tripInfoByDestination : origin.getCellBehaviour().getTripInfoByDestinationMap()
.values())
// only select the final destinations: where Trips are heading to
{
BoundedNode destination = (BoundedNode) tripInfoByDestination.getDestination();
if (tripsFrom != null)
{
if (tripsFrom.get(destination.getId()) != null)
{
if (tripsFrom.get(destination.getId()).getNumberOfTrips() > 0)
{
// get the share of Trips of this time slice (NTM simulation step of 10 seconds)
// these new Trips are added to the TRIPS that are already on their way (passing an
// NTM area): the AccumulatedCars specified by their specific destination
// TODO if maxAccumulation, put TRIPS in a reservoir
double startingTrips =
shareToAdd * TripDemand.getTotalNumberOfTripsFromOriginToDestinationByTimeStep(
model.tripDemandToUse, origin.getId(), destination.getId(), currentTime,
model.getSettingsNTM().getTimeStepDurationNTM());
startingTrips += shareToAdd * tripInfoByDestination.getTripsInReservoir();
tripInfoByDestination.addTripsInReservoir((1 - shareToAdd) * startingTrips);
// **** RELEVANT (scale to other time periods or likewise)
if (model.getSettingsNTM().isIncreaseDemandByArea())
{
startingTrips *= model.getSettingsNTM().getScalingFactorDemand();
}
for (BoundedNode neighbour : origin.getCellBehaviour().getTripInfoByDestinationMap()
.get(destination).getRouteFractionToNeighbours().keySet())
{
double share = origin.getCellBehaviour().getTripInfoByDestinationMap().get(destination)
.getRouteFractionToNeighbours().get(neighbour);
double oldAccumulated =
tripInfoByDestination.getAccumulatedCarsToNeighbour().get(neighbour);
tripInfoByDestination.getAccumulatedCarsToNeighbour().put(neighbour,
oldAccumulated + share * startingTrips);
}
tripInfoByDestination.addAccumulatedCarsToDestination(startingTrips);
tripInfoByDestination.addDepartedTrips(startingTrips);
// increases the total number of accumulated cars in the area, that is
// used for NTM computations
// **** RELEVANT
origin.getCellBehaviour().addAccumulatedCars(startingTrips);
origin.getCellBehaviour().addDepartures(startingTrips);
}
}
}
}
}
}
// in the next steps, the dynamics of demand and supply create a certain flow between areas
catch (Exception e)
{
e.printStackTrace();
}
}
if (model.getInputNTM().isWRITEDATA())
{
WriteOutput.writeOutputDataFlowLinks(model, steps, MAXSTEPS);
WriteOutput.writeOutputDataNTM(model, steps, MAXSTEPS);
}
// FIRST: Loop through all nodes and reset the relevant variables such as DemandToEnter (for nodes with more
// than one entrance) to zero
for (LinkEdge link : model.getAreaGraph().edgeSet())
{
{
// only the feeding areas of the type NTM and Cordon can generate new traffic from the trip demand
// matrix
if (link.getLink().getBehaviourType() == TrafficBehaviourType.NTM
|| link.getLink().getBehaviourType() == TrafficBehaviourType.CORDON)
{
BoundedNode node = (BoundedNode) link.getLink().getStartNode();
BoundedNode nodeGraph = (BoundedNode) model.getNodeAreaGraphMap().get(node.getId());
nodeGraph.getCellBehaviour().setDemandToEnter(0);
nodeGraph.getCellBehaviour().setArrivals(0);
// set all fluxes from node to neighbour by destination to zero
for (TripInfoByDestination tripInfoByDestination : nodeGraph.getCellBehaviour()
.getTripInfoByDestinationMap().values())
{
tripInfoByDestination.setFluxToNeighbour(0);
tripInfoByDestination.setArrivedTrips(0);
tripInfoByDestination.setDemandToDestination(0);
}
}
else if (link.getLink().getBehaviourType() == TrafficBehaviourType.FLOW)
{
LinkCellTransmission ctmLink = (LinkCellTransmission) link.getLink();
// set the demand of trips that want to enter the first cell to zero
// **** RELEVANT
ctmLink.getCells().get(0).getCellBehaviourFlow().setDemandToEnter(0);
for (FlowCell cell : ctmLink.getCells())
{
for (TripInfoByDestination tripInfoByDestination : cell.getCellBehaviourFlow()
.getTripInfoByDestinationMap().values())
{
tripInfoByDestination.setFluxToNeighbour(0);
}
}
}
}
}
}
/**
* @param areaGraph SimpleDirectedWeightedGraph<NTMNode,LinkEdge<NTMLink>>;
* @param ctmLink LinkCellTransmission;
*/
public static void simulateFlowLink(SimpleDirectedWeightedGraph> areaGraph,
Map nodeAreaGraph, LinkCellTransmission ctmLink)
{
// Retrieve the cell transmission link
// all cells (should) have identical characteristics
FlowCell prevCell = null;
BoundedNode neighbour = (BoundedNode) nodeAreaGraph.get(ctmLink.getEndNode().getId());
for (FlowCell cell : ctmLink.getCells())
{
if (prevCell == null)
{
prevCell = cell;
}
else
{
double demandCell;
double supplyCell;
// determine the downstream cells (but the last):
if (ctmLink.getCells().indexOf(cell) < ctmLink.getCells().size())
{
supplyCell = cell.getCellBehaviourFlow().getSupply();
demandCell = prevCell.getCellBehaviourFlow().getDemand();
for (TripInfoByDestination tripInfoByDestination : cell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.values())
{
BoundedNode destination = (BoundedNode) tripInfoByDestination.getDestination();
double demandToNextCell = prevCell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination)
.getDemandToDestination();
if (demandToNextCell > 0)
{
// tripInfoByDestination.getDemandToNeighbour();
double flowToDestination = 0;
if (demandCell > 0)
{
flowToDestination = Math.min(supplyCell / demandCell, 1.0) * demandToNextCell;
}
// **** RELEVANT
cell.getCellBehaviourFlow().addAccumulatedCars(flowToDestination);
cell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(flowToDestination);
prevCell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(-flowToDestination);
prevCell.getCellBehaviourFlow().addAccumulatedCars(-flowToDestination);
}
}
}
}
double demandCell;
double supplyCell;
// the last cell of the cell transmission link transfers trips to the next Link/AreaNode
if (ctmLink.getCells().indexOf(cell) == ctmLink.getCells().size() - 1)
// at the last cell, the trips are forwarded to the next Node: could
// be NTM, Flow or Cordon
{
for (TripInfoByDestination tripInfoByDestination : cell.getCellBehaviourFlow().getTripInfoByDestinationMap()
.values())
{
BoundedNode destination = (BoundedNode) tripInfoByDestination.getDestination();
double demandToNextCell =
cell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination).getDemandToDestination();
if (demandToNextCell > 0)
{
TripInfoByDestination neighbourTripInfoByDestination;
neighbourTripInfoByDestination =
neighbour.getCellBehaviour().getTripInfoByDestinationMap().get(destination);
Set nextNeighbours =
neighbourTripInfoByDestination.getRouteFractionToNeighbours().keySet();
for (BoundedNode nextNeighbour : nextNeighbours)
{
double share = neighbourTripInfoByDestination.getRouteFractionToNeighbours().get(nextNeighbour);
if (nextNeighbour.getBehaviourType() == TrafficBehaviourType.NTM
|| nextNeighbour.getBehaviourType() == TrafficBehaviourType.CORDON)
{
supplyCell = nextNeighbour.getCellBehaviour().getSupply();
demandCell = nextNeighbour.getCellBehaviour().getDemandToEnter();
// tripInfoByDestination.getDemandToNeighbour();
double flowToDestination = 0;
if (demandCell > 0)
{
flowToDestination = share * Math.min(supplyCell / demandCell, 1.0) * demandToNextCell;
}
// **** RELEVANT
if (nextNeighbour != destination)
{
nextNeighbour.getCellBehaviour().addAccumulatedCars(flowToDestination);
nextNeighbour.getCellBehaviour().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(flowToDestination);
cell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(-flowToDestination);
cell.getCellBehaviourFlow().addAccumulatedCars(-flowToDestination);
}
else
{
// trips have arrived at destination!
if (demandToNextCell > 0)
{
destination.getCellBehaviour().addArrivals(flowToDestination);
cell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(-flowToDestination);
cell.getCellBehaviourFlow().addAccumulatedCars(-flowToDestination);
}
}
}
else if (nextNeighbour.getBehaviourType() == TrafficBehaviourType.FLOW)
{
// set the variables for the next loop (while...)
LinkCellTransmission ctmLinkNext =
(LinkCellTransmission) areaGraph.getEdge(neighbour, nextNeighbour).getLink();
// at the first cell after the NTM/Cordon Node, demand comes from
// the FlowNode!!
FlowCell firstCell = ctmLinkNext.getCells().get(0);
// * tripInfoByDestination.getNeighbourAndRouteShare().get(
// nextNeighbour);
if (demandToNextCell >= 0)
{
// maximum to enter this cell
supplyCell = firstCell.getCellBehaviourFlow().getSupply();
demandCell = firstCell.getCellBehaviourFlow().getDemandToEnter();
// tripInfoByDestination.getDemandToNeighbour();
double flowToDestination = 0;
if (demandCell > 0)
{
flowToDestination = share * Math.min(supplyCell / demandCell, 1.0) * demandToNextCell;
}
// **** RELEVANT
firstCell.getCellBehaviourFlow().addAccumulatedCars(flowToDestination);
firstCell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(flowToDestination);
cell.getCellBehaviourFlow().addAccumulatedCars(-flowToDestination);
cell.getCellBehaviourFlow().getTripInfoByDestinationMap().get(destination)
.addAccumulatedCarsToDestination(-flowToDestination);
}
}
}
}
}
}
prevCell = cell;
}
}
}