source: trunk/GSASIIspc.py @ 1564

Last change on this file since 1564 was 1564, checked in by vondreele, 8 years ago

fix display of space group stuff
mode work on super symmetry

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 104.3 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: 2014-11-05 23:05:13 +0000 (Wed, 05 Nov 2014) $
12# $Author: vondreele $
13# $Revision: 1564 $
14# $URL: trunk/GSASIIspc.py $
15# $Id: GSASIIspc.py 1564 2014-11-05 23:05:13Z vondreele $
16########### SVN repository information ###################
17import numpy as np
18import numpy.ma as ma
19import numpy.linalg as nl
20import scipy.optimize as so
21import math
22import sys
23import os.path as ospath
24
25import GSASIIpath
26GSASIIpath.SetVersionNumber("$Revision: 1564 $")
27import pyspg
28
29npsind = lambda x: np.sin(x*np.pi/180.)
30npcosd = lambda x: np.cos(x*np.pi/180.)
31   
32################################################################################
33#### Space group codes
34################################################################################
35
36def SpcGroup(SGSymbol):
37    """
38    Determines cell and symmetry information from a short H-M space group name
39
40    :param SGSymbol: space group symbol (string) with spaces between axial fields
41    :returns: (SGError,SGData)
42       * SGError = 0 for no errors; >0 for errors (see SGErrors below for details)
43       * SGData - is a dict (see :ref:`Space Group object<SGData_table>`) with entries:
44       
45             * 'SpGrp': space group symbol, slightly cleaned up
46             * 'SGLaue':  one of '-1', '2/m', 'mmm', '4/m', '4/mmm', '3R',
47               '3mR', '3', '3m1', '31m', '6/m', '6/mmm', 'm3', 'm3m'
48             * 'SGInv': boolean; True if centrosymmetric, False if not
49             * 'SGLatt': one of 'P', 'A', 'B', 'C', 'I', 'F', 'R'
50             * 'SGUniq': one of 'a', 'b', 'c' if monoclinic, '' otherwise
51             * 'SGCen': cell centering vectors [0,0,0] at least
52             * 'SGOps': symmetry operations as [M,T] so that M*x+T = x'
53             * 'SGSys': one of 'triclinic', 'monoclinic', 'orthorhombic',
54               'tetragonal', 'rhombohedral', 'trigonal', 'hexagonal', 'cubic'
55             * 'SGPolax': one of '', 'x', 'y', 'x y', 'z', 'x z', 'y z',
56               'xyz', '111' for arbitrary axes
57             * 'SGPtGrp': one of 32 point group symbols (with some permutations)
58                - filled by SGPtGroup - is external (KE) part of supersymmetry point group
59             * 'SSGKl': default internal (Kl) part of supersymmetry point group; modified
60             in supersymmetry stuff depending on chosen modulation vector for Mono & Ortho
61
62    """
63    LaueSym = ('-1','2/m','mmm','4/m','4/mmm','3R','3mR','3','3m1','31m','6/m','6/mmm','m3','m3m')
64    LattSym = ('P','A','B','C','I','F','R')
65    UniqSym = ('','','a','b','c','',)
66    SysSym = ('triclinic','monoclinic','orthorhombic','tetragonal','rhombohedral','trigonal','hexagonal','cubic')
67    SGData = {}
68    SGInfo = pyspg.sgforpy(SGSymbol)
69    SGData['SpGrp'] = SGSymbol.strip().lower().capitalize()
70    SGData['SGLaue'] = LaueSym[SGInfo[0]-1]
71    SGData['SGInv'] = bool(SGInfo[1])
72    SGData['SGLatt'] = LattSym[SGInfo[2]-1]
73    SGData['SGUniq'] = UniqSym[SGInfo[3]+1]
74    if SGData['SGLatt'] == 'P':
75        SGData['SGCen'] = np.array(([0,0,0],))
76    elif SGData['SGLatt'] == 'A':
77        SGData['SGCen'] = np.array(([0,0,0],[0,.5,.5]))
78    elif SGData['SGLatt'] == 'B':
79        SGData['SGCen'] = np.array(([0,0,0],[.5,0,.5]))
80    elif SGData['SGLatt'] == 'C':
81        SGData['SGCen'] = np.array(([0,0,0],[.5,.5,0,]))
82    elif SGData['SGLatt'] == 'I':
83        SGData['SGCen'] = np.array(([0,0,0],[.5,.5,.5]))
84    elif SGData['SGLatt'] == 'F':
85        SGData['SGCen'] = np.array(([0,0,0],[0,.5,.5],[.5,0,.5],[.5,.5,0,]))
86    elif SGData['SGLatt'] == 'R':
87        SGData['SGCen'] = np.array(([0,0,0],[1./3.,2./3.,2./3.],[2./3.,1./3.,1./3.]))
88    SGData['SGOps'] = []
89    for i in range(SGInfo[5]):
90        Mat = np.array(SGInfo[6][i])
91        Trns = np.array(SGInfo[7][i])
92        SGData['SGOps'].append([Mat,Trns])
93    if SGData['SGLaue'] in '-1':
94        SGData['SGSys'] = SysSym[0]
95    elif SGData['SGLaue'] in '2/m':
96        SGData['SGSys'] = SysSym[1]
97    elif SGData['SGLaue'] in 'mmm':
98        SGData['SGSys'] = SysSym[2]
99    elif SGData['SGLaue'] in ['4/m','4/mmm']:
100        SGData['SGSys'] = SysSym[3]
101    elif SGData['SGLaue'] in ['3R','3mR']:
102        SGData['SGSys'] = SysSym[4]
103    elif SGData['SGLaue'] in ['3','3m1','31m']:
104        SGData['SGSys'] = SysSym[5]
105    elif SGData['SGLaue'] in ['6/m','6/mmm']:
106        SGData['SGSys'] = SysSym[6]
107    elif SGData['SGLaue'] in ['m3','m3m']:
108        SGData['SGSys'] = SysSym[7]
109    SGData['SGPolax'] = SGpolar(SGData)
110    SGData['SGPtGrp'],SGData['SSGKl'] = SGPtGroup(SGData)
111    return SGInfo[8],SGData
112
113def SGErrors(IErr):
114    '''
115    Interprets the error message code from SpcGroup. Used in SpaceGroup.
116   
117    :param IErr: see SGError in :func:`SpcGroup`
118    :returns:
119        ErrString - a string with the error message or "Unknown error"
120    '''
121
122    ErrString = [' ',
123        'Less than 2 operator fields were found',
124        'Illegal Lattice type, not P, A, B, C, I, F or R',
125        'Rhombohedral lattice requires a 3-axis',
126        'Minus sign does not preceed 1, 2, 3, 4 or 6',
127        'Either a 5-axis anywhere or a 3-axis in field not allowed',
128        ' ',
129        'I for COMPUTED GO TO out of range.',
130        'An a-glide mirror normal to A not allowed',
131        'A b-glide mirror normal to B not allowed',
132        'A c-glide mirror normal to C not allowed',
133        'D-glide in a primitive lattice not allowed',
134        'A 4-axis not allowed in the 2nd operator field',
135        'A 6-axis not allowed in the 2nd operator field',
136        'More than 24 matrices needed to define group',
137        ' ',
138        'Improper construction of a rotation operator',
139        'Mirror following a / not allowed',
140        'A translation conflict between operators',
141        'The 2bar operator is not allowed',
142        '3 fields are legal only in R & m3 cubic groups',
143        'Syntax error. Expected I -4 3 d at this point',
144        ' ',
145        'A or B centered tetragonal not allowed',
146        ' ','unknown error in sgroup',' ',' ',' ',
147        'Illegal character in the space group symbol',
148        ]
149    try:
150        return ErrString[IErr]
151    except:
152        return "Unknown error"
153
154def SGpolar(SGData):
155    '''
156    Determine identity of polar axes if any
157    '''
158    POL = ('','x','y','x y','z','x z','y z','xyz','111')
159    NP = [1,2,4]
160    NPZ = [0,1]
161    for M,T in SGData['SGOps']:
162        for i in range(3):
163            if M[i][i] <= 0.: NP[i] = 0
164        if M[0][2] > 0: NPZ[0] = 8
165        if M[1][2] > 0: NPZ[1] = 0
166    NPol = (NP[0]+NP[1]+NP[2]+NPZ[0]*NPZ[1])*(1-int(SGData['SGInv']))
167    return POL[NPol]
168   
169def SGPtGroup(SGData):
170    '''
171    Determine point group of the space group - done after space group symbol has
172    been evaluated by SpcGroup. Only short symbols are allowed
173   
174    :param SGData: from :func SpcGroup
175    returns SSGPtGrp & SSGKl (only defaults for Mono & Ortho)
176    '''
177    Flds = SGData['SpGrp'].split()
178    if len(Flds) < 2:
179        return '',[]
180    if SGData['SGLaue'] == '-1':    #triclinic
181        if '-' in Flds[1]:
182            return '-1',[-1,]
183        else:
184            return '1',[1,]
185    elif SGData['SGLaue'] == '2/m': #monoclinic - default for 2D modulation vector
186        if '/' in SGData['SpGrp']:
187            return '2/m',[-1,1]
188        elif '2' in SGData['SpGrp']:
189            return '2',[-1,]
190        else:
191            return 'm',[1,]
192    elif SGData['SGLaue'] == 'mmm': #orthorhombic
193        if SGData['SpGrp'].count('2') == 3:
194            return '222',[1,1,1]
195        elif SGData['SpGrp'].count('2') == 1:
196            if SGData['SGPolax'] == 'x':
197                return '2mm',[1,1,1]
198            elif SGData['SGPolax'] == 'y':
199                return 'm2m',[1,1,1]
200            elif SGData['SGPolax'] == 'z':
201                return 'mm2',[1,1,1]
202        else:
203            return 'mmm',[1,1,-1]
204    elif SGData['SGLaue'] == '4/m': #tetragonal
205        if '/' in SGData['SpGrp']:
206            return '4/m',[1,-1]
207        elif '-' in Flds[1]:
208            return '-4',[-1,]
209        else:
210            return '4',[1,]
211    elif SGData['SGLaue'] == '4/mmm':
212        if '/' in SGData['SpGrp']:
213            return '4/mmm',[1,-1,1,1]
214        elif '-' in Flds[1]:
215            if '2' in Flds[2]:
216                return '-42m',[-1,-1,1]
217            else:
218                return '-4m2',[-1,1,-1]             
219        elif '2' in Flds[2:]:
220            return '422',[1,-1,-1]
221        else:
222            return '4mm',[1,1,1]
223    elif SGData['SGLaue'] in ['3','3R']:  #trigonal/rhombohedral
224        if '-' in Flds[1]:
225            return '-3',[-1,]
226        else:
227            return '3',[1,]
228    elif SGData['SGLaue'] == '3mR' or 'R' in Flds[0]:
229        if '2' in Flds[2]:
230            return '32',[1,-1]
231        elif '-' in Flds[1]:
232            return '-3m',[-1,1]
233        else:
234            return '3m',[1,1]
235    elif SGData['SGLaue'] == '3m1':
236        if '2' in Flds[2]:
237            return '321',[1,-1,1]
238        elif '-' in Flds[1]:
239            return '-3m1',[-1,1,1]
240        else:
241            return '3m1',[1,1,1]
242    elif SGData['SGLaue'] == '31m':
243        if '2' in Flds[3]:
244            return '312',[1,1,-1]
245        elif '-' in Flds[1]:
246            return '-31m',[-1,1,1]
247        else:
248            return '31m',[1,1,1]
249    elif SGData['SGLaue'] == '6/m': #hexagonal
250        if '/' in SGData['SpGrp']:
251            return '6/m',[1,-1]
252        elif '-' in SGData['SpGrp']:
253            return '-6',[-1,]
254        else:
255            return '6',[1,]
256    elif SGData['SGLaue'] == '6/mmm':
257        if '/' in SGData['SpGrp']:
258            return '6/mmm',[1,-1,1,1]
259        elif '-' in Flds[1]:
260            if '2' in Flds[2]:
261                return '-62m',[-1,-1,1]
262            else:
263                return '-6m2',[-1,1,-1]                 
264        elif '2' in Flds[2:]:
265            return '622',[1,-1,-1]
266        else:
267            return '6mm',[1,1,1]   
268    elif SGData['SGLaue'] == 'm3':      #cubic - no (3+1) supersymmetry
269        if '2' in Flds[1]:
270            return '23',[]
271        else: 
272            return 'm3',[]
273    elif SGData['SGLaue'] == 'm3m':
274        if '4' in Flds[1]:
275            if '-' in Flds[1]:
276                return '-43m',[]
277            else:
278                return '432',[]
279        else:
280            return 'm-3m',[]
281   
282def SGPrint(SGData):
283    '''
284    Print the output of SpcGroup in a nicely formatted way. Used in SpaceGroup
285
286    :param SGData: from :func:`SpcGroup`
287    :returns:
288        SGText - list of strings with the space group details
289        SGTable - list of strings for each of the operations
290    '''
291    Mult = len(SGData['SGCen'])*len(SGData['SGOps'])*(int(SGData['SGInv'])+1)
292    SGText = []
293    SGText.append(' Space Group: '+SGData['SpGrp'])
294    CentStr = 'centrosymmetric'
295    if not SGData['SGInv']:
296        CentStr = 'non'+CentStr
297    if SGData['SGLatt'] in 'ABCIFR':
298        SGText.append(' The lattice is '+CentStr+' '+SGData['SGLatt']+'-centered '+SGData['SGSys'].lower())
299    else:
300        SGText.append(' The lattice is '+CentStr+' '+'primitive '+SGData['SGSys'].lower()) 
301    SGText.append(' The Laue symmetry is '+SGData['SGLaue'])
302    if 'SGPtGrp' in SGData:         #patch
303        SGText.append(' The lattice point group is '+SGData['SGPtGrp'])
304    SGText.append(' Multiplicity of a general site is '+str(Mult))
305    if SGData['SGUniq'] in ['a','b','c']:
306        SGText.append(' The unique monoclinic axis is '+SGData['SGUniq'])
307    if SGData['SGInv']:
308        SGText.append(' The inversion center is located at 0,0,0')
309    if SGData['SGPolax']:
310        SGText.append(' The location of the origin is arbitrary in '+SGData['SGPolax'])
311    SGText.append(' ')
312    if SGData['SGLatt'] == 'P':
313        SGText.append(' The equivalent positions are:\n')
314    else:   
315        SGText.append(' The equivalent positions are:')
316        SGText.append(' ('+Latt2text(SGData['SGLatt'])+')+\n')
317    SGTable = []
318    for i,Opr in enumerate(SGData['SGOps']):
319        SGTable.append('(%2d) %s'%(i+1,MT2text(Opr)))
320    return SGText,SGTable
321
322def AllOps(SGData):
323    '''
324    Returns a list of all operators for a space group, including those for
325    centering and a center of symmetry
326   
327    :param SGData: from :func:`SpcGroup`
328    :returns: (SGTextList,offsetList,symOpList,G2oprList) where
329
330      * SGTextList: a list of strings with formatted and normalized
331        symmetry operators.
332      * offsetList: a tuple of (dx,dy,dz) offsets that relate the GSAS-II
333        symmetry operation to the operator in SGTextList and symOpList.
334        these dx (etc.) values are added to the GSAS-II generated
335        positions to provide the positions that are generated
336        by the normalized symmetry operators.       
337      * symOpList: a list of tuples with the normalized symmetry
338        operations as (M,T) values
339        (see ``SGOps`` in the :ref:`Space Group object<SGData_table>`)
340      * G2oprList: The GSAS-II operations for each symmetry operation as
341        a tuple with (center,mult,opnum), where center is (0,0,0), (0.5,0,0),
342        (0.5,0.5,0.5),...; where mult is 1 or -1 for the center of symmetry
343        and opnum is the number for the symmetry operation, in ``SGOps``
344        (starting with 0).
345    '''
346    SGTextList = []
347    offsetList = []
348    symOpList = []
349    G2oprList = []
350    onebar = (1,)
351    if SGData['SGInv']:
352        onebar += (-1,)
353    for cen in SGData['SGCen']:
354        for mult in onebar:
355            for j,(M,T) in enumerate(SGData['SGOps']):
356                offset = [0,0,0]
357                Tprime = (mult*T)+cen
358                for i in range(3):
359                    while Tprime[i] < 0:
360                        Tprime[i] += 1
361                        offset[i] += 1
362                    while Tprime[i] >= 1:
363                        Tprime[i] += -1
364                        offset[i] += -1
365                Opr = [mult*M,Tprime]
366                OPtxt = MT2text(Opr)
367                SGTextList.append(OPtxt.replace(' ',''))
368                offsetList.append(tuple(offset))
369                symOpList.append((mult*M,Tprime))
370                G2oprList.append((cen,mult,j))
371    return SGTextList,offsetList,symOpList,G2oprList
372   
373def MT2text(Opr):
374    "From space group matrix/translation operator returns text version"
375    XYZ = ('-Z','-Y','-X','X-Y','ERR','Y-X','X','Y','Z')
376    TRA = ('   ','ERR','1/6','1/4','1/3','ERR','1/2','ERR','2/3','3/4','5/6','ERR')
377    Fld = ''
378    M,T = Opr
379    for j in range(3):
380        IJ = int(round(2*M[j][0]+3*M[j][1]+4*M[j][2]+4))%12
381        IK = int(round(T[j]*12))%12
382        if IK:
383            if IJ < 3:
384                Fld += (TRA[IK]+XYZ[IJ]).rjust(5)
385            else:
386                Fld += (TRA[IK]+'+'+XYZ[IJ]).rjust(5)
387        else:
388            Fld += XYZ[IJ].rjust(5)
389        if j != 2: Fld += ', '
390    return Fld
391   
392def Latt2text(Latt):
393    "From lattice type ('P',A', etc.) returns ';' delimited cell centering vectors"
394    lattTxt = {'A':'0,0,0; 0,1/2,1/2','B':'0,0,0; 1/2,0,1/2',
395        'C':'0,0,0; 1/2,1/2,0','I':'0,0,0; 1/2,1/2,1/2',
396        'F':'0,0,0; 0,1/2,1/2; 1/2,0,1/2; 1/2,1/2,0',
397        'R':'0,0,0; 1/3,2/3,2/3; 2/3,1/3,1/3','P':'0,0,0'}
398    return lattTxt[Latt]   
399       
400def SpaceGroup(SGSymbol):
401    '''
402    Print the output of SpcGroup in a nicely formatted way.
403
404    :param SGSymbol: space group symbol (string) with spaces between axial fields
405    :returns: nothing
406    '''
407    E,A = SpcGroup(SGSymbol)
408    if E > 0:
409        print SGErrors(E)
410        return
411    for l in SGPrint(A):
412        print l
413       
414################################################################################
415#### Superspace group codes
416################################################################################
417       
418def SSpcGroup(SGData,SSymbol):
419    """
420    Determines supersymmetry information from superspace group name; currently only for (3+1) superlattices
421
422    :param SGData: space group data structure as defined in SpcGroup above.
423    :param SSymbol: superspace group symbol extension (string) defining modulation direction & generator info.
424    :returns: (SSGError,SSGData)
425       * SGError = 0 for no errors; >0 for errors (see SGErrors below for details)
426       * SSGData - is a dict (see :ref:`Superspace Group object<SSGData_table>`) with entries:
427       
428             * 'SSpGrp': superspace group symbol extension to space group symbol, accidental spaces removed
429             * 'SSGCen': 4D cell centering vectors [0,0,0,0] at least
430             * 'SSGOps': 4D symmetry operations as [M,T] so that M*x+T = x'
431
432    """
433   
434    def checkModSym():
435        '''
436        Checks to see if proposed modulation form is allowed for Laue group
437        '''
438        if LaueId in [0,] and LaueModId in [0,]:
439            return True
440        elif LaueId in [1,]:
441            try:
442                if modsym.index('1/2') != ['A','B','C'].index(SGData['SGLatt']):
443                    return False
444                if 'I'.index(SGData['SGLatt']) and modsym.count('1/2') not in [0,2]:
445                    return False
446            except ValueError:
447                pass
448            if SGData['SGUniq'] == 'a' and LaueModId in [5,6,7,8,9,10,]:
449                return True
450            elif SGData['SGUniq'] == 'b' and LaueModId in [3,4,13,14,15,16,]:
451                return True
452            elif SGData['SGUniq'] == 'c' and LaueModId in [1,2,19,20,21,22,]:
453                return True
454        elif LaueId in [2,] and LaueModId in [i+7 for i in range(18)]:
455            try:
456                if modsym.index('1/2') != ['A','B','C'].index(SGData['SGLatt']):
457                    return False
458                if SGData['SGLatt'] in ['I','F',] and modsym.index('1/2'):
459                    return False
460            except ValueError:
461                pass
462            return True
463        elif LaueId in [3,4,] and LaueModId in [19,22,]:
464            try:
465                if SGData['SGLatt'] == 'I' and modsym.count('1/2'):
466                    return False
467            except ValueError:
468                pass
469            return True
470        elif LaueId in [7,8,9,] and LaueModId in [19,25,]:
471            if (SGData['SGLatt'] == 'R' or SGData['SGPtGrp'] in ['3m1','-3m1']) and modsym.count('1/3'):
472                return False
473            return True
474        elif LaueId in [10,11,] and LaueModId in [19,]:
475            return True
476        return False
477       
478    def fixMonoOrtho():
479        mod = ''.join(modsym).replace('1/2','0').replace('1','0')
480        if SGData['SGPtGrp'] in ['2','m']:  #OK
481            if mod in ['a00','0b0','00g']:
482                result = [i*-1 for i in SGData['SSGKl']]
483            else:
484                result = SGData['SSGKl'][:]
485            if '/' in mod:
486                return [i*-1 for i in result]
487            else:
488                return result
489        elif SGData['SGPtGrp'] == '2/m':    #OK
490            if mod in ['a00','0b0','00g']:
491                result =  SGData['SSGKl'][:]
492            else:
493                result = [i*-1 for i in SGData['SSGKl']]
494            if '/' in mod:
495                return [i*-1 for i in result]
496            else:
497                return result
498        else:   #orthorhombic
499            if SGData['SGPtGrp'] == '222':
500                return [1 if i in ['a','b','g'] else -1 for i in mod]
501            elif SGData['SGPtGrp'] == 'mm2':
502                if 'g' in mod:
503                    return [1,1,1]
504                elif 'b' in mod:
505                    return [1,-1,-1]
506                else:
507                    return [-1,1,-1]
508            elif SGData['SGPtGrp'] == 'm2m':
509                if 'b' in mod:
510                    return [1,1,1]
511                elif 'g' in mod:
512                    return [1,-1,-1]
513                else:
514                    return [-1,-1,1]               
515            elif SGData['SGPtGrp'] == '2mm':
516                if 'a' in mod:
517                    return [1,1,1]
518                elif 'b' in mod:
519                    return [-1,-1,1]
520                else:
521                    return [-1,1,-1]
522            else:
523                return [-1 if i in ['a','b','g'] else 1 for i in mod]
524               
525    def extendSSGOps(SSGOps):
526        nOps = len(SSGOps)
527        for OpA in SSGOps[1:]:
528            OpAtxt = SSMT2text(OpA)
529            if 't' not in OpAtxt:
530                continue
531            for OpB in SSGOps[1:]:
532                OpBtxt = SSMT2text(OpB)
533                if 't' not in OpBtxt:
534                    continue
535                OpC = list(SGProd(OpA,OpB))
536                OpC[1] %= 1.
537                OpCtxt = SSMT2text(OpC)
538#                print OpAtxt.replace(' ','')+'*'+OpBtxt.replace(' ','')+'='+OpCtxt.replace(' ','')
539                for k,OpD in enumerate(SSGOps[1:]):
540                    OpDtxt = SSMT2text(OpD)
541                    if 't' in OpDtxt:
542                        continue
543#                    print '    ('+OpCtxt.replace(' ','')+'=? '+OpDtxt.replace(' ','')+')'
544                    if OpCtxt == OpDtxt:
545                        continue
546                    elif OpCtxt.split(',')[:3] == OpDtxt.split(',')[:3]:
547                        if 't' not in OpDtxt:
548                            SSGOps[k+1] = OpC
549#                            print k+1,'   new:',OpCtxt.replace(' ','')
550                        else:
551                            OpCtxt = OpCtxt.replace(' ','')
552                            OpDtxt = OpDtxt.replace(' ','')
553                            Txt = OpCtxt+' conflict with '+OpDtxt
554                            print Txt
555                            return False,Txt
556        return True,SSGOps
557               
558    def genSSGOps():
559        SSGOps = SSGData['SSGOps'][:]
560        iFrac = {}
561        for i,frac in enumerate(SSGData['modSymb']):
562            if frac in ['1/2','1/3','1/4','1/6','1']:
563                iFrac[i] = frac
564        print SGData['SpGrp']+SSymbol
565        print 'SSGKl',SSGKl,'genQ',genQ,'iFrac',iFrac,'modSymb',SSGData['modSymb']
566# set identity & 1,-1; triclinic
567        SSGOps[0][0][3,3] = 1.
568# expand if centrosymmetric
569        if SGData['SGInv']:
570            SSGOps += [[-1*M,V] for M,V in SSGOps[:]]
571# monoclinic - all done & all checked
572        if SGData['SGPtGrp'] in ['2','m']:  #OK
573            SSGOps[1][0][3,3] = SSGKl[0]
574            SSGOps[1][1][3] = genQ[0]
575            for i in iFrac:
576                SSGOps[1][0][3,i] = -SSGKl[0]
577        elif SGData['SGPtGrp'] == '2/m':    #OK
578            for i,j in enumerate([1,3]):
579                SSGOps[j][0][3,3] = -SSGKl[i]
580                if genQ[i]:
581                    SSGOps[j][1][3] = genQ[i]
582                for k in iFrac:
583                    SSGOps[j][0][3,k] = SSGKl[i]
584           
585# orthorhombic
586        elif SGData['SGPtGrp'] in ['222','mm2','m2m','2mm','mmm']:
587            for i in [0,1,2]:
588                SSGOps[i+1][0][3,3] = SSGKl[i]
589                SSGOps[i+1][1][3] = genQ[i]
590            for i in iFrac:
591                SSGOps[1][0][3,i] = -1
592            print SSMT2text(SSGOps[1]).replace(' ',''),SSMT2text(SSGOps[2]).replace(' ',''), \
593                SSMT2text(SSGOps[3]).replace(' ','')
594               
595# tetragonal
596        elif SGData['SGPtGrp'] == '4':  #OK
597            SSGOps[1][0][3,3] = SSGKl[0]
598            SSGOps[1][1][3] = genQ[0]
599            if '1/2' in SSGData['modSymb']:
600                SSGOps[1][0][3,1] = -1
601        elif SGData['SGPtGrp'] == '-4': #OK
602            SSGOps[1][0][3,3] = SSGKl[0]
603            if '1/2' in SSGData['modSymb']:
604                SSGOps[1][0][3,1] = 1
605        elif SGData['SGPtGrp'] in ['4/m',]: #OK
606            if '1/2' in SSGData['modSymb']:
607                SSGOps[1][0][3,1] = -SSGKl[0]
608            for i,j in enumerate([1,6]):
609                SSGOps[j][0][3,3] = SSGKl[i]
610                if genQ[i]:
611                    SSGOps[j][1][3] = genQ[i]
612                E,SSGOps = extendSSGOps(SSGOps)
613                if not E:
614                    return E,SSGOps
615        elif SGData['SGPtGrp'] in ['422','4mm','-42m','-4m2',]:
616            for i,j in enumerate([1,4,5]):
617                if '1/2' in SSGData['modSymb']:
618                    SSGOps[j][0][3,1] = -SSGKl[i]
619                if SGData['SGPtGrp'] in ['422','-42m','-4m2',]:
620                    SSGOps[j][0][3,3] = SSGKl[i]
621                    if i and genQ[2]:
622                        SSGOps[j][1][3] = genQ[i]
623                else:     #4mm               
624                    SSGOps[j][0][3,3] = -SSGKl[i]
625                    if genQ[i]:
626                        SSGOps[j][1][3] = genQ[i]
627                E,SSGOps = extendSSGOps(SSGOps)
628                if not E:
629                    return E,SSGOps
630        elif SGData['SGPtGrp'] in ['4/mmm',]:
631            if '1/2' in SSGData['modSymb']:
632                SSGOps[1][0][3,1] = -SSGKl[0]
633                SSGOps[6][0][3,1] = SSGKl[1]
634            for i,j in enumerate([1,10,6,7]):
635                SSGOps[j][0][3,3] = SSGKl[i]
636                if genQ[i]:
637                    SSGOps[j][1][3] = genQ[i]
638                E,Result = extendSSGOps(SSGOps)
639                if not E:
640                    for Op in SSGOps:
641                        print SSMT2text(Op)
642                    return E,Result
643                else:
644                    SSGOps = Result
645               
646# trigonal - all done
647        elif SGData['SGPtGrp'] == '3':  #OK
648            SSGOps[1][0][3,3] = SSGKl[0]
649            if '1/3' in SSGData['modSymb']:
650                SSGOps[1][0][3,1] = -1
651            SSGOps[1][1][3] = genQ[0]
652        elif SGData['SGPtGrp'] == '-3': #OK
653            SSGOps[1][0][3,3] = -SSGKl[0]
654            if '1/3' in SSGData['modSymb']:
655                SSGOps[1][0][3,1] = -1
656            SSGOps[1][1][3] = genQ[0]
657        elif SGData['SGPtGrp'] in ['312','3m','-3m','-3m1','3m1']:   #OK
658            if '1/3' in SSGData['modSymb']:
659                SSGOps[1][0][3,1] = -1
660            for i,j in enumerate([1,5]):
661                if SGData['SGPtGrp'] in ['3m','-3m']:
662                    SSGOps[j][0][3,3] = 1
663                else:                   
664                    SSGOps[j][0][3,3] = SSGKl[i+1]
665                if genQ[i]:
666                    SSGOps[j][1][3] = genQ[i]
667        elif SGData['SGPtGrp'] in ['321','32']:   #OK
668            for i,j in enumerate([1,4]):
669                SSGOps[j][0][3,3] = SSGKl[i]
670                if genQ[i]:
671                    SSGOps[j][1][3] = genQ[i]
672        elif SGData['SGPtGrp'] in ['31m','-31m']:   #OK
673            ids = [1,3]
674            if SGData['SGPtGrp'] == '-31m':
675                ids = [7,3]
676            if '1/3' in SSGData['modSymb']:
677                SSGOps[ids[0]][0][3,1] = -SSGKl[0]
678            for i,j in enumerate(ids):
679                SSGOps[j][0][3,3] = SSGKl[i]
680                if genQ[i+1]:
681                    SSGOps[j][1][3] = genQ[i+1]
682                     
683# hexagonal
684        elif SGData['SGPtGrp'] == '6':  #OK
685            SSGOps[1][0][3,3] = SSGKl[0]
686            SSGOps[1][1][3] = genQ[0]
687        elif SGData['SGPtGrp'] == '-6': #OK
688            SSGOps[1][0][3,3] = SSGKl[0]
689        elif SGData['SGPtGrp'] in ['6/m',]: #OK
690            SSGOps[1][0][3,3] = -SSGKl[1]
691            SSGOps[1][1][3] = genQ[0]
692            SSGOps[2][1][3] = genQ[1]
693        elif SGData['SGPtGrp'] in ['622','6mm','-62m','-6m2',]: #OK
694            for i,j in enumerate([1,10,11]):
695                SSGOps[j][0][3,3] = SSGKl[i]
696                if genQ[i]:
697                    SSGOps[j][1][3] = genQ[i]
698                E,SSGOps = extendSSGOps(SSGOps)
699        elif SGData['SGPtGrp'] in ['6/mmm',]: #not OK
700            for i,j in enumerate([1,15,10,11]):
701                SSGOps[j][0][3,3] = SSGKl[i]
702                if genQ[i]:
703                    SSGOps[j][1][3] = genQ[i]
704                E,SSGOps = extendSSGOps(SSGOps)
705        elif SGData['SGPtGrp'] in ['1','-1']: #triclinic - done
706            return True,SSGOps
707        E,SSGOps = extendSSGOps(SSGOps)
708        return E,SSGOps
709       
710    def specialGen(gensym):
711        sym = ''.join(gensym)
712        if SGData['SGPtGrp'] in ['2/m',] and 'n' in SGData['SpGrp']:
713            if 's' in sym:
714                gensym = 'ss'
715        if SGData['SGPtGrp'] in ['-62m',] and sym == '00s':
716            gensym = '0ss'
717        elif SGData['SGPtGrp'] in ['222',]:
718            if sym == '00s':
719                gensym = '0ss'
720            elif sym == '0s0':
721                gensym = 'ss0'
722            elif sym == 's00':
723                gensym = 's0s'
724        return gensym
725                   
726    def checkGen(gensym):
727        sym = ''.join(gensym)
728        print str(SSGKl),sym
729# monoclinic - all done
730        if str(SSGKl) == '[-1]' and sym == 's':
731            return False
732        elif SGData['SGPtGrp'] in ['2/m',]:
733            if str(SSGKl) == '[-1, 1]' and sym == '0s':
734                return False
735            elif str(SSGKl) == '[1, -1]' and sym == 's0':
736                return False
737#orthorhombic - all
738        elif SGData['SGPtGrp'] in ['222',] and sym not in ['','s00','0s0','00s']:
739            return False 
740        elif SGData['SGPtGrp'] in ['2mm','m2m','mm2','mmm'] and sym not in GenSymList[4:15]:
741            return False 
742#tetragonal - all done
743        elif SGData['SGPtGrp'] in ['4',] and sym not in ['','s','q']:
744            return False 
745        elif SGData['SGPtGrp'] in ['-4',] and sym not in ['',]:
746            return False             
747        elif SGData['SGPtGrp'] in ['4/m',] and sym not in ['','s0','q0']:
748            return False
749        elif SGData['SGPtGrp'] in ['422',] and sym not in ['','q00','s00']:
750            return False         
751        elif SGData['SGPtGrp'] in ['4mm',] and sym not in ['','ss0','s0s','0ss','qq0','qqs']:
752            return False
753        elif SGData['SGPtGrp'] in ['-4m2',] and sym not in ['','0s0','0q0']:
754            return False
755        elif SGData['SGPtGrp'] in ['-42m',] and sym not in ['','00s','00q']:
756            return False
757        elif SGData['SGPtGrp'] in ['4/mmm',] and sym not in ['','s00s','s0s0','00ss','000s',]:
758            return False
759#trigonal/rhombohedral - all done
760        elif SGData['SGPtGrp'] in ['3',] and sym not in ['','t']:
761            return False 
762        elif SGData['SGPtGrp'] in ['-3',] and sym not in ['',]:
763            return False 
764        elif SGData['SGPtGrp'] in ['32',] and sym not in ['','t0']:
765            return False 
766        elif SGData['SGPtGrp'] in ['321','312'] and sym not in ['','t00']:
767            return False 
768        elif SGData['SGPtGrp'] in ['3m','-3m'] and sym not in ['','0s']:
769            return False 
770        elif SGData['SGPtGrp'] in ['3m1','-3m1'] and sym not in ['','0s0']:
771            return False 
772        elif SGData['SGPtGrp'] in ['31m','-31m'] and sym not in ['','00s']:
773            return False 
774#hexagonal - all done
775        elif SGData['SGPtGrp'] in ['6',] and sym not in ['','s','h','t']:
776            return False 
777        elif SGData['SGPtGrp'] in ['-6',] and sym not in ['',]:
778            return False
779        elif SGData['SGPtGrp'] in ['6/m',] and sym not in ['','s0']:
780            return False
781        elif SGData['SGPtGrp'] in ['622',] and sym not in ['','h00','t00','s00']:
782            return False         
783        elif SGData['SGPtGrp'] in ['6mm',] and sym not in ['','ss0','s0s','0ss']:
784            return False
785        elif SGData['SGPtGrp'] in ['-6m2',] and sym not in ['','0s0']:
786            return False
787        elif SGData['SGPtGrp'] in ['-62m',] and sym not in ['','00s']:
788            return False
789        elif SGData['SGPtGrp'] in ['6/mmm',] and sym not in ['','s00s','s0s0','00ss']:
790            return False
791        return True
792       
793    LaueModList = [
794        'abg','ab0','ab1/2','a0g','a1/2g',  '0bg','1/2bg','a00','a01/2','a1/20',
795        'a1/21/2','a01','a10','0b0','0b1/2', '1/2b0','1/2b1/2','0b1','1b0','00g',
796        '01/2g','1/20g','1/21/2g','01g','10g', '1/31/3g']
797    LaueList = ['-1','2/m','mmm','4/m','4/mmm','3R','3mR','3','3m1','31m','6/m','6/mmm','m3','m3m']
798    GenSymList = ['','s','0s','s0', '00s','0s0','s00','s0s','ss0','0ss','q00','0q0','00q','qq0','q0q', '0qq',
799        'q','qqs','s0s0','00ss','s00s','000s','t','t00','t0','h','h00']
800    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.}
801    LaueId = LaueList.index(SGData['SGLaue'])
802    if SGData['SGLaue'] in ['m3','m3m']:
803        return '(3+1) superlattices not defined for cubic space groups',None
804    elif SGData['SGLaue'] in ['3R','3mR']:
805        return '(3+1) superlattices not defined for rhombohedral settings - use hexagonal setting',None
806    try:
807        modsym,gensym = splitSSsym(SSymbol)
808    except ValueError:
809        return 'Error in superspace symbol '+SSymbol,None
810    if ''.join(gensym) not in GenSymList:
811        return 'unknown generator symbol '+''.join(gensym),None
812    try:
813        print modsym,''.join(modsym)
814        LaueModId = LaueModList.index(''.join(modsym))
815    except ValueError:
816        return 'Unknown modulation symbol '+''.join(modsym),None
817    if not checkModSym():
818        return 'Modulation '+''.join(modsym)+' not consistent with space group '+SGData['SpGrp'],None
819    modQ = [Fracs[mod] for mod in modsym]
820    SSGKl = SGData['SSGKl'][:]
821    if SGData['SGLaue'] in ['2/m','mmm']:
822        SSGKl = fixMonoOrtho()
823    if len(gensym) and len(gensym) != len(SSGKl):
824        return 'Wrong number of items in generator symbol '+''.join(gensym),None
825    if not checkGen(gensym):
826        return 'Generator '+''.join(gensym)+' not consistent with space group '+SGData['SpGrp'],None
827    gensym = specialGen(gensym)
828    genQ = [Fracs[mod] for mod in gensym]
829    if not genQ:
830        genQ = [0,0,0,0]
831    SSGData = {'SSpGrp':SGData['SpGrp']+SSymbol,'modQ':modQ,'modSymb':modsym}
832    SSCen = np.zeros((len(SGData['SGCen']),4))
833    for icen,cen in enumerate(SGData['SGCen']):
834        SSCen[icen,0:3] = cen
835    SSCen[0] = np.zeros(4)
836    SSGData['SSGCen'] = SSCen
837    SSGData['SSGOps'] = []
838    for iop,op in enumerate(SGData['SGOps']):
839        T = np.zeros(4)
840        ssop = np.zeros((4,4))
841        ssop[:3,:3] = op[0]
842        T[:3] = op[1]
843        SSGData['SSGOps'].append([ssop,T])
844    E,Result = genSSGOps()
845    if E:
846        SSGData['SSGOps'] = Result                     
847        return None,SSGData
848    else:
849        return Result+'\nOperator conflict - incorrect superspace symbol',None
850
851def splitSSsym(SSymbol):
852    '''
853    Splits supersymmetry symbol into two lists of strings
854    '''
855    modsym,gensym = SSymbol.replace(' ','').split(')')
856    nfrac = modsym.count('/')
857    modsym = modsym.lstrip('(')
858    if nfrac == 0:
859        modsym = list(modsym)
860    elif nfrac == 1:
861        pos = modsym.find('/')
862        if pos == 1:
863            modsym = [modsym[:3],modsym[3],modsym[4]]
864        elif pos == 2:
865            modsym = [modsym[0],modsym[1:4],modsym[4]]
866        else:
867            modsym = [modsym[0],modsym[1],modsym[2:]]
868    else:
869        lpos = modsym.find('/')
870        rpos = modsym.rfind('/')
871        if lpos == 1 and rpos == 4:
872            modsym = [modsym[:3],modsym[3:6],modsym[6]]
873        elif lpos == 1 and rpos == 5:
874            modsym = [modsym[:3],modsym[3],modsym[4:]]
875        else:
876            modsym = [modsym[0],modsym[1:4],modsym[4:]]
877    gensym = list(gensym)
878    return modsym,gensym
879       
880def SSGPrint(SGData,SSGData):
881    '''
882    Print the output of SSpcGroup in a nicely formatted way. Used in SSpaceGroup
883
884    :param SGData: space group data structure as defined in SpcGroup above.
885    :param SSGData: from :func:`SSpcGroup`
886    :returns:
887        SSGText - list of strings with the superspace group details
888        SGTable - list of strings for each of the operations
889    '''
890    Mult = len(SSGData['SSGCen'])*len(SSGData['SSGOps'])
891    SSGText = []
892    SSGText.append(' Superspace Group: '+SSGData['SSpGrp'])
893    CentStr = 'centrosymmetric'
894    if not SGData['SGInv']:
895        CentStr = 'non'+CentStr
896    if SGData['SGLatt'] in 'ABCIFR':
897        SSGText.append(' The lattice is '+CentStr+' '+SGData['SGLatt']+'-centered '+SGData['SGSys'].lower())
898    else:
899        SSGText.append(' The superlattice is '+CentStr+' '+'primitive '+SGData['SGSys'].lower())       
900    SSGText.append(' The Laue symmetry is '+SGData['SGLaue'])
901    SSGText.append(' The superlattice point group is '+SGData['SGPtGrp']+','+''.join([str(i) for i in SGData['SSGKl']]))
902    SSGText.append(' The number of superspace group generators is '+str(len(SGData['SSGKl'])))
903    SSGText.append(' Multiplicity of a general site is '+str(Mult))
904    if SGData['SGUniq'] in ['a','b','c']:
905        SSGText.append(' The unique monoclinic axis is '+SGData['SGUniq'])
906    if SGData['SGInv']:
907        SSGText.append(' The inversion center is located at 0,0,0')
908    if SGData['SGPolax']:
909        SSGText.append(' The location of the origin is arbitrary in '+SGData['SGPolax'])
910    SSGText.append(' ')
911    if len(SSGData['SSGCen']) > 1:
912        SSGText.append(' The equivalent positions are:')
913        SSGText.append(' ('+SSLatt2text(SSGData['SSGCen'])+')+\n')
914    else:
915        SSGText.append(' The equivalent positions are:\n')
916    SSGTable = []
917    for i,Opr in enumerate(SSGData['SSGOps']):
918        SSGTable.append('(%2d) %s'%(i+1,SSMT2text(Opr)))
919    return SSGText,SSGTable
920   
921def SSGModCheck(Vec,modSymb):
922    ''' Checks modulation vector compatibility with supersymmetry space group symbol.
923    Superspace group symbol takes precidence & the vector will be modified accordingly
924    '''
925    Fracs = {'1/2':0.5,'1/3':1./3,'1':1.0,'0':0.,'a':0.,'b':0.,'g':0.}
926    modQ = [Fracs[mod] for mod in modSymb]
927    Vec = [0.1 if (vec == 0.0 and mod in ['a','b','g']) else vec for [vec,mod] in zip(Vec,modSymb)]
928    return [Q if mod not in ['a','b','g'] and vec != Q else vec for [vec,mod,Q] in zip(Vec,modSymb,modQ)],  \
929        [True if mod in ['a','b','g'] else False for mod in modSymb]
930
931def SSMT2text(Opr):
932    "From superspace group matrix/translation operator returns text version"
933    XYZS = ('x','y','z','t')    #Stokes, Campbell & van Smaalen notation
934    TRA = ('   ','ERR','1/6','1/4','1/3','ERR','1/2','ERR','2/3','3/4','5/6','ERR')
935    Fld = ''
936    M,T = Opr
937    for j in range(4):
938        IJ = ''
939        for k in range(4):
940            txt = str(int(round(M[j][k])))
941            txt = txt.replace('1',XYZS[k]).replace('0','')
942            if '2' in txt:
943                txt += XYZS[k]
944            if IJ and M[j][k] > 0:
945                IJ += '+'+txt
946            else:
947                IJ += txt
948        IK = int(round(T[j]*12))%12
949        if IK:
950            if not IJ:
951                break
952            if IJ[0] == '-':
953                Fld += (TRA[IK]+IJ).rjust(8)
954            else:
955                Fld += (TRA[IK]+'+'+IJ).rjust(8)
956        else:
957            Fld += IJ.rjust(8)
958        if j != 3: Fld += ', '
959    return Fld
960   
961def SSLatt2text(SSGCen):
962    "Lattice centering vectors to text"
963    lattTxt = ''
964    for vec in SSGCen:
965        lattTxt += ' '
966        for item in vec:
967            if int(item*12.):
968                lattTxt += '1/%d,'%(12/int(item*12))
969            else:
970                lattTxt += '0,'
971        lattTxt = lattTxt.rstrip(',')
972        lattTxt += ';'
973    lattTxt = lattTxt.rstrip(';').lstrip(' ')
974    return lattTxt
975       
976def SSpaceGroup(SGSymbol,SSymbol):
977    '''
978    Print the output of SSpcGroup in a nicely formatted way.
979
980    :param SGSymbol: space group symbol with spaces between axial fields.
981    :param SSymbol: superspace group symbol extension (string).
982    :returns: nothing
983    '''
984
985    E,A = SpcGroup(SGSymbol)
986    if E > 0:
987        print SGErrors(E)
988        return
989    E,B = SSpcGroup(A,SSymbol)   
990    if E > 0:
991        print E
992        return
993    for l in SSGPrint(B):
994        print l
995       
996def SGProd(OpA,OpB):
997    '''
998    Form space group operator product. OpA & OpB are [M,V] pairs;
999        both must be of same dimension (3 or 4). Returns [M,V] pair
1000    '''
1001    A,U = OpA
1002    B,V = OpB
1003    M = np.inner(B,A)
1004    W = np.inner(B,U)+V
1005    return M,W
1006       
1007def MoveToUnitCell(xyz):
1008    '''
1009    Translates a set of coordinates so that all values are >=0 and < 1
1010
1011    :param xyz: a list or numpy array of fractional coordinates
1012    :returns: XYZ - numpy array of new coordinates now 0 or greater and less than 1
1013    '''
1014    XYZ = np.zeros(3)
1015    for i,x in enumerate(xyz):
1016        XYZ[i] = (x-int(x))%1.0
1017    return XYZ
1018       
1019def Opposite(XYZ,toler=0.0002):
1020    '''
1021    Gives opposite corner, edge or face of unit cell for position within tolerance.
1022        Result may be just outside the cell within tolerance
1023
1024    :param XYZ: 0 >= np.array[x,y,z] > 1 as by MoveToUnitCell
1025    :param toler: unit cell fraction tolerance making opposite
1026    :returns:
1027        XYZ: array of opposite positions; always contains XYZ
1028    '''
1029    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]]
1030    TB = np.where(abs(XYZ-1)<toler,-1,0)+np.where(abs(XYZ)<toler,1,0)
1031    perm = TB*perm3
1032    cperm = ['%d%d%d'%(i,j,k) for i,j,k in perm]
1033    D = dict(zip(cperm,perm))
1034    new = []
1035    for key in D:
1036        new.append(np.array(D[key])+np.array(XYZ))
1037    return new
1038       
1039def GenAtom(XYZ,SGData,All=False,Uij=[],Move=True):
1040    '''
1041    Generates the equivalent positions for a specified coordinate and space group
1042
1043    :param XYZ: an array, tuple or list containing 3 elements: x, y & z
1044    :param SGData: from :func:`SpcGroup`
1045    :param All: True return all equivalent positions including duplicates;
1046      False return only unique positions
1047    :param Uij: [U11,U22,U33,U12,U13,U23] or [] if no Uij
1048    :param Move: True move generated atom positions to be inside cell
1049      False do not move atoms       
1050    :return: [[XYZEquiv],Idup,[UijEquiv]]
1051
1052      *  [XYZEquiv] is list of equivalent positions (XYZ is first entry)
1053      *  Idup = [-][C]SS where SS is the symmetry operator number (1-24), C (if not 0,0,0)
1054      * is centering operator number (1-4) and - is for inversion
1055        Cell = unit cell translations needed to put new positions inside cell
1056        [UijEquiv] - equivalent Uij; absent if no Uij given
1057       
1058    '''
1059    XYZEquiv = []
1060    UijEquiv = []
1061    Idup = []
1062    Cell = []
1063    X = np.array(XYZ)
1064    if Move:
1065        X = MoveToUnitCell(X)
1066    for ic,cen in enumerate(SGData['SGCen']):
1067        C = np.array(cen)
1068        for invers in range(int(SGData['SGInv']+1)):
1069            for io,[M,T] in enumerate(SGData['SGOps']):
1070                idup = ((io+1)+100*ic)*(1-2*invers)
1071                XT = np.inner(M,X)+T
1072                if len(Uij):
1073                    U = Uij2U(Uij)
1074                    U = np.inner(M,np.inner(U,M).T)
1075                    newUij = U2Uij(U)
1076                if invers:
1077                    XT = -XT
1078                XT += C
1079                if Move:
1080                    newX = MoveToUnitCell(XT)
1081                else:
1082                    newX = XT
1083                cell = np.asarray(np.rint(newX-XT),dtype=np.int32)
1084                if All:
1085                    if np.allclose(newX,X,atol=0.0002):
1086                        idup = False
1087                else:
1088                    if True in [np.allclose(newX,oldX,atol=0.0002) for oldX in XYZEquiv]:
1089                        idup = False
1090                if All or idup:
1091                    XYZEquiv.append(newX)
1092                    Idup.append(idup)
1093                    Cell.append(cell)
1094                    if len(Uij):
1095                        UijEquiv.append(newUij)                   
1096    if len(Uij):
1097        return zip(XYZEquiv,UijEquiv,Idup,Cell)
1098    else:
1099        return zip(XYZEquiv,Idup,Cell)
1100
1101def GenHKLf(HKL,SGData):
1102    '''
1103    Uses old GSAS Fortran routine genhkl.for
1104
1105    :param HKL:  [h,k,l]
1106    :param SGData: space group data obtained from SpcGroup
1107    :returns: iabsnt,mulp,Uniq,phi
1108
1109     *   iabsnt = True if reflection is forbidden by symmetry
1110     *   mulp = reflection multiplicity including Friedel pairs
1111     *   Uniq = numpy array of equivalent hkl in descending order of h,k,l
1112
1113    '''
1114    hklf = HKL+[0,]
1115    Ops = SGData['SGOps']
1116    OpM = np.array([op[0] for op in Ops])
1117    OpT = np.array([op[1] for op in Ops])
1118    Inv = SGData['SGInv']
1119    Cen = np.array([cen for cen in SGData['SGCen']])
1120   
1121    Nuniq,Uniq,iabsnt,mulp = pyspg.genhklpy(hklf,len(Ops),OpM,OpT,SGData['SGInv'],len(Cen),Cen)
1122    h,k,l,f = Uniq
1123    Uniq=np.array(zip(h[:Nuniq],k[:Nuniq],l[:Nuniq]))
1124    phi = f[:Nuniq]
1125   
1126    return iabsnt,mulp,Uniq,phi
1127                                 
1128def GetOprPtrName(key):
1129    'Needs a doc string'
1130    OprPtrName = {
1131        '-6643':[   2,' 1bar ', 1],'6479' :[  10,'  2z  ', 2],'-6479':[   9,'  mz  ', 3],
1132        '6481' :[   7,'  my  ', 4],'-6481':[   6,'  2y  ', 5],'6641' :[   4,'  mx  ', 6],
1133        '-6641':[   3,'  2x  ', 7],'6591' :[  28,' m+-0 ', 8],'-6591':[  27,' 2+-0 ', 9],
1134        '6531' :[  25,' m110 ',10],'-6531':[  24,' 2110 ',11],'6537' :[  61,'  4z  ',12],
1135        '-6537':[  62,' -4z  ',13],'975'  :[  68,' 3+++1',14],'6456' :[ 114,'  3z1 ',15],
1136        '-489' :[  73,' 3+-- ',16],'483'  :[  78,' 3-+- ',17],'-969' :[  83,' 3--+ ',18],
1137        '819'  :[  22,' m+0- ',19],'-819' :[  21,' 2+0- ',20],'2431' :[  16,' m0+- ',21],
1138        '-2431':[  15,' 20+- ',22],'-657' :[  19,' m101 ',23],'657'  :[  18,' 2101 ',24],
1139        '1943' :[  48,' -4x  ',25],'-1943':[  47,'  4x  ',26],'-2429':[  13,' m011 ',27],
1140        '2429' :[  12,' 2011 ',28],'639'  :[  55,' -4y  ',29],'-639' :[  54,'  4y  ',30],
1141        '-6484':[ 146,' 2010 ', 4],'6484' :[ 139,' m010 ', 5],'-6668':[ 145,' 2100 ', 6],
1142        '6668' :[ 138,' m100 ', 7],'-6454':[ 148,' 2120 ',18],'6454' :[ 141,' m120 ',19],
1143        '-6638':[ 149,' 2210 ',20],'6638' :[ 142,' m210 ',21],              #search ends here
1144        '2223' :[  68,' 3+++2',39],
1145        '6538' :[ 106,'  6z1 ',40],'-2169':[  83,' 3--+2',41],'2151' :[  73,' 3+--2',42],
1146        '2205' :[  79,'-3-+-2',43],'-2205':[  78,' 3-+-2',44],'489'  :[  74,'-3+--1',45],
1147        '801'  :[  53,'  4y1 ',46],'1945' :[  47,'  4x3 ',47],'-6585':[  62,' -4z3 ',48],
1148        '6585' :[  61,'  4z3 ',49],'6584' :[ 114,'  3z2 ',50],'6666' :[ 106,'  6z5 ',51],
1149        '6643' :[   1,' Iden ',52],'-801' :[  55,' -4y1 ',53],'-1945':[  48,' -4x3 ',54],
1150        '-6666':[ 105,' -6z5 ',55],'-6538':[ 105,' -6z1 ',56],'-2223':[  69,'-3+++2',57],
1151        '-975' :[  69,'-3+++1',58],'-6456':[ 113,' -3z1 ',59],'-483' :[  79,'-3-+-1',60],
1152        '969'  :[  84,'-3--+1',61],'-6584':[ 113,' -3z2 ',62],'2169' :[  84,'-3--+2',63],
1153        '-2151':[  74,'-3+--2',64],'0':[0,' ????',0]
1154        }
1155    return OprPtrName[key]
1156
1157def GetKNsym(key):
1158    'Needs a doc string'
1159    KNsym = {
1160        '0'         :'    1   ','1'         :'   -1   ','64'        :'    2(x)','32'        :'    m(x)',
1161        '97'        :'  2/m(x)','16'        :'    2(y)','8'         :'    m(y)','25'        :'  2/m(y)',
1162        '2'         :'    2(z)','4'         :'    m(z)','7'         :'  2/m(z)','134217728' :'   2(yz)',
1163        '67108864'  :'   m(yz)','201326593' :' 2/m(yz)','2097152'   :'  2(0+-)','1048576'   :'  m(0+-)',
1164        '3145729'   :'2/m(0+-)','8388608'   :'   2(xz)','4194304'   :'   m(xz)','12582913'  :' 2/m(xz)',
1165        '524288'    :'  2(+0-)','262144'    :'  m(+0-)','796433'    :'2/m(+0-)','1024'      :'   2(xy)',
1166        '512'       :'   m(xy)','1537'      :' 2/m(xy)','256'       :'  2(+-0)','128'       :'  m(+-0)',
1167        '385'       :'2/m(+-0)','76'        :'  mm2(x)','52'        :'  mm2(y)','42'        :'  mm2(z)',
1168        '135266336' :' mm2(yz)','69206048'  :'mm2(0+-)','8650760'   :' mm2(xz)','4718600'   :'mm2(+0-)',
1169        '1156'      :' mm2(xy)','772'       :'mm2(+-0)','82'        :'  222   ','136314944' :'  222(x)',
1170        '8912912'   :'  222(y)','1282'      :'  222(z)','127'       :'  mmm   ','204472417' :'  mmm(x)',
1171        '13369369'  :'  mmm(y)','1927'      :'  mmm(z)','33554496'  :'  4(100)','16777280'  :' -4(100)',
1172        '50331745'  :'4/m(100)','169869394' :'422(100)','84934738'  :'-42m 100','101711948' :'4mm(100)',
1173        '254804095' :'4/mmm100','536870928 ':'  4(010)','268435472' :' -4(010)','805306393' :'4/m (10)',
1174        '545783890' :'422(010)','272891986' :'-42m 010','541327412' :'4mm(010)','818675839' :'4/mmm010',
1175        '2050'      :'  4(001)','4098'      :' -4(001)','6151'      :'4/m(001)','3410'      :'422(001)',
1176        '4818'      :'-42m 001','2730'      :'4mm(001)','8191'      :'4/mmm001','8192'      :'  3(111)',
1177        '8193'      :' -3(111)','2629888'   :' 32(111)','1319040'   :' 3m(111)','3940737'   :'-3m(111)',
1178        '32768'     :'  3(+--)','32769'     :' -3(+--)','10519552'  :' 32(+--)','5276160'   :' 3m(+--)',
1179        '15762945'  :'-3m(+--)','65536'     :'  3(-+-)','65537'     :' -3(-+-)','134808576' :' 32(-+-)',
1180        '67437056'  :' 3m(-+-)','202180097' :'-3m(-+-)','131072'    :'  3(--+)','131073'    :' -3(--+)',
1181        '142737664' :' 32(--+)','71434368'  :' 3m(--+)','214040961' :'-3m(--+)','237650'    :'   23   ',
1182        '237695'    :'   m3   ','715894098' :'   432  ','358068946' :'  -43m  ','1073725439':'   m3m  ',
1183        '68157504'  :' mm2d100','4456464'   :' mm2d010','642'       :' mm2d001','153092172' :'-4m2 100',
1184        '277348404' :'-4m2 010','5418'      :'-4m2 001','1075726335':'  6/mmm ','1074414420':'-6m2 100',
1185        '1075070124':'-6m2 120','1075069650':'   6mm  ','1074414890':'   622  ','1073758215':'   6/m  ',
1186        '1073758212':'   -6   ','1073758210':'    6   ','1073759865':'-3m(100)','1075724673':'-3m(120)',
1187        '1073758800':' 3m(100)','1075069056':' 3m(120)','1073759272':' 32(100)','1074413824':' 32(120)',
1188        '1073758209':'   -3   ','1073758208':'    3   ','1074135143':'mmm(100)','1075314719':'mmm(010)',
1189        '1073743751':'mmm(110)','1074004034':' mm2z100','1074790418':' mm2z010','1073742466':' mm2z110',
1190        '1074004004':'mm2(100)','1074790412':'mm2(010)','1073742980':'mm2(110)','1073872964':'mm2(120)',
1191        '1074266132':'mm2(210)','1073742596':'mm2(+-0)','1073872930':'222(100)','1074266122':'222(010)',
1192        '1073743106':'222(110)','1073741831':'2/m(001)','1073741921':'2/m(100)','1073741849':'2/m(010)',
1193        '1073743361':'2/m(110)','1074135041':'2/m(120)','1075314689':'2/m(210)','1073742209':'2/m(+-0)',
1194        '1073741828':' m(001) ','1073741888':' m(100) ','1073741840':' m(010) ','1073742336':' m(110) ',
1195        '1074003968':' m(120) ','1074790400':' m(210) ','1073741952':' m(+-0) ','1073741826':' 2(001) ',
1196        '1073741856':' 2(100) ','1073741832':' 2(010) ','1073742848':' 2(110) ','1073872896':' 2(120) ',
1197        '1074266112':' 2(210) ','1073742080':' 2(+-0) ','1073741825':'   -1   '
1198        }
1199    return KNsym[key]       
1200
1201def GetNXUPQsym(siteSym):       
1202    'Needs a doc string'
1203    NXUPQsym = {
1204        '    1   ':(28,29,28,28),'   -1   ':( 1,29,28, 0),'    2(x)':(12,18,12,25),'    m(x)':(25,18,12,25),
1205        '  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),
1206        '    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),
1207        '   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),
1208        '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),
1209        '  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),
1210        '   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),
1211        '2/m(+-0)':( 1,20, 0,-1),'  mm2(x)':(12,10, 0,-1),'  mm2(y)':(13,10, 0,-1),'  mm2(z)':(14,10, 0,-1),
1212        ' mm2(yz)':(10,13, 0,-1),'mm2(0+-)':(11,13, 0,-1),' mm2(xz)':( 8,12, 0,-1),'mm2(+0-)':( 9,12, 0,-1),
1213        ' mm2(xy)':( 6,11, 0,-1),'mm2(+-0)':( 7,11, 0,-1),'  222   ':( 1,10, 0,-1),'  222(x)':( 1,13, 0,-1),
1214        '  222(y)':( 1,12, 0,-1),'  222(z)':( 1,11, 0,-1),'  mmm   ':( 1,10, 0,-1),'  mmm(x)':( 1,13, 0,-1),
1215        '  mmm(y)':( 1,12, 0,-1),'  mmm(z)':( 1,11, 0,-1),'  4(100)':(12, 4,12, 0),' -4(100)':( 1, 4,12, 0),
1216        '4/m(100)':( 1, 4,12,-1),'422(100)':( 1, 4, 0,-1),'-42m 100':( 1, 4, 0,-1),'4mm(100)':(12, 4, 0,-1),
1217        '4/mmm100':( 1, 4, 0,-1),'  4(010)':(13, 3,13, 0),' -4(010)':( 1, 3,13, 0),'4/m (10)':( 1, 3,13,-1),
1218        '422(010)':( 1, 3, 0,-1),'-42m 010':( 1, 3, 0,-1),'4mm(010)':(13, 3, 0,-1),'4/mmm010':(1, 3, 0,-1,),
1219        '  4(001)':(14, 2,14, 0),' -4(001)':( 1, 2,14, 0),'4/m(001)':( 1, 2,14,-1),'422(001)':( 1, 2, 0,-1),
1220        '-42m 001':( 1, 2, 0,-1),'4mm(001)':(14, 2, 0,-1),'4/mmm001':( 1, 2, 0,-1),'  3(111)':( 2, 5, 2, 0),
1221        ' -3(111)':( 1, 5, 2, 0),' 32(111)':( 1, 5, 0, 2),' 3m(111)':( 2, 5, 0, 2),'-3m(111)':( 1, 5, 0,-1),
1222        '  3(+--)':( 5, 8, 5, 0),' -3(+--)':( 1, 8, 5, 0),' 32(+--)':( 1, 8, 0, 5),' 3m(+--)':( 5, 8, 0, 5),
1223        '-3m(+--)':( 1, 8, 0,-1),'  3(-+-)':( 4, 7, 4, 0),' -3(-+-)':( 1, 7, 4, 0),' 32(-+-)':( 1, 7, 0, 4),
1224        ' 3m(-+-)':( 4, 7, 0, 4),'-3m(-+-)':( 1, 7, 0,-1),'  3(--+)':( 3, 6, 3, 0),' -3(--+)':( 1, 6, 3, 0),
1225        ' 32(--+)':( 1, 6, 0, 3),' 3m(--+)':( 3, 6, 0, 3),'-3m(--+)':( 1, 6, 0,-1),'   23   ':( 1, 1, 0, 0),
1226        '   m3   ':( 1, 1, 0, 0),'   432  ':( 1, 1, 0, 0),'  -43m  ':( 1, 1, 0, 0),'   m3m  ':( 1, 1, 0, 0),
1227        ' mm2d100':(12,13, 0,-1),' mm2d010':(13,12, 0,-1),' mm2d001':(14,11, 0,-1),'-4m2 100':( 1, 4, 0,-1),
1228        '-4m2 010':( 1, 3, 0,-1),'-4m2 001':( 1, 2, 0,-1),'  6/mmm ':( 1, 9, 0,-1),'-6m2 100':( 1, 9, 0,-1),
1229        '-6m2 120':( 1, 9, 0,-1),'   6mm  ':(14, 9, 0,-1),'   622  ':( 1, 9, 0,-1),'   6/m  ':( 1, 9,14,-1),
1230        '   -6   ':( 1, 9,14, 0),'    6   ':(14, 9,14, 0),'-3m(100)':( 1, 9, 0,-1),'-3m(120)':( 1, 9, 0,-1),
1231        ' 3m(100)':(14, 9, 0,14),' 3m(120)':(14, 9, 0,14),' 32(100)':( 1, 9, 0,14),' 32(120)':( 1, 9, 0,14),
1232        '   -3   ':( 1, 9,14, 0),'    3   ':(14, 9,14, 0),'mmm(100)':( 1,14, 0,-1),'mmm(010)':( 1,15, 0,-1),
1233        'mmm(110)':( 1,11, 0,-1),' mm2z100':(14,14, 0,-1),' mm2z010':(14,15, 0,-1),' mm2z110':(14,11, 0,-1),
1234        'mm2(100)':(12,14, 0,-1),'mm2(010)':(13,15, 0,-1),'mm2(110)':( 6,11, 0,-1),'mm2(120)':(15,14, 0,-1),
1235        'mm2(210)':(16,15, 0,-1),'mm2(+-0)':( 7,11, 0,-1),'222(100)':( 1,14, 0,-1),'222(010)':( 1,15, 0,-1),
1236        '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),
1237        '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),
1238        ' m(001) ':(23,16,14,23),' m(100) ':(26,25,12,26),' m(010) ':(27,28,13,27),' m(110) ':(18,19, 6,18),
1239        ' m(120) ':(24,27,15,24),' m(210) ':(25,26,16,25),' m(+-0) ':(17,20, 7,17),' 2(001) ':(14,16,14,23),
1240        ' 2(100) ':(12,25,12,26),' 2(010) ':(13,28,13,27),' 2(110) ':( 6,19, 6,18),' 2(120) ':(15,27,15,24),
1241        ' 2(210) ':(16,26,16,25),' 2(+-0) ':( 7,20, 7,17),'   -1   ':( 1,29,28, 0)
1242        }
1243    return NXUPQsym[siteSym]
1244
1245def GetCSxinel(siteSym): 
1246    'Needs a doc string'
1247    CSxinel = [[],                         # 0th empty - indices are Fortran style
1248        [[0,0,0],[ 0.0, 0.0, 0.0]],      #1  0  0  0
1249        [[1,1,1],[ 1.0, 1.0, 1.0]],      #2  X  X  X
1250        [[1,1,1],[ 1.0, 1.0,-1.0]],      #3  X  X -X
1251        [[1,1,1],[ 1.0,-1.0, 1.0]],      #4  X -X  X
1252        [[1,1,1],[ 1.0,-1.0,-1.0]],      #5 -X  X  X
1253        [[1,1,0],[ 1.0, 1.0, 0.0]],      #6  X  X  0
1254        [[1,1,0],[ 1.0,-1.0, 0.0]],      #7  X -X  0
1255        [[1,0,1],[ 1.0, 0.0, 1.0]],      #8  X  0  X
1256        [[1,0,1],[ 1.0, 0.0,-1.0]],      #9  X  0 -X
1257        [[0,1,1],[ 0.0, 1.0, 1.0]],      #10  0  Y  Y
1258        [[0,1,1],[ 0.0, 1.0,-1.0]],      #11 0  Y -Y
1259        [[1,0,0],[ 1.0, 0.0, 0.0]],      #12  X  0  0
1260        [[0,1,0],[ 0.0, 1.0, 0.0]],      #13  0  Y  0
1261        [[0,0,1],[ 0.0, 0.0, 1.0]],      #14  0  0  Z
1262        [[1,1,0],[ 1.0, 2.0, 0.0]],      #15  X 2X  0
1263        [[1,1,0],[ 2.0, 1.0, 0.0]],      #16 2X  X  0
1264        [[1,1,2],[ 1.0, 1.0, 1.0]],      #17  X  X  Z
1265        [[1,1,2],[ 1.0,-1.0, 1.0]],      #18  X -X  Z
1266        [[1,2,1],[ 1.0, 1.0, 1.0]],      #19  X  Y  X
1267        [[1,2,1],[ 1.0, 1.0,-1.0]],      #20  X  Y -X
1268        [[1,2,2],[ 1.0, 1.0, 1.0]],      #21  X  Y  Y
1269        [[1,2,2],[ 1.0, 1.0,-1.0]],      #22  X  Y -Y
1270        [[1,2,0],[ 1.0, 1.0, 0.0]],      #23  X  Y  0
1271        [[1,0,2],[ 1.0, 0.0, 1.0]],      #24  X  0  Z
1272        [[0,1,2],[ 0.0, 1.0, 1.0]],      #25  0  Y  Z
1273        [[1,1,2],[ 1.0, 2.0, 1.0]],      #26  X 2X  Z
1274        [[1,1,2],[ 2.0, 1.0, 1.0]],      #27 2X  X  Z
1275        [[1,2,3],[ 1.0, 1.0, 1.0]],      #28  X  Y  Z
1276        ]
1277    indx = GetNXUPQsym(siteSym)
1278    return CSxinel[indx[0]]
1279   
1280def GetCSuinel(siteSym):
1281    "returns Uij terms, multipliers, GUI flags & Uiso2Uij multipliers"
1282    CSuinel = [[],                                             # 0th empty - indices are Fortran style
1283        [[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
1284        [[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
1285        [[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
1286        [[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
1287        [[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
1288        [[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
1289        [[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
1290        [[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
1291        [[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
1292        [[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
1293        [[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
1294        [[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
1295        [[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
1296        [[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
1297        [[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
1298        [[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
1299        [[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
1300        [[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
1301        [[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
1302        [[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
1303        [[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
1304        [[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
1305        [[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
1306        [[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
1307        [[1,2,3,2,4,4],[ 1.0, 1.0, 1.0, 0.5, 0.5, 1.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
1308        [[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
1309        [[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
1310        [[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
1311        [[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
1312        ]
1313    indx = GetNXUPQsym(siteSym)
1314    return CSuinel[indx[1]]
1315   
1316def MustrainNames(SGData):
1317    'Needs a doc string'
1318    laue = SGData['SGLaue']
1319    uniq = SGData['SGUniq']
1320    if laue in ['m3','m3m']:
1321        return ['S400','S220']
1322    elif laue in ['6/m','6/mmm','3m1']:
1323        return ['S400','S004','S202']
1324    elif laue in ['31m','3']:
1325        return ['S400','S004','S202','S211']
1326    elif laue in ['3R','3mR']:
1327        return ['S400','S220','S310','S211']
1328    elif laue in ['4/m','4/mmm']:
1329        return ['S400','S004','S220','S022']
1330    elif laue in ['mmm']:
1331        return ['S400','S040','S004','S220','S202','S022']
1332    elif laue in ['2/m']:
1333        SHKL = ['S400','S040','S004','S220','S202','S022']
1334        if uniq == 'a':
1335            SHKL += ['S013','S031','S211']
1336        elif uniq == 'b':
1337            SHKL += ['S301','S103','S121']
1338        elif uniq == 'c':
1339            SHKL += ['S130','S310','S112']
1340        return SHKL
1341    else:
1342        SHKL = ['S400','S040','S004','S220','S202','S022']
1343        SHKL += ['S310','S103','S031','S130','S301','S013']
1344        SHKL += ['S211','S121','S112']
1345        return SHKL
1346       
1347def HStrainVals(HSvals,SGData):
1348    laue = SGData['SGLaue']
1349    uniq = SGData['SGUniq']
1350    DIJ = np.zeros(6)
1351    if laue in ['m3','m3m']:
1352        DIJ[:3] = [HSvals[0],HSvals[0],HSvals[0]]
1353    elif laue in ['6/m','6/mmm','3m1','31m','3']:
1354        DIJ[:4] = [HSvals[0],HSvals[0],HSvals[1],HSvals[0]]
1355    elif laue in ['3R','3mR']:
1356        DIJ = [HSvals[0],HSvals[0],HSvals[0],HSvals[1],HSvals[1],HSvals[1]]
1357    elif laue in ['4/m','4/mmm']:
1358        DIJ[:3] = [HSvals[0],HSvals[0],HSvals[1]]
1359    elif laue in ['mmm']:
1360        DIJ[:3] = [HSvals[0],HSvals[1],HSvals[2]]
1361    elif laue in ['2/m']:
1362        DIJ[:3] = [HSvals[0],HSvals[1],HSvals[2]]
1363        if uniq == 'a':
1364            DIJ[5] = HSvals[3]
1365        elif uniq == 'b':
1366            DIJ[4] = HSvals[3]
1367        elif uniq == 'c':
1368            DIJ[3] = HSvals[3]
1369    else:
1370        DIJ = [HSvals[0],HSvals[1],HSvals[2],HSvals[3],HSvals[4],HSvals[5]]
1371    return DIJ
1372
1373def HStrainNames(SGData):
1374    'Needs a doc string'
1375    laue = SGData['SGLaue']
1376    uniq = SGData['SGUniq']
1377    if laue in ['m3','m3m']:
1378        return ['D11','eA']         #add cubic strain term
1379    elif laue in ['6/m','6/mmm','3m1','31m','3']:
1380        return ['D11','D33']
1381    elif laue in ['3R','3mR']:
1382        return ['D11','D12']
1383    elif laue in ['4/m','4/mmm']:
1384        return ['D11','D33']
1385    elif laue in ['mmm']:
1386        return ['D11','D22','D33']
1387    elif laue in ['2/m']:
1388        Dij = ['D11','D22','D33']
1389        if uniq == 'a':
1390            Dij += ['D23']
1391        elif uniq == 'b':
1392            Dij += ['D13']
1393        elif uniq == 'c':
1394            Dij += ['D12']
1395        return Dij
1396    else:
1397        Dij = ['D11','D22','D33','D12','D13','D23']
1398        return Dij
1399   
1400def MustrainCoeff(HKL,SGData):
1401    'Needs a doc string'
1402    #NB: order of terms is the same as returned by MustrainNames
1403    laue = SGData['SGLaue']
1404    uniq = SGData['SGUniq']
1405    h,k,l = HKL
1406    Strm = []
1407    if laue in ['m3','m3m']:
1408        Strm.append(h**4+k**4+l**4)
1409        Strm.append(3.0*((h*k)**2+(h*l)**2+(k*l)**2))
1410    elif laue in ['6/m','6/mmm','3m1']:
1411        Strm.append(h**4+k**4+2.0*k*h**3+2.0*h*k**3+3.0*(h*k)**2)
1412        Strm.append(l**4)
1413        Strm.append(3.0*((h*l)**2+(k*l)**2+h*k*l**2))
1414    elif laue in ['31m','3']:
1415        Strm.append(h**4+k**4+2.0*k*h**3+2.0*h*k**3+3.0*(h*k)**2)
1416        Strm.append(l**4)
1417        Strm.append(3.0*((h*l)**2+(k*l)**2+h*k*l**2))
1418        Strm.append(4.0*h*k*l*(h+k))
1419    elif laue in ['3R','3mR']:
1420        Strm.append(h**4+k**4+l**4)
1421        Strm.append(3.0*((h*k)**2+(h*l)**2+(k*l)**2))
1422        Strm.append(2.0*(h*l**3+l*k**3+k*h**3)+2.0*(l*h**3+k*l**3+l*k**3))
1423        Strm.append(4.0*(k*l*h**2+h*l*k**2+h*k*l**2))
1424    elif laue in ['4/m','4/mmm']:
1425        Strm.append(h**4+k**4)
1426        Strm.append(l**4)
1427        Strm.append(3.0*(h*k)**2)
1428        Strm.append(3.0*((h*l)**2+(k*l)**2))
1429    elif laue in ['mmm']:
1430        Strm.append(h**4)
1431        Strm.append(k**4)
1432        Strm.append(l**4)
1433        Strm.append(3.0*(h*k)**2)
1434        Strm.append(3.0*(h*l)**2)
1435        Strm.append(3.0*(k*l)**2)
1436    elif laue in ['2/m']:
1437        Strm.append(h**4)
1438        Strm.append(k**4)
1439        Strm.append(l**4)
1440        Strm.append(3.0*(h*k)**2)
1441        Strm.append(3.0*(h*l)**2)
1442        Strm.append(3.0*(k*l)**2)
1443        if uniq == 'a':
1444            Strm.append(2.0*k*l**3)
1445            Strm.append(2.0*l*k**3)
1446            Strm.append(4.0*k*l*h**2)
1447        elif uniq == 'b':
1448            Strm.append(2.0*l*h**3)
1449            Strm.append(2.0*h*l**3)
1450            Strm.append(4.0*h*l*k**2)
1451        elif uniq == 'c':
1452            Strm.append(2.0*h*k**3)
1453            Strm.append(2.0*k*h**3)
1454            Strm.append(4.0*h*k*l**2)
1455    else:
1456        Strm.append(h**4)
1457        Strm.append(k**4)
1458        Strm.append(l**4)
1459        Strm.append(3.0*(h*k)**2)
1460        Strm.append(3.0*(h*l)**2)
1461        Strm.append(3.0*(k*l)**2)
1462        Strm.append(2.0*k*h**3)
1463        Strm.append(2.0*h*l**3)
1464        Strm.append(2.0*l*k**3)
1465        Strm.append(2.0*h*k**3)
1466        Strm.append(2.0*l*h**3)
1467        Strm.append(2.0*k*l**3)
1468        Strm.append(4.0*k*l*h**2)
1469        Strm.append(4.0*h*l*k**2)
1470        Strm.append(4.0*k*h*l**2)
1471    return Strm
1472   
1473def Muiso2Shkl(muiso,SGData,cell):
1474    "this is to convert isotropic mustrain to generalized Shkls"
1475    import GSASIIlattice as G2lat
1476    A = G2lat.cell2AB(cell)[0]
1477   
1478    def minMus(Shkl,muiso,H,SGData,A):
1479        U = np.inner(A.T,H)
1480        S = np.array(MustrainCoeff(U,SGData))
1481        Sum = np.sqrt(np.sum(np.multiply(S,Shkl[:,np.newaxis]),axis=0))
1482        rad = np.sqrt(np.sum((Sum[:,np.newaxis]*H)**2,axis=1))
1483        return (muiso-rad)**2
1484       
1485    laue = SGData['SGLaue']
1486    PHI = np.linspace(0.,360.,60,True)
1487    PSI = np.linspace(0.,180.,60,True)
1488    X = np.outer(npsind(PHI),npsind(PSI))
1489    Y = np.outer(npcosd(PHI),npsind(PSI))
1490    Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
1491    HKL = np.dstack((X,Y,Z))
1492    if laue in ['m3','m3m']:
1493        S0 = [1000.,1000.]
1494    elif laue in ['6/m','6/mmm','3m1']:
1495        S0 = [1000.,1000.,1000.]
1496    elif laue in ['31m','3']:
1497        S0 = [1000.,1000.,1000.,1000.]
1498    elif laue in ['3R','3mR']:
1499        S0 = [1000.,1000.,1000.,1000.]
1500    elif laue in ['4/m','4/mmm']:
1501        S0 = [1000.,1000.,1000.,1000.]
1502    elif laue in ['mmm']:
1503        S0 = [1000.,1000.,1000.,1000.,1000.,1000.]
1504    elif laue in ['2/m']:
1505        S0 = [1000.,1000.,1000.,0.,0.,0.,0.,0.,0.]
1506    else:
1507        S0 = [1000.,1000.,1000.,1000.,1000., 1000.,1000.,1000.,1000.,1000., 
1508            1000.,1000.,0.,0.,0.]
1509    S0 = np.array(S0)
1510    HKL = np.reshape(HKL,(-1,3))
1511    result = so.leastsq(minMus,S0,(np.ones(HKL.shape[0])*muiso,HKL,SGData,A))
1512    return result[0]
1513       
1514def SytSym(XYZ,SGData):
1515    '''
1516    Generates the number of equivalent positions and a site symmetry code for a specified coordinate and space group
1517
1518    :param XYZ: an array, tuple or list containing 3 elements: x, y & z
1519    :param SGData: from SpcGroup
1520    :Returns: a two element tuple:
1521
1522     * The 1st element is a code for the site symmetry (see GetKNsym)
1523     * The 2nd element is the site multiplicity
1524
1525    '''
1526    def PackRot(SGOps):
1527        IRT = []
1528        for ops in SGOps:
1529            M = ops[0]
1530            irt = 0
1531            for j in range(2,-1,-1):
1532                for k in range(2,-1,-1):
1533                    irt *= 3
1534                    irt += M[k][j]
1535            IRT.append(int(irt))
1536        return IRT
1537       
1538    SymName = ''
1539    Mult = 1
1540    Isym = 0
1541    if SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']:
1542        Isym = 1073741824
1543    Jdup = 0
1544    Xeqv = GenAtom(XYZ,SGData,True)
1545    IRT = PackRot(SGData['SGOps'])
1546    L = -1
1547    for ic,cen in enumerate(SGData['SGCen']):
1548        for invers in range(int(SGData['SGInv']+1)):
1549            for io,ops in enumerate(SGData['SGOps']):
1550                irtx = (1-2*invers)*IRT[io]
1551                L += 1
1552                if not Xeqv[L][1]:
1553                    Jdup += 1
1554                    jx = GetOprPtrName(str(irtx))
1555                    if jx[2] < 39:
1556                        Isym += 2**(jx[2]-1)
1557    if Isym == 1073741824: Isym = 0
1558    Mult = len(SGData['SGOps'])*len(SGData['SGCen'])*(int(SGData['SGInv'])+1)/Jdup
1559         
1560    return GetKNsym(str(Isym)),Mult
1561   
1562def ElemPosition(SGData):
1563    ''' Under development.
1564    Object here is to return a list of symmetry element types and locations suitable
1565    for say drawing them.
1566    So far I have the element type... getting all possible locations without lookup may be impossible!
1567    '''
1568    SymElements = []
1569    Inv = SGData['SGInv']
1570    Cen = SGData['SGCen']
1571    eleSym = {-3:['','-1'],-2:['',-6],-1:['2','-4'],0:['3','-3'],1:['4','m'],2:['6',''],3:['1','']}
1572    # get operators & expand if centrosymmetric
1573    Ops = SGData['SGOps']
1574    opM = np.array([op[0].T for op in Ops])
1575    opT = np.array([op[1] for op in Ops])
1576    if Inv:
1577        opM = np.concatenate((opM,-opM))
1578        opT = np.concatenate((opT,-opT))
1579    opMT = zip(opM,opT)
1580    for M,T in opMT[1:]:        #skip I
1581        Dt = int(nl.det(M))
1582        Tr = int(np.trace(M))
1583        Dt = -(Dt-1)/2
1584        Es = eleSym[Tr][Dt]
1585        if Dt:              #rotation-inversion
1586            I = np.eye(3)
1587            if Tr == 1:     #mirrors/glides
1588                if np.any(T):       #glide
1589                    M2 = np.inner(M,M)
1590                    MT = np.inner(M,T)+T
1591                    print 'glide',Es,MT
1592                    print M2
1593                else:               #mirror
1594                    print 'mirror',Es,T
1595                    print I-M
1596                X = [-1,-1,-1]
1597            elif Tr == -3:  # pure inversion
1598                X = np.inner(nl.inv(I-M),T)
1599                print 'inversion',Es,X
1600            else:           #other rotation-inversion
1601                M2 = np.inner(M,M)
1602                MT = np.inner(M,T)+T
1603                print 'rot-inv',Es,MT
1604                print M2
1605                X = [-1,-1,-1]
1606        else:               #rotations
1607            print 'rotation',Es
1608            X = [-1,-1,-1]
1609        #SymElements.append([Es,X])
1610       
1611    return #SymElements
1612   
1613def ApplyStringOps(A,SGData,X,Uij=[]):
1614    'Needs a doc string'
1615    SGOps = SGData['SGOps']
1616    SGCen = SGData['SGCen']
1617    Ax = A.split('+')
1618    Ax[0] = int(Ax[0])
1619    iC = 0
1620    if Ax[0] < 0:
1621        iC = 1
1622    Ax[0] = abs(Ax[0])
1623    nA = Ax[0]%100-1
1624    cA = Ax[0]/100
1625    Cen = SGCen[cA]
1626    M,T = SGOps[nA]
1627    if len(Ax)>1:
1628        cellA = Ax[1].split(',')
1629        cellA = np.array([int(a) for a in cellA])
1630    else:
1631        cellA = np.zeros(3)
1632    newX = (1-2*iC)*(Cen+np.inner(M,X)+T)+cellA
1633    if len(Uij):
1634        U = Uij2U(Uij)
1635        U = np.inner(M,np.inner(U,M).T)
1636        newUij = U2Uij(U)
1637        return [newX,newUij]
1638    else:
1639        return newX
1640       
1641def StringOpsProd(A,B,SGData):
1642    """
1643    Find A*B where A & B are in strings '-' + '100*c+n' + '+ijk'
1644    where '-' indicates inversion, c(>0) is the cell centering operator,
1645    n is operator number from SgOps and ijk are unit cell translations (each may be <0).
1646    Should return resultant string - C. SGData - dictionary using entries:
1647
1648       *  'SGCen': cell centering vectors [0,0,0] at least
1649       *  'SGOps': symmetry operations as [M,T] so that M*x+T = x'
1650
1651    """
1652    SGOps = SGData['SGOps']
1653    SGCen = SGData['SGCen']
1654    #1st split out the cell translation part & work on the operator parts
1655    Ax = A.split('+'); Bx = B.split('+')
1656    Ax[0] = int(Ax[0]); Bx[0] = int(Bx[0])
1657    iC = 0
1658    if Ax[0]*Bx[0] < 0:
1659        iC = 1
1660    Ax[0] = abs(Ax[0]); Bx[0] = abs(Bx[0])
1661    nA = Ax[0]%100-1;  nB = Bx[0]%100-1
1662    cA = Ax[0]/100;  cB = Bx[0]/100
1663    Cen = (SGCen[cA]+SGCen[cB])%1.0
1664    cC = np.nonzero([np.allclose(C,Cen) for C in SGCen])[0][0]
1665    Ma,Ta = SGOps[nA]; Mb,Tb = SGOps[nB]
1666    Mc = np.inner(Ma,Mb.T)
1667#    print Ma,Mb,Mc
1668    Tc = (np.add(np.inner(Mb,Ta)+1.,Tb))%1.0
1669#    print Ta,Tb,Tc
1670#    print [np.allclose(M,Mc)&np.allclose(T,Tc) for M,T in SGOps]
1671    nC = np.nonzero([np.allclose(M,Mc)&np.allclose(T,Tc) for M,T in SGOps])[0][0]
1672    #now the cell translation part
1673    if len(Ax)>1:
1674        cellA = Ax[1].split(',')
1675        cellA = [int(a) for a in cellA]
1676    else:
1677        cellA = [0,0,0]
1678    if len(Bx)>1:
1679        cellB = Bx[1].split(',')
1680        cellB = [int(b) for b in cellB]
1681    else:
1682        cellB = [0,0,0]
1683    cellC = np.add(cellA,cellB)
1684    C = str(((nC+1)+(100*cC))*(1-2*iC))+'+'+ \
1685        str(int(cellC[0]))+','+str(int(cellC[1]))+','+str(int(cellC[2]))
1686    return C
1687           
1688def U2Uij(U):
1689    #returns the UIJ vector U11,U22,U33,U12,U13,U23 from tensor U
1690    return [U[0][0],U[1][1],U[2][2],2.*U[0][1],2.*U[0][2],2.*U[1][2]]
1691   
1692def Uij2U(Uij):
1693    #returns the thermal motion tensor U from Uij as numpy array
1694    return np.array([[Uij[0],Uij[3]/2.,Uij[4]/2.],[Uij[3]/2.,Uij[1],Uij[5]/2.],[Uij[4]/2.,Uij[5]/2.,Uij[2]]])
1695
1696def StandardizeSpcName(spcgroup):
1697    '''Accept a spacegroup name where spaces may have not been used
1698    in the names according to the GSAS convention (spaces between symmetry
1699    for each axis) and return the space group name as used in GSAS
1700    '''
1701    rspc = spcgroup.replace(' ','').upper()
1702    # deal with rhombohedral and hexagonal setting designations
1703    rhomb = ''
1704    if rspc[-1:] == 'R':
1705        rspc = rspc[:-1]
1706        rhomb = ' R'
1707    elif rspc[-1:] == 'H': # hexagonal is assumed and thus can be ignored
1708        rspc = rspc[:-1]
1709    # look for a match in the spacegroup lists
1710    for i in spglist.values():
1711        for spc in i:
1712            if rspc == spc.replace(' ','').upper():
1713                return spc + rhomb
1714    # how about the post-2002 orthorhombic names?
1715    for i,spc in sgequiv_2002_orthorhombic:
1716        if rspc == i.replace(' ','').upper():
1717            return spc
1718    # not found
1719    return ''
1720
1721   
1722spglist = {}
1723'''A dictionary of space groups as ordered and named in the pre-2002 International
1724Tables Volume A, except that spaces are used following the GSAS convention to
1725separate the different crystallographic directions.
1726Note that the symmetry codes here will recognize many non-standard space group
1727symbols with different settings. They are ordered by Laue group
1728'''
1729spglist = {
1730    'P1' : ('P 1','P -1',), # 1-2
1731    'P2/m': ('P 2','P 21','P m','P a','P c','P n',
1732        '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
1733    'C2/m':('C 2','C m','C c','C n',
1734        'C 2/m','C 2/c','C 2/n',),
1735    'Pmmm':('P 2 2 2',
1736        'P 2 2 21','P 2 21 2','P 21 2 2',
1737        'P 21 21 2','P 21 2 21','P 2 21 21',
1738        'P 21 21 21',
1739        'P m m 2','P m 2 m','P 2 m m',
1740        'P m c 21','P c m 21','P 21 m a','P 21 a m','P b 21 m','P m 21 b',
1741        'P c c 2','P 2 a a','P b 2 b',
1742        'P m a 2','P b m 2','P 2 m b','P 2 c m','P c 2 m','P m 2 a',
1743        'P c a 21','P b c 21','P 21 a b','P 21 c a','P c 21 b','P b 21 a',
1744        'P n c 2','P c n 2','P 2 n a','P 2 a n','P b 2 n','P n 2 b',
1745        'P m n 21','P n m 21','P 21 m n','P 21 n m','P n 21 m','P m 21 n',
1746        'P b a 2','P 2 c b','P c 2 a',
1747        'P n a 21','P b n 21','P 21 n b','P 21 c n','P c 21 n','P n 21 a',
1748        'P n n 2','P 2 n n','P n 2 n',
1749        'P m m m','P n n n',
1750        'P c c m','P m a a','P b m b',
1751        'P b a n','P n c b','P c n a',
1752        'P m m a','P m m b','P b m m','P c m m','P m c m','P m a m',
1753        'P n n a','P n n b','P b n n','P c n n','P n c n','P n a n',
1754        'P m n a','P n m b','P b m n','P c n m','P n c m','P m a n',
1755        'P c c a','P c c b','P b a a','P c a a','P b c b','P b a b',
1756        'P b a m','P m c b','P c m a',
1757        'P c c n','P n a a','P b n b',
1758        'P b c m','P c a m','P m c a','P m a b','P b m a','P c m b',
1759        'P n n m','P m n n','P n m n',
1760        'P m m n','P n m m','P m n m',
1761        'P b c n','P c a n','P n c a','P n a b','P b n a','P c n b',
1762        'P b c a','P c a b',
1763        'P n m a','P m n b','P b n m','P c m n','P m c n','P n a m',
1764        ),
1765    'Cmmm':('C 2 2 21','C 2 2 2','C m m 2','C m c 21','C c c 2','C m 2 m','C 2 m m',
1766        'C m 2 a','C 2 m b','C 2 c m','C c 2 m','C 2 c m',
1767        'C m c a','C m m m','C c c m','C m m a','C c c a','C m c m',),
1768    'Immm':('I 2 2 2','I 21 21 21','I m m m',
1769        'I m m 2','I m 2 m','I 2 m m',
1770        'I b a 2','I 2 c b','I c 2 a',
1771        'I m a 2','I b m 2','I 2 m b','I 2 c m','I c 2 m','I m 2 a',
1772        'I b a m','I m c b','I c m a',
1773        'I b c a','I c a b',
1774        'I m m a','I m m b','I b m m ','I c m m','I m c m','I m a m',),
1775    'Fmmm':('F 2 2 2','F m m m', 'F d d d',
1776        'F m m 2','F m 2 m','F 2 m m',
1777        'F d d 2','F d 2 d','F 2 d d',),
1778    'P4/mmm':('P 4','P 41','P 42','P 43','P -4','P 4/m','P 42/m','P 4/n','P 42/n',
1779        'P 4 2 2','P 4 21 2','P 41 2 2','P 41 21 2','P 42 2 2',
1780        'P 42 21 2','P 43 2 2','P 43 21 2','P 4 m m','P 4 b m','P 42 c m',
1781        'P 42 n m','P 4 c c','P 4 n c','P 42 m c','P 42 b c','P -4 2 m',
1782        'P -4 2 c','P -4 21 m','P -4 21 c','P -4 m 2','P -4 c 2','P -4 b 2',
1783        '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',
1784        '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',
1785        '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',
1786        'P 42/n c m',),
1787    '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',
1788        'I 4 c m','I 41 m d','I 41 c d',
1789        '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',
1790        'I 41/a m d','I 41/a c d'),
1791    'R3-H':('R 3','R -3','R 3 2','R 3 m','R 3 c','R -3 m','R -3 c',),
1792    'P6/mmm': ('P 3','P 31','P 32','P -3','P 3 1 2','P 3 2 1','P 31 1 2',
1793        'P 31 2 1','P 32 1 2','P 32 2 1', 'P 3 m 1','P 3 1 m','P 3 c 1',
1794        'P 3 1 c','P -3 1 m','P -3 1 c','P -3 m 1','P -3 c 1','P 6','P 61',
1795        'P 65','P 62','P 64','P 63','P -6','P 6/m','P 63/m','P 6 2 2',
1796        'P 61 2 2','P 65 2 2','P 62 2 2','P 64 2 2','P 63 2 2','P 6 m m',
1797        'P 6 c c','P 63 c m','P 63 m c','P -6 m 2','P -6 c 2','P -6 2 m',
1798        'P -6 2 c','P 6/m m m','P 6/m c c','P 63/m c m','P 63/m m c',),
1799    'Pm3m': ('P 2 3','P 21 3','P m 3','P n 3','P a 3','P 4 3 2','P 42 3 2',
1800        'P 43 3 2','P 41 3 2','P -4 3 m','P -4 3 n','P m 3 m','P n 3 n',
1801        'P m 3 n','P n 3 m',),
1802    'Im3m':('I 2 3','I 21 3','I m -3','I a -3', 'I 4 3 2','I 41 3 2',
1803        'I -4 3 m', 'I -4 3 d','I m -3 m','I m 3 m','I a -3 d',),
1804    'Fm3m':('F 2 3','F m -3','F d -3','F 4 3 2','F 41 3 2','F -4 3 m',
1805        'F -4 3 c','F m -3 m','F m 3 m','F m -3 c','F d -3 m','F d -3 c',),
1806}
1807
1808ssdict = {}
1809'''A dictionary of superspace group symbols allowed for each entry in spglist
1810(except cubics). Monoclinics are all b-unique setting.
1811'''
1812ssdict = {
1813    'P 1':['(abg)',],'P -1':['(abg)',],
1814    #monoclinic - done
1815    'P 2':['(a0g)','(a1/2g)','(0b0)','(0b0)s','(1/2b0)','(0b1/2)','(1/2b0)s','(0b1/2)s',],
1816    'P 21':['(a0g)','(0b0)','(0b0)s','(1/2b0)','(0b1/2)','(1/2b0)s','(0b1/2)s',],
1817    'P m':['(a0g)','(a0g)s','(a1/2g)','(a1/2g)s','(0b0)','(1/2b0)','(0b1/2)',],
1818    'P a':['(a0g)','(a1/2g)','(a0g)s','(a1/2g)s','(0b0)','(0b1/2)',],
1819    'P c':['(a0g)','(a1/2g)','(a0g)s','(a1/2g)s','(0b0)','(1/2b0)',],
1820    'P n':['(a0g)','(a1/2g)','(a0g)s','(a1/2g)s','(0b0)','(1/2b1/2)',],
1821    'P 2/m':['(a0g)','(a1/2g)','(a0g)0s','(a1/2g)0s',
1822        '(0b0)','(0b0)s0','(1/2b0)','(0b1/2)','(1/2b0)s0','(0b1/2)s0',],
1823    'P 21/m':['(a0g)','(a0g)0s','(0b0)','(0b0)s0',
1824        '(1/2b0)','(0b1/2)','(1/2b0)s0','(0b1/2)s0'],
1825    'P 2/c':['(a0g)','(a1/2g)','(a0g)0s','(a1/2g)0s',
1826        '(0b0)','(0b0)s0','(1/2b0)','(1/2b0)s0',],
1827    'P 2/a':['(a0g)','(a1/2g)','(a0g)0s','(a1/2g)0s',
1828        '(0b0)','(0b0)s0','(0b1/2)','(0b1/2)s0',],
1829    'P 2/n':['(a0g)','(a1/2g)','(a0g)0s','(a1/2g)0s',
1830        '(0b0)','(0b0)s0','(1/2b1/2)','(1/2b1/2)s0',],
1831    'P 21/c':['(a0g)','(a0g)0s','(0b0)','(0b0)s0','(1/2b0)','(1/2b0)s0',],
1832    'P 21/a':['(a0g)','(a0g)0s','(0b0)','(0b0)s0','(0b1/2)','(0b1/2)s0',],
1833    'P 21/n':['(a0g)','(a0g)0s','(0b0)','(0b0)s0','(1/2b1/2)','(1/2b1/2)s0',],
1834    'C 2':['(a0g)','(0b0)','(0b0)s','(0b1/2)','(0b1/2)s',],
1835    'C m':['(a0g)','(a0g)s','(0b0)','(0b1/2)',],
1836    'C c':['(a0g)','(a0g)s','(0b0)',],
1837    'C n':['(a0g)','(a0g)s','(0b0)',],
1838    'C 2/m':['(a0g)','(a0g)0s','(0b0)','(0b0)s0','(0b1/2)','(0b1/2)s0',],
1839    'C 2/c':['(a0g)','(a0g)0s','(0b0)','(0b0)s0',],
1840    'C 2/n':['(a0g)','(a0g)0s','(0b0)','(0b0)s0',],
1841    #orthorhombic   
1842    'P 2 2 2':['(a00)','(0b0)','(00g)','(a00)s00','(0b0)0s0','(00g)00s',
1843        '(a1/20)','(a01/2)','(0b1/2)','(1/2b0)','(01/2g)','(1/20g)',
1844        '(a1/21/2)','(1/2b1/2)','(1/21/2g)',],
1845       
1846    'P 2 2 21':['(a00)','(0b0)','(00g)','(a00)s00','(0b0)0s0',
1847        '(a1/20)','(1/2b0)','(01/2g)','(1/20g)','(1/21/2g)',],
1848    'P 2 21 2':['(a00)','(0b0)','(00g)','(a00)s00','(00g)00s',
1849        '(a01/2)','(1/20g)','(0b1/2)','(1/2b0)','(1/2b1/2)',],
1850    'P 21 2 2':['(a00)','(0b0)','(00g)','(0b0)0s0','(00g)00s',
1851        '(01/2g)','(0b1/2)','(a01/2)','(a1/20)','(a1/21/2)',],
1852       
1853    'P 21 21 2':['(a00)','(0b0)','(00g)','(00g)00s','(a01/2)','(0b1/2)',],
1854    'P 21 2 21':['(a00)','(0b0)','(00g)','(0b0)0s0','(a1/20)','(01/2g)',],
1855    'P 2 21 21':['(a00)','(0b0)','(00g)','(a00)s00','(1/2b0)','(1/20g)',],
1856       
1857    'P 21 21 21':['(a00)','(0b0)','(00g)',],
1858       
1859    'P m m 2':['(a00)','(0b0)','(00g)',
1860        '(a00)s00','(0b0)0s0','(00g)s0s','(00g)ss0','(00g)0ss',
1861        '(a1/20)','(a01/2)','(0b1/2)','(1/2b0)','(01/2g)','(1/20g)',
1862        '(a1/21/2)','(1/2b1/2)','(1/21/2g)',],
1863    'P m 2 m':[],
1864    'P 2 m m':[],
1865       
1866    'P m c 21':[],
1867    'P c m 21':[],
1868    'P 21 m a':[],
1869    'P 21 a m':[],
1870    'P b 21 m':[],
1871    'P m 21 b':[],
1872       
1873    'P c c 2':[],
1874    'P 2 a a':[],
1875    'P b 2 b':[],
1876       
1877    'P m a 2':[],
1878    'P b m 2':[],
1879    'P 2 m b':[],
1880    'P 2 c m':[],
1881    'P c 2 m':[],
1882    'P m 2 a':[],
1883       
1884    'P c a 21':[],
1885    'P b c 21':[],
1886    'P 21 a b':[],
1887    'P 21 c a':[],
1888    'P c 21 b':[],
1889    'P b 21 a':[],
1890       
1891    'P n c 2':[],
1892    'P c n 2':[],
1893    'P 2 n a':[],
1894    'P 2 a n':[],
1895    'P b 2 n':[],
1896    'P n 2 b':[],
1897       
1898    'P m n 21':[],
1899    'P n m 21':[],
1900    'P 21 m n':[],
1901    'P 21 n m':[],
1902    'P n 21 m':[],
1903    'P m 21 n':[],
1904       
1905    'P b a 2':[],
1906    'P 2 c b':[],
1907    'P c 2 a':[],
1908       
1909    'P n a 21':[],
1910    'P b n 21':[],
1911    'P 21 n b':[],
1912    'P 21 c n':[],
1913    'P c 21 n':[],
1914    'P n 21 a':[],
1915       
1916    'P n n 2':[],
1917    'P 2 n n':[],
1918    'P n 2 n':[],
1919       
1920    'P m m m':[],
1921       
1922    'P n n n':[],
1923       
1924    'P c c m':[],
1925    'P m a a':[],
1926    'P b m b':[],
1927       
1928    'P b a n':[],
1929    'P n c b':[],
1930    'P c n a':[],
1931       
1932    'P m m a':[],
1933    'P m m b':[],
1934    'P b m m':[],
1935    'P c m m':[],
1936    'P m c m':[],
1937    'P m a m':[],
1938       
1939    'P n n a':[],
1940    'P n n b':[],
1941    'P b n n':[],
1942    'P c n n':[],
1943    'P n c n':[],
1944    'P n a n':[],
1945       
1946    'P m n a':[],
1947    'P n m b':[],
1948    'P b m n':[],
1949    'P c n m':[],
1950    'P n c m':[],
1951    'P m a n':[],
1952       
1953    'P c c a':[],
1954    'P c c b':[],
1955    'P b a a':[],
1956    'P c a a':[],
1957    'P b c b':[],
1958    'P b a b':[],
1959       
1960    'P b a m':[],
1961    'P m c b':[],
1962    'P c m a':[],
1963       
1964    'P c c n':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1965        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1966    'P n a a':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1967        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1968    'P b n b':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1969        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1970       
1971    'P b c m':[],
1972    'P c a m':[],
1973    'P m c a':[],
1974    'P m a b':[],
1975    'P b m a':[],
1976    'P c m b':[],
1977       
1978    'P n n m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1979        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1980    'P m n n':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1981        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1982    'P n m n':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1983        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1984       
1985    'P m m n':[],
1986    'P n m m':[],
1987    'P m n m':[],
1988       
1989    'P b c n':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1990        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1991    'P c a n':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1992        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1993    'P n c a':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1994        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1995    'P n a b':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1996        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1997    'P b n a':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
1998        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
1999    'P c n b':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2000        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2001       
2002    'P b c a':[],
2003    'P c a b':[],
2004       
2005    'P n m a':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2006        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2007    'P m n b':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2008        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2009    'P b n m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2010        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2011    'P c m n':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2012        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2013    'P m c n':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2014        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2015    'P n a m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2016        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2017       
2018    'C 2 2 21':['(a00)','(0b0)','(00g)','(10g)','(01g)',],
2019    'C 2 2 2':[],
2020    'C m m 2':[],
2021    'C m c 21':[],
2022    'C c c 2':[],
2023       
2024    'C m 2 m':[],
2025    'C 2 m m':[],
2026       
2027    'C m 2 a':[],
2028    'C 2 m b':[],
2029       
2030    'C 2 c m':[],
2031    'C c 2 m':[],
2032    'C 2 c m':[],
2033       
2034    'C m c a':[],
2035    'C m m m':[],
2036    'C c c m':[],
2037    'C m m a':[],
2038    'C c c a':[],
2039    'C m c m':[],
2040    'I 2 2 2':[],
2041    'I 21 21 21':[],
2042    'I m m m':[],
2043       
2044    'I m m 2':[],
2045    'I m 2 m':[],
2046    'I 2 m m':[],
2047       
2048    'I b a 2':[],
2049    'I 2 c b':[],
2050    'I c 2 a':[],
2051       
2052    'I m a 2':[],
2053    'I b m 2':[],
2054    'I 2 m b':[],
2055       
2056    'I 2 c m':[],
2057    'I c 2 m':[],
2058    'I m 2 a':[],
2059       
2060    'I b a m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2061        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2062    'I m c b':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2063        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2064    'I c m a':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2065        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2066       
2067    'I b c a':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2068        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2069    'I c a b':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2070        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2071       
2072    'I m m a':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2073        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2074    'I m m b':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2075        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2076    'I b m m ':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2077        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2078    'I c m m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2079        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2080    'I m c m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2081        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2082    'I m a m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2083        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2084       
2085    'F 2 2 2':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2086        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s',
2087        '(10g)','(10g)s00','(10g)0s0','(10g)ss0','(a10)','(a10)0s0',
2088        '(a10)00s','(a10)0ss','(0b1)','(0b1)s00','(0b1)00s','(0b1)s0s',
2089        '(01g)','(01g)s00','(01g)0s0','(01g)ss0','(a01)','(a01)0s0',
2090        '(a01)00s','(a01)0ss','(1b0)','(1b0)s00','(1b0)00s','(1b0)s0s'],
2091       
2092    'F m m m':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2093        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s',
2094        '(10g)','(10g)s00','(10g)0s0','(10g)ss0','(a10)','(a10)0s0',
2095        '(a10)00s','(a10)0ss','(0b1)','(0b1)s00','(0b1)00s','(0b1)s0s',
2096        '(01g)','(01g)s00','(01g)0s0','(01g)ss0','(a01)','(a01)0s0',
2097        '(a01)00s','(a01)0ss','(1b0)','(1b0)s00','(1b0)00s','(1b0)s0s'],
2098       
2099    'F d d d':['(00g)','(00g)s00','(00g)0s0','(00g)ss0','(a00)','(a00)0s0',
2100        '(a00)00s','(a00)0ss','(0b0)','(0b0)s00','(0b0)00s','(0b0)s0s'],
2101       
2102    'F m m 2':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s','(a00)','(a00)0s0',
2103        '(0b0)','(0b0)s00','(10g)','(10g)ss0','(10g)0ss','(10g)s0s','(a01)','(a01)0s0',
2104        '(1b0)','(1b0)s00','(01g)','(01g)ss0','(01g)0ss','(01g)s0s','(a10)','(a10)0s0',
2105        '(0b1)','(0b1)s00',],
2106       
2107    'F m 2 m':['(0b0)','(0b0)ss0','(0b0)0ss','(0b0)s0s','(a00)','(a00)00s',
2108        '(00g)','(00g)s00','(1b0)','(1b0)ss0','(1b0)0ss','(1b0)s0s','(a01)','(a01)00s',
2109        '(01g)','(01g)s00','(0b1)','(0b1)ss0','(0b1)0ss','(0b1)s0s','(a10)','(a10)00s',
2110        '(10g)','(10g)s00',],
2111       
2112    'F 2 m m':['(a00)','(a00)ss0','(a00)0ss','(a00)s0s','(0b0)','(0b0)00s',
2113        '(00g)','(00g)0s0','(a10)','(a10)ss0','(a10)0ss','(a10)s0s','(0b1)','(0b1)00s',
2114        '(10g)','(10g)0s0','(a01)','(a01)ss0','(a01)0ss','(a01)s0s','(1b0)','(1b0)00s',
2115        '(01g)','(01g)0s0',],
2116       
2117    'F d d 2':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s','(a00)','(a00)0s0',
2118        '(0b0)','(0b0)s00',],
2119    'F d 2 d':['(0b0)','(0b0)ss0','(0b0)0ss','(0b0)s0s','(a00)','(a00)00s',
2120        '(00g)','(00g)s00',],
2121    'F 2 d d':['(a00)','(a00)ss0','(a00)0ss','(a00)s0s','(0b0)','(0b0)00s',
2122        '(00g)','(00g)0s0',],       
2123    #tetragonal - done
2124    'P 4':['(00g)','(00g)q','(00g)s','(1/21/2g)','(1/21/2g)q',],
2125    'P 41':['(00g)','(00g)q','(00g)s','(1/21/2g)','(1/21/2g)q',],
2126    'P 42':['(00g)','(00g)q','(00g)s','(1/21/2g)','(1/21/2g)q',],
2127    'P 43':['(00g)','(00g)q','(00g)s','(1/21/2g)','(1/21/2g)q',],
2128    'P -4':['(00g)','(1/21/2g)',],
2129    'P 4/m':['(00g)','(00g)s0','(1/21/2g)','(1/21/2g)s0',],
2130    'P 42/m':['(00g)','(00g)s0','(1/21/2g)','(1/21/2g)s0',],
2131    'P 4/n':['(00g)','(00g)s0','(1/21/2g)',],
2132    'P 42/n':['(00g)','(00g)s0','(1/21/2g)',],
2133    'P 4 2 2':['(00g)','(00g)q00','(00g)s00','(1/21/2g)','(1/21/2g)q00','(1/21/2g)s00',],
2134    'P 4 21 2':['(00g)','(00g)q00','(00g)s00',],
2135    'P 41 2 2':['(00g)','(00g)q00','(00g)s00','(1/21/2g)','(1/21/2g)q00','(1/21/2g)s00',],
2136    'P 41 21 2':['(00g)','(00g)q00','(00g)s00',],
2137    'P 42 2 2':['(00g)','(00g)q00','(00g)s00','(1/21/2g)','(1/21/2g)q00','(1/21/2g)s00',],
2138    'P 42 21 2':['(00g)','(00g)q00','(00g)s00',],
2139    'P 43 2 2':['(00g)','(00g)q00','(00g)s00','(1/21/2g)','(1/21/2g)q00','(1/21/2g)s00',],
2140    'P 43 21 2':['(00g)','(00g)q00','(00g)s00',],
2141    'P 4 m m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',
2142        '(1/21/2g)','(1/21/2g)ss0','(1/21/2g)0ss','(1/21/2g)s0s',],
2143    'P 4 b m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s','(1/21/2g)','(1/21/2g)qq0','(1/21/2g)qqs',],
2144    'P 42 c m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',
2145        '(1/21/2g)','(1/21/2g)ss0','(1/21/2g)0ss','(1/21/2g)s0s',],
2146    'P 42 n m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s','(1/21/2g)','(1/21/2g)qq0','(1/21/2g)qqs',],
2147    'P 4 c c':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',
2148        '(1/21/2g)','(1/21/2g)ss0','(1/21/2g)0ss','(1/21/2g)s0s',],
2149    'P 4 n c':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s','(1/21/2g)','(1/21/2g)qq0','(1/21/2g)qqs'],
2150    'P 42 m c':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',
2151        '(1/21/2g)','(1/21/2g)ss0','(1/21/2g)0ss','(1/21/2g)s0s',],
2152    'P 42 b c':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s','(1/21/2g)','(1/21/2g)qq0','(1/21/2g)qqs'],
2153    'P -4 2 m':['(00g)','(00g)00s','(1/21/2g)','(1/21/2g)00s',],
2154    'P -4 2 c':['(00g)','(00g)00s',],
2155    'P -4 21 m':['(00g)','(00g)00s',],
2156    'P -4 21 c':['(00g)','(00g)00s',],
2157    'P -4 m 2':['(00g)','(00g)0s0','(1/21/2g)','(1/21/2g)0s0',],
2158    'P -4 c 2':['(00g)','(00g)0s0','(1/21/2g)','(1/21/2g)0s0',],
2159    'P -4 b 2':['(00g)','(00g)0s0','(1/21/2g)','(1/21/2g)0q0',],
2160    'P -4 n 2':['(00g)','(00g)0s0','(1/21/2g)','(1/21/2g)0q0',],
2161    'P 4/m m m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2162        '(1/21/2g)','(1/21/2g)s0s0','(1/21/2g)00ss','(1/21/2g)s00s',],
2163    'P 4/m c c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2164        '(1/21/2g)','(1/21/2g)s0s0','(1/21/2g)00ss','(1/21/2g)s00s',],
2165    'P 4/n b m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2166        '(1/21/2g)','(1/21/2g)000s',],
2167    'P 4/n n c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2168        '(1/21/2g)','(1/21/2g)000s',],
2169    'P 4/m b m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2170    'P 4/m n c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2171    'P 4/n m m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2172    'P 4/n c c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2173    'P 42/m m c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2174        '(1/21/2g)','(1/21/2g)s0s0','(1/21/2g)00ss','(1/21/2g)s00s',],
2175    'P 42/m c m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2176        '(1/21/2g)','(1/21/2g)s0s0','(1/21/2g)00ss','(1/21/2g)s00s',],
2177    'P 42/n b c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2178        '(1/21/2g)','(1/21/2g)000s',],
2179    'P 42/n n m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',
2180        '(1/21/2g)','(1/21/2g)000s',],
2181    'P 42/m b c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2182    'P 42/m n m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2183    'P 42/n m c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2184    'P 42/n c m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2185    'I 4':['(00g)','(00g)q','(00g)s',],
2186    'I 41':['(00g)','(00g)q','(00g)s',],
2187    'I -4':['(00g)',],
2188    'I 4/m':['(00g)','(00g)s0',],
2189    'I 41/a':['(00g)','(00g)s0',],  #s0?
2190    'I 4 2 2':['(00g)','(00g)q00','(00g)s00',],
2191    'I 41 2 2':['(00g)','(00g)q00','(00g)s00',],
2192    'I 4 m m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2193    'I 4 c m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2194    'I 41 m d':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2195    'I 41 c d':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2196    'I -4 m 2':['(00g)','(00g)0s0',],
2197    'I -4 c 2':['(00g)','(00g)0s0',],
2198    'I -4 2 m':['(00g)','(00g)00s',],
2199    'I -4 2 d':['(00g)','(00g)00s',],
2200    'I 4/m m m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2201    'I 4/m c m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2202    'I 41/a m d':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2203    'I 41/a c d':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2204    #trigonal/rhombahedral - done & checked
2205    'R 3':['(00g)','(00g)t',],
2206    'R -3':['(00g)',],
2207    'R 3 2':['(00g)','(00g)t0',],
2208    'R 3 m':['(00g)','(00g)0s',],
2209    'R 3 c':['(00g)','(00g)0s',],   #not 0s0
2210    'R -3 m':['(00g)','(00g)0s',],
2211    'R -3 c':['(00g)','(00g)0s',],  #not 0s0
2212    'P 3':['(00g)','(00g)t','(1/31/3g)','(1/31/3g)t',],
2213    'P 31':['(00g)','(00g)t','(1/31/3g)','(1/31/3g)t',],
2214    'P 32':['(00g)','(00g)t','(1/31/3g)','(1/31/3g)t',],
2215    'P -3':['(00g)','(1/31/3g)',],
2216    'P 3 1 2':['(00g)','(00g)t00','(1/31/3g)','(1/31/3g)t00',],
2217    'P 3 2 1':['(00g)','(00g)t00',],
2218    'P 31 1 2':['(00g)','(00g)t00','(1/31/3g)','(1/31/3g)t00',],
2219    'P 31 2 1':['(00g)','(00g)t00',],
2220    'P 32 1 2':['(00g)','(00g)t00','(1/31/3g)','(1/31/3g)t00',],
2221    'P 32 2 1':['(00g)','(00g)t00',],
2222    'P 3 m 1':['(00g)','(00g)0s0',],
2223    'P 3 1 m':['(00g)','(00g)00s','(1/31/3g)','(1/31/3g)00s',],
2224    'P 3 c 1':['(00g)','(00g)0s0',],
2225    'P 3 1 c':['(00g)','(00g)00s','(1/31/3g)','(1/31/3g)00s',],
2226    'P -3 1 m':['(00g)','(00g)00s','(1/31/3g)','(1/31/3g)00s',],
2227    'P -3 1 c':['(00g)','(00g)00s','(1/31/3g)','(1/31/3g)00s',],
2228    'P -3 m 1':['(00g)','(00g)0s0',],
2229    'P -3 c 1':['(00g)','(00g)0s0',],
2230    #hexagonal - done & checked
2231    'P 6':['(00g)','(00g)h','(00g)t','(00g)s',],
2232    'P 61':['(00g)','(00g)h','(00g)t','(00g)s',],
2233    'P 65':['(00g)','(00g)h','(00g)t','(00g)s',],
2234    'P 62':['(00g)','(00g)h','(00g)t','(00g)s',],
2235    'P 64':['(00g)','(00g)h','(00g)t','(00g)s',],
2236    'P 63':['(00g)','(00g)h','(00g)t','(00g)s',],
2237    'P -6':['(00g)',],
2238    'P 6/m':['(00g)','(00g)s0',],
2239    'P 63/m':['(00g)','(00g)s0'],
2240    'P 6 2 2':['(00g)','(00g)h00','(00g)t00','(00g)s00',],
2241    'P 61 2 2':['(00g)','(00g)h00','(00g)t00','(00g)s00',],
2242    'P 65 2 2':['(00g)','(00g)h00','(00g)t00','(00g)s00',],
2243    'P 62 2 2':['(00g)','(00g)h00','(00g)t00','(00g)s00',],
2244    'P 64 2 2':['(00g)','(00g)h00','(00g)t00','(00g)s00',],
2245    'P 63 2 2':['(00g)','(00g)h00','(00g)t00','(00g)s00',],
2246    'P 6 m m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2247    'P 6 c c':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2248    'P 63 c m':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2249    'P 63 m c':['(00g)','(00g)ss0','(00g)0ss','(00g)s0s',],
2250    'P -6 m 2':['(00g)','(00g)0s0',],
2251    'P -6 c 2':['(00g)','(00g)0s0',],
2252    'P -6 2 m':['(00g)','(00g)00s',],
2253    'P -6 2 c':['(00g)','(00g)00s',],
2254    'P 6/m m m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2255    'P 6/m c c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2256    'P 63/m c m':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2257    'P 63/m m c':['(00g)','(00g)s0s0','(00g)00ss','(00g)s00s',],
2258    }
2259
2260#'A few non-standard space groups for test use'
2261nonstandard_sglist = ('P 21 1 1','P 1 21 1','P 1 1 21','R 3 r','R 3 2 h', 
2262                      'R -3 r', 'R 3 2 r','R 3 m h', 'R 3 m r',
2263                      'R 3 c r','R -3 c r','R -3 m r',),
2264
2265#A list of orthorhombic space groups that were renamed in the 2002 Volume A,
2266# along with the pre-2002 name. The e designates a double glide-plane'''
2267sgequiv_2002_orthorhombic= (('A e m 2', 'A b m 2',),
2268                            ('A e a 2', 'A b a 2',),
2269                            ('C m c e', 'C m c a',),
2270                            ('C m m e', 'C m m a',),
2271                            ('C c c e', 'C c c a'),)
2272#Use the space groups types in this order to list the symbols in the
2273#order they are listed in the International Tables, vol. A'''
2274symtypelist = ('triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 
2275               'trigonal', 'hexagonal', 'cubic')
2276
2277# self-test materials follow. Requires files in directory testinp
2278selftestlist = []
2279'''Defines a list of self-tests'''
2280selftestquiet = True
2281def _ReportTest():
2282    'Report name and doc string of current routine when ``selftestquiet`` is False'
2283    if not selftestquiet:
2284        import inspect
2285        caller = inspect.stack()[1][3]
2286        doc = eval(caller).__doc__
2287        if doc is not None:
2288            print('testing '+__file__+' with '+caller+' ('+doc+')')
2289        else:
2290            print('testing '+__file__()+" with "+caller)
2291def test0():
2292    '''self-test #0: exercise MoveToUnitCell'''
2293    _ReportTest()
2294    msg = "MoveToUnitCell failed"
2295    assert (MoveToUnitCell([1,2,3]) == [0,0,0]).all, msg
2296    assert (MoveToUnitCell([2,-1,-2]) == [0,0,0]).all, msg
2297    assert abs(MoveToUnitCell(np.array([-.1]))[0]-0.9) < 1e-6, msg
2298    assert abs(MoveToUnitCell(np.array([.1]))[0]-0.1) < 1e-6, msg
2299selftestlist.append(test0)
2300
2301def test1():
2302    '''self-test #1: SpcGroup against previous results'''
2303    #'''self-test #1: SpcGroup and SGPrint against previous results'''
2304    _ReportTest()
2305    testdir = ospath.join(ospath.split(ospath.abspath( __file__ ))[0],'testinp')
2306    if ospath.exists(testdir):
2307        if testdir not in sys.path: sys.path.insert(0,testdir)
2308    import spctestinp
2309    def CompareSpcGroup(spc, referr, refdict, reflist): 
2310        'Compare output from GSASIIspc.SpcGroup with results from a previous run'
2311        # if an error is reported, the dictionary can be ignored
2312        msg0 = "CompareSpcGroup failed on space group %s" % spc
2313        result = SpcGroup(spc)
2314        if result[0] == referr and referr > 0: return True
2315        keys = result[1].keys()
2316        #print result[1]['SpGrp']
2317        #msg = msg0 + " in list lengths"
2318        #assert len(keys) == len(refdict.keys()), msg
2319        for key in refdict.keys():
2320            if key == 'SGOps' or  key == 'SGCen':
2321                msg = msg0 + (" in key %s length" % key)
2322                assert len(refdict[key]) == len(result[1][key]), msg
2323                for i in range(len(refdict[key])):
2324                    msg = msg0 + (" in key %s level 0" % key)
2325                    assert np.allclose(result[1][key][i][0],refdict[key][i][0]), msg
2326                    msg = msg0 + (" in key %s level 1" % key)
2327                    assert np.allclose(result[1][key][i][1],refdict[key][i][1]), msg
2328            else:
2329                msg = msg0 + (" in key %s" % key)
2330                assert result[1][key] == refdict[key], msg
2331        msg = msg0 + (" in key %s reflist" % key)
2332        #for (l1,l2) in zip(reflist, SGPrint(result[1])):
2333        #    assert l2.replace('\t','').replace(' ','') == l1.replace(' ',''), 'SGPrint ' +msg
2334        # for now disable SGPrint testing, output has changed
2335        #assert reflist == SGPrint(result[1]), 'SGPrint ' +msg
2336    for spc in spctestinp.SGdat:
2337        CompareSpcGroup(spc, 0, spctestinp.SGdat[spc], spctestinp.SGlist[spc] )
2338selftestlist.append(test1)
2339
2340def test2():
2341    '''self-test #2: SpcGroup against cctbx (sgtbx) computations'''
2342    _ReportTest()
2343    testdir = ospath.join(ospath.split(ospath.abspath( __file__ ))[0],'testinp')
2344    if ospath.exists(testdir):
2345        if testdir not in sys.path: sys.path.insert(0,testdir)
2346    import sgtbxtestinp
2347    def CompareWcctbx(spcname, cctbx_in, debug=0):
2348        'Compare output from GSASIIspc.SpcGroup with results from cctbx.sgtbx'
2349        cctbx = cctbx_in[:] # make copy so we don't delete from the original
2350        spc = (SpcGroup(spcname))[1]
2351        if debug: print spc['SpGrp']
2352        if debug: print spc['SGCen']
2353        latticetype = spcname.strip().upper()[0]
2354        # lattice type of R implies Hexagonal centering", fix the rhombohedral settings
2355        if latticetype == "R" and len(spc['SGCen']) == 1: latticetype = 'P'
2356        assert latticetype == spc['SGLatt'], "Failed: %s does not match Lattice: %s" % (spcname, spc['SGLatt'])
2357        onebar = [1]
2358        if spc['SGInv']: onebar.append(-1)
2359        for (op,off) in spc['SGOps']:
2360            for inv in onebar:
2361                for cen in spc['SGCen']:
2362                    noff = off + cen
2363                    noff = MoveToUnitCell(noff)
2364                    mult = tuple((op*inv).ravel().tolist())
2365                    if debug: print "\n%s: %s + %s" % (spcname,mult,noff)
2366                    for refop in cctbx:
2367                        if debug: print refop
2368                        # check the transform
2369                        if refop[:9] != mult: continue
2370                        if debug: print "mult match"
2371                        # check the translation
2372                        reftrans = list(refop[-3:])
2373                        reftrans = MoveToUnitCell(reftrans)
2374                        if all(abs(noff - reftrans) < 1.e-5):
2375                            cctbx.remove(refop)
2376                            break
2377                    else:
2378                        assert False, "failed on %s:\n\t %s + %s" % (spcname,mult,noff)
2379    for key in sgtbxtestinp.sgtbx:
2380        CompareWcctbx(key, sgtbxtestinp.sgtbx[key])
2381selftestlist.append(test2)
2382
2383def test3(): 
2384    '''self-test #3: exercise SytSym (includes GetOprPtrName, GenAtom, GetKNsym)
2385     for selected space groups against info in IT Volume A '''
2386    _ReportTest()
2387    def ExerciseSiteSym (spc, crdlist):
2388        'compare site symmetries and multiplicities for a specified space group'
2389        msg = "failed on site sym test for %s" % spc
2390        (E,S) = SpcGroup(spc)
2391        assert not E, msg
2392        for t in crdlist:
2393            symb, m = SytSym(t[0],S)
2394            if symb.strip() != t[2].strip() or m != t[1]:
2395                print spc,t[0],m,symb,t[2]
2396            assert m == t[1]
2397            #assert symb.strip() == t[2].strip()
2398
2399    ExerciseSiteSym('p 1',[
2400            ((0.13,0.22,0.31),1,'1'),
2401            ((0,0,0),1,'1'),
2402            ])
2403    ExerciseSiteSym('p -1',[
2404            ((0.13,0.22,0.31),2,'1'),
2405            ((0,0.5,0),1,'-1'),
2406            ])
2407    ExerciseSiteSym('C 2/c',[
2408            ((0.13,0.22,0.31),8,'1'),
2409            ((0.0,.31,0.25),4,'2(y)'),
2410            ((0.25,.25,0.5),4,'-1'),
2411            ((0,0.5,0),4,'-1'),
2412            ])
2413    ExerciseSiteSym('p 2 2 2',[
2414            ((0.13,0.22,0.31),4,'1'),
2415            ((0,0.5,.31),2,'2(z)'),
2416            ((0.5,.31,0.5),2,'2(y)'),
2417            ((.11,0,0),2,'2(x)'),
2418            ((0,0.5,0),1,'222'),
2419            ])
2420    ExerciseSiteSym('p 4/n',[
2421            ((0.13,0.22,0.31),8,'1'),
2422            ((0.25,0.75,.31),4,'2(z)'),
2423            ((0.5,0.5,0.5),4,'-1'),
2424            ((0,0.5,0),4,'-1'),
2425            ((0.25,0.25,.31),2,'4(001)'),
2426            ((0.25,.75,0.5),2,'-4(001)'),
2427            ((0.25,.75,0.0),2,'-4(001)'),
2428            ])
2429    ExerciseSiteSym('p 31 2 1',[
2430            ((0.13,0.22,0.31),6,'1'),
2431            ((0.13,0.0,0.833333333),3,'2(100)'),
2432            ((0.13,0.13,0.),3,'2(110)'),
2433            ])
2434    ExerciseSiteSym('R 3 c',[
2435            ((0.13,0.22,0.31),18,'1'),
2436            ((0.0,0.0,0.31),6,'3'),
2437            ])
2438    ExerciseSiteSym('R 3 c R',[
2439            ((0.13,0.22,0.31),6,'1'),
2440            ((0.31,0.31,0.31),2,'3(111)'),
2441            ])
2442    ExerciseSiteSym('P 63 m c',[
2443            ((0.13,0.22,0.31),12,'1'),
2444            ((0.11,0.22,0.31),6,'m(100)'),
2445            ((0.333333,0.6666667,0.31),2,'3m(100)'),
2446            ((0,0,0.31),2,'3m(100)'),
2447            ])
2448    ExerciseSiteSym('I a -3',[
2449            ((0.13,0.22,0.31),48,'1'),
2450            ((0.11,0,0.25),24,'2(x)'),
2451            ((0.11,0.11,0.11),16,'3(111)'),
2452            ((0,0,0),8,'-3(111)'),
2453            ])
2454selftestlist.append(test3)
2455
2456if __name__ == '__main__':
2457    # run self-tests
2458    selftestquiet = False
2459    for test in selftestlist:
2460        test()
2461    print "OK"
Note: See TracBrowser for help on using the repository browser.