Skip to content

Engines

aircraftdetective.calculations.engines

calculate_air_mass_flow_rate

calculate_air_mass_flow_rate(
    df, altitude=12000.0 * ureg.meter
)

Calculates the air mass flow rate for each engine in the provided DataFrame.

Air mass flow rate \(m\) is defined as: $$ m = r^2 \pi C_a \rho(h) $$ as a dimensionality analysis shows: $$ [kg/s] = [m^2][m/s][kg/m^3] = [m^3/s][kg/m^3] $$ where

Symbol Units Description
\(m\) kg/s Air mass flow rate
\(\rho\) kg/m³ Air density
\(C_a\) m/s Cruise speed = intake air velocity
\(r\) m Fan radius
\(h\) m Altitude
Warnings

Air density \(\rho(h)\) is dependent on altitude \(h\). The default altitude is 12'000 meters, but can be changed via the altitude parameter.

See Also

Parameters:

Name Type Description Default
df DataFrame

pint-pandas DataFrame containing the columns:

Column Name Dimension
Cruise Speed [length/time]
Fan Diameter [length]
required
altitude float[distance]

Altitude above sea level, by default 12000.0 * ureg.meter

12000.0 * meter

Raises:

Type Description
ValueError

If df is empty or does not contain the required columns.

Returns:

Type Description
DataFrame

pint-pandas DataFrame with a new column Air Mass Flow [weight/time] added.

Source code in aircraftdetective/calculations/engines.py
def calculate_air_mass_flow_rate(
    df: pd.DataFrame,
    altitude: float = 12000.0 * ureg.meter,
) -> pd.DataFrame:
    r"""
    Calculates the air mass flow rate for each engine in the provided DataFrame.

    Air mass flow rate $m$ is defined as:
    $$
    m = r^2 \pi C_a \rho(h)
    $$
    as a dimensionality analysis shows:
    $$
    [kg/s] = [m^2][m/s][kg/m^3] = [m^3/s][kg/m^3]
    $$
    where

    | Symbol       | Units         | Description                                |
    |--------------|---------------|--------------------------------------------|
    | $m$          | kg/s          | Air mass flow rate                         |
    | $\rho$       | kg/m³         | Air density                                |
    | $C_a$        | m/s           | Cruise speed = intake air velocity         |
    | $r$          | m             | Fan radius                                 |
    | $h$          | m             | Altitude                                   |

    Warnings
    --------
    Air density $\rho(h)$ is dependent on altitude $h$. 
    The default altitude is 12'000 meters, but can be changed via the `altitude` parameter.

    See Also
    --------
    - [Mass Flow Rate on Wikipedia](https://en.wikipedia.org/wiki/Mass_flow_rate#Air_mass_flow_rate)
    - [`aircraftdetective.utility.physics._calculate_atmospheric_conditions`][]

    Parameters
    ----------
    df : pd.DataFrame
        [`pint-pandas`](https://pint-pandas.readthedocs.io/en/latest/) DataFrame containing the columns:

        | Column Name   | Dimension     |
        |---------------|---------------|
        | `Cruise Speed`| [length/time] |
        | `Fan Diameter`| [length]      |

    altitude : float [distance], optional
        Altitude above sea level, by default 12000.0 * ureg.meter   

    Raises
    ------
    ValueError
        If `df` is empty or does not contain the required columns.

    Returns
    -------
    pd.DataFrame
        [`pint-pandas`](https://pint-pandas.readthedocs.io/en/latest/) DataFrame with a new column `Air Mass Flow` [weight/time] added.
    """
    if df.empty:
        raise ValueError("DataFrame is empty.")
    required_columns = [
        'Cruise Speed',
        'Fan Diameter',
    ]
    missing_columns = [col for col in required_columns if col not in df.columns]
    if missing_columns:
        raise KeyError(f"DataFrame is missing required columns: {missing_columns}")

    df_func = df.copy()

    air_density = _calculate_atmospheric_conditions(altitude)['density']
    df_func['Air Mass Flow'] = (air_density * df['Cruise Speed'] * math.pi * (df['Fan Diameter']/2)**2)
    return df_func

calculate_engine_efficiencies

calculate_engine_efficiencies(df)

Calculates the overall engine efficiency, propulsive efficiency and thermal efficiency for each engine in the provided DataFrame. The calculation combines multiple equations describing turbofan engine performance.

