C++ Tutorials¶
This section present some hand-on tutorials to the chemfiles C++ API. All the code here is under the CC-0 Universal Licence which means that you are free to do whatever you want with it (i.e. it is Public Domain code)
Read a single frame¶
In this tutorials we will read a frame from a trajectory, and print the indexes
of all the atom in the half-space x < 5
.
We start by including the headers we need: chemfiles header, iostream for
printing and vector for storing the indexes. All the public classes of chemfiles
are accessible through the chemfiles.hpp
header, which is the only header
needed.
#include <chemfiles.hpp>
#include <iostream>
#include <vector>
Then we open a Trajectory and read the first frame:
chemfiles::Trajectory file("filename.xyz");
chemfiles::Frame frame = file.read();
We can now create a vector to store the indices of the atoms with x < 5
, and
get the positions of the atoms in the frame with the
chemfiles::Frame::positions()
function
std::vector<size_t> less_than_five;
auto positions = frame.positions();
Iterating through the atoms in the frame, we get the ones matching our
condition. Frame::size()
gives the number of atoms in the frame, which
is also the size of the positions
array. This array contains
chemfiles::Vector3D
representing the positions of the atoms in
Angstroms.
for (size_t i=0; i<frame.size(); i++) {
if (positions[i][0] < 5) {
less_than_five.push_back(i);
}
}
And finally we can print our results
std::cout << "Atoms with x < 5: " << std::endl;
for (auto i : less_than_five) {
std::cout << " - " << i << std::endl;
}
Click here to see the whole program
#include <chemfiles.hpp>
#include <iostream>
#include <vector>
int main() {
chemfiles::Trajectory file("filename.xyz");
chemfiles::Frame frame = file.read();
std::vector<size_t> less_than_five;
auto positions = frame.positions();
for (size_t i=0; i<frame.size(); i++) {
if (positions[i][0] < 5) {
less_than_five.push_back(i);
}
}
std::cout << "Atoms with x < 5: " << std::endl;
for (auto i : less_than_five) {
std::cout << " - " << i << std::endl;
}
return 0;
}
For more information about reading frame in a trajectory, see the following functions:
Trajectory::nsteps()
andTrajectory::done()
to know when to stop readingTrajectory::read_step()
to directlty read a given step.Trajectory::set_cell()
andTrajectory::set_topology()
to specify an unit cell or a topology for all frames in a trajectory.
Generating a structure¶
Now that we know how to read frames from a trajectory, let’s try to create a new structure and write it to a file. As previsouly, we start by including the chemfiles header:
#include <chemfiles.hpp>
We start with a chemfiles::Frame
that we construct by giving it the
chemfiles::UnitCell
that defines the periodic boundary conditions.
auto frame = chemfiles::Frame(chemfiles::UnitCell({10, 10, 10}));
We can then add three chemfiles::Atom
to this frame with their
positions; and set the bonds between them to define a water molecule.
frame.add_atom(chemfiles::Atom("H"), {1, 0, 0});
frame.add_atom(chemfiles::Atom("O"), {0, 0, 0});
frame.add_atom(chemfiles::Atom("H"), {0, 1, 0});
frame.add_bond(0, 1);
frame.add_bond(2, 1);
Now that our frame is constructed, it is time to write it to a file. For that,
we open a trajectory in write ('w'
) mode, and write to it.
auto trajectory = chemfiles::Trajectory("water.pdb", 'w');
trajectory.write(frame);
Click here to see the whole program
#include <chemfiles.hpp>
int main() {
auto frame = chemfiles::Frame(chemfiles::UnitCell({10, 10, 10}));
frame.add_atom(chemfiles::Atom("H"), {1, 0, 0});
frame.add_atom(chemfiles::Atom("O"), {0, 0, 0});
frame.add_atom(chemfiles::Atom("H"), {0, 1, 0});
frame.add_bond(0, 1);
frame.add_bond(2, 1);
auto trajectory = chemfiles::Trajectory("water.pdb", 'w');
trajectory.write(frame);
return 0;
}
Using selections¶
Now that we know how to read and write frame from trajectories, how about we do a bit a filtering? In this tutorial, we will read all the frames from a file, and use selections to filter which atoms we will write back to another file. This example will also show how chemfiles can be used to convert from a file format to another one.
We start by opening the two trajectories we will need
auto input = chemfiles::Trajectory("input.arc");
auto output = chemfiles::Trajectory("output.pdb", 'w');
And we create a chemfiles::Selection
object to filter the atoms we
want to keep.
auto selection = chemfiles::Selection("name Zn or name N");
Then we can iterate over all the frames in the trajectory
for (size_t step=0; step<input.nsteps(); step++) {
auto frame = input.read();
And use the selection to get the list of atoms to remove. The result of
Selection::list()
is a std::vector<size_t>
containing the atoms
matching the selection.
auto to_remove = selection.list(frame);
In order to remove the atoms from the frame, we need to sort the to_remove
vector in descending order: removing the atom at index i will shift the index of
all the atoms after i. So we start from the end and work toward the start of the
frame.
std::sort(std::begin(to_remove), std::end(to_remove), std::greater<size_t>());
for (auto i: to_remove) {
frame.remove(i);
}
Finally, we can write the cleaned frame to the output file, and start the next iteration.
output.write(frame);
Click here to see the whole program
#include <chemfiles.hpp>
#include <functional>
int main() {
auto input = chemfiles::Trajectory("input.arc");
auto output = chemfiles::Trajectory("output.pdb", 'w');
auto selection = chemfiles::Selection("name Zn or name N");
for (size_t step=0; step<input.nsteps(); step++) {
auto frame = input.read();
auto to_remove = selection.list(frame);
std::sort(std::begin(to_remove), std::end(to_remove), std::greater<size_t>());
for (auto i: to_remove) {
frame.remove(i);
}
output.write(frame);
}
return 0;
}