1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
|
\section{Building Molecules}
Since BALL is intended for Molecular Modeling, the classes representing atoms,
bonds, and molecules are of central importance. In this first example, we will
construct a water molecule ``by hand'' to illustrate the use of these classes.
Typical applications would read molecular structures from a file. This will be
shown in the second example.
\noindent
The whole source code for the first example is given in
Listing~\ref{lst:tutorial1}. To start the construction of a water molecule, we
first create an (empty) \Index{atom}. Atoms are represented by the class
\class{Atom}.
\begin{lstlisting}{}
Atom* oxygen = new Atom;
\end{lstlisting}
\noindent
Atoms have a number of attributes. As we created this atom without specifying
any of its properties, these attributes are set to their defaults.
Table \ref{table:atomattributes} lists the attributes of an atom along with its
\newtermdef{accessors} (methods to access or modify an attribute) and default
values.
\begin{table} [h]
\centering
\begin{tabular}{|l|l|l|c|}
\hline
\bf Attribute & \bf Type & \bf Accessors & \bf Default value\\
\hline
name & String & \accessor{setName} & {\tt ""}\\
& & \accessor{getName} & \\
element & Element & \accessor{setElement} & {\tt Element::UNKNOWN}\\
& & \accessor{getElement} & \\
charge & float & \accessor{setCharge} & 0.0\\
& & \accessor{getCharge} & \\
radius & float & \accessor{setRadius} & 0.0\\
& & \accessor{getRadius} & \\
type name & String & \accessor{setTypeName} & {\tt ""}\\
& & \accessor{getTypeName} & \\
type & Atom::Type & \accessor{setType} & {\tt Atom::INVALID\_TYPE}\\
& & \accessor{getType} & \\
position & Vector3 & \accessor{setPosition} & (0, 0, 0)\\
& & \accessor{getPosition} & \\
velocity & Vector3 & \accessor{setVelocity} & (0, 0, 0)\\
& & \accessor{getVelocity} & \\
force & Vector3 & \accessor{setForce} & (0, 0, 0) \\
& & \accessor{getForce} & \\
\hline
\end{tabular}
\caption[Attributes of an atom]
{Atttributes of an atom along with its accessors and default values.}
\label{table:atomattributes}
\end{table}
\noindent
For example, we can assign the \Index{element} for our new atom:
\begin{lstlisting}{}
oxygen->setElement(PTE[Element::O]);
\end{lstlisting}
\noindent
The expression {\tt PTE[\class{Element}::O]} returns an instance of class
\class{Element}. It is assigned to our atom using the \method{setElement}
method. We leave our new atom at the default position (0, 0, 0) and create two
new atoms, which will be the still missing hydrogen atoms:
\begin{lstlisting}{}
Atom* hydrogen1 = new Atom;
Atom* hydrogen2 = new Atom;
hydrogen1->setElement(PTE[Element::H]);
hydrogen2->setElement(PTE[Element::H]);
\end{lstlisting}
\noindent
Now we have to assign the correct coordinates to the two hydrogen atoms. The
method \method{setPosition} takes an instance of \class{Vector3} as an
argument. This class is used to represent coordinates and vectors in
three--dimensional space. An object of type \class{Vector3} can be constructed
from three floating point numbers which represent the $x$, $y$, and $z$
coordinates. Thus, we can assign the coordinates as follows:
\begin{lstlisting}{}
hydrogen1->setPosition(Vector3(-0.95, 0.00, 0.0));
hydrogen2->setPosition(Vector3( 0.25, 0.87, 0.0));
\end{lstlisting}
\noindent
Now, our three atoms are of the right type and at the right positions. However,
we do not yet have a molecule, so let's create one:
\begin{lstlisting}{}
Molecule* water = new Molecule;
\end{lstlisting}
\noindent
Molecules are representd by the \class{Molecule} class. Each instance of this
class may contain an arbitrary number of atoms. Using the \method{insert}
method, we can construct a molecule from our atoms:
\begin{lstlisting}{}
water->insert(*oxygen);
water->insert(*hydrogen1);
water->insert(*hydrogen2);
\end{lstlisting}
\noindent
For a complete water molecule, we still need two \Index{bonds}. This can be
achieved with the method \method{createBond}:
\begin{lstlisting}{}
oxygen->createBond(*hydrogen1);
oxygen->createBond(*hydrogen2);
\end{lstlisting}
or
\begin{lstlisting}{}
hydrogen2->createBond(*oxygen);
\end{lstlisting}
\noindent
To verify that everything worked as expected, we might print the number of
atoms in the molecule or the number of bonds for each atom:
\begin{lstlisting}{}
cout << "# of atoms in water: "
<< water->countAtoms() << endl;
cout << "# of bonds of oxygen: "
<< oxygen->countBonds() << endl;
cout << "# of bonds of hydrogen1: "
<< hydrogen1->countBonds() << endl;
cout << "# of bonds of hydrogen2: "
<< hydrogen2->countBonds() << endl;
\end{lstlisting}
\noindent
The method \method{countAtoms} is available for all kernel classes that might
contain atoms and returns the total number of atoms for this object. The
method \method{countBonds} returns the number of bonds the atom shares. An
atom can have at most eight bonds.
We can also verify the bond distances:
\begin{lstlisting}{}
float distance = oxygen->getDistance(*hydrogen1);
cout << "bond length: " << distance << endl;
\end{lstlisting}
\noindent
\method{getPosition} is the complementary method of \method{setPosition}: it
returns the current position of an atom. The return value is again of type
\class{Vector3}. The length of this vector is then returned by the
\method{getLength} method.
Water molecules rarely occur alone, so we are going to create further water molecules.
All BALL kernel classes are container classes and support \newterm{deep
copying}, \ie when assigned or copy constructed their contents are copied as
well. So, we can easily create a new water molecule:
\begin{lstlisting}{}
Molecule* water2 = new Molecule(*water);
\end{lstlisting}
\noindent
This molecule is an exact copy of our original water molecule. Especially, the
atoms have the same position as in the original. So we want to shift the whole
molecule to another position. In principle, we could access all atoms in the
copy and add a constant translation vector to their position. However, there
is a simpler way. BALL provides so--called \newterm{processors}. These
processors may be applied to any of the kernel objects and perform an
operation on any objects they encounter. For example the
\class{TranslationProcessor} performs a simple translation on every atom it
finds. The use of the processors is very simple. All kernel classes define an
\method{apply} method which takes a processor as an argument. In order to
translate our second water molecule by a certain distance, we first create a
\class{TranslationProcessor}:
\begin{lstlisting}{}
TranslationProcessor translation(Vector3(5, 0, 0));
\end{lstlisting}
\noindent
The translation vector is specified as the argument of the constructor. Now we
may translate the atoms of our water molecule by a simple call to \method{apply}
\begin{lstlisting}{}
water2->apply(translation);
\end{lstlisting}
\noindent
Another important kernel class besides atoms and molecules is \class{System}.
A system is a collection of atoms, molecules, or any other kernel objects. For
example, we can store our two water molecules in a system object:
\begin{lstlisting}{}
System S;
S.insert(*water);
S.insert(*water2);
\end{lstlisting}
\begin{figure}[t]
\centering
\includegraphics[width=\textwidth]{tut1_screenshot}
\caption{Screenshot of BALLView showing the result of the first example
({\tt tutorial1.C})}
\label{fig:tut1-screenshot}
\end{figure}
\noindent
We can now manipulate the two water molecules simultaneously. For example a
further application of the translation processor to the system would apply
the translation to both molecules.
But now we would like to have a look at what we built. So we might write our
system to a file and inspect it with a molecule viewer (\eg BALLView).
BALL supports a variety of file formats. For example we could write the system
to a HyperChem HIN file~\cite{HyperChem}: \label{useofstreams}
\begin{lstlisting}{}
HINFile outfile("water.hin", std::ios::out);
outfile << S;
outfile.close();
\end{lstlisting}
\noindent
These three lines of code create a \class{HINFile} object which is used to
read and write HyperChem files. The first line opens a file named {\tt
water.hin} for output ({\tt std::ios::out}; if the second argument is omitted the
file is opened for reading only). The second line uses the stream operator
{\tt <<} \index{operator {\tt <<}} to write the contents of system {\tt S} to
this file. The file is closed in the third line.
\noindent
As all molecules and atoms we created are inside the system, they are deleted
automatically as soon as the system is deleted.
If this short program is run it creates the following output:
\begin{lstlisting}[frameround=]{}
# of atoms in water: 3
# of bonds of oxygen: 2
# of bonds of hydrogen1: 1
# of bonds of hydrogen2: 1
bond distance: 0.95
\end{lstlisting}
\noindent
It also creates a file named {\tt water.hin} which contains the two water
molecules. Fig.~\ref{fig:tut1-screenshot} shows the contents of this file
in the BALLView viewer.
To compile this small example, we still have to include a few header files.
The complete file is shown in Listing~\ref{lst:tutorial1} on
Page~\pageref{lst:tutorial1} and can be found in \mbox{{\tt
BALL/source/TUTORIAL/tutorial1.C}}.
First, we have to include \file{iostream} as we use {\tt cout} and {\tt endl}
to print some text. Then, we have to include the headers for the BALL kernel
classes \class{Atom}, \class{Bond}, \class{Molecule}, \class{System}, and
\class{PTE}. The headers for the HyperChem file support are found in the
directory \directory{BALL/FORMAT} and the headers for the
\class{TranslationProcessor} class are found in
{\tt BALL/STRUCTURE/geometricProperties.h}.
As all BALL classes are hidden in the \namespace{BALL} namespace, we have to
give access to this namespace with the command
\begin{lstlisting}{}
using namespace BALL;
\end{lstlisting}
Similarly, we also use the namespace \namespace{std} which contains {\tt cout}
and {\tt endl}.
If you have BALL installed, you might now try to compile this example. Simply
change to the directory \directory{BALL/source/TUTORIAL} which contains all
the examples from this tutorial and type
\begin{lstlisting}[frameround=]{}
make tutorial1
./tutorial1
\end{lstlisting}
This should build and execute the example. Please remember to set the
environment variable {\tt LD\_LIBRARY\_PATH} to the directory where your
libraries are installed.
After the successful execution of the example, a file named {\tt water.hin}
should appear in the current directory. This file contains the two water
molecules. You might inspect it with the molecule viewer \mbox{BALLView}.
Fig.~\ref{fig:tut1-screenshot} shows a screenshot of \mbox{BALLView}
displaying the two water molecules.
\newpage
\begin{lstlisting}[captionpos=t,caption={The complete source code of the first example.}\label{lst:tutorial1}]{}
// tutorial example 1
// ------------------
// build two water molecules and write them to a file
// needed for cout
#include <iostream>
// the BALL kernel classes
#include <BALL/KERNEL/atom.h>
#include <BALL/KERNEL/bond.h>
#include <BALL/KERNEL/molecule.h>
#include <BALL/KERNEL/system.h>
#include <BALL/KERNEL/PTE.h>
// reading and writing of HyperChem files
#include <BALL/FORMAT/HINFile.h>
// the TranslationProcessor
#include <BALL/STRUCTURE/geometricTransformations.h>
// we use the BALL namespace and the std namespace
using namespace BALL;
using namespace std;
int main()
{
// we create a new atom called oxygen
// and set its element to oxygen (PTE[Element::O])
Atom* oxygen = new Atom;
oxygen->setElement(PTE[Element::O]);
// now we create two hydrogen atoms...
Atom* hydrogen1 = new Atom;
Atom* hydrogen2 = new Atom;
hydrogen1->setElement(PTE[Element::H]);
hydrogen2->setElement(PTE[Element::H]);
// ...and move them to approximately correct positions
hydrogen1->setPosition(Vector3(-0.95, 0.00, 0.0));
hydrogen2->setPosition(Vector3( 0.25, 0.87, 0.0));
// We create our water molecule...
Molecule* water = new Molecule;
// ...and insert the three atoms into the molecule.
water->insert(*oxygen);
water->insert(*hydrogen1);
water->insert(*hydrogen2);
// Then we build the two O-H bonds
oxygen->createBond(*hydrogen1);
oxygen->createBond(*hydrogen2);
// Some statistics: Molecule::countAtoms()
// returns the number of atoms, Atom::countBonds()
// the number of bonds the atom shares
cout << "# of atoms in water: "
<< water->countAtoms() << endl;
cout << "# of bonds in oxygen: "
<< oxygen->countBonds() << endl;
cout << "# of bonds of hydrogen1: "
<< hydrogen1->countBonds() << endl;
cout << "# of bonds of hydrogen2: "
<< hydrogen2->countBonds() << endl;
// compute the bond length
float distance = oxygen->getDistance(*hydrogen1);
cout << "bond length: " << distance << endl;
// Now we copy our molecule using a copy constructor.
Molecule* water2 = new Molecule(*water);
// A translation processor moves the second molecule
// 5 Angstrom along the x axis
TranslationProcessor translation(Vector3(5, 0, 0));
water2->apply(translation);
// We insert our two molecules into a system
System* S = new System;
S->insert(*water);
S->insert(*water2);
// and write this system to a HyperChem file
HINFile outfile("water.hin", std::ios::out);
outfile << *S;
outfile.close();
// We delete the system. This also deletes
// the molecules and the atoms therein
delete S;
}
\end{lstlisting}
|