20 package org.matsim.core.mobsim.qsim.qnetsimengine;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.LinkedList;
26 import java.util.Queue;
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.apache.commons.lang3.tuple.Pair;
30 import org.apache.logging.log4j.LogManager;
31 import org.apache.logging.log4j.Logger;
78 final class QueueWithBuffer
implements QLaneI, SignalizeableItem {
79 private static final Logger log = LogManager.getLogger( QueueWithBuffer.class ) ;
82 public final void addFromWait(
final QVehicle veh) {
85 if (flowcap_accumulate.getValue() <= 0.0 && veh.getVehicle().getType().getPcuEquivalents() > context.qsimConfig
86 .getPcuThresholdForFlowCapacityEasing()) {
87 throw new IllegalStateException(
"Buffer of link " + this.
id +
" has no space left!");
117 this.value += value1;
118 this.timeStep = now ;
127 private boolean thisTimeStepGreen = true ;
128 private double inverseFlowCapacityPerTimeStep;
132 private double flowCapacityPerTimeStep;
133 private double remainingHolesStorageCapacity = 0.0 ;
135 private final Queue<QueueWithBuffer.Hole> holes =
new LinkedList<>();
138 private double bufferLastMovedTime = Double.NEGATIVE_INFINITY ;
146 private double storageCapacity;
147 private double usedStorageCapacity;
154 private final Queue<Pair<QVehicle,Double>> buffer =
new LinkedList<>() ;
164 private final AbstractQLink.QLinkInternalInterface qLinkInternalInterface;
166 private static int spaceCapWarningCount = 0;
167 final static double HOLE_SPEED_KM_H = 15.0;
169 private final double length ;
170 private double unscaledFlowCapacity_s = Double.NaN ;
171 private double effectiveNumberOfLanes = Double.NaN ;
176 private Pair<QVehicle,Double> lastBufferEntry = null;
181 private Pair<QVehicle,Double> lastQueueEntry = null;
184 private final VisData visData =
new VisDataImpl() ;
187 private double maxInflowUsedInQsim = Double.POSITIVE_INFINITY ;
188 private double effectiveNumberOfLanesUsedInQsim = Double.POSITIVE_INFINITY ;
190 private double accumulatedInflowCap = 1. ;
195 double length,
double effectiveNumberOfLanes,
double flowCapacity_s,
final NetsimEngineContext context,
203 this.flowEfficiencyCalculator = flowEfficiencyCalculator;
204 this.qLinkInternalInterface = qlink;
206 this.context = context ;
207 this.vehQueue = vehicleQueue ;
208 this.length = length;
209 this.unscaledFlowCapacity_s = flowCapacity_s ;
210 this.effectiveNumberOfLanes = effectiveNumberOfLanes;
216 this.calculateFlowCapacity();
217 this.calculateStorageCapacity();
219 flowcap_accumulate.
setValue(flowCapacityPerTimeStep);
222 private void addToBuffer(
final QVehicle veh) {
228 double flowConsumption = (lastBufferEntry == null) ?
229 getFlowCapacityConsumptionInEquivalents(veh, null, null) : getFlowCapacityConsumptionInEquivalents(veh, lastBufferEntry.getKey(), now - lastBufferEntry.getValue());
230 this.flowcap_accumulate.
addValue(-flowConsumption, now);
232 buffer.add(
new ImmutablePair<>(veh,flowConsumption));
233 lastBufferEntry =
new ImmutablePair<>(veh,now);
235 if (buffer.size() == 1) {
236 bufferLastMovedTime = now;
241 final QNodeI toNode = qLinkInternalInterface.getToNodeQ();
242 if ( toNode instanceof AbstractQNode ) {
243 ((AbstractQNode) toNode).activateNode();
255 private void moveQueueToBuffer() {
259 while ((veh = peekFromVehQueue()) != null) {
261 if (veh.getEarliestLinkExitTime() > now) {
269 now, veh, (TransitDriverAgent) driver, this.qLinkInternalInterface.
getId()
273 removeVehicleFromQueue(veh);
287 if ( qLinkInternalInterface.letVehicleArrive(veh )) {
289 removeVehicleFromQueue(veh);
297 if (!hasFlowCapacityLeft(veh)) {
301 removeVehicleFromQueue(veh);
307 noOfSeepModeBringFwd++;
313 public final boolean isAcceptingFromWait(
QVehicle veh) {
314 return this.hasFlowCapacityLeft(veh) ;
317 private boolean hasFlowCapacityLeft(
VisVehicle veh) {
319 updateFastFlowAccumulation();
326 private void updateFastFlowAccumulation(){
329 double remainingFlowCapThisTimeStep = subtractConsumptionOfVehiclesThatAreAlreadyInTheBuffer();
332 && this.flowcap_accumulate.
getValue() < remainingFlowCapThisTimeStep){
335 double accumulateFlowCap = timeSteps * flowCapacityPerTimeStep;
336 double newFlowCap = Math.min(flowcap_accumulate.
getValue() + accumulateFlowCap,
337 remainingFlowCapThisTimeStep);
339 flowcap_accumulate.
setValue(newFlowCap);
344 private void updateSlowFlowAccumulation(){
345 double remainingFlowCapThisTimeStep = subtractConsumptionOfVehiclesThatAreAlreadyInTheBuffer();
347 if (this.thisTimeStepGreen
348 && this.flowcap_accumulate.
getValue() < remainingFlowCapThisTimeStep){
349 double newFlowCap = Math.min(flowcap_accumulate.
getValue() + flowCapacityPerTimeStep,
350 remainingFlowCapThisTimeStep);
351 flowcap_accumulate.
setValue(newFlowCap);
355 private double subtractConsumptionOfVehiclesThatAreAlreadyInTheBuffer() {
356 double remainingFlowCapThisTimeStep = flowCapacityPerTimeStep;
357 for (Pair<QVehicle,Double> vehEfficiencyPair : buffer) {
359 remainingFlowCapThisTimeStep -= vehEfficiencyPair.getValue();
361 return remainingFlowCapThisTimeStep;
365 public final void initBeforeSimStep() {
367 updateSlowFlowAccumulation();
370 private static int wrnCnt=0 ;
371 private void calculateFlowCapacity() {
377 inverseFlowCapacityPerTimeStep = 1.0 / flowCapacityPerTimeStep;
380 this.effectiveNumberOfLanesUsedInQsim = this.effectiveNumberOfLanes;
381 this.maxInflowUsedInQsim = this.flowCapacityPerTimeStep;
400 final double maxFlowFromFdiag = (context.qsimConfig.
getFlowCapFactor() * this.effectiveNumberOfLanes/context.effectiveCellSize)
401 / ( 1./(HOLE_SPEED_KM_H/3.6) + 1/this.qLinkInternalInterface.getFreespeed() ) ;
402 final double minimumNumberOfLanesFromFdiag = this.flowCapacityPerTimeStep * context.effectiveCellSize * ( 1./(HOLE_SPEED_KM_H/3.6) + 1/this.qLinkInternalInterface.getFreespeed() );
409 log.warn(
"you are using the maximum capacity for one lane as the inflow capacity. This is the old standard behavior of the qsim and probably leads to wrong results " +
410 " as it does not respect the actual number of lanes nor the user-defined flow capacity. Please consider using" +
411 "InflowCapacitySetting.INCREASE_NUMBER_OF_LANES or InflowCapacitySetting.REDUCE_INFLOW_CAPACITY instead.");
418 this.maxInflowUsedInQsim = (1/context.effectiveCellSize) / ( 1./(HOLE_SPEED_KM_H/3.6) + 1/this.qLinkInternalInterface.getFreespeed() ) ;
420 qLinkInternalInterface.getLink().getAttributes().putAttribute(
"maxInflowUsedInQsim", 3600* maxInflowUsedInQsim /context.qsimConfig.
getTimeStepSize() );
425 log.warn(
"max flow from fdiag < flow cap in network file; linkId=" + qLinkInternalInterface.getId() +
426 "; network file flow cap/h=" + 3600. * flowCapacityPerTimeStep / context.qsimConfig.
getTimeStepSize() +
427 "; max flow from fdiag/h=" + 3600 * maxFlowFromFdiag / context.qsimConfig.
getTimeStepSize());
429 log.warn(
"number of lanes from fdiag > number of lanes in network file; linkId=" + qLinkInternalInterface.getId() +
430 "; number of lanes in network file=" + this.effectiveNumberOfLanes +
431 "; number of lanes from fdiag=" + minimumNumberOfLanesFromFdiag);
434 log.warn(
"The flow capacity will be reduced. See link attribute 'maxInflowUsedInQsim' written into the output network.");
436 log.warn(
"The number of lanes will be increased. See link attribute 'effectiveNumberOfLanesUsedInQsim' written into the output network.");
446 this.maxInflowUsedInQsim = maxFlowFromFdiag;
448 qLinkInternalInterface.getLink().getAttributes().putAttribute(
"maxInflowUsedInQsim", 3600* maxInflowUsedInQsim /context.qsimConfig.
getTimeStepSize() );
450 this.effectiveNumberOfLanesUsedInQsim = minimumNumberOfLanesFromFdiag;
452 qLinkInternalInterface.getLink().getAttributes().putAttribute(
"effectiveNumberOfLanesUsedInQsim", effectiveNumberOfLanesUsedInQsim );
454 throw new RuntimeException(
"The approach "+ inflowCapacitySetting.toString()+
" is not implemented yet.");
466 private void calculateStorageCapacity() {
472 storageCapacity = this.length * this.effectiveNumberOfLanesUsedInQsim / context.effectiveCellSize * context.qsimConfig.
getStorageCapFactor() ;
476 storageCapacity = Math.max(storageCapacity, getBufferStorageCapacity());
486 double freespeedTravelTime = this.length / qLinkInternalInterface.getFreespeed();
489 if (Double.isNaN(freespeedTravelTime)) {
490 throw new IllegalStateException(
"Double.NaN is not a valid freespeed travel time for a link. Please check the attributes length and freespeed!");
494 double tempStorageCapacity = freespeedTravelTime * unscaledFlowCapacity_s * context.qsimConfig.
getFlowCapFactor();
498 if (storageCapacity < tempStorageCapacity) {
499 if (QueueWithBuffer.spaceCapWarningCount <= 10) {
500 log.warn(
"Link " + this.
id +
" too small: enlarge storage capacity from: " + storageCapacity
501 +
" Vehicles to: " + tempStorageCapacity +
" Vehicles. This is not fatal, but modifies the traffic flow dynamics.");
502 if (QueueWithBuffer.spaceCapWarningCount == 10) {
503 log.warn(
"Additional warnings of this type are suppressed.");
505 QueueWithBuffer.spaceCapWarningCount++;
507 storageCapacity = tempStorageCapacity;
510 qLinkInternalInterface.getLink().getAttributes().putAttribute(
"storageCapacityUsedInQsim", storageCapacity );
530 final double freeSpeed = qLinkInternalInterface.getFreespeed() ;
531 final double holeSpeed = HOLE_SPEED_KM_H/3.6;
532 final double minStorCapForHoles = length * flowCapacityPerTimeStep * (freeSpeed + holeSpeed) / freeSpeed / holeSpeed ;
537 if ( storageCapacity < minStorCapForHoles ) {
538 if ( spaceCapWarningCount <= 10 ) {
539 log.warn(
"storage capacity not sufficient for holes; increasing from " + storageCapacity +
" to " + minStorCapForHoles ) ;
540 QueueWithBuffer.spaceCapWarningCount++;
542 storageCapacity = minStorCapForHoles ;
544 qLinkInternalInterface.getLink().getAttributes().putAttribute(
"storageCapacityUsedInQsim", storageCapacity );
547 remainingHolesStorageCapacity = this.storageCapacity;
556 private double getBufferStorageCapacity() {
557 return flowCapacityPerTimeStep;
561 public final boolean doSimStep( ) {
566 this.processArrivalOfHoles( ) ;
569 this.accumulatedInflowCap = Math.min(accumulatedInflowCap + maxInflowUsedInQsim, maxInflowUsedInQsim);
570 this.processArrivalOfHoles( ) ;
574 this.moveQueueToBuffer();
578 private void processArrivalOfHoles() {
580 while ( this.holes.size()>0 && this.holes.peek().getEarliestLinkExitTime() < now ) {
581 Hole hole = this.holes.poll();
582 this.remainingHolesStorageCapacity += hole.getSizeInEquivalents();
587 public final void addFromUpstream(
final QVehicle veh) {
591 if (hasMoreThanOneLane()) {
597 qLinkInternalInterface.activateLink();
607 double linkTravelTime = this.length / this.qLinkInternalInterface.getMaximumVelocityFromLinkSpeedCalculator(veh, now );
610 veh.setEarliestLinkExitTime(now + linkTravelTime);
622 this.qLinkInternalInterface.setCurrentLinkToVehicle(veh );
633 double flowConsumption = (lastQueueEntry == null) ?
634 getFlowCapacityConsumptionInEquivalents(veh, null, null) : getFlowCapacityConsumptionInEquivalents(veh, lastQueueEntry.getKey(), now - lastQueueEntry.getValue());
635 this.accumulatedInflowCap -= flowConsumption;
641 lastQueueEntry =
new ImmutablePair<>(veh, now);
644 private void removeVehicleFromQueue(
final QVehicle veh2Remove) {
651 QVehicle veh = pollFromVehQueue(veh2Remove);
666 QueueWithBuffer.Hole hole =
new QueueWithBuffer.Hole() ;
685 var holeTravelTime = length * 3.6 / HOLE_SPEED_KM_H;
686 var earliestExitTime = now + holeTravelTime;
687 hole.setEarliestLinkExitTime(earliestExitTime) ;
696 public final boolean isActive() {
698 return (!this.vehQueue.isEmpty())
699 || (!this.isNotOfferingVehicle() && context.qsimConfig.
isUseLanes())
700 || ( !this.holes.isEmpty() ) ;
702 return (this.flowcap_accumulate.
getValue() < flowCapacityPerTimeStep)
703 || (!this.vehQueue.isEmpty())
704 || (!this.isNotOfferingVehicle() && context.qsimConfig.
isUseLanes())
705 || ( !this.holes.isEmpty() );
710 public final void setSignalStateAllTurningMoves(
final SignalGroupState state) {
711 qSignalizedItem.setSignalStateAllTurningMoves(state);
713 thisTimeStepGreen = qSignalizedItem.hasGreenForAllToLinks();
718 public final double getSimulatedFlowCapacityPerTimeStep() {
719 return this.flowCapacityPerTimeStep;
723 public final boolean isAcceptingFromUpstream() {
724 boolean storageOk = usedStorageCapacity < storageCapacity ;
732 && remainingHolesStorageCapacity <= 0. ) {
745 return this.accumulatedInflowCap > 0;
750 public void recalcTimeVariantAttributes() {
757 calculateFlowCapacity();
758 calculateStorageCapacity();
759 flowcap_accumulate.
setValue(flowCapacityPerTimeStep);
772 for (
QVehicle veh : this.vehQueue) {
773 if (veh.
getId().equals(vehicleId))
776 for (Pair<QVehicle,Double> vehEfficiencyPair : this.buffer) {
777 if (vehEfficiencyPair.getKey().getId().equals(vehicleId))
778 return vehEfficiencyPair.getKey();
784 public final Collection<MobsimVehicle> getAllVehicles() {
788 Collection<MobsimVehicle> vehicles =
new ArrayList<>();
789 for (Pair<QVehicle, Double> pair : buffer) {
790 vehicles.add(pair.getKey());
792 vehicles.addAll(vehQueue);
797 public final QVehicle popFirstVehicle() {
799 QVehicle veh = removeFirstVehicle();
801 if ( hasMoreThanOneLane() ) {
808 private final QVehicle removeFirstVehicle(){
810 QVehicle veh = buffer.poll().getKey();
811 bufferLastMovedTime = now;
820 if (!qLinkInternalInterface.getToNode().getOutLinks().containsKey(toLinkId )){
821 throw new IllegalArgumentException(
"ToLink " + toLinkId +
" is not reachable from QLink Id " + this.
id );
823 qSignalizedItem.setSignalStateForTurningMove(state, toLinkId);
825 thisTimeStepGreen = qSignalizedItem.hasGreenForAllToLinks();
831 public final boolean hasGreenForToLink(
final Id<Link> toLinkId) {
832 if (qSignalizedItem != null){
833 return qSignalizedItem.hasGreenForToLink(toLinkId);
839 public boolean hasGreenForAllToLinks() {
840 if (qSignalizedItem != null) {
841 return qSignalizedItem.hasGreenForAllToLinks();
847 public final double getStorageCapacity() {
848 return storageCapacity;
852 public final boolean isNotOfferingVehicle() {
853 return buffer.isEmpty();
857 public final void clearVehicles() {
866 context.getAgentCounter().
incLost();
871 for (Pair<QVehicle,Double> bufferEntry : buffer) {
872 QVehicle veh = bufferEntry.getKey();
876 context.getAgentCounter().
incLost();
882 this.remainingHolesStorageCapacity = this.storageCapacity;
885 private double getFlowCapacityConsumptionInEquivalents(
QVehicle vehicle,
QVehicle prevVehicle, Double timeDiff) {
886 double flowEfficiency = flowEfficiencyCalculator.
calculateFlowEfficiency(vehicle, prevVehicle, timeDiff, qLinkInternalInterface.getLink(), id );
890 private boolean hasMoreThanOneLane() {
891 return this.qLinkInternalInterface.getAcceptingQLane() != this.qLinkInternalInterface.getOfferingQLanes().get(0 );
900 return this.visData ;
904 public final QVehicle getFirstVehicle() {
905 if (this.buffer.isEmpty()) {
906 return this.vehQueue.peek();
908 return this.buffer.peek().getKey() ;
912 public final double getLastMovementTimeOfFirstVehicle() {
913 return this.bufferLastMovedTime ;
920 public final void addTransitSlightlyUpstreamOfStop(
final QVehicle veh) {
925 public final void setSignalized(
final boolean isSignalized) {
930 public final void changeUnscaledFlowCapacityPerSecond(
final double val ) {
931 this.unscaledFlowCapacity_s = val ;
933 this.recalcTimeVariantAttributes();
937 public final void changeEffectiveNumberOfLanes(
final double val ) {
938 this.effectiveNumberOfLanes = val ;
940 this.recalcTimeVariantAttributes();
947 static final class Hole
implements QItem {
948 private double earliestLinkEndTime ;
952 public final double getEarliestLinkExitTime() {
953 return earliestLinkEndTime;
957 public final void setEarliestLinkExitTime(
double earliestLinkEndTime ) {
958 this.earliestLinkEndTime = earliestLinkEndTime;
962 public final double getSizeInEquivalents() {
966 final void setSizeInEquivalents(
double pcuFactorOfHole) {
967 this.pcu = pcuFactorOfHole;
987 private Coord upstreamCoord;
988 private Coord downstreamCoord;
991 public final Collection<AgentSnapshotInfo> addAgentSnapshotInfo(Collection<AgentSnapshotInfo> positions,
double now) {
992 if ( !buffer.isEmpty() || !vehQueue.isEmpty() || !holes.isEmpty() ) {
995 if ( this.upstreamCoord==null ) {
996 this.upstreamCoord = qLinkInternalInterface.getFromNode().getCoord() ;
998 if ( this.downstreamCoord==null ) {
999 this.downstreamCoord = qLinkInternalInterface.getToNode().getCoord() ;
1002 positions = context.snapshotInfoBuilder.positionVehiclesAlongLine(
1007 storageCapacity + getBufferStorageCapacity(),
1009 this.downstreamCoord,
1010 inverseFlowCapacityPerTimeStep,
1011 qLinkInternalInterface.getFreespeed(now ),
1013 qLinkInternalInterface.getNumberOfLanesAsInt(now ) ,
1015 qLinkInternalInterface
1022 void setVisInfo(
Coord upstreamCoord,
Coord downstreamCoord) {
1023 this.upstreamCoord = upstreamCoord;
1024 this.downstreamCoord = downstreamCoord;
1028 private int noOfSeepModeBringFwd = 0;
1030 private QVehicle peekFromVehQueue(){
1032 QVehicle returnVeh = vehQueue.peek();
1037 int maxSeepModeAllowed = 4;
1039 noOfSeepModeBringFwd = 0;
1044 newVehQueue.addAll(vehQueue);
1046 Iterator<QVehicle> it = newVehQueue.iterator();
1048 while(it.hasNext()){
1060 if(vehQueue.remove(veh2Remove)){
1063 throw new RuntimeException(
"Desired vehicle is not removed from vehQueue. Aborting...");
1068 public double getLoadIndicator() {
1069 return usedStorageCapacity;
1072 static final class Builder
implements LaneFactory {
1076 private Double length = null;
1077 private Double effectiveNumberOfLanes = null;
1078 private Double flowCapacity_s = null;
1082 this.context = context;
1090 this.vehicleQueue = vehicleQueue;
1097 void setLength(Double length) {
1098 this.length = length;
1101 void setEffectiveNumberOfLanes(Double effectiveNumberOfLanes) {
1102 this.effectiveNumberOfLanes = effectiveNumberOfLanes;
1105 void setFlowCapacity_s(Double flowCapacity_s) {
1106 this.flowCapacity_s = flowCapacity_s;
1110 this.flowEfficiencyCalculator = flowEfficiencyCalculator;
1114 public QueueWithBuffer createLane(AbstractQLink qLink) {
1119 if (length == null) {
1120 length = qLink.getLink().getLength();
1122 if (effectiveNumberOfLanes == null) {
1123 effectiveNumberOfLanes = qLink.getLink().getNumberOfLanes();
1125 if (flowCapacity_s == null) {
1126 flowCapacity_s = qLink.getLink().getFlowCapacityPerSec();
1128 if (flowEfficiencyCalculator == null) {
1131 return new QueueWithBuffer(qLink.getInternalInterface(), vehicleQueue, id, length, effectiveNumberOfLanes, flowCapacity_s, context, flowEfficiencyCalculator ) ;
InflowCapacitySetting getInflowCapacitySetting()
TrafficDynamics getTrafficDynamics()
boolean isSeepModeStorageFree
final boolean isUsingFastCapacityUpdate()
double getStorageCapFactor()
void setValue(double value)
LinkDynamics getLinkDynamics()
static final String FUTURE_SUPPRESSED
final double getPcuEquivalents()
boolean isRestrictingSeepage
double getFlowCapFactor()
void addFirst(E previous)
static< T > Id< T > create(final long key, final Class< T > type)
void processEvent(final Event event)
void addValue(double value1, double now)
void setTimeStep(double now)
Collection< String > getSeepModes()
static void assertNotNull(Object obj)
boolean isWantingToArriveOnCurrentLink()
final Id< VehicleType > getId()
MobsimDriverAgent getDriver()
double getSizeInEquivalents()
double getPcuThresholdForFlowCapacityEasing()
double calculateFlowEfficiency(QVehicle qVehicle, @Nullable QVehicle previousQVehicle, @Nullable Double timeGapToPreviousVeh, Link link, Id< Lane > laneId)