Beispiel #1
0
def get_new_hd(lcsidefol, veh, lcsidelane):
    """Calculates new headways for a vehicle and its left or right follower.

    Args:
        lcsidefol: either the lfol or rfol of veh.
        veh: Vehicle whose lane changing model is being evaluated
        lcsidelane: the lcside lane of veh.

    Returns:
        newlcsidefolhd: new float headway for lcsidefol
        newlcsidehd: new float headway for veh
    """
    # helper functino for set_lc_helper
    lcsidelead = lcsidefol.lead
    if lcsidelead is None:
        newlcsidehd = None
    else:
        newlcsidehd = get_headway(veh, lcsidelead)
        # if newlcsidehd < 0:
        # newlcsidehd = 1e-6
    if lcsidefol.cf_parameters is None:
        newlcsidefolhd = None
    else:
        newlcsidefolhd = get_headway(lcsidefol, veh)
        # if newlcsidefolhd < 0:
        # newlcsidefolhd = 1e-6

    return newlcsidefolhd, newlcsidehd
Beispiel #2
0
def update_calibration(vehicles, add_events, lc_events, addtime, lctime, timeind, dt, lc_event):
    """Main logic for a single step of the Calibration simulation.

    At the beginning of the timestep, vehicles/states/events are assumed to be fully updated. Then, in order,
        -call each vehicle's cf model (done in Calibration.step).
        -check for lead change events in the next timestep, and apply them if applicable.
        -update all vehicle's states, except headway, for the next timestep.
        -check for add events, and add the vehicles if applicable.
        -update all vehicle's headways.
    Then, when the timestep is incremented, all vehicles/states/events are fully updated, and the iteration
    can continue.

    Args:
        vehicles: set of vehicles currently in simulation
        add_events: sorted list of current add events
        lc_events: sorted list of current lead change events
        addtime: next time an add event occurs
        lctime: next time a lead change event occurs
        timeind: time index
        dt: timestep
        lc_event: function to apply a single entry in lc_events
    """
    lctime = update_lc_event(lc_events, lctime, timeind, dt, lc_event)

    for veh in vehicles:
        veh.update(timeind, dt)

    addtime = update_add_event(vehicles, add_events, addtime, timeind, dt, lc_event)

    for veh in vehicles:
        veh.hd = get_headway(veh, veh.lead)

    return addtime, lctime
Beispiel #3
0
def new_relaxation(veh, timeind, dt, relax_speed=False):
    """Generates relaxation for a vehicle after it experiences a lane change.

    This is called directly after a vehicle changes it lane, while it still has the old value for its
    headway, and its position has not yet been updated.
    See (https://arxiv.org/abs/1904.08395) for an explanation of the relaxation model.
    This implements single parameter relaxation. The Vehicle attributes associated with relaxation are
        relax_parameters: float which gives the relaxation constant (units of time)
        in_relax: bool of whether or not vehicle is experiencing relaxation currently
        relax: list of floats which stores the relaxation values
        relax_start: time index (timeind) of the 0 index of relax
        relaxmem: memory of past relaxation, list of tuples of (start, end, relax)

    Args:
        veh: Vehicle to add relaxation to
        timeind: int giving the timestep of the simulation (0 indexed)
        dt: float of time unit that passes in each timestep
        relax_speed: If True, we relax leader's speed as well.

    Returns:
        None. Modifies relaxation attributes for vehicle in place.
    """
    rp = veh.relax_parameters
    if rp is None:  # no relax -> do nothing
        return
    if veh.lead is None:
        if veh.in_relax:  # new lead is None -> reset relaxation
            veh.in_relax = False
            veh.relax = veh.relax[:timeind - veh.relax_start]
            veh.relaxmem.append((veh.relax, veh.relax_start))
        return

    prevlead = veh.leadmem[-2][0]
    if prevlead is None:
        olds = veh.get_eql(veh.speed)
        # olds = veh.get_eql(veh.lead.speed)
    else:
        olds = veh.hd
    news = get_headway(veh, veh.lead)

    if relax_speed:  # relax speed + headway so we have a list of tuples
        if prevlead is None:
            oldv = veh.speed
        else:
            oldv = prevlead.speed
        newv = veh.lead.speed

        relaxamount_s = olds - news
        relaxamount_v = oldv - newv
        relax_helper_vhd(rp, relaxamount_s, relaxamount_v, veh, timeind, dt)

    else:  # relax headway only = list of float of relax values
        relaxamount = olds - news  # edge case error here, see update_lane_routes.update_veh_after_lc
        relax_helper(rp, relaxamount, veh, timeind, dt)