Overall engine efficiency \(\eta_0\) is defined as: $$ \eta_0 = \frac{C_a}{TSFC} \frac{1}{Q} $$ Propulsive efficiency \(\eta_P\) is defined as: $$ \eta_p = \frac{2}{1 + \frac{C_j}{C_a}} $$ Since the exhaust jet velocity \(C_j\) is not typically known, the thrust equation $$ F = m (C_j - C_a) $$ is rearranged to solve for \(C_j\): $$ C_j = \frac{F}{m} + C_a $$ which gives $$ \eta_p = \frac{2}{2 + \frac{F}{m C_a}} $$ Using a different definition for overall efficiency \(\eta_0\): $$ \eta_0 = \frac{FC_a}{m_fQ} $$ net thrust \(F\) can be obtained: $$ F = \frac{\eta_0 m_f Q}{C_a} $$ which for propulsive efficiency \(\eta_P\) finally gives: $$ \eta_p = \frac{2}{2 + \frac{\eta_0 m_f Q}{m C_a^2}} $$

Symbol Units Description
\(\eta_0\) dimensionless Overall engine efficiency
\(C_a\) m/s Cruise speed = intake air velocity
\(C_j\) m/s Jet velocity = exhaust air velocity
\(TSFC\) kg/(N*s) Thrust-Specific Fuel Consumption at cruise
\(m\) kg/s Air mass flow rate
\(m_f\) kg/s Fuel mass flow rate
\(F\) N Net engine thrust
\(Q\) J/kg Fuel Heating Value
Warnings

In \(F = \frac{\eta_0 m_f Q}{C_a}\), the fuel mass flow rate \(m_f\) is assumed to be for all engines of the aircraft. This means that air mass flow rate \(m\) is also calculated for all engines of the aircraft.

References
See Also

Parameters:

Name Type Description Default
df DataFrame

pint-pandas DataFrame containing the columns:

Column Name Dimension
Cruise Speed [length/time]
TSFC (cruise) [mass/(force*time)]
Fuel Flow [mass/time]
Air Mass Flow [mass/time]
Number of Engines [dimensionless]
B/P Ratio [dimensionless]
required

Returns:

Type Description
DataFrame

pint-pandas DataFrame with a new column Engine Efficiency [dimensionless] added.

