Spatial translation of a molecule
Note that we will here explain in full detail how actions are called, this will be omitted in other examples. Note that all code references, such as functions, are linked to the respective files in the source browsers such that you can check on the code right-away.
You can call this translation action via any of the three interface, here we explain it via the CommandLineUI.
The following will start molecuilder with the CommandLineUI as the UIFactory
molecuilder -i test.conf --select-atom-by-id 0 -t "2,0,0"
It will parse the molecular system found in the pcp config file test.conf and translate atom with id 0 by (2,0,0), i.e. by 2 angstroem in the x direction, and save the file on exit.
But what happens exactly, behind the scenes inside molecuilder?
Code explained
I will break it down into three steps:
- Parsing the input file
- Doing the translation
- Storing updated coordinates to file
Parsing the input file
- What happens exactly here, i.e. how the command line argument -i test.conf is parsed, is explained further below with the translation action. Here, we will emphasize on the parsers for transfering information about molecular systems from the hard drive into the memory.
- For this purpose there is a FormatParser and a ChangeTracker class. The latter is just an observer of the World, noting when something changes, i.e. an atom changes position or its element. Note that the World is the single most important class, containing all atoms, all molecules. FormatParser is a general class to be derived from, i.e. it has virtual functions load() and save() that have to be written in the specializations such as XyzParser or TremoloParser. For each format specification there is such specialization of this FormatParser. Note that not every change in the World is instantly written, just a variable is set noting that something has changed.
- Now, by the file suffix we recognize its format (".conf" means pcp format, i.e. PcpParser). Then, this parser is instantiated with the FormatParserStorage which is simply a container for all instantiated parsers and handles that all output files consist of the same filename only with different suffixes. And the parser's load function will extract the atoms, coordinates and elements, out of the input stream and put them into the World.
Selecting the atom
- As a matter of fact selecting the atom is an action all by itself, see src/Action/SelectionAction/AtomByIdAction.cpp.The World class offers specifics storages for selectedAtoms and selectedMolecules in order to select a specific subset of atoms. The World class offers various methods to select and unselect which are simply projected onto actions.
Doing the translation
- Now, there is an action class called MoleculeTranslateAction. It has a unique(!) NAME. An Action is nothing but a class containing some data and a performCall() member function wherein the specific doing of the action is coded.
- All actions are instantiated in the so-called MapOfActions by its member function populateActions(), hence also the MoleculeTranslateAction(). Thereby, its NAME and the pointer to its instance is placed in an ActionRegistry, which is a singleton, can be called from anywhere and asked for the instance of a specific action when knowing its name. In this registry the instantiated actions are simply sitting there and waiting to be called forth to do something.
- Moreover, the MapOfActions has lists about which Action belongs to which menu, a short description, what the short form for the command-option is (the long form is always --NAME), what type its parameter has (if any) and also default values (also if any). This information is used by the CommandLineParser to fill in the http://www.boost.org/doc/libs/1_43_0/doc/html/program_options.html boost::program_options with the necessary details to parse all command line arguments into a map for later retrieval.
- See the main() function for an overview what has happened so far and happens now: The CommandLineParser was set, it has parsed all command line arguments and has also noted down their specific ordering on the command line in CommandLineParser::SequenceOfActions. Now, the MainWindow is called. This is the central instance of any user interface (whether textmenu, commandline or graphical) to wait for user interaction.
- In the CommandLineUI variant of the MainWindow in the function display(), we go through this SequenceOfActions. All actions are asked from the repository. If not present, we assume that is not an action but an additonal argument (such as --input or -i above). If it is present, the action is called forth to do the user's bidding.
- In MoleculeTranslateAction::performCall() you notice that first a "macrofied" function [getParametersfromValueStorage() is called. The construction of dialogs and querying information necessary for the execution of the action has been hidden inside macros such that the actual implementation really only deals with the perform...() functions. This single call fills a parameter structure, basically defined in src/Actions/MoleculeAction/TranslateAction.def and residing inside the Action class from user input. Afterwards, we may proceed and simply translate the selected atoms. For this translation action the following information is necessary: The atoms to translate, the vector to translate by and a statement whether periodic boundary conditions shall be adhered.
- These dialogs and their queries hidden inside the macro are bit more difficult. Each UIFactory variant has its own queries. In case of the text menu, the user is asked on the console for the specific set of values, in the graphical interface a window pops up with boxes to enter the values. However, on the command line these values have to be fed in already by the knowledgable user: In our case the parameter to our (-t) command line argument, i.e. to our action is a vector. Hence, the dialog receives the action's NAME as reference, whereas for the molecule and the periodicity statement we give the names of options that are defined in MapOfActions. Note that periodicity has a default value of 0 and thus may be omitted.
- These references are necessary for the queries. As in our command line case the queries themselves will turn to CommandLineParser and ask its parsed map of command line arguments for the value belonging to this reference. I.e. for "select-atom-by-id" CommandLineParser::vm, which contains 0.
- With the complete information we simply add the desired vector onto the position of each selected atom. Afterwards, if periodic boundary conditions are enforced, we call upon Box::WrapPeriodically() for the periodic correction of the whole system.
- Finally, we return an ActionState which contains all the selected atoms that have been translated to allow for undoing of the action.
Undoing and Redoing
- Within the additional functions performUndo() and performRedo() we specify how to undo and redo said translation.
- For a translation this is very simple: We simply negate the vector and perform the same operation again. However, we pick the atoms to translate from the stored state.
- Redoing is the same as undoing only without negating the vector.
Storing update info to file
- At the end of the main function, a FormatParserStorage is called again which will create the output streams for all desired formats and ChangeTracker will tell the specific FormatParsers to write the updated information from the World src/World.hpp to file.
- As FormatParserStorage is destroyed at the end of the program, streams are closed.
And that's it.
It might sound complicated but look at the ActionHowto to get a break-down of what is actually needed to create a new action.
And that's all.