Welcome to another post in my publishing my work in my perpetual pursuit of get better at coding in the object oriented (OO) coding paradigm and its application(s) to computational material science. Recently I have been working on iOS app development, and it is amazing how important it is to understand OO coding principles.
I noticed I had an issue with not being fully able to enjoy the fruits of the QE module I have made. You can see the previous posts here: Original Post, and followup which both have information of how the code has been evolving. In this post I will talk about how I am extending it with two other functions: a method to print the coordinates and another to create a cif file.
The biggest issue with the QE module was that it was not easy to print out the coordinates of the system, something I have to do on a regular basis as I routinely have to play with the coordinates to modify them in some way or another. Having to drop into a python console just to print the positions and lattice constants was getting annoying very quickly. I dont know why I put it off for so long — typing habit probably — but this nifty little function will now directly print out the lattice and coordinates in Angstrom:
def print(self): print("Units are in Angstrom") print("Unit Cell Parameters") for i in ['a','b','c']: string = " ".join([str(x) for x in self.lattice[i]]) print(string) print("\nAtomic Positions") for i in self.atoms: string = " ".join([str(x) for x in i]) print(string)
This should be pretty explanatory. The first loop prints out the lattice constants (in row vector format) and the second prints out the atomic positions, also in Angstrom. The QE struct class that I have made stores everything internally in Angstrom and eV.
The second extension is to create a CIF file. CIF files are Crystallographic Information Files that are the standard for storing information about crystal structures and sharing them online, in databases, or in journnal publications. They can be read by almost all crystallographic programs out there and are a nifty way to store your simulation data. You can find a brief tutorial on how to create CIF files here, here, and here. This is a very simple implementation, but that is all I really needed right now.
def CIF(self,filename=""): ciffile = "data_global\n" ciffile += "_chemical_name " + self.to_Formula().strip() + "\n" for i in ['a','b','c']: ciffile += '_cell_length_' + i.strip() + " " + str(self.norms[i]) + "\n" for i in ['alpha','beta','gamma']: ciffile += '_cell_angle_' + i.strip() + " " + str(self.angles[i]) + "\n" ciffile += "loop_\n" ciffile += "_atom_site_label\n" ciffile += "_atom_site_fract_x\n" ciffile += "_atom_site_fract_y\n" ciffile += "_atom_site_fract_z\n" conversion = np.linalg.inv(self.From_Crystal()) counter = 0 for i in self.atoms: tmp = np.array([i,i,i]) dot = np.dot(conversion,tmp) ciffile += i + " " + " ".join([str(x) for x in dot]) + "\n" if filename == "": print(ciffile) else: f = open(filename, 'w') f.write(ciffile) f.close()
Most of this code snippet is to make the file conform to the CIF standard. The loop is the same as the first loop, except that the CIF files dont store the lattices in vectors, but rather in their lattice constants and angles. (Chemists, I tell you…). It also does some conversion to fractional coordinates (remember that this internally stores data in eV and Angstrom). It takes an optional argument
as and output name for the cif file. If it is present, it writes the cif file, else it writes it to the stdout.
This now allows a post processing script to be run as following:
from QE import Struct as qe import sys PP = qe() PP.File_Process(sys.argv) PP.CIF(sys.argv) #or alternatively just print it PP.print()
The whole QE structure that I have now updated can be found here: http://www.levilentz.com/Codes/QE.py.
Hope these little guides are helping you understand simple tricks of computational sciences.