source: trunk/GSASIIscriptable.py @ 3012

Last change on this file since 3012 was 3012, checked in by odonnell, 4 years ago

Switch GSASIIscriptable.py to argparse, fixed bug with .instprm files

File size: 76.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3########### SVN repository information ###################
4# $Date: 2017-04-12 15:12:45 -0500 (Wed, 12 Apr 2017) $
5# $Author: vondreele $
6# $Revision: 2777 $
7# $URL: https://subversion.xray.aps.anl.gov/pyGSAS/trunk/GSASIIscriptable.py $
8# $Id: GSASIIIO.py 2777 2017-04-12 20:12:45Z vondreele $
9########### SVN repository information ###################
10"""
11*GSASIIscriptable: Scripting Tools*
12-----------------------------------
13
14Routines for reading, writing, modifying and creating GSAS-II project (.gpx) files.
15
16Supports a command line interface as well.
17
18Look at :class:`G2Project` to start.
19
20=====================
21Refinement parameters
22=====================
23
24There are three classes of refinement parameters:
25
26    * Histogram. Turned on and off through :func:`~G2PwdrData.set_refinements`
27      and :func:`~G2PwdrData.clear_refinements`
28    * Phase. Turned on and off through :func:`~G2Phase.set_refinements`
29      and :func:`~G2Phase.clear_refinements`
30    * Histogram-and-phase (HAP). Turned on and off through
31      :func:`~G2Phase.set_HAP_refinements` and :func:`~G2Phase.clear_HAP_refinements`
32"""
33from __future__ import division, print_function # needed?
34import argparse
35import os.path as ospath
36import datetime as dt
37import sys
38import cPickle
39import imp
40import copy
41import os
42import random as ran
43
44import numpy.ma as ma
45import scipy.interpolate as si
46import numpy as np
47import scipy as sp
48
49import GSASIIpath
50GSASIIpath.SetBinaryPath(False) # would rather have this in __name__ == '__main__' stanza
51import GSASIIobj as G2obj
52import GSASIIpwd as G2pwd
53import GSASIIstrMain as G2strMain
54import GSASIIspc as G2spc
55import GSASIIElem as G2elem
56
57# Delay imports to not slow down small scripts
58G2fil = None
59PwdrDataReaders = []
60PhaseReaders = []
61
62
63def LoadG2fil():
64    """Delay importing this module, it is slow"""
65    global G2fil
66    global PwdrDataReaders
67    global PhaseReaders
68    if G2fil is None:
69        import GSASIIfiles
70        G2fil = GSASIIfiles
71        PwdrDataReaders = G2fil.LoadImportRoutines("pwd", "Powder_Data")
72        PhaseReaders = G2fil.LoadImportRoutines("phase", "Phase")
73
74
75def LoadDictFromProjFile(ProjFile):
76    '''Read a GSAS-II project file and load items to dictionary
77    :param str ProjFile: GSAS-II project (name.gpx) full file name
78    :returns: Project,nameList, where
79
80      * Project (dict) is a representation of gpx file following the GSAS-II tree struture
81        for each item: key = tree name (e.g. 'Controls','Restraints',etc.), data is dict
82        data dict = {'data':item data whch may be list, dict or None,'subitems':subdata (if any)}
83      * nameList (list) has names of main tree entries & subentries used to reconstruct project file
84
85    Example for fap.gpx::
86
87      Project = {                 #NB:dict order is not tree order
88        u'Phases':{'data':None,'fap':{phase dict}},
89        u'PWDR FAP.XRA Bank 1':{'data':[histogram data list],'Comments':comments,'Limits':limits, etc},
90        u'Rigid bodies':{'data': {rigid body dict}},
91        u'Covariance':{'data':{covariance data dict}},
92        u'Controls':{'data':{controls data dict}},
93        u'Notebook':{'data':[notebook list]},
94        u'Restraints':{'data':{restraint data dict}},
95        u'Constraints':{'data':{constraint data dict}}]
96        }
97      nameList = [                #NB: reproduces tree order
98        [u'Notebook',],
99        [u'Controls',],
100        [u'Covariance',],
101        [u'Constraints',],
102        [u'Restraints',],
103        [u'Rigid bodies',],
104        [u'PWDR FAP.XRA Bank 1',
105             u'Comments',
106             u'Limits',
107             u'Background',
108             u'Instrument Parameters',
109             u'Sample Parameters',
110             u'Peak List',
111             u'Index Peak List',
112             u'Unit Cells List',
113             u'Reflection Lists'],
114        [u'Phases', u'fap']
115        ]
116    '''
117    # Let IOError be thrown if file does not exist
118    # if not ospath.exists(ProjFile):
119    #     print ('\n*** Error attempt to open project file that does not exist:\n   '+
120    #         str(ProjFile))
121    #     return
122    file = open(ProjFile,'rb')
123    # print('loading from file: {}'.format(ProjFile))
124    Project = {}
125    nameList = []
126    try:
127        while True:
128            try:
129                data = cPickle.load(file)
130            except EOFError:
131                break
132            datum = data[0]
133            Project[datum[0]] = {'data':datum[1]}
134            nameList.append([datum[0],])
135            for datus in data[1:]:
136                Project[datum[0]][datus[0]] = datus[1]
137                nameList[-1].append(datus[0])
138        file.close()
139        # print('project load successful')
140    except:
141        raise IOError("Error reading file "+str(ProjFile)+". This is not a GSAS-II .gpx file")
142    finally:
143        file.close()
144    return Project,nameList
145
146def SaveDictToProjFile(Project,nameList,ProjFile):
147    '''Save a GSAS-II project file from dictionary/nameList created by LoadDictFromProjFile
148
149    :param dict Project: representation of gpx file following the GSAS-II
150        tree structure as described for LoadDictFromProjFile
151    :param list nameList: names of main tree entries & subentries used to reconstruct project file
152    :param str ProjFile: full file name for output project.gpx file (including extension)
153    '''
154    file = open(ProjFile,'wb')
155    # print('save to file: {}'.format(ProjFile))
156    try:
157        for name in nameList:
158            data = []
159            item = Project[name[0]]
160            data.append([name[0],item['data']])
161            for item2 in name[1:]:
162                data.append([item2,item[item2]])
163            cPickle.dump(data,file,1)
164    finally:
165        file.close()
166    # print('project save successful')
167
168def ImportPowder(reader,filename):
169    '''Use a reader to import a powder diffraction data file
170
171    :param str reader: a scriptable reader
172    :param str filename: full name of powder data file; can be "multi-Bank" data
173
174    :returns list rdlist: list of reader objects containing powder data, one for each
175        "Bank" of data encountered in file. Items in reader object of interest are:
176
177          * rd.comments: list of str: comments found on powder file
178          * rd.dnames: list of str: data nammes suitable for use in GSASII data tree NB: duplicated in all rd entries in rdlist
179          * rd.powderdata: list of numpy arrays: pos,int,wt,zeros,zeros,zeros as needed for a PWDR entry in  GSASII data tree.
180    '''
181    rdfile,rdpath,descr = imp.find_module(reader)
182    rdclass = imp.load_module(reader,rdfile,rdpath,descr)
183    rd = rdclass.GSAS_ReaderClass()
184    if not rd.scriptable:
185        print(u'**** ERROR: '+reader+u' is not a scriptable reader')
186        return None
187    fl = open(filename,'rb')
188    rdlist = []
189    if rd.ContentsValidator(fl):
190        fl.seek(0)
191        repeat = True
192        rdbuffer = {} # create temporary storage for file reader
193        block = 0
194        while repeat: # loop if the reader asks for another pass on the file
195            block += 1
196            repeat = False
197            rd.objname = ospath.basename(filename)
198            flag = rd.Reader(filename,fl,None,buffer=rdbuffer,blocknum=block,)
199            if flag:
200                rdlist.append(copy.deepcopy(rd)) # save the result before it is written over
201                if rd.repeat:
202                    repeat = True
203        return rdlist
204    print(rd.errors)
205    return None
206
207def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
208    '''Create an initial Histogram dictionary
209
210    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
211    '''
212    if dType in ['SXC','SNC']:
213        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
214            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
215            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
216            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}],
217            'Flack':[0.0,False]}
218    elif dType == 'SNT':
219        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
220            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
221            'Extinction':['Lorentzian','None', {
222            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
223    elif 'P' in dType:
224        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
225            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{},[],0.1],
226            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
227                [1.,1.,1.,0.,0.,0.],6*[False,]],
228            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
229                NShkl*[0.01,],NShkl*[False,]],
230            'HStrain':[NDij*[0.0,],NDij*[False,]],
231            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
232
233
234def PreSetup(data):
235    '''Create part of an initial (empty) phase dictionary
236
237    from GSASIIphsGUI.py, near end of UpdatePhaseData
238
239    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
240    '''
241    if 'RBModels' not in data:
242        data['RBModels'] = {}
243    if 'MCSA' not in data:
244        data['MCSA'] = {'Models':[{'Type':'MD','Coef':[1.0,False,[.8,1.2],],'axis':[0,0,1]}],'Results':[],'AtInfo':{}}
245    if 'dict' in str(type(data['MCSA']['Results'])):
246        data['MCSA']['Results'] = []
247    if 'Modulated' not in data['General']:
248        data['General']['Modulated'] = False
249    if 'modulated' in data['General']['Type']:
250        data['General']['Modulated'] = True
251        data['General']['Type'] = 'nuclear'
252
253
254def SetupGeneral(data, dirname):
255    """Helps initialize phase data.
256
257    From GSASIIphsGui.py, function of the same name. Minor changes for imports etc.
258
259    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
260    """
261    mapDefault = {'MapType':'','RefList':'','Resolution':0.5,'Show bonds':True,
262                'rho':[],'rhoMax':0.,'mapSize':10.0,'cutOff':50.,'Flip':False}
263    generalData = data['General']
264    atomData = data['Atoms']
265    generalData['AtomTypes'] = []
266    generalData['Isotopes'] = {}
267
268    if 'Isotope' not in generalData:
269        generalData['Isotope'] = {}
270    if 'Data plot type' not in generalData:
271        generalData['Data plot type'] = 'Mustrain'
272    if 'POhkl' not in generalData:
273        generalData['POhkl'] = [0,0,1]
274    if 'Map' not in generalData:
275        generalData['Map'] = mapDefault.copy()
276    if 'Flip' not in generalData:
277        generalData['Flip'] = {'RefList':'','Resolution':0.5,'Norm element':'None',
278            'k-factor':0.1,'k-Max':20.,}
279    if 'testHKL' not in generalData['Flip']:
280        generalData['Flip']['testHKL'] = [[0,0,2],[2,0,0],[1,1,1],[0,2,0],[1,2,3]]
281    if 'doPawley' not in generalData:
282        generalData['doPawley'] = False     #ToDo: change to ''
283    if 'Pawley dmin' not in generalData:
284        generalData['Pawley dmin'] = 1.0
285    if 'Pawley neg wt' not in generalData:
286        generalData['Pawley neg wt'] = 0.0
287    if 'Algolrithm' in generalData.get('MCSA controls',{}) or \
288        'MCSA controls' not in generalData:
289        generalData['MCSA controls'] = {'Data source':'','Annealing':[50.,0.001,50],
290        'dmin':2.0,'Algorithm':'log','Jump coeff':[0.95,0.5],'boltzmann':1.0,
291        'fast parms':[1.0,1.0,1.0],'log slope':0.9,'Cycles':1,'Results':[],'newDmin':True}
292    if 'AtomPtrs' not in generalData:
293        generalData['AtomPtrs'] = [3,1,7,9]
294        if generalData['Type'] == 'macromolecular':
295            generalData['AtomPtrs'] = [6,4,10,12]
296        elif generalData['Type'] == 'magnetic':
297            generalData['AtomPtrs'] = [3,1,10,12]
298    if generalData['Type'] in ['modulated',]:
299        generalData['Modulated'] = True
300        generalData['Type'] = 'nuclear'
301        if 'Super' not in generalData:
302            generalData['Super'] = 1
303            generalData['SuperVec'] = [[0,0,.1],False,4]
304            generalData['SSGData'] = {}
305        if '4DmapData' not in generalData:
306            generalData['4DmapData'] = mapDefault.copy()
307            generalData['4DmapData'].update({'MapType':'Fobs'})
308    if 'Modulated' not in generalData:
309        generalData['Modulated'] = False
310    if 'HydIds' not in generalData:
311        generalData['HydIds'] = {}
312    cx,ct,cs,cia = generalData['AtomPtrs']
313    generalData['NoAtoms'] = {}
314    generalData['BondRadii'] = []
315    generalData['AngleRadii'] = []
316    generalData['vdWRadii'] = []
317    generalData['AtomMass'] = []
318    generalData['Color'] = []
319    if generalData['Type'] == 'magnetic':
320        generalData['MagDmin'] = generalData.get('MagDmin',1.0)
321        landeg = generalData.get('Lande g',[])
322    generalData['Mydir'] = dirname
323    badList = {}
324    for iat,atom in enumerate(atomData):
325        atom[ct] = atom[ct].lower().capitalize()              #force to standard form
326        if generalData['AtomTypes'].count(atom[ct]):
327            generalData['NoAtoms'][atom[ct]] += atom[cx+3]*float(atom[cs+1])
328        elif atom[ct] != 'UNK':
329            Info = G2elem.GetAtomInfo(atom[ct])
330            if not Info:
331                if atom[ct] not in badList:
332                    badList[atom[ct]] = 0
333                badList[atom[ct]] += 1
334                atom[ct] = 'UNK'
335                continue
336            atom[ct] = Info['Symbol'] # N.B. symbol might be changed by GetAtomInfo
337            generalData['AtomTypes'].append(atom[ct])
338            generalData['Z'] = Info['Z']
339            generalData['Isotopes'][atom[ct]] = Info['Isotopes']
340            generalData['BondRadii'].append(Info['Drad'])
341            generalData['AngleRadii'].append(Info['Arad'])
342            generalData['vdWRadii'].append(Info['Vdrad'])
343            if atom[ct] in generalData['Isotope']:
344                if generalData['Isotope'][atom[ct]] not in generalData['Isotopes'][atom[ct]]:
345                    isotope = generalData['Isotopes'][atom[ct]].keys()[-1]
346                    generalData['Isotope'][atom[ct]] = isotope
347                generalData['AtomMass'].append(Info['Isotopes'][generalData['Isotope'][atom[ct]]]['Mass'])
348            else:
349                generalData['Isotope'][atom[ct]] = 'Nat. Abund.'
350                if 'Nat. Abund.' not in generalData['Isotopes'][atom[ct]]:
351                    isotope = generalData['Isotopes'][atom[ct]].keys()[-1]
352                    generalData['Isotope'][atom[ct]] = isotope
353                generalData['AtomMass'].append(Info['Mass'])
354            generalData['NoAtoms'][atom[ct]] = atom[cx+3]*float(atom[cs+1])
355            generalData['Color'].append(Info['Color'])
356            if generalData['Type'] == 'magnetic':
357                if len(landeg) < len(generalData['AtomTypes']):
358                    landeg.append(2.0)
359    if generalData['Type'] == 'magnetic':
360        generalData['Lande g'] = landeg[:len(generalData['AtomTypes'])]
361
362    if badList:
363        msg = 'Warning: element symbol(s) not found:'
364        for key in badList:
365            msg += '\n\t' + key
366            if badList[key] > 1:
367                msg += ' (' + str(badList[key]) + ' times)'
368        raise Exception("Phase error:\n" + msg)
369        # wx.MessageBox(msg,caption='Element symbol error')
370    F000X = 0.
371    F000N = 0.
372    for i,elem in enumerate(generalData['AtomTypes']):
373        F000X += generalData['NoAtoms'][elem]*generalData['Z']
374        isotope = generalData['Isotope'][elem]
375        F000N += generalData['NoAtoms'][elem]*generalData['Isotopes'][elem][isotope]['SL'][0]
376    generalData['F000X'] = F000X
377    generalData['F000N'] = F000N
378    import GSASIImath as G2mth
379    generalData['Mass'] = G2mth.getMass(generalData)
380
381
382############################################
383############ CIF export helpers ############
384############################################
385## These functions are translated from
386## exports/G2export_CIF.py
387## They are used in G2Phase.export_CIF
388
389
390
391def make_empty_project(author=None, filename=None):
392    """Creates an dictionary in the style of GSASIIscriptable, for an empty
393    project.
394
395    If no author name or filename is supplied, 'no name' and
396    <current dir>/test_output.gpx are used , respectively.
397
398    Returns: project dictionary, name list
399
400    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
401    """
402    if not filename:
403        filename = os.path.join(os.getcwd(), 'test_output.gpx')
404    else:
405        filename = os.path.abspath(filename)
406    gsasii_version = str(GSASIIpath.GetVersionNumber())
407    LoadG2fil()
408    import matplotlib as mpl
409    python_library_versions = G2fil.get_python_versions([mpl, np, sp])
410
411    controls_data = dict(G2obj.DefaultControls)
412    controls_data['LastSavedAs'] = unicode(filename)
413    controls_data['LastSavedUsing'] = gsasii_version
414    controls_data['PythonVersions'] = python_library_versions
415    if author:
416        controls_data['Author'] = author
417
418    output = {'Constraints': {'data': {'HAP': [], 'Hist': [], 'Phase': [],
419                                       'Global': []}},
420              'Controls': {'data': controls_data},
421              u'Covariance': {'data': {}},
422              u'Notebook': {'data': ['']},
423              u'Restraints': {'data': {}},
424              u'Rigid bodies': {'data': {'RBIds': {'Residue': [], 'Vector': []},
425                                'Residue': {'AtInfo': {}},
426                                'Vector':  {'AtInfo': {}}}}}
427
428    names = [[u'Notebook'], [u'Controls'], [u'Covariance'],
429             [u'Constraints'], [u'Restraints'], [u'Rigid bodies']]
430
431    return output, names
432
433
434class G2ImportException(Exception):
435    pass
436
437
438def import_generic(filename, readerlist):
439    """Attempt to import a filename, using a list of reader objects.
440
441    Returns the first reader object which worked."""
442    # Translated from OnImportGeneric method in GSASII.py
443    primaryReaders, secondaryReaders = [], []
444    for reader in readerlist:
445        flag = reader.ExtensionValidator(filename)
446        if flag is None:
447            secondaryReaders.append(reader)
448        elif flag:
449            primaryReaders.append(reader)
450    if not secondaryReaders and not primaryReaders:
451        raise G2ImportException("Could not read file: ", filename)
452
453    with open(filename, 'Ur') as fp:
454        rd_list = []
455
456        for rd in primaryReaders + secondaryReaders:
457            # Initialize reader
458            rd.selections = []
459            rd.dnames = []
460            rd.ReInitialize()
461            # Rewind file
462            fp.seek(0)
463            rd.errors = ""
464            if not rd.ContentsValidator(fp):
465                # Report error
466                pass
467            if len(rd.selections) > 1:
468                # Select data?
469                # GSASII.py:543
470                raise G2ImportException("Not sure what data to select")
471
472            block = 0
473            rdbuffer = {}
474            fp.seek(0)
475            repeat = True
476            while repeat:
477                repeat = False
478                block += 1
479                rd.objname = os.path.basename(filename)
480                flag = rd.Reader(filename, fp, buffer=rdbuffer, blocknum=block)
481                if flag:
482                    # Omitting image loading special cases
483                    rd.readfilename = filename
484                    rd_list.append(copy.deepcopy(rd))
485                    repeat = rd.repeat
486                else:
487                    raise G2ImportException("{}.Reader() returned:".format(rd),
488                                            flag)
489
490            if rd_list:
491                if rd.warnings:
492                    print("Read warning by", rd.formatName, "reader:",
493                          rd.warnings, file=sys.stderr)
494                return rd_list
495    raise G2ImportException("No reader could read file: " + filename)
496
497
498def load_iprms(instfile, reader):
499    """Loads instrument parameters from a file, and edits the
500    given reader.
501
502    Returns a 2-tuple of (Iparm1, Iparm2) parameters
503    """
504    LoadG2fil()
505    ext = os.path.splitext(instfile)[1]
506
507    if ext.lower() == '.instprm':
508        # New GSAS File, load appropriately
509        with open(instfile) as f:
510            lines = f.readlines()
511        bank = reader.powderentry[2]
512        numbanks = reader.numbanks
513        iparms = G2fil.ReadPowderInstprm(lines, bank, numbanks, reader)
514
515        reader.instfile = instfile
516        reader.instmsg = 'GSAS-II file' + instfile
517        return iparms
518    elif ext.lower() not in ('.prm', '.inst', '.ins'):
519        raise ValueError('Expected .prm file, found: ', instfile)
520
521    # It's an old GSAS file, load appropriately
522    Iparm = {}
523    with open(instfile, 'Ur') as fp:
524        for line in fp:
525            if '#' in line:
526                continue
527            Iparm[line[:12]] = line[12:-1]
528    ibanks = int(Iparm.get('INS   BANK  ', '1').strip())
529    if ibanks == 1:
530        reader.instbank = 1
531        reader.powderentry[2] = 1
532        reader.instfile = instfile
533        reader.instmsg = instfile + ' bank ' + str(reader.instbank)
534        return G2fil.SetPowderInstParms(Iparm, reader)
535    # TODO handle >1 banks
536    raise NotImplementedError("Check GSASIIfiles.py: ReadPowderInstprm")
537
538def load_pwd_from_reader(reader, instprm, existingnames=[]):
539    """Loads powder data from a reader object, and assembles it into a GSASII data tree.
540
541    :returns: (name, tree) - 2-tuple of the histogram name (str), and data
542
543    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
544    """
545    HistName = 'PWDR ' + G2obj.StripUnicode(reader.idstring, '_')
546    HistName = unicode(G2obj.MakeUniqueLabel(HistName, existingnames))
547
548    try:
549        Iparm1, Iparm2 = instprm
550    except ValueError:
551        Iparm1, Iparm2 = load_iprms(instprm, reader)
552
553    Ymin = np.min(reader.powderdata[1])
554    Ymax = np.max(reader.powderdata[1])
555    valuesdict = {'wtFactor': 1.0,
556                  'Dummy': False,
557                  'ranId': ran.randint(0, sys.maxint),
558                  'Offset': [0.0, 0.0], 'delOffset': 0.02*Ymax,
559                  'refOffset': -0.1*Ymax, 'refDelt': 0.1*Ymax,
560                  'qPlot': False, 'dPlot': False, 'sqrtPlot': False,
561                  'Yminmax': [Ymin, Ymax]}
562    reader.Sample['ranId'] = valuesdict['ranId']
563
564    # Ending keys:
565    # [u'Reflection Lists',
566    #  u'Limits',
567    #  'data',
568    #  u'Index Peak List',
569    #  u'Comments',
570    #  u'Unit Cells List',
571    #  u'Sample Parameters',
572    #  u'Peak List',
573    #  u'Background',
574    #  u'Instrument Parameters']
575    Tmin = np.min(reader.powderdata[0])
576    Tmax = np.max(reader.powderdata[0])
577
578    default_background = [['chebyschev', False, 3, 1.0, 0.0, 0.0],
579                          {'nDebye': 0, 'debyeTerms': [], 'nPeaks': 0, 'peaksList': []}]
580
581    output_dict = {u'Reflection Lists': {},
582                   u'Limits': reader.pwdparms.get('Limits', [(Tmin, Tmax), [Tmin, Tmax]]),
583                   u'data': [valuesdict, reader.powderdata, HistName],
584                   u'Index Peak List': [[], []],
585                   u'Comments': reader.comments,
586                   u'Unit Cells List': [],
587                   u'Sample Parameters': reader.Sample,
588                   u'Peak List': {'peaks': [], 'sigDict': {}},
589                   u'Background': reader.pwdparms.get('Background', default_background),
590                   u'Instrument Parameters': [Iparm1, Iparm2],
591                   }
592
593    names = [u'Comments',
594             u'Limits',
595             u'Background',
596             u'Instrument Parameters',
597             u'Sample Parameters',
598             u'Peak List',
599             u'Index Peak List',
600             u'Unit Cells List',
601             u'Reflection Lists']
602
603    # TODO controls?? GSASII.py:1664-7
604
605    return HistName, [HistName] + names, output_dict
606
607
608def _deep_copy_into(from_, into):
609    """Helper function for reloading .gpx file. See G2Project.reload()
610
611    :author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
612    """
613    if isinstance(from_, dict) and isinstance(into, dict):
614        combined_keys = set(from_.keys()).union(into.keys())
615        for key in combined_keys:
616            if key in from_ and key in into:
617                both_dicts = (isinstance(from_[key], dict)
618                              and isinstance(into[key], dict))
619                both_lists = (isinstance(from_[key], list)
620                              and isinstance(into[key], list))
621                if both_dicts or both_lists:
622                    _deep_copy_into(from_[key], into[key])
623                else:
624                    into[key] = from_[key]
625            elif key in from_:
626                into[key] = from_[key]
627            else:  # key in into
628                del into[key]
629    elif isinstance(from_, list) and isinstance(into, list):
630        if len(from_) == len(into):
631            for i in xrange(len(from_)):
632                both_dicts = (isinstance(from_[i], dict)
633                              and isinstance(into[i], dict))
634                both_lists = (isinstance(from_[i], list)
635                              and isinstance(into[i], list))
636                if both_dicts or both_lists:
637                    _deep_copy_into(from_[i], into[i])
638                else:
639                    into[i] = from_[i]
640        else:
641            into[:] = from_
642
643
644class G2ObjectWrapper(object):
645    """Base class for all GSAS-II object wrappers.
646
647    The underlying GSAS-II format can be accessed as `wrapper.data`. A number
648    of overrides are implemented so that the wrapper behaves like a dictionary.
649
650    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
651    """
652    def __init__(self, datadict):
653        self.data = datadict
654
655    def __getitem__(self, key):
656        return self.data[key]
657
658    def __setitem__(self, key, value):
659        self.data[key] = value
660
661    def __contains__(self, key):
662        return key in self.data
663
664    def get(self, k, d=None):
665        return self.data.get(k, d)
666
667    def keys(self):
668        return self.data.keys()
669
670    def values(self):
671        return self.data.values()
672
673    def items(self):
674        return self.data.items()
675
676
677class G2Project(G2ObjectWrapper):
678    """Represents an entire GSAS-II project."""
679    def __init__(self, gpxfile=None, author=None, filename=None):
680        """Loads a GSAS-II project from a specified filename.
681
682        :param str gpxfile: Existing .gpx file to be loaded. If nonexistent,
683            creates an empty project.
684        :param str author: Author's name. Optional.
685        :param str filename: The filename the project should be saved to in
686            the future. If both filename and gpxfile are present, the project is
687            loaded from the gpxfile, then set to  save to filename in the future"""
688        if gpxfile is None:
689            filename = os.path.abspath(os.path.expanduser(filename))
690            self.filename = filename
691            self.data, self.names = make_empty_project(author=author, filename=filename)
692        elif isinstance(gpxfile, str):
693            # TODO set author, filename
694            self.filename = os.path.abspath(os.path.expanduser(gpxfile))
695            self.data, self.names = LoadDictFromProjFile(gpxfile)
696            self.index_ids()
697        else:
698            raise ValueError("Not sure what to do with gpxfile")
699
700    @classmethod
701    def from_dict_and_names(cls, gpxdict, names, filename=None):
702        """Creates a :class:`G2Project` directly from
703        a dictionary and a list of names. If in doubt, do not use this.
704
705        :returns: a :class:`G2Project`
706        """
707        out = cls()
708        if filename:
709            filename = os.path.abspath(os.path.expanduser(filename))
710            out.filename = filename
711            gpxdict['Controls']['data']['LastSavedAs'] = filename
712        else:
713            try:
714                out.filename = gpxdict['Controls']['data']['LastSavedAs']
715            except KeyError:
716                out.filename = None
717        out.data = gpxdict
718        out.names = names
719
720    def save(self, filename=None):
721        """Saves the project, either to the current filename, or to a new file.
722
723        Updates self.filename if a new filename provided"""
724        # TODO update LastSavedUsing ?
725        if filename:
726            filename = os.path.abspath(os.path.expanduser(filename))
727            self.data['Controls']['data']['LastSavedAs'] = filename
728            self.filename = filename
729        elif not self.filename:
730            raise AttributeError("No file name to save to")
731        SaveDictToProjFile(self.data, self.names, self.filename)
732
733    def add_powder_histogram(self, datafile, iparams):
734        """Loads a powder data histogram into the project.
735
736        Automatically checks for an instrument parameter file, or one can be
737        provided.
738
739        :param str datafile: The powder data file to read, a filename.
740        :param str iparams: The instrument parameters file, a filename.
741
742        :returns: A :class:`G2PwdrData` object representing
743            the histogram
744        """
745        LoadG2fil()
746        datafile = os.path.abspath(os.path.expanduser(datafile))
747        iparams = os.path.abspath(os.path.expanduser(iparams))
748        pwdrreaders = import_generic(datafile, PwdrDataReaders)
749        histname, new_names, pwdrdata = load_pwd_from_reader(
750                                          pwdrreaders[0], iparams,
751                                          [h.name for h in self.histograms()])
752        if histname in self.data:
753            print("Warning - redefining histogram", histname)
754        else:
755            if self.names[-1][0] == 'Phases':
756                self.names.insert(-1, new_names)
757            else:
758                self.names.append(new_names)
759        self.data[histname] = pwdrdata
760        return self.histogram(histname)
761
762    def add_phase(self, phasefile, phasename=None, histograms=[]):
763        """Loads a phase into the project from a .cif file
764
765        :param str phasefile: The CIF file from which to import the phase.
766        :param str phasename: The name of the new phase, or None for the default
767        :param list histograms: The names of the histograms to associate with
768            this phase
769
770        :returns: A :class:`G2Phase` object representing the
771            new phase.
772        """
773        LoadG2fil()
774        histograms = [self.histogram(h).name for h in histograms]
775        phasefile = os.path.abspath(os.path.expanduser(phasefile))
776
777        # TODO handle multiple phases in a file
778        phasereaders = import_generic(phasefile, PhaseReaders)
779        phasereader = phasereaders[0]
780
781        phasename = phasename or phasereader.Phase['General']['Name']
782        phaseNameList = [p.name for p in self.phases()]
783        phasename = G2obj.MakeUniqueLabel(phasename, phaseNameList)
784        phasereader.Phase['General']['Name'] = phasename
785
786        if 'Phases' not in self.data:
787            self.data[u'Phases'] = { 'data': None }
788        assert phasename not in self.data['Phases'], "phase names should be unique"
789        self.data['Phases'][phasename] = phasereader.Phase
790
791        if phasereader.Constraints:
792            Constraints = self.data['Constraints']
793            for i in phasereader.Constraints:
794                if isinstance(i, dict):
795                    if '_Explain' not in Constraints:
796                        Constraints['_Explain'] = {}
797                    Constraints['_Explain'].update(i)
798                else:
799                    Constraints['Phase'].append(i)
800
801        data = self.data['Phases'][phasename]
802        generalData = data['General']
803        SGData = generalData['SGData']
804        NShkl = len(G2spc.MustrainNames(SGData))
805        NDij = len(G2spc.HStrainNames(SGData))
806        Super = generalData.get('Super', 0)
807        if Super:
808            SuperVec = np.array(generalData['SuperVec'][0])
809        else:
810            SuperVec = []
811        UseList = data['Histograms']
812
813        for histname in histograms:
814            if histname.startswith('HKLF '):
815                raise NotImplementedError("Does not support HKLF yet")
816            elif histname.startswith('PWDR '):
817                hist = self.histogram(histname)
818                hist['Reflection Lists'][generalData['Name']] = {}
819                UseList[histname] = SetDefaultDData('PWDR', histname, NShkl=NShkl, NDij=NDij)
820                for key, val in [('Use', True), ('LeBail', False),
821                                 ('newLeBail', True),
822                                 ('Babinet', {'BabA': [0.0, False],
823                                              'BabU': [0.0, False]})]:
824                    if key not in UseList[histname]:
825                        UseList[histname][key] = val
826            else:
827                raise NotImplementedError("Unexpected histogram" + histname)
828
829        for obj in self.names:
830            if obj[0] == 'Phases':
831                phasenames = obj
832                break
833        else:
834            phasenames = [u'Phases']
835            self.names.append(phasenames)
836        phasenames.append(unicode(phasename))
837
838        # TODO should it be self.filename, not phasefile?
839        SetupGeneral(data, os.path.dirname(phasefile))
840        self.index_ids()
841
842        return self.phase(phasename)
843
844    def reload(self):
845        """Reload self from self.filename"""
846        data, names = LoadDictFromProjFile(self.filename)
847        self.names = names
848        # Need to deep copy the new data file data into the current tree,
849        # so that any existing G2Phase, or G2PwdrData objects will still be
850        # valid
851        _deep_copy_into(from_=data, into=self.data)
852
853    def refine(self, newfile=None, printFile=None, makeBack=False):
854        # index_ids will automatically save the project
855        self.index_ids()
856        # TODO G2strMain does not properly use printFile
857        G2strMain.Refine(self.filename, makeBack=makeBack)
858        # Reload yourself
859        self.reload()
860
861    def histogram(self, histname):
862        """Returns the histogram named histname, or None if it does not exist.
863
864        :param histname: The name of the histogram (str), or ranId or index.
865        :returns: A :class:`G2PwdrData` object, or None if
866            the histogram does not exist
867
868        .. seealso::
869            :func:`~GSASIIscriptable.G2Project.histograms`
870            :func:`~GSASIIscriptable.G2Project.phase`
871            :func:`~GSASIIscriptable.G2Project.phases`
872            """
873        if isinstance(histname, G2PwdrData):
874            if histname.proj == self:
875                return histname
876        if histname in self.data:
877            return G2PwdrData(self.data[histname], self)
878        for key, val in G2obj.HistIdLookup.items():
879            name, ranId = val
880            # histname can be either ranId (key) or index (val)
881            if ranId == histname or key == str(histname):
882                return self.histogram(name)
883
884    def histograms(self):
885        """Return a list of all histograms, as
886        :class:`G2PwdrData` objects
887
888        .. seealso::
889            :func:`~GSASIIscriptable.G2Project.histograms`
890            :func:`~GSASIIscriptable.G2Project.phase`
891            :func:`~GSASIIscriptable.G2Project.phases`
892            """
893        output = []
894        for obj in self.names:
895            if len(obj) > 1 and obj[0] != u'Phases':
896                output.append(self.histogram(obj[0]))
897        return output
898
899    def phase(self, phasename):
900        """
901        Gives an object representing the specified phase in this project.
902
903        :param str phasename: The name of the desired phase. Either the name
904            (str), the phase's ranId, or the phase's index
905        :returns: A :class:`G2Phase` object
906        :raises: KeyError
907
908        .. seealso::
909            :func:`~GSASIIscriptable.G2Project.histograms`
910            :func:`~GSASIIscriptable.G2Project.phase`
911            :func:`~GSASIIscriptable.G2Project.phases`
912            """
913        phases = self.data['Phases']
914        if phasename in phases:
915            return G2Phase(phases[phasename], phasename, self)
916        for key, val in G2obj.PhaseIdLookup.items():
917            name, ranId = val
918            # phasename can be either ranId (key) or index (val)
919            if ranId == phasename or key == str(phasename):
920                return self.phase(name)
921
922    def phases(self):
923        """
924        Returns a list of all the phases in the project.
925
926        :returns: A list of :class:`G2Phase` objects
927
928        .. seealso::
929            :func:`~GSASIIscriptable.G2Project.histogram`
930            :func:`~GSASIIscriptable.G2Project.histograms`
931            :func:`~GSASIIscriptable.G2Project.phase`
932            """
933        for obj in self.names:
934            if obj[0] == 'Phases':
935                return [self.phase(p) for p in obj[1:]]
936        return []
937
938    def do_refinements(self, refinements, histogram='all', phase='all',
939                       outputnames=None):
940        """Conducts a series of refinements.
941
942        :param list refinements: A list of dictionaries defining refinements
943        :param str histogram: Name of histogram for refinements to be applied
944            to, or 'all'
945        :param str phase: Name of phase for refinements to be applied to, or
946            'all'
947        """
948        if outputnames:
949            if len(refinements) != len(outputnames):
950                raise ValueError("Should have same number of outuputs to"
951                                 "refinements")
952        else:
953            outputnames = [None for r in refinements]
954
955        for output, refinement in zip(outputnames, refinements):
956            self.set_refinement(refinement, histogram)
957            # Handle 'once' args - refinements that are disabled after this
958            # refinement
959            if 'once' in refinement:
960                temp = {'set': refinement['once']}
961                self.set_refinement(temp, histogram, phase)
962
963            if output:
964                self.save(output)
965
966            self.refine()  # newFile=output)
967
968            # Handle 'once' args - refinements that are disabled after this
969            # refinement
970            if 'once' in refinement:
971                temp = {'clear': refinement['once']}
972                self.set_refinement(temp, histogram, phase)
973
974    def set_refinement(self, refinement, histogram='all', phase='all'):
975        """Apply specified refinements to a given histogram(s) or phase(s).
976
977        Refinement parameters are categorize in three groups:
978
979        1. Histogram parameters
980        2. Phase parameters
981        3. Histogram-and-Phase (HAP) parameters
982
983        :param dict refinement: The refinements to be conducted
984        :param histogram: Either a name of a histogram (str), a list of
985            histogram names, or 'all' (default)
986        :param phase: Either a name of a phase (str), a list of phase names, or
987            'all' (default)
988
989        .. seealso::
990            :func:`~G2PwdrData.set_refinements`
991            :func:`~G2PwdrData.clear_refinements`
992            :func:`~G2Phase.set_refinements`
993            :func:`~G2Phase.clear_refinements`
994            :func:`~G2Phase.set_HAP_refinements`
995            :func:`~G2Phase.clear_HAP_refinements`"""
996
997        if histogram == 'all':
998            hists = self.histograms()
999        elif isinstance(histogram, str) or isinstance(histogram, int):
1000            hists = [self.histogram(histogram)]
1001        else:
1002            hists = [self.histogram(name) for name in histogram]
1003
1004        if phase == 'all':
1005            phases = self.phases()
1006        elif isinstance(phase, str) or isinstance(phase, int):
1007            phases = [self.phase(phase)]
1008        else:
1009            phases = [self.phase(name) for name in phase]
1010
1011
1012        # TODO: HAP parameters:
1013        #   Babinet
1014        #   Extinction
1015        #   HStrain
1016        #   Mustrain
1017        #   Pref. Ori
1018        #   Size
1019
1020        pwdr_set = {}
1021        phase_set = {}
1022        hap_set = {}
1023        for key, val in refinement.get('set', {}).items():
1024            # Apply refinement options
1025            if G2PwdrData.is_valid_refinement_key(key):
1026                pwdr_set[key] = val
1027            elif G2Phase.is_valid_refinement_key(key):
1028                phase_set[key] = val
1029            elif G2Phase.is_valid_HAP_refinement_key(key):
1030                hap_set[key] = val
1031            else:
1032                raise ValueError("Unknown refinement key", key)
1033
1034        for hist in hists:
1035            hist.set_refinements(pwdr_set)
1036        for phase in phases:
1037            phase.set_refinements(phase_set)
1038        for phase in phases:
1039            phase.set_HAP_refinements(hap_set, hists)
1040
1041        pwdr_clear = {}
1042        phase_clear = {}
1043        hap_clear = {}
1044        for key, val in refinement.get('clear', {}).items():
1045            # Clear refinement options
1046            if G2PwdrData.is_valid_refinement_key(key):
1047                pwdr_clear[key] = val
1048            elif G2Phase.is_valid_refinement_key(key):
1049                phase_clear[key] = val
1050            elif G2Phase.is_valid_HAP_refinement_key(key):
1051                hap_set[key] = val
1052            else:
1053                raise ValueError("Unknown refinement key", key)
1054
1055        for hist in hists:
1056            hist.clear_refinements(pwdr_clear)
1057        for phase in phases:
1058            phase.clear_refinements(phase_clear)
1059        for phase in phases:
1060            phase.clear_HAP_refinements(hap_clear, hists)
1061
1062    def index_ids(self):
1063        import GSASIIstrIO as G2strIO
1064        self.save()
1065        return G2strIO.GetUsedHistogramsAndPhases(self.filename)
1066
1067    def add_constraint_raw(self, cons_scope, constr):
1068        """Adds a constraint of type consType to the project.
1069        cons_scope should be one of "Hist", "Phase", "HAP", or "Global".
1070
1071        WARNING it does not check the constraint is well-constructed"""
1072        constrs = self['Constraints']['data']
1073        if 'Global' not in constrs:
1074            constrs['Global'] = []
1075        constrs[cons_scope].append(constr)
1076
1077    def hold_many(self, vars, type):
1078        """Apply holds for all the variables in vars, for constraint of a given type.
1079
1080        type is passed directly to add_constraint_raw as consType
1081
1082        :param list vars: A list of variables to hold. Either :class:`GSASIIobj.G2VarObj` objects,
1083            string variable specifiers, or arguments for :meth:`make_var_obj`
1084        :param str type: A string constraint type specifier. See
1085        :class:`~GSASIIscriptable.G2Project.add_constraint_raw`"""
1086        for var in vars:
1087            if isinstance(var, str):
1088                var = self.make_var_obj(var)
1089            elif not isinstance(var, G2obj.G2VarObj):
1090                var = self.make_var_obj(*var)
1091            self.add_constraint_raw(type, [[1.0, var], None, None, 'h'])
1092
1093    def make_var_obj(self, phase=None, hist=None, varname=None, atomId=None,
1094                     reloadIdx=True):
1095        """Wrapper to create a G2VarObj. Takes either a string representaiton ("p:h:name:a")
1096        or individual names of phase, histogram, varname, and atomId.
1097
1098        Automatically converts string phase, hist, or atom names into the ID required
1099        by G2VarObj."""
1100
1101        if reloadIdx:
1102            self.index_ids()
1103
1104        # If string representation, short circuit
1105        if hist is None and varname is None and atomId is None:
1106            if isinstance(phase, str) and ':' in phase:
1107                return G2obj.G2VarObj(phase)
1108
1109        # Get phase index
1110        phaseObj = None
1111        if isinstance(phase, G2Phase):
1112            phaseObj = phase
1113            phase = G2obj.PhaseRanIdLookup[phase.ranId]
1114        elif phase in self['Phases']:
1115            phaseObj = self.phase(phase)
1116            phaseRanId = phaseObj.ranId
1117            phase = G2obj.PhaseRanIdLookup[phaseRanId]
1118
1119        # Get histogram index
1120        if isinstance(hist, G2PwdrData):
1121            hist = G2obj.HistRanIdLookup[hist.ranId]
1122        elif hist in self.data:
1123            histRanId = self.histogram(hist).ranId
1124            hist = G2obj.HistRanIdLookup[histRanId]
1125
1126        # Get atom index (if any)
1127        if isinstance(atomId, G2AtomRecord):
1128            atomId = G2obj.AtomRanIdLookup[phase][atomId.ranId]
1129        elif phaseObj:
1130            atomObj = phaseObj.atom(atomId)
1131            if atomObj:
1132                atomRanId = atomObj.ranId
1133                atomId = G2obj.AtomRanIdLookup[phase][atomRanId]
1134
1135        return G2obj.G2VarObj(phase, hist, varname, atomId)
1136
1137
1138class G2AtomRecord(G2ObjectWrapper):
1139    """Wrapper for an atom record. Has convenient accessors via @property.
1140
1141
1142    Available accessors: label, type, refinement_flags, coordinates,
1143        occupancy, ranId, id, adp_flag, uiso
1144
1145    Example:
1146
1147    >>> atom = some_phase.atom("O3")
1148    >>> # We can access the underlying data format
1149    >>> atom.data
1150    ['O3', 'O-2', '', ... ]
1151    >>> # We can also use wrapper accessors
1152    >>> atom.coordinates
1153    (0.33, 0.15, 0.5)
1154    >>> atom.refinement_flags
1155    u'FX'
1156    >>> atom.ranId
1157    4615973324315876477
1158    >>> atom.occupancy
1159    1.0
1160    """
1161    def __init__(self, data, indices, proj):
1162        self.data = data
1163        self.cx, self.ct, self.cs, self.cia = indices
1164        self.proj = proj
1165
1166    @property
1167    def label(self):
1168        return self.data[self.ct-1]
1169
1170    @property
1171    def type(self):
1172        return self.data[self.ct]
1173
1174    @property
1175    def refinement_flags(self):
1176        return self.data[self.ct+1]
1177
1178    @refinement_flags.setter
1179    def refinement_flags(self, other):
1180        # Automatically check it is a valid refinement
1181        for c in other:
1182            if c not in ' FXU':
1183                raise ValueError("Invalid atom refinement: ", other)
1184        self.data[self.ct+1] = unicode(other)
1185
1186    @property
1187    def coordinates(self):
1188        return tuple(self.data[self.cx:self.cx+3])
1189
1190    @property
1191    def occupancy(self):
1192        return self.data[self.cx+3]
1193
1194    @occupancy.setter
1195    def occupancy(self, val):
1196        self.data[self.cx+3] = float(val)
1197
1198    @property
1199    def ranId(self):
1200        return self.data[self.cia+8]
1201
1202    @property
1203    def adp_flag(self):
1204        # Either 'I' or 'A'
1205        return self.data[self.cia]
1206
1207    @property
1208    def uiso(self):
1209        if self.adp_flag == 'I':
1210            return self.data[self.cia+1]
1211        else:
1212            return self.data[self.cia+2:self.cia+8]
1213
1214
1215class G2PwdrData(G2ObjectWrapper):
1216    """Wraps a Powder Data Histogram."""
1217    def __init__(self, data, proj):
1218        self.data = data
1219        self.proj = proj
1220
1221    @staticmethod
1222    def is_valid_refinement_key(key):
1223        valid_keys = ['Limits', 'Sample Parameters', 'Background',
1224                      'Instrument Parameters']
1225        return key in valid_keys
1226
1227    @property
1228    def name(self):
1229        return self['data'][-1]
1230
1231    @property
1232    def ranId(self):
1233        return self['data'][0]['ranId']
1234
1235    @property
1236    def residuals(self):
1237        data = self['data'][0]
1238        return {key: data[key]
1239                for key in ['R', 'Rb', 'wR', 'wRb', 'wRmin']}
1240
1241    @property
1242    def id(self):
1243        return G2obj.HistRanIdLookup[self.ranId]
1244
1245    def fit_fixed_points(self):
1246        """Attempts to apply a background fit to the fixed points currently specified."""
1247        def SetInstParms(Inst):
1248            dataType = Inst['Type'][0]
1249            insVary = []
1250            insNames = []
1251            insVals = []
1252            for parm in Inst:
1253                insNames.append(parm)
1254                insVals.append(Inst[parm][1])
1255                if parm in ['U','V','W','X','Y','SH/L','I(L2)/I(L1)','alpha',
1256                    'beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q',] and Inst[parm][2]:
1257                        Inst[parm][2] = False
1258            instDict = dict(zip(insNames, insVals))
1259            instDict['X'] = max(instDict['X'], 0.01)
1260            instDict['Y'] = max(instDict['Y'], 0.01)
1261            if 'SH/L' in instDict:
1262                instDict['SH/L'] = max(instDict['SH/L'], 0.002)
1263            return dataType, instDict, insVary
1264
1265        bgrnd = self['Background']
1266
1267        # Need our fixed points in order
1268        bgrnd[1]['FixedPoints'].sort(key=lambda pair: pair[0])
1269        X = [x for x, y in bgrnd[1]['FixedPoints']]
1270        Y = [y for x, y in bgrnd[1]['FixedPoints']]
1271
1272        limits = self['Limits'][1]
1273        if X[0] > limits[0]:
1274            X = [limits[0]] + X
1275            Y = [Y[0]] + Y
1276        if X[-1] < limits[1]:
1277            X += [limits[1]]
1278            Y += [Y[-1]]
1279
1280        # Some simple lookups
1281        controls = self.proj['Controls']['data']
1282        inst, inst2 = self['Instrument Parameters']
1283        pwddata = self['data'][1]
1284
1285        # Construct the data for background fitting
1286        xBeg = np.searchsorted(pwddata[0], limits[0])
1287        xFin = np.searchsorted(pwddata[0], limits[1])
1288        xdata = pwddata[0][xBeg:xFin]
1289        ydata = si.interp1d(X,Y)(ma.getdata(xdata))
1290
1291        W = [1]*len(xdata)
1292        Z = [0]*len(xdata)
1293
1294        dataType, insDict, insVary = SetInstParms(inst)
1295        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
1296
1297        # Do the fit
1298        data = np.array([xdata, ydata, W, Z, Z, Z])
1299        G2pwd.DoPeakFit('LSQ', [], bgrnd, limits, inst, inst2, data,
1300                        prevVaryList=bakVary, controls=controls)
1301
1302        # Post-fit
1303        parmDict = {}
1304        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
1305        parmDict.update(bakDict)
1306        parmDict.update(insDict)
1307        pwddata[3][xBeg:xFin] *= 0
1308        pwddata[5][xBeg:xFin] *= 0
1309        pwddata[4][xBeg:xFin] = G2pwd.getBackground('', parmDict, bakType, dataType, xdata)[0]
1310
1311        # TODO adjust pwddata? GSASIIpwdGUI.py:1041
1312        # TODO update background
1313        self.proj.save()
1314
1315    def y_calc(self):
1316        return self['data'][1][3]
1317
1318    def plot(self, Yobs=True, Ycalc=True, Background=True, Residual=True):
1319        try:
1320            import matplotlib.pyplot as plt
1321            data = self['data'][1]
1322            if Yobs:
1323                plt.plot(data[0], data[1], label='Yobs')
1324            if Ycalc:
1325                plt.plot(data[0], data[3], label='Ycalc')
1326            if Background:
1327                plt.plot(data[0], data[4], label='Background')
1328            if Residual:
1329                plt.plot(data[0], data[5], label="Residual")
1330        except ImportError:
1331            pass
1332
1333    def set_refinements(self, refs):
1334        """Sets the refinement parameter 'key' to the specification 'value'
1335
1336        :param dict refs: A dictionary of the parameters to be set.
1337
1338        :returns: None"""
1339        do_fit_fixed_points = False
1340        for key, value in refs.items():
1341            if key == 'Limits':
1342                old_limits = self.data['Limits'][1]
1343                new_limits = value
1344                if isinstance(new_limits, dict):
1345                    if 'low' in new_limits:
1346                        old_limits[0] = new_limits['low']
1347                    if 'high' in new_limits:
1348                        old_limits[1] = new_limits['high']
1349                else:
1350                    old_limits[0], old_limits[1] = new_limits
1351            elif key == 'Sample Parameters':
1352                sample = self.data['Sample Parameters']
1353                for sparam in value:
1354                    sample[sparam][1] = True
1355            elif key == 'Background':
1356                bkg, peaks = self.data['Background']
1357
1358                # If True or False, just set the refine parameter
1359                if value in (True, False):
1360                    bkg[1] = value
1361                    return
1362
1363                if 'type' in value:
1364                    bkg[0] = value['type']
1365                if 'refine' in value:
1366                    bkg[1] = value['refine']
1367                if 'no. coeffs' in value:
1368                    cur_coeffs = bkg[2]
1369                    n_coeffs = value['no. coeffs']
1370                    if n_coeffs > cur_coeffs:
1371                        for x in range(n_coeffs - cur_coeffs):
1372                            bkg.append(0.0)
1373                    else:
1374                        for _ in range(cur_coeffs - n_coeffs):
1375                            bkg.pop()
1376                    bkg[2] = n_coeffs
1377                if 'coeffs' in value:
1378                    bkg[3:] = value['coeffs']
1379                if 'FixedPoints' in value:
1380                    peaks['FixedPoints'] = [(float(a), float(b))
1381                                            for a, b in value['FixedPoints']]
1382                if value.get('fit fixed points', False):
1383                    do_fit_fixed_points = True
1384
1385            elif key == 'Instrument Parameters':
1386                instrument, secondary = self.data['Instrument Parameters']
1387                for iparam in value:
1388                    try:
1389                        instrument[iparam][2] = True
1390                    except IndexError:
1391                        raise ValueError("Invalid key:", iparam)
1392            else:
1393                raise ValueError("Unknown key:", key)
1394        # Fit fixed points after the fact - ensure they are after fixed points
1395        # are added
1396        if do_fit_fixed_points:
1397            # Background won't be fit if refinement flag not set
1398            orig = self['Background'][0][1]
1399            self['Background'][0][1] = True
1400            self.fit_fixed_points()
1401            # Restore the previous value
1402            self['Background'][0][1] = orig
1403
1404    def clear_refinements(self, refs):
1405        """Clears the refinement parameter 'key' and its associated value.
1406
1407        :param dict refs: A dictionary of parameters to clear."""
1408        for key, value in refs.items():
1409            if key == 'Limits':
1410                old_limits, cur_limits = self.data['Limits']
1411                cur_limits[0], cur_limits[1] = old_limits
1412            elif key == 'Sample Parameters':
1413                sample = self.data['Sample Parameters']
1414                for sparam in value:
1415                    sample[sparam][1] = False
1416            elif key == 'Background':
1417                bkg, peaks = self.data['Background']
1418
1419                # If True or False, just set the refine parameter
1420                if value in (True, False):
1421                    bkg[1] = False
1422                    return
1423
1424                bkg[1] = False
1425                if 'FixedPoints' in value:
1426                    if 'FixedPoints' in peaks:
1427                        del peaks['FixedPoints']
1428            elif key == 'Instrument Parameters':
1429                instrument, secondary = self.data['Instrument Parameters']
1430                for iparam in value:
1431                    instrument[iparam][2] = False
1432            else:
1433                raise ValueError("Unknown key:", key)
1434
1435
1436class G2Phase(G2ObjectWrapper):
1437    """A wrapper object around a given phase.
1438
1439    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1440    """
1441    def __init__(self, data, name, proj):
1442        self.data = data
1443        self.name = name
1444        self.proj = proj
1445
1446    @staticmethod
1447    def is_valid_refinement_key(key):
1448        valid_keys = ["Cell", "Atoms", "LeBail"]
1449        return key in valid_keys
1450
1451    @staticmethod
1452    def is_valid_HAP_refinement_key(key):
1453        valid_keys = ["Babinet", "Extinction", "HStrain", "Mustrain",
1454                      "Pref.Ori.", "Show", "Size", "Use", "Scale"]
1455        return key in valid_keys
1456
1457    def atom(self, atomlabel):
1458        """Returns the atom specified by atomlabel, or None if it does not
1459        exist.
1460
1461        :param str atomlabel: The name of the atom (e.g. "O2")
1462        :returns: A :class:`G2AtomRecord` object
1463            representing the atom.
1464        """
1465        # Consult GSASIIobj.py for the meaning of this
1466        cx, ct, cs, cia = self.data['General']['AtomPtrs']
1467        ptrs = [cx, ct, cs, cia]
1468        atoms = self.data['Atoms']
1469        for atom in atoms:
1470            if atom[ct-1] == atomlabel:
1471                return G2AtomRecord(atom, ptrs, self.proj)
1472
1473    def atoms(self):
1474        """Returns a list of atoms present in the phase.
1475
1476        :returns: A list of :class:`G2AtomRecord` objects.
1477
1478        .. seealso::
1479            :func:`~GSASIIscriptable.G2Phase.atom`
1480            :class:`G2AtomRecord`
1481        """
1482        ptrs = self.data['General']['AtomPtrs']
1483        output = []
1484        atoms = self.data['Atoms']
1485        for atom in atoms:
1486            output.append(G2AtomRecord(atom, ptrs, self.proj))
1487        return output
1488
1489    def histograms(self):
1490        output = []
1491        for hname in self.data.get('Histograms', {}).keys():
1492            output.append(self.proj.histogram(hname))
1493        return output
1494
1495    @property
1496    def ranId(self):
1497        return self.data['ranId']
1498
1499    @property
1500    def id(self):
1501        return G2obj.PhaseRanIdLookup[self.ranId]
1502
1503    def get_cell(self):
1504        """Returns a dictionary of the cell parameters, with keys:
1505            'length_a', 'length_b', 'length_c', 'angle_alpha', 'angle_beta', 'angle_gamma', 'volume'
1506
1507        :returns: a dict"""
1508        cell = self['General']['Cell']
1509        return {'length_a': cell[1], 'length_b': cell[2], 'length_c': cell[3],
1510                'angle_alpha': cell[4], 'angle_beta': cell[5], 'angle_gamma': cell[6],
1511                'volume': cell[7]}
1512
1513    def export_CIF(self, outputname, quickmode=True):
1514        """Write this phase to a .cif file named outputname
1515
1516        :param str outputname: The name of the .cif file to write to
1517        :param bool quickmode: Currently ignored. Carryover from exports.G2export_CIF"""
1518        # This code is all taken from exports/G2export_CIF.py
1519        # Functions copied have the same names
1520        import GSASIImath as G2mth
1521        import GSASIImapvars as G2mv
1522        from exports import G2export_CIF as cif
1523
1524        CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
1525        CIFname = os.path.splitext(self.proj.filename)[0]
1526        CIFname = os.path.split(CIFname)[1]
1527        CIFname = ''.join([c if ord(c) < 128 else ''
1528                           for c in CIFname.replace(' ', '_')])
1529        try:
1530            author = self.proj['Controls'].get('Author','').strip()
1531        except KeyError:
1532            pass
1533        oneblock = True
1534
1535        covDict = self.proj['Covariance']
1536        parmDict = dict(zip(covDict.get('varyList',[]),
1537                            covDict.get('variables',[])))
1538        sigDict = dict(zip(covDict.get('varyList',[]),
1539                           covDict.get('sig',[])))
1540
1541        if covDict.get('covMatrix') is not None:
1542            sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],
1543                                              covDict['varyList'],
1544                                              parmDict))
1545
1546        with open(outputname, 'w') as fp:
1547            fp.write(' \n' + 70*'#' + '\n')
1548            cif.WriteCIFitem(fp, 'data_' + CIFname)
1549            # from exports.G2export_CIF.WritePhaseInfo
1550            cif.WriteCIFitem(fp, '\n# phase info for '+str(self.name) + ' follows')
1551            cif.WriteCIFitem(fp, '_pd_phase_name', self.name)
1552            # TODO get esds
1553            cellDict = self.get_cell()
1554            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
1555            names = ['length_a','length_b','length_c',
1556                     'angle_alpha','angle_beta ','angle_gamma',
1557                     'volume']
1558            for key, val in cellDict.items():
1559                cif.WriteCIFitem(fp, '_cell_' + key, G2mth.ValEsd(val))
1560
1561            cif.WriteCIFitem(fp, '_symmetry_cell_setting',
1562                         self['General']['SGData']['SGSys'])
1563
1564            spacegroup = self['General']['SGData']['SpGrp'].strip()
1565            # regularize capitalization and remove trailing H/R
1566            spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
1567            cif.WriteCIFitem(fp, '_symmetry_space_group_name_H-M', spacegroup)
1568
1569            # generate symmetry operations including centering and center of symmetry
1570            SymOpList, offsetList, symOpList, G2oprList, G2opcodes = G2spc.AllOps(
1571                self['General']['SGData'])
1572            cif.WriteCIFitem(fp, 'loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
1573            for i, op in enumerate(SymOpList,start=1):
1574                cif.WriteCIFitem(fp, '   {:3d}  {:}'.format(i,op.lower()))
1575
1576            # TODO skipped histograms, exports/G2export_CIF.py:880
1577
1578            # report atom params
1579            if self['General']['Type'] in ['nuclear','macromolecular']:        #this needs macromolecular variant, etc!
1580                cif.WriteAtomsNuclear(fp, self.data, self.name, parmDict, sigDict, [])
1581                # self._WriteAtomsNuclear(fp, parmDict, sigDict)
1582            else:
1583                raise Exception,"no export for "+str(self['General']['Type'])+" coordinates implemented"
1584            # report cell contents
1585            cif.WriteComposition(fp, self.data, self.name, parmDict)
1586            if not quickmode and self['General']['Type'] == 'nuclear':      # report distances and angles
1587                # WriteDistances(fp,self.name,SymOpList,offsetList,symOpList,G2oprList)
1588                raise NotImplementedError("only quickmode currently supported")
1589            if 'Map' in self['General'] and 'minmax' in self['General']['Map']:
1590                cif.WriteCIFitem(fp,'\n# Difference density results')
1591                MinMax = self['General']['Map']['minmax']
1592                cif.WriteCIFitem(fp,'_refine_diff_density_max',G2mth.ValEsd(MinMax[0],-0.009))
1593                cif.WriteCIFitem(fp,'_refine_diff_density_min',G2mth.ValEsd(MinMax[1],-0.009))
1594
1595
1596    def set_refinements(self, refs):
1597        """Sets the refinement parameter 'key' to the specification 'value'
1598
1599        :param dict refs: A dictionary of the parameters to be set.
1600
1601        :returns: None"""
1602        for key, value in refs.items():
1603            if key == "Cell":
1604                self.data['General']['Cell'][0] = True
1605            elif key == "Atoms":
1606                cx, ct, cs, cia = self.data['General']['AtomPtrs']
1607
1608                for atomlabel, atomrefinement in value.items():
1609                    if atomlabel == 'all':
1610                        for atom in self.atoms():
1611                            atom.refinement_flags = atomrefinement
1612                    else:
1613                        atom = self.atom(atomlabel)
1614                        atom.refinement_flags = atomrefinement
1615            elif key == "LeBail":
1616                hists = self.data['Histograms']
1617                for hname, hoptions in hists.items():
1618                    if 'LeBail' not in hoptions:
1619                        hoptions['newLeBail'] = True
1620                    hoptions['LeBail'] = bool(value)
1621            else:
1622                raise ValueError("Unknown key:", key)
1623
1624    def clear_refinements(self, refs):
1625        """Clears a given set of parameters.
1626
1627        :param dict refs: The parameters to clear"""
1628        for key, value in refs.items():
1629            if key == "Cell":
1630                self.data['General']['Cell'][0] = False
1631            elif key == "Atoms":
1632                cx, ct, cs, cia = self.data['General']['AtomPtrs']
1633
1634                for atomlabel in value:
1635                    atom = self.atom(atomlabel)
1636                    # Set refinement to none
1637                    atom.refinement_flags = ' '
1638            elif key == "LeBail":
1639                hists = self.data['Histograms']
1640                for hname, hoptions in hists.items():
1641                    if 'LeBail' not in hoptions:
1642                        hoptions['newLeBail'] = True
1643                    hoptions['LeBail'] = False
1644            else:
1645                raise ValueError("Unknown key:", key)
1646
1647    def set_HAP_refinements(self, refs, histograms='all'):
1648        """Sets the given HAP refinement parameters between this phase and
1649        the given histograms
1650
1651        :param dict refs: A dictionary of the parameters to be set.
1652        :param histograms: Either 'all' (default) or a list of the histograms
1653            whose HAP parameters will be set with this phase. Histogram and phase
1654            must already be associated
1655
1656        :returns: None
1657        """
1658        if histograms == 'all':
1659            histograms = self['Histograms'].values()
1660        else:
1661            histograms = [self['Histograms'][h.name] for h in histograms]
1662
1663        for key, val in refs.items():
1664            for h in histograms:
1665                if key == 'Babinet':
1666                    try:
1667                        sets = list(val)
1668                    except ValueError:
1669                        sets = ['BabA', 'BabU']
1670                    for param in sets:
1671                        if param not in ['BabA', 'BabU']:
1672                            raise ValueError("Not sure what to do with" + param)
1673                        for hist in histograms:
1674                            hist['Babinet'][param][1] = True
1675                elif key == 'Extinction':
1676                    for h in histograms:
1677                        h['Extinction'][1] = True
1678                elif key == 'HStrain':
1679                    for h in histograms:
1680                        hist['HStrain'][1] = [True for p in hist['Hstrain'][0]]
1681                elif key == 'Mustrain':
1682                    for h in histograms:
1683                        mustrain = h['Mustrain']
1684                        newType = None
1685                        if isinstance(val, str):
1686                            if val in ['isotropic', 'uniaxial', 'generalized']:
1687                                newType = val
1688                            else:
1689                                raise ValueError("Not a Mustrain type: " + val)
1690                        elif isinstance(val, dict):
1691                            newType = val.get('type', None)
1692                            direction = val.get('direction', None)
1693
1694                        if newType:
1695                            mustrain[0] = newType
1696                            if newType == 'isotropic':
1697                                mustrain[2][0] = True
1698                                mustrain[5] = [False for p in mustrain[4]]
1699                            elif newType == 'uniaxial':
1700                                pass
1701                            else:  # newtype == 'generalized'
1702                                mustrain[2] = [False for p in mustrain[1]]
1703                        if direction:
1704                            direction = [int(n) for n in direction]
1705                            if len(direction) != 3:
1706                                raise ValueError("Expected hkl, found", direction)
1707                            mustrain[3] = direction
1708                elif key == 'Pref.Ori.':
1709                    for h in histograms:
1710                        h['Pref.Ori.'][2] = True
1711                elif key == 'Show':
1712                    for h in histograms:
1713                        h['Show'] = True
1714                elif key == 'Size':
1715                    raise NotImplementedError()
1716                elif key == 'Use':
1717                    for h in histograms:
1718                        h['Use'] = True
1719                elif key == 'Scale':
1720                    for h in histograms:
1721                        h['Scale'][1] = False
1722
1723    def clear_HAP_refinements(self, refs, histograms='all'):
1724        """Clears the given HAP refinement parameters between this phase and
1725        the given histograms
1726
1727        :param dict refs: A dictionary of the parameters to be cleared.
1728        :param histograms: Either 'all' (default) or a list of the histograms
1729            whose HAP parameters will be cleared with this phase. Histogram and
1730            phase must already be associated
1731
1732        :returns: None
1733        """
1734        if histograms == 'all':
1735            histograms = self['Histograms'].values()
1736        else:
1737            histograms = [self['Histograms'][h.name] for h in histograms]
1738
1739        for key, val in refs.items():
1740            for h in histograms:
1741                if key == 'Babinet':
1742                    try:
1743                        sets = list(val)
1744                    except ValueError:
1745                        sets = ['BabA', 'BabU']
1746                    for param in sets:
1747                        if param not in ['BabA', 'BabU']:
1748                            raise ValueError("Not sure what to do with" + param)
1749                        for hist in histograms:
1750                            hist['Babinet'][param][1] = False
1751                elif key == 'Extinction':
1752                    for h in histograms:
1753                        h['Extinction'][1] = False
1754                elif key == 'HStrain':
1755                    for h in histograms:
1756                        hist['HStrain'][1] = [False for p in hist['Hstrain'][0]]
1757                elif key == 'Mustrain':
1758                    for h in histograms:
1759                        mustrain = h['Mustrain']
1760                        mustrain[2] = [False for p in mustrain[1]]
1761                        mustrain[5] = [False for p in mustrain[4]]
1762                elif key == 'Pref.Ori.':
1763                    for h in histograms:
1764                        h['Pref.Ori.'][2] = False
1765                elif key == 'Show':
1766                    for h in histograms:
1767                        h['Show'] = False
1768                elif key == 'Size':
1769                    raise NotImplementedError()
1770                elif key == 'Use':
1771                    for h in histograms:
1772                        h['Use'] = False
1773                elif key == 'Scale':
1774                    for h in histograms:
1775                        h['Scale'][1] = False
1776
1777
1778##########################
1779# Command Line Interface #
1780##########################
1781
1782
1783def create(args):
1784    """The create subcommand."""
1785    proj = G2Project(filename=args.filename)
1786
1787    hist_objs = []
1788    for h in args.histograms:
1789        hist_objs.append(proj.add_powder_histogram(h, args.iparams))
1790
1791    for p in args.phases:
1792        proj.add_phase(p, histograms=hist_objs)
1793
1794    proj.save()
1795
1796
1797def dump(args):
1798    """The dump subcommand"""
1799    if not args.histograms and not args.phases:
1800        args.raw = True
1801    if args.raw:
1802        import IPython.lib.pretty as pretty
1803
1804    for fname in args.files:
1805        if args.raw:
1806            proj, nameList = LoadDictFromProjFile(fname)
1807            print("file:", fname)
1808            print("names:", nameList)
1809            for key, val in proj.items():
1810                print(key, ":")
1811                pretty.pprint(val)
1812        else:
1813            proj = G2Project(fname)
1814            if args.histograms:
1815                hists = proj.histograms()
1816                for h in hists:
1817                    print(fname, "hist", h.id, h.name)
1818            if args.phases:
1819                phase_list = proj.phases()
1820                for p in phase_list:
1821                    print(fname, "phase", p.id, p.name)
1822
1823
1824def IPyBrowse(args):
1825    """Load a .gpx file and then open a IPython shell to browse it
1826    """
1827    for fname in args.files:
1828        proj, nameList = LoadDictFromProjFile(fname)
1829        msg = "\nfname {} loaded into proj (dict) with names in nameList".format(fname)
1830        GSASIIpath.IPyBreak_base(msg)
1831        break
1832
1833
1834def refine(args):
1835    """The refine subcommand"""
1836    proj = G2Project(args.gpxfile)
1837    if args.refinements is None:
1838        proj.refine()
1839    else:
1840        import json
1841        with open(args.refinements) as refs:
1842            refs = json.load(refs)
1843        proj.do_refinements(refs['refinements'])
1844
1845
1846def seqrefine(args):
1847    """The seqrefine subcommand"""
1848    raise NotImplementedError("seqrefine is not yet implemented")
1849
1850
1851def export(args):
1852    """The export subcommand"""
1853    # Export phase as CIF to args.exportfile
1854    proj = G2Project(args.gpxfile)
1855    phase = proj.phase(args.phase)
1856    phase.export_CIF(args.exportfile)
1857
1858
1859def _args_kwargs(*args, **kwargs):
1860    return args, kwargs
1861
1862# A dictionary of the name of each subcommand, and a tuple
1863# of its associated function and a list of its arguments
1864# The arguments are passed directly to the add_argument() method
1865# of an argparse.ArgumentParser
1866subcommands = {"create":
1867               (create, [_args_kwargs('filename',
1868                                      help='the project file to create. should end in .gpx'),
1869
1870                         _args_kwargs('-i', '--iparams',
1871                                      help='instrument parameter file'),
1872
1873                         _args_kwargs('-d', '--histograms',
1874                                      nargs='+',
1875                                      help='list of datafiles to add as histograms'),
1876
1877                         _args_kwargs('-p', '--phases',
1878                                      nargs='+',
1879                                      help='list of phases to add. phases are '
1880                                           'automatically associated with all '
1881                                           'histograms given.')]),
1882
1883               "dump": (dump, [_args_kwargs('-d', '--histograms',
1884                                     action='store_true',
1885                                     help='list histograms in files, overrides --raw'),
1886
1887                               _args_kwargs('-p', '--phases',
1888                                            action='store_true',
1889                                            help='list phases in files, overrides --raw'),
1890
1891                               _args_kwargs('-r', '--raw',
1892                                      action='store_true', help='dump raw file contents, default'),
1893
1894                               _args_kwargs('files', nargs='+')]),
1895
1896               "refine":
1897               (refine, [_args_kwargs('gpxfile', help='the project file to refine'),
1898                         _args_kwargs('refinements',
1899                                      help='json file of refinements to apply. if not present'
1900                                           ' refines file as-is',
1901                                      default=None,
1902                                      nargs='?')]),
1903
1904               "seqrefine": (seqrefine, []),
1905               "export": (export, [_args_kwargs('gpxfile',
1906                                                help='the project file from which to export'),
1907                                   _args_kwargs('phase', help='identifier of phase to export'),
1908                                   _args_kwargs('exportfile', help='the .cif file to export to')]),
1909               "browse": (IPyBrowse, [_args_kwargs('files', nargs='+',
1910                                                   help='list of files to browse')])}
1911
1912
1913def main():
1914    '''The command line interface for GSASIIscriptable.
1915
1916    Executes one of the following subcommands:
1917
1918        * :func:`create`
1919        * :func:`dump`
1920        * :func:`refine`
1921        * :func:`seqrefine`
1922        * :func:`export`
1923        * :func:`browse`
1924
1925    .. seealso::
1926        :func:`create`
1927        :func:`dump`
1928        :func:`refine`
1929        :func:`seqrefine`
1930        :func:`export`
1931        :func:`browse`
1932    '''
1933    parser = argparse.ArgumentParser()
1934    subs = parser.add_subparsers()
1935
1936    # Create all of the specified subparsers
1937    for name, (func, args) in subcommands.items():
1938        new_parser = subs.add_parser(name)
1939        for listargs, kwds in args:
1940            new_parser.add_argument(*listargs, **kwds)
1941        new_parser.set_defaults(func=func)
1942
1943    # Parse and trigger subcommand
1944    result = parser.parse_args()
1945    result.func(result)
1946
1947    # argv = sys.argv
1948    # if len(argv) > 1 and argv[1] in subcommands:
1949    #     subcommands[argv[1]](*argv[2:])
1950    # elif len(argv) == 1 or argv[1] in ('help', '--help', '-h'):
1951    #     # TODO print usage
1952    #     subcommand_names = ' | '.join(sorted(subcommands.keys()))
1953    #     print("USAGE: {} [ {} ] ...".format(argv[0], subcommand_names))
1954    # else:
1955    #     print("Unknown subcommand: {}".format(argv[1]))
1956    #     print("Available subcommands:")
1957    #     for name in sorted(subcommands.keys()):
1958    #         print("\t{}".format(name))
1959    #     sys.exit(-1)
1960    # sys.exit(0)
1961
1962if __name__ == '__main__':
1963    main()
1964
1965
1966    # from refinements.py
1967#      USAGE = """USAGE: {} datafile instparams phasefile projectname refinements
1968
1969# datafile:    Input powder data
1970# intparams:   Corresponding instrument parameter file
1971# phasefile:   Phase to refine against data
1972# projectname: Project file to be created, should end in .gpx
1973# refinements: JSON file of refinements to be executed
1974# """
1975#     try:
1976#         datafile, instprm, phasefile, projectname, refinements = sys.argv[1:]
1977#     except ValueError:
1978#         print(USAGE.format(sys.argv[0]))
1979#         sys.exit(-1)
1980
1981#     try:
1982#         with open(refinements) as f:
1983#             refinements = json.load(f)
1984#     except IOError:
1985#         print("No such refinements file: {}".format(refinements))
1986
1987#     print("Creating project file \"{}\"...".format(projectname))
1988#     proj = G2Project(filename=projectname)
1989#     # Add the histogram
1990#     hist = proj.add_powder_histogram(datafile, instprm)
1991#     # Add the phase, and associate it with the histogram
1992#     proj.add_phase(phasefile, histograms=[hist.name])
1993
1994#     proj.do_refinements(refinements['refinements'])
1995#     proj.save()
1996
1997
1998    # from gpx_dumper
1999    # import IPython.lib.pretty as pretty
2000    # proj, nameList = LoadDictFromProjFile(sys.argv[1])
2001    # print("names:", nameList)
2002    # for key, val in proj.items():
2003    #     print(key, ":", sep='')
2004    #     pretty.pprint(val)
Note: See TracBrowser for help on using the repository browser.