Skip to content

Rangeequation

Info

The relation describing the specific air range of an aircraft in cruise can be adapted to calculate the fuel required for a given range \(R\). The governing equation for the range is:

\[ R = \frac{1}{g} \int_{m_2}^{m_1} \frac{V\cdot L/D}{TSFC \cdot m} \text{d}m \]

Depending on which variables are assumed constant during the flight, different analytical solutions to the integral can be derived:

Variables assumed Constant Variables allowed to Vary Additional Assumptions Solution Function
\(TSFC\)
\(L/D\)
\(V\)
\(m\) none "Breguet range equation (climb-cruise)" calculate_fuel_consumption_breguet
calculate_fuel_consumption_breguet_improved
\(TSFC\)
\(h\)
\(V\)
\(m\)
\(L/D\)
low-speed drag polar applies \(C_D = C_{D_0} + KC_L^2\) "arctan solution (step-climb)" calculate_fuel_consumption_stepclimb_arctan
\(TSFC\)
\(h\)
\(V\)
\(m\)
\(L/D\)
none "numerical solution (step-climb)" calculate_fuel_consumption_stepclimb_integration

References

Section 13.2 in Young, T. M. (2018). Performance of the Jet Transport Airplane (Section 13.7.3 "Fuel Required for Specified Range"). John Wiley & Sons. doi:10.1002/9781118534786

Note also that some authors spell "Breguet" as "Bréguet" (with accent on the "e"). This is not limited to non-French speakers. Even in his original 1923 publication "Calcul du Poids de Combustible Consummé par un Avion en vol Ascendant" , Breguet's name is spelled without an accent:

But in the same publication, an aircraft manufactured by his company is spelled with accent:

jetfuelburn.rangeequation

calculate_fuel_consumption_breguet

calculate_fuel_consumption_breguet(
    R, LD, m_after_cruise, V, TSFC
)

Given a flight distance (=range) \(R\) and basic aircraft performance parameters (see table), returns the fuel mass burned during the flight \(m_f\) [kg] based on a flight schedule where the lift-coefficient and velocity are constant during cruise ("cruise-climb"). This solution is also known as the Breguet range equation.

Fuel mass is calculated as:

\[ m_f = (e^{\frac{R \cdot TSFC \cdot g}{L/D \cdot v}} - 1 ) m_2 \]

where:

Symbol Dimension Description
\(m_{fuel}\) [mass] Fuel required for the mission of the aircraft
\(R\) [distance] Range of the aircraft (=mission distance)
\(TSFC\) [time/distance] Thrust Specific Fuel Consumption of the aircraft during cruise
\(g\) [acceleration] Acceleration due to gravity
\(L/D\) [dimensionless] Lift-to-Drag ratio of the aircraft
\(v\) [speed] Average cruise speed of the aircraft (TAS)
\(m_2\) [mass] Mass of the aircraft after cruise segment

Note that thrust-specific fuel consumption (TSFC) is of dimension [time/distance], as the commonly used unit [g/kNs] simplifies accordingly.

Notes

Key assumptions of this fuel calculation function:

Parameter Assumption
data availability adequate for both current aircraft (reports) and future aircraft (studies)
aircraft payload variable
climb/descent not considered, only cruise-phase
fuel reserves can be considered through \(m_2\)
alternate airport can be considered through \(m_2\)
Warnings

This analytical fuel calculation method represents a simplification of actual flight dynamics. Specifically, this function assumes a flight schedule with constant airspeed and constant lift coefficient, resulting in a cruise-climb. Young (2017) shows this in Section 13.3.3 "Second Flight Schedule":

(...) the airplane must be flown in a way that will ensure that the ratio (...) [of weight to air density] remains constant. This is possible if the airplane is allowed to climb very slowly so that the relative density (...) decreases in direct proportion to the decrease in airplane weight (...).

References
See Also

jetfuelburn.rangeequation.calculate_fuel_consumption_breguet_improved

Raises:

Type Description
ValueError

If the dimensions of the inputs are invalid.

ValueError

If the magnitude of the inputs are invalid (eg. negative).

Parameters:

Name Type Description Default
R float

Range of the aircraft (=mission distance) [distance]

required
LD float

Lift-to-Drag ratio of the aircraft [dimensionless]

required
m_after_cruise float

Mass of the aircraft after landing (eg. OEW + Payload + Crew + Reserves) [mass]

required
V float

Average cruise speed of the aircraft (TAS) [speed]

required
TSFC float

Average Thrust Specific Fuel Consumption of the aircraft during cruise [time/distance (results from the definition of TSFC)]

required

Returns:

Type Description
float

Required fuel mass [kg]

Example

Editor (session: default)Run
import jetfuelburn
from jetfuelburn import ureg
from jetfuelburn.rangeequation import calculate_fuel_consumption_breguet
calculate_fuel_consumption_breguet(
    R=2000*ureg.nmi,
    LD=18,
    m_after_cruise=100*ureg.metric_ton,
    V=800*ureg.kph,
    TSFC=17*(ureg.mg/ureg.N/ureg.s),
)
OutputClear