Source code in aircraftdetective/calculations/engines.py
def calculate_engine_efficiencies(
    df: pd.DataFrame
) -> pd.DataFrame:
    r"""
    Calculates the overall engine efficiency, propulsive efficiency and thermal efficiency 
    for each engine in the provided DataFrame. The calculation combines multiple equations describing turbofan engine performance.

    Overall engine efficiency $\eta_0$ is defined as:
    $$
    \eta_0 = \frac{C_a}{TSFC} \frac{1}{Q}
    $$
    Propulsive efficiency $\eta_P$ is defined as:
    $$
    \eta_p = \frac{2}{1 + \frac{C_j}{C_a}}
    $$
    Since the exhaust jet velocity $C_j$ is not typically known,
    the thrust equation
    $$
    F = m (C_j - C_a)
    $$
    is rearranged to solve for $C_j$:
    $$
    C_j = \frac{F}{m} + C_a
    $$
    which gives
    $$
    \eta_p = \frac{2}{2 + \frac{F}{m C_a}}
    $$
    Using a different definition for overall efficiency $\eta_0$:
    $$
    \eta_0 = \frac{FC_a}{m_fQ}
    $$
    net thrust $F$ can be obtained:
    $$
    F = \frac{\eta_0 m_f Q}{C_a}
    $$
    which for propulsive efficiency $\eta_P$ finally gives: 
    $$
    \eta_p = \frac{2}{2 + \frac{\eta_0 m_f Q}{m C_a^2}}
    $$

    | Symbol       | Units         | Description                                |
    |--------------|---------------|--------------------------------------------|
    | $\eta_0$     | dimensionless | Overall engine efficiency                  |
    | $C_a$        | m/s           | Cruise speed = intake air velocity         |
    | $C_j$        | m/s           | Jet velocity = exhaust air velocity        |
    | $TSFC$       | kg/(N*s)      | Thrust-Specific Fuel Consumption at cruise |
    | $m$          | kg/s          | Air mass flow rate                         |
    | $m_f$        | kg/s          | Fuel mass flow rate                        |
    | $F$          | N             | Net engine thrust                          |
    | $Q$          | J/kg          | Fuel Heating Value                         |

    Warnings
    --------
    In $F = \frac{\eta_0 m_f Q}{C_a}$, the fuel mass flow rate $m_f$
    is assumed to be for _all_ engines of the aircraft. 
    This means that air mass flow rate $m$ is also calculated for _all_ engines of the aircraft.

    References
    ----------
    - Overall engine efficiency $\eta_0$: [Eqn. (3.5)-(3.7) in Saravanamuttoo et al. (2017)](http://books.google.com/books?vid=ISBN9781292093093)
    - Propulsive efficiency $\eta_P$: [Eqn. (3.3) in Saravanamuttoo et al. (2017)](http://books.google.com/books?vid=ISBN9781292093093)
    - Net engine thrust $F$: [Eqn. (3.1) in Saravanamuttoo et al. (2017)](http://books.google.com/books?vid=ISBN9781292093093)

    See Also
    --------
    - [Jet Fuel on Wikipedia](https://en.wikipedia.org/wiki/Aviation_fuel)

    Parameters
    ----------
    df : pd.DataFrame
        [`pint-pandas`](https://pint-pandas.readthedocs.io/en/latest/) DataFrame containing the columns:

        | Column Name         | Dimension           |
        |---------------------|---------------------|
        | `Cruise Speed`      | [length/time]       |
        | `TSFC (cruise)`     | [mass/(force*time)] |
        | `Fuel Flow`         | [mass/time]         |
        | `Air Mass Flow`     | [mass/time]         |
        | `Number of Engines` | [dimensionless]     |
        | `B/P Ratio`         | [dimensionless]     |

    Returns
    -------
    pd.DataFrame
        [`pint-pandas`](https://pint-pandas.readthedocs.io/en/latest/) DataFrame with a new column `Engine Efficiency` [dimensionless] added.
    """
    required_columns = [
        'Cruise Speed',
        'TSFC (cruise)',
        'Fuel Flow',
        'Air Mass Flow',
        'Number of Engines',
        'B/P Ratio'
    ]
    missing_columns = [col for col in required_columns if col not in df.columns]
    if missing_columns:
        raise ValueError(f"DataFrame is missing required columns: {missing_columns}")

    df['Engine Efficiency'] = df['Cruise Speed'] / (jeta1_specificenergy * df['TSFC (cruise)'])

    df['Propulsive Efficiency'] = 2 / (2 + (df['Engine Efficiency'] * df['Fuel Flow'] * jeta1_density * jeta1_specificenergy) / (df['Air Mass Flow'] * df['Cruise Speed']**2 * df['Number of Engines']))

    df['Thermal Efficiency'] = df['Engine Efficiency']/df['Propulsive Efficiency']

    df.loc[df['B/P Ratio']<=2, 'Thermal Efficiency'] = np.nan
    df.loc[df['B/P Ratio']<=2, 'Propulsive Efficiency'] = np.nan

    return df

determine_takeoff_to_cruise_tsfc_ratio

determine_takeoff_to_cruise_tsfc_ratio(
    degree=2,
    plot=False,
    path_excel_engine_data_for_calibration=PATH_ZENODO_ENGINE_TSFC_CALIBRATION_FILE,
)

Given a path to an Excel file with engine TSFC data for takeoff and cruise, computes two fitted polynomials (linear and quadratic). Also returns the \(R^2\) values for both fits and the cleaned DataFrame with engine data.

A degree 1 polynomial (linear fit) is implemented as: $$ TSFC_{cruise} = \beta_0 + \beta_1 \cdot TSFC_{takeoff} + \epsilon $$ A degree 2 polynomial (quadratic fit) is implemented as: $$ TSFC_{cruise} = \beta_0 + \beta_1 \cdot TSFC_{takeoff} + \beta_2 \cdot TSFC_{takeoff}^2 + \epsilon $$ etc., where

Symbol Description
\(TSFC_{cruise}\) Thrust-Specific Fuel Consumption at cruise
\(TSFC_{takeoff}\) Thrust-Specific Fuel Consumption at takeoff
\(\beta_0\) Intercept of the fitted polynomial
\(\beta_1\) Slope of the fitted polynomial
\(\beta_2\) Quadratic coefficient of the fitted polynomial
\(\epsilon\) Error term
Notes

