Saturday, February 23, 2013

Chanteloup profile revised, and the unreliability of Strava profile data

I previously posted a route profile for the Chanteloup climb near Paris, France. This is a climb with an amazingly rich cycling history, going back to Velocio who used it to demonstrate the superiority of multi-gear bikes to the fixed-gear bikes which dominated professional racing in the early 20th century. Later it became the sight for the "Poly de Chanteloup" event, which was contested by randonneurs and professional racers. The randonneur event unfortunately seems to have died, but the professional race continued as the Trophée des Grimpeurs, a traditional last race of the season until sponsorship was lost in 2010 and it also expired.

Jan Heine, in his highly recommended book "René Herse", has the following quote about the gearing of the winning tandem in the 1949 randonneur contest. I posted this last time but I repeat it here because I find it remarkable:

A single chainring was sufficient for the 14% of the climb of Chanteloup, which had to be climbed eight times. In 1950 a report listed the gearing of their tandem: a single chainring with 46 teeth and a 5-speed freewheel with 13-15-18-21-22 teeth. They never used the 22-tooth cog, and they made their attack with the 46-18.

How could they ride a tandem up the climb whose profile I got from Strava? It boggled the mind.

But it soon became obvious to me something was amiss. First I checked StreetView, and the hill simply didn't look as steep as advertised. That's not uncommon, but the places where the grade changed dramatically in the profile weren't reflected in the StreetView.

The other factor was the profile failed to match the crude one published in Heine's book on page 175. That profile, "le profil du parcours", was obviously taken from some old race guide.

The nail in the coffin was when someone on the Weight Weenies forum who'd ridden the climb said it "wasn't more than 12%".

As is often the case, I'd succumbed to laziness, taking the first available data. The problem is when one defines a segment on Strava, the data from the activity used to define the segment is taken as the reference data. Unfortunately ride data varies substantially in quality. Data from position data without altimetry is interpolated by Strava onto reference altitude data. This can be quite good, except position errors, particularly from older iPhones, translate into altitude datas from slopes transverse to the road, and these transverse slopes are often considerable (grades of 100% not uncommon). A 10 meter position error could yield a 10 meter altitude error and an error of 10 meters in the difference between altitudes of two nearby points is obviously going to have a profound effect on extracted road grade.

Worse, perhaps, is GPS altitude. GPS-only altitude, reported for example by the Garmin Edge 200, varies substantially in quality, and is unreliable.

Barometric altimetry is better. There the unit measures the ambient pressure and temperature, converts the combination into an altitude, and reports that. This is better for short-term differences in altitude. It becomes even better if the unit uses GPS altimetry as calibration.

