Back to main menu

## Informations for developers

Lasciate ogni speranza voi ch'entrate.
Dante's Inferno, Canto III 9

Atomsk is a quite simple and dumb program, written by a humble physicist not so skilled at programming. The program was designed so that, in its properly compiled binary form, it can be understood by a computer, but that does not mean that the source code can easily be understood by a human being. I make this statement as a hint of what you will get into if you want to look at or modify the source code: lousy programming, non-optimized allocations, some GOTO statements, and a zest of global variables. And did I mention the many modules?

This page contains some informations for courageous developers who would like to make their way through the source code, and some informations for those who would like to add their contribution, be it for their personal use or for public release. Take a deep breath before diving.

#### Structure of the program

Atomsk is made of many modules, each of which handling a very specific operation. For instance one module is responsible for writing XYZ file (`out_xyz.f90`), and it does just that -if you need to change the format of XYZ files you have to modify only this module. This structure facilitates the maintainance of the code. Also it (hopefully) makes it easier for new users to look into the code, as they can look only at the modules dealing with the operations they need.

The global variables are stored in `comv.f90`, and should be as little as possible. For more flexibility, most variables and arrays are passed to modules. The array that is used to store atomic coordinates is named "P". It has a size of (Natoms,4), where the three first columns are the X, Y and Z cartesian coordinates, and the fourth column is the atomic number. The array used to store supercell vectors is H, and has dimension (3,3).

The modules are "organized" in the following way (see also the Makefile for a list of modules corresponding to each bullet point below):

- general modules contain global variables (comv.f90), general subroutines (subroutines.f90), functions (functions.f90), etc. that are used by several other modules.
- input modules have the role of loading data from one input file into the memory (in the array P). The main module performing this task is "readin.f90", which first calls the module "guess_format.f90" to guess the format of the input file, and then calls the appropriate module to read the input file (these modules have a name that start with `in_`).
- option modules apply transformations to the system (by applying some operations to the array P). The main module performing this task is "options.f90", which then calls the necessary options modules (which names usually start with `opt_`).
- output modules have the role of writing the content of the array P into one or several files. The main module performing this task is "writeout.f90", which calls the necessary output modules, each of which writes the data into one specific format (these modules have a name that start with `out_`).
- "modes" modules decide how files are treated, and can use any of the aforementionned modules.

In order to keep things simple, some rules should be respected within each type of module. Input modules, option modules, and output modules, should NOT depend on one another, so be careful when introducing a "USE" statement in any module. For instance do not introduce a dependence to an input module (like "`USE in_xyz`") in an option module. In output modules, the arrays P and H must not be modified and should be `INTENT(IN)`. "Modes" modules can use any of the input, options and/or output.

A good way to start digging into the source code is to look at the structure of the file "mode_normal.f90". One can see that this module reads in a file (by calling the reading module "readin.f90"), applies options if any (by calling the module "options.f90"), and then outputs one or several files (by calling "writeout.f90"). This is exactly what Atomsk uses when converting a file.

In other modes, these modules are called in more or less different ways.

#### Auxiliary properties

From version 0.6 Atomsk reads and writes auxiliary properties, i.e. any per-atom property like velocities, forces, electric charges, etc. The array "AUXNAMES" contains strings that are the names of the auxiliary properties; the array "AUX" contains real numbers which are the values of the properties for each atom. As such, the array "AUX" must always have its first dimension equal to the first dimension of the array P.

When reading file formats Atomsk reads some or all auxiliary properties it may contain. The arrays "AUXNAMES" and "AUX" must then be allocated properly and consistently. If no auxiliary property exist then these arrays must not be allocated by input modules.

Some common properties are recognized by Atomsk by their names. Below is a list of names that one should use to store properties in AUXNAMES:

- type for atom types (e.g. used in LAMMPS data and custom formats);
- q for ion charges, and qs for the charge of shells in ionic core-shell models;
- vx, vy, vz for atom velocities;
- fx, fy, fz for forces on atoms;
- fixx, fixy, fixz for fixed coordinates of atoms (=1 if atom coordinate is fixed, 0 otherwise).