The Excel file must have a sheet named Data with at least the columns [Engine Identification, TSFC (cruise), TSFC (takeoff)]. The first row of the sheet must contain the column names. The second row must contain the units of the columns in square brackets [], in a format supported by Pint. Columns with no relevant units must be marked with No Unit.

Engine Identification TSFC (cruise) TSFC (takeoff)
No Unit [g/(kN*s)] [g/(kN*s)]
D-30KU 19.82788889 13.87952222
D-100 15.2958 8.101108889
CFE738 18.26998333 10.45213
See Also
References
Notes

With no parameters passed, the function will download the relevant Excel file from the aircraftdetective Zenodo repository.

Parameters:

Name Type Description Default
path_excel_engine_data_for_calibration str

description

PATH_ZENODO_ENGINE_TSFC_CALIBRATION_FILE

Returns:

Type Description
tuple[DataFrame, Polynomial, Polynomial, float, float]

DataFrame with engine data and fitted polynomials with their R² values

Raises:

Type Description
ValueError

If the Excel file does not contain the required columns or units.

ValueError

If the degree parameter is not a positive integer.

Source code in aircraftdetective/calculations/engines.py
def determine_takeoff_to_cruise_tsfc_ratio(
    degree: int = 2,
    plot: bool = False,
    path_excel_engine_data_for_calibration: str = PATH_ZENODO_ENGINE_TSFC_CALIBRATION_FILE,
) -> dict[str, Any]:
    r"""
    Given a path to an Excel file with engine TSFC data for takeoff and cruise,
    computes two fitted polynomials (linear and quadratic).
    Also returns the $R^2$ values for both fits and the cleaned DataFrame with engine data.

    A degree 1 polynomial (linear fit) is implemented as:
    $$
    TSFC_{cruise} = \beta_0 + \beta_1 \cdot TSFC_{takeoff} + \epsilon
    $$
    A degree 2 polynomial (quadratic fit) is implemented as:
    $$
    TSFC_{cruise} = \beta_0 + \beta_1 \cdot TSFC_{takeoff} + \beta_2 \cdot TSFC_{takeoff}^2 + \epsilon
    $$
    etc., where

    | Symbol           | Description                                      |
    |------------------|--------------------------------------------------|
    | $TSFC_{cruise}$  | Thrust-Specific Fuel Consumption at cruise       |
    | $TSFC_{takeoff}$ | Thrust-Specific Fuel Consumption at takeoff      |
    | $\beta_0$        | Intercept of the fitted polynomial               |
    | $\beta_1$        | Slope of the fitted polynomial                   |
    | $\beta_2$        | Quadratic coefficient of the fitted polynomial   |
    | $\epsilon$       | Error term                                       |

    Notes
    -----
    The Excel file must have a sheet named `Data` with at least the columns `[Engine Identification, TSFC (cruise), TSFC (takeoff)]`.
    The first row of the sheet must contain the column names.
    The second row must contain the units of the columns in square brackets `[]`, [in a format supported by Pint](https://pint.readthedocs.io/en/stable/user/formatting.html#pint-format-types).
    Columns with no relevant units must be marked with `No Unit`.

    | Engine Identification | TSFC (cruise)  | TSFC (takeoff) |
    |-----------------------|----------------|----------------|
    | **No Unit**           | **[g/(kN*s)]** | **[g/(kN*s)]** |
    | D-30KU                | 19.82788889    | 13.87952222    |
    | D-100                 | 15.2958        | 8.101108889    |
    | CFE738                | 18.26998333    | 10.45213       |

    See Also
    --------
    - [`numpy.polynomial.polynomial.Polynomial.fit()`](https://numpy.org/doc/stable/reference/generated/numpy.polynomial.polynomial.Polynomial.fit.html)
    - [`aircraftdetective.calculations.engines.scale_engine_data_from_icao_emissions_database`][]

    References
    ----------
    - [Section 4.6.2 in Sadraey, 2nd Edition (2023)](https://doi.org/10.1201/9781003279068)
    - [Thrust-Specific Fuel Consumption on Wikipedia](https://en.wikipedia.org/wiki/Thrust-specific_fuel_consumption)

    Notes
    -----
    With no parameters passed, the function will download the relevant Excel file 
    from the [`aircraftdetective` Zenodo repository](https://doi.org/10.5281/zenodo.14382100).

    Parameters
    ----------
    path_excel_engine_data_for_calibration : str
        _description_

    Returns
    -------
    tuple[pd.DataFrame, np.polynomial.Polynomial, np.polynomial.Polynomial, float, float]
        DataFrame with engine data and fitted polynomials with their R² values

    Raises
    ------
    ValueError
        If the Excel file does not contain the required columns or units.
    ValueError
        If the degree parameter is not a positive integer.
    """
    if not isinstance(degree, int) or degree < 1:
        raise ValueError("degree must be a positive integer.")

    df_engines = pd.read_excel(
        io=path_excel_engine_data_for_calibration,
        sheet_name='Data',
        header=[0, 1],
        engine='openpyxl', 
    )
    df_engines = df_engines.pint.quantify(level=1)

    if ('TSFC (cruise)') not in df_engines.columns or ('TSFC (takeoff)') not in df_engines.columns:
        raise ValueError(f"Excel file must contain 'TSFC (cruise)' and 'TSFC (takeoff)' columns.")

    df_engines = df_engines[df_engines[['TSFC (cruise)','TSFC (takeoff)']].notna().all(axis=1)]
    df_engines_grouped = df_engines.groupby(['Engine Identification'], as_index=False).agg(
        {
            'TSFC (cruise)' : 'mean',
            'TSFC (takeoff)' : 'mean',
        }
    )

    df_engines_grouped['TSFC (cruise)'] = df_engines_grouped['TSFC (cruise)'].astype("pint[g/(kN*s)]")
    df_engines_grouped['TSFC (takeoff)'] = df_engines_grouped['TSFC (takeoff)'].astype("pint[g/(kN*s)]")

    return _compute_polynomials_from_dataframe(
        df=df_engines_grouped,
        col_name_x='TSFC (takeoff)',
        list_col_names_y=['TSFC (cruise)'],
        degree=degree,
        plot=plot
    )

