Package {MFO}


Title: Maximal Fat Oxidation and Kinetics Calculation
Version: 0.2.1
Date: 2026-11-11
Description: Computes the maximal fat oxidation (MFO), the exercise intensity that elicits it (Fatmax), the Fatmax zone and the crossover point from indirect calorimetry data of a graded exercise test. Substrate oxidation is estimated with the stoichiometric equations of Frayn (1983) <doi:10.1152/jappl.1983.55.2.628>, Peronnet and Massicotte (1991, Can J Sport Sci 16(1):23-29) or Jeukendrup and Wallis (2005) <doi:10.1055/s-2004-830512>, and Fatmax is located with a third- or second-degree polynomial, the measured values or the MRER method. Fat oxidation kinetics are described with the SIN model of Cheneviere et al. (2009) <doi:10.1249/MSS.0b013e31819e2f91>, which returns dilatation, symmetry and translation, together with the intensity at which fat oxidation becomes negligible (Fatmin). Applications of these methods are given in Achten et al. (2002) <doi:10.1097/00005768-200201000-00015> and Montes de Oca et al. (2021) <doi:10.1080/17461391.2020.1788650>.
License: MIT + file LICENSE
Encoding: UTF-8
Language: en-GB
LazyData: true
RoxygenNote: 7.3.3
URL: https://github.com/JorgeDelro/MFO, https://jorgedelro.github.io/MFO/
BugReports: https://github.com/JorgeDelro/MFO/issues
Imports: stats, dplyr, ggplot2, tibble, minpack.lm, openxlsx, readxl, tidyr
Depends: R (≥ 4.1.0)
Suggests: covr, knitr, rmarkdown, testthat (≥ 3.0.0), withr
VignetteBuilder: knitr
Config/testthat/edition: 3
NeedsCompilation: no
Packaged: 2026-11-12 09:10:10 UTC; jorge
Author: Jorge R Fernandez-Santos ORCID iD [aut, cre], Jesus G Ponce-Gonzalez ORCID iD [ctb], Cristina Casals ORCID iD [ctb], Jose L Gonzalez-Montesinos ORCID iD [ctb], Juan Corral-Perez ORCID iD [ctb], Alejandro Perez-Bey ORCID iD [ctb]
Maintainer: Jorge R Fernandez-Santos <jorgedelrosario.fernandez@uca.es>
Repository: CRAN
Date/Publication: 2026-11-12 14:40:08 UTC

Maximal Fat Oxidation & FatMax Function

Description

Calculate Maximal Fat Oxidation (MFO), the exercise intensity that elicits MFO (FatMax), the FatMax zone, and the crossover point. Supports multiple methods to locate MFO (P3, P2, MV, MRER) and stoichiometric equations (Frayn, Jeukendrup, Peronnet).

Usage

MFO(
  step_time,
  db_MFO,
  db_basal,
  db_graded = NULL,
  cv_var,
  author,
  VO2max = NULL,
  method = "P3",
  step_duration = 180,
  steady_state_duration = 60,
  RER_cutoff = NULL,
  negative_FAT = "truncate",
  body_mass = NULL,
  fat_free_mass = NULL,
  HRmax = NULL,
  vo2max_window = 30,
  include_basal = FALSE,
  energy_equivalents = c("classic", "source"),
  use_incomplete_step = FALSE,
  min_steps = 6
)

Arguments

step_time

how often the data was collected (in seconds).

db_MFO

database containing MFO test.

db_basal

database containing basal test.

db_graded

database containing incremental exercise test (optional if VO2max provided). VO2max is the highest mean VO2 over 'vo2max_window' seconds of consecutive records.

cv_var

variable to estimate coefficient of variation. Can be: "VO2", "VCO2" or "RER".

author

author to estimate MFO. Can be: "Frayn", "Jeukendrup" or "Peronnet". "Jeukendrup" warns when an exercise step is outside 40-75 % VO2max, the range of Jeukendrup & Wallis (2005, Table 5); the resting row never warns.