Beispiel #4
0
    def simulate(self, parameters):
        """Does a full simulation and returns the loss.

        Args:
            parameters: list of parameters for the vehicles.

        Returns:
            loss (float).
        """
        # reset all vehicles, and assign their parameters
        for veh in self.all_vehicles:
            veh.initialize(parameters)

        # initialize events, add the first vehicle(s), initialize vehicles headways/LeadVehicles
        self.vehicles = set()
        self.add_events = self.all_add_events.copy()
        self.lc_events = self.all_lc_events.copy()
        self.addtime = self.add_events[-1][0]
        self.lctime = self.lc_events[-1][0] if len(self.lc_events)>0 else math.inf
        self.timeind = self.starttime
        self.addtime = update_add_event(self.vehicles, self.add_events, self.addtime, self.timeind-1, self.dt,
                                        self.lc_event)
        for veh in self.vehicles:
            if veh.in_leadveh:
                veh.leadveh.update(self.timeind)
            veh.hd = get_headway(veh, veh.lead)


        # do simulation by calling step repeatedly
        for i in range(self.endtime - self.starttime):
            self.step()

        # calculate and return loss
        loss = 0
        for veh in self.all_vehicles:
            loss += veh.loss()

        return loss
Beispiel #5
0
def update_net(vehicles, lc_actions, inflow_lanes, merge_lanes, vehid, timeind,
               dt):
    """Updates all quantities for a road network.

    The simulation logics are as follows. At the beginning of the timestep, all vehicles/states/events
    are assumed to be fully updated for the current timestep. Then, in order:
        -evaluate the longitudinal action (cf model) for all vehicles (done in Simulation.step)
        -evaluation the latitudinal action (lc model) for all vehicles (done in Simulation.step)
        -complete requested lane changes
            -move vehicle to the new lane and set its new route/lane events
            -updating all leader/follower relationships (including l/rfol) for all vehicles involved. This
            means updating vehicles in up to 4 lanes (opside, self lane, lcside, and new lcside).
            -apply relaxation to any vehicles as necessary
        -update all states and memory for all vehicles
        -update headway for all vehicles
        -update any merge anchors
        -updating lane/route events for all vehicles, including removing vehicles if they leave the network.
        -update all lfol/rfol relationships
        -update the inflow conditions for all lanes with inflow, including possible adding new vehicles,
            and generating the parameters for the next new vehicle
    After the updating is complete, all vehicles/states/events are updated to their values for the next
    timestep, so when the time index is incremented, the iteration can continue.

    Args:
        vehicles: set containing all vehicle objects being actively simulated
        lc_actions: dictionary with keys as vehicles which request lane changes in the current timestep,
            values are a string either 'l' or 'r' which indicates the side of the change
        inflow_lanes: iterable of lanes which have upstream (inflow) boundary conditions. These lanes
            must have get_inflow, increment_inflow, and new_vehicle methods
        merge_lanes: iterable of lanes which have merge anchors. These lanes must have a merge_anchors
            attribute
        vehid: int giving the unique vehicle ID for the next vehicle to be generated
        timeind: int giving the timestep of the simulation (0 indexed)
        dt: float of time unit that passes in each timestep

    Returns:
        vehid: updated int value of next vehicle ID to be added
        remove_vehicles: list of vehicles which were removed from simulation at current timestep

        Modifies vehicles in place (potentially all their non-parameter/bounds attributes, e.g. pos/spd,
        lead/fol relationships, their lane/roads, memory, etc.).
    """
    # update followers/leaders for all lane changes
    relaxvehs = []  # keeps track of vehicles which have relaxation applied
    for veh in lc_actions.keys():
        relaxvehs.append(veh.fol)

        # update leader follower relationships, lane/road
        update_lane_routes.update_veh_after_lc(
            lc_actions, veh, timeind)  # this is the only thing
        # which can't be done in parralel

        relaxvehs.append(veh)
        relaxvehs.append(veh.fol)

        # update a vehicle's lane events and route events for the new lane
        update_lane_routes.set_lane_events(veh)
        update_lane_routes.set_route_events(veh, timeind)

    for veh in set(relaxvehs):  # apply relaxation
        veh.set_relax(timeind, dt)

    # update all states, memory and headway
    for veh in vehicles:
        veh.update(timeind, dt)
    for veh in vehicles:
        if veh.lead is not None:
            veh.hd = get_headway(veh, veh.lead)

    # update left and right followers
    # vehicle_orders.update_all_lrfol(vehicles)
    vehicle_orders.update_all_lrfol_multiple(
        vehicles)  # handles edge cases explicitly, slower

    # update merge_anchors
    for curlane in merge_lanes:
        update_lane_routes.update_merge_anchors(curlane, lc_actions)

    # update roads (lane events) and routes
    remove_vehicles = []
    for veh in vehicles:
        # check vehicle's lane events and route events, acting if necessary
        update_lane_routes.update_lane_events(veh, timeind, remove_vehicles)
        update_lane_routes.update_route_events(veh, timeind)
    # remove vehicles which leave
    for veh in remove_vehicles:
        vehicles.remove(veh)

    # update inflow, adding vehicles if necessary
    for curlane in inflow_lanes:
        vehid = curlane.increment_inflow(vehicles, vehid, timeind, dt)

    # for veh in vehicles:  # debugging
    #     if not veh._chk_leadfol(verbose = False):
    #         # print('-------- Report for Vehicle '+str(veh.vehid)+' at time '+str(timeind)+'--------')
    #         # veh._leadfol()
    #         veh._chk_leadfol()
    #         # raise ValueError('incorrect vehicle order')

    return vehid, remove_vehicles
