Power and Load modelling - looking past TSS

So, this is in part a serious look at how we could improve upon TSS as a way to record training load and use it to inform us of the way we should train on a day to day basis.

The other part of this is that I wanted to start programming in Python after years of tinkering about in spreadsheets. So bear with me, this is all a work in progress:

To do this I’ve broken the system down into four main parts:

  • Power Duration modelling
  • Ride classification
  • Athlete load modelling
  • Training advice

If all of this sounds familiar it should come as no surprise that this is the model used by Xert, of which I’m a user and advocate. As I say, I’m just doing this for my own interest, but it may be of use to others, so here goes:

1. The power duration model

No point in reinventing the wheel here as there are some great models out there. The best, as far as I can see is the one by Peronnet and Thibault, which you can read about in this article on Veloclinic:

The model is fairly straight-forward mathematically being broken down into 2 components:

P(awc) = AWC / t * (1-exp(-t / tau))

P(map) = MAP * (1 - exp(-t / tau2));  when t <= Tmap

P(map) = MAP * (1 - exp(-t / tau2)) – a * Ln(t / Tmap):  when t > Tmap

The inputs for the model are as follows:

Pmax = maximum power (Watts)
AWC = Anearobic work capacity (Joules)
tau = Pmax / AWC

MAP = maximum aerobic power or FTP (Watts)
Tmap = time to exhaustion for MAP (seconds)

a = factor to determine the slope of P(map) after Tmap
tau2 = factor to determine the shape of P(map) before Tmap

I have taken a slightly different approach (just because I can) for P(awc) at the extreme right hand end of the model: Originally P(awc) would continue to be a non-zero value to t = infinity. In my model I wanted to make the contribution zero beyond a power equal to LT1. Fot this I’ve pinched a model for LT1:

LT1 = MAP * (1 - (5 / 2) * (( AWC / 1000) / MAP))

For every power it’s now possible to calculate the Aerobic contribution and the Anaerobic contribution.

Here’s the power model with a sample Mean Maximal Power curve:

2. Ride classification

By having a power duration model that allows us to workout contributions from the anaerobic and aerobic systems we can start to think about how these two systems contribute to overall system load.

At this stage, I’m not looking to reinvent the wheel completely, just to investigate if what we have already can be improved upon, so I’m sticking with TSS!

NP = (average (30 second rolling average **4)) **0.25


TSS = (t * NP **2) / (36 * MAP **2)

My first thought was that although TSS is usually just calculated at the end of a ride and is related to your FTP and the normalized power of the ride, it occurred to me that you can calculate TSS after every second, in exactly the same way that it’s displayed on your bike computer.

From that you can calculate the rate at which TSS is built up on a second by second basis and then apportion that to either TSSr(awc) or TSSr(map). Add these back together at the end and you get TSS(awc) and TSS(map)

Here’s how a workout looks (based on perfect data):

And here’s a ride:

I’m happy with the way that this working based on the model I developed - there’s a small bug but I’ll catch that soon.

In the examples above you can see that for any output under LTP (225 Watts in this case) there is no TSSr(awc) and if I’d tried a bit harder, the TSSr(map) drops off as you push higher and high wattages.

What it does highlight is that TSS is far from perfect and that it accrues much quicker at the start of a workout than at the end, however, so long as the proportions are correct between TSSr(awc) and TSSr(map) it still gets.

3. Athlete load modelling

No point breaking a good model - just split it in two…

Now that we’ve calculated TSS(awc) and TSS(map) we can track them both separately and combined.

It’s long been a complaint of the Performance Management chart that all load is lumped together and tracked with the same decay rates. Now that these are separated we can track them independently and with different decay values.

The graphs below use made up data with the following decay rates:

MAP Load: 60
MAP Recovery: 7

AWC Load: 20
AWC Recovery: 5

MAP Performance management chart:

AWC Performance management chart:

Total Performance management chart:

4. Training advice

The idea here is to develop a way of advising what types of workouts or rides you should do next:

Depending on where your Low form sits you might be advised not to train (if it’s less than -30% of you training load).

Similarly, for AWC you’d only be advised to do higher intensity workouts if you AWC form was high enough.

That’s really as far as I’ve got - things need to be worked out and my programming needs to improve from the basic level it’s at right now. I can create workouts, import fit files and manipulate the data based on these, but it’s a long way from being a system.

I hope this is of interest to some of you out there.



Add on for Golden Cheetah?

From overview the idea seems to be simialr to aerobic and anaerobic training score? Not saying as a bad thing, as I think those are the best two parameters to quantify training and I always wondered how great it would be to incorporate them into training load.

That’s quite possible. I haven’t used WK05 since it first came out. I didn’t like the changes and it was slow so didn’t fancy forking out for it. I’ll have to read up about it.

This looks wrong. When you go from 150 to 300 watts the initial tss should go to awc and quickly decay as it switches to map as the source of the power. Map derived power takes a tiny bit of time to turn on fully

This is really cool.

Do you follow Xert’s training advice now for the types of rides you should go on based on your low, high, and peak fitness/fatigue?

Probably not such an easy problem to solve, but I understand what you’re saying.

At the moment the TSS is split proportionately the same no matter how far along the 300 Watt portion you are. It looks as though there is more MAP than AWC, but that’s just more to do with rate at which TSS grows earlier in the workout.

I try to. It’s an excellent system but it always bugged me that all work carried out below threshold is considered to be Low.

1 Like

Busy at work but I quickly looked at your first post. Maybe I need a nap but didn’t follow how you apportioned. The % aerobic/anaerobic lookup from power model is based on duration. Since you are doing this using NP and 30-second rolling averages, for 300W are you doing a lookup at 30-sec on the power model?

I think (but am completely unsure) that this is what Xert does with their XSS (Xert Strain Score) metric, which essentially measure strain, and weights workouts higher based on efforts and duration above threshold… or some combination of similar factors.

The XSS metric has always been a better indicator of my fatigue than TSS

Is there an easy way to extract out the peak/high/low XSS and/or fatigue? Maybe to seperate out all the low fatigue? Curious what they don’t have one additional metric around their LTP that should be around LT1

For a single point in time there is a ‘Power’ and ‘rate of TSS’ (TSSr):

For that ‘Power’ I go back and look at the power model and determine the split between AWC and MAP. At the moment it’s an iterative solution but I’m looking at a better way to return the ‘time’ at ‘Power’ more gracefully…

My understanding of the Xert model is that it works, conceptually at least, in almost the same way as I’m trying to get this to work.

For each second of a ride Strain is divided into 3 systems - Low, High and Peak. The rate at which the overall Strain is accrued is dependent on two factors - the power and how far your MPA has been drawn down. That makes intuitive sense: if you do 2 x 4 vs 1 x 8, you’re going to get more Stress from the 1 x 8 if you do them at the same power.


I totally agree. TSS does some strange things that XSS is designed not to do.