Andrew Cooke | Contents | RSS | Previous


Welcome to my blog, which was once a mailing list of the same name and is still generated by mail. Please reply via the "comment" links.

Always interested in offers/projects/new ideas. Eclectic experience in fields like: numerical computing; Python web; Java enterprise; functional languages; GPGPU; SQL databases; etc. Based in Santiago, Chile; telecommute worldwide. CV; email.

© 2006-2015 Andrew Cooke (site) / post authors (content).

[COVID] Coronavirus And Cycling

From: andrew cooke <andrew@...>

Date: Tue, 12 Jan 2021 17:52:10 -0300

There's a long and infamous tradition of physicists / engineers using simple
mathematical models to say something about complex systems (biology,
economics) and getting things horribly wrong.

I'm hoping to avoid that here by not drawing any strong conclusions.  Instead
I just want to throw some light on possible factors that people should take
into account when trying to decide what to do.

OK, so the question people keep asking in various places is: with Coronavirus
being a risk, is it safe to go cycling in a group?

If we assume that transmission is via someone breathing out virus-laden
particles that you breath in (which is, I think, roughly accepted) then you
can try to assess your risk yourself:

* The more people, the more likely someone is to be infected, so the higher
  your risk.

* Breathing out more energetically may increase the number of particles (if it
  increases as the volume increases, for example), which is likely a higher

* Wearing masks hopefully filters out some of the particles, so other people
  not wearing masks increases your risk.  The type of mask is likely also

* How diluted the particles become as they move through the air is also going
  to be important.  In a closed, poorly ventilated space they will collect.
  In the open air they are more likely to be dispersed by the wind.  What
  happens when riding in a group?  Is the air 'captured' by the flow through
  the group or does it get dispersed?  Presumably a more coherent flow
  increases risk.

* Increased spacing between people gives more chance for the particles to be

* Wearing a mask is also important when breathing in, so you not wearing a
  mask increases your risk.  And again the type of mask is likely important.

* Breathing in more deeply - as during exercise - may draw more of the virus
  more deeply into the lungs, increasing your risk.

Putting all this together, individuals riding separately, well separated,
wearing good quality masks, and not exerting too hard are likely pretty safe.
Change any of those variables and you increase risk.  So, at the other
extreme, many people in a compact group, without masks, working hard, hoping
to drag a "bubble" of air along for efficiency, are going to be more at risk.

Again, I am not a virologoist or an infectologist.  The above is just what
seems like common sense to me.

One example of how I may be incorrect is that, in the argument above, it
doesn't seem to matter who wears the mask (if only one person wears it, and it
filters out 90% of particles, it filters out 90% of particles either when
breathing out, or when breathing in).  This contradicts what I have read
elsewhere, which emphasises that masks are more important on "other people"
and less important for self-protection.


Permalink | Comment on this post

Previous Entries

For comments, see relevant pages (permalinks).

[Programming] Docker on OpenSuse

From: andrew cooke <andrew@...>

Date: Tue, 29 Dec 2020 13:07:46 -0300

* Install docker package(s)

* Start sservice

    systemctl enable docker
    systemctl start docker

* Add current user to docker group (check it ecxists in /etc/group)

    usermod -aG docker username

* Check that things are OK

    docker ps



[Bike] Gearing For Real Cyclists

From: andrew cooke <andrew@...>

Date: Sun, 20 Dec 2020 16:58:17 -0300

On the Internet, when discussing lower gearing for a bike, you can expect
someone to comment "just learn to climb".  Which drives me crazy, because the
problem is not the cyclist, but the gears.

Gearing on most bikes sold to new cyclists is not that different to the
gearing that professional cyclists use.  Yet professional cyclists are hugely
more powerful than new cyclists (or even experienced amateurs).  A reasonably
fit, new cyclist can expect to produce around 100W sustained power.  A pro can
maintain 4x that, or more.

The physics of cycling uphill - at least at speeds slow enough for air
resistance to be negligible - is simple.  It's so simple that it's basically
linear - you can adjust things by scaling relative numbers.  In other words,
if a newbie cyclist has 1/4 the power of a pro they should have gears that are
4x as easy.  This is not the case.

