Tutorial:Passing atoms between objects: the AtomPointerVector

From MSL-Libraries

This is an example on how to pass atoms between objects that use or manipulated them, following the example program example_AtomPointerVector.cpp in the examples/ subdirectory.


Background

The AtomPointerVector is a vector of Atom pointers (vector<Atom*>) expanded with a few added functions by inheritance. For example, it computes the geometric center of the atoms, it can save the current coordinates to a buffer and restore them later, and can calculate RMSD with another AtomPointerVector of the same size.

Its main use in MSL is as a way for objects that manipulate or interact with Atoms to communicate.

To compile

% make bin/example_AtomPointerVector

To run the program

Go to the main directory and run the command

% bin/example_AtomPointerVector

Program description

  • First the program creates a new AtomPointerVector from scratch. We can use the vector's push_back functions and create new atoms by passing their atomId (chain, resnum, residue type and atom name) and the coordinates. Here we create a tri-peptide (ALA, ILE, ALA);
56	AtomPointerVector atoms;
57	atoms.push_back(new Atom("A,1,ALA,N",    2.143,  1.328,  0.000));
58	atoms.push_back(new Atom("A,1,ALA,CA",   1.539,  0.000,  0.000));
59	atoms.push_back(new Atom("A,1,ALA,CB",   2.095, -0.791,  1.207));
...
73	atoms.push_back(new Atom("A,3,ALA,C",   -5.297, -5.192, -0.000));
74	atoms.push_back(new Atom("A,3,ALA,OT1", -6.104, -4.223,  0.000));
75	atoms.push_back(new Atom("A,3,ALA,OT2", -5.649, -6.401, -0.000));
  • The AtomPointerVectors supports printing of all atoms with the << operator.
80	cout << atoms << endl;

which outputs:

N    ALA    1  A [     2.143      1.328      0.000] (conf   1/  1) +
CA   ALA    1  A [     1.539      0.000      0.000] (conf   1/  1) +
...
OT1  ALA    3  A [    -6.104     -4.223      0.000] (conf   1/  1) +
OT2  ALA    3  A [    -5.649     -6.401     -0.000] (conf   1/  1) +
  • The elements of an AtomPointerVector can be accessed with a for-loop. Here we print each atom. The [] operator returns the i-th element (an Atom pointer). If we use the () operator (such as in the commented line 87), that would return an Atom.
85	for (unsigned int i=0; i<atoms.size(); i++) {
86		cout << "Atom " << i << ": " << *(atoms[i]) << endl; // the [] returns an Atom pointer
87		//cout << "Atom " << i << ": "  << atoms(i) << endl; // alternative way, the () operator returns and Atom object (by reference)
88	}

output:

Atom 0: N    ALA    1  A [     2.143      1.328      0.000] (conf   1/  1) +
Atom 1: CA   ALA    1  A [     1.539      0.000      0.000] (conf   1/  1) +
...
Atom 17: OT1  ALA    3  A [    -6.104     -4.223      0.000] (conf   1/  1) +
Atom 18: OT2  ALA    3  A [    -5.649     -6.401     -0.000] (conf   1/  1) +
  • The program then demostrates a couple of added feature of this enhanced vector<Atom*>. First, the calculation of the geometric center of the atoms.
96	cout << "The geometric center of the atoms is " << atoms.getGeometricCenter() << endl;

which outputs:

The geometric center of the atoms is [    -2.248     -2.138     -0.260]
  • Then, the ability of taking snap-shots of the coordinates and saving them under labelled buffers that can be restored later. In this example, the coordinates are saved, then the atoms are translated by 1Å in the X-axis (using the Transforms objec), then the original coordinates are recalled.
107	atoms.saveCoor("initialCoors");
108
109	// use the Transforms object to translate the atoms
110	Transforms tr;
111	tr.translate(atoms, CartesianPoint(1.0, 0.0, 0.0));
112	cout << "Atoms after translation by (1, 0, 0)" << endl;
113	cout << atoms << endl;
114
115	atoms.applySavedCoor("initialCoors");
116	cout << "Atoms after restoring the initial coordinates" << endl;
117	cout << atoms << endl;

which outputs:

Atoms after translation by (1, 0, 0)
N    ALA    1  A [     3.143      1.328      0.000] (conf   1/  1) +
CA   ALA    1  A [     2.539      0.000      0.000] (conf   1/  1) +
...
OT1  ALA    3  A [    -5.104     -4.223      0.000] (conf   1/  1) +
OT2  ALA    3  A [    -4.649     -6.401      0.000] (conf   1/  1) +

Atoms after restoring the initial coordinates
N    ALA    1  A [     2.143      1.328      0.000] (conf   1/  1) +
CA   ALA    1  A [     1.539      0.000      0.000] (conf   1/  1) +
...
OT1  ALA    3  A [    -6.104     -4.223      0.000] (conf   1/  1) +
OT2  ALA    3  A [    -5.649     -6.401     -0.000] (conf   1/  1) +
  • The main purpose of the AtomPointerVector is to connect objects that manipulate or act on Atoms. For example, and AtomPointerVector can be passed to an emtpy System to populate it. The System can pass its atoms to a Transforms objects to tranlate them.
  • First let's create a System with from the atoms AtomPointerVector.
129	System sys(atoms);
130	cout << "Print the System created from the AtomPointerVector" << endl;
131	cout << sys << endl;

which outputs the sequence of the System:

Print the System created from the AtomPointerVector
A: {1}ALA ILE {3}ALA
  • The System makes its own internal copies of the atoms received. The atoms are identical but the memory addresses are different, as the following check will demonstrate:
138	string atomId = atoms[0]->getAtomId(); 
139	Atom * pAtom = &(sys.getAtom(atomId)); 
140	// print both atoms and their address, show they are different
141	cout << "From the system         : " << *pAtom << " (memory address " << pAtom << ")" << endl;
142	cout << "From the original vector: " << *(atoms[0]) << " (memory address " << atoms[0] << ")" << endl;

which outputs:

From the system         : N    ALA    1  A [     2.143      1.328      0.000] (conf   1/  1) + (memory address 0x210d620)
From the original vector: N    ALA    1  A [     2.143      1.328      0.000] (conf   1/  1) + (memory address 0x20fc930)


  • The program then illustrates how the System and Transforms can communicate using an AtomPointerVector. The System can temporarily pass its atoms to the Transforms to apply a translation. The getAtomPointers() returns an AtomPointerVector of the System's Atoms.
156	tr.translate(sys.getAtomPointers(), CartesianPoint(3.0, 0.0, 0.0));
...
163	cout << "Print the System atoms after a translation by (3, 0, 0)" << endl;
164	cout << sys.getAtomPointers() << endl;

which outputs:

Print the System atoms after a translation by (3, 0, 0)
N    ALA    1  A [     5.143      1.328      0.000] (conf   1/  1) +
CA   ALA    1  A [     4.539      0.000      0.000] (conf   1/  1) +
  • Before we quit this program, we need to take care of the memory. If an AtomPointerVector is created by an object, the garbage collection is normally done automatically by the object's distruptor. However, inn this program we created Atom pointers manually at the beginning of the program with the new operator, so we need to delete them. The deletePoitners() function takes care of freeing the memory and clearing the vector to size zero.
170	atoms.deletePointers();
171	cout << "Final cleanup, the atom pointer vector has now size " << atoms.size() << " and all allocated memory has been freed" << endl;

which outputs:

Final cleanup, the atom pointer vector has now size 0 and all allocated memory has been freed



Back to the tutorial page