source: trunk/GSASIIspc.py @ 3434

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

working magnetic extinction check routine w. reference & comments - works

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 156.8 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 22:48:33 +0000 (Wed, 13 Jun 2018) $
12# $Author: vondreele $
13# $Revision: 3434 $
14# $URL: trunk/GSASIIspc.py $
15# $Id: GSASIIspc.py 3434 2018-06-13 22:48:33Z 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: 3434 $")
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   
1913def checkSSLaue(HKL,SGData,SSGData):
1914    #Laue check here - Toss HKL if outside unique Laue part
1915    h,k,l,m = HKL
1916    if SGData['SGLaue'] == '2/m':
1917        if SGData['SGUniq'] == 'a':
1918            if 'a' in SSGData['modSymb'] and h == 0 and m < 0:
1919                return False
1920            elif 'b' in SSGData['modSymb'] and k == 0 and l ==0 and m < 0:
1921                return False
1922            else:
1923                return True
1924        elif SGData['SGUniq'] == 'b':
1925            if 'b' in SSGData['modSymb'] and k == 0 and m < 0:
1926                return False
1927            elif 'a' in SSGData['modSymb'] and h == 0 and l ==0 and m < 0:
1928                return False
1929            else:
1930                return True
1931        elif SGData['SGUniq'] == 'c':
1932            if 'g' in SSGData['modSymb'] and l == 0 and m < 0:
1933                return False
1934            elif 'a' in SSGData['modSymb'] and h == 0 and k ==0 and m < 0:
1935                return False
1936            else:
1937                return True
1938    elif SGData['SGLaue'] == 'mmm':
1939        if 'a' in SSGData['modSymb']:
1940            if h == 0 and m < 0:
1941                return False
1942            else:
1943                return True
1944        elif 'b' in SSGData['modSymb']:
1945            if k == 0 and m < 0:
1946                return False
1947            else:
1948                return True
1949        elif 'g' in SSGData['modSymb']:
1950            if l == 0 and m < 0:
1951                return False
1952            else:
1953                return True
1954    else:   #tetragonal, trigonal, hexagonal (& triclinic?)
1955        if l == 0 and m < 0:
1956            return False
1957        else:
1958            return True
1959       
1960def checkHKLextc(HKL,SGData):
1961    '''
1962    Checks if reflection extinct
1963
1964    :param HKL:  [h,k,l]
1965    :param SGData: space group data obtained from SpcGroup
1966    :returns: False if  extinct; True if allowed
1967
1968    '''
1969    Ops = SGData['SGOps']
1970    OpM = np.array([op[0] for op in Ops])
1971    OpT = np.array([op[1] for op in Ops])
1972    HKLS = np.array([HKL,-HKL])     #Freidel's Law
1973    DHKL = np.reshape(np.inner(HKLS,OpM)-HKL,(-1,3))
1974    PHKL = np.reshape(np.inner(HKLS,OpT),(-1,))
1975    for dhkl,phkl in zip(DHKL,PHKL)[1:]:    #skip identity
1976        if dhkl.any():
1977            continue
1978        else:
1979            if phkl%1.:
1980                return False
1981    return True
1982
1983def checkMagextc(HKL,SGData):
1984    '''
1985    Checks if reflection magnetically extinct;
1986    uses algorthm from Gallego, et al., J. Appl. Cryst. 45, 1236-1247 (2012)
1987
1988    :param HKL:  [h,k,l]
1989    :param SGData: space group data obtained from SpcGroup; must have magnetic symmetry SpnFlp data
1990    :returns: False if magnetically extinct; True if allowed
1991
1992    '''
1993    Ops = SGData['SGOps']
1994    Ncen = len(SGData['SGCen'])
1995    OpM = np.array([op[0] for op in Ops])
1996    OpT = np.array([op[1] for op in Ops])
1997    if SGData['SGInv']:
1998        OpM = np.vstack((OpM,-OpM))
1999        OpT = np.vstack((OpT,-OpT))%1.
2000    OpM = np.reshape(np.array(list(OpM)*Ncen),(-1,3,3))
2001    OpT = np.reshape(np.array([OpT+cen for cen in SGData['SGCen']]),(-1,3))
2002    Spn = SGData['SpnFlp'][:len(OpM)]
2003    Mag = np.array([nl.det(opm) for opm in OpM])*Spn
2004    DHKL = np.reshape(np.inner(HKL,OpM),(-1,3))
2005    PHKL = np.reshape(np.cos(twopi*np.inner(HKL,OpT))*Mag,(-1,))[:,nxs,nxs]*OpM     #compute T(R,theta) eq(7)
2006    Ftest = np.random.rand(3)       #random magnetic moment
2007    Psum = np.zeros(3)
2008    nsum = 0.
2009    nA = 0
2010    for dhkl,phkl in zip(DHKL,PHKL):
2011        if not np.allclose(dhkl,HKL):           #test for eq(5)
2012            continue
2013        else:
2014            nA += 1
2015            nsum += np.trace(phkl)          #eq(8)
2016            pterm = np.inner(Ftest,phkl)    #eq(9)
2017            Psum += pterm
2018    if nsum/nA > 1.:        #only need to look at nA=1 frok eq(8)
2019        return True
2020    if np.allclose(Psum,np.zeros(3)):
2021        return False
2022    else:
2023        if np.inner(HKL,Psum):
2024            return False
2025        return True
2026   
2027def checkSSextc(HKL,SSGData):
2028    Ops = SSGData['SSGOps']
2029    OpM = np.array([op[0] for op in Ops])
2030    OpT = np.array([op[1] for op in Ops])
2031    HKLS = np.array([HKL,-HKL])     #Freidel's Law
2032    DHKL = np.reshape(np.inner(HKLS,OpM)-HKL,(-1,4))
2033    PHKL = np.reshape(np.inner(HKLS,OpT),(-1,))
2034    for dhkl,phkl in zip(DHKL,PHKL)[1:]:    #skip identity
2035        if dhkl.any():
2036            continue
2037        else:
2038            if phkl%1.:
2039                return False
2040    return True
2041   
2042################################################################################
2043#### Site symmetry tables
2044################################################################################
2045     
2046OprName = {
2047    '-6643':       ['-1',1],'6479' :    ['2(z)',2],'-6479':     ['m(z)',3],
2048    '6481' :     ['m(y)',4],'-6481':    ['2(y)',5],'6641' :     ['m(x)',6],
2049    '-6641':     ['2(x)',7],'6591' :  ['m(+-0)',8],'-6591':   ['2(+-0)',9],
2050    '6531' :  ['m(110)',10],'-6531': ['2(110)',11],'6537' :    ['4(z)',12],
2051    '-6537':   ['-4(z)',13],'975'  : ['3(111)',14],'6456' :       ['3',15],
2052    '-489' :  ['3(+--)',16],'483'  : ['3(-+-)',17],'-969' :  ['3(--+)',18],
2053    '819'  :  ['m(+0-)',19],'-819' : ['2(+0-)',20],'2431' :  ['m(0+-)',21],
2054    '-2431':  ['2(0+-)',22],'-657' :  ['m(xz)',23],'657'  :   ['2(xz)',24],
2055    '1943' :   ['-4(x)',25],'-1943':   ['4(x)',26],'-2429':   ['m(yz)',27],
2056    '2429' :   ['2(yz)',28],'639'  :  ['-4(y)',29],'-639' :    ['4(y)',30],
2057    '-6484':   ['2(010)',4],'6484' :  ['m(010)',5],'-6668':   ['2(100)',6],
2058    '6668' :   ['m(100)',7],'-6454': ['2(120)',18],'6454' :  ['m(120)',19],
2059    '-6638':  ['2(210)',20],'6638' : ['m(210)',21],   #search in SytSym ends at m(210)
2060    '2223' : ['3(+++)2',39],
2061    '6538' :   ['6(z)1',40],'-2169':['3(--+)2',41],'2151' : ['3(+--)2',42],
2062    '2205' :['-3(-+-)2',43],'-2205':[' (-+-)2',44],'489'  :['-3(+--)1',45],
2063    '801'  :   ['4(y)1',46],'1945' :  ['4(x)3',47],'-6585': ['-4(z)3 ',48],
2064    '6585' :   ['4(z)3',49],'6584' :  ['3(z)2',50],'6666' :  ['6(z)5 ',51],
2065    '6643' :       ['1',52],'-801' : ['-4(y)1',53],'-1945': ['-4(x)3 ',54],
2066    '-6666':  ['-6(z)5',55],'-6538': ['-6(z)1',56],'-2223':['-3(+++)2',57],
2067    '-975' :['-3(+++)1',58],'-6456': ['-3(z)1',59],'-483' :['-3(-+-)1',60],
2068    '969'  :['-3(--+)1',61],'-6584': ['-3(z)2',62],'2169' :['-3(--+)2',63],
2069    '-2151':['-3(+--)2',64],   }                               
2070
2071KNsym = {
2072    '0'         :'    1   ','1'         :'   -1   ','64'        :'    2(x)','32'        :'    m(x)',
2073    '97'        :'  2/m(x)','16'        :'    2(y)','8'         :'    m(y)','25'        :'  2/m(y)',
2074    '2'         :'    2(z)','4'         :'    m(z)','7'         :'  2/m(z)','134217728' :'   2(yz)',
2075    '67108864'  :'   m(yz)','201326593' :' 2/m(yz)','2097152'   :'  2(0+-)','1048576'   :'  m(0+-)',
2076    '3145729'   :'2/m(0+-)','8388608'   :'   2(xz)','4194304'   :'   m(xz)','12582913'  :' 2/m(xz)',
2077    '524288'    :'  2(+0-)','262144'    :'  m(+0-)','796433'    :'2/m(+0-)','1024'      :'   2(xy)',
2078    '512'       :'   m(xy)','1537'      :' 2/m(xy)','256'       :'  2(+-0)','128'       :'  m(+-0)',
2079    '385'       :'2/m(+-0)','76'        :'  mm2(x)','52'        :'  mm2(y)','42'        :'  mm2(z)',
2080    '135266336' :' mm2(yz)','69206048'  :'mm2(0+-)','8650760'   :' mm2(xz)','4718600'   :'mm2(+0-)',
2081    '1156'      :' mm2(xy)','772'       :'mm2(+-0)','82'        :'  222   ','136314944' :'  222(x)',
2082    '8912912'   :'  222(y)','1282'      :'  222(z)','127'       :'  mmm   ','204472417' :'  mmm(x)',
2083    '13369369'  :'  mmm(y)','1927'      :'  mmm(z)','33554496'  :'  4(x)','16777280'  :' -4(x)',
2084    '50331745'  :'4/m(x)'  ,'169869394' :'422(x)','84934738'  :'-42m(x)','101711948' :'4mm(x)',
2085    '254804095' :'4/mmm(x)','536870928 ':'  4(y)','268435472' :' -4(y)','805306393' :'4/m(y)',
2086    '545783890' :'422(y)','272891986' :'-42m(y)','541327412' :'4mm(y)','818675839' :'4/mmm(y)',
2087    '2050'      :'  4(z)','4098'      :' -4(z)','6151'      :'4/m(z)','3410'      :'422(z)',
2088    '4818'      :'-42m(z)','2730'      :'4mm(z)','8191'      :'4/mmm(z)','8192'      :'  3(111)',
2089    '8193'      :' -3(111)','2629888'   :' 32(111)','1319040'   :' 3m(111)','3940737'   :'-3m(111)',
2090    '32768'     :'  3(+--)','32769'     :' -3(+--)','10519552'  :' 32(+--)','5276160'   :' 3m(+--)',
2091    '15762945'  :'-3m(+--)','65536'     :'  3(-+-)','65537'     :' -3(-+-)','134808576' :' 32(-+-)',
2092    '67437056'  :' 3m(-+-)','202180097' :'-3m(-+-)','131072'    :'  3(--+)','131073'    :' -3(--+)',
2093    '142737664' :' 32(--+)','71434368'  :' 3m(--+)','214040961' :'-3m(--+)','237650'    :'   23   ',
2094    '237695'    :'   m3   ','715894098' :'   432  ','358068946' :'  -43m  ','1073725439':'   m3m  ',
2095    '68157504'  :' mm2(d100)','4456464'   :' mm2(d010)','642'       :' mm2(d001)','153092172' :'-4m2(x)',
2096    '277348404' :'-4m2(y)','5418'      :'-4m2(z)','1075726335':'  6/mmm ','1074414420':'-6m2(100)',
2097    '1075070124':'-6m2(120)','1075069650':'   6mm  ','1074414890':'   622  ','1073758215':'   6/m  ',
2098    '1073758212':'   -6   ','1073758210':'    6   ','1073759865':'-3m(100)','1075724673':'-3m(120)',
2099    '1073758800':' 3m(100)','1075069056':' 3m(120)','1073759272':' 32(100)','1074413824':' 32(120)',
2100    '1073758209':'   -3   ','1073758208':'    3   ','1074135143':'mmm(100)','1075314719':'mmm(010)',
2101    '1073743751':'mmm(110)','1074004034':' mm2(z100)','1074790418':' mm2(z010)','1073742466':' mm2(z110)',
2102    '1074004004':'mm2(100)','1074790412':'mm2(010)','1073742980':'mm2(110)','1073872964':'mm2(120)',
2103    '1074266132':'mm2(210)','1073742596':'mm2(+-0)','1073872930':'222(100)','1074266122':'222(010)',
2104    '1073743106':'222(110)','1073741831':'2/m(001)','1073741921':'2/m(100)','1073741849':'2/m(010)',
2105    '1073743361':'2/m(110)','1074135041':'2/m(120)','1075314689':'2/m(210)','1073742209':'2/m(+-0)',
2106    '1073741828':' m(001) ','1073741888':' m(100) ','1073741840':' m(010) ','1073742336':' m(110) ',
2107    '1074003968':' m(120) ','1074790400':' m(210) ','1073741952':' m(+-0) ','1073741826':' 2(001) ',
2108    '1073741856':' 2(100) ','1073741832':' 2(010) ','1073742848':' 2(110) ','1073872896':' 2(120) ',
2109    '1074266112':' 2(210) ','1073742080':' 2(+-0) ','1073741825':'   -1   ',
2110    }
2111
2112NXUPQsym = {
2113    '1'        :(28,29,28,28),'-1'       :( 1,29,28, 0),'2(x)'     :(12,18,12,25),'m(x)'     :(25,18,12,25),
2114    '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),
2115    '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),
2116    '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),
2117    '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),
2118    '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),
2119    '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),
2120    '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),
2121    'mm2(yz)'  :(10,13, 0,-1),'mm2(0+-)' :(11,13, 0,-1),'mm2(xz)'  :( 8,12, 0,-1),'mm2(+0-)' :( 9,12, 0,-1),
2122    'mm2(xy)'  :( 6,11, 0,-1),'mm2(+-0)' :( 7,11, 0,-1),'222'      :( 1,10, 0,-1),'222(x)'   :( 1,13, 0,-1),
2123    '222(y)'   :( 1,12, 0,-1),'222(z)'   :( 1,11, 0,-1),'mmm'      :( 1,10, 0,-1),'mmm(x)'   :( 1,13, 0,-1),
2124    'mmm(y)'   :( 1,12, 0,-1),'mmm(z)'   :( 1,11, 0,-1),'4(x)'     :(12, 4,12, 0),'-4(x)'    :( 1, 4,12, 0),
2125    '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),
2126    '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),
2127    '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,),
2128    '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),
2129    '-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),
2130    '-3(111)'  :( 1, 5, 2, 0),'32(111)'  :( 1, 5, 0, 2),'3m(111)'  :( 2, 5, 0, 2),'-3m(111)' :( 1, 5, 0,-1),
2131    '3(+--)'   :( 5, 8, 5, 0),'-3(+--)'  :( 1, 8, 5, 0),'32(+--)'  :( 1, 8, 0, 5),'3m(+--)'  :( 5, 8, 0, 5),
2132    '-3m(+--)' :( 1, 8, 0,-1),'3(-+-)'   :( 4, 7, 4, 0),'-3(-+-)'  :( 1, 7, 4, 0),'32(-+-)'  :( 1, 7, 0, 4),
2133    '3m(-+-)'  :( 4, 7, 0, 4),'-3m(-+-)' :( 1, 7, 0,-1),'3(--+)'   :( 3, 6, 3, 0),'-3(--+)'  :( 1, 6, 3, 0),
2134    '32(--+)'  :( 1, 6, 0, 3),'3m(--+)'  :( 3, 6, 0, 3),'-3m(--+)' :( 1, 6, 0,-1),'23'       :( 1, 1, 0, 0),
2135    'm3'       :( 1, 1, 0, 0),'432'      :( 1, 1, 0, 0),'-43m'     :( 1, 1, 0, 0),'m3m'      :( 1, 1, 0, 0),
2136    'mm2(d100)':(12,13, 0,-1),'mm2(d010)':(13,12, 0,-1),'mm2(d001)':(14,11, 0,-1),'-4m2(x)'  :( 1, 4, 0,-1),
2137    '-4m2(y)'  :( 1, 3, 0,-1),'-4m2(z)'  :( 1, 2, 0,-1),'6/mmm'    :( 1, 9, 0,-1),'-6m2(100)':( 1, 9, 0,-1),
2138    '-6m2(120)':( 1, 9, 0,-1),'6mm'      :(14, 9, 0,-1),'622'      :( 1, 9, 0,-1),'6/m'      :( 1, 9,14,-1),
2139    '-6'       :( 1, 9,14, 0),'6'        :(14, 9,14, 0),'-3m(100)' :( 1, 9, 0,-1),'-3m(120)' :( 1, 9, 0,-1),
2140    '3m(100)'  :(14, 9, 0,14),'3m(120)'  :(14, 9, 0,14),'32(100)'  :( 1, 9, 0,14),'32(120)'  :( 1, 9, 0,14),
2141    '-3'       :( 1, 9,14, 0),'3'        :(14, 9,14, 0),'mmm(100)' :( 1,14, 0,-1),'mmm(010)' :( 1,15, 0,-1),
2142    'mmm(110)' :( 1,11, 0,-1),'mm2(z100)':(14,14, 0,-1),'mm2(z010)':(14,15, 0,-1),'mm2(z110)':(14,11, 0,-1),
2143    'mm2(100)' :(12,14, 0,-1),'mm2(010)' :(13,15, 0,-1),'mm2(110)' :( 6,11, 0,-1),'mm2(120)' :(15,14, 0,-1),
2144    'mm2(210)' :(16,15, 0,-1),'mm2(+-0)' :( 7,11, 0,-1),'222(100)' :( 1,14, 0,-1),'222(010)' :( 1,15, 0,-1),
2145    '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),
2146    '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),
2147    'm(001)'   :(23,16,14,23),'m(100)'   :(26,25,12,26),'m(010)'   :(27,28,13,27),'m(110)'   :(18,19, 6,18),
2148    'm(120)'   :(24,27,15,24),'m(210)'   :(25,26,16,25),'m(+-0)'   :(17,20, 7,17),'2(001)'   :(14,16,14,23),
2149    '2(100)'   :(12,25,12,26),'2(010)'   :(13,28,13,27),'2(110)'   :( 6,19, 6,18),'2(120)'   :(15,27,15,24),
2150    '2(210)'   :(16,26,16,25),'2(+-0)'   :( 7,20, 7,17),'-1'       :( 1,29,28, 0)
2151    }
2152       
2153CSxinel = [[],      # 0th empty - indices are Fortran style
2154    [[0,0,0],[ 0.0, 0.0, 0.0]],      #1  0  0  0
2155    [[1,1,1],[ 1.0, 1.0, 1.0]],      #2  X  X  X
2156    [[1,1,1],[ 1.0, 1.0,-1.0]],      #3  X  X -X
2157    [[1,1,1],[ 1.0,-1.0, 1.0]],      #4  X -X  X
2158    [[1,1,1],[ 1.0,-1.0,-1.0]],      #5 -X  X  X
2159    [[1,1,0],[ 1.0, 1.0, 0.0]],      #6  X  X  0
2160    [[1,1,0],[ 1.0,-1.0, 0.0]],      #7  X -X  0
2161    [[1,0,1],[ 1.0, 0.0, 1.0]],      #8  X  0  X
2162    [[1,0,1],[ 1.0, 0.0,-1.0]],      #9  X  0 -X
2163    [[0,1,1],[ 0.0, 1.0, 1.0]],      #10  0  Y  Y
2164    [[0,1,1],[ 0.0, 1.0,-1.0]],      #11 0  Y -Y
2165    [[1,0,0],[ 1.0, 0.0, 0.0]],      #12  X  0  0
2166    [[0,1,0],[ 0.0, 1.0, 0.0]],      #13  0  Y  0
2167    [[0,0,1],[ 0.0, 0.0, 1.0]],      #14  0  0  Z
2168    [[1,1,0],[ 1.0, 2.0, 0.0]],      #15  X 2X  0
2169    [[1,1,0],[ 2.0, 1.0, 0.0]],      #16 2X  X  0
2170    [[1,1,2],[ 1.0, 1.0, 1.0]],      #17  X  X  Z
2171    [[1,1,2],[ 1.0,-1.0, 1.0]],      #18  X -X  Z
2172    [[1,2,1],[ 1.0, 1.0, 1.0]],      #19  X  Y  X
2173    [[1,2,1],[ 1.0, 1.0,-1.0]],      #20  X  Y -X
2174    [[1,2,2],[ 1.0, 1.0, 1.0]],      #21  X  Y  Y
2175    [[1,2,2],[ 1.0, 1.0,-1.0]],      #22  X  Y -Y
2176    [[1,2,0],[ 1.0, 1.0, 0.0]],      #23  X  Y  0
2177    [[1,0,2],[ 1.0, 0.0, 1.0]],      #24  X  0  Z
2178    [[0,1,2],[ 0.0, 1.0, 1.0]],      #25  0  Y  Z
2179    [[1,1,2],[ 1.0, 2.0, 1.0]],      #26  X 2X  Z
2180    [[1,1,2],[ 2.0, 1.0, 1.0]],      #27 2X  X  Z
2181    [[1,2,3],[ 1.0, 1.0, 1.0]],      #28  X  Y  Z
2182    ]
2183
2184CSuinel = [[],      # 0th empty - indices are Fortran style
2185    [[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
2186    [[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
2187    [[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
2188    [[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
2189    [[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
2190    [[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
2191    [[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
2192    [[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
2193    [[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
2194    [[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
2195    [[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
2196    [[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
2197    [[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
2198    [[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
2199    [[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
2200    [[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
2201    [[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
2202    [[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
2203    [[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
2204    [[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
2205    [[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
2206    [[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
2207    [[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
2208    [[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
2209    [[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
2210    [[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
2211    [[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
2212    [[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
2213    [[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
2214    ]
2215   
2216################################################################################
2217#### Site symmetry routines
2218################################################################################
2219   
2220def GetOprPtrName(key):
2221    'Needs a doc string'
2222    try:
2223        oprName = OprName[key][0]
2224    except KeyError:
2225        return key
2226    return oprName.replace('(','').replace(')','')
2227
2228def GetOprPtrNumber(key):
2229    'Needs a doc string'
2230    try:
2231        return OprName[key][1]
2232    except KeyError:
2233        return key
2234
2235def GetOprName(key):
2236    'Needs a doc string'
2237    return OprName[key][0]
2238
2239def GetKNsym(key):
2240    'Needs a doc string'
2241    try:
2242        return KNsym[key].strip()
2243    except KeyError:
2244        return 'sp'
2245
2246def GetNXUPQsym(siteSym):
2247    '''       
2248    The codes XUPQ are for lookup of symmetry constraints for position(X), thermal parm(U) & magnetic moments (P & Q)
2249    '''
2250    return NXUPQsym[siteSym]
2251
2252def GetCSxinel(siteSym): 
2253    "returns Xyz terms, multipliers, GUI flags"
2254    indx = GetNXUPQsym(siteSym.strip())
2255    return CSxinel[indx[0]]
2256   
2257def GetCSuinel(siteSym):
2258    "returns Uij terms, multipliers, GUI flags & Uiso2Uij multipliers"
2259    indx = GetNXUPQsym(siteSym.strip())
2260    return CSuinel[indx[1]]
2261   
2262def GetCSpqinel(SpnFlp,dupDir): 
2263    "returns Mxyz terms, multipliers, GUI flags"
2264    CSI = [[1,2,3],[1.0,1.0,1.0]]
2265    for sopr in dupDir:
2266#        print (sopr,dupDir[sopr])
2267        opr = sopr.replace("'",'')
2268        indx = GetNXUPQsym(opr)
2269        if SpnFlp[dupDir[sopr]] > 0:
2270            csi = CSxinel[indx[2]]  #P
2271        else:
2272            csi = CSxinel[indx[3]]  #Q
2273#        print(opr,indx,csi,CSI)
2274        if not len(csi):
2275            return [[0,0,0],[0.,0.,0.]]
2276        for kcs in [0,1,2]:
2277            if csi[0][kcs] == 0 and CSI[0][kcs] != 0:
2278                jcs = CSI[0][kcs]
2279                for ics in [0,1,2]:
2280                    if CSI[0][ics] == jcs:
2281                        CSI[0][ics] = 0
2282                        CSI[1][ics] = 0.
2283                    elif CSI[0][ics] > jcs:
2284                        CSI[0][ics] = CSI[0][ics]-1
2285            elif (CSI[0][kcs] == csi[0][kcs]) and (CSI[1][kcs] != csi[1][kcs]):
2286                CSI[1][kcs] = csi[1][kcs]
2287            elif CSI[0][kcs] >= csi[0][kcs]:
2288                CSI[0][kcs] = min(CSI[0][kcs],csi[0][kcs])
2289                if CSI[1][kcs] != csi[1][kcs]:
2290                    if CSI[1][kcs] == 1.:
2291                        CSI[1][kcs] = csi[1][kcs]
2292#        print(CSI)
2293    return CSI
2294   
2295def getTauT(tau,sop,ssop,XYZ,wave=np.zeros(3)):
2296    phase = np.sum(XYZ*wave)
2297    ssopinv = nl.inv(ssop[0])
2298    mst = ssopinv[3][:3]
2299    epsinv = ssopinv[3][3]
2300    sdet = nl.det(sop[0])
2301    ssdet = nl.det(ssop[0])
2302    dtau = mst*(XYZ-sop[1])-epsinv*ssop[1][3]
2303    dT = 1.0
2304    if np.any(dtau%.5):
2305        sumdtau = np.sum(dtau%.5)
2306        dT = 0.
2307        if np.abs(sumdtau-.5) > 1.e-4:
2308            dT = np.tan(np.pi*sumdtau)
2309    tauT = np.inner(mst,XYZ-sop[1])+epsinv*(tau-ssop[1][3]+phase)
2310    return sdet,ssdet,dtau,dT,tauT
2311   
2312def OpsfromStringOps(A,SGData,SSGData):
2313    SGOps = SGData['SGOps']
2314    SSGOps = SSGData['SSGOps']
2315    Ax = A.split('+')
2316    Ax[0] = int(Ax[0])
2317    iC = 1
2318    if Ax[0] < 0:
2319        iC = -1
2320    Ax[0] = abs(Ax[0])
2321    nA = Ax[0]%100-1
2322    nC = Ax[0]//100
2323    unit = [0,0,0]
2324    if len(Ax) > 1:
2325        unit = eval('['+Ax[1]+']')
2326    return SGOps[nA],SSGOps[nA],iC,SGData['SGCen'][nC],unit
2327   
2328def GetSSfxuinel(waveType,Stype,nH,XYZ,SGData,SSGData,debug=False):
2329   
2330    def orderParms(CSI):
2331        parms = [0,]
2332        for csi in CSI:
2333            for i in [0,1,2]:
2334                if csi[i] not in parms:
2335                    parms.append(csi[i])
2336        for csi in CSI:
2337            for i in [0,1,2]:
2338                csi[i] = parms.index(csi[i])
2339        return CSI
2340       
2341    def fracCrenel(tau,Toff,Twid):
2342        Tau = (tau-Toff[:,nxs])%1.
2343        A = np.where(Tau<Twid[:,nxs],1.,0.)
2344        return A
2345       
2346    def fracFourier(tau,nH,fsin,fcos):
2347        SA = np.sin(2.*nH*np.pi*tau)
2348        CB = np.cos(2.*nH*np.pi*tau)
2349        A = SA[nxs,nxs,:]*fsin[:,:,nxs]
2350        B = CB[nxs,nxs,:]*fcos[:,:,nxs]
2351        return A+B
2352       
2353    def posFourier(tau,nH,psin,pcos):
2354        SA = np.sin(2*nH*np.pi*tau)
2355        CB = np.cos(2*nH*np.pi*tau)
2356        A = SA[nxs,nxs,:]*psin[:,:,nxs]
2357        B = CB[nxs,nxs,:]*pcos[:,:,nxs]
2358        return A+B   
2359
2360    def posSawtooth(tau,Toff,slopes):
2361        Tau = (tau-Toff)%1.
2362        A = slopes[:,nxs]*Tau
2363        return A
2364   
2365    def posZigZag(tau,Tmm,XYZmax):
2366        DT = Tmm[1]-Tmm[0]
2367        slopeUp = 2.*XYZmax/DT
2368        slopeDn = 2.*XYZmax/(1.-DT)
2369        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])
2370        return A
2371
2372    def posBlock(tau,Tmm,XYZmax):
2373        A = np.array([np.where(Tmm[0] < t <= Tmm[1],XYZmax,-XYZmax) for t in tau])
2374        return A
2375       
2376    def DoFrac():
2377        dF = None
2378        dFTP = None
2379        if siteSym == '1':
2380            CSI = [[1,0],[2,0]],2*[[1.,0.],]
2381        elif siteSym == '-1':
2382            CSI = [[1,0],[0,0]],2*[[1.,0.],]
2383        else:
2384            delt2 = np.eye(2)*0.001
2385            FSC = np.ones(2,dtype='i')
2386            CSI = [np.zeros((2),dtype='i'),np.zeros(2)]
2387            if 'Crenel' in waveType:
2388                dF = np.zeros_like(tau)
2389            else:
2390                dF = fracFourier(tau,nH,delt2[:1],delt2[1:]).squeeze()
2391            dFT = np.zeros_like(dF)
2392            dFTP = []
2393            for i in SdIndx:
2394                sop = Sop[i]
2395                ssop = SSop[i]           
2396                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2397                fsc = np.ones(2,dtype='i')
2398                if 'Crenel' in waveType:
2399                    dFT = np.zeros_like(tau)
2400                    fsc = [1,1]
2401                else:   #Fourier
2402                    dFT = fracFourier(tauT,nH,delt2[:1],delt2[1:]).squeeze()
2403                    dFT = nl.det(sop[0])*dFT
2404                    dFT = dFT[:,np.argsort(tauT)]
2405                    dFT[0] *= ssdet
2406                    dFT[1] *= sdet
2407                    dFTP.append(dFT)
2408               
2409                    if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2410                        fsc = [1,1]
2411                        if dT:
2412                            CSI = [[[1,0],[1,0]],[[1.,0.],[1/dT,0.]]]
2413                        else:
2414                            CSI = [[[1,0],[0,0]],[[1.,0.],[0.,0.]]]
2415                        FSC = np.zeros(2,dtype='i')
2416                        return CSI,dF,dFTP
2417                    else:
2418                        for i in range(2):
2419                            if np.allclose(dF[i,:],dFT[i,:],atol=1.e-6):
2420                                fsc[i] = 1
2421                            else:
2422                                fsc[i] = 0
2423                        FSC &= fsc
2424                        if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,fsc)
2425            n = -1
2426            for i,F in enumerate(FSC):
2427                if F:
2428                    n += 1
2429                    CSI[0][i] = n+1
2430                    CSI[1][i] = 1.0
2431           
2432        return CSI,dF,dFTP
2433       
2434    def DoXYZ():
2435        dX,dXTP = None,None
2436        if siteSym == '1':
2437            CSI = [[1,0,0],[2,0,0],[3,0,0], [4,0,0],[5,0,0],[6,0,0]],6*[[1.,0.,0.],]
2438        elif siteSym == '-1':
2439            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.],]
2440        else:
2441            delt4 = np.ones(4)*0.001
2442            delt5 = np.ones(5)*0.001
2443            delt6 = np.eye(6)*0.001
2444            if 'Fourier' in waveType:
2445                dX = posFourier(tau,nH,delt6[:3],delt6[3:]) #+np.array(XYZ)[:,nxs,nxs]
2446                  #3x6x12 modulated position array (X,Spos,tau)& force positive
2447                CSI = [np.zeros((6,3),dtype='i'),np.zeros((6,3))]
2448            elif waveType == 'Sawtooth':
2449                dX = posSawtooth(tau,delt4[0],delt4[1:])
2450                CSI = [np.array([[1,0,0],[2,0,0],[3,0,0],[4,0,0]]),
2451                    np.array([[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0]])]
2452            elif waveType in ['ZigZag','Block']:
2453                if waveType == 'ZigZag':
2454                    dX = posZigZag(tau,delt5[:2],delt5[2:])
2455                else:
2456                    dX = posBlock(tau,delt5[:2],delt5[2:])
2457                CSI = [np.array([[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0]]),
2458                    np.array([[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0],[1.0,.0,.0]])]
2459            XSC = np.ones(6,dtype='i')
2460            dXTP = []
2461            for i in SdIndx:
2462                sop = Sop[i]
2463                ssop = SSop[i]
2464                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2465                xsc = np.ones(6,dtype='i')
2466                if 'Fourier' in waveType:
2467                    dXT = posFourier(np.sort(tauT),nH,delt6[:3],delt6[3:])   #+np.array(XYZ)[:,nxs,nxs]
2468                elif waveType == 'Sawtooth':
2469                    dXT = posSawtooth(tauT,delt4[0],delt4[1:])+np.array(XYZ)[:,nxs,nxs]
2470                elif waveType == 'ZigZag':
2471                    dXT = posZigZag(tauT,delt5[:2],delt5[2:])+np.array(XYZ)[:,nxs,nxs]
2472                elif waveType == 'Block':
2473                    dXT = posBlock(tauT,delt5[:2],delt5[2:])+np.array(XYZ)[:,nxs,nxs]
2474                dXT = np.inner(sop[0],dXT.T)    # X modulations array(3x6x49) -> array(3x49x6)
2475                dXT = np.swapaxes(dXT,1,2)      # back to array(3x6x49)
2476                dXT[:,:3,:] *= (ssdet*sdet)            # modify the sin component
2477                dXTP.append(dXT)
2478                if waveType == 'Fourier':
2479                    for i in range(3):
2480                        if not np.allclose(dX[i,i,:],dXT[i,i,:]):
2481                            xsc[i] = 0
2482                        if not np.allclose(dX[i,i+3,:],dXT[i,i+3,:]):
2483                            xsc[i+3] = 0
2484                    if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2485                        xsc[3:6] = 0
2486                        CSI = [[[1,0,0],[2,0,0],[3,0,0], [1,0,0],[2,0,0],[3,0,0]],
2487                            [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2488                        if dT:
2489                            if '(x)' in siteSym:
2490                                CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]
2491                                if 'm' in siteSym and len(SdIndx) == 1:
2492                                    CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[1./dT,0.,0.]
2493                            elif '(y)' in siteSym:
2494                                CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2495                                if 'm' in siteSym and len(SdIndx) == 1:
2496                                    CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2497                            elif '(z)' in siteSym:
2498                                CSI[1][3:] = [-dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2499                                if 'm' in siteSym and len(SdIndx) == 1:
2500                                    CSI[1][3:] = [1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2501                        else:
2502                            CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2503                    if '4/mmm' in laue:
2504                        if np.any(dtau%.5) and '1/2' in SSGData['modSymb']:
2505                            if '(xy)' in siteSym:
2506                                CSI[0] = [[1,0,0],[1,0,0],[2,0,0], [1,0,0],[1,0,0],[2,0,0]]
2507                                if dT:
2508                                    CSI[1][3:] = [[1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]]
2509                                else:
2510                                    CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2511                        if '(xy)' in siteSym or '(+-0)' in siteSym:
2512                            mul = 1
2513                            if '(+-0)' in siteSym:
2514                                mul = -1
2515                            if np.allclose(dX[0,0,:],dXT[1,0,:]):
2516                                CSI[0][3:5] = [[11,0,0],[11,0,0]]
2517                                CSI[1][3:5] = [[1.,0,0],[mul,0,0]]
2518                                xsc[3:5] = 0
2519                            if np.allclose(dX[0,3,:],dXT[0,4,:]):
2520                                CSI[0][:2] = [[12,0,0],[12,0,0]]
2521                                CSI[1][:2] = [[1.,0,0],[mul,0,0]]
2522                                xsc[:2] = 0
2523                XSC &= xsc
2524                if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,xsc)
2525            if waveType == 'Fourier':
2526                n = -1
2527                if debug: print (XSC)
2528                for i,X in enumerate(XSC):
2529                    if X:
2530                        n += 1
2531                        CSI[0][i][0] = n+1
2532                        CSI[1][i][0] = 1.0
2533           
2534        return list(CSI),dX,dXTP
2535       
2536    def DoUij():
2537        dU,dUTP = None,None
2538        if siteSym == '1':
2539            CSI = [[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0], 
2540                [7,0,0],[8,0,0],[9,0,0],[10,0,0],[11,0,0],[12,0,0]],12*[[1.,0.,0.],]
2541        elif siteSym == '-1':
2542            CSI = 6*[[0,0,0],]+[[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],   \
2543                6*[[0.,0.,0.],]+[[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]
2544        else:
2545            tau = np.linspace(0,1,49,True)
2546            delt12 = np.eye(12)*0.0001
2547            dU = posFourier(tau,nH,delt12[:6],delt12[6:])                  #Uij modulations - 6x12x12 array
2548            CSI = [np.zeros((12,3),dtype='i'),np.zeros((12,3))]
2549            USC = np.ones(12,dtype='i')
2550            dUTP = []
2551            dtau = 0.
2552            for i in SdIndx:
2553                sop = Sop[i]
2554                ssop = SSop[i]
2555                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2556                usc = np.ones(12,dtype='i')
2557                dUT = posFourier(tauT,nH,delt12[:6],delt12[6:])                  #Uij modulations - 6x12x49 array
2558                dUijT = np.rollaxis(np.rollaxis(np.array(Uij2U(dUT)),3),3)    #convert dUT to 12x49x3x3
2559                dUijT = np.rollaxis(np.inner(np.inner(sop[0],dUijT),sop[0].T),3) #transform by sop - 3x3x12x49
2560                dUT = np.array(U2Uij(dUijT))    #convert to 6x12x49
2561                dUT = dUT[:,:,np.argsort(tauT)]
2562                dUT[:,:6,:] *=(ssdet*sdet)
2563                dUTP.append(dUT)
2564                if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2565                    if dT:
2566                        CSI = [[[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0], 
2567                        [1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],
2568                        [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.],
2569                        [1./dT,0.,0.],[1./dT,0.,0.],[1./dT,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2570                    else:
2571                        CSI = [[[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0], 
2572                        [1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],
2573                        [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.],
2574                        [0.,0.,0.],[0.,0.,0.],[0.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2575                    if 'mm2(x)' in siteSym and dT:
2576                        CSI[1][9:] = [0.,0.,0.],[-dT,0.,0.],[0.,0.,0.]
2577                        USC = [1,1,1,0,1,0,1,1,1,0,1,0]
2578                    elif '(xy)' in siteSym and dT:
2579                        CSI[0] = [[1,0,0],[1,0,0],[2,0,0],[3,0,0],[4,0,0],[4,0,0],
2580                            [1,0,0],[1,0,0],[2,0,0],[3,0,0],[4,0,0],[4,0,0]]
2581                        CSI[1][9:] = [[1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]]
2582                        USC = [1,1,1,1,1,1,1,1,1,1,1,1]                             
2583                    elif '(x)' in siteSym and dT:
2584                        CSI[1][9:] = [-dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2585                    elif '(y)' in siteSym and dT:
2586                        CSI[1][9:] = [-dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2587                    elif '(z)' in siteSym and dT:
2588                        CSI[1][9:] = [1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]
2589                    for i in range(6):
2590                        if not USC[i]:
2591                            CSI[0][i] = [0,0,0]
2592                            CSI[1][i] = [0.,0.,0.]
2593                            CSI[0][i+6] = [0,0,0]
2594                            CSI[1][i+6] = [0.,0.,0.]
2595                else:                       
2596                    for i in range(6):
2597                        if not np.allclose(dU[i,i,:],dUT[i,i,:]):  #sin part
2598                            usc[i] = 0
2599                        if not np.allclose(dU[i,i+6,:],dUT[i,i+6,:]):   #cos part
2600                            usc[i+6] = 0
2601                    if np.any(dUT[1,0,:]):
2602                        if '4/m' in siteSym:
2603                            CSI[0][6:8] = [[12,0,0],[12,0,0]]
2604                            if ssop[1][3]:
2605                                CSI[1][6:8] = [[1.,0.,0.],[-1.,0.,0.]]
2606                                usc[9] = 1
2607                            else:
2608                                CSI[1][6:8] = [[1.,0.,0.],[1.,0.,0.]]
2609                                usc[9] = 0
2610                        elif '4' in siteSym:
2611                            CSI[0][6:8] = [[12,0,0],[12,0,0]]
2612                            CSI[0][:2] = [[11,0,0],[11,0,0]]
2613                            if ssop[1][3]:
2614                                CSI[1][:2] = [[1.,0.,0.],[-1.,0.,0.]]
2615                                CSI[1][6:8] = [[1.,0.,0.],[-1.,0.,0.]]
2616                                usc[2] = 0
2617                                usc[8] = 0
2618                                usc[3] = 1
2619                                usc[9] = 1
2620                            else:
2621                                CSI[1][:2] = [[1.,0.,0.],[1.,0.,0.]]
2622                                CSI[1][6:8] = [[1.,0.,0.],[1.,0.,0.]]
2623                                usc[2] = 1
2624                                usc[8] = 1
2625                                usc[3] = 0               
2626                                usc[9] = 0
2627                        elif 'xy' in siteSym or '+-0' in siteSym:
2628                            if np.allclose(dU[0,0,:],dUT[0,1,:]*sdet):
2629                                CSI[0][4:6] = [[12,0,0],[12,0,0]]
2630                                CSI[0][6:8] = [[11,0,0],[11,0,0]]
2631                                CSI[1][4:6] = [[1.,0.,0.],[sdet,0.,0.]]
2632                                CSI[1][6:8] = [[1.,0.,0.],[sdet,0.,0.]]
2633                                usc[4:6] = 0
2634                                usc[6:8] = 0
2635                           
2636                    if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,usc)
2637                USC &= usc
2638            if debug: print (USC)
2639            if not np.any(dtau%.5):
2640                n = -1
2641                for i,U in enumerate(USC):
2642                    if U:
2643                        n += 1
2644                        CSI[0][i][0] = n+1
2645                        CSI[1][i][0] = 1.0
2646   
2647        return list(CSI),dU,dUTP
2648   
2649    def DoMag():
2650        CSI = [[1,0,0],[2,0,0],[3,0,0], [4,0,0],[5,0,0],[6,0,0]],6*[[1.,0.,0.],]
2651        if siteSym == '1':
2652            CSI = [[1,0,0],[2,0,0],[3,0,0],[4,0,0],[5,0,0],[6,0,0]],6*[[1.,0.,0.],]
2653        elif siteSym in ['-1','mmm','2/m(x)','2/m(y)','2/m(z)','4/mmm001']:
2654            CSI = 3*[[0,0,0],]+[[1,0,0],[2,0,0],[3,0,0]],3*[[0.,0.,0.],]+3*[[1.,0.,0.],]
2655        else:
2656            delt6 = np.eye(6)*0.001
2657            dM = posFourier(tau,nH,delt6[:3],delt6[3:]) #+np.array(Mxyz)[:,nxs,nxs]
2658              #3x6x12 modulated moment array (M,Spos,tau)& force positive
2659            CSI = [np.zeros((6,3),dtype='i'),np.zeros((6,3))]
2660            MSC = np.ones(6,dtype='i')
2661            dMTP = []
2662            for i in SdIndx:
2663                sop = Sop[i]
2664                ssop = SSop[i]
2665                sdet,ssdet,dtau,dT,tauT = getTauT(tau,sop,ssop,XYZ)
2666                msc = np.ones(6,dtype='i')
2667                dMT = posFourier(np.sort(tauT),nH,delt6[:3],delt6[3:])   #+np.array(XYZ)[:,nxs,nxs]
2668                dMT = np.inner(sop[0],dMT.T)    # X modulations array(3x6x49) -> array(3x49x6)
2669                dMT = np.swapaxes(dMT,1,2)      # back to array(3x6x49)
2670                dMT[:,:3,:] *= (ssdet*sdet)            # modify the sin component
2671                dMTP.append(dMT)
2672                for i in range(3):
2673                    if not np.allclose(dM[i,i,:],dMT[i,i,:]):
2674                        msc[i] = 0
2675                    if not np.allclose(dM[i,i+3,:],dMT[i,i+3,:]):
2676                        msc[i+3] = 0
2677                if np.any(dtau%.5) and ('1/2' in SSGData['modSymb'] or '1' in SSGData['modSymb']):
2678                    msc[3:6] = 0
2679                    CSI = [[[1,0,0],[2,0,0],[3,0,0], [1,0,0],[2,0,0],[3,0,0]],
2680                        [[1.,0.,0.],[1.,0.,0.],[1.,0.,0.], [1.,0.,0.],[1.,0.,0.],[1.,0.,0.]]]
2681                    if dT:
2682                        if '(x)' in siteSym:
2683                            CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[-dT,0.,0.]
2684                            if 'm' in siteSym and len(SdIndx) == 1:
2685                                CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[1./dT,0.,0.]
2686                        elif '(y)' in siteSym:
2687                            CSI[1][3:] = [-dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2688                            if 'm' in siteSym and len(SdIndx) == 1:
2689                                CSI[1][3:] = [1./dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2690                        elif '(z)' in siteSym:
2691                            CSI[1][3:] = [-dT,0.,0.],[-dT,0.,0.],[1./dT,0.,0.]
2692                            if 'm' in siteSym and len(SdIndx) == 1:
2693                                CSI[1][3:] = [1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]
2694                    else:
2695                        CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2696                if '4/mmm' in laue:
2697                    if np.any(dtau%.5) and '1/2' in SSGData['modSymb']:
2698                        if '(xy)' in siteSym:
2699                            CSI[0] = [[1,0,0],[1,0,0],[2,0,0], [1,0,0],[1,0,0],[2,0,0]]
2700                            if dT:
2701                                CSI[1][3:] = [[1./dT,0.,0.],[1./dT,0.,0.],[-dT,0.,0.]]
2702                            else:
2703                                CSI[1][3:] = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.]
2704                    if '(xy)' in siteSym or '(+-0)' in siteSym:
2705                        mul = 1
2706                        if '(+-0)' in siteSym:
2707                            mul = -1
2708                        if np.allclose(dM[0,0,:],dMT[1,0,:]):
2709                            CSI[0][3:5] = [[11,0,0],[11,0,0]]
2710                            CSI[1][3:5] = [[1.,0,0],[mul,0,0]]
2711                            msc[3:5] = 0
2712                        if np.allclose(dM[0,3,:],dMT[0,4,:]):
2713                            CSI[0][:2] = [[12,0,0],[12,0,0]]
2714                            CSI[1][:2] = [[1.,0,0],[mul,0,0]]
2715                            msc[:2] = 0
2716                MSC &= msc
2717                if debug: print (SSMT2text(ssop).replace(' ',''),sdet,ssdet,epsinv,msc)
2718            n = -1
2719            if debug: print (MSC)
2720            for i,M in enumerate(MSC):
2721                if M:
2722                    n += 1
2723                    CSI[0][i][0] = n+1
2724                    CSI[1][i][0] = 1.0
2725
2726        return CSI,None,None
2727       
2728    if debug: print ('super space group: '+SSGData['SSpGrp'])
2729    xyz = np.array(XYZ)%1.
2730    SGOps = copy.deepcopy(SGData['SGOps'])
2731    laue = SGData['SGLaue']
2732    siteSym = SytSym(XYZ,SGData)[0].strip()
2733    if debug: print ('siteSym: '+siteSym)
2734    SSGOps = copy.deepcopy(SSGData['SSGOps'])
2735    #expand ops to include inversions if any
2736    if SGData['SGInv']:
2737        for op,sop in zip(SGData['SGOps'],SSGData['SSGOps']):
2738            SGOps.append([-op[0],-op[1]%1.])
2739            SSGOps.append([-sop[0],-sop[1]%1.])
2740    #build set of sym ops around special position       
2741    SSop = []
2742    Sop = []
2743    Sdtau = []
2744    for iop,Op in enumerate(SGOps):         
2745        nxyz = (np.inner(Op[0],xyz)+Op[1])%1.
2746        if np.allclose(xyz,nxyz,1.e-4) and iop and MT2text(Op).replace(' ','') != '-X,-Y,-Z':
2747            SSop.append(SSGOps[iop])
2748            Sop.append(SGOps[iop])
2749            ssopinv = nl.inv(SSGOps[iop][0])
2750            mst = ssopinv[3][:3]
2751            epsinv = ssopinv[3][3]
2752            Sdtau.append(np.sum(mst*(XYZ-SGOps[iop][1])-epsinv*SSGOps[iop][1][3]))
2753    SdIndx = np.argsort(np.array(Sdtau))     # just to do in sensible order
2754    if debug: print ('special pos super operators: ',[SSMT2text(ss).replace(' ','') for ss in SSop])
2755    #setup displacement arrays
2756    tau = np.linspace(-1,1,49,True)
2757    #make modulation arrays - one parameter at a time
2758    if Stype == 'Sfrac':
2759        CSI,dF,dFTP = DoFrac()
2760    elif Stype == 'Spos':
2761        CSI,dF,dFTP = DoXYZ()
2762        CSI[0] = orderParms(CSI[0])
2763    elif Stype == 'Sadp':
2764        CSI,dF,dFTP = DoUij()
2765        CSI[0] = orderParms(CSI[0]) 
2766    elif Stype == 'Smag':
2767        CSI,dF,dFTP = DoMag()
2768       
2769    if debug:
2770        return CSI,dF,dFTP
2771    else:
2772        return CSI
2773   
2774def MustrainNames(SGData):
2775    'Needs a doc string'
2776    laue = SGData['SGLaue']
2777    uniq = SGData['SGUniq']
2778    if laue in ['m3','m3m']:
2779        return ['S400','S220']
2780    elif laue in ['6/m','6/mmm','3m1']:
2781        return ['S400','S004','S202']
2782    elif laue in ['31m','3']:
2783        return ['S400','S004','S202','S211']
2784    elif laue in ['3R','3mR']:
2785        return ['S400','S220','S310','S211']
2786    elif laue in ['4/m','4/mmm']:
2787        return ['S400','S004','S220','S022']
2788    elif laue in ['mmm']:
2789        return ['S400','S040','S004','S220','S202','S022']
2790    elif laue in ['2/m']:
2791        SHKL = ['S400','S040','S004','S220','S202','S022']
2792        if uniq == 'a':
2793            SHKL += ['S013','S031','S211']
2794        elif uniq == 'b':
2795            SHKL += ['S301','S103','S121']
2796        elif uniq == 'c':
2797            SHKL += ['S130','S310','S112']
2798        return SHKL
2799    else:
2800        SHKL = ['S400','S040','S004','S220','S202','S022']
2801        SHKL += ['S310','S103','S031','S130','S301','S013']
2802        SHKL += ['S211','S121','S112']
2803        return SHKL
2804       
2805def HStrainVals(HSvals,SGData):
2806    laue = SGData['SGLaue']
2807    uniq = SGData['SGUniq']
2808    DIJ = np.zeros(6)
2809    if laue in ['m3','m3m']:
2810        DIJ[:3] = [HSvals[0],HSvals[0],HSvals[0]]
2811    elif laue in ['6/m','6/mmm','3m1','31m','3']:
2812        DIJ[:4] = [HSvals[0],HSvals[0],HSvals[1],HSvals[0]]
2813    elif laue in ['3R','3mR']:
2814        DIJ = [HSvals[0],HSvals[0],HSvals[0],HSvals[1],HSvals[1],HSvals[1]]
2815    elif laue in ['4/m','4/mmm']:
2816        DIJ[:3] = [HSvals[0],HSvals[0],HSvals[1]]
2817    elif laue in ['mmm']:
2818        DIJ[:3] = [HSvals[0],HSvals[1],HSvals[2]]
2819    elif laue in ['2/m']:
2820        DIJ[:3] = [HSvals[0],HSvals[1],HSvals[2]]
2821        if uniq == 'a':
2822            DIJ[5] = HSvals[3]
2823        elif uniq == 'b':
2824            DIJ[4] = HSvals[3]
2825        elif uniq == 'c':
2826            DIJ[3] = HSvals[3]
2827    else:
2828        DIJ = [HSvals[0],HSvals[1],HSvals[2],HSvals[3],HSvals[4],HSvals[5]]
2829    return DIJ
2830
2831def HStrainNames(SGData):
2832    'Needs a doc string'
2833    laue = SGData['SGLaue']
2834    uniq = SGData['SGUniq']
2835    if laue in ['m3','m3m']:
2836        return ['D11','eA']         #add cubic strain term
2837    elif laue in ['6/m','6/mmm','3m1','31m','3']:
2838        return ['D11','D33']
2839    elif laue in ['3R','3mR']:
2840        return ['D11','D12']
2841    elif laue in ['4/m','4/mmm']:
2842        return ['D11','D33']
2843    elif laue in ['mmm']:
2844        return ['D11','D22','D33']
2845    elif laue in ['2/m']:
2846        Dij = ['D11','D22','D33']
2847        if uniq == 'a':
2848            Dij += ['D23']
2849        elif uniq == 'b':
2850            Dij += ['D13']
2851        elif uniq == 'c':
2852            Dij += ['D12']
2853        return Dij
2854    else:
2855        Dij = ['D11','D22','D33','D12','D13','D23']
2856        return Dij
2857   
2858def MustrainCoeff(HKL,SGData):
2859    'Needs a doc string'
2860    #NB: order of terms is the same as returned by MustrainNames
2861    laue = SGData['SGLaue']
2862    uniq = SGData['SGUniq']
2863    h,k,l = HKL
2864    Strm = []
2865    if laue in ['m3','m3m']:
2866        Strm.append(h**4+k**4+l**4)
2867        Strm.append(3.0*((h*k)**2+(h*l)**2+(k*l)**2))
2868    elif laue in ['6/m','6/mmm','3m1']:
2869        Strm.append(h**4+k**4+2.0*k*h**3+2.0*h*k**3+3.0*(h*k)**2)
2870        Strm.append(l**4)
2871        Strm.append(3.0*((h*l)**2+(k*l)**2+h*k*l**2))
2872    elif laue in ['31m','3']:
2873        Strm.append(h**4+k**4+2.0*k*h**3+2.0*h*k**3+3.0*(h*k)**2)
2874        Strm.append(l**4)
2875        Strm.append(3.0*((h*l)**2+(k*l)**2+h*k*l**2))
2876        Strm.append(4.0*h*k*l*(h+k))
2877    elif laue in ['3R','3mR']:
2878        Strm.append(h**4+k**4+l**4)
2879        Strm.append(3.0*((h*k)**2+(h*l)**2+(k*l)**2))
2880        Strm.append(2.0*(h*l**3+l*k**3+k*h**3)+2.0*(l*h**3+k*l**3+l*k**3))
2881        Strm.append(4.0*(k*l*h**2+h*l*k**2+h*k*l**2))
2882    elif laue in ['4/m','4/mmm']:
2883        Strm.append(h**4+k**4)
2884        Strm.append(l**4)
2885        Strm.append(3.0*(h*k)**2)
2886        Strm.append(3.0*((h*l)**2+(k*l)**2))
2887    elif laue in ['mmm']:
2888        Strm.append(h**4)
2889        Strm.append(k**4)
2890        Strm.append(l**4)
2891        Strm.append(3.0*(h*k)**2)
2892        Strm.append(3.0*(h*l)**2)
2893        Strm.append(3.0*(k*l)**2)
2894    elif laue in ['2/m']:
2895        Strm.append(h**4)
2896        Strm.append(k**4)
2897        Strm.append(l**4)
2898        Strm.append(3.0*(h*k)**2)
2899        Strm.append(3.0*(h*l)**2)
2900        Strm.append(3.0*(k*l)**2)
2901        if uniq == 'a':
2902            Strm.append(2.0*k*l**3)
2903            Strm.append(2.0*l*k**3)
2904            Strm.append(4.0*k*l*h**2)
2905        elif uniq == 'b':
2906            Strm.append(2.0*l*h**3)
2907            Strm.append(2.0*h*l**3)
2908            Strm.append(4.0*h*l*k**2)
2909        elif uniq == 'c':
2910            Strm.append(2.0*h*k**3)
2911            Strm.append(2.0*k*h**3)
2912            Strm.append(4.0*h*k*l**2)
2913    else:
2914        Strm.append(h**4)
2915        Strm.append(k**4)
2916        Strm.append(l**4)
2917        Strm.append(3.0*(h*k)**2)
2918        Strm.append(3.0*(h*l)**2)
2919        Strm.append(3.0*(k*l)**2)
2920        Strm.append(2.0*k*h**3)
2921        Strm.append(2.0*h*l**3)
2922        Strm.append(2.0*l*k**3)
2923        Strm.append(2.0*h*k**3)
2924        Strm.append(2.0*l*h**3)
2925        Strm.append(2.0*k*l**3)
2926        Strm.append(4.0*k*l*h**2)
2927        Strm.append(4.0*h*l*k**2)
2928        Strm.append(4.0*k*h*l**2)
2929    return Strm
2930
2931def MuShklMean(SGData,Amat,Shkl):
2932   
2933    def genMustrain(xyz,Shkl):
2934        uvw = np.inner(Amat.T,xyz)
2935        Strm = np.array(MustrainCoeff(uvw,SGData))
2936        Sum = np.sum(np.multiply(Shkl,Strm))
2937        Sum = np.where(Sum > 0.01,Sum,0.01)
2938        Sum = np.sqrt(Sum)
2939        return Sum*xyz
2940       
2941    PHI = np.linspace(0.,360.,30,True)
2942    PSI = np.linspace(0.,180.,30,True)
2943    X = np.outer(npcosd(PHI),npsind(PSI))
2944    Y = np.outer(npsind(PHI),npsind(PSI))
2945    Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
2946    XYZ = np.dstack((X,Y,Z))
2947    XYZ = np.nan_to_num(np.apply_along_axis(genMustrain,2,XYZ,Shkl))
2948    return np.sqrt(np.sum(XYZ**2)/900.)
2949   
2950def Muiso2Shkl(muiso,SGData,cell):
2951    "this is to convert isotropic mustrain to generalized Shkls"
2952    import GSASIIlattice as G2lat
2953    A = G2lat.cell2AB(cell)[0]
2954   
2955    def minMus(Shkl,muiso,H,SGData,A):
2956        U = np.inner(A.T,H)
2957        S = np.array(MustrainCoeff(U,SGData))
2958        Sum = np.sqrt(np.sum(np.multiply(S,Shkl[:,nxs]),axis=0))
2959        rad = np.sqrt(np.sum((Sum[:,nxs]*H)**2,axis=1))
2960        return (muiso-rad)**2
2961       
2962    laue = SGData['SGLaue']
2963    PHI = np.linspace(0.,360.,60,True)
2964    PSI = np.linspace(0.,180.,60,True)
2965    X = np.outer(npsind(PHI),npsind(PSI))
2966    Y = np.outer(npcosd(PHI),npsind(PSI))
2967    Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
2968    HKL = np.dstack((X,Y,Z))
2969    if laue in ['m3','m3m']:
2970        S0 = [1000.,1000.]
2971    elif laue in ['6/m','6/mmm','3m1']:
2972        S0 = [1000.,1000.,1000.]
2973    elif laue in ['31m','3']:
2974        S0 = [1000.,1000.,1000.,1000.]
2975    elif laue in ['3R','3mR']:
2976        S0 = [1000.,1000.,1000.,1000.]
2977    elif laue in ['4/m','4/mmm']:
2978        S0 = [1000.,1000.,1000.,1000.]
2979    elif laue in ['mmm']:
2980        S0 = [1000.,1000.,1000.,1000.,1000.,1000.]
2981    elif laue in ['2/m']:
2982        S0 = [1000.,1000.,1000.,0.,0.,0.,0.,0.,0.]
2983    else:
2984        S0 = [1000.,1000.,1000.,1000.,1000., 1000.,1000.,1000.,1000.,1000., 
2985            1000.,1000.,0.,0.,0.]
2986    S0 = np.array(S0)
2987    HKL = np.reshape(HKL,(-1,3))
2988    result = so.leastsq(minMus,S0,(np.ones(HKL.shape[0])*muiso,HKL,SGData,A))
2989    return result[0]
2990       
2991def PackRot(SGOps):
2992    IRT = []
2993    for ops in SGOps:
2994        M = ops[0]
2995        irt = 0
2996        for j in range(2,-1,-1):
2997            for k in range(2,-1,-1):
2998                irt *= 3
2999                irt += M[k][j]
3000        IRT.append(int(irt))
3001    return IRT
3002       
3003def SytSym(XYZ,SGData):
3004    '''
3005    Generates the number of equivalent positions and a site symmetry code for a specified coordinate and space group
3006
3007    :param XYZ: an array, tuple or list containing 3 elements: x, y & z
3008    :param SGData: from SpcGroup
3009    :Returns: a four element tuple:
3010
3011     * The 1st element is a code for the site symmetry (see GetKNsym)
3012     * The 2nd element is the site multiplicity
3013     * Ndup number of overlapping operators
3014     * dupDir Dict - dictionary of overlapping operators
3015
3016    '''
3017    Mult = 1
3018    Isym = 0
3019    if SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']:
3020        Isym = 1073741824
3021    Jdup = 0
3022    Ndup = 0
3023    dupDir = {}
3024    inv = SGData['SGInv']+1
3025    icen = SGData['SGCen']
3026    if SGData['SGFixed']:       #already in list of operators
3027        inv = 1
3028    Xeqv = list(GenAtom(XYZ,SGData,True))
3029#    for xeqv in Xeqv:   print(xeqv)
3030    IRT = PackRot(SGData['SGOps'])
3031    L = -1
3032    for ic,cen in enumerate(icen):
3033        for invers in range(int(inv)):
3034            for io,ops in enumerate(SGData['SGOps']):
3035                irtx = (1-2*invers)*IRT[io]
3036                L += 1
3037                if not Xeqv[L][1]:
3038                    Ndup = io
3039                    Jdup += 1
3040                    jx = GetOprPtrNumber(str(irtx))   #[KN table no,op name,KNsym ptr]
3041                    if jx < 39:
3042                        px = GetOprName(str(irtx))
3043                        if Xeqv[L][-1] < 0:
3044                            if '(' in px:
3045                                px = px.split('(')
3046                                px[0] += "'"
3047                                px = '('.join(px)
3048                            else:   
3049                                px += "'"
3050                        dupDir[px] = L
3051                        Isym += 2**(jx-1)
3052    if Isym == 1073741824: Isym = 0
3053    Mult = len(SGData['SGOps'])*len(SGData['SGCen'])*inv//Jdup
3054         
3055    return GetKNsym(str(Isym)),Mult,Ndup,dupDir
3056   
3057def MagSytSym(SytSym,dupDir,SGData):
3058    '''
3059    site sym operations: 1,-1,2,3,-3,4,-4,6,-6,m need to be marked if spin inversion
3060    '''
3061    SGData['GenSym'],SGData['GenFlg'] = GetGenSym(SGData)[:2]
3062#    print('SGPtGrp',SGData['SGPtGrp'],'SytSym',SytSym,'MagSpGrp',SGData['MagSpGrp'])
3063#    print('dupDir',dupDir)
3064    SplitSytSym = SytSym.split('(')
3065    if SGData['SGGray']:
3066        return SytSym+"1'"
3067    if SytSym == '1':       #genersl position
3068        return SytSym
3069    if SplitSytSym[0] == SGData['SGPtGrp']:     #simple cases
3070        try:
3071            MagSytSym = SGData['MagSpGrp'].split()[1]
3072        except IndexError:
3073            MagSytSym = SGData['MagSpGrp'][1:].strip("1'")
3074        if len(SplitSytSym) > 1:
3075            MagSytSym += '('+SplitSytSym[1]
3076        return MagSytSym
3077    if len(dupDir) == 1:
3078        return dupDir.keys()[0]
3079   
3080   
3081    if '2/m' in SytSym:         #done I think; last 2wo might be not needed
3082        ops = {'(x)':['2(x)','m(x)'],'(y)':['2(y)','m(y)'],'(z)':['2(z)','m(z)'],
3083               '(100)':['2(100)','m(100)'],'(010)':['2(010)','m(010)'],'(001)':['2(001)','m(001)'],
3084               '(120)':['2(120)','m(120)'],'(210)':['2(210)','m(210)'],'(+-0)':['2(+-0)','m(+-0)'],
3085               '(110)':['2(110)','m(110)']}
3086   
3087    elif '4/mmm' in SytSym:
3088        ops = {'(x)':['4(x)','m(x)','m(y)','m(0+-)'],   #m(0+-) for cubic m3m?
3089               '(y)':['4(y)','m(y)','m(z)','m(+0-)'],   #m(+0-)
3090               '(z)':['4(z)','m(z)','m(x)','m(+-0)']}   #m(+-0)
3091    elif '4mm' in SytSym:
3092        ops = {'(x)':['4(x)','m(y)','m(yz)'],'(y)':['4(y)','m(z)','m(xz)'],'(z)':['4(z)','m(x)','m(110)']}
3093    elif '422' in SytSym:
3094        ops = {'(x)':['4(x)','2(y)','2(yz)'],'(y)':['4(y)','2(z)','2(xz)'],'(z)':['4(z)','2(x)','2(110)']}
3095    elif '-4m2' in SytSym:
3096        ops = {'(x)':['-4(x)','m(x)','2(yz)'],'(y)':['-4(y)','m(y)','2(xz)'],'(z)':['-4(z)','m(z)','2(110)']}
3097    elif '-42m' in SytSym:
3098        ops = {'(x)':['-4(x)','2(y)','m(yz)'],'(y)':['-4(y)','2(z)','m(xz)'],'(z)':['-4(z)','2(x)','m(110)']}
3099    elif '-4' in SytSym:
3100        ops = {'(x)':['-4(x)',],'(y)':['-4(y)',],'(z)':['-4(z)',],}
3101    elif '4' in SytSym:
3102        ops = {'(x)':['4(x)',],'(y)':['4(y)',],'(z)':['4(z)',],}
3103
3104    elif '222' in SytSym:
3105        ops = {'':['2(x)','2(y)','2(z)'],
3106                   '(x)':['2(y)','2(z)','2(x)'],'(y)':['2(x)','2(z)','2(y)'],'(z)':['2(x)','2(y)','2(z)'],
3107                   '(100)':['2(z)','2(100)','2(120)',],'(010)':['2(z)','2(010)','2(210)',],
3108                   '(110)':['2(z)','2(110)','2(+-0)',],}
3109    elif 'mm2' in SytSym:
3110        ops = {'(x)':['m(y)','m(z)','2(x)'],'(y)':['m(x)','m(z)','2(y)'],'(z)':['m(x)','m(y)','2(z)'],
3111               '(xy)':['m(+-0)','m(z)','2(110)'],'(yz)':['m(0+-)','m(xz)','2(yz)'],     #not 2(xy)!
3112               '(xz)':['m(+0-)','m(y)','2(xz)'],'(z100)':['m(100)','m(120)','2(z)'],
3113               '(z010)':['m(010)','m(210)','2(z)'],'(z110)':['m(110)','m(+-0)','2(z)'],
3114               '(+-0)':[ 'm(110)','m(z)','2(+-0)'],'(d100)':['m(yz)','m(0+-)','2(xz)'],
3115               '(d010)':['m(xz)','m(+0-)','2(y)'],'(d001)':['m(110)','m(+-0)','2(z)'],
3116               '(210)':['m(z)','m(010)','2(210)'],
3117               '(100)':['m(z)','m(120)','2(100)',],'(010)':['m(z)','m(210)','2(010)',],
3118               '(110)':['m(z)','m(+-0)','2(110)',],}
3119    elif 'mmm' in SytSym:
3120        ops = {'':['m(x)','m(y)','m(z)'],
3121                   '(100)':['m(z)','m(100)','m(120)',],'(010)':['m(z)','m(010)','m(210)',],
3122                   '(110)':['m(z)','m(110)','m(+-0)',],
3123                   '(x)':['m(x)','m(y)','m(z)'],'(y)':['m(x)','m(y)','m(z)'],'(z)':['m(x)','m(y)','m(z)'],}
3124       
3125    elif '32' in SytSym:
3126        ops = {'(120)':['3','2(120)',],'(100)':['3','2(100)']}
3127    elif '23' in SytSym:
3128        ops = {'':['2(x)','3(111)']}
3129    elif 'm3' in SytSym:
3130        ops = {'(100)':['(+-0)',],'(+--)':[],'(-+-)':[],'(--+)':[]}
3131    elif '3m' in SytSym:
3132        ops = {'(111)':['3(111)','m(+-0)',],'(+--)':['3(+--)','m(0+-)',],
3133               '(-+-)':['3(-+-)','m(+0-)',],'(--+)':['3(--+)','m(+-0)',],
3134               '(100)':['3','m(100)'],'(120)':['3','m(210)',]}
3135   
3136    if SytSym.split('(')[0] in ['6/m','6mm','-6m2','622','-6','-3','-3m',]:     #not simple cases
3137        MagSytSym = SytSym
3138        if "-1'" in dupDir:
3139            if '-6' in SytSym:
3140                MagSytSym = MagSytSym.replace('-6',"-6'")
3141            elif '-3m' in SytSym:
3142                MagSytSym = MagSytSym.replace('-3m',"-3'm'")
3143            elif '-3' in SytSym:
3144                MagSytSym = MagSytSym.replace('-3',"-3'")
3145        elif '-6m2' in SytSym:
3146            if "m'(110)" in dupDir:
3147                MagSytSym = "-6m'2'("+SytSym.split('(')[1]
3148        elif '6/m' in SytSym:
3149            if "m'(z)" in dupDir:
3150                MagSytSym = "6'/m'"
3151        elif '6mm' in SytSym:
3152            if "m'(110)" in dupDir:
3153                MagSytSym = "6'm'm"
3154        return MagSytSym
3155    try:
3156        axis = '('+SytSym.split('(')[1]
3157    except IndexError:
3158        axis = ''
3159    MagSytSym = ''
3160    for m in ops[axis]:
3161        if m in dupDir:
3162            MagSytSym += m.split('(')[0]
3163        else:
3164            MagSytSym += m.split('(')[0]+"'"
3165        if '2/m' in SytSym and '2' in m:
3166            MagSytSym += '/'
3167        if '-3/m' in SytSym:
3168            MagSytSym = '-'+MagSytSym
3169       
3170    MagSytSym += axis
3171# some exceptions & special rules         
3172    if MagSytSym == "4'/m'm'm'": MagSytSym = "4/m'm'm'"
3173    return MagSytSym
3174   
3175#    if len(GenSym) == 3:
3176#        if SGSpin[1] < 0:
3177#            if 'mm2' in SytSym:
3178#                MagSytSym = "m'm'2"+'('+SplitSytSym[1]
3179#            else:   #bad rule for I41/a
3180#                MagSytSym = SplitSytSym[0]+"'"
3181#                if len(SplitSytSym) > 1:
3182#                    MagSytSym += '('+SplitSytSym[1]
3183#        else:
3184#            MagSytSym = SytSym
3185#        if len(SplitSytSym) >1:
3186#            if "-4'"+'('+SplitSytSym[1] in dupDir:
3187#                MagSytSym = MagSytSym.replace('-4',"-4'")
3188#            if "-6'"+'('+SplitSytSym[1] in dupDir:
3189#                MagSytSym = MagSytSym.replace('-6',"-6'")
3190#        return MagSytSym
3191#           
3192    return SytSym
3193
3194def UpdateSytSym(Phase):
3195    ''' Update site symmetry/site multiplicity after space group/VNS lattice change
3196    '''
3197    generalData = Phase['General']
3198    SGData = generalData['SGData']
3199    Atoms = Phase['Atoms']
3200    cx,ct,cs,cia = generalData['AtomPtrs']
3201    for atom in Atoms:
3202        XYZ = atom[cx:cx+3]
3203        sytsym,Mult = SytSym(XYZ,SGData)[:2]
3204        sytSym,Mul,Nop,dupDir = SytSym(XYZ,SGData)
3205        atom[cs] = sytsym
3206        if generalData['Type'] == 'magnetic':
3207            magSytSym = MagSytSym(sytSym,dupDir,SGData)
3208            atom[cs] = magSytSym
3209        atom[cs+1] = Mult
3210    return
3211   
3212def ElemPosition(SGData):
3213    ''' Under development.
3214    Object here is to return a list of symmetry element types and locations suitable
3215    for say drawing them.
3216    So far I have the element type... getting all possible locations without lookup may be impossible!
3217    '''
3218    Inv = SGData['SGInv']
3219    eleSym = {-3:['','-1'],-2:['',-6],-1:['2','-4'],0:['3','-3'],1:['4','m'],2:['6',''],3:['1','']}
3220    # get operators & expand if centrosymmetric
3221    Ops = SGData['SGOps']
3222    opM = np.array([op[0].T for op in Ops])
3223    opT = np.array([op[1] for op in Ops])
3224    if Inv:
3225        opM = np.concatenate((opM,-opM))
3226        opT = np.concatenate((opT,-opT))
3227    opMT = list(zip(opM,opT))
3228    for M,T in opMT[1:]:        #skip I
3229        Dt = int(nl.det(M))
3230        Tr = int(np.trace(M))
3231        Dt = -(Dt-1)//2
3232        Es = eleSym[Tr][Dt]
3233        if Dt:              #rotation-inversion
3234            I = np.eye(3)
3235            if Tr == 1:     #mirrors/glides
3236                if np.any(T):       #glide
3237                    M2 = np.inner(M,M)
3238                    MT = np.inner(M,T)+T
3239                    print ('glide',Es,MT)
3240                    print (M2)
3241                else:               #mirror
3242                    print ('mirror',Es,T)
3243                    print (I-M)
3244                X = [-1,-1,-1]
3245            elif Tr == -3:  # pure inversion
3246                X = np.inner(nl.inv(I-M),T)
3247                print ('inversion',Es,X)
3248            else:           #other rotation-inversion
3249                M2 = np.inner(M,M)
3250                MT = np.inner(M,T)+T
3251                print ('rot-inv',Es,MT)
3252                print (M2)
3253                X = [-1,-1,-1]
3254        else:               #rotations
3255            print ('rotation',Es)
3256            X = [-1,-1,-1]
3257        #SymElements.append([Es,X])
3258       
3259    return #SymElements
3260   
3261def ApplyStringOps(A,SGData,X,Uij=[]):
3262    'Needs a doc string'
3263    SGOps = SGData['SGOps']
3264    SGCen = SGData['SGCen']
3265    Ax = A.split('+')
3266    Ax[0] = int(Ax[0])
3267    iC = 1
3268    if Ax[0] < 0:
3269        iC = -1
3270    Ax[0] = abs(Ax[0])
3271    nA = Ax[0]%100-1
3272    cA = Ax[0]//100
3273    Cen = SGCen[cA]
3274    M,T = SGOps[nA]
3275    if len(Ax)>1:
3276        cellA = Ax[1].split(',')
3277        cellA = np.array([int(a) for a in cellA])
3278    else:
3279        cellA = np.zeros(3)
3280    newX = Cen+iC*(np.inner(M,X).T+T)+cellA
3281    if len(Uij):
3282        U = Uij2U(Uij)
3283        U = np.inner(M,np.inner(U,M).T)
3284        newUij = U2Uij(U)
3285        return [newX,newUij]
3286    else:
3287        return newX
3288       
3289def ApplyStringOpsMom(A,SGData,Mom):
3290    'Needs a doc string'
3291    SGOps = SGData['SGOps']
3292    Ax = A.split('+')
3293    Ax[0] = int(Ax[0])
3294    iAx = abs(Ax[0])
3295    nA = iAx%100-1
3296    nC = len(SGOps)*(iAx//100)
3297    NA = nA
3298    if Ax[0] < 0:
3299        NA += len(SGOps)
3300    M,T = SGOps[nA]
3301    if SGData['SGGray']:        #no nonzero moments for gray groups!
3302        newMom = [0.,0.,0.]
3303    else:
3304        newMom = np.inner(Mom,M).T*nl.det(M)*SGData['SpnFlp'][NA+nC]
3305#        print(len(SGOps),Ax[0],iAx,nC,nA,NA,SGData['SpnFlp'][NA],Mom,newMom)
3306    return newMom
3307       
3308def StringOpsProd(A,B,SGData):
3309    """
3310    Find A*B where A & B are in strings '-' + '100*c+n' + '+ijk'
3311    where '-' indicates inversion, c(>0) is the cell centering operator,
3312    n is operator number from SgOps and ijk are unit cell translations (each may be <0).
3313    Should return resultant string - C. SGData - dictionary using entries:
3314
3315       *  'SGCen': cell centering vectors [0,0,0] at least
3316       *  'SGOps': symmetry operations as [M,T] so that M*x+T = x'
3317
3318    """
3319    SGOps = SGData['SGOps']
3320    SGCen = SGData['SGCen']
3321    #1st split out the cell translation part & work on the operator parts
3322    Ax = A.split('+'); Bx = B.split('+')
3323    Ax[0] = int(Ax[0]); Bx[0] = int(Bx[0])
3324    iC = 0
3325    if Ax[0]*Bx[0] < 0:
3326        iC = 1
3327    Ax[0] = abs(Ax[0]); Bx[0] = abs(Bx[0])
3328    nA = Ax[0]%100-1;  nB = Bx[0]%100-1
3329    cA = Ax[0]//100;  cB = Bx[0]//100
3330    Cen = (SGCen[cA]+SGCen[cB])%1.0
3331    cC = np.nonzero([np.allclose(C,Cen) for C in SGCen])[0][0]
3332    Ma,Ta = SGOps[nA]; Mb,Tb = SGOps[nB]
3333    Mc = np.inner(Ma,Mb.T)
3334#    print Ma,Mb,Mc
3335    Tc = (np.add(np.inner(Mb,Ta)+1.,Tb))%1.0
3336#    print Ta,Tb,Tc
3337#    print [np.allclose(M,Mc)&np.allclose(T,Tc) for M,T in SGOps]
3338    nC = np.nonzero([np.allclose(M,Mc)&np.allclose(T,Tc) for M,T in SGOps])[0][0]
3339    #now the cell translation part
3340    if len(Ax)>1:
3341        cellA = Ax[1].split(',')
3342        cellA = [int(a) for a in cellA]
3343    else:
3344        cellA = [0,0,0]
3345    if len(Bx)>1:
3346        cellB = Bx[1].split(',')
3347        cellB = [int(b) for b in cellB]
3348    else:
3349        cellB = [0,0,0]
3350    cellC = np.add(cellA,cellB)
3351    C = str(((nC+1)+(100*cC))*(1-2*iC))+'+'+ \
3352        str(int(cellC[0]))+','+str(int(cellC[1]))+','+str(int(cellC[2]))
3353    return C
3354           
3355def U2Uij(U):
3356    #returns the UIJ vector U11,U22,U33,U12,U13,U23 from tensor U
3357    return [U[0][0],U[1][1],U[2][2],U[0][1],U[0][2],U[1][2]]
3358   
3359def Uij2U(Uij):
3360    #returns the thermal motion tensor U from Uij as numpy array
3361    return np.array([[Uij[0],Uij[3],Uij[4]],[Uij[3],Uij[1],Uij[5]],[Uij[4],Uij[5],Uij[2]]])
3362
3363def StandardizeSpcName(spcgroup):
3364    '''Accept a spacegroup name where spaces may have not been used
3365    in the names according to the GSAS convention (spaces between symmetry
3366    for each axis) and return the space group name as used in GSAS
3367    '''
3368    rspc = spcgroup.replace(' ','').upper()
3369    # deal with rhombohedral and hexagonal setting designations
3370    rhomb = ''
3371    if rspc[-1:] == 'R':
3372        rspc = rspc[:-1]
3373        rhomb = ' R'
3374    gray = ''
3375    if "1'" in rspc:
3376        gray = " 1'"
3377        rspc = rspc.replace("1'",'')
3378    elif rspc[-1:] == 'H': # hexagonal is assumed and thus can be ignored
3379        rspc = rspc[:-1]
3380    else:
3381        rspc = rspc.replace("'",'')
3382    # look for a match in the spacegroup lists
3383    for i in spglist.values():
3384        for spc in i:
3385            if rspc == spc.replace(' ','').upper():
3386                return spc+gray+rhomb
3387    # how about the post-2002 orthorhombic names?
3388    if rspc in sgequiv_2002_orthorhombic:
3389        return sgequiv_2002_orthorhombic[rspc]+gray
3390    else:
3391    # not found
3392        return ''
3393
3394spgbyNum = []
3395'''Space groups indexed by number'''
3396spgbyNum = [None,
3397        'P 1','P -1',                                                   #1-2
3398        'P 2','P 21','C 2','P m','P c','C m','C c','P 2/m','P 21/m',
3399        'C 2/m','P 2/c','P 21/c','C 2/c',                               #3-15
3400        'P 2 2 2','P 2 2 21','P 21 21 2','P 21 21 21',
3401        'C 2 2 21','C 2 2 2','F 2 2 2','I 2 2 2','I 21 21 21',
3402        'P m m 2','P m c 21','P c c 2','P m a 2','P c a 21',
3403        'P n c 2','P m n 21','P b a 2','P n a 21','P n n 2',
3404        'C m m 2','C m c 21','C c c 2',
3405        'A m m 2','A b m 2','A m a 2','A b a 2',
3406        'F m m 2','F d d 2','I m m 2','I b a 2','I m a 2',
3407        'P m m m','P n n n','P c c m','P b a n',
3408        'P m m a','P n n a','P m n a','P c c a','P b a m','P c c n',
3409        'P b c m','P n n m','P m m n','P b c n','P b c a','P n m a',
3410        'C m c m','C m c a','C m m m','C c c m','C m m a','C c c a',
3411        'F m m m', 'F d d d',
3412        'I m m m','I b a m','I b c a','I m m a',                        #16-74
3413        'P 4','P 41','P 42','P 43',
3414        'I 4','I 41',
3415        'P -4','I -4','P 4/m','P 42/m','P 4/n','P 42/n',
3416        'I 4/m','I 41/a',
3417        'P 4 2 2','P 4 21 2','P 41 2 2','P 41 21 2','P 42 2 2',
3418        'P 42 21 2','P 43 2 2','P 43 21 2',
3419        'I 4 2 2','I 41 2 2',
3420        'P 4 m m','P 4 b m','P 42 c m','P 42 n m','P 4 c c','P 4 n c',
3421        'P 42 m c','P 42 b c',
3422        'I 4 m m','I 4 c m','I 41 m d','I 41 c d',
3423        'P -4 2 m','P -4 2 c','P -4 21 m','P -4 21 c','P -4 m 2',
3424        'P -4 c 2','P -4 b 2','P -4 n 2',
3425        'I -4 m 2','I -4 c 2','I -4 2 m','I -4 2 d',
3426        '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',
3427        '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',
3428        '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',
3429        'P 42/n c m',
3430        'I 4/m m m','I 4/m c m','I 41/a m d','I 41/a c d',
3431        'P 3','P 31','P 32','R 3','P -3','R -3',
3432        'P 3 1 2','P 3 2 1','P 31 1 2','P 31 2 1','P 32 1 2','P 32 2 1',
3433        'R 3 2',
3434        'P 3 m 1','P 3 1 m','P 3 c 1','P 3 1 c',
3435        'R 3 m','R 3 c',
3436        'P -3 1 m','P -3 1 c','P -3 m 1','P -3 c 1',
3437        'R -3 m','R -3 c',                                               #75-167
3438        'P 6','P 61',
3439        'P 65','P 62','P 64','P 63','P -6','P 6/m','P 63/m','P 6 2 2',
3440        'P 61 2 2','P 65 2 2','P 62 2 2','P 64 2 2','P 63 2 2','P 6 m m',
3441        'P 6 c c','P 63 c m','P 63 m c','P -6 m 2','P -6 c 2','P -6 2 m',
3442        '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
3443        'P 2 3','F 2 3','I 2 3','P 21 3','I 21 3','P m 3','P n 3',
3444        'F m -3','F d -3','I m -3',
3445        'P a -3','I a -3','P 4 3 2','P 42 3 2','F 4 3 2','F 41 3 2',
3446        'I 4 3 2','P 43 3 2','P 41 3 2','I 41 3 2','P -4 3 m',
3447        'F -4 3 m','I -4 3 m','P -4 3 n','F -4 3 c','I -4 3 d',
3448        'P m -3 m','P n -3 n','P m -3 n','P n -3 m',
3449        'F m -3 m','F m -3 c','F d -3 m','F d -3 c',
3450        'I m -3 m','I a -3 d',]                                       #195-230
3451spg2origins = {}
3452''' A dictionary of all spacegroups that have 2nd settings; the value is the
34531st --> 2nd setting transformation vector as X(2nd) = X(1st)-V, nonstandard ones are included.
3454'''
3455spg2origins = {
3456        'P n n n':[-.25,-.25,-.25],
3457        'P b a n':[-.25,-.25,0],'P n c b':[0,-.25,-.25],'P c n a':[-.25,0,-.25],
3458        'P m m n':[-.25,-.25,0],'P n m m':[0,-.25,-.25],'P m n m':[-.25,0,-.25],
3459        'C c c a':[0,-.25,-.25],'C c c b':[-.25,0,-.25],'A b a a':[-.25,0,-.25],
3460        'A c a a':[-.25,-.25,0],'B b c b':[-.25,-.25,0],'B b a b':[0,-.25,-.25],
3461        'F d d d':[-.125,-.125,-.125],
3462        'P 4/n':[-.25,-.25,0],'P 42/n':[-.25,-.25,-.25],'I 41/a':[0,-.25,-.125],
3463        '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],
3464        '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],
3465        'I 41/a m d':[0,.25,-.125],'I 41/a c d':[0,.25,-.125],
3466        'p n -3':[-.25,-.25,-.25],'F d -3':[-.125,-.125,-.125],'P n -3 n':[-.25,-.25,-.25],
3467        'P n -3 m':[-.25,-.25,-.25],'F d -3 m':[-.125,-.125,-.125],'F d -3 c':[-.375,-.375,-.375],
3468        'p n 3':[-.25,-.25,-.25],'F d 3':[-.125,-.125,-.125],'P n 3 n':[-.25,-.25,-.25],
3469        'P n 3 m':[-.25,-.25,-.25],'F d 3 m':[-.125,-.125,-.125],'F d - c':[-.375,-.375,-.375]}
3470spglist = {}
3471'''A dictionary of space groups as ordered and named in the pre-2002 International
3472Tables Volume A, except that spaces are used following the GSAS convention to
3473separate the different crystallographic directions.
3474Note that the symmetry codes here will recognize many non-standard space group
3475symbols with different settings. They are ordered by Laue group
3476'''
3477spglist = {
3478    'P1' : ('P 1','P -1',), # 1-2
3479    'C1' : ('C 1','C -1',),
3480    'P2/m': ('P 2','P 21','P m','P a','P c','P n',
3481        '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
3482    'C2/m':('C 2','C m','C c','C n',
3483        'C 2/m','C 2/c','C 2/n',),
3484    'A2/m':('A 2','A m','A a','A n',
3485        'A 2/m','A 2/a','A 2/n',),
3486   'Pmmm':('P 2 2 2',
3487        'P 2 2 21','P 21 2 2','P 2 21 2',
3488        'P 21 21 2','P 2 21 21','P 21 2 21',
3489        'P 21 21 21',
3490        'P m m 2','P 2 m m','P m 2 m',
3491        'P m c 21','P 21 m a','P b 21 m','P m 21 b','P c m 21','P 21 a m',
3492        'P c c 2','P 2 a a','P b 2 b',
3493        'P m a 2','P 2 m b','P c 2 m','P m 2 a','P b m 2','P 2 c m',
3494        'P c a 21','P 21 a b','P c 21 b','P b 21 a','P b c 21','P 21 c a',
3495        'P n c 2','P 2 n a','P b 2 n','P n 2 b','P c n 2','P 2 a n',
3496        'P m n 21','P 21 m n','P n 21 m','P m 21 n','P n m 21','P 21 n m',
3497        'P b a 2','P 2 c b','P c 2 a',
3498        'P n a 21','P 21 n b','P c 21 n','P n 21 a','P b n 21','P 21 c n',
3499        'P n n 2','P 2 n n','P n 2 n',
3500        'P m m m','P n n n',
3501        'P c c m','P m a a','P b m b',
3502        'P b a n','P n c b','P c n a',
3503        'P m m a','P b m m','P m c m','P m a m','P m m b','P c m m',
3504        'P n n a','P b n n','P n c n','P n a n','P n n b','P c n n',
3505        'P m n a','P b m n','P n c m','P m a n','P n m b','P c n m',
3506        'P c c a','P b a a','P b c b','P b a b','P c c b','P c a a',
3507        'P b a m','P m c b','P c m a',
3508        'P c c n','P n a a','P b n b',
3509        'P b c m','P m c a','P b m a','P c m b','P c a m','P m a b',
3510        'P n n m','P m n n','P n m n',
3511        'P m m n','P n m m','P m n m',
3512        'P b c n','P n c a','P b n a','P c n b','P c a n','P n a b',
3513        'P b c a','P c a b',
3514        'P n m a','P b n m','P m c n','P n a m','P m n b','P c m n',
3515        ),
3516    'Cmmm':('C 2 2 21','C 2 2 2','C m m 2',
3517        'C m c 21','C c m 21','C c c 2','C m 2 m','C 2 m m',
3518        'C m 2 a','C 2 m b','C c 2 m','C 2 c m','C c 2 a','C 2 c b',
3519        'C m c m','C c m m','C m c a','C c m b',
3520        'C m m m','C c c m','C m m a','C m m b','C c c a','C c c b',),
3521    'Ammm':('A 21 2 2','A 2 2 2','A 2 m m',
3522        'A 21 m a','A 21 a m','A 2 a a','A m 2 m','A m m 2',
3523        'A b m 2','A c 2 m','A m a 2','A m 2 a','A b a 2','A c 2 a',
3524        'A m m a','A m a m','A b m a','A c a m',
3525        'A m m m','A m a a','A b m m','A c m m','A c a a','A b a a',),
3526    'Bmmm':('B 2 21 2','B 2 2 2','B m 2 m',
3527        'B m 21 b','B b 21 m','B b 2 b','B m m 2','B 2 m m',
3528        'B 2 c m','B m a 2','B 2 m b','B b m 2','B 2 c b','B b a 2',
3529        'B b m m','B m m b','B b c m','B m a b',
3530        'B m m m','B b m b','B m a m','B m c m','B b a b','B b c b',),
3531    'Immm':('I 2 2 2','I 21 21 21',
3532        'I m m 2','I m 2 m','I 2 m m',
3533        'I b a 2','I 2 c b','I c 2 a',
3534        'I m a 2','I 2 m b','I c 2 m','I m 2 a','I b m 2','I 2 c m',
3535        'I m m m','I b a m','I m c b','I c m a',
3536        'I b c a','I c a b',
3537        'I m m a','I b m m ','I m c m','I m a m','I m m b','I c m m',),
3538    'Fmmm':('F 2 2 2','F m m m', 'F d d d',
3539        'F m m 2','F m 2 m','F 2 m m',
3540        'F d d 2','F d 2 d','F 2 d d',),
3541    'P4/mmm':('P 4','P 41','P 42','P 43','P -4','P 4/m','P 42/m','P 4/n','P 42/n',
3542        'P 4 2 2','P 4 21 2','P 41 2 2','P 41 21 2','P 42 2 2',
3543        'P 42 21 2','P 43 2 2','P 43 21 2','P 4 m m','P 4 b m','P 42 c m',
3544        'P 42 n m','P 4 c c','P 4 n c','P 42 m c','P 42 b c','P -4 2 m',
3545        'P -4 2 c','P -4 21 m','P -4 21 c','P -4 m 2','P -4 c 2','P -4 b 2',
3546        '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',
3547        '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',
3548        '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',
3549        'P 42/n c m',),
3550    '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',
3551        'I 4 c m','I 41 m d','I 41 c d',
3552        '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',
3553        'I 41/a m d','I 41/a c d'),
3554    'R3-H':('R 3','R -3','R 3 2','R 3 m','R 3 c','R -3 m','R -3 c',),
3555    'P6/mmm': ('P 3','P 31','P 32','P -3','P 3 1 2','P 3 2 1','P 31 1 2',
3556        'P 31 2 1','P 32 1 2','P 32 2 1', 'P 3 m 1','P 3 1 m','P 3 c 1',
3557        'P 3 1 c','P -3 1 m','P -3 1 c','P -3 m 1','P -3 c 1','P 6','P 61',
3558        'P 65','P 62','P 64','P 63','P -6','P 6/m','P 63/m','P 6 2 2',
3559        'P 61 2 2','P 65 2 2','P 62 2 2','P 64 2 2','P 63 2 2','P 6 m m',
3560        'P 6 c c','P 63 c m','P 63 m c','P -6 m 2','P -6 c 2','P -6 2 m',
3561        'P -6 2 c','P 6/m m m','P 6/m c c','P 63/m c m','P 63/m m c',),
3562    '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',
3563        'P 4 3 2','P 42 3 2','P 43 3 2','P 41 3 2','P -4 3 m','P -4 3 n',
3564        'P m 3 m','P m -3 m','P n 3 n','P n -3 n',
3565        'P m 3 n','P m -3 n',