To illustrate this I've written a small program that calculates the gearing
you would need (expressed as front x back number of teeth on the gears) for
different power output, gradients, and cadences.

The power levels cover the range from newbie (100W) to pro (400W+), the
gradients from "normal" hills (6%) to the steepest the Alps can offer (14%),
and the cadences reflect climbing standing (30rpm), grinding slowly sitting
(60rpm), and spinning (90rpm) while sitting.

Note that I have assumed a rider of about my weight (65kg, 143lb).  If you
(plus bike) weigh twice what I do then you need gears twice as low.  Again,
it's simple scaling.

Full results (and the program) are below.  Here I'll pick a few interesting

  * A 400W pro can spin (90rpm) up a 12% gradient using 39x27.  This is
    typical of the lowest gearing on a professional bike (which makes sense).

  * A 100W newbie, to do the same, would need 26x72.  That's a 26 tooth gear
    at the front and an 72 tooth rear - so extreme it's not even available on
    mountain bikes (a 200W rider would need 26x36, which is a MTB gear).

  * A 200W amateur rider, with 34x28 gears (about the lowest most new road
    bikes go) can spin (90rpm) up a gradient of around 7%, but can manage over
    14% if they learn to climb standing at a low cadence (30rpm).

I think the last point is the source of "learn to climb" - learn to ride
standing at low cadence.  But note that this is only an option if you're
a similar weight to me (and many riders seem to weight much more).

Finally, remember that these are broad generalisations, based only on work
against gravity (ignoring rolling resistance and wind resistance).  But when
we're dealing in differences of a factor of 4, a rough categorization within
10% or so is fine.


Cyclist generating 100W

  Climb of 6%
    30rpm  26x12 30x14 34x16 39x18 
    60rpm  26x24 30x28 34x31 39x36 
    90rpm  26x36 30x41 34x47 

  Climb of 8%
    30rpm  26x16 30x18 34x21 39x24 
    60rpm  26x32 30x37 34x42 39x48 
    90rpm  26x48 

  Climb of 10%
    30rpm  26x20 30x23 34x26 39x30 
    60rpm  26x40 30x46 
    90rpm  26x60 

  Climb of 12%
    30rpm  26x24 30x28 34x31 39x36 
    60rpm  26x48 
    90rpm  26x72 

  Climb of 14%
    30rpm  26x28 30x32 34x36 39x42 
    60rpm  26x56 
    90rpm  26x84 

Cyclist generating 200W

  Climb of 6%
    60rpm  26x12 30x14 34x16 39x18 
    90rpm  26x18 30x21 34x23 39x27 

  Climb of 8%
    30rpm  34x10 39x12 
    60rpm  26x16 30x18 34x21 39x24 
    90rpm  26x24 30x28 34x31 39x36 

  Climb of 10%
    30rpm  26x10 30x11 34x13 39x15 
    60rpm  26x20 30x23 34x26 39x30 
    90rpm  26x30 30x34 34x39 39x45 

  Climb of 12%
    30rpm  26x12 30x14 34x16 39x18 
    60rpm  26x24 30x28 34x31 39x36 
    90rpm  26x36 30x41 34x47 

  Climb of 14%
    30rpm  26x14 30x16 34x18 39x21 
    60rpm  26x28 30x32 34x36 39x42 
    90rpm  26x42 30x48 

Cyclist generating 300W

  Climb of 6%
    Note: speed > 20kmh, air resistance significant
    60rpm  34x10 39x12 
    90rpm  26x12 30x14 34x16 39x18 

  Climb of 8%
    60rpm  26x11 30x12 34x14 39x16 
    90rpm  26x16 30x18 34x21 39x24 

  Climb of 10%
    30rpm  39x10 
    60rpm  26x13 30x15 34x17 39x20 
    90rpm  26x20 30x23 34x26 39x30 

  Climb of 12%
    30rpm  34x10 39x12 
    60rpm  26x16 30x18 34x21 39x24 
    90rpm  26x24 30x28 34x31 39x36 

  Climb of 14%
    30rpm  30x11 34x12 39x14 
    60rpm  26x19 30x21 34x24 39x28 
    90rpm  26x28 30x32 34x36 39x42 

