running-tools

A collection of tools for runners and their coaches
git clone https://git.ashermorgan.net/running-tools/
Log | Files | Refs | README

commit d3917d0ae528d5deeeaf188bce6ca90f8fe50c69
parent 76f513a5d4ee7351051f06209ec192c414d0c62e
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Thu,  5 Sep 2024 20:02:26 -0700

Update VO2 Max model constants

Diffstat:
Msrc/utils/races.js | 16++++++++--------
Mtests/e2e/batch-calculator.spec.js | 4++--
Mtests/e2e/cross-calculator.spec.js | 6+++---
Mtests/e2e/race-calculator.spec.js | 4++--
Mtests/e2e/workout-calculator.spec.js | 2+-
Mtests/unit/views/RaceCalculator.spec.js | 2+-
6 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/src/utils/races.js b/src/utils/races.js @@ -5,19 +5,18 @@ * @param {Function} method The function * @param {Function} derivative The function derivative * @param {Number} precision The acceptable precision - * @param {Number} iterations The maximum number of iterations * @returns {Number} The refined estimate */ -function NewtonsMethod(initialEstimate, target, method, derivative, precision, iterations = 500) { +function NewtonsMethod(initialEstimate, target, method, derivative, precision) { // Initialize estimate let estimate = initialEstimate; let estimateValue; - for (let i = 0; i < iterations; i += 1) { + for (let i = 0; i < 500; i += 1) { // Evaluate function at estimate estimateValue = method(estimate); - // Check if estimate is close enough + // Check if estimate is close enough (usually occurs way before i = 500) if (Math.abs(target - estimateValue) < precision) { break; } @@ -174,6 +173,7 @@ const PurdyPointsModel = { /* * Methods that implement the VO2 Max race prediction model * http://run-down.com/statistics/calcs_explained.php + * https://vdoto2.com/Calculator */ const VO2MaxModel = { /** @@ -185,7 +185,7 @@ const VO2MaxModel = { getVO2(d, t) { const minutes = t / 60; const v = d / minutes; - const result = -4.6 + (0.182 * v) + (0.000104 * (v ** 2)); + const result = -4.6 + (0.182258 * v) + (0.000104 * (v ** 2)); return result; }, @@ -196,7 +196,7 @@ const VO2MaxModel = { */ getVO2Percentage(t) { const minutes = t / 60; - const result = 0.8 + (0.189 * Math.exp(-0.0128 * minutes)) + (0.299 * Math.exp(-0.193 + const result = 0.8 + (0.189439 * Math.exp(-0.012778 * minutes)) + (0.298956 * Math.exp(-0.193261 * minutes)); return result; }, @@ -237,7 +237,7 @@ const VO2MaxModel = { */ predictTime(d1, t1, d2) { // Calculate input VO2 max - const inputVO2 = VO2MaxModel.getVO2Max(d1, t1); + const inputVO2Max = VO2MaxModel.getVO2Max(d1, t1); // Initialize estimate let estimate = (t1 * d2) / d1; @@ -245,7 +245,7 @@ const VO2MaxModel = { // Refine estimate const method = (x) => VO2MaxModel.getVO2Max(d2, x); const derivative = (x) => VO2MaxModel.VO2MaxTimeDerivative(d2, x); - estimate = NewtonsMethod(estimate, inputVO2, method, derivative, 0.0001); + estimate = NewtonsMethod(estimate, inputVO2Max, method, derivative, 0.0001); // Return estimate return estimate; diff --git a/tests/e2e/batch-calculator.spec.js b/tests/e2e/batch-calculator.spec.js @@ -36,7 +36,7 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41.21'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.91'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.90'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row')).toHaveCount(16); @@ -107,7 +107,7 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:14.60'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:43.61'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:43.62'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row')).toHaveCount(16); diff --git a/tests/e2e/cross-calculator.spec.js b/tests/e2e/cross-calculator.spec.js @@ -149,10 +149,10 @@ test('Cross-calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m @ 5 km'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41.93'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41.92'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.98'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.97'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row')).toHaveCount(16); @@ -201,6 +201,6 @@ test('Cross-calculator', async ({ page }) => { // Assert workout splits are correct (input race and prediction model not reset) await expect(page.getByRole('row').nth(1)).toHaveText('400 m @ 1 mi' + '1:14.81'); - await expect(page.getByRole('row').nth(3)).toHaveText('1600 m @ 1:00:00' + '5:53.58'); + await expect(page.getByRole('row').nth(3)).toHaveText('1600 m @ 1:00:00' + '5:53.56'); await expect(page.getByRole('row')).toHaveCount(5); }); diff --git a/tests/e2e/race-calculator.spec.js b/tests/e2e/race-calculator.spec.js @@ -25,7 +25,7 @@ test('Race Calculator', async ({ page }) => { // Assert race predictions are correct await expect(page.getByRole('row').nth(5)).toHaveText('1 mi' + '4:55.53' + '4:56 / mi'); - await expect(page.getByRole('row').nth(10)).toHaveText('5 km' + '16:47.58' + '5:24 / mi'); + await expect(page.getByRole('row').nth(10)).toHaveText('5 km' + '16:47.57' + '5:24 / mi'); await expect(page.getByRole('row')).toHaveCount(17); // Assert race statistics are correct @@ -40,7 +40,7 @@ test('Race Calculator', async ({ page }) => { // Assert race predictions are correct await expect(page.getByRole('row').nth(5)).toHaveText('1 mi' + '4:55.53' + '3:04 / km'); - await expect(page.getByRole('row').nth(10)).toHaveText('5 km' + '16:47.58' + '3:22 / km'); + await expect(page.getByRole('row').nth(10)).toHaveText('5 km' + '16:47.57' + '3:22 / km'); await expect(page.getByRole('row')).toHaveCount(17); // Change prediction model diff --git a/tests/e2e/workout-calculator.spec.js b/tests/e2e/workout-calculator.spec.js @@ -24,7 +24,7 @@ test('Workout Calculator', async ({ page }) => { // Assert workout splits are correct await expect(page.getByRole('row').nth(1)).toHaveText('400 m @ 1 mi' + '1:13.45'); - await expect(page.getByRole('row').nth(3)).toHaveText('1600 m @ 1:00:00' + '5:45.44'); + await expect(page.getByRole('row').nth(3)).toHaveText('1600 m @ 1:00:00' + '5:45.43'); await expect(page.getByRole('row')).toHaveCount(5); // Change prediction model diff --git a/tests/unit/views/RaceCalculator.spec.js b/tests/unit/views/RaceCalculator.spec.js @@ -119,7 +119,7 @@ test('should correctly calculate race statistics', async () => { // Assert race statistics are correct expect(purdyPoints).to.equal('Purdy Points: 454.5'); - expect(vo2).to.equal('V̇O₂: 47.4 ml/kg/min (95.3% of max)') + expect(vo2).to.equal('V̇O₂: 47.5 ml/kg/min (95.3% of max)') expect(vo2Max).to.equal('V̇O₂ Max: 49.8 ml/kg/min') });