VO2max

VO2max can be passed directly (ml/min) instead of using db_graded. If both are given, 'VO2max' is used as is and 'db_graded' is ignored with a warning.

method

method to locate MFO and Fatmax: "P3" (3rd-degree polynomial, default), "P2" (2nd-degree polynomial), "MV" (measured values) or "MRER" (RER method). See the section *Methods*.

step_duration

duration of each exercise step in seconds (default 180 = 3 min).

steady_state_duration

duration of steady-state period to average within each step, in seconds (default 60).

RER_cutoff

RER value above which steps are excluded (default NULL = no cutoff), as a number or by name, following Amaro-Gahete et al. (2019, Front Physiol 10: 909, doi:10.3389/fphys.2019.00909): "classic" = 1.00 (Cheneviere et al. 2009 averaged only steps with RER <= 1.0); "safe" = 0.95, the end-of-test RER they recommend as safe to capture MFO; "sedentary" = 0.93 and "trained" = 0.90, the mean RER at MFO they report in those groups. Low cutoffs leave few steps (SPEC 4, 19 finding 2).

negative_FAT

how to handle negative fat oxidation values: "truncate" (set to 0, default), "exclude" (remove from fitting), or "keep" (include in fitting).

body_mass

body mass in kg (optional, for relative MFO calculation).

fat_free_mass

fat-free mass in kg (optional, for FFM-relative MFO calculation).

HRmax

maximum heart rate in bpm (optional, for FatMax as %HRmax).

vo2max_window

averaging window in seconds for VO2max from 'db_graded' (default 30). If it is not a multiple of 'step_time', the number of records is rounded up (30 s with 20-s records uses 2 records = 40 s). See SPEC 9.5 and 14.

include_basal

logical. If 'TRUE', the resting row (from 'db_basal') enters 'MFO_db' as step 0 and is part of the curve fit, as in Montes de Oca et al. (2021, Eur J Sport Sci, doi:10.1080/17461391.2020.1788650). Default 'FALSE' fits exercise steps only and reproduces v0.1.0/v0.2.0, which dropped the resting row. With 'TRUE' the P3/P2 fit changes; the resting row has 'HR = NA' and 'Load = "basal"'. See SPEC 9.5.

energy_equivalents

kcal per g used for 'Kcal' and 'x_Kcal': "classic" (4 kcal/g CHO, 9 kcal/g FAT, as in v0.2.0) or "source" (values of the source of 'author': Peronnet & Massicotte 1991 for "Peronnet" and, as a package decision, "Frayn"; Jeukendrup & Wallis 2005, Table 5, for "Jeukendrup"). Default "classic" in 0.2.x; it will change to "source" in 0.3.0, and a message says so once per session when the argument is not set. MFO and Fatmax (g/min) do not depend on it. See [substrate_oxidation()] and SPEC 19 (finding 1).

use_incomplete_step

logical. Records left after the last complete step (fewer than 'step_duration' s) always give a warning stating "X s of Y s". With 'FALSE' (default, as in v0.2.0) they are dropped. With 'TRUE' that incomplete step is used only if it lasts at least 'steady_state_duration'; its last 'steady_state_duration' s are averaged, as for complete steps, although VO2 may not have reached its plateau. See SPEC 9.5.

min_steps

minimum number of exercise steps recommended for the fit (default 6). A warning is given when fewer steps remain after 'RER_cutoff' and 'negative_FAT = "exclude"'; the resting row of 'include_basal = TRUE' does not count. See SPEC 14.

Value

Returns a list which contains:

Quality warnings

Besides the warnings on inputs, 'MFO()' warns when the result needs checking (SPEC 9.5, 14):

Methods

Up to v0.2.0, '"MRER"' returned the step with the highest observed 1.7 (1 - RER) VO2, which is a measured-values method: use '"MV"' for that behaviour (same step; MFO from the 'author' equation). SPEC 19, finding 4.

