def is_valid_formation(aircraft_list):

    # Get the segments
    segments = map(lambda aircraft: aircraft.route.segments[0], aircraft_list)

    # Find the reference point collection of segments
    midpoints = map(lambda x: x.midpoint, segments)
    reference = util.midpoint (midpoints)
    deviation_sum = 0
    
    for segment in segments:

        crosstrack = Segment(segment.start, reference)
        
        deviation = util.cross_track_distance (
            crosstrack.get_length(),
            crosstrack.get_initial_bearing(),
            segment.get_initial_bearing()
        )
        deviation_sum = deviation_sum + abs(deviation)

        if abs(deviation) > config.max_deviation:
            return False

        #print '%s (ref = %s) crosstrack distance: %.1f' %\
              #(segment, reference, d_x)

    return deviation_sum
Beispiel #2
0
def create_segments(flights):

    segments = {'benchmark': [], 'formation': [], 'solo': []}

    for aircraft in flights:

        # Temp for timing analysis (no formations yet)
        # We place these segment in the formation var so that the lines are
        # nice and green
        #segments['formation'].append(Segment(aircraft.origin, aircraft.hub))
        #segments['formation'].append(Segment(aircraft.hub, aircraft.destination))
        #return

        if hasattr(aircraft, 'formation'):
            segments['formation'].append(Segment(aircraft.origin,
                                                 aircraft.hub))
            segments['formation'].append(
                Segment(aircraft.hub, aircraft.hookoff_point))
            segments['formation'].append(
                Segment(aircraft.hookoff_point, aircraft.destination))
        else:
            # Note: not all aircraft fly via the hub. If their origin is within the
            # lock area, they fly directly to the destination
            route = Route(aircraft.waypoints_passed)
            for segment in route.segments:
                segments['solo'].append(segment)

    return segments
Beispiel #3
0
def rank_origins(origins, destinations):
    
    p('debug', 'Ranking origins: %s' % origins)

    midpoint_origins = midpoint(origins)
    midpoint_destinations = midpoint(destinations)
    hub_route = Segment(midpoint_origins, midpoint_destinations)
    for origin in origins:
        
        AC = Segment(midpoint_origins, origin)
        orthogonal_heading = hub_route.get_initial_bearing() + 90    
        (a, b) = project_segment(
            abs(orthogonal_heading - AC.get_initial_bearing()),
            AC.get_length()
        )
        projection = midpoint_origins.get_position(
            orthogonal_heading, a
        )
        midpoint_to_projection = Segment(
            midpoint_origins,
            projection
        )
        
        angle = abs(
            hub_route.get_initial_bearing() - 
            midpoint_to_projection.get_initial_bearing()
        )
        if abs(angle - 90) < 0.1:
            distance = -1 * midpoint_to_projection.get_length()
        else:
            distance = midpoint_to_projection.get_length()
        origin.distance_to_midpoint = distance
        
    return sorted(origins, key = lambda point: point.distance_to_midpoint)
def get_hub(flights):
    o = []
    d = []
    for flight in flights:
        o.append(flight['route'].waypoints[0])
        d.append(flight['route'].waypoints[-1])
    mid_o = midpoint(o)
    mid_d = midpoint(d)
    trunk_route = Segment(mid_o, mid_d)
    return mid_o.get_position(trunk_route.get_initial_bearing(),
                              trunk_route.get_length() * config.Z)
        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' %
                (aircraft, leader_heading, buddy, buddy_heading, phi_obs))
            return phi_obs <= (config.phi_max / 2)
def get_hub(flights):
    o = []
    d = []
    for flight in flights:
        o.append(flight['route'].waypoints[0])
        d.append(flight['route'].waypoints[-1])
    mid_o = midpoint(o)
    mid_d = midpoint(d)
    trunk_route = Segment(mid_o, mid_d)
    return mid_o.get_position(
        trunk_route.get_initial_bearing(),
        trunk_route.get_length() * config.Z
    )
Beispiel #7
0
def create_segments(flights):
    
    segments = {
        'benchmark' : [],
        'formation' : [],
        'solo'      : []
    }

    for aircraft in flights:
        segments['benchmark'].append(Segment(aircraft.origin, aircraft.hub))
        segments['benchmark'].append(Segment(aircraft.hub, aircraft.destination))

    return segments
Beispiel #8
0
        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' % (
                    aircraft, leader_heading, buddy, buddy_heading, phi_obs
                )
            )
            return phi_obs <= (config.phi_max/2)
Beispiel #9
0
def construct_hub(origins, destinations, Z):

    midpoint_origins = midpoint(origins)
    midpoint_destinations = midpoint(destinations)

    hub_route = Segment(midpoint_origins, midpoint_destinations)
    hub = hub_route.start.get_position(hub_route.get_initial_bearing(),
                                       hub_route.get_length() * Z)

    hub.origins = origins
    hub.destinations = destinations

    p('debug', 'Constructed hub at %s' % (hub))

    return hub
Beispiel #10
0
def construct_hub(origins, destinations, Z):

    midpoint_origins      = midpoint(origins)
    midpoint_destinations = midpoint(destinations)
    
    hub_route = Segment(midpoint_origins, midpoint_destinations)
    hub = hub_route.start.get_position(
        hub_route.get_initial_bearing(),
        hub_route.get_length() * Z
    )
    
    hub.origins      = origins
    hub.destinations = destinations
    
    p('debug', 'Constructed hub at %s' % (hub))
    
    return hub
 def init_segments(self):
     """Uses the waypoint list to generate the segments in this route"""
     self.segments = []
     previous_waypoint = None
     for waypoint in self.waypoints:
         if previous_waypoint is not None:
             self.segments.append(Segment(previous_waypoint, waypoint))
         previous_waypoint = waypoint
