Changeset 2968


Ignore:
Timestamp:
Aug 8, 2017 9:26:44 AM (6 years ago)
Author:
odonnell
Message:

More sphinx docstrings, start of command line interface, wrappers have project reference

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branch/2frame/GSASIIscriptable.py

    r2967 r2968  
     1#!/usr/bin/env python
    12# -*- coding: utf-8 -*-
    23########### SVN repository information ###################
     
    1112-----------------------------------
    1213
    13 Routines for reading, writing, modifying and creating GSAS-II project (.gpx) files. 
     14Routines for reading, writing, modifying and creating GSAS-II project (.gpx) files.
    1415
    1516"""
    16 from __future__ import division, print_function # needed? 
     17from __future__ import division, print_function # needed?
    1718import os.path as ospath
    1819import sys
     
    4748    :param str ProjFile: GSAS-II project (name.gpx) full file name
    4849    :returns: Project,nameList, where
    49    
     50
    5051      * Project (dict) is a representation of gpx file following the GSAS-II tree struture
    5152        for each item: key = tree name (e.g. 'Controls','Restraints',etc.), data is dict
     
    5455
    5556    Example for fap.gpx::
    56    
     57
    5758      Project = {                 #NB:dict order is not tree order
    58         u'Phases':{'data':None,'fap':{phase dict}}, 
    59         u'PWDR FAP.XRA Bank 1':{'data':[histogram data list],'Comments':comments,'Limits':limits, etc}, 
     59        u'Phases':{'data':None,'fap':{phase dict}},
     60        u'PWDR FAP.XRA Bank 1':{'data':[histogram data list],'Comments':comments,'Limits':limits, etc},
    6061        u'Rigid bodies':{'data': {rigid body dict}},
    6162        u'Covariance':{'data':{covariance data dict}},
    62         u'Controls':{'data':{controls data dict}}, 
    63         u'Notebook':{'data':[notebook list]}, 
    64         u'Restraints':{'data':{restraint data dict}}, 
     63        u'Controls':{'data':{controls data dict}},
     64        u'Notebook':{'data':[notebook list]},
     65        u'Restraints':{'data':{restraint data dict}},
    6566        u'Constraints':{'data':{constraint data dict}}]
    6667        }
    6768      nameList = [                #NB: reproduces tree order
    6869        [u'Notebook',],
    69         [u'Controls',], 
    70         [u'Covariance',], 
     70        [u'Controls',],
     71        [u'Covariance',],
    7172        [u'Constraints',],
    72         [u'Restraints',], 
    73         [u'Rigid bodies',], 
     73        [u'Restraints',],
     74        [u'Rigid bodies',],
    7475        [u'PWDR FAP.XRA Bank 1',
    75              u'Comments', 
    76              u'Limits', 
    77              u'Background', 
     76             u'Comments',
     77             u'Limits',
     78             u'Background',
    7879             u'Instrument Parameters',
    79              u'Sample Parameters', 
    80              u'Peak List', 
    81              u'Index Peak List', 
    82              u'Unit Cells List', 
    83              u'Reflection Lists'], 
     80             u'Sample Parameters',
     81             u'Peak List',
     82             u'Index Peak List',
     83             u'Unit Cells List',
     84             u'Reflection Lists'],
    8485        [u'Phases', u'fap']
    8586        ]
    86        
    8787    '''
    8888    if not ospath.exists(ProjFile):
     
    112112        return None
    113113    return Project,nameList
    114    
     114
    115115def SaveDictToProjFile(Project,nameList,ProjFile):
    116116    '''Save a GSAS-II project file from dictionary/nameList created by LoadDictFromProjFile
    117    
     117
    118118    :param dict Project: representation of gpx file following the GSAS-II
    119119      tree structure as described for LoadDictFromProjFile
    120120    :param list nameList: names of main tree entries & subentries used to reconstruct project file
    121121    :param str ProjFile: full file name for output project.gpx file (including extension)
    122    
    123122    '''
    124123    file = open(ProjFile,'wb')
     
    133132    file.close()
    134133    print('project save successful')
    135    
     134
    136135def ImportPowder(reader,filename):
    137136    '''Use a reader to import a powder diffraction data file
    138    
     137
    139138    :param str reader: a scriptable reader
    140139    :param str filename: full name of powder data file; can be "multi-Bank" data
    141    
    142     :returns: list rdlist: list of rrader objects containing powder data, one for each
     140
     141    :returns list rdlist: list of reader objects containing powder data, one for each
    143142      "Bank" of data encountered in file. Items in reader object of interest are:
    144      
     143
    145144        * rd.comments: list of str: comments found on powder file
    146145        * rd.dnames: list of str: data nammes suitable for use in GSASII data tree NB: duplicated in all rd entries in rdlist
    147146        * rd.powderdata: list of numpy arrays: pos,int,wt,zeros,zeros,zeros as needed for a PWDR entry in  GSASII data tree.
    148              
    149147    '''
    150148    rdfile,rdpath,descr = imp.find_module(reader)
    151149    rdclass = imp.load_module(reader,rdfile,rdpath,descr)
    152     rd = rdclass.GSAS_ReaderClass()   
     150    rd = rdclass.GSAS_ReaderClass()
    153151    if not rd.scriptable:
    154152        print(u'**** ERROR: '+reader+u' is not a scriptable reader')
     
    173171    print(rd.errors)
    174172    return None
    175    
     173
    176174def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
    177175    '''Create an initial Histogram dictionary
    178    
     176
    179177    Author: Jackson O'Donnell jacksonhodonnell@gmail.com
    180178    '''
     
    203201def PreSetup(data):
    204202    '''Create part of an initial (empty) phase dictionary
    205    
     203
    206204    from GSASIIphsGUI.py, near end of UpdatePhaseData
    207205    Author: Jackson O'Donnell jacksonhodonnell@gmail.com
     
    594592
    595593class G2ObjectWrapper(object):
    596     """Base class for all GSAS-II object wrappers
     594    """Base class for all GSAS-II object wrappers.
     595
     596    The underlying GSAS-II format can be accessed as `wrapper.data`. A number
     597    of overrides are implemented so that the wrapper behaves like a dictionary.
     598
    597599    Author: Jackson O'Donnell jacksonhodonnell@gmail.com
    598600    """
     
    633635        loaded from the gpxfile, then set to  save to filename in the future"""
    634636        if gpxfile is None:
     637            filename = os.path.abspath(os.path.expanduser(filename))
    635638            self.filename = filename
    636639            self.data, self.names = make_empty_project(author=author, filename=filename)
    637640        elif isinstance(gpxfile, str):
    638641            # TODO set author, filename
    639             self.filename = gpxfile
     642            self.filename = os.path.abspath(os.path.expanduser(gpxfile))
    640643            self.data, self.names = LoadDictFromProjFile(gpxfile)
    641644            self.index_ids()
     
    645648    @classmethod
    646649    def from_dict_and_names(cls, gpxdict, names, filename=None):
     650        """Creates a :class:`G2Project` directly from
     651        a dictionary and a list of names. If in doubt, do not use this.
     652
     653        :returns: a :class:`G2Project`
     654        """
    647655        out = cls()
    648656        if filename:
     657            filename = os.path.abspath(os.path.expanduser(filename))
    649658            out.filename = filename
    650659            gpxdict['Controls']['data']['LastSavedAs'] = filename
     
    663672        # TODO update LastSavedUsing ?
    664673        if filename:
     674            filename = os.path.abspath(os.path.expanduser(filename))
    665675            self.data['Controls']['data']['LastSavedAs'] = filename
    666676            self.filename = filename
     
    673683
    674684        Automatically checks for an instrument parameter file, or one can be
    675         provided."""
     685        provided.
     686
     687        :param str datafile: The powder data file to read, a filename.
     688        :param str iparams: The instrument parameters file, a filename.
     689
     690        :returns: A :class:`G2PwdrData` object representing
     691        the histogram"""
     692        datafile = os.path.abspath(os.path.expanduser(datafile))
     693        iparams = os.path.abspath(os.path.expanduser(iparams))
    676694        pwdrreaders = import_generic(datafile, PwdrDataReaders)
    677695        histname, new_names, pwdrdata = load_pwd_from_reader(
     
    691709        """Loads a phase into the project from a .cif file
    692710
    693         :returns: A :class:`gpx_manipulations.G2Phase` object representing the
     711        :param str phasefile: The CIF file from which to import the phase.
     712        :param str phasename: The name of the new phase, or None for the default
     713        :param list histograms: The names of the histograms to associate with
     714        this phase
     715
     716        :returns: A :class:`G2Phase` object representing the
    694717        new phase."""
     718        phasefile = os.path.abspath(os.path.expanduser(phasefile))
     719
    695720        # TODO handle multiple phases in a file
    696721        phasereaders = import_generic(phasefile, PhaseReaders)
     
    778803
    779804    def histogram_names(self):
    780         """Gives an iterator of the names of each histogram in the project."""
     805        """Gives a list of the names of each histogram in the project.
     806
     807        :returns: list of strings
     808
     809        .. seealso::
     810            :func:`~GSASIIscriptable.G2Project.histogram`
     811            :func:`~GSASIIscriptable.G2Project.histograms`
     812            :func:`~GSASIIscriptable.G2Project.phase`
     813            :func:`~GSASIIscriptable.G2Project.phases`
     814            :func:`~GSASIIscriptable.G2Project.phase_names`
     815            """
     816        output = []
    781817        for obj in self.names:
    782818            if len(obj) > 1 and obj[0] != u'Phases':
    783                 yield obj[0]
     819                output.append(obj[0])
     820        return output
    784821
    785822    def histogram(self, histname):
    786         """Returns the histogram named histname, or None if it does not exist."""
     823        """Returns the histogram named histname, or None if it does not exist.
     824
     825        :returns: A :class:`G2PwdrData` object, or None if
     826        the histogram does not exist
     827        .. seealso::
     828            :func:`~GSASIIscriptable.G2Project.histogram_names`
     829            :func:`~GSASIIscriptable.G2Project.histograms`
     830            :func:`~GSASIIscriptable.G2Project.phase`
     831            :func:`~GSASIIscriptable.G2Project.phases`
     832            :func:`~GSASIIscriptable.G2Project.phase_names`
     833            """
    787834        if histname in self.data:
    788             return G2PwdrData(self.data[histname])
     835            return G2PwdrData(self.data[histname], self)
    789836        return None
    790837
    791838    def histograms(self):
    792839        """Return a list of all histograms, as
    793         :class:`gpx_manipulations.G2PwdrData` objects"""
     840        :class:`G2PwdrData` objects
     841        .. seealso::
     842            :func:`~GSASIIscriptable.G2Project.histogram_names`
     843            :func:`~GSASIIscriptable.G2Project.histograms`
     844            :func:`~GSASIIscriptable.G2Project.phase`
     845            :func:`~GSASIIscriptable.G2Project.phases`
     846            :func:`~GSASIIscriptable.G2Project.phase_names`
     847            """
    794848        return [self.histogram(name) for name in self.histogram_names()]
    795849
    796850    def phase_names(self):
    797         """Gives an iterator of the names of each phase in the project."""
     851        """Gives an list of the names of each phase in the project.
     852
     853        :returns: A list of strings
     854        .. seealso::
     855            :func:`~GSASIIscriptable.G2Project.histogram`
     856            :func:`~GSASIIscriptable.G2Project.histograms`
     857            :func:`~GSASIIscriptable.G2Project.histogram_names`
     858            :func:`~GSASIIscriptable.G2Project.phase`
     859            :func:`~GSASIIscriptable.G2Project.phases`
     860            """
    798861        for obj in self.names:
    799862            if obj[0] == 'Phases':
    800                 for name in obj[1:]:
    801                     yield name
    802                 break
     863                return obj[1:]
     864        return []
    803865
    804866    def phase(self, phasename):
    805         return G2Phase(self.data['Phases'][phasename])
     867        """
     868        Gives an object representing the specified phase in this project.
     869
     870        :param str phasename: The name of the desired phase
     871        :returns: A :class:`G2Phase` object
     872        :raises: KeyError
     873        .. seealso::
     874            :func:`~GSASIIscriptable.G2Project.histogram_names`
     875            :func:`~GSASIIscriptable.G2Project.histograms`
     876            :func:`~GSASIIscriptable.G2Project.phase`
     877            :func:`~GSASIIscriptable.G2Project.phases`
     878            :func:`~GSASIIscriptable.G2Project.phase_names`
     879            """
     880        phases = self.data['Phases']
     881        if phasename in phases:
     882            return G2Phase(phases[phasename], self)
    806883
    807884    def phases(self):
     885        """
     886        Returns a list of all the phases in the project.
     887
     888        :returns: A :class:`G2Phase`
     889        .. seealso::
     890            :func:`~GSASIIscriptable.G2Project.histogram`
     891            :func:`~GSASIIscriptable.G2Project.histograms`
     892            :func:`~GSASIIscriptable.G2Project.histogram_names`
     893            :func:`~GSASIIscriptable.G2Project.phase`
     894            :func:`~GSASIIscriptable.G2Project.phase_names`
     895            """
    808896        return [self.phase(name) for name in self.phase_names()]
    809897
    810898    def do_refinements(self, refinements, histogram='all', phase='all',
    811899                       outputnames=None):
    812         """Conducts a series of refinements."""
     900        """Conducts a series of refinements.
     901
     902        :param list refinements: A list of dictionaries defining refinements
     903        :param str histogram: Name of histogram for refinements to be applied
     904        to, or 'all'
     905        :param str phase: Name of phase for refinements to be applied to, or
     906        'all'
     907        """
    813908        if outputnames:
    814909            if len(refinements) != len(outputnames):
     
    904999        string variable specifiers, or arguments for :meth:`make_var_obj`
    9051000        :param str type: A string constraint type specifier. See
    906         :meth:`add_constraint_raw`"""
     1001        :class:`~GSASIIscriptable.G2Project.add_constraint_raw`"""
    9071002        for var in vars:
    9081003            if isinstance(var, str):
     
    9571052
    9581053class G2AtomRecord(G2ObjectWrapper):
    959     """Wrapper for an atom record
    960     Author: Jackson O'Donnell jacksonhodonnell@gmail.com
     1054    """Wrapper for an atom record. Has convenient accessors via @property.
     1055
     1056    Example:
     1057
     1058    >>> atom = some_phase.atom("O3")
     1059    >>> # We can access the underlying data format
     1060    >>> atom.data
     1061    ['O3', 'O-2', '', ... ]
     1062    >>> # We can also use wrapper accessors
     1063    >>> atom.coordinates
     1064    (0.33, 0.15, 0.5)
     1065    >>> atom.refinement_flags
     1066    u'FX'
     1067    >>> atom.ranId
     1068    4615973324315876477
    9611069    """
    962     def __init__(self, data, indices):
     1070    def __init__(self, data, indices, proj):
    9631071        self.data = data
    9641072        self.cx, self.ct, self.cs, self.cia = indices
     1073        self.proj = proj
    9651074
    9661075    @property
     
    9771086
    9781087    @refinement_flags.setter
    979     def set_refinement(self, other):
     1088    def refinement_flags(self, other):
     1089        # Automatically check it is a valid refinement
    9801090        for c in other:
    9811091            if c not in ' FXU':
     
    10051115
    10061116class G2PwdrData(G2ObjectWrapper):
    1007     """Wraps a Poweder Data Histogram.
    1008     Author: Jackson O'Donnell jacksonhodonnell@gmail.com
    1009     """
     1117    """Wraps a Poweder Data Histogram."""
     1118    def __init__(self, data, proj):
     1119        self.data = data
     1120        self.proj = proj
     1121
    10101122    @staticmethod
    10111123    def is_valid_refinement_key(key):
     
    10501162        Y = [y for x, y in bgrnd[1]['FixedPoints']]
    10511163
     1164        limits = self['Limits'][1]
     1165        if X[0] > limits[0]:
     1166            X = [limits[0]] + X
     1167            Y = [Y[0]] + Y
     1168        if X[-1] < limits[1]:
     1169            X += [limits[1]]
     1170            Y += [Y[-1]]
     1171
    10521172        # Some simple lookups
    1053         limits = self['Limits']
    10541173        inst, inst2 = self['Instrument Parameters']
    10551174        pwddata = self['data'][1]
     
    10711190        data = np.array([xdata, ydata, W, Z, Z, Z])
    10721191        G2pwd.DoPeakFit('LSQ', [], bgrnd, limits, inst, inst2, data,
    1073                         preVaryList=bakVary, controls={})
     1192                        bakVary, controls={})
    10741193
    10751194        # Post-fit
     
    11281247                bkg[3:] = value['coeffs']
    11291248            if 'FixedPoints' in value:
    1130                 peaks['FixedPoints'] = value
     1249                peaks['FixedPoints'] = [(float(a), float(b))
     1250                                        for a, b in value['FixedPoints']]
    11311251            if value.get('fit fixed points', False):
    11321252                self.fit_fixed_points()
     
    11751295    Author: Jackson O'Donnell jacksonhodonnell@gmail.com
    11761296    """
     1297    def __init__(self, data, proj):
     1298        self.data = data
     1299        self.proj = proj
     1300
    11771301    @staticmethod
    11781302    def is_valid_refinement_key(key):
     
    11821306    def atom(self, atomlabel):
    11831307        """Returns the atom specified by atomlabel, or None if it does not
    1184         exist."""
     1308        exist.
     1309
     1310        :param str atomlabel: The name of the atom (e.g. "O2")
     1311
     1312        :returns: A :class:`G2AtomRecord` object
     1313        representing the atom."""
    11851314        # Consult GSASIIobj.py for the meaning of this
    11861315        cx, ct, cs, cia = self.data['General']['AtomPtrs']
     
    11891318        for atom in atoms:
    11901319            if atom[ct-1] == atomlabel:
    1191                 return G2AtomRecord(atom, ptrs)
     1320                return G2AtomRecord(atom, ptrs, self.proj)
    11921321
    11931322    def atoms(self):
    1194         """Returns a list of atoms present in the phase."""
     1323        """Returns a list of atoms present in the phase.
     1324
     1325        :returns: A list of :class:`G2AtomRecord` objects. See
     1326        also :func:`~GSASIIscriptable.G2Phase.atom`"""
    11951327        ptrs = self.data['General']['AtomPtrs']
    11961328        output = []
    11971329        atoms = self.data['Atoms']
    11981330        for atom in atoms:
    1199             output.append(G2AtomRecord(atom, ptrs))
     1331            output.append(G2AtomRecord(atom, ptrs, self.proj))
     1332        return output
     1333
     1334    def histograms(self):
     1335        output = []
     1336        for hname in self.data.get('Histograms', {}).keys():
     1337            output.append(self.proj.histogram(hname))
    12001338        return output
    12011339
     
    12051343
    12061344    def cell_dict(self):
     1345        """Returns a dictionary of the cell parameters, with keys:
     1346            'a', 'b', 'c', 'alpha', 'beta', 'gamma', 'vol'
     1347
     1348        :returns: a dict"""
    12071349        cell = self['General']['Cell']
    12081350        return {'a': cell[1], 'b': cell[2], 'c': cell[3],
    12091351                'alpha': cell[4], 'beta': cell[5], 'gamma': cell[6],
    1210                 'vol': cell[6]}
     1352                'vol': cell[7]}
    12111353
    12121354    def set_refinement(self, key, value):
     
    12171359
    12181360            for atomlabel, atomrefinement in value.items():
    1219                 atom = self.atom(atomlabel)
    1220                 atom.refinement_flags = atomrefinement
     1361                if atomlabel == 'all':
     1362                    for atom in self.atoms():
     1363                        atom.refinement_flags = atomrefinement
     1364                else:
     1365                    atom = self.atom(atomlabel)
     1366                    atom.refinement_flags = atomrefinement
    12211367        elif key == "LeBail":
    12221368            hists = self.data['Histograms']
     
    12471393            raise ValueError("Unknown key:", key)
    12481394
     1395
     1396##########################
     1397# Command Line Interface #
     1398##########################
     1399
     1400
     1401def create(*args):
     1402    """The create subcommand.
     1403
     1404    Should be passed all the command-line arguments after `create`"""
     1405    proj = G2Project(filename=args[0])
     1406
     1407    isPhase = False
     1408    isPowderData = False
     1409    isInstPrms = False
     1410    instPrms = None
     1411
     1412    # TODO how to associate phase with histogram?
     1413    for arg in args[1:]:
     1414        if arg == '--phases':
     1415            isPhase = True
     1416            isPowderData = False
     1417            isInstPrms = False
     1418        elif arg == '--powder':
     1419            isPhase = False
     1420            isPowderData = True
     1421            isInstPrms = False
     1422        # Instrument parameters must be specified before
     1423        # any powder data files are passed
     1424        elif arg == '--iparams':
     1425            isPhase = False
     1426            isPowderData = False
     1427            isInstPrms = True
     1428        elif isPhase:
     1429            proj.add_phase(arg)
     1430        elif isPowderData:
     1431            proj.add_powder_histogram(arg, instPrms)
     1432        elif isInstPrms:
     1433            instPrms = arg
     1434            isInstPrms = False
     1435        else:
     1436            print("Not sure what to do with: {}".format(arg))
     1437
     1438    proj.save()
     1439
     1440
     1441def dump(*args):
     1442    """The dump subcommand"""
     1443    import IPython.lib.pretty as pretty
     1444    proj, nameList = LoadDictFromProjFile(args[0])
     1445    print("names:", nameList)
     1446    for key, val in proj.items():
     1447        print(key, ":", sep='')
     1448        pretty.pprint(val)
     1449
     1450
     1451def refine(*args):
     1452    """The refine subcommand"""
     1453    proj = G2Project(args[0])
     1454    proj.refine()
     1455
     1456
     1457def seqrefine(*args):
     1458    """The seqrefine subcommand"""
     1459    pass
     1460
     1461
     1462def export(*args):
     1463    pass
     1464
     1465
     1466subcommands = {"create": create,
     1467               "dump": dump,
     1468               "refine": refine,
     1469               "seqrefine": seqrefine,
     1470               "export": export}
     1471
     1472
    12491473def main():
    12501474    '''TODO: the command-line options need some thought
    12511475    '''
    1252     arg = sys.argv
    1253     print(arg)
    1254     if len(arg) > 1:
    1255         GPXfile = arg[1]
    1256         if not ospath.exists(GPXfile):
    1257             print(u'ERROR - '+GPXfile+u" doesn't exist!")
    1258             exit()
    1259         Project,nameList = LoadDictFromProjFile(GPXfile)
    1260         SaveDictToProjFile(Project,nameList,'testout.gpx')
     1476    argv = sys.argv
     1477    if len(argv) > 1 and argv[1] in subcommands:
     1478        subcommands[argv[1]](*argv[2:])
     1479    elif len(argv) == 1 or argv[1] in ('help', '--help', '-h'):
     1480        # TODO print usage
     1481        subcommand_names = ' | '.join(subcommands.keys())
     1482        print("USAGE: {} [ {} ] ...".format(argv[0], subcommand_names))
    12611483    else:
    1262         print('ERROR - missing filename')
    1263         exit()
    1264     print("Done")
    1265          
     1484        print("Unknown subcommand: {}".format(argv[1]))
     1485        print("Available subcommands:")
     1486        for name in subcommands.keys():
     1487            print("\t{}".format(name))
     1488        sys.exit(-1)
     1489    # arg = sys.argv
     1490    # print(arg)
     1491    # if len(arg) > 1:
     1492    #     GPXfile = arg[1]
     1493    #     if not ospath.exists(GPXfile):
     1494    #         print(u'ERROR - '+GPXfile+u" doesn't exist!")
     1495    #         exit()
     1496    #     Project,nameList = LoadDictFromProjFile(GPXfile)
     1497    #     SaveDictToProjFile(Project,nameList,'testout.gpx')
     1498    # else:
     1499    #     print('ERROR - missing filename')
     1500    #     exit()
     1501    # print("Done")
     1502
     1503PwdrDataReaders = G2fil.LoadImportRoutines("pwd", "Powder_Data")
     1504PhaseReaders = G2fil.LoadImportRoutines("phase", "Phase")
    12661505if __name__ == '__main__':
    1267     PwdrDataReaders = G2fil.LoadImportRoutines("pwd", "Powder_Data")
    1268     PhaseReaders = G2fil.LoadImportRoutines("phase", "Phase")
    12691506    main()
    12701507
     
    13181555    #     print(key, ":", sep='')
    13191556    #     pretty.pprint(val)
    1320    
     1557
Note: See TracChangeset for help on using the changeset viewer.