Cyclist generating 400W

  Climb of 6%
    Note: speed > 20kmh, air resistance significant
    90rpm  30x10 34x12 39x13 

  Climb of 8%
    Note: speed > 20kmh, air resistance significant
    60rpm  34x10 39x12 
    90rpm  26x12 30x14 34x16 39x18 

  Climb of 10%
    Note: speed > 20kmh, air resistance significant
    60rpm  26x10 30x11 34x13 39x15 
    90rpm  26x15 30x17 34x20 39x22 

  Climb of 12%
    60rpm  26x12 30x14 34x16 39x18 
    90rpm  26x18 30x21 34x23 39x27 

  Climb of 14%
    30rpm  39x10 
    60rpm  26x14 30x16 34x18 39x21 
    90rpm  26x21 30x24 34x27 39x31 

Python 3 code:

circumference = 2.14  # m (measured from rolling road bike)
g = 9.8  # m/s2 (gravitational acceleration)
mass = 8 + 65  # kg (bike + me)
front_gears = [26, 30, 34, 39]  # teeth
gradients = [6, 8, 10, 12, 14]  # %
powers = [100, 200, 300, 400]  # W
cadences = [30, 60, 90]  # standing, grinding, spinning

for power in powers:
    print(f'\nCyclist generating {power}W')
    vertical_speed = power / (mass * g)  # m/s
    for gradient in gradients:
        print(f'  Climb of {gradient}%')
        horizontal_speed = vertical_speed * 100 / gradient  # m/s
        if horizontal_speed * 3.6 > 20:
            print('    Note: speed > 20kmh, air resistance significant')
        wheel_rpm = 60 * horizontal_speed / circumference
        for cadence in cadences:
            subtitle = False
            for front_gear in front_gears:
                gear_ratio = cadence / wheel_rpm
                rear_gear = int(front_gear * gear_ratio + 0.5)  # round
                if rear_gear > 9 and (rear_gear <= 50 or not subtitle):
                    if not subtitle:
                        print(f'    {cadence}rpm  ', end='')
                        subtitle = True
                    print(f'{front_gear}x{rear_gear} ', end='')
            if subtitle:


[Programming] React plotting - visx

From: andrew cooke <andrew@...>

Date: Thu, 10 Dec 2020 17:25:49 -0300

Thought I'd share experience / a recommendation for a library for plotting
with React.

Unfortunately the React wrapper for Sencha / Ext (which is what Mihaela demoed
last Thursday) is not available for free (AFAICT).

So if you're working on the cheap you need to look at options like Recharts,
react-vis, or nivo.  I tried those and Recharts seemed best, but they were all
'opinionated' high-level libraries aimed more at business infographics than
displaying 'scientific' information (IMHO).  And because they are high level
it's difficult to adjust them if your aims are elsewhere.  For example, with
Recharts it was difficult to get an auto-scaling linear axis with 'nice'
intervals - it has much better support for data in groups.

In the end, then, I ended up with visx, which is a lower-level library.  There
is no API for a 'plot' or 'chart' - you need to piece things together from
lines, axes, etc.  But it works well and appears to be reliable.

One problem with a lower-level library is that you describe everything in
pixel coordinates.  Which can be a problem in a responsive context where the
size is not known when React runs

The solution is to use a React hook that provides the width of a component and
then iterate (so render blank, get called again via the hook, and render with
the known width).  This is not trivial - if the new render changes the width
you get an infinite loop - but can be made to work (in fact, this was a bug in
nivo, which I didn't understand until the same logic was made explicit in
visx).  I used useDimensions from react-recipes.

[Edit:] It turns out that visx has a component (ParentSize) that does exactly
this, so I dropped react-recipes/useDimensions and used that.