Beispiel #12
0
def handle_alive(event):

    global vars

    formation = event.sender
    
    # We should have a hookoff point for each participant, and it should be
    # the current segment
    for aircraft in formation:
        assert aircraft.hookoff_point
        # The remaining segment should be hookoff-destination
        #debug.print_object(aircraft)
        assert len(aircraft.route.segments) > 0

        # Let the aircraft know in which formation it belongs
        aircraft.formation = formation

        #assert aircraft.route.segments[0].end.coincides(
        #    aircraft.hookoff_point
        #)
    
    vars["formation_count"] += 1
    vars['formation_aircraft_count'] += len(formation)
    
    formation_phi = 0
    for aircraft in formation:
        
        hub_to_dest = Segment(aircraft.hub, aircraft.destination)
        hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)
        
        bearing = hub_to_dest.get_initial_bearing()
        formation_bearing = hub_to_hookoff.get_initial_bearing()
        phi_obs = abs(bearing - formation_bearing)
        p('debug', 'Aircraft %s, phi_obs: %.2f' % (aircraft, phi_obs))
        #assert phi_obs <= config.phi_max
        formation_phi += phi_obs
        #print aircraft.route.segments[0].initial_bearing()
    vars['phi_obs_sum'] += formation_phi
        
    for aircraft in formation:
        vars['Q_sum']   = vars['Q_sum'] + aircraft.Q
def get_exit(hub, trunk_route, flight):
    dest = flight['route'].waypoints[-1]
    hub_to_dest = Segment(hub, dest)
    theta = abs(hub_to_dest.get_initial_bearing() -
                trunk_route.get_initial_bearing())
    (a, b) = project_segment(theta, hub_to_dest.get_length())
    Q = get_hookoff_quotient(a, b, config.alpha)
    d_hub_to_exit = a * Q
    
    # Exit to destination must be long enough to allow for safe descent
    # TOD is at 150NM out of dest, so exit->dest must be longer than 150NM
    d_exit_to_dest = 0
    while d_exit_to_dest < 150:
        exit_point = hub.get_position(
            trunk_route.get_initial_bearing(),
            d_hub_to_exit
        )
        d_hub_to_exit -= 1
        exit_dest = Segment(exit_point, dest)
        d_exit_to_dest = exit_dest.get_length()
    assert exit_dest.get_length() > 150
    print 'For flight %s, the exit point is %dNM away from dest' % (
        flight['route'], d_exit_to_dest
    )
    return exit_point
Beispiel #14
0
def run():

    # Discount incurred on formation trajectory by follower
    alpha = .15

    # The formation hub
    hub = Waypoint('AMS')

    # The end point of trunk route (midpoint of the destinations)
    mid = Waypoint('JFK')

    # The actual destination of current participant
    des = Waypoint('SFO')

    # The trunk route itself
    trunk = Segment(hub, mid)

    hookoff = get_hookoff(trunk, des, alpha)

    print 'The trunk length = %d NM' % trunk.get_length()
    print 'The hookoff distance = %d NM' % d(hub, hookoff)
    print 'Therefore, Q = %.2f' % (d(hub, hookoff) / trunk.get_length())
def get_exit(hub, trunk_route, flight):
    dest = flight['route'].waypoints[-1]
    hub_to_dest = Segment(hub, dest)
    theta = abs(hub_to_dest.get_initial_bearing() -
                trunk_route.get_initial_bearing())
    (a, b) = project_segment(theta, hub_to_dest.get_length())
    Q = get_hookoff_quotient(a, b, config.alpha)
    d_hub_to_exit = a * Q

    # Exit to destination must be long enough to allow for safe descent
    # TOD is at 150NM out of dest, so exit->dest must be longer than 150NM
    d_exit_to_dest = 0
    while d_exit_to_dest < 150:
        exit_point = hub.get_position(trunk_route.get_initial_bearing(),
                                      d_hub_to_exit)
        d_hub_to_exit -= 1
        exit_dest = Segment(exit_point, dest)
        d_exit_to_dest = exit_dest.get_length()
    assert exit_dest.get_length() > 150
    print 'For flight %s, the exit point is %dNM away from dest' % (
        flight['route'], d_exit_to_dest)
    return exit_point
def handle_alive(event):

    global vars

    formation = event.sender

    # We should have a hookoff point for each participant, and it should be
    # the current segment
    for aircraft in formation:
        assert aircraft.hookoff_point
        # The remaining segment should be hookoff-destination
        #debug.print_object(aircraft)
        assert len(aircraft.route.segments) > 0

        # Let the aircraft know in which formation it belongs
        aircraft.formation = formation

        #assert aircraft.route.segments[0].end.coincides(
        #    aircraft.hookoff_point
        #)

    vars["formation_count"] += 1
    vars['formation_aircraft_count'] += len(formation)

    formation_phi = 0
    for aircraft in formation:

        hub_to_dest = Segment(aircraft.hub, aircraft.destination)
        hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)

        bearing = hub_to_dest.get_initial_bearing()
        formation_bearing = hub_to_hookoff.get_initial_bearing()
        phi_obs = abs(bearing - formation_bearing)
        p('debug', 'Aircraft %s, phi_obs: %.2f' % (aircraft, phi_obs))
        #assert phi_obs <= config.phi_max
        formation_phi += phi_obs
        #print aircraft.route.segments[0].initial_bearing()
    vars['phi_obs_sum'] += formation_phi

    for aircraft in formation:
        vars['Q_sum'] = vars['Q_sum'] + aircraft.Q
def get_trunk_route(hub, formation):
    destinations = []
    for aircraft in formation:
        destinations.append(aircraft['route'].waypoints[-1])
    mid_d = midpoint(destinations)
    return Segment(hub, mid_d)