The Garmin Edge 500 is fairly good with altitude once it has equilibriated with the outdoor temperature. Edge 800 is even better. So if I can find data from either of these units, I use that in preference to other sources. (Aside: the best of all is perhaps iBike, since that measures grade directly, which I can combine with the reported altitude to generate data which has good grade to even small length scales, but iBike data is rare and isn't found on Strava).

The issue with looking at Strava segments is there is no direct indicator from where the data used to generate the profile came. Activities are tagged, but when an activity is used to define a segment, the segment is not.

So better than using the segment data directly is to take data from the activities in the leaderboard. I prefer recent activities near the top of leaderboards, especially if the leaderboard is deep. Then I check to make sure the activity was recorded with a trusted unit (Edge 500, 501, 800, or 801, for example) and that the position data track the road or path nicely. That's the best approach, the approach I didn't take in my previous blog post.

revised profile, using data from Strava KOM

So here's a revised profile, showing as well the profile from Heine and the data I used last time. I vertically displaced the Edge 500 data to match the peak altitude of the Heine profile. On the Heine profile the first and last point were explicitly listed, but the center point was not, so I eyeballed it to match the graphic in the book. The result is the Edge 500 data falls in close agreement with the data from Heine, while the data from the Strava segment shows the climb to be much steeper, gaining more vertical.

so the conclusion is the climb sustains around 4.3% for awhile, then after a brief flattening sustains 10%, then finishes with a very brief 16% or so gaining only 8 meters. 10% is a steep climb, for sure, especially on those gears, but this a big difference from the much higher numbers concluded from the Strava segment data.

Tuesday, February 19, 2013

gearing on the 1949 Poly Chanteloup by the Herse tandem

I've been reading Jan Heine's truly excellent book, "René Herse". Here's a quote, referring to the tandem ridden by the builder's daughter Lili and Rene Prestat in winning the 1949 Poly Chanteloup, a circuit race which included a popular climb outside of Paris:

A single chainring was sufficient for the 14% of the climb of Chanteloup, which had to be climbed eight times. In 1950 a report listed the gearing of their tandem: a single chainring with 46 teeth and a 5-speed freewheel with 13-15-18-21-22 teeth. They never used the 22-tooth cog, and they made their attack with the 46-18.

I figured 14%? Sure, typical French hyperbole. It's probably 14% on the inside of a switchback. And indeed, the book has a route profile which shows the climb to average only 7%. Riding a 46-18 up a 7% grade is still a considerable gear. I PR'ed Old La Honda Road, which averages 7.3%, riding a 36-18, and the tandem gear was 28% bigger.

So I checked Strava. Here's the Chanteloup climb as viewed with VeloViewer, which produces excellent hill profiles from Strava data:

Chanteloup profile
Chanteloup map

14%? It's more like 15% sustained, with a nice little dose of 16% to loosen up the legs. Even the portion leading up to that is steep, exceeding 8%. I simply cannot imagine climbing that on a tandem, where riders typically spin due to the synchronization challenges, with a 46-21 low gear.

The profile I prepared directly from the Strava data is even meaner-looking:


At least the climb is short. The portion in the segment (it continues to climb a bit more, gradually, following the segment gains 120 meters, which is only 30% of Old La Honda's altitude gain. But the riders climbed it eight times in the race.

A word about these races: it is curious seeing a race where the winning bike has a front rack, full-length fenders, and even mud flaps. But those were required by the rules, as they were randonneuring events. These were in the spirit of the Low-Key Hillclimbs, where results were counted, winners were announced, but participation was at least as important as results. This was particularly important because racing at the time lacked the deep age-and-accomplishment based category system available in U.S. racing.

Tuesday, February 5, 2013

second-order differentiation of speed with respect to wind speed using the chain rule

I was reminded of some issues in differential calculus when working on the previous blog post.

I had an equation for power as a function of speed and wind speed:

p(s, sw)

Since I assume that power is constant, it is an implicit equation: it defines contours in (s, sw) space of constant power. In the case of power, solving s as a function of sw may be a considerable challenge. But The Chain Rule comes to the rescue.

With the Chain Rule I can write:

dP = (∂ P / ∂ s) ds + (∂P / ∂ sw) dsw .

But I'm assuming constant P, so dP = 0. I can then write:

(∂ P / ∂ s) ds = −(∂ P / ∂ sw) dsw .

And with re-arranging terms I get what I want:

ds / dsw = −(∂ P / ∂ sw) / (∂ P / ∂ s) .

This was all fairly simple (although that minus sign confused me when I first saw it as an undergraduate). The tricky bit for me was the second derivative. But there's no reason for it to be complicated, either. If I write the first derivative as a function r ≣ ds / dsw :

d2s / d sw2 = d r / d sw .

So I apply the chain rule as before with the derivative function r:

dr = (∂ r / ∂ sw) dsw + (∂ r / ∂ s) ds .

But what I care about is dr / dsw, so I divide all terms by dsw:

d2s / d sw2 = dr / dsw = (∂ r / ∂ sw) + (∂ r / ∂ s) / r .

Note the substitution ds / dsw = 1/r. This equation is all in terms of r ≣ ds / dsw, and so I can evaluate it, since I already evaluated r. So I solve the first and second derivative of speed with respect to wind speed without ever actually evaluating a closed-form solution.

It took me several hours before I had clarity on this. Note the first term is a partial derivative with respect to the wind speed. The second term is a partial derivative with respect to speed multiplied by a full derivative of speed with respect to wind speed. The first term is due to, at a constant rider speed, the effect of wind speed changes on the rate of change of rider speed with respect to wind speed (this takes a bit of thought to wrap your head around). The second term is the effect of rider speed on the dependence of wind speed on rider speed multiplied by the effect on rider speed of wind speed. So it's adding two critical components, each ignoring the other, to come up with the net effect.

What I posted yesterday included the first term but omitted the second term. That's more complicated. I'll fix yesterday's post when I get it right.

Monday, February 4, 2013

tail/headwind effect on speed: analytic second-order evaluation

I already showed my "simple" model for how an arbitrary wind affects speed. This was based in part on a numerical fit to an implicit power-speed calculation, where I found to decent approximation the logarithm of speed as a function of tailwind/headwind was well fit by a parabola. The formula I used, combining the effect of a tailwind/headwind with the effect of a crosswind, was:

s' = exp[ −(sw' / 3)2 ] exp[ 2 swx'/ 3 ],

where v0' is the ratio of flat-road speed with the wind to flat-road speed without the wind, sw' is the ratio of wind speed to flat-road speed without wind, and swx' is the component of that in the direction of rider travel.

I already showed an analytic derivation of the cross-wind term, which is proportional to the square of sw'. I also did a first-order dependence of s' on swx'. However, to justify this full model other than numerically requires a second-order dependence of s' on swx'.

So to do this I return to the power-speed model, where I assume two coefficients for power: fw is a coefficient for wind resistance, and fm is a coefficient for gravity (rolling resistance and climbing). I exclude drivetrain losses, assuming they are constant at constant power. Since I eventually constrain power to be constant, drivetrain losses are also constant, and so I am okay simply omitting it.

The power-speed equation is, in terms of these variables:

P' = s' [ fw (s' - sw')2 + fm] ,

where I assume a normalized power P' = 1 when s' = 1 and sw' = 0, yielding the constraint fw + fm = 1.

For the first-order analysis I differentiated this once:

dP' = d { s' [ fw (s' - sw')2 + fm] } = 0 .