Examples


## Not run: 
# Read dfs
data(list = c("basal_df", "MFO_df", "VO2max_df"), package = "MFO")

# Convert to data.frame
basal_df <- data.frame(basal_df)
MFO_df <- data.frame(MFO_df)
VO2max_df <- data.frame(VO2max_df)

# Calculate MFO and Fatmax (P3 method)
result_MFO <- MFO(step_time = 20,
                 db_MFO = MFO_df,
                 db_basal = basal_df,
                 db_graded = VO2max_df,
                 cv_var = "RER",
                 author = "Frayn",
                 method = "P3")

# With all options
result_MFO <- MFO(step_time = 20,
                 db_MFO = MFO_df,
                 db_basal = basal_df,
                 db_graded = VO2max_df,
                 cv_var = "RER",
                 author = "Frayn",
                 method = "P3",
                 RER_cutoff = 1.0,
                 body_mass = 75,
                 fat_free_mass = 60,
                 HRmax = 190)

## End(Not run)


MFO test dataframe

Description

Synthetic breath-by-breath data from an incremental exercise test with 8 stages (25-200 W, 3 min per stage). Simulates a moderately trained male cyclist covering ~20-75% VO2max. The fat oxidation pattern shows a clear inverted U-shape with a peak around 35-40% VO2max, suitable for demonstrating P3, P2, MRER, and SIN model fitting.

Usage

data(MFO_df)

Format

A data frame with 72 rows and 8 variables:

Time

time test, in seconds (20s intervals)

HR

heart rate, in beats/min

VO2

volume of oxygen consumption, in mL/min

VCO2

volume of exhaled carbon dioxide, in mL/min

RER

respiratory exchange ratio

BF

breathing frequency, in breaths/min

VE

ventilation, in L/min

PETCO2

end-tidal carbon dioxide pressure, in mmHg


Maximal Fat Oxidation Kinetics (SIN Model)

Description

Fit the SIN model (Cheneviere et al., 2009) to describe fat oxidation kinetics and extract three parameters: dilatation (d), symmetry (s), and translation (t). Also calculates Fatmin (the intensity where fat oxidation reaches zero), which is a unique capability of the SIN model over polynomial methods.

Usage

MFO_kinetics(MFO_data, fatmin_threshold = NULL)

Arguments

MFO_data

a data frame obtained from the MFO function (must contain FAT and porc_VO2 columns).

fatmin_threshold

'NULL' (default) for Fatmin at the second zero of the curve, or a proportion of MFO in (0, 1), e.g. 0.05, for the intensity above Fatmax where fat oxidation falls to that fraction of MFO (closed form: \[(pi + 2d) ((pi - asin(threshold)) / pi)^(1/s) - d - t\] / K). v0.2.0 used a fixed 5 % threshold searched on a grid.

Details

The model is equation \[14\] of Cheneviere et al. (2009, Med Sci Sports Exerc 41: 1615-1625, p. 1618, doi:10.1249/MSS.0b013e31819e2f91):

\%MFO = \sin\{[\pi^{1/s} / (\pi + 2d) \cdot (Kx + d + t)]^s\}, \quad K = \pi/100,

x in % VO2max. Up to v0.2.0 the package computed (pi^(1/s)/pi) + 2d, so its 'd' (and 't') are not comparable with published values (SPEC 6.5, 19).

Value

Returns a list which contains:

Fitting procedure

As in Cheneviere et al. (2009), the model is fitted by non-linear least squares (Levenberg-Marquardt, [minpack.lm::nlsLM()], start d = 0, s = 1, t = 0) directly to the observed rows of 'MFO_data', with fat oxidation expressed as a proportion of the highest observed value (%MFO / 100). There is no smoothing and no synthetic grid: v0.1.0/v0.2.0 fitted the SIN to a fine grid predicted by a third-degree polynomial, which inflated n and shrank the standard errors of d, s and t (SPEC 19, finding 7). R-squared and RMSE are computed on the observed proportions. At least 4 rows are needed for 3 parameters: with fewer the function warns and returns 'convergence = FALSE'; with 4 or 5 it fits but warns that the fit is unreliable and that at least 6 steps are recommended (SPEC 6.5).