Beispiel #18
0
def handle_arrive(event):
    
    global vars, hubs

    aircraft = event.sender
    
    # Temporarily use one model for everything
    model = copy.deepcopy(config.model)
    
    # Also calculate the direct distance
    segment = Segment(aircraft.origin, aircraft.destination)
    direct = segment.get_length()
    #direct = 3193 #temp overwrite for validation
    p('validate', 'Distance direct for %s is %dNM' % (
        aircraft, direct
    ))
    p('validate', 'Getting the benchmark fuel')
    vars['distance_direct'] += direct
    vars['fuel_direct'] += get_fuel_burned_during_cruise(direct, model)
    p('validate', 'OK, we have the benchmark fuel now')
    
    hub = aircraft.hub
    assert hub in config.hubs
    
    # Sometimes (especially with very low Z and high L) aircraft are excluded
    # from formation flight due to the origin being within the lock area.
    if hasattr(aircraft, 'is_excluded') and aircraft.is_excluded:
        vars['distance_solo'] += direct
        vars['fuel_actual'] += get_fuel_burned_during_cruise(direct, model)
        return

    #key = 'flight_count_%s' % hub
    #if key not in vars:
    #    vars[key] = 0
    #vars[key] = vars[key] + 1

    # Aircraft always fly solo to the hub
    segment = Segment(aircraft.origin, hub)
    origin_to_hub = segment.get_length()
    p('Distance origin_to_hub for %s is %dNM' % (
        aircraft,
        origin_to_hub
    ))
    vars['distance_solo'] += origin_to_hub
    
    # If in formation
    if hasattr(aircraft, 'formation'):
        
        segment = Segment(hub, aircraft.hookoff_point)
        hub_to_hookoff = segment.get_length()
        p('Distance hub_to_hookoff for %s is %dNM' % (
            aircraft,
            hub_to_hookoff
        ))
        
        vars['distance_formation'] += hub_to_hookoff

        segment = Segment(aircraft.hookoff_point, aircraft.destination)
        hookoff_to_destination = segment.get_length()
        p('Distance hookoff_to_destination for %s is %dNM' % (
            aircraft,
            hookoff_to_destination
        ))
        vars['distance_solo'] += hookoff_to_destination

        # Collect all hub delays
        # The calibration aircraft was never delayed
        if hasattr(aircraft, 'hub_delay'):
            vars['hub_delay_sum'] = vars['hub_delay_sum'] +\
                aircraft.hub_delay
            
        if aircraft.incurs_benefits:
            discount = config.alpha
        else:
            discount = 0

        p('validate', 'Discount = %s for %s' % (discount, aircraft))
        
        fuel_formation = formationburn(
            int(origin_to_hub),
            int(hub_to_hookoff),
            int(hookoff_to_destination),
            model = model,
            discount = discount
        )
        
        p('validate', 'Fuel burn formation = %d for %s' % (
            fuel_formation, aircraft
        ))

        vars['fuel_actual'] += fuel_formation
    # If fully solo
    else:

        p('validate', 'Discount = false for %s' % (aircraft))

        segment = Segment(hub, aircraft.destination)
        hub_to_destination = segment.get_length()
        p('Distance hub_to_destination for %s is %dNM' % (
            aircraft,
            hub_to_destination
        ))
        vars['distance_solo'] += hub_to_destination
        
        fuel_solo = get_fuel_burned_during_cruise(
            origin_to_hub + hub_to_destination,
            model = model,
        )

        p('validate', 'Fuel burn solo = %d for %s' % (
            fuel_solo, aircraft
        ))

        vars['fuel_actual'] += fuel_solo
def execute():

    models = {
        '777'  : {
            'V'    : 500,
            'c_T'  : .56,
            'L_D'  : 19.26,
            'MTOW' : 200000
        },
        '767'  : {
            'V'    : 500,
            'c_T'  : .54,
            'L_D'  : 17,
            'MTOW' : 190000
        },
        '330'  : {
            'V'    : 500,
            'c_T'  : .54,
            'L_D'  : 17,
            'MTOW' : 190000
        },
        '340'  : {
            'V'    : 500,
            'c_T'  : .54,
            'L_D'  : 17,
            'MTOW' : 250000
        }
    }

    formation = [{
        'aircraft' : '777',
        'route'    : Route([Waypoint('DUS'), Waypoint('IAD')]),
        'discount' : 0,
    },{
        'aircraft' : '777',
        'route'    : Route([Waypoint('BRU'), Waypoint('ORD')]),
        'discount' : config.alpha
    },{
        'aircraft' : '777',
        'route'    : Route([Waypoint('AMS'), Waypoint('IAH')]),
        'discount' : config.alpha
    }]

    solo = [{
        'aircraft' : '777',
        'route'    : Route([Waypoint('LHR'), Waypoint('ATL')])
    },{
        'aircraft' : '777',
        'route'    : Route([Waypoint('FRA'), Waypoint('SFO')])
    }]
    
    hub = get_hub(formation + solo)
    
    benchmark = copy.deepcopy(formation) + copy.deepcopy(solo)
    
    trunk_route = get_trunk_route(hub, formation)
    print trunk_route

    # Origin to TOC
    d_0 = 100
    
    # Exit to TOD
    d_3 = 0

    # TOD to Destination
    d_4 = 150

    F_formation = 0
    for flight in formation:

        # Direct great circle connection
        d_direct = flight['route'].get_length()
        
        # Distance from origin to hub
        origin_to_hub   = Route([flight['route'].waypoints[0], hub])
        d_origin_to_hub = origin_to_hub.get_length()
        
        # TOC to hub
        d_1 = d_origin_to_hub - d_0
        
        # Hub to exit
        exit_point = get_exit(hub, trunk_route, flight)
        hub_to_exit = Segment(hub, exit_point)
        d_2 = hub_to_exit.get_length()
        
        # Exit to TOD
        exit_to_dest = Segment(exit_point, flight['route'].waypoints[-1])
        d_3 = exit_to_dest.get_length() - d_4
        
        flight['f'] = fuel_per_stage(
            d_1, d_2, models[flight['aircraft']], flight['discount']
        )
        flight['d'] = [d_0, d_1, d_2, d_3, d_4]

        F_formation += sum(flight['f'])

    print 'formation fuel burn: %.2f' % (F_formation)
    
    # Origin to TOC
    d_0 = 100
    
    # Exit to TOD
    d_3 = 0

    # TOD to Destination
    d_4 = 150

    F_solo = 0
    for flight in solo:

        # Total route length
        full_route = Route([
            flight['route'].waypoints[0],
            hub,
            flight['route'].waypoints[-1],
        ])
        d_total = full_route.get_length()

        # Distance from origin to hub
        origin_to_hub   = Route([flight['route'].waypoints[0], hub])
        d_origin_to_hub = origin_to_hub.get_length()

        # TOC to hub
        d_1 = d_origin_to_hub - d_0

        # Hub to TOD (not to exit point)
        d_2 = d_total - d_0 - d_1 - d_4

        flight['f'] = fuel_per_stage(
            d_1, d_2, models[flight['aircraft']]
        )
        flight['d'] = [d_0, d_1, d_2, d_3, d_4]

        F_solo += sum(flight['f'])

    print 'solo fuel burn: %.2f' % (F_solo)

    F_b = 0
    for flight in benchmark:

        # In the benchmark scenario, fly directly from origin to destination
        d_1 = flight['route'].get_length() - d_0 - d_4
        d_2 = 0

        flight['f'] = fuel_per_stage(
            d_1, d_2, models[flight['aircraft']]
        )
        flight['d'] = [d_0, d_1, d_2, d_3, d_4]
        
        F_b += sum(flight['f'])

