5

There are a few popular running training-pace calculators online that all use the same underlying equations:

All underlying calculators are the same: the user enter's their best time for a given distance; and the calculators provide different recommended training paces for: 1) easy runs; tempo / lactate threshold workouts; VO2 max workouts; speed workouts; Yasso 800s; and long runs.

Given that the results are the same for all sites; I know the underlying equations are the same. But what are the equations?

--Edit--

I found the Riegel equation, used to predict the time for a given distance using the time already run for a different distance. This equation might have been used for the above calculators, but I think it is unlikely because the training distances are very short in comparison to the endurance race distances the Riegel method was designed for.

I also don't know what distances the "VO2 max" or "speed form" paces were designed for, so I do not know what distance to enter into the Reigel equation. If I go with the assumption that "VO2 max" pace is good for 800m intervals, things still don't match the calculator results. See example Python code below.

def riegel(t1, d1, d2):
    t1_seconds = t1*60
    d1_meters = d1 * 1000
    d2_meters = d2 * 1000
    t2_seconds = t1_seconds*(d2_meters/d1_meters)**1.06
    v = d2_meters/t2_seconds
    v_minutes_per_km = 1/(v*60/1000)
    minutes = int(v_minutes_per_km)
    seconds = (v_minutes_per_km*60) % 60
    return minutes, seconds

# use the function
t1 = 40  # minutes
d1 = 10  # kilometers
d2 = 0.8 # kilometers for interval distance
interval_pace = riegel(t1, d1, d2)

the output interval pace is 3min26s/km, however the calculators return a pace of 3min43s/km.

1 Answer 1

3

I don't know anything about running; I haven't gone on a run in 5+ years. However, when inspecting the Calculate button from Runner's World, the site runs a function called runConversion(). A quick Ctrl+F for that in the source gives us script:

<script language="JavaScript">
    <!--

    var metric;
    var VO2max;

    function initGlobals() {
        metric = false;
        VO2Max = -1;
    }

    function runConversion() {
        var frm = document.forms.input1;
        // race time in min, length in m and speed in m/min.
        var time = document.forms.input1.hours.value * 60 + document.forms.input1.minutes.value * 1 + document.forms.input1.seconds.value / 60;
        var rlength = document.forms.input1.length.value;
        var speed;

        if (time <= 0 || isNaN(time)) {
            alert('Please input a valid time');
            return;
        }

        if (rlength <= 0 || isNaN(rlength)) {
            alert('Please input a valid race length.');
            return;
        }

        if (frm.units.options[0].selected) {
            rlength *= 1000;
        } else {
            rlength *= 1609;
        }

        speed = rlength / time;

        VO2Max = velToVO2(speed) / timeToPercentVO2Max(time);
        makeCalculations();
    }

    function makeCalculations() {

        if (VO2Max <= 0) {
            return;
        }

        var velEasy = VO2ToVel(VO2Max * .7);
        var velTempo = VO2ToVel(VO2Max * .88);
        var velMaximum = VO2ToVel(VO2Max);
        var velSpeed = VO2ToVel(VO2Max * 1.1);
        var velxlong = VO2ToVel(VO2Max * .6);
        var velYasso = velMaximum * 1.95;

        var toAppend;
        if (metric) {
            toAppend = ' min/km';
        } else {
            toAppend = ' min/mile';
        }

        var frm = document.forms.input1;

        frm.easy.value = '' + timeConvert(velEasy) + toAppend;
        frm.tempo.value = '' + timeConvert(velTempo) + toAppend;
        frm.maximum.value = '' + timeConvert(velMaximum) + toAppend;
        frm.speed.value = '' + timeConvert(velSpeed) + toAppend;
        frm.xlong.value = '' + timeConvert(velEasy) + ' - ' + timeConvert(velxlong) + toAppend;
        var oldMetric = metric;
        metric = false;
        frm.yasso.value = '' + timeConvert(velYasso) + ' min/800';
        metric = oldMetric;
    }

    // Toggle output type of paces.
    function toggleMetric() {
        if (document.forms.input1.paceType.options[0].selected) {
            metric = false;
        } else {
            metric = true;
        }
        makeCalculations();
    }

    // Takes a velocity and converts it to a VO2 level.   
    function velToVO2(vel) {
        return (-4.60 + 0.182258 * vel + 0.000104 * vel * vel);
    }

    // Takes a VO2 measurement and converts it to a velocity.
    function VO2ToVel(VO2) {
        return (29.54 + 5.000663 * VO2 - 0.007546 * VO2 * VO2);
    }

    // Takes a time in minutes and uses EQ 2 to convert it to a percent of VO2 maximum.   
    function timeToPercentVO2Max(minutes) {
        return (.8 + 0.1894393 * Math.exp(-.012778 * minutes) + 0.2989558 * Math.exp(-.1932695 * minutes));
    }

    // Takes a speed in metres / minute a converts it to a string representing a pace in
    // minutes per mile or km.   
    function timeConvert(speed) {
        var ans;
        if (!metric) {
            ans = (1 / speed) * 1609;
        } else {
            ans = (1 / speed) * 1000;
        }
        minutes = Math.floor(ans);
        seconds = Math.floor((ans - minutes) * 60);
        if (seconds > 9) {
            return '' + minutes + ':' + seconds;
        } else {
            return '' + minutes + ':0' + seconds;
        }
    }
    // -->
