MATSIM
HLink.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * *
4  * *********************************************************************** *
5  * *
6  * copyright : (C) 2014 by the members listed in the COPYING, *
7  * LICENSE and WARRANTY file. *
8  * email : info at matsim dot org *
9  * *
10  * *********************************************************************** *
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * See also COPYING, LICENSE and WARRANTY file *
17  * *
18  * *********************************************************************** */
19 package org.matsim.core.mobsim.hermes;
20 
21 import java.util.Iterator;
22 
23 class HLink {
24 
25  private float currentCapacity;
26  private final int initialCapacity;
27 
28  // Id of the link.
29  private final int id;
30 
31  // The whole purpose of this implementation is to have a dynamically sized queue that never goes over the capacity
32  // restriction. This becomes a big memory waste when large scenarios are used. This implementation is inspired in
33  // Java's implementation of ArrayDequeue.
34  public static class AgentQueue implements Iterable<Agent> {
35 
36  // the storage
37  private Agent[] array;
38  // the max capacity of the queue
39  private int maxCapacity;
40 
41  // Pop/peak from head
42  private int head;
43  // Push to tail
44  private int tail;
45  // Number of elements in the queue
46  private int size;
47 
48  public AgentQueue(int maxCapacity, int initialcapacity) {
49  this.maxCapacity = maxCapacity;
50  this.array = new Agent[initialcapacity];
51  }
52 
53  private int inc(int number) {
54  if (++number == array.length) {
55  number = 0;
56  }
57  return number;
58  }
59 
60  public boolean forcePush(Agent agent) {
61  maxCapacity++;
62  return push(agent);
63  }
64 
65  public boolean push(Agent agent) {
66 
67  if (size == 0) {
68  array[tail] = agent;
69  size += 1;
70  return true;
71  } else if (array.length > size) {
72  array[tail = inc(tail)] = agent;
73  size += 1;
74  return true;
75  } else {
76  // expand array
77  Agent[] narray = new Agent[array.length * 2];
78  for (int i = head, left = size, dst = 0; left > 0; i = inc(i), left--, dst++) {
79  narray[dst] = array[i];
80  }
81  array = narray;
82  head = 0;
83  tail = size - 1;
84  // push
85  array[tail = inc(tail)] = agent;
86  size += 1;
87  return true;
88  }
89  }
90 
91  public Agent peek() {
92  return size == 0 ? null : array[head];
93  }
94 
95  public void pop() {
96  if (size > 0) {
97  array[head] = null;
98  head = inc(head);
99  if (--size == 0) {
100  tail = head = 0;
101  }
102  }
103  }
104 
105  public int size() {
106  return size;
107  }
108 
109  public void clear() {
110  for (int i = head, left = size; left > 0; i = inc(i), left--) {
111  array[i] = null;
112  }
113  head = tail = size = 0;
114  }
115 
116  @Override
117  public Iterator<Agent> iterator() {
118  return new Iterator<>() {
119 
120  private int idx = head;
121  private int left = size;
122 
123  @Override
124  public boolean hasNext() {
125  return left-- > 0;
126  }
127 
128  @Override
129  public Agent next() {
130  Agent agent = array[idx];
131  idx = inc(idx);
132  return agent;
133  }
134  };
135  }
136 
137  public int capacity() {
138  return maxCapacity;
139  }
140  }
141  // Length of the link in meters.
142  private final int length;
143  // Max velocity within the link (meters per second).
144  private final int velocity;
145  // Queues of agents on this link. Boundary links use both queues.
146  private final AgentQueue queue;
147  // Number of vehicles that can leave the link per time second.
148  private final float flowCapacityPerS;
149  private float flowLeftInTimestep;
150  private int lastUpdate;
151  // When (which timestep) flow was updated the last time.
152  private int nextFreeFlowSlot;
153  private int lastPush;
154  private final int stuckTimePeriod;
155 
156  public HLink(int id, int capacity, int length, int velocity, float flowCapacityperSecond, int stuckTimePeriod) {
157  this.id = id;
158  this.length = length;
159  this.velocity = velocity;
160  this.flowCapacityPerS = flowCapacityperSecond;
161  this.stuckTimePeriod = stuckTimePeriod;
162  this.lastPush = 0;
163  this.lastUpdate = 0;
164  this.nextFreeFlowSlot = 0;
165  this.initialCapacity = capacity;
166  this.currentCapacity = capacity;
167  this.flowLeftInTimestep = flowCapacityperSecond;
168 
169  // We do not preallocate using the capacity because it leads to huge memory waste.
170  //this.queue = new AgentQueue(Math.max(1, capacity));
171  this.queue = new AgentQueue(Math.max(1, capacity), Math.min(capacity, 16));
172  }
173 
174  public void reset() {
175  queue.clear();
176  this.nextFreeFlowSlot = 0;
177  this.lastPush = 0;
178  this.lastUpdate = 0;
179  this.currentCapacity = initialCapacity;
180  this.flowLeftInTimestep = flowCapacityPerS;
181 
182  }
183 
184  public boolean push(Agent agent, int timestep, float storageCapacityPCU) {
185  //avoid long vehicles not being able to enter a short link
186  float effectiveStorageCapacity = Math.min(storageCapacityPCU, initialCapacity);
187  if (currentCapacity - effectiveStorageCapacity >= 0) {
188  if (queue.push(agent)) {
189  lastPush = timestep;
190  currentCapacity = currentCapacity - effectiveStorageCapacity;
191  return true;
192  } else {
193  throw new RuntimeException("should not happen?");
194  }
195  } else if (stuckTimePeriod != Integer.MAX_VALUE && (lastPush + stuckTimePeriod) < timestep) {
196  boolean result = queue.forcePush(agent);
197  lastPush = timestep;
198  currentCapacity = currentCapacity - effectiveStorageCapacity;
199  return result;
200  } else {
201  return false;
202  }
203  }
204 
205  public void pop(float storageCapacityPCE) {
206  queue.pop();
207  currentCapacity += storageCapacityPCE;
208  }
209 
210  public int nexttime () {
211  if (queue.size() == 0) {
212  return 0;
213  } else {
214  return queue.peek().linkFinishTime;
215  }
216  }
217 
218  public int length() {
219  return this.length;
220  }
221 
222  public boolean flow(int timestep, float requestedFlow) {
223  if (timestep >= nextFreeFlowSlot) {
224  // if requestedFlow<flowCapacityPerS, more than one vehicle can pass per timestep
225  if (lastUpdate == timestep){
226  if (flowLeftInTimestep >= 0) {
227  flowLeftInTimestep-=requestedFlow;
228  nextFreeFlowSlot = timestep + (int) Math.floor( requestedFlow / flowCapacityPerS);
229  return true;
230  } else return false;
231  } else {
232  flowLeftInTimestep=flowLeftInTimestep+flowCapacityPerS-requestedFlow;
233  lastUpdate = timestep;
234  nextFreeFlowSlot = timestep + (int) Math.floor( requestedFlow / flowCapacityPerS);
235  return true;
236  }
237 
238  } else {
239  return false;
240  }
241  }
242 
243  public int velocity() {
244  return this.velocity;
245  }
246 
247  public AgentQueue queue() {
248  return this.queue;
249  }
250 
251  public int capacity() {
252  return this.queue.capacity();
253  }
254 
255  public int id() {
256  return this.id;
257  }
258 }