def split_links_break_nodes(O, D, travel_time, new_node, break_time, reset_time): """split the link from O to D in half arguments: O: origin node, integer D: destination node, integer travel_time: time from O to D, integer starting_node: starting point for new nodes, integer returns: 2 dimensional array of travel times for new nodes. This array is one-directional, from O to D. Nodes are numbered from starting_node + zero, sequentially, new_node """ bn = BN.BreakNode(O, D, travel_time, new_node, break_time, reset_time) new_times = Demand.zeroed_trip_triplets(3) # np.zeros(3,dtype=[('x', np.int), ('y', np.int),('t',np.float)]) # # copy existing. this is redundant # new_times[0] = [O,O,0] # new_times[1] = [O,D,travel_time] # new_times[2] = [D,D,0] # compute travel minutes new_times[0] = (O, new_node, bn.tt_o) new_times[1] = (new_node, new_node, 0) new_times[2] = (new_node, D, bn.tt_d) # new nodes are stored in "new_times" as keys of second dimension # not symmetric, but rather, directional. Opposite way is impossible # so those values are NaN and easily set to infinity return (new_times, bn)
def split_break_node(record, travel_times, min_start=None): """Pass in a demand record, and split out all the required break nodes to get to the origin from the depot, to the destination from the origin, and back to the depot from the destination This function knows about the break rules. Currently only one is implemented (drive 11, break 10). Working on drive 8 break 0.5, then will work on on-duty 14 break 10. """ if min_start == None: min_start = len(travel_times.index) new_times = Demand.zeroed_trip_triplets(0) # np.zeros(0,dtype=[('x', np.int), ('y', np.int),('t',np.float)]) new_nodes = [] tt = travel_times.loc[0, record.origin] if not np.isnan(tt): pair = break_node_splitter(0, record.origin, tt, min_start) new_times = np.concatenate((new_times, pair[0]), axis=0) new_nodes.extend(pair[1]) min_start = pair[2] tt = travel_times.loc[record.origin, record.destination] if not np.isnan(tt): pair = break_node_splitter(record.origin, record.destination, tt, min_start) new_times = np.concatenate((new_times, pair[0]), axis=0) new_times = np.concatenate((new_times, pair[0]), axis=0) new_nodes.extend(pair[1]) min_start = pair[2] tt = travel_times.loc[record.destination, 0] if not np.isnan(tt): pair = break_node_splitter(record.destination, 0, tt, min_start) new_times = np.concatenate((new_times, pair[0]), axis=0) new_nodes.extend(pair[1]) min_start = pair[2] #print(new_times) return (new_times, new_nodes, min_start)
def break_node_splitter(origin, destination, tt, min_start): """Given an Origin and a Destination node, plus travel time between and the numbering of nodes (min start is an integer for the first node that will be created), create necessary break nodes between O and D that will satisfy the break rules. Currently knows only about the 11hr drive, 10hr break rule. Going to make it work for 8hr drive, 0.5hr break. """ new_times = Demand.zeroed_trip_triplets(0) # np.zeros(0,dtype=[('x', np.int), ('y', np.int),('t',np.float)]) new_nodes = [] long_break_time = 60 * 10 long_break_interval = 60 * 11 short_break_time = 30 short_break_interval = 60 * 8 # for the 11 hour drive rule long_possible_breaks = math.ceil(tt / (11 * 60)) if long_possible_breaks == 0: long_possible_breaks = 1 # no real need to count up 8 hr breaks...at a minimum, can slot # one in between each 11 hr break segment_tt = tt for i in range(0, long_possible_breaks): # insert 11 hr break opportunity pair11 = split_links_break_nodes(origin, destination, segment_tt, min_start, long_break_time, long_break_interval) min_start += 1 node11 = pair11[1] # possibly set up a dimension thing here? # node11.add_dimension_name('Drive') # or similar? # slot in an 8 hr break between origin and 11 hr pair8 = split_links_break_nodes(origin, node11.node, node11.tt_o, min_start, short_break_time, short_break_interval) node8 = pair8[1] # need to correct the destination of the node8 because of the # way the demand object stores and retrieves breaks between an # OD pair node8.destination = destination min_start += 1 # possibly set up a dimension thing here too? # pair8[1].add_dimension_name('halfhrbreak') # or similar? # if i==long_possible_breaks-1: # # closing in on destination, so include potential to get # # from short break to goal? # extra_link = np.array([node8.node,destination,node8.tt_d+node11.tt_d]) # new_times = np.concatenate(new_times, # pair11[0], # pair8[0]) new_times = np.concatenate((new_times, pair11[0], pair8[0]), axis=0) # but I want the 8hr break nodes coming before the 11 hr ones new_nodes.append(pair8[1]) new_nodes.append(pair11[1]) # set for next loop segment_tt = node11.tt_d origin = node11.node # end of loop. Consider boundary conditions node8 = new_nodes[-2] node11 = new_nodes[-1] # might not need a short break after long break prior to # arrival at destination, so make it so can just get to dest # from short break extra_connection = np.array( [(node8.node, destination, node8.tt_d + node11.tt_d)], dtype=[('x', np.int), ('y', np.int), ('t', np.float)]) new_times = np.concatenate((new_times, extra_connection), axis=0) # print(tt,long_possible_breaks*long_break_interval, # tt - long_possible_breaks*long_break_interval) # might need another 8 hr break node, but # only put in another 8 hr node if need to do so if tt - ((long_possible_breaks - 1) * long_break_interval) > short_break_interval: # in this case, might need a short break before long break, # then another short break prior to destination (worst case, # pickup, 0 time, long break, 8hrs, need short break, # dest). so make it here pair8 = split_links_break_nodes(node11.node, destination, node11.tt_d, min_start, short_break_time, short_break_interval) min_start += 1 # not necessary, but good habit new_times = np.concatenate((new_times, pair8[0]), axis=0) new_nodes.append(pair8[1]) return (new_times, new_nodes, min_start)