source: trunk/imports/G2phase_CIF.py @ 3266

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

change modulated wave type assignments so one for each kind of wave (pos, frac, etc).
implement optional display of PNT data set positions on pole figure plots. Picker displays histo. name.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 45.1 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2018-02-08 19:31:55 +0000 (Thu, 08 Feb 2018) $
4# $Author: vondreele $
5# $Revision: 3266 $
6# $URL: trunk/imports/G2phase_CIF.py $
7# $Id: G2phase_CIF.py 3266 2018-02-08 19:31:55Z vondreele $
8########### SVN repository information ###################
9'''
10*Module G2phase_CIF: Coordinates from CIF*
11------------------------------------------
12
13Parses a CIF using  PyCifRW from James Hester and pulls out the
14structural information.
15
16If a CIF generated by ISODISTORT is encountered, extra information is
17added to the phase entry and constraints are generated.
18
19'''
20# Routines to import Phase information from CIF files
21from __future__ import division, print_function
22import sys
23import random as ran
24import numpy as np
25import re
26import GSASIIIO as G2IO
27import GSASIIobj as G2obj
28import GSASIIspc as G2spc
29import GSASIIElem as G2elem
30import GSASIIlattice as G2lat
31import GSASIIpy3 as G2p3
32import GSASIIpath
33GSASIIpath.SetVersionNumber("$Revision: 3266 $")
34import CifFile as cif # PyCifRW from James Hester
35
36class CIFPhaseReader(G2obj.ImportPhase):
37    'Implements a phase importer from a possibly multi-block CIF file'
38    def __init__(self):
39        super(self.__class__,self).__init__( # fancy way to say ImportPhase.__init__
40            extensionlist=('.CIF','.cif','.mcif'),
41            strictExtension=False,
42            formatName = 'CIF',
43            longFormatName = 'Crystallographic Information File import'
44            )
45       
46    def ContentsValidator(self, filename):
47        fp = open(filename,'r')
48        return self.CIFValidator(fp)
49        fp.close()
50
51    def Reader(self,filename, ParentFrame=None, usedRanIdList=[], **unused):
52        self.isodistort_warnings = ''
53        self.Phase = G2obj.SetNewPhase(Name='new phase') # create a new empty phase dict
54        # make sure the ranId is really unique!
55        while self.Phase['ranId'] in usedRanIdList:
56            self.Phase['ranId'] = ran.randint(0,sys.maxsize)
57        self.MPhase = G2obj.SetNewPhase(Name='new phase') # create a new empty phase dict
58        # make sure the ranId is really unique!
59        while self.MPhase['ranId'] in usedRanIdList:
60            self.MPhase['ranId'] = ran.randint(0,sys.maxsize)
61        returnstat = False
62        cellitems = (
63            '_cell_length_a','_cell_length_b','_cell_length_c',
64            '_cell_angle_alpha','_cell_angle_beta','_cell_angle_gamma',)
65#        cellwaveitems = (
66#            '_cell_wave_vector_seq_id',
67#            '_cell_wave_vector_x','_cell_wave_vector_y','_cell_wave_vector_z')
68        reqitems = (
69             '_atom_site_fract_x',
70             '_atom_site_fract_y',
71             '_atom_site_fract_z',
72            )
73        phasenamefields = (
74            '_chemical_name_common',
75            '_pd_phase_name',
76            '_chemical_formula_sum'
77            )
78        try:
79            cf = G2obj.ReadCIF(filename)
80        except cif.StarError as msg:
81            msg  = 'Unreadable cif file\n'+str(msg)
82            self.errors = msg
83            self.warnings += msg
84            return False
85        # scan blocks for structural info
86        self.errors = 'Error during scan of blocks for datasets'
87        str_blklist = []
88        for blk in cf.keys():
89            for r in reqitems+cellitems:
90                if r not in cf[blk].keys():
91                    break
92            else:
93                str_blklist.append(blk)
94        if not str_blklist:
95            selblk = None # no block to choose
96        elif len(str_blklist) == 1: # only one choice
97            selblk = 0
98        else:                       # choose from options
99            choice = []
100            for blknm in str_blklist:
101                choice.append('')
102                # accumumlate some info about this phase
103                choice[-1] += blknm + ': '
104                for i in phasenamefields: # get a name for the phase
105                    name = cf[blknm].get(i,'phase name').strip()
106                    if name is None or name == '?' or name == '.':
107                        continue
108                    else:
109                        choice[-1] += name.strip()[:20] + ', '
110                        break
111                na = len(cf[blknm].get("_atom_site_fract_x"))
112                if na == 1:
113                    choice[-1] += '1 atom'
114                else:
115                    choice[-1] += ('%d' % na) + ' atoms'
116                choice[-1] += ', cell: '
117                fmt = "%.2f,"
118                for i,key in enumerate(cellitems):
119                    if i == 3: fmt = "%.f,"
120                    if i == 5: fmt = "%.f"
121                    choice[-1] += fmt % cif.get_number_with_esd(
122                        cf[blknm].get(key))[0]
123                sg = cf[blknm].get("_symmetry_space_group_name_H-M",'')
124                if not sg: sg = cf[blknm].get("_space_group_name_H-M_alt",'')
125                if not sg: sg = cf[blknm].get("_space_group_ssg_name",'')
126                if not sg: sg = cf[blknm].get("_space_group.magn_ssg_name_BNS",'')
127                if not sg: sg = cf[blknm].get("_space_group.magn_ssg_name",'')
128                #how about checking for super/magnetic ones as well? - reject 'X'?
129                sg = sg.replace('_','')
130                if sg: choice[-1] += ', (' + sg.strip() + ')'
131            selblk = G2IO.PhaseSelector(choice,ParentFrame=ParentFrame,
132                title= 'Select a phase from one the CIF data_ blocks below',size=(600,100))
133        self.errors = 'Error during reading of selected block'
134#process selected phase
135        if selblk is None:
136            returnstat = False # no block selected or available
137        else:   #do space group symbol & phase type first
138            blknm = str_blklist[selblk]
139            blk = cf[str_blklist[selblk]]
140            E = True
141            Super = False
142            magnetic = False
143            moddim = int(blk.get("_cell_modulation_dimension",'0'))
144            if moddim:      #incommensurate
145                if moddim > 1:
146                    msg = 'more than 3+1 super symmetry is not allowed in GSAS-II'
147                    self.errors = msg
148                    self.warnings += '\n'+msg
149                    return False
150                if blk.get('_cell_subsystems_number'):
151                    msg = 'Composite super structures not allowed in GSAS-II'
152                    self.errors = msg
153                    self.warnings += '\n'+msg
154                    return False
155                sspgrp = blk.get("_space_group_ssg_name",'')
156                if not sspgrp:          #might be incommensurate magnetic
157                    MSSpGrp = blk.get("_space_group.magn_ssg_name_BNS",'')
158                    if not MSSpGrp:
159                        MSSpGrp = blk.get("_space_group.magn_ssg_name",'') 
160                    if not MSSpGrp:
161                        msg = 'No incommensurate space group name was found in the CIF.'
162                        self.errors = msg
163                        self.warnings += '\n'+msg
164                        return False                                                           
165                    if 'X' in MSSpGrp:
166                        msg = 'Ad hoc incommensurate magnetic space group '+MSSpGrp+' is not allowed in GSAS-II'
167                        self.warnings += '\n'+msg
168                        self.errors = msg
169                        return False
170                    magnetic = True
171                if 'X' in sspgrp:
172                    msg = 'Ad hoc incommensurate space group '+sspgrp+' is not allowed in GSAS-II'
173                    self.warnings += '\n'+msg
174                    self.errors = msg
175                    return False
176                Super = True
177                if magnetic:
178                    sspgrp = MSSpGrp.split('(')
179                    sspgrp[1] = "("+sspgrp[1]
180                    SpGrp = G2spc.StandardizeSpcName(sspgrp[0])
181                    if "1'" in SpGrp: sspgrp[1] = sspgrp[1][:-1]  #take off extra 's'; gets put back later
182                    MSpGrp = sspgrp[0]
183                    self.MPhase['General']['Type'] = 'magnetic'
184                    self.MPhase['General']['AtomPtrs'] = [3,1,10,12]
185                else:
186                    sspgrp = sspgrp.split('(')
187                    sspgrp[1] = "("+sspgrp[1]
188                    SpGrp = sspgrp[0]
189                    SpGrp = G2spc.StandardizeSpcName(SpGrp)
190                    self.Phase['General']['Type'] = 'nuclear'
191                if not SpGrp:
192                    print (sspgrp)
193                    self.warnings += 'Space group name '+sspgrp[0]+sspgrp[1]+' not recognized by GSAS-II'
194                    return False
195                SuperSg = sspgrp[1].replace('\\','')
196                SuperVec = [[0,0,.1],False,4]
197            else:   #not incommensurate
198                SpGrp = blk.get("_symmetry_space_group_name_H-M",'')
199                if not SpGrp:
200                    SpGrp = blk.get("_space_group_name_H-M_alt",'')
201                if not SpGrp:   #try magnetic           
202                    MSpGrp = blk.get("_space_group.magn_name_BNS",'')
203                    if not MSpGrp:
204                        MSpGrp = blk.get("_space_group_magn.name_BNS",'')
205                        if not MSpGrp:
206                            msg = 'No recognizable space group name was found in the CIF.'
207                            self.errors = msg
208                            self.warnings += '\n'+msg
209                            return False
210                    SpGrp = blk.get('_parent_space_group.name_H-M_alt')
211                    if not SpGrp:
212                        SpGrp = blk.get('_parent_space_group.name_H-M')
213#                    SpGrp = MSpGrp.replace("'",'')
214                    SpGrp = SpGrp[:2]+SpGrp[2:].replace('_','')   #get rid of screw '_'
215                    if '_' in SpGrp[1]: SpGrp = SpGrp.split('_')[0]+SpGrp[3:]
216                    SpGrp = G2spc.StandardizeSpcName(SpGrp)
217                    magnetic = True
218                    self.MPhase['General']['Type'] = 'magnetic'
219                    self.MPhase['General']['AtomPtrs'] = [3,1,10,12]
220                    if not SpGrp:
221                        print (MSpGrp)
222                        self.warnings += 'No space group name was found in the CIF.'
223                        return False
224                else:
225                    SpGrp = SpGrp.replace('_','')
226                    self.Phase['General']['Type'] = 'nuclear'
227#process space group symbol
228            E,SGData = G2spc.SpcGroup(SpGrp)
229            if E and SpGrp:
230                SpGrpNorm = G2spc.StandardizeSpcName(SpGrp)
231                if SpGrpNorm:
232                    E,SGData = G2spc.SpcGroup(SpGrpNorm)
233            # nope, try the space group "out of the Box"
234            if E:
235                self.warnings += 'ERROR in space group symbol '+SpGrp
236                self.warnings += '\nThe space group has been set to "P 1". '
237                self.warnings += "Change this in phase's General tab."
238                self.warnings += '\nAre there spaces separating axial fields?\n\nError msg: '
239                self.warnings += G2spc.SGErrors(E)
240                SGData = G2obj.P1SGData # P 1
241            self.Phase['General']['SGData'] = SGData
242            if Super:
243                E,SSGData = G2spc.SSpcGroup(SGData,SuperSg)
244                if E:
245                    self.warnings += 'Invalid super symmetry symbol '+SpGrp+SuperSg
246                    self.warnings += '\n'+E
247                    SuperSg = SuperSg[:SuperSg.index(')')+1]
248                    self.warnings += '\nNew super symmetry symbol '+SpGrp+SuperSg
249                    E,SSGData = G2spc.SSpcGroup(SGData,SuperSg)
250                self.Phase['General']['SSGData'] = SSGData
251                if magnetic:
252                    self.MPhase['General']['SGData'] = SGData
253                    self.MPhase['General']['SGData']['MagSpGrp'] = MSSpGrp.replace(',','').replace('\\','')
254                    self.MPhase['General']['SSGData'] = SSGData
255
256            if magnetic:    #replace std operaors with those from cif file - probably not the same!
257                SGData['SGFixed'] = True
258                SGData['SGOps'] = []
259                SGData['SGCen'] = []
260                if Super:
261                    SSGData['SSGOps'] = []
262                    SSGData['SSGCen'] = []
263                    try:
264                        sgoploop = blk.GetLoop('_space_group_symop_magn_ssg_operation.id')
265                        sgcenloop = blk.GetLoop('_space_group_symop_magn_ssg_centering.id')
266                        opid = sgoploop.GetItemPosition('_space_group_symop_magn_ssg_operation.algebraic')[1]
267                        centid = sgcenloop.GetItemPosition('_space_group_symop_magn_ssg_centering.algebraic')[1]                   
268                    except KeyError:        #old mag cif names
269                        sgoploop = blk.GetLoop('_space_group_symop.magn_ssg_id')
270                        sgcenloop = blk.GetLoop('_space_group_symop.magn_ssg_centering_id')
271                        opid = sgoploop.GetItemPosition('_space_group_symop.magn_ssg_operation_algebraic')[1]
272                        centid = sgcenloop.GetItemPosition('_space_group_symop.magn_ssg_centering_algebraic')[1]
273                    spnflp = []
274                    for op in sgoploop:
275                        M,T,S = G2spc.MagSSText2MTS(op[opid])
276                        SSGData['SSGOps'].append([np.array(M,dtype=float),T])
277                        SGData['SGOps'].append([np.array(M,dtype=float)[:3,:3],T[:3]])
278                        spnflp.append(S)
279                    censpn = []
280                    for cent in sgcenloop:
281                        M,C,S = G2spc.MagSSText2MTS(cent[centid])
282                        SSGData['SSGCen'].append(C)
283                        if not C[3]:
284                            SGData['SGCen'].append(C[:3])
285                        censpn += list(np.array(spnflp)*S)
286                    self.MPhase['General']['SSGData'] = SSGData
287                else:   
288                    try:
289                        sgoploop = blk.GetLoop('_space_group_symop_magn.id')
290                        sgcenloop = blk.GetLoop('_space_group_symop_magn_centering.id')
291                        opid = sgoploop.GetItemPosition('_space_group_symop_magn_operation.xyz')[1]
292                        centid = sgcenloop.GetItemPosition('_space_group_symop_magn_centering.xyz')[1]                   
293                    except KeyError:        #old mag cif names
294                        sgoploop = blk.GetLoop('_space_group_symop.magn_id')
295                        sgcenloop = blk.GetLoop('_space_group_symop.magn_centering_id')
296                        opid = sgoploop.GetItemPosition('_space_group_symop.magn_operation_xyz')[1]
297                        centid = sgcenloop.GetItemPosition('_space_group_symop.magn_centering_xyz')[1]
298                    spnflp = []
299                    for op in sgoploop:
300                        try:
301                            M,T,S = G2spc.MagText2MTS(op[opid])
302                            SGData['SGOps'].append([np.array(M,dtype=float),T])
303                            spnflp.append(S)
304                        except KeyError:
305                            self.warnings += 'Space group operator '+op[opid]+' is not recognized by GSAS-II'
306                            return False
307                    censpn = []
308                    for cent in sgcenloop:
309                        M,C,S = G2spc.MagText2MTS(cent[centid])
310                        SGData['SGCen'].append(C)
311                        censpn += list(np.array(spnflp)*S)
312                self.MPhase['General']['SGData'] = SGData
313                self.MPhase['General']['SGData']['SpnFlp'] = censpn
314                self.MPhase['General']['SGData']['MagSpGrp'] = MSpGrp
315                MagPtGp = blk.get('_space_group.magn_point_group')
316                if not MagPtGp:
317                    MagPtGp = blk.get('_space_group.magn_point_group_name')
318                if not MagPtGp:
319                    MagPtGp = blk.get('_space_group_magn.point_group_name')
320                self.MPhase['General']['SGData']['MagPtGp'] = MagPtGp
321
322            # cell parameters
323            cell = []
324            for lbl in cellitems:
325                cell.append(cif.get_number_with_esd(blk[lbl])[0])
326            Volume = G2lat.calc_V(G2lat.cell2A(cell))
327            self.Phase['General']['Cell'] = [False,]+cell+[Volume,]
328            if magnetic:
329                self.MPhase['General']['Cell'] = [False,]+cell+[Volume,]               
330            if Super:
331                waveloop = blk.GetLoop('_cell_wave_vector_seq_id')
332                waveDict = dict(waveloop.items())
333                SuperVec = [[cif.get_number_with_esd(waveDict['_cell_wave_vector_x'][0].replace('?','0'))[0],
334                    cif.get_number_with_esd(waveDict['_cell_wave_vector_y'][0].replace('?','0'))[0],
335                    cif.get_number_with_esd(waveDict['_cell_wave_vector_z'][0].replace('?','0'))[0]],False,4]
336
337            # read in atoms
338            self.errors = 'Error during reading of atoms'
339            atomlbllist = [] # table to look up atom IDs
340            atomloop = blk.GetLoop('_atom_site_label')
341            atomkeys = [i.lower() for i in atomloop.keys()]
342            if not blk.get('_atom_site_type_symbol'):
343                self.isodistort_warnings += '\natom types are missing. \n Check & revise atom types as needed'
344            if magnetic:
345                try:
346                    magmoment = '_atom_site_moment.label'
347                    magatomloop = blk.GetLoop(magmoment)
348                    magatomkeys = [i.lower() for i in magatomloop.keys()]
349                    magatomlabels = blk.get(magmoment)
350                    G2MagDict = {'_atom_site_moment.label': 0,
351                                 '_atom_site_moment.crystalaxis_x':7,
352                                 '_atom_site_moment.crystalaxis_y':8,
353                                 '_atom_site_moment.crystalaxis_z':9}
354                except KeyError:
355                    magmoment = '_atom_site_moment_label'
356                    magatomloop = blk.GetLoop(magmoment)
357                    magatomkeys = [i.lower() for i in magatomloop.keys()]
358                    magatomlabels = blk.get(magmoment)
359                    G2MagDict = {'_atom_site_moment_label': 0,
360                                 '_atom_site_moment_crystalaxis_x':7,
361                                 '_atom_site_moment_crystalaxis_y':8,
362                                 '_atom_site_moment_crystalaxis_z':9}
363                   
364            if blk.get('_atom_site_aniso_label'):
365                anisoloop = blk.GetLoop('_atom_site_aniso_label')
366                anisokeys = [i.lower() for i in anisoloop.keys()]
367                anisolabels = blk.get('_atom_site_aniso_label')
368            else:
369                anisoloop = None
370                anisokeys = []
371                anisolabels = []
372            if Super:
373                occFloop = None
374                occCloop = None
375                occFdict = {}
376                occCdict = {}
377                displSloop = None
378                displFloop = None
379                MagFloop = None
380                displSdict = {}
381                displFdict = {}
382                MagFdict = {}
383                UijFloop = None
384                UijFdict = {}
385                #occupancy modulation
386                if blk.get('_atom_site_occ_Fourier_atom_site_label'):
387                    occFloop = blk.GetLoop('_atom_site_occ_Fourier_atom_site_label')
388                    occFdict = dict(occFloop.items())
389                if blk.get('_atom_site_occ_special_func_atom_site_label'):  #Crenel (i.e. Block Wave) occ
390                    occCloop = blk.GetLoop('_atom_site_occ_special_func_atom_site_label')
391                    occCdict = dict(occCloop.items())
392                #position modulation
393                if blk.get('_atom_site_displace_Fourier_atom_site_label'):
394                    displFloop = blk.GetLoop('_atom_site_displace_Fourier_atom_site_label')
395                    displFdict = dict(displFloop.items())                           
396                if blk.get('_atom_site_displace_special_func_atom_site_label'): #sawtooth
397                    displSloop = blk.GetLoop('_atom_site_displace_special_func_atom_site_label')
398                    displSdict = dict(displSloop.items())
399                #U modulation
400                if blk.get('_atom_site_U_Fourier_atom_site_label'):
401                    UijFloop = blk.GetLoop('_atom_site_U_Fourier_atom_site_label')
402                    UijFdict = dict(UijFloop.items())
403                #Mag moment modulation
404                if blk.get('_atom_site_moment_Fourier_atom_site_label'):
405                    MagFloop = blk.GetLoop('_atom_site_moment_Fourier_atom_site_label')
406                    MagFdict = dict(MagFloop.items())
407                    Mnames =  ['_atom_site_moment_fourier_atom_site_label',
408                               '_atom_site_moment_fourier_axis','_atom_site_moment_fourier_wave_vector_seq_id',
409                               '_atom_site_moment_fourier_param_sin','_atom_site_moment_fourier_param_cos']
410                elif blk.get('_atom_site_moment_Fourier.atom_site_label'):
411                    MagFloop = blk.GetLoop('_atom_site_moment_Fourier.atom_site_label')
412                    MagFdict = dict(MagFloop.items())                           
413                    Mnames =  ['_atom_site_moment_fourier.atom_site_label',
414                               '_atom_site_moment_fourier.axis','_atom_site_moment_fourier.wave_vector_seq_id',
415                               '_atom_site_moment_fourier_param.sin','_atom_site_moment_fourier_param.cos']
416            self.Phase['Atoms'] = []
417            if magnetic:
418                self.MPhase['Atoms'] = []
419            G2AtomDict = {  '_atom_site_type_symbol' : 1,
420                            '_atom_site_label' : 0,
421                            '_atom_site_fract_x' : 3,
422                            '_atom_site_fract_y' : 4,
423                            '_atom_site_fract_z' : 5,
424                            '_atom_site_occupancy' : 6,
425                            '_atom_site_aniso_u_11' : 11,
426                            '_atom_site_aniso_u_22' : 12,
427                            '_atom_site_aniso_u_33' : 13,
428                            '_atom_site_aniso_u_12' : 14,
429                            '_atom_site_aniso_u_13' : 15,
430                            '_atom_site_aniso_u_23' : 16, }
431
432            ranIdlookup = {}
433            for aitem in atomloop:
434                atomlist = ['','','',0.,0.,0.,1.0,'',0.,'I',0.01, 0.,0.,0.,0.,0.,0.,]
435                for val,key in zip(aitem,atomkeys):
436                    col = G2AtomDict.get(key,-1)
437                    if col >= 3:
438                        atomlist[col] = cif.get_number_with_esd(val)[0]
439                        if col >= 11: atomlist[9] = 'A' # if any Aniso term is defined, set flag
440                    elif col is not None and col != -1:
441                        atomlist[col] = val
442                    elif key in ('_atom_site_thermal_displace_type',
443                               '_atom_site_adp_type'):   #Iso or Aniso?
444                        if val.lower() == 'uani':
445                            atomlist[9] = 'A'
446                    elif key == '_atom_site_u_iso_or_equiv':
447                        uisoval = cif.get_number_with_esd(val)[0]
448                        if uisoval is not None: 
449                            atomlist[10] = uisoval
450                if not atomlist[1] and atomlist[0]:
451                    typ = atomlist[0].rstrip('0123456789-+')
452                    if G2elem.CheckElement(typ):
453                        atomlist[1] = typ
454                    if not atomlist[1]:
455                        atomlist[1] = 'Xe'
456                        self.warnings += ' Atom type '+typ+' not recognized; Xe assumed\n'
457                if atomlist[0] in anisolabels: # does this atom have aniso values in separate loop?
458                    atomlist[9] = 'A'
459                    for val,key in zip(anisoloop.GetKeyedPacket('_atom_site_aniso_label',atomlist[0]),anisokeys):
460                        col = G2AtomDict.get(key)
461                        if col:
462                            atomlist[col] = cif.get_number_with_esd(val)[0]
463                atomlist[7],atomlist[8] = G2spc.SytSym(atomlist[3:6],SGData)[:2]
464                atomlist[1] = G2elem.FixValence(atomlist[1])
465                atomlist.append(ran.randint(0,sys.maxsize)) # add a random Id
466                self.Phase['Atoms'].append(atomlist)
467                ranIdlookup[atomlist[0]] = atomlist[-1]
468                if atomlist[0] in atomlbllist:
469                    self.warnings += ' ERROR: repeated atom label: '+atomlist[0]
470                else:
471                    atomlbllist.append(atomlist[0])
472
473                if magnetic and atomlist[0] in magatomlabels:
474                    matomlist = atomlist[:7]+[0.,0.,0.,]+atomlist[7:]
475                    for mval,mkey in zip(magatomloop.GetKeyedPacket(magmoment,atomlist[0]),magatomkeys):
476                        mcol = G2MagDict.get(mkey,-1)
477                        if mcol:
478                            matomlist[mcol] = cif.get_number_with_esd(mval)[0]
479                    self.MPhase['Atoms'].append(matomlist)
480                if Super:
481                    Sfrac = np.zeros((4,2))
482                    Sadp = np.zeros((4,12))
483                    Spos = np.zeros((4,6))
484                    Smag = np.zeros((4,6))
485                    nim = -1
486                    waveType = 'Fourier'
487                    if  occCdict:
488                        for i,item in enumerate(occCdict['_atom_site_occ_special_func_atom_site_label']):
489                            if item == atomlist[0]:
490                                waveType = 'Crenel'
491                                val = occCdict['_atom_site_occ_special_func_crenel_c'][i]
492                                Sfrac[0][0] = cif.get_number_with_esd(val)[0]
493                                val = occCdict['_atom_site_occ_special_func_crenel_w'][i]
494                                Sfrac[0][1] = cif.get_number_with_esd(val)[0]
495                                nim = 1
496                   
497                    if nim >= 0:
498                        Sfrac = [waveType,]+[[sfrac,False] for sfrac in Sfrac[:nim]]
499                    else:
500                        Sfrac = []
501                    if displFdict:
502                        for i,item in enumerate(displFdict['_atom_site_displace_fourier_atom_site_label']):
503                            if item == atomlist[0]:
504                                waveType = 'Fourier'                               
505                                ix = ['x','y','z'].index(displFdict['_atom_site_displace_fourier_axis'][i])
506                                im = int(displFdict['_atom_site_displace_fourier_wave_vector_seq_id'][i])
507                                if im != nim:
508                                    nim = im
509                                val = displFdict['_atom_site_displace_fourier_param_sin'][i]
510                                Spos[im-1][ix] = cif.get_number_with_esd(val)[0]
511                                val = displFdict['_atom_site_displace_fourier_param_cos'][i]
512                                Spos[im-1][ix+3] = cif.get_number_with_esd(val)[0]
513                    if nim >= 0:
514                        Spos = [waveType,]+[[spos,False] for spos in Spos[:nim]]
515                    else:
516                        Spos = []
517                    if UijFdict:
518                        nim = -1
519                        for i,item in enumerate(UijFdict['_atom_site_u_fourier_atom_site_label']):
520                            if item == atomlist[0]:
521                                ix = ['U11','U22','U33','U12','U13','U23'].index(UijFdict['_atom_site_u_fourier_tens_elem'][i])
522                                im = int(UijFdict['_atom_site_u_fourier_wave_vector_seq_id'][i])
523                                if im != nim:
524                                    nim = im
525                                val = UijFdict['_atom_site_u_fourier_param_sin'][i]
526                                Sadp[im-1][ix] = cif.get_number_with_esd(val)[0]
527                                val = UijFdict['_atom_site_u_fourier_param_cos'][i]
528                                Sadp[im-1][ix+6] = cif.get_number_with_esd(val)[0]
529                    if nim >= 0:
530                        Sadp = ['Fourier',]+[[sadp,False] for sadp in Sadp[:nim]]
531                    else:
532                        Sadp = []
533                    if MagFdict:
534                        nim = -1
535                        for i,item in enumerate(MagFdict[Mnames[0]]):
536                            if item == atomlist[0]:
537                                ix = ['x','y','z'].index(MagFdict[Mnames[1]][i])
538                                im = int(MagFdict[Mnames[2]][i])
539                                if im != nim:
540                                    nim = im
541                                val = MagFdict[Mnames[3]][i]
542                                Smag[im-1][ix] = cif.get_number_with_esd(val)[0]
543                                val = MagFdict[Mnames[4]][i]
544                                Smag[im-1][ix+3] = cif.get_number_with_esd(val)[0]
545                    if nim >= 0:
546                        Smag = ['Fourier',]+[[smag,False] for smag in Smag[:nim]]
547                    else:
548                        Smag = []
549                    SSdict = {'SS1':{'Sfrac':Sfrac,'Spos':Spos,'Sadp':Sadp,'Smag':Smag}}
550                    if magnetic and atomlist[0] in magatomlabels:
551                        matomlist.append(SSdict)
552                    atomlist.append(SSdict)
553            if len(atomlbllist) != len(self.Phase['Atoms']):
554                self.isodistort_warnings += '\nRepeated atom labels prevents ISODISTORT decode'
555            for lbl in phasenamefields: # get a name for the phase
556                name = blk.get(lbl)
557                if name is None:
558                    continue
559                name = name.strip()
560                if name == '?' or name == '.':
561                    continue
562                else:
563                    break
564            else: # no name found, use block name for lack of a better choice
565                name = blknm
566            self.Phase['General']['Name'] = name.strip()[:20]
567            self.Phase['General']['Super'] = Super
568            if magnetic:
569                self.MPhase['General']['Type'] = 'magnetic'               
570                self.MPhase['General']['Name'] = name.strip()[:20]+' mag'
571                self.MPhase['General']['Super'] = Super
572                if Super:
573                    if self.MPhase['General']['SGData']['SGGray']:
574                        SuperSg += 's'
575                    self.MPhase['General']['Modulated'] = True
576                    self.MPhase['General']['SuperVec'] = SuperVec
577                    self.MPhase['General']['SuperSg'] = SuperSg
578            else:
579                self.MPhase = None
580            if Super:
581                self.Phase['General']['Modulated'] = True
582                self.Phase['General']['SuperVec'] = SuperVec
583                self.Phase['General']['SuperSg'] = SuperSg
584                if not self.Phase['General']['SGData']['SGFixed']:
585                    self.Phase['General']['SSGData'] = G2spc.SSpcGroup(SGData,SuperSg)[1]
586            if not self.isodistort_warnings:
587                if blk.get('_iso_displacivemode_label') or blk.get('_iso_occupancymode_label'):
588                    self.errors = "Error while processing ISODISTORT constraints"
589                    self.ISODISTORT_proc(blk,atomlbllist,ranIdlookup)
590            else:
591                self.warnings += self.isodistort_warnings
592            returnstat = True
593        return returnstat
594
595    def ISODISTORT_proc(self,blk,atomlbllist,ranIdlookup):
596        'Process ISODISTORT items to create constraints etc.'
597        varLookup = {'dx':'dAx','dy':'dAy','dz':'dAz','do':'Afrac'}
598        'Maps ISODISTORT parm names to GSAS-II names'
599        #----------------------------------------------------------------------
600        # read in the ISODISTORT displacement modes
601        #----------------------------------------------------------------------
602        self.Constraints = []
603        explaination = {}
604        if blk.get('_iso_displacivemode_label'):
605            modelist = []
606            shortmodelist = []
607            for lbl in blk.get('_iso_displacivemode_label'):
608                modelist.append(lbl)
609                # assume lbl is of form SSSSS[x,y,z]AAAA(a,b,...)BBBBB
610                # where SSSSS is the parent spacegroup, [x,y,z] is a location
611                regexp = re.match(r'.*?\[.*?\](.*?)\(.*?\)(.*)',lbl)
612                # this extracts the AAAAA and BBBBB parts of the string
613                if regexp:
614                    lbl = regexp.expand(r'\1\2') # parse succeeded, make a short version
615                G2obj.MakeUniqueLabel(lbl,shortmodelist) # make unique and add to list
616            # read in the coordinate offset variables names and map them to G2 names/objects
617            coordVarLbl = []
618            G2varLbl = []
619            G2varObj = []
620            error = False
621            for lbl in blk.get('_iso_deltacoordinate_label'):
622                coordVarLbl.append(lbl)
623                if '_' in lbl:
624                    albl = lbl[:lbl.rfind('_')]
625                    vlbl = lbl[lbl.rfind('_')+1:]
626                else:
627                    self.warnings += ' ERROR: _iso_deltacoordinate_label not parsed: '+lbl
628                    error = True
629                    continue
630                if albl not in atomlbllist:
631                    self.warnings += ' ERROR: _iso_deltacoordinate_label atom not found: '+lbl
632                    error = True
633                    continue
634                else:
635                    anum = atomlbllist.index(albl)
636                var = varLookup.get(vlbl)
637                if not var:
638                    self.warnings += ' ERROR: _iso_deltacoordinate_label variable not found: '+lbl
639                    error = True
640                    continue
641                G2varLbl.append('::'+var+':'+str(anum)) # variable name, less phase ID
642                G2varObj.append(G2obj.G2VarObj(
643                    (self.Phase['ranId'],None,var,ranIdlookup[albl])
644                    ))
645            if error:
646                raise Exception("Error decoding variable labels")
647
648            if len(G2varObj) != len(modelist):
649                print ("non-square input")
650                raise Exception("Rank of _iso_displacivemode != _iso_deltacoordinate")
651
652            error = False
653            ParentCoordinates = {}
654            for lbl,exp in zip(
655                blk.get('_iso_coordinate_label'),
656                blk.get('_iso_coordinate_formula'),
657                ):
658                if '_' in lbl:
659                    albl = lbl[:lbl.rfind('_')]
660                    vlbl = lbl[lbl.rfind('_')+1:]
661                else:
662                    self.warnings += ' ERROR: _iso_coordinate_label not parsed: '+lbl
663                    error = True
664                    continue
665                if vlbl not in 'xyz' or len(vlbl) != 1:
666                    self.warnings += ' ERROR: _iso_coordinate_label coordinate not parsed: '+lbl
667                    error = True
668                    continue
669                i = 'xyz'.index(vlbl)
670                if not ParentCoordinates.get(albl):
671                    ParentCoordinates[albl] = [None,None,None]
672                if '+' in exp:
673                    val = exp.split('+')[0].strip()
674                    val = G2p3.FormulaEval(val)
675                    if val is None:
676                        self.warnings += ' ERROR: _iso_coordinate_formula coordinate not interpreted: '+lbl
677                        error = True
678                        continue
679                    ParentCoordinates[albl][i] = val
680                else:
681                    ParentCoordinates[albl][i] = G2p3.FormulaEval(exp)
682            if error:
683                print (self.warnings)
684                raise Exception("Error decoding variable labels")
685            # get mapping of modes to atomic coordinate displacements
686            displacivemodematrix = np.zeros((len(G2varObj),len(G2varObj)))
687            for row,col,val in zip(
688                blk.get('_iso_displacivemodematrix_row'),
689                blk.get('_iso_displacivemodematrix_col'),
690                blk.get('_iso_displacivemodematrix_value'),):
691                displacivemodematrix[int(row)-1,int(col)-1] = float(val)
692            # Invert to get mapping of atom displacements to modes
693            displacivemodeInvmatrix = np.linalg.inv(displacivemodematrix)
694            # create the constraints
695            for i,row in enumerate(displacivemodeInvmatrix):
696                constraint = []
697                for j,(lbl,k) in enumerate(zip(coordVarLbl,row)):
698                    if k == 0: continue
699                    constraint.append([k,G2varObj[j]])
700                constraint += [shortmodelist[i],False,'f']
701                self.Constraints.append(constraint)
702            #----------------------------------------------------------------------
703            # save the ISODISTORT info for "mode analysis"
704            if 'ISODISTORT' not in self.Phase: self.Phase['ISODISTORT'] = {}
705            self.Phase['ISODISTORT'].update({
706                'IsoModeList' : modelist,
707                'G2ModeList' : shortmodelist,
708                'IsoVarList' : coordVarLbl,
709                'G2VarList' : G2varObj,
710                'ParentStructure' : ParentCoordinates,
711                'Var2ModeMatrix' : displacivemodeInvmatrix,
712                'Mode2VarMatrix' : displacivemodematrix,
713                })
714            # make explaination dictionary
715            for mode,shortmode in zip(modelist,shortmodelist):
716                explaination[shortmode] = "ISODISTORT full name "+str(mode)
717        #----------------------------------------------------------------------
718        # now read in the ISODISTORT occupancy modes
719        #----------------------------------------------------------------------
720        if blk.get('_iso_occupancymode_label'):
721            modelist = []
722            shortmodelist = []
723            for lbl in blk.get('_iso_occupancymode_label'):
724                modelist.append(lbl)
725                # assume lbl is of form SSSSS[x,y,z]AAAA(a,b,...)BBBBB
726                # where SSSSS is the parent spacegroup, [x,y,z] is a location
727                regexp = re.match(r'.*?\[.*?\](.*?)\(.*?\)(.*)',lbl)
728                # this extracts the AAAAA and BBBBB parts of the string
729                if regexp:
730                    lbl = regexp.expand(r'\1\2') # parse succeeded, make a short version
731                lbl = lbl.replace('order','o')
732                G2obj.MakeUniqueLabel(lbl,shortmodelist) # make unique and add to list
733            # read in the coordinate offset variables names and map them to G2 names/objects
734            occVarLbl = []
735            G2varLbl = []
736            G2varObj = []
737            error = False
738            for lbl in blk.get('_iso_deltaoccupancy_label'):
739                occVarLbl.append(lbl)
740                if '_' in lbl:
741                    albl = lbl[:lbl.rfind('_')]
742                    vlbl = lbl[lbl.rfind('_')+1:]
743                else:
744                    self.warnings += ' ERROR: _iso_deltaoccupancy_label not parsed: '+lbl
745                    error = True
746                    continue
747                if albl not in atomlbllist:
748                    self.warnings += ' ERROR: _iso_deltaoccupancy_label atom not found: '+lbl
749                    error = True
750                    continue
751                else:
752                    anum = atomlbllist.index(albl)
753                var = varLookup.get(vlbl)
754                if not var:
755                    self.warnings += ' ERROR: _iso_deltaoccupancy_label variable not found: '+lbl
756                    error = True
757                    continue
758                G2varLbl.append('::'+var+':'+str(anum)) # variable name, less phase ID
759                G2varObj.append(G2obj.G2VarObj(
760                    (self.Phase['ranId'],None,var,ranIdlookup[albl])
761                    ))
762            if error:
763                raise Exception("Error decoding variable labels")
764
765            if len(G2varObj) != len(modelist):
766                print ("non-square input")
767                raise Exception("Rank of _iso_occupancymode != _iso_deltaoccupancy")
768
769            error = False
770            ParentCoordinates = {}
771            for lbl,exp in zip(
772                blk.get('_iso_occupancy_label'),
773                blk.get('_iso_occupancy_formula'),
774                ):
775                if '_' in lbl:
776                    albl = lbl[:lbl.rfind('_')]
777                    vlbl = lbl[lbl.rfind('_')+1:]
778                else:
779                    self.warnings += ' ERROR: _iso_occupancy_label not parsed: '+lbl
780                    error = True
781                    continue
782                if vlbl != 'occ':
783                    self.warnings += ' ERROR: _iso_occupancy_label coordinate not parsed: '+lbl
784                    error = True
785                    continue
786                if '+' in exp:
787                    val = exp.split('+')[0].strip()
788                    val = G2p3.FormulaEval(val)
789                    if val is None:
790                        self.warnings += ' ERROR: _iso_occupancy_formula coordinate not interpreted: '+lbl
791                        error = True
792                        continue
793                    ParentCoordinates[albl] = val
794            if error:
795                raise Exception("Error decoding occupancy labels")
796            # get mapping of modes to atomic coordinate displacements
797            occupancymodematrix = np.zeros((len(G2varObj),len(G2varObj)))
798            for row,col,val in zip(
799                blk.get('_iso_occupancymodematrix_row'),
800                blk.get('_iso_occupancymodematrix_col'),
801                blk.get('_iso_occupancymodematrix_value'),):
802                occupancymodematrix[int(row)-1,int(col)-1] = float(val)
803            # Invert to get mapping of atom displacements to modes
804            occupancymodeInvmatrix = np.linalg.inv(occupancymodematrix)
805            # create the constraints
806            for i,row in enumerate(occupancymodeInvmatrix):
807                constraint = []
808                for j,(lbl,k) in enumerate(zip(occVarLbl,row)):
809                    if k == 0: continue
810                    constraint.append([k,G2varObj[j]])
811                constraint += [shortmodelist[i],False,'f']
812                self.Constraints.append(constraint)
813            #----------------------------------------------------------------------
814            # save the ISODISTORT info for "mode analysis"
815            if 'ISODISTORT' not in self.Phase: self.Phase['ISODISTORT'] = {}
816            self.Phase['ISODISTORT'].update({
817                'OccModeList' : modelist,
818                'G2OccModeList' : shortmodelist,
819                'OccVarList' : occVarLbl,
820                'G2OccVarList' : G2varObj,
821                'BaseOcc' : ParentCoordinates,
822                'Var2OccMatrix' : occupancymodeInvmatrix,
823                'Occ2VarMatrix' : occupancymodematrix,
824                })
825            # make explaination dictionary
826            for mode,shortmode in zip(modelist,shortmodelist):
827                explaination[shortmode] = "ISODISTORT full name "+str(mode)
828        #----------------------------------------------------------------------
829        # done with read
830        #----------------------------------------------------------------------
831        if explaination: self.Constraints.append(explaination)
832
833        # # debug: show the mode var to mode relations
834        # for i,row in enumerate(displacivemodeInvmatrix):
835        #     l = ''
836        #     for j,(lbl,k) in enumerate(zip(coordVarLbl,row)):
837        #         if k == 0: continue
838        #         if l: l += ' + '
839        #         #l += lbl+' * '+str(k)
840        #         l += G2varLbl[j]+' * '+str(k)
841        #     print str(i) + ': '+shortmodelist[i]+' = '+l
842        # print 70*'='
843
844        # # debug: Get the ISODISTORT offset values
845        # coordVarDelta = {}
846        # for lbl,val in zip(
847        #     blk.get('_iso_deltacoordinate_label'),
848        #     blk.get('_iso_deltacoordinate_value'),):
849        #     coordVarDelta[lbl] = float(val)
850        # modeVarDelta = {}
851        # for lbl,val in zip(
852        #     blk.get('_iso_displacivemode_label'),
853        #     blk.get('_iso_displacivemode_value'),):
854        #     modeVarDelta[lbl] = cif.get_number_with_esd(val)[0]
855
856        # print 70*'='
857        # # compute the mode values from the reported coordinate deltas
858        # for i,row in enumerate(displacivemodeInvmatrix):
859        #     l = ''
860        #     sl = ''
861        #     s = 0.
862        #     for lbl,k in zip(coordVarLbl,row):
863        #         if k == 0: continue
864        #         if l: l += ' + '
865        #         l += lbl+' * '+str(k)
866        #         if sl: sl += ' + '
867        #         sl += str(coordVarDelta[lbl])+' * '+str(k)
868        #         s += coordVarDelta[lbl] * k
869        #     print 'a'+str(i)+' = '+l
870        #     print '\t= '+sl
871        #     print  modelist[i],shortmodelist[i],modeVarDelta[modelist[i]],s
872        #     print
873
874        # print 70*'='
875        # # compute the coordinate displacements from the reported mode values
876        # for i,lbl,row in zip(range(len(coordVarLbl)),coordVarLbl,displacivemodematrix):
877        #     l = ''
878        #     sl = ''
879        #     s = 0.0
880        #     for j,k in enumerate(row):
881        #         if k == 0: continue
882        #         if l: l += ' + '
883        #         l += 'a'+str(j+1)+' * '+str(k)
884        #         if sl: sl += ' + '
885        #         sl += str(shortmodelist[j]) +' = '+ str(modeVarDelta[modelist[j]]) + ' * '+str(k)
886        #         s += modeVarDelta[modelist[j]] * k
887        #     print lbl+' = '+l
888        #     print '\t= '+sl
889        #     print lbl,G2varLbl[i],coordVarDelta[lbl],s
890        #     print
891
892        # determine the coordinate delta values from deviations from the parent structure
893        # for atmline in self.Phase['Atoms']:
894        #     lbl = atmline[0]
895        #     x,y,z = atmline[3:6]
896        #     if lbl not in ParentCoordinates:
897        #         print lbl,x,y,z
898        #         continue
899        #     px,py,pz = ParentCoordinates[lbl]
900        #     print lbl,x,y,z,x-px,y-py,z-pz
Note: See TracBrowser for help on using the repository browser.