Most of the fundamentals of pymatgen are expressed in terms of Molecule
and Structure
objects.
While we will mostly be using Structure
, Stucture
and Molecule
are very similar conceptually. The main difference is that Structure
supports full periodicity required to describe crystallographic structures.
Creating a Structure
can be done in one line, even for complicated crystallographic
structures. However, we'll start by introducing the somewhat simpler Molecule
object, and then use this understanding of Molecule
to introduce Structure
.
Creating a Molecule¶
Start by importing Molecule
:
from pymatgen.core.structure import Molecule
In a Jupyter notebook, you can show help for any Python object by clicking on the object and pressing Shift+Tab. This will give you a list of arguments and keyword arguments necessary to construct the object, as well as the documentation ('docstring') which gives more information on what each argument means.
Molecule
Molecule takes input arguments species
and coords
, and input keyword arguments charge
, spin_multiplicity
, validate_proximity
and site_properties
.
Keyword arguments come with a default value (the value after the equals sign), and so keyword arguments are optional.
Arguments (without default values) are mandatory.
c_monox = Molecule(["C","O"], [[0.0, 0.0, 0.0], [0.0, 0.0, 1.2]])
print(c_monox)
Alright, now let's use a keyword variable to change a default. How about we make an anion?
oh_minus = Molecule(["O", "H"], [[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]], charge=-1)
print(oh_minus)
You
can also create Molecule objects from files. Let's say you have an
*.xyz file called "water.xyz". You can import that into pymatgen with Molecule.from_file
, like:
water = Molecule.from_file("water.xyz")
print(water)
Exercise: Making Molecules¶
Try it yourself! Create molecules however you like!
In this folder are several example molecules (methane.xyz
, furan.xyz
, and benzene.xyz
). Try loading these files with Molecule.from_file
.
You can also try making a Molecule from a list of species and
coordinates. Try changing the default parameters - see what you can and
cannot do (for instance, look at varying the charge and the spin
multiplicity).
What's in a Molecule? Introducing Sites, Elements and Species¶
You can access properties of the molecule, such as the Cartesian coordinates of its sites:
print(c_monox.cart_coords)
or properties that are computed on-the-fly, such as its center of mass:
print(c_monox.center_of_mass)
To see the full list of available properties and methods, press Tab after typing my_molecule.
in your Jupyter notebook. There are methods used to modify the molecule
and these take additional argument(s). For example, to add a charge to
the molecule:
c_monox.set_charge_and_spin(charge=1)
print(c_monox)
A molecule is essentially a list of Site
objects. We can access these sites like we would a list in Python. For
example, to obtain the total number of sites in the molecule:
len(c_monox)
Or to access the first site (note that Python is a 0-indexed programming language, so the first site is site 0):
print(c_monox[0])
And just like a list, I can even change the elements of a molecule.
c_monox[0] = "O"
c_monox[1] = "C"
print(c_monox)
A site object contains information on the site's identity and position in space.
site0 = c_monox[0]
site0.coords
site0.specie
Here, because we switched the elements, the site holds the element O. In general, a site can hold an Element
, a Specie
or a Composition
. Let's look at each of these in turn.
from pymatgen.core.composition import Element, Composition
from pymatgen.core.periodic_table import Specie
An Element
is simply an element from the Periodic Table.
carbon = Element('C')
Elements have properties such as atomic mass, average ionic radius and more:
carbon.average_ionic_radius
A Specie
can contain additional information, such as oxidation state:
o_ion = Specie('O', oxidation_state=-2)
o_ion
Again, we can access both these Specie
-specific properties and more general properties of Elements.
o_ion.oxi_state
o_ion.atomic_mass
Or, for convenience, we can use strings, which will be interpreted as elements with oxidation states:
Specie.from_string('O2-')
Finally, a Composition
is an object that can hold certain amounts of different elements or
species. This is most useful in a disordered Structure, and would rarely
be used in a Molecule. For example, a site that holds 50% Au and 50% Cu
would be set as follows:
comp = Composition({'Au': 0.5, 'Cu': 0.5})
A Composition
contains more information than either an Element
or a Specie
. Because it can contain multiple Elements
/Species
, you can obtain the formula, or the chemical system.
print("formula", comp.alphabetical_formula)
print("chemical system", comp.chemical_system)
When we construct a Molecule
, the input argument will automatically be converted into one of Element
, Specie
or Composition
. Thus, in the previous example, when we first defined carbon monoxide, the input ['C', 'O']
was converted to [Element C, Element O]
.
Exercise: Hydrogen Cyanide¶
Construct the linear HCN molecule where each bond distance is the sum of the two atomic radii.
HINT: To do this, you'll probably want to use some Element properties!
H_rad = Element('H').atomic_radius
C_rad = Element('C').atomic_radius
N_rad = Element('N').atomic_radius
HC_bond_dist = H_rad + C_rad
CN_bond_dist = C_rad + N_rad
H_pos = 0
C_pos = H_pos + HC_bond_dist
N_pos = C_pos + CN_bond_dist
hcn = Molecule(['H','C','N'], [[H_pos, 0, 0], [C_pos, 0, 0],[N_pos, 0, 0]])
print(hcn)
Creating a Structure and Lattice¶
Creating a Structure
is very similar to creating a Molecule
, except we now also have to specify a Lattice
.
from pymatgen.core import Lattice, Structure
A Lattice
can be created in one of several ways, such as by inputting a 3x3
matrix describing the individual lattice vectors. For example, a cubic
lattice of length 5 Ångstrom:
my_lattice = Lattice([[5, 0, 0], [0, 5, 0], [0, 0, 5]])
print(my_lattice)
Equivalently, we can create it from its lattice parameters:
my_lattice_2 = Lattice.from_parameters(5, 5, 5, 90, 90, 90)
Or, since we know in this case that we have a cubic lattice, a == b == c and alpha == beta == gamma == 90 degrees, so we can simply put:
my_lattice_3 = Lattice.cubic(5)
We can confirm that these lattices are the same:
my_lattice == my_lattice_2 == my_lattice_3
Now, we can create a simple crystal structure. Let's start with body-centered-cubic iron:
bcc_fe = Structure(Lattice.cubic(2.8), ["Fe", "Fe"], [[0, 0, 0], [0.5, 0.5, 0.5]])
print(bcc_fe)
print(bcc_fe)
Creating this Structure
was similar to creating a Molecule
in that we provided a list of elements and a list of positions. However, there are two key differences: we had to include our Lattice
object when creating the Structure
and since we have a lattice, we can define the positions of our sites in fractional coordinates with respect to that lattice instead of Cartesian coordinates.
It's also possible to create an equivalent Structure
using Cartesian coordinates:
bcc_fe_from_cart = Structure(Lattice.cubic(2.8), ["Fe", "Fe"], [[0, 0, 0], [1.4, 1.4, 1.4]],
coords_are_cartesian=True)
print(bcc_fe_from_cart)
We can check that both structures are equivalent:
bcc_fe == bcc_fe_from_cart
As in a Molecule
, we can access properties of the structure, such as its volume:
bcc_fe.volume
Creating Structures from Spacegroups¶
Structures can also be created directly from their spacegroup:
bcc_fe = Structure.from_spacegroup("Im-3m", Lattice.cubic(2.8), ["Fe"], [[0, 0, 0]])
print(bcc_fe)
nacl = Structure.from_spacegroup("Fm-3m", Lattice.cubic(5.692), ["Na+", "Cl-"],
[[0, 0, 0], [0.5, 0.5, 0.5]])
print(nacl)
And spacegroups can be obtained from a structure:
nacl.get_space_group_info()
Where 225 is the spacegroup number.
Supercells¶
Alright, we are now well-versed in the art of creating singular structures. But in some cases, you really don't just want one unit cell; you want a supercell. Pymatgen provides a very simple interface to create superstructures. Let's start with the simplest structure that we can imagine: Polonium, a simple cubic metal.
polonium = Structure(Lattice.cubic(3.4), ["Po"], [[0.0, 0.0, 0.0]])
print(polonium)
To make a supercell, we can just multiply the structure by a tuple!
supercell = polonium * (2, 2, 2)
print(supercell)
Exercise: Barium Titanate¶
Load BaTiO3 from the CIF file provided (BaTiO3.cif
).
Replace the barium with a new element with atomic number equal to the
the day of the month you were born (e.g. 1st = hydrogen, 31st =
gallium). Then, make a supercell of N unit cells in one cartesian
direction of your choice where N is the integer of your birth month
(e.g. January = 1, December = 12).
BaTiO3=Structure.from_file("BaTiO3.cif")
print(BaTiO3.get_space_group_info())
BaTiO3.replace(0,'Mg')
BaTiO3=BaTiO3*(1,1,4)
print(BaTiO3)
print(BaTiO3.get_space_group_info())
0 Comments