source: trunk/GSASIIfiles.py @ 3296

Last change on this file since 3296 was 3216, checked in by toby, 7 years ago

add data & parameter access in scripting

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 17.5 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2018-01-08 03:06:19 +0000 (Mon, 08 Jan 2018) $
4# $Author: vondreele $
5# $Revision: 3216 $
6# $URL: trunk/GSASIIfiles.py $
7# $Id: GSASIIfiles.py 3216 2018-01-08 03:06:19Z vondreele $
8########### SVN repository information ###################
9'''
10*GSASIIfile: data (non-GUI) I/O routines*
11=========================================
12
13Module with miscellaneous routines for input and output from files.
14
15This module should not contain any references to wxPython so that it
16can be imported for scriptable use or potentially on clients where
17wx is not installed.
18
19Future refactoring: This module and GSASIIIO.py needs some work to
20move non-wx routines here. It may will likely make sense to rename the module(s)
21at that point.
22'''
23from __future__ import division, print_function
24import os
25import sys
26import glob
27import imp
28import inspect
29import platform
30import numpy as np
31
32import GSASIIpath
33GSASIIpath.SetVersionNumber("$Revision: 3216 $")
34
35# N.B. This is duplicated in G2IO
36def sfloat(S):
37    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
38    if S.strip():
39        try:
40            return float(S)
41        except ValueError:
42            pass
43    return 0.0
44
45def get_python_versions(packagelist):
46    versions = [['Python', sys.version.split()[0]]]
47    for pack in packagelist:
48        try:
49            versions.append([pack.__name__, pack.__version__])
50        except:
51            pass
52    versions.append(['Platform',
53                     sys.platform + ' ' + platform.architecture()[0] +
54                     ' ' + platform.machine()])
55    return versions
56
57def makeInstDict(names,data,codes):
58    inst = dict(zip(names,zip(data,data,codes)))
59    for item in inst:
60        inst[item] = list(inst[item])
61    return inst
62
63def SetPowderInstParms(Iparm, rd):
64    '''extracts values from instrument parameters in rd.instdict
65    or in array Iparm.
66    Create and return the contents of the instrument parameter tree entry.
67    '''
68    Irads = {0:' ',1:'CrKa',2:'FeKa',3:'CuKa',4:'MoKa',5:'AgKa',6:'TiKa',7:'CoKa'}
69    DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
70    # override inst values with values read from data file
71    Bank = rd.powderentry[2]    #should be used in multibank iparm files
72    if rd.instdict.get('type'):
73        DataType = rd.instdict.get('type')
74    data = [DataType,]
75    instname = Iparm.get('INS  1INAME ')
76    irad = int(Iparm.get('INS  1 IRAD ','0'))
77    if instname:
78        rd.Sample['InstrName'] = instname.strip()
79    if 'C' in DataType:
80        wave1 = None
81        wave2 = 0.0
82        if rd.instdict.get('wave'):
83            wl = rd.instdict.get('wave')
84            wave1 = wl[0]
85            if len(wl) > 1: wave2 = wl[1]
86        s = Iparm['INS  1 ICONS']
87        if not wave1:
88            wave1 = sfloat(s[:10])
89            wave2 = sfloat(s[10:20])
90        v = (wave1,wave2,
91             sfloat(s[20:30])/100.,sfloat(s[55:65]),sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
92        if not v[1]:
93            names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth']
94            v = (v[0],v[2],v[4])
95            codes = [0,0,0,0,0]
96            rd.Sample.update({'Type':'Debye-Scherrer','Absorption':[0.,False],'DisplaceX':[0.,False],'DisplaceY':[0.,False]})
97        else:
98            names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth']
99            codes = [0,0,0,0,0,0,0]
100            rd.Sample.update({'Type':'Bragg-Brentano','Shift':[0.,False],'Transparency':[0.,False],
101                'SurfRoughA':[0.,False],'SurfRoughB':[0.,False]})
102        data.extend(v)
103        if 'INS  1PRCF  ' in Iparm:
104            v1 = Iparm['INS  1PRCF  '].split()
105            v = Iparm['INS  1PRCF 1'].split()
106            data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
107            azm = float(Iparm.get('INS  1DETAZM','0.0'))
108            v = Iparm['INS  1PRCF 2'].split()
109            if v1[0] == 3:
110                data.extend([float(v[0]),float(v[1]),0.0,float(v[2])+float(v[3],azm)])  #get LX, LY, Z, S+H/L & azimuth
111            else:
112                data.extend([0.0,0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
113        else:
114            v1 = Iparm['INS  1PRCF1 '].split()
115            v = Iparm['INS  1PRCF11'].split()
116            data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
117            azm = float(Iparm.get('INS  1DETAZM','0.0'))
118            v = Iparm['INS  1PRCF12'].split()
119            if v1[0] == 3:
120                data.extend([float(v[0]),float(v[1]),0.0,float(v[2])+float(v[3],azm)])  #get LX, LY, Z, S+H/L & azimuth
121            else:
122                data.extend([0.0,0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
123        codes.extend([0,0,0,0,0,0,0])
124        Iparm1 = makeInstDict(names,data,codes)
125        Iparm1['Source'] = [Irads[irad],Irads[irad]]
126        Iparm1['Bank'] = [Bank,Bank,0]
127        return [Iparm1,{}]
128    elif 'T' in DataType:
129        names = ['Type','fltPath','2-theta','difC','difA', 'difB','Zero','alpha','beta-0','beta-1',
130            'beta-q','sig-0','sig-1','sig-2','sig-q', 'X','Y','Z','Azimuth',]
131        codes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,]
132        azm = 0.
133        if 'INS  1DETAZM' in Iparm:
134            azm = float(Iparm['INS  1DETAZM'])
135        rd.Sample['Azimuth'] = azm
136        fltPath0 = 20.                      #arbitrary
137        if 'INS   FPATH1' in Iparm:
138            s = Iparm['INS   FPATH1'].split()
139            fltPath0 = sfloat(s[0])
140        if 'INS  1BNKPAR' not in Iparm:     #bank missing from Iparm file
141            return []
142        s = Iparm['INS  1BNKPAR'].split()
143        fltPath1 = sfloat(s[0])
144        data.extend([fltPath0+fltPath1,])               #Flight path source-sample-detector
145        data.extend([sfloat(s[1]),])               #2-theta for bank
146        s = Iparm['INS  1 ICONS'].split()
147        data.extend([sfloat(s[0]),sfloat(s[1]),0.0,sfloat(s[2])])    #difC,difA,difB,Zero
148        if 'INS  1PRCF  ' in Iparm:
149            s = Iparm['INS  1PRCF  '].split()
150            pfType = int(s[0])
151            s = Iparm['INS  1PRCF 1'].split()
152            if abs(pfType) == 1:
153                data.extend([sfloat(s[1]),sfloat(s[2]),sfloat(s[3])]) #alpha, beta-0, beta-1
154                s = Iparm['INS  1PRCF 2'].split()
155                data.extend([0.0,0.0,sfloat(s[1]),sfloat(s[2]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
156            elif abs(pfType) in [3,4,5]:
157                data.extend([sfloat(s[0]),sfloat(s[1]),sfloat(s[2])]) #alpha, beta-0, beta-1
158                if abs(pfType) == 4:
159                    data.extend([0.0,0.0,sfloat(s[3]),0.0,0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
160                else:
161                    s = Iparm['INS  1PRCF 2'].split()
162                    data.extend([0.0,0.0,sfloat(s[0]),sfloat(s[1]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
163            elif abs(pfType) == 2:
164                data.extend([sfloat(s[1]),0.0,1./sfloat(s[3])]) #alpha, beta-0, beta-1
165                data.extend([0.0,0.0,sfloat(s[1]),0.0,0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
166        else:
167            s = Iparm['INS  1PRCF1 '].split()
168            pfType = int(s[0])
169            s = Iparm['INS  1PRCF11'].split()
170            if abs(pfType) == 1:
171                data.extend([sfloat(s[1]),sfloat(s[2]),sfloat(s[3])]) #alpha, beta-0, beta-1
172                s = Iparm['INS  1PRCF12'].split()
173                data.extend([0.0,0.0,sfloat(s[1]),sfloat(s[2]),0.0,0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
174            elif abs(pfType) in [3,4,5]:
175                data.extend([sfloat(s[0]),sfloat(s[1]),sfloat(s[2])]) #alpha, beta-0, beta-1
176                if abs(pfType) == 4:
177                    data.extend([0.0,0.0,sfloat(s[3]),0.0,0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
178                else:
179                    s = Iparm['INS  1PRCF12'].split()
180                    data.extend([0.0,0.0,sfloat(s[0]),sfloat(s[1]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
181        Inst1 = makeInstDict(names,data,codes)
182        Inst1['Bank'] = [Bank,Bank,0]
183        Inst2 = {}
184        if pfType < 0:
185            Ipab = 'INS  1PAB'+str(-pfType)
186            Npab = int(Iparm[Ipab+'  '].strip())
187            Inst2['Pdabc'] = []
188            for i in range(Npab):
189                k = Ipab+str(i+1).rjust(2)
190                s = Iparm[k].split()
191                Inst2['Pdabc'].append([float(t) for t in s])
192            Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
193            Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
194        if 'INS  1I ITYP' in Iparm:
195            s = Iparm['INS  1I ITYP'].split()
196            Ityp = int(s[0])
197            Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
198            Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
199            if Ityp in [1,2,4]:
200                Inst2['Itype'] = Itypes[Ityp-1]
201                Inst2['Tminmax'] = Tminmax
202                Icoeff = []
203                Iesd = []
204                Icovar = []
205                for i in range(3):
206                    s = Iparm['INS  1ICOFF'+str(i+1)].split()
207                    Icoeff += [float(S) for S in s]
208                    s = Iparm['INS  1IECOF'+str(i+1)].split()
209                    Iesd += [float(S) for S in s]
210                NT = 10
211                for i in range(8):
212                    s = Iparm['INS  1IECOR'+str(i+1)]
213                    if i == 7:
214                        NT = 8
215                    Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
216                Inst2['Icoeff'] = Icoeff
217                Inst2['Iesd'] = Iesd
218                Inst2['Icovar'] = Icovar
219        return [Inst1,Inst2]
220
221def ReadPowderInstprm(instLines, bank, databanks, rd):
222    '''Read lines from a GSAS-II (new) instrument parameter file
223    similar to G2pwdGUI.OnLoad
224    If instprm file has multiple banks each with header #Bank n: ..., this
225    finds matching bank no. to load - problem with nonmatches?
226   
227    Note that this routine performs a similar role to :meth:`GSASIIdataGUI.GSASII.ReadPowderInstprm`,
228    but that will call a GUI routine for selection when needed. This routine will raise exceptions
229    on errors and will select the first bank when a choice might be appropriate.
230    TODO: refactor to combine the two routines.
231   
232    :param list instLines: strings from GSAS-II parameter file; can be concatenated with ';'
233    :param int  bank: bank number to check when instprm file has '#BANK n:...' strings
234         when bank = n then use parameters; otherwise skip that set. Ignored if BANK n:
235         not present. NB: this kind of instprm file made by a Save all profile command in Instrument Par     ameters
236    :return dict: Inst  instrument parameter dict if OK, or
237             str: Error message if failed
238   
239    (transliterated from GSASIIdataGUI.py:1235 (rev 3008), function of the same name)
240     ''' 
241    if 'GSAS-II' not in instLines[0]:
242        raise ValueError("Not a valid GSAS-II instprm file")
243
244    newItems = []
245    newVals = []
246    Found = False
247    il = 0
248    if bank is None:
249        banklist = set()
250        for S in instLines:
251            if S[0] == '#' and 'Bank' in S:
252                banklist.add(int(S.split(':')[0].split()[1]))
253        # Picks the first bank by default
254        if len(banklist) > 1:
255            bank = sorted(banklist)[0]
256        else:
257            bank = 1
258        rd.powderentry[2] = bank
259    while il < len(instLines):
260        S = instLines[il]
261        if S[0] == '#':
262            if Found:
263                break
264            if 'Bank' in S:
265                if bank == int(S.split(':')[0].split()[1]):
266                    il += 1
267                    S = instLines[il]
268                else:
269                    il += 1
270                    S = instLines[il]
271                    while il < len(instLines) and '#Bank' not in S:
272                        il += 1
273                        if il == len(instLines):
274                            raise ValueError("Bank {} not found in instprm file".format(bank))
275                        S = instLines[il]
276                    continue
277            else:
278                il += 1
279                S = instLines[il]
280        Found = True
281        if '"""' in S:
282            delim = '"""'
283        elif "'''" in S:
284            delim = "'''"
285        else:
286            S = S.replace(' ', '')
287            SS = S.strip().split(';')
288            for s in SS:
289                item, val = s.split(':', 1)
290                newItems.append(item)
291                try:
292                    newVals.append(float(val))
293                except ValueError:
294                    newVals.append(val)
295            il += 1
296            continue
297        # read multiline values, delimited by ''' or """
298        item, val = S.strip().split(':', 1)
299        val = val.replace(delim, '').rstrip()
300        val += '\n'
301        while True:
302            il += 1
303            if il >= len(instLines):
304                break
305            S = instLines[il]
306            if delim in S:
307                val += S.replace(delim, '').rstrip()
308                val += '\n'
309                break
310            else:
311                val += S.rstrip()
312                val += '\n'
313        newItems.append(item)
314        newVals.append(val)
315        il += 1
316    return [makeInstDict(newItems, newVals, len(newVals)*[False]), {}]
317
318def LoadImportRoutines(prefix, errprefix=None, traceback=False):
319    '''Routine to locate GSASII importers matching a prefix string
320    '''
321    if errprefix is None:
322        errprefix = prefix
323
324    readerlist = []
325    pathlist = sys.path[:]
326    if '.' not in pathlist:
327        pathlist.append('.')
328
329    potential_files = []
330    for path in pathlist:
331        for filename in glob.iglob(os.path.join(path, 'G2'+prefix+'*.py')):
332            potential_files.append(filename)
333
334    potential_files = sorted(list(set(potential_files)))
335    for filename in potential_files:
336        path, rootname = os.path.split(filename)
337        pkg = os.path.splitext(rootname)[0]
338
339        try:
340            fp = None
341            fp, fppath, desc = imp.find_module(pkg, [path])
342            pkg = imp.load_module(pkg, fp, fppath, desc)
343            for name, value in inspect.getmembers(pkg):
344                if name.startswith('_'):
345                    continue
346                if inspect.isclass(value):
347                    for method in 'Reader', 'ExtensionValidator', 'ContentsValidator':
348                        if not hasattr(value, method):
349                            break
350                        if not callable(getattr(value, method)):
351                            break
352                    else:
353                        reader = value()
354                        if reader.UseReader:
355                            readerlist.append(reader)
356        except AttributeError:
357            print ('Import_' + errprefix + ': Attribute Error ' + filename)
358            if traceback:
359                traceback.print_exc(file=sys.stdout)
360        except Exception as exc:
361            print ('\nImport_' + errprefix + ': Error importing file ' + filename)
362            print (u'Error message: {}\n'.format(exc))
363            if traceback:
364                traceback.print_exc(file=sys.stdout)
365        finally:
366            if fp:
367                fp.close()
368
369    return readerlist
370
371def LoadExportRoutines(parent, traceback=False):
372    '''Routine to locate GSASII exporters
373    '''
374    exporterlist = []
375    pathlist = sys.path
376    filelist = []
377    for path in pathlist:
378        for filename in glob.iglob(os.path.join(path,"G2export*.py")):
379                    filelist.append(filename)   
380    filelist = sorted(list(set(filelist))) # remove duplicates
381    # go through the routines and import them, saving objects that
382    # have export routines (method Exporter)
383    for filename in filelist:
384        path,rootname = os.path.split(filename)
385        pkg = os.path.splitext(rootname)[0]
386        try:
387            fp = None
388            fp, fppath,desc = imp.find_module(pkg,[path,])
389            pkg = imp.load_module(pkg,fp,fppath,desc)
390            for clss in inspect.getmembers(pkg): # find classes defined in package
391                if clss[0].startswith('_'): continue
392                if not inspect.isclass(clss[1]): continue
393                # check if we have the required methods
394                if not hasattr(clss[1],'Exporter'): continue
395                if not callable(getattr(clss[1],'Exporter')): continue
396                if parent is None:
397                    if not hasattr(clss[1],'Writer'): continue
398                else:
399                    if not hasattr(clss[1],'loadParmDict'): continue
400                    if not callable(getattr(clss[1],'loadParmDict')): continue
401                try:
402                    exporter = clss[1](parent) # create an export instance
403                except AttributeError:
404                    pass
405                except Exception as exc:
406                    print ('\nExport init: Error substantiating class ' + clss[0])
407                    print (u'Error message: {}\n'.format(exc))
408                    if traceback:
409                        traceback.print_exc(file=sys.stdout)
410                    continue
411                exporterlist.append(exporter)
412        except AttributeError:
413            print ('Export Attribute Error ' + filename)
414            if traceback:
415                traceback.print_exc(file=sys.stdout)
416        except Exception as exc:
417            print ('\nExport init: Error importing file ' + filename)
418            print (u'Error message: {}\n'.format(exc))
419            if traceback:
420                traceback.print_exc(file=sys.stdout)
421        finally:
422            if fp:
423                fp.close()
424    return exporterlist
Note: See TracBrowser for help on using the repository browser.