Skip to content

fix(performance): use the BADA3 OPF maximum payload in the mass estimate - #73

Merged
ramondalmau merged 2 commits into
devfrom
fix/bada3-max-payload
Oct 21, 2026
Merged

ramondalmau merged 2 commits into
devfrom
fix/bada3-max-payload

Conversation

@ramondalmau

Copy link
Copy Markdown
Collaborator

What and why

Audit item I4.

BADA3Adapter.MPL always returned None, although pyBADA reads the maximum payload from the BADA3 OPF file. The mass estimate then used MTOW − OEW as the payload. With the default payload factor 1.0 that gives ZFW = MTOW, so every BADA3 flight took off at MTOW: 1,050 of 1,050 in the NEATS_FINAL sample.

BADA4 flights already used their real maximum payload. The same aircraft therefore got two different masses depending on the BADA family. For the A20N, BADA 3.16 and 4.2.1 give identical MTOW 79 t, OEW 46 t and MPL 18.3 t, yet the BADA3 path was about 11 t heavier.

Change

  • BADA3Adapter.MPL returns the OPF maximum payload, as BADA4Adapter.MPL already does.
  • The mass estimate treats a missing, zero, negative or NaN MPL as unusable and falls back to MTOW − OEW, with a warning. BADA 3.16 U2____.OPF gives 0; pyBADA also uses that file for G850, GFLY, M17 and M55. None of these types is in the NEATS mapping.
  • Unchanged:
    • the payload factor default (1.0, the regulation default);
    • the 15 % reserve;
    • the fuel-burn guardrail, which uses MTOW − OEW on purpose because it covers payload and fuel together.
  • Docs:
    • audit report (I4 note and the follow-up status table);
    • the two user-doc notes that said "BADA 3 does not provide MPL";
    • docstrings.

Effect on results (please review, Philippe)

Before/after on every flight of the NEATS_FINAL NM sample (4,959 flights), real code, no patches. The current dev reproduces production exactly on all 4,302 computed flights.

Before (dev) After Change
BADA3 fleet fuel (1,050 flights) 7,312.8 t 6,983.0 t −4.51 %
BADA3 fleet CO2 −4.48 %
BADA3 NOx −8.1 %
BADA3 nvPM number +2.1 %
Whole-fleet fuel (4,303 computed flights) −0.66 %
BADA4 flights (3,253) bit-identical, point by point
Failed flights (656) same set, same messages

BADA3 per flight (fuel):

  • median −5.7 %; 5th percentile −10.4 %; minimum −21 %;
  • the minimum is on business jets with small OPF payloads (GLF5, GLEX);
  • 42 flights are unchanged (still MTOW-limited, mostly long-haul);
  • 5 small business jets go up by ≤ +0.6 % (H25B, C25A, LJ45), because a lower mass changes BADA3's phase selection;
  • take-off mass: median −8.2 t.

NOx falls more than fuel and nvPM number rises: both emission indices depend on thrust, which is lower at lower mass.

What to look at

  1. src/pyneats/steps/performance/bada_adapters.py: BADA3Adapter.MPL (4 lines).
  2. src/pyneats/steps/performance/bada_model.py: the MPL guard in _estimate_initial_mass_iterative.
  3. docs/internal/pyneats_units_audit.md: the I4 note and the status table.

Everything else is docstrings, user docs and tests.

Verification

  • New tests in tests/test_bada3_max_payload.py:
    • the adapter returns the OPF value or None;
    • the mass estimate uses MPL, falls back on missing, zero or NaN, applies the payload factor and the MTOW cap;
    • a real-data check that A20N BADA3 MPL = BADA4 A320-271N MPL = 18,300 kg.
  • The tests catch the old bug: two of them fail on the old code.
  • Full suite with BADA and weather data on the final commit: 294 passed, 0 failed (4 skipped, the usual structural skips; 23 xfailed), 53 min. The golden files contain only BADA4 flights and are unchanged.
  • Independent review (4 angles, every finding checked again): no code defects. Its two documentation points are fixed in this PR.

Not in this PR

  • docs/explanation/input-prioritization.md and mrv-specification.md give the fuel reserve as 3 % of trip fuel; the code uses 15 %. This is pre-existing and will be a separate fix.

ramondalmau and others added 2 commits October 21, 2026 09:10
BADA3Adapter.MPL always returned None, although pyBADA reads the maximum
payload from the OPF file. The mass estimate then took MTOW - OEW as the
payload, so with the default payload factor 1.0 the zero-fuel weight was
MTOW and every BADA3 flight took off at MTOW. BADA4 flights already used
their BADA maximum payload.

BADA3Adapter.MPL now returns the OPF value, as BADA4Adapter.MPL does. The
MTOW - OEW fallback is kept for a BADA file without MPL. The fuel-burn
guardrail still uses MTOW - OEW on purpose.

Updates the units audit (I4), the user docs that said BADA 3 has no MPL,
and adds tests for the adapter and the mass estimate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BADA 3.16 U2____.OPF (also used by pyBADA for G850, GFLY, M17 and M55)
gives a maximum payload of 0, which the mass estimate would take as zero
payload (ZFW = OEW) whatever the payload factor. A missing, zero,
negative or NaN MPL now falls back to MTOW - OEW with the warning. No type
in the NEATS BADA mapping is affected; BADA 4.2.1 has no such value.

Also corrects the I4 audit note (the -21 % minimum is a GLF5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ramondalmau
ramondalmau merged commit e8e6f8f into dev Oct 21, 2026
6 checks passed
@ramondalmau
ramondalmau deleted the fix/bada3-max-payload branch October 21, 2026 08:25
ramondalmau added a commit that referenced this pull request Oct 21, 2026
… the code runs them (#74)

* docs: state the 15 % fuel reserve used in the initial mass estimate

input-prioritization.md and mrv-specification.md gave the fuel reserve of
the initial mass estimate as about 3 % of trip fuel. The code uses 15 %
(DEFAULT_FUEL_RESERVE_FRACTION = 0.15, following Teoh et al. 2024).

Records the item in the units audit (Section 9 and the follow-up status
table) and sets the I4 status row to #73.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: describe the mass-estimate iterations as the code runs them

The user docs said steps 2-4 of the mass estimate run twice. The code
runs one constant-mass BADA pass, then up to three passes from the
updated TOW, and stops when the TOW changes by 1 % or less of the
previous TOW (max_mass_estimation_iter = 4, max_rel_mass_diff = 0.01).
The last pass is kept; a BADA 3 or altitude-fallback retry starts the
estimate again.

Measured with the real code on the 4,303 computed NEATS_FINAL sample
flights: 2,769 stop after two passes, 1,415 after three, 119 after four
(3 of them at the cap with a change of 1.0-1.4 %). Recorded in the units
audit, Section 9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant