flight 0.0.23 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +619 -20
- data/README.markdown +4 -0
- data/README.rdoc +1 -1
- data/features/flight_committees.feature +311 -160
- data/features/flight_emissions.feature +101 -8
- data/features/support/env.rb +1 -1
- data/lib/flight.rb +0 -10
- data/lib/flight/carbon_model.rb +342 -153
- data/lib/flight/characterization.rb +18 -17
- data/lib/flight/data.rb +19 -18
- data/lib/flight/relationships.rb +17 -0
- data/lib/flight/summarization.rb +5 -2
- data/lib/test_support/flight_record.rb +4 -26
- metadata +48 -49
@@ -1,14 +1,107 @@
|
|
1
1
|
Feature: Flight Emissions Calculations
|
2
2
|
The flight model should generate correct emission calculations
|
3
3
|
|
4
|
-
Scenario
|
5
|
-
Given a flight has
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
Scenario: Calculations from default
|
5
|
+
Given a flight has nothing
|
6
|
+
When emissions are calculated
|
7
|
+
Then the emission value should be within "0.1" kgs of "28.3"
|
8
|
+
|
9
|
+
Scenario Outline: Calculations from date
|
10
|
+
Given a flight has "date" of "<date>"
|
10
11
|
When emissions are calculated
|
11
12
|
Then the emission value should be within "0.1" kgs of "<emission>"
|
12
13
|
Examples:
|
13
|
-
|
|
14
|
-
|
|
14
|
+
| date | emission |
|
15
|
+
| 2009-06-25 | 0.0 |
|
16
|
+
| 2010-06-25 | 28.3 |
|
17
|
+
| 2011-06-25 | 0.0 |
|
18
|
+
|
19
|
+
Scenario Outline: Calculations from date and timeframe
|
20
|
+
Given a flight has "date" of "<date>"
|
21
|
+
And it has "timeframe" of "<timeframe>"
|
22
|
+
When emissions are calculated
|
23
|
+
Then the emission value should be within "0.1" kgs of "<emission>"
|
24
|
+
Examples:
|
25
|
+
| date | timeframe | emission |
|
26
|
+
| 2009-06-25 | 2009-01-01/2009-01-31 | 0 |
|
27
|
+
| 2009-06-25 | 2009-01-01/2009-12-31 | 28.3 |
|
28
|
+
| 2009-06-25 | 2009-12-01/2009-12-31 | 0 |
|
29
|
+
|
30
|
+
Scenario: Calculations from cohort based on origin
|
31
|
+
Given a flight has "segments_per_trip" of "1"
|
32
|
+
And it has "origin_airport.iata_code" of "ADA"
|
33
|
+
When emissions are calculated
|
34
|
+
Then the emission value should be within "0.1" kgs of "4.2"
|
35
|
+
|
36
|
+
Scenario: Calculations from cohort based on destination
|
37
|
+
Given a flight has "segments_per_trip" of "1"
|
38
|
+
And it has "destination_airport.iata_code" of "AIA"
|
39
|
+
When emissions are calculated
|
40
|
+
Then the emission value should be within "0.1" kgs of "24.4"
|
41
|
+
|
42
|
+
Scenario: Calculations from cohort based on aircraft
|
43
|
+
Given a flight has "segments_per_trip" of "1"
|
44
|
+
And it has "aircraft.icao_code" of "FM1"
|
45
|
+
When emissions are calculated
|
46
|
+
Then the emission value should be within "0.1" kgs of "4.2"
|
47
|
+
|
48
|
+
Scenario: Calculations from cohort based on airline
|
49
|
+
Given a flight has "segments_per_trip" of "1"
|
50
|
+
And it has "airline.iata_code" of "DA"
|
51
|
+
When emissions are calculated
|
52
|
+
Then the emission value should be within "0.1" kgs of "4.2"
|
53
|
+
|
54
|
+
Scenario: Calculations from segments per trip
|
55
|
+
Given a flight has "segments_per_trip" of "2"
|
56
|
+
When emissions are calculated
|
57
|
+
Then the emission value should be within "0.1" kgs of "30.5"
|
58
|
+
|
59
|
+
Scenario: Calculations from seat class
|
60
|
+
Given a flight has "seat_class.name" of "economy"
|
61
|
+
When emissions are calculated
|
62
|
+
Then the emission value should be within "0.1" kgs of "25.5"
|
63
|
+
|
64
|
+
Scenario: Calculations from trips
|
65
|
+
Given a flight has "trips" of "2"
|
66
|
+
When emissions are calculated
|
67
|
+
Then the emission value should be within "0.1" kgs of "29.2"
|
68
|
+
|
69
|
+
Scenario: Calculations from load factor
|
70
|
+
Given a flight has "load_factor" of "1"
|
71
|
+
When emissions are calculated
|
72
|
+
Then the emission value should be within "0.1" kgs of "23.2"
|
73
|
+
|
74
|
+
Scenario: Calculations from seats estimate
|
75
|
+
Given a flight has "seats_estimate" of "100"
|
76
|
+
When emissions are calculated
|
77
|
+
Then the emission value should be within "0.1" kgs of "34.6"
|
78
|
+
|
79
|
+
Scenario: Calculations from aircraft class
|
80
|
+
Given a flight has "aircraft_class.brighter_planet_aircraft_class_code" of "BX"
|
81
|
+
When emissions are calculated
|
82
|
+
Then the emission value should be within "0.1" kgs of "41.5"
|
83
|
+
|
84
|
+
Scenario: Calculations from country
|
85
|
+
Given a flight has "country.iso_3166_code" of "UK"
|
86
|
+
When emissions are calculated
|
87
|
+
Then the emission value should be within "0.1" kgs of "30.9"
|
88
|
+
|
89
|
+
Scenario: Calculations from distance estimate
|
90
|
+
Given a flight has "distance_estimate" of "185.2"
|
91
|
+
When emissions are calculated
|
92
|
+
Then the emission value should be within "0.1" kgs of "7.7"
|
93
|
+
|
94
|
+
Scenario: Calculations from distance class
|
95
|
+
Given a flight has "distance_class.name" of "petite"
|
96
|
+
When emissions are calculated
|
97
|
+
Then the emission value should be within "0.1" kgs of "7.7"
|
98
|
+
|
99
|
+
Scenario: Calculations from aviation multiplier
|
100
|
+
Given a flight has "aviation_multiplier" of "1"
|
101
|
+
When emissions are calculated
|
102
|
+
Then the emission value should be within "0.1" kgs of "14.2"
|
103
|
+
|
104
|
+
Scenario: Calculations from fuel type
|
105
|
+
Given a flight has "fuel_type.name" of "Aviation Gasoline"
|
106
|
+
When emissions are calculated
|
107
|
+
Then the emission value should be within "0.1" kgs of "56.7"
|
data/features/support/env.rb
CHANGED
@@ -5,4 +5,4 @@ require 'cucumber'
|
|
5
5
|
require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support
|
6
6
|
|
7
7
|
require 'sniff'
|
8
|
-
Sniff.init File.join(File.dirname(__FILE__), '..', '..'), :earth => :air, :cucumber => true
|
8
|
+
Sniff.init File.join(File.dirname(__FILE__), '..', '..'), :earth => [:air, :locality, :fuel], :cucumber => true
|
data/lib/flight.rb
CHANGED
@@ -3,15 +3,5 @@ require 'emitter'
|
|
3
3
|
module BrighterPlanet
|
4
4
|
module Flight
|
5
5
|
extend BrighterPlanet::Emitter
|
6
|
-
|
7
|
-
def self.flight_model
|
8
|
-
if Object.const_defined? 'Flight'
|
9
|
-
::Flight
|
10
|
-
elsif Object.const_defined? 'FlightRecord'
|
11
|
-
FlightRecord
|
12
|
-
else
|
13
|
-
raise 'There is no flight model'
|
14
|
-
end
|
15
|
-
end
|
16
6
|
end
|
17
7
|
end
|
data/lib/flight/carbon_model.rb
CHANGED
@@ -1,22 +1,41 @@
|
|
1
|
-
|
1
|
+
# Copyright © 2010 Brighter Planet.
|
2
|
+
# See LICENSE for details.
|
3
|
+
# Contact Brighter Planet for dual-license arrangements.
|
4
|
+
|
2
5
|
require 'weighted_average'
|
3
6
|
|
7
|
+
## Flight:carbon model
|
8
|
+
# This module is used by [Brighter Planet](http://brighterplanet.com)'s carbon emission [web service](http://carbon.brighterplanet.com) to estimate the **greenhouse gas emissions of passenger air travel**.
|
9
|
+
#
|
10
|
+
# The final estimate is the result of the **calculations** detailed below.
|
11
|
+
# These calculations are performed in reverse order, starting with the last calculation listed and finishing with the `emission` calculation.
|
12
|
+
#
|
13
|
+
# To accomodate varying input data, each calculation may have one or more **methods**. These are listed under each calculation in order from most to least preferred.
|
4
14
|
module BrighterPlanet
|
5
15
|
module Flight
|
6
16
|
module CarbonModel
|
7
17
|
def self.included(base)
|
8
18
|
base.extend FastTimestamp
|
9
19
|
base.decide :emission, :with => :characteristics do
|
20
|
+
### Emission
|
21
|
+
# This calculation returns the `emission` estimate in *kg CO<sub>2</sub>e*.
|
22
|
+
# The `emission` estimate is the passenger's share of the total flight emissions that occured during the `timeframe`.
|
10
23
|
committee :emission do
|
11
|
-
|
12
|
-
|
13
|
-
|
24
|
+
##### From fuel, emission factor, freight share, passengers, and multipliers
|
25
|
+
# This method:
|
26
|
+
#
|
27
|
+
# 1. Checks that the flight occured during the `timeframe`
|
28
|
+
# 2. Multiplies `fuel use` (*kg fuel*) by an `emission factor` (*kg CO<sub>2</sub>e / kg fuel*) and an `aviation multiplier` to give total flight emissions in *kg CO<sub>2</sub>e*
|
29
|
+
# 3. Multiplies by (1 - `freight share`) to take out emissions attributed to freight cargo and mail, leaving emissions attributed to passengers and their baggage
|
30
|
+
# 4. Divides by the number of `passengers` and multiplies by a `seat class multiplier` to give `emission` for the passenger
|
31
|
+
quorum 'from fuel, emission factor, freight share, passengers, multipliers, and date',
|
32
|
+
:needs => [:fuel, :emission_factor, :freight_share, :passengers, :seat_class_multiplier, :aviation_multiplier, :date], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics, timeframe|
|
14
33
|
date = characteristics[:date].is_a?(Date) ?
|
15
34
|
characteristics[:date] :
|
16
35
|
Date.parse(characteristics[:date].to_s)
|
17
36
|
if timeframe.include? date
|
18
|
-
|
19
|
-
|
37
|
+
characteristics[:fuel] * characteristics[:emission_factor] * characteristics[:aviation_multiplier] * (1 - characteristics[:freight_share]) / characteristics[:passengers] * characteristics[:seat_class_multiplier]
|
38
|
+
# If the flight did not occur during the `timeframe`, `emission` is zero.
|
20
39
|
else
|
21
40
|
0
|
22
41
|
end
|
@@ -27,14 +46,46 @@ module BrighterPlanet
|
|
27
46
|
end
|
28
47
|
end
|
29
48
|
|
30
|
-
|
31
|
-
|
32
|
-
|
49
|
+
### Emission factor
|
50
|
+
# This calculation returns the `emission factor` in *kg CO<sub>2</sub>e / kg fuel*.
|
51
|
+
committee :emission_factor do
|
52
|
+
##### From fuel type
|
53
|
+
# This method looks up data on [fuel types](http://data.brighterplanet.com/fuel_types) and divides the `fuel type` `emission factor` (*kg CO<sub>2</sub> / litre fuel*) by the `fuel type` `density` (*kg fuel / litre fuel*) to give *kg CO<sub>2</sub>e / kg fuel*.
|
54
|
+
quorum 'from fuel type', :needs => :fuel_type, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
55
|
+
characteristics[:fuel_type].emission_factor / characteristics[:fuel_type].density
|
33
56
|
end
|
34
57
|
end
|
35
58
|
|
36
|
-
|
37
|
-
|
59
|
+
### Aviation multiplier
|
60
|
+
# This calculation returns the `aviation multiplier`, which approximates the extra climate impact of emissions high in the atmosphere.
|
61
|
+
committee :aviation_multiplier do
|
62
|
+
##### Default
|
63
|
+
# This method uses an `aviation multiplier` of **2.0** after [Kolmuss and Crimmins (2009)](http://sei-us.org/publications/id/13).
|
64
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
65
|
+
base.fallback.aviation_multiplier
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
### Fuel
|
70
|
+
# This calculation returns the flight's total `fuel` use in *kg fuel*.
|
71
|
+
committee :fuel do
|
72
|
+
##### From fuel per segment and segments per trip and trips
|
73
|
+
# This method multiplies the `fuel per segment` (*kg fuel*) by the `segments per trip` and the number of `trips` to give *kg fuel*.
|
74
|
+
quorum 'from fuel per segment and segments per trip and trips', :needs => [:fuel_per_segment, :segments_per_trip, :trips], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
75
|
+
characteristics[:fuel_per_segment] * characteristics[:segments_per_trip].to_f * characteristics[:trips].to_f
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
### Fuel per segment
|
80
|
+
# This calculation returns the `fuel per segment` in *kg fuel*.
|
81
|
+
committee :fuel_per_segment do
|
82
|
+
##### From adjusted distance per segment and fuel use coefficients
|
83
|
+
# This method uses a third-order polynomial equation to calculate the fuel used per segment:
|
84
|
+
#
|
85
|
+
# (m<sub>3</sub> * d^3 ) + (m<sub>2</sub> * d^2 ) + (m<sub>1</sub> * d) + endpoint fuel
|
86
|
+
#
|
87
|
+
# Where d is the `adjusted distance per segment` and m<sub>3</sub>, m<sub>2</sub>, m<sub>2</sub>, and endpoint fuel are the `fuel use coefficients`.
|
88
|
+
quorum 'from adjusted distance per segment and fuel use coefficients', :needs => [:adjusted_distance_per_segment, :fuel_use_coefficients], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
38
89
|
characteristics[:fuel_use_coefficients].m3.to_f * characteristics[:adjusted_distance_per_segment].to_f ** 3 +
|
39
90
|
characteristics[:fuel_use_coefficients].m2.to_f * characteristics[:adjusted_distance_per_segment].to_f ** 2 +
|
40
91
|
characteristics[:fuel_use_coefficients].m1.to_f * characteristics[:adjusted_distance_per_segment].to_f +
|
@@ -42,30 +93,131 @@ module BrighterPlanet
|
|
42
93
|
end
|
43
94
|
end
|
44
95
|
|
96
|
+
### Adjusted distance per segment
|
97
|
+
# This calculation returns the `adjusted distance per segment` in *nautical miles*.
|
45
98
|
committee :adjusted_distance_per_segment do
|
46
|
-
|
47
|
-
|
99
|
+
##### From adjusted distance and segments per trip
|
100
|
+
# This method divides the `adjusted distance` (*nautical miles*) by `segments per trip` to give *nautical miles*.
|
101
|
+
quorum 'from adjusted distance and segments per trip', :needs => [:adjusted_distance, :segments_per_trip], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
102
|
+
characteristics[:adjusted_distance] / characteristics[:segments_per_trip]
|
48
103
|
end
|
49
104
|
end
|
50
105
|
|
106
|
+
### Adjusted distance
|
107
|
+
# This calculation returns the `adjusted distance` in *nautical miles*.
|
108
|
+
# The `adjusted distance` accounts for factors that increase the actual distance traveled by real world flights.
|
109
|
+
committee :adjusted_distance do
|
110
|
+
##### From distance, route inefficiency factor, and dogleg factor
|
111
|
+
# This method multiplies `distance` (*nautical miles*) by a `route inefficiency factor` and a `dogleg factor` to give *nautical miles*.
|
112
|
+
quorum 'from distance, route inefficiency factor, and dogleg factor', :needs => [:distance, :route_inefficiency_factor, :dogleg_factor], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
113
|
+
characteristics[:distance] * characteristics[:route_inefficiency_factor] * characteristics[:dogleg_factor]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
### Distance
|
118
|
+
# This calculation returns the flight's base `distance` in *nautical miles*.
|
119
|
+
committee :distance do
|
120
|
+
##### From airports
|
121
|
+
# This first-tier method calculates the great circle distance between the `origin airport` and `destination airport` and converts from *km* to *nautical miles*.
|
122
|
+
quorum 'from airports', :needs => [:origin_airport, :destination_airport], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
123
|
+
if characteristics[:origin_airport].latitude and
|
124
|
+
characteristics[:origin_airport].longitude and
|
125
|
+
characteristics[:destination_airport].latitude and
|
126
|
+
characteristics[:destination_airport].longitude
|
127
|
+
characteristics[:origin_airport].distance_to(characteristics[:destination_airport], :units => :kms).kilometres.to :nautical_miles
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
##### From distance estimate
|
132
|
+
# This second-tier method converts the `distance_estimate` in *km* to *nautical miles*.
|
133
|
+
quorum 'from distance estimate', :needs => :distance_estimate, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
134
|
+
characteristics[:distance_estimate].kilometres.to :nautical_miles
|
135
|
+
end
|
136
|
+
|
137
|
+
##### From distance class
|
138
|
+
# This third-tier method looks up the [distance class](http://data.brighterplanet.com/flight_distance_classes)'s `distance` and converts from *km* to *nautical miles*.
|
139
|
+
quorum 'from distance class', :needs => :distance_class, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
140
|
+
characteristics[:distance_class].distance.kilometres.to :nautical_miles
|
141
|
+
end
|
142
|
+
|
143
|
+
##### From cohort
|
144
|
+
# This fourth-tier method calculates the average `distance` of the `cohort` segments, weighted by their passengers, and converts from *km* to *nautical miles*.
|
145
|
+
quorum 'from cohort', :needs => :cohort do |characteristics| # cohort here will be some combo of origin, airline, and aircraft
|
146
|
+
distance = characteristics[:cohort].weighted_average(:distance, :weighted_by => :passengers).kilometres.to(:nautical_miles)
|
147
|
+
distance > 0 ? distance : nil
|
148
|
+
end
|
149
|
+
|
150
|
+
##### Default
|
151
|
+
# This default method calculates the average `distance` of [all segments in the T-100 database](http://data.brighterplanet.com/flight_segments), weighted by their passengers, and converts from *km* to *nautical miles*.
|
152
|
+
quorum 'default' do
|
153
|
+
FlightSegment.fallback.distance.kilometres.to :nautical_miles
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
### Route inefficiency factor
|
158
|
+
# This calculation returns the `route inefficiency factor`, a measure of how much farther real world flights travel than the great circle distance between their origin and destination.
|
159
|
+
# It accounts for factors like flight path routing around controlled airspace and circling while waiting for clearance to land.
|
160
|
+
committee :route_inefficiency_factor do
|
161
|
+
##### From country
|
162
|
+
# This first-tier method looks up the `route inefficiency factor` for the [country](http://data.brighterplanet.com/countries) in which the flight occurs.
|
163
|
+
quorum 'from country', :needs => :country, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
164
|
+
characteristics[:country].andand.flight_route_inefficiency_factor
|
165
|
+
end
|
166
|
+
|
167
|
+
##### Default
|
168
|
+
# This default method uses a `route inefficiency factor` of **10%** based on [Kettunen et al. (2005)](http://www.atmseminar.org/seminarContent/seminar6/papers/p_055_MPM.pdf)
|
169
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
170
|
+
Country.fallback.flight_route_inefficiency_factor
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
### Dogleg factor
|
175
|
+
# This calculation returns the `dogleg factor`, a measure of how far out of the way the average layover is compared to a direct flight.
|
176
|
+
committee :dogleg_factor do
|
177
|
+
##### From segments per trip
|
178
|
+
# This method assumes that each layover increases the total flight distance by **25%**.
|
179
|
+
quorum 'from segments per trip', :needs => :segments_per_trip, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
180
|
+
base.fallback.dogleg_factor ** (characteristics[:segments_per_trip] - 1)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
### Distance estimate
|
185
|
+
# This implied calculation returns the client-input 'distance estimate' in *km*.
|
186
|
+
|
187
|
+
### Distance class
|
188
|
+
# This implied calculation returns the client-input [distance class](http://data.brighterplanet.com/distance_classes).
|
189
|
+
|
190
|
+
### Fuel use coefficients
|
191
|
+
# This calculation returns the `fuel use coefficients`, the coefficients of the third-order polynomial equation that describes aircraft fuel use.
|
51
192
|
committee :fuel_use_coefficients do
|
52
|
-
|
193
|
+
##### From aircraft
|
194
|
+
# This first-tier method looks up the [aircraft](http://data.brighterplanet.com/aircraft)'s `fuel use coefficients`.
|
195
|
+
quorum 'from aircraft', :needs => :aircraft, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
53
196
|
aircraft = characteristics[:aircraft]
|
54
|
-
FuelUseEquation.new aircraft.m3, aircraft.m2, aircraft.m1, aircraft.endpoint_fuel
|
197
|
+
fuel_use = FuelUseEquation.new aircraft.m3, aircraft.m2, aircraft.m1, aircraft.endpoint_fuel
|
198
|
+
if fuel_use.empty?
|
199
|
+
nil
|
200
|
+
else
|
201
|
+
fuel_use
|
202
|
+
end
|
55
203
|
end
|
56
204
|
|
57
|
-
|
205
|
+
##### From aircraft class
|
206
|
+
# This second-tier method looks up the [aircraft class](http://data.brighterplanet.com/aircraft_classes)'s `fuel use coefficients`.
|
207
|
+
quorum 'from aircraft class', :needs => :aircraft_class, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
58
208
|
aircraft_class = characteristics[:aircraft_class]
|
59
209
|
FuelUseEquation.new aircraft_class.m3, aircraft_class.m2, aircraft_class.m1, aircraft_class.endpoint_fuel
|
60
210
|
end
|
61
211
|
|
62
|
-
|
212
|
+
##### From cohort
|
213
|
+
# This third-tier method calculates the average `fuel use coefficients` of the `cohort` segments, weighted by their passengers.
|
214
|
+
quorum 'from cohort', :needs => :cohort, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
63
215
|
flight_segments = characteristics[:cohort]
|
64
216
|
|
65
217
|
passengers = flight_segments.inject(0) do |passengers, flight_segment|
|
66
218
|
passengers + flight_segment.passengers
|
67
219
|
end
|
68
|
-
|
220
|
+
|
69
221
|
flight_segment_aircraft = flight_segments.inject({}) do |hsh, flight_segment|
|
70
222
|
bts_code = flight_segment.bts_aircraft_type_code
|
71
223
|
key = flight_segment.row_hash
|
@@ -93,7 +245,7 @@ module BrighterPlanet
|
|
93
245
|
else
|
94
246
|
m2 = Aircraft.fallback.m2
|
95
247
|
end
|
96
|
-
|
248
|
+
|
97
249
|
if flight_segment_aircraft.values.map(&:m1).any?
|
98
250
|
m1 = flight_segments.inject(0) do |m1, flight_segment|
|
99
251
|
aircraft = flight_segment_aircraft[flight_segment.row_hash]
|
@@ -103,7 +255,7 @@ module BrighterPlanet
|
|
103
255
|
else
|
104
256
|
m1 = Aircraft.fallback.m1
|
105
257
|
end
|
106
|
-
|
258
|
+
|
107
259
|
if flight_segment_aircraft.values.map(&:endpoint_fuel).any?
|
108
260
|
endpoint_fuel = flight_segments.inject(0) do |endpoint_fuel, flight_segment|
|
109
261
|
aircraft = flight_segment_aircraft[flight_segment.row_hash]
|
@@ -113,18 +265,20 @@ module BrighterPlanet
|
|
113
265
|
else
|
114
266
|
endpoint_fuel = Aircraft.fallback.endpoint_fuel
|
115
267
|
end
|
116
|
-
|
268
|
+
|
117
269
|
if [m3, m2, m1, endpoint_fuel, passengers].any?(&:nonzero?)
|
118
270
|
m3 = m3 / passengers
|
119
271
|
m2 = m2 / passengers
|
120
272
|
m1 = m1 / passengers
|
121
273
|
endpoint_fuel = endpoint_fuel / passengers
|
122
|
-
|
274
|
+
|
123
275
|
FuelUseEquation.new m3, m2, m1, endpoint_fuel
|
124
276
|
end
|
125
277
|
end
|
126
278
|
|
127
|
-
|
279
|
+
##### Default
|
280
|
+
# This default method calculates the average `fuel use coefficients` of [all segments in the T-100 database](http://data.brighterplanet.com/flight_segments), weighted by their passengers.
|
281
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
128
282
|
fallback = Aircraft.fallback
|
129
283
|
if fallback
|
130
284
|
FuelUseEquation.new fallback.m3, fallback.m2, fallback.m1, fallback.endpoint_fuel
|
@@ -132,29 +286,47 @@ module BrighterPlanet
|
|
132
286
|
end
|
133
287
|
end
|
134
288
|
|
289
|
+
### Fuel type
|
290
|
+
# This calculation returns the `fuel type`.
|
291
|
+
committee :fuel_type do
|
292
|
+
##### From client input
|
293
|
+
# This implied first-tier method uses the client-input [fuel type](http://data.brighterplanet.com/fuel_types).
|
294
|
+
|
295
|
+
##### Default
|
296
|
+
# This method assumes the flight uses **Jet Fuel**.
|
297
|
+
quorum 'default' do
|
298
|
+
FuelType.find_by_name 'Jet Fuel'
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
### Passengers
|
303
|
+
# This calculation returns the number of `passengers`.
|
135
304
|
committee :passengers do
|
136
|
-
|
305
|
+
##### From seats and load factor
|
306
|
+
# This method multiplies the number of `seats` by the `load factor`.
|
307
|
+
quorum 'from seats and load factor', :needs => [:seats, :load_factor], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
137
308
|
(characteristics[:seats] * characteristics[:load_factor]).round
|
138
309
|
end
|
139
310
|
end
|
140
311
|
|
312
|
+
### Seats
|
313
|
+
# This calculation returns the number of `seats`.
|
141
314
|
committee :seats do
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
#SE aircraft.seats
|
146
|
-
#SE end
|
147
|
-
#SE end
|
148
|
-
|
149
|
-
quorum 'from aircraft', :needs => :aircraft do |characteristics|
|
315
|
+
##### From aircraft
|
316
|
+
# This first-tier method looks up the [aircraft](http://data.brighterplanet.com/aircraft)'s average number of `seats`.
|
317
|
+
quorum 'from aircraft', :needs => :aircraft, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
150
318
|
characteristics[:aircraft].seats
|
151
319
|
end
|
152
320
|
|
153
|
-
|
321
|
+
##### From seats estimate
|
322
|
+
# This second-tier method uses the input estimate of the number of `seats`.
|
323
|
+
quorum 'from seats estimate', :needs => :seats_estimate, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
154
324
|
characteristics[:seats_estimate]
|
155
325
|
end
|
156
326
|
|
157
|
-
|
327
|
+
##### From cohort
|
328
|
+
# This third-tier method calculates the average number of `seats` of the `cohort` segments, weighted by their passengers.
|
329
|
+
quorum 'from cohort', :needs => :cohort, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
158
330
|
seats = characteristics[:cohort].weighted_average :seats, :weighted_by => :passengers
|
159
331
|
if seats.nil? or seats.zero?
|
160
332
|
nil
|
@@ -163,160 +335,131 @@ module BrighterPlanet
|
|
163
335
|
end
|
164
336
|
end
|
165
337
|
|
166
|
-
|
167
|
-
|
338
|
+
##### From aircraft class
|
339
|
+
# This fourth-tier method looks up the [aircraft class](http://data.brighterplanet.com/aircraft_classes)'s average number of `seats`.
|
340
|
+
quorum 'from aircraft class', :needs => :aircraft_class, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
341
|
+
characteristics[:aircraft_class].seats_before_type_cast
|
168
342
|
end
|
169
343
|
|
344
|
+
##### Default
|
345
|
+
# This default method calculates the average number of `seats` of [all segments in the T-100 database](http://data.brighterplanet.com/flight_segments), weighted by their passengers.
|
170
346
|
quorum 'default' do
|
171
|
-
FlightSegment.fallback.
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
committee :load_factor do
|
176
|
-
quorum 'from cohort', :needs => :cohort do |characteristics|
|
177
|
-
characteristics[:cohort].weighted_average(:load_factor, :weighted_by => :passengers)
|
178
|
-
end
|
179
|
-
|
180
|
-
quorum 'default' do
|
181
|
-
base.fallback.andand.load_factor
|
347
|
+
FlightSegment.fallback.seats_before_type_cast # need before_type_cast b/c seats is an integer but the fallback value is a float
|
182
348
|
end
|
183
349
|
end
|
184
350
|
|
185
|
-
|
186
|
-
|
187
|
-
route_inefficiency_factor = base.research(:route_inefficiency_factor)
|
188
|
-
dogleg_factor = base.research(:dogleg_factor)
|
189
|
-
characteristics[:distance] * route_inefficiency_factor * ( dogleg_factor ** (characteristics[:emplanements_per_trip] - 1) )
|
190
|
-
end
|
191
|
-
end
|
351
|
+
### Seats estimate
|
352
|
+
# This implied calculation returns the client-input `seats estimate`.
|
192
353
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
characteristics[:origin_airport].distance_to(characteristics[:destination_airport], :units => :kms).kilometres.to :nautical_miles
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
quorum 'from distance estimate', :needs => :distance_estimate do |characteristics|
|
204
|
-
characteristics[:distance_estimate].kilometres.to :nautical_miles
|
205
|
-
end
|
206
|
-
|
207
|
-
quorum 'from distance class', :needs => :distance_class do |characteristics|
|
208
|
-
characteristics[:distance_class].distance.kilometres.to :nautical_miles
|
209
|
-
end
|
354
|
+
### Load factor
|
355
|
+
# This calculation returns the `load factor`.
|
356
|
+
# The `load factor` is the portion of available seats that are occupied.
|
357
|
+
committee :load_factor do
|
358
|
+
##### From client input
|
359
|
+
# This implied first-tier method uses the client-input `load factor`.
|
210
360
|
|
211
|
-
|
212
|
-
|
213
|
-
|
361
|
+
##### From cohort
|
362
|
+
# This second-tier method calculates the average `load factor` of the `cohort` segments, weighted by their passengers.
|
363
|
+
quorum 'from cohort', :needs => :cohort, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
364
|
+
characteristics[:cohort].weighted_average(:load_factor, :weighted_by => :passengers)
|
214
365
|
end
|
215
366
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
committee :emplanements_per_trip do # per trip
|
222
|
-
quorum 'default' do
|
223
|
-
base.fallback.emplanements_per_trip_before_type_cast
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
committee :radiative_forcing_index do
|
228
|
-
quorum 'from fuel type', :needs => :fuel_type do |characteristics|
|
229
|
-
characteristics[:fuel_type].radiative_forcing_index
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
committee :emission_factor do # returns kg CO2 / kg fuel
|
234
|
-
quorum 'from fuel type', :needs => :fuel_type do |characteristics|
|
235
|
-
#( kg CO2 / litres fuel ) * ( litres fuel / kg fuel )
|
236
|
-
characteristics[:fuel_type].emission_factor * ( 1 / characteristics[:fuel_type].density).gallons.to(:litres)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
committee :fuel_type do
|
241
|
-
quorum 'default' do
|
242
|
-
FlightFuelType.fallback
|
367
|
+
##### Default
|
368
|
+
# This default method calculates the average `load factor` of [all segments in the T-100 database](http://data.brighterplanet.com/flight_segments), weighted by their passengers.
|
369
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
370
|
+
FlightSegment.fallback.load_factor
|
243
371
|
end
|
244
372
|
end
|
245
373
|
|
374
|
+
### Freight share
|
375
|
+
# This calculation returns the `freight share`.
|
376
|
+
# The `freight share` is the percent of the total aircraft weight that is freight cargo and mail (as opposed to passengers and their baggage).
|
246
377
|
committee :freight_share do
|
247
|
-
|
378
|
+
##### From cohort
|
379
|
+
# This first-tier method calculates the average `freight share` of the `cohort` segments, weighted by their passengers
|
380
|
+
quorum 'from cohort', :needs => :cohort, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
248
381
|
characteristics[:cohort].weighted_average(:freight_share, :weighted_by => :passengers)
|
249
382
|
end
|
250
383
|
|
251
|
-
|
252
|
-
|
384
|
+
##### Default
|
385
|
+
# This default method calculates the average `freight share` of [all segments in the T-100 database](http://data.brighterplanet.com/flight_segments), weighted by their passengers.
|
386
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
387
|
+
FlightSegment.fallback.freight_share
|
253
388
|
end
|
254
389
|
end
|
255
390
|
|
391
|
+
### Trips
|
392
|
+
# This calculation returns the number of `trips`.
|
393
|
+
# A one-way flight has one trip; a round-trip flight has two trips.
|
256
394
|
committee :trips do
|
257
|
-
|
258
|
-
|
395
|
+
##### From client input
|
396
|
+
# This implied first-tier method uses the client-input number of `trips`.
|
397
|
+
|
398
|
+
##### Default
|
399
|
+
# This default method calculates the average number of `trips` from the [U.S. National Household Travel Survey](http://www.bts.gov/publications/america_on_the_go/long_distance_transportation_patterns/html/table_07.html).
|
400
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
401
|
+
base.fallback.trips_before_type_cast # need before_type_cast b/c trips is an integer but fallback value is a float
|
259
402
|
end
|
260
403
|
end
|
261
404
|
|
262
|
-
|
263
|
-
#
|
264
|
-
# committee :domesticity do
|
265
|
-
# quorum 'from airports', :needs => [:origin_airport, :destination_airport] do |characteristics|
|
266
|
-
# if [characteristics[:origin_airport], characteristics[:destination_airport]].all?(&:united_states?)
|
267
|
-
# FlightDomesticity.find_by_name('domestic')
|
268
|
-
# elsif [characteristics[:origin_airport], characteristics[:destination_airport]].any?(&:united_states?)
|
269
|
-
# FlightDomesticity.find_by_name('international')
|
270
|
-
# end
|
271
|
-
# end
|
272
|
-
#
|
273
|
-
# quorum 'from origin', :needs => :origin_airport do |characteristics|
|
274
|
-
# if characteristics[:origin_airport].all_flights_from_here_domestic?
|
275
|
-
# FlightDomesticity.find_by_name('domestic')
|
276
|
-
# end
|
277
|
-
# end
|
278
|
-
#
|
279
|
-
# quorum 'from destination', :needs => :destination_airport do |characteristics|
|
280
|
-
# if characteristics[:destination_airport].all_flights_to_here_domestic?
|
281
|
-
# FlightDomesticity.find_by_name('domestic')
|
282
|
-
# end
|
283
|
-
# end
|
284
|
-
#
|
285
|
-
# quorum 'from airline', :needs => :airline do |characteristics|
|
286
|
-
# if characteristics[:airline].all_flights_domestic?
|
287
|
-
# FlightDomesticity.find_by_name('domestic')
|
288
|
-
# end
|
289
|
-
# end
|
290
|
-
# end
|
291
|
-
|
405
|
+
### Seat class multiplier
|
406
|
+
# This calculation returns the `seat class multiplier`, which reflects the amount of cabin space occupied by the passenger's seat.
|
292
407
|
committee :seat_class_multiplier do
|
293
|
-
|
408
|
+
##### From seat class
|
409
|
+
# This first-tier method looks up the [seat class](http://data.brighterplanet.com/flight_seat_classes)'s `seat class multiplier`.
|
410
|
+
quorum 'from seat class', :needs => :seat_class, :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
294
411
|
characteristics[:seat_class].multiplier
|
295
412
|
end
|
296
413
|
|
297
|
-
|
298
|
-
|
414
|
+
##### Default
|
415
|
+
# This default method uses a `seat class multiplier` of **1**.
|
416
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
417
|
+
FlightSeatClass.fallback.multiplier
|
299
418
|
end
|
300
419
|
end
|
301
420
|
|
302
|
-
|
303
|
-
|
304
|
-
|
421
|
+
### Seat class
|
422
|
+
# This implied calculation returns the client-input [seat class](http://data.brighterplanet.com/seat_classes).
|
423
|
+
|
424
|
+
### Country
|
425
|
+
# This calculation returns the [country](http://data.brighterplanet.com/countries) in which a flight occurs.
|
426
|
+
committee :country do
|
427
|
+
##### From origin airport and destination airport
|
428
|
+
# This method checks that the flight's `origin airport` and `destination airport` are within the same country.
|
429
|
+
# If so, that country is the `country`.
|
430
|
+
quorum 'from origin airport and destination airport', :needs => [:origin_airport, :destination_airport], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
431
|
+
if characteristics[:origin_airport].country == characteristics[:destination_airport].country
|
432
|
+
characteristics[:origin_airport].country
|
433
|
+
end
|
305
434
|
end
|
306
435
|
|
307
|
-
|
308
|
-
|
309
|
-
end
|
436
|
+
##### From client input
|
437
|
+
# This implied method uses the client-input [country](http://data.brighterplanet.com/countries).
|
310
438
|
end
|
311
439
|
|
440
|
+
### Cohort
|
441
|
+
# This calculation returns the `cohort`, which is a set of flight segment records in the [T-100 database](http://data.brighterplanet.com/flight_segments) that match certain client-input values.
|
312
442
|
committee :cohort do
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
443
|
+
##### From segments per trip and input
|
444
|
+
# This method:
|
445
|
+
#
|
446
|
+
# 1. Checks that the flight is direct
|
447
|
+
# 2. Takes the input values for `origin airport`, `destination airport`, `aircraft`, and `airline`
|
448
|
+
# 3. Selects all the records in the T-100 database that match the available input values
|
449
|
+
# 4. Drops the last input value (initially `airline`, then `aircraft`, etc.) if no records match all of the available input values
|
450
|
+
# 5. Repeats steps 3 and 4 until some records match or no input values remain
|
451
|
+
#
|
452
|
+
# If no records match any of the input values, or if the flight is indirect, then `cohort` is undefined.
|
453
|
+
quorum 'from segments per trip and input', :needs => :segments_per_trip, :appreciates => [:origin_airport, :destination_airport, :aircraft, :airline], :complies => [:ghg_protocol, :iso, :tcr] do |characteristics|
|
454
|
+
cohort = {}
|
455
|
+
if characteristics[:segments_per_trip] == 1
|
456
|
+
provided_characteristics = [:origin_airport, :destination_airport, :aircraft, :airline].
|
457
|
+
inject(ActiveSupport::OrderedHash.new) do |memo, characteristic_name|
|
458
|
+
memo[characteristic_name] = characteristics[characteristic_name]
|
459
|
+
memo
|
460
|
+
end
|
461
|
+
cohort = FlightSegment.strict_cohort provided_characteristics
|
462
|
+
end
|
320
463
|
if cohort.any?
|
321
464
|
cohort
|
322
465
|
else
|
@@ -324,10 +467,56 @@ module BrighterPlanet
|
|
324
467
|
end
|
325
468
|
end
|
326
469
|
end
|
470
|
+
|
471
|
+
### Origin airport
|
472
|
+
# This implied calculation returns the client-input [origin airport](http://data.brighterplanet.com/airports).
|
473
|
+
|
474
|
+
### Destination airport
|
475
|
+
# This implied calculation returns the client-input [destination airport](http://data.brighterplanet.com/airports).
|
476
|
+
|
477
|
+
### Aircraft
|
478
|
+
# This implied calculation returns the client-input type of [aircraft](http://data.brighterplanet.com/aircraft).
|
479
|
+
|
480
|
+
### Aircraft class
|
481
|
+
# This implied calculation returns the client-input [aircraft_class](http://data.brighterplanet.com/aircraft_classes).
|
482
|
+
|
483
|
+
### Airline
|
484
|
+
# This implied calculation returns the client-input [airline](http://data.brighterplanet.com/airlines) operating the flight.
|
485
|
+
|
486
|
+
### Segments per trip
|
487
|
+
# This calculation returns the `segments per trip`.
|
488
|
+
# Direct flights have a single segment per trip. Indirect flights with one or more layovers have two or more segments per trip.
|
489
|
+
committee :segments_per_trip do
|
490
|
+
##### From client input
|
491
|
+
# This implied first-tier method uses the client-input `segments per trip`.
|
492
|
+
|
493
|
+
##### Default
|
494
|
+
# This default method calculates the average `segments per trip` from the [U.S. National Household Travel Survey](http://nhts.ornl.gov/).
|
495
|
+
quorum 'default', :complies => [:ghg_protocol, :iso, :tcr] do
|
496
|
+
base.fallback.segments_per_trip_before_type_cast # need before_type_cast b/c segments_per_trip is an integer but fallback value is a float
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
### Date
|
501
|
+
# This calculation returns the `date` on which the flight occured.
|
502
|
+
committee :date do
|
503
|
+
##### From client input
|
504
|
+
# This implied first-tier method uses the client-input value for `date`.
|
505
|
+
|
506
|
+
##### From timeframe
|
507
|
+
# This second-tier method assumes the flight occured on the first day of the `timeframe`.
|
508
|
+
quorum 'from timeframe', :complies => [:ghg_protocol, :iso, :tcr] do |characteristics, timeframe|
|
509
|
+
timeframe.from
|
510
|
+
end
|
511
|
+
end
|
327
512
|
end
|
328
513
|
end
|
329
514
|
|
330
|
-
class FuelUseEquation < Struct.new(:m3, :m2, :m1, :endpoint_fuel)
|
515
|
+
class FuelUseEquation < Struct.new(:m3, :m2, :m1, :endpoint_fuel)
|
516
|
+
def empty?
|
517
|
+
m3.nil? and m2.nil? and m1.nil? and endpoint_fuel.nil?
|
518
|
+
end
|
519
|
+
end
|
331
520
|
end
|
332
521
|
end
|
333
522
|
end
|