diff --git a/docs/explanation/input-prioritization.md b/docs/explanation/input-prioritization.md index fbac529..402147a 100644 --- a/docs/explanation/input-prioritization.md +++ b/docs/explanation/input-prioritization.md @@ -11,10 +11,10 @@ This page describes the decision logic for determining which performance and fue | Data | Primary (AO-provided) | Secondary (modelled) | | --- | --- | --- | | Trajectory | OFP, QAR | FTFM/RTFM, CTFM | -| Fuel flow | Recorded fuel flow | BADA estimate | -| Engine efficiency | Recorded η | BADA thrust / fuel flow | -| Aircraft mass | Mass evolution | BADA iterative estimate | -| Take-off weight | AO-declared TOW | OEW + load_factor × (MTOW − OEW) | +| Fuel flow | Recorded fuel flow, or derived from the recorded mass evolution | BADA estimate | +| Engine efficiency | Recorded η | From BADA thrust and BADA fuel flow | +| Aircraft mass | Mass evolution | Take-off weight decreased along the trajectory with the BADA fuel flow | +| Take-off weight | AO-declared TOW | Iterative estimate: min(MTOW, OEW + load_factor × MPL + 1.15 × trip fuel), see [Mass Estimation](#mass-estimation) | | Fuel properties | AO-declared (H/C, q_fuel) | JET-A defaults | | Engine UID | AO-declared | MRR conservative value | @@ -22,30 +22,67 @@ This page describes the decision logic for determining which performance and fue ## Performance Input Decision Tree +Each performance quantity has its own source, so the cases are not exclusive: for example, an AO fuel flow can be combined with a BADA aircraft mass. The source of one quantity can still depend on what else is provided, as the tree shows. + ```mermaid graph TD - A{"Fuel flow +
engine η
provided?"} -->|Yes| B["Use directly
Skip performance step"] - A -->|No| C{"Fuel flow
only
provided?"} - C -->|Yes| D["Use AO fuel flow
Compute η via BADA
thrust + fuel flow"] - C -->|No| E{"Aircraft mass
evolution
provided?"} - E -->|Yes| F["Use mass in BADA
→ fuel flow, thrust, η"] - E -->|No| G{"TOW
provided?"} - G -->|Yes| H["Use TOW in BADA
→ mass evolution
→ fuel flow, thrust, η"] - G -->|No| I["Infer mass from
load factor
Iterative estimation"] - - style B fill:#4caf50,color:#fff - style D fill:#ff9800,color:#fff - style F fill:#ff9800,color:#fff - style H fill:#2196f3,color:#fff - style I fill:#f44336,color:#fff + A{"Aircraft type in
the BADA mapping?"} -->|No| X["Flight fails"] + A -->|Yes| B{"Fuel flow + engine η
+ aircraft mass
all provided?"} + B -->|Yes| S["Use them directly
No BADA thrust or
fuel-flow calculation
"] + B -->|No| M{"Aircraft mass
provided?"} + M -->|Yes| M1["Mass: AO values"] + M -->|No| T{"TOW
provided?"} + T -->|Yes| M2["Mass: AO TOW, decreased
with the BADA fuel flow"] + T -->|No| M3["Mass: iterative estimate
from the load factor"] + M1 --> R["BADA thrust and
BADA fuel flow at each point"] + M2 --> R + M3 --> R + R --> FF{"Fuel flow
provided?"} + FF -->|Yes| F1["Fuel flow: AO"] + FF -->|No, mass provided| F2["Fuel flow: from the
AO mass decrease"] + FF -->|No, no mass| F3["Fuel flow: BADA"] + R --> E{"Engine η
provided?"} + E -->|Yes| E1["η: AO"] + E -->|No| E2["η: from BADA thrust
and BADA fuel flow"] + + style X fill:#9e9e9e,color:#fff + style S fill:#4caf50,color:#fff + style M1 fill:#ff9800,color:#fff + style M2 fill:#2196f3,color:#fff + style M3 fill:#f44336,color:#fff ``` -!!! note "Case 2 — Fuel flow without engine efficiency" - Even when the AO provides fuel flow, BADA fuel flow and thrust must still be computed at each trajectory point to derive engine efficiency correctly. The AO fuel flow is used for downstream emission calculations. +| AO provides | BADA calculation | Aircraft mass | Fuel flow | Engine efficiency | +| --- | --- | --- | --- | --- | +| Fuel flow, engine η and aircraft mass | none (see low-speed points below) | AO | AO | AO | +| Aircraft mass, with at most one of fuel flow and engine η | thrust and fuel flow | AO | AO if provided, else from the AO mass decrease | AO if provided, else BADA | +| TOW, no aircraft mass | thrust and fuel flow | AO TOW, decreased with the BADA fuel flow | AO if provided, else BADA | AO if provided, else BADA | +| Neither aircraft mass nor TOW | thrust and fuel flow | iterative estimate (below), decreased with the BADA fuel flow | AO if provided, else BADA | AO if provided, else BADA | + +!!! note "BADA mapping" + Every flight needs its aircraft type in the BADA mapping, even when the AO provides fuel flow, engine efficiency and aircraft mass. The BADA file gives the wingspan (needed for contrails). The mapping gives the number of engines (needed for emissions; read from the BADA file when no BADA calculation is made) and, when no engine is declared, the engine UID: the MRR conservative engine where the type has one, otherwise the engine of the mapping row. A type that is not in the mapping makes the flight fail. + +!!! note "Engine efficiency with AO fuel flow" + When the AO provides fuel flow but not engine efficiency, the efficiency is computed from BADA thrust and BADA fuel flow, so that thrust and fuel flow come from the same model. The AO fuel flow is used for the downstream emission calculations. + +!!! note "Aircraft mass with AO fuel flow" + When the AO provides fuel flow but no aircraft mass, the aircraft mass decreases along the trajectory with the BADA fuel flow, not with the AO fuel flow. This holds whether the mass starts from the AO TOW or from the estimated TOW, whose trip fuel also uses the BADA fuel flow. The mass decrease along the trajectory then differs from the reported fuel burn. + +!!! note "Mass decrease" + With AO aircraft mass and no AO fuel flow, the fuel flow at each point is the mass decrease since the previous point divided by the time step to the next point, set to 0 where the mass increases. It is 0 at the first point and empty at the last point, so the mass decrease over the last segment is not counted in the fuel burn. + +!!! note "Provided values" + A quantity counts as provided when at least one point has a value. Points left empty are not filled from BADA: they stay empty and are left out of the fuel totals. After the resampling to 60 s, an empty point also empties the resampled points between its two neighbours. + +!!! note "Fuel-burn plausibility check" + A flight is rejected when its total fuel burn, with the fuel flow actually used (AO, derived from the mass, or BADA), exceeds 1.2 × (MTOW − OEW). The check is not applied when fuel flow, engine efficiency and aircraft mass are all provided. A BADA fuel flow above 100 kg/s at a point counts as a BADA failure at that point; AO fuel flow is not checked point by point. + +!!! note "Low-speed points" + Before any of the cases above, points whose true airspeed is below the BADA stall speed (at MTOW, take-off configuration), or that have no true airspeed, are removed, also when the AO provides fuel flow, engine efficiency and aircraft mass. If more than 80 % of the points would be removed, none are. Currently this applies only to aircraft computed with BADA 3; this filter is under review. --- -## Mass Estimation (Case 5) +## Mass Estimation When neither aircraft mass nor TOW is available, PyNeats applies an **iterative** procedure using the load factor (AO-provided or conservative default of 1): @@ -78,11 +115,13 @@ Reduce aircraft mass along the trajectory according to fuel burnt at each point. The correspondence between ICAO aircraft types, aircraft versions, engine identifiers and BADA types follows a multi-level fallback strategy: -1. **BADA 4** match (preferred — higher fidelity) -2. **BADA 3** fallback if no BADA 4 type exists -3. **Fleet statistics** used as frequency-based proxies when no direct mapping is possible +1. **BADA 4** type from the mapping tables (preferred — higher fidelity), looked up by ICAO type, aircraft version and engine, then with fewer of these keys +2. **BADA 3** type when the mapping gives no BADA 4 type, or when the BADA 4 calculation fails +3. If that also fails and the error allows a retry, points below FL20 are removed and the calculation is tried again (BADA 4, then BADA 3) + +The mapping tables were built with fleet statistics, used as frequency-based proxies where no direct correspondence exists. A type absent from the tables makes the flight fail. -The engine UID for emission computations defaults to the **MRR conservative value** if not provided as primary data. +The engine UID for emission computations, if not provided as primary data, is the **MRR conservative value** where the type has one, otherwise the engine of the mapping row. --- @@ -93,7 +132,7 @@ The granularity of AO-provided data significantly affects output accuracy: | Input level | Performance accuracy | Notes | | --- | --- | --- | | Full primary (mass + fuel + η) | ★★★★★ | Best — no modelling needed | -| Fuel flow only | ★★★★☆ | η derived from BADA | -| Mass evolution | ★★★☆☆ | Full BADA performance chain | +| Fuel flow only | ★★★★☆ | η from BADA thrust and BADA fuel flow; aircraft mass and TOW estimated from the load factor | +| Mass evolution | ★★★☆☆ | Fuel flow from the AO mass decrease; thrust (and η) from BADA | | TOW only | ★★☆☆☆ | Mass evolution estimated | | Load factor only | ★☆☆☆☆ | Fully modelled — conservative | diff --git a/docs/explanation/mrv-specification.md b/docs/explanation/mrv-specification.md index dbcb94a..edb74de 100644 --- a/docs/explanation/mrv-specification.md +++ b/docs/explanation/mrv-specification.md @@ -116,7 +116,7 @@ The interpolation module converts the input trajectory (primary or secondary) to *PyNeats currently uses straightforward linear interpolation via PyContrails' `resample_and_fill` method. For secondary data, the coarse native sampling can yield segments that are not fully realistic and may stray outside the flight envelope after interpolation. To obtain more realistic trajectories, smoothing is applied to ground speed using the **Savitzky–Golay filter** from PyContrails, reducing the number of segments that fall outside the flight envelope.* -For optional primary variables along the trajectory (e.g. true airspeed, aircraft mass, fuel flow, or engine efficiency), **linear interpolation** is applied to align their values with the trajectory timestamps. Missing data points are filled using the same interpolation method. +For optional primary variables along the trajectory (e.g. true airspeed, aircraft mass, fuel flow, or engine efficiency), **linear interpolation** is applied to align their values with the trajectory timestamps. Missing data points are not filled: a point the AO leaves empty also empties the resampled points between its two neighbours, and these stay empty in the performance step. ### 3.3 Weather @@ -169,7 +169,7 @@ The emissions and climate modules require the following aircraft-performance val - **Fuel flow** - **Engine efficiency** -The performance model is used to estimate these values when they are not available at a point along the trajectory. +The performance model is used to estimate these values when the AO does not provide them. A quantity is taken from the AO when at least one point has a value; points the AO leaves empty are not estimated (see Input Decision Logic). **BADA** is the current chosen model for performance computation in PyNeats, in its open-source version from EUROCONTROL (**pyBADA**). BADA requires, at a given point of the trajectory: @@ -178,18 +178,26 @@ The performance model is used to estimate these values when they are not availab #### Basic Parameterisation -- If TAS is not provided by the AO, wind fields **above 840 hPa (FL050)** are used to compute it; below that pressure level TAS is set to 0. +- If TAS is not provided by the AO, it is computed from the (smoothed) ground speed and the wind fields, available **above 840 hPa (FL050)**; where no wind is available (below that level) the wind is set to 0, so TAS equals the smoothed ground speed. - The International Standard Atmosphere is corrected with actual air above 550 hPa, backfilling and forward-filling the correction for lower altitudes. - BADA flight phases are determined from **vertical speed** using simple decision thresholds. #### Input Decision Logic -The following rules are applied **sequentially** to determine how the primary data provided by the AO is used: +Each performance quantity has its own source, so the cases below are not exclusive (an AO fuel flow can be combined with a BADA aircraft mass); the source of one quantity can still depend on what else is provided, as rules 2 and 4 show: + +1. **BADA mapping:** every flight needs its aircraft type in the BADA mapping, even when the AO provides all performance data. The BADA file gives the wingspan (contrails); the mapping gives the number of engines (emissions; read from the BADA file when no BADA calculation is made) and, when no engine is declared, the engine UID (the MRR conservative engine where the type has one, otherwise the engine of the mapping row). A type not in the mapping makes the flight fail. +2. **Fuel flow, engine efficiency and aircraft mass all provided:** no BADA thrust or fuel-flow calculation; the provided values are used directly downstream. The fuel-burn plausibility check (total fuel burn at most 1.2 × (MTOW − OEW), applied on every other path with the fuel flow actually used) is not applied on this path. +3. **Otherwise BADA computes thrust and fuel flow at each point**, with the aircraft mass taken from, in order: the AO aircraft mass, if provided; else the AO take-off weight (TOW), decreased along the trajectory with the BADA fuel flow; else the iterative estimate of rule 5. +4. **Outputs:** + - **Fuel flow:** AO fuel flow (kg/s) if provided; else, if the AO provides aircraft mass, the mass decrease since the previous point divided by the time step to the next point (set to 0 where the mass increases; 0 at the first point and empty at the last, so the last segment's mass decrease is not counted); else the BADA fuel flow. + - **Engine efficiency:** AO value if provided; else computed from BADA thrust and BADA fuel flow. This also holds when the AO provides fuel flow: **engine efficiency requires thrust AND fuel flow** from a consistent source, so engine efficiency = f(BADA thrust, BADA fuel flow), while the AO fuel flow is used downstream. + - **Aircraft mass:** as in rule 3. When the AO provides fuel flow but no aircraft mass, the mass decreases with the BADA fuel flow (from the AO TOW or from the estimated TOW, whose trip fuel also uses the BADA fuel flow), so the mass decrease differs from the reported fuel burn. + + A quantity counts as provided when at least one point has a value; points left empty are not filled from BADA and are left out of the fuel totals. + + Before any of these rules, points whose true airspeed is below the BADA stall speed (at MTOW, take-off configuration), or that have no true airspeed, are removed, also on the path of rule 2; if more than 80 % of the points would be removed, none are. Currently this applies only to aircraft computed with BADA 3; this filter is under review. -1. **Fuel flow and engine efficiency provided at each point:** no additional computation; the performance step is **skipped** and provided values are used directly downstream. -2. **Fuel flow only provided at each point:** AO fuel flow (kg/s) is used downstream. However, **engine efficiency requires thrust AND fuel flow** from a consistent source. To avoid mixing AO fuel flow with BADA-estimated thrust (which would be inconsistent), BADA fuel flow must **still be computed** at each trajectory point so that **engine efficiency = f(BADA thrust, BADA fuel flow)**. -3. **Aircraft mass evolution provided:** used directly in BADA to compute fuel flow, thrust, and engine efficiency. -4. **Only take-off weight (TOW) provided:** TOW is used in the performance model to estimate mass evolution along the trajectory, and then fuel flow, thrust, and engine efficiency are computed with BADA. 5. **Neither mass nor TOW provided:** the load factor is used (AO-provided or conservative default of 1). The following approach (similar to PyContrails) is applied: 1. **Initial estimate** (constant along the trajectory): diff --git a/docs/internal/pyneats_units_audit.md b/docs/internal/pyneats_units_audit.md index 8cc036a..881e384 100644 --- a/docs/internal/pyneats_units_audit.md +++ b/docs/internal/pyneats_units_audit.md @@ -7,7 +7,8 @@ before the comment patch was applied. The patch moves lines in the files it touc ## Follow-up status The findings below are the audit as written. Each fix updates this table and adds a status -note under its item. Items not listed here are open. +note under its item. Items not listed here are open. Rows and items added after the audit +cite line numbers on `dev` before the fix they describe. | Item | Status | Branch / PR | Date | | --- | --- | --- | --- | @@ -15,7 +16,10 @@ note under its item. Items not listed here are open. | One flight that cannot be converted no longer stops the run (D2 follow-up) | Fixed | #71 | 2026-10-20 | | D1 (bare single-flight object) | Open; now a clear `TypeError` on the `FleetRunner` JSON path | #71 | 2026-10-20 | | I4 (BADA3 maximum payload) | Fixed; changes BADA3 fuel and CO2 | #73 | 2026-10-21 | -| Fuel reserve and number of mass iterations misstated in the user docs (Section 9) | Fixed (docs only) | `docs/fuel-reserve-15pct` | 2026-10-21 | +| Fuel reserve and number of mass iterations misstated in the user docs (Section 9) | Fixed (docs only) | #74 | 2026-10-21 | +| Performance input decision tree and data-sources table in the user docs (Section 9) | Fixed (docs only) | `docs/performance-input-logic` | 2026-10-21 | +| A13 (mixed fuel-flow provenance) | Documented in the user docs; the design choice stays open | `docs/performance-input-logic` | 2026-10-21 | +| I9 (`segment_duration` not recomputed after the speed filter) | Open; recorded, part of the low-speed filter work | `docs/performance-input-logic` | 2026-10-21 | ## 1. Outcome @@ -442,6 +446,9 @@ Each entry gives: - Units agree throughout. The issue is provenance, not scale. - **Recommendation**: document this, or integrate mass from the provided `ff`. The decision belongs to the method owner. +- *Documented in `input-prioritization.md` and `mrv-specification.md` on branch + `docs/performance-input-logic` (see the follow-up status table). The design choice + (integrate mass from the provided `ff` or not) stays open for the method owner.* ## 7. New discrepancies @@ -708,8 +715,14 @@ discrepancies: | `docs/internal/performance_use_cases_analysis.md` | TAS always recomputed; early exit on FF+EE; FF never derived from mass | TAS kept when provided; early exit needs FF+EE+AM; FF derived from AM | mark superseded | | `scripts/parquet_to_neats_json.py` | treats `altitude` as feet and stamps `+0000` without converting | basis (barometric or geometric) and time zone unverified | document expected input | | `tests/test_performance_fallback.py:4,74,80`; `tests/test_guardrails.py:133,142,151` | FL15; 500 hPa | FL20; 550 hPa | update test docstrings | -| `docs/explanation/input-prioritization.md:60`; `docs/explanation/mrv-specification.md:203` | initial-mass fuel reserve ≈ 3 % of trip fuel | 15 % (`DEFAULT_FUEL_RESERVE_FRACTION = 0.15`, `neats_default_parameters.py:148`; `bada_model.py:1058`). Found after this audit, during the I4 review | fixed (`docs/fuel-reserve-15pct`) | -| `docs/explanation/input-prioritization.md:70`; `docs/explanation/mrv-specification.md:214` | the mass estimate runs steps 2–4 "twice" | when neither an `aircraft_mass` column nor a `takeoff_weight` attribute is given, steps 2–4 run at least twice and at most four times per estimate: one constant-mass BADA pass, then up to three passes from the updated TOW, stopping when the TOW changes by ≤ 1 % of the previous TOW (`max_mass_estimation_iter = 4`, `max_rel_mass_diff = 0.01`, `bada_model.py:1065-1117`); the last pass is kept. Measured on the 4,303 flights computed from the NEATS_FINAL sample: 2,769 stop after two passes, 1,415 after three, 119 after four, 3 of which stop at the cap with a change of 1.0–1.4 % (1.04, 1.17 and 1.39 %) | fixed (`docs/fuel-reserve-15pct`) | +| `docs/explanation/input-prioritization.md:60`; `docs/explanation/mrv-specification.md:203` | initial-mass fuel reserve ≈ 3 % of trip fuel | 15 % (`DEFAULT_FUEL_RESERVE_FRACTION = 0.15`, `neats_default_parameters.py:148`; `bada_model.py:1058`). Found after this audit, during the I4 review | fixed (#74) | +| `docs/explanation/input-prioritization.md:70`; `docs/explanation/mrv-specification.md:214` | the mass estimate runs steps 2–4 "twice" | when neither an `aircraft_mass` column nor a `takeoff_weight` attribute is given, steps 2–4 run at least twice and at most four times per estimate: one constant-mass BADA pass, then up to three passes from the updated TOW, stopping when the TOW changes by ≤ 1 % of the previous TOW (`max_mass_estimation_iter = 4`, `max_rel_mass_diff = 0.01`, `bada_model.py:1065-1117`); the last pass is kept. Measured on the 4,303 flights computed from the NEATS_FINAL sample: 2,769 stop after two passes, 1,415 after three, 119 after four, 3 of which stop at the cap with a change of 1.0–1.4 % (1.04, 1.17 and 1.39 %) | fixed (#74) | +| `docs/explanation/input-prioritization.md:23-44`; `docs/explanation/mrv-specification.md:187-193` | the decision tree and rules 1–5 are exclusive cases checked in order: fuel flow + η skips the performance step; fuel flow only uses BADA only for η; with aircraft mass, BADA computes the fuel flow | each quantity has its own source. The BADA thrust and fuel-flow calculation is skipped only when fuel flow, η and aircraft mass are all given (`bada_model.py:741-745`); the BADA adapter is still resolved and, for BADA3 runs, the low-speed filter still removes rows on that path (`bada_model.py:483-495`). Otherwise the mass is the AO mass, else the AO TOW, else the iterative estimate, whatever fuel flow or η were given (`bada_model.py:850-874`); with aircraft mass and no fuel flow, the fuel flow is derived from the mass decrease (`bada_model.py:762-812`); the BADA mapping is needed for every flight. Checked by running one golden flight (EAF2143) with six input combinations (fuel flow + η + mass, fuel flow + η, fuel flow, mass, TOW, none) and with an aircraft type missing from the BADA mapping. A retry bug (a BADA3 retry after a BADA4 guardrail rejection reused the BADA4 columns and could take this exit) is fixed separately | fixed (`docs/performance-input-logic`) | +| `docs/explanation/input-prioritization.md:17` | modelled take-off weight = OEW + load_factor × (MTOW − OEW) | that is only the constant first-pass mass; the take-off weight used is min(MTOW, OEW + load_factor × MPL + 1.15 × trip fuel) from the iteration (MPL = MTOW − OEW when BADA gives no positive MPL). On the 4,303 computed NEATS_FINAL sample flights the first-pass mass equals MTOW on all, the take-off weight on 243 | fixed (`docs/performance-input-logic`) | +| `docs/explanation/input-prioritization.md:83,85` | unmapped types fall back to fleet-statistics proxies; the engine UID defaults to the MRR conservative value | the fleet statistics were used to build the mapping tables; a type absent from them makes the flight fail. Without a declared engine, the engine UID is the MRR conservative engine where the type has one (`MRR_conservative_mapping.csv`), otherwise the engine of the mapping row | fixed (`docs/performance-input-logic`) | +| `docs/explanation/input-prioritization.md:96-97` | "Fuel flow only: η derived from BADA"; "Mass evolution: full BADA performance chain" | with fuel flow only, the mass and TOW are also estimated from the load factor; with mass evolution, the fuel flow is derived from the mass decrease (star ranking left unchanged) | fixed (`docs/performance-input-logic`) | +| `docs/explanation/mrv-specification.md:119,172` | missing points of optional AO variables are filled by interpolation; the performance model estimates values "not available at a point" | a provided column is used whole; empty points are not filled, and resampling widens them (D4) | fixed (`docs/performance-input-logic`) | +| `docs/explanation/mrv-specification.md:181` | without AO TAS, TAS is set to 0 below 840 hPa | the wind is set to 0 where it is missing (`weather_provider.py:382-383`), so TAS equals the smoothed ground speed | fixed (`docs/performance-input-logic`) | ## 10. Incidental observations outside the unit scope @@ -752,6 +765,25 @@ for a separate decision and were not fixed. rebuilt from attrs (corrected). - **I8. Humidity-scaling adapter fallbacks.** The fallback branches rebuild `Flight` without its fuel (`weather_provider.py:139-147`). They are unreachable with stock pycontrails. +- **I9. `segment_duration` not recomputed after the speed filter.** Recorded after this audit + (2026-10-21); open, part of the low-speed filter work. `segment_duration`, `rocd` and + `acceleration` are forward differences computed in `_compute_kinematics` + (`bada_model.py:680-700`) before `filter_low_speed_points` (`bada_model.py:488-495`), and + are not recomputed on the rows kept: + - a removed row's own segment adds no fuel; + - when trailing rows are removed, the last kept row keeps a finite duration that points to + the first removed row. On the computed-TAS path the last row always has NaN TAS, so it is + always removed when the filter runs and this always happens; + - pycontrails `Emissions` recomputes durations from the timestamps, so CO2 differs from + 3.159 × fuel; + - the take-off weight (given or estimated) applies at the first kept row (A6), and the trip + fuel of the mass estimate covers the kept rows only. + + Today only BADA3 runs are affected, because the BADA4 filter never runs (I1). Measured on + the 1,050 BADA3 flights of the NEATS_FINAL sample (production v1.0.0 code): 3,173 rows + removed (639 leading, 1,947 trailing, 587 interior), carrying 1.36 % of flight time; + CO2 / (3.159 × fuel) from 0.961 to 1.360 (median 0.995) per flight, 0.17 % below at fleet + level. ## 11. Patch summary and checks performed