The differential power is zero because I assume constant power. This yields:

dP' = ds' [ fw (s' - sw')2 + fm ] + 2 fw s' (s' - sw') (ds' - dsw') .

I am interested in ds' / dsw', so :

ds' [ 2 fw s'(s' − sw') + fw (s' - sw')2 + fm ] = dsw' 2 fw s' (s' − sw') .

I can then trivially calculate the derivative:

ds' / dsw' = 2 fw s' (s' − sw') / [ 2 fw s'(s' − sw') + fw (s' - sw')2 + fm ]

So if I am starting on flat roads with minimal wind, then s' = 1 and sw = 0 and I get:

d s' / d sw' = 2fw / (3fw + fm) .

If I define f to be the fraction of power originally going into wind resistance, this becomes:

d s' / d sw' = 2f / (2f + 1)

But now I want to make sure I get this to second-order, as well. This is tricky (see next post) because I need to apply the chain rule properly, and I had it incorrect before I finally revised it. The result is, which I verified by comparison with numerical calculation:

d2s' / dsw'2 = 2 f (4 f - 1) / (2 f + 1)3 .

Here again I use f as the fraction of power, in zero wind, from wind resistance.

I'll assume an exponential dependence, which was validated in the numerical fits. Exponential fits make sense, since at constant power wind can't change direction from forward to backward, speed can only approach zero, and an exponential dependence has that behavior. With the exponential dependence matching the first derivative results in a non-zero second derivative, so the second-order coefficient in the exponent needs to be evaluated to account only for the difference.

When I do this I get:

v0 =
  v00 ×
  exp [ 2f sw / (2f + 1) v00 ] ×
  exp [ −[f (4 f2 - 2 f + 1) / (2 f + 1)3] (sw / v00)2]

This matches what I have been using assuming f = 1, or in other words that wind resistance dominates. This isn't a great approximation, but it's a good compromise for consolidation with the cross-wind term.

So this gets rid of the ugly numerical fits in the wind analysis. There is a first-order dependence when there's a tail-wind or head-wind, but the analysis needs to be done to second-order on a closed circuit with constant wind because the first-order component cancels on the upwind and downwind portions, so second-order components become important.

Sunday, February 3, 2013

analytic approximation to random direction wind

Last time I gave up trying to solve this integral. I wanted to watch Cyclocross Worlds so got lazy and did a numerical solution and fit. But I realized while out running after the racing that I could have done much better. So I'm back for more.

