HeadwayGeneratorDemand.java
package org.opentrafficsim.road.gtu.generator;
import java.rmi.RemoteException;
import org.djunits.unit.TimeUnit;
import org.djunits.value.ValueException;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Frequency;
import org.djunits.value.vdouble.scalar.Time;
import org.djunits.value.vdouble.vector.FrequencyVector;
import org.djunits.value.vdouble.vector.TimeVector;
import org.opentrafficsim.core.distributions.Generator;
import org.opentrafficsim.core.distributions.ProbabilityException;
import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
import nl.tudelft.simulation.language.Throw;
/**
* <p>
* Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
* <p>
* @version $Revision$, $LastChangedDate$, by $Author$, initial version 17 nov. 2016 <br>
* @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
* @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
* @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
*/
// TODO Link this class to an OD
public class HeadwayGeneratorDemand implements Generator<Duration>
{
/** Interpolation of demand. */
private final Interpolation interpolation;
/** Vector of time. */
private final TimeVector timeVector;
/** Vector of flow values. */
private final FrequencyVector demandVector;
/** Simulator. */
private final OTSSimulatorInterface simulator;
/** Stream name of headway generation. */
private static final String HEADWAY_STREAM = "headwayGeneration";
/**
* @param timeVector a time vector
* @param demandVector the corresponding demand vector
* @param simulator the simulator
*/
public HeadwayGeneratorDemand(final TimeVector timeVector, final FrequencyVector demandVector,
final OTSSimulatorInterface simulator)
{
this(timeVector, demandVector, simulator, Interpolation.STEPWISE);
}
/**
* @param timeVector a time vector
* @param demandVector the corresponding demand vector
* @param simulator the simulator
* @param interpolation interpolation type
*/
public HeadwayGeneratorDemand(final TimeVector timeVector, final FrequencyVector demandVector,
final OTSSimulatorInterface simulator, final Interpolation interpolation)
{
Throw.whenNull(timeVector, "Time vector may not be null.");
Throw.whenNull(demandVector, "Demand vector may not be null.");
Throw.whenNull(simulator, "Simulator may not be null.");
Throw.whenNull(interpolation, "Interpolation may not be null.");
try
{
Throw.whenNull(simulator.getReplication().getStream(HEADWAY_STREAM),
"Could not obtain random stream '" + HEADWAY_STREAM + "'.");
}
catch (RemoteException exception)
{
throw new RuntimeException("Could not obtain replication.", exception);
}
for (int i = 0; i < timeVector.size() - 1; i++)
{
try
{
Throw.when(timeVector.get(i).ge(timeVector.get(i + 1)), IllegalArgumentException.class,
"Time vector is not increasing.");
}
catch (ValueException exception)
{
throw new RuntimeException(
"Value out of range of time vector. Note that HeadwayGenerator does not create a safe copy.",
exception);
}
}
Throw.when(timeVector.size() != demandVector.size(), IllegalArgumentException.class,
"Time and flow vector should be of the same size.");
Throw.when(timeVector.size() < 2, IllegalArgumentException.class, "Time and flow vector should be at least of size 2.");
this.timeVector = timeVector;
this.demandVector = demandVector;
this.simulator = simulator;
this.interpolation = interpolation;
}
/** {@inheritDoc} */
@Override
public Duration draw() throws ProbabilityException, ParameterException
{
Time time = this.simulator.getSimulatorTime().getTime();
try
{
Throw.when(time.lt(this.timeVector.get(0)), IllegalArgumentException.class,
"Cannot return a headway at time before first time in vector.");
// get time period of current time
int i = 0;
while (this.timeVector.get(i + 1).lt(time) && i < this.timeVector.size() - 1)
{
i++;
}
try
{
return nextArrival(i, time.minus(this.timeVector.get(i)), 1.0).minus(time);
}
catch (RemoteException exception)
{
throw new RuntimeException("Could not obtain replication.", exception);
}
}
catch (ValueException exception)
{
throw new RuntimeException(
"Value out of range of time or demand vector. Note that HeadwayGenerator does not create safe copies.",
exception);
}
}
/**
* Recursive determination of the next arrival time. Each recursion moves to the next time period. This occurs if a randomly
* determined arrival falls outside of a time period, or when demand in a time period is 0.
* @param i index of time period
* @param start reference time from start of period i, pertains to previous arrival, or zero during recursion
* @param fractionRemaining remaining fraction of headway to apply due to time in earlier time periods
* @return time of next arrival
* @throws ValueException in case of an illegal time vector
* @throws RemoteException in case of not being able to retrieve the replication
*/
private Time nextArrival(final int i, final Duration start, final double fractionRemaining)
throws ValueException, RemoteException
{
// escape if beyond specified time by infinite next arrival (= no traffic)
if (i == this.timeVector.size() - 1)
{
return new Time(Double.POSITIVE_INFINITY, TimeUnit.BASE);
}
// skip zero-demand periods
if (this.demandVector.get(i).equals(Frequency.ZERO))
{
// after zero-demand, the next headway is a random fraction of a random headway as there is no previous arrival
return nextArrival(i + 1, Duration.ZERO, this.simulator.getReplication().getStream(HEADWAY_STREAM).nextDouble());
}
// calculate headway from demand
Frequency demand;
if (this.interpolation.isStepWise())
{
demand = this.demandVector.get(i);
}
else
{
double f = start.si / (this.timeVector.get(i + 1).si - this.timeVector.get(i).si);
demand = Frequency.interpolate(this.demandVector.get(i), this.demandVector.get(i + 1), f);
}
double t = -Math.log(this.simulator.getReplication().getStream(HEADWAY_STREAM).nextDouble()) / demand.si;
// calculate arrival
Time arrival = new Time(this.timeVector.get(i).si + start.si + t * fractionRemaining, TimeUnit.BASE);
// go to next period if arrival is beyond current period
if (arrival.gt(this.timeVector.get(i + 1)))
{
double inStep = this.timeVector.get(i + 1).si - (this.timeVector.get(i).si + start.si);
return nextArrival(i + 1, Duration.ZERO, fractionRemaining - inStep / t);
}
return arrival;
}
}