'FatMax_SIN' and 'Fatmin' come from closed forms derived from eq. \[14\]: Fatmax = \[(pi + 2d) 2^(-1/s) - d - t\] / K and, by default, Fatmin = second zero of the curve = (pi + d - t) / K, Cheneviere's definition (SPEC 19, finding 3). Fatmin can lie beyond the last observed step (extrapolation).

Which rows are fitted

'MFO_kinetics()' fits every row it receives. Cheneviere et al. (2009) fitted exercise steps only, and averaged only steps with RER <= 1.0:

The SIN is sensitive to the last steps because the curve must reach zero: in the example data ('author = "Frayn"') the last step (74.7 % VO2max, RER 1.03) has negative fat oxidation.

Report which option was used when publishing SIN parameters.

Examples

## Not run: 
# Read dfs
data(list = c("basal_df", "MFO_df", "VO2max_df"), package = "MFO")

# Convert to data.frame
basal_df <- data.frame(basal_df)
MFO_df <- data.frame(MFO_df)
VO2max_df <- data.frame(VO2max_df)

# Calculate MFO and Fatmax
result_MFO <- MFO(step_time = 20,
                 db_MFO = MFO_df,
                 db_basal = basal_df,
                 db_graded = VO2max_df,
                 cv_var = "RER",
                 author = "Frayn")

# Calculate MFO Kinetics
result_kinetics <- MFO_kinetics(result_MFO$MFO_db)

# Fatmin at 5 % of MFO instead of the second zero
MFO_kinetics(result_MFO$MFO_db, fatmin_threshold = 0.05)$Fatmin

## End(Not run)


Maximal Fat Oxidation calculation of multiple databases

Description

Process multiple participants' MFO data in batch, generating plots and summary results. Supports all parameters from the MFO() and MFO_kinetics() functions.

Usage

MFOs(
  from = c("folder", "files"),
  path,
  db_basal_name,
  db_MFO_name,
  db_graded_name,
  step_time,
  cv_var,
  author,
  method = "P3",
  VO2max = NULL,
  step_duration = 180,
  steady_state_duration = 60,
  RER_cutoff = NULL,
  negative_FAT = "truncate",
  body_mass = NULL,
  fat_free_mass = NULL,
  HRmax = NULL,
  remove_rows = NULL,
  col_name_VO2 = "VO2",
  col_name_VCO2 = "VCO2",
  col_name_RER = "RER",
  col_name_HR = "HR",
  save_plot = TRUE,
  save_result = TRUE,
  output_format = "xlsx",
  vo2max_window = 30,
  include_basal = FALSE,
  fatmin_threshold = NULL,
  energy_equivalents = c("classic", "source"),
  use_incomplete_step = FALSE,
  min_steps = 6
)

Arguments

from

select "folder" (basal, MFO and graded databases of the same participant are stored in different files but in the same folder) or "files" (basal, MFO and graded databases of the same participant are stored in one file but in different sheets)

path

path to the folder with the databases

db_basal_name

name given to the basal database, e.g. "basal_df"

db_MFO_name

name given to the MFO database, e.g. "MFO_df"

db_graded_name

name given to the graded database, e.g. "VO2max_df"

step_time

how often the data was collected (in seconds).

cv_var

variable to estimate coefficient of variation. Can be: "VO2", "VCO2" or "RER".

author

equation set: "Frayn", "Jeukendrup" or "Peronnet".

method

method to locate MFO: "P3" (default), "P2", "MV" or "MRER". See [MFO()].

VO2max

VO2max can be passed directly (ml/min). Default NULL. If given, it is used for every participant and the graded databases are ignored.

step_duration

duration of each exercise step in seconds (default 180).

