flight 0.0.23 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|