source: trunk/GSASIIspc.py @ 3208

Last change on this file since 3208 was 3208, checked in by vondreele, 7 years ago

fix transformation bug abc -> abc* needed transform of matrix - G2phsGUI line 373
work on modullated magnetic cif import; fix some magnetic & incommensurate bugs - more to do.

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