steady_state_duration

duration of steady-state period within each step in seconds (default 60).

RER_cutoff

RER value above which steps are excluded (default NULL), as a number or "classic", "safe", "sedentary", "trained". See [MFO()].

negative_FAT

how to handle negative fat oxidation: "truncate", "exclude", or "keep".

body_mass

body mass in kg (optional).

fat_free_mass

fat-free mass in kg (optional).

HRmax

maximum heart rate in bpm (optional).

remove_rows

rows to exclude from analysis.

col_name_VO2

column name for VO2 in databases. Default "VO2".

col_name_VCO2

column name for VCO2 in databases. Default "VCO2".

col_name_RER

column name for RER in databases. Default "RER".

col_name_HR

column name for HR in databases. Default "HR".

save_plot

save plots to MFO_plots/ directory (default TRUE).

save_result

save results file (default TRUE).

output_format

format for results file: "xlsx", "csv", or "rds". Default "xlsx".

vo2max_window

averaging window in seconds for VO2max from the graded database (default 30). See [MFO()].

include_basal

logical; include the resting row as step 0 in the fit (default 'FALSE'). See [MFO()].

fatmin_threshold

'NULL' (default, Fatmin at the second zero of the SIN) or a proportion of MFO in (0, 1). See [MFO_kinetics()].

energy_equivalents

"classic" (default in 0.2.x) or "source". See [MFO()].

use_incomplete_step

logical; use an incomplete last step that holds the steady-state segment (default 'FALSE'). See [MFO()].

min_steps

minimum number of exercise steps recommended for the fit (default 6); fewer give a warning. See [MFO()].

Details

Every warning raised for a participant is re-issued with the participant ID first (e.g. '"P02: No HR data in db_MFO: ..."'), so the quality warnings of [MFO()] (missing HR, few steps, Fatmax at the edge, incomplete last step) can be traced in a batch. A participant whose data cannot be processed gives a row of 'NA' and a warning with the error.

Value

Data frame with one row per participant containing:

Examples


## Not run: 
# MFOs function
results <- MFOs(from = "folder",
                path = "/path/to/participant_data",
                db_basal_name = "basal_df",
                db_MFO_name = "MFO_df",
                db_graded_name = "graded_df",
                step_time = 20,
                cv_var = "RER",
                author = "Frayn",
                method = "P3")

## End(Not run)


Graded exercise test dataframe

Description

Synthetic breath-by-breath data from a graded exercise test to exhaustion (10 stages, 75-300 W). Simulates a moderately trained male cyclist reaching VO2max ~3200 mL/min and HRmax ~185 bpm. Used to determine VO2max when this value is not known a priori.

Usage

data(VO2max_df)

Format

A data frame with 30 rows and 9 variables:

Time

time test, in seconds (20s intervals)

HR

heart rate, in beats/min

Load

workload, in watts

VO2

volume of oxygen consumption, in mL/min

VCO2

volume of exhaled carbon dioxide, in mL/min

RER

respiratory exchange ratio

BF

breathing frequency, in breaths/min

VE

ventilation, in L/min

PETCO2

end-tidal carbon dioxide pressure, in mmHg


Basal test dataframe

Description

Synthetic breath-by-breath data from a 15-minute resting metabolic assessment. Simulates a moderately trained male cyclist (VO2max ~3200 mL/min) with an initial stabilization period followed by steady-state resting metabolism.

Usage

data(basal_df)

Format

A data frame with 45 rows and 8 variables:

Time

time test, in seconds (20s intervals)

HR

heart rate, in beats/min

VO2

volume of oxygen consumption, in mL/min

VCO2

volume of exhaled carbon dioxide, in mL/min

RER

respiratory exchange ratio

BF

breathing frequency, in breaths/min

VE

ventilation, in L/min

PETCO2

end-tidal carbon dioxide pressure, in mmHg


Calculate step means from raw data

Description

