Molecule
In its formal definition, a molecule is a group of two or more atoms held
together by chemical bonds. In Chemist, Molecule
class represents any group
of atoms (set of Atom
objects) or nuclei (set of Nucleus
objects forming
Nuclei
object) without any restrictions about bonding.
Construction
There are five different ways of constructing a Molecule
object including
the default and copy constructors in C++. The codes below show these methods for
C++ and the available constructors for Python. The most explicit constructor
enables a user to set the charge and multiplicity of the molecule, which would
be set to 0 and 1 if not given. One can add more atoms, or modify the charge and
multiplicity of the molecule after it is created. It should also be noted that
once a molecule object is created, the atoms that create the molecule cannot be
accessed or modified directly, only the nuclei can be accessed.
// Creating Molecule objects using different constructors
// Default constructor, no atoms, charge = 0.0, multiplicity = 1
Molecule m0;
// Creating Molecule with an initializer list of atoms
// charge = 0.0, multiplicity = 1
Atom a_H1("H", 1ul, 1.0079, 0.0, 0.0, 0.0);
Atom a_H2("H", 1ul, 1.0079, 1.0, 0.0, 0.0);
Molecule m1{a_H1, a_H2};
// Creating Molecule with a specific charge and multiplicity and an
// initializer list of atoms
Molecule m2(1., 2, {a_H1, a_H2});
// Creating Molecule with a specific charge and multiplicity and an
// initializer list of nuclei
Nucleus n_H1("H", 1ul, 1.0079, 0.0, 0.0, 0.0);
Nucleus n_H2("H", 1ul, 1.0079, 1.0, 0.0, 0.0);
Nuclei n_set{n_H1, n_H2};
Molecule m3(1, 2, n_set);
// Copy constructor
Molecule m4(m1);
# Default constructor, no atoms, charge = 0.0, multiplicity = 1
m0 = chemist.Molecule()
# PyBind11 doesn't support constructors with an initializer list
# Therefore, using the push_back method to add atoms to the molecule
a_H1 = chemist.Atom("H", 1, 1.0079, 0.0, 0.0, 0.0)
a_H2 = chemist.Atom("H", 1, 1.0079, 1.0, 0.0, 0.0)
m1 = chemist.Molecule()
m1.push_back(a_H1)
m1.push_back(a_H2)
# Creating Molecule with a specific charge, multiplicity, and nuclei
n_H1 = chemist.Nucleus("H", 1, 1.0079, 0.0, 0.0, 0.0)
n_H2 = chemist.Nucleus("H", 1, 1.0079, 1.0, 0.0, 0.0)
# Constructing a Nuclei object by pushing back Nucleus objects
nuclei = chemist.Nuclei()
nuclei.push_back(n_H1)
nuclei.push_back(n_H2)
m2 = chemist.Molecule(1, 2, nuclei)
Properties
Molecule class provides read access to the charge, multiplicity, and number of
electrons of a molecule object with charge()
, multiplicity()
, and
n_electrons()
methods, respectively. One can modify the charge and multiplicity of a
molecule with set_charge(int)
and set_multiplicity(int)
methods. The
codes below show how to access and modify these properties in both C++ and Python.
// Accessing the properties of a Molecule object
// Accessing the size (number of atoms)
std::size_t m1_size = m1.size();
REQUIRE(m1_size == 2ul);
REQUIRE(m0.size() == 0ul);
// Accessing the charge
double m1_charge = m1.charge();
REQUIRE(m0.charge() == 0);
REQUIRE(m1.charge() == 0);
REQUIRE(m2.charge() == 1);
REQUIRE(m1_charge == 0);
// Accessing the multiplicity
std::size_t m1_multiplicity = m1.multiplicity();
REQUIRE(m1_multiplicity == 1ul);
REQUIRE(m0.multiplicity() == 1ul);
// Accessing the number of electrons
REQUIRE(m0.n_electrons() == 0ul);
REQUIRE(m1.n_electrons() == 2ul);
REQUIRE(m2.n_electrons() == 1ul);
// Comparing Molecule objects
// Since m0 has no state
REQUIRE(m0 != m1);
// Since m1 and m2 has different states
REQUIRE(m1 != m2);
// Since all states are equal, m2 == m3 is true
REQUIRE(m2 == m3);
// Modifying the properties of an Molecule object
m1.set_charge(1.0);
m1.set_multiplicity(2ul);
REQUIRE(m1 == m2);
// We can add atoms to a Molecule object
Atom a_O1("O", 8ul, 15.9994, 0.0, 1.0, 0.0);
m1.push_back(a_O1);
REQUIRE(m1.size() == 3ul);
REQUIRE(m1.n_electrons() == 9ul);
# Accessing the size (number of atoms)
m1_size = m1.size()
assert m1_size == 2
assert m0.size() == 0
# Accessing the charge
m1_charge = m1.charge()
assert m0.charge() == 0.0
assert m1.charge() == 0.0
assert m2.charge() == 1.0
# Accessing the multiplicity
assert m0.multiplicity() == 1
assert m1.multiplicity() == 1
# Accessing the number of electrons
assert m0.n_electrons() == 0
assert m1.n_electrons() == 2
assert m2.n_electrons() == 1
# Comparing Molecule objects
assert m0 != m1
assert m1 != m2
assert m2 == m2 # This might be a typo in the original C++ code. m2 is being compared to itself.
# Modifying the properties of a Molecule object
m1.set_charge(1)
m1.set_multiplicity(2)
assert m1 == m2
# We can add atoms to a Molecule object
a_O1 = chemist.Atom("O", 8, 15.9994, 2.0, 0.0, 0.0)
m1.push_back(a_O1)
assert m1.size() == 3
assert m1.n_electrons() == 9
Serializing
Users can serialize a Molecule
object into a binary, XML, or JSON archive and
deserialize accordingly with the C++ interface. We use Cereal library for
serialization. Note that corresponding Cereal header files need to be included.
std::stringstream ssb, ssj, ssx;
{
cereal::BinaryOutputArchive output_binary_archive(ssb);
cereal::JSONOutputArchive output_json_archive(ssj);
cereal::XMLOutputArchive output_xml_archive(ssx);
output_binary_archive(m3);
output_json_archive(m3);
output_xml_archive(m3);
}
// Deserializing an Atom object
Molecule m7, m8, m9;
{
cereal::BinaryInputArchive input_binary_archive(ssb);
cereal::JSONInputArchive input_json_archive(ssj);
cereal::XMLInputArchive input_xml_archive(ssx);
input_binary_archive(m7);
input_json_archive(m8);
input_xml_archive(m9);
}
REQUIRE(m3 == m7);
REQUIRE(m3 == m8);
REQUIRE(m3 == m9);