Beispiel #6
0
    def _chk_leadfol(self, verbose=True):
        """Returns True if the leader/follower relationships of the Vehicle are correct."""
        # If verbose = True, we print whether each test is passing or not.
        lfolpass = True
        lfolmsg = []
        if self.lfol is not None:
            if self.lfol is self:
                lfolpass = False
                lfolmsg.append('lfol is self')
            if self not in self.lfol.rlead:
                lfolpass = False
                lfolmsg.append('rlead of lfol is missing self')
            if self.lfol.lane.anchor is not self.llane.anchor:
                lfolpass = False
                lfolmsg.append('lfol is not in left lane')
            if get_dist(self, self.lfol) > 0:
                lfolpass = False
                lfolmsg.append('lfol is in front of self')
            if self.lfol.lead is not None:
                if get_dist(self, self.lfol.lead) < 0:
                    lfolpass = False
                    lfolmsg.append('lfol leader is behind self')
            lead, fol = self.lane.leadfol_find(self, self.lfol)
            if fol is not self.lfol:
                if self.lfol.pos == self.pos:
                    pass
                else:
                    lfolpass = False
                    lfolmsg.append('lfol is not correct vehicle - should be ' +
                                   str(fol))
        elif self.llane is not None:
            unused, fol = self.llane.leadfol_find(self, self.llane.anchor)
            lfolpass = False
            lfolmsg.append('lfol is None - should be ' + str(fol))
        rfolpass = True
        rfolmsg = []
        if self.rfol is not None:
            if self.rfol is self:
                rfolpass = False
                rfolmsg.append('rfol is self')
            if self not in self.rfol.llead:
                rfolpass = False
                rfolmsg.append('llead of rfol is missing self')
            if self.rfol.lane.anchor is not self.rlane.anchor:
                rfolpass = False
                rfolmsg.append('rfol is not in right lane')
            if get_dist(self, self.rfol) > 0:
                rfolpass = False
                rfolmsg.append('rfol is in front of self')
            if self.rfol.lead is not None:
                if get_dist(self, self.rfol.lead) < 0:
                    rfolpass = False
                    rfolmsg.append('rfol leader is behind self')
            lead, fol = self.lane.leadfol_find(self, self.rfol)
            if fol is not self.rfol:
                if self.rfol.pos == self.pos:
                    pass
                else:
                    rfolpass = False
                    rfolmsg.append('rfol is not correct vehicle - should be ' +
                                   str(fol))
        elif self.rlane is not None:
            unused, fol = self.rlane.leadfol_find(self, self.rlane.anchor)
            rfolpass = False
            rfolmsg.append('lfol is None - should be ' + str(fol))
        rleadpass = True
        rleadmsg = []
        for i in self.rlead:
            if i.lfol is not self:
                rleadpass = False
                rleadmsg.append('rlead does not have self as lfol')
            if get_dist(self, i) < 0:
                rleadpass = False
                rleadmsg.append('rlead is behind self')
        if len(self.rlead) != len(set(self.rlead)):
            rleadpass = False
            rleadmsg.append('repeated rlead')
        lleadpass = True
        lleadmsg = []
        for i in self.llead:
            if i.rfol is not self:
                lleadpass = False
                lleadmsg.append('llead does not have self as rfol')
            if get_dist(self, i) < 0:
                lleadpass = False
                lleadmsg.append('llead is behind self')
        if len(self.llead) != len(set(self.llead)):
            lleadpass = False
            lleadmsg.append('repeated llead')
        leadpass = True
        leadmsg = []
        if self.lead is not None:
            if self.lead.fol is not self:
                leadpass = False
                leadmsg.append('leader does not have self as follower')
            if get_headway(self, self.lead) < 0:
                leadpass = False
                leadmsg.append('leader is behind self')

        folpass = True
        folmsg = []
        if self.fol.lead is not self:
            folpass = False
            folmsg.append('follower does not have self as leader')
        if get_headway(self, self.fol) > 0:
            folpass = False
            folmsg.append('follower is ahead of self')

        res = lfolpass and rfolpass and rleadpass and lleadpass and leadpass and folpass
        if verbose:
            if res:
                print('passing results for ' + str(self))
            else:
                print('errors for ' + str(self))
            if not lfolpass:
                for i in lfolmsg:
                    print(i)
            if not rfolpass:
                for i in rfolmsg:
                    print(i)
            if not rleadpass:
                for i in rleadmsg:
                    print(i)
            if not lleadpass:
                for i in lleadmsg:
                    print(i)
            if not leadpass:
                for i in leadmsg:
                    print(i)
            if not folpass:
                for i in folmsg:
                    print(i)

        return res