Using these names ensure that these properties are properly recognized in all modules, especially output modules. As an example, if forces are stored with the names "force_x, force_y, force_z" then they will be seen as ordinary properties, not as forces, and as a result they will not be written to XSF format for instance. On the contrary if they are saved with the names above (fx, fy, fz) then they will be recognized as forces and written to XSF format.

The AUX array is passed as a whole to output modules. It is up to each module to use the properties relevant for its file format. For instance "out_xsf.f90" searches for forces (fx, fy and fz) in the AUXNAMES array, and if present, writes forces to the XSF file. Each module shall look for properties supported by the file format it is writing.

#### Array names

The following describes some arrays commonly used in Atomsk and passed between modules:

- H(3,3): the three vectors of the supercell (cartesian coordinates in Å);
- P(:,:): atom positions (cartesian coordinates in Å); first dimension corresponds to the number of atoms; second dimension is 4 for storing X, Y, Z and atomic number;
- S(:,:): shell positions (similar to P); note that S must have exactly the same size as P; if atom number i possesses a shell then S(i,4)=P(i,4), otherwise S(i,4)=0.0; if no shell exist then S should be unallocated;
- AUX(:,:): values of auxiliary properties (real); first dimension must equal the number of particles in P; second dimension must be equal to the size of AUXNAMES, i.e. AUX(i,j) is the j-th property of atom i; if no auxiliary property exist then AUX and AUXNAMES should be unallocated;
- AUXNAMES(:): names of auxiliary properties (characters); AUXNAMES(j) is the name associated with the j-th property (i.e. the property which values are stored in AUX(:,j));
- REGION(:): boolean array deciding if each atom is selected (`.TRUE.`) or not (`.FALSE.`); this array is created when the option `-region` is invoked; this array is passed to some options, and the option is applied only to atoms that have a value `.TRUE.` in REGION, or to all atoms if the array REGION is unallocated.

One in all one can have in mind that atom properties are stored according to the following scheme:

Atom positions + atomic number | Shells positions (optional) | Auxiliary properties (optional) | REGION (optional)
--- | --- | --- | ---
`P(1,1) P(1,2) P(1,3) P(1,4)` | `S(1,1) S(1,2) S(1,3) S(1,4)` | `AUX(1,1) AUX(1,2)...AUX(1,M)` | `REGION(1)`
`P(2,1) P(2,2) P(2,3) P(2,4)` | `S(2,1) S(2,2) S(2,3) S(2,4)` | `AUX(2,1) AUX(2,2)...AUX(2,M)` | `REGION(2)`
... ... ... | ... ... ... | ... ... ... | ... ... ...
`P(NP,1) P(NP,2) P(NP,3) P(NP,4)` | `S(NP,1) S(NP,2) S(NP,3) S(NP,4)` | `AUX(NP,1) AUX(NP,2)...AUX(NP,M)` | `REGION(NP)`

#### Units for files

The following UNITs are used by specific files and should be followed by all modules:

- 20: logfile (by default "atomsk.log");
- 30-34: input files;
- 35-39: intermediate files (used internally by Atomsk);
- 40-45: output files.

#### Adding a file format to read in

Each module managing input (which names should start with in_) should read in one specific file format, and load it to memory (supercell parameters in the array "H", atom positions in "P", and if relevant, shells in "S", auxiliary properties in "AUX", and names of auxiliary properties in "AUXNAMES").

If necessary, take example on the existing modules.

If you wrote a reading module and want to integrate it to Atomsk, the source code needs to be edited in the following way:

- readin.f90: load your module at the beginning of the file (USE ...); add the corresponding line in the 'read' section (label 200);
- guess_format.f90: add some rules by which Atomsk can recognize the file format you add: file extension, keywords...
- Makefile: add your module in the SIN variable of the Makefile so it is compiled together with the program. Make sure to add it before the module "readin.f90".

Also, please add the new format in the documentation (i.e. DISPLAY_HELP in the file `messages_EN.f90`, and in doc/formats.html), and indicate the restrictions if there are any.

#### Adding a file format to write to

