In the last section we showed the TDO formula and justified its usefulness:

$$TDO_{prop} = DPS1 * HP * DEF$$

HP and DEF are the base stats of the pokemon whose TDO we are finding. Base stats can be found in the game master file or on gamepress. Now we just need to find our pokemon's DPS. We can find DPS instead of DPS1 because they only differ by a constant factor equal to the enemy's defense... but we are only finding something proportional to TDO anyway, so that constant factor doesn't matter.

To figure out the DPS formula, let's write a few definitions. A "cycle" is a series of Fmoves, and then one Cmove. wDPS, or "weaveDPS" is the damage per second you do on average throughout that cycle, while "weaving" together fast and charge moves. Since DPS is damage over time, over one cycle wDPS can be calculated as

FDmg, CDmg are the damage done by your pokemon's Fast and Charge moves. This depends on the damage formula.

are the damage done by your pokemon's Fast and Charge moves. This depends on the damage formula. nFPC is the number of Fast Moves Per Cycle

is the number of Fast Moves Per Cycle CT is cycle time: how many seconds the cycle lasts.

$$wDPS_{simple} = $$ $$\frac{Damage}{Time}=\frac{CDmg + nFPC * FDmg}{CT}$$ where

So now we need to find expressions for nFPC and CT. CT can be broken down into the time spent on each move type:

$$CT = CDur + nFPC * FDur$$

CDuration and FDuration can be found in the game master file. So now we just need nFPC to plug into the DPS equation. The number of fmoves per cycle can be calculated as

FE is the "Fmove Energy gain", how much energy you gain for using that Fmove.

$$nFmovesPerCycle$$ $$ = \frac{totalFmoveEnergyGainsPerCycle}{FE}$$

We can find totalFmoveEnergyGainsPerCycle based on what percent of energy gains come from Fmoves (the rest comes from taking damage). We will look only at the time period from the start of a cycle until the beginning of the next Cmove. During this time the percent of energy gains from Fmoves is:

EPS is the Energy Per Second gained by either spamming FMoves or taking damage from the enemy.

$$PercentEnergyFromFmoves = \frac{EPS_{FM}}{EPS_{FM} + EPS_{dmg}}$$

The formula above is just the ratio of Fmove energy gains divided by total energy gains. Remember that in pokemon go, you take gain half of the damage you take back in energy. So next we can plug in

$$EPS_{dmg}=EnemyDPS/2, EPS_{Fmoves}=\frac{FE}{FDur}$$

EnemyDPS is not known since much of the usefulness of TDO is that it only depends on the attacker, but an approximate formula we will use for tier 5 raids is EnemyDPS = 1930/DEF. With PercentEnergyFromFmoves, we are now ready to calculate totalFmoveEnergyGainsPerCycle.

From the start of a cycle until the beginning of a Cmove (assuming you use the Cmove as soon as you have enough energy) you gain energy equal to

CE is the Cmove's Energy cost.

$$CE - CDur * EPS_{dmg}$$

This formula needs some explanation. At first you might think that the whole thing should just be CE. The remaining term is there to account for leftover energy from the previous cycle:

- CDuration * EPS_dmg is there because we are only counting the period from the end of the previous Cmove until the beginning of the next Cmove. But before this period - namely, while the previous Cmove was happening - you were still taking damage and gaining energy for it. That energy gain is equal to CDur * EPS_dmg. On the first cycle you do not have this energy, but we do not change the formula to account for that because there are extra energy gains in the first cycle from the extra Fmove which the defender does at the very beginning of the battle.

A little note: For full-bar charge moves we can make the above expression for energy gains more accurate by replacing it with $$CE +FE/2- (CDur - CDWS) * EPS_{dmg}/2$$

CDWS is the Cmove DamageWindowStart, which is a variable listed in the game master file. It is the time at which damage is applied and energy is used up for a Cmove. This is different for 100energy moves because since your bar was full after starting the Cmove you could not gain as much energy before starting the next cycle.

is the Cmove DamageWindowStart, which is a variable listed in the game master file. It is the time at which damage is applied and energy is used up for a Cmove. This is different for 100energy moves because since your bar was full after starting the Cmove you could not gain as much energy before starting the next cycle. The +FE/2 term is there because unless you have a full bar Cmove, you most likely gained a bit more energy than you needed from spamming Fmoves in the previous cycle. The "overshoot energy" is, on average, FE/2.

The above correction comes from the fact that for full-bar moves you can only begin to gain energy once you have used up the Cmove's energy. This modification won't make a huge difference, and it adds an "if" statement to the formula depending on CE, so it may be skipped.

Now we can wrap the previous equations together:

$$totalFmoveEnergyGainsPerCycle$$ $$ =(CE - CDur * EPS_{dmg}) * PercentEnergyFromFmoves $$ $$ =(CE - CDur * EPS_{dmg}) * \frac{EPS_{FM}}{EPS_{FM} + EPS_{dmg}} $$

..and divide to get

nC is the number of Cmoves you do throughout the battle

$$ nFPC = \frac{totalFmoveEnergyGainsPerCycle}{FE} $$ Now we are finally ready to calculate, which is the average damage per second you get throughout a cycle. We had $$wDPS_{simple} = \frac{CDmg + nFPC * FDmg}{CT}$$ Why did I add the "simple" subscript to? Often, the battle ends and you still have wasted energy left. So for the last 0.5 cycles or so, your DPS will just be FMove DPS. We can account for this by taking a weighted average ofwith, which is the DPS you get from just spamming Fmoves: $$wDPS_{actual} = $$ $$\frac{nC * wDPS_{simple} + 0.5 * FDPS}{nC + 0.5}$$ where $$FmoveDPS = FmoveDamage/FmoveDuration$$

Now we need nC:

$$nC = SurvivalTime/CT - 0.5$$

0.5 is subtracted here because this formula should have a floor() function in it: If you don't survive until the end of the cycle, you don't get to do the Cmove. But the floor would give it discontinuities which give bad results, so we "average it out". But since a floor() function always rounds down, biases by 0.5. This bias must be included.

At this point, there is a chain of variables to plug in, everything is expressed in terms of numbers which can be found in the game_master file or on Pokemon go websites, like base stats of a Pokemon. Below is a summary.