Documentation for visx is good, but not perfect.  There's an API guide and a
lot of examples but nothing inbetween - nothing to tell you what to do.  This
means that you need to read the code, which put me off at first, but turned
out to be a good thing.  It's nicely written code and includes useful
patterns.  The API guide seems to (I may have overlooked something or be
confused) assume that you understand what attributes are passed through to SVG

Anyway, the end result was a declarative, interactive (you can move a slider
to change the display), responsive (using MUI breakpoints) plot.  Happy.

Finally, note that most of these libraries are SVG-based (nivo includes canvas
support but it was buggy for me) so not suitable for huge amounts of data.



[Programming] React Leaflet

From: andrew cooke <andrew@...>

Date: Wed, 18 Nov 2020 17:29:13 -0300

If you're using Leaflet (the map presenatation library) in React, you will
probably use React Leaflet.  There are a few things you should know:

* It was just rewritten as v3.

* The rewrite is functional React (hooks etc) and works just fine.

* But it breaks all the examples of event handling on the web.

* And it breaks all the JSFiddle examples.

* And you probably forgot to add the leaflet CSS to your website.

But once you have it working, it really does work.



AliExpress Independent Sellers

From: andrew cooke <andrew@...>

Date: Sat, 14 Nov 2020 09:59:36 -0300