Beispiel #7
0
def set_lc_helper(veh,
                  lside,
                  rside,
                  timeind,
                  chk_lc=True,
                  chk_lc_prob=1,
                  get_fol=True):
    """Calculates the new headways to be passed to the lane changing (LC) model.

    Evaluates the lane changing situation to decide if we need to evaluate lane changing model on the
    left side, right side, both sides, or neither. For any sides we need to evaluate, finds the new headways
    (new vehicle headway, new lcside follower headway).
    If new headways are negative, returns positive instead.

    Args:
        veh: Vehicle to have their lane changing model called.
        timeind: time index
        chk_lc: bool, if True we are in mandatory or active discretionary state. If False we are in normal
            discretionary state and don't necessarily evaluate the LC model every timestep.
        chk_lc_prob: float between 0 and 1 which gives the probability of checking the lane changing model
            when the vehicle is in the normal discretionary state.
        get_fol: if True, we also find the new follower headway.

    Returns:
        bool: True if we want to call the lane changing model.
        tuple of floats: (newlfolhd, newlhd, newrfolhd, newrhd, newfolhd). float headways,
            giving the new headway for that vehicle. If get_fol = False, newfolhd is not present.
            If a vehicle would have no leader in the new configuration, None is returned as the headway. If
            a AnchorVehicle acts as a (l/r)fol, the headway is computed as normal.
    """
    if not lside and not rside:
        return False, None
    if not chk_lc:  # decide if we want to evaluate lc model or not - this only applies to discretionary state
        # when vehicle is not actively trying to change
        if timeind < veh.disc_cooldown:
            return False, None
        if chk_lc_prob >= 1:
            pass
        elif np.random.rand() > chk_lc_prob:
            return False, None

    # next we compute quantities to send to LC model for the required sides
    if lside:
        newlfolhd, newlhd = get_new_hd(veh.lfol, veh, veh.llane)
    else:
        newlfolhd = newlhd = None

    if rside:
        newrfolhd, newrhd = get_new_hd(veh.rfol, veh, veh.rlane)
    else:
        newrfolhd = newrhd = None

    if get_fol:
        if veh.lead is None:
            newfolhd = None
        else:
            newfolhd = get_headway(veh.fol, veh.lead)
    else:
        return True, (newlfolhd, newlhd, newrfolhd, newrhd)

    return True, (newlfolhd, newlhd, newrfolhd, newrhd, newfolhd)
