#!/usr/bin/env python import sys import numpy as np lattice_symbols = { 'P': [[0, 0, 0]], 'A': [[0, 0, 0], [0, 12, 12]], 'B': [[0, 0, 0], [12, 0, 12]], 'C': [[0, 0, 0], [12, 12, 0]], 'I': [[0, 0, 0], [12, 12, 12]], 'R': [[0, 0, 0]], 'H': [[0, 0, 0], [16, 8, 8], [8, 16, 16]], 'F': [[0, 0, 0], [0, 12, 12], [12, 0, 12], [12, 12, 0]] } def position2operation(p): r = np.zeros((3, 3), dtype=int) xyz = 'xyz' for i in range(3): for j, c in enumerate(xyz): pos = p[i].find(c) # if exist xyz if pos > -1: if pos == 0: r[i, j] = 1 if pos > 0: if p[i][pos-1] == '+': # +y, +z r[i, j] = 1 elif p[i][pos-1] == '-': # -x, -y, -z r[i, j] = -1 elif p[i][pos-1].isdigit(): # 2x if pos > 1: if p[i][pos-2] == '-': r[i, j] = - int(p[i][pos-1]) else: r[i, j] = int(p[i][pos-1]) else: r[i, j] = int(p[i][pos-1]) else: print("error") sys.exit(1) # print p, p[i], r[i,j] t = np.zeros(3, dtype=int) # translation multiplied by 24 try: table = str.maketrans({'x': '', 'y': '', 'z': '', '+': '', '-': ''}) except AttributeError: pass for i in range(3): try: trans = p[i].translate(None, 'xyz+-') except TypeError: trans = p[i].translate(table) pos = trans.find('/') if pos > -1: frac = trans.split('/') t[i] = int(frac[0]) * (24 / int(frac[1])) return r, t def encode_rotation(r): enc = [] for i in range(3): enc.append((r[i][0] + 2) * 9 + (r[i][1] + 1) * 3 + r[i][2] + 1) return enc[0] * 45**2 + enc[1] * 45 + enc[2] def decode_rotation(c): r = np.zeros((3, 3), dtype=int) dec = [c // 45**2, (c % 45**2) // 45, c % 45] for i in range(3): r[i][0] = dec[i] // 9 - 2 r[i][1] = (dec[i] % 9) // 3 - 1 r[i][2] = dec[i] % 3 - 1 return r def decode_trans(c): return c // 576, (c % 576) // 24, (c % 24) def run_test(len_wyckoffs, len_site, o_flat): sum_elem = 1 for i in range(1, 531): sum_site = 0 for j in range(len_wyckoffs[i], len_wyckoffs[i+1]): print(len_site[j+1] - len_site[j]), sum_site += len_site[j+1] - len_site[j] for x in o_flat[len_site[j]:len_site[j+1]]: t = decode_trans(x // 3**9) r = decode_rotation(x % 3**9) print(r, t) print(" %d" % sum_site) sum_elem += sum_site return sum_elem def damp_array_wyckoffs(len_wyckoff): print("static const int position_wyckoff[] =") text = " { %4d," % 0 for i, x in enumerate(len_wyckoffs[1:]): if i % 10 == 0: print(text) text = " " text += " %4d," % x print(text + " };") def damp_array_numsites(len_site): text = " { %5d," % 0 for i, x in enumerate(len_site[1:]): if i % 10 == 0: print(text) text = " " text += " %5d," % x print(text + " };") def damp_array_positions(orbits): text = " { %10d," % 0 for i, x in enumerate(orbits[1:]): if i % 5 == 0: print(text) text = " " text += " %10d," % x print(text + " };") def damp_array_positions_short(orbits, len_site): count = 0 print("static const int coordinates_first[] =") text = " { %9d," % 0 for i, x in enumerate(len_site[1:-1]): if count % 5 == 0: print(text) text = " " text += " %9d," % orbits[x] count += 1 print(text + " };") count = 0 print("static const int num_sitesym[] =") text = " { %3d," % 0 for i, x in enumerate(len_site[1:-1]): if count % 10 == 0: print(text) text = " " text += " %3d," % (len_site[i+2] - len_site[i+1]) count += 1 print(text + " };") def damp_array_site_symmetries(site_symmetries): print("static const char site_symmetries[][7] =") text = " { %8s," % "\" \"" for i, x in enumerate(site_symmetries): if i % 6 == 0: print(text) text = " " text += " %8s," % x print(text + " };") def read_wyckoff_csv(filename): with open(filename) as wyckoff_file: return parse_wyckoff_csv(wyckoff_file) def parse_wyckoff_csv(wyckoff_file): """Parse Wyckoff.csv There are 530 data sets. For one example: 9:C 1 2 1::::::: ::4:c:1:(x,y,z):(-x,y,-z):: ::2:b:2:(0,y,1/2)::: ::2:a:2:(0,y,0)::: """ rowdata = [] points = [] hP_nums = [433, 436, 444, 450, 452, 458, 460] for i, line in enumerate(wyckoff_file): if line.strip() == 'end of data': break rowdata.append(line.strip().split(':')) # 2:P -1 ::::::: <-- store line number if first element is number if rowdata[-1][0].isdigit(): points.append(i) points.append(i) wyckoff = [] for i in range(len(points) - 1): # 0 to 529 symbol = rowdata[points[i]][1] # e.g. "C 1 2 1" if i + 1 in hP_nums: symbol = symbol.replace('R', 'H', 1) wyckoff.append({'symbol': symbol.strip()}) # When the number of positions is larger than 4, # the positions are written in the next line. # So those positions are connected. for i in range(len(points) - 1): count = 0 wyckoff[i]['wyckoff'] = [] for j in range(points[i] + 1, points[i + 1]): # Hook if the third element is a number (multiplicity), e.g., # # 232:P 2/b 2/m 2/b::::::: <- ignored # ::8:r:1:(x,y,z):(-x,y,-z):(x,-y+1/2,-z):(-x,-y+1/2,z) # :::::(-x,-y,-z):(x,-y,z):(-x,y+1/2,z):(x,y+1/2,-z) <- ignored # ::4:q:..m:(x,0,z):(-x,0,-z):(x,1/2,-z):(-x,1/2,z) # ::4:p:..2:(0,y,1/2):(0,-y+1/2,1/2):(0,-y,1/2):(0,y+1/2,1/2) # ::4:o:..2:(1/2,y,0):(1/2,-y+1/2,0):(1/2,-y,0):(1/2,y+1/2,0) # ... if rowdata[j][2].isdigit(): pos = [] w = {'letter': rowdata[j][3].strip(), 'multiplicity': int(rowdata[j][2]), 'site_symmetry': rowdata[j][4].strip(), 'positions': pos} wyckoff[i]['wyckoff'].append(w) for k in range(4): if rowdata[j][k + 5]: # check if '(x,y,z)' or '' count += 1 pos.append(rowdata[j][k + 5]) else: for k in range(4): if rowdata[j][k + 5]: count += 1 pos.append(rowdata[j][k + 5]) # assertion for w in wyckoff[i]['wyckoff']: n_pos = len(w['positions']) n_pos *= len(lattice_symbols[wyckoff[i]['symbol'][0]]) assert n_pos == w['multiplicity'] return wyckoff def get_wyckoff_positions(wyckoff): positions = [] for i, w in enumerate(wyckoff): # print "%d:%s" % (i+1, w['symbol']) positions_hall = [] for p in w['wyckoff']: # print " %d %s %s" % (p['multiplicity'], # p['letter'], # p['site_symmetry']), # print # for o in p['positions']: # print o, # print positions_site = [] for pure_trans in lattice_symbols[w['symbol'][0]]: for o in p['positions']: r, t = position2operation(o.strip('()').split(',')) t += np.array(pure_trans) t = t % 24 positions_site.append((r, t)) positions_hall.append(positions_site) positions.append(positions_hall) return positions def get_site_symmetries(wyckoff): """List up site symmetries The data structure is as follows: wyckoff[0]['wyckoff'][0]['site_symmetry'] Note ---- Maximum length of string is 6. """ ssyms = [] for w in wyckoff: ssyms += ["\"%-6s\"" % w_s['site_symmetry'] for w_s in w['wyckoff']] damp_array_site_symmetries(ssyms) def encode_wyckoff_positions(positions): maxval = 0 maxmulti = 0 maxr = None maxt = None number = 0 pos_encoded = [] numerators = np.zeros(24, dtype=int) for i, positions_hall in enumerate(positions): pos_encoded_hall = [] for positions_site in positions_hall: pos_encoded_site = [] for r, t in positions_site: # for j in range(3): # numerators[t[j]] += 1 t_encode = t[0] * 576 + t[1] * 24 + t[2] r_encode = encode_rotation(r) total = t_encode * 45**3 + r_encode pos_encoded_site.append(total) if (r - decode_rotation(r_encode)).any(): print("Error in r %d" % (i+1)) sys.exit(1) # else: # if (abs(decode_rotation(r_encode)) > 1).any(): # print i+1 # print decode_rotation(r_encode) # print t # print if (t - decode_trans(t_encode)).any(): print("Error in t") sys.exit(1) if total > maxval: maxval = total maxr = r.copy() maxt = t.copy() number = i+1 pos_encoded_hall.append(pos_encoded_site) pos_encoded.append(pos_encoded_hall) # for i, n in enumerate(numerators): # print i, n # print "MAX: %d" % maxmulti # print "MAX: %d (%d)" % (maxval,number) # print maxr # print maxt return pos_encoded def get_data_arrays(encoded_positions): len_wyckoffs = [0] len_site = [0] sum_wyckoffs = 1 sum_site = 1 encpos_flat = [0] for h in encoded_positions: len_wyckoffs.append(sum_wyckoffs) sum_wyckoffs += len(h) for w in h: len_site.append(sum_site) sum_site += len(w) encpos_flat += w len_wyckoffs.append(sum_wyckoffs) len_site.append(sum_site) return len_wyckoffs, len_site, encpos_flat if __name__ == '__main__': import argparse parser = argparse.ArgumentParser() parser.set_defaults(is_site=False, is_position=False, is_wyckoff=False, is_site_short=False, is_site_symbol=False, is_wyckoff_short=False) parser.add_argument("--site", dest="is_site", action="store_true", help="Index of site symmetries of each space group") parser.add_argument("--site-symbol", dest="is_site_symbol", action="store_true", help="Site symmetry symbols") parser.add_argument("--position", dest="is_position", action="store_true", help=("Index of positions of each site symmetries and " "space group")) parser.add_argument("--wyckoff", dest="is_wyckoff", action="store_true", help="Encoded Wyckoff position operators") parser.add_argument("--wyckoff_short", dest="is_wyckoff_short", action="store_true", help="Encoded Wyckoff position operators") parser.add_argument("filename", nargs='?', help="Filename of cvs file") args = parser.parse_args() wyckoff = read_wyckoff_csv(args.filename) operations = get_wyckoff_positions(wyckoff) encoded_positions = encode_wyckoff_positions(operations) len_wyckoffs, len_site, encpos_flat = get_data_arrays(encoded_positions) # Test # sum_elem = run_test(len_wyckoffs, len_site, o_flat) # print "%d = %d" % (len(o_flat), sum_elem) # Damp full arrays if args.is_position: damp_array_wyckoffs(len_wyckoffs) if args.is_site: damp_array_numsites(len_site) if args.is_wyckoff: damp_array_positions(encpos_flat) if args.is_wyckoff_short: damp_array_positions_short(encpos_flat, len_site) if args.is_site_symbol: get_site_symmetries(wyckoff)