Russell's Blog

New. Improved. Stays crunchy in milk.

Why doesn't your lab have a 3D printer yet?

Posted by Russell on November 20, 2012 at 6:30 a.m.
Electrophoresis setups are like Tupperware. You can never find the right lid when you need it, and someone always seems to be borrowing the doohicky you need.

Here in the Eisen Lab, it turns out we've been using Marc Facciotti's electrophoresis stuff for years. He keeps his stuff organized, and, well... that's not been our strong suit lately. John, our lab manager, has been gently but inexorably herding us towards a semblance of respectability in our lab behavior. As part of this, he decided that it was time for us to get our electrophoresis stuff straightened out. So, he ordered a bunch nice of gel combs from one of our suppliers. They cost $51 each (see the "12 tooth double-sided comb", catalog number 669-B2-12, for the exact one pictured below). We bought six of them with different sizes and spacing, for a total exceeding $300.

While I appreciate that companies need to make money, this is a ridiculous price for a lousy little scrap of plastic. $300 for a couple of gel combs is cartel pricing, not market pricing. Fortunately, we happen to have a very nice 3D printer. It is very good at making little scraps of plastic. So, I busted out the calipers and tossed together some models of gel combs in OpenSCAD. A few minutes of printing later, and the $51 gel combs are heading back to the store.

