In [2]:
import pymatgen as pmg
si = pmg.Element('Si')
print('Si:')
print(si.atomic_mass)
print(si.common_oxidation_states)
print(si.number)
lattice = pmg.Lattice.cubic(4.2)
cscl = pmg.Structure(lattice, ["Cs", "Cl"], [[0,0,0],[0.5,0.5,0.5]])
print('\nCsCl structure')
print(cscl)
print('\n')
print(cscl.composition)
cscl.to(filename='POSCAR')
# ...
# The examples are online at http://pymatgen.org/#quick-start
In [3]:
from pymatgen import Structure, Lattice
# convert 335 pm to A:
a = 335*10**-2 # lattice parameter, a
latt = Lattice([[a, 0, 0], [0, a, 0], [0, 0, a]])
Po = Structure(latt, ['Po'], [(0, 0, 0)])
print(Po)
print(Po.volume)
print(Po.density)
In [4]:
a = 2.866 # lattice parameter, a
latt = Lattice([[a, 0, 0], [0, a, 0], [0, 0, a]])
Fe = Structure(latt, ['Fe', 'Fe'], [(0, 0, 0), (0.5, 0.5, 0.5)])
print(Fe)
print(Fe.volume)
print(Fe.density)
In [5]:
a = 65.89**(1.0/3.0) # lattice parameter, a
latt = Lattice([[a, 0, 0], [0, a, 0], [0, 0, a]])
Al = Structure(latt, ['Al', 'Al', 'Al', 'Al'], [(0.0,0.0,0.0), (0.5,0.5,0.0),
(0.5,0.0,0.5), (0.0,0.5,0.5)])
print(Al)
print(Al.volume)
print(Al.density)
In [6]:
a = 5.6402 # lattice parameter, a
latt = Lattice.cubic(a)
NaCl = Structure(latt, ['Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl'],
[(0.0,0.0,0.0), (0.5,0.5,0.0), (0.5,0.0,0.5), (0.0,0.5,0.5),
(0.5,0.0,0.0), (0.0,0.5,0.0), (0.0,0.0,0.5), (0.5,0.5,0.5)])
print(NaCl)
print(NaCl.volume)
print(NaCl.density)
NaCl.to(filename='POSCAR')
An interesting note here: first principles calculations find the most stable Ti phase to be omega-Ti, when we usually find beta-Ti (bcc) in nature. The reason for this is that bcc is dynamically stabilized (with temperature). See Kadkhodaei, Hong, and van de Walle. Phys. Rev. B 95 (2017) 064101.
Any answer for this question is really fine, but the idea was to use the hexagonal constructor and the true hcp structure is mp-46 (the omega phase is mp-72)
In [7]:
# found lattice parameters for P6_3/mmc at https://materialsproject.org/materials/mp-46/
# this i
a = 2.939
c = 4.641
latt = Lattice.hexagonal(a, c)
Ti = Structure(latt, ['Ti', 'Ti'],
[(0.0,0.33333,0.0), (0.33333,0.0, 0.5)])
print(Ti)
print(Ti.volume)
print(Ti.density)
Ti.to(filename='POSCAR')
HCP Ti
HCP Ti with space filling
Note that the Structure.from_spacegroup
classmethod can take either the symbol or the number. I've used both here. For NaCl, you should get exactly the same result as above (unless you use a different lattice or site coordinates)
In [8]:
a = 5.6402 # lattice parameter, a
latt = Lattice.cubic(a)
NaCl_spg = Structure.from_spacegroup('Fm-3m', latt, ['Na', 'Cl'], [(0,0,0), (0.5,0.5,0.5)])
print(NaCl_spg)
print(NaCl_spg.volume)
print(NaCl_spg.density)
For this I chose forsterite. After applying symmetry operations, there are 28 atoms in the cell but we only needed to enter 6 positions. Symmetry is great.
In [9]:
a, b, c = 4.803, 6.048, 10.323 # lattice parameter, a
latt = Lattice.orthorhombic(a, b, c)
forsterite = Structure.from_spacegroup(62, latt, ['Mg', 'Mg', 'Si', 'O', 'O', 'O'],
[(0, 0, 0),
(0.222736, 0.750000, 0.491625),
(0.093638, 0.250000, 0.425974),
(0.162835, 0.033014, 0.276946),
(0.053218, 0.750000, 0.722149),
(0.091677, 0.250000, 0.765961),])
print(forsterite)
print(forsterite.volume)
print(forsterite.density)
forsterite.to(filename='POSCAR')
Keep in mind that we are using with MPRester() as mpr
syntax. This is called a context manager.
This Al only has one site and the lattice is different, but the density is the same. The lattice vectors are printed below. You can see in the structure result that the angles are different and the lattice parameters are smaller as well. Tiling this structure in space gives the same structure as an fcc cell that is defined by 4 atoms.
In [10]:
from pymatgen import MPRester
with MPRester() as mpr:
Al_api = mpr.get_structure_by_material_id('mp-134')
print(Al_api)
print()
print(Al_api.lattice.matrix)
print(Al_api.volume)
print(Al_api.density)
In [11]:
with MPRester() as mpr:
result = mpr.get_entry_by_material_id('mp-134', property_data=['energy_per_atom'])
print(result.energy)
print(result.energy_per_atom)
# since we just saw that the structure only had one atom, this makes sense.
In [12]:
with MPRester() as mpr:
result = mpr.get_data('C', prop='energy')
print(result)
In [13]:
def mp_dict_to_energies(d):
"""Return list of energies from a list of dictionaries `{'material_id':mp-id, 'energy': energy}`
Parameters
----------
d (list): List of dictionaries of the form `{'material_id':mp-id, 'energy': energy}`
Returns
-------
list:
List of energies from the input dictionary
"""
energies = []
for entry in d:
energies.append(entry['energy'])
return energies
print(mp_dict_to_energies(result))
In [14]:
energies = [entry['energy'] for entry in result]
print(energies)
Variable answers. The idea was to think about differences between dicts and lists.
In [15]:
with MPRester() as mpr:
struct = mpr.get_structure_by_material_id('mp-510624')
print(struct)
print(struct.formula)
print(struct.volume)
print(struct.density)
There is a gap in the bandstructure at about 5 eV above the Fermi energy. The gap is 0.332 eV. That means SrFeO3 is predicted to be a semiconductor
In [16]:
struct.scale_lattice(56.965)
print(struct.volume)
In [17]:
struct.replace_species({'Fe':'Co'})
print(struct)
In [18]:
struct.make_supercell([2,2,2]) # or just struct.make_supercell(2) works for this
print(struct)
In [19]:
print(struct.volume)
struct.scale_lattice(struct.volume*1.1)
print(struct.volume)
In [20]:
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
sga = SpacegroupAnalyzer(struct)
print(sga.get_space_group_symbol())
In [21]:
struct.replace(24, 'Fe')
print(struct)
sga = SpacegroupAnalyzer(struct)
print(sga.get_space_group_symbol())
In [22]:
struct.perturb(0.1)
sga = SpacegroupAnalyzer(struct)
print(sga.get_space_group_symbol())
Mutable objects can be changed in place. They (usually) have a return value of None
. In order for your structure to be updated, you run the method or function that updates the variable without you having to explictly set it again. The methods for the Structure
class change the object's state so it is a mutable object. There is also an IStructure
object, where I
means immutable. You may have seen this in the documentation. Structure
is a subclass of this object.
More examples of mutable objects are lists and dictionaries. Lists have append()
and insert()
methods that update the object for you. Dictionaries are similar with the update()
method.
Immutable objects cannot be changed and if you want the value of a variable with an immutable object to change, you must get a new object. Methods usually return All of the methods for immutable objects return a new object. A main example is the string (str) object in Python. Strings cannot be changed so to update the variable, you have to set it again to the result of the method. An example of this is below, when we use the replace()
method for a string, the return value is None
.
In [23]:
my_string = 'Python is a scientific programming language'
print('Original string:')
print(my_string)
print('Try to mutate the string:')
my_string.replace('scientific', 'fun')
print(my_string)
print('It wasn\'t replaced!')
my_string = my_string.replace('scientific', 'fun')
print(my_string)
print('Now it has been replaced.')