Beispiel #8
0
def set_lc_helper(veh, chk_lc=1, get_fol=True):
    """Calculates the new headways to be passed to the lane changing (LC) model.

    Evaluates the lane changing situation to decide if we need to evaluate lane changing model on the
    left side, right side, both sides, or neither. For any sides we need to evaluate, finds the new headways
    (new vehicle headway, new lcside follower headway).
    If new headways are negative, returns positive instead.

    Args:
        veh: Vehicle to have their lane changing model called.
        chk_lc: float between 0 and 1 which gives the probability of checking the lane changing model
            when the vehicle is in a discretionary state.
        get_fol: if True, we also find the new follower headway.

    Returns:
        bool: True if we want to call the lane changing model.
        tuple of floats: (lside, rside, newlfolhd, newlhd, newrfolhd, newrhd, newfolhd). lside/rside
            are bools which are True if we need to check that side in the LC model. rest are float headways,
            giving the new headway for that vehicle. If get_fol = False, newfolhd is not present.
    """
    # first determine what situation we are in and which sides we need to check
    l_lc, r_lc = veh.l_lc, veh.r_lc
    if l_lc is None:  # TODO keep the lside, rside, chk_cond in memory instead of always updating them
        if r_lc is None:
            return False, None
        elif r_lc == 'discretionary':
            lside, rside = False, True
            chk_cond = not veh.lc_side
        else:
            lside, rside = False, True
            chk_cond = False
    elif l_lc == 'discretionary':
        if r_lc is None:
            lside, rside = True, False
            chk_cond = not veh.lc_side
        elif r_lc == 'discretionary':
            if veh.lc_side is not None:
                chk_cond = False
                if veh.lc_side == 'l':
                    lside, rside = True, False
                else:
                    lside, rside = False, True
            else:
                chk_cond = True
                lside, rside = True, True
        else:
            lside, rside = False, True
            chk_cond = False
    else:
        lside, rside = True, False
        chk_cond = False

    if chk_cond:  # decide if we want to evaluate lc model or not - this only applies to discretionary state
        # when there is no cooperation or tactical positioning
        if chk_lc >= 1:
            pass
        elif np.random.rand() > chk_lc:
            return False, None

    # next we compute quantities to send to LC model for the required sides
    if lside:
        newlfolhd, newlhd = get_new_hd(
            veh.lfol, veh, veh.llane)  # better to just store left/right lanes
    else:
        newlfolhd = newlhd = None

    if rside:
        newrfolhd, newrhd = get_new_hd(veh.rfol, veh, veh.rlane)
    else:
        newrfolhd = newrhd = None

    # if get_fol option is given to wrapper, it means model requires the follower's quantities as well
    if get_fol:
        fol, lead = veh.fol, veh.lead
        if fol.cf_parameters is None:
            newfolhd = None
        elif lead is None:
            newfolhd = None
        else:
            newfolhd = get_headway(fol, lead)
    else:
        return True, (lside, rside, newlfolhd, newlhd, newrfolhd, newrhd)

    return True, (lside, rside, newlfolhd, newlhd, newrfolhd, newrhd, newfolhd)