source: trunk/GSASIIspc.py @ 3433

Last change on this file since 3433 was 3433, checked in by vondreele, 5 years ago

objectScanIgnore additions
put in phase fro mag reflections
better checkMagextc

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 155.9 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3*GSASIIspc: Space group module*
4-------------------------------
5
6Space group interpretation routines. Note that space group information is
7stored in a :ref:`Space Group (SGData)<SGData_table>` object.
8
9"""
10########### SVN repository information ###################
11# $Date: 2018-06-13 17:58:28 +0000 (Wed, 13 Jun 2018) $
12# $Author: vondreele $
13# $Revision: 3433 $
14# $URL: trunk/GSASIIspc.py $
15# $Id: GSASIIspc.py 3433 2018-06-13 17:58:28Z vondreele $
16########### SVN repository information ###################
17from __future__ import division, print_function
18import numpy as np
19import numpy.linalg as nl
20import scipy.optimize as so
21import sys
22import copy
23import os.path as ospath
24
25import GSASIIpath
26GSASIIpath.SetVersionNumber("$Revision: 3433 $")
27
28npsind = lambda x: np.sin(x*np.pi/180.)
29npcosd = lambda x: np.cos(x*np.pi/180.)
30nxs = np.newaxis
31twopi = 2.0*np.pi
32DEBUG = False
33   
34################################################################################
35#### Space group codes
36################################################################################
37
38def SpcGroup(SGSymbol):
39    """
40    Determines cell and symmetry information from a short H-M space group name
41
42    :param SGSymbol: space group symbol (string) with spaces between axial fields
43    :returns: (SGError,SGData)
44   
45       * SGError = 0 for no errors; >0 for errors (see SGErrors below for details)
46       * SGData - is a dict (see :ref:`Space Group object<SGData_table>`) with entries:
47       
48             * 'SpGrp': space group symbol, slightly cleaned up
49             * 'SGFixed': True if space group data can not be changed, e.g. from magnetic cif; otherwise False
50             * 'SGGray': True if 1' in symbol - gray group for mag. incommensurate phases
51             * 'SGLaue':  one of '-1', '2/m', 'mmm', '4/m', '4/mmm', '3R',
52               '3mR', '3', '3m1', '31m', '6/m', '6/mmm', 'm3', 'm3m'
53             * 'SGInv': boolean; True if centrosymmetric, False if not
54             * 'SGLatt': one of 'P', 'A', 'B', 'C', 'I', 'F', 'R'
55             * 'SGUniq': one of 'a', 'b', 'c' if monoclinic, '' otherwise
56             * 'SGCen': cell centering vectors [0,0,0] at least
57             * 'SGOps': symmetry operations as [M,T] so that M*x+T = x'
58             * 'SGSys': one of 'triclinic', 'monoclinic', 'orthorhombic',
59               'tetragonal', 'rhombohedral', 'trigonal', 'hexagonal', 'cubic'
60             * 'SGPolax': one of ' ', 'x', 'y', 'x y', 'z', 'x z', 'y z',
61               'xyz', '111' for arbitrary axes
62             * 'SGPtGrp': one of 32 point group symbols (with some permutations), which
63                is filled by SGPtGroup, is external (KE) part of supersymmetry point group
64             * 'SSGKl': default internal (Kl) part of supersymmetry point group; modified
65                in supersymmetry stuff depending on chosen modulation vector for Mono & Ortho
66             * 'BNSlattsym': BNS lattice symbol & cenering op - used for magnetic structures
67
68    """
69    LaueSym = ('-1','2/m','mmm','4/m','4/mmm','3R','3mR','3','3m1','31m','6/m','6/mmm','m3','m3m')
70    LattSym = ('P','A','B','C','I','F','R')
71    UniqSym = ('','','a','b','c','',)
72    SysSym = ('triclinic','monoclinic','orthorhombic','tetragonal','rhombohedral','trigonal','hexagonal','cubic')
73    SGData = {}
74    if len(SGSymbol.split()) < 2:
75        return SGErrors(0),SGData
76    if ':R' in SGSymbol:
77        SGSymbol = SGSymbol.replace(':',' ')    #get rid of ':' in R space group symbols from some cif files
78    SGData['SGGray'] = False
79    if "1'" in SGSymbol:        #set for incommensurate magnetic
80        SGData['SGGray'] = True
81        SGSymbol = SGSymbol.replace("1'",'')
82    SGSymbol = SGSymbol.split(':')[0]   #remove :1/2 setting symbol from some cif files
83    if '-2' in SGSymbol:    #replace bad but legal symbols with correct equivalents
84        SGSymbol = SGSymbol.replace('-2','m')
85    if SGSymbol.split()[1] =='3/m':
86        SGSymbol = SGSymbol.replace('3/m','-6')
87    import pyspg
88    SGInfo = pyspg.sgforpy(SGSymbol)
89    SGData['SpGrp'] = SGSymbol.strip().lower().capitalize()
90    SGData['SGLaue'] = LaueSym[SGInfo[0]-1]
91    SGData['SGInv'] = bool(SGInfo[1])
92    SGData['SGLatt'] = LattSym[SGInfo[2]-1]
93    SGData['SGUniq'] = UniqSym[SGInfo[3]+1]
94    SGData['SGFixed'] = False
95    SGData['SGOps'] = []
96    SGData['SGGen'] = []
97    for i in range(SGInfo[5]):
98        Mat = np.array(SGInfo[6][i])
99        Trns = np.array(SGInfo[7][i])
100        SGData['SGOps'].append([Mat,Trns])
101        if 'array' in str(type(SGInfo[8])):        #patch for old fortran bin?
102            SGData['SGGen'].append(int(SGInfo[8][i]))
103    SGData['BNSlattsym'] = [LattSym[SGInfo[2]-1],[0,0,0]]
104    lattSpin = []
105    if SGData['SGLatt'] == 'P':
106        SGData['SGCen'] = np.array(([0,0,0],))
107    elif SGData['SGLatt'] == 'A':
108        SGData['SGCen'] = np.array(([0,0,0],[0,.5,.5]))
109        lattSpin += [1,]
110    elif SGData['SGLatt'] == 'B':
111        SGData['SGCen'] = np.array(([0,0,0],[.5,0,.5]))
112        lattSpin += [1,]
113    elif SGData['SGLatt'] == 'C':
114        SGData['SGCen'] = np.array(([0,0,0],[.5,.5,0,]))
115        lattSpin += [1,]
116    elif SGData['SGLatt'] == 'I':
117        SGData['SGCen'] = np.array(([0,0,0],[.5,.5,.5]))
118        lattSpin += [1,]
119    elif SGData['SGLatt'] == 'F':
120        SGData['SGCen'] = np.array(([0,0,0],[0,.5,.5],[.5,0,.5],[.5,.5,0,]))
121        lattSpin += [1,1,1,1]
122    elif SGData['SGLatt'] == 'R':
123        SGData['SGCen'] = np.array(([0,0,0],[2./3,1./3,1./3],[1./3,2./3,2./3]))
124
125    if SGData['SGInv']:
126        if SGData['SGLaue'] in ['-1','2/m','mmm']:
127            Ibar = 7
128        elif SGData['SGLaue'] in ['4/m','4/mmm']:
129            Ibar = 1
130        elif SGData['SGLaue'] in ['3R','3mR','3','3m1','31m','6/m','6/mmm']:
131            Ibar = 15 #8+4+2+1
132        else:
133            Ibar = 4
134        Ibarx = Ibar&14
135    else:
136        Ibarx = 8
137        if SGData['SGLaue'] in ['-1','2/m','mmm','m3','m3m']:
138            Ibarx = 0
139    moregen = []
140    for i,gen in enumerate(SGData['SGGen']):
141        if SGData['SGLaue'] in ['m3','m3m']:
142            if gen in [1,2,4]:
143                SGData['SGGen'][i] = 4
144            elif gen < 7:
145                SGData['SGGen'][i] = 0
146        elif SGData['SGLaue'] in ['4/m','4/mmm','3R','3mR','3','3m1','31m','6/m','6/mmm']:
147            if gen == 2:
148                SGData['SGGen'][i] = 4
149            elif gen in [3,5]:
150                SGData['SGGen'][i] = 3
151            elif gen == 6:
152                if SGData['SGLaue'] in ['4/m','4/mmm']:
153                    SGData['SGGen'][i] = 128
154                else:
155                    SGData['SGGen'][i] = 16
156            elif not SGData['SGInv'] and gen == 12:
157                SGData['SGGen'][i] = 8
158            elif (not SGData['SGInv']) and (SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']) and (gen == 1):
159                SGData['SGGen'][i] = 24
160        gen = SGData['SGGen'][i]
161        if gen == 99:
162            gen = 8
163            if SGData['SGLaue'] in ['3m1','31m','6/m','6/mmm']:
164                gen = 3
165            elif SGData['SGLaue'] == 'm3m':
166                gen = 12
167            SGData['SGGen'][i] = gen
168        elif gen == 98:
169            gen = 8
170            if SGData['SGLaue'] in ['3m1','31m','6/m','6/mmm']:
171                gen = 4
172            SGData['SGGen'][i] = gen
173        elif not SGData['SGInv'] and gen in [23,] and SGData['SGLaue'] in ['m3','m3m']:
174            SGData['SGGen'][i] = 24
175        elif gen >= 16 and gen != 128:
176            if not SGData['SGInv']:
177                gen = 31
178            else:
179                gen ^= Ibarx
180            SGData['SGGen'][i] = gen
181        if SGData['SGInv']:
182            if gen < 128:
183                moregen.append(SGData['SGGen'][i]^Ibar)
184            else:
185                moregen.append(1)
186    SGData['SGGen'] += moregen
187    if SGData['SGLaue'] in '-1':
188        SGData['SGSys'] = SysSym[0]
189    elif SGData['SGLaue'] in '2/m':
190        SGData['SGSys'] = SysSym[1]
191    elif SGData['SGLaue'] in 'mmm':
192        SGData['SGSys'] = SysSym[2]
193    elif SGData['SGLaue'] in ['4/m','4/mmm']:
194        SGData['SGSys'] = SysSym[3]
195    elif SGData['SGLaue'] in ['3R','3mR']:
196        SGData['SGSys'] = SysSym[4]
197    elif SGData['SGLaue'] in ['3','3m1','31m']:
198        SGData['SGSys'] = SysSym[5]
199    elif SGData['SGLaue'] in ['6/m','6/mmm']:
200        SGData['SGSys'] = SysSym[6]
201    elif SGData['SGLaue'] in ['m3','m3m']:
202        SGData['SGSys'] = SysSym[7]
203    SGData['SGPolax'] = SGpolar(SGData)
204    SGData['SGPtGrp'],SGData['SSGKl'] = SGPtGroup(SGData)
205
206    if SGData['SGLatt'] == 'R':
207        if SGData['SGPtGrp'] in ['3',]:
208            SGData['SGSpin'] = 4*[1,]
209        elif SGData['SGPtGrp'] in  ['-3','32','3m']:
210            SGData['SGSpin'] = 5*[1,]
211        elif SGData['SGPtGrp'] in  ['-3m',]:
212            SGData['SGSpin'] = 6*[1,]
213       
214    else:
215        if SGData['SGPtGrp'] in ['1','3','23',]:
216            SGData['SGSpin'] = lattSpin+[1,]
217        elif SGData['SGPtGrp'] in ['-1','2','m','4','-4','-3','312','321','3m1','31m','6','-6','432','-43m']:
218            SGData['SGSpin'] = lattSpin+[1,1,]
219        elif SGData['SGPtGrp'] in ['2/m','4/m','422','4mm','-42m','-4m2','-3m1','-31m',
220            '6/m','622','6mm','-6m2','-62m','m3','m3m']:
221            SGData['SGSpin'] = lattSpin+[1,1,1,]
222        else: #'222'-'mmm','4/mmm','6/mmm'
223            SGData['SGSpin'] = lattSpin+[1,1,1,1,]
224    return SGInfo[-1],SGData
225
226def SGErrors(IErr):
227    '''
228    Interprets the error message code from SpcGroup. Used in SpaceGroup.
229   
230    :param IErr: see SGError in :func:`SpcGroup`
231    :returns:
232        ErrString - a string with the error message or "Unknown error"
233    '''
234
235    ErrString = [' ',
236        'Less than 2 operator fields were found',
237        'Illegal Lattice type, not P, A, B, C, I, F or R',
238        'Rhombohedral lattice requires a 3-axis',
239        'Minus sign does not preceed 1, 2, 3, 4 or 6',
240        'Either a 5-axis anywhere or a 3-axis in field not allowed',
241        ' ',
242        'I for COMPUTED GO TO out of range.',
243        'An a-glide mirror normal to A not allowed',
244        'A b-glide mirror normal to B not allowed',
245        'A c-glide mirror normal to C not allowed',
246        'D-glide in a primitive lattice not allowed',
247        'A 4-axis not allowed in the 2nd operator field',
248        'A 6-axis not allowed in the 2nd operator field',
249        'More than 24 matrices needed to define group',
250        ' ',
251        'Improper construction of a rotation operator',
252        'Mirror following a / not allowed',
253        'A translation conflict between operators',
254        'The 2bar operator is not allowed',
255        '3 fields are legal only in R & m3 cubic groups',
256        'Syntax error. Expected I -4 3 d at this point',
257        ' ',
258        'A or B centered tetragonal not allowed',
259        ' ','unknown error in sgroup',' ',' ',' ',
260        'Illegal character in the space group symbol',
261        ]
262    try:
263        return ErrString[IErr]
264    except:
265        return "Unknown error"
266
267def SGpolar(SGData):
268    '''
269    Determine identity of polar axes if any
270    '''
271    POL = ('','x','y','x y','z','x z','y z','xyz','111')
272    NP = [1,2,4]
273    NPZ = [0,1]
274    for M,T in SGData['SGOps']:
275        for i in range(3):
276            if M[i][i] <= 0.: NP[i] = 0
277        if M[0][2] > 0: NPZ[0] = 8
278        if M[1][2] > 0: NPZ[1] = 0
279    NPol = (NP[0]+NP[1]+NP[2]+NPZ[0]*NPZ[1])*(1-int(SGData['SGInv']))
280    return POL[NPol]
281   
282def SGPtGroup(SGData):
283    '''
284    Determine point group of the space group - done after space group symbol has
285    been evaluated by SpcGroup. Only short symbols are allowed
286   
287    :param SGData: from :func SpcGroup
288    :returns: SSGPtGrp & SSGKl (only defaults for Mono & Ortho)
289    '''
290    Flds = SGData['SpGrp'].split()
291    if len(Flds) < 2:
292        return '',[]
293    if SGData['SGLaue'] == '-1':    #triclinic
294        if '-' in Flds[1]:
295            return '-1',[-1,]
296        else:
297            return '1',[1,]
298    elif SGData['SGLaue'] == '2/m': #monoclinic - default for 2D modulation vector
299        if '/' in SGData['SpGrp']:
300            return '2/m',[-1,1]
301        elif '2' in SGData['SpGrp']:
302            return '2',[-1,]
303        else:
304            return 'm',[1,]
305    elif SGData['SGLaue'] == 'mmm': #orthorhombic
306        if SGData['SpGrp'].count('2') == 3:
307            return '222',[-1,-1,-1]
308        elif SGData['SpGrp'].count('2') == 1:
309            if SGData['SGPolax'] == 'x':
310                return '2mm',[-1,1,1]
311            elif SGData['SGPolax'] == 'y':
312                return 'm2m',[1,-1,1]
313            elif SGData['SGPolax'] == 'z':
314                return 'mm2',[1,1,-1]
315        else:
316            return 'mmm',[1,1,1]
317    elif SGData['SGLaue'] == '4/m': #tetragonal
318        if '/' in SGData['SpGrp']:
319            return '4/m',[1,-1]
320        elif '-' in Flds[1]:
321            return '-4',[-1,]
322        else:
323            return '4',[1,]
324    elif SGData['SGLaue'] == '4/mmm':
325        if '/' in SGData['SpGrp']:
326            return '4/mmm',[1,-1,1,1]
327        elif '-' in Flds[1]:
328            if '2' in Flds[2]:
329                return '-42m',[-1,-1,1]
330            else:
331                return '-4m2',[-1,1,-1]             
332        elif '2' in Flds[2:]:
333            return '422',[1,-1,-1]
334        else:
335            return '4mm',[1,1,1]
336    elif SGData['SGLaue'] in ['3','3R']:  #trigonal/rhombohedral
337        if '-' in Flds[1]:
338            return '-3',[-1,]
339        else:
340            return '3',[1,]
341    elif SGData['SGLaue'] == '3mR' or 'R' in Flds[0]:
342        if '2' in Flds[2]:
343            return '32',[1,-1]
344        elif '-' in Flds[1]:
345            return '-3m',[-1,1]
346        else:
347            return '3m',[1,1]
348    elif SGData['SGLaue'] == '3m1':
349        if '2' in Flds[2]:
350            return '321',[1,-1,1]
351        elif '-' in Flds[1]:
352            return '-3m1',[-1,1,1]
353        else:
354            return '3m1',[1,1,1]
355    elif SGData['SGLaue'] == '31m':
356        if '2' in Flds[3]:
357            return '312',[1,1,-1]
358        elif '-' in Flds[1]:
359            return '-31m',[-1,1,1]
360        else:
361            return '31m',[1,1,1]
362    elif SGData['SGLaue'] == '6/m': #hexagonal
363        if '/' in SGData['SpGrp']:
364            return '6/m',[1,-1]
365        elif '-' in SGData['SpGrp']:
366            return '-6',[-1,]
367        else:
368            return '6',[1,]
369    elif SGData['SGLaue'] == '6/mmm':
370        if '/' in SGData['SpGrp']:
371            return '6/mmm',[1,-1,1,1]
372        elif '-' in Flds[1]:
373            if '2' in Flds[2]:
374                return '-62m',[-1,-1,1]
375            else:
376                return '-6m2',[-1,1,-1]                 
377        elif '2' in Flds[2:]:
378            return '622',[1,-1,-1]
379        else:
380            return '6mm',[1,1,1]   
381    elif SGData['SGLaue'] == 'm3':      #cubic - no (3+1) supersymmetry
382        if '2' in Flds[1]:
383            return '23',[]
384        else: 
385            return 'm3',[]
386    elif SGData['SGLaue'] == 'm3m':
387        if '4' in Flds[1]:
388            if '-' in Flds[1]:
389                return '-43m',[]
390            else:
391                return '432',[]
392        else:
393            return 'm3m',[]
394   
395def SGPrint(SGData,AddInv=False):
396    '''
397    Print the output of SpcGroup in a nicely formatted way. Used in SpaceGroup
398
399    :param SGData: from :func:`SpcGroup`
400    :returns:
401        SGText - list of strings with the space group details
402        SGTable - list of strings for each of the operations
403    '''
404    if SGData.get('SGFixed',False):       #inverses included in ops for cif fixed
405        Mult = len(SGData['SGCen'])*len(SGData['SGOps'])
406    else:
407        Mult = len(SGData['SGCen'])*len(SGData['SGOps'])*(int(SGData['SGInv'])+1)
408    SGText = []
409    SGText.append(' Space Group: '+SGData['SpGrp'])
410    if SGData.get('SGGray',False): SGText[-1] += " 1'"
411    CentStr = 'centrosymmetric'
412    if not SGData['SGInv']:
413        CentStr = 'non'+CentStr
414    if SGData['SGLatt'] in 'ABCIFR':
415        SGText.append(' The lattice is '+CentStr+' '+SGData['SGLatt']+'-centered '+SGData['SGSys'].lower())
416    else:
417        SGText.append(' The lattice is '+CentStr+' '+'primitive '+SGData['SGSys'].lower()) 
418    SGText.append(' The Laue symmetry is '+SGData['SGLaue'])
419    if 'SGPtGrp' in SGData:         #patch
420        SGText.append(' The lattice point group is '+SGData['SGPtGrp'])
421    SGText.append(' Multiplicity of a general site is '+str(Mult))
422    if SGData['SGUniq'] in ['a','b','c']:
423        SGText.append(' The unique monoclinic axis is '+SGData['SGUniq'])
424    if SGData['SGInv']:
425        SGText.append(' The inversion center is located at 0,0,0')
426    if SGData['SGPolax']:
427        SGText.append(' The location of the origin is arbitrary in '+SGData['SGPolax'])
428    SGText.append(' ')
429    if len(SGData['SGCen']) == 1:
430        SGText.append(' The equivalent positions are:\n')
431    else:   
432        SGText.append(' The equivalent positions are:\n')
433        SGText.append(' ('+Latt2text(SGData['SGCen'])+')+\n')
434    SGTable = []
435    for i,Opr in enumerate(SGData['SGOps']):
436        SGTable.append('(%2d) %s'%(i+1,MT2text(Opr)))
437    if AddInv and SGData['SGInv']:
438        for i,Opr in enumerate(SGData['SGOps']):
439            IOpr = [-Opr[0],-Opr[1]]
440            SGTable.append('(%2d) %s'%(i+1,MT2text(IOpr)))       
441#    if SGData.get('SGGray',False) and not SGData.get('SGFixed',False):
442#        SGTable.append("     for 1'")
443#        for i,Opr in enumerate(SGData['SGOps']):
444#            SGTable.append('(%2d) %s'%(i+1,MT2text(Opr)))
445#        if AddInv and SGData['SGInv']:
446#            for i,Opr in enumerate(SGData['SGOps']):
447#                IOpr = [-Opr[0],-Opr[1]]
448#                SGTable.append('(%2d) %s'%(i+1,MT2text(IOpr)))       
449    return SGText,SGTable
450
451def AllOps(SGData):
452    '''
453    Returns a list of all operators for a space group, including those for
454    centering and a center of symmetry
455   
456    :param SGData: from :func:`SpcGroup`
457    :returns: (SGTextList,offsetList,symOpList,G2oprList) where
458
459      * SGTextList: a list of strings with formatted and normalized
460        symmetry operators.
461      * offsetList: a tuple of (dx,dy,dz) offsets that relate the GSAS-II
462        symmetry operation to the operator in SGTextList and symOpList.
463        these dx (etc.) values are added to the GSAS-II generated
464        positions to provide the positions that are generated
465        by the normalized symmetry operators.       
466      * symOpList: a list of tuples with the normalized symmetry
467        operations as (M,T) values
468        (see ``SGOps`` in the :ref:`Space Group object<SGData_table>`)
469      * G2oprList: The GSAS-II operations for each symmetry operation as
470        a tuple with (center,mult,opnum,opcode), where center is (0,0,0), (0.5,0,0),
471        (0.5,0.5,0.5),...; where mult is 1 or -1 for the center of symmetry
472        where opnum is the number for the symmetry operation, in ``SGOps``
473        (starting with 0) and opcode is mult*(100*icen+j+1).
474    '''
475    SGTextList = []
476    offsetList = []
477    symOpList = []
478    G2oprList = []
479    G2opcodes = []
480    onebar = (1,)
481    if SGData['SGInv']:
482        onebar += (-1,)
483    for icen,cen in enumerate(SGData['SGCen']):
484        for mult in onebar:
485            for j,(M,T) in enumerate(SGData['SGOps']):
486                offset = [0,0,0]
487                Tprime = (mult*T)+cen
488                for i in range(3):
489                    while Tprime[i] < 0:
490                        Tprime[i] += 1
491                        offset[i] += 1
492                    while Tprime[i] >= 1:
493                        Tprime[i] += -1
494                        offset[i] += -1
495                Opr = [mult*M,Tprime]
496                OPtxt = MT2text(Opr)
497                SGTextList.append(OPtxt.replace(' ',''))
498                offsetList.append(tuple(offset))
499                symOpList.append((mult*M,Tprime))
500                G2oprList.append((cen,mult,j))
501                G2opcodes.append(mult*(100*icen+j+1))
502    return SGTextList,offsetList,symOpList,G2oprList,G2opcodes
503   
504def MT2text(Opr,reverse=False):
505    "From space group matrix/translation operator returns text version"
506    XYZ = ('-Z','-Y','-X','X-Y','ERR','Y-X','X','Y','Z')
507    TRA = ('   ','ERR','1/6','1/4','1/3','ERR','1/2','ERR','2/3','3/4','5/6','ERR')
508    Fld = ''
509    M,T = Opr
510    for j in range(3):
511        IJ = int(round(2*M[j][0]+3*M[j][1]+4*M[j][2]+4))%12
512        IK = int(round(T[j]*12))%12
513        if IK:
514            if IJ < 3:
515                if reverse:
516                    Fld += (XYZ[IJ]+'+'+TRA[IK]).rjust(5)
517                else:
518                    Fld += (TRA[IK]+XYZ[IJ]).rjust(5)
519            else:
520                if reverse:
521                    Fld += (XYZ[IJ]+'+'+TRA[IK]).rjust(5)
522                else:
523                    Fld += (TRA[IK]+'+'+XYZ[IJ]).rjust(5)
524        else:
525            Fld += XYZ[IJ].rjust(5)
526        if j != 2: Fld += ', '
527    return Fld
528   
529def Latt2text(Cen):
530    "From lattice centering vectors returns ';' delimited cell centering vectors"
531    lattTxt = ''
532    fracList = ['1/2','1/3','2/3','1/4','3/4','1/5','2/5','3/5','4/5','1/6','5/6',
533        '1/7','2/7','3/7','4/7','5/7','6/7','1/8','3/8','5/8','7/8','1/9','2/9','4/9','5/9','7/9','8/9']
534    mulList = [2,3,3,4,4,5,5,5,5,6,6,7,7,7,7,7,7,8,8,8,8,9,9,9,9,9,9]
535    prodList = [1.,1.,2.,1.,3.,1.,2.,3.,4.,1.,5.,1.,2.,3.,4.,5.,6.,1.,3.,5.,7.,1.,2.,4.,5.,7.,8.]
536    nCen = len(Cen)
537    for i,cen in enumerate(Cen):
538        txt = ''
539        for icen in cen:
540            if not icen:
541                txt += '0,'
542                continue
543            for mul,prod,frac in zip(mulList,prodList,fracList):
544                if abs(icen*mul-prod) < 1.e-5:
545                    txt += frac+','
546                    break
547        lattTxt += txt[:-1]+'; '
548        if i and not i%8 and i < nCen-1:    #not for the last cen!
549            lattTxt += '\n     '
550    return lattTxt[:-2]
551
552def SpaceGroup(SGSymbol):
553    '''
554    Print the output of SpcGroup in a nicely formatted way.
555
556    :param SGSymbol: space group symbol (string) with spaces between axial fields
557    :returns: nothing
558    '''
559    E,A = SpcGroup(SGSymbol)
560    if E > 0:
561        print (SGErrors(E))
562        return
563    for l in SGPrint(A):
564        print (l)
565################################################################################
566#### Magnetic space group stuff
567################################################################################
568
569def GetGenSym(SGData):
570    '''
571    Get the space group generator symbols
572    :param SGData: from :func:`SpcGroup`
573    LaueSym = ('-1','2/m','mmm','4/m','4/mmm','3R','3mR','3','3m1','31m','6/m','6/mmm','m3','m3m')
574    LattSym = ('P','A','B','C','I','F','R')
575   
576    '''
577    OprNames = [GetOprPtrName(str(irtx)) for irtx in PackRot(SGData['SGOps'])]
578    if SGData['SGInv']:
579        OprNames += [GetOprPtrName(str(-irtx)) for irtx in PackRot(SGData['SGOps'])]
580    Nsyms = len(SGData['SGOps'])
581    if SGData['SGInv'] and not SGData['SGFixed']: Nsyms *= 2
582    UsymOp = ['1',]
583    OprFlg = [0,] 
584    if Nsyms == 2:                    #Centric triclinic or acentric monoclinic
585        UsymOp.append(OprNames[1])
586        OprFlg.append(SGData['SGGen'][1])
587    elif Nsyms == 4:                    #Point symmetry 2/m, 222, 22m, or 4
588        if '4z' in OprNames[1]:          #Point symmetry 4 or -4
589            UsymOp.append(OprNames[1])
590            OprFlg.append(SGData['SGGen'][1])
591        elif not SGData['SGInv']:       #Acentric Orthorhombic
592            if 'm' in OprNames[1:4]:    #22m, 2m2 or m22
593                if '2' in OprNames[1]:      #Acentric orthorhombic, 2mm
594                    UsymOp.append(OprNames[2])
595                    OprFlg.append(SGData['SGGen'][2])
596                    UsymOp.append(OprNames[3])
597                    OprFlg.append(SGData['SGGen'][3])
598                elif '2' in OprNames[2]:    #Acentric orthorhombic, m2m
599                    UsymOp.append(OprNames[1])
600                    OprFlg.append(SGData['SGGen'][1])
601                    UsymOp.append(OprNames[3])
602                    OprFlg.append(SGData['SGGen'][3])
603                else:                       #Acentric orthorhombic, mm2
604                    UsymOp.append(OprNames[1])
605                    OprFlg.append(SGData['SGGen'][1])
606                    UsymOp.append(OprNames[2])
607                    OprFlg.append(SGData['SGGen'][2])
608            else:                           #Acentric orthorhombic, 222
609                SGData['SGGen'][1:] = [4,2,1]
610                UsymOp.append(OprNames[1])
611                OprFlg.append(SGData['SGGen'][1])
612                UsymOp.append(OprNames[2])
613                OprFlg.append(SGData['SGGen'][2])
614                UsymOp.append(OprNames[3])
615                OprFlg.append(SGData['SGGen'][3])
616        else:                               #Centric Monoclinic
617            UsymOp.append(OprNames[1])
618            OprFlg.append(SGData['SGGen'][1])
619            UsymOp.append(OprNames[3])
620            OprFlg.append(SGData['SGGen'][3])
621    elif Nsyms == 6:                    #Point symmetry 32, 3m or 6
622            if '6' in OprNames[1]:      #Hexagonal 6/m Laue symmetry
623                UsymOp.append(OprNames[1])
624                OprFlg.append(SGData['SGGen'][1])
625            else:                       #Trigonal
626                UsymOp.append(OprNames[4])
627                OprFlg.append(SGData['SGGen'][3])
628                if '2110' in OprNames[1]: UsymOp[-1] = ' 2100 '
629    elif Nsyms == 8:                    #Point symmetry mmm, 4/m, or 422, etc
630        if '4' in OprNames[1]:           #Tetragonal
631            if SGData['SGInv']:         #4/m
632                UsymOp.append(OprNames[1])
633                OprFlg.append(SGData['SGGen'][1])
634                UsymOp.append(OprNames[6])
635                OprFlg.append(SGData['SGGen'][6])
636            else:
637                if 'x' in OprNames[4]:      #4mm type group
638                    UsymOp.append(OprNames[4])
639                    OprFlg.append(6)
640                    UsymOp.append(OprNames[7])
641                    OprFlg.append(8)
642                else:                       #-42m, -4m2, and 422 type groups
643                    UsymOp.append(OprNames[5])
644                    OprFlg.append(8)
645                    UsymOp.append(OprNames[6])
646                    OprFlg.append(19)
647        else:                               #Orthorhombic, mmm
648            UsymOp.append(OprNames[1])
649            OprFlg.append(SGData['SGGen'][1])
650            UsymOp.append(OprNames[2])
651            OprFlg.append(SGData['SGGen'][2])
652            UsymOp.append(OprNames[7])
653            OprFlg.append(SGData['SGGen'][7])
654    elif Nsyms == 12 and '3' in OprNames[1] and SGData['SGSys'] != 'cubic':        #Trigonal
655        UsymOp.append(OprNames[3])
656        OprFlg.append(SGData['SGGen'][3])
657        UsymOp.append(OprNames[9])
658        OprFlg.append(SGData['SGGen'][9])
659    elif Nsyms == 12 and '6' in OprNames[1]:        #Hexagonal
660        if 'mz' in OprNames[9]:                     #6/m
661            UsymOp.append(OprNames[1])
662            OprFlg.append(SGData['SGGen'][1])
663            UsymOp.append(OprNames[6])
664            OprFlg.append(SGData['SGGen'][6])
665        else:                                       #6mm, -62m, -6m2 or 622
666            UsymOp.append(OprNames[6])
667            OprFlg.append(18)
668            if 'm' in UsymOp[-1]: OprFlg[-1] = 20
669            UsymOp.append(OprNames[7])
670            OprFlg.append(24)
671    elif Nsyms in [16,24]:
672        if '3' in OprNames[1]:
673            UsymOp.append('')
674            OprFlg.append(SGData['SGGen'][3])
675            for i in range(Nsyms):
676                if 'mx' in OprNames[i]:
677                    UsymOp[-1] = OprNames[i]
678                elif 'm11' in OprNames[i]:
679                    UsymOp[-1] = OprNames[i]
680                elif '211' in OprNames[i]:
681                    UsymOp[-1] = OprNames[i]
682                    OprFlg[-1] = 24
683        else:                                     #4/mmm or 6/mmm
684            UsymOp.append('  mz  ')
685            OprFlg.append(1)
686            if '4' in OprNames[1]:                  #4/mmm
687                UsymOp.append('  mx  ')
688                OprFlg.append(20)
689                UsymOp.append(' m110 ')
690                OprFlg.append(24)
691            else:                                   #6/mmm
692                UsymOp.append(' m110 ')
693                OprFlg.append(4)
694                UsymOp.append(' m+-0 ')
695                OprFlg.append(8)
696    else:                                           #System is cubic
697        if Nsyms == 48:
698            UsymOp.append('  mx  ')
699            OprFlg.append(4)
700            UsymOp.append(' m110 ')
701            OprFlg.append(24)
702           
703    if 'P' in SGData['SGLatt']:
704        if SGData['SGSys'] == 'triclinic':
705            BNSsym = {'P_a':[.5,0,0],'P_b':[0,.5,0],'P_c':[0,0,.5]}           
706        elif SGData['SGSys'] == 'monoclinic':
707            BNSsym = {'P_a':[.5,0,0],'P_b':[0,.5,0],'P_c':[0,0,.5]}
708            if SGData['SGUniq'] == 'a':
709                BNSsym.update({'P_B':[.5,0,.5],'P_C':[.5,.5,0]})
710            elif SGData['SGUniq'] == 'b':
711                BNSsym.update({'P_A':[.5,.5,0],'P_C':[0,.5,.5]})
712            elif SGData['SGUniq'] == 'c':
713                BNSsym.update({'P_A':[0,.5,.5],'P_B':[.5,0,.5]})
714        elif SGData['SGSys'] == 'orthorhombic':
715            BNSsym = {'P_a':[.5,0,0],'P_b':[0,.5,0],'P_c':[0,0,.5],
716                'P_A':[0,.5,.5],'P_B':[.5,0,.5],'P_C':[.5,.5,0],'P_I':[.5,.5,.5]}
717        elif SGData['SGSys'] == 'tetragonal':
718            BNSsym = {'P_c':[0,0,.5],'P_C':[.5,.5,0],'P_I':[.5,.5,.5]}           
719        elif SGData['SGSys'] in ['trigonal','hexagonal']:
720            BNSsym = {'P_c':[0,0,.5]}           
721        elif SGData['SGSys'] == 'cubic':
722            BNSsym = {'P_I':[.5,.5,.5]}           
723           
724    elif 'A' in SGData['SGLatt']:
725        if SGData['SGSys'] == 'monoclinic':
726            BNSsym = {}
727            if SGData['SGUniq'] == 'b':
728                BNSsym.update({'A_a':[.5,0,0],'A_c':[0,0,.5]})
729            elif SGData['SGUniq'] == 'c':
730                BNSsym.update({'A_a':[.5,0,0],'A_b':[0,.5,0]})
731        elif SGData['SGSys'] == 'orthorhombic':
732            BNSsym = {'A_a':[.5,0,0],'A_b':[0,.5,0],'A_c':[0,0,.5],
733               'A_B':[.5,0,.5],'A_C':[.5,.5,0]}   
734        elif SGData['SGSys'] == 'triclinic':
735            BNSsym = {'A_a':[.5,0,0],'A_b':[0,.5,0],'A_c':[0,0,.5]}   
736           
737    elif 'B' in SGData['SGLatt']:
738        if SGData['SGSys'] == 'monoclinic':
739            BNSsym = {}
740            if SGData['SGUniq'] == 'a':
741                BNSsym.update({'B_b':[0,.5,0],'B_c':[0,0,.5]})
742            elif SGData['SGUniq'] == 'c':
743                BNSsym.update({'B_a':[.5,0,0],'B_b':[0,.5,0]})
744        elif SGData['SGSys'] == 'orthorhombic':
745            BNSsym = {'B_a':[.5,0,0],'B_b':[0,.5,0],'B_c':[0,0,.5],
746                'B_A':[0,.5,.5],'B_C':[.5,.5,0]}     
747        elif SGData['SGSys'] == 'triclinic':
748            BNSsym = {'B_a':[.5,0,0],'B_b':[0,.5,0],'B_c':[0,0,.5]}     
749           
750    elif 'C' in SGData['SGLatt']:
751        if SGData['SGSys'] == 'monoclinic':
752            BNSsym = {}
753            if SGData['SGUniq'] == 'a':
754                BNSsym.update({'C_b':[0,.5,.0],'C_c':[0,0,.5]})
755            elif SGData['SGUniq'] == 'b':
756                BNSsym.update({'C_a':[.5,0,0],'C_c':[0,0,.5]})
757        elif SGData['SGSys'] == 'orthorhombic':
758            BNSsym = {'C_a':[.5,0,0],'C_b':[0,.5,0],'C_c':[0,0,.5],
759                'C_A':[0,.5,.5],'C_B':[.5,0,.5]}     
760        elif SGData['SGSys'] == 'triclinic':
761            BNSsym = {'C_a':[.5,0,0],'C_b':[0,.5,0],'C_c':[0,0,.5]}     
762           
763    elif 'I' in SGData['SGLatt']:
764        if SGData['SGSys'] in ['monoclinic','orthorhombic','triclinic']:
765            BNSsym = {'I_a':[.5,0,0],'I_b':[0,.5,0],'I_c':[0,0,.5]}
766        elif SGData['SGSys'] == 'tetragonal':
767            BNSsym = {'I_c':[0,0,.5]}
768        elif SGData['SGSys'] == 'cubic':
769            BNSsym = {} 
770           
771    elif 'F' in SGData['SGLatt']:
772        if SGData['SGSys'] in ['monoclinic','orthorhombic','cubic','triclinic']:
773            BNSsym = {'F_s':[.5,.5,.5]}
774           
775    elif 'R' in SGData['SGLatt']:
776        BNSsym = {'R_I':[0,0,.5]}
777    return UsymOp,OprFlg,BNSsym
778
779def ApplyBNSlatt(SGData,BNSlatt):
780    Tmat = np.eye(3)
781    BNS = BNSlatt[0]
782    A = np.array(BNSlatt[1])
783    SGCen = SGData['SGCen']
784    if '_a' in BNS:
785        Tmat[0,0] = 2.0
786    elif '_b' in BNS:
787        Tmat[1,1] = 2.0
788    elif '_c' in BNS:
789        Tmat[2,2] = 2.0
790    elif '_A' in BNS:
791        Tmat[0,0] = 2.0
792    elif '_B' in BNS:
793        Tmat[1,1] = 2.0
794    elif '_C' in BNS:
795        Tmat[2,2] = 2.0
796    elif '_I' in BNS:
797        Tmat *= 2.0
798        SGData['SGSpin'][-1] = -1
799        if 'R' in BNS:
800            SGData['SGSpin'][-1] = -1
801    elif '_s' in BNS:
802        SGData['SGSpin'][-1] = -1
803        SGData['SGSpin'] += [-1,-1,-1,]
804        Tmat *= 2.0
805    else:
806        return Tmat
807    SGData['SGSpin'].append(-1)
808    if 'P' not in BNS:
809        SGData['SGSpin'].append(-1)
810    C = SGCen+A
811    SGData['SGCen'] = np.vstack((SGCen,C))%1.
812    return Tmat
813       
814def CheckSpin(isym,SGData):
815    ''' Check for exceptions in spin rules
816    '''
817    if SGData['SGPtGrp'] in ['222','mm2','2mm','m2m']:      #only 2/3 can be red; not 1/3 or 3/3
818        if SGData['SGSpin'][1]*SGData['SGSpin'][2]*SGData['SGSpin'][3] < 0:
819            SGData['SGSpin'][(isym+1)%3+1] *= -1
820        if SGData['SpGrp'][0] == 'F' and isym > 2:
821            SGData['SGSpin'][(isym+1)%3+3] == 1
822    elif SGData['SGPtGrp'] == 'mmm':
823        if SGData['SpGrp'][0] == 'F' and isym > 2:
824            SGData['SGSpin'][(isym+1)%3+3] == 1
825
826def MagSGSym(SGData):       #needs to use SGPtGrp not SGLaue!
827    SGLaue = SGData['SGLaue']
828    if '1' not in SGData['GenSym']:        #patch for old gpx files
829        SGData['GenSym'] = ['1',]+SGData['GenSym']
830        SGData['SGSpin'] = [1,]+list(SGData['SGSpin'])
831    if len(SGData['SGSpin'])<len(SGData['GenSym']):
832        SGData['SGSpin'] = [1,]+list(SGData['SGSpin'])      #end patch
833    GenSym = SGData['GenSym'][1:]       #skip identity
834    SpnFlp = SGData['SGSpin']
835#    print('SpnFlp',SpnFlp)
836    SGPtGrp = SGData['SGPtGrp']
837    if len(SpnFlp) == 1:
838        SGData['MagPtGp'] = SGPtGrp
839        return SGData['SpGrp']
840    magSym = SGData['SpGrp'].split()
841    if SGLaue in ['-1',]:
842        SGData['MagPtGp'] = SGPtGrp
843        if SpnFlp[1] == -1:
844            magSym[1] += "'"
845            SGData['MagPtGp'] += "'"
846    elif SGLaue in ['2/m','4/m','6/m']: #all ok
847        Uniq = {'a':1,'b':2,'c':3,'':1}
848        id = [0,1]
849        if len(magSym) > 2:
850            id = [0,Uniq[SGData['SGUniq']]]
851        sym = magSym[id[1]].split('/')
852        Ptsym = SGLaue.split('/')
853        if len(GenSym) == 3:
854            for i in [0,1,2]:
855                if SpnFlp[i+1] < 0:
856                    sym[i] += "'"
857                    Ptsym[i] += "'"
858        else:
859            for i in range(len(GenSym)):
860                if SpnFlp[i+1] < 0:                     
861                    sym[i] += "'"
862                    Ptsym[i] += "'"
863        SGData['MagPtGp'] = '/'.join(Ptsym)
864        magSym[id[1]] = '/'.join(sym)
865    elif SGPtGrp in ['mmm','mm2','m2m','2mm','222']:
866        SGData['MagPtGp'] = ''
867        for i in [0,1,2]:
868            SGData['MagPtGp'] += SGPtGrp[i]
869            if SpnFlp[i+1] < 0:
870                magSym[i+1] += "'"
871                SGData['MagPtGp'] += "'"
872    elif SGLaue == '6/mmm': #ok
873        magPtGp = list(SGPtGrp)
874        if len(GenSym) == 2:
875            for i in [0,1]:
876                if SpnFlp[i+1] < 0:
877                    magSym[i+2] += "'"
878                    magPtGp[i+1] += "'"
879            if SpnFlp[1]*SpnFlp[2] < 0:
880                magSym[1] += "'"
881                magPtGp[0] += "'"
882        else:
883            sym = magSym[1].split('/')
884            Ptsym = ['6','m']
885            magPtGp = ['','m','m']
886            for i in [0,1,2]:
887                if SpnFlp[i+1] < 0:
888                    if i:
889                        magSym[i+1] += "'"
890                        magPtGp[i] += "'"
891                    else:
892                        sym[1] += "'"
893                        Ptsym[0] += "'"
894            if SpnFlp[2]*SpnFlp[3] < 0:
895                sym[0] += "'"                   
896                Ptsym[0] += "'"                   
897            magSym[1] = '/'.join(sym)
898            magPtGp[0] = '/'.join(Ptsym)
899        SGData['MagPtGp'] = ''.join(magPtGp)
900    elif SGLaue == '4/mmm':
901        magPtGp = list(SGPtGrp)
902        if len(GenSym) == 2:
903            for i in [0,1]:
904                if SpnFlp[i+1] < 0:
905                    magSym[i+2] += "'"
906                    magPtGp[i+1] += "'"
907            if SpnFlp[1]*SpnFlp[2] < 0:
908                magSym[1] += "'"
909                magPtGp[0] += "'"
910        else:
911            if '/' in magSym[1]:    #P 4/m m m, etc.
912                sym = magSym[1].split('/')
913                Ptsym = ['4','m']
914                magPtGp = ['','m','m']
915                for i in [0,1,2]:
916                    if SpnFlp[i+1] < 0:
917                        if i:
918                            magSym[i+1] += "'"
919                            magPtGp[i] += "'"
920                        else:
921                            sym[1] += "'"
922                            Ptsym[1] += "'"
923                if SpnFlp[2]*SpnFlp[3] < 0:
924                    sym[0] += "'"                   
925                    Ptsym[0] += "'"                   
926                magSym[1] = '/'.join(sym)
927                magPtGp[0] = '/'.join(Ptsym)
928            else:
929                for i in [0,1]:
930                    if SpnFlp[i+1] < 0:
931                        magSym[i+2] += "'"
932                if SpnFlp[1]*SpnFlp[2] < 0:
933                    magSym[1] += "'"
934        SGData['MagPtGp'] = ''.join(magPtGp)
935    elif SGLaue in ['3','3m1','31m']:   #ok
936        Ptsym = list(SGLaue)
937        if len(GenSym) == 1:    #all ok
938            id = 2
939            if (len(magSym) == 4) and (magSym[2] == '1'):
940                id = 3
941            if '3' in GenSym[0]:
942                id = 1
943            magSym[id].strip("'")
944            if SpnFlp[1] < 0:
945                magSym[id] += "'"
946                Ptsym[id-1] += "'"
947        elif len(GenSym) == 2:
948            if 'R' in GenSym[1]:
949                magSym[-1].strip("'")
950                if SpnFlp[1] < 0:
951                    magSym[-1] += "'"
952                    Ptsym[-1] += "'"
953            else:
954                i,j = [1,2]
955                if magSym[2] == '1':
956                    i,j = [1,3]
957                magSym[i].strip("'")
958                Ptsym[i-1].strip("'")
959                magSym[j].strip("'")
960                Ptsym[j-1].strip("'")
961                if SpnFlp[1:3] == [1,-1]:
962                    magSym[i] += "'"
963                    Ptsym[i-1] += "'"
964                elif SpnFlp[1:3] == [-1,-1]:
965                    magSym[j] += "'"
966                    Ptsym[j-1] += "'"
967                elif SpnFlp[1:3] == [-1,1]:
968                    magSym[i] += "'"
969                    Ptsym[i-1] += "'"
970                    magSym[j] += "'"
971                    Ptsym[j-1] += "'"
972        elif len(GenSym):
973            if 'c' not in magSym[2]:
974                i,j = [1,2]
975                magSym[i].strip("'")
976                Ptsym[i-1].strip("'")
977                magSym[j].strip("'")
978                Ptsym[j-1].strip("'")
979                if SpnFlp[1:3] == [1,-1]:
980                    magSym[i] += "'"
981                    Ptsym[i-1] += "'"
982                elif SpnFlp[1:3] == [-1,-1]:
983                    magSym[j] += "'"
984                    Ptsym[j-1] += "'"
985                elif SpnFlp[2] == [-1,1]:
986                    magSym[i] += "'"
987                    Ptsym[i-1] += "'"
988                    magSym[j] += "'"
989                    Ptsym[j-1] += "'"
990        SGData['MagPtGp'] = ''.join(Ptsym)
991    elif SGData['SGPtGrp'] == '23' and len(magSym):
992        SGData['MagPtGp'] = '23'
993    elif SGData['SGPtGrp'] == 'm3':
994        SGData['MagPtGp'] = "m3"
995        if SpnFlp[1] < 0:
996            magSym[1] += "'"
997            magSym[2] += "'"
998            SGData['MagPtGp'] = "m'3'"
999        if SpnFlp[2] < 0:
1000            if not 'm' in magSym[1]:    #only Ia3
1001                magSym[1].strip("'")
1002                SGData['MagPtGp'] = "m3'"
1003    elif SGData['SGPtGrp'] in ['432','-43m']:
1004        Ptsym = SGData['SGPtGrp'].split('3')
1005        if SpnFlp[1] < 0:
1006            magSym[1] += "'"
1007            Ptsym[0] += "'"
1008            magSym[3] += "'"
1009            Ptsym[1] += "'"
1010        SGData['MagPtGp'] = '3'.join(Ptsym)
1011    elif SGData['SGPtGrp'] == 'm3m':
1012        Ptsym = ['m','3','m']
1013        if SpnFlp[1:3] == [-1,1]:
1014            magSym[1] += "'"
1015            Ptsym[0] += "'"
1016            magSym[2] += "'"
1017            Ptsym[1] += "'"
1018        elif SpnFlp[1:3] == [1,-1]:
1019            magSym[3] += "'"
1020            Ptsym[2] += "'"
1021        elif SpnFlp[1:3] == [-1,-1]:
1022            magSym[1] += "'"
1023            Ptsym[0] += "'"
1024            magSym[2] += "'"
1025            Ptsym[1] += "'"
1026            magSym[3] += "'"
1027            Ptsym[2] += "'"
1028        SGData['MagPtGp'] = ''.join(Ptsym)
1029#    print SpnFlp
1030    magSym[0] = SGData.get('BNSlattsym',[SGData['SGLatt'],[0,0,0]])[0]
1031    return ' '.join(magSym)
1032
1033def MagText2MTS(mcifOpr,CIF=True):
1034    "From magnetic space group cif text returns matrix/translation + spin flip"
1035    XYZ = {'x':[1,0,0],'+x':[1,0,0],'-x':[-1,0,0],'y':[0,1,0],'+y':[0,1,0],'-y':[0,-1,0],
1036           'z':[0,0,1],'+z':[0,0,1],'-z':[0,0,-1],'x-y':[1,-1,0],'-x+y':[-1,1,0],'y-x':[-1,1,0],
1037           '+x-y':[1,-1,0],'+y-x':[-1,1,0]}
1038    ops = mcifOpr.split(",")
1039    M = []
1040    T = []
1041    for op in ops[:3]:
1042        ip = len(op)
1043        if '/' in op:
1044            if CIF:
1045                nP = op.count('+')
1046                opMT = op.split('+')
1047                T.append(eval(opMT[nP]))
1048                if nP == 2:
1049                    opMT[0] = '+'.join(opMT[0:2])
1050            else:
1051                ip = op.index('/')
1052                T.append(eval(op[:ip+2]))
1053                opMT = [op[ip+2:],'']
1054        else:
1055            opMT = [op,'']
1056            T.append(0.)
1057        M.append(XYZ[opMT[0].lower()])
1058    spnflp = 1
1059    if '-1' in ops[3]:
1060        spnflp = -1
1061    return np.array(M),np.array(T),spnflp
1062           
1063def MagSSText2MTS(mcifOpr):
1064    "From magnetic super space group cif text returns matrix/translation + spin flip"
1065    XYZ = {'x1':[1,0,0,0],'-x1':[-1,0,0,0],
1066           'x2':[0,1,0,0],'-x2':[0,-1,0,0],
1067           'x3':[0,0,1,0],'-x3':[0,0,-1,0],
1068           'x4':[0,0,0,1],'-x4':[0,0,0,-1],
1069           'x1-x2':[1,-1,0,0],'-x1+x2':[-1,1,0,0],
1070           'x1-x4':[1,0,0,-1],'-x1+x4':[-1,0,0,1],
1071           'x2-x4':[0,1,0,-1],'-x2+x4':[0,-1,0,1],
1072           '-x1-x2+x4':[-1,-1,0,1],'x1+x2-x4':[1,1,0,-1]}
1073    ops = mcifOpr.split(",")
1074    M = []
1075    T = []
1076    for op in ops[:4]:
1077        ip = len(op)
1078        if '/' in op:
1079            ip = op.index('/')-2
1080            T.append(eval(op[ip:]))
1081        else:
1082            T.append(0.)
1083        M.append(XYZ[op[:ip]])
1084    spnflp = 1
1085    if '-1' in ops[4]:
1086        spnflp = -1
1087    return np.array(M),np.array(T),spnflp
1088           
1089def GenMagOps(SGData):
1090    FlpSpn = SGData['SGSpin']
1091    Nsym = len(SGData['SGOps'])
1092    Ncv = len(SGData['SGCen'])
1093    sgOp = [M for M,T in SGData['SGOps']]
1094    oprName = [GetOprPtrName(str(irtx)) for irtx in PackRot(SGData['SGOps'])]
1095    if SGData['SGInv'] and not SGData['SGFixed'] and not SGData['SGGray']:
1096        Nsym *= 2
1097        sgOp += [-M for M,T in SGData['SGOps']]
1098        oprName += [GetOprPtrName(str(-irtx)) for irtx in PackRot(SGData['SGOps'])]
1099    Nsyms = 0
1100    sgOps = []
1101    OprNames = []
1102    for incv in range(Ncv):
1103        Nsyms += Nsym
1104        sgOps += sgOp
1105        OprNames += oprName
1106    if SGData['SGFixed']:
1107        SpnFlp = SGData['SpnFlp']
1108    else:
1109        SpnFlp = np.ones(Nsym,dtype=np.int)
1110        GenFlg = SGData.get('GenFlg',[0])
1111        Ngen = len(SGData['SGGen'])
1112    #    print ('GenFlg:',SGData['GenFlg'])
1113    #    print ('GenSym:',SGData['GenSym'])
1114        Nfl = len(GenFlg)
1115        for ieqv in range(Nsym):
1116            for iunq in range(Nfl):
1117                if SGData['SGGen'][ieqv%Ngen] & GenFlg[iunq]:
1118                    SpnFlp[ieqv] *= FlpSpn[iunq]
1119    #        print ('\nMagSpGrp:',SGData['MagSpGrp'],Ncv)
1120    #        print ('FlpSpn:',Nfl,FlpSpn)
1121        for incv in range(Ncv):
1122            if incv:
1123                try:
1124                    SpnFlp = np.concatenate((SpnFlp,SpnFlp[:Nsym]*FlpSpn[Nfl+incv-1]))
1125                except IndexError:
1126                    FlpSpn = [1,]+FlpSpn
1127                    SpnFlp = np.concatenate((SpnFlp,SpnFlp[:Nsym]*FlpSpn[Nfl+incv-1]))                   
1128    detM = [nl.det(M) for M in sgOp]
1129    MagMom = SpnFlp*np.array(Ncv*detM)      #duplicate for no. centerings
1130    SGData['MagMom'] = MagMom
1131#        print ('SgOps:',OprNames)
1132#        print ('SGGen:',SGData['SGGen'])
1133#        print ('SpnFlp:',SpnFlp)
1134#        print ('MagMom:',MagMom)
1135    return OprNames,SpnFlp
1136   
1137def GetOpNum(Opr,SGData):
1138    Nops = len(SGData['SGOps'])
1139    opNum = abs(Opr)%100
1140    cent = abs(Opr)//100
1141    if Opr < 0 and not SGData['SGFixed']:
1142        opNum += Nops
1143    if SGData['SGInv'] and not SGData['SGFixed']:
1144        Nops *= 2
1145    opNum += cent*Nops
1146    return opNum
1147       
1148################################################################################
1149#### Superspace group codes
1150################################################################################
1151       
1152def SSpcGroup(SGData,SSymbol):
1153    """
1154    Determines supersymmetry information from superspace group name; currently only for (3+1) superlattices
1155
1156    :param SGData: space group data structure as defined in SpcGroup above (see :ref:`SGData<SGData_table>`).
1157    :param SSymbol: superspace group symbol extension (string) defining modulation direction & generator info.
1158    :returns: (SSGError,SSGData)
1159   
1160       * SGError = 0 for no errors; >0 for errors (see SGErrors below for details)
1161       * SSGData - is a dict (see :ref:`Superspace Group object<SSGData_table>`) with entries:
1162       
1163             * 'SSpGrp': full superspace group symbol, accidental spaces removed; for display only
1164             * 'SSGCen': 4D cell centering vectors [0,0,0,0] at least
1165             * 'SSGOps': 4D symmetry operations as [M,T] so that M*x+T = x'
1166
1167    """
1168           
1169    def fixMonoOrtho():
1170        mod = ''.join(modsym).replace('1/2','0').replace('1','0')
1171        if SGData['SGPtGrp'] in ['2','m']:  #OK
1172            if mod in ['a00','0b0','00g']:
1173                result = [i*-1 for i in SGData['SSGKl']]
1174            else:
1175                result = SGData['SSGKl'][:]
1176            if '/' in mod:
1177                return [i*-1 for i in result]
1178            else:
1179                return result
1180        elif SGData['SGPtGrp'] == '2/m':    #OK
1181            if mod in ['a00','0b0','00g']:
1182                result =  SGData['SSGKl'][:]
1183            else:
1184                result = [i*-1 for i in SGData['SSGKl']]
1185            if '/' in mod:
1186                return [i*-1 for i in result]
1187            else:
1188                return result
1189        else:   #orthorhombic
1190            return [-SSGKl[i] if mod[i] in ['a','b','g'] else SSGKl[i] for i in range(3)]
1191       
1192    def extendSSGOps(SSGOps):
1193        for OpA in SSGOps:
1194            OpAtxt = SSMT2text(OpA)
1195            if 't' not in OpAtxt:
1196                continue
1197            for OpB in SSGOps:
1198                OpBtxt = SSMT2text(OpB)
1199                if 't' not in OpBtxt:
1200                    continue
1201                OpC = list(SGProd(OpB,OpA))
1202                OpC[1] %= 1.
1203                OpCtxt = SSMT2text(OpC)
1204#                print OpAtxt.replace(' ','')+' * '+OpBtxt.replace(' ','')+' = '+OpCtxt.replace(' ','')
1205                for k,OpD in enumerate(SSGOps):
1206                    OpDtxt = SSMT2text(OpD)
1207                    OpDtxt2 = ''
1208                    if SGData['SGGray']:                       
1209                        OpDtxt2 = SSMT2text([OpD[0],OpD[1]+np.array([0.,0.,0.,.5])])
1210#                    print '    ('+OpCtxt.replace(' ','')+' = ? '+OpDtxt.replace(' ','')+')'
1211                    if OpCtxt == OpDtxt:
1212                        continue
1213                    elif OpCtxt == OpDtxt2:
1214                        continue
1215                    elif OpCtxt.split(',')[:3] == OpDtxt.split(',')[:3]:
1216                        if 't' not in OpDtxt:
1217                            SSGOps[k] = OpC
1218#                            print k,'   new:',OpCtxt.replace(' ','')
1219                            break
1220                        else:
1221                            OpCtxt = OpCtxt.replace(' ','')
1222                            OpDtxt = OpDtxt.replace(' ','')
1223                            Txt = OpCtxt+' conflicts with '+OpDtxt
1224#                            print (Txt)
1225                            return False,Txt
1226        return True,SSGOps
1227       
1228    def findMod(modSym):
1229        for a in ['a','b','g']:
1230            if a in modSym:
1231                return a
1232               
1233    def genSSGOps():
1234        SSGOps = SSGData['SSGOps'][:]
1235        iFrac = {}
1236        for i,frac in enumerate(SSGData['modSymb']):
1237            if frac in ['1/2','1/3','1/4','1/6','1']:
1238                iFrac[i] = frac+'.'
1239#        print SGData['SpGrp']+SSymbol
1240#        print 'SSGKl',SSGKl,'genQ',genQ,'iFrac',iFrac,'modSymb',SSGData['modSymb']
1241# set identity & 1,-1; triclinic
1242        SSGOps[0][0][3,3] = 1.
1243## expand if centrosymmetric
1244#        if SGData['SGInv']:
1245#            SSGOps += [[-1*M,V] for M,V in SSGOps[:]]
1246# monoclinic - all done & all checked
1247        if SGData['SGPtGrp'] in ['2','m']:  #OK
1248            SSGOps[1][0][3,3] = SSGKl[0]
1249            SSGOps[1][1][3] = genQ[0]
1250            for i in iFrac:
1251                SSGOps[1][0][3,i] = -SSGKl[0]
1252        elif SGData['SGPtGrp'] == '2/m':    #OK
1253            SSGOps[1][0][3,3] = SSGKl[1]
1254            if gensym:
1255                SSGOps[1][1][3] = 0.5
1256            for i in iFrac:
1257                SSGOps[1][0][3,i] = SSGKl[0]
1258           
1259# orthorhombic - all OK not fully checked
1260        elif SGData['SGPtGrp'] in ['222','mm2','m2m','2mm']:    #OK
1261            if SGData['SGPtGrp'] == '222':
1262                OrOps = {'g':{0:[1,3],1:[2,3]},'a':{1:[1,2],2:[1,3]},'b':{2:[3,2],0:[1,2]}} #OK
1263            elif SGData['SGPtGrp'] == 'mm2':
1264                OrOps = {'g':{0:[1,3],1:[2,3]},'a':{1:[2,1],2:[3,1]},'b':{0:[1,2],2:[3,2]}} #OK
1265            elif SGData['SGPtGrp'] == 'm2m':
1266                OrOps = {'b':{0:[1,2],2:[3,2]},'g':{0:[1,3],1:[2,3]},'a':{1:[2,1],2:[3,1]}} #OK
1267            elif SGData['SGPtGrp'] == '2mm':
1268                OrOps = {'a':{1:[2,1],2:[3,1]},'b':{0:[1,2],2:[3,2]},'g':{0:[1,3],1:[2,3]}} #OK
1269            a = findMod(SSGData['modSymb'])
1270            OrFrac = OrOps[a]
1271            for j in iFrac:
1272                for i in OrFrac[j]:
1273                    SSGOps[i][0][3,j] = -2.*eval(iFrac[j])*SSGKl[i-1]
1274            for i in [0,1,2]:
1275                SSGOps[i+1][0][3,3] = SSGKl[i]
1276                SSGOps[i+1][1][3] = genQ[i]
1277                E,SSGOps = extendSSGOps(SSGOps)
1278                if not E:
1279                    return E,SSGOps
1280        elif SGData['SGPtGrp'] == 'mmm':    #OK
1281            OrOps = {'g':{0:[1,3],1:[2,3]},'a':{1:[2,1],2:[3,1]},'b':{0:[1,2],2:[3,2]}} 
1282            a = findMod(SSGData['modSymb'])
1283            if a == 'g':
1284                SSkl = [1,1,1]
1285            elif a == 'a':
1286                SSkl = [-1,1,-1]
1287            else:
1288                SSkl = [1,-1,-1]
1289            OrFrac = OrOps[a]
1290            for j in iFrac:
1291                for i in OrFrac[j]:
1292                    SSGOps[i][0][3,j] = -2.*eval(iFrac[j])*SSkl[i-1]
1293            for i in [0,1,2]:
1294                SSGOps[i+1][0][3,3] = SSkl[i]
1295                SSGOps[i+1][1][3] = genQ[i]
1296                E,SSGOps = extendSSGOps(SSGOps)
1297                if not E:
1298                    return E,SSGOps               
1299# tetragonal - all done & checked
1300        elif SGData['SGPtGrp'] == '4':  #OK
1301            SSGOps[1][0][3,3] = SSGKl[0]
1302            SSGOps[1][1][3] = genQ[0]
1303            if '1/2' in SSGData['modSymb']:
1304                SSGOps[1][0][3,1] = -1
1305        elif SGData['SGPtGrp'] == '-4': #OK
1306            SSGOps[1][0][3,3] = SSGKl[0]
1307            if '1/2' in SSGData['modSymb']:
1308                SSGOps[1][0][3,1] = 1
1309        elif SGData['SGPtGrp'] in ['4/m',]: #OK
1310            if '1/2' in SSGData['modSymb']:
1311                SSGOps[1][0][3,1] = -SSGKl[0]
1312            for i,j in enumerate([1,3]):
1313                SSGOps[j][0][3,3] = 1
1314                if genQ[i]:
1315                    SSGOps[j][1][3] = genQ[i]
1316                E,SSGOps = extendSSGOps(SSGOps)
1317                if not E:
1318                    return E,SSGOps
1319        elif SGData['SGPtGrp'] in ['422','4mm','-42m','-4m2',]: #OK
1320            iGens = [1,4,5]
1321            if SGData['SGPtGrp'] in ['4mm','-4m2',]:
1322                iGens = [1,6,7]
1323            for i,j in enumerate(iGens):
1324                if '1/2' in SSGData['modSymb'] and i < 2:
1325                    SSGOps[j][0][3,1] = SSGKl[i]
1326                SSGOps[j][0][3,3] = SSGKl[i]
1327                if genQ[i]:
1328                    if 's' in gensym and j == 6:
1329                        SSGOps[j][1][3] = -genQ[i]
1330                    else:
1331                        SSGOps[j][1][3] = genQ[i]
1332                E,SSGOps = extendSSGOps(SSGOps)
1333                if not E:
1334                    return E,SSGOps
1335        elif SGData['SGPtGrp'] in ['4/mmm',]:#OK
1336            if '1/2' in SSGData['modSymb']:
1337                SSGOps[1][0][3,1] = -SSGKl[0]
1338                SSGOps[6][0][3,1] = SSGKl[1]
1339                if modsym:
1340                   SSGOps[1][1][3]  = -genQ[3]
1341            for i,j in enumerate([1,2,6,7]):
1342                SSGOps[j][0][3,3] = 1
1343                SSGOps[j][1][3] = genQ[i]
1344                E,Result = extendSSGOps(SSGOps)
1345                if not E:
1346                    return E,Result
1347                else:
1348                    SSGOps = Result
1349               
1350# trigonal - all done & checked
1351        elif SGData['SGPtGrp'] == '3':  #OK
1352            SSGOps[1][0][3,3] = SSGKl[0]
1353            if '1/3' in SSGData['modSymb']:
1354                SSGOps[1][0][3,1] = -1
1355            SSGOps[1][1][3] = genQ[0]
1356        elif SGData['SGPtGrp'] == '-3': #OK
1357            SSGOps[1][0][3,3] = -SSGKl[0]
1358            if '1/3' in SSGData['modSymb']:
1359                SSGOps[1][0][3,1] = -1
1360            SSGOps[1][1][3] = genQ[0]
1361        elif SGData['SGPtGrp'] in ['312','3m','-3m','-3m1','3m1']:   #OK
1362            if '1/3' in SSGData['modSymb']:
1363                SSGOps[1][0][3,1] = -1
1364            for i,j in enumerate([1,5]):
1365                if SGData['SGPtGrp'] in ['3m','-3m']:
1366                    SSGOps[j][0][3,3] = 1
1367                else:                   
1368                    SSGOps[j][0][3,3] = SSGKl[i+1]
1369                if genQ[i]:
1370                    SSGOps[j][1][3] = genQ[i]
1371        elif SGData['SGPtGrp'] in ['321','32']:   #OK
1372            for i,j in enumerate([1,4]):
1373                SSGOps[j][0][3,3] = SSGKl[i]
1374                if genQ[i]:
1375                    SSGOps[j][1][3] = genQ[i]
1376        elif SGData['SGPtGrp'] in ['31m','-31m']:   #OK
1377            ids = [1,3]
1378            if SGData['SGPtGrp'] == '-31m':
1379                ids = [1,3]
1380            if '1/3' in SSGData['modSymb']:
1381                SSGOps[ids[0]][0][3,1] = -SSGKl[0]
1382            for i,j in enumerate(ids):
1383                SSGOps[j][0][3,3] = 1
1384                if genQ[i+1]:
1385                    SSGOps[j][1][3] = genQ[i+1]
1386                     
1387# hexagonal all done & checked
1388        elif SGData['SGPtGrp'] == '6':  #OK
1389            SSGOps[1][0][3,3] = SSGKl[0]
1390            SSGOps[1][1][3] = genQ[0]
1391        elif SGData['SGPtGrp'] == '-6': #OK
1392            SSGOps[1][0][3,3] = SSGKl[0]
1393        elif SGData['SGPtGrp'] in ['6/m',]: #OK
1394            SSGOps[1][0][3,3] = -SSGKl[1]
1395            SSGOps[1][1][3] = genQ[0]
1396            SSGOps[2][1][3] = genQ[1]
1397        elif SGData['SGPtGrp'] in ['622',]: #OK
1398            for i,j in enumerate([1,8,9]):
1399                SSGOps[j][0][3,3] = SSGKl[i]
1400                if genQ[i]:
1401                    SSGOps[j][1][3] = genQ[i]
1402                E,SSGOps = extendSSGOps(SSGOps)
1403           
1404        elif SGData['SGPtGrp'] in ['6mm','-62m','-6m2',]: #OK
1405            for i,j in enumerate([1,6,7]):
1406                SSGOps[j][0][3,3] = SSGKl[i]
1407                if genQ[i]:
1408                    SSGOps[j][1][3] = genQ[i]
1409                E,SSGOps = extendSSGOps(SSGOps)
1410        elif SGData['SGPtGrp'] in ['6/mmm',]: # OK
1411            for i,j in enumerate([1,2,10,11]):
1412                SSGOps[j][0][3,3] = 1
1413                if genQ[i]:
1414                    SSGOps[j][1][3] = genQ[i]
1415                E,SSGOps = extendSSGOps(SSGOps)
1416        elif SGData['SGPtGrp'] in ['1','-1']: #triclinic - done
1417            return True,SSGOps
1418        E,SSGOps = extendSSGOps(SSGOps)
1419        return E,SSGOps
1420       
1421    def specialGen(gensym,modsym):
1422        sym = ''.join(gensym)
1423        if SGData['SGPtGrp'] in ['2/m',] and 'n' in SGData['SpGrp']:
1424            if 's' in sym:
1425                gensym = 'ss'
1426        if SGData['SGPtGrp'] in ['-62m',] and sym == '00s':
1427            gensym = '0ss'
1428        elif SGData['SGPtGrp'] in ['222',]:
1429            if sym == '00s':
1430                gensym = '0ss'
1431            elif sym == '0s0':
1432                gensym = 'ss0'
1433            elif sym == 's00':
1434                gensym = 's0s'
1435        elif SGData['SGPtGrp'] in ['mmm',]:
1436            if 'g' in modsym:
1437                if sym == 's00':
1438                    gensym = 's0s'
1439                elif sym == '0s0':
1440                    gensym = '0ss'
1441            elif 'a' in modsym:
1442                if sym == '0s0':
1443                    gensym = 'ss0'
1444                elif sym == '00s':
1445                    gensym = 's0s'
1446            elif 'b' in modsym:
1447                if sym == '00s':
1448                    gensym = '0ss'
1449                elif sym == 's00':
1450                    gensym = 'ss0'
1451        return gensym
1452                           
1453    Fracs = {'1/2':0.5,'1/3':1./3,'1':1.0,'0':0.,'s':.5,'t':1./3,'q':.25,'h':1./6,'a':0.,'b':0.,'g':0.}
1454    if SGData['SGLaue'] in ['m3','m3m']:
1455        return '(3+1) superlattices not defined for cubic space groups',None
1456    elif SGData['SGLaue'] in ['3R','3mR']:
1457        return '(3+1) superlattices not defined for rhombohedral settings - use hexagonal setting',None
1458    if SGData['SGGray'] and SSymbol[-1] == 's':
1459        SSymbol = SSymbol[:-1]
1460    try:
1461        modsym,gensym = splitSSsym(SSymbol)
1462    except ValueError:
1463        return 'Error in superspace symbol '+SSymbol,None
1464    modQ = [Fracs[mod] for mod in modsym]
1465    SSGKl = SGData['SSGKl'][:]
1466    if SGData['SGLaue'] in ['2/m','mmm']:
1467        SSGKl = fixMonoOrtho()
1468    Ngen = len(gensym)
1469    if len(gensym) and Ngen != len(SSGKl):
1470        return 'Wrong number of items in generator symbol '+''.join(gensym),None
1471    gensym = specialGen(gensym[:Ngen],modsym)
1472    genQ = [Fracs[mod] for mod in gensym[:Ngen]]
1473    if not genQ:
1474        genQ = [0,0,0,0]
1475    SSgSpc = SGData['SpGrp']+SSymbol
1476    if SGData['SGGray']:
1477        SSgSpc = SSgSpc.replace('('," 1'(")
1478        SSgSpc += 's'
1479    SSGData = {'SSpGrp':SSgSpc,'modQ':modQ,'modSymb':modsym,'SSGKl':SSGKl}
1480    SSCen = np.zeros((len(SGData['SGCen']),4))
1481    for icen,cen in enumerate(SGData['SGCen']):
1482        SSCen[icen,0:3] = cen
1483    SSCen[0] = np.zeros(4)
1484    SSGData['SSGCen'] = SSCen
1485    SSGData['SSGOps'] = []
1486    for iop,op in enumerate(SGData['SGOps']):
1487        T = np.zeros(4)
1488        ssop = np.zeros((4,4))
1489        ssop[:3,:3] = op[0]
1490        T[:3] = op[1]
1491        SSGData['SSGOps'].append([ssop,T])
1492    E,Result = genSSGOps()
1493    if E:
1494        SSGData['SSGOps'] = Result
1495        if DEBUG:
1496            print ('Super spacegroup operators for '+SSGData['SSpGrp'])
1497            for Op in Result:
1498                print (SSMT2text(Op).replace(' ',''))
1499            if SGData['SGInv']:                                 
1500                for Op in Result:
1501                    Op = [-Op[0],-Op[1]%1.]
1502                    print (SSMT2text(Op).replace(' ',''))                                 
1503        return None,SSGData
1504    else:
1505        return Result+'\nOperator conflict - incorrect superspace symbol',None
1506   
1507def SSChoice(SGData):
1508    '''
1509    Gets the unique set of possible super space groups for a given space group
1510    '''
1511    laueSS = {'-1':['(abg)',],
1512            '2/m':['(a0g)','(a1/2g)','(0b0)','(1/2b0)','(0b1/2)','(1/2b1/2)'],
1513            'mmm':['(00g)','(1/20g)','(01/2g)','(1/21/2g)','(10g)','(01g)',
1514                   '(a00)','(a1/20)','(a01/2)','(a1/21/2)','(a10)','(a01)',
1515                   '(0b0)','(1/2b0)','(0b1/2)','(1/2b1/2)','(1b0)','(0b1)',],
1516            '4/m':['(00g)','(1/21/2g)'],
1517            '4/mmm':['(00g)','(1/21/2g)'],
1518            '3':['(00g)','(1/31/3g)'],
1519            '3m1':['(00g)'],
1520            '31m':['(00g)','(1/31/3g)'],
1521            '6/m':['(00g)',],
1522            '6/mmm':['(00g)',]}
1523           
1524    laueTS = {'-1':['',],
1525            '2/m':['','s','0s','ss','s0'],
1526            'mmm':['','s00','0s0','00s','ss0','s0s','0ss','q00','0q0','00q','0qq','q0q','qq0'],
1527            '4/m':['','q','s','s0',],
1528            '4/mmm':['','q00','s00','s0s','ss0','0ss','qq0','qqs','0q0','s0s0','00ss','s00s','ss00','0ss0','0s0s'],
1529            '3':['','t'],
1530            '3m1':['','t0','0s','t00','0s0'],
1531            '31m':['','t00','0ss'],
1532            '6/m':['','h','t','s','s0'],
1533            '6/mmm':['','h00','t00','s00','ss0','0ss','s0s','s0s0','00ss','s00s','ss00','0ss0','0s0s']}
1534    laue = SGData['SGLaue']
1535    SSChoice = []
1536    for ax in laueSS[laue]:
1537        for sx in laueTS[laue]:
1538            SSChoice.append(ax+sx)               
1539    ssChoice = []
1540    ssHash = []
1541    for item in SSChoice:
1542        E,SSG = SSpcGroup(SGData,item)
1543        if SSG:
1544            sshash = hash(str(SSGPrint(SGData,SSG)[1]))
1545            if sshash not in ssHash:
1546                ssHash.append(sshash)
1547                ssChoice.append(item)
1548    return ssChoice
1549
1550def fixGray(SGData,SSymbol):
1551    modsym,gensym = SSymbol.replace(' ','').split(')')
1552    modsym += ')'
1553    sgPtGp = SGData['SGPtGrp']
1554    if gensym:
1555        if sgPtGp in ['1','2','m','3','4','6'] and len(gensym) == 1:
1556            gensym += 's'
1557        elif sgPtGp in ['2/m','4/m','6/m'] and len(gensym) == 2:
1558            gensym += 's'
1559        elif sgPtGp in ['4/mmm','6/mmm'] and len(gensym) == 4:
1560            gensym += 's'
1561        elif len(gensym) == 3:
1562            gensym += 's'
1563    else:
1564        if sgPtGp in ['1','2','m','3','4','6']:
1565            gensym += '0s'
1566        elif sgPtGp in ['2/m','4/m','6/m']:
1567            gensym += '00s'
1568        elif sgPtGp in ['4/mmm','6/mmm']:
1569            gensym += '0000s'
1570        else:
1571            gensym += '000s'
1572    return modsym+gensym
1573           
1574def splitSSsym(SSymbol):
1575    '''
1576    Splits supersymmetry symbol into two lists of strings
1577    '''
1578    modsym,gensym = SSymbol.replace(' ','').split(')')
1579    modsym = modsym.replace(',','')
1580    if "1'" in modsym:
1581        gensym = gensym[:-1]
1582    modsym = modsym.replace("1'",'')
1583    if gensym in ['0','00','000','0000']:       #get rid of extraneous symbols
1584        gensym = ''
1585    nfrac = modsym.count('/')
1586    modsym = modsym.lstrip('(')
1587    if nfrac == 0:
1588        modsym = list(modsym)
1589    elif nfrac == 1:
1590        pos = modsym.find('/')
1591        if pos == 1:
1592            modsym = [modsym[:3],modsym[3],modsym[4]]
1593        elif pos == 2:
1594            modsym = [modsym[0],modsym[1:4],modsym[4]]
1595        else:
1596            modsym = [modsym[0],modsym[1],modsym[2:]]
1597    else:
1598        lpos = modsym.find('/')
1599        rpos = modsym.rfind('/')
1600        if lpos == 1 and rpos == 4:
1601            modsym = [modsym[:3],modsym[3:6],modsym[6]]
1602        elif lpos == 1 and rpos == 5:
1603            modsym = [modsym[:3],modsym[3],modsym[4:]]
1604        else:
1605            modsym = [modsym[0],modsym[1:4],modsym[4:]]
1606    gensym = list(gensym)
1607    return modsym,gensym
1608       
1609def SSGPrint(SGData,SSGData):
1610    '''
1611    Print the output of SSpcGroup in a nicely formatted way. Used in SSpaceGroup
1612
1613    :param SGData: space group data structure as defined in SpcGroup above.
1614    :param SSGData: from :func:`SSpcGroup`
1615    :returns:
1616        SSGText - list of strings with the superspace group details
1617        SGTable - list of strings for each of the operations
1618    '''
1619    Mult = len(SSGData['SSGCen'])*len(SSGData['SSGOps'])*(int(SGData['SGInv'])+1)
1620    SSGText = []
1621    SSGText.append(' Superspace Group: '+SSGData['SSpGrp'])
1622    CentStr = 'centrosymmetric'
1623    if not SGData['SGInv']:
1624        CentStr = 'non'+CentStr
1625    if SGData['SGLatt'] in 'ABCIFR':
1626        SSGText.append(' The lattice is '+CentStr+' '+SGData['SGLatt']+'-centered '+SGData['SGSys'].lower())
1627    else:
1628        SSGText.append(' The superlattice is '+CentStr+' '+'primitive '+SGData['SGSys'].lower())
1629    SSGText.append(' The Laue symmetry is '+SGData['SGLaue'])
1630    SGptGp = SGData['SGPtGrp']
1631    if SGData['SGGray']:
1632        SGptGp += "1'"
1633    SSGText.append(' The superlattice point group is '+SGptGp+', '+''.join([str(i) for i in SSGData['SSGKl']]))
1634    SSGText.append(' The number of superspace group generators is '+str(len(SGData['SSGKl'])))
1635    SSGText.append(' Multiplicity of a general site is '+str(Mult))
1636    if SGData['SGUniq'] in ['a','b','c']:
1637        SSGText.append(' The unique monoclinic axis is '+SGData['SGUniq'])
1638    if SGData['SGInv']:
1639        SSGText.append(' The inversion center is located at 0,0,0')
1640    if SGData['SGPolax']:
1641        SSGText.append(' The location of the origin is arbitrary in '+SGData['SGPolax'])
1642    SSGText.append(' ')
1643    if len(SSGData['SSGCen']) > 1:
1644        SSGText.append(' The equivalent positions are:')
1645        SSGText.append(' ('+SSLatt2text(SSGData['SSGCen'])+')+\n')
1646    else:
1647        SSGText.append(' The equivalent positions are:\n')
1648    SSGTable = []
1649    for i,Opr in enumerate(SSGData['SSGOps']):
1650        SSGTable.append('(%2d) %s'%(i+1,SSMT2text(Opr)))
1651    if SGData['SGGray']:
1652        SSGTable.append("     for 1'")
1653        for i,Opr in enumerate(SSGData['SSGOps']):
1654            Opr2 = [Opr[0],Opr[1]+np.array([0,0,0,.5])]
1655            SSGTable.append('(%2d) %s'%(i+1,SSMT2text(Opr2)))
1656    return SSGText,SSGTable
1657   
1658def SSGModCheck(Vec,modSymb,newMod=True):
1659    ''' Checks modulation vector compatibility with supersymmetry space group symbol.
1660    if newMod: Superspace group symbol takes precidence & the vector will be modified accordingly
1661    '''
1662    Fracs = {'1/2':0.5,'1/3':1./3,'1':1.0,'0':0.,'a':0.,'b':0.,'g':0.}
1663    modQ = [Fracs[mod] for mod in modSymb]
1664    if newMod:
1665        newVec = Vec
1666        if not np.any(Vec):
1667            newVec = [0.1 if (vec == 0.0 and mod in ['a','b','g']) else vec for [vec,mod] in zip(Vec,modSymb)]
1668        return [Q if mod not in ['a','b','g'] and vec != Q else vec for [vec,mod,Q] in zip(newVec,modSymb,modQ)],  \
1669            [True if mod in ['a','b','g'] else False for mod in modSymb]
1670    else:
1671        return Vec,[True if mod in ['a','b','g'] else False for mod in modSymb]
1672
1673def SSMT2text(Opr):
1674    "From superspace group matrix/translation operator returns text version"
1675    XYZS = ('x','y','z','t')    #Stokes, Campbell & van Smaalen notation
1676    TRA = ('   ','ERR','1/6','1/4','1/3','ERR','1/2','ERR','2/3','3/4','5/6','ERR')
1677    Fld = ''
1678    M,T = Opr
1679    for j in range(4):
1680        IJ = ''
1681        for k in range(4):
1682            txt = str(int(round(M[j][k])))
1683            txt = txt.replace('1',XYZS[k]).replace('0','')
1684            if '2' in txt:
1685                txt += XYZS[k]
1686            if IJ and M[j][k] > 0:
1687                IJ += '+'+txt
1688            else:
1689                IJ += txt
1690        IK = int(round(T[j]*12))%12
1691        if IK:
1692            if not IJ:
1693                break
1694            if IJ[0] == '-':
1695                Fld += (TRA[IK]+IJ).rjust(8)
1696            else:
1697                Fld += (TRA[IK]+'+'+IJ).rjust(8)
1698        else:
1699            Fld += IJ.rjust(8)
1700        if j != 3: Fld += ', '
1701    return Fld
1702   
1703def SSLatt2text(SSGCen):
1704    "Lattice centering vectors to text"
1705    lattTxt = ''
1706    lattDir = {4:'1/3',6:'1/2',8:'2/3',0:'0'}
1707    for vec in SSGCen:
1708        lattTxt += ' '
1709        for item in vec:
1710            lattTxt += '%s,'%(lattDir[int(item*12)])
1711        lattTxt = lattTxt.rstrip(',')
1712        lattTxt += ';'
1713    lattTxt = lattTxt.rstrip(';').lstrip(' ')
1714    return lattTxt
1715       
1716def SSpaceGroup(SGSymbol,SSymbol):
1717    '''
1718    Print the output of SSpcGroup in a nicely formatted way.
1719
1720    :param SGSymbol: space group symbol with spaces between axial fields.
1721    :param SSymbol: superspace group symbol extension (string).
1722    :returns: nothing
1723    '''
1724
1725    E,A = SpcGroup(SGSymbol)
1726    if E > 0:
1727        print (SGErrors(E))
1728        return
1729    E,B = SSpcGroup(A,SSymbol)   
1730    if E > 0:
1731        print (E)
1732        return
1733    for l in SSGPrint(B):
1734        print (l)
1735       
1736def SGProd(OpA,OpB):
1737    '''
1738    Form space group operator product. OpA & OpB are [M,V] pairs;
1739        both must be of same dimension (3 or 4). Returns [M,V] pair
1740    '''
1741    A,U = OpA
1742    B,V = OpB
1743    M = np.inner(B,A.T)
1744    W = np.inner(B,U)+V
1745    return M,W
1746       
1747def GetLittleGrpOps(SGData,vec):
1748    ''' Find rotation part of operators that leave vec unchanged
1749   
1750    :param SGData: space group data structure as defined in SpcGroup above.
1751    :param vec: a numpy array of fractional vector coordinates
1752    :returns: Little - list of operators [M,T] that form the little gropu
1753    '''
1754    Little = []
1755    Ops = SGData['SGOps'][:]
1756    if SGData['SGInv']:
1757        Ops += [[-M,-T] for [M,T] in Ops]
1758    for [M,T] in Ops:
1759        tvec = np.inner(M,vec)%1.
1760        if np.allclose(tvec,vec%1.):
1761            Little.append([M,T])
1762    return Little
1763       
1764def MoveToUnitCell(xyz):
1765    '''
1766    Translates a set of coordinates so that all values are >=0 and < 1
1767
1768    :param xyz: a list or numpy array of fractional coordinates
1769    :returns: XYZ - numpy array of new coordinates now 0 or greater and less than 1
1770    '''
1771    XYZ = (np.array(xyz)+10.)%1.
1772    cell = np.asarray(np.rint(XYZ-xyz),dtype=np.int32)
1773    return XYZ,cell
1774       
1775def Opposite(XYZ,toler=0.0002):
1776    '''
1777    Gives opposite corner, edge or face of unit cell for position within tolerance.
1778        Result may be just outside the cell within tolerance
1779
1780    :param XYZ: 0 >= np.array[x,y,z] > 1 as by MoveToUnitCell
1781    :param toler: unit cell fraction tolerance making opposite
1782    :returns:
1783        XYZ: dict of opposite positions; key=unit cell & always contains XYZ
1784    '''
1785    perm3 = [[1,1,1],[0,1,1],[1,0,1],[1,1,0],[1,0,0],[0,1,0],[0,0,1],[0,0,0]]
1786    TB = np.where(abs(XYZ-1)<toler,-1,0)+np.where(abs(XYZ)<toler,1,0)
1787    perm = TB*perm3
1788    cperm = ['%d,%d,%d'%(i,j,k) for i,j,k in perm]
1789    D = dict(zip(cperm,perm))
1790    new = {}
1791    for key in D:
1792        new[key] = np.array(D[key])+np.array(XYZ)
1793    return new
1794       
1795def GenAtom(XYZ,SGData,All=False,Uij=[],Move=True):
1796    '''
1797    Generates the equivalent positions for a specified coordinate and space group
1798
1799    :param XYZ: an array, tuple or list containing 3 elements: x, y & z
1800    :param SGData: from :func:`SpcGroup`
1801    :param All: True return all equivalent positions including duplicates;
1802      False return only unique positions
1803    :param Uij: [U11,U22,U33,U12,U13,U23] or [] if no Uij
1804    :param Move: True move generated atom positions to be inside cell
1805      False do not move atoms       
1806    :return: [[XYZEquiv],Idup,[UijEquiv],spnflp]
1807
1808      *  [XYZEquiv] is list of equivalent positions (XYZ is first entry)
1809      *  Idup = [-][C]SS where SS is the symmetry operator number (1-24), C (if not 0,0,0)
1810      * is centering operator number (1-4) and - is for inversion
1811        Cell = unit cell translations needed to put new positions inside cell
1812        [UijEquiv] - equivalent Uij; absent if no Uij given
1813      * +1/-1 for spin inversion of operator - empty if not magnetic
1814       
1815    '''
1816    XYZEquiv = []
1817    UijEquiv = []
1818    Idup = []
1819    Cell = []
1820    inv = int(SGData['SGInv']+1)
1821    icen = SGData['SGCen']
1822    if SGData['SGFixed']:
1823        inv = 1
1824    SpnFlp = SGData.get('SpnFlp',[])
1825    spnflp = []
1826    X = np.array(XYZ)
1827    mj = 0
1828    cell0 = np.zeros(3,dtype=np.int32)
1829    if Move:
1830        X,cell0 = MoveToUnitCell(X)
1831    for ic,cen in enumerate(icen):
1832        C = np.array(cen)
1833        for invers in range(inv):
1834            for io,[M,T] in enumerate(SGData['SGOps']):
1835                idup = ((io+1)+100*ic)*(1-2*invers)
1836                XT = np.inner(M,X)+T
1837                if len(Uij):
1838                    U = Uij2U(Uij)
1839                    U = np.inner(M,np.inner(U,M).T)
1840                    newUij = U2Uij(U)
1841                if invers:
1842                    XT = -XT
1843                XT += C
1844                cell = np.zeros(3,dtype=np.int32)+cell0
1845                cellj = np.zeros(3,dtype=np.int32)
1846                if Move:
1847                    newX,cellj = MoveToUnitCell(XT)
1848                else:
1849                    newX = XT
1850                cell += cellj
1851                if All:
1852                    if np.allclose(newX,X,atol=0.0002):
1853                        idup = False
1854                else:
1855                    if True in [np.allclose(newX,oldX,atol=0.0002) for oldX in XYZEquiv]:
1856                        idup = False
1857                if All or idup:
1858                    XYZEquiv.append(newX)
1859                    Idup.append(idup)
1860                    Cell.append(cell)
1861                    if len(Uij):
1862                        UijEquiv.append(newUij)
1863                    if len(SpnFlp):
1864                        spnflp.append(SpnFlp[mj])
1865                    else:
1866                        spnflp.append(1)
1867                mj += 1
1868    if len(Uij):
1869        return zip(XYZEquiv,UijEquiv,Idup,Cell,spnflp)
1870    else:
1871        return zip(XYZEquiv,Idup,Cell,spnflp)
1872       
1873def GenHKL(HKL,SGData):
1874    ''' Generates all equivlent reflections including Friedel pairs
1875    :param HKL:  [h,k,l] must be integral values
1876    :param SGData: space group data obtained from SpcGroup
1877    :returns: array Uniq: equivalent reflections
1878    '''
1879   
1880    Ops = SGData['SGOps']
1881    OpM = np.array([op[0] for op in Ops])
1882    Uniq = np.inner(OpM,HKL)
1883    Uniq = list(Uniq)+list(-1*Uniq)
1884    return np.array(Uniq)
1885
1886def GenHKLf(HKL,SGData):
1887    '''
1888    Uses old GSAS Fortran routine genhkl.for
1889
1890    :param HKL:  [h,k,l] must be integral values for genhkl.for to work
1891    :param SGData: space group data obtained from SpcGroup
1892    :returns: iabsnt,mulp,Uniq,phi
1893
1894     *   iabsnt = True if reflection is forbidden by symmetry
1895     *   mulp = reflection multiplicity including Friedel pairs
1896     *   Uniq = numpy array of equivalent hkl in descending order of h,k,l
1897     *   phi = phase offset for each equivalent h,k,l
1898
1899    '''
1900    hklf = list(HKL)+[0,]       #could be numpy array!
1901    Ops = SGData['SGOps']
1902    OpM = np.array([op[0] for op in Ops],order='F')
1903    OpT = np.array([op[1] for op in Ops])
1904    Cen = np.array([cen for cen in SGData['SGCen']],order='F')
1905   
1906    import pyspg
1907    Nuniq,Uniq,iabsnt,mulp = pyspg.genhklpy(hklf,len(Ops),OpM,OpT,SGData['SGInv'],len(Cen),Cen)
1908    h,k,l,f = Uniq
1909    Uniq=np.array(list(zip(h[:Nuniq],k[:Nuniq],l[:Nuniq])))
1910    phi = f[:Nuniq]
1911    return iabsnt,mulp,Uniq,phi
1912
1913#def MagHKLchk(HKL,SGData):
1914#    SpnFlp = SGData['SpnFlp']
1915#    print(HKL)
1916#    Uniq = GenHKL(HKL,SGData)
1917   
1918def checkSSLaue(HKL,SGData,SSGData):
1919    #Laue check here - Toss HKL if outside unique Laue part
1920    h,k,l,m = HKL
1921    if SGData['SGLaue'] == '2/m':
1922        if SGData['SGUniq'] == 'a':
1923            if 'a' in SSGData['modSymb'] and h == 0 and m < 0:
1924                return False
1925            elif 'b' in SSGData['modSymb'] and k == 0 and l ==0 and m < 0:
1926                return False
1927            else:
1928                return True
1929        elif SGData['SGUniq'] == 'b':
1930            if 'b' in SSGData['modSymb'] and k == 0 and m < 0:
1931                return False
1932            elif 'a' in SSGData['modSymb'] and h == 0 and l ==0 and m < 0:
1933                return False
1934            else:
1935                return True
1936        elif SGData['SGUniq'] == 'c':
1937            if 'g' in SSGData['modSymb'] and l == 0 and m < 0:
1938                return False
1939            elif 'a' in SSGData['modSymb'] and h == 0 and k ==0 and m < 0:
1940                return False
1941            else:
1942                return True
1943    elif SGData['SGLaue'] == 'mmm':
1944        if 'a' in SSGData['modSymb']:
1945            if h == 0 and m < 0:
1946                return False
1947            else:
1948                return True
1949        elif 'b' in SSGData['modSymb']:
1950            if k == 0 and m < 0:
1951                return False
1952            else:
1953                return True
1954        elif 'g' in SSGData['modSymb']:
1955            if l == 0 and m < 0:
1956                return False
1957            else:
1958                return True
1959    else:   #tetragonal, trigonal, hexagonal (& triclinic?)
1960        if l == 0 and m < 0:
1961            return False
1962        else:
1963            return True
1964       
1965def checkHKLextc(HKL,SGData):
1966    Ops = SGData['SGOps']
1967    OpM = np.array([op[0] for op in Ops])
1968    OpT = np.array([op[1] for op in Ops])
1969    HKLS = np.array([HKL,-HKL])     #Freidel's Law
1970    DHKL = np.reshape(np.inner(HKLS,OpM)-HKL,(-1,3))
1971    PHKL = np.reshape(np.inner(HKLS,OpT),(-1,))
1972    for dhkl,phkl in zip(DHKL,PHKL)[1:]:    #skip identity
1973        if dhkl.any():
1974            continue
1975        else:
1976            if phkl%1.:
1977                return False
1978    return True
1979
1980def checkMagextc(HKL,SGData):
1981    Ops = SGData['SGOps']
1982    OpM = np.array([op[0] for op in Ops])
1983    OpT = np.array([op[1] for op in Ops])
1984    if SGData['SGInv']:
1985        OpM = np.vstack((OpM,-OpM))
1986        OpT = np.vstack((OpT,-OpT))%1.
1987    Spn = SGData['SpnFlp'][:len(OpM)]
1988    Mag = np.array([nl.det(opm) for opm in OpM])*Spn
1989    DHKL = np.reshape(np.inner(HKL,OpM),(-1,3))
1990    PHKL = np.reshape(np.cos(twopi*np.inner(HKL,OpT))*Mag,(-1,))[:,nxs,nxs]*OpM
1991    Ftest = np.random.rand(3)
1992    Psum = np.zeros(3)
1993    for dhkl,phkl in zip(DHKL,PHKL):
1994        if not np.allclose(dhkl,HKL):
1995            continue
1996        else:
1997            Psum += np.inner(Ftest,phkl)
1998    if np.allclose(Psum,np.zeros(3)):
1999        return False
2000    else:
2001        if np.inner(HKL,Psum):
2002            return False
2003        return True
2004   
2005def checkSSextc(HKL,SSGData):
2006    Ops = SSGData['SSGOps']
2007    OpM = np.array([op[0] for op in Ops])
2008    OpT = np.array([op[1] for op in Ops])
2009    HKLS = np.array([HKL,-HKL])     #Freidel's Law
2010    DHKL = np.reshape(np.inner(HKLS,OpM)-HKL,(-1,4))
2011    PHKL = np.reshape(np.inner(HKLS,OpT),(-1,))
2012    for dhkl,phkl in zip(DHKL,PHKL)[1:]:    #skip identity
2013        if dhkl.any():
2014            continue
2015        else:
2016            if phkl%1.:
2017                return False
2018    return True
2019   
2020################################################################################
2021#### Site symmetry tables
2022################################################################################
2023     
2024OprName = {
2025    '-6643':       ['-1',1],'6479' :    ['2(z)',2],'-6479':     ['m(z)',3],
2026    '6481' :     ['m(y)',4],'-6481':    ['2(y)',5],'6641' :     ['m(x)',6],
2027    '-6641':     ['2(x)',7],'6591' :  ['m(+-0)',8],'-6591':   ['2(+-0)',9],
2028    '6531' :  ['m(110)',10],'-6531': ['2(110)',11],'6537' :    ['4(z)',12],
2029    '-6537':   ['-4(z)',13],'975'  : ['3(111)',14],'6456' :       ['3',15],
2030    '-489' :  ['3(+--)',16],'483'  : ['3(-+-)',17],'-969' :  ['3(--+)',18],
2031    '819'  :  ['m(+0-)',19],'-819' : ['2(+0-)',20],'2431' :  ['m(0+-)',21],
2032    '-2431':  ['2(0+-)',22],'-657' :  ['m(xz)',23],'657'  :   ['2(xz)',24],
2033    '1943' :   ['-4(x)',25],'-1943':   ['4(x)',26],'-2429':   ['m(yz)',27],
2034    '2429' :   ['2(yz)',28],'639'  :  ['-4(y)',29],'-639' :    ['4(y)',30],
2035    '-6484':   ['2(010)',4],'6484' :  ['m(010)',5],'-6668':   ['2(100)',6],
2036    '6668' :   ['m(100)',7],'-6454': ['2(120)',18],'6454' :  ['m(120)',19],
2037    '-6638':  ['2(210)',20],'6638' : ['m(210)',21],   #search in SytSym ends at m(210)
2038    '2223' : ['3(+++)2',39],
2039    '6538' :   ['6(z)1',40],'-2169':['3(--+)2',41],'2151' : ['3(+--)2',42],
2040    '2205' :['-3(-+-)2',43],'-2205':[' (-+-)2',44],'489'  :['-3(+--)1',45],
2041    '801'  :   ['4(y)1',46],'1945' :  ['4(x)3',47],'-6585': ['-4(z)3 ',48],
2042    '6585' :   ['4(z)3',49],'6584' :  ['3(z)2',50],'6666' :  ['6(z)5 ',51],
2043    '6643' :       ['1',52],'-801' : ['-4(y)1',53],'-1945': ['-4(x)3 ',54],
2044    '-6666':  ['-6(z)5',55],'-6538': ['-6(z)1',56],'-2223':['-3(+++)2',57],
2045    '-975' :['-3(+++)1',58],'-6456': ['-3(z)1',59],'-483' :['-3(-+-)1',60],
2046    '969'  :['-3(--+)1',61],'-6584': ['-3(z)2',62],'2169' :['-3(--+)2',63],
2047    '-2151':['-3(+--)2',64],   }                               
2048
2049KNsym = {
2050    '0'         :'    1   ','1'         :'   -1   ','64'        :'    2(x)','32'        :'    m(x)',
2051    '97'        :'  2/m(x)','16'        :'    2(y)','8'         :'    m(y)','25'        :'  2/m(y)',
2052    '2'         :'    2(z)','4'         :'    m(z)','7'         :'  2/m(z)','134217728' :'   2(yz)',
2053    '67108864'  :'   m(yz)','201326593' :' 2/m(yz)','2097152'   :'  2(0+-)','1048576'   :'  m(0+-)',
2054    '3145729'   :'2/m(0+-)','8388608'   :'   2(xz)','4194304'   :'   m(xz)','12582913'  :' 2/m(xz)',
2055    '524288'    :'  2(+0-)','262144'    :'  m(+0-)','796433'    :'2/m(+0-)','1024'      :'   2(xy)',
2056    '512'       :'   m(xy)','1537'      :' 2/m(xy)','256'       :'  2(+-0)','128'       :'  m(+-0)',
2057    '385'       :'2/m(+-0)','76'        :'  mm2(x)','52'        :'  mm2(y)','42'        :'  mm2(z)',
2058    '135266336' :' mm2(yz)','69206048'  :'mm2(0+-)','8650760'   :' mm2(xz)','4718600'   :'mm2(+0-)',
2059    '1156'      :' mm2(xy)','772'       :'mm2(+-0)','82'        :'  222   ','136314944' :'  222(x)',
2060    '8912912'   :'  222(y)','1282'      :'  222(z)','127'       :'  mmm   ','204472417' :'  mmm(x)',
2061    '13369369'  :'  mmm(y)','1927'      :'  mmm(z)','33554496'  :'  4(x)','16777280'  :' -4(x)',
2062    '50331745'  :'4/m(x)'  ,'169869394' :'422(x)','84934738'  :'-42m(x)','101711948' :'4mm(x)',
2063    '254804095' :'4/mmm(x)','536870928 ':'  4(y)','268435472' :' -4(y)','805306393' :'4/m(y)',
2064    '545783890' :'422(y)','272891986' :'-42m(y)','541327412' :'4mm(y)','818675839' :'4/mmm(y)',
2065    '2050'      :'  4(z)','4098'      :' -4(z)','6151'      :'4/m(z)','3410'      :'422(z)',
2066    '4818'      :'-42m(z)','2730'      :'4mm(z)','8191'      :'4/mmm(z)','8192'      :'  3(111)',
2067    '8193'      :' -3(111)','2629888'   :' 32(111)','1319040'   :' 3m(111)','3940737'   :'-3m(111)',
2068    '32768'     :'  3(+--)','32769'     :' -3(+--)','10519552'  :' 32(+--)','5276160'   :' 3m(+--)',
2069    '15762945'  :'-3m(+--)','65536'     :'  3(-+-)','65537'     :' -3(-+-)','134808576' :' 32(-+-)',
2070    '67437056'  :' 3m(-+-)','202180097' :'-3m(-+-)','131072'    :'  3(--+)','131073'    :' -3(--+)',
2071    '142737664' :' 32(--+)','71434368'  :' 3m(--+)','214040961' :'-3m(--+)','237650'    :'   23   ',
2072    '237695'    :'   m3   ','715894098' :'   432  ','358068946' :'  -43m  ','1073725439':'   m3m  ',
2073    '68157504'  :' mm2(d100)','4456464'   :' mm2(d010)','642'       :' mm2(d001)','153092172' :'-4m2(x)',
2074    '277348404' :'-4m2(y)','5418'      :'-4m2(z)','1075726335':'  6/mmm ','1074414420':'-6m2(100)',
2075    '1075070124':'-6m2(120)','1075069650':'   6mm  ','1074414890':'   622  ','1073758215':'   6/m  ',
2076    '1073758212':'   -6   ','1073758210':'    6   ','1073759865':'-3m(100)','1075724673':'-3m(120)',
2077    '1073758800':' 3m(100)','1075069056':' 3m(120)','1073759272':' 32(100)','1074413824':' 32(120)',
2078    '1073758209':'   -3   ','1073758208':'    3   ','1074135143':'mmm(100)','1075314719':'mmm(010)',
2079    '1073743751':'mmm(110)','1074004034':' mm2(z100)','1074790418':' mm2(z010)','1073742466':' mm2(z110)',
2080    '1074004004':'mm2(100)','1074790412':'mm2(010)','1073742980':'mm2(110)','1073872964':'mm2(120)',
2081    '1074266132':'mm2(210)','1073742596':'mm2(+-0)','1073872930':'222(100)','1074266122':'222(010)',
2082    '1073743106':'222(110)','1073741831':'2/m(001)','1073741921':'2/m(100)','1073741849':'2/m(010)',
2083    '1073743361':'2/m(110)','1074135041':'2/m(120)','1075314689':'2/m(210)','1073742209':'2/m(+-0)',
2084    '1073741828':' m(001) ','1073741888':' m(100) ','1073741840':' m(010) ','1073742336':' m(110) ',
2085    '1074003968':' m(120) ','1074790400':' m(210) ','1073741952':' m(+-0) ','1073741826':' 2(001) ',
2086    '1073741856':' 2(100) ','1073741832':' 2(010) ','1073742848':' 2(110) ','1073872896':' 2(120) ',
2087    '1074266112':' 2(210) ','1073742080':' 2(+-0) ','1073741825':'   -1   ',
2088    }
2089
2090NXUPQsym = {
2091    '1'        :(28,29,28,28),'-1'       :( 1,29,28, 0),'2(x)'     :(12,18,12,25),'m(x)'     :(25,18,12,25),
2092    '2/m(x)'   :( 1,18, 0,-1),'2(y)'     :(13,17,13,24),'m(y)'     :(24,17,13,24),'2/m(y)'   :( 1,17, 0,-1),
2093    '2(z)'     :(14,16,14,23),'m(z)'     :(23,16,14,23),'2/m(z)'   :( 1,16, 0,-1),'2(yz)'    :(10,23,10,22),
2094    'm(yz)'    :(22,23,10,22),' 2/m(yz)' :( 1,23, 0,-1),'2(0+-)'   :(11,24,11,21),'m(0+-)'   :(21,24,11,21),
2095    '2/m(0+-)' :( 1,24, 0,-1),'2(xz)'    :( 8,21, 8,20),'m(xz)'    :(20,21, 8,20),'2/m(xz)'  :( 1,21, 0,-1),
2096    '2(+0-)'   :( 9,22, 9,19),'m(+0-)'   :(19,22, 9,19),'2/m(+0-)' :( 1,22, 0,-1),'2(xy)'    :( 6,19, 6,18),
2097    'm(xy)'    :(18,19, 6,18),' 2/m(xy)' :( 1,19, 0,-1),'2(+-0)'   :( 7,20, 7,17),'m(+-0)'   :(17,20, 7,17),
2098    '2/m(+-0)' :( 1,20, 17,-1),'mm2(x)'  :(12,10, 0,-1),'mm2(y)'   :(13,10, 0,-1),'mm2(z)'   :(14,10, 0,-1),
2099    'mm2(yz)'  :(10,13, 0,-1),'mm2(0+-)' :(11,13, 0,-1),'mm2(xz)'  :( 8,12, 0,-1),'mm2(+0-)' :( 9,12, 0,-1),
2100    'mm2(xy)'  :( 6,11, 0,-1),'mm2(+-0)' :( 7,11, 0,-1),'222'      :( 1,10, 0,-1),'222(x)'   :( 1,13, 0,-1),
2101    '222(y)'   :( 1,12, 0,-1),'222(z)'   :( 1,11, 0,-1),'mmm'      :( 1,10, 0,-1),'mmm(x)'   :( 1,13, 0,-1),
2102    'mmm(y)'   :( 1,12, 0,-1),'mmm(z)'   :( 1,11, 0,-1),'4(x)'     :(12, 4,12, 0),'-4(x)'    :( 1, 4,12, 0),
2103    '4/m(x)'   :( 1, 4,12,-1),'422(x)'   :( 1, 4, 0,-1),'-42m(x)'  :( 1, 4, 0,-1),'4mm(x)'   :(12, 4, 0,-1),
2104    '4/mmm(x)' :( 1, 4, 0,-1),'4(y)'     :(13, 3,13, 0),'-4(y)'    :( 1, 3,13, 0),'4/m(y)'   :( 1, 3,13,-1),
2105    '422(y)'   :( 1, 3, 0,-1),'-42m(y)'  :( 1, 3, 0,-1),'4mm(y)'   :(13, 3, 0,-1),'4/mmm(y)' :(1, 3, 0,-1,),
2106    '4(z)'     :(14, 2,14, 0),'-4(z)'    :( 1, 2,14, 0),'4/m(z)'   :( 1, 2,14,-1),'422(z)'   :( 1, 2, 0,-1),
2107    '-42m(z)'  :( 1, 2, 0,-1),'4mm(z)'   :(14, 2, 0,-1),'4/mmm(z)' :( 1, 2, 0,-1),'3(111)'   :( 2, 5, 2, 0),
2108    '-3(111)'  :( 1, 5, 2, 0),'32(111)'  :( 1, 5, 0, 2),'3m(111)'  :( 2, 5, 0, 2),'-3m(111)' :( 1, 5, 0,-1),
2109    '3(+--)'   :( 5, 8, 5, 0),'-3(+--)'  :( 1, 8, 5, 0),'32(+--)'  :( 1, 8, 0, 5),'3m(+--)'  :( 5, 8, 0, 5),
2110    '-3m(+--)' :( 1, 8, 0,-1),'3(-+-)'   :( 4, 7, 4, 0),'-3(-+-)'  :( 1, 7, 4, 0),'32(-+-)'  :( 1, 7, 0, 4),
2111    '3m(-+-)'  :( 4, 7, 0, 4),'-3m(-+-)' :( 1, 7, 0,-1),'3(--+)'   :( 3, 6, 3, 0),'-3(--+)'  :( 1, 6, 3, 0),
2112    '32(--+)'  :( 1, 6, 0, 3),'3m(--+)'  :( 3, 6, 0, 3),'-3m(--+)' :( 1, 6, 0,-1),'23'       :( 1, 1, 0, 0),
2113    'm3'       :( 1, 1, 0, 0),'432'      :( 1, 1, 0, 0),'-43m'     :( 1, 1, 0, 0),'m3m'      :( 1, 1, 0, 0),
2114    'mm2(d100)':(12,13, 0,-1),'mm2(d010)':(13,12, 0,-1),'mm2(d001)':(14,11, 0,-1),'-4m2(x)'  :( 1, 4, 0,-1),
2115    '-4m2(y)'  :( 1, 3, 0,-1),'-4m2(z)'  :( 1, 2, 0,-1),'6/mmm'    :( 1, 9, 0,-1),'-6m2(100)':( 1, 9, 0,-1),
2116    '-6m2(120)':( 1, 9, 0,-1),'6mm'      :(14, 9, 0,-1),'622'      :( 1, 9, 0,-1),'6/m'      :( 1, 9,14,-1),
2117    '-6'       :( 1, 9,14, 0),'6'        :(14, 9,14, 0),'-3m(100)' :( 1, 9, 0,-1),'-3m(120)' :( 1, 9, 0,-1),
2118    '3m(100)'  :(14, 9, 0,14),'3m(120)'  :(14, 9, 0,14),'32(100)'  :( 1, 9, 0,14),'32(120)'  :( 1, 9, 0,14),
2119    '-3'       :( 1, 9,14, 0),'3'        :(14, 9,14, 0),'mmm(100)' :( 1,14, 0,-1),'mmm(010)' :( 1,15, 0,-1),
2120    'mmm(110)' :( 1,11, 0,-1),'mm2(z100)':(14,14, 0,-1),'mm2(z010)':(14,15, 0,-1),'mm2(z110)':(14,11, 0,-1),
2121    'mm2(100)' :(12,14, 0,-1),'mm2(010)' :(13,15, 0,-1),'mm2(110)' :( 6,11, 0,-1),'mm2(120)' :(15,14, 0,-1),
2122    'mm2(210)' :(16,15, 0,-1),'mm2(+-0)' :( 7,11, 0,-1),'222(100)' :( 1,14, 0,-1),'222(010)' :( 1,15, 0,-1),
2123    '222(110)' :( 1,11, 0,-1),'2/m(001)' :( 1,16,14,-1),'2/m(100)' :( 1,25,12,-1),'2/m(010)' :( 1,28,13,-1),
2124    '2/m(110)' :( 1,19, 6,-1),'2/m(120)' :( 1,27,15,-1),'2/m(210)' :( 1,26,16,-1),'2/m(+-0)' :( 1,20,17,-1),
2125    'm(001)'   :(23,16,14,23),'m(100)'   :(26,25,12,26),'m(010)'   :(27,28,13,27),'m(110)'   :(18,19, 6,18),
2126    'm(120)'   :(24,27,15,24),'m(210)'   :(25,26,16,25),'m(+-0)'   :(17,20, 7,17),'2(001)'   :(14,16,14,23),
2127    '2(100)'   :(12,25,12,26),'2(010)'   :(13,28,13,27),'2(110)'   :( 6,19, 6,18),'2(120)'   :(15,27,15,24),
2128    '2(210)'   :(16,26,16,25),'2(+-0)'   :( 7,20, 7,17),'-1'       :( 1,29,28, 0)
2129    }
2130       
2131CSxinel = [[],      # 0th empty - indices are Fortran style
2132    [[0,0,0],[ 0.0, 0.0, 0.0]],      #1  0  0  0
2133    [[1,1,1],[ 1.0, 1.0, 1.0]],      #2  X  X  X
2134    [[1,1,1],[ 1.0, 1.0,-1.0]],      #3  X  X -X
2135    [[1,1,1],[ 1.0,-1.0, 1.0]],      #4  X -X  X
2136    [[1,1,1],[ 1.0,-1.0,-1.0]],      #5 -X  X  X
2137    [[1,1,0],[ 1.0, 1.0, 0.0]],      #6  X  X  0
2138    [[1,1,0],[ 1.0,-1.0, 0.0]],      #7  X -X  0
2139    [[1,0,1],[ 1.0, 0.0, 1.0]],      #8  X  0  X
2140    [[1,0,1],[ 1.0, 0.0,-1.0]],      #9  X  0 -X
2141    [[0,1,1],[ 0.0, 1.0, 1.0]],      #10  0  Y  Y
2142    [[0,1,1],[ 0.0, 1.0,-1.0]],      #11 0  Y -Y
2143    [[1,0,0],[ 1.0, 0.0, 0.0]],      #12  X  0  0
2144    [[0,1,0],[ 0.0, 1.0, 0.0]],      #13  0  Y  0
2145    [[0,0,1],[ 0.0, 0.0, 1.0]],      #14  0  0  Z
2146    [[1,1,0],[ 1.0, 2.0, 0.0]],      #15  X 2X  0
2147    [[1,1,0],[ 2.0, 1.0, 0.0]],      #16 2X  X  0
2148    [[1,1,2],[ 1.0, 1.0, 1.0]],      #17  X  X  Z
2149    [[1,1,2],[ 1.0,-1.0, 1.0]],      #18  X -X  Z
2150    [[1,2,1],[ 1.0, 1.0, 1.0]],      #19  X  Y  X
2151    [[1,2,1],[ 1.0, 1.0,-1.0]],      #20  X  Y -X
2152    [[1,2,2],[ 1.0, 1.0, 1.0]],      #21  X  Y  Y
2153    [[1,2,2],[ 1.0, 1.0,-1.0]],      #22  X  Y -Y
2154    [[1,2,0],[ 1.0, 1.0, 0.0]],      #23  X  Y  0
2155    [[1,0,2],[ 1.0, 0.0, 1.0]],      #24  X  0  Z
2156    [[0,1,2],[ 0.0, 1.0, 1.0]],      #25  0  Y  Z
2157    [[1,1,2],[ 1.0, 2.0, 1.0]],      #26  X 2X  Z
2158    [[1,1,2],[ 2.0, 1.0, 1.0]],      #27 2X  X  Z
2159    [[1,2,3],[ 1.0, 1.0, 1.0]],      #28  X  Y  Z
2160    ]
2161
2162CSuinel = [[],      # 0th empty - indices are Fortran style
2163    [[1,1,1,0,0,0],[ 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],[1,0,0,0,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #1  A  A  A  0  0  0
2164    [[1,1,2,0,0,0],[ 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],[1,0,1,0,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #2  A  A  C  0  0  0
2165    [[1,2,1,0,0,0],[ 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],[1,1,0,0,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #3  A  B  A  0  0  0
2166    [[1,2,2,0,0,0],[ 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],[1,1,0,0,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #4  A  B  B  0  0  0
2167    [[1,1,1,2,2,2],[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],[1,0,0,1,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #5  A  A  A  D  D  D
2168    [[1,1,1,2,2,2],[ 1.0, 1.0, 1.0, 1.0,-1.0,-1.0],[1,0,0,1,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #6  A  A  A  D -D -D
2169    [[1,1,1,2,2,2],[ 1.0, 1.0, 1.0, 1.0,-1.0, 1.0],[1,0,0,1,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #7  A  A  A  D -D  D
2170    [[1,1,1,2,2,2],[ 1.0, 1.0, 1.0, 1.0, 1.0,-1.0],[1,0,0,1,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #8  A  A  A  D  D -D
2171    [[1,1,2,1,0,0],[ 1.0, 1.0, 1.0, 0.5, 0.0, 0.0],[1,0,1,0,0,0],[1.0,1.0,1.0,0.5,0.0,0.0]],    #9  A  A  C A/2 0  0
2172    [[1,2,3,0,0,0],[ 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],[1,1,1,0,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #10  A  B  C  0  0  0
2173    [[1,1,2,3,0,0],[ 1.0, 1.0, 1.0, 1.0, 0.0, 0.0],[1,0,1,1,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #11  A  A  C  D  0  0
2174    [[1,2,1,0,3,0],[ 1.0, 1.0, 1.0, 0.0, 1.0, 0.0],[1,1,0,0,1,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #12  A  B  A  0  E  0
2175    [[1,2,2,0,0,3],[ 1.0, 1.0, 1.0, 0.0, 0.0, 1.0],[1,1,0,0,0,1],[1.0,1.0,1.0,0.0,0.0,0.0]],    #13  A  B  B  0  0  F
2176    [[1,2,3,2,0,0],[ 1.0, 1.0, 1.0, 0.5, 0.0, 0.0],[1,1,1,0,0,0],[1.0,1.0,1.0,0.0,0.5,0.0]],    #14  A  B  C B/2 0  0
2177    [[1,2,3,1,0,0],[ 1.0, 1.0, 1.0, 0.5, 0.0, 0.0],[1,1,1,0,0,0],[1.0,1.0,1.0,0.0,0.5,0.0]],    #15  A  B  C A/2 0  0
2178    [[1,2,3,4,0,0],[ 1.0, 1.0, 1.0, 1.0, 0.0, 0.0],[1,1,1,1,0,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #16  A  B  C  D  0  0
2179    [[1,2,3,0,4,0],[ 1.0, 1.0, 1.0, 0.0, 1.0, 0.0],[1,1,1,0,1,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #17  A  B  C  0  E  0
2180    [[1,2,3,0,0,4],[ 1.0, 1.0, 1.0, 0.0, 0.0, 1.0],[1,1,1,0,0,1],[1.0,1.0,1.0,0.0,0.0,0.0]],    #18  A  B  C  0  0  F
2181    [[1,1,2,3,4,4],[ 1.0, 1.0, 1.0, 1.0, 1.0,-1.0],[1,0,1,1,1,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #19  A  A  C  D  E -E
2182    [[1,1,2,3,4,4],[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],[1,0,1,1,1,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #20  A  A  C  D  E  E
2183    [[1,2,1,3,4,3],[ 1.0, 1.0, 1.0, 1.0, 1.0,-1.0],[1,1,0,1,1,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #21  A  B  A  D  E -D
2184    [[1,2,1,3,4,3],[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],[1,1,0,1,1,0],[1.0,1.0,1.0,0.0,0.0,0.0]],    #22  A  B  A  D  E  D
2185    [[1,2,2,3,3,4],[ 1.0, 1.0, 1.0, 1.0,-1.0, 1.0],[1,1,0,1,0,1],[1.0,1.0,1.0,0.0,0.0,0.0]],    #23  A  B  B  D -D  F
2186    [[1,2,2,3,3,4],[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],[1,1,0,1,0,1],[1.0,1.0,1.0,0.0,0.0,0.0]],    #24  A  B  B  D  D  F
2187    [[1,2,3,2,4,4],[ 1.0, 1.0, 1.0, 0.5, 1.0, 2.0],[1,1,1,0,0,1],[1.0,1.0,1.0,0.5,0.0,0.0]],    #25  A  B  C B/2 F/2 F
2188    [[1,2,3,1,0,4],[ 1.0, 1.0, 1.0, 0.5, 0.0, 1.0],[1,1,1,0,0,1],[1.0,1.0,1.0,0.5,0.0,0.0]],    #26  A  B  C A/2  0  F
2189    [[1,2,3,2,4,0],[ 1.0, 1.0, 1.0, 0.5, 1.0, 0.0],[1,1,1,0,1,0],[1.0,1.0,1.0,0.5,0.0,0.0]],    #27  A  B  C B/2  E  0
2190    [[1,2,3,1,4,4],[ 1.0, 1.0, 1.0, 0.5, 1.0, 0.5],[1,1,1,0,1,0],[1.0,1.0,1.0,0.5,0.0,0.0]],    #28  A  B  C A/2  E E/2
2191    [[1,2,3,4,5,6],[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],[1,1,1,1,1,1],[1.0,1.0,1.0,0.0,0.0,0.0]],    #29  A  B  C  D  E   F
2192    ]
2193   
2194################################################################################
2195#### Site symmetry routines
2196################################################################################
2197   
2198def GetOprPtrName(key):
2199    'Needs a doc string'
2200    try:
2201        oprName = OprName[key][0]
2202    except KeyError:
2203        return key
2204    return oprName.replace('(','').replace(')','')
2205
2206def GetOprPtrNumber(key):
2207    'Needs a doc string'
2208    try:
2209        return OprName[key][1]
2210    except KeyError:
2211        return key
2212
2213def GetOprName(key):
2214    'Needs a doc string'
2215    return OprName[key][0]
2216
2217def GetKNsym(key):
2218    'Needs a doc string'
2219    try:
2220        return KNsym[key].strip()
2221    except KeyError:
2222        return 'sp'
2223
2224def GetNXUPQsym(siteSym):
2225    '''       
2226    The codes XUPQ are for lookup of symmetry constraints for position(X), thermal parm(U) & magnetic moments (P & Q)
2227    '''
2228    return NXUPQsym[siteSym]
2229
2230def GetCSxinel(siteSym): 
2231    "returns Xyz terms, multipliers, GUI flags"
2232    indx = GetNXUPQsym(siteSym.strip())
2233    return CSxinel[indx[0]]
2234   
2235def GetCSuinel(siteSym):
2236    "returns Uij terms, multipliers, GUI flags & Uiso2Uij multipliers"
2237    indx = GetNXUPQsym(siteSym.strip())
2238    return CSuinel[indx[1]]
2239   
2240def GetCSpqinel(SpnFlp,dupDir): 
2241    "returns Mxyz terms, multipliers, GUI flags"
2242    CSI = [[1,2,3],[1.0,1.0,1.0]]
2243    for sopr in dupDir:
2244#        print (sopr,dupDir[sopr])
2245        opr = sopr.replace("'",'')
2246        indx = GetNXUPQsym(opr)
2247        if SpnFlp[dupDir[sopr]] > 0:
2248            csi = CSxinel[indx[2]]  #P
2249        else:
2250            csi = CSxinel[indx[3]]  #Q
2251#        print(opr,indx,csi,CSI)
2252        if not len(csi):
2253            return [[0,0,0],[0.,0.,0.]]
2254        for kcs in [0,1,2]:
2255            if csi[0][kcs] == 0 and CSI[0][kcs] != 0:
2256                jcs = CSI[0][kcs]
2257                for ics in [0,1,2]:
2258                    if CSI[0][ics] == jcs:
2259                        CSI[0][ics] = 0
2260                        CSI[1][ics] = 0.
2261                    elif CSI[0][ics] > jcs:
2262                        CSI[0][ics] = CSI[0][ics]-1
2263            elif (CSI[0][kcs] == csi[0][kcs]) and (CSI[1][kcs] != csi[1][kcs]):
2264                CSI[1][kcs] = csi[1][kcs]
2265            elif CSI[0][kcs] >= csi[0][kcs]:
2266                CSI[0][kcs] = min(CSI[0][kcs],csi[0][kcs])
2267                if CSI[1][kcs] != csi[1][kcs]:
2268                    if CSI[1][kcs] == 1.:
2269                        CSI[1][kcs] = csi[1][kcs]
2270#        print(CSI)
2271    return CSI
2272   
2273def getTauT(tau,sop,ssop,XYZ,wave=np.zeros(3)):
2274    phase = np.sum(XYZ*wave)
2275    ssopinv = nl.inv(ssop[0])
2276    mst = ssopinv[3][:3]
2277    epsinv = ssopinv[3][3]
2278    sdet = nl.det(sop[0])
2279    ssdet = nl.det(ssop[0])
2280    dtau = mst*(XYZ-sop[1])-epsinv*ssop[1][3]
2281    dT = 1.0
2282    if np.any(dtau%.5):
2283        sumdtau = np.sum(dtau%.5)
2284        dT = 0.
2285        if np.abs(sumdtau-.5) > 1.e-4:
2286            dT = np.tan(np.pi*sumdtau)
2287    tauT = np.inner(mst,XYZ-sop[1])+epsinv*(tau-ssop[1][3]+phase)
2288    return sdet,ssdet,dtau,dT,tauT
2289   
2290def OpsfromStringOps(A,SGData,SSGData):
2291    SGOps = SGData['SGOps']
2292    SSGOps = SSGData['SSGOps']
2293    Ax = A.split('+')
2294    Ax[0] = int(Ax[0])
2295    iC = 1
2296    if Ax[0] < 0:
2297        iC = -1
2298    Ax[0] = abs(Ax[0])
2299    nA = Ax[0]%100-1
2300    nC = Ax[0]//100
2301    unit = [0,0,0]
2302    if len(Ax) > 1:
2303        unit = eval('['+Ax[1]+']')
2304    return SGOps[nA],SSGOps[nA],iC,SGData['SGCen'][nC],unit
2305   
2306def GetSSfxuinel(waveType,Stype,nH,XYZ,SGData,SSGData,debug=False):
2307   
2308    def orderParms(CSI):
2309        parms = [0,]
2310        for csi in CSI:
2311            for i in [0,1,2]:
2312                if csi[i] not in parms:
2313                    parms.append(csi[i])
2314        for csi in CSI:
2315            for i in [0,1,2]:
2316                csi[i] = parms.index(csi[i])
2317        return CSI
2318       
2319    def fracCrenel(tau,Toff,Twid):
2320        Tau = (tau-Toff[:,nxs])%1.
2321        A = np.where(Tau<Twid[:,nxs],1.,0.)
2322        return A
2323       
2324    def fracFourier(tau,nH,fsin,fcos):
2325        SA = np.sin(2.*nH*np.pi*tau)
2326        CB = np.cos(2.*nH*np.pi*tau)
2327        A = SA[nxs,nxs,:]*fsin[:,:,nxs]
2328        B = CB[nxs,nxs,:]*fcos[:,:,nxs]
2329        return A+B
2330       
2331    def posFourier(tau,nH,psin,pcos):
2332        SA = np.sin(2*nH*np.pi*tau)
2333        CB = np.cos(2*nH*np.pi*tau)
2334        A = SA[nxs,nxs,:]*psin[:,:,nxs]
2335        B = CB[nxs,nxs,:]*pcos[:,:,nxs]
2336        return A+B   
2337
2338    def posSawtooth(tau,Toff,slopes):
2339        Tau = (tau-Toff)%1.
2340        A = slopes[:,nxs]*Tau
2341        return A
2342   
2343    def posZigZag(tau,Tmm,XYZmax):
2344        DT = Tmm[1]-Tmm[0]
2345        slopeUp = 2.*XYZmax/DT
2346        slopeDn = 2.*XYZmax/(1.-DT)
2347        A = np.array([np.where(Tmm[0] < t%1. <= Tmm[1],-XYZmax+slopeUp*((t-Tmm[0])%1.),XYZmax-slopeDn*((t-Tmm[1])%1.)) for t in tau])
2348        return A
2349
2350    def posBlock(tau,Tmm,XYZmax):
2351        A = np.array([np.where(Tmm[0] < t <= Tmm[1],XYZmax,-XYZmax) for t in tau])
2352        return A
2353       
2354    def DoFrac():
2355        dF = None
2356        dFTP = None
2357        if siteSym == '1':
2358            CSI = [[1,0],[2,0]],2*[[1.,0.],]
2359        elif siteSym == '-1':
2360            CSI = [[1,0],[0,0]],2*[[1.,0.],]
2361        else:
2362            delt2 = np.eye(2)*0.001
2363            FSC = np.ones(2,dtype='i')
2364            CSI = [np.zeros((2),dtype='i'),np.zeros(2)]
2365            if 'Crenel' in waveType:
2366                dF = np.zeros_like(tau)
2367            else:
2368                dF = fracFourier(tau,nH,delt2[:1],delt2[1:]).squeeze()
2369            dFT = np.zeros_like(dF)
2370            dFTP = []
2371            for i in SdIndx:
2372                sop = Sop[i]
2373                ssop = SSop[i]           
2374                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2375                fsc = np.ones(2,dtype='i')
2376                if 'Crenel' in waveType:
2377                    dFT = np.zeros_like(tau)
2378                    fsc = [1,1]
2379                else:   #Fourier
2380                    dFT = fracFourier(tauT,nH,delt2[:1],delt2[1:]).squeeze()
2381                    dFT = nl.det(sop[0])*dFT
2382                    dFT = dFT[:,np.argsort(tauT)]
2383                    dFT[0] *= ssdet
2384                    dFT[1] *= sdet
2385                    dFTP.append(dFT)
2386               
2387                    if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2388                        fsc = [1,1]
2389                        if dT:
2390                            CSI = [[[1,0],[1,0]],[[1.,0.],[1/dT,0.]]]
2391                        else:
2392                            CSI = [[[1,0],[0,0]],[[1.,0.],[0.,0.]]]
2393                        FSC = np.zeros(2,dtype='i')
2394                        return CSI,dF,dFTP
2395                    else:
2396                        for i in range(2):
2397                            if np.allclose(dF[i,:],dFT[i,:],atol=1.e-6):
2398                                fsc[i] = 1
2399                            else:
2400                                fsc[i] = 0
2401                        FSC &= fsc
2402                        if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,fsc)
2403            n = -1
2404            for i,F in enumerate(FSC):
2405                if F:
2406                    n += 1
2407                    CSI[0][i] = n+1
2408                    CSI[1][i] = 1.0
2409           
2410        return CSI,dF,dFTP
2411       
2412    def DoXYZ():
2413        dX,dXTP = None,None
2414        if siteSym == '1':
2415            CSI = [[1,0,0],[2,0,0],[3,0,0], [4,0,0],[5,0,0],[6,0,0]],6*[[1.,0.,0.],]
2416        elif siteSym == '-1':
2417            CSI = [[1,0,0],[2,0,0],[3,0,0], [0,0,0],[0,0,0],[0,0,0]],3*[[1.,0.,0.],]+3*[[0.,0.,0.],]
2418        else:
2419            delt4 = np.ones(4)*0.001
2420            delt5 = np.ones(5)*0.001
2421            delt6 = np.eye(6)*0.001
2422            if 'Fourier' in waveType:
2423                dX = posFourier(tau,nH,delt6[:3],delt6[3:]) #+np.array(XYZ)[:,nxs,nxs]
2424                  #3x6x12 modulated position array (X,Spos,tau)& force positive
2425                CSI = [np.zeros((6,3),dtype='i'),np.zeros((6,3))]
2426            elif waveType == 'Sawtooth':
2427                dX = posSawtooth(tau,delt4[0],delt4[1:])
2428                CSI = [np.array([[1,0,0],[2,0,0],[3,0,0],[4,0,0]]),
2429                    np.array([[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0]])]
2430            elif waveType in ['ZigZag','Block']:
2431                if waveType == 'ZigZag':
2432                    dX = posZigZag(tau,delt5[:2],delt5[2:])
2433                else:
2434                    dX = posBlock(tau,delt5[:2],delt5[2:])
2435                CSI = [np.array([[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0]]),
2436                    np.array([[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0]])]
2437            XSC = np.ones(6,dtype='i')
2438            dXTP = []
2439            for i in SdIndx:
2440                sop = Sop[i]
2441                ssop = SSop[i]
2442                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2443                xsc = np.ones(6,dtype='i')
2444                if 'Fourier' in waveType:
2445                    dXT = posFourier(np.sort(tauT),nH,delt6[:3],delt6[3:])   #+np.array(XYZ)[:,nxs,nxs]
2446                elif waveType == 'Sawtooth':
2447                    dXT = posSawtooth(tauT,delt4[0],delt4[1:])+np.array(XYZ)[:,nxs,nxs]
2448                elif waveType == 'ZigZag':
2449                    dXT = posZigZag(tauT,delt5[:2],delt5[2:])+np.array(XYZ)[:,nxs,nxs]
2450                elif waveType == 'Block':
2451                    dXT = posBlock(tauT,delt5[:2],delt5[2:])+np.array(XYZ)[:,nxs,nxs]
2452                dXT = np.inner(sop[0],dXT.T)    # X modulations array(3x6x49) -> array(3x49x6)
2453                dXT = np.swapaxes(dXT,1,2)      # back to array(3x6x49)
2454                dXT[:,:3,:] *= (ssdet*sdet)            # modify the sin component
2455                dXTP.append(dXT)
2456                if waveType == 'Fourier':
2457                    for i in range(3):
2458                        if not np.allclose(dX[i,i,:],dXT[i,i,:]):
2459                            xsc[i] = 0
2460                        if not np.allclose(dX[i,i+3,:],dXT[i,i+3,:]):
2461                            xsc[i+3] = 0
2462                    if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2463                        xsc[3:6] = 0
2464                        CSI = [[[1,0,0],[2,0,0],[3,0,0], [1,0,0],[2,0,0],[3,0,0]],
2465                            [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2466                        if dT:
2467                            if '(x)' in siteSym:
2468                                CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]
2469                                if 'm' in siteSym and len(SdIndx) == 1:
2470                                    CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[1./dT,0.,0.]
2471                            elif '(y)' in siteSym:
2472                                CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2473                                if 'm' in siteSym and len(SdIndx) == 1:
2474                                    CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2475                            elif '(z)' in siteSym:
2476                                CSI[1][3:] = [-dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2477                                if 'm' in siteSym and len(SdIndx) == 1:
2478                                    CSI[1][3:] = [1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2479                        else:
2480                            CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2481                    if '4/mmm' in laue:
2482                        if np.any(dtau%.5) and '1/2' in SSGData['modSymb']:
2483                            if '(xy)' in siteSym:
2484                                CSI[0] = [[1,0,0],[1,0,0],[2,0,0], [1,0,0],[1,0,0],[2,0,0]]
2485                                if dT:
2486                                    CSI[1][3:] = [[1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]]
2487                                else:
2488                                    CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2489                        if '(xy)' in siteSym or '(+-0)' in siteSym:
2490                            mul = 1
2491                            if '(+-0)' in siteSym:
2492                                mul = -1
2493                            if np.allclose(dX[0,0,:],dXT[1,0,:]):
2494                                CSI[0][3:5] = [[11,0,0],[11,0,0]]
2495                                CSI[1][3:5] = [[1.,0,0],[mul,0,0]]
2496                                xsc[3:5] = 0
2497                            if np.allclose(dX[0,3,:],dXT[0,4,:]):
2498                                CSI[0][:2] = [[12,0,0],[12,0,0]]
2499                                CSI[1][:2] = [[1.,0,0],[mul,0,0]]
2500                                xsc[:2] = 0
2501                XSC &= xsc
2502                if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,xsc)
2503            if waveType == 'Fourier':
2504                n = -1
2505                if debug: print (XSC)
2506                for i,X in enumerate(XSC):
2507                    if X:
2508                        n += 1
2509                        CSI[0][i][0] = n+1
2510                        CSI[1][i][0] = 1.0
2511           
2512        return list(CSI),dX,dXTP
2513       
2514    def DoUij():
2515        dU,dUTP = None,None
2516        if siteSym == '1':
2517            CSI = [[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0], 
2518                [7,0,0],[8,0,0],[9,0,0],[10,0,0],[11,0,0],[12,0,0]],12*[[1.,0.,0.],]
2519        elif siteSym == '-1':
2520            CSI = 6*[[0,0,0],]+[[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],   \
2521                6*[[0.,0.,0.],]+[[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]
2522        else:
2523            tau = np.linspace(0,1,49,True)
2524            delt12 = np.eye(12)*0.0001
2525            dU = posFourier(tau,nH,delt12[:6],delt12[6:])                  #Uij modulations - 6x12x12 array
2526            CSI = [np.zeros((12,3),dtype='i'),np.zeros((12,3))]
2527            USC = np.ones(12,dtype='i')
2528            dUTP = []
2529            dtau = 0.
2530            for i in SdIndx:
2531                sop = Sop[i]
2532                ssop = SSop[i]
2533                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2534                usc = np.ones(12,dtype='i')
2535                dUT = posFourier(tauT,nH,delt12[:6],delt12[6:])                  #Uij modulations - 6x12x49 array
2536                dUijT = np.rollaxis(np.rollaxis(np.array(Uij2U(dUT)),3),3)    #convert dUT to 12x49x3x3
2537                dUijT = np.rollaxis(np.inner(np.inner(sop[0],dUijT),sop[0].T),3) #transform by sop - 3x3x12x49
2538                dUT = np.array(U2Uij(dUijT))    #convert to 6x12x49
2539                dUT = dUT[:,:,np.argsort(tauT)]
2540                dUT[:,:6,:] *=(ssdet*sdet)
2541                dUTP.append(dUT)
2542                if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2543                    if dT:
2544                        CSI = [[[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0], 
2545                        [1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],
2546                        [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.],
2547                        [1./dT,0.,0.],[1./dT,0.,0.],[1./dT,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2548                    else:
2549                        CSI = [[[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0], 
2550                        [1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],
2551                        [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.],
2552                        [0.,0.,0.],[0.,0.,0.],[0.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2553                    if 'mm2(x)' in siteSym and dT:
2554                        CSI[1][9:] = [0.,0.,0.],[-dT,0.,0.],[0.,0.,0.]
2555                        USC = [1,1,1,0,1,0,1,1,1,0,1,0]
2556                    elif '(xy)' in siteSym and dT:
2557                        CSI[0] = [[1,0,0],[1,0,0],[2,0,0],[3,0,0],[4,0,0],[4,0,0],
2558                            [1,0,0],[1,0,0],[2,0,0],[3,0,0],[4,0,0],[4,0,0]]
2559                        CSI[1][9:] = [[1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]]
2560                        USC = [1,1,1,1,1,1,1,1,1,1,1,1]                             
2561                    elif '(x)' in siteSym and dT:
2562                        CSI[1][9:] = [-dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2563                    elif '(y)' in siteSym and dT:
2564                        CSI[1][9:] = [-dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2565                    elif '(z)' in siteSym and dT:
2566                        CSI[1][9:] = [1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]
2567                    for i in range(6):
2568                        if not USC[i]:
2569                            CSI[0][i] = [0,0,0]
2570                            CSI[1][i] = [0.,0.,0.]
2571                            CSI[0][i+6] = [0,0,0]
2572                            CSI[1][i+6] = [0.,0.,0.]
2573                else:                       
2574                    for i in range(6):
2575                        if not np.allclose(dU[i,i,:],dUT[i,i,:]):  #sin part
2576                            usc[i] = 0
2577                        if not np.allclose(dU[i,i+6,:],dUT[i,i+6,:]):   #cos part
2578                            usc[i+6] = 0
2579                    if np.any(dUT[1,0,:]):
2580                        if '4/m' in siteSym:
2581                            CSI[0][6:8] = [[12,0,0],[12,0,0]]
2582                            if ssop[1][3]:
2583                                CSI[1][6:8] = [[1.,0.,0.],[-1.,0.,0.]]
2584                                usc[9] = 1
2585                            else:
2586                                CSI[1][6:8] = [[1.,0.,0.],[1.,0.,0.]]
2587                                usc[9] = 0
2588                        elif '4' in siteSym:
2589                            CSI[0][6:8] = [[12,0,0],[12,0,0]]
2590                            CSI[0][:2] = [[11,0,0],[11,0,0]]
2591                            if ssop[1][3]:
2592                                CSI[1][:2] = [[1.,0.,0.],[-1.,0.,0.]]
2593                                CSI[1][6:8] = [[1.,0.,0.],[-1.,0.,0.]]
2594                                usc[2] = 0
2595                                usc[8] = 0
2596                                usc[3] = 1
2597                                usc[9] = 1
2598                            else:
2599                                CSI[1][:2] = [[1.,0.,0.],[1.,0.,0.]]
2600                                CSI[1][6:8] = [[1.,0.,0.],[1.,0.,0.]]
2601                                usc[2] = 1
2602                                usc[8] = 1
2603                                usc[3] = 0               
2604                                usc[9] = 0
2605                        elif 'xy' in siteSym or '+-0' in siteSym:
2606                            if np.allclose(dU[0,0,:],dUT[0,1,:]*sdet):
2607                                CSI[0][4:6] = [[12,0,0],[12,0,0]]
2608                                CSI[0][6:8] = [[11,0,0],[11,0,0]]
2609                                CSI[1][4:6] = [[1.,0.,0.],[sdet,0.,0.]]
2610                                CSI[1][6:8] = [[1.,0.,0.],[sdet,0.,0.]]
2611                                usc[4:6] = 0
2612                                usc[6:8] = 0
2613                           
2614                    if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,usc)
2615                USC &= usc
2616            if debug: print (USC)
2617            if not np.any(dtau%.5):
2618                n = -1
2619                for i,U in enumerate(USC):
2620                    if U:
2621                        n += 1
2622                        CSI[0][i][0] = n+1
2623                        CSI[1][i][0] = 1.0
2624   
2625        return list(CSI),dU,dUTP
2626   
2627    def DoMag():
2628        CSI = [[1,0,0],[2,0,0],[3,0,0], [4,0,0],[5,0,0],[6,0,0]],6*[[1.,0.,0.],]
2629        if siteSym == '1':
2630            CSI = [[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],6*[[1.,0.,0.],]
2631        elif siteSym in ['-1','mmm','2/m(x)','2/m(y)','2/m(z)','4/mmm001']:
2632            CSI = 3*[[0,0,0],]+[[1,0,0],[2,0,0],[3,0,0]],3*[[0.,0.,0.],]+3*[[1.,0.,0.],]
2633        else:
2634            delt6 = np.eye(6)*0.001
2635            dM = posFourier(tau,nH,delt6[:3],delt6[3:]) #+np.array(Mxyz)[:,nxs,nxs]
2636              #3x6x12 modulated moment array (M,Spos,tau)& force positive
2637            CSI = [np.zeros((6,3),dtype='i'),np.zeros((6,3))]
2638            MSC = np.ones(6,dtype='i')
2639            dMTP = []
2640            for i in SdIndx:
2641                sop = Sop[i]
2642                ssop = SSop[i]
2643                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2644                msc = np.ones(6,dtype='i')
2645                dMT = posFourier(np.sort(tauT),nH,delt6[:3],delt6[3:])   #+np.array(XYZ)[:,nxs,nxs]
2646                dMT = np.inner(sop[0],dMT.T)    # X modulations array(3x6x49) -> array(3x49x6)
2647                dMT = np.swapaxes(dMT,1,2)      # back to array(3x6x49)
2648                dMT[:,:3,:] *= (ssdet*sdet)            # modify the sin component
2649                dMTP.append(dMT)
2650                for i in range(3):
2651                    if not np.allclose(dM[i,i,:],dMT[i,i,:]):
2652                        msc[i] = 0
2653                    if not np.allclose(dM[i,i+3,:],dMT[i,i+3,:]):
2654                        msc[i+3] = 0
2655                if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2656                    msc[3:6] = 0
2657                    CSI = [[[1,0,0],[2,0,0],[3,0,0], [1,0,0],[2,0,0],[3,0,0]],
2658                        [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2659                    if dT:
2660                        if '(x)' in siteSym:
2661                            CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]
2662                            if 'm' in siteSym and len(SdIndx) == 1:
2663                                CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[1./dT,0.,0.]
2664                        elif '(y)' in siteSym:
2665                            CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2666                            if 'm' in siteSym and len(SdIndx) == 1:
2667                                CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2668                        elif '(z)' in siteSym:
2669                            CSI[1][3:] = [-dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2670                            if 'm' in siteSym and len(SdIndx) == 1:
2671                                CSI[1][3:] = [1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2672                    else:
2673                        CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2674                if '4/mmm' in laue:
2675                    if np.any(dtau%.5) and '1/2' in SSGData['modSymb']:
2676                        if '(xy)' in siteSym:
2677                            CSI[0] = [[1,0,0],[1,0,0],[2,0,0], [1,0,0],[1,0,0],[2,0,0]]
2678                            if dT:
2679                                CSI[1][3:] = [[1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]]
2680                            else:
2681                                CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2682                    if '(xy)' in siteSym or '(+-0)' in siteSym:
2683                        mul = 1
2684                        if '(+-0)' in siteSym:
2685                            mul = -1
2686                        if np.allclose(dM[0,0,:],dMT[1,0,:]):
2687                            CSI[0][3:5] = [[11,0,0],[11,0,0]]
2688                            CSI[1][3:5] = [[1.,0,0],[mul,0,0]]
2689                            msc[3:5] = 0
2690                        if np.allclose(dM[0,3,:],dMT[0,4,:]):
2691                            CSI[0][:2] = [[12,0,0],[12,0,0]]
2692                            CSI[1][:2] = [[1.,0,0],[mul,0,0]]
2693                            msc[:2] = 0
2694                MSC &= msc
2695                if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,msc)
2696            n = -1
2697            if debug: print (MSC)
2698            for i,M in enumerate(MSC):
2699                if M:
2700                    n += 1
2701                    CSI[0][i][0] = n+1
2702                    CSI[1][i][0] = 1.0
2703
2704        return CSI,None,None
2705       
2706    if debug: print ('super space group: '+SSGData['SSpGrp'])
2707    xyz = np.array(XYZ)%1.
2708    SGOps = copy.deepcopy(SGData['SGOps'])
2709    laue = SGData['SGLaue']
2710    siteSym = SytSym(XYZ,SGData)[0].strip()
2711    if debug: print ('siteSym: '+siteSym)
2712    SSGOps = copy.deepcopy(SSGData['SSGOps'])
2713    #expand ops to include inversions if any
2714    if SGData['SGInv']:
2715        for op,sop in zip(SGData['SGOps'],SSGData['SSGOps']):
2716            SGOps.append([-op[0],-op[1]%1.])
2717            SSGOps.append([-sop[0],-sop[1]%1.])
2718    #build set of sym ops around special position       
2719    SSop = []
2720    Sop = []
2721    Sdtau = []
2722    for iop,Op in enumerate(SGOps):         
2723        nxyz = (np.inner(Op[0],xyz)+Op[1])%1.
2724        if np.allclose(xyz,nxyz,1.e-4) and iop and MT2text(Op).replace(' ','') != '-X,-Y,-Z':
2725            SSop.append(SSGOps[iop])
2726            Sop.append(SGOps[iop])
2727            ssopinv = nl.inv(SSGOps[iop][0])
2728            mst = ssopinv[3][:3]
2729            epsinv = ssopinv[3][3]
2730            Sdtau.append(np.sum(mst*(XYZ-SGOps[iop][1])-epsinv*SSGOps[iop][1][3]))
2731    SdIndx = np.argsort(np.array(Sdtau))     # just to do in sensible order
2732    if debug: print ('special pos super operators: ',[SSMT2text(ss).replace(' ','') for ss in SSop])
2733    #setup displacement arrays
2734    tau = np.linspace(-1,1,49,True)
2735    #make modulation arrays - one parameter at a time
2736    if Stype == 'Sfrac':
2737        CSI,dF,dFTP = DoFrac()
2738    elif Stype == 'Spos':
2739        CSI,dF,dFTP = DoXYZ()
2740        CSI[0] = orderParms(CSI[0])
2741    elif Stype == 'Sadp':
2742        CSI,dF,dFTP = DoUij()
2743        CSI[0] = orderParms(CSI[0]) 
2744    elif Stype == 'Smag':
2745        CSI,dF,dFTP = DoMag()
2746       
2747    if debug:
2748        return CSI,dF,dFTP
2749    else:
2750        return CSI
2751   
2752def MustrainNames(SGData):
2753    'Needs a doc string'
2754    laue = SGData['SGLaue']
2755    uniq = SGData['SGUniq']
2756    if laue in ['m3','m3m']:
2757        return ['S400','S220']
2758    elif laue in ['6/m','6/mmm','3m1']:
2759        return ['S400','S004','S202']
2760    elif laue in ['31m','3']:
2761        return ['S400','S004','S202','S211']
2762    elif laue in ['3R','3mR']:
2763        return ['S400','S220','S310','S211']
2764    elif laue in ['4/m','4/mmm']:
2765        return ['S400','S004','S220','S022']
2766    elif laue in ['mmm']:
2767        return ['S400','S040','S004','S220','S202','S022']
2768    elif laue in ['2/m']:
2769        SHKL = ['S400','S040','S004','S220','S202','S022']
2770        if uniq == 'a':
2771            SHKL += ['S013','S031','S211']
2772        elif uniq == 'b':
2773            SHKL += ['S301','S103','S121']
2774        elif uniq == 'c':
2775            SHKL += ['S130','S310','S112']
2776        return SHKL
2777    else:
2778        SHKL = ['S400','S040','S004','S220','S202','S022']
2779        SHKL += ['S310','S103','S031','S130','S301','S013']
2780        SHKL += ['S211','S121','S112']
2781        return SHKL
2782       
2783def HStrainVals(HSvals,SGData):
2784    laue = SGData['SGLaue']
2785    uniq = SGData['SGUniq']
2786    DIJ = np.zeros(6)
2787    if laue in ['m3','m3m']:
2788        DIJ[:3] = [HSvals[0],HSvals[0],HSvals[0]]
2789    elif laue in ['6/m','6/mmm','3m1','31m','3']:
2790        DIJ[:4] = [HSvals[0],HSvals[0],HSvals[1],HSvals[0]]
2791    elif laue in ['3R','3mR']:
2792        DIJ = [HSvals[0],HSvals[0],HSvals[0],HSvals[1],HSvals[1],HSvals[1]]
2793    elif laue in ['4/m','4/mmm']:
2794        DIJ[:3] = [HSvals[0],HSvals[0],HSvals[1]]
2795    elif laue in ['mmm']:
2796        DIJ[:3] = [HSvals[0],HSvals[1],HSvals[2]]
2797    elif laue in ['2/m']:
2798        DIJ[:3] = [HSvals[0],HSvals[1],HSvals[2]]
2799        if uniq == 'a':
2800            DIJ[5] = HSvals[3]
2801        elif uniq == 'b':
2802            DIJ[4] = HSvals[3]
2803        elif uniq == 'c':
2804            DIJ[3] = HSvals[3]
2805    else:
2806        DIJ = [HSvals[0],HSvals[1],HSvals[2],HSvals[3],HSvals[4],HSvals[5]]
2807    return DIJ
2808
2809def HStrainNames(SGData):
2810    'Needs a doc string'
2811    laue = SGData['SGLaue']
2812    uniq = SGData['SGUniq']
2813    if laue in ['m3','m3m']:
2814        return ['D11','eA']         #add cubic strain term
2815    elif laue in ['6/m','6/mmm','3m1','31m','3']:
2816        return ['D11','D33']
2817    elif laue in ['3R','3mR']:
2818        return ['D11','D12']
2819    elif laue in ['4/m','4/mmm']:
2820        return ['D11','D33']
2821    elif laue in ['mmm']:
2822        return ['D11','D22','D33']
2823    elif laue in ['2/m']:
2824        Dij = ['D11','D22','D33']
2825        if uniq == 'a':
2826            Dij += ['D23']
2827        elif uniq == 'b':
2828            Dij += ['D13']
2829        elif uniq == 'c':
2830            Dij += ['D12']
2831        return Dij
2832    else:
2833        Dij = ['D11','D22','D33','D12','D13','D23']
2834        return Dij
2835   
2836def MustrainCoeff(HKL,SGData):
2837    'Needs a doc string'
2838    #NB: order of terms is the same as returned by MustrainNames
2839    laue = SGData['SGLaue']
2840    uniq = SGData['SGUniq']
2841    h,k,l = HKL
2842    Strm = []
2843    if laue in ['m3','m3m']:
2844        Strm.append(h**4+k**4+l**4)
2845        Strm.append(3.0*((h*k)**2+(h*l)**2+(k*l)**2))
2846    elif laue in ['6/m','6/mmm','3m1']:
2847        Strm.append(h**4+k**4+2.0*k*h**3+2.0*h*k**3+3.0*(h*k)**2)
2848        Strm.append(l**4)
2849        Strm.append(3.0*((h*l)**2+(k*l)**2+h*k*l**2))
2850    elif laue in ['31m','3']:
2851        Strm.append(h**4+k**4+2.0*k*h**3+2.0*h*k**3+3.0*(h*k)**2)
2852        Strm.append(l**4)
2853        Strm.append(3.0*((h*l)**2+(k*l)**2+h*k*l**2))
2854        Strm.append(4.0*h*k*l*(h+k))
2855    elif laue in ['3R','3mR']:
2856        Strm.append(h**4+k**4+l**4)
2857        Strm.append(3.0*((h*k)**2+(h*l)**2+(k*l)**2))
2858        Strm.append(2.0*(h*l**3+l*k**3+k*h**3)+2.0*(l*h**3+k*l**3+l*k**3))
2859        Strm.append(4.0*(k*l*h**2+h*l*k**2+h*k*l**2))
2860    elif laue in ['4/m','4/mmm']:
2861        Strm.append(h**4+k**4)
2862        Strm.append(l**4)
2863        Strm.append(3.0*(h*k)**2)
2864        Strm.append(3.0*((h*l)**2+(k*l)**2))
2865    elif laue in ['mmm']:
2866        Strm.append(h**4)
2867        Strm.append(k**4)
2868        Strm.append(l**4)
2869        Strm.append(3.0*(h*k)**2)
2870        Strm.append(3.0*(h*l)**2)
2871        Strm.append(3.0*(k*l)**2)
2872    elif laue in ['2/m']:
2873        Strm.append(h**4)
2874        Strm.append(k**4)
2875        Strm.append(l**4)
2876        Strm.append(3.0*(h*k)**2)
2877        Strm.append(3.0*(h*l)**2)
2878        Strm.append(3.0*(k*l)**2)
2879        if uniq == 'a':
2880            Strm.append(2.0*k*l**3)
2881            Strm.append(2.0*l*k**3)
2882            Strm.append(4.0*k*l*h**2)
2883        elif uniq == 'b':
2884            Strm.append(2.0*l*h**3)
2885            Strm.append(2.0*h*l**3)
2886            Strm.append(4.0*h*l*k**2)
2887        elif uniq == 'c':
2888            Strm.append(2.0*h*k**3)
2889            Strm.append(2.0*k*h**3)
2890            Strm.append(4.0*h*k*l**2)
2891    else:
2892        Strm.append(h**4)
2893        Strm.append(k**4)
2894        Strm.append(l**4)
2895        Strm.append(3.0*(h*k)**2)
2896        Strm.append(3.0*(h*l)**2)
2897        Strm.append(3.0*(k*l)**2)
2898        Strm.append(2.0*k*h**3)
2899        Strm.append(2.0*h*l**3)
2900        Strm.append(2.0*l*k**3)
2901        Strm.append(2.0*h*k**3)
2902        Strm.append(2.0*l*h**3)
2903        Strm.append(2.0*k*l**3)
2904        Strm.append(4.0*k*l*h**2)
2905        Strm.append(4.0*h*l*k**2)
2906        Strm.append(4.0*k*h*l**2)
2907    return Strm
2908
2909def MuShklMean(SGData,Amat,Shkl):
2910   
2911    def genMustrain(xyz,Shkl):
2912        uvw = np.inner(Amat.T,xyz)
2913        Strm = np.array(MustrainCoeff(uvw,SGData))
2914        Sum = np.sum(np.multiply(Shkl,Strm))
2915        Sum = np.where(Sum > 0.01,Sum,0.01)
2916        Sum = np.sqrt(Sum)
2917        return Sum*xyz
2918       
2919    PHI = np.linspace(0.,360.,30,True)
2920    PSI = np.linspace(0.,180.,30,True)
2921    X = np.outer(npcosd(PHI),npsind(PSI))
2922    Y = np.outer(npsind(PHI),npsind(PSI))
2923    Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
2924    XYZ = np.dstack((X,Y,Z))
2925    XYZ = np.nan_to_num(np.apply_along_axis(genMustrain,2,XYZ,Shkl))
2926    return np.sqrt(np.sum(XYZ**2)/900.)
2927   
2928def Muiso2Shkl(muiso,SGData,cell):
2929    "this is to convert isotropic mustrain to generalized Shkls"
2930    import GSASIIlattice as G2lat
2931    A = G2lat.cell2AB(cell)[0]
2932   
2933    def minMus(Shkl,muiso,H,SGData,A):
2934        U = np.inner(A.T,H)
2935        S = np.array(MustrainCoeff(U,SGData))
2936        Sum = np.sqrt(np.sum(np.multiply(S,Shkl[:,nxs]),axis=0))
2937        rad = np.sqrt(np.sum((Sum[:,nxs]*H)**2,axis=1))
2938        return (muiso-rad)**2
2939       
2940    laue = SGData['SGLaue']
2941    PHI = np.linspace(0.,360.,60,True)
2942    PSI = np.linspace(0.,180.,60,True)
2943    X = np.outer(npsind(PHI),npsind(PSI))
2944    Y = np.outer(npcosd(PHI),npsind(PSI))
2945    Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
2946    HKL = np.dstack((X,Y,Z))
2947    if laue in ['m3','m3m']:
2948        S0 = [1000.,1000.]
2949    elif laue in ['6/m','6/mmm','3m1']:
2950        S0 = [1000.,1000.,1000.]
2951    elif laue in ['31m','3']:
2952        S0 = [1000.,1000.,1000.,1000.]
2953    elif laue in ['3R','3mR']:
2954        S0 = [1000.,1000.,1000.,1000.]
2955    elif laue in ['4/m','4/mmm']:
2956        S0 = [1000.,1000.,1000.,1000.]
2957    elif laue in ['mmm']:
2958        S0 = [1000.,1000.,1000.,1000.,1000.,1000.]
2959    elif laue in ['2/m']:
2960        S0 = [1000.,1000.,1000.,0.,0.,0.,0.,0.,0.]
2961    else:
2962        S0 = [1000.,1000.,1000.,1000.,1000., 1000.,1000.,1000.,1000.,1000., 
2963            1000.,1000.,0.,0.,0.]
2964    S0 = np.array(S0)
2965    HKL = np.reshape(HKL,(-1,3))
2966    result = so.leastsq(minMus,S0,(np.ones(HKL.shape[0])*muiso,HKL,SGData,A))
2967    return result[0]
2968       
2969def PackRot(SGOps):
2970    IRT = []
2971    for ops in SGOps:
2972        M = ops[0]
2973        irt = 0
2974        for j in range(2,-1,-1):
2975            for k in range(2,-1,-1):
2976                irt *= 3
2977                irt += M[k][j]
2978        IRT.append(int(irt))
2979    return IRT
2980       
2981def SytSym(XYZ,SGData):
2982    '''
2983    Generates the number of equivalent positions and a site symmetry code for a specified coordinate and space group
2984
2985    :param XYZ: an array, tuple or list containing 3 elements: x, y & z
2986    :param SGData: from SpcGroup
2987    :Returns: a four element tuple:
2988
2989     * The 1st element is a code for the site symmetry (see GetKNsym)
2990     * The 2nd element is the site multiplicity
2991     * Ndup number of overlapping operators
2992     * dupDir Dict - dictionary of overlapping operators
2993
2994    '''
2995    Mult = 1
2996    Isym = 0
2997    if SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']:
2998        Isym = 1073741824
2999    Jdup = 0
3000    Ndup = 0
3001    dupDir = {}
3002    inv = SGData['SGInv']+1
3003    icen = SGData['SGCen']
3004    if SGData['SGFixed']:       #already in list of operators
3005        inv = 1
3006    Xeqv = list(GenAtom(XYZ,SGData,True))
3007#    for xeqv in Xeqv:   print(xeqv)
3008    IRT = PackRot(SGData['SGOps'])
3009    L = -1
3010    for ic,cen in enumerate(icen):
3011        for invers in range(int(inv)):
3012            for io,ops in enumerate(SGData['SGOps']):
3013                irtx = (1-2*invers)*IRT[io]
3014                L += 1
3015                if not Xeqv[L][1]:
3016                    Ndup = io
3017                    Jdup += 1
3018                    jx = GetOprPtrNumber(str(irtx))   #[KN table no,op name,KNsym ptr]
3019                    if jx < 39:
3020                        px = GetOprName(str(irtx))
3021                        if Xeqv[L][-1] < 0:
3022                            if '(' in px:
3023                                px = px.split('(')
3024                                px[0] += "'"
3025                                px = '('.join(px)
3026                            else:   
3027                                px += "'"
3028                        dupDir[px] = L
3029                        Isym += 2**(jx-1)
3030    if Isym == 1073741824: Isym = 0
3031    Mult = len(SGData['SGOps'])*len(SGData['SGCen'])*inv//Jdup
3032         
3033    return GetKNsym(str(Isym)),Mult,Ndup,dupDir
3034   
3035def MagSytSym(SytSym,dupDir,SGData):
3036    '''
3037    site sym operations: 1,-1,2,3,-3,4,-4,6,-6,m need to be marked if spin inversion
3038    '''
3039    SGData['GenSym'],SGData['GenFlg'] = GetGenSym(SGData)[:2]
3040#    print('SGPtGrp',SGData['SGPtGrp'],'SytSym',SytSym,'MagSpGrp',SGData['MagSpGrp'])
3041#    print('dupDir',dupDir)
3042    SplitSytSym = SytSym.split('(')
3043    if SGData['SGGray']:
3044        return SytSym+"1'"
3045    if SytSym == '1':       #genersl position
3046        return SytSym
3047    if SplitSytSym[0] == SGData['SGPtGrp']:     #simple cases
3048        try:
3049            MagSytSym = SGData['MagSpGrp'].split()[1]
3050        except IndexError:
3051            MagSytSym = SGData['MagSpGrp'][1:].strip("1'")
3052        if len(SplitSytSym) > 1:
3053            MagSytSym += '('+SplitSytSym[1]
3054        return MagSytSym
3055    if len(dupDir) == 1:
3056        return dupDir.keys()[0]
3057   
3058   
3059    if '2/m' in SytSym:         #done I think; last 2wo might be not needed
3060        ops = {'(x)':['2(x)','m(x)'],'(y)':['2(y)','m(y)'],'(z)':['2(z)','m(z)'],
3061               '(100)':['2(100)','m(100)'],'(010)':['2(010)','m(010)'],'(001)':['2(001)','m(001)'],
3062               '(120)':['2(120)','m(120)'],'(210)':['2(210)','m(210)'],'(+-0)':['2(+-0)','m(+-0)'],
3063               '(110)':['2(110)','m(110)']}
3064   
3065    elif '4/mmm' in SytSym:
3066        ops = {'(x)':['4(x)','m(x)','m(y)','m(0+-)'],   #m(0+-) for cubic m3m?
3067               '(y)':['4(y)','m(y)','m(z)','m(+0-)'],   #m(+0-)
3068               '(z)':['4(z)','m(z)','m(x)','m(+-0)']}   #m(+-0)
3069    elif '4mm' in SytSym:
3070        ops = {'(x)':['4(x)','m(y)','m(yz)'],'(y)':['4(y)','m(z)','m(xz)'],'(z)':['4(z)','m(x)','m(110)']}
3071    elif '422' in SytSym:
3072        ops = {'(x)':['4(x)','2(y)','2(yz)'],'(y)':['4(y)','2(z)','2(xz)'],'(z)':['4(z)','2(x)','2(110)']}
3073    elif '-4m2' in SytSym:
3074        ops = {'(x)':['-4(x)','m(x)','2(yz)'],'(y)':['-4(y)','m(y)','2(xz)'],'(z)':['-4(z)','m(z)','2(110)']}
3075    elif '-42m' in SytSym:
3076        ops = {'(x)':['-4(x)','2(y)','m(yz)'],'(y)':['-4(y)','2(z)','m(xz)'],'(z)':['-4(z)','2(x)','m(110)']}
3077    elif '-4' in SytSym:
3078        ops = {'(x)':['-4(x)',],'(y)':['-4(y)',],'(z)':['-4(z)',],}
3079    elif '4' in SytSym:
3080        ops = {'(x)':['4(x)',],'(y)':['4(y)',],'(z)':['4(z)',],}
3081
3082    elif '222' in SytSym:
3083        ops = {'':['2(x)','2(y)','2(z)'],
3084                   '(x)':['2(y)','2(z)','2(x)'],'(y)':['2(x)','2(z)','2(y)'],'(z)':['2(x)','2(y)','2(z)'],
3085                   '(100)':['2(z)','2(100)','2(120)',],'(010)':['2(z)','2(010)','2(210)',],
3086                   '(110)':['2(z)','2(110)','2(+-0)',],}
3087    elif 'mm2' in SytSym:
3088        ops = {'(x)':['m(y)','m(z)','2(x)'],'(y)':['m(x)','m(z)','2(y)'],'(z)':['m(x)','m(y)','2(z)'],
3089               '(xy)':['m(+-0)','m(z)','2(110)'],'(yz)':['m(0+-)','m(xz)','2(yz)'],     #not 2(xy)!
3090               '(xz)':['m(+0-)','m(y)','2(xz)'],'(z100)':['m(100)','m(120)','2(z)'],
3091               '(z010)':['m(010)','m(210)','2(z)'],'(z110)':['m(110)','m(+-0)','2(z)'],
3092               '(+-0)':[ 'm(110)','m(z)','2(+-0)'],'(d100)':['m(yz)','m(0+-)','2(xz)'],
3093               '(d010)':['m(xz)','m(+0-)','2(y)'],'(d001)':['m(110)','m(+-0)','2(z)'],
3094               '(210)':['m(z)','m(010)','2(210)'],
3095               '(100)':['m(z)','m(120)','2(100)',],'(010)':['m(z)','m(210)','2(010)',],
3096               '(110)':['m(z)','m(+-0)','2(110)',],}
3097    elif 'mmm' in SytSym:
3098        ops = {'':['m(x)','m(y)','m(z)'],
3099                   '(100)':['m(z)','m(100)','m(120)',],'(010)':['m(z)','m(010)','m(210)',],
3100                   '(110)':['m(z)','m(110)','m(+-0)',],
3101                   '(x)':['m(x)','m(y)','m(z)'],'(y)':['m(x)','m(y)','m(z)'],'(z)':['m(x)','m(y)','m(z)'],}
3102       
3103    elif '32' in SytSym:
3104        ops = {'(120)':['3','2(120)',],'(100)':['3','2(100)']}
3105    elif '23' in SytSym:
3106        ops = {'':['2(x)','3(111)']}
3107    elif 'm3' in SytSym:
3108        ops = {'(100)':['(+-0)',],'(+--)':[],'(-+-)':[],'(--+)':[]}
3109    elif '3m' in SytSym:
3110        ops = {'(111)':['3(111)','m(+-0)',],'(+--)':['3(+--)','m(0+-)',],
3111               '(-+-)':['3(-+-)','m(+0-)',],'(--+)':['3(--+)','m(+-0)',],
3112               '(100)':['3','m(100)'],'(120)':['3','m(210)',]}
3113   
3114    if SytSym.split('(')[0] in ['6/m','6mm','-6m2','622','-6','-3','-3m',]:     #not simple cases
3115        MagSytSym = SytSym
3116        if "-1'" in dupDir:
3117            if '-6' in SytSym:
3118                MagSytSym = MagSytSym.replace('-6',"-6'")
3119            elif '-3m' in SytSym:
3120                MagSytSym = MagSytSym.replace('-3m',"-3'm'")
3121            elif '-3' in SytSym:
3122                MagSytSym = MagSytSym.replace('-3',"-3'")
3123        elif '-6m2' in SytSym:
3124            if "m'(110)" in dupDir:
3125                MagSytSym = "-6m'2'("+SytSym.split('(')[1]
3126        elif '6/m' in SytSym:
3127            if "m'(z)" in dupDir:
3128                MagSytSym = "6'/m'"
3129        elif '6mm' in SytSym:
3130            if "m'(110)" in dupDir:
3131                MagSytSym = "6'm'm"
3132        return MagSytSym
3133    try:
3134        axis = '('+SytSym.split('(')[1]
3135    except IndexError:
3136        axis = ''
3137    MagSytSym = ''
3138    for m in ops[axis]:
3139        if m in dupDir:
3140            MagSytSym += m.split('(')[0]
3141        else:
3142            MagSytSym += m.split('(')[0]+"'"
3143        if '2/m' in SytSym and '2' in m:
3144            MagSytSym += '/'
3145        if '-3/m' in SytSym:
3146            MagSytSym = '-'+MagSytSym
3147       
3148    MagSytSym += axis
3149# some exceptions & special rules         
3150    if MagSytSym == "4'/m'm'm'": MagSytSym = "4/m'm'm'"
3151    return MagSytSym
3152   
3153#    if len(GenSym) == 3:
3154#        if SGSpin[1] < 0:
3155#            if 'mm2' in SytSym:
3156#                MagSytSym = "m'm'2"+'('+SplitSytSym[1]
3157#            else:   #bad rule for I41/a
3158#                MagSytSym = SplitSytSym[0]+"'"
3159#                if len(SplitSytSym) > 1:
3160#                    MagSytSym += '('+SplitSytSym[1]
3161#        else:
3162#            MagSytSym = SytSym
3163#        if len(SplitSytSym) >1:
3164#            if "-4'"+'('+SplitSytSym[1] in dupDir:
3165#                MagSytSym = MagSytSym.replace('-4',"-4'")
3166#            if "-6'"+'('+SplitSytSym[1] in dupDir:
3167#                MagSytSym = MagSytSym.replace('-6',"-6'")
3168#        return MagSytSym
3169#           
3170    return SytSym
3171
3172def UpdateSytSym(Phase):
3173    ''' Update site symmetry/site multiplicity after space group/VNS lattice change
3174    '''
3175    generalData = Phase['General']
3176    SGData = generalData['SGData']
3177    Atoms = Phase['Atoms']
3178    cx,ct,cs,cia = generalData['AtomPtrs']
3179    for atom in Atoms:
3180        XYZ = atom[cx:cx+3]
3181        sytsym,Mult = SytSym(XYZ,SGData)[:2]
3182        sytSym,Mul,Nop,dupDir = SytSym(XYZ,SGData)
3183        atom[cs] = sytsym
3184        if generalData['Type'] == 'magnetic':
3185            magSytSym = MagSytSym(sytSym,dupDir,SGData)
3186            atom[cs] = magSytSym
3187        atom[cs+1] = Mult
3188    return
3189   
3190def ElemPosition(SGData):
3191    ''' Under development.
3192    Object here is to return a list of symmetry element types and locations suitable
3193    for say drawing them.
3194    So far I have the element type... getting all possible locations without lookup may be impossible!
3195    '''
3196    Inv = SGData['SGInv']
3197    eleSym = {-3:['','-1'],-2:['',-6],-1:['2','-4'],0:['3','-3'],1:['4','m'],2:['6',''],3:['1','']}
3198    # get operators & expand if centrosymmetric
3199    Ops = SGData['SGOps']
3200    opM = np.array([op[0].T for op in Ops])
3201    opT = np.array([op[1] for op in Ops])
3202    if Inv:
3203        opM = np.concatenate((opM,-opM))
3204        opT = np.concatenate((opT,-opT))
3205    opMT = list(zip(opM,opT))
3206    for M,T in opMT[1:]:        #skip I
3207        Dt = int(nl.det(M))
3208        Tr = int(np.trace(M))
3209        Dt = -(Dt-1)//2
3210        Es = eleSym[Tr][Dt]
3211        if Dt:              #rotation-inversion
3212            I = np.eye(3)
3213            if Tr == 1:     #mirrors/glides
3214                if np.any(T):       #glide
3215                    M2 = np.inner(M,M)
3216                    MT = np.inner(M,T)+T
3217                    print ('glide',Es,MT)
3218                    print (M2)
3219                else:               #mirror
3220                    print ('mirror',Es,T)
3221                    print (I-M)
3222                X = [-1,-1,-1]
3223            elif Tr == -3:  # pure inversion
3224                X = np.inner(nl.inv(I-M),T)
3225                print ('inversion',Es,X)
3226            else:           #other rotation-inversion
3227                M2 = np.inner(M,M)
3228                MT = np.inner(M,T)+T
3229                print ('rot-inv',Es,MT)
3230                print (M2)
3231                X = [-1,-1,-1]
3232        else:               #rotations
3233            print ('rotation',Es)
3234            X = [-1,-1,-1]
3235        #SymElements.append([Es,X])
3236       
3237    return #SymElements
3238   
3239def ApplyStringOps(A,SGData,X,Uij=[]):
3240    'Needs a doc string'
3241    SGOps = SGData['SGOps']
3242    SGCen = SGData['SGCen']
3243    Ax = A.split('+')
3244    Ax[0] = int(Ax[0])
3245    iC = 1
3246    if Ax[0] < 0:
3247        iC = -1
3248    Ax[0] = abs(Ax[0])
3249    nA = Ax[0]%100-1
3250    cA = Ax[0]//100
3251    Cen = SGCen[cA]
3252    M,T = SGOps[nA]
3253    if len(Ax)>1:
3254        cellA = Ax[1].split(',')
3255        cellA = np.array([int(a) for a in cellA])
3256    else:
3257        cellA = np.zeros(3)
3258    newX = Cen+iC*(np.inner(M,X).T+T)+cellA
3259    if len(Uij):
3260        U = Uij2U(Uij)
3261        U = np.inner(M,np.inner(U,M).T)
3262        newUij = U2Uij(U)
3263        return [newX,newUij]
3264    else:
3265        return newX
3266       
3267def ApplyStringOpsMom(A,SGData,Mom):
3268    'Needs a doc string'
3269    SGOps = SGData['SGOps']
3270    Ax = A.split('+')
3271    Ax[0] = int(Ax[0])
3272    iAx = abs(Ax[0])
3273    nA = iAx%100-1
3274    nC = len(SGOps)*(iAx//100)
3275    NA = nA
3276    if Ax[0] < 0:
3277        NA += len(SGOps)
3278    M,T = SGOps[nA]
3279    if SGData['SGGray']:        #no nonzero moments for gray groups!
3280        newMom = [0.,0.,0.]
3281    else:
3282        newMom = np.inner(Mom,M).T*nl.det(M)*SGData['SpnFlp'][NA+nC]
3283#        print(len(SGOps),Ax[0],iAx,nC,nA,NA,SGData['SpnFlp'][NA],Mom,newMom)
3284    return newMom
3285       
3286def StringOpsProd(A,B,SGData):
3287    """
3288    Find A*B where A & B are in strings '-' + '100*c+n' + '+ijk'
3289    where '-' indicates inversion, c(>0) is the cell centering operator,
3290    n is operator number from SgOps and ijk are unit cell translations (each may be <0).
3291    Should return resultant string - C. SGData - dictionary using entries:
3292
3293       *  'SGCen': cell centering vectors [0,0,0] at least
3294       *  'SGOps': symmetry operations as [M,T] so that M*x+T = x'
3295
3296    """
3297    SGOps = SGData['SGOps']
3298    SGCen = SGData['SGCen']
3299    #1st split out the cell translation part & work on the operator parts
3300    Ax = A.split('+'); Bx = B.split('+')
3301    Ax[0] = int(Ax[0]); Bx[0] = int(Bx[0])
3302    iC = 0
3303    if Ax[0]*Bx[0] < 0:
3304        iC = 1
3305    Ax[0] = abs(Ax[0]); Bx[0] = abs(Bx[0])
3306    nA = Ax[0]%100-1;  nB = Bx[0]%100-1
3307    cA = Ax[0]//100;  cB = Bx[0]//100
3308    Cen = (SGCen[cA]+SGCen[cB])%1.0
3309    cC = np.nonzero([np.allclose(C,Cen) for C in SGCen])[0][0]
3310    Ma,Ta = SGOps[nA]; Mb,Tb = SGOps[nB]
3311    Mc = np.inner(Ma,Mb.T)
3312#    print Ma,Mb,Mc
3313    Tc = (np.add(np.inner(Mb,Ta)+1.,Tb))%1.0
3314#    print Ta,Tb,Tc
3315#    print [np.allclose(M,Mc)&np.allclose(T,Tc) for M,T in SGOps]
3316    nC = np.nonzero([np.allclose(M,Mc)&np.allclose(T,Tc) for M,T in SGOps])[0][0]
3317    #now the cell translation part
3318    if len(Ax)>1:
3319        cellA = Ax[1].split(',')
3320        cellA = [int(a) for a in cellA]
3321    else:
3322        cellA = [0,0,0]
3323    if len(Bx)>1:
3324        cellB = Bx[1].split(',')
3325        cellB = [int(b) for b in cellB]
3326    else:
3327        cellB = [0,0,0]
3328    cellC = np.add(cellA,cellB)
3329    C = str(((nC+1)+(100*cC))*(1-2*iC))+'+'+ \
3330        str(int(cellC[0]))+','+str(int(cellC[1]))+','+str(int(cellC[2]))
3331    return C
3332           
3333def U2Uij(U):
3334    #returns the UIJ vector U11,U22,U33,U12,U13,U23 from tensor U
3335    return [U[0][0],U[1][1],U[2][2],U[0][1],U[0][2],U[1][2]]
3336   
3337def Uij2U(Uij):
3338    #returns the thermal motion tensor U from Uij as numpy array
3339    return np.array([[Uij[0],Uij[3],Uij[4]],[Uij[3],Uij[1],Uij[5]],[Uij[4],Uij[5],Uij[2]]])
3340
3341def StandardizeSpcName(spcgroup):
3342    '''Accept a spacegroup name where spaces may have not been used
3343    in the names according to the GSAS convention (spaces between symmetry
3344    for each axis) and return the space group name as used in GSAS
3345    '''
3346    rspc = spcgroup.replace(' ','').upper()
3347    # deal with rhombohedral and hexagonal setting designations
3348    rhomb = ''
3349    if rspc[-1:] == 'R':
3350        rspc = rspc[:-1]
3351        rhomb = ' R'
3352    gray = ''
3353    if "1'" in rspc:
3354        gray = " 1'"
3355        rspc = rspc.replace("1'",'')
3356    elif rspc[-1:] == 'H': # hexagonal is assumed and thus can be ignored
3357        rspc = rspc[:-1]
3358    else:
3359        rspc = rspc.replace("'",'')
3360    # look for a match in the spacegroup lists
3361    for i in spglist.values():
3362        for spc in i:
3363            if rspc == spc.replace(' ','').upper():
3364                return spc+gray+rhomb
3365    # how about the post-2002 orthorhombic names?
3366    if rspc in sgequiv_2002_orthorhombic:
3367        return sgequiv_2002_orthorhombic[rspc]+gray
3368    else:
3369    # not found
3370        return ''
3371
3372spgbyNum = []
3373'''Space groups indexed by number'''
3374spgbyNum = [None,
3375        'P 1','P -1',                                                   #1-2
3376        'P 2','P 21','C 2','P m','P c','C m','C c','P 2/m','P 21/m',
3377        'C 2/m','P 2/c','P 21/c','C 2/c',                               #3-15
3378        'P 2 2 2','P 2 2 21','P 21 21 2','P 21 21 21',
3379        'C 2 2 21','C 2 2 2','F 2 2 2','I 2 2 2','I 21 21 21',
3380        'P m m 2','P m c 21','P c c 2','P m a 2','P c a 21',
3381        'P n c 2','P m n 21','P b a 2','P n a 21','P n n 2',
3382        'C m m 2','C m c 21','C c c 2',
3383        'A m m 2','A b m 2','A m a 2','A b a 2',
3384        'F m m 2','F d d 2','I m m 2','I b a 2','I m a 2',
3385        'P m m m','P n n n','P c c m','P b a n',
3386        'P m m a','P n n a','P m n a','P c c a','P b a m','P c c n',
3387        'P b c m','P n n m','P m m n','P b c n','P b c a','P n m a',
3388        'C m c m','C m c a','C m m m','C c c m','C m m a','C c c a',
3389        'F m m m', 'F d d d',
3390        'I m m m','I b a m','I b c a','I m m a',                        #16-74
3391        'P 4','P 41','P 42','P 43',
3392        'I 4','I 41',
3393        'P -4','I -4','P 4/m','P 42/m','P 4/n','P 42/n',
3394        'I 4/m','I 41/a',
3395        'P 4 2 2','P 4 21 2','P 41 2 2','P 41 21 2','P 42 2 2',
3396        'P 42 21 2','P 43 2 2','P 43 21 2',
3397        'I 4 2 2','I 41 2 2',
3398        'P 4 m m','P 4 b m','P 42 c m','P 42 n m','P 4 c c','P 4 n c',
3399        'P 42 m c','P 42 b c',
3400        'I 4 m m','I 4 c m','I 41 m d','I 41 c d',
3401        'P -4 2 m','P -4 2 c','P -4 21 m','P -4 21 c','P -4 m 2',
3402        'P -4 c 2','P -4 b 2','P -4 n 2',
3403        'I -4 m 2','I -4 c 2','I -4 2 m','I -4 2 d',
3404        'P 4/m m m','P 4/m c c','P 4/n b m','P 4/n n c','P 4/m b m',
3405        'P 4/m n c','P 4/n m m','P 4/n c c','P 42/m m c','P 42/m c m',
3406        'P 42/n b c','P 42/n n m','P 42/m b c','P 42/m n m','P 42/n m c',
3407        'P 42/n c m',
3408        'I 4/m m m','I 4/m c m','I 41/a m d','I 41/a c d',
3409        'P 3','P 31','P 32','R 3','P -3','R -3',
3410        'P 3 1 2','P 3 2 1','P 31 1 2','P 31 2 1','P 32 1 2','P 32 2 1',
3411        'R 3 2',
3412        'P 3 m 1','P 3 1 m','P 3 c 1','P 3 1 c',
3413        'R 3 m','R 3 c',
3414        'P -3 1 m','P -3 1 c','P -3 m 1','P -3 c 1',
3415        'R -3 m','R -3 c',                                               #75-167
3416        'P 6','P 61',
3417        'P 65','P 62','P 64','P 63','P -6','P 6/m','P 63/m','P 6 2 2',
3418        'P 61 2 2','P 65 2 2','P 62 2 2','P 64 2 2','P 63 2 2','P 6 m m',
3419        'P 6 c c','P 63 c m','P 63 m c','P -6 m 2','P -6 c 2','P -6 2 m',
3420        'P -6 2 c','P 6/m m m','P 6/m c c','P 63/m c m','P 63/m m c',   #168-194
3421        'P 2 3','F 2 3','I 2 3','P 21 3','I 21 3','P m 3','P n 3',
3422        'F m -3','F d -3','I m -3',
3423        'P a -3','I a -3','P 4 3 2','P 42 3 2','F 4 3 2','F 41 3 2',
3424        'I 4 3 2','P 43 3 2','P 41 3 2','I 41 3 2','P -4 3 m',
3425        'F -4 3 m','I -4 3 m','P -4 3 n','F -4 3 c','I -4 3 d',
3426        'P m -3 m','P n -3 n','P m -3 n','P n -3 m',
3427        'F m -3 m','F m -3 c','F d -3 m','F d -3 c',
3428        'I m -3 m','I a -3 d',]                                       #195-230
3429spg2origins = {}
3430''' A dictionary of all spacegroups that have 2nd settings; the value is the
34311st --> 2nd setting transformation vector as X(2nd) = X(1st)-V, nonstandard ones are included.
3432'''
3433spg2origins = {
3434        'P n n n':[-.25,-.25,-.25],
3435        'P b a n':[-.25,-.25,0],'P n c b':[0,-.25,-.25],'P c n a':[-.25,0,-.25],
3436        'P m m n':[-.25,-.25,0],'P n m m':[0,-.25,-.25],'P m n m':[-.25,0,-.25],
3437        'C c c a':[0,-.25,-.25],'C c c b':[-.25,0,-.25],'A b a a':[-.25,0,-.25],
3438        'A c a a':[-.25,-.25,0],'B b c b':[-.25,-.25,0],'B b a b':[0,-.25,-.25],
3439        'F d d d':[-.125,-.125,-.125],
3440        'P 4/n':[-.25,-.25,0],'P 42/n':[-.25,-.25,-.25],'I 41/a':[0,-.25,-.125],
3441        'P 4/n b m':[-.25,-.25,0],'P 4/n n c':[-.25,-.25,-.25],'P 4/n m m':[-.25,-.25,0],'P 4/n c c':[-.25,-.25,0],
3442        'P 42/n b c':[-.25,-.25,-.25],'P 42/n n m':[-.25,.25,-.25],'P 42/n m c':[-.25,.25,-.25],'P 42/n c m':[-.25,.25,-.25],
3443        'I 41/a m d':[0,.25,-.125],'I 41/a c d':[0,.25,-.125],
3444        'p n -3':[-.25,-.25,-.25],'F d -3':[-.125,-.125,-.125],'P n -3 n':[-.25,-.25,-.25],
3445        'P n -3 m':[-.25,-.25,-.25],'F d -3 m':[-.125,-.125,-.125],'F d -3 c':[-.375,-.375,-.375],
3446        'p n 3':[-.25,-.25,-.25],'F d 3':[-.125,-.125,-.125],'P n 3 n':[-.25,-.25,-.25],
3447        'P n 3 m':[-.25,-.25,-.25],'F d 3 m':[-.125,-.125,-.125],'F d - c':[-.375,-.375,-.375]}
3448spglist = {}
3449'''A dictionary of space groups as ordered and named in the pre-2002 International
3450Tables Volume A, except that spaces are used following the GSAS convention to
3451separate the different crystallographic directions.
3452Note that the symmetry codes here will recognize many non-standard space group
3453symbols with different settings. They are ordered by Laue group
3454'''
3455spglist = {
3456    'P1' : ('P 1','P -1',), # 1-2
3457    'C1' : ('C 1','C -1',),
3458    'P2/m': ('P 2','P 21','P m','P a','P c','P n',
3459        'P 2/m','P 21/m','P 2/c','P 2/a','P 2/n','P 21/c','P 21/a','P 21/n',), #3-15
3460    'C2/m':('C 2','C m','C c','C n',
3461        'C 2/m','C 2/c','C 2/n',),
3462    'A2/m':('A 2','A m','A a','A n',
3463        'A 2/m','A 2/a','A 2/n',),
3464   'Pmmm':('P 2 2 2',
3465        'P 2 2 21','P 21 2 2','P 2 21 2',
3466        'P 21 21 2','P 2 21 21','P 21 2 21',
3467        'P 21 21 21',
3468        'P m m 2','P 2 m m','P m 2 m',
3469        'P m c 21','P 21 m a','P b 21 m','P m 21 b','P c m 21','P 21 a m',
3470        'P c c 2','P 2 a a','P b 2 b',
3471        'P m a 2','P 2 m b','P c 2 m','P m 2 a','P b m 2','P 2 c m',
3472        'P c a 21','P 21 a b','P c 21 b','P b 21 a','P b c 21','P 21 c a',
3473        'P n c 2','P 2 n a','P b 2 n','P n 2 b','P c n 2','P 2 a n',
3474        'P m n 21','P 21 m n','P n 21 m','P m 21 n','P n m 21','P 21 n m',
3475        'P b a 2','P 2 c b','P c 2 a',
3476        'P n a 21','P 21 n b','P c 21 n','P n 21 a','P b n 21','P 21 c n',
3477        'P n n 2','P 2 n n','P n 2 n',
3478        'P m m m','P n n n',
3479        'P c c m','P m a a','P b m b',
3480        'P b a n','P n c b','P c n a',
3481        'P m m a','P b m m','P m c m','P m a m','P m m b','P c m m',
3482        'P n n a','P b n n','P n c n','P n a n','P n n b','P c n n',
3483        'P m n a','P b m n','P n c m','P m a n','P n m b','P c n m',
3484        'P c c a','P b a a','P b c b','P b a b','P c c b','P c a a',
3485        'P b a m','P m c b','P c m a',
3486        'P c c n','P n a a','P b n b',
3487        'P b c m','P m c a','P b m a','P c m b','P c a m','P m a b',
3488        'P n n m','P m n n','P n m n',
3489        'P m m n','P n m m','P m n m',
3490        'P b c n','P n c a','P b n a','P c n b','P c a n','P n a b',
3491        'P b c a','P c a b',
3492        'P n m a','P b n m','P m c n','P n a m','P m n b','P c m n',
3493        ),
3494    'Cmmm':('C 2 2 21','C 2 2 2','C m m 2',
3495        'C m c 21','C c m 21','C c c 2','C m 2 m','C 2 m m',
3496        'C m 2 a','C 2 m b','C c 2 m','C 2 c m','C c 2 a','C 2 c b',
3497        'C m c m','C c m m','C m c a','C c m b',
3498        'C m m m','C c c m','C m m a','C m m b','C c c a','C c c b',),
3499    'Ammm':('A 21 2 2','A 2 2 2','A 2 m m',
3500        'A 21 m a','A 21 a m','A 2 a a','A m 2 m','A m m 2',
3501        'A b m 2','A c 2 m','A m a 2','A m 2 a','A b a 2','A c 2 a',
3502        'A m m a','A m a m','A b m a','A c a m',
3503        'A m m m','A m a a','A b m m','A c m m','A c a a','A b a a',),
3504    'Bmmm':('B 2 21 2','B 2 2 2','B m 2 m',
3505        'B m 21 b','B b 21 m','B b 2 b','B m m 2','B 2 m m',
3506        'B 2 c m','B m a 2','B 2 m b','B b m 2','B 2 c b','B b a 2',
3507        'B b m m','B m m b','B b c m','B m a b',
3508        'B m m m','B b m b','B m a m','B m c m','B b a b','B b c b',),
3509    'Immm':('I 2 2 2','I 21 21 21',
3510        'I m m 2','I m 2 m','I 2 m m',
3511        'I b a 2','I 2 c b','I c 2 a',
3512        'I m a 2','I 2 m b','I c 2 m','I m 2 a','I b m 2','I 2 c m',
3513        'I m m m','I b a m','I m c b','I c m a',
3514        'I b c a','I c a b',
3515        'I m m a','I b m m ','I m c m','I m a m','I m m b','I c m m',),
3516    'Fmmm':('F 2 2 2','F m m m', 'F d d d',
3517        'F m m 2','F m 2 m','F 2 m m',
3518        'F d d 2','F d 2 d','F 2 d d',),
3519    'P4/mmm':('P 4','P 41','P 42','P 43','P -4','P 4/m','P 42/m','P 4/n','P 42/n',
3520        'P 4 2 2','P 4 21 2','P 41 2 2','P 41 21 2','P 42 2 2',
3521        'P 42 21 2','P 43 2 2','P 43 21 2','P 4 m m','P 4 b m','P 42 c m',
3522        'P 42 n m','P 4 c c','P 4 n c','P 42 m c','P 42 b c','P -4 2 m',
3523        'P -4 2 c','P -4 21 m','P -4 21 c','P -4 m 2','P -4 c 2','P -4 b 2',
3524        'P -4 n 2','P 4/m m m','P 4/m c c','P 4/n b m','P 4/n n c','P 4/m b m',
3525        'P 4/m n c','P 4/n m m','P 4/n c c','P 42/m m c','P 42/m c m',
3526        'P 42/n b c','P 42/n n m','P 42/m b c','P 42/m n m','P 42/n m c',
3527        'P 42/n c m',),
3528    'I4/mmm':('I 4','I 41','I -4','I 4/m','I 41/a','I 4 2 2','I 41 2 2','I 4 m m',
3529        'I 4 c m','I 41 m d','I 41 c d',
3530        'I -4 m 2','I -4 c 2','I -4 2 m','I -4 2 d','I 4/m m m','I 4/m c m',
3531        'I 41/a m d','I 41/a c d'),
3532    'R3-H':('R 3','R -3','R 3 2','R 3 m','R 3 c','R -3 m','R -3 c',),
3533    'P6/mmm': ('P 3','P 31','P 32','P -3','P 3 1 2','P 3 2 1','P 31 1 2',
3534        'P 31 2 1','P 32 1 2','P 32 2 1', 'P 3 m 1','P 3 1 m','P 3 c 1',
3535        'P 3 1 c','P -3 1 m','P -3 1 c','P -3 m 1','P -3 c 1','P 6','P 61',
3536        'P 65','P 62','P 64','P 63','P -6','P 6/m','P 63/m','P 6 2 2',
3537        'P 61 2 2','P 65 2 2','P 62 2 2','P 64 2 2','P 63 2 2','P 6 m m',
3538        'P 6 c c','P 63 c m','P 63 m c','P -6 m 2','P -6 c 2','P -6 2 m',
3539        'P -6 2 c','P 6/m m m','P 6/m c c','P 63/m c m','P 63/m m c',),
3540    'Pm3m': ('P 2 3','P 21 3','P m 3','P m -3','P n 3','P n -3','P a 3','P a -3',
3541        'P 4 3 2','P 42 3 2','P 43 3 2','P 41 3 2','P -4 3 m','P -4 3 n',
3542        'P m 3 m','P m -3 m','P n 3 n','P n -3 n',
3543        'P m 3 n','P m -3 n','P n 3 m','P n -3 m',),
3544    'Im3m':('I 2 3','I 21 3','I m 3','I m -3','I a 3','I a -3', 'I 4 3 2','I 41 3 2',
3545        'I -4 3 m', 'I -4 3 d','I m -3 m','I m 3 m','I a 3 d','I a -3 d','I n 3 n','I n -3 n'),
3546    'Fm3m':('F 2 3','F m 3','F m -3','F d 3','F d -3',
3547        'F 4 3 2','F 41 3 2','F -4 3 m','F -4 3 c',
3548        'F m 3 m','F m -3 m','F m 3 c','F m -3 c',
3549        'F d 3 m','F d -3 m','F d 3 c','F d -3 c',),
3550}
3551sgequiv_2002_orthorhombic = {}
3552''' A dictionary of orthorhombic space groups that were renamed in the 2002 Volume A,
3553 along with the pre-2002 name. The e designates a double glide-plane
3554'''
3555sgequiv_2002_orthorhombic = {
3556        'AEM2':'A b m 2','B2EM':'B 2 c m','CM2E':'C m 2 a',
3557        'AE2M':'A c 2 m','BME2':'B m a 2','C2ME':'C