Source code in jetfuelburn/rangeequation.py
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
@ureg.check("[length]", "[]", "[mass]", "[speed]", "[time]/[length]")  # [mg/Ns] = s/m
def calculate_fuel_consumption_breguet(
    R: pint.Quantity[float | int],
    LD: float | int | pint.Quantity[float | int],
    m_after_cruise: pint.Quantity[float | int],
    V: pint.Quantity[float | int],
    TSFC: pint.Quantity[float | int],
) -> float:
    r"""
    Given a flight distance (=range) $R$ and basic aircraft performance parameters (see table),
    returns the fuel mass burned during the flight $m_f$ [kg] based on a flight schedule where
    the lift-coefficient and velocity are constant during cruise ("cruise-climb").
    This solution is also known as the **Breguet range equation**.

    Fuel mass is calculated as:

    $$
        m_f = (e^{\frac{R \cdot TSFC \cdot g}{L/D \cdot v}} - 1 ) m_2
    $$

    where:

    | Symbol     | Dimension         | Description                                                            |
    |------------|-------------------|------------------------------------------------------------------------|
    | $m_{fuel}$ | [mass]            | Fuel required for the mission of the aircraft                          |
    | $R$        | [distance]        | Range of the aircraft (=mission distance)                              |
    | $TSFC$     | [time/distance]   | Thrust Specific Fuel Consumption of the aircraft during cruise         |
    | $g$        | [acceleration]    | Acceleration due to gravity                                            |
    | $L/D$      | [dimensionless]   | Lift-to-Drag ratio of the aircraft                                     |
    | $v$        | [speed]           | Average cruise speed of the aircraft (TAS)                             |
    | $m_2$      | [mass]            | Mass of the aircraft after cruise segment                              |

    Note that thrust-specific fuel consumption (TSFC) is of dimension [time/distance], as the commonly used unit [g/kNs] simplifies accordingly.

    Notes
    -----
    Key assumptions of this fuel calculation function:

    | Parameter         | Assumption                                                                 |
    |-------------------|----------------------------------------------------------------------------|
    | data availability | adequate for both current aircraft (reports) and future aircraft (studies) |
    | aircraft payload  | variable                                                                   |
    | climb/descent     | not considered, only cruise-phase                                          |
    | fuel reserves     | can be considered through $m_2$                                            |
    | alternate airport | can be considered through $m_2$                                            |

    Warnings
    --------
    This analytical fuel calculation method represents a simplification of actual flight dynamics.
    Specifically, this function assumes a _flight schedule_ with constant airspeed and constant lift coefficient, resulting in a cruise-climb.
    Young (2017) shows this in Section 13.3.3 "Second Flight Schedule":

    > (...) the airplane must be flown in a way that will ensure that the ratio (...) [of weight to air density] remains constant.
    > This is possible if the airplane is allowed to climb very slowly so that the relative density (...) decreases in direct proportion to the decrease in airplane weight (...).

    References
    --------
    - Young, T. M. (2018).
    Performance of the Jet Transport Airplane (Section 13.7.3 "Fuel Required for Specified Range"). _John Wiley & Sons_. doi:[10.1002/9781118534786](https://doi.org/10.1002/9781118534786)
    - Cavcar, M. (2006). Bréguet range equation?. _Journal of Aircraft_. doi:[10.2514/1.17696](https://doi.org/10.2514/1.17696)
    - [Range (Aeronautics) entry on Wikipedia](https://en.wikipedia.org/wiki/Range_(aeronautics))

    See Also
    --------
    [`jetfuelburn.rangeequation.calculate_fuel_consumption_breguet_improved`][]

    Raises
    ------
    ValueError
        If the dimensions of the inputs are invalid.
    ValueError
        If the magnitude of the inputs are invalid (eg. negative).

    Parameters
    ----------
    R : float
        Range of the aircraft (=mission distance) [distance]
    LD : float
        Lift-to-Drag ratio of the aircraft [dimensionless]
    m_after_cruise : float
        Mass of the aircraft after landing (eg. OEW + Payload + Crew + Reserves) [mass]
    V : float
        Average cruise speed of the aircraft (TAS) [speed]
    TSFC : float
        Average Thrust Specific Fuel Consumption of the aircraft during cruise [time/distance (results from the definition of TSFC)]

    Returns
    -------
    float
        Required fuel mass [kg]

    Example
    -------
    ```pyodide install='jetfuelburn'
    import jetfuelburn
    from jetfuelburn import ureg
    from jetfuelburn.rangeequation import calculate_fuel_consumption_breguet
    calculate_fuel_consumption_breguet(
        R=2000*ureg.nmi,
        LD=18,
        m_after_cruise=100*ureg.metric_ton,
        V=800*ureg.kph,
        TSFC=17*(ureg.mg/ureg.N/ureg.s),
    )
    ```
    """

    if R.magnitude < 0:
        raise ValueError("Range must be greater than zero.")
    if LD <= 1:
        raise ValueError("Lift-to-Drag ratio must be greater than 1.")
    if m_after_cruise < 0 * ureg.kg:
        raise ValueError("Mass after cruise must be greater than zero.")
    if V <= 0 * ureg.kph:
        raise ValueError("Cruise speed must be greater than zero.")
    if TSFC.magnitude <= 0:
        raise ValueError("Thrust Specific Fuel Consumption must be greater than zero.")

    if R == 0 * ureg.meter:
        return 0 * ureg.kg
    else:
        m_fuel = m_after_cruise * (math.exp((R * TSFC * ureg.gravity) / (LD * V)) - 1)
        return m_fuel.to("kg")

calculate_fuel_consumption_breguet_improved

calculate_fuel_consumption_breguet_improved(
    R,
    LD,
    m_after_cruise,
    V,
    V_headwind,
    TSFC,
    lost_fuel_fraction=0.0152,
    recovered_fuel_fraction=0.001,
)

Given a flight distance (=range) \(R\) and basic aircraft performance parameters (see table), returns the fuel mass burned during the flight \(m_f\) [kg] based on a flight schedule where the lift-coefficient and velocity are constant during cruise ("cruise-climb"), while taking into account headwind effects and fuel losses during takeoff and climb, as well as fuel recovery during descent and landing.

Fuel mass is calculated as:

\[ m_{\text{fuel}} = m_{\text{LDG}} \left( \frac{1}{\exp\left\{ \frac{-R}{H \left(1 - \frac{V_{\text{HW}}}{V}\right)} \right\} - \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} + \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - 1 \right) \]

with

\[ H = \frac{L/D \cdot V}{TSFC \cdot g} \]

where:

