source: trunk/GSASIIscriptable.py @ 3292

Last change on this file since 3292 was 3292, checked in by toby, 5 years ago

rework Scriptable docs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 127.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3########### SVN repository information ###################
4# $Date: 2018-02-19 23:57:29 +0000 (Mon, 19 Feb 2018) $
5# $Author: toby $
6# $Revision: 3292 $
7# $URL: trunk/GSASIIscriptable.py $
8# $Id: GSASIIscriptable.py 3292 2018-02-19 23:57:29Z toby $
9########### SVN repository information ###################
10#
11# TODO: integrate 2d data based on a model
12#
13"""
14*GSASIIscriptable: Scripting Interface*
15=======================================
16
17Routines for reading, writing, modifying and creating GSAS-II project (.gpx) files.
18This file specifies several wrapper classes around GSAS-II data representations.
19They all inherit from :class:`G2ObjectWrapper`. The chief class is :class:`G2Project`,
20which represents an entire GSAS-II project and provides several methods to access
21phases, powder histograms, and execute Rietveld refinements. These routines can be
22accessed in two ways:
23the :ref:`CommandlineInterface` provides access a number of features without writing
24Python scripts of from the :ref:`API`, which allows much more versatile access from
25within Python.
26
27
28=====================
29Overview
30=====================
31The most commonly used routines are:
32
33:class:`G2Project`
34   Always needed: used to create a new project (.gpx) file or read in an existing one.
35
36:meth:`G2Project.add_powder_histogram`
37   Used to read in powder diffraction data to a project file.
38
39:meth:`G2Project.add_simulated_powder_histogram`
40   Defines a "dummy" powder diffraction data that will be simulated after a refinement step.
41
42:meth:`G2Project.save`
43   Writes the current project to disk.
44
45:meth:`G2Project.do_refinements`
46   This is passed a list of dictionaries, where each dict defines a refinement step.
47   Passing a list with a single empty dict initiates a refinement with the current
48   parameters and flags.
49   
50:meth:`G2Project.set_refinement`
51   This is passed a single dictionaries which is used to set parameters and flags.
52   These actions can be performed also in :meth:`G2Project.do_refinements`.
53
54Refinement step dicts
55   A refinement step dict, as used in :meth:`G2Project.do_refinements` and described in
56   :ref:`Project_dicts`,
57   specifies parameter & refinement flag changes, which are usually followed by a refinement and
58   optionally by calls to locally-defined Python functions. The keys used in these dicts are
59   defined in the :ref:`Refinement_recipe` table, below.
60
61There are several ways to set parameters project using different level objects, as is
62are described in sections below, but the simplest way to access parameters and flags
63in a project is to use the above routines.
64
65.. _Project_dicts:
66
67-----------------------------
68Project-level Parameter Dict
69-----------------------------
70
71As noted below (:ref:`Refinement_parameters_kinds`), there are three types of refinement parameters,
72which can be accessed individually by the objects that encapsulate individual phases and histograms
73but it will be usually simplest to create a composite dictionary
74that is used at the project-level. A dict is created with keys
75"set" and "clear" that can be supplied to :meth:`G2Project.set_refinement`
76(or :meth:`G2Project.do_refinements`, see :ref:`Refinement_recipe` below) that will
77determine parameter values and will determine which parameters will be refined.
78
79The specific keys and subkeys that can be used are defined in tables
80:ref:`Histogram_parameters_table`, :ref:`Phase_parameters_table` and :ref:`HAP_parameters_table`.
81
82Note that optionally a list of histograms and/or phases can be supplied in the call to
83:meth:`G2Project.set_refinement`, but if not specified, the default is to use all defined
84phases and histograms.
85
86As an example:
87
88.. code-block::  python
89
90    pardict = {'set': { 'Limits': [0.8, 12.0],
91                       'Sample Parameters': ['Absorption', 'Contrast', 'DisplaceX'],
92                       'Background': {'type': 'chebyschev', 'refine': True}},
93              'clear': {'Instrument Parameters': ['U', 'V', 'W']}}
94    my_project.set_refinement(pardict)
95   
96.. _Refinement_recipe:
97   
98------------------------
99Refinement recipe
100------------------------
101Building on the :ref:`Project_dicts`,
102it is possible to specify a sequence of refinement actions as a list of
103these dicts and supplying this list
104as an argument to :meth:`G2Project.do_refinements`.
105
106As an example, this code performs the same actions as in the example in the section above:
107
108.. code-block::  python
109   
110    pardict = {'set': { 'Limits': [0.8, 12.0],
111                       'Sample Parameters': ['Absorption', 'Contrast', 'DisplaceX'],
112                       'Background': {'type': 'chebyschev', 'refine': True}},
113              'clear': {'Instrument Parameters': ['U', 'V', 'W']}}
114    my_project.do_refinements([pardict])
115
116However, in addition to setting a number of parameters, this example will perform a refinement as well.
117If more than one dict is specified in the list, as is done in this example,
118two refinement steps will be performed:
119
120.. code-block::  python
121
122    my_project.do_refinements([pardict,pardict1])
123
124
125The keys defined in the following table
126may be used in a dict supplied to :meth:`G2Project.do_refinements`. Note that now keys ``histograms``
127and ``phases`` are used to limit actions to specific sets of parameters within the project.
128
129========== ============================================================================
130key         explanation
131========== ============================================================================
132set                    Specifies a dict with keys and subkeys as described in the
133                       :ref:`Refinement_parameters_fmt` section. Items listed here
134                       will be set to be refined.
135clear                  Specifies a dict, as above for set, except that parameters are
136                       cleared and thus will not be refined.
137once                   Specifies a dict as above for set, except that parameters are
138                       set for the next cycle of refinement and are cleared once the
139                       refinement step is completed.
140skip                   Normally, once parameters are processed with a set/clear/once
141                       action(s), a refinement is started. If skip is defined as True
142                       (or any other value) the refinement step is not performed.
143output                 If a file name is specified for output is will be used to save
144                       the current refinement.
145histograms             Should contain a list of histogram(s) to be used for the
146                       set/clear/once action(s) on :ref:`Histogram_parameters_table` or
147                       :ref:`HAP_parameters_table`. Note that this will be
148                       ignored for :ref:`Phase_parameters_table`. Histograms may be
149                       specified as a list of strings [('PWDR ...'),...], indices
150                       [0,1,2] or as list of objects [hist1, hist2].
151phases                 Should contain a list of phase(s) to be used for the
152                       set/clear/once action(s) on :ref:`Phase_parameters_table` or
153                       :ref:`HAP_parameters_table`. Note that this will be
154                       ignored for :ref:`Histogram_parameters_table`.
155                       Phases may be specified as a list of strings
156                       [('Phase name'),...], indices [0,1,2] or as list of objects
157                       [phase0, phase2].
158call                   Specifies a function to call after a refinement is completed.
159                       The value supplied can be the object (typically a function)
160                       that will be called or a string that will evaluate (in the
161                       namespace inside :meth:`G2Project.iter_refinements` where
162                       ``self`` references the project.)
163                       Nothing is called if this is not specified.
164callargs               Provides a list of arguments that will be passed to the function
165                       in call (if any). If call is defined and callargs is not, the
166                       current <tt>G2Project</tt> is passed as a single argument.
167========== ============================================================================
168
169An example the performs a series of refinement steps follows:
170
171.. code-block::  python
172
173    reflist = [
174            {"set": { "Limits": { "low": 0.7 },
175                      "Background": { "no. coeffs": 3,
176                                      "refine": True }}},
177            {"set": { "LeBail": True,
178                      "Cell": True }},
179            {"set": { "Sample Parameters": ["DisplaceX"]}},
180            {"set": { "Instrument Parameters": ["U", "V", "W", "X", "Y"]}},
181            {"set": { "Mustrain": { "type": "uniaxial",
182                                    "refine": "equatorial",
183                                    "direction": [0, 0, 1]}}},
184            {"set": { "Mustrain": { "type": "uniaxial",
185                                    "refine": "axial"}}},
186            {"clear": { "LeBail": True},
187             "set": { "Atoms": { "Mn": "X" }}},
188            {"set": { "Atoms": { "O1": "X", "O2": "X" }}},]
189    my_project.do_refinements(reflist)
190   
191
192In this example, a separate refinement step will be performed for each dict in the list, since
193"skip" is not included.
194Note that in the second from last refinement step, parameters are both set and cleared.
195   
196.. _Refinement_parameters_kinds:
197
198=====================
199Refinement parameters
200=====================
201
202Note that parameters and refinement flags used in GSAS-II fall into three classes:
203
204    * **Histogram**: There will be a set of these for each dataset loaded into a
205      project file. The parameters available depend on the type of histogram
206      (Bragg-Brentano, Single-Crystal, TOF,...). Typical Histogram parameters
207      include the overall scale factor, background, instrument and sample parameters;
208      see the :ref:`Histogram_parameters_table` table for a list of the histogram
209      parameters where access has been provided.
210     
211    * **Phase**: There will be a set of these for each phase loaded into a
212      project file. While some parameters are found in all types of phases,
213      others are only found in certain types (modulated, magnetic, protein...).
214      Typical phase parameters include unit cell lengths and atomic positions; see the
215      :ref:`Phase_parameters_table` table for a list of the phase     
216      parameters where access has been provided.
217     
218    * **Histogram-and-phase** (HAP): There is a set of these for every histogram
219      that is associated with each phase, so that if there are ``N`` phases and ``M``
220      histograms, there can be ``N*M`` total sets of "HAP" parameters sets (fewer if all
221      histograms are not linked to all phases.) Typical HAP parameters include the
222      phase fractions, sample microstrain and crystallite size broadening terms,
223      hydrostatic strain pertibations of the unit cell and preferred orientation
224      values.
225      See the :ref:`HAP_parameters_table` table for the HAP parameters where access has
226      been provided.
227
228.. _Refinement_parameters_fmt:
229
230============================
231Refinement specifiers format
232============================
233
234Refinement parameters are specified within dictionaries. The details of these dicts depends on the
235type of parameter (see :ref:`Refinement_parameters_kinds`), with a different set
236of keys described below for each of the three parameter classes.
237
238.. _Histogram_parameters_table:
239
240--------------------
241Histogram parameters
242--------------------
243
244This table describes the dictionaries supplied to :func:`G2PwdrData.set_refinements`
245and :func:`G2PwdrData.clear_refinements`. As an example,
246
247.. code-block::  python
248
249   hist.set_refinements({"Background": {"no.coeffs": 3, "refine": True},
250                         "Sample Parameters": ["Scale"],
251                         "Limits": [10000, 40000]})
252
253With :meth:`G2Project.do_refinements`, these parameters should be placed inside a dict with a key
254``set``, ``clear``, or ``once``. Values will be set for all histograms, unless the ``histograms``
255key is used to define specific histograms. As an example:
256
257.. code-block::  python
258
259  gsas_proj.do_refinements([{
260      'set': {
261          'Background': {'no.coeffs': 3, 'refine': True},
262          'Sample Parameters': ['Scale'],
263          'Limits': [10000, 40000]},
264      'histograms': [1,2]
265                            }])
266
267Note that below in Instrument Parameters,
268related profile parameters (such as U and V) are listed
269separated by commas to save space in the table.
270
271.. tabularcolumns:: |l|l|p{3.5in}|
272
273===================== ====================  =================================================
274key                   subkey                explanation
275===================== ====================  =================================================
276Limits                                      The range of 2-theta (degrees) or TOF (in
277                                            microsec) range of values to use. Can
278                                            be either a dictionary of 'low' and/or 'high',
279                                            or a list of 2 items [low, high]
280\                     low                   Sets the low limit
281\                     high                  Sets the high limit
282
283Sample Parameters                           Should be provided as a **list** of subkeys
284                                            to set or clear, e.g. ['DisplaceX', 'Scale']
285\                     Absorption
286\                     Contrast
287\                     DisplaceX             Sample displacement along the X direction
288\                     DisplaceY             Sample displacement along the Y direction
289\                     Scale                 Histogram Scale factor
290
291Background                                  Sample background. If value is a boolean,
292                                            the background's 'refine' parameter is set
293                                            to the given boolean. Usually should be a
294                                            dictionary with any of the following keys:
295\                     type                  The background model, e.g. 'chebyschev'
296\                     refine                The value of the refine flag, boolean
297\                     no. coeffs            Number of coefficients to use, integer
298\                     coeffs                List of floats, literal values for background
299\                     FixedPoints           List of (2-theta, intensity) values for fixed points
300\                     fit fixed points      If True, triggers a fit to the fixed points to
301                                            be calculated. It is calculated when this key is
302                                            detected, regardless of calls to refine.
303
304Instrument Parameters                       As in Sample Paramters, provide as a **list** of
305                                            subkeys to
306                                            set or clear, e.g. ['X', 'Y', 'Zero', 'SH/L']
307\                     U, V, W               Gaussian peak profile terms
308\                     X, Y, Z               Lorentzian peak profile terms
309\                     alpha, beta-0,        TOF profile terms
310                      beta-1, beta-q,
311\                     sig-0, sig-1,         TOF profile terms
312                      sig-2, sig-q
313\                     difA, difB, difC      TOF Calibration constants
314\                     Zero                  Zero shift
315\                     SH/L                  Finger-Cox-Jephcoat low-angle peak asymmetry
316\                     Polariz.              Polarization parameter
317\                     Lam                   Lambda, the incident wavelength
318===================== ====================  =================================================
319
320.. _Phase_parameters_table:
321
322----------------
323Phase parameters
324----------------
325
326This table describes the dictionaries supplied to :func:`G2Phase.set_refinements`
327and :func:`G2Phase.clear_refinements`. With :meth:`G2Project.do_refinements`,
328these parameters should be placed inside a dict with a key
329``set``, ``clear``, or ``once``. Values will be set for all phases, unless the ``phases``
330key is used to define specific phase(s).
331
332
333.. tabularcolumns:: |l|p{4.5in}|
334
335======= ==========================================================
336key                   explanation
337======= ==========================================================
338Cell                  Whether or not to refine the unit cell.
339Atoms                 Dictionary of atoms and refinement flags.
340                      Each key should be an atom label, e.g.
341                      'O3', 'Mn5', and each value should be
342                      a string defining what values to refine.
343                      Values can be any combination of 'F'
344                      for fractional occupancy, 'X' for position,
345                      and 'U' for Debye-Waller factor
346LeBail                Enables LeBail intensity extraction.
347======= ==========================================================
348
349
350.. _HAP_parameters_table:
351
352------------------------------
353Histogram-and-phase parameters
354------------------------------
355
356This table describes the dictionaries supplied to :func:`G2Phase.set_HAP_refinements`
357and :func:`G2Phase.clear_HAP_refinements`. When supplied to
358:meth:`G2Project.do_refinements`, these parameters should be placed inside a dict with a key
359``set``, ``clear``, or ``once``. Values will be set for all histograms used in each phase,
360unless the ``histograms`` and ``phases`` keys are used to define specific phases and histograms.
361
362.. tabularcolumns:: |l|l|p{3.5in}|
363
364=============  ==========  ============================================================
365key             subkey                 explanation
366=============  ==========  ============================================================
367Babinet                                Should be a **list** of the following
368                                       subkeys. If not, assumes both
369                                       BabA and BabU
370\               BabA
371\               BabU
372Extinction                             Boolean, True to refine.
373HStrain                                Boolean, True to refine all appropriate
374                                       $D_ij$ terms.
375Mustrain
376\               type                   Mustrain model. One of 'isotropic',
377                                       'uniaxial', or 'generalized'. Should always
378                                       be specified.
379\              direction               For uniaxial only. A list of three
380                                       integers,
381                                       the [hkl] direction of the axis.
382\               refine                 Usually boolean, set to True to refine.
383                                       When in doubt, set it to true.
384                                       For uniaxial model, can specify list
385                                       of 'axial' or 'equatorial' or a single
386                                       boolean sets both axial and equatorial.
387Size                                   Not yet implemented
388\               type                   Size broadening model. One of 'isotropic',
389                                       'uniaxial', or 'ellipsoid'. Should always
390                                       be specified.
391\              direction               For uniaxial only. A list of three
392                                       integers,
393                                       the [hkl] direction of the axis.
394\               refine                 Boolean, True to refine.
395Pref.Ori.                              Boolean, True to refine
396Show                                   Boolean, True to refine
397Use                                    Boolean, True to refine
398Scale                                  Phase fraction; Boolean, True to refine
399=============  ==========  ============================================================
400
401------------------------
402Histogram/Phase objects
403------------------------
404Each phase and powder histogram in a :class:`G2Project` object has an associated
405object. Parameters within each individual object can be turned on and off by calling
406:meth:`G2PwdrData.set_refinements` or :meth:`G2PwdrData.clear_refinements`
407for histogram parameters;
408:meth:`G2Phase.set_refinements` or :meth:`G2Phase.clear_refinements`
409for phase parameters; and :meth:`G2Phase.set_HAP_refinements` or
410:meth:`G2Phase.clear_HAP_refinements`. As an example, if some_histogram is a histogram object (of type :class:`G2PwdrData`), use this to set parameters in that histogram:
411
412.. code-block::  python
413
414    params = { 'Limits': [0.8, 12.0],
415               'Sample Parameters': ['Absorption', 'Contrast', 'DisplaceX'],
416               'Background': {'type': 'chebyschev', 'refine': True}}
417    some_histogram.set_refinements(params)
418
419Likewise to turn refinement flags on, use code such as this:
420
421.. code-block::  python
422
423    params = { 'Instrument Parameters': ['U', 'V', 'W']}
424    some_histogram.set_refinements(params)
425
426and to turn these refinement flags, off use this (Note that the
427``.clear_refinements()`` methods will usually will turn off refinement even
428if a refinement parameter is set in the dict to True.):
429
430.. code-block::  python
431
432    params = { 'Instrument Parameters': ['U', 'V', 'W']}
433    some_histogram.clear_refinements(params)
434
435For phase parameters, use code such as this:
436   
437.. code-block::  python
438
439    params = { 'LeBail': True, 'Cell': True,
440               'Atoms': { 'Mn1': 'X',
441                          'O3': 'XU',
442                          'V4': 'FXU'}}
443    some_histogram.set_refinements(params)
444
445
446and here is an example for HAP parameters:
447
448.. code-block::  python
449
450    params = { 'Babinet': 'BabA',
451               'Extinction': True,
452               'Mustrain': { 'type': 'uniaxial',
453                             'direction': [0, 0, 1],
454                             'refine': True}}
455    some_phase.set_HAP_refinements(params)
456
457Note that the parameters must match the object type and method (phase vs. histogram vs. HAP).
458
459.. _CommandlineInterface:
460
461=======================================
462GSASIIscriptable Command-line Interface
463=======================================
464
465The routines described above are intended to be called from a Python script, but an
466alternate way to access some of the same functionality is to
467invoke the ``GSASIIscriptable.py`` script from
468the command line usually from within a shell script or batch file. This
469will usually be done with a command such as::
470
471       python <path/>GSASIIscriptable.py <subcommand> <file.gpx> <options>
472
473    The following subcommands are defined:
474
475        * create, see :func:`create`
476        * add, see :func:`add`
477        * dump, see :func:`dump`
478        * refine, see :func:`refine`
479        * seqrefine, see :func:`seqrefine`
480        * export, :func:`export`
481        * browse, see :func:`IPyBrowse`
482       
483Run::
484
485   python GSASIIscriptable.py --help
486
487to show the available subcommands, and inspect each subcommand with
488`python GSASIIscriptable.py <subcommand> --help` or see the documentation for each of the above routines.
489
490.. _JsonFormat:
491
492-------------------------
493Parameters in JSON files
494-------------------------
495
496The refine command requires two inputs: an existing GSAS-II project (.gpx) file and
497a JSON format file
498(see `Introducing JSON <http://json.org/>`_) that contains a single dict.
499This dict may have two keys:
500
501refinements:
502  This defines the a set of refinement steps in a JSON representation of a
503  :ref:`Refinement_recipe` list.
504
505code:
506  This optionally defines Python code that will be executed after the project is loaded,
507  but before the refinement is started. This can be used to execute Python code to change
508  parameters that are not accessible via a :ref:`Refinement_recipe` dict (note that the
509  project object is accessed with variable ``proj``) or to define code that will be called
510  later (see key ``call`` in the :ref:`Refinement_recipe` section.)
511   
512JSON website: `Introducing JSON <http://json.org/>`_.
513
514.. _API:
515
516============================================================
517GSASIIscriptable Application Layer
518============================================================
519
520This module provides a large number of classes and modules, as described below.
521Most commonly a script will create a G2Project object using :class:`G2Project` and then
522perform actions such as
523adding a histogram (method :meth:`G2Project.add_powder_histogram`),
524adding a phase (method :meth:`G2Project.add_phase`),
525or setting parameters and performing a refinement
526(method :meth:`G2Project.do_refinements`).
527
528In some cases, it may be easier or more options may be available by direct access to
529methods inside :class:`G2PwdrData` or :class:`G2Phase`
530
531---------------------------------------------------------------
532Complete Documentation: GSASIIscriptable Classes and functions
533---------------------------------------------------------------
534"""
535from __future__ import division, print_function
536import argparse
537import os.path as ospath
538import datetime as dt
539import sys
540import platform
541if '2' in platform.python_version_tuple()[0]:
542    import cPickle
543    strtypes = (str,unicode)
544else:
545    import _pickle as cPickle
546    strtypes = (str,bytes)
547import imp
548import copy
549import os
550import random as ran
551
552import numpy.ma as ma
553import scipy.interpolate as si
554import numpy as np
555import scipy as sp
556
557import GSASIIpath
558GSASIIpath.SetBinaryPath(True)  # for now, this is needed before some of these modules can be imported
559import GSASIIobj as G2obj
560import GSASIIpwd as G2pwd
561import GSASIIstrMain as G2strMain
562import GSASIIspc as G2spc
563import GSASIIElem as G2elem
564
565
566# Delay imports to not slow down small scripts
567G2fil = None
568PwdrDataReaders = []
569PhaseReaders = []
570exportersByExtension = {}
571'''Specifies the list of extensions that are supported for Powder data export'''
572
573def LoadG2fil():
574    """Delay importing this module, it is slow"""
575    global G2fil
576    if G2fil is None:
577        import GSASIIfiles
578        G2fil = GSASIIfiles
579        global PwdrDataReaders
580        global PhaseReaders
581        PwdrDataReaders = G2fil.LoadImportRoutines("pwd", "Powder_Data")
582        PhaseReaders = G2fil.LoadImportRoutines("phase", "Phase")
583        AllExporters = G2fil.LoadExportRoutines(None)
584        global exportersByExtension
585        exportersByExtension = {}
586        for obj in AllExporters:
587            try:
588                obj.Writer
589            except AttributeError:
590                continue
591            for typ in obj.exporttype:
592                if typ not in exportersByExtension:
593                    exportersByExtension[typ] = {obj.extension:obj}
594                else:
595                    exportersByExtension[typ][obj.extension] = obj
596
597def LoadDictFromProjFile(ProjFile):
598    '''Read a GSAS-II project file and load items to dictionary
599   
600    :param str ProjFile: GSAS-II project (name.gpx) full file name
601    :returns: Project,nameList, where
602
603      * Project (dict) is a representation of gpx file following the GSAS-II tree structure
604        for each item: key = tree name (e.g. 'Controls','Restraints',etc.), data is dict
605        data dict = {'data':item data whch may be list, dict or None,'subitems':subdata (if any)}
606      * nameList (list) has names of main tree entries & subentries used to reconstruct project file
607
608    Example for fap.gpx::
609
610      Project = {                 #NB:dict order is not tree order
611        u'Phases':{'data':None,'fap':{phase dict}},
612        u'PWDR FAP.XRA Bank 1':{'data':[histogram data list],'Comments':comments,'Limits':limits, etc},
613        u'Rigid bodies':{'data': {rigid body dict}},
614        u'Covariance':{'data':{covariance data dict}},
615        u'Controls':{'data':{controls data dict}},
616        u'Notebook':{'data':[notebook list]},
617        u'Restraints':{'data':{restraint data dict}},
618        u'Constraints':{'data':{constraint data dict}}]
619        }
620      nameList = [                #NB: reproduces tree order
621        [u'Notebook',],
622        [u'Controls',],
623        [u'Covariance',],
624        [u'Constraints',],
625        [u'Restraints',],
626        [u'Rigid bodies',],
627        [u'PWDR FAP.XRA Bank 1',
628             u'Comments',
629             u'Limits',
630             u'Background',
631             u'Instrument Parameters',
632             u'Sample Parameters',
633             u'Peak List',
634             u'Index Peak List',
635             u'Unit Cells List',
636             u'Reflection Lists'],
637        [u'Phases', u'fap']
638        ]
639    '''
640    # Let IOError be thrown if file does not exist
641    # if not ospath.exists(ProjFile):
642    #     print ('\n*** Error attempt to open project file that does not exist:\n   '+
643    #         str(ProjFile))
644    #     return
645    file = open(ProjFile,'rb')
646    # print('loading from file: {}'.format(ProjFile))
647    Project = {}
648    nameList = []
649    try:
650        while True:
651            try:
652                data = cPickle.load(file)
653            except EOFError:
654                break
655            datum = data[0]
656            Project[datum[0]] = {'data':datum[1]}
657            nameList.append([datum[0],])
658            for datus in data[1:]:
659                Project[datum[0]][datus[0]] = datus[1]
660                nameList[-1].append(datus[0])
661        file.close()
662        # print('project load successful')
663    except:
664        raise IOError("Error reading file "+str(ProjFile)+". This is not a GSAS-II .gpx file")
665    finally:
666        file.close()
667    return Project,nameList
668
669def SaveDictToProjFile(Project,nameList,ProjFile):
670    '''Save a GSAS-II project file from dictionary/nameList created by LoadDictFromProjFile
671
672    :param dict Project: representation of gpx file following the GSAS-II
673        tree structure as described for LoadDictFromProjFile
674    :param list nameList: names of main tree entries & subentries used to reconstruct project file
675    :param str ProjFile: full file name for output project.gpx file (including extension)
676    '''
677    file = open(ProjFile,'wb')
678    try:
679        for name in nameList:
680            data = []
681            item = Project[name[0]]
682            data.append([name[0],item['data']])
683            for item2 in name[1:]:
684                data.append([item2,item[item2]])
685            cPickle.dump(data,file,1)
686    finally:
687        file.close()
688
689# def ImportPowder(reader,filename):
690#     '''Use a reader to import a powder diffraction data file
691
692#     :param str reader: a scriptable reader
693#     :param str filename: full name of powder data file; can be "multi-Bank" data
694
695#     :returns: list rdlist: list of reader objects containing powder data, one for each
696#         "Bank" of data encountered in file. Items in reader object of interest are:
697
698#           * rd.comments: list of str: comments found on powder file
699#           * rd.dnames: list of str: data nammes suitable for use in GSASII data tree NB: duplicated in all rd entries in rdlist
700#           * rd.powderdata: list of numpy arrays: pos,int,wt,zeros,zeros,zeros as needed for a PWDR entry in  GSASII data tree.
701#     '''
702#     rdfile,rdpath,descr = imp.find_module(reader)
703#     rdclass = imp.load_module(reader,rdfile,rdpath,descr)
704#     rd = rdclass.GSAS_ReaderClass()
705#     if not rd.scriptable:
706#         print(u'**** ERROR: '+reader+u' is not a scriptable reader')
707#         return None
708#     rdlist = []
709#     if rd.ContentsValidator(filename):
710#         repeat = True
711#         rdbuffer = {} # create temporary storage for file reader
712#         block = 0
713#         while repeat: # loop if the reader asks for another pass on the file
714#             block += 1
715#             repeat = False
716#             rd.objname = ospath.basename(filename)
717#             flag = rd.Reader(filename,None,buffer=rdbuffer,blocknum=block,)
718#             if flag:
719#                 rdlist.append(copy.deepcopy(rd)) # save the result before it is written over
720#                 if rd.repeat:
721#                     repeat = True
722#         return rdlist
723#     print(rd.errors)
724#     return None
725
726def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
727    '''Create an initial Histogram dictionary
728
729    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
730    '''
731    if dType in ['SXC','SNC']:
732        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
733            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
734            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
735            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}],
736            'Flack':[0.0,False]}
737    elif dType == 'SNT':
738        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
739            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
740            'Extinction':['Lorentzian','None', {
741            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
742    elif 'P' in dType:
743        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
744            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{},[],0.1],
745            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
746                [1.,1.,1.,0.,0.,0.],6*[False,]],
747            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
748                NShkl*[0.01,],NShkl*[False,]],
749            'HStrain':[NDij*[0.0,],NDij*[False,]],
750            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
751
752
753def PreSetup(data):
754    '''Create part of an initial (empty) phase dictionary
755
756    from GSASIIphsGUI.py, near end of UpdatePhaseData
757
758    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
759    '''
760    if 'RBModels' not in data:
761        data['RBModels'] = {}
762    if 'MCSA' not in data:
763        data['MCSA'] = {'Models':[{'Type':'MD','Coef':[1.0,False,[.8,1.2],],'axis':[0,0,1]}],'Results':[],'AtInfo':{}}
764    if 'dict' in str(type(data['MCSA']['Results'])):
765        data['MCSA']['Results'] = []
766    if 'Modulated' not in data['General']:
767        data['General']['Modulated'] = False
768#    if 'modulated' in data['General']['Type']:
769#        data['General']['Modulated'] = True
770#        data['General']['Type'] = 'nuclear'
771
772
773def SetupGeneral(data, dirname):
774    """Helps initialize phase data.
775
776    From GSASIIphsGui.py, function of the same name. Minor changes for imports etc.
777
778    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
779    """
780    mapDefault = {'MapType':'','RefList':'','Resolution':0.5,'Show bonds':True,
781                'rho':[],'rhoMax':0.,'mapSize':10.0,'cutOff':50.,'Flip':False}
782    generalData = data['General']
783    atomData = data['Atoms']
784    generalData['AtomTypes'] = []
785    generalData['Isotopes'] = {}
786
787    if 'Isotope' not in generalData:
788        generalData['Isotope'] = {}
789    if 'Data plot type' not in generalData:
790        generalData['Data plot type'] = 'Mustrain'
791    if 'POhkl' not in generalData:
792        generalData['POhkl'] = [0,0,1]
793    if 'Map' not in generalData:
794        generalData['Map'] = mapDefault.copy()
795    if 'Flip' not in generalData:
796        generalData['Flip'] = {'RefList':'','Resolution':0.5,'Norm element':'None',
797            'k-factor':0.1,'k-Max':20.,}
798    if 'testHKL' not in generalData['Flip']:
799        generalData['Flip']['testHKL'] = [[0,0,2],[2,0,0],[1,1,1],[0,2,0],[1,2,3]]
800    if 'doPawley' not in generalData:
801        generalData['doPawley'] = False     #ToDo: change to ''
802    if 'Pawley dmin' not in generalData:
803        generalData['Pawley dmin'] = 1.0
804    if 'Pawley neg wt' not in generalData:
805        generalData['Pawley neg wt'] = 0.0
806    if 'Algolrithm' in generalData.get('MCSA controls',{}) or \
807        'MCSA controls' not in generalData:
808        generalData['MCSA controls'] = {'Data source':'','Annealing':[50.,0.001,50],
809        'dmin':2.0,'Algorithm':'log','Jump coeff':[0.95,0.5],'boltzmann':1.0,
810        'fast parms':[1.0,1.0,1.0],'log slope':0.9,'Cycles':1,'Results':[],'newDmin':True}
811    if 'AtomPtrs' not in generalData:
812        generalData['AtomPtrs'] = [3,1,7,9]
813        if generalData['Type'] == 'macromolecular':
814            generalData['AtomPtrs'] = [6,4,10,12]
815        elif generalData['Type'] == 'magnetic':
816            generalData['AtomPtrs'] = [3,1,10,12]
817    if generalData['Modulated']:
818        generalData['Type'] = 'nuclear'
819        if 'Super' not in generalData:
820            generalData['Super'] = 1
821            generalData['SuperVec'] = [[0,0,.1],False,4]
822            generalData['SSGData'] = {}
823        if '4DmapData' not in generalData:
824            generalData['4DmapData'] = mapDefault.copy()
825            generalData['4DmapData'].update({'MapType':'Fobs'})
826    if 'Modulated' not in generalData:
827        generalData['Modulated'] = False
828    if 'HydIds' not in generalData:
829        generalData['HydIds'] = {}
830    cx,ct,cs,cia = generalData['AtomPtrs']
831    generalData['NoAtoms'] = {}
832    generalData['BondRadii'] = []
833    generalData['AngleRadii'] = []
834    generalData['vdWRadii'] = []
835    generalData['AtomMass'] = []
836    generalData['Color'] = []
837    if generalData['Type'] == 'magnetic':
838        generalData['MagDmin'] = generalData.get('MagDmin',1.0)
839        landeg = generalData.get('Lande g',[])
840    generalData['Mydir'] = dirname
841    badList = {}
842    for iat,atom in enumerate(atomData):
843        atom[ct] = atom[ct].lower().capitalize()              #force to standard form
844        if generalData['AtomTypes'].count(atom[ct]):
845            generalData['NoAtoms'][atom[ct]] += atom[cx+3]*float(atom[cs+1])
846        elif atom[ct] != 'UNK':
847            Info = G2elem.GetAtomInfo(atom[ct])
848            if not Info:
849                if atom[ct] not in badList:
850                    badList[atom[ct]] = 0
851                badList[atom[ct]] += 1
852                atom[ct] = 'UNK'
853                continue
854            atom[ct] = Info['Symbol'] # N.B. symbol might be changed by GetAtomInfo
855            generalData['AtomTypes'].append(atom[ct])
856            generalData['Z'] = Info['Z']
857            generalData['Isotopes'][atom[ct]] = Info['Isotopes']
858            generalData['BondRadii'].append(Info['Drad'])
859            generalData['AngleRadii'].append(Info['Arad'])
860            generalData['vdWRadii'].append(Info['Vdrad'])
861            if atom[ct] in generalData['Isotope']:
862                if generalData['Isotope'][atom[ct]] not in generalData['Isotopes'][atom[ct]]:
863                    isotope = list(generalData['Isotopes'][atom[ct]].keys())[-1]
864                    generalData['Isotope'][atom[ct]] = isotope
865                generalData['AtomMass'].append(Info['Isotopes'][generalData['Isotope'][atom[ct]]]['Mass'])
866            else:
867                generalData['Isotope'][atom[ct]] = 'Nat. Abund.'
868                if 'Nat. Abund.' not in generalData['Isotopes'][atom[ct]]:
869                    isotope = list(generalData['Isotopes'][atom[ct]].keys())[-1]
870                    generalData['Isotope'][atom[ct]] = isotope
871                generalData['AtomMass'].append(Info['Mass'])
872            generalData['NoAtoms'][atom[ct]] = atom[cx+3]*float(atom[cs+1])
873            generalData['Color'].append(Info['Color'])
874            if generalData['Type'] == 'magnetic':
875                if len(landeg) < len(generalData['AtomTypes']):
876                    landeg.append(2.0)
877    if generalData['Type'] == 'magnetic':
878        generalData['Lande g'] = landeg[:len(generalData['AtomTypes'])]
879
880    if badList:
881        msg = 'Warning: element symbol(s) not found:'
882        for key in badList:
883            msg += '\n\t' + key
884            if badList[key] > 1:
885                msg += ' (' + str(badList[key]) + ' times)'
886        raise G2ScriptException("Phase error:\n" + msg)
887        # wx.MessageBox(msg,caption='Element symbol error')
888    F000X = 0.
889    F000N = 0.
890    for i,elem in enumerate(generalData['AtomTypes']):
891        F000X += generalData['NoAtoms'][elem]*generalData['Z']
892        isotope = generalData['Isotope'][elem]
893        F000N += generalData['NoAtoms'][elem]*generalData['Isotopes'][elem][isotope]['SL'][0]
894    generalData['F000X'] = F000X
895    generalData['F000N'] = F000N
896    import GSASIImath as G2mth
897    generalData['Mass'] = G2mth.getMass(generalData)
898
899
900def make_empty_project(author=None, filename=None):
901    """Creates an dictionary in the style of GSASIIscriptable, for an empty
902    project.
903
904    If no author name or filename is supplied, 'no name' and
905    <current dir>/test_output.gpx are used , respectively.
906
907    Returns: project dictionary, name list
908
909    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
910    """
911    if not filename:
912        filename = 'test_output.gpx'
913    filename = os.path.abspath(filename)
914    gsasii_version = str(GSASIIpath.GetVersionNumber())
915    LoadG2fil()
916    import matplotlib as mpl
917    python_library_versions = G2fil.get_python_versions([mpl, np, sp])
918
919    controls_data = dict(G2obj.DefaultControls)
920    controls_data['LastSavedAs'] = filename
921    controls_data['LastSavedUsing'] = gsasii_version
922    controls_data['PythonVersions'] = python_library_versions
923    if author:
924        controls_data['Author'] = author
925
926    output = {'Constraints': {'data': {'HAP': [], 'Hist': [], 'Phase': [],
927                                       'Global': []}},
928              'Controls': {'data': controls_data},
929              u'Covariance': {'data': {}},
930              u'Notebook': {'data': ['']},
931              u'Restraints': {'data': {}},
932              u'Rigid bodies': {'data': {'RBIds': {'Residue': [], 'Vector': []},
933                                'Residue': {'AtInfo': {}},
934                                'Vector':  {'AtInfo': {}}}}}
935
936    names = [[u'Notebook'], [u'Controls'], [u'Covariance'],
937             [u'Constraints'], [u'Restraints'], [u'Rigid bodies']]
938
939    return output, names
940
941
942class G2ImportException(Exception):
943    pass
944
945class G2ScriptException(Exception):
946    pass
947
948def import_generic(filename, readerlist, fmthint=None):
949    """Attempt to import a filename, using a list of reader objects.
950
951    Returns the first reader object which worked."""
952    # Translated from OnImportGeneric method in GSASII.py
953    primaryReaders, secondaryReaders = [], []
954    for reader in readerlist:
955        if fmthint is not None and fmthint not in reader.formatName: continue
956        flag = reader.ExtensionValidator(filename)
957        if flag is None:
958            secondaryReaders.append(reader)
959        elif flag:
960            primaryReaders.append(reader)
961    if not secondaryReaders and not primaryReaders:
962        raise G2ImportException("Could not read file: ", filename)
963
964    with open(filename, 'Ur') as fp:
965        rd_list = []
966
967        for rd in primaryReaders + secondaryReaders:
968            # Initialize reader
969            rd.selections = []
970            rd.dnames = []
971            rd.ReInitialize()
972            # Rewind file
973            rd.errors = ""
974            if not rd.ContentsValidator(filename):
975                # Report error
976                pass
977            if len(rd.selections) > 1:
978                # Select data?
979                # GSASII.py:543
980                raise G2ImportException("Not sure what data to select")
981
982            block = 0
983            rdbuffer = {}
984            repeat = True
985            while repeat:
986                repeat = False
987                block += 1
988                rd.objname = os.path.basename(filename)
989                try:
990                    flag = rd.Reader(filename,buffer=rdbuffer, blocknum=block)
991                except:
992                    flag = False
993                if flag:
994                    # Omitting image loading special cases
995                    rd.readfilename = filename
996                    rd_list.append(copy.deepcopy(rd))
997                    repeat = rd.repeat
998                else:
999                    if GSASIIpath.GetConfigValue('debug'): print("{} Reader failed to read {}".format(rd.formatName,filename))
1000            if rd_list:
1001                if rd.warnings:
1002                    print("Read warning by", rd.formatName, "reader:",
1003                          rd.warnings, file=sys.stderr)
1004                else:
1005                    print("{} read by Reader {}\n".format(filename,rd.formatName))                   
1006                return rd_list
1007    raise G2ImportException("No reader could read file: " + filename)
1008
1009
1010def load_iprms(instfile, reader):
1011    """Loads instrument parameters from a file, and edits the
1012    given reader.
1013
1014    Returns a 2-tuple of (Iparm1, Iparm2) parameters
1015    """
1016    LoadG2fil()
1017    ext = os.path.splitext(instfile)[1]
1018
1019    if ext.lower() == '.instprm':
1020        # New GSAS File, load appropriately
1021        with open(instfile) as f:
1022            lines = f.readlines()
1023        bank = reader.powderentry[2]
1024        numbanks = reader.numbanks
1025        iparms = G2fil.ReadPowderInstprm(lines, bank, numbanks, reader)
1026
1027        reader.instfile = instfile
1028        reader.instmsg = 'GSAS-II file' + instfile
1029        return iparms
1030    elif ext.lower() not in ('.prm', '.inst', '.ins'):
1031        raise ValueError('Expected .prm file, found: ', instfile)
1032
1033    # It's an old GSAS file, load appropriately
1034    Iparm = {}
1035    with open(instfile, 'Ur') as fp:
1036        for line in fp:
1037            if '#' in line:
1038                continue
1039            Iparm[line[:12]] = line[12:-1]
1040    ibanks = int(Iparm.get('INS   BANK  ', '1').strip())
1041    if ibanks == 1:
1042        reader.instbank = 1
1043        reader.powderentry[2] = 1
1044        reader.instfile = instfile
1045        reader.instmsg = instfile + ' bank ' + str(reader.instbank)
1046        return G2fil.SetPowderInstParms(Iparm, reader)
1047    # TODO handle >1 banks
1048    raise NotImplementedError("Check GSASIIfiles.py: ReadPowderInstprm")
1049
1050def load_pwd_from_reader(reader, instprm, existingnames=[]):
1051    """Loads powder data from a reader object, and assembles it into a GSASII data tree.
1052
1053    :returns: (name, tree) - 2-tuple of the histogram name (str), and data
1054
1055    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1056    """
1057    HistName = 'PWDR ' + G2obj.StripUnicode(reader.idstring, '_')
1058    HistName = G2obj.MakeUniqueLabel(HistName, existingnames)
1059
1060    try:
1061        Iparm1, Iparm2 = instprm
1062    except ValueError:
1063        Iparm1, Iparm2 = load_iprms(instprm, reader)
1064
1065    Ymin = np.min(reader.powderdata[1])
1066    Ymax = np.max(reader.powderdata[1])
1067    valuesdict = {'wtFactor': 1.0,
1068                  'Dummy': False,
1069                  'ranId': ran.randint(0, sys.maxsize),
1070                  'Offset': [0.0, 0.0], 'delOffset': 0.02*Ymax,
1071                  'refOffset': -0.1*Ymax, 'refDelt': 0.1*Ymax,
1072                  'qPlot': False, 'dPlot': False, 'sqrtPlot': False,
1073                  'Yminmax': [Ymin, Ymax]}
1074    reader.Sample['ranId'] = valuesdict['ranId']
1075
1076    # Ending keys:
1077    # [u'Reflection Lists',
1078    #  u'Limits',
1079    #  'data',
1080    #  u'Index Peak List',
1081    #  u'Comments',
1082    #  u'Unit Cells List',
1083    #  u'Sample Parameters',
1084    #  u'Peak List',
1085    #  u'Background',
1086    #  u'Instrument Parameters']
1087    Tmin = np.min(reader.powderdata[0])
1088    Tmax = np.max(reader.powderdata[0])
1089
1090    default_background = [['chebyschev', False, 3, 1.0, 0.0, 0.0],
1091                          {'nDebye': 0, 'debyeTerms': [], 'nPeaks': 0, 'peaksList': []}]
1092
1093    output_dict = {u'Reflection Lists': {},
1094                   u'Limits': reader.pwdparms.get('Limits', [(Tmin, Tmax), [Tmin, Tmax]]),
1095                   u'data': [valuesdict, reader.powderdata, HistName],
1096                   u'Index Peak List': [[], []],
1097                   u'Comments': reader.comments,
1098                   u'Unit Cells List': [],
1099                   u'Sample Parameters': reader.Sample,
1100                   u'Peak List': {'peaks': [], 'sigDict': {}},
1101                   u'Background': reader.pwdparms.get('Background', default_background),
1102                   u'Instrument Parameters': [Iparm1, Iparm2],
1103                   }
1104
1105    names = [u'Comments',
1106             u'Limits',
1107             u'Background',
1108             u'Instrument Parameters',
1109             u'Sample Parameters',
1110             u'Peak List',
1111             u'Index Peak List',
1112             u'Unit Cells List',
1113             u'Reflection Lists']
1114
1115    # TODO controls?? GSASII.py:1664-7
1116
1117    return HistName, [HistName] + names, output_dict
1118
1119
1120def _deep_copy_into(from_, into):
1121    """Helper function for reloading .gpx file. See G2Project.reload()
1122
1123    :author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1124    """
1125    if isinstance(from_, dict) and isinstance(into, dict):
1126        combined_keys = set(from_.keys()).union(into.keys())
1127        for key in combined_keys:
1128            if key in from_ and key in into:
1129                both_dicts = (isinstance(from_[key], dict)
1130                              and isinstance(into[key], dict))
1131                both_lists = (isinstance(from_[key], list)
1132                              and isinstance(into[key], list))
1133                if both_dicts or both_lists:
1134                    _deep_copy_into(from_[key], into[key])
1135                else:
1136                    into[key] = from_[key]
1137            elif key in from_:
1138                into[key] = from_[key]
1139            else:  # key in into
1140                del into[key]
1141    elif isinstance(from_, list) and isinstance(into, list):
1142        if len(from_) == len(into):
1143            for i in range(len(from_)):
1144                both_dicts = (isinstance(from_[i], dict)
1145                              and isinstance(into[i], dict))
1146                both_lists = (isinstance(from_[i], list)
1147                              and isinstance(into[i], list))
1148                if both_dicts or both_lists:
1149                    _deep_copy_into(from_[i], into[i])
1150                else:
1151                    into[i] = from_[i]
1152        else:
1153            into[:] = from_
1154
1155
1156class G2ObjectWrapper(object):
1157    """Base class for all GSAS-II object wrappers.
1158
1159    The underlying GSAS-II format can be accessed as `wrapper.data`. A number
1160    of overrides are implemented so that the wrapper behaves like a dictionary.
1161
1162    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1163    """
1164    def __init__(self, datadict):
1165        self.data = datadict
1166
1167    def __getitem__(self, key):
1168        return self.data[key]
1169
1170    def __setitem__(self, key, value):
1171        self.data[key] = value
1172
1173    def __contains__(self, key):
1174        return key in self.data
1175
1176    def get(self, k, d=None):
1177        return self.data.get(k, d)
1178
1179    def keys(self):
1180        return self.data.keys()
1181
1182    def values(self):
1183        return self.data.values()
1184
1185    def items(self):
1186        return self.data.items()
1187
1188
1189class G2Project(G2ObjectWrapper):
1190    """
1191    Represents an entire GSAS-II project.
1192
1193    There are two ways to initialize it:
1194
1195    >>> # Load an existing project file
1196    >>> proj = G2Project('filename.gpx')
1197   
1198    >>> # Create a new project
1199    >>> proj = G2Project(newgpx='new_file.gpx')
1200   
1201    Histograms can be accessed easily.
1202
1203    >>> # By name
1204    >>> hist = proj.histogram('PWDR my-histogram-name')
1205   
1206    >>> # Or by index
1207    >>> hist = proj.histogram(0)
1208    >>> assert hist.id == 0
1209   
1210    >>> # Or by random id
1211    >>> assert hist == proj.histogram(hist.ranId)
1212
1213    Phases can be accessed the same way.
1214
1215    >>> phase = proj.phase('name of phase')
1216
1217    New data can also be loaded via :meth:`~G2Project.add_phase` and
1218    :meth:`~G2Project.add_powder_histogram`.
1219
1220    >>> hist = proj.add_powder_histogram('some_data_file.chi',
1221                                         'instrument_parameters.prm')
1222    >>> phase = proj.add_phase('my_phase.cif', histograms=[hist])
1223
1224    Parameters for Rietveld refinement can be turned on and off as well.
1225    See :meth:`~G2Project.set_refinement`, :meth:`~G2Project.clear_refinements`,
1226    :meth:`~G2Project.iter_refinements`, :meth:`~G2Project.do_refinements`.
1227    """
1228    def __init__(self, gpxfile=None, author=None, filename=None, newgpx=None):
1229        """Loads a GSAS-II project from a specified filename.
1230
1231        :param str gpxfile: Existing .gpx file to be loaded. If nonexistent,
1232            creates an empty project.
1233        :param str author: Author's name (not yet implemented)
1234        :param str newgpx: The filename the project should be saved to in
1235            the future. If both newgpx and gpxfile are present, the project is
1236            loaded from the gpxfile, then when saved will be written to newgpx.
1237        :param str filename: Name to be used to save the project. Has same function as
1238            parameter newgpx (do not use both gpxfile and filename). Use of newgpx
1239            is preferred over filename.
1240        """
1241        if filename is not None and newgpx is not None:
1242            raise G2ScriptException('Do not use filename and newgpx together')
1243        elif newgpx is not None:
1244            filename = newgpx
1245        if gpxfile is None:
1246            filename = os.path.abspath(os.path.expanduser(filename))
1247            self.filename = filename
1248            self.data, self.names = make_empty_project(author=author, filename=filename)
1249        elif isinstance(gpxfile, str): # TODO: create replacement for isinstance that checks if path exists
1250                                       # filename is valid, etc.
1251            if isinstance(filename, str): 
1252                self.filename = os.path.abspath(os.path.expanduser(filename)) # both are defined
1253            else: 
1254                self.filename = os.path.abspath(os.path.expanduser(gpxfile))
1255            # TODO set author
1256            self.data, self.names = LoadDictFromProjFile(gpxfile)
1257            self.update_ids()
1258        else:
1259            raise ValueError("Not sure what to do with gpxfile")
1260
1261    @classmethod
1262    def from_dict_and_names(cls, gpxdict, names, filename=None):
1263        """Creates a :class:`G2Project` directly from
1264        a dictionary and a list of names. If in doubt, do not use this.
1265
1266        :returns: a :class:`G2Project`
1267        """
1268        out = cls()
1269        if filename:
1270            filename = os.path.abspath(os.path.expanduser(filename))
1271            out.filename = filename
1272            gpxdict['Controls']['data']['LastSavedAs'] = filename
1273        else:
1274            try:
1275                out.filename = gpxdict['Controls']['data']['LastSavedAs']
1276            except KeyError:
1277                out.filename = None
1278        out.data = gpxdict
1279        out.names = names
1280
1281    def save(self, filename=None):
1282        """Saves the project, either to the current filename, or to a new file.
1283
1284        Updates self.filename if a new filename provided"""
1285        # TODO update LastSavedUsing ?
1286        if filename:
1287            filename = os.path.abspath(os.path.expanduser(filename))
1288            self.data['Controls']['data']['LastSavedAs'] = filename
1289            self.filename = filename
1290        elif not self.filename:
1291            raise AttributeError("No file name to save to")
1292        SaveDictToProjFile(self.data, self.names, self.filename)
1293
1294    def add_powder_histogram(self, datafile, iparams, phases=[], fmthint=None):
1295        """Loads a powder data histogram into the project.
1296
1297        Automatically checks for an instrument parameter file, or one can be
1298        provided. Note that in unix fashion, "~" can be used to indicate the
1299        home directory (e.g. ~/G2data/data.fxye).
1300
1301        :param str datafile: The powder data file to read, a filename.
1302        :param str iparams: The instrument parameters file, a filename.
1303        :param list phases: Phases to link to the new histogram
1304        :param str fmthint: If specified, only importers where the format name
1305          (reader.formatName, as shown in Import menu) contains the
1306          supplied string will be tried as importers. If not specified, all
1307          importers consistent with the file extension will be tried
1308          (equivalent to "guess format" in menu).
1309
1310        :returns: A :class:`G2PwdrData` object representing
1311            the histogram
1312        """
1313        LoadG2fil()
1314        datafile = os.path.abspath(os.path.expanduser(datafile))
1315        iparams = os.path.abspath(os.path.expanduser(iparams))
1316        pwdrreaders = import_generic(datafile, PwdrDataReaders,fmthint=fmthint)
1317        histname, new_names, pwdrdata = load_pwd_from_reader(
1318                                          pwdrreaders[0], iparams,
1319                                          [h.name for h in self.histograms()])
1320        if histname in self.data:
1321            print("Warning - redefining histogram", histname)
1322        elif self.names[-1][0] == 'Phases':
1323            self.names.insert(-1, new_names)
1324        else:
1325            self.names.append(new_names)
1326        self.data[histname] = pwdrdata
1327        self.update_ids()
1328
1329        for phase in phases:
1330            phase = self.phase(phase)
1331            self.link_histogram_phase(histname, phase)
1332
1333        return self.histogram(histname)
1334
1335    def add_simulated_powder_histogram(self, histname, iparams, Tmin, Tmax, Tstep,
1336                                       wavelength=None, scale=None, phases=[]):
1337        """Loads a powder data histogram into the project.
1338
1339        Requires an instrument parameter file.
1340        Note that in unix fashion, "~" can be used to indicate the
1341        home directory (e.g. ~/G2data/data.prm). The instrument parameter file
1342        will determine if the histogram is x-ray, CW neutron, TOF, etc. as well
1343        as the instrument type.
1344
1345        :param str histname: A name for the histogram to be created.
1346        :param str iparams: The instrument parameters file, a filename.
1347        :param float Tmin: Minimum 2theta or TOF (ms) for dataset to be simulated
1348        :param float Tmax: Maximum 2theta or TOF (ms) for dataset to be simulated
1349        :param float Tstep: Step size in 2theta or TOF (ms) for dataset to be simulated       
1350        :param float wavelength: Wavelength for CW instruments, overriding the value
1351           in the instrument parameters file if specified.
1352        :param float scale: Histogram scale factor which multiplies the pattern. Note that
1353           simulated noise is added to the pattern, so that if the maximum intensity is
1354           small, the noise will mask the computed pattern. The scale
1355           needs to be a large number for CW neutrons.
1356           The default, None, provides a scale of 1 for x-rays and TOF; 10,000 for CW neutrons.
1357        :param list phases: Phases to link to the new histogram. Use proj.phases() to link to
1358           all defined phases.
1359
1360        :returns: A :class:`G2PwdrData` object representing the histogram
1361        """
1362        LoadG2fil()
1363        iparams = os.path.abspath(os.path.expanduser(iparams))
1364        if not os.path.exists(iparams):
1365            raise G2ScriptException("File does not exist:"+iparams)
1366        rd = G2obj.ImportPowderData( # Initialize a base class reader
1367            extensionlist=tuple(),
1368            strictExtension=False,
1369            formatName = 'Simulate dataset',
1370            longFormatName = 'Compute a simulated pattern')
1371        rd.powderentry[0] = '' # no filename
1372        rd.powderentry[2] = 1 # only one bank
1373        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1374        #Iparm1, Iparm2 = load_iprms(iparams, rd)
1375        if Tmax < Tmin:
1376            Tmin,Tmax = Tmax,Tmin
1377        Tstep = abs(Tstep)
1378        if 'TOF' in rd.idstring:
1379                N = (np.log(Tmax)-np.log(Tmin))/Tstep
1380                x = np.exp((np.arange(0,N))*Tstep+np.log(Tmin*1000.))
1381                N = len(x)
1382        else:           
1383                N = int((Tmax-Tmin)/Tstep)+1
1384                x = np.linspace(Tmin,Tmax,N,True)
1385                N = len(x)
1386        if N < 3:
1387            raise G2ScriptException("Error: Range is too small or step is too large, <3 points")
1388        rd.powderdata = [
1389            np.array(x), # x-axis values
1390            np.zeros_like(x), # powder pattern intensities
1391            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1392            np.zeros_like(x), # calc. intensities (zero)
1393            np.zeros_like(x), # calc. background (zero)
1394            np.zeros_like(x), # obs-calc profiles
1395            ]
1396        Tmin = rd.powderdata[0][0]
1397        Tmax = rd.powderdata[0][-1]
1398        rd.idstring = histname
1399        histname, new_names, pwdrdata = load_pwd_from_reader(rd, iparams,
1400                                                            [h.name for h in self.histograms()])
1401        if histname in self.data:
1402            print("Warning - redefining histogram", histname)
1403        elif self.names[-1][0] == 'Phases':
1404            self.names.insert(-1, new_names)
1405        else:
1406            self.names.append(new_names)
1407        if scale is not None:
1408            pwdrdata['Sample Parameters']['Scale'][0] = scale
1409        elif pwdrdata['Instrument Parameters'][0]['Type'][0].startswith('PNC'):
1410            pwdrdata['Sample Parameters']['Scale'][0] = 10000.
1411        self.data[histname] = pwdrdata
1412        self.update_ids()
1413
1414        for phase in phases:
1415            phase = self.phase(phase)
1416            self.link_histogram_phase(histname, phase)
1417
1418        return self.histogram(histname)
1419   
1420    def add_phase(self, phasefile, phasename=None, histograms=[], fmthint=None):
1421        """Loads a phase into the project from a .cif file
1422
1423        :param str phasefile: The CIF file from which to import the phase.
1424        :param str phasename: The name of the new phase, or None for the default
1425        :param list histograms: The names of the histograms to associate with
1426            this phase. Use proj.Histograms() to add to all histograms.
1427        :param str fmthint: If specified, only importers where the format name
1428          (reader.formatName, as shown in Import menu) contains the
1429          supplied string will be tried as importers. If not specified, all
1430          importers consistent with the file extension will be tried
1431          (equivalent to "guess format" in menu).
1432
1433        :returns: A :class:`G2Phase` object representing the
1434            new phase.
1435        """
1436        LoadG2fil()
1437        histograms = [self.histogram(h).name for h in histograms]
1438        phasefile = os.path.abspath(os.path.expanduser(phasefile))
1439
1440        # TODO handle multiple phases in a file
1441        phasereaders = import_generic(phasefile, PhaseReaders, fmthint=fmthint)
1442        phasereader = phasereaders[0]
1443       
1444        phasename = phasename or phasereader.Phase['General']['Name']
1445        phaseNameList = [p.name for p in self.phases()]
1446        phasename = G2obj.MakeUniqueLabel(phasename, phaseNameList)
1447        phasereader.Phase['General']['Name'] = phasename
1448
1449        if 'Phases' not in self.data:
1450            self.data[u'Phases'] = { 'data': None }
1451        assert phasename not in self.data['Phases'], "phase names should be unique"
1452        self.data['Phases'][phasename] = phasereader.Phase
1453
1454        if phasereader.Constraints:
1455            Constraints = self.data['Constraints']
1456            for i in phasereader.Constraints:
1457                if isinstance(i, dict):
1458                    if '_Explain' not in Constraints:
1459                        Constraints['_Explain'] = {}
1460                    Constraints['_Explain'].update(i)
1461                else:
1462                    Constraints['Phase'].append(i)
1463
1464        data = self.data['Phases'][phasename]
1465        generalData = data['General']
1466        SGData = generalData['SGData']
1467        NShkl = len(G2spc.MustrainNames(SGData))
1468        NDij = len(G2spc.HStrainNames(SGData))
1469        Super = generalData.get('Super', 0)
1470        if Super:
1471            SuperVec = np.array(generalData['SuperVec'][0])
1472        else:
1473            SuperVec = []
1474        UseList = data['Histograms']
1475
1476        for hist in histograms:
1477            self.link_histogram_phase(hist, phasename)
1478
1479        for obj in self.names:
1480            if obj[0] == 'Phases':
1481                phasenames = obj
1482                break
1483        else:
1484            phasenames = [u'Phases']
1485            self.names.append(phasenames)
1486        phasenames.append(phasename)
1487
1488        # TODO should it be self.filename, not phasefile?
1489        SetupGeneral(data, os.path.dirname(phasefile))
1490        self.index_ids()
1491
1492        self.update_ids()
1493        return self.phase(phasename)
1494
1495    def link_histogram_phase(self, histogram, phase):
1496        """Associates a given histogram and phase.
1497
1498        .. seealso::
1499
1500            :meth:`G2Project.histogram`
1501            :meth:`G2Project.phase`"""
1502        hist = self.histogram(histogram)
1503        phase = self.phase(phase)
1504
1505        generalData = phase['General']
1506
1507        if hist.name.startswith('HKLF '):
1508            raise NotImplementedError("HKLF not yet supported")
1509        elif hist.name.startswith('PWDR '):
1510            hist['Reflection Lists'][generalData['Name']] = {}
1511            UseList = phase['Histograms']
1512            SGData = generalData['SGData']
1513            NShkl = len(G2spc.MustrainNames(SGData))
1514            NDij = len(G2spc.HStrainNames(SGData))
1515            UseList[hist.name] = SetDefaultDData('PWDR', hist.name, NShkl=NShkl, NDij=NDij)
1516            UseList[hist.name]['hId'] = hist.id
1517            for key, val in [('Use', True), ('LeBail', False),
1518                             ('newLeBail', True),
1519                             ('Babinet', {'BabA': [0.0, False],
1520                                          'BabU': [0.0, False]})]:
1521                if key not in UseList[hist.name]:
1522                    UseList[hist.name][key] = val
1523        else:
1524            raise RuntimeError("Unexpected histogram" + hist.name)
1525
1526
1527    def reload(self):
1528        """Reload self from self.filename"""
1529        data, names = LoadDictFromProjFile(self.filename)
1530        self.names = names
1531        # Need to deep copy the new data file data into the current tree,
1532        # so that any existing G2Phase, or G2PwdrData objects will still be
1533        # valid
1534        _deep_copy_into(from_=data, into=self.data)
1535
1536    def refine(self, newfile=None, printFile=None, makeBack=False):
1537        # TODO migrate to RefineCore
1538        # G2strMain.RefineCore(Controls,Histograms,Phases,restraintDict,rigidbodyDict,parmDict,varyList,
1539        #      calcControls,pawleyLookup,ifPrint,printFile,dlg)
1540        # index_ids will automatically save the project
1541        self.index_ids()
1542        # TODO G2strMain does not properly use printFile
1543        G2strMain.Refine(self.filename, makeBack=makeBack)
1544        # Reload yourself
1545        self.reload()
1546
1547    def histogram(self, histname):
1548        """Returns the histogram named histname, or None if it does not exist.
1549
1550        :param histname: The name of the histogram (str), or ranId or index.
1551        :returns: A :class:`G2PwdrData` object, or None if
1552            the histogram does not exist
1553
1554        .. seealso::
1555            :meth:`G2Project.histograms`
1556            :meth:`G2Project.phase`
1557            :meth:`G2Project.phases`
1558            """
1559        if isinstance(histname, G2PwdrData):
1560            if histname.proj == self:
1561                return histname
1562        if histname in self.data:
1563            return G2PwdrData(self.data[histname], self)
1564        try:
1565            # see if histname is an id or ranId
1566            histname = int(histname)
1567        except ValueError:
1568            return
1569
1570        for histogram in self.histograms():
1571            if histogram.id == histname or histogram.ranId == histname:
1572                return histogram
1573
1574    def histograms(self):
1575        """Return a list of all histograms, as
1576        :class:`G2PwdrData` objects
1577
1578        .. seealso::
1579            :meth:`G2Project.histograms`
1580            :meth:`G2Project.phase`
1581            :meth:`G2Project.phases`
1582            """
1583        output = []
1584        for obj in self.names:
1585            if len(obj) > 1 and obj[0] != u'Phases':
1586                output.append(self.histogram(obj[0]))
1587        return output
1588
1589    def phase(self, phasename):
1590        """
1591        Gives an object representing the specified phase in this project.
1592
1593        :param str phasename: A reference to the desired phase. Either the phase
1594            name (str), the phase's ranId, the phase's index (both int) or
1595            a phase object (:class:`G2Phase`)
1596        :returns: A :class:`G2Phase` object
1597        :raises: KeyError
1598
1599        .. seealso::
1600            :meth:`G2Project.histograms`
1601            :meth:`G2Project.phase`
1602            :meth:`G2Project.phases`
1603            """
1604        if isinstance(phasename, G2Phase):
1605            if phasename.proj == self:
1606                return phasename
1607        phases = self.data['Phases']
1608        if phasename in phases:
1609            return G2Phase(phases[phasename], phasename, self)
1610
1611        try:
1612            # phasename should be phase index or ranId
1613            phasename = int(phasename)
1614        except ValueError:
1615            return
1616
1617        for phase in self.phases():
1618            if phase.id == phasename or phase.ranId == phasename:
1619                return phase
1620
1621    def phases(self):
1622        """
1623        Returns a list of all the phases in the project.
1624
1625        :returns: A list of :class:`G2Phase` objects
1626
1627        .. seealso::
1628            :meth:`G2Project.histogram`
1629            :meth:`G2Project.histograms`
1630            :meth:`G2Project.phase`
1631            """
1632        for obj in self.names:
1633            if obj[0] == 'Phases':
1634                return [self.phase(p) for p in obj[1:]]
1635        return []
1636
1637    def update_ids(self):
1638        """Makes sure all phases and histograms have proper hId and pId"""
1639        # Translated from GetUsedHistogramsAndPhasesfromTree,
1640        #   GSASIIdataGUI.py:4107
1641        for i, h in enumerate(self.histograms()):
1642            h.id = i
1643        for i, p in enumerate(self.phases()):
1644            p.id = i
1645
1646    def do_refinements(self, refinements, histogram='all', phase='all',
1647                       outputnames=None, makeBack=False):
1648        """Conducts one or a series of refinements according to the
1649           input provided in parameter refinements. This is a wrapper
1650           around :meth:`iter_refinements`
1651
1652        :param list refinements: A list of dictionaries specifiying changes to be made to
1653            parameters before refinements are conducted.
1654            See the :ref:`Refinement_recipe` section for how this is defined.
1655        :param str histogram: Name of histogram for refinements to be applied
1656            to, or 'all'; note that this can be overridden for each refinement
1657            step via a "histograms" entry in the dict.
1658        :param str phase: Name of phase for refinements to be applied to, or
1659            'all'; note that this can be overridden for each refinement
1660            step via a "phases" entry in the dict.
1661        :param list outputnames: Provides a list of project (.gpx) file names
1662            to use for each refinement step (specifying None skips the save step).
1663            See :meth:`save`.
1664            Note that this can be overridden using an "output" entry in the dict.
1665        :param bool makeBack: determines if a backup ).bckX.gpx) file is made
1666            before a refinement is performed. The default is False.
1667           
1668        To perform a single refinement without changing any parameters, use this
1669        call:
1670
1671        .. code-block::  python
1672       
1673            my_project.do_refinements([])
1674        """
1675       
1676        for proj in self.iter_refinements(refinements, histogram, phase,
1677                                          outputnames, makeBack):
1678            pass
1679        return self
1680
1681    def iter_refinements(self, refinements, histogram='all', phase='all',
1682                         outputnames=None, makeBack=False):
1683        """Conducts a series of refinements, iteratively. Stops after every
1684        refinement and yields this project, to allow error checking or
1685        logging of intermediate results. Parameter use is the same as for
1686        :meth:`do_refinements` (which calls this method).
1687
1688        >>> def checked_refinements(proj):
1689        ...     for p in proj.iter_refinements(refs):
1690        ...         # Track intermediate results
1691        ...         log(p.histogram('0').residuals)
1692        ...         log(p.phase('0').get_cell())
1693        ...         # Check if parameter diverged, nonsense answer, or whatever
1694        ...         if is_something_wrong(p):
1695        ...             raise Exception("I need a human!")
1696
1697           
1698        """
1699        if outputnames:
1700            if len(refinements) != len(outputnames):
1701                raise ValueError("Should have same number of outputs to"
1702                                 "refinements")
1703        else:
1704            outputnames = [None for r in refinements]
1705
1706        for output, refinedict in zip(outputnames, refinements):
1707            if 'histograms' in refinedict:
1708                hist = refinedict['histograms']
1709            else:
1710                hist = histogram
1711            if 'phases' in refinedict:
1712                ph = refinedict['phases']
1713            else:
1714                ph = phase
1715            if 'output' in refinedict:
1716                output = refinedict['output']
1717            self.set_refinement(refinedict, hist, ph)
1718            # Handle 'once' args - refinements that are disabled after this
1719            # refinement
1720            if 'once' in refinedict:
1721                temp = {'set': refinedict['once']}
1722                self.set_refinement(temp, hist, ph)
1723
1724            if output:
1725                self.save(output)
1726
1727            if 'skip' not in refinedict:
1728                self.refine(makeBack=makeBack)
1729            yield self
1730
1731            # Handle 'once' args - refinements that are disabled after this
1732            # refinement
1733            if 'once' in refinedict:
1734                temp = {'clear': refinedict['once']}
1735                self.set_refinement(temp, hist, ph)
1736            if 'call' in refinedict:
1737                fxn = refinedict['call']
1738                if callable(fxn):
1739                    fxn(*refinedict.get('callargs',[self]))
1740                elif callable(eval(fxn)):
1741                    eval(fxn)(*refinedict.get('callargs',[self]))
1742                else:
1743                    raise G2ScriptException("Error: call value {} is not callable".format(fxn))
1744
1745    def set_refinement(self, refinement, histogram='all', phase='all'):
1746        """Apply specified refinements to a given histogram(s) or phase(s).
1747
1748        :param dict refinement: The refinements to be conducted
1749        :param histogram: Specifies either 'all' (default), a single histogram or
1750          a list of histograms. Histograms may be specified as histogram objects
1751          (see :class:`G2PwdrData`), the histogram name (str) or the index number (int)
1752          of the histogram in the project, numbered starting from 0.
1753          Omitting the parameter or the string 'all' indicates that parameters in
1754          all histograms should be set.
1755        :param phase: Specifies either 'all' (default), a single phase or
1756          a list of phases. Phases may be specified as phase objects
1757          (see :class:`G2Phase`), the phase name (str) or the index number (int)
1758          of the phase in the project, numbered starting from 0.
1759          Omitting the parameter or the string 'all' indicates that parameters in
1760          all phases should be set.
1761
1762        Note that refinement parameters are categorized as one of three types:
1763
1764        1. Histogram parameters
1765        2. Phase parameters
1766        3. Histogram-and-Phase (HAP) parameters
1767       
1768        .. seealso::
1769            :meth:`G2PwdrData.set_refinements`
1770            :meth:`G2PwdrData.clear_refinements`
1771            :meth:`G2Phase.set_refinements`
1772            :meth:`G2Phase.clear_refinements`
1773            :meth:`G2Phase.set_HAP_refinements`
1774            :meth:`G2Phase.clear_HAP_refinements`"""
1775
1776        if histogram == 'all':
1777            hists = self.histograms()
1778        elif isinstance(histogram, list) or isinstance(histogram, tuple):
1779            hists = []
1780            for h in histogram:
1781                if isinstance(h, str) or isinstance(h, int):
1782                    hists.append(self.histogram(h))
1783                else:
1784                    hists.append(h)
1785        elif isinstance(histogram, str) or isinstance(histogram, int):
1786            hists = [self.histogram(histogram)]
1787        else:
1788            hists = [histogram]
1789
1790        if phase == 'all':
1791            phases = self.phases()
1792        elif isinstance(phase, list) or isinstance(phase, tuple):
1793            phases = []
1794            for ph in phase:
1795                if isinstance(ph, str) or isinstance(ph, int):
1796                    phases.append(self.phase(ph))
1797                else:
1798                    phases.append(ph)
1799        elif isinstance(phase, str) or isinstance(phase, int):
1800            phases = [self.phase(phase)]
1801        else:
1802            phases = [phase]
1803
1804        # TODO: HAP parameters:
1805        #   Babinet
1806        #   Extinction
1807        #   HStrain
1808        #   Mustrain
1809        #   Pref. Ori
1810        #   Size
1811
1812        pwdr_set = {}
1813        phase_set = {}
1814        hap_set = {}
1815        for key, val in refinement.get('set', {}).items():
1816            # Apply refinement options
1817            if G2PwdrData.is_valid_refinement_key(key):
1818                pwdr_set[key] = val
1819            elif G2Phase.is_valid_refinement_key(key):
1820                phase_set[key] = val
1821            elif G2Phase.is_valid_HAP_refinement_key(key):
1822                hap_set[key] = val
1823            else:
1824                raise ValueError("Unknown refinement key", key)
1825
1826        for hist in hists:
1827            hist.set_refinements(pwdr_set)
1828        for phase in phases:
1829            phase.set_refinements(phase_set)
1830        for phase in phases:
1831            phase.set_HAP_refinements(hap_set, hists)
1832
1833        pwdr_clear = {}
1834        phase_clear = {}
1835        hap_clear = {}
1836        for key, val in refinement.get('clear', {}).items():
1837            # Clear refinement options
1838            if G2PwdrData.is_valid_refinement_key(key):
1839                pwdr_clear[key] = val
1840            elif G2Phase.is_valid_refinement_key(key):
1841                phase_clear[key] = val
1842            elif G2Phase.is_valid_HAP_refinement_key(key):
1843                hap_set[key] = val
1844            else:
1845                raise ValueError("Unknown refinement key", key)
1846
1847        for hist in hists:
1848            hist.clear_refinements(pwdr_clear)
1849        for phase in phases:
1850            phase.clear_refinements(phase_clear)
1851        for phase in phases:
1852            phase.clear_HAP_refinements(hap_clear, hists)
1853
1854    def index_ids(self):
1855        import GSASIIstrIO as G2strIO
1856        self.save()
1857        return G2strIO.GetUsedHistogramsAndPhases(self.filename)
1858
1859    def add_constraint_raw(self, cons_scope, constr):
1860        """Adds a constraint of type consType to the project.
1861        cons_scope should be one of "Hist", "Phase", "HAP", or "Global".
1862
1863        WARNING it does not check the constraint is well-constructed"""
1864        constrs = self.data['Constraints']['data']
1865        if 'Global' not in constrs:
1866            constrs['Global'] = []
1867        constrs[cons_scope].append(constr)
1868
1869    def hold_many(self, vars, type):
1870        """Apply holds for all the variables in vars, for constraint of a given type.
1871
1872        type is passed directly to add_constraint_raw as consType
1873
1874        :param list vars: A list of variables to hold. Either :class:`GSASIIobj.G2VarObj` objects,
1875            string variable specifiers, or arguments for :meth:`make_var_obj`
1876        :param str type: A string constraint type specifier. See
1877            :class:`G2Project.add_constraint_raw`
1878
1879        """
1880        for var in vars:
1881            if isinstance(var, str):
1882                var = self.make_var_obj(var)
1883            elif not isinstance(var, G2obj.G2VarObj):
1884                var = self.make_var_obj(*var)
1885            self.add_constraint_raw(type, [[1.0, var], None, None, 'h'])
1886
1887    def make_var_obj(self, phase=None, hist=None, varname=None, atomId=None,
1888                     reloadIdx=True):
1889        """Wrapper to create a G2VarObj. Takes either a string representaiton ("p:h:name:a")
1890        or individual names of phase, histogram, varname, and atomId.
1891
1892        Automatically converts string phase, hist, or atom names into the ID required
1893        by G2VarObj."""
1894
1895        if reloadIdx:
1896            self.index_ids()
1897
1898        # If string representation, short circuit
1899        if hist is None and varname is None and atomId is None:
1900            if isinstance(phase, str) and ':' in phase:
1901                return G2obj.G2VarObj(phase)
1902
1903        # Get phase index
1904        phaseObj = None
1905        if isinstance(phase, G2Phase):
1906            phaseObj = phase
1907            phase = G2obj.PhaseRanIdLookup[phase.ranId]
1908        elif phase in self.data['Phases']:
1909            phaseObj = self.phase(phase)
1910            phaseRanId = phaseObj.ranId
1911            phase = G2obj.PhaseRanIdLookup[phaseRanId]
1912
1913        # Get histogram index
1914        if isinstance(hist, G2PwdrData):
1915            hist = G2obj.HistRanIdLookup[hist.ranId]
1916        elif hist in self.data:
1917            histRanId = self.histogram(hist).ranId
1918            hist = G2obj.HistRanIdLookup[histRanId]
1919
1920        # Get atom index (if any)
1921        if isinstance(atomId, G2AtomRecord):
1922            atomId = G2obj.AtomRanIdLookup[phase][atomId.ranId]
1923        elif phaseObj:
1924            atomObj = phaseObj.atom(atomId)
1925            if atomObj:
1926                atomRanId = atomObj.ranId
1927                atomId = G2obj.AtomRanIdLookup[phase][atomRanId]
1928
1929        return G2obj.G2VarObj(phase, hist, varname, atomId)
1930
1931
1932class G2AtomRecord(G2ObjectWrapper):
1933    """Wrapper for an atom record. Has convenient accessors via @property.
1934
1935
1936    Available accessors: label, type, refinement_flags, coordinates,
1937        occupancy, ranId, id, adp_flag, uiso
1938
1939    Example:
1940
1941    >>> atom = some_phase.atom("O3")
1942    >>> # We can access the underlying data format
1943    >>> atom.data
1944    ['O3', 'O-2', '', ... ]
1945    >>> # We can also use wrapper accessors
1946    >>> atom.coordinates
1947    (0.33, 0.15, 0.5)
1948    >>> atom.refinement_flags
1949    u'FX'
1950    >>> atom.ranId
1951    4615973324315876477
1952    >>> atom.occupancy
1953    1.0
1954    """
1955    def __init__(self, data, indices, proj):
1956        self.data = data
1957        self.cx, self.ct, self.cs, self.cia = indices
1958        self.proj = proj
1959
1960    @property
1961    def label(self):
1962        return self.data[self.ct-1]
1963
1964    @property
1965    def type(self):
1966        return self.data[self.ct]
1967
1968    @property
1969    def refinement_flags(self):
1970        return self.data[self.ct+1]
1971
1972    @refinement_flags.setter
1973    def refinement_flags(self, other):
1974        # Automatically check it is a valid refinement
1975        for c in other:
1976            if c not in ' FXU':
1977                raise ValueError("Invalid atom refinement: ", other)
1978        self.data[self.ct+1] = other
1979
1980    @property
1981    def coordinates(self):
1982        return tuple(self.data[self.cx:self.cx+3])
1983
1984    @property
1985    def occupancy(self):
1986        return self.data[self.cx+3]
1987
1988    @occupancy.setter
1989    def occupancy(self, val):
1990        self.data[self.cx+3] = float(val)
1991
1992    @property
1993    def ranId(self):
1994        return self.data[self.cia+8]
1995
1996    @property
1997    def adp_flag(self):
1998        # Either 'I' or 'A'
1999        return self.data[self.cia]
2000
2001    @property
2002    def uiso(self):
2003        if self.adp_flag == 'I':
2004            return self.data[self.cia+1]
2005        else:
2006            return self.data[self.cia+2:self.cia+8]
2007
2008    @uiso.setter
2009    def uiso(self, value):
2010        if self.adp_flag == 'I':
2011            self.data[self.cia+1] = float(value)
2012        else:
2013            assert len(value) == 6
2014            self.data[self.cia+2:self.cia+8] = [float(v) for v in value]
2015
2016
2017class G2PwdrData(G2ObjectWrapper):
2018    """Wraps a Powder Data Histogram."""
2019    def __init__(self, data, proj):
2020        self.data = data
2021        self.proj = proj
2022
2023    @staticmethod
2024    def is_valid_refinement_key(key):
2025        valid_keys = ['Limits', 'Sample Parameters', 'Background',
2026                      'Instrument Parameters']
2027        return key in valid_keys
2028
2029    @property
2030    def name(self):
2031        return self.data['data'][-1]
2032
2033    @property
2034    def ranId(self):
2035        return self.data['data'][0]['ranId']
2036
2037    @property
2038    def residuals(self):
2039        '''Provides a dictionary with with the R-factors for this histogram.
2040        Includes the weighted and unweighted profile terms (R, Rb, wR, wRb, wRmin)
2041        as well as the Bragg R-values for each phase (ph:H:Rf and ph:H:Rf^2).
2042        '''
2043        data = self.data['data'][0]
2044        return {key: data[key] for key in data
2045                if key in ['R', 'Rb', 'wR', 'wRb', 'wRmin']
2046                   or ':' in key}
2047
2048    @property
2049    def InstrumentParameters(self):
2050        '''Provides a dictionary with with the Instrument Parameters
2051        for this histogram.
2052        '''
2053        return self.data['Instrument Parameters'][0]
2054
2055    @property
2056    def SampleParameters(self):
2057        '''Provides a dictionary with with the Sample Parameters
2058        for this histogram.
2059        '''
2060        return self.data['Sample Parameters']
2061   
2062    @property
2063    def id(self):
2064        self.proj.update_ids()
2065        return self.data['data'][0]['hId']
2066
2067    @id.setter
2068    def id(self, val):
2069        self.data['data'][0]['hId'] = val
2070
2071    def fit_fixed_points(self):
2072        """Attempts to apply a background fit to the fixed points currently specified."""
2073        def SetInstParms(Inst):
2074            dataType = Inst['Type'][0]
2075            insVary = []
2076            insNames = []
2077            insVals = []
2078            for parm in Inst:
2079                insNames.append(parm)
2080                insVals.append(Inst[parm][1])
2081                if parm in ['U','V','W','X','Y','Z','SH/L','I(L2)/I(L1)','alpha',
2082                    'beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q',] and Inst[parm][2]:
2083                        Inst[parm][2] = False
2084            instDict = dict(zip(insNames, insVals))
2085            instDict['X'] = max(instDict['X'], 0.01)
2086            instDict['Y'] = max(instDict['Y'], 0.01)
2087            if 'SH/L' in instDict:
2088                instDict['SH/L'] = max(instDict['SH/L'], 0.002)
2089            return dataType, instDict, insVary
2090
2091        bgrnd = self.data['Background']
2092
2093        # Need our fixed points in order
2094        bgrnd[1]['FixedPoints'].sort(key=lambda pair: pair[0])
2095        X = [x for x, y in bgrnd[1]['FixedPoints']]
2096        Y = [y for x, y in bgrnd[1]['FixedPoints']]
2097
2098        limits = self.data['Limits'][1]
2099        if X[0] > limits[0]:
2100            X = [limits[0]] + X
2101            Y = [Y[0]] + Y
2102        if X[-1] < limits[1]:
2103            X += [limits[1]]
2104            Y += [Y[-1]]
2105
2106        # Some simple lookups
2107        controls = self.proj['Controls']['data']
2108        inst, inst2 = self.data['Instrument Parameters']
2109        pwddata = self.data['data'][1]
2110
2111        # Construct the data for background fitting
2112        xBeg = np.searchsorted(pwddata[0], limits[0])
2113        xFin = np.searchsorted(pwddata[0], limits[1])
2114        xdata = pwddata[0][xBeg:xFin]
2115        ydata = si.interp1d(X,Y)(ma.getdata(xdata))
2116
2117        W = [1]*len(xdata)
2118        Z = [0]*len(xdata)
2119
2120        dataType, insDict, insVary = SetInstParms(inst)
2121        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
2122
2123        # Do the fit
2124        data = np.array([xdata, ydata, W, Z, Z, Z])
2125        G2pwd.DoPeakFit('LSQ', [], bgrnd, limits, inst, inst2, data,
2126                        prevVaryList=bakVary, controls=controls)
2127
2128        # Post-fit
2129        parmDict = {}
2130        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
2131        parmDict.update(bakDict)
2132        parmDict.update(insDict)
2133        pwddata[3][xBeg:xFin] *= 0
2134        pwddata[5][xBeg:xFin] *= 0
2135        pwddata[4][xBeg:xFin] = G2pwd.getBackground('', parmDict, bakType, dataType, xdata)[0]
2136
2137        # TODO adjust pwddata? GSASIIpwdGUI.py:1041
2138        # TODO update background
2139        self.proj.save()
2140
2141    def getdata(self,datatype):
2142        '''Provides access to the histogram data of the selected data type
2143
2144        :param str datatype: must be one of the following values (case is ignored)
2145       
2146           * 'X': the 2theta or TOF values for the pattern
2147           * 'Yobs': the observed intensity values
2148           * 'Yweight': the weights for each data point (1/sigma**2)
2149           * 'Ycalc': the computed intensity values
2150           * 'Background': the computed background values
2151           * 'Residual': the difference between Yobs and Ycalc (obs-calc)
2152
2153        :returns: an numpy MaskedArray with data values of the requested type
2154       
2155        '''
2156        enums = ['x', 'yobs', 'yweight', 'ycalc', 'background', 'residual']
2157        if datatype.lower() not in enums:
2158            raise G2ScriptException("Invalid datatype = "+datatype+" must be one of "+str(enums))
2159        return self.data['data'][1][enums.index(datatype.lower())]
2160       
2161    def y_calc(self):
2162        return self.data['data'][1][3]
2163
2164    def Export(self,fileroot,extension):
2165        '''Write the histogram into a file. The path is specified by fileroot and
2166        extension.
2167       
2168        :param str fileroot: name of the file, optionally with a path (extension is
2169           ignored)
2170        :param str extension: includes '.', must match an extension in global
2171           exportersByExtension['powder'] or a Exception is raised.
2172        :returns: name of file that was written
2173        '''
2174        if extension not in exportersByExtension.get('powder',[]):
2175            raise G2ScriptException('No Writer for file type = "'+extension+'"')
2176        fil = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2177        obj = exportersByExtension['powder'][extension]
2178        obj.SetFromArray(hist=self.data,histname=self.name)
2179        obj.Writer(self.name,fil)
2180           
2181    def plot(self, Yobs=True, Ycalc=True, Background=True, Residual=True):
2182        try:
2183            import matplotlib.pyplot as plt
2184            data = self.data['data'][1]
2185            if Yobs:
2186                plt.plot(data[0], data[1], label='Yobs')
2187            if Ycalc:
2188                plt.plot(data[0], data[3], label='Ycalc')
2189            if Background:
2190                plt.plot(data[0], data[4], label='Background')
2191            if Residual:
2192                plt.plot(data[0], data[5], label="Residual")
2193        except ImportError:
2194            pass
2195
2196    def get_wR(self):
2197        """returns the overall weighted profile R factor for a histogram
2198       
2199        :returns: a wR value as a percentage or None if not defined
2200        """
2201        return self['data'][0].get('wR')
2202
2203    def set_refinements(self, refs):
2204        """Sets the refinement parameter 'key' to the specification 'value'
2205
2206        :param dict refs: A dictionary of the parameters to be set. See
2207                          :ref:`Histogram_parameters_table` for a description of
2208                          what these dictionaries should be.
2209
2210        :returns: None
2211
2212        """
2213        do_fit_fixed_points = False
2214        for key, value in refs.items():
2215            if key == 'Limits':
2216                old_limits = self.data['Limits'][1]
2217                new_limits = value
2218                if isinstance(new_limits, dict):
2219                    if 'low' in new_limits:
2220                        old_limits[0] = new_limits['low']
2221                    if 'high' in new_limits:
2222                        old_limits[1] = new_limits['high']
2223                else:
2224                    old_limits[0], old_limits[1] = new_limits
2225            elif key == 'Sample Parameters':
2226                sample = self.data['Sample Parameters']
2227                for sparam in value:
2228                    if sparam not in sample:
2229                        raise ValueError("Unknown refinement parameter, "
2230                                         + str(sparam))
2231                    sample[sparam][1] = True
2232            elif key == 'Background':
2233                bkg, peaks = self.data['Background']
2234
2235                # If True or False, just set the refine parameter
2236                if value in (True, False):
2237                    bkg[1] = value
2238                    return
2239
2240                if 'type' in value:
2241                    bkg[0] = value['type']
2242                if 'refine' in value:
2243                    bkg[1] = value['refine']
2244                if 'no. coeffs' in value:
2245                    cur_coeffs = bkg[2]
2246                    n_coeffs = value['no. coeffs']
2247                    if n_coeffs > cur_coeffs:
2248                        for x in range(n_coeffs - cur_coeffs):
2249                            bkg.append(0.0)
2250                    else:
2251                        for _ in range(cur_coeffs - n_coeffs):
2252                            bkg.pop()
2253                    bkg[2] = n_coeffs
2254                if 'coeffs' in value:
2255                    bkg[3:] = value['coeffs']
2256                if 'FixedPoints' in value:
2257                    peaks['FixedPoints'] = [(float(a), float(b))
2258                                            for a, b in value['FixedPoints']]
2259                if value.get('fit fixed points', False):
2260                    do_fit_fixed_points = True
2261
2262            elif key == 'Instrument Parameters':
2263                instrument, secondary = self.data['Instrument Parameters']
2264                for iparam in value:
2265                    try:
2266                        instrument[iparam][2] = True
2267                    except IndexError:
2268                        raise ValueError("Invalid key:", iparam)
2269            else:
2270                raise ValueError("Unknown key:", key)
2271        # Fit fixed points after the fact - ensure they are after fixed points
2272        # are added
2273        if do_fit_fixed_points:
2274            # Background won't be fit if refinement flag not set
2275            orig = self.data['Background'][0][1]
2276            self.data['Background'][0][1] = True
2277            self.fit_fixed_points()
2278            # Restore the previous value
2279            self.data['Background'][0][1] = orig
2280
2281    def clear_refinements(self, refs):
2282        """Clears the refinement parameter 'key' and its associated value.
2283
2284        :param dict refs: A dictionary of parameters to clear."""
2285        for key, value in refs.items():
2286            if key == 'Limits':
2287                old_limits, cur_limits = self.data['Limits']
2288                cur_limits[0], cur_limits[1] = old_limits
2289            elif key == 'Sample Parameters':
2290                sample = self.data['Sample Parameters']
2291                for sparam in value:
2292                    sample[sparam][1] = False
2293            elif key == 'Background':
2294                bkg, peaks = self.data['Background']
2295
2296                # If True or False, just set the refine parameter
2297                if value in (True, False):
2298                    bkg[1] = False
2299                    return
2300
2301                bkg[1] = False
2302                if 'FixedPoints' in value:
2303                    if 'FixedPoints' in peaks:
2304                        del peaks['FixedPoints']
2305            elif key == 'Instrument Parameters':
2306                instrument, secondary = self.data['Instrument Parameters']
2307                for iparam in value:
2308                    instrument[iparam][2] = False
2309            else:
2310                raise ValueError("Unknown key:", key)
2311
2312
2313class G2Phase(G2ObjectWrapper):
2314    """A wrapper object around a given phase.
2315
2316    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
2317    """
2318    def __init__(self, data, name, proj):
2319        self.data = data
2320        self.name = name
2321        self.proj = proj
2322
2323    @staticmethod
2324    def is_valid_refinement_key(key):
2325        valid_keys = ["Cell", "Atoms", "LeBail"]
2326        return key in valid_keys
2327
2328    @staticmethod
2329    def is_valid_HAP_refinement_key(key):
2330        valid_keys = ["Babinet", "Extinction", "HStrain", "Mustrain",
2331                      "Pref.Ori.", "Show", "Size", "Use", "Scale"]
2332        return key in valid_keys
2333
2334    def atom(self, atomlabel):
2335        """Returns the atom specified by atomlabel, or None if it does not
2336        exist.
2337
2338        :param str atomlabel: The name of the atom (e.g. "O2")
2339        :returns: A :class:`G2AtomRecord` object
2340            representing the atom.
2341        """
2342        # Consult GSASIIobj.py for the meaning of this
2343        cx, ct, cs, cia = self.data['General']['AtomPtrs']
2344        ptrs = [cx, ct, cs, cia]
2345        atoms = self.data['Atoms']
2346        for atom in atoms:
2347            if atom[ct-1] == atomlabel:
2348                return G2AtomRecord(atom, ptrs, self.proj)
2349
2350    def atoms(self):
2351        """Returns a list of atoms present in the phase.
2352
2353        :returns: A list of :class:`G2AtomRecord` objects.
2354
2355        .. seealso::
2356            :meth:`G2Phase.atom`
2357            :class:`G2AtomRecord`
2358        """
2359        ptrs = self.data['General']['AtomPtrs']
2360        output = []
2361        atoms = self.data['Atoms']
2362        for atom in atoms:
2363            output.append(G2AtomRecord(atom, ptrs, self.proj))
2364        return output
2365
2366    def histograms(self):
2367        output = []
2368        for hname in self.data.get('Histograms', {}).keys():
2369            output.append(self.proj.histogram(hname))
2370        return output
2371
2372    @property
2373    def ranId(self):
2374        return self.data['ranId']
2375
2376    @property
2377    def id(self):
2378        return self.data['pId']
2379
2380    @id.setter
2381    def id(self, val):
2382        self.data['pId'] = val
2383
2384    def get_cell(self):
2385        """Returns a dictionary of the cell parameters, with keys:
2386            'length_a', 'length_b', 'length_c', 'angle_alpha', 'angle_beta', 'angle_gamma', 'volume'
2387
2388        :returns: a dict
2389
2390        .. seealso::
2391           :meth:`G2Phase.get_cell_and_esd`
2392
2393        """
2394        cell = self.data['General']['Cell']
2395        return {'length_a': cell[1], 'length_b': cell[2], 'length_c': cell[3],
2396                'angle_alpha': cell[4], 'angle_beta': cell[5], 'angle_gamma': cell[6],
2397                'volume': cell[7]}
2398
2399    def get_cell_and_esd(self):
2400        """
2401        Returns a pair of dictionaries, the first representing the unit cell, the second
2402        representing the estimated standard deviations of the unit cell.
2403
2404        :returns: a tuple of two dictionaries
2405
2406        .. seealso::
2407           :meth:`G2Phase.get_cell`
2408
2409        """
2410        # translated from GSASIIstrIO.ExportBaseclass.GetCell
2411        import GSASIIstrIO as G2stIO
2412        import GSASIIlattice as G2lat
2413        import GSASIImapvars as G2mv
2414        try:
2415            pfx = str(self.id) + '::'
2416            sgdata = self['General']['SGData']
2417            covDict = self.proj['Covariance']['data']
2418
2419            parmDict = dict(zip(covDict.get('varyList',[]),
2420                                covDict.get('variables',[])))
2421            sigDict = dict(zip(covDict.get('varyList',[]),
2422                               covDict.get('sig',[])))
2423
2424            if covDict.get('covMatrix') is not None:
2425                sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],
2426                                                  covDict['varyList'],
2427                                                  parmDict))
2428
2429            A, sigA = G2stIO.cellFill(pfx, sgdata, parmDict, sigDict)
2430            cellSig = G2stIO.getCellEsd(pfx, sgdata, A, self.proj['Covariance']['data'])
2431            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2432            cellDict, cellSigDict = {}, {}
2433            for i, key in enumerate(['length_a', 'length_b', 'length_c',
2434                                     'angle_alpha', 'angle_beta', 'angle_gamma',
2435                                     'volume']):
2436                cellDict[key] = cellList[i]
2437                cellSigDict[key] = cellSig[i]
2438            return cellDict, cellSigDict
2439        except KeyError:
2440            cell = self.get_cell()
2441            return cell, {key: 0.0 for key in cell}
2442
2443    def export_CIF(self, outputname, quickmode=True):
2444        """Write this phase to a .cif file named outputname
2445
2446        :param str outputname: The name of the .cif file to write to
2447        :param bool quickmode: Currently ignored. Carryover from exports.G2export_CIF"""
2448        # This code is all taken from exports/G2export_CIF.py
2449        # Functions copied have the same names
2450        import GSASIImath as G2mth
2451        import GSASIImapvars as G2mv
2452        from exports import G2export_CIF as cif
2453
2454        CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
2455        CIFname = os.path.splitext(self.proj.filename)[0]
2456        CIFname = os.path.split(CIFname)[1]
2457        CIFname = ''.join([c if ord(c) < 128 else ''
2458                           for c in CIFname.replace(' ', '_')])
2459        try:
2460            author = self.proj['Controls']['data'].get('Author','').strip()
2461        except KeyError:
2462            pass
2463        oneblock = True
2464
2465        covDict = self.proj['Covariance']['data']
2466        parmDict = dict(zip(covDict.get('varyList',[]),
2467                            covDict.get('variables',[])))
2468        sigDict = dict(zip(covDict.get('varyList',[]),
2469                           covDict.get('sig',[])))
2470
2471        if covDict.get('covMatrix') is not None:
2472            sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],
2473                                              covDict['varyList'],
2474                                              parmDict))
2475
2476        with open(outputname, 'w') as fp:
2477            fp.write(' \n' + 70*'#' + '\n')
2478            cif.WriteCIFitem(fp, 'data_' + CIFname)
2479            # from exports.G2export_CIF.WritePhaseInfo
2480            cif.WriteCIFitem(fp, '\n# phase info for '+str(self.name) + ' follows')
2481            cif.WriteCIFitem(fp, '_pd_phase_name', self.name)
2482            # TODO get esds
2483            cellDict = self.get_cell()
2484            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
2485            names = ['length_a','length_b','length_c',
2486                     'angle_alpha','angle_beta ','angle_gamma',
2487                     'volume']
2488            for key, val in cellDict.items():
2489                cif.WriteCIFitem(fp, '_cell_' + key, G2mth.ValEsd(val))
2490
2491            cif.WriteCIFitem(fp, '_symmetry_cell_setting',
2492                         self.data['General']['SGData']['SGSys'])
2493
2494            spacegroup = self.data['General']['SGData']['SpGrp'].strip()
2495            # regularize capitalization and remove trailing H/R
2496            spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2497            cif.WriteCIFitem(fp, '_symmetry_space_group_name_H-M', spacegroup)
2498
2499            # generate symmetry operations including centering and center of symmetry
2500            SymOpList, offsetList, symOpList, G2oprList, G2opcodes = G2spc.AllOps(
2501                self.data['General']['SGData'])
2502            cif.WriteCIFitem(fp, 'loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
2503            for i, op in enumerate(SymOpList,start=1):
2504                cif.WriteCIFitem(fp, '   {:3d}  {:}'.format(i,op.lower()))
2505
2506            # TODO skipped histograms, exports/G2export_CIF.py:880
2507
2508            # report atom params
2509            if self.data['General']['Type'] in ['nuclear','macromolecular']:        #this needs macromolecular variant, etc!
2510                cif.WriteAtomsNuclear(fp, self.data, self.name, parmDict, sigDict, [])
2511                # self._WriteAtomsNuclear(fp, parmDict, sigDict)
2512            else:
2513                raise G2ScriptException("no export for "+str(self.data['General']['Type'])+" coordinates implemented")
2514            # report cell contents
2515            cif.WriteComposition(fp, self.data, self.name, parmDict)
2516            if not quickmode and self.data['General']['Type'] == 'nuclear':      # report distances and angles
2517                # WriteDistances(fp,self.name,SymOpList,offsetList,symOpList,G2oprList)
2518                raise NotImplementedError("only quickmode currently supported")
2519            if 'Map' in self.data['General'] and 'minmax' in self.data['General']['Map']:
2520                cif.WriteCIFitem(fp,'\n# Difference density results')
2521                MinMax = self.data['General']['Map']['minmax']
2522                cif.WriteCIFitem(fp,'_refine_diff_density_max',G2mth.ValEsd(MinMax[0],-0.009))
2523                cif.WriteCIFitem(fp,'_refine_diff_density_min',G2mth.ValEsd(MinMax[1],-0.009))
2524
2525
2526    def set_refinements(self, refs):
2527        """Sets the refinement parameter 'key' to the specification 'value'
2528
2529        :param dict refs: A dictionary of the parameters to be set. See
2530                          :ref:`Phase_parameters_table` for a description of
2531                          this dictionary.
2532
2533        :returns: None"""
2534        for key, value in refs.items():
2535            if key == "Cell":
2536                self.data['General']['Cell'][0] = value
2537
2538            elif key == "Atoms":
2539                for atomlabel, atomrefinement in value.items():
2540                    if atomlabel == 'all':
2541                        for atom in self.atoms():
2542                            atom.refinement_flags = atomrefinement
2543                    else:
2544                        atom = self.atom(atomlabel)
2545                        if atom is None:
2546                            raise ValueError("No such atom: " + atomlabel)
2547                        atom.refinement_flags = atomrefinement
2548
2549            elif key == "LeBail":
2550                hists = self.data['Histograms']
2551                for hname, hoptions in hists.items():
2552                    if 'LeBail' not in hoptions:
2553                        hoptions['newLeBail'] = bool(True)
2554                    hoptions['LeBail'] = bool(value)
2555            else:
2556                raise ValueError("Unknown key:", key)
2557
2558    def clear_refinements(self, refs):
2559        """Clears a given set of parameters.
2560
2561        :param dict refs: The parameters to clear"""
2562        for key, value in refs.items():
2563            if key == "Cell":
2564                self.data['General']['Cell'][0] = False
2565            elif key == "Atoms":
2566                cx, ct, cs, cia = self.data['General']['AtomPtrs']
2567
2568                for atomlabel in value:
2569                    atom = self.atom(atomlabel)
2570                    # Set refinement to none
2571                    atom.refinement_flags = ' '
2572            elif key == "LeBail":
2573                hists = self.data['Histograms']
2574                for hname, hoptions in hists.items():
2575                    if 'LeBail' not in hoptions:
2576                        hoptions['newLeBail'] = True
2577                    hoptions['LeBail'] = False
2578            else:
2579                raise ValueError("Unknown key:", key)
2580
2581    def set_HAP_refinements(self, refs, histograms='all'):
2582        """Sets the given HAP refinement parameters between this phase and
2583        the given histograms
2584
2585        :param dict refs: A dictionary of the parameters to be set. See
2586                          :ref:`HAP_parameters_table` for a description of this
2587                          dictionary.
2588        :param histograms: Either 'all' (default) or a list of the histograms
2589            whose HAP parameters will be set with this phase. Histogram and phase
2590            must already be associated
2591
2592        :returns: None
2593        """
2594        if histograms == 'all':
2595            histograms = self.data['Histograms'].values()
2596        else:
2597            histograms = [self.data['Histograms'][h.name] for h in histograms]
2598
2599        for key, val in refs.items():
2600            for h in histograms:
2601                if key == 'Babinet':
2602                    try:
2603                        sets = list(val)
2604                    except ValueError:
2605                        sets = ['BabA', 'BabU']
2606                    for param in sets:
2607                        if param not in ['BabA', 'BabU']:
2608                            raise ValueError("Not sure what to do with" + param)
2609                        for hist in histograms:
2610                            hist['Babinet'][param][1] = True
2611                elif key == 'Extinction':
2612                    for h in histograms:
2613                        h['Extinction'][1] = bool(val)
2614                elif key == 'HStrain':
2615                    for h in histograms:
2616                        h['HStrain'][1] = [bool(val) for p in h['HStrain'][1]]
2617                elif key == 'Mustrain':
2618                    for h in histograms:
2619                        mustrain = h['Mustrain']
2620                        newType = None
2621                        direction = None
2622                        if isinstance(val, strtypes):
2623                            if val in ['isotropic', 'uniaxial', 'generalized']:
2624                                newType = val
2625                            else:
2626                                raise ValueError("Not a Mustrain type: " + val)
2627                        elif isinstance(val, dict):
2628                            newType = val.get('type', None)
2629                            direction = val.get('direction', None)
2630
2631                        if newType:
2632                            mustrain[0] = newType
2633                            if newType == 'isotropic':
2634                                mustrain[2][0] = True
2635                                mustrain[5] = [False for p in mustrain[4]]
2636                            elif newType == 'uniaxial':
2637                                if 'refine' in val:
2638                                    types = val['refine']
2639                                    if isinstance(types, strtypes):
2640                                        types = [types]
2641                                    elif isinstance(types, bool):
2642                                        mustrain[2][0] = types
2643                                        mustrain[2][1] = types
2644                                        types = []
2645                                    else:
2646                                        raise ValueError("Not sure what to do with: "
2647                                                         + str(types))
2648                                else:
2649                                    types = []
2650
2651                                for unitype in types:
2652                                    if unitype == 'equatorial':
2653                                        mustrain[2][0] = True
2654                                    elif unitype == 'axial':
2655                                        mustrain[2][1] = True
2656                                    else:
2657                                        msg = 'Invalid uniaxial mustrain type'
2658                                        raise ValueError(msg + ': ' + unitype)
2659                            else:  # newtype == 'generalized'
2660                                mustrain[2] = [False for p in mustrain[1]]
2661
2662                        if direction:
2663                            if len(direction) != 3:
2664                                raise ValueError("Expected hkl, found", direction)
2665                            direction = [int(n) for n in direction]
2666                            mustrain[3] = direction
2667                elif key == 'Size':
2668                    for h in histograms:
2669                        size = h['Size']
2670                        newType = None
2671                        direction = None
2672                        if isinstance(val, strtypes):
2673                            if val in ['isotropic', 'uniaxial', 'ellipsoidal']:
2674                                newType = val
2675                            else:
2676                                raise ValueError("Not a valid Size type: " + val)
2677                        elif isinstance(val, dict):
2678                            newType = val.get('type', None)
2679                            direction = val.get('direction', None)
2680
2681                        if newType:
2682                            size[0] = newType
2683                            refine = val.get('refine')
2684                            if newType == 'isotropic' and refine is not None:
2685                                size[2][0] = bool(refine)
2686                            elif newType == 'uniaxial' and refine is not None:
2687                                size[2][1] = bool(refine)
2688                                size[2][2] = bool(refine)
2689                            elif newType == 'ellipsoidal' and refine is not None:
2690                                size[5] = [bool(refine) for p in size[5]]
2691
2692                        if direction:
2693                            if len(direction) != 3:
2694                                raise ValueError("Expected hkl, found", direction)
2695                            direction = [int(n) for n in direction]
2696                            size[3] = direction
2697                elif key == 'Pref.Ori.':
2698                    for h in histograms:
2699                        h['Pref.Ori.'][2] = bool(val)
2700                elif key == 'Show':
2701                    for h in histograms:
2702                        h['Show'] = bool(val)
2703                elif key == 'Use':
2704                    for h in histograms:
2705                        h['Use'] = bool(val)
2706                elif key == 'Scale':
2707                    for h in histograms:
2708                        h['Scale'][1] = bool(val)
2709                else:
2710                    print(u'Unknown HAP key: '+key)
2711
2712    def getHAPvalues(self, histname):
2713        """Returns a dict with HAP values for the selected histogram
2714
2715        :param histogram: is a histogram object (:class:`G2PwdrData`) or
2716            a histogram name or the index number of the histogram
2717
2718        :returns: HAP value dict
2719        """
2720        if isinstance(histname, G2PwdrData):
2721            histname = histname.name
2722        elif histname in self.data['Histograms']:
2723            pass
2724        elif type(histname) is int:
2725            histname = self.proj.histograms()[histname].name
2726        else:
2727            raise G2ScriptException("Invalid histogram reference: "+str(histname))
2728        return self.data['Histograms'][histname]
2729                   
2730    def clear_HAP_refinements(self, refs, histograms='all'):
2731        """Clears the given HAP refinement parameters between this phase and
2732        the given histograms
2733
2734        :param dict refs: A dictionary of the parameters to be cleared.
2735        :param histograms: Either 'all' (default) or a list of the histograms
2736            whose HAP parameters will be cleared with this phase. Histogram and
2737            phase must already be associated
2738
2739        :returns: None
2740        """
2741        if histograms == 'all':
2742            histograms = self.data['Histograms'].values()
2743        else:
2744            histograms = [self.data['Histograms'][h.name] for h in histograms]
2745
2746        for key, val in refs.items():
2747            for h in histograms:
2748                if key == 'Babinet':
2749                    try:
2750                        sets = list(val)
2751                    except ValueError:
2752                        sets = ['BabA', 'BabU']
2753                    for param in sets:
2754                        if param not in ['BabA', 'BabU']:
2755                            raise ValueError("Not sure what to do with" + param)
2756                        for hist in histograms:
2757                            hist['Babinet'][param][1] = False
2758                elif key == 'Extinction':
2759                    for h in histograms:
2760                        h['Extinction'][1] = False
2761                elif key == 'HStrain':
2762                    for h in histograms:
2763                        h['HStrain'][1] = [False for p in h['HStrain'][1]]
2764                elif key == 'Mustrain':
2765                    for h in histograms:
2766                        mustrain = h['Mustrain']
2767                        mustrain[2] = [False for p in mustrain[2]]
2768                        mustrain[5] = [False for p in mustrain[4]]
2769                elif key == 'Pref.Ori.':
2770                    for h in histograms:
2771                        h['Pref.Ori.'][2] = False
2772                elif key == 'Show':
2773                    for h in histograms:
2774                        h['Show'] = False
2775                elif key == 'Size':
2776                    for h in histograms:
2777                        size = h['Size']
2778                        size[2] = [False for p in size[2]]
2779                        size[5] = [False for p in size[5]]
2780                elif key == 'Use':
2781                    for h in histograms:
2782                        h['Use'] = False
2783                elif key == 'Scale':
2784                    for h in histograms:
2785                        h['Scale'][1] = False
2786                else:
2787                    print(u'Unknown HAP key: '+key)
2788
2789
2790##########################
2791# Command Line Interface #
2792##########################
2793# Each of these takes an argparse.Namespace object as their argument,
2794# representing the parsed command-line arguments for the relevant subcommand.
2795# The argument specification for each is in the subcommands dictionary (see
2796# below)
2797
2798commandhelp={}
2799commandhelp["create"] = "creates a GSAS-II project, optionally adding histograms and/or phases"
2800def create(args):
2801    """Implements the create command-line subcommand. This creates a GSAS-II project, optionally adding histograms and/or phases::
2802
2803  usage: GSASIIscriptable.py create [-h] [-d HISTOGRAMS [HISTOGRAMS ...]]
2804                                  [-i IPARAMS [IPARAMS ...]]
2805                                  [-p PHASES [PHASES ...]]
2806                                  filename
2807                                 
2808positional arguments::
2809
2810  filename              the project file to create. should end in .gpx
2811
2812optional arguments::
2813
2814  -h, --help            show this help message and exit
2815  -d HISTOGRAMS [HISTOGRAMS ...], --histograms HISTOGRAMS [HISTOGRAMS ...]
2816                        list of datafiles to add as histograms
2817  -i IPARAMS [IPARAMS ...], --iparams IPARAMS [IPARAMS ...]
2818                        instrument parameter file, must be one for every
2819                        histogram
2820  -p PHASES [PHASES ...], --phases PHASES [PHASES ...]
2821                        list of phases to add. phases are automatically
2822                        associated with all histograms given.
2823
2824    """
2825    proj = G2Project(gpxname=args.filename)
2826
2827    hist_objs = []
2828    if args.histograms:
2829        for h,i in zip(args.histograms,args.iparams):
2830            print("Adding histogram from",h,"with instparm ",i)
2831            hist_objs.append(proj.add_powder_histogram(h, i))
2832
2833    if args.phases: 
2834        for p in args.phases:
2835            print("Adding phase from",p)
2836            proj.add_phase(p, histograms=hist_objs)
2837        print('Linking phase(s) to histogram(s):')
2838        for h in hist_objs:
2839            print ('   '+h.name)
2840
2841    proj.save()
2842
2843commandhelp["add"] = "adds histograms and/or phases to GSAS-II project"
2844def add(args):
2845    """Implements the add command-line subcommand. This adds histograms and/or phases to GSAS-II project::
2846
2847  usage: GSASIIscriptable.py add [-h] [-d HISTOGRAMS [HISTOGRAMS ...]]
2848                               [-i IPARAMS [IPARAMS ...]]
2849                               [-hf HISTOGRAMFORMAT] [-p PHASES [PHASES ...]]
2850                               [-pf PHASEFORMAT] [-l HISTLIST [HISTLIST ...]]
2851                               filename
2852
2853
2854positional arguments::
2855
2856  filename              the project file to open. Should end in .gpx
2857
2858optional arguments::
2859
2860  -h, --help            show this help message and exit
2861  -d HISTOGRAMS [HISTOGRAMS ...], --histograms HISTOGRAMS [HISTOGRAMS ...]
2862                        list of datafiles to add as histograms
2863  -i IPARAMS [IPARAMS ...], --iparams IPARAMS [IPARAMS ...]
2864                        instrument parameter file, must be one for every
2865                        histogram
2866  -hf HISTOGRAMFORMAT, --histogramformat HISTOGRAMFORMAT
2867                        format hint for histogram import. Applies to all
2868                        histograms
2869  -p PHASES [PHASES ...], --phases PHASES [PHASES ...]
2870                        list of phases to add. phases are automatically
2871                        associated with all histograms given.
2872  -pf PHASEFORMAT, --phaseformat PHASEFORMAT
2873                        format hint for phase import. Applies to all phases.
2874                        Example: -pf CIF
2875  -l HISTLIST [HISTLIST ...], --histlist HISTLIST [HISTLIST ...]
2876                        list of histgram indices to associate with added
2877                        phases. If not specified, phases are associated with
2878                        all previously loaded histograms. Example: -l 2 3 4
2879   
2880    """
2881    proj = G2Project(args.filename)
2882
2883    if args.histograms:
2884        for h,i in zip(args.histograms,args.iparams):
2885            print("Adding histogram from",h,"with instparm ",i)
2886            proj.add_powder_histogram(h, i, fmthint=args.histogramformat)
2887
2888    if args.phases: 
2889        if not args.histlist:
2890            histlist = proj.histograms()
2891        else:
2892            histlist = [proj.histogram(i) for i in args.histlist]
2893
2894        for p in args.phases:
2895            print("Adding phase from",p)
2896            proj.add_phase(p, histograms=histlist, fmthint=args.phaseformat)
2897           
2898        if not args.histlist:
2899            print('Linking phase(s) to all histogram(s)')
2900        else:
2901            print('Linking phase(s) to histogram(s):')
2902            for h in histlist:
2903                print ('   '+h.name)
2904
2905    proj.save()
2906
2907
2908commandhelp["dump"] = "Shows the contents of a GSAS-II project"
2909def dump(args):
2910    """Implements the dump command-line subcommand, which shows the contents of a GSAS-II project::
2911
2912       usage: GSASIIscriptable.py dump [-h] [-d] [-p] [-r] files [files ...]
2913
2914positional arguments::
2915
2916  files
2917
2918optional arguments::
2919
2920  -h, --help        show this help message and exit
2921  -d, --histograms  list histograms in files, overrides --raw
2922  -p, --phases      list phases in files, overrides --raw
2923  -r, --raw         dump raw file contents, default
2924 
2925    """
2926    if not args.histograms and not args.phases:
2927        args.raw = True
2928    if args.raw:
2929        import IPython.lib.pretty as pretty
2930
2931    for fname in args.files:
2932        if args.raw:
2933            proj, nameList = LoadDictFromProjFile(fname)
2934            print("file:", fname)
2935            print("names:", nameList)
2936            for key, val in proj.items():
2937                print(key, ":")
2938                pretty.pprint(val)
2939        else:
2940            proj = G2Project(fname)
2941            if args.histograms:
2942                hists = proj.histograms()
2943                for h in hists:
2944                    print(fname, "hist", h.id, h.name)
2945            if args.phases:
2946                phase_list = proj.phases()
2947                for p in phase_list:
2948                    print(fname, "phase", p.id, p.name)
2949
2950
2951commandhelp["browse"] = "Load a GSAS-II project and then open a IPython shell to browse it"
2952def IPyBrowse(args):
2953    """Load a .gpx file and then open a IPython shell to browse it::
2954
2955  usage: GSASIIscriptable.py browse [-h] files [files ...]
2956
2957positional arguments::
2958
2959  files       list of files to browse
2960
2961optional arguments::
2962
2963  -h, --help  show this help message and exit
2964
2965    """
2966    for fname in args.files:
2967        proj, nameList = LoadDictFromProjFile(fname)
2968        msg = "\nfname {} loaded into proj (dict) with names in nameList".format(fname)
2969        GSASIIpath.IPyBreak_base(msg)
2970        break
2971
2972
2973commandhelp["refine"] = '''
2974Conducts refinements on GSAS-II projects according to a list of refinement
2975steps in a JSON dict
2976'''
2977def refine(args):
2978    """Implements the refine command-line subcommand:
2979    conducts refinements on GSAS-II projects according to a JSON refinement dict::
2980
2981        usage: GSASIIscriptable.py refine [-h] gpxfile [refinements]
2982
2983positional arguments::
2984
2985  gpxfile      the project file to refine
2986  refinements  json file of refinements to apply. if not present refines file
2987               as-is
2988
2989optional arguments::
2990
2991  -h, --help   show this help message and exit
2992 
2993    """
2994    proj = G2Project(args.gpxfile)
2995    if args.refinements is None:
2996        proj.refine()
2997    else:
2998        import json
2999        with open(args.refinements) as refs:
3000            refs = json.load(refs)
3001        if type(refs) is not dict:
3002            raise G2ScriptException("Error: JSON object must be a dict.")
3003        if "code" in refs:
3004            print("executing code:\n|  ",'\n|  '.join(refs['code']))
3005            exec('\n'.join(refs['code']))
3006        proj.do_refinements(refs['refinements'])
3007
3008
3009commandhelp["seqrefine"] = "Not implemented. Placeholder for eventual sequential refinement implementation"
3010def seqrefine(args):
3011    """Future implementation for the seqrefine command-line subcommand """
3012    raise NotImplementedError("seqrefine is not yet implemented")
3013
3014
3015commandhelp["export"] = "Export phase as CIF"
3016def export(args):
3017    """Implements the export command-line subcommand: Exports phase as CIF::
3018
3019      usage: GSASIIscriptable.py export [-h] gpxfile phase exportfile
3020
3021positional arguments::
3022
3023  gpxfile     the project file from which to export
3024  phase       identifier of phase to export
3025  exportfile  the .cif file to export to
3026
3027optional arguments::
3028
3029  -h, --help  show this help message and exit
3030
3031    """
3032    proj = G2Project(args.gpxfile)
3033    phase = proj.phase(args.phase)
3034    phase.export_CIF(args.exportfile)
3035
3036
3037def _args_kwargs(*args, **kwargs):
3038    return args, kwargs
3039
3040# A dictionary of the name of each subcommand, and a tuple
3041# of its associated function and a list of its arguments
3042# The arguments are passed directly to the add_argument() method
3043# of an argparse.ArgumentParser
3044
3045subcommands = {"create":
3046               (create, [_args_kwargs('filename',
3047                                      help='the project file to create. should end in .gpx'),
3048
3049                         _args_kwargs('-d', '--histograms',
3050                                      nargs='+',
3051                                      help='list of datafiles to add as histograms'),
3052                                     
3053                         _args_kwargs('-i', '--iparams',
3054                                      nargs='+',
3055                                      help='instrument parameter file, must be one'
3056                                           ' for every histogram'
3057                                      ),
3058
3059                         _args_kwargs('-p', '--phases',
3060                                      nargs='+',
3061                                      help='list of phases to add. phases are '
3062                                           'automatically associated with all '
3063                                           'histograms given.')]),
3064               "add": (add, [_args_kwargs('filename',
3065                                      help='the project file to open. Should end in .gpx'),
3066
3067                         _args_kwargs('-d', '--histograms',
3068                                      nargs='+',
3069                                      help='list of datafiles to add as histograms'),
3070                                     
3071                         _args_kwargs('-i', '--iparams',
3072                                      nargs='+',
3073                                      help='instrument parameter file, must be one'
3074                                           ' for every histogram'
3075                                      ),
3076                                     
3077                         _args_kwargs('-hf', '--histogramformat',
3078                                      help='format hint for histogram import. Applies to all'
3079                                           ' histograms'
3080                                      ),
3081
3082                         _args_kwargs('-p', '--phases',
3083                                      nargs='+',
3084                                      help='list of phases to add. phases are '
3085                                           'automatically associated with all '
3086                                           'histograms given.'),
3087
3088                         _args_kwargs('-pf', '--phaseformat',
3089                                      help='format hint for phase import. Applies to all'
3090                                           ' phases. Example: -pf CIF'
3091                                      ),
3092                                     
3093                         _args_kwargs('-l', '--histlist',
3094                                      nargs='+',
3095                                      help='list of histgram indices to associate with added'
3096                                           ' phases. If not specified, phases are'
3097                                           ' associated with all previously loaded'
3098                                           ' histograms. Example: -l 2 3 4')]),
3099                                           
3100               "dump": (dump, [_args_kwargs('-d', '--histograms',
3101                                     action='store_true',
3102                                     help='list histograms in files, overrides --raw'),
3103
3104                               _args_kwargs('-p', '--phases',
3105                                            action='store_true',
3106                                            help='list phases in files, overrides --raw'),
3107
3108                               _args_kwargs('-r', '--raw',
3109                                      action='store_true', help='dump raw file contents, default'),
3110
3111                               _args_kwargs('files', nargs='+')]),
3112
3113               "refine":
3114               (refine, [_args_kwargs('gpxfile', help='the project file to refine'),
3115                         _args_kwargs('refinements',
3116                                      help='JSON file of refinements to apply. if not present'
3117                                           ' refines file as-is',
3118                                      default=None,
3119                                      nargs='?')]),
3120
3121               "seqrefine": (seqrefine, []),
3122               "export": (export, [_args_kwargs('gpxfile',
3123                                                help='the project file from which to export'),
3124                                   _args_kwargs('phase', help='identifier of phase to export'),
3125                                   _args_kwargs('exportfile', help='the .cif file to export to')]),
3126               "browse": (IPyBrowse, [_args_kwargs('files', nargs='+',
3127                                                   help='list of files to browse')])}
3128
3129
3130def main():
3131    '''The command-line interface for calling GSASIIscriptable as a shell command,
3132    where it is expected to be called as::
3133
3134       python GSASIIscriptable.py <subcommand> <file.gpx> <options>
3135
3136    The following subcommands are defined:
3137
3138        * create, see :func:`create`
3139        * add, see :func:`add`
3140        * dump, see :func:`dump`
3141        * refine, see :func:`refine`
3142        * seqrefine, see :func:`seqrefine`
3143        * export, :func:`export`
3144        * browse, see :func:`IPyBrowse`
3145
3146    .. seealso::
3147        :func:`create`
3148        :func:`add`
3149        :func:`dump`
3150        :func:`refine`
3151        :func:`seqrefine`
3152        :func:`export`
3153        :func:`IPyBrowse`
3154    '''
3155    parser = argparse.ArgumentParser(description=
3156        "Use of "+os.path.split(__file__)[1]+" Allows GSAS-II actions from command line."
3157        )
3158    subs = parser.add_subparsers()
3159
3160    # Create all of the specified subparsers
3161    for name, (func, args) in subcommands.items():
3162        new_parser = subs.add_parser(name,help=commandhelp.get(name),
3163                                     description='Command "'+name+'" '+commandhelp.get(name))
3164        for listargs, kwds in args:
3165            new_parser.add_argument(*listargs, **kwds)
3166        new_parser.set_defaults(func=func)
3167
3168    # Parse and trigger subcommand
3169    result = parser.parse_args()
3170    result.func(result)
3171
3172if __name__ == '__main__':
3173    main()
Note: See TracBrowser for help on using the repository browser.