src/app/simulation/simulation.service.ts
Applies the traffic flow propagation algorithm.
Properties |
|
Methods |
|
constructor(store: Store
|
||||||||||||
Defined in src/app/simulation/simulation.service.ts:58
|
||||||||||||
Parameters :
|
Private endLtm |
endLtm()
|
Defined in src/app/simulation/simulation.service.ts:235
|
The algorithm ends when all the vehicles are started and there is no more traffic volume on the links.
Returns :
boolean
|
Private getAvgSpeed |
getAvgSpeed()
|
Defined in src/app/simulation/simulation.service.ts:416
|
The sum of the densities for the velocity square divided by the sum of the flows.
Returns :
number
|
Private getCounts |
getCounts()
|
Defined in src/app/simulation/simulation.service.ts:394
|
Returns :
Counts
|
Public getStatistics |
getStatistics()
|
Defined in src/app/simulation/simulation.service.ts:167
|
Extracts the network statistics.
Returns :
any
|
Private getTotalAvgSpeed |
getTotalAvgSpeed()
|
Defined in src/app/simulation/simulation.service.ts:434
|
Returns :
number
|
Public init |
init()
|
Defined in src/app/simulation/simulation.service.ts:83
|
Initializes the simulation.
Returns :
Observable<any>
|
Private initEdges |
initEdges()
|
Defined in src/app/simulation/simulation.service.ts:346
|
Returns :
void
|
Private initOdNodes |
initOdNodes()
|
Defined in src/app/simulation/simulation.service.ts:315
|
Returns :
void
|
Private initPaths |
initPaths()
|
Defined in src/app/simulation/simulation.service.ts:264
|
Initializes existing paths.
Returns :
void
|
Private initPathsDemand | ||||||||||||
initPathsDemand(odMatrix: number[], startingTimes: number[])
|
||||||||||||
Defined in src/app/simulation/simulation.service.ts:293
|
||||||||||||
Initializes the demand for paths as the rate of the total demand.
Parameters :
Returns :
void
|
Private initTimeInterval |
initTimeInterval()
|
Defined in src/app/simulation/simulation.service.ts:251
|
Initializes the time interval as the smallest connection travel time.
Returns :
void
|
Private ltm |
ltm()
|
Defined in src/app/simulation/simulation.service.ts:190
|
Link Transmission Model algorithm.
Returns :
void
|
Private numericalSimulation |
numericalSimulation()
|
Defined in src/app/simulation/simulation.service.ts:373
|
Returns :
NumericalSimulation[]
|
Public propagateFlows |
propagateFlows()
|
Defined in src/app/simulation/simulation.service.ts:115
|
Performs a flows propagation cycle.
Returns :
void
|
Public reset |
reset()
|
Defined in src/app/simulation/simulation.service.ts:66
|
Returns :
void
|
Public resetFlows |
resetFlows()
|
Defined in src/app/simulation/simulation.service.ts:144
|
Resets the initial values.
Returns :
void
|
Private takeFirstStep | ||||||||
takeFirstStep(node: LtmNode)
|
||||||||
Defined in src/app/simulation/simulation.service.ts:204
|
||||||||
Calculates sending and receiving flows.
Parameters :
Returns :
void
|
Private takeSecondStep | ||||||||
takeSecondStep(node: LtmNode)
|
||||||||
Defined in src/app/simulation/simulation.service.ts:219
|
||||||||
Determines the transition flows from incoming links to outgoing links.
Parameters :
Returns :
void
|
Private takeThirdStep | ||||||||
takeThirdStep(node: LtmNode)
|
||||||||
Defined in src/app/simulation/simulation.service.ts:227
|
||||||||
Updates the cumulative vehicles number.
Parameters :
Returns :
void
|
Private updateStatistics |
updateStatistics()
|
Defined in src/app/simulation/simulation.service.ts:360
|
Returns :
void
|
Private updateTimePeriods |
updateTimePeriods()
|
Defined in src/app/simulation/simulation.service.ts:369
|
Returns :
void
|
Private avgSpeeds |
avgSpeeds:
|
Type : number[]
|
Default value : []
|
Defined in src/app/simulation/simulation.service.ts:58
|
The network average speed for each step. |
Private graph |
graph:
|
Type : LtmGraph
|
Defined in src/app/simulation/simulation.service.ts:25
|
Ltm graph instance. |
Private paths |
paths:
|
Type : any[]
|
Default value : []
|
Defined in src/app/simulation/simulation.service.ts:43
|
Existing paths for each:
|
Private pathsDemand |
pathsDemand:
|
Type : number[]
|
Default value : []
|
Defined in src/app/simulation/simulation.service.ts:48
|
Demand of each path. |
Private pathsStartingTimes |
pathsStartingTimes:
|
Type : number[]
|
Default value : []
|
Defined in src/app/simulation/simulation.service.ts:53
|
Starting time of each path. |
Private timeInterval |
timeInterval:
|
Type : number
|
Defined in src/app/simulation/simulation.service.ts:35
|
The time period is divided into time intervals. |
Private timePeriods |
timePeriods:
|
Type : number[]
|
Default value : []
|
Defined in src/app/simulation/simulation.service.ts:30
|
The cumulated time period. |
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Store } from '@ngrx/store';
import { NetworkService } from '../network/network.service';
import { DemandService } from '../demand/demand.service';
import * as fromSimulation from './models/reducers';
import { SimulationActionTypes } from './models/actions/simulation.actions';
import { Tag } from '../network/graph';
import { LtmGraph, LtmEdge, LtmNode } from './ltm-graph';
import { NumericalSimulation, Counts } from './models/simulation-state';
import { Statistics } from './statistics';
import { round } from '../utils';
import { uiConfig } from '../ui/ui-config';
/**
* Applies the traffic flow propagation algorithm.
*/
@Injectable() export class SimulationService {
/**
* Ltm graph instance.
*/
private graph: LtmGraph;
/**
* The cumulated time period.
*/
private timePeriods: number[] = [];
/**
* The time period is divided into time intervals.
*/
private timeInterval: number;
/**
* Existing paths for each:
* - [path][origin][link]
* - [path][link][link]
* - [path][link][destination]
*/
private paths: any[] = [];
/**
* Demand of each path.
*/
private pathsDemand: number[] = [];
/**
* Starting time of each path.
*/
private pathsStartingTimes: number[] = [];
/**
* The network average speed for each step.
*/
private avgSpeeds: number[] = [];
constructor(
private store: Store<fromSimulation.SimulationState>,
private network: NetworkService,
private demand: DemandService
) { }
public reset(): void {
this.graph = null;
this.timePeriods = [];
this.timeInterval = 0;
this.paths = [];
this.pathsDemand = [];
this.pathsStartingTimes = [];
this.avgSpeeds = [];
// Simulation state.
this.store.dispatch({
type: SimulationActionTypes.Reset
});
}
/**
* Initializes the simulation.
*/
public init(): Observable<any> {
// Gets graph from network.
const graph = this.network.getGraph();
// Gets O/D matrix from demand.
const demand = this.demand.getOdMatrix();
// Gets starting times from demand.
const startingTimes = this.demand.getStartingTimes();
// Instances LTM graph from graph.
this.graph = new LtmGraph(graph);
// Sets the time period.
this.timePeriods[0] = 0;
// Initializes time interval.
this.initTimeInterval();
// Initializes existing paths.
this.initPaths();
// Initializes paths demand.
this.initPathsDemand(demand, startingTimes);
// Initializes O/D nodes expected flows.
this.initOdNodes();
// Initializes edges upstream and downstream.
this.initEdges();
// Updates simulation state.
this.store.dispatch({
type: SimulationActionTypes.PeriodsChanged,
payload: { timeInterval: this.timeInterval, timePeriods: this.timePeriods }
});
return of(null);
}
/**
* Performs a flows propagation cycle.
*/
public propagateFlows(): void {
// Performs a LTM algorithm cycle.
this.ltm();
// Updates statistics.
this.updateStatistics();
// Updates time period.
this.updateTimePeriods();
// Updates simulation state.
this.store.dispatch({
type: SimulationActionTypes.PeriodsChanged,
payload: { timeInterval: this.timeInterval, timePeriods: this.timePeriods }
});
this.store.dispatch({
type: SimulationActionTypes.SimulationChanged,
payload: { simulation: { data: this.numericalSimulation(), counts: this.getCounts(), avgSpeed: this.getAvgSpeed() } }
});
// Checks if the simulation is finished.
if (this.endLtm()) {
// Updates simulation state.
this.store.dispatch({
type: SimulationActionTypes.SimulationEnded,
payload: true
});
}
}
/**
* Resets the initial values.
*/
public resetFlows(): void {
// Resets.
this.graph.reset();
this.timePeriods = [];
this.avgSpeeds = [];
// Reinitializes.
this.timePeriods[0] = 0;
this.initOdNodes();
this.initEdges();
// Updates simulation state.
this.store.dispatch({
type: SimulationActionTypes.PeriodsChanged,
payload: { timeInterval: this.timeInterval, timePeriods: this.timePeriods }
});
this.store.dispatch({
type: SimulationActionTypes.SimulationChanged,
payload: { simulation: { data: [], counts: {}, avgSpeed: null } }
});
}
/**
* Extracts the network statistics.
*/
public getStatistics(): any {
const edges = this.graph.getEdges().filter((edge: LtmEdge) =>
edge.distance > uiConfig.minDistance && edge.duration > uiConfig.minDuration
);
return {
totalTime: this.timePeriods[this.timePeriods.length - 1],
totalAvgSpeed: this.getTotalAvgSpeed(),
heavyTrafficLabels: Statistics.getHeavyTrafficLabels(edges),
moderateTrafficLabels: Statistics.getModerateTrafficLabels(edges),
heavyTrafficData: Statistics.getHeavyTrafficData(edges, this.timeInterval),
moderateTrafficData: Statistics.getModerateyTrafficData(edges, this.timeInterval),
busiestEdgeLabel: Statistics.getBusiestEdgeLabel(edges),
busiestEdgeData: Statistics.getBusiestEdgeData(edges),
busiestEdgeCapacity: Statistics.getBusiestEdgeCapacity(edges),
busiestEdgeDelay: Statistics.getBusiestEdgeDelay(edges, this.timePeriods),
periods: this.timePeriods
};
}
/**
* Link Transmission Model algorithm.
*/
private ltm(): void {
const nodes = this.graph.getNodes();
// For each node on the paths.
for (const node of nodes) {
this.takeFirstStep(node);
this.takeSecondStep(node);
this.takeThirdStep(node);
}
}
/**
* Calculates sending and receiving flows.
* @param node The node on the paths
*/
private takeFirstStep(node: LtmNode): void {
// Sending flows.
for (const edge of node.incomingEdges) {
edge.calcSendingFlows(this.timePeriods, this.timeInterval, this.paths);
}
// Receiving flows.
for (const edge of node.outgoingEdges) {
edge.calcReceivingFlow(this.timePeriods, this.timeInterval);
}
}
/**
* Determines the transition flows from incoming links to outgoing links.
* @param node The node on the paths
*/
private takeSecondStep(node: LtmNode): void {
node.calcTransitionFlows(this.timePeriods, this.timeInterval, this.paths);
}
/**
* Updates the cumulative vehicles number.
* @param node The node on the paths
*/
private takeThirdStep(node: LtmNode): void {
node.updateCumulativeFlows(this.paths);
}
/**
* The algorithm ends when all the vehicles are started
* and there is no more traffic volume on the links.
*/
private endLtm(): boolean {
const originNodes = this.graph.getOriginNodes();
const edges = this.graph.getEdges();
const started = originNodes.filter((node: LtmNode) =>
node.origin.sendingFlow == node.origin.expectedInflow
).length == originNodes.length;
const busy = edges.filter(
(edge: LtmEdge) =>
edge.trafficVolume > 0
).length > 0;
return started && !busy;
}
/**
* Initializes the time interval as the smallest connection travel time.
*/
private initTimeInterval(): void {
const edges = this.graph.getEdges();
const edge = edges.reduce((prev: LtmEdge, curr: LtmEdge) => prev.duration < curr.duration ? prev : curr);
if (edge.duration > uiConfig.maxTimeInterval) {
this.timeInterval = uiConfig.maxTimeInterval;
} else {
this.timeInterval = edge.duration;
}
}
/**
* Initializes existing paths.
*/
private initPaths(): void {
const shortestPaths = this.graph.getShortestPaths();
let i = 0;
for (let z = 0; z < shortestPaths.length; z++) {
for (let n = 0; n < shortestPaths[z].length; n++) {
this.paths[i] = {};
for (let m = 0; m < shortestPaths[z][n].length; m++) {
const edge = shortestPaths[z][n][m];
if (m == 0) {
this.paths[i][edge.origin.label] = edge.label;
}
if (m < shortestPaths[z][n].length - 1) {
this.paths[i][edge.label] = shortestPaths[z][n][m + 1].label;
}
if (m == shortestPaths[z][n].length - 1) {
this.paths[i][edge.label] = edge.destination.label;
}
}
i++;
}
}
}
/**
* Initializes the demand for paths as the rate of the total demand.
* @param odMatrix The O/D matrix
* @param startingTimes The starting times of O/D pairs
*/
private initPathsDemand(odMatrix: number[], startingTimes: number[]): void {
const assignmentMatrix = this.graph.getAssignmentMatrix();
let i = 0;
for (let z = 0; z < assignmentMatrix.length; z++) {
if (odMatrix[z] != null) {
const pos = i;
let sum = 0;
for (let n = 0; n < assignmentMatrix[z].length; n++) {
const p = assignmentMatrix[z][n].find(value => value > 0) || 0;
this.pathsDemand[i] = round(p * odMatrix[z]);
this.pathsStartingTimes[i] = startingTimes[z];
sum += this.pathsDemand[i];
i++;
}
if (odMatrix[z] - sum > 0) { this.pathsDemand[pos] = odMatrix[z] - sum; }
}
}
}
private initOdNodes(): void {
const shortestPaths = this.graph.getShortestPaths();
let i = 0;
for (let z = 0; z < shortestPaths.length; z++) {
for (let n = 0; n < shortestPaths[z].length; n++) {
const origin = shortestPaths[z][n][0].origin;
const destination = shortestPaths[z][n][shortestPaths[z][n].length - 1].destination;
if (!origin.origin) {
origin.origin = {
sendingFlow: 0,
expectedInflows: [],
expectedInflow: 0,
startingTimes: []
};
}
origin.origin.expectedInflows[i] = this.pathsDemand[i];
origin.origin.expectedInflow += this.pathsDemand[i];
origin.origin.startingTimes[i] = this.pathsStartingTimes[i];
if (!destination.destination) {
destination.destination = {
receivingFlow: 0,
expectedOutFlow: 0
};
}
destination.destination.expectedOutFlow += this.pathsDemand[i];
i++;
}
}
}
private initEdges(): void {
const edges = this.graph.getEdges();
for (const edge of edges) {
for (let i = 0; i < this.paths.length; i++) {
edge.upstreams[i] = [];
edge.upstreams[i][0] = 0;
edge.upstream[0] = 0;
edge.downstreams[i] = [];
edge.downstreams[i][0] = 0;
edge.downstream[0] = 0;
}
}
}
private updateStatistics(): void {
const edges = this.graph.getEdges();
for (const edge of edges) {
edge.updateTrafficVolume();
edge.updateTrafficCounts();
edge.updateMoes(this.timeInterval);
}
}
private updateTimePeriods(): void {
this.timePeriods.push(this.timePeriods[this.timePeriods.length - 1] + this.timeInterval);
}
private numericalSimulation(): NumericalSimulation[] {
const data: NumericalSimulation[] = [];
const edges = this.graph.getEdges();
for (const edge of edges) {
const wayTag = edge.tags.find((tag: Tag) => tag.key == 'name');
const wayName = wayTag ? wayTag.value : '';
data.push(
{
edge: edge.label,
wayName: wayName,
trafficVolume: edge.trafficVolume,
trafficCount: edge.trafficCount,
delay: edge.delay,
stops: edge.stops
}
);
}
return data;
}
private getCounts(): Counts {
let startedVehicles = 0;
let arrivedVehicles = 0;
const nodes = this.graph.getNodes();
for (const node of nodes) {
if (node.origin) {
startedVehicles += node.origin.sendingFlow;
}
if (node.destination) {
arrivedVehicles += node.destination.receivingFlow;
}
}
return {
startedVehicles: startedVehicles,
arrivedVehicles: arrivedVehicles
};
}
/**
* The sum of the densities for the velocity square divided by the sum of the flows.
*/
private getAvgSpeed(): number {
const edges = this.graph.getEdges();
let sum = 0;
let flow = 0;
for (const edge of edges) {
sum += edge.velocity > 0 ?
(edge.getKjam() * (edge.freeFlowVelocity - edge.velocity) / edge.freeFlowVelocity) * Math.pow(edge.velocity, 2)
: 0;
flow += edge.velocity > 0 ?
(edge.getKjam() * (edge.freeFlowVelocity - edge.velocity) / edge.freeFlowVelocity) * edge.velocity
: 0;
}
const speed = flow > 0 ? round(sum / flow, 2) : 0;
this.avgSpeeds.push(speed);
return speed;
}
private getTotalAvgSpeed(): number {
const sum = this.avgSpeeds.reduce((a, b) => a + b, 0);
return round(sum / this.avgSpeeds.length, 2);
}
}