scale_engine_data_from_icao_emissions_database

scale_engine_data_from_icao_emissions_database(
    scaling_polynomial,
    path_excel_engine_data_icao_in=PATH_EASA_ENGINE_EMISSIONS_DATABANK_FILE,
)

Given a path or URL to the ICAO Aircraft Engine Emissions Databank Excel file, scales the TSFC (takeoff) values to cruise using a provided polynomial. $$ TSFC_{cruise} = f(TSFC_{takeoff}) $$ where \(f\) is the scaling polynomial.

Notes

The returns DataFrame has only a subset of columns relevant to engine efficiency. Columns labeled 🆕 are new columns computed by this function:

Output DataFrame Column Name Unit/Data Type
Engine Identification str
Final Test Date float
🆕 TSFC (cruise) pint[g/(kN*s)]
🆕 TSFC (takeoff) pint[g/(kN*s)]
Fuel Flow (takeoff) pint[kg/s]
Fuel Flow (climbout) pint[kg/s]
Fuel Flow (approach) pint[kg/s]
Fuel Flow (idle) pint[kg/s]
B/P Ratio pint[dimensionless]
Pressure Ratio pint[dimensionless]
Rated Thrust pint[kN]
See Also

Parameters:

Name Type Description Default
path_excel_engine_data_icao_in str

Path or URL to the ICAO Aircraft Engine Emissions Databank Excel file. Equivalent to parameter io in pandas.read_excel.

PATH_EASA_ENGINE_EMISSIONS_DATABANK_FILE
scaling_polynomial Polynomial

numpy.polynomial.polynomial.Polynomial Polynomial to scale TSFC (takeoff) to TSFC (cruise).

required

Returns:

Type Description
DataFrame

DataFrame with engine data and scaled TSFC (cruise) values.

Raises:

Type Description
ValueError

If the provided scaling_polynomial is not a numpy.polynomial.polynomial.Polynomial object.