Each module managing output (whose names should start with out_) must read the arrays "H" (supercell parameters), "P" (atom positions), and if the format supports it, "S" (positions of shells), "AUX" and "AUXNAMES" (auxiliary properties), and write data to one specific file format. These input arrays ("H", "P", "S"...) must NOT be modified inside these modules, and should be declared as "`INTENT(IN)`".

If necessary, take example on the existing modules.

If you wrote a writing module and want to integrate it to Atomsk, the source code needs to be edited in the following way:

- comv.f90: add the extension of your file format (e.g. 'xyz', 'cfg'...) to the global array "listofformats";
- read_cla.f90: add the command-line keyword corresponding to the file format you are adding (e.g. 'xyz', 'cfg'...);
- writeout.f90: load your module at the beginning of the file (USE ...); and then add the corresponding lines to the "write" section (label 200);
- guess_format.f90: for Atomsk to recognize the file format, add the file extension at label 100 (in `SELECT CASE(extension)`), in label 300 (line `likely=MAX(...)` and in the following `IF` statement);
- Makefile: add your module in the SOU variable of the Makefile so it is compiled together with the program. Make sure to add it before the module "writeout.f90".

Also, please add the new format in the documentation (i.e. DISPLAY_HELP in the file `messages_EN.f90`, and in doc/formats.html), and indicate the restrictions if there are any.

#### Adding an option

Options can apply to the supercell (array "H"), to the atomic positions (array "P"), to the shells ("S"), and/or to the auxiliary properties ("AUX"). After the option is applied, the module must also return the values of the modified arrays. Keep in mind that these modules use the same arrays as input and output, and writing to an array while reading it can lead to errors. If necessary the arrays can be duplicated to apply the option -just don't forget to save the updated values to the correct arrays.

If you develop a module that applies such a transformation, you can include it in the program by editing the following files:

- read_cla.f90: you have to add your option parameters to the list of strings that are read from the command-line. Your option should start with a keyword (for instance "`-cut`") followed by a given number of parameters. All of this should be stored as a string in the global variable `options_array`.
- options.f90: you need to call your module at the beginning of the file (`USE ...`), and to call your module in the DO loop. Please respect the alphabetical order so that other developers can find an option easily.
- Makefile: add your module in the SOP variable of the Makefile so it is compiled together with the program. Make sure to add it before the module "options.f90".

Also, please add the new option in the documentation (i.e. DISPLAY_HELP in the file `messages_EN.f90`, and in doc/options.html), and indicate the restrictions if there are any.

#### Adding a mode

A "mode" is a routine that decides what is done with the atom positions. For instance the default mode (so-called "normal mode") reads a file and converts it to one or many other formats. The "mode difference" computes the difference between two files. Many different things can be imagined, and the appropriate way to do them within Atomsk is by using modes. If you add a new mode, it is advised that you take advantage of routines that are already implemented in Atomsk to read/write files, and apply transformations.

If your mode computes per-atom quantities, it is advised to save them as auxiliary properties (see definition of arrays AUX and AUXNAMES above) so that output modules can write these auxiliary properties. For instance one could compute the local von Mises strain of each atom, save it in AUX, and write it as auxiliary properties in a CFG file. This is of course a dumb example as this quantity can already be computed and displayed within Atomeye, but hopefully it gives the idea.

#### Adding a language

All messages displayed by Atomsk are in a separate module to make it easy to add translations to other languages. For the program to speak a language of your choice, you will need to edit the following files:

- messages_XX.f90: copy the file "messages_EN.f90" to a new file, replacing the "EN" in the file name by the two-letters abbreviation of your language (e.g. "messages_FR.f90" for French, "messages_DE.f90" for German, etc.). Then you can start editing the new file and translating the English messages. The condition `CASE DEFAULT` should redirect to the English messages.
- messages.f90: add the conditions (`CASE("XX") ...`) for using the new language in the four subroutines. You may also define the one-letter abbreviations for "yes" and "no" in the subroutine PIKASHU_MSG (take example on existing languages).
- Makefile: add your module in the MSG variable of the Makefile so it is compiled together with the program. Make sure to add it after the module "messages.f90".

Back to main menu