Extending WaveBlocksND

In this chapter we describe the steps to extend the WaveBlocksND code base with respect to various central aspects. The most important aspect is probably the possibility to add new potentials of whatever kind. The other topics include the question of how to save more data and what it takes to implement new simulation algorithms.

Structure of potential definitions

Performing a simulation with a new potential that is not (yet) part of the PotentialLibrary is not difficult. All potentials are specified as a Python dict. An simple example of a potential definition could look like:

quadratic = {}
quadratic["variables"] = ["x"]
quadratic["potential"] = "1/2 * sigma * x**2"
quadratic["defaults"] = {"sigma":"1/2"}
quadratic["number_levels"] = 1

We see that this is nothing else than a Python dict (here assigned to the variable quadratic). This dict must have a key variables whose value is just a list of the position space variables, in this case just x. Next, there must be a key potential and the corresponding value is the symbolic expression of the potential formula. This formula must be a function of the position space variables listed. It may optionally have an arbitrary number of parameters, here for example the sigma variable. The dict may have a further key called defaults whose value is another dict containing default values for the parameters. These values are used if you do not specify a value for a parameter in the simulation configuration file. You may omit the defaults here, then you always have to provide a value in the simulation configuration. Values given in the simulation configuration always precede default values. One can specify the number energy levels with the number_levels key.

The analytic potential formula is given as a string. This string is parsed by the sympify function of Sympy. You can use many of the usual mathematical operations and functions, for example (fractional) powers (**), roots (sqrt), trigonometric and hyperbolic functions and their inverse functions (but remember, it is called atan and not arctan) and also some constants like pi. Please refer to the Sympy manual for a more thorough description of [sympify]. The most important point is to make sure that the formula finally yields a scalar expression in the position variable(s).

This is an example of a quadratic elliptic potential well in two dimensions, using the variables x and y:

quadratic_2d = {}
quadratic_2d["variables"] = ["x", "y"]
quadratic_2d["potential"] = "1/2 * (sigmax * x**2 + sigmay * y**2)"
quadratic_2d["defaults"] = {"sigmax":"1/2", "sigmay":"1/2"}
quadratic_2d["number_levels"] = 1

A more complicated potential could be given by the following Python code:

delta_gap = {}
delta_gap["variables"] = ["x"]
delta_gap["potential"] = [["1/2 * tanh(x)", "delta"         ],
                          ["delta",         "-1/2 * tanh(x)"]]
delta_gap["defaults"] = {"delta":"0.2"}
delta_gap["number_levels"] = 2

This potential has two energy levels and is given by a 2 \times 2 matrix. The matrix is constructed as two nested Python lists. Each entry is a single string as above. Note also, that this potential does not specify a default for delta, you have to assign a scalar float value to a variable called delta in the simulation configuration file. You may specify potentials with as many energy levels as you like.

A final word on the strings representing formulas and parameter values: any string that only consists of a numerical constant my be replaced by a float. But be careful with divisions, in Python 2.7, 1/2 yields 0!

[sympify]http://docs.sympy.org/latest/modules/core.html#sympy.core.sympify

Adding new potentials

After we have seen the general structure of a potential definition in the last section, let’s now look at how to add and use a new potential. For new potentials you have basically two options. Either place the potential definition in the PotentialLibrary.py file. Make sure your variable name (for example delta_gap) is unique. If you decide to go this way, you can use the new potential by referring to it in the simulation configuration as:

potential = "delta_gap"

The other option is to place the whole potential definition inside the simulation configuration file. Then this would look like:

potential = {}
potential["variables"] = ["x"]
potential["potential"] = [["1/2 * tanh(x)", "delta"         ],
                          ["delta",         "-1/2 * tanh(x)"]]

This time you must name the dict potential because that’s the variable name where the ParameterLoader looks for a potential. For the first time it might be confusing that the dict as well as its main key both bear the name potential but this is actually correct. Of course you can specify default values here too, but probably it has no real use case.

As a small side note, here is an example of a custom potential when we create the configurations by the ConfigurationGenerator. (This is only a small excerpt of a metaconfiguration file.)

# A custom potential without a default for "alpha"
GP["potential"] = {}
GP["potential[\"potential\"]"] = "\"cosh(alpha*x)\""

# Different values for "alpha" in each simulation
LP["alpha"] = [0.1*i for i in range(1,5)]

Please note the escaped quotes, it’s essential to do this very carefully.

Compute and store more data

About the IOM and IOM Plugins and how to teach the IOM to store more or other data.

New propagation algorithms

Implementing the SimulationLoop and Propagator interfaces.