Source code in aircraftdetective/calculations/engines.py
def scale_engine_data_from_icao_emissions_database(
    scaling_polynomial: np.polynomial.Polynomial,
    path_excel_engine_data_icao_in: str = PATH_EASA_ENGINE_EMISSIONS_DATABANK_FILE,
) -> pd.DataFrame:
    r"""
    Given a path or URL to the ICAO Aircraft Engine Emissions Databank Excel file,
    scales the TSFC (takeoff) values to cruise using a provided polynomial.
    $$
    TSFC_{cruise} = f(TSFC_{takeoff})
    $$
    where $f$ is the scaling polynomial.

    Notes
    -----
    The returns DataFrame has only a subset of columns relevant to engine efficiency.
    Columns labeled `🆕` are new columns computed by this function:

    | Output DataFrame Column Name | Unit/Data Type    |
    |------------------------|-------------------------|
    | Engine Identification  | `str`                   |
    | Final Test Date        | `float`                 |
    | 🆕 TSFC (cruise)       | `pint[g/(kN*s)]`        |
    | 🆕 TSFC (takeoff)      | `pint[g/(kN*s)]`        |
    | Fuel Flow (takeoff)    | `pint[kg/s]`            |
    | Fuel Flow (climbout)   | `pint[kg/s]`            |
    | Fuel Flow (approach)   | `pint[kg/s]`            |
    | Fuel Flow (idle)       | `pint[kg/s]`            |
    | B/P Ratio              | `pint[dimensionless]`   |
    | Pressure Ratio         | `pint[dimensionless]`   |
    | Rated Thrust           | `pint[kN]`              |

    See Also
    --------
    - [`aircraftdetective.calculations.engines.determine_takeoff_to_cruise_tsfc_ratio`][]
    - [ICAO Aircraft Engine Emissions Databank](https://www.easa.europa.eu/en/domains/environment/icao-aircraft-engine-emissions-databank)

    Parameters
    ----------
    path_excel_engine_data_icao_in : str
        Path or URL to the ICAO Aircraft Engine Emissions Databank Excel file.
        Equivalent to parameter `io` in [`pandas.read_excel`](https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html#pandas-read-excel).
    scaling_polynomial : np.polynomial.Polynomial
        [`numpy.polynomial.polynomial.Polynomial`](https://numpy.org/doc/stable/reference/generated/numpy.polynomial.polynomial.Polynomial.html#numpy.polynomial.polynomial.Polynomial) Polynomial to scale TSFC (takeoff) to TSFC (cruise).

    Returns
    -------
    pd.DataFrame
        DataFrame with engine data and scaled TSFC (cruise) values.

    Raises
    ------
    ValueError
        If the provided scaling_polynomial is not a [`numpy.polynomial.polynomial.Polynomial`](https://numpy.org/doc/stable/reference/generated/numpy.polynomial.polynomial.Polynomial.html#numpy.polynomial.polynomial.Polynomial) object.
    """
    if not isinstance(scaling_polynomial, np.polynomial.Polynomial):
        raise ValueError("scaling_polynomial must be a numpy Polynomial object.")

    df_engines = pd.read_excel(
        io=path_excel_engine_data_icao_in,
        sheet_name='Gaseous Emissions and Smoke',
        header=0,
        engine='openpyxl',
    )
    df_engines['Final Test Date'] = df_engines['Final Test Date'].dt.year.astype('Int64')

    df_engines = tabular._rename_columns_and_set_units(
        df=df_engines,
        return_only_renamed_columns=True,
        column_names_and_units=[
            ("Engine Identification", "Engine Identification", str),
            ("Final Test Date", "Final Test Date", "Int64"), # 'Int64' to ensure null values do not raise an error
            ("Fuel Flow T/O (kg/sec)", "Fuel Flow (takeoff)", "pint[kg/s]"),
            ("Fuel Flow C/O (kg/sec)", "Fuel Flow (climbout)", "pint[kg/s]"),
            ("Fuel Flow App (kg/sec)", "Fuel Flow (approach)", "pint[kg/s]"),
            ("Fuel Flow Idle (kg/sec)", "Fuel Flow (idle)", "pint[kg/s]"),
            ("B/P Ratio", "B/P Ratio", "pint[dimensionless]"),
            ("Pressure Ratio", "Pressure Ratio", "pint[dimensionless]"),
            ("Rated Thrust (kN)", "Rated Thrust", "pint[kN]")
        ],
    )

    df_engines = df_engines.groupby(['Engine Identification'], as_index=False).agg('mean')

    df_engines['TSFC (takeoff)'] = df_engines['Fuel Flow (takeoff)'] / df_engines['Rated Thrust']
    df_engines['TSFC (takeoff)'] = df_engines['TSFC (takeoff)'].astype("pint[g/(kN*s)]") # commonly used unit for TSFC, to ensure compatibility with the polynomial

    df_engines['TSFC (cruise)'] = df_engines['TSFC (takeoff)'].apply(lambda x: scaling_polynomial(x))
    df_engines['TSFC (cruise)'] = df_engines['TSFC (cruise)'].astype("pint[g/(kN*s)]") # commonly used unit for TSFC

    return df_engines