source: trunk/imports/G2pwd_CIF.py @ 1168

Last change on this file since 1168 was 1168, checked in by toby, 8 years ago

update imports to provide error messages

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 15.1 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2013-12-16 16:43:01 +0000 (Mon, 16 Dec 2013) $
4# $Author: toby $
5# $Revision: 1168 $
6# $URL: trunk/imports/G2pwd_CIF.py $
7# $Id: G2pwd_CIF.py 1168 2013-12-16 16:43:01Z toby $
8########### SVN repository information ###################
9'''
10*Module G2pwd_CIF: CIF powder data*
11------------------------------------
12
13Routine to read in powder data from a CIF.
14
15'''
16import sys
17import numpy as np
18import os.path
19import GSASIIIO as G2IO
20import CifFile as cif # PyCifRW from James Hester
21import GSASIIpath
22GSASIIpath.SetVersionNumber("$Revision: 1168 $")
23
24class CIFpwdReader(G2IO.ImportPowderData):
25    'Routines to import powder data from a CIF file'
26    def __init__(self):
27        super(self.__class__,self).__init__( # fancy way to self-reference
28            extensionlist=('.CIF','.cif'),
29            strictExtension=False,
30            formatName = 'CIF',
31            longFormatName = 'Powder data from CIF'
32            )
33    # Validate the contents
34    def ContentsValidator(self, filepointer):
35        'Use standard CIF validator'
36        return self.CIFValidator(filepointer)
37
38    def Reader(self,filename,filepointer, ParentFrame=None, **kwarg):
39        '''Read powder data from a CIF.
40        If multiple datasets are requested, use self.repeat and buffer caching.
41        '''
42
43        # Define lists of data names used for holding powder diffraction data
44        # entries of a type that are not implemented are commented out in case
45        # we will want them later.
46        xDataItems = (  # "x-axis" data names"
47            ('_pd_meas_2theta_range_min', '_pd_meas_2theta_range_max', '_pd_meas_2theta_range_inc'),
48            ('_pd_proc_2theta_range_min', '_pd_proc_2theta_range_max', '_pd_proc_2theta_range_inc'),
49            '_pd_meas_2theta_scan',
50            '_pd_meas_time_of_flight',
51            '_pd_proc_2theta_corrected',
52            #'_pd_proc_d_spacing',
53            #'_pd_proc_energy_incident',
54            #'_pd_proc_energy_detection',
55            #'_pd_proc_recip_len_q',
56            #'_pd_proc_wavelength',
57        )
58        intDataItems = ( # intensity axis data names
59            '_pd_meas_counts_total',
60            #'_pd_meas_counts_background',
61            #'_pd_meas_counts_container',
62            '_pd_meas_intensity_total',
63            #'_pd_meas_intensity_background',
64            #'_pd_meas_intensity_container',
65            '_pd_proc_intensity_net',
66            '_pd_proc_intensity_total',
67            #'_pd_proc_intensity_bkg_calc',
68            #'_pd_proc_intensity_bkg_fix',
69            '_pd_calc_intensity_net',   # allow computed patterns as input data?
70            '_pd_calc_intensity_total',
71            )
72
73        ESDDataItems = ( # items that can be used to compute uncertainties for obs data
74            '_pd_proc_ls_weight',
75            '_pd_meas_counts_total'
76            )
77       
78        ModDataItems = ( # items that modify the use of the data
79            '_pd_meas_step_count_time',
80            '_pd_meas_counts_monitor',
81            '_pd_meas_intensity_monitor',
82            '_pd_proc_intensity_norm',
83            '_pd_proc_intensity_incident',
84            )
85        rdbuffer = kwarg.get('buffer')
86        cf = None
87        choicelist = None
88        selections = None
89        # reload previously saved values
90        if self.repeat and rdbuffer is not None:
91            cf = rdbuffer.get('lastcif')
92            choicelist = rdbuffer.get('choicelist')
93            print 'debug: Reuse previously parsed CIF'
94            selections = rdbuffer.get('selections')
95        if cf is None:
96            self.ShowBusy() # this can take a while
97            print "Starting parse of CIF file"
98            try:
99                cf = G2IO.ReadCIF(filename)
100            except Exception as detail:
101                self.errors = "Parse or reading of file failed in pyCifRW; check syntax of file in enCIFer or CheckCIF"
102                return False
103            finally:
104                self.DoneBusy()
105            print "CIF file parsed"
106        # scan all blocks for sets of data
107        if choicelist is None:
108            try:
109                choicelist = []
110                for blk in cf.keys():
111                    blkkeys = [k.lower() for k in cf[blk].keys()] # save a list of the data items, since we will use it often
112                    # scan through block for x items
113                    xldict = {}
114                    for x in xDataItems:
115                        if type(x) is tuple: # check for the presence of all three items that define a range of data
116                            if not all([i in blkkeys for i in x]): continue
117                            try:
118                                items = [float(cf[blk][xi]) for xi in x]
119                                l = 1 + int(0.5 + (items[1]-items[0])/items[2])
120                            except:
121                                continue
122                        else:
123                            if x not in blkkeys: continue
124                            l = len(cf[blk][x])
125                        if xldict.get(l) is None:
126                            xldict[l] = [x]
127                        else:
128                            xldict[l].append(x)
129                    # now look for matching intensity items
130                    yldict = {}
131                    suldict = {}
132                    for y in intDataItems:
133                        if y in blkkeys:
134                            l = len(cf[blk][y])
135                            if yldict.get(l) is None:
136                                yldict[l] = [y]
137                            else:
138                                yldict[l].append(y)
139                            # now check if the first item has an uncertainty
140                            if cif.get_number_with_esd(cf[blk][y][0])[1] is None: continue
141                            if suldict.get(l) is None:
142                                suldict[l] = [y]
143                            else:
144                                suldict[l].append(y)
145                    for y in ESDDataItems:
146                        if y in blkkeys:
147                            l = len(cf[blk][y])
148                            if suldict.get(l) is None:
149                                suldict[l] = [y]
150                            else:
151                                suldict[l].append(y)
152                    modldict = {}
153                    for y in ModDataItems:
154                        if y in blkkeys:
155                            l = len(cf[blk][y])
156                            if modldict.get(l) is None:
157                                modldict[l] = [y]
158                            else:
159                                modldict[l].append(y)
160                    for l in xldict:
161                        if yldict.get(l) is None: continue
162                        choicelist.append([blk,l,xldict[l],yldict[l],suldict.get(l,[]),modldict.get(l,[])])
163                        #print blk,l,xldict[l],yldict[l],suldict.get(l,[]),modldict.get(l,[])
164            except Exception as detail:
165                self.errors = "Error scanning blocks"
166                self.errors += "\n  Read exception: "+str(detail)
167                print self.formatName+' read error:'+str(detail) # for testing
168                import traceback
169                traceback.print_exc(file=sys.stdout)
170                #self.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
171                return False
172            print "CIF file scanned for blocks with data"
173        if not choicelist:
174            selblk = None # no block to choose
175            self.errors = "No powder diffraction blocks found"
176            return False
177        elif len(choicelist) == 1: # only one choice
178            selblk = 0
179        elif self.repeat and selections is not None:
180            # we were called to repeat the read
181            print 'debug: repeat #',self.repeatcount,'selection',selections[self.repeatcount]
182            selblk = selections[self.repeatcount]
183            self.repeatcount += 1
184            if self.repeatcount >= len(selections): self.repeat = False
185        else:                       # choose from options
186            # compile a list of choices for the user
187            choices = []
188            for blk,l,x,y,su,mod in choicelist:
189                sx = x[0]
190                if len(x) > 1: sx += '...'
191                sy = y[0]
192                if len(y) > 1: sy += '...'
193                choices.append(
194                    'Block '+str(blk)+', '+str(l)+' points. X='+sx+' & Y='+sy
195                    )
196            selections = self.MultipleBlockSelector(
197                choices,
198                ParentFrame=ParentFrame,
199                title='Select dataset(s) to read from the list below',
200                size=(600,100),
201                header='Dataset Selector')
202            if len(selections) == 0:
203                self.errors = "Abort: block not selected"
204                return False
205            selblk = selections[0] # select first in list
206            if len(selections) > 1: # prepare to loop through again
207                self.repeat = True
208                self.repeatcount = 1
209                if rdbuffer is not None:
210                    rdbuffer['selections'] = selections
211                    rdbuffer['lastcif'] = cf # save the parsed cif for the next loop
212                    rdbuffer['choicelist'] = choicelist # save the parsed choices for the future
213
214        # got a selection, now read it
215        # do we need to ask which fields to read?
216        blk,l,xch,ych,such,modch = choicelist[selblk]
217        xi,yi,sui,modi = 0,0,0,0
218        if len(xch) > 1 or len(ych) > 1 or len(such) > 1 or len(modch) > 0:
219            choices = []
220            chlbls = []
221            chlbls.append('Select the scanned data item')
222            xchlst = []
223            for x in xch:
224                if type(x) is tuple:
225                    xchlst.append(x[0])
226                else:
227                    xchlst.append(x)
228            choices.append(xchlst)
229            chlbls.append('Select the intensity data item')
230            choices.append(ych)
231            chlbls.append('Select the data item to be used for weighting')
232            choices.append(such)
233            chlbls.append('Divide intensities by data item')
234            choices.append(['none']+modch)
235            res = self.MultipleChoicesDialog(choices,chlbls)
236            if not res:
237                self.errors = "Abort: data items not selected"
238                return False
239            xi,yi,sui,modi = res
240
241        # now read in the values
242        try:
243            self.ShowBusy() # this can also take a while
244            # x-values
245            xcf = xch[xi]
246            if type(xcf) is tuple:
247                vals = [float(cf[blk][xi]) for xi in xcf]
248                x = np.array(
249                    [(i*vals[2] + vals[0]) for i in range(1 + int(0.5 + (vals[1]-vals[0])/vals[2]))]
250                    )
251            else:
252                vl = []
253                for val in cf[blk].get(xcf,'?'):
254                    v,e = cif.get_number_with_esd(val)
255                    if v is None: # not parsed
256                        vl.append(np.NaN)
257                    else:
258                        vl.append(v)
259                x = np.array(vl)
260            # y-values
261            ycf = ych[yi]
262            vl = []
263            for val in cf[blk].get(ycf,'?'):
264                v,e = cif.get_number_with_esd(val)
265                if v is None: # not parsed
266                    vl.append(np.NaN)
267                else:
268                    vl.append(v)
269            y = np.array(vl)
270            # weights
271            if sui == -1:
272                # no weights
273                vl = np.zeros(len(x)) + 1.
274            else:
275                sucf = such[sui]
276                if sucf ==  '_pd_proc_ls_weight':
277                    for val in cf[blk].get(sucf,'?'):
278                        v,e = cif.get_number_with_esd(val)
279                        if v is None: # not parsed
280                            vl.append(0.)
281                        else:
282                            vl.append(v)
283                elif sucf ==  '_pd_meas_counts_total':
284                    for val in cf[blk].get(sucf,'?'):
285                        v,e = cif.get_number_with_esd(val)
286                        if v is None: # not parsed
287                            vl.append(0.)
288                        elif v <= 0:
289                            vl.append(1.)
290                        else:
291                            vl.append(1./v)
292                else:
293                    for val in cf[blk].get(sucf,'?'):
294                        v,e = cif.get_number_with_esd(val)
295                        if v is None: # not parsed
296                            vl.append(0.0)
297                        elif v <= 0:
298                            vl.append(0.0)
299                        else:
300                            vl.append(1./(v*v))
301            w = np.array(vl)
302            # intensity modification factor
303            if modi >= 1:
304                ycf = modch[modi-1]
305                vl = []
306                for val in cf[blk].get(ycf,'?'):
307                    v,e = cif.get_number_with_esd(val)
308                    if v is None: # not parsed
309                        vl.append(np.NaN)
310                    else:
311                        vl.append(v)
312                y /= np.array(vl)
313                w /= np.array(vl)
314            N = len(x)
315        except Exception as detail:
316            self.errors = "Error reading from selected block"
317            self.errors += "\n  Read exception: "+str(detail)
318            print self.formatName+' read error:'+str(detail) # for testing
319            import traceback
320            traceback.print_exc(file=sys.stdout)
321            #self.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
322            return False
323        finally:
324            self.DoneBusy()
325            print "CIF file, read from selected block"
326
327        self.errors = "Error while storing read values"
328        self.powderdata = [
329                np.array(x), # x-axis values
330                np.array(y), # powder pattern intensities
331                np.array(w), # 1/sig(intensity)^2 values (weights)
332                np.zeros(N), # calc. intensities (zero)
333                np.zeros(N), # calc. background (zero)
334                np.zeros(N), # obs-calc profiles
335            ]
336        self.powderentry[0] = filename
337        #self.powderentry[1] = pos # bank offset (N/A here)
338        self.powderentry[2] = 1 # xye file only has one bank
339        self.idstring = os.path.basename(filename) + ': ' + blk
340        if cf[blk].get('_diffrn_radiation_probe'):
341            if cf[blk]['_diffrn_radiation_probe'] == 'neutron':
342                self.instdict['type'] = 'PNC'
343                #if cf[blk].get('_pd_meas_time_of_flight'): self.instdict['type'] = 'PNT' # not supported yet
344            else:
345                self.instdict['type'] = 'PXC'
346        if cf[blk].get('_diffrn_radiation_wavelength'):
347            val = cf[blk]['_diffrn_radiation_wavelength']
348            wl = []
349            if type(val) is list:
350                for v in val:
351                    w,e = cif.get_number_with_esd(v)
352                    if w: wl.append(w)
353            else:
354                w,e = cif.get_number_with_esd(val)
355                if w: wl.append(w)
356            if wl: self.instdict['wave'] = wl
357        if cf[blk].get('_diffrn_ambient_temperature'):
358            val = cf[blk]['_diffrn_ambient_temperature']
359            w,e = cif.get_number_with_esd(val)
360            if w: 
361                self.Sample['Temperature'] = w
362        return True
Note: See TracBrowser for help on using the repository browser.