Here's the code for the six well 1.5mm by 9mm comb :

      cube( [ 80, 27, 3 ] );
      translate( [ 5.25, 14.3, f ] ) cube( [ 68, 9.3, 7.25 ] );
    for ( i = [ 0:5 ] ) {
      translate( [ 17.1+i*11.0, -f, -f ] ) cube( [ 1.75, 12, 5 ] );
    translate( [ -f,   -f, -f ] ) cube( [ 7,  12, 7] );
    translate( [ 73+f, -f, -f ] ) cube( [ 7,  12, 7] );
    translate( [ 0,    -f, 1.6] ) cube( [ 80, 12, 8] );
Pretty easy to grasp, even if you've never seen SCAD before.

So, how much did this cost?

I ordered this plastic from ProtoParadigm at $42 for a kilogram. That's about four pennies a gram. Each of these gel combs cost about 21 cents to print. That's 1/243rd the price.

The 3D printer cost €1,194.00 ($1524.62), which is less than the laptop I use for most of my work. The savings on just these gel combs has recuperated 18% of the cost of the printer.

It's also important that I was able to make some minor improvements to the design. The printed combs fit into the gel mold a bit better than the "official" ones. I also made separate combs for the 1.0mm and 1.5mm versions, and the labels are easier to read. If I wanted, tiny tweaks to my SCAD file would let me make all sorts of fun combinations of thicknesses and widths that aren't available from the manufacturer. So, these gel combs are not only 1/243rd the price, but they are also better.

If you read the media hype about 3D printing, you will undoubtedly encounter a lot of fantastical-sounding speculation about how consumers will someday be able to print living goldfish, or computers, or bicycles. Maybe so. Maybe not. However, right now, you can print basic lab supplies and save a pile of money.

Buy your lab manager a little FDM printer and hook them up with some basic CAD training. Yes, the printer will probably mostly get used to make bottle openers and Tardis cookie cutters. So what? Your paper-printer, if you will excuse the retronym, mostly gets used for non-essential stuff too. I'd wager that for every important document printed in your lab, a hundred sheets have gone to Far Side cartoons and humorous notices taped up in the bathroom. It's a negligible expense compared to the benefits of having a machine that spits out documents when you really need them, and the social value of those the Far Side cartoons probably sums to a net positive anyway.

Conclusion : If you have a lab, and you don't have a 3D printer, you are wasting your money. Seriously.

In the time it took write this post, I printed $150 worth of gel combs, and it cost less than a cup of coffee.

Updates : Here is the tweet I originally posted about this article, before the URL for it vanishes into Twitter's memory hole. Here's an encouraging post from the Genome Web blog, and a nice article by Tim Dean at Australian Life Scientist. My article here seems to have spawned a thread on BioStar. Also, it made Ed Yong's Missing Links for November 24 over at Discover, and Megan Treacy did a really spiffy article over at Treehugger.

Many people have asked, and so I decided to see how well these kinds of 3D printed parts do in the autoclave. I tried it out with a couple of bad prints, and they seemed to hold up just fine after one or two cycles. Very thin parts did warp a bit, though, so I recommend printing parts you plan to autoclave nice and solid. Here is a before and after of a single-wall part (less than half a millimeter thick). I was expecting a puddle.

Update 2 : Check out Lauren Wolf's awesome article in Chemical & Engineering News, featuring the infamous gel combs, among other things!

Trouble with SoftSerial on the Arduino Leonardo

Posted by Russell on May 25, 2012 at 12:05 p.m.
While I was wandering around at Maker Faire last weekend, I heard someone say, "Woah, is this the Leonardo?" And lo, there was a handful of Arduino Leonardo boards lined up on a shelf for sale. I instantly grabbed one, and bundled it home to play with it.

The Leonardo is Arduino's latest board, announced last September. It uses the Atmega32u4 chip, which has onboard USB. This has two important implications; first, the Leonardo costs less than the Uno, and second it will be able to operate in any USB mode. That means people can make Human Interface Devices (HID), like mice and keyboards and printers, with Arduino, and present themselves to the host using the standard USB interfaces for those devices. That means you can build things that don't need to talk via serial, and use the host's built-in drivers for mice and printers and whatnot. This is a big step forward for Open Hardware.

Anyway, I'm developing an little remote environmental data logger to use for part of my dissertation project, and I thought I'd see if I could use the Leonardo board in my design. I'm using the Arduino board to talk to an Atlas Scientific pH stamp, which communicates by serial. It works fine on the Uno with SoftwareSerial (formerly known as NewSoftSerial until it was beamed up into the Arduino Core mothership).

Unfortunately, it didn't go so well on the Leo. The board can send commands to the pH stamp, but doesn't receive anything. I swapped in an FTDI for the pH stamp, and confirmed that the Leonardo is indeed sending data, but it didn't seem to be able to receive any characters I sent back. I tried moving the rx line to each the digital pins, and had no luck. Here is my test program :

#include <SoftwareSerial.h>

#define rxPin 2
#define txPin 3

SoftwareSerial mySerial( rxPin, txPin );

byte i;
byte startup = 0;

void setup() {

  mySerial.begin( 38400  );
  Serial.begin(   9600   );

void loop() {
  if( startup == 0 ) {             // begin startup
    for( i = 1; i <= 2; i++ ) {
      delay( 1000 );
      mySerial.print( "l0\r" );    // turn the LED off
      delay( 1000 );
      mySerial.print( "l1\r" );    // turn the LED on
    startup = 1;                   // don't re-enter
  }                                // end startup

  Serial.println( "taking reading..." );
  mySerial.print( "r\r" );
  Serial.println( mySerial.available() );
On the Uno, I see the number increasing as the read buffer fills up :
taking reading...
taking reading...
taking reading...
taking reading...
taking reading...
On the Leo, it seems that nothing ever gets added to the read buffer, no matter how any characters I send over from the FTDI or which pins I used for the rx line :
taking reading...
taking reading...
taking reading...
taking reading...
taking reading...
taking reading...
I really wanted to see if I was crazy here, but I'm one of the first people among the General Public to get their hands on a Leonardo board. So, I started talking with Ken Jordan on #arduino on Freenode (he goes by Xark) who has a similar board, the Atmega32u4 Breakout+. It's based on the same chip as the Leonardo, but it has different pinouts and a different bootloader. He flashed the Leonardo bootloder onto his board, and worked out the following pin mapping :
Arduino 1.0.1     Adafruit         ATMEL
digitalWrite pin  atmega32u4+ pin  AVR pin function(s)
----------------  ---------------  ------------------
D0                D2               PD2 (#INT2/RXD1)
D1                D3               PD3 (#INT3/TXD1)
D2                D1               PD1 (#INT1/SDA)
D3#               D0               PD0 (#INT0/OC0B)
D4/A6             D4               PD4 (ICP1/ADC8)
D5#               C6               PC6 (OC3A/#OC4A)
D6#/A7            D7               PD7 (T0/OC4D/ADC10)
D7                E6 (LED)         PE6 (INT6/AIN0)
D8/A8             B4               PB4 (PCINT4/ADC11)
D9#/A9            B5               PB5 (OC1A/PCINT5/#OC4B/ADC12)
D10#/A10          B6               PB6 (OC1B/PCINT6/OC4B/ADC13)
D11#              B7               PB7 (OC0A/OC1C/PCINT7/#RTS)
D12/A11           D6               PD6 (T1/#OC4D/ADC9)
D13#  (LED)       C7               PC7 (ICP3/CLK0/OC4A)
D14   (MISO)      B3               PB3 (PDO/MISO/PCINT3)
D15   (SCK)       B1               PB1 (SCLK/PCINT1)
D16   (MOSI)      B2               PB2 (PDI/MOSI/PCINT2)
D17   (RXLED)     B0               PB0 (SS/PCINT0)
D18/A0            F7               PF7 (ADC7/TDI)
D19/A1            F6               PF6 (ADC6/TDO)
D20/A2            F5               PF5 (ADC5/TMS)
D21/A3            F4               PF4 (ADC4/TCK)
D22/A4            F1               PF1 (ADC1)
D23/A5            F0               PF0 (ADC0)
-     (TXLED)     D5               PD5 (XCK1/#CTS)
-     (HWB)       -  (HWB)         PE2 (#HWB)
This was derived from the ATmega 32U4-Arduino Pin Mapping and ATMEL's datasheet for the ATmega32U4 chip. Once that was worked out, he flashed my test program onto his board, and also found that SoftwareSerial could transmit fine, but couldn't receive anything.

Ken rummaged around a little more, and had this to say :

The SoftSerial seems to use PCINT0-3 so there seems to me a minor problem in Leo-land in that only PCINT0 appears to be supported (and it is on "funky" output for RXLED). Hopefully I am just misunderstanding something (but it imay be the interrupt remap table is incorrect for Leo).
Then he disappeared for a little while, and came back with :
I have confirmed my suspicion. When I disassemble SoftSerial.cpp.o I can see that only __vector_9 is compiled (i.e., one of 4 #ifdefs for PCINT0-3) and the interrupt vector 10 is PCINT0 (0 is reset vector so offset by one makes sense). So, unless you hook serial to RXLED pin of CPU I don't believe it will work with the current libs.

Also I believe the Leo page is just wrong when it says pins 2 & 3 support pin change interrupts (I think this was copied from Uno but it is incorrect, the only (exposed) pins are D8 D9 D10 and D11 that support PCINT according to the ATMEL datasheet (and these are PCINT 4-7 not the ones in the interrupt mapping table AFAICT).

I believe this is where I can stop worrying that I'd be wasting the time of the core Arduino developers, and say quod erat demonstrandum; it a bug in SoftwareSerial. Hopefully they can update the Arduino IDE before the boards hits wider distribution.

Update : So, it turns out that this is a known limitation of the Leonardo. David Mellis looked into it, and left this comment :

You're right that the Leonardo only has one pin change interrupt, meaning that the software serial receive doesn't work on every pin. You should, however, be able to use pins 8 to 11 (inclusive) as receive pins for software serial. Additionally, the SPI pins (MISO, SCK, MOSI) available on the ICSP header and addressable from the Arduino software as pins 14, 15, and 16 should work.
He is, of course, correct. I'm not sure why my testing didn't work on pins 8-11, but they do indeed work fine. Unfortunately, this means that the Leonardo is not compatible with a number of cool shields. The Arduino SoftSerial Library Reference documentation has been updated with a more detailed list of limitations.

Sneak Pique

Posted by Russell on July 13, 2011 at 3:41 a.m.
I'm about to release a new piece of Open Source software; it's a fast, fully automated and very accurate analysis package for doing ChIP-seq with bacteria and archaea. I'm doing my best to avoid the sundry annoyances of of bioinformatics software; it uses standard, widely used file formats, it generates error messages that might actually help the user figure out what's wrong, and I've designed the internals to be easily hackable.

I have two problems, though.

Most of the people who will be interested in using this software are microbiologists and systems biologists, not computer people. At the moment, the software is a python package that depends on scipy, numpy and pysam. I used setuptools, wrote tests with nose, and hosted it on github. If I were going to distribute it to experienced Linux users, it's basically done. However, installing these dependencies on Windows and MacOS is a showstopper for most people -- scipy in particular. So, how would you suggest distributing it to MacOS and Windows users?

The second problem is... it's kind of ugly. I wrote the GUI in Tk, which is not particularly great in the looks department. Should I bother creating native tooklkit GUIs? Would that make people significantly more comfortable? Or would it be a waste of time?

Bioengineering side project

Posted by Russell on February 22, 2011 at 5:10 p.m.
I've been working on a little bioengineering side project, and I just finished putting together a working version of the firmware. It'll probably take some refinement, but I've managed to get the microcontroller to do what I need it to do -- measure visible light irradiance over a wide range of intensities.

This is the light intensity in microwatts per square centimeter measured at about a 0.3 second resolution. I haven't done any of the actual bio- part of the bioengineering, so for the moment the light curve is the beginnings of a sunset at Mishka's cafe.

I'll post more about this once I have the prototype working. Next up, 3D printing!

Needed : Django hacker

Posted by Russell on December 23, 2010 at 6:41 p.m.
So, back in 2008, I ported fro Rails to Django. It was about one evening of hacking, and since then I haven't touched the code, nor have I upgraded to Django 1.0. All I've done is patch security holes. Since then, a whole new comment framework was introduced, the one I'm using has been obsoleted, and a whole bunch of other changes have happened.

I've been much too busy to maintain or upgrade the code, and it wasn't very good to begin with. So, I'd like to hire someone who actually has some professional experience with Django to institute a do-over. I can handle importing all my articles and comments myself, even if the DB model is a little different. If you can do some nice graphic design, I'll happily pay extra for that.

As it happens, our lab is looking to hire a full-time web developer for a very awesome new project. If I'm impressed with the way you handle this simple little project, you can consider it a job interview for the full time position in our lab. And honestly, how often do you get paid for a job interview?

Email me your resume and a portfolio if you are interested.

Fun with de Bruijn graphs

Posted by Russell on October 29, 2010 at 4:34 a.m.
One of the projects I'm working on right now involves searching a better approaches to assembling short read data metagenomic data. Many of the popular short read assembly algorithms rely on a mathematical object called a de Bruijn graph. I wanted to play around with these things without having to rummage around in the guts of a real assembler. Real assemblers have to be designed with speed and memory conservation in mind -- or, at least they ought to be. So, I decided to write my own. My implementation is written in pure Python, so it's probably not going to win any points for speed (I may add some optimization later). However, it is pretty useful if all you want to tinker around with de Bruijn graphs.

Anyway, here is the de Bruijn graph for the sequence gggctagcgtttaagttcga projected into 4-mer space :

This is the de Bruijn graph in 32-mer space for a longer sequence (it happens to be a 16S rRNA sequence for a newly discovered, soon-to-be-announced species of Archaea).

It looks like a big scribble because it's folded up to fit into the viewing box. Topologically, it's actually just two long strands; one for the forward sequence, and one for its reverse compliment. There are only four termini, and if you follow them around the scribble, you won't find any branching.

raygun : a simple NCBI BLAST wrapper for Python

Posted by Russell on October 25, 2010 at 11:42 a.m.
Things have been a little quiet on for the last couple of weeks, but a lot of frantic activity has been going on behind the peaceful lack blog updates. When I returned from Kamchatka, Jonathan had a little present for me -- he took the DNA from the 2005 Uzon field season for Arkashin and Zavarzin hotsprings, and ran a whole lane of paired-end sequencing on one of our Illumina machines. Charlie made some really beautiful libraries, and the data is really, really excellent. For the last couple of weeks, I've been trying to do justice to it.

I'm not quite ready to talk about what I've been finding, but I thought I would take a moment to share a little tool I wrote along the way. It's made my life a lot easier, and maybe other people could get some use out of it.

It's called raygun, a very simple Python interface for running local NCBI BLAST queries. You initialize a RayGun object with a FASTA file containing your target sequences, and then you can query it with strings or other FASTA files. It parses the BLAST output into a list of dictionary objects, so that you can get right to work.

It doesn't take a lot of scripting chops to do this without an interface, of course, and there are other Python tools for running BLAST queries. The advantage of raygun over either the DIY approach or the BioPython approach is that raygun is extremely simple to use. I wanted something that would basically be point-and-shoot :

import raygun
import cleverness

rg = raygun.RayGun( 'ZOMG_DNA_OMG_OMG.fa' )

hits = rg.blastfile( 'very_clever_query.fa' )

results = []
for hit in hits :
    results.append( cleverness.good_idea( hit[ 'subject' ] ) )

cleverness.output_phd_thesis( results )
Unfortunately, you must furnish your own implementation of the cleverness module.

I designed raygun is with interactive use in mind, particularly with ipython (by the way, if you do a lot of work in python and you're not using ipython, you're being silly). The code is available on github.

Updates, continuing

Posted by Russell on August 15, 2010 at 4:59 p.m.
My apologies for getting behind on posting my updates from Uzon. After we returned from Uzon, we rested for a day, and then crammed ourselves and our equipment into a van and went to Peratunka for the Biodiversity, Molecular Biology and Biogeochemistry of Thermophiles international workshop, where I was scheduled to give a 20 minute talk.

The speaking docket got shuffled around a lot, and I ended up having to give my talk much earlier than planned. I suppose this is the inevitable downside of procrastination. While I was scrambling to finish it, I didn't have much time for blog updates!

I survived the talk. There were lots and lots of excellent questions, and I have a lot to think about now. Anyway, back to the updates from Uzon.

Rooted phone

Posted by Russell on September 12, 2009 at 4:05 a.m.
I finally got fed up with the pathetic official Android release from T-Mobile, and rooted my G1 and installed the CyanogenMod firmware. Cyanogen feels about twice as responsive as Cupcake! It's like a whole new device.

Also, the tethering app is awesome. It turns your G1 into a WiFi base station and routes traffic from WiFi to 3G. Since I'm still waiting for broadband at my new apartment, it's a lifesaver.

I suppose tethering (and rooting the phone) technically violates T-Mobile's TOS, but I'm convinced that T-Mobile will allow both sooner or later. It's just too awsome, and it would help them sell more contracts.

It's kind of difficult to abuse tethering anyway; it sucks down the battery very quickly, and the latency is significant. It's the sort of thing you'd only use in a pinch. Those happen to be the situations where a little benevolence or selfishness from a big company can shape a customer's opinion forever. T-Mobile seems to be more sensitive to that kind of thing than the other networks. I know they've got their reasons for banning tethering apps, but I think they could be convinced to change their minds. (You can download various petitions from the Android Marketplace.)

Openness is where Google and T-Mobile could really go after the unwholesome, anticompetitive and un-American AT&T/iPhone alliance. The open nature of Android is a step in the right direction, but T-Mobile needs to get its legal department on the Open Access bandwagon if it wants to press the advantage.

After all, if some random people on the internet can roll better firmware for the G1 than their in-house developers, isn't it a strategic business advantage to let them?

The nocturnal velocity of cats

Posted by Russell on September 12, 2009 at 2:09 a.m.
I've been trying and failimg to get to sleep. The average interval of cat bombardment, and the mean momentum of the incident cat field has been unusually high. It took me a while to realize that this was related to the weather; it is lightly sprinkling. My cats have never seen rain.

That's a sobering thought in two ways. On one hand, they are old enough to have very well developed personalities, and yet are younger than the Obama administration by several months. On the other hand, it's been that long since it rained.

A busy month

Posted by Russell on September 09, 2009 at 8:06 p.m.
This summer has turned out a bit like a chocolate bar in the sun; everything started out nice and neat, but everything ended up squished into the bottom of the wrapper.

Right now, I'm working with Andrey Kislyuk on our little piece of the DARPA FunBio project. We're in the middle of a two-week code sprint, so I'll save that for a later post.

I also moved to a new apartment, and that didn't go nearly as smoothly as it could have. The guy we subleased from was in the process of buying a house, and the loan underwriter decided to yank back the money after he'd closed escrow (or was in escrow, or something). Evidently they wanted a sworn affidavit from the gardener that he was contracted to take care of the grounds. Anyway, the upshot was that instead of a nice leisurely move, he got stuck in the apartment for three weeks longer than he expected, and I was homeless for a week. Fortunately, one of the staff scientists in our lab was generous enough to let me stay at his apartment. Neil and Buzz got to learn about stairs, which they evidently adore.

Over Labor Day weekend, I went with Srijak and some of his friends from San Diego on a day hike at Lassen Volcanic National Park. I've always loved California, but it's nice to be reminded from time to time exactly why I love this place so much.

Because it's awesome.

Android usability fail nuber two

Posted by Russell on July 28, 2009 at 6:42 p.m.
If you should happen to be filling out a longish web form (say, for example, a blog post) using the Android browser, and someone calls you, the browser will dump all your windows and obliterate the POSTDATA. Gee, thanks. That's exactly what I wanted.

Come on people.

The Davis Crash Map

Posted by Russell on April 29, 2009 at 12:02 a.m.
I want announce a little project I put together over the weekend. For want of a better name, I'll call it the Davis Crash Map. Basically, I analyzed the accident report spreadsheets from the City of Davis Public Works Department, and made an overlay for Google Maps to visualize the data. The spreadsheets are a bit difficult to analyze, so I'm leaving out the reports that aren't clear to me (about 15% of the reports). The reports that gave me some trouble seem to be randomly distributed over the city, so the overlay should still give an unbiased picture of what is happening.

In particular, this is map is intended to examine bicycle accidents. I hope people will look at this map, and think about how they behave on the roads, weather on foot, on a bicycle, or in a car. How you behave on the road has direct, and sometimes dire, consequences for you and for other people.

However, there is more to this than behavior. This is also a design question. Roads are not natural features. They are designed and built by people for use by people. As with anything that is made by humans, there are good designs and bad designs. These designs have a real impact on peoples' lives. In the case of streets, the impact on your life can be very literal, as this map shows.

Even good designs can always be improved. Davis is a pretty safe town in which to walk, bicycle and drive. But if you study this map, and think about it as you go about the town, it's also clear that things could be better.

I'm not a traffic engineer, or a civil engineer, or a city planner. I claim no expertise in those areas. I'll leave it to other people to make specific suggestions. However, I think it is important for the users of streets -- pretty much everybody -- to think about what kind of streets they want. This map should help give you a better idea of what kind of streets we actually have.

For some reason, people seem to get very emotional about traffic. I grew up in Los Angeles, home of the nation's worst traffic jams. Perhaps this is to make up for our lack of a professional football franchise. Passions about transportation, especially mundane things like parking spaces and HOV lanes, get people really worked up. Los Angeles is also famous for road rage, and nowhere is it in greater evidence than in the corridors of City Hall. Public meetings on traffic can make I-405 look like afternoon tea. In fact, thousands of people from all over the world tune into the internet broadcast of the Santa Monica city council meetings to listen to Californians scream at each other over the exact position of little blobs of paint on little strips of asphalt.

What the conversation needs, I think, is some perspective. Data can help provide that perspective, especially if it can be represented in a way that is easy to understand. Maps are good at that.

If you will indulge me, I'd like to share my perspective on this data. Each marker represents a traumatic event for someone. Under some of those markers, a life came to a sudden, violent end. I'd like to share a picture of what kind of event a marker on this map represents. You won't find a marker for this event because it happened in Norman, Oklahoma, a college town that is a lot like Davis.

Anna and me

In October of 2007, my little sister was riding her bicycle near her house. A lady in a Mercedes made a lazy left turn, and crossed onto the wrong side of the road. She hit Anna head-on. Anna went up and over the hood of the car, and face-planted on the windshield, breaking her nose and her front teeth. The lady slammed on the breaks, and Anna then went flying off the car and slammed her head on the pavement. That much is clear from where my mother photographed the tire marks, the blood stains, and scattered teeth.

Who designed this street, anyway?

The sequence of events afterward are a little unclear, since Anna does not remember anything from that day, or for several days before and after the accident. The police report includes several details that are impossible or don't make any sense; for example, the officer thought she was coming out of a driveway onto the street, but the driveway did not belong to anyone she knew, and was paved in gravel (extremely annoying to bicycle on). The report also places the accident on the wrong side of the street, which was obvious enough based on the tire marks and blood. Based on what her friends say she was doing -- biking from her house to a friend's house -- she would have just been pedaling along the side of the road. The details of what happened are somewhat unclear, other than the evidence left on the road and gouged onto my sister's face.

After hitting the pavement, she evidently got up and staggered around for a bit, and then collapsed. She stopped breathing, and officer on the scene couldn't find a pulse, and assumed that she was dead. This was the reason given for not immediately summoning an ambulance.

Then she suddenly revived and started mumbling. The lady who ran her down went into screaming hysterics, and had to be restrained (or evacuated, or something). It was only then that an ambulance was called. From the report, it appears that paramedics and police spent a good deal of time tending to the driver of the car, who was having an anxiety attack, instead of Anna, who was bleeding from massive head trauma.

Anna then spent the next several days in the hospital. My mother got on the next flight to stay with her. For the next several days, Anna went through long and short memory lapses and dizzy spells of various lengths. When I spoke to her on the phone over the next several days, she also had some kind of aphasia, which was very jarring to me because she is normally a very articulate person. And then there was the puking. Brain injuries often come with a heavy dose of overpowering nausea. She was on anti-nausea drugs for a long time after the accident.

It took a long time for he to start feeling "normal" again. Almost two years later, she's still not sure she feels completely normal. Fortunately, thanks to some really great work by her surgeons, she looks normal. Needless to say, she is both very lucky and very tough.

Anna's bicycle. The police kept it as evidence, but allowed my mother to photograph it.

You could say that I have a personal stake in this, and I will not claim to be unbiased. Many people who argue against safety measures that would slow traffic argue their case on the basis of personal responsibility. We are each responsible for our actions, they argue, and if you do something stupid, you are responsible for the consequences. Why should people who don't do stupid things be inconvenienced?

I agree completely. However, if one casts any real issue into the frame of personal responsibility, then things are rarely so simple. Everyone who could act in a situation has responsibilities, even if they are not they are directly involved. When you have the power to prevent something bad from happening, and you choose not to act, then some of the responsibility falls on you. Every unfortunate, stupid thing that happens involves a cast of thousands of silent, but not blameless, bystanders.

We have a responsibility to at least attempt to protect people regardless of what they are doing -- even if it is stupid. This is especially true when it comes to the things we build. We shouldn't, if we can possibly avoid it, build things that injure and kill people. If we can think of ways to make something we build less dangerous, we ought to give it a try.

Anna and Earnie, about a year after the accident.

My little sister was stupid not to wear a helmet that day. The lady in the car was stupid not to have been on the lookout for cyclists. But neither of them deserved what happened. Each of them is obviously bears some measure of responsiblity (and I have my own opinions on how those measures are apportioned), but the city of Norman is also responsible. The city didn't even bother to paint a line down the middle of the road; what was the driver supposed to be on the wrong side of?

Yes, this is about personal responsibility. We, the public, build the roads. We are responsible for the markers on this map, and all the terror, trauma and tragedy they represent. Let's try to do better.

Bike saftey in Davis

Posted by Russell on April 25, 2009 at 11:18 p.m.
I've been tinkering with a little data visualization applet for looking at bicycle crash data in Davis, and I thought this map might be interesting to people. This is a image was generated with Google Maps and a heatmap overlay generated with a gheat tile server.

This is for 168 bicycle accidents that happened between 2004 and 2006. I have a lot more data, but 95% of the work in this little project involves parsing and renormalizing it. Evidently, police reports are not written with data processing in mind! I suppose that makes perfect sense. An officer at the scene of an accident probably has things on her mind besides generating a nice, easy to parse data point for future analysis. The priority seems to be completeness, rather than consistency. My parsing code, for example, has to be able to correctly detect and calculate distances measured in units of "feeet".

I'll release the applet here once I make an interface for it (and get the rest of the data imported). Stay tuned.

A plauge of duplicates

Posted by Russell on April 19, 2008 at 7:47 a.m.
I have a bad habit. I often open my IMAP mailbox directly on the mail host with mutt. This inevitably causes occasional complicated messes when I butterfinger something. For example, a few days ago I accidentally moved all of the read messages to a separate mailbox. I merged the message back in, but not before my desktop, which was patiently monitoring my IMAP mailbox on the other side of town, decided to synchronize a few thousand messages with the IMAP server. This resulted in lots and lots of duplicate messages.

The trouble was, the duplicate messages had different X-IDs so, their MD5 hashes would be different. After fiddling around with formail for a few minutes, I got impatient and banged out this fun little Python hack :

import email, imaplib, getpass

M = imaplib.IMAP4_SSL( '**********' )

typ, data = M.login( getpass.getuser(), getpass.getpass() )
if typ != 'OK' :
    raise Exception, 'Login failed.'

typ, data =
if typ != 'OK' :
    raise Exception, 'Selection failed.'

typ, data = None, 'ALL' )
if typ != 'OK' :
    raise Exception, 'Could not get message IDs.'

id_list = data[0].split()
mids = []
for id in id_list :
    typ, data = M.fetch( id, '(RFC822)' )
    if typ != 'OK' :
        raise Exception, 'Could not fetch message ' + id
    mail = email.message_from_string( data[0][1] )
    mID = mail.get( 'message-id' )
    print mID
    mids.append( (mID, id) )


dupes = []
for i in range(len(mids)) :
    if m[i] == m[i+1] :
        dupes.append( m[i+1] )

print 'Found ' + len(dupes) + ' duplicate messages.'

for m in dupes :
    typ, data = m[1], "+FLAGS", '(\\Deleted)')

print 'Marked ' + len(dupes) + ' for deletion.'

typ, data = M.expunge()

print 'Expunged ' + len(data.split()) + ' messages.'
Duplicates begone!

It's a little annoying that imaplib doesn't have a friendly wrapper function for marking messages for deletion, but m[1], "+FLAGS", '(\\Deleted)') does the job just fine. now running on Django

Posted by Russell on March 27, 2008 at 12:53 a.m.
For the last couple of years, I've been running Typo, a Ruby on Rails blogging tool. It was nice, but there were a few persistent problems I encountered :
  • It was sloooooow. Nothing I did seemed to get it to run faster, even with carefully tuned caching.
  • It was unstable. Typo would run happily for months, and then mysteriously explode. This usually happened while I was traveling, or busy with something more important.
  • It was difficult to fix. Usually, when Typo would come down, it took a few days of research and pestering people to figure out why.
  • The database migrations between versions were awful. You'll notice that the first year of posts don't have any tags. They were deleted by a bad migration. I have backups, but merging them back in is nightmarish.
A lot of the problems I experienced are with older versions of Typo. They've definitely gotten better over the years. But there is one big reason I'm abandoning Typo; I don't want to code in Ruby. I do most of my coding in python. I like python. I seems silly not to use the knowledge I have from doing computational physics in python.

I have used Blogmaker for most of the main elements on my site, but with a fair bit of hacking to make it do more of what I want. I also wrote a Typo-to-Django import utility, if anyone is interested. The URLs are slightly different, so I'm going to watch the 404s for a few days.

Tasty Python Snacks

Posted by Russell on November 06, 2006 at 2:51 p.m.
Languages like Python are great for tasks that require manipulating complicated data. The main complaint about such languages is that they aren't as fast as compiled languages. For scientific computing (and a lot of other things), that's a show-stopper. However...
from weave import inline
a = 25
code = \
	int i = a;
	while( i > 1 ) {
		printf("a number: %d\\n",i);
		i = i / 2;
	return_val = i;
inline(code, ['a'])

a number: 25
a number: 12
a number: 6
a number: 3
My mind just spins thinking of all the horrible, horrible things I can do with that.