(1 / 2π) ∫ dφ exp[ (sw' / 3)2 ] exp[ −2 swx' / 3 ],

where the integral is over the full circle and φ is the angle of the wind relative to the rider (0 = pure tail wind).

The key here is to recognize that this can be well-approximated by a Gaussian for sw' to at least 1. This isn't the solution of the integral, but it's a good approximation, so once I recognize the analytic form of the solution I can get away just matching derivatives with respect to sw' = the ratio of the wind speed to the zero-wind rider speed.

So my solution will be the following, where I must solve for K:

exp[ (sw' / K)2 ].

I recognize that for every value of positive swx', there is a corresponding negative value, and so I can combine them in the integral and then integrate over the half-circle for which swx' are positive (from −π/2 to +π/2):

(1 / π) ∫ dφ exp[ (sw' / 3)2 ] cosh[ 2 swx' / 3 ].

I can then do a low-order expansion on these exponentials to generate a linear equation: exp(x) ≈ 1 + x, and cos(x) ≈ 1 + x2/2. The integral thus becomes the following approximation:

(1 / π) ∫ dφ [1 + (sw' / 3)2] [ 1 + 2 (swx' / 3)2 ].

Now I plug in the formula for the tail wind component swx':

(1 / π) ∫ dφ [1 + sw' / 3)2] [ 1 + 2 (sw' / 3)2 sin2φ ].

I can omit terms proportional to sw'4:

(1 / π) ∫ dφ [1 + (1 + 2 sin2φ) (sw' / 3)2].

Recognizing the average value of sin2φ = 1/2, the integral is trivial:

1 + 2 (sw' / 3)2 .

The low-order expansion of the Gaussian approximation is:

1 + (sw' / K)2.

So I conclude K = sqrt(9/2). This is essentially what I got numerically.

So written more elegantly, the approximation to the average speed of a rider slowed down by a constant wind coming from wind distributed evenly from all possible directions during a closed course is:

<v0> = v00 exp[− 2 (sw / 3 v00)2],

where <v0> is the average flat-road speed and v00 is the zero-wind flat-road speed.

The interesting thing about this result is the average speed reduction, including the headwind and tailwind sections, is twice the speed reduction in the cross-wind sections, for relatively small speed reductions. So if the cross-winds slow me down by 1%, over the whole course I'll be slowed around 2%, considering the effects of the head-winds, tail-winds, and partial cross-winds.

It's interesting to compare this to the result for an out-and-back with direct head and tail wind. There the average time per unit distance is scaled by cosh[ 2 sw' / 3 ] exp[ (sw' / 3)2 ] ≈ exp[3 (sw' / 3)2]. So the result in wind equally distributed over all angles equally is 2/3 as much as that from a pure headwind plus a pure tailwind.

Saturday, February 2, 2013

attempt at calculating effect of random-direction wind

Last time, I discussed a simple approximation which comes fairly close to predicting the speed at constant power in an arbitrary wind of reasonable magnitude. The formula can be written as a normalized speed s' (normalized to no wind) and a normalized wind sw' (normalized to the same scale) as follows:

s' = exp[ −(sw' / 3)2 ] exp[ 2 swx'/ 3 ],

where swx' is the normalized speed of the wind in the direction of the rider (positive for tailwind, negative for headwind).

For a typical closed circuit, the rider will head in random directions relative to the wind. If the wind is constant than the average distance-weighted direction is zero: the rider starts where he begins. Last time I calculated a result assuming a square course aligned with the wind. That's an over-simplification, even if it's probably representative. But I'd prefer to consider the more generally applicable case of all directions equally likely.

So I assume the rider goes in a big circle. I can then integrate 1/s' around the circle. 1/s' is the time taken per unit distance for a given point on the course. The equation as follows:

(1 / 2π) ∫ dφ exp[ (sw' / 3)2 ] exp[ −2 swx' / 3 ],

where the integral goes from 0 to 2π.

Note that swx' is a function of φ so I need to convert one to the other to evaluate the integral. I want to avoid the exponential of a cosine (transcendental of transcendental) so I choose to get rid of φ:

d swx' = sw' d cos φ = −sw' sin φ dφ.

I convert the differential dφ:

dφ = dz / sin φ,

where I define a temporary variable:

z ≣ swx' / sw' = cos φ.

I can get rid of the sin φ using:

sin φ = sqrt(1 - z2)

The integral becomes:

(1 / π) exp[ (sw' / 3)2 ] ∫ dz exp[ 2 sw' z / 3 ] / sqrt(1 - z2)

where z goes from −1 to +1, and where I made a few trivial simplifications.

Here I'm stuck. It looks like it shouldn't be hard, but I simply don't know what to do next. It obviously converges despite going infinite at the endpoints of integration: for z = 1 - δ where δ is small and positive, 1 / sqrt(1 − z2) = 1 / sqrt(1 − (1 − δ)2) ≈ 1 / sqrt(1 − (1 − 2δ)) = 1 / sqrt(2δ), and the integral of that with respect to δ is sqrt(2δ), which goes to zero for δ from 0 to a small positive limit. So that's not a problem. But I was hoping for a simple analytic solution.

Back in the day, that would be the end of it unless I could come up with some clever approximation. But instead I quickly succumb to the temptation of numerical evaluation and fit so I can watch the cyclocross world championship web feed. The result is in the following plot:

numerical result

I did the fit using only the function from −0.5 to +0.5, since winds outside this range at ground level would be rare. Despite this, the function extrapolates nicely out to my plotted range of −2 to +2.

Converting from time to average speed, and going from normalized speeds back to actual speeds for rider and wind, the fit is:

<v0> = v00 exp[ − (sw / 2.12 v00)2 ]

Here's the same plot with log-log axes, showing the time delay rather than time:

numerical result

The result is a wind equal to half the zero-wind rider speed delays the rider around the circular course by approximately 5%.

Friday, February 1, 2013

adding wind to heuristic bike-speed model

The motivation for the preceding analysis was to add wind effects to my heuristic speed model. The philosophy of the heuristic speed model is to not rely on a constant power approximation, but try to model cyclist behavior directly. So how do riders behave when faced with a wind?

When I first started riding with a heart rate monitor, an early lesson was my heart rate dropped when I was riding into the wind. This was obviously psychological: the wind was defeating me and I lost the motivation to pedal hard.

Then I moved from the San Francisco Bay area to Austin where I lived for three years and winds were a predictable part of every ride, while extended hills were essentially gone. Instead of challenging myself on long hills, I learned to use treat the headwinds as a challenge rather than bad luck. As a result, I began increasing my heartrate, rather than decreasing it, when I encountered headwinds. With groups it's different: the group may be motivated to hammer into the wind, or it may be that nobody wants to pull and the speed drops.

Crosswinds are another matter: gusting crosswinds tend to reduce power because the rider needs to focus more on controlling the bike. It's hard to get into a rhythm with heavy cross-winds. Groups lose their coherence because riders get ridden into the gutter.

So lacking insight into behavior in winds, and with data on ground-level wind speed so much less reliable than climbing data, I'll just assume people ride constant power. Using a simple power-speed model with "reasonable" coefficients I derived for a headwind, on flat ground:

v0 = v00 exp[0.62 sw / v0 − (0.3 sw / v0)2],   tail/head-wind

where v00 refers to the level-ground speed in zero wind, and v0 is the level-ground speed at the given wind speed (measured in the same direction as the rider) sw.

For the cross-wind, the fit yielded:

v0 = v00 exp[−(0.37 sw / v0)2].   cross-wind

I can simplify this and combine them to arbitraty wind direction with the following, using sw to represent the total wind speed and swx to represent the wind in direction of travel:

v0 = v00 exp[2 swx /3 v0 − (sw / 3v0)2].   arbitrary wind

Note the square term applies to the absolute wind speed indpendent of direction, while the other term applies to the component of the wind speed in the rider direction. There is no explicit cross-wind term. The nice round numbers are justified since wind is never really constant anyway, either across the rider's body or over time, so higher prevision would be wasted.

The v0 in this equation is then a revised value to use for my heuristic speed model.

To test this, I constructed data for a rider traveling a 5 km radius circle at 8 mps nominal speed on the flat with a 3 mps wind from the north. The speed model with the wind adjustment predicted a speed varying from 5.15 mps into the wind (64%) to 9.11 mps with the tailwind (114%). Power varied from 112 watts into the wind to 126 watts with the tailwind. So that's a +/- 6% variation about the mean power for a variation in speed from -36% to +14%. So the rider maintained much closer to constant power than he would have with a constant wind-independent speed.

It's interesting to ask what this model predicts for the effect of constant wind for a closed circuit. As a test case consider a square course aligned with the constant wind direction. First, the 2nd-order term is constant: time is increased by a factor exp[(sw / 3v00)2]. But then there's the first order term. That aids on the headwind leg, but then reduces time on the tailwind section. The net effect on time is (1 + cosh[ 2 sw / 3 v00 ] ) / 2 (hyperbolic cosine). So the result is for a square course aligned with the wind the wind will increase time by a factor (neglecting fatigue):

exp[(sw / 3v00)2] ( 1 + cosh[ 2 sw / 3 v00 ] ) / 2

For a circular course the result will be a bit different, the cosh term replaced by something similar, the exponential term the same.