Developing scientific algorithms is a highly iterative process often involving changing lots of parameters that I will want to vary either as part of my experimental design or as part of tweaking algorithm performance. What strategies can I take for structuring these parameters so that I can easily change them between iterations and so that I can easily add new ones?
It is cumbersome for the user to specify every aspect of an algorithm. If the algorithm allows nested components, then no finite number of options would be sufficient. Therefore, it is critical that options do not necessarily "bubble up" to the top level, as in the case of explicit arguments or template parameters. This is sometimes called the "configuration problem" in software engineering. I believe PETSc has a uniquely powerful system for configuration management. It is similar to the Service Locator pattern in Martin Fowler's essay on inversion of control.
PETSc's configuration system works through a combination of user-specified configuration managed by the solver objects (with get and set queries) and the Options Database. Any component of the simulation can declare a configuration option, a default value, and a place to put the result. Nested objects have prefixes which can be composed, such that every object that needs configuration can be addressed independently. The options themselves can be read from the command line, environment, configuration files, or from code. When an option is declared, a help string and man page are specified, so that the
The user calls a
A typical configuration, called via
I've faced this problem several times when developing my own simulation codes from scratch: which parameters should go in an input file, which should be taken from the command line, etc. After some experimenting, the following turned out to be efficient. (It is not as advanced as PETSc.)
Instead of writing an experimental simulation 'program', I'm more inclined to write a Python package that contains all the functions & classes needed to run the simulation. The traditional input file is then replaced by small Python script with 5 to 10 lines of code. Some lines are typically related to loading data files and specifying output. Others are instructions for the actual computation. Good default values for optional arguments in the Python package make it doable for beginners to use the library for simple simulations, while the advanced user still has access to all the bells and whistles.
A few examples:
As a first point, I would do the algorithm AND software as general as possible. I've learned this the hard way.
Let's say you start with a simple test case. You can do this faster. But then, if you made the software too specific (too few parameters) for this initial case, you'll loose more and more time adapting it every time you add a new degree of freedom. What I do now it's spend more time at the beginning making the thing pretty general, and increasing the variation of the parameters as I move forward.
This involves more testing from the beginning since you'll have more parameters from the starting point, but will mean that you can latter play a lot with the algorithm at zero or a very low cost.
Example: the algorithm involves calculating the surface integral the dot product of two vector functions. Don't assume from the beginning the size, geometry and discretization of the surface if in the future you may want to change that. Make a dot-product function, make the surface as general as possibly, calculate the integral in a nice formal way. You can test each function you make separately.
At the beginning, you can and start integrating over simple geometries and declaring may parameters at the start as constants. As time goes by, if you want to change the geometry, you can do it easily. Had you made assumptions at the beginning, you would have to change the whole code every time.