#def temp():

    print 'benchmark fuel burn: %.2f' % (F_b)

    F_s = 100 * (F_b - (F_formation + F_solo)) / F_b
    print 'fuel saved: %.2f%%' % F_s
    
    print '--DISTANCE: formation flying scenario--'
    for i in [0,1,2,3,4]:
        row = r'$d_i^%d$' % i
        for flight in formation + solo:
            row += ' & %d' % flight['d'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in formation + solo:
        row += ' & %d' % sum(flight['d'])
    row += r' \\'
    print row
    
    print '--DISTANCE: benchmark scenario--'
    for i in [0,1,2,3,4]:
        row = r'$F_i^%d$' % i
        for flight in benchmark:
            row += ' & %d' % flight['d'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in benchmark:
        row += ' & %d' % sum(flight['d'])
    row += r' \\'
    print row

    print '--FUEL: formation flying scenario--'
    for i in [0,1,2,3,4]:
        row = r'$F_i^%d$' % i
        for flight in formation + solo:
            row += ' & %d' % flight['f'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in formation + solo:
        row += ' & %d' % sum(flight['f'])
    row += r' \\'
    print row

    print '--FUEL: benchmark scenario--'
    for i in [0,1,2,3,4]:
        row = r'$F_i^%d$' % i
        for flight in benchmark:
            row += ' & %d' % flight['f'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in benchmark:
        row += ' & %d' % sum(flight['f'])
    row += r' \\'
    print row
    
    S_d = (
        formation[0]['d'][2] +\
        formation[1]['d'][2] +\
        formation[2]['d'][2]
    )/(
        sum(formation[0]['d']) +\
        sum(formation[1]['d']) +\
        sum(formation[2]['d']) +\
        sum(solo[0]['d']) +\
        sum(solo[1]['d'])
    )
    
    F_s = 1 - (
        sum(formation[0]['f']) +\
        sum(formation[1]['f']) +\
        sum(formation[2]['f']) +\
        sum(solo[0]['f']) +\
        sum(solo[1]['f'])
    )/(
        sum(benchmark[0]['f']) +\
        sum(benchmark[1]['f']) +\
        sum(benchmark[2]['f']) +\
        sum(benchmark[3]['f']) +\
        sum(benchmark[4]['f'])
    )
    
    p_tot = (
        sum(formation[0]['d']) +\
        sum(formation[1]['d']) +\
        sum(formation[2]['d']) +\
        sum(solo[0]['d']) +\
        sum(solo[1]['d'])
    )/(
        sum(benchmark[0]['d']) +\
        sum(benchmark[1]['d']) +\
        sum(benchmark[2]['d']) +\
        sum(benchmark[3]['d']) +\
        sum(benchmark[4]['d'])
    ) - 1

    print '--ANALYTICAL OUTPUT RESULTS--'
    print r'$S_f$ & 0.6 & 0.6 \\'
    print r'$S_d$ & %.4f & %.4f \\' % (S_d, S_d)
    print r'$F_s$ & FILLIN & FILLIN \\'
    print r'$F^{rel}_s$ & %.4f & %.4f \\' % (F_s, F_s)
    print r'$p_{tot}$ & %.4f & %.4f \\' % (p_tot, p_tot)
    def allocate(self, aircraft):

        p('debug', 'Starting formation allocation for %s' % aircraft)

        # Do not perform allocation if no hub exists in the flight route.
        if len(aircraft.route.segments) == 0:
            return

        self.formations = []
        intervals = []
        candidates = self.aircraft_queue
        hub = aircraft.route.waypoints[0]

        # This is bad. We don't want to filter anything.
        # @todo: pre-process at a higher level.
        # Only consider other aircraft flying to the same hub
        candidates = filter(lambda a: a.route.waypoints[0] is hub, candidates)

        p('debug', 'Full candidate set: %s' % candidates)

        # Only consider aircraft having a maximum heading difference between
        # the hub and their destination
        segment = Segment(aircraft.hub, aircraft.destination)
        leader_heading = segment.get_initial_bearing()

        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' %
                (aircraft, leader_heading, buddy, buddy_heading, phi_obs))
            return phi_obs <= (config.phi_max / 2)

        candidates = filter(heading_filter, candidates)

        # Other interesting filters
        if 'same-airline' in config.restrictions:
            airline = aircraft.label[0:2]
            candidates = filter(lambda a: a.label[0:2] == airline, candidates)
        if 'same-aircraft-type' in config.restrictions:
            aircraft_type = aircraft.aircraft_type
            candidates = filter(lambda a: a.aircraft_type == aircraft_type,
                                candidates)

        p('debug', 'Reduced candidate set: %s' % candidates)

        for candidate in candidates:

            # Quick and dirty: recalc position. Instead, pull eta from var.
            candidate.controller.update_position()
            tth = candidate.time_to_waypoint()  # time to hub
            hub_eta = sim.time + tth

            # From the moment the aircraft enters the lock area, the slack
            # decreases linearly to zero upon hub arrival.
            if tth < config.lock_time:
                slack = tth * config.etah_slack / config.lock_time
            else:
                slack = config.etah_slack

            p('Time = %s, Hub (= %s) eta %s for candidate %s' %\
              (sim.time, hub, hub_eta, candidate))
            intervals.append(
                Interval(candidate,
                         int(hub_eta) - slack,
                         int(hub_eta) + slack))

        for interval_group in group(intervals):
            formation = Formation()
            for interval in interval_group:
                formation.append(interval.obj)
            self.formations.append(formation)
def execute():

    models = {
        '777': {
            'V': 500,
            'c_T': .56,
            'L_D': 19.26,
            'MTOW': 200000
        },
        '767': {
            'V': 500,
            'c_T': .54,
            'L_D': 17,
            'MTOW': 190000
        },
        '330': {
            'V': 500,
            'c_T': .54,
            'L_D': 17,
            'MTOW': 190000
        },
        '340': {
            'V': 500,
            'c_T': .54,
            'L_D': 17,
            'MTOW': 250000
        }
    }

    formation = [{
        'aircraft': '777',
        'route': Route([Waypoint('DUS'), Waypoint('IAD')]),
        'discount': 0,
    }, {
        'aircraft': '777',
        'route': Route([Waypoint('BRU'), Waypoint('ORD')]),
        'discount': config.alpha
    }, {
        'aircraft': '777',
        'route': Route([Waypoint('AMS'), Waypoint('IAH')]),
        'discount': config.alpha
    }]

    solo = [{
        'aircraft': '777',
        'route': Route([Waypoint('LHR'), Waypoint('ATL')])
    }, {
        'aircraft': '777',
        'route': Route([Waypoint('FRA'), Waypoint('SFO')])
    }]

    hub = get_hub(formation + solo)

    benchmark = copy.deepcopy(formation) + copy.deepcopy(solo)

    trunk_route = get_trunk_route(hub, formation)
    print trunk_route

    # Origin to TOC
    d_0 = 100

    # Exit to TOD
    d_3 = 0

    # TOD to Destination
    d_4 = 150

    F_formation = 0
    for flight in formation:

        # Direct great circle connection
        d_direct = flight['route'].get_length()

        # Distance from origin to hub
        origin_to_hub = Route([flight['route'].waypoints[0], hub])
        d_origin_to_hub = origin_to_hub.get_length()

        # TOC to hub
        d_1 = d_origin_to_hub - d_0

        # Hub to exit
        exit_point = get_exit(hub, trunk_route, flight)
        hub_to_exit = Segment(hub, exit_point)
        d_2 = hub_to_exit.get_length()

        # Exit to TOD
        exit_to_dest = Segment(exit_point, flight['route'].waypoints[-1])
        d_3 = exit_to_dest.get_length() - d_4

        flight['f'] = fuel_per_stage(d_1, d_2, models[flight['aircraft']],
                                     flight['discount'])
        flight['d'] = [d_0, d_1, d_2, d_3, d_4]

        F_formation += sum(flight['f'])

    print 'formation fuel burn: %.2f' % (F_formation)

    # Origin to TOC
    d_0 = 100

    # Exit to TOD
    d_3 = 0

    # TOD to Destination
    d_4 = 150

    F_solo = 0
    for flight in solo:

        # Total route length
        full_route = Route([
            flight['route'].waypoints[0],
            hub,
            flight['route'].waypoints[-1],
        ])
        d_total = full_route.get_length()

        # Distance from origin to hub
        origin_to_hub = Route([flight['route'].waypoints[0], hub])
        d_origin_to_hub = origin_to_hub.get_length()

        # TOC to hub
        d_1 = d_origin_to_hub - d_0

        # Hub to TOD (not to exit point)
        d_2 = d_total - d_0 - d_1 - d_4

        flight['f'] = fuel_per_stage(d_1, d_2, models[flight['aircraft']])
        flight['d'] = [d_0, d_1, d_2, d_3, d_4]

        F_solo += sum(flight['f'])

    print 'solo fuel burn: %.2f' % (F_solo)

    F_b = 0
    for flight in benchmark:

        # In the benchmark scenario, fly directly from origin to destination
        d_1 = flight['route'].get_length() - d_0 - d_4
        d_2 = 0

        flight['f'] = fuel_per_stage(d_1, d_2, models[flight['aircraft']])
        flight['d'] = [d_0, d_1, d_2, d_3, d_4]

        F_b += sum(flight['f'])

#def temp():

    print 'benchmark fuel burn: %.2f' % (F_b)

    F_s = 100 * (F_b - (F_formation + F_solo)) / F_b
    print 'fuel saved: %.2f%%' % F_s

    print '--DISTANCE: formation flying scenario--'
    for i in [0, 1, 2, 3, 4]:
        row = r'$d_i^%d$' % i
        for flight in formation + solo:
            row += ' & %d' % flight['d'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in formation + solo:
        row += ' & %d' % sum(flight['d'])
    row += r' \\'
    print row

    print '--DISTANCE: benchmark scenario--'
    for i in [0, 1, 2, 3, 4]:
        row = r'$F_i^%d$' % i
        for flight in benchmark:
            row += ' & %d' % flight['d'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in benchmark:
        row += ' & %d' % sum(flight['d'])
    row += r' \\'
    print row

    print '--FUEL: formation flying scenario--'
    for i in [0, 1, 2, 3, 4]:
        row = r'$F_i^%d$' % i
        for flight in formation + solo:
            row += ' & %d' % flight['f'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in formation + solo:
        row += ' & %d' % sum(flight['f'])
    row += r' \\'
    print row

    print '--FUEL: benchmark scenario--'
    for i in [0, 1, 2, 3, 4]:
        row = r'$F_i^%d$' % i
        for flight in benchmark:
            row += ' & %d' % flight['f'][i]
        row += r' \\'
        print row
    print r'\hline'
    row = r'$\sum$'
    for flight in benchmark:
        row += ' & %d' % sum(flight['f'])
    row += r' \\'
    print row

    S_d = (
        formation[0]['d'][2] +\
        formation[1]['d'][2] +\
        formation[2]['d'][2]
    )/(
        sum(formation[0]['d']) +\
        sum(formation[1]['d']) +\
        sum(formation[2]['d']) +\
        sum(solo[0]['d']) +\
        sum(solo[1]['d'])
    )

    F_s = 1 - (
        sum(formation[0]['f']) +\
        sum(formation[1]['f']) +\
        sum(formation[2]['f']) +\
        sum(solo[0]['f']) +\
        sum(solo[1]['f'])
    )/(
        sum(benchmark[0]['f']) +\
        sum(benchmark[1]['f']) +\
        sum(benchmark[2]['f']) +\
        sum(benchmark[3]['f']) +\
        sum(benchmark[4]['f'])
    )

    p_tot = (
        sum(formation[0]['d']) +\
        sum(formation[1]['d']) +\
        sum(formation[2]['d']) +\
        sum(solo[0]['d']) +\
        sum(solo[1]['d'])
    )/(
        sum(benchmark[0]['d']) +\
        sum(benchmark[1]['d']) +\
        sum(benchmark[2]['d']) +\
        sum(benchmark[3]['d']) +\
        sum(benchmark[4]['d'])
    ) - 1

    print '--ANALYTICAL OUTPUT RESULTS--'
    print r'$S_f$ & 0.6 & 0.6 \\'
    print r'$S_d$ & %.4f & %.4f \\' % (S_d, S_d)
    print r'$F_s$ & FILLIN & FILLIN \\'
    print r'$F^{rel}_s$ & %.4f & %.4f \\' % (F_s, F_s)
    print r'$p_{tot}$ & %.4f & %.4f \\' % (p_tot, p_tot)
Beispiel #22
0
    def allocate(self, aircraft):
        
        p('debug', 'Starting formation allocation for %s' % aircraft)
        
        # Do not perform allocation if no hub exists in the flight route.
        if len(aircraft.route.segments) == 0:
            return

        self.formations = []
        intervals       = []
        candidates      = self.aircraft_queue
        hub             = aircraft.route.waypoints[0]

        # This is bad. We don't want to filter anything. 
        # @todo: pre-process at a higher level.
        # Only consider other aircraft flying to the same hub
        candidates = filter(lambda a: a.route.waypoints[0] is hub, 
                            candidates)
        
        p('debug', 'Full candidate set: %s' % candidates)

        # Only consider aircraft having a maximum heading difference between
        # the hub and their destination
        segment = Segment(aircraft.hub, aircraft.destination)
        leader_heading = segment.get_initial_bearing()

        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' % (
                    aircraft, leader_heading, buddy, buddy_heading, phi_obs
                )
            )
            return phi_obs <= (config.phi_max/2)

        candidates = filter(heading_filter, candidates)

        # Other interesting filters
        if 'same-airline' in config.restrictions:
            airline = aircraft.label[0:2]
            candidates = filter(lambda a: a.label[0:2] == airline,
                                candidates)
        if 'same-aircraft-type' in config.restrictions:
            aircraft_type = aircraft.aircraft_type
            candidates = filter(lambda a: a.aircraft_type == aircraft_type,
                                candidates)
        
        p('debug', 'Reduced candidate set: %s' % candidates)

        for candidate in candidates:

            # Quick and dirty: recalc position. Instead, pull eta from var.
            candidate.controller.update_position()
            tth = candidate.time_to_waypoint() # time to hub
            hub_eta = sim.time + tth
            
            # From the moment the aircraft enters the lock area, the slack
            # decreases linearly to zero upon hub arrival.
            if tth < config.lock_time:
                slack = tth * config.etah_slack / config.lock_time
            else:
                slack = config.etah_slack

            p('Time = %s, Hub (= %s) eta %s for candidate %s' %\
              (sim.time, hub, hub_eta, candidate))
            intervals.append(Interval(
                candidate,
                int(hub_eta) - slack,
                int(hub_eta) + slack
            ))
            
        for interval_group in group(intervals):
            formation = Formation()
            for interval in interval_group:
                formation.append(interval.obj)
            self.formations.append(formation)
def handle_arrive(event):

    global vars, hubs

    aircraft = event.sender

    # Temporarily use one model for everything
    model = copy.deepcopy(config.model)

    # Also calculate the direct distance
    segment = Segment(aircraft.origin, aircraft.destination)
    direct = segment.get_length()
    #direct = 3193 #temp overwrite for validation
    p('validate', 'Distance direct for %s is %dNM' % (aircraft, direct))
    p('validate', 'Getting the benchmark fuel')
    vars['distance_direct'] += direct
    vars['fuel_direct'] += get_fuel_burned_during_cruise(direct, model)
    p('validate', 'OK, we have the benchmark fuel now')

    hub = aircraft.hub
    assert hub in config.hubs

    # Sometimes (especially with very low Z and high L) aircraft are excluded
    # from formation flight due to the origin being within the lock area.
    if hasattr(aircraft, 'is_excluded') and aircraft.is_excluded:
        vars['distance_solo'] += direct
        vars['fuel_actual'] += get_fuel_burned_during_cruise(direct, model)
        return

    #key = 'flight_count_%s' % hub
    #if key not in vars:
    #    vars[key] = 0
    #vars[key] = vars[key] + 1

    # Aircraft always fly solo to the hub
    segment = Segment(aircraft.origin, hub)
    origin_to_hub = segment.get_length()
    p('Distance origin_to_hub for %s is %dNM' % (aircraft, origin_to_hub))
    vars['distance_solo'] += origin_to_hub

    # If in formation
    if hasattr(aircraft, 'formation'):

        segment = Segment(hub, aircraft.hookoff_point)
        hub_to_hookoff = segment.get_length()
        p('Distance hub_to_hookoff for %s is %dNM' %
          (aircraft, hub_to_hookoff))

        vars['distance_formation'] += hub_to_hookoff

        segment = Segment(aircraft.hookoff_point, aircraft.destination)
        hookoff_to_destination = segment.get_length()
        p('Distance hookoff_to_destination for %s is %dNM' %
          (aircraft, hookoff_to_destination))
        vars['distance_solo'] += hookoff_to_destination

        # Collect all hub delays
        # The calibration aircraft was never delayed
        if hasattr(aircraft, 'hub_delay'):
            vars['hub_delay_sum'] = vars['hub_delay_sum'] +\
                aircraft.hub_delay

        if aircraft.incurs_benefits:
            discount = config.alpha
        else:
            discount = 0

        p('validate', 'Discount = %s for %s' % (discount, aircraft))

        fuel_formation = formationburn(int(origin_to_hub),
                                       int(hub_to_hookoff),
                                       int(hookoff_to_destination),
                                       model=model,
                                       discount=discount)

        p('validate',
          'Fuel burn formation = %d for %s' % (fuel_formation, aircraft))

        vars['fuel_actual'] += fuel_formation
    # If fully solo
    else:

        p('validate', 'Discount = false for %s' % (aircraft))

        segment = Segment(hub, aircraft.destination)
        hub_to_destination = segment.get_length()
        p('Distance hub_to_destination for %s is %dNM' %
          (aircraft, hub_to_destination))
        vars['distance_solo'] += hub_to_destination

        fuel_solo = get_fuel_burned_during_cruise(
            origin_to_hub + hub_to_destination,
            model=model,
        )

        p('validate', 'Fuel burn solo = %d for %s' % (fuel_solo, aircraft))

        vars['fuel_actual'] += fuel_solo
Beispiel #24
0
    def calibrate(self):
        """Determines the trunk route and hookoff points"""
        
        # Determine formation trunk route
        destinations = []
        for aircraft in self:
            destinations.append(aircraft.destination)
        arrival_midpoint = midpoint(destinations)
        p('destinations: %s' % destinations)
        p('midpoint = %s' % arrival_midpoint)
        hub_to_midpoint = Segment(aircraft.hub, arrival_midpoint)
    
        # Determine hookoff point for each aircraft, except the last
        for aircraft in self:
            
            hub_to_destination = Segment(aircraft.hub, aircraft.destination)
            
            p('flight %s hub %s to destination: %s' % (
                aircraft,
                '%s{%d, %d}' % (
                    aircraft.hub,
                    aircraft.hub.lat,
                    aircraft.hub.lon
                ),
                aircraft.destination
            ))
            p('flight %s hub %s to midpoint: %s' % (
                aircraft,
                '%s{%d, %d}' % (
                    aircraft.hub,
                    aircraft.hub.lat,
                    aircraft.hub.lon
                ),
                arrival_midpoint
            ))
            
            aircraft.hookoff_point = get_hookoff(
                hub_to_midpoint,
                aircraft.destination,
                config.alpha
            )
            
            hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)
            
            aircraft.Q = hub_to_hookoff.get_length() /\
                         hub_to_midpoint.get_length()

            p('flight %s, hub %s to hook-off point: %s' % (
                aircraft,
                '%s{%d, %d}' % (
                    aircraft.hub,
                    aircraft.hub.lat,
                    aircraft.hub.lon
                ),
                aircraft.hookoff_point
            ))
    
            aircraft.hookoff_point.name = 'hookoff-%s' % aircraft.hookoff_point
            
        # Place aircraft in order, ascending with Q, to fulfill LIFO condition.
        formation = sorted(self, key = lambda item: item.Q)
    
        # All aircraft at the front of the formation having the same destination
        # should hook off where the previous buddy (having a different
        # destination) hooked off.
    
        # Example: formation AMS-SFO, BRU-SFO, LHR-ATL.
        # AMS-SFO and BRU-SFO should hook off where LHR-ATL hooked off.
        # @todo Let AMS-SFO and BRU-SFO continue together along a new average
        #       formation trajectory (in this case directly to the destination)
    
        # First find the leading set of aircraft having the same destination
        formation.reverse()
        leading_destination = formation[0].destination
        leaders = []
        for aircraft in formation:
            
            # Start with always incurring benefits
            aircraft.incurs_benefits = True
    
            if not aircraft.destination.coincides(leading_destination):
                aircraft.is_leader = False
                continue

            aircraft.is_leader = True
            
            # Only the first leader incurs no benefits at all
            if len(leaders) == 0:
                aircraft.incurs_benefits = False
            
            leaders.append(aircraft)
    
        p('Leaders of formation %s are %s' % (
            formation,
            leaders
        ))
    
        # Then find the buddy just before the set of leading aircraft, if
        # it exists.
        try:
            # The leaders: same hookoff point as last buddy.
            last_buddy = formation[len(leaders)]
            for aircraft in leaders:
                aircraft.Q = last_buddy.Q
                #aircraft.P = last_buddy.P
                aircraft.hookoff_point = last_buddy.hookoff_point
        except IndexError:
            pass
    
        # Change reversed formation back to normal
        formation.reverse()

        for aircraft in formation:
    
            p('Adjusting waypoints of %s. Initial waypoints: %s' % (
                aircraft,
                aircraft.route.waypoints
            ))
            aircraft.route.waypoints = [
                #aircraft.hub,
                aircraft.hookoff_point,
                aircraft.destination]
            aircraft.route.init_segments()
            p('Adjusted waypoints of %s. New waypoints: %s' % (
                aircraft,
                aircraft.route.waypoints
            ))
            p('Need to calibrate aircraft %s (%s) in formation %s' % (
                aircraft, aircraft.route, formation
            ))
            aircraft.controller.calibrate()
Beispiel #25
0
def rank_origins(origins, destinations):

    p('debug', 'Ranking origins: %s' % origins)

    midpoint_origins = midpoint(origins)
    midpoint_destinations = midpoint(destinations)
    hub_route = Segment(midpoint_origins, midpoint_destinations)
    for origin in origins:

        AC = Segment(midpoint_origins, origin)
        orthogonal_heading = hub_route.get_initial_bearing() + 90
        (a, b) = project_segment(
            abs(orthogonal_heading - AC.get_initial_bearing()),
            AC.get_length())
        projection = midpoint_origins.get_position(orthogonal_heading, a)
        midpoint_to_projection = Segment(midpoint_origins, projection)

        angle = abs(hub_route.get_initial_bearing() -
                    midpoint_to_projection.get_initial_bearing())
        if abs(angle - 90) < 0.1:
            distance = -1 * midpoint_to_projection.get_length()
        else:
            distance = midpoint_to_projection.get_length()
        origin.distance_to_midpoint = distance

    return sorted(origins, key=lambda point: point.distance_to_midpoint)
Beispiel #26
0
    def calibrate(self):
        """Determines the trunk route and hookoff points"""

        # Determine formation trunk route
        destinations = []
        for aircraft in self:
            destinations.append(aircraft.destination)
        arrival_midpoint = midpoint(destinations)
        p('destinations: %s' % destinations)
        p('midpoint = %s' % arrival_midpoint)
        hub_to_midpoint = Segment(aircraft.hub, arrival_midpoint)

        # Determine hookoff point for each aircraft, except the last
        for aircraft in self:

            hub_to_destination = Segment(aircraft.hub, aircraft.destination)

            p('flight %s hub %s to destination: %s' %
              (aircraft, '%s{%d, %d}' %
               (aircraft.hub, aircraft.hub.lat, aircraft.hub.lon),
               aircraft.destination))
            p('flight %s hub %s to midpoint: %s' %
              (aircraft, '%s{%d, %d}' %
               (aircraft.hub, aircraft.hub.lat, aircraft.hub.lon),
               arrival_midpoint))

            aircraft.hookoff_point = get_hookoff(hub_to_midpoint,
                                                 aircraft.destination,
                                                 config.alpha)

            hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)

            aircraft.Q = hub_to_hookoff.get_length() /\
                         hub_to_midpoint.get_length()

            p('flight %s, hub %s to hook-off point: %s' %
              (aircraft, '%s{%d, %d}' %
               (aircraft.hub, aircraft.hub.lat, aircraft.hub.lon),
               aircraft.hookoff_point))

            aircraft.hookoff_point.name = 'hookoff-%s' % aircraft.hookoff_point

        # Place aircraft in order, ascending with Q, to fulfill LIFO condition.
        formation = sorted(self, key=lambda item: item.Q)

        # All aircraft at the front of the formation having the same destination
        # should hook off where the previous buddy (having a different
        # destination) hooked off.

        # Example: formation AMS-SFO, BRU-SFO, LHR-ATL.
        # AMS-SFO and BRU-SFO should hook off where LHR-ATL hooked off.
        # @todo Let AMS-SFO and BRU-SFO continue together along a new average
        #       formation trajectory (in this case directly to the destination)

        # First find the leading set of aircraft having the same destination
        formation.reverse()
        leading_destination = formation[0].destination
        leaders = []
        for aircraft in formation:

            # Start with always incurring benefits
            aircraft.incurs_benefits = True

            if not aircraft.destination.coincides(leading_destination):
                aircraft.is_leader = False
                continue

            aircraft.is_leader = True

            # Only the first leader incurs no benefits at all
            if len(leaders) == 0:
                aircraft.incurs_benefits = False

            leaders.append(aircraft)

        p('Leaders of formation %s are %s' % (formation, leaders))

        # Then find the buddy just before the set of leading aircraft, if
        # it exists.
        try:
            # The leaders: same hookoff point as last buddy.
            last_buddy = formation[len(leaders)]
            for aircraft in leaders:
                aircraft.Q = last_buddy.Q
                #aircraft.P = last_buddy.P
                aircraft.hookoff_point = last_buddy.hookoff_point
        except IndexError:
            pass

        # Change reversed formation back to normal
        formation.reverse()

        for aircraft in formation:

            p('Adjusting waypoints of %s. Initial waypoints: %s' %
              (aircraft, aircraft.route.waypoints))
            aircraft.route.waypoints = [
                #aircraft.hub,
                aircraft.hookoff_point,
                aircraft.destination
            ]
            aircraft.route.init_segments()
            p('Adjusted waypoints of %s. New waypoints: %s' %
              (aircraft, aircraft.route.waypoints))
            p('Need to calibrate aircraft %s (%s) in formation %s' %
              (aircraft, aircraft.route, formation))
            aircraft.controller.calibrate()