Not sure the title makes much sense - these are AliExpress stores that sell
their own branded cycling tops (rather than copies of other people's designs).
This list isn't exhaustive and may contain errors, but it's a useful starting
point for trying to find sellers that are doing more than pushing copies (you
might hope these companies are more invested in making something exceptional -
perhaps better quality materials, a different style, or better service).

There's quite a mix here and no obvious way to group, so ordered
alphabetically by name.  I haven't bought from most of these and make no
guarantees about quality or reliability.


Cheji -

All products are own brand.  Designs vary (perhaps aimed at non-western

Cusroo -

Most products are own brand.  Designs vary.  Indonesian?

Darevie -

All(?) products are own brand, with some shots using own model.  Designs tend
to be geometric patterns.

Frenesi -

Most of the products are copies, but those branded Frenesi appear to be
own-designs.  Generally simple designs.


All(?) products are own brand.  Designs are generally garish novelty items.


All products are own brand.  Designs vary (like Cheji, maybe aimed at
non-western market?).

Pimmer -

A few products are own brand (beware - many appear own brand but contain other
company logos on close inspection).  Minimal designs.  Possibly experimenting
with different fabrics / cuts (see for example).

Racmmer -

All products are own brand.  Designs are, well, mixed.  Well known and high
volume.  A bit more mass-market that Santic or Spexcel (but I Like their pro

Rion -

All(?) products are own brand, with some shots using own model.  Designs are
basic.  Seems to be fairly well known / large volume (like Santic, Spexcel,

Runchita -

Most products are copies, but Runchita in the catgeory menu selects
own-branded.  Designs vary.

Santic -

All products are own brand.  Has an identity outside Aliexpress and tends to
be more expensive.  Shots with own model.  Generally simpler designs.

Spexcel -

All(?) products are own brand, with some shots using own model.  Designs are
simple (Rapha influenced?) with horizontal bands.  Have bought from here -
seem to experiment more with different fabrics and designs.  Fairly well

Swiftofo -

All products are own brand.  Designs are generally simple, but looks like they
may be aiming at a more athletic (triathlon?) market - more athletic cuts and
different materials.  Many shots using own model.


[Politics] Back + US Elections

From: andrew cooke <andrew@...>

Date: Sun, 8 Nov 2020 16:56:34 -0300

Well, this is something of a test post to see if posting here is working

Also, we are just post-US elections, which were really worrying.

There's a lot of euphemisms being thrown around.  On 538 people keep talking
about how 'this kind of politics' is not going to go away.  They mean Fascism.
A huge chunk of the country made clear they thought continuing to support an
openly fascist regime was a good idea.  Call it by it's name.



[Programming,Exercise] Simple Timer Script

From: andrew cooke <andrew@...>

Date: Sun, 29 Mar 2020 17:29:17 -0300

The following may be useful to someone doing exercises at home during
quarantine.  It will only work on Linux since it uses ALSA for sound.

For example

  time 1 -x 3 -d 5

will give 3 beeps, 1 second apart, 5 seconds after you hit return.

If there is a delay the start is indicated by a high-pitched beep.
The last interval is ended with a low-pitched beep.



function beep() {
    ( speaker-test -Dsysdefault -t sine -f $FREQ 1> /dev/null 2> /dev/null)& pid=$! ; sleep $TIME ; kill $pid 2>&1


while (( "$#" )); do
    if [ "$1" == "-h" ]; then
	echo "timer (mainly for timing exercises)"
	echo "usage:"
	echo " $0 TIME [-x N] [-d SECS]"
	echo " -x N     repeat N times"
	echo " -d SECS  wait SECS before starting"
	exit 1
    elif [ "$1" == "-x" ]; then
    elif [ "$1" == "-d" ]; then

if [ -z ${DURATION+x} ]; then
    echo "no duration (see $0 -h)"
    exit 2

echo "duration: ${DURATION}s"
echo "delay: ${DELAY}s"
echo "count: ${COUNT}"

if [ $DELAY -gt 0 ]; then
    sleep $DELAY
    beep 2000 0.5

while [ $LOOP -lt $COUNT ]; do
    LOOP=$(( $LOOP + 1 ));
    echo "$LOOP"
    sleep $DURATION
    if [ $LOOP -eq $COUNT ]; then
	beep 500 0.1
	beep 1000 0.1


[Politics] The world's most-surveilled cities

From: andrew cooke <andrew@...>

Date: Thu, 19 Sep 2019 17:23:23 -0300



[Bike] Hope Freehub

From: andrew cooke <andrew@...>

Date: Sat, 31 Aug 2019 21:54:18 -0400

Some notes on the Hope specific freehub and related cassettes.  In
general, more force is needed than you might expect when manhandling
these things.

 * The two-piece cassette is difficult to separate.  It helps to cool
   the entire cassette (since aluminium has a higher thermal expansion
   coeff than steel) and then lever them apart with something wooden.

 * Putting them back together is similarly fraught.  The spot on the
   aluminium spider aligns with the biggest gap in the internal
   'teeth' of the steel part.  Aligned correctly you can see all teeth
   should be OK (any other alignment and some teeth are 'blocked').
   With that, place the alloy side down on a flat wooden surface and
   then lean on the steel part (I used some stiff gloves to protect my
   hand and a fair amount of weight).

 * Removing end-caps on the front hub is easily done by pushing them
   out with a pencil from the other side.

 * Removing the existing freehub was possibly by clamping it in a
   wooden vice and pulling the hub upwards.

 * The replacement freehub goes on easily enough - you need to push
   the pawls into place - but the green seal again needs a fair amount
   of force from a wooden implement before it clicks inside.

 * The funny looking endcap is QR; the shorter normal endcap is
   12x135; the longer 12x142.

And hope are responsive (if not overly effusive) to enquiries at the
normal email address.



[Restaurant] Mama Chau's (Chinese, Providencia)

From: andrew cooke <andrew@...>

Date: Sat, 31 Aug 2019 21:10:22 -0400

Really excellent food.  A refreshing change.

We went here Friday evening, a belated celebration of Paulina's
birthday.  Fairly early, because they close some time around 8pm.
Paulina ordered a selection of dumplings and a bao (stuffed steamed
bread); I ordered a crepe.  Sharing, so that we sampled as much as
possible, there was more than enough for us both.  Relatively healthy
food with plenty of taste that was still solid enough to leave you
contentedly full.

It has a very small eating area, but also does take-aways.  Everyone
else appeared to be half our age.  It was very popular, perhaps
because of this recent review - - or
perhaps because it's damn good.

Service is minimal - you order and receive a pinger.  When the pinger
pings you go collect your tray of food.  There's a fair amount of
packaging, but it's mainly paper-based.