24 March 2009

En dash

Today, having noticed the behavior in my literature review, I learned that en dashes are the appropriate punctuation to use between the names of joint authors. For example, "Newton-Krylov methods" should be rewritten as "Newton–Krylov methods."

See this google books result.

16 March 2009

Overwriting lines in terminal output

For weeks I've been wondering, off and on, if there existed an easy way to write information to the terminal output and be able to change it later, to overwrite or overtype or erase or something. I'd seen how curl will update the fraction downloaded and download rate at the end of the line rather than print a new line for every status change; such a feature would be very nice my code, where I could just use one line of output to write information about the time step. I'd seen the "curses" library, but that looked ridiculously complex for what I was asking. (It creates a whole GUI in the terminal rather than allowing simple operations.)

Well, after finally hitting the right keywords in a google search, I came across Python with ANSI escape codes (more ANSI escape sequences here). This is just what I want. All you have to do is to send the '\r' carriage return sequence to move to the beginning of the line, and then another ANSI sequence can erase the line. That behavior is actually just what I would expect based on the descriptions of the escape sequences: '\n', the "new line" character, should create a new line; '\r', which is called the 'carriage return', actually *does* in this contact emulate the behavior of a typewriter carriage returning to the rightmost position. Mostly due to the cross-platform confusion of the "end-of-line" character business, I'd never really thought this was the way it would work. The long and short of it, in a few lines of python code:

from time import sleep
from sys import stdout
for i in range(10):
stdout.write('Time step %03d/%03d ' % (i+1, 10))

The '\r\x1b[K' moves the cursor to the beginning of the line the following ANSI escape sequence clears the line completely. The next line actually writes the code. The trailing spaces are in case some sort of output gets print during the time step: it will go at the end of the line, and assuming it has a newline of its own, the time step at which the output occurs will be preserved on the screen, and the next line will have the actual updating time step information.

10 March 2009

Lessons learned of the evening

To make a long story short, or more precisely, to summarize an hour of frustration: when using SWIG with Python, watch that you actually instantiate an object by calling its constructor, because apparently you can change the values of the class itself without creating an object from it. Then, when you later want to pass an instantiated object to another C++ method, it won't fail with an inscrutable error. You probably don't want to read the rest of this post, but it's preserved for posterity.

I have a struct of data, SourceDataUniform, that I pass to a class called RadiationController. My original code was:
newSource = ImcTransport.SourceDataUniform
newSource.emissionRate = 2.0
newSource.cellIndex = 1

but this failed with TypeError: in method 'RadiationControllerT_addUniformSource', argument 2 of type 'iMc::SourceDataUniform'. Yet I could print the data in the object, and it said it was of type <class 'ImcTransport.SourceDataUniform'>.

I knew I'd gotten the exact same thing to work with a different class, so I wasn't sure if the problem was that I was using a struct, or what.

I spent a good while looking through the SWIG documentation on proxy classes, structs, the ".this" method. Eventually, noting that the similar case that actually worked gave a repr of <ImcPhysics.OpacityModelConstant; proxy of <Swig Object of type 'iMc::OpacityModelConstant *' at 0x305670> >, and realizing that the "this" object was only set in the constructor for the SourceDataUniform Python wrapper, I discovered that I wasn't instantiating an object. Adding parentheses
newSource = ImcTransport.SourceDataUniform()
solved the problem.

09 March 2009

Unit tests again

Life is funny sometimes. It's occasionally the case that one thinks some almost-trivial task is totally unnecessary, yet the "best practice" is to do it anyway&emdash;and the task turns out to catch or prevent a subtle but insidious error.

This evening I started writing unit tests for my code, and the first one on the list was the (almost) dead-simple "check direction vector" routine, which is overloaded on Blitz tinyvector length. The one-D case has to ensure that the cosine is bounded by one. Simple enough, right?
inline bool checkDirectionVector(
const blitz::TinyVector<double, 1>& omega)
return (abs(omega[0]) <= 1.0);

Well, in addition to testing that it called a value of 1.0 and -0.5 allowed, I tested whether -1.1 was disallowed. I was flabbergasted when I got the message
  /---- Unit test tBlitzStuff failed
| in <build/imclib/tests/tBlitzStuff.cpp> on line 32
| !checkDirectionVector(mu)

which corresponded to the code
    mu = -1.1;

What the heck? Then I realized the mistake. My compiler (gcc 4.0) was silently downcasting a double to an integer, truncating -1.1 to -1, and then upcasting the result to the double -1.0, which satisfies the condition.

The solution is simple, to just use std::fabs(omega[0]) instead. But seriously? I never would have expected a failure on that unit test of all places. These things aren't as worthless as one might think.

02 March 2009

Zoidberg, on the economy

Once again, the conservative, sandwich-heavy portfolio pays off for the hungry investor!

01 March 2009

Ok, I like Python now

Whoever said that Perl had to be the language in which to write super-complicated commands all on one line? This Python command will turn a list of lists into a string amenable to writing to a file as MATLAB input:
result = '[' + "\n ".join([" ".join(["%6g" % val for val in line]) for line in array]) + ']'

EDIT: using generator comprehensions rather than list comprehensions for greater efficiency (thanks, Mike):result = '[' + "\n ".join(" ".join("%6g" % val for val in line) for line in array) + ']'