Generalized function that works with any step_time and step_duration. Extracts the last minute of each step (steady-state) for averaging.

Usage

calculate_vars(
  step_time,
  db_MFO,
  VO2max,
  author,
  step_duration = 180,
  steady_state_duration = 60,
  RER_cutoff = NULL,
  energy_equivalents = "classic",
  use_incomplete_step = FALSE
)

Arguments

step_time

how often the data was collected (in seconds).

db_MFO

data frame with MFO test (columns: VO2, VCO2, RER, HR).

VO2max

maximum oxygen uptake in ml/min.

author

equation set: "Frayn", "Jeukendrup", or "Peronnet".

step_duration

duration of each exercise step in seconds (default 180 = 3 min).

steady_state_duration

duration of steady-state period to average within each step, in seconds (default 60 = last minute).

RER_cutoff

RER value above which data is excluded (default NULL = no cutoff), or one of "classic", "safe", "sedentary", "trained" (see [MFO()]).

energy_equivalents

"classic" (default) or "source"; see [substrate_oxidation()].

use_incomplete_step

logical (default 'FALSE'). See [MFO()].

Details

With 'author = "Jeukendrup"', a single warning lists the steps (after 'RER_cutoff') whose intensity is outside 40-75 % VO2max, the range of the Jeukendrup & Wallis (2005, Table 5) equations (SPEC 3, 9.5).

Value

data frame with columns: VO2, VCO2, HR, RER, CHO, FAT, Kcal (one row per step)


Fat oxidation with the RER simplification (MRER)

Description

FAT (mg/min) = 1.7 (1 - RER) VO2 (ml/min), the simplification of the Peronnet & Massicotte (1991) equation used by Perez-Martin et al. (2001) and Cheneviere et al. (2009, p. 1617). Used by 'method = "MRER"' in [MFO()].

Usage

fat_oxidation_MRER(VO2, RER)

Arguments

VO2

vector of oxygen consumption values (ml/min)

RER

vector of respiratory exchange ratio values

Value

fat oxidation rate in g/min


Basal metabolic rate

Description

Calculate basal metabolism from rest test data using a rolling window to identify the most metabolically stable 5-minute period.

Usage

met_basal(
  step_time,
  db,
  cv_var,
  window_duration = 300,
  author = "Frayn",
  energy_equivalents = "classic"
)

Arguments

step_time

how often the data was collected (in seconds).

db

a data frame with columns VO2, VCO2, RER, HR.

cv_var

variable to calculate coefficient of variation: "VO2", "VCO2" or "RER".

window_duration

duration of the stable window in seconds (default 300 = 5 min).

author

equation set used for resting substrate oxidation: "Frayn" (default), "Jeukendrup" or "Peronnet". 'MFO()' passes its own 'author', so resting and exercise values use the same equations (SPEC 9.5). Jeukendrup & Wallis (2005) propose no resting equation; with "Jeukendrup" the package uses the 100 % glucose pair derived from their Table 4 (see [substrate_oxidation()], 'phase = "rest"').

energy_equivalents

"classic" (default) or "source"; see [substrate_oxidation()].


Preprocess breath-by-breath data into fixed intervals

Description

Averages breath-by-breath data into fixed time intervals.

Usage

preprocess_bxb(db, target_interval = 20)

Arguments

db

data frame with breath-by-breath data. Must contain a "Time" column in seconds and columns for VO2, VCO2, RER, HR.

target_interval

target interval in seconds (default 20).

Value

data frame with data averaged into fixed intervals.


Read databases for MFO package

Description

Read databases for MFO package

Usage

read_MFO_databases(
  from = c("folder", "files"),
  path,
  db_basal_name,
  db_MFO_name,
  db_graded_name,
  col_name_VO2 = "VO2",
  col_name_VCO2 = "VCO2",
  col_name_RER = "RER",
  col_name_HR = "HR",
  remove_rows = NULL
)

Arguments

from

select either from "folder" or "files"

path

path to the databases