Symbol Dimension Description
\(m_{f}\) [mass] Fuel required for the mission of the aircraft
\(m_{LDG}\) [mass] Mass of the aircraft after cruise segment
\(R\) [distance] Range of the aircraft (=mission distance)
\(TSFC\) [time/distance] Thrust Specific Fuel Consumption of the aircraft during cruise
\(g\) [acceleration] Acceleration due to gravity
\(L/D\) [dimensionless] Lift-to-Drag ratio of the aircraft
\(V\) [speed] Average speed of the aircraft (TAS)
\(V_{HW}\) [speed] Average speed of headwind component
\(\frac{\Delta m_L}{m_{TO}}\) [dimensionless] Lost fuel fraction
\(\frac{\Delta m_R}{m_{TO}}\) [dimensionless] Recovered fuel fraction
Derivation

Equation 19 in Randle et al. (2011) expresses the fuel mass burned \(m_f\) during a flight of distance \(s\) (=range \(R\)) as a function of the takeoff mass \(m_{TO}\). However, in practical applications, the more relevant known parameter is the landing mass after cruise

\(m_{LDG}\) (= \(m_{after\_cruise}\))

Therefore, the equation is rearranged here to solve for \(m_f\) as a function of \(m_{LDG}\):

\[\begin{align} m_f &= m_{\text{TO}} \left( 1 - e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\ m_f &= (m_f + m_{\text{LDG}}) \left( 1 - e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\ m_f &= (m_f + m_{\text{LDG}}) \cdot 1 + (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\ m_f &= m_f + m_{\text{LDG}} + (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\ 0 &= m_{\text{LDG}} + (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\ -m_{\text{LDG}} &= (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\ \frac{-m_{\text{LDG}}}{-e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} &= m_f + m_{\text{LDG}} \\ m_f &= \frac{-m_{\text{LDG}}}{-e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - m_{\text{LDG}} \\ m_f &= m_{\text{LDG}} \left( \frac{-1}{-e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - 1 \right) \\ m_f &= m_{\text{LDG}} \left( \frac{1}{e^{-\frac{s}{H}} - \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} + \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - 1 \right) \end{align}\]
Warnings

Unlike the equation in Randle et al. (2011), this function uses mass [kg] instead of weight [N] for the fuel.

Notes

Assumes a lost fuel fraction \(\Delta m_{lost}=0.0152\) and a recovered fuel fraction \(\Delta m_{rec}=0.001\) as per section D of Randle et al. (2011). Values can be adjusted through function parameters.

References

Eqn. 19 in Randle, W. E., Hall, C. A., & Vera-Morales, M. (2011). Improved range equation based on aircraft flight data. Journal of Aircraft. doi:10.2514/1.C031262

See Also

jetfuelburn.rangeequation.calculate_fuel_consumption_breguet

Raises:

Type Description
ValueError

If the dimensions of the inputs are invalid.

ValueError

If the magnitude of the inputs are invalid (eg. negative).

Example

Editor (session: default)Run
import jetfuelburn
from jetfuelburn import ureg
from jetfuelburn.rangeequation import calculate_fuel_consumption_breguet_improved
calculate_fuel_consumption_breguet_improved(
    R=2000*ureg.nmi,
    LD=18,
    m_after_cruise=100*ureg.metric_ton,
    V=800*ureg.kph,
    V_headwind=50*ureg.kph,
    TSFC=17*(ureg.mg/ureg.N/ureg.s),
)
OutputClear

Source code in jetfuelburn/rangeequation.py
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
@ureg.check(
    "[length]",
    "[]",
    "[mass]",
    "[speed]",
    "[speed]",
    "[time]/[length]",  # [mg/Ns] = s/m
    "[]",
    "[]",
)
def calculate_fuel_consumption_breguet_improved(
    R: pint.Quantity[float | int],
    LD: float | int | pint.Quantity[float | int],
    m_after_cruise: pint.Quantity[float | int],
    V: pint.Quantity[float | int],
    V_headwind: pint.Quantity[float | int],
    TSFC: pint.Quantity[float | int],
    lost_fuel_fraction: float | int | pint.Quantity[float | int] = 0.0152,
    recovered_fuel_fraction: float | int | pint.Quantity[float | int] = 0.001,
) -> float:
    r"""
    Given a flight distance (=range) $R$ and basic aircraft performance parameters (see table),
    returns the fuel mass burned during the flight $m_f$ [kg] based on a flight schedule 
    where the lift-coefficient and velocity are constant during cruise ("cruise-climb"), 
    while taking into account headwind effects and fuel losses during takeoff and climb, as well as fuel recovery during descent and landing. 

    Fuel mass is calculated as:

    $$
    m_{\text{fuel}} = m_{\text{LDG}} \left( \frac{1}{\exp\left\{ \frac{-R}{H \left(1 - \frac{V_{\text{HW}}}{V}\right)} \right\} - \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} + \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - 1 \right)
    $$

    with

    $$
    H = \frac{L/D \cdot V}{TSFC \cdot g}
    $$

    where:

    | Symbol                      | Dimension         | Description                                                    |
    |-----------------------------|-------------------|----------------------------------------------------------------|
    | $m_{f}$                     | [mass]            | Fuel required for the mission of the aircraft                  |
    | $m_{LDG}$                   | [mass]            | Mass of the aircraft after cruise segment                      |
    | $R$                         | [distance]        | Range of the aircraft (=mission distance)                      |
    | $TSFC$                      | [time/distance]   | Thrust Specific Fuel Consumption of the aircraft during cruise |
    | $g$                         | [acceleration]    | Acceleration due to gravity                                    |
    | $L/D$                       | [dimensionless]   | Lift-to-Drag ratio of the aircraft                             |
    | $V$                         | [speed]           | Average speed of the aircraft (TAS)                            |
    | $V_{HW}$                    | [speed]           | Average speed of headwind component                            |
    | $\frac{\Delta m_L}{m_{TO}}$ | [dimensionless]   | Lost fuel fraction                                             |
    | $\frac{\Delta m_R}{m_{TO}}$ | [dimensionless]   | Recovered fuel fraction                                        | 

    ??? info "Derivation"

        Equation 19 in Randle et al. (2011) expresses the fuel mass burned $m_f$
        during a flight of distance $s$ (=range $R$) as a function of the takeoff mass $m_{TO}$.
        However, in practical applications, the more relevant known parameter is the landing mass after cruise 

        $m_{LDG}$ (= $m_{after\_cruise}$)

        Therefore, the equation is rearranged here to solve for $m_f$ as a function of $m_{LDG}$:

        \begin{align}
            m_f &= m_{\text{TO}} \left( 1 - e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\
            m_f &= (m_f + m_{\text{LDG}}) \left( 1 - e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\
            m_f &= (m_f + m_{\text{LDG}}) \cdot 1 + (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\
            m_f &= m_f + m_{\text{LDG}} + (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\
            0 &= m_{\text{LDG}} + (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\
            -m_{\text{LDG}} &= (m_f + m_{\text{LDG}}) \left( -e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}} \right) \\
            \frac{-m_{\text{LDG}}}{-e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} &= m_f + m_{\text{LDG}} \\
            m_f &= \frac{-m_{\text{LDG}}}{-e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - m_{\text{LDG}} \\
            m_f &= m_{\text{LDG}} \left( \frac{-1}{-e^{-\frac{s}{H}} + \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} - \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - 1 \right) \\
            m_f &= m_{\text{LDG}} \left( \frac{1}{e^{-\frac{s}{H}} - \frac{\Delta m_{\text{lost}}}{m_{\text{TO}}} + \frac{\Delta m_{\text{rec}}}{m_{\text{TO}}}} - 1 \right)
        \end{align}

    Warnings
    --------
    Unlike the equation in Randle et al. (2011), this function uses mass [kg] 
    instead of weight [N] for the fuel.

    Notes
    -----
    Assumes a lost fuel fraction $\Delta m_{lost}=0.0152$ 
    and a recovered fuel fraction $\Delta m_{rec}=0.001$ as per 
    section D of Randle et al. (2011). 
    Values can be adjusted through function parameters.

    References
    --------
    Eqn. 19 in Randle, W. E., Hall, C. A., & Vera-Morales, M. (2011). 
    Improved range equation based on aircraft flight data. 
    _Journal of Aircraft_. 
    doi:[10.2514/1.C031262](https://doi.org/10.2514/1.C031262)

    See Also
    --------
    [`jetfuelburn.rangeequation.calculate_fuel_consumption_breguet`][]

    Raises
    ------
    ValueError
        If the dimensions of the inputs are invalid.
    ValueError
        If the magnitude of the inputs are invalid (eg. negative).

    Example
    -------
    ```pyodide install='jetfuelburn'
    import jetfuelburn
    from jetfuelburn import ureg
    from jetfuelburn.rangeequation import calculate_fuel_consumption_breguet_improved
    calculate_fuel_consumption_breguet_improved(
        R=2000*ureg.nmi,
        LD=18,
        m_after_cruise=100*ureg.metric_ton,
        V=800*ureg.kph,
        V_headwind=50*ureg.kph,
        TSFC=17*(ureg.mg/ureg.N/ureg.s),
    )
    ```
    """
    if R == 0 * ureg.meter:
        return 0 * ureg.kg
    else:
        H = (LD * V) / (TSFC * ureg.gravity)
        m_fuel = m_after_cruise * (
            (1 / math.exp((-R / H) * (1 - (V_headwind / V))))
            - lost_fuel_fraction
            + recovered_fuel_fraction
            - 1
        )
        return m_fuel.to("kg")

calculate_fuel_consumption_stepclimb_arctan

calculate_fuel_consumption_stepclimb_arctan(
    R, h, K, C_D0, m_after_cruise, S, V, TSFC
)

Given a flight distance (=range) \(R\) and basic aircraft performance parameters (see table), returns the fuel mass burned during the flight \(m_f\) [kg] based on a flight schedule where aircraft velocity and altitude are constant during cruise.

Fuel mass is calculated as:

\[ m_f = \frac{(B + m_{LDG}^2) \tan(\theta)}{\sqrt{B} - m_{LDG} \tan(\theta)} \]

with

\[\begin{align*} \theta &= \frac{R \cdot g \cdot TSFC}{2 E_{max} V} \\ B &= \left( \frac{C_{D_0}}{K} \right) \left( \frac{\rho V^2 S}{2g} \right)^2 \\ E_{max} &= \frac{1}{2 \sqrt{C_{D_0} K}} \end{align*}\]

where:

Symbol Dimension Description
\(m_{fuel}\) [mass] Fuel required for the mission of the aircraft
\(R\) [distance] Range of the aircraft (=mission distance)
\(TSFC\) [time/distance] Thrust Specific Fuel Consumption of the aircraft during cruise
\(g\) [acceleration] Acceleration due to gravity
\(V\) [speed] Average cruise speed of the aircraft (TAS)
\(E_{max}\) [energy/mass] Maximum lift-to-drag ratio according to drag parabola
\(C_{D_0}\) [dimensionless] Zero-lift drag coefficient of the aircraft
\(K\) [dimensionless] Lift-dependent drag factor of the aircraft
\(\rho\) [mass/volume] Air density at cruise altitude
\(S\) [area] Wing reference area of the aircraft
Derivation

Eqn. 7.43 in Young (2018) expresses the range \(R\) of an aircraft as a function of the takeoff mass \(m_1\) and landing mass \(m_2\) after cruise, as well as the aircraft performance parameters. However, in practical applications, the fuel mass \(m_f\) should be calculated based on the landing mass after cruise \(m_2\).

Starting from the definition of aircraft masses:

\[\begin{align*} m_f &= m_1 - m_2 \\ m_1 &= m_f + m_2 \\ \end{align*}\]

The expression for range can be rearranged:

\[\begin{align*} R &= \frac{2E_{max}V}{g\bar{c}} \arctan\left\{ \frac{\sqrt{B_3}(m_1 - m_2)}{B_3 + m_1 m_2} \right\} \\ \Theta &:= \frac{R g \bar{c}}{2 E_{max} V} \\ \tan(\Theta) &= \frac{\sqrt{B_3}(m_1 - m_2)}{B_3 + m_1 m_2} \\ \tan(\Theta) &= \frac{\sqrt{B_3} m_f}{B_3 + (m_f + m_2)m_2} \\ \tan(\Theta)(B_3 + m_f m_2 + m_2^2) &= \sqrt{B_3} m_f \\ B_3 \tan(\Theta) + m_f m_2 \tan(\Theta) + m_2^2 \tan(\Theta) &= \sqrt{B_3} m_f \\ \tan(\Theta)(B_3 + m_2^2) &= m_f (\sqrt{B_3} - m_2 \tan(\Theta)) \\ m_f &= \frac{(B_3 + m_2^2) \tan(\Theta)}{\sqrt{B_3} - m_2 \tan(\Theta)} \\ \end{align*}\]

where:

\[\begin{equation*} B_3 = \left(\frac{C_{D_0}}{K}\right) \left(\frac{\rho V^2 S}{2g}\right)^2 \end{equation*}\]
References

Eqn. 7.43 and Eqn. 13.25a ff. in Young, T. M. (2018). Performance of the Jet Transport Airplane. John Wiley & Sons. doi:10.1002/9781118534786

Example

Editor (session: default)Run
import jetfuelburn
from jetfuelburn import ureg
from jetfuelburn.rangeequation import calculate_fuel_consumption_arctan
calculate_fuel_consumption_arctan(
    R=2000*ureg.nmi,
    h=35000*ureg.feet,
    K=0.045,
    C_D0=0.02,
    m_after_cruise=100*ureg.metric_ton,
    S=122.6*ureg.meter**2,
    V=800*ureg.kph,
    TSFC=17*(ureg.mg/ureg.N/ureg.s),
)
OutputClear

Source code in jetfuelburn/rangeequation.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
@ureg.check(
    "[length]",
    "[length]",
    "[]",
    "[]",
    "[mass]",
    "[area]",
    "[speed]",
    "[time]/[length]",  # [mg/Ns] = s/m
)
def calculate_fuel_consumption_stepclimb_arctan(
    R: pint.Quantity[float | int],
    h: pint.Quantity[float | int],
    K: float | int | pint.Quantity[float | int],
    C_D0: float | int | pint.Quantity[float | int],
    m_after_cruise: pint.Quantity[float | int],
    S: pint.Quantity[float | int],
    V: pint.Quantity[float | int],
    TSFC: pint.Quantity[float | int],
) -> float:
    r"""
    Given a flight distance (=range) $R$ and basic aircraft performance parameters (see table),
    returns the fuel mass burned during the flight $m_f$ [kg] based on a flight schedule where 
    aircraft velocity and altitude are constant during cruise.

    Fuel mass is calculated as:

    $$
        m_f = \frac{(B + m_{LDG}^2) \tan(\theta)}{\sqrt{B} - m_{LDG} \tan(\theta)}
    $$

    with

    \begin{align*}
        \theta &= \frac{R \cdot g \cdot TSFC}{2 E_{max} V} \\
        B &= \left( \frac{C_{D_0}}{K} \right) \left( \frac{\rho V^2 S}{2g} \right)^2 \\
        E_{max} &= \frac{1}{2 \sqrt{C_{D_0} K}}
    \end{align*}

    where:

    | Symbol     | Dimension         | Description                                                            |
    |------------|-------------------|------------------------------------------------------------------------|
    | $m_{fuel}$ | [mass]            | Fuel required for the mission of the aircraft                          |
    | $R$        | [distance]        | Range of the aircraft (=mission distance)                              |
    | $TSFC$     | [time/distance]   | Thrust Specific Fuel Consumption of the aircraft during cruise         |
    | $g$        | [acceleration]    | Acceleration due to gravity                                            |
    | $V$        | [speed]           | Average cruise speed of the aircraft (TAS)                             |
    | $E_{max}$  | [energy/mass]     | Maximum lift-to-drag ratio according to drag parabola                  |
    | $C_{D_0}$  | [dimensionless]   | Zero-lift drag coefficient of the aircraft                             |
    | $K$        | [dimensionless]   | Lift-dependent drag factor of the aircraft                             |
    | $\rho$     | [mass/volume]     | Air density at cruise altitude                                         |
    | $S$        | [area]            | Wing reference area of the aircraft                                    |


    ??? info "Derivation"

        Eqn. 7.43 in Young (2018) expresses the range $R$ of an aircraft as a function of the takeoff mass $m_1$ 
        and landing mass $m_2$ after cruise, as well as the aircraft performance parameters. 
        However, in practical applications, the fuel mass $m_f$ should be calculated based on the landing mass after cruise $m_2$.

        Starting from the definition of aircraft masses:

        \begin{align*}
        m_f &= m_1 - m_2 \\
        m_1 &= m_f + m_2 \\
        \end{align*}

        The expression for range can be rearranged:

        \begin{align*}
        R &= \frac{2E_{max}V}{g\bar{c}} \arctan\left\{ \frac{\sqrt{B_3}(m_1 - m_2)}{B_3 + m_1 m_2} \right\} \\
        \Theta &:= \frac{R g \bar{c}}{2 E_{max} V} \\
        \tan(\Theta) &= \frac{\sqrt{B_3}(m_1 - m_2)}{B_3 + m_1 m_2} \\
        \tan(\Theta) &= \frac{\sqrt{B_3} m_f}{B_3 + (m_f + m_2)m_2} \\
        \tan(\Theta)(B_3 + m_f m_2 + m_2^2) &= \sqrt{B_3} m_f \\
        B_3 \tan(\Theta) + m_f m_2 \tan(\Theta) + m_2^2 \tan(\Theta) &= \sqrt{B_3} m_f \\
        \tan(\Theta)(B_3 + m_2^2) &= m_f (\sqrt{B_3} - m_2 \tan(\Theta)) \\
        m_f &= \frac{(B_3 + m_2^2) \tan(\Theta)}{\sqrt{B_3} - m_2 \tan(\Theta)} \\
        \end{align*}

        where:

        \begin{equation*}
        B_3 = \left(\frac{C_{D_0}}{K}\right) \left(\frac{\rho V^2 S}{2g}\right)^2
        \end{equation*}


    References
    ----------
    Eqn. 7.43 and Eqn. 13.25a ff. in 
    Young, T. M. (2018). 
    Performance of the Jet Transport Airplane. 
    _John Wiley & Sons_. 
    doi:[10.1002/9781118534786](https://doi.org/10.1002/9781118534786)

    Example
    -------
    ```pyodide install='jetfuelburn'
    import jetfuelburn
    from jetfuelburn import ureg
    from jetfuelburn.rangeequation import calculate_fuel_consumption_arctan
    calculate_fuel_consumption_arctan(
        R=2000*ureg.nmi,
        h=35000*ureg.feet,
        K=0.045,
        C_D0=0.02,
        m_after_cruise=100*ureg.metric_ton,
        S=122.6*ureg.meter**2,
        V=800*ureg.kph,
        TSFC=17*(ureg.mg/ureg.N/ureg.s),
    )
    ```

    """
    if R == 0 * ureg.meter:
        return 0 * ureg.kg
    if R < 0 * ureg.meter:
        raise ValueError("Range must be greater than zero.")
    if h < 0 * ureg.meter:
        raise ValueError("Altitude must be greater than zero.")
    if m_after_cruise < 0 * ureg.kg:
        raise ValueError("Mass after cruise must be greater than zero.")
    if S <= 1 * ureg.meter**2:
        raise ValueError("Lift-to-Drag ratio must be greater than 1.")
    if V <= 0 * ureg.kph:
        raise ValueError("Cruise speed must be greater than zero.")
    if TSFC.magnitude <= 0:
        raise ValueError("Thrust Specific Fuel Consumption must be greater than zero.")

    E_max = 0.5 * (1 / math.sqrt(C_D0 * K))
    rho = _calculate_atmospheric_density(altitude=h)
    theta = (R * ureg.gravity * TSFC) / (2 * E_max * V)
    B = (C_D0 / K) * ((rho * V**2 * S) / (2 * ureg.gravity)) ** 2
    m_fuel = ((B + m_after_cruise**2) * math.tan(theta)) / (
        B**0.5 - m_after_cruise * math.tan(theta)
    )  # math.sqrt(pint.Quantity) is not supported
    return m_fuel.to("kg")

calculate_fuel_consumption_stepclimb_integration

calculate_fuel_consumption_stepclimb_integration(
    m_after_cruise,
    R,
    h,
    M,
    TSFC,
    LD,
    integration_mass_step=100 * ureg.kg,
)

Given a flight distance (=range) \(R\) and basic aircraft performance parameters (see table), returns the fuel mass burned during the flight \(m_f\) [kg] based on a flight schedule where aircraft velocity and altitude are constant during cruise, while allowing for variable thrust-specific fuel consumption and lift-to-drag ratio as a function of Mach number and altitude.

Fuel mass is calculated through numerical integration of the specific air range (SAR) over the flight distance, using the trapezoidal rule:

\[\begin{align*} R &\approx \sum_{i=0}^{n-1} \frac{r_a(m_i) + r_a(m_{i+1})}{2} \cdot (m_{i+1} - m_i) \\ r_a &= \frac{M \cdot a_0 \sqrt{\Theta} \cdot (L/D)}{\text{TSFC} \cdot L} \end{align*}\]

where:

Symbol Dimension Description
\(m_{fuel}\) [mass] Fuel required for the mission of the aircraft
\(R\) [distance] Range of the aircraft (=mission distance)
\(h\) [length] Cruise altitude of the aircraft
\(M\) [dimensionless] Mach number during cruise
\(TSFC\) [time/distance] Thrust Specific Fuel Consumption of the aircraft during cruise
\(L/D\) [dimensionless] Lift-to-drag ratio of the aircraft during cruise
\(a_0\) [speed] Speed of sound at sea level
\(\Theta\) [dimensionless] Temperature ratio at cruise altitude (temperature at cruise altitude divided by temperature at sea level)
\(m_i\) [mass] Mass of the aircraft at step \(i\) during cruise, starting with \(m_0 = m_{TO}\) and ending with \(m_n = m_{LDG}\) (= \(m_{after\_cruise}\))
\(g\) [acceleration] Gravitational acceleration
Derivation

Equation 19 in Randle et al. (2011) expresses the fuel mass burned \(m_f\) during a flight of distance \(s\) (=range \(R\)) as a function of the takeoff mass \(m_{TO}\). However, in practical applications, the more relevant known parameter is the landing mass after cruise

\[\begin{align} r_a &= -\frac{\text{d}x}{\text{d}m} \\ \text{d}x &= -r_a \text{d}m \\ R = \int_{start}^{end} \text{d}x &= -\int_{m_{TO}}^{m_{LDG}} r_a \text{d}m \\ \end{align}\]

with the specific air range (SAR) defined as:

\[\begin{align} r_a &= -\frac{\text{d}x}{\text{d}m} \\ r_a &= -\frac{\text{d}x/\text{d}t}{\text{d}m/\text{d}t} \\ r_a &= \frac{V}{Q} \\ \end{align}\]

with the definition of true airspeed (=velocity) \(V\) and fuel flow rate \(Q\):

\[\begin{align} V &= M \cdot a_0 \sqrt{\Theta} \\ Q &= \text{TSFC} \cdot D = \text{TSFC} \cdot \frac{L}{(L/D)} \end{align}\]
\[\begin{align} R = \int_{m_{TO}}^{m_{LDG}} \frac{V}{Q} \text{d}m &= \int_{m_{TO}}^{m_{LDG}} \frac{M \cdot a_0 \sqrt{\Theta}}{\text{TSFC} \cdot \frac{L}{(L/D)}} \text{d}m \\ R = \int_{m_{TO}}^{m_{LDG}} \frac{M \cdot a_0 \sqrt{\Theta} \cdot (L/D)}{\text{TSFC} \cdot L} \text{d}m &= \int_{m_{TO}}^{m_{LDG}} \frac{M \cdot a_0 \sqrt{\Theta} \cdot (L/D)}{\text{TSFC} \cdot m \cdot g} \text{d}m \end{align}\]

which can now be solved through numerical integration, using the trapezoidal rule:

\[\begin{equation} R \approx \sum_{i=0}^{n-1} \frac{r_a(m_i) + r_a(m_{i+1})}{2} \cdot (m_{i+1} - m_i) \end{equation}\]
See Also

jetfuelburn.rangeequation.calculate_fuel_consumption_stepclimb_arctan

References

Eqn. 13.1aff. and Eqn. 13.28a ff. in Young, T. M. (2018). Performance of the Jet Transport Airplane. John Wiley & Sons. doi:10.1002/9781118534786

Parameters:

Name Type Description Default
m_after_cruise float

Mass of the aircraft after cruise segment (eg. OEW + Payload + Crew + Reserves) [mass]

required
R float

Range of the aircraft (=mission distance) [distance]

required
h float

Cruise altitude of the aircraft [length]

required
M float

Mach number during cruise [dimensionless]

required
TSFC float or Callable

Thrust Specific Fuel Consumption of the aircraft during cruise, either as a constant value [time/distance] or as a function of Mach number and altitude.

required
LD float or Callable

Lift-to-drag ratio of the aircraft during cruise, either as a constant value [dimensionless] or as a function of lift, Mach number and altitude.

required
integration_mass_step float

Mass step for numerical integration [mass]. A smaller step will yield a more accurate result, but will also increase computation time. Default is 100 kg.

100 * kg

Returns:

Type Description
Quantity[mass]

Required fuel mass [kg]

Raises:

Type Description
ValueError

If the dimensions of the inputs are invalid.

ValueError

If the magnitude of the inputs are invalid (eg. negative).

Example

Editor (session: default)Run
import jetfuelburn
from jetfuelburn import ureg
from jetfuelburn.rangeequation import calculate_integrated_range
calculate_integrated_range(
    m_after_cruise=100*ureg.metric_ton,
    R=2000*ureg.nmi,
    h=35000*ureg.feet,
    M=0.78,
    TSFC=17*(ureg.mg/ureg.N/ureg.s),
    LD=18,
)
OutputClear

Source code in jetfuelburn/rangeequation.py
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
@ureg.check(
    "[mass]",
    "[length]",
    "[length]",
    "[]",
    None,
    None,
    "[mass]",
)
def calculate_fuel_consumption_stepclimb_integration(
    m_after_cruise: pint.Quantity,
    R: pint.Quantity,
    h: pint.Quantity,
    M: float,
    TSFC: pint.Quantity | Callable,
    LD: float | pint.Quantity | Callable,
    integration_mass_step: pint.Quantity = 100 * ureg.kg,
) -> pint.Quantity:
    r"""
    Given a flight distance (=range) $R$ and basic aircraft performance parameters (see table),
    returns the fuel mass burned during the flight $m_f$ [kg] based on a flight schedule 
    where aircraft velocity and altitude are constant during cruise, while allowing for variable thrust-specific fuel consumption
    and lift-to-drag ratio as a function of Mach number and altitude.

    Fuel mass is calculated through numerical integration of the specific air range (SAR) over the flight distance, using the trapezoidal rule:

    \begin{align*}
        R &\approx \sum_{i=0}^{n-1} \frac{r_a(m_i) + r_a(m_{i+1})}{2} \cdot (m_{i+1} - m_i) \\
        r_a &= \frac{M \cdot a_0 \sqrt{\Theta} \cdot (L/D)}{\text{TSFC} \cdot L}
    \end{align*}

    where:

    | Symbol     | Dimension         | Description                                                                                                              |
    |------------|-------------------|--------------------------------------------------------------------------------------------------------------------------|
    | $m_{fuel}$ | [mass]            | Fuel required for the mission of the aircraft                                                                            |
    | $R$        | [distance]        | Range of the aircraft (=mission distance)                                                                                |
    | $h$        | [length]          | Cruise altitude of the aircraft                                                                                          |
    | $M$        | [dimensionless]   | Mach number during cruise                                                                                                |
    | $TSFC$     | [time/distance]   | Thrust Specific Fuel Consumption of the aircraft during cruise                                                           |
    | $L/D$      | [dimensionless]   | Lift-to-drag ratio of the aircraft during cruise                                                                         |
    | $a_0$      | [speed]           | Speed of sound at sea level                                                                                              |
    | $\Theta$   | [dimensionless]   | Temperature ratio at cruise altitude (temperature at cruise altitude divided by temperature at sea level)               |
    | $m_i$      | [mass]            | Mass of the aircraft at step $i$ during cruise, starting with $m_0 = m_{TO}$ and ending with $m_n = m_{LDG}$ (= $m_{after\_cruise}$) |
    | $g$        | [acceleration]    | Gravitational acceleration                                                                                               |


    ??? info "Derivation"

        Equation 19 in Randle et al. (2011) expresses the fuel mass burned $m_f$
        during a flight of distance $s$ (=range $R$) as a function of the takeoff mass $m_{TO}$.
        However, in practical applications, the more relevant known parameter is the landing mass after cruise 

        \begin{align}
        r_a &= -\frac{\text{d}x}{\text{d}m} \\
        \text{d}x &= -r_a \text{d}m \\
        R = \int_{start}^{end} \text{d}x &= -\int_{m_{TO}}^{m_{LDG}} r_a \text{d}m \\
        \end{align}

        with the specific air range (SAR) defined as:

        \begin{align}
        r_a &= -\frac{\text{d}x}{\text{d}m} \\
        r_a &= -\frac{\text{d}x/\text{d}t}{\text{d}m/\text{d}t} \\
        r_a &= \frac{V}{Q} \\
        \end{align}

        with the definition of true airspeed (=velocity) $V$ and fuel flow rate $Q$:

        \begin{align}
        V &= M \cdot a_0 \sqrt{\Theta} \\
        Q &= \text{TSFC} \cdot D  = \text{TSFC} \cdot \frac{L}{(L/D)}
        \end{align}

        \begin{align}
        R = \int_{m_{TO}}^{m_{LDG}} \frac{V}{Q} \text{d}m &= \int_{m_{TO}}^{m_{LDG}} \frac{M \cdot a_0 \sqrt{\Theta}}{\text{TSFC} \cdot \frac{L}{(L/D)}} \text{d}m \\
        R = \int_{m_{TO}}^{m_{LDG}} \frac{M \cdot a_0 \sqrt{\Theta} \cdot (L/D)}{\text{TSFC} \cdot L} \text{d}m &= \int_{m_{TO}}^{m_{LDG}} \frac{M \cdot a_0 \sqrt{\Theta} \cdot (L/D)}{\text{TSFC} \cdot m \cdot g} \text{d}m
        \end{align}

        which can now be solved through numerical integration, using the trapezoidal rule:

        \begin{equation}
        R \approx \sum_{i=0}^{n-1} \frac{r_a(m_i) + r_a(m_{i+1})}{2} \cdot (m_{i+1} - m_i)
        \end{equation}

    See Also
    --------
    [`jetfuelburn.rangeequation.calculate_fuel_consumption_stepclimb_arctan`][]

    References
    ----------
    Eqn. 13.1aff. and Eqn. 13.28a ff. in 
    Young, T. M. (2018). 
    Performance of the Jet Transport Airplane. 
    _John Wiley & Sons_. 
    doi:[10.1002/9781118534786](https://doi.org/10.1002/9781118534786)

    Parameters
    ----------
    m_after_cruise : float
        Mass of the aircraft after cruise segment (eg. OEW + Payload + Crew + Reserves) [mass]
    R : float
        Range of the aircraft (=mission distance) [distance]
    h : float
        Cruise altitude of the aircraft [length]
    M : float
        Mach number during cruise [dimensionless]
    TSFC : float or Callable
        Thrust Specific Fuel Consumption of the aircraft during cruise, either as a constant value [time/distance] or as a function of Mach number and altitude.  
    LD : float or Callable
        Lift-to-drag ratio of the aircraft during cruise, either as a constant value [dimensionless] or as a function of lift, Mach number and altitude.  
    integration_mass_step : float
        Mass step for numerical integration [mass]. A smaller step will yield a more accurate result, but will also increase computation time. Default is 100 kg.

    Returns
    -------
    pint.Quantity [mass]
        Required fuel mass [kg]

    Raises
    ------
    ValueError
        If the dimensions of the inputs are invalid.
    ValueError
        If the magnitude of the inputs are invalid (eg. negative).

    Example
    -------
    ```pyodide install='jetfuelburn'
    import jetfuelburn
    from jetfuelburn import ureg
    from jetfuelburn.rangeequation import calculate_integrated_range
    calculate_integrated_range(
        m_after_cruise=100*ureg.metric_ton,
        R=2000*ureg.nmi,
        h=35000*ureg.feet,
        M=0.78,
        TSFC=17*(ureg.mg/ureg.N/ureg.s),
        LD=18,
    )
    ```
    """
    if integration_mass_step < 1 * ureg.kg:
        raise ValueError("integration_mass_step must be at least 1 kg")
    if m_after_cruise <= 0 * ureg.kg:
        raise ValueError("m_after_cruise must be greater than zero")
    if M <= 0:
        raise ValueError("Mach number must be greater than zero")
    if h < 0 * ureg.meter:
        raise ValueError("Altitude must be non-negative")

    if type(TSFC) == Callable:
        _validate_physics_function_parameters(
            func=TSFC,
            required_params={"M", "h"},
        )
    if type(LD) == Callable:
        _validate_physics_function_parameters(
            func=LD,
            required_params={"L", "M", "h"},
        )
    func_TSFC: Callable = _normalize_physics_function_or_scalar(TSFC)
    func_LD: Callable = _normalize_physics_function_or_scalar(LD)

    m_after_cruise = m_after_cruise.to("kg")
    V = _calculate_airspeed_from_mach(mach_number=M, altitude=h)

    m_current = m_after_cruise
    R_current = 0 * ureg.km

    while R_current < R:
        L_A = m_current * ureg.gravity
        L_B = (m_current + integration_mass_step) * ureg.gravity
        SAR_A = (V * func_LD(L=L_A, M=M, h=h)) / (func_TSFC(M=M, h=h) * L_A)
        SAR_B = (V * func_LD(L=L_B, M=M, h=h)) / (func_TSFC(M=M, h=h) * L_B)
        SAR_avg = (SAR_A + SAR_B) / 2
        delta_R = (SAR_avg * integration_mass_step).to("km")
        R_current += delta_R
        m_current += integration_mass_step

        if R_current >= R:
            R_excess = R_current - R
            m_excess = R_excess / SAR_avg
            m_current -= m_excess
            break

    m_fuel = m_current - m_after_cruise
    return m_fuel.to("kg")