Module isopydistort.isoget
Tools to interface with ISODISTORT
Expand source code
#!/usr/bin/env python
##############################################################################
#
# isopydistort by Frandsen Group
# Benjamin A. Frandsen benfrandsen@byu.edu
# (c) 2023 Benjamin Allen Frandsen
# All rights reserved
#
# File coded by: Parker Hamilton, Tobias Bird, Ben Frandsen
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE.txt for license information.
# We acknowledge Brian Toby and Robert Von Dreele as the first to develop a
# python interface to ISODISTORT, which served as a valuable starting point
# for this package. Their work can be found as part of the GSAS-II software at
# https://subversion.xray.aps.anl.gov/trac/pyGSAS/browser/trunk/ISODISTORT.py
##############################################################################
"""Tools to interface with ISODISTORT"""
import requests
ISO_UPLOAD_SITE = "https://iso.byu.edu/iso/isodistortuploadfile.php"
ISO_FORM_SITE = "https://iso.byu.edu/iso/isodistortform.php"
def _uploadCIF(cif):
"""Upload CIF to ISODISTORT.
"""
f = open(cif, 'rb')
up = {'toProcess': (cif, f), }
out = requests.post(ISO_UPLOAD_SITE, files=up).text
f.close()
start = out.index("VALUE=")
start = out.index('"', start + 1) + 1
end = out.index('"', start)
fname = out[start:end]
return fname
def _postParentCIF(fname):
#posts initially uploaded CIF, sets all data
up = {'filename': fname, 'input': 'uploadparentcif'}
out = requests.post(ISO_FORM_SITE, up)
data = {}
line_iter = out.iter_lines()
for line in line_iter:
if b'INPUT TYPE="hidden"' in line:
items = line.decode('utf-8').split(' ', 3)
name = items[2].split('=')[1].strip('"')
val = items[3].split('=', 1)[1].strip('>"')
data[name] = val
return out, data
def _setDatam3(out, data, var_dict = {}, selection = 1):
"""sets necessary data for method 3 - rolls in postIsosubgroup and part of postParentm3"""
line_iter = out.iter_lines()
for line in line_iter:
if b"Method 3" in line:
break
for line in line_iter:
if b'INPUT TYPE="hidden"' in line:
items = line.decode('utf-8').split(' ', 3)
name = items[2].split('=')[1].strip('"')
val = items[3].split('=', 1)[1].strip('>"')
data[name] = val
if b'Method 4' in line:
break
data['subgroupsym'] = '1 P1 C1-1'
data['pointgroupsym'] = '0'
data['latticetype'] = 'direct'
data['centering'] = 'd'
data['basis11'] = '1'
data['basis12'] = '0'
data['basis13'] = '0'
data['basis21'] = '0'
data['basis22'] = '1'
data['basis23'] = '0'
data['basis31'] = '0'
data['basis32'] = '0'
data['basis33'] = '1'
for key, value in var_dict.items():
data[key] = value
out = requests.post(ISO_FORM_SITE, data=data)
data = {}
line_iter = out.iter_lines()
for line in line_iter:
if b"<FORM ACTION" in line:
break
for line in line_iter:
if b'INPUT TYPE="hidden"' in line:
items = line.decode('utf-8').split(' ', 3)
name = items[2].split('=')[1].strip('"')
val = items[3].split('=', 1)[1].strip('>"')
data[name] = val
if b'<br>' in line:
break
counter = 0 # keep track of which distortion we are on
for line in line_iter:
if b'RADIO' in line:
counter += 1
if counter == selection: # grab data just for the one we want
items = line.decode('utf-8').split(' ', 3)
name = items[2].split('=')[1].strip('"')
val = items[3].split('=', 1)[1].strip('>"')
data[name] = val
if b'</FORM>' in line:
break
return data
def _setDatam4(data, subcif, specify = False, basis = [], var_dict = {}):
subfname = _uploadCIF(subcif)
data['input'] = 'uploadsubgroupcif'
data['filename'] = subfname
out = requests.post(ISO_FORM_SITE, data=data)
line_iter = out.iter_lines()
# for line in line_iter:
# print(line.decode('utf-8'))
for line in line_iter:
if b'INPUT TYPE="hidden"' in line:
items = line.decode('utf-8').split(' ', 3)
name = items[2].split('=')[1].strip('"')
val = items[3].split('=', 1)[1].strip('>"')
data[name] = val
if specify == False and b'OPTION VALUE=' in line:
data['inputbasis'] = 'list'
items = line.decode('utf-8').split(' ', 1)
data['basisselect'] = items[1].split('=')[1].split(">")[0].strip('"')
print(items[1].split('=')[1].split(">")[0].strip('"'))
data['input'] = 'distort'
data['origintype'] = 'method4'
if specify == True:
basis_vars = ["basis11", "basis12", "basis13", "basis21", "basis22", "basis23", "basis31", "basis32", "basis33"]
if not basis:
print("No basis specified. Exiting...")
print("Try specifying a basis or setting specify to False")
if len(basis) != 9:
print("Incorrect number of elements for basis, expected 9")
else:
data['inputbasis'] = 'specify'
for i in range(len(basis_vars)):
data[basis_vars[i]] = f'{basis[i]}'
data['chooseorigin'] = False
data['trynearest'] = True
data['dmax'] = 1
data['zeromodes'] = False
for key, value in var_dict.items():
data[key] = value
return data
def _postDistort(data, isoformat):
"""Prepare the data for downloading.
"""
out = requests.post(ISO_FORM_SITE, data=data)
data = {}
line_iter = out.iter_lines()
for line in line_iter:
if b"<FORM ACTION" in line:
break
for line in line_iter:
if b'INPUT TYPE="hidden"' in line:
items = line.decode('utf-8').split(' ', 3)
name = items[2].split('=')[1].strip('"')
val = items[3].split('=', 1)[1].strip('>"')
data[name] = val
if b'input type="text"' in line:
items = line.decode('utf-8').split(' ', 5)
name = [s for s in items if 'name=' in s][0].split('=')[1].strip('"')
val = [s for s in items if 'value=' in s][0].split('=')[1].strip('"')
data[name] = val
if b'RADIO' in line and b'CHECKED' in line:
items = line.decode('utf-8').split(' ', 3)
name = items[2].split('"')[1]
val = items[3].split('"')[1]
data[name] = val
if b'</FORM>' in line:
break
data['origintype'] = isoformat
return data
def _postDisplayDistort(data, fname):
"""Download the ISODISTORT output.
"""
out = requests.post(ISO_FORM_SITE, data=data)
f = open(fname, 'wb')
f.write(out.text.encode('utf-8'))
f.close()
def get(cifname, outfname, method=3, var_dict={}, isoformat='topas', selection=1, subcif = "", specify = False, basis = []):
"""Interacts with the ISODISTORT website to get distortion modes.
Args:
cifname (str): The name of the local cif file you want to use.
outfname (str): The name of the file where you want the ISODISTORT
output saved.
method (int): Method number to be used by the ISODISTORT server. The
currently available method numbers are 3 and 4.
var_dict (dict): Variables to pass to ISODISTORT to set up the
subgroup symmetry, lattice, and basis. The required dictionary
keys depend on the method number chosen, as described below.
Method 3: Keys and default values are given below. It defaults to
P1 symmetry and an identity matrix for the supercell basis.\n
'subgroupsym' = '1 P1 C1-1'\n
'pointgroupsym' = '0'\n
'latticetype' = 'direct'\n
'centering' = 'd' (for default)\n
'basis11' = '1'\n
'basis12' = '0'\n
'basis13' = '0'\n
'basis21' = '0'\n
'basis22' = '1'\n
'basis23' = '0'\n
'basis31' = '0'\n
'basis32' = '0'\n
'basis33' = '1'\n
Note that the 'subgroupsym' value can simply be the space group
number (as a string) for the desired subgroup. It is not
recommended to use the space group symbol alone, since this is
not always read correctly.
isoformat (str): format of the output file requested from the
ISODISTORT server. Allowed values are:\n
'isovizdistortion'\n
'isovizdiffraction'\n
'structurefile'\n
'distortionfile'\n
'domains'\n
'primary'\n
'modesdetails'\n
'completemodesdetails'\n
'topas'\n
'fullprof'\n
'irreps'\n
'tree'\n
See https://stokes.byu.edu/iso/isodistorthelp.php#savedist for
information about each format.
selection (int): The number of the desired distortion from the list
of possible distortions provided by ISODISTORT, starting from 1
at the top and increasing as you move downward through the list.
Default value is 1.
"""
### check that the format and method number are acceptable
formatlist = ['isovizdistortion',
'isovizdiffraction',
'structurefile',
'distortionfile',
'domains',
'primary',
'modesdetails',
'completemodesdetails',
'topas',
'fullprof',
'irreps',
'tree']
methodlist = [3,4]
### if everything is good, move on to the interaction with ISODISTORT
if (isoformat in formatlist) and (method in methodlist):
parentcif = _uploadCIF(cifname)
out, data = _postParentCIF(parentcif)
# use the correct post function for the user-supplied method number
#data = eval('_postParentCIFm' + str(method) + '(parentcif, var_dict)')
if method == 3:
data = _setDatam3(out, data, var_dict = var_dict, selection = selection)
if method == 4:
data = _setDatam4(data, subcif, specify = specify, basis = basis, var_dict = var_dict)
#data = _postIsoSubGroup(data, selection)
data = _postDistort(data, isoformat)
_postDisplayDistort(data, outfname)
### inform the user if there is a problem
if isoformat not in formatlist:
print('This is not a valid format. Acceptable options are:\n')
print('isovizdistortion')
print('isovizdiffraction')
print('structurefile')
print('distortionfile')
print('domains')
print('primary')
print('modesdetails')
print('completemodesdetails')
print('topas')
print('fullprof')
print('irreps')
print('tree\n')
print('Please try again with one of these formats.')
return
if method not in methodlist:
print('This is not a valid method number. Acceptable options are:\n')
print(methodlist)
print('Please try again with one of these methods.')
print('Additional methods may become available in the future.')
return
Functions
def get(cifname, outfname, method=3, var_dict={}, isoformat='topas', selection=1, subcif='', specify=False, basis=[])-
Interacts with the ISODISTORT website to get distortion modes.
Args
cifname:str- The name of the local cif file you want to use.
outfname:str- The name of the file where you want the ISODISTORT output saved.
method:int- Method number to be used by the ISODISTORT server. The currently available method numbers are 3 and 4.
var_dict:dict-
Variables to pass to ISODISTORT to set up the subgroup symmetry, lattice, and basis. The required dictionary keys depend on the method number chosen, as described below.
Method 3: Keys and default values are given below. It defaults to P1 symmetry and an identity matrix for the supercell basis.
'subgroupsym' = '1 P1 C1-1' 'pointgroupsym' = '0' 'latticetype' = 'direct' 'centering' = 'd' (for default) 'basis11' = '1' 'basis12' = '0' 'basis13' = '0' 'basis21' = '0' 'basis22' = '1' 'basis23' = '0' 'basis31' = '0' 'basis32' = '0' 'basis33' = '1'Note that the 'subgroupsym' value can simply be the space group number (as a string) for the desired subgroup. It is not recommended to use the space group symbol alone, since this is not always read correctly.
isoformat:str-
format of the output file requested from the ISODISTORT server. Allowed values are:
'isovizdistortion' 'isovizdiffraction' 'structurefile' 'distortionfile' 'domains' 'primary' 'modesdetails' 'completemodesdetails' 'topas' 'fullprof' 'irreps' 'tree'See https://stokes.byu.edu/iso/isodistorthelp.php#savedist for information about each format.
selection:int- The number of the desired distortion from the list of possible distortions provided by ISODISTORT, starting from 1 at the top and increasing as you move downward through the list. Default value is 1.
Expand source code
def get(cifname, outfname, method=3, var_dict={}, isoformat='topas', selection=1, subcif = "", specify = False, basis = []): """Interacts with the ISODISTORT website to get distortion modes. Args: cifname (str): The name of the local cif file you want to use. outfname (str): The name of the file where you want the ISODISTORT output saved. method (int): Method number to be used by the ISODISTORT server. The currently available method numbers are 3 and 4. var_dict (dict): Variables to pass to ISODISTORT to set up the subgroup symmetry, lattice, and basis. The required dictionary keys depend on the method number chosen, as described below. Method 3: Keys and default values are given below. It defaults to P1 symmetry and an identity matrix for the supercell basis.\n 'subgroupsym' = '1 P1 C1-1'\n 'pointgroupsym' = '0'\n 'latticetype' = 'direct'\n 'centering' = 'd' (for default)\n 'basis11' = '1'\n 'basis12' = '0'\n 'basis13' = '0'\n 'basis21' = '0'\n 'basis22' = '1'\n 'basis23' = '0'\n 'basis31' = '0'\n 'basis32' = '0'\n 'basis33' = '1'\n Note that the 'subgroupsym' value can simply be the space group number (as a string) for the desired subgroup. It is not recommended to use the space group symbol alone, since this is not always read correctly. isoformat (str): format of the output file requested from the ISODISTORT server. Allowed values are:\n 'isovizdistortion'\n 'isovizdiffraction'\n 'structurefile'\n 'distortionfile'\n 'domains'\n 'primary'\n 'modesdetails'\n 'completemodesdetails'\n 'topas'\n 'fullprof'\n 'irreps'\n 'tree'\n See https://stokes.byu.edu/iso/isodistorthelp.php#savedist for information about each format. selection (int): The number of the desired distortion from the list of possible distortions provided by ISODISTORT, starting from 1 at the top and increasing as you move downward through the list. Default value is 1. """ ### check that the format and method number are acceptable formatlist = ['isovizdistortion', 'isovizdiffraction', 'structurefile', 'distortionfile', 'domains', 'primary', 'modesdetails', 'completemodesdetails', 'topas', 'fullprof', 'irreps', 'tree'] methodlist = [3,4] ### if everything is good, move on to the interaction with ISODISTORT if (isoformat in formatlist) and (method in methodlist): parentcif = _uploadCIF(cifname) out, data = _postParentCIF(parentcif) # use the correct post function for the user-supplied method number #data = eval('_postParentCIFm' + str(method) + '(parentcif, var_dict)') if method == 3: data = _setDatam3(out, data, var_dict = var_dict, selection = selection) if method == 4: data = _setDatam4(data, subcif, specify = specify, basis = basis, var_dict = var_dict) #data = _postIsoSubGroup(data, selection) data = _postDistort(data, isoformat) _postDisplayDistort(data, outfname) ### inform the user if there is a problem if isoformat not in formatlist: print('This is not a valid format. Acceptable options are:\n') print('isovizdistortion') print('isovizdiffraction') print('structurefile') print('distortionfile') print('domains') print('primary') print('modesdetails') print('completemodesdetails') print('topas') print('fullprof') print('irreps') print('tree\n') print('Please try again with one of these formats.') return if method not in methodlist: print('This is not a valid method number. Acceptable options are:\n') print(methodlist) print('Please try again with one of these methods.') print('Additional methods may become available in the future.') return