source: trunk/GSASIIscriptable.py @ 3011

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

CIF export. changelist:

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