I have just finished Ticket [#124], which introduced some renaming of very basic classes. And while asking Burkhard for his opinion, he was not able to dig through my explanations. Plus, the Matlab version started a new blog, so I thought I would try to wrap up all the ideas why the new names are great in a blog post and explain along the way how a good part of the C++ version works.
Let us look at four different equations for propagating some quantum-mechanical state:
The equations are:
The goal with the C++ library was to be able to solve all of these equations with as much reuse as possible. To get there, we first need to decompose the equations into components that we can then combine to suit all our needs. If we look hard at these equations, we notice some common patterns:
This decomposition into four distinct entities is used by the C++ Wavepacket package. Now let us demonstrate how to build the skeleton of a propagation scheme out of that. Let us assume we have a one-dimensional grid set up in a Representation instance called "rep" and let us further assume that the Hamiltonian is just the kinetic energy (i.e., we have a free particle with mass "m"). Then we would set up the equations as follows:
:::c++
auto H = std::make_shared<CartesianKineticEnergy>(rep, 0, mass); // 0 - first degree of freedom
auto equation1 = std::make_shared<SchroedingerEquation>(H);
auto solver1 = std::make_shared<ChebychevPrimitive>(equation1);
auto equation2 = std::make_shared<CommutatorLiouvillian>(H);
auto solver2 = std::make_shared<ChebychevPrimitive>(equation2);
auto equation3 = std::make_shared<SchroedingerEquation>(H);
auto solver3 = std::make_shared<ChebychevRelax>(equation3);
auto equation4 = std::make_shared<OneSidedLiouvillian>(H);
auto solver4 = std::make_shared<ChebychevRelax>(equation4);
auto combinedEquations = std::make_shared<EquationSystem>(2);
combinedEquations->setEntry(0, 0, equation1);
combinedEquations->setEntry(1, 1, equation2);
auto combinedSolver = std::make_shared<ChebychevPrimitive>(combinedEquations);
// note: this will actually not work, because the Chebychev solver needs the spectrum
// and refuses to guess it here, but that is a technical detail that I leave out here.
Of course, to get the full simulation going, you need some more boilerplate code, but that is the gist of the setup of the equations. Hence with just changing two lines of code you can turn each of the equations of motion into another one. You can also add operators and Liouvillians to form more complex expressions, but that is beyond the scope of this post.
Now as an addendum, we can come back to the original reason for this post: I wanted to rename some classes. So far we have established four different components to specify the equations of motion, but how do we name them? Here, naming means both the name of the base class from which the concrete implementation classes derive and the name to use in the documentation for coherence.
The new naming scheme is as follows: The solvers are called PropagationPrimitive (come to think of it, "Solver" might be a better name...anyway), the operators are called "Operator", the Schrödinger equation and Liouvillians are called "Expression", and the system of equations is called "EquationSystem".
The old naming scheme used "OperatorPrimitive" to denote an operator and "LinearOperator" for the equation of motion. The latter was also sometimes called "complex operator" for lack of a better word. To top it off, the system of equations was called "OperatorMatrix". It was pretty confusing and ugly to name two different concepts with almost the same word, so this should be much better now.