db_basal_name

name of the database with the basal metabolic rate test

db_MFO_name

name of the database of MFO test

db_graded_name

name of the database of the graded exercise test

col_name_VO2

name given to the variable VO2 in the databases. Default "VO2"

col_name_VCO2

name given to the variable VCO2 in the databases. Default "VCO2"

col_name_RER

name given to the variable RER in the databases. Default "RER"

col_name_HR

name given to the variable HR in the databases. Default "HR"

remove_rows

An integer (or a vector of integers) representing the position of the rows to delete

Value

Returns 3 databases:


Substrate oxidation equations

Description

Calculate fat and carbohydrate oxidation from VO2 and VCO2 using different stoichiometric equations.

Usage

substrate_oxidation(
  VO2,
  VCO2,
  author,
  VO2max = NULL,
  phase = c("exercise", "rest"),
  energy_equivalents = c("classic", "source")
)

Arguments

VO2

oxygen consumption in ml/min (scalar or vector)

VCO2

carbon dioxide production in ml/min (same length as VO2)

author

equation set: "Frayn", "Jeukendrup", or "Peronnet"

VO2max

maximum oxygen uptake in ml/min (required for Jeukendrup during exercise)

phase

"exercise" (default) or "rest". Only changes "Jeukendrup".

energy_equivalents

kcal per g of CHO and FAT used for 'Kcal': "classic" (default, 4 and 9) or "source" (values of the equation's source, see Details).

Details

Rule: a coefficient printed in the source is used as printed; a derived coefficient is used only where the source prints no equation (SPEC 19).

**Frayn (1983)** (J Appl Physiol 55: 628-634, eqs. 5 and 6, n = 0), as printed: CHO = 4.55 VCO2 - 3.21 VO2, FAT = 1.67 VO2 - 1.67 VCO2. Deriving them from his Table 1 constants (glucose 0.746/0.746, fat 2.03/1.43 L/g) gives 4.535/3.195 and 1.667/1.667; this is only a cross-check in the tests.

**Peronnet & Massicotte (1991)** (Can J Sport Sci 16: 23-29) print no equations. The package derives them from the O2/CO2 constants of their Table 1 (glucose 0.7455/0.7426, fatty acid 2.0092/1.4136 L/g) by solving VO2 = 0.7455 CHO + 2.0092 FAT and VCO2 = 0.7426 CHO + 1.4136 FAT for CHO and FAT: CHO = 4.58519 VCO2 - 3.22597 VO2, FAT = 1.69469 VO2 - 1.70130 VCO2. Up to v0.2.0 the rounded 4.585 / 3.2255 / 1.6946 / 1.7012 were used (SPEC 19, finding 6).

**Jeukendrup & Wallis (2005)** (Int J Sports Med 26 Suppl 1: S28-S37), with the coefficients printed in their Table 5:

With 'phase = "rest"' there is no printed equation: Jeukendrup & Wallis propose none. As a package decision (pure glucose, no glycogen use at rest), the pair is derived from the O2/CO2 constants of their Table 4 (glucose 0.7455/0.7426, fatty acid 2.0092/1.4136 L/g) by solving the 2x2 system: CHO = 4.58519 VCO2 - 3.22597 VO2, FAT = 1.69469 VO2 - 1.70130 VCO2. The same derivation with 50/50 and 20/80 glucose/glycogen reproduces the Table 5 coefficients within 0.005 and is kept as a cross-check in the tests.

The Table 5 sets are defined for 40-75 % VO2max only. Below 40 % the 40-50 % set and above 75 % the 50-75 % set are extrapolated; [calculate_vars()] warns when an exercise step falls outside the range (SPEC 3, 9.5). Resting values ('phase = "rest"') never trigger that warning.

**Energy equivalents** ('Kcal = CHO * e_cho + FAT * e_fat', SPEC 19, finding 1):

Value

Named list with CHO (g/min), FAT (g/min), Kcal (kcal/min)