</script>

When dissecting this a bit, it looks like every estimate is your calculated V02Max multiplied by varying coefficients. This is then calculated back into a velocity to give you a pace. The calculator found at Helpful Runner gives the same results because it's the identical code pushed into a file.

The V02Max coefficients are:

  • Easy = 70% * VO2Max
  • Tempo = 88% * V02Max
  • Maximum = 100% * VO2Max
  • Speed = 110% * VO2Max
  • xlong = 60% * VO2Max
  • Yasso = Maximum Velocity * 1.95

I'm having a bit more trouble dissecting Omni Calculator but they calculate velocity_to_V02 using the exact same formula so I'd take a guesstimation that the rest is the same formulae just re-packaged:

from: training-pace.js

'use strict';

omni.onResult(['x'], function(ctx, x) {
  ctx.hideVariables('base1', 'base2', 'percent_max',
                    'vo2max', 'vo2', 'easy_vo2','x','y','velocity', 'velocity_easy', 
                    'velocity_vo2max','velocity_tempo', 'velocity_speed', 'velocity_long',
                    'speed_vo2','tempo_vo2', 'long_vo2' );
});
/*
omni.onInit(function(ctx) {
  var countryCode = ctx.getCountryCode();
  if (countryCode === 'US') {
    ctx.setDefault('result_units_type', '1');
  } else {
    ctx.setDefault('result_units_type', '0');
  }
});



omni.define('velocity_to_vo2', function(_velocity) {
  var velocity = _velocity.toNumber();
  return (-4.60 + 0.182258 * velocity + 0.000104 * velocity * velocity);
});

omni.define('time_convert', function(_trainingVelocity, _useImperial) {
  var trainingVelocity = _trainingVelocity.toNumber();
  var useImperial = _useImperial.toNumber();
  var trainingPace;
  if (useImperial) {
    trainingPace = (1 / trainingVelocity) * 1609;
  } else {
    trainingPace = (1 / trainingVelocity) * 1000;
  }
  return trainingPace;
});
*/

Unfortunately, I can't find anywhere that specifies that these are the coefficients to be used. No one has really set in stone that an easy run should be 70% of your V02Max. I find a lot of websites that list ranges (i.e. 65-75%). My guess is that this code is simply being passed from one site to the next.

1
  • 1
    If anyone has academic sources or wants to tack something onto this I/you/we can gladly make a community wiki. Running is out of my element but I hope this helps or guides you in the right direction.
    – C. Lange
    Commented Aug 30, 2020 at 18:07

Not the answer you're looking for? Browse other questions tagged or ask your own question.