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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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