TM1.0
How TrailMath rates a course
Every TrailMath number on this site - the effort-km on a race page, the reference time beside it, the index a calculator estimates - comes from one frozen, versioned model. This page is that model: the formulas, every parameter and where it came from, and the tables that show where it is wrong. Current version: TM1.0, fitted 2026-09-25.
Effort-km: the size of a course
effort-km = distance_km + gain_m / 100 + loss_m / 200. The uphill term is ITRA's public km-effort exactly, so an ITRA figure and a TrailMath figure differ only by the descent term. When an organiser publishes no descent, loss is taken equal to gain and the number is marked as assumed.
The descent divisor of 200 is a published convention, not a fitted value. Nothing in the data TrailMath may fit on - twenty-four public results with gain-only course figures, and no split times - identifies a descent cost, and the two values previously in use here (150 on the site, 250 in the app) were both unfitted guesses. Minetti's energy-cost curve deliberately does not enter: it is an energy cost, and as a time cost it has the wrong sign on descents.
Worked examples, from the official figures: UTMB Mont-Blanc is 324.0 effort-km (174 km, +10,000 m, -10,000 m); CCC (Courmayeur-Champex-Chamonix) is 201.7 effort-km.
The TrailMath Index: how fast a runner is
index = a + b · ln(v · κ) + c · ln(effort-km), where v is effort-km per hour and κ is the course's terrain-class factor. Solved for time: ln t = (1 + c/b) · ln(effort-km) + (a - index)/b + ln κ, so finish time scales as effort-km to the power 1.225 - one continuous exponent, published, instead of the classic road value of 1.06.
The coefficients are an ordinary least-squares fit to 20 public results whose runners carry a public UTMB category index, with 4 further results held out and never fitted. Fitted values: a = -1385.575779, b = 561.716376, c = 126.161787. One hundred index points are a factor of 1.19 in time.
The error that matters is out of sample. Leave-one-out over the training results: 65.9 points. Leaving out every result of one runner: 66.7. Leaving out every result on one course: 66.7. The largest of these, 66.7, is the number every range on this site is built from. In-sample error is 51.3; it is reported, not used.
Below 50 effort-km the fit is extrapolating (two results, one of them the worst-fitting row), so no reference time is quoted there. Every time in the model is in hours.
Terrain classes
κ is one number per terrain class, fixed before the fit and normalised so that mountain = 1: road to_trail 0.88, forest 0.90, trail 0.93, mountain 1.00, ultra mountain 1.05. It carries everything a course brings that effort-km does not - typical altitude, weather and night exposure, ground - as one average per class. A per-race factor needs results the model does not yet have and is not published.
The classes are the ones the TrailMath app uses for its catalogue, assigned from gain per kilometre: trail 10-45 m/km, mountain 30-80, and ultra mountain from 45 m/km on courses of at least 100 km.
The reference time
The reference time on a race page is the model at index 500 with the class factor and no day-specific conditions. Index 500 is defined as the median finisher of a UTMB World Series ultra: over the timed editions of UTMB, CCC, OCC, TDS and Val d'Aran in the table below, the median finisher implies an index of 536.
UTMB Mont-Blanc: about 43 h. CCC (Courmayeur-Champex-Chamonix): about 24 h. These are descriptions of a runner of that ability, not a pacing plan, and never a guarantee against a cutoff.
Every parameter
| model_version | 1.0.0 | displayed as TM1.0; a refit bumps the minor, a formula change the major |
|---|---|---|
| gain_divisor | 100 | ITRA's public km-effort, held fixed for compatibility |
| loss_divisor | 200 | published convention, not a fit: between the two unfitted in-house values (150 web, 250 app); no time-bearing data we may fit on identifies a descent cost |
| a (intercept) | -1385.575779 | least squares on the training results |
| b (speed coefficient) | 561.716376 | same fit; 100 index points = ×1.195 in time |
| c (effort coefficient) | 126.161787 | same fit |
| exponent 1 + c/b | 1.2246 | the Riegel-style exponent on effort-km |
| rmse (in sample) | 51.34 | reported, not used |
| rmse (leave one out) | 65.90 | training results, one row at a time |
| rmse (leave one runner out) | 66.73 | runners repeat in the table |
| rmse (leave one course out) | 66.72 | courses repeat in the table |
| rmse (out of sample) | 66.73 | the largest of the three; what every range is built from |
| min_effort_km | 50 | below this the index is extrapolated and no reference time is shown |
| reference_index | 500 | the median finisher of a UTMB World Series ultra |
| κ by class | road_to_trail 0.88 · forest 0.9 · trail 0.93 · mountain 1 · ultra_mountain 1.05 | mountain = 1 |
| anchors | 20 fitted + 4 held out | hash 05e114a59751; fitted 2026-09-25 |
The results the index is fitted to
Each row is one public result and the public UTMB category index its runner carries. Loss is assumed equal to gain on every row - no runner page publishes descent - and is marked. The residual is the runner's index minus the model's, with that row left out of the fit (for held-out rows, against the full fit). Positive means the runner scored above the curve.
| Race | Year | Course | Class | Effort-km | Time (h) | Index | Residual |
|---|---|---|---|---|---|---|---|
| OCC - HOKA UTMB Mont-Blanc | 2025 | 57 km · +3,500 · −3,500 (assumed) | mountain | 109.5 | 5.01 | 918 | -25 |
| Sierre-Zinal | 2024 | 31.3 km · +2,190 · −2,190 (assumed) | mountain | 64.2 | 2.43 | 945 | -50 |
| UTMB Mont-Blanc | 2025 | 174.2 km · +9,920 · −9,920 (assumed) | ultra mountain | 323.0 | 19.32 | 957 | +5 |
| Western States 100 | 2026 | 162.2 km · +5,790 · −7,238 (assumed) | trail | 256.3 | 13.77 | 943 | +31 |
| WMTRC Long Trail 82km | 2025 | 81.2 km · +5,413 · −5,413 (assumed) | mountain | 162.4 | 8.59 | 959 | +57 |
| WMTRC Long Trail 82km | 2025 | 81.2 km · +5,413 · −5,413 (assumed) | mountain | 162.4 | 9.97 | 859 | +37 |
| Chianti Ultra Trail | 2026 | 118.9 km · +5,121 · −5,121 (assumed) | mountain | 195.7 | 11.53 | 859 | -13 |
| Hardrock 100 | 2025 | 157.7 km · +9,720 · −9,720 (assumed) | ultra mountain | 303.5 | 24.07 | 887 | +115 |
| UTMB Mont-Blancheld out | 2025 | 174.2 km · +9,920 · −9,920 (assumed) | ultra mountain | 323.0 | 22.94 | 812 | -44 |
| OCC - HOKA UTMB Mont-Blanc | 2024 | 57 km · +3,500 · −3,500 (assumed) | mountain | 109.5 | 6.41 | 741 | -64 |
| OCC - HOKA UTMB Mont-Blanc | 2024 | 57 km · +3,500 · −3,500 (assumed) | mountain | 109.5 | 6.22 | 811 | -7 |
| Lavaredo 80Kheld out | 2026 | 80 km · +4,600 · −4,600 (assumed) | mountain | 149.0 | 10.41 | 725 | -16 |
| Tatra Fest Bieg | 2025 | 68 km · +5,000 · −5,000 (assumed) | mountain | 143.0 | 8.24 | 846 | +2 |
| Istria 100 by UTMB - 168K | 2026 | 168.2 km · +7,140 · −7,140 (assumed) | mountain | 275.3 | 22.12 | 712 | -31 |
| UTMB Mont-Blanc | 2025 | 174.2 km · +9,920 · −9,920 (assumed) | ultra mountain | 323.0 | 28.51 | 742 | +9 |
| Speed'Goat Trail des Geantsheld out | 2026 | 132 km · +8,024 · −8,024 (assumed) | ultra mountain | 252.4 | 39.93 | 356 | -19 |
| Ultra Trail du Vercors - Solo | 2025 | 82 km · +4,400 · −4,400 (assumed) | mountain | 148.0 | 18.94 | 366 | -49 |
| Trail de la Cochette | 2025 | 42 km · +2,200 · −2,200 (assumed) | mountain | 75.0 | 6.22 | 470 | -100 |
| Alpi Trail de Pichauris | 2025 | 21 km · +1,100 · −1,100 (assumed) | mountain | 37.5 | 2.55 | 549 | -44 |
| Trans Aubrac TA 105 | 2026 | 108 km · +3,500 · −3,500 (assumed) | trail | 160.5 | 14.65 | 550 | -10 |
| SKY - Baqueira Beret | 2026 | 18 km · +900 · −900 (assumed) | mountain | 31.5 | 2.68 | 565 | +198 |
| Ultra Trail du Puy Mary Aurillac - Solo | 2026 | 114 km · +5,500 · −5,500 (assumed) | ultra mountain | 196.5 | 18.27 | 581 | -69 |
| Javelina Jundredheld out | 2024 | 159.2 km · +1,810 · −1,810 (assumed) | trail | 186.4 | 19.76 | 594 | +100 |
| EcoTrail Stockholm 45km | 2025 | 45.2 km · +1,250 · −1,250 (assumed) | trail | 64.0 | 4.47 | 621 | +33 |
Where the model and the results disagree
For every timed edition in the results library: the reference time at index 500, the observed median, and the index a runner finishing at that median would carry. This is not a fit target - a slow median can be a hard course or a weak field, and nothing here can tell the two apart. Rows outside 420-600 are flagged (10 of 30); each is a case for a future per-race factor, once enough results exist to fit one.
| Race | Year | Class | Effort-km | Reference (h) | Median (h) | Ratio | Implied index | Finishers |
|---|---|---|---|---|---|---|---|---|
| CCC (Courmayeur-Champex-Chamonix) | 2026 | ultra mountain | 201.7 | 24.3 | 22.2 | 0.91 | 551 | 1,512 |
| Chiang Mai Thailand by UTMB - Chiang Dao 160 | 2025 | ultra mountain | 313.7* | 41.7 | 42.0 | 1.01 | 497 | 367 |
| Chiang Mai Thailand by UTMB - Elephant 100 | 2025 | ultra mountain | 172.8* | 20.1 | 22.1 | 1.10 | 448 | 974 |
| Diagonale des Fous (Grand Raid Réunion) | 2025 | ultra mountain | 335.5* | 45.3 | 51.0 | 1.12 | 434 | 2,064 |
| MaXi-Race Demi tOur du Lac | 2026 | mountain | 108.8* | 10.9 | 9.5 | 0.87 | 575 | 1,244 |
| MaXi-Race Marathon-eXperience | 2026 | mountain | 68.0* | 6.1 | 5.6 | 0.92 | 549 | 1,259 |
| MaXi-Race tOur du Lac Soloflagged | 2026 | ultra mountain | 186.5* | 22.1 | 18.2 | 0.83 | 607 | 1,418 |
| OCC (Orsières-Champex-Chamonix) | 2026 | mountain | 111.9 | 11.3 | 11.4 | 1.01 | 494 | 1,556 |
| SaintéLyon | 2025 | trail | 109.1* | 10.1 | 11.0 | 1.08 | 455 | 6,152 |
| SkyRun 100flagged | 2024 | mountain | 160.7 | 17.5 | 25.6 | 1.46 | 288 | 125 |
| SkyRun 65flagged | 2024 | mountain | 112.2 | 11.3 | 16.4 | 1.45 | 291 | 96 |
| TDS (Sur les Traces des Ducs de Savoie) | 2026 | ultra mountain | 288.4 | 37.7 | 37.5 | 1.00 | 502 | 714 |
| Trail del Cinghiale 100K | 2025 | ultra mountain | 179.0* | 21.0 | 19.3 | 0.92 | 547 | 80 |
| Trail del Cinghiale 20K | 2025 | mountain | 35.7* | below floor | 3.1 | - | 437 | 344 |
| Transgrancanaria Advanced | 2026 | ultra mountain | 145.9* | 16.3 | 14.7 | 0.90 | 560 | 598 |
| Transgrancanaria Classicflagged | 2025 | ultra mountain | 228.8* | 28.4 | 23.6 | 0.83 | 602 | 568 |
| Transgrancanaria Marathon | 2026 | mountain | 72.6* | 6.6 | 7.1 | 1.07 | 464 | 1,478 |
| Ultra-Trail Harricana - UTHC 125flagged | 2026 | trail | 186.3 | 19.5 | 24.5 | 1.25 | 373 | 279 |
| Ultra-Trail Harricana - UTHC 42 Classicflagged | 2026 | trail | 59.8 | 4.9 | 6.3 | 1.30 | 351 | 366 |
| Ultra-Trail Harricana - UTHC 42 Saint-Simeonflagged | 2026 | trail | 62.2 | 5.1 | 7.1 | 1.40 | 312 | 370 |
| Ultra-Trail Harricana - UTHC 60 MS Canadaflagged | 2026 | trail | 97.8 | 8.9 | 11.5 | 1.30 | 353 | 41 |
| Ultra-Trail Harricana - UTHC 65flagged | 2026 | trail | 88.4 | 7.8 | 10.2 | 1.30 | 351 | 410 |
| Ultra-Trail Harricana - UTHC 80flagged | 2026 | trail | 107.6 | 10.0 | 12.0 | 1.21 | 394 | 249 |
| UTMB Mont-Blanc | 2026 | ultra mountain | 324.0 | 43.4 | 38.2 | 0.88 | 573 | 1,676 |
| UTMB Mont-Blanc | 2025 | ultra mountain | 324.0 | 43.4 | 40.8 | 0.94 | 535 | 1,665 |
| UTMB Mont-Blanc | 2024 | ultra mountain | 325.1* | 43.6 | 40.6 | 0.93 | 540 | 1,760 |
| Val d'Aran by UTMB - CDH 110K | 2026 | ultra mountain | 204.3 | 24.7 | 22.0 | 0.89 | 565 | 854 |
| Val d'Aran by UTMB - PDA 55K | 2026 | mountain | 105.9 | 10.5 | 10.6 | 1.00 | 498 | 1,478 |
| Val d'Aran by UTMB - TDL 75K | 2026 | mountain | 150.2 | 16.1 | 15.1 | 0.94 | 536 | 610 |
| Val d'Aran by UTMB - VDA 163K | 2026 | ultra mountain | 313.0 | 41.6 | 39.5 | 0.95 | 529 | 358 |
* assumed
What TM1.0 does not model
- A shaped descent cost. Steep or technical descents cost more than the linear term says; fitting that needs split times, which the model does not have.
- A per-race factor. Only the class factor exists; the table above says where a race-level one would go.
- Fade within a race. The exponent is fitted across races and cannot produce the second-half slowdown real splits show; Race Plan splits keep their own ramp, unvalidated.
- Night, heat and altitude in the headline. Whatever the fitted results experienced is averaged into the coefficients; the Conditions Simulator applies day-specific deltas and says so.
- Technicality. A GPX cannot see rock, roots or scree.
Changelog
- TM1.0 (2026-09-25): first frozen model. Loss divisor 200 replaces the 150 used on this site and the 250 used in the app; the index is refitted on effort-km conditional on the terrain classes; ranges come from out-of-sample error instead of a fixed 25 points.