source: trunk/GSASIIscriptable.py @ 3845

Last change on this file since 3845 was 3845, checked in by toby, 3 years ago

more sequential timing; doc fixes for scriptable

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 178.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3########### SVN repository information ###################
4# $Date: 2019-03-08 18:59:26 +0000 (Fri, 08 Mar 2019) $
5# $Author: toby $
6# $Revision: 3845 $
7# $URL: trunk/GSASIIscriptable.py $
8# $Id: GSASIIscriptable.py 3845 2019-03-08 18:59:26Z toby $
9########### SVN repository information ###################
10#
11"""
12*GSASIIscriptable: Scripting Interface*
13=======================================
14
15Routines to use an increasing amount of GSAS-II's capabilities from scripts,
16without use of the graphical user interface (GUI). GSASIIscriptable can create and access
17GSAS-II project (.gpx) files and can directly perform image handling and refinements. 
18The module defines wrapper classes (inheriting from :class:`G2ObjectWrapper`) for a growing number
19of data tree items.
20
21GSASIIscriptable can be used in two ways. It offers a command-line mode
22(see :ref:`CommandlineInterface`) that
23provides access a number of features without writing Python scripts
24via shell/batch commands. The more powerful mode of GSASIIscriptable is
25use is through Python scripts that
26call the module's application interface (API), see API summary that follows or the :ref:`API`
27section.
28
29==================================================
30Application Interface (API) Summary
31==================================================
32This section of the documentation provides an overview to API, with full documentation
33in the :ref:`API` section. The typical API use will be with a Python script, such as this:
34
35.. code-block::  python
36
37    from __future__ import division, print_function
38    import os,sys
39    sys.path.insert(0,'/Users/toby/software/G2/GSASII') # needed to "find" GSAS-II modules
40    import GSASIIscriptable as G2sc
41    datadir = "/Users/Scratch/"
42    gpx = G2sc.G2Project(os.path.join(datadir,'test2.gpx'))
43    gpx.histogram(0).add_back_peak(4.5,30000,5000,0)
44    pardict = {'set': {'Sample Parameters': ['Absorption', 'Contrast', 'DisplaceX'],
45                       'Background': {'type': 'chebyschev', 'refine': True,
46                                      'peaks':[[0,True]]}}}
47    gpx.set_refinement(pardict)
48
49Most functionallity is provided via the objects and methods described in this section.
50
51---------------------
52:class:`G2Project`
53---------------------
54
55  All GSASIIscriptable scripts will need to create a :class:`G2Project` object
56  either for a new GSAS-II project or to read in an existing project (.gpx) file.
57  The most commonly used routines in this object are:
58
59.. tabularcolumns:: |l|p{3.5in}|
60
61==================================================    ===============================================================================================================
62method                                                Use
63==================================================    ===============================================================================================================
64:meth:`G2Project.save`                                Writes the current project to disk.
65
66:meth:`G2Project.add_powder_histogram`                Used to read in powder diffraction data into a project file.
67
68:meth:`G2Project.add_simulated_powder_histogram`      Defines a "dummy" powder diffraction data that will be simulated after a refinement step.
69
70:meth:`G2Project.add_image`                           Reads in an image into a project.
71
72:meth:`G2Project.add_phase`                           Adds a phase to a project
73
74:meth:`G2Project.histograms`                          Provides a list of histograms in the current project, as :class:`G2PwdrData` objects
75
76:meth:`G2Project.phases`                              Provides a list of phases defined in the current project, as :class:`G2Phase` objects
77
78:meth:`G2Project.images`                              Provides a list of images in the current project, as :class:`G2Image` objects
79
80:meth:`G2Project.do_refinements`                      This is passed a list of dictionaries, where each dict defines a refinement step.
81                                                      Passing a list with a single empty dict initiates a refinement with the current
82                                                      parameters and flags. A refinement dict sets up a single refinement step
83                                                      (as described in :ref:`Project_dicts`). Also see :ref:`Refinement_recipe`.
84
85:meth:`G2Project.set_refinement`                      This is passed a single dict which is used to set parameters and flags.
86                                                      These actions can be performed also in :meth:`G2Project.do_refinements`.
87==================================================    ===============================================================================================================
88
89---------------------
90:class:`G2Phase`
91---------------------
92
93  Another common object in GSASIIscriptable scripts is :class:`G2Phase`, used to encapsulate each phase in a project, with commonly used methods:
94
95.. tabularcolumns:: |l|p{3.5in}|
96
97==================================================    ===============================================================================================================
98method                                                Use
99==================================================    ===============================================================================================================
100:meth:`G2Phase.set_refinements`                       Provides a mechanism to set values and refinement flags for the phase. See the :ref:`Phase_parameters_table`
101                                                      for more details. This information also can be supplied within a call to :meth:`G2Project.do_refinements`
102                                                      or :meth:`G2Project.set_refinement`.
103:meth:`G2Phase.clear_refinements`                     Unsets refinement flags for the phase.
104:meth:`G2Phase.set_HAP_refinements`                   Provides a mechanism to set values and refinement flags for parameters specific to both this phase and
105                                                      one of its histograms. See the :ref:`HAP_parameters_table`. This information also can be supplied within
106                                                      a call to :meth:`G2Project.do_refinements` or :meth:`G2Project.set_refinement`.
107:meth:`G2Phase.clear_HAP_refinements`                 Clears refinement flags specific to both this phase and one of its histograms.
108:meth:`G2Phase.getHAPvalues`                          Returns values of parameters specific to both this phase and one of its histograms.
109:meth:`G2Phase.atoms`                                 Returns a list of atoms in the phase
110:meth:`G2Phase.atom`                                  Returns an atom from its label
111:meth:`G2Phase.histograms`                            Returns a list of histograms linked to the phase
112:meth:`G2Phase.get_cell`                              Returns unit cell parameters (also see :meth:`G2Phase.get_cell_and_esd`)
113:meth:`G2Phase.export_CIF`                            Writes a CIF for the phase
114==================================================    ===============================================================================================================
115
116---------------------
117:class:`G2PwdrData`
118---------------------
119
120  Another common object in GSASIIscriptable scripts is :class:`G2PwdrData`, which encapsulate each powder diffraction histogram in a project, with commonly used methods:
121
122.. tabularcolumns:: |l|p{3.5in}|
123
124==================================================    ===============================================================================================================
125method                                                Use
126==================================================    ===============================================================================================================
127:meth:`G2PwdrData.set_refinements`                    Provides a mechanism to set values and refinement flags for the powder histogram. See the
128                                                      :ref:`Histogram_parameters_table` for details. 
129:meth:`G2PwdrData.clear_refinements`                  Unsets refinement flags for the the powder histogram.
130:meth:`G2PwdrData.residuals`                          Reports R-factors etc. for the the powder histogram (also see :meth:`G2PwdrData.get_wR`
131:meth:`G2PwdrData.add_back_peak`                      Adds a background peak to the histogram. Also see :meth:`G2PwdrData.del_back_peak` and
132                                                      :meth:`G2PwdrData.ref_back_peak`.
133:meth:`G2PwdrData.fit_fixed_points`                   Fits background to the specified fixed points.
134:meth:`G2PwdrData.getdata`                            Provides access to the diffraction data associated with the histogram.
135:meth:`G2PwdrData.Export`                             Writes the diffraction data into a file
136==================================================    ===============================================================================================================
137
138---------------------
139:class:`G2Image`
140---------------------
141
142  When working with images, there will be a :class:`G2Image` object for each image (also see :meth:`G2Project.add_image`  and :meth:`G2Project.images`).
143
144.. tabularcolumns:: |l|p{3.5in}|
145
146==================================================    ===============================================================================================================
147method                                                Use
148==================================================    ===============================================================================================================
149:meth:`G2Image.Recalibrate`                           Invokes a recalibration fit starting from the current Image Controls calibration coefficients.
150:meth:`G2Image.Integrate`                             Invokes an image integration All parameters Image Controls will have previously been set.
151:meth:`G2Image.setControl`                            Set an Image Controls parameter in the current image.
152:meth:`G2Image.getControl`                            Return an Image Controls parameter in the current image.
153:meth:`G2Image.findControl`                           Get the names of Image Controls parameters.
154:meth:`G2Image.loadControls`                          Load controls from a .imctrl file (also see :meth:`G2Image.saveControls`).
155:meth:`G2Image.loadMasks`                             Load masks from a .immask file.
156:meth:`G2Image.setVary`                               Set a refinement flag for Image Controls parameter in the current image. (Also see :meth:`G2Image.getVary`)
157:meth:`G2Image.setCalibrant`                          Set a calibrant type (or show choices) for the current image.
158:meth:`G2Image.setControlFile`                        Set a image to be used as a background/dark/gain map image.
159==================================================    ===============================================================================================================
160
161----------------------
162:class:`G2AtomRecord`
163----------------------
164
165  When working with phases, :class:`G2AtomRecord` objects provide access to the contents of each atom in a phase. This provides access to "properties" that can be
166  used to get values of much of the atoms associated settings: label, type, refinement_flags, coordinates, occupancy, ranId, adp_flag, and uiso. In addition,
167  refinement_flags, occupancy and uiso can be used to set values. See the class docs and source code.
168
169.. _Refinement_dicts:
170
171=====================
172Refinement parameters
173=====================
174While scripts can be written that setup refinements by changing individual parameters
175through calls to the methods associated with objects that wrap each data tree item,
176many of these actions can be combined into fairly complex dict structures to conduct refinement
177steps. Use of these dicts is required with the :ref:`CommandlineInterface`. This section of the
178documentation describes these dicts.
179
180.. _Project_dicts:
181
182-----------------------------
183Project-level Parameter Dict
184-----------------------------
185
186As noted below (:ref:`Refinement_parameters_kinds`), there are three types of refinement parameters,
187which can be accessed individually by the objects that encapsulate individual phases and histograms
188but it will often be simplest to create a composite dictionary
189that is used at the project-level. A dict is created with keys
190"set" and "clear" that can be supplied to :meth:`G2Project.set_refinement`
191(or :meth:`G2Project.do_refinements`, see :ref:`Refinement_recipe` below) that will
192determine parameter values and will determine which parameters will be refined.
193
194The specific keys and subkeys that can be used are defined in tables
195:ref:`Histogram_parameters_table`, :ref:`Phase_parameters_table` and :ref:`HAP_parameters_table`.
196
197Note that optionally a list of histograms and/or phases can be supplied in the call to
198:meth:`G2Project.set_refinement`, but if not specified, the default is to use all defined
199phases and histograms.
200
201As an example:
202
203.. code-block::  python
204
205    pardict = {'set': { 'Limits': [0.8, 12.0],
206                       'Sample Parameters': ['Absorption', 'Contrast', 'DisplaceX'],
207                       'Background': {'type': 'chebyschev', 'refine': True,
208                                      'peaks':[[0,True],[1,1,1]] }},
209              'clear': {'Instrument Parameters': ['U', 'V', 'W']}}
210    my_project.set_refinement(pardict)
211   
212.. _Refinement_recipe:
213   
214------------------------
215Refinement recipe
216------------------------
217Building on the :ref:`Project_dicts`,
218it is possible to specify a sequence of refinement actions as a list of
219these dicts and supplying this list
220as an argument to :meth:`G2Project.do_refinements`.
221
222As an example, this code performs the same actions as in the example in the section above:
223
224.. code-block::  python
225   
226    pardict = {'set': { 'Limits': [0.8, 12.0],
227                       'Sample Parameters': ['Absorption', 'Contrast', 'DisplaceX'],
228                       'Background': {'type': 'chebyschev', 'refine': True}},
229              'clear': {'Instrument Parameters': ['U', 'V', 'W']}}
230    my_project.do_refinements([pardict])
231
232However, in addition to setting a number of parameters, this example will perform a refinement as well,
233after setting the parameters. More than one refinement can be performed by including more
234than one dict in the list.
235
236In this example, two refinement steps will be performed:
237
238.. code-block::  python
239
240    my_project.do_refinements([pardict,pardict1])
241
242
243The keys defined in the following table
244may be used in a dict supplied to :meth:`G2Project.do_refinements`. Note that keys ``histograms``
245and ``phases`` are used to limit actions to specific sets of parameters within the project.
246
247========== ============================================================================
248key         explanation
249========== ============================================================================
250set                    Specifies a dict with keys and subkeys as described in the
251                       :ref:`Refinement_parameters_fmt` section. Items listed here
252                       will be set to be refined.
253clear                  Specifies a dict, as above for set, except that parameters are
254                       cleared and thus will not be refined.
255once                   Specifies a dict as above for set, except that parameters are
256                       set for the next cycle of refinement and are cleared once the
257                       refinement step is completed.
258skip                   Normally, once parameters are processed with a set/clear/once
259                       action(s), a refinement is started. If skip is defined as True
260                       (or any other value) the refinement step is not performed.
261output                 If a file name is specified for output is will be used to save
262                       the current refinement.
263histograms             Should contain a list of histogram(s) to be used for the
264                       set/clear/once action(s) on :ref:`Histogram_parameters_table` or
265                       :ref:`HAP_parameters_table`. Note that this will be
266                       ignored for :ref:`Phase_parameters_table`. Histograms may be
267                       specified as a list of strings [('PWDR ...'),...], indices
268                       [0,1,2] or as list of objects [hist1, hist2].
269phases                 Should contain a list of phase(s) to be used for the
270                       set/clear/once action(s) on :ref:`Phase_parameters_table` or
271                       :ref:`HAP_parameters_table`. Note that this will be
272                       ignored for :ref:`Histogram_parameters_table`.
273                       Phases may be specified as a list of strings
274                       [('Phase name'),...], indices [0,1,2] or as list of objects
275                       [phase0, phase2].
276call                   Specifies a function to call after a refinement is completed.
277                       The value supplied can be the object (typically a function)
278                       that will be called or a string that will evaluate (in the
279                       namespace inside :meth:`G2Project.iter_refinements` where
280                       ``self`` references the project.)
281                       Nothing is called if this is not specified.
282callargs               Provides a list of arguments that will be passed to the function
283                       in call (if any). If call is defined and callargs is not, the
284                       current <tt>G2Project</tt> is passed as a single argument.
285========== ============================================================================
286
287An example that performs a series of refinement steps follows:
288
289.. code-block::  python
290
291    reflist = [
292            {"set": { "Limits": { "low": 0.7 },
293                      "Background": { "no. coeffs": 3,
294                                      "refine": True }}},
295            {"set": { "LeBail": True,
296                      "Cell": True }},
297            {"set": { "Sample Parameters": ["DisplaceX"]}},
298            {"set": { "Instrument Parameters": ["U", "V", "W", "X", "Y"]}},
299            {"set": { "Mustrain": { "type": "uniaxial",
300                                    "refine": "equatorial",
301                                    "direction": [0, 0, 1]}}},
302            {"set": { "Mustrain": { "type": "uniaxial",
303                                    "refine": "axial"}}},
304            {"clear": { "LeBail": True},
305             "set": { "Atoms": { "Mn": "X" }}},
306            {"set": { "Atoms": { "O1": "X", "O2": "X" }}},]
307    my_project.do_refinements(reflist)
308   
309
310In this example, a separate refinement step will be performed for each dict in the list. The keyword
311"skip" can be used to specify a dict that should not include a refinement.
312Note that in the second from last refinement step, parameters are both set and cleared.
313   
314.. _Refinement_parameters_kinds:
315
316----------------------------
317Refinement parameter types
318----------------------------
319
320Note that parameters and refinement flags used in GSAS-II fall into three classes:
321
322    * **Histogram**: There will be a set of these for each dataset loaded into a
323      project file. The parameters available depend on the type of histogram
324      (Bragg-Brentano, Single-Crystal, TOF,...). Typical Histogram parameters
325      include the overall scale factor, background, instrument and sample parameters;
326      see the :ref:`Histogram_parameters_table` table for a list of the histogram
327      parameters where access has been provided.
328     
329    * **Phase**: There will be a set of these for each phase loaded into a
330      project file. While some parameters are found in all types of phases,
331      others are only found in certain types (modulated, magnetic, protein...).
332      Typical phase parameters include unit cell lengths and atomic positions; see the
333      :ref:`Phase_parameters_table` table for a list of the phase     
334      parameters where access has been provided.
335     
336    * **Histogram-and-phase** (HAP): There is a set of these for every histogram
337      that is associated with each phase, so that if there are ``N`` phases and ``M``
338      histograms, there can be ``N*M`` total sets of "HAP" parameters sets (fewer if all
339      histograms are not linked to all phases.) Typical HAP parameters include the
340      phase fractions, sample microstrain and crystallite size broadening terms,
341      hydrostatic strain perturbations of the unit cell and preferred orientation
342      values.
343      See the :ref:`HAP_parameters_table` table for the HAP parameters where access has
344      been provided.
345
346.. _Refinement_parameters_fmt:
347
348=================================
349Specifying Refinement Parameters
350=================================
351
352Refinement parameter values and flags to turn refinement on and off are specified within dictionaries,
353where the details of these dicts are organized depends on the
354type of parameter (see :ref:`Refinement_parameters_kinds`), with a different set
355of keys (as described below) for each of the three types of parameters.
356
357.. _Histogram_parameters_table:
358
359--------------------
360Histogram parameters
361--------------------
362
363This table describes the dictionaries supplied to :func:`G2PwdrData.set_refinements`
364and :func:`G2PwdrData.clear_refinements`. As an example,
365
366.. code-block::  python
367
368   hist.set_refinements({"Background": {"no.coeffs": 3, "refine": True},
369                         "Sample Parameters": ["Scale"],
370                         "Limits": [10000, 40000]})
371
372With :meth:`G2Project.do_refinements`, these parameters should be placed inside a dict with a key
373``set``, ``clear``, or ``once``. Values will be set for all histograms, unless the ``histograms``
374key is used to define specific histograms. As an example:
375
376.. code-block::  python
377
378  gsas_proj.do_refinements([
379      {'set': {
380          'Background': {'no.coeffs': 3, 'refine': True},
381          'Sample Parameters': ['Scale'],
382          'Limits': [10000, 40000]},
383      'histograms': [1,2]}
384                            ])
385
386Note that below in the Instrument Parameters section,
387related profile parameters (such as U and V) are grouped together but
388separated by commas to save space in the table.
389
390.. tabularcolumns:: |l|l|p{3.5in}|
391
392===================== ====================  =================================================
393key                   subkey                explanation
394===================== ====================  =================================================
395Limits                                      The range of 2-theta (degrees) or TOF (in
396                                            microsec) range of values to use. Can
397                                            be either a dictionary of 'low' and/or 'high',
398                                            or a list of 2 items [low, high]
399\                     low                   Sets the low limit
400\                     high                  Sets the high limit
401
402Sample Parameters                           Should be provided as a **list** of subkeys
403                                            to set or clear, e.g. ['DisplaceX', 'Scale']
404\                     Absorption
405\                     Contrast
406\                     DisplaceX             Sample displacement along the X direction
407\                     DisplaceY             Sample displacement along the Y direction
408\                     Scale                 Histogram Scale factor
409
410Background                                  Sample background. If value is a boolean,
411                                            the background's 'refine' parameter is set
412                                            to the given boolean. Usually should be a
413                                            dictionary with any of the following keys:
414\                     type                  The background model, e.g. 'chebyschev'
415\                     refine                The value of the refine flag, boolean
416\                     no. coeffs            Number of coefficients to use, integer
417\                     coeffs                List of floats, literal values for background
418\                     FixedPoints           List of (2-theta, intensity) values for fixed points
419\                     fit fixed points      If True, triggers a fit to the fixed points to
420                                            be calculated. It is calculated when this key is
421                                            detected, regardless of calls to refine.
422                      peaks                 Specifies a set of flags for refining
423                                            background peaks as a nested list. There may
424                                            be an item for each defined background peak
425                                            (or fewer) and each item is a list with the flag
426                                            values for pos,int,sig & gam (fewer than 4 values
427                                            are allowed).
428
429Instrument Parameters                       As in Sample Paramters, provide as a **list** of
430                                            subkeys to
431                                            set or clear, e.g. ['X', 'Y', 'Zero', 'SH/L']
432\                     U, V, W               Gaussian peak profile terms
433\                     X, Y, Z               Lorentzian peak profile terms
434\                     alpha, beta-0,        TOF profile terms
435                      beta-1, beta-q,
436\                     sig-0, sig-1,         TOF profile terms
437                      sig-2, sig-q
438\                     difA, difB, difC      TOF Calibration constants
439\                     Zero                  Zero shift
440\                     SH/L                  Finger-Cox-Jephcoat low-angle peak asymmetry
441\                     Polariz.              Polarization parameter
442\                     Lam                   Lambda, the incident wavelength
443===================== ====================  =================================================
444
445.. _Phase_parameters_table:
446
447----------------
448Phase parameters
449----------------
450
451This table describes the dictionaries supplied to :func:`G2Phase.set_refinements`
452and :func:`G2Phase.clear_refinements`. With :meth:`G2Project.do_refinements`,
453these parameters should be placed inside a dict with a key
454``set``, ``clear``, or ``once``. Values will be set for all phases, unless the ``phases``
455key is used to define specific phase(s).
456
457
458.. tabularcolumns:: |l|p{4.5in}|
459
460======= ==========================================================
461key                   explanation
462======= ==========================================================
463Cell                  Whether or not to refine the unit cell.
464Atoms                 Dictionary of atoms and refinement flags.
465                      Each key should be an atom label, e.g.
466                      'O3', 'Mn5', and each value should be
467                      a string defining what values to refine.
468                      Values can be any combination of 'F'
469                      for fractional occupancy, 'X' for position,
470                      and 'U' for Debye-Waller factor
471LeBail                Enables LeBail intensity extraction.
472======= ==========================================================
473
474
475.. _HAP_parameters_table:
476
477
478Histogram-and-phase parameters
479------------------------------
480
481This table describes the dictionaries supplied to :func:`G2Phase.set_HAP_refinements`
482and :func:`G2Phase.clear_HAP_refinements`. When supplied to
483:meth:`G2Project.do_refinements`, these parameters should be placed inside a dict with a key
484``set``, ``clear``, or ``once``. Values will be set for all histograms used in each phase,
485unless the ``histograms`` and ``phases`` keys are used to define specific phases and histograms.
486
487.. tabularcolumns:: |l|l|p{3.5in}|
488
489=============  ==========  ============================================================
490key             subkey                 explanation
491=============  ==========  ============================================================
492Babinet                                Should be a **list** of the following
493                                       subkeys. If not, assumes both
494                                       BabA and BabU
495\               BabA
496\               BabU
497Extinction                             Boolean, True to refine.
498HStrain                                Boolean, True to refine all appropriate
499                                       $D_ij$ terms.
500Mustrain
501\               type                   Mustrain model. One of 'isotropic',
502                                       'uniaxial', or 'generalized'. Should always
503                                       be specified.
504\              direction               For uniaxial only. A list of three
505                                       integers,
506                                       the [hkl] direction of the axis.
507\               refine                 Usually boolean, set to True to refine.
508                                       or False to clear.
509                                       For uniaxial model, can specify a value
510                                       of 'axial' or 'equatorial' to set that flag
511                                       to True or a single
512                                       boolean sets both axial and equatorial.
513Size                                   
514\               type                   Size broadening model. One of 'isotropic',
515                                       'uniaxial', or 'ellipsoid'. Should always
516                                       be specified.
517\              direction               For uniaxial only. A list of three
518                                       integers,
519                                       the [hkl] direction of the axis.
520\               refine                 Boolean, True to refine.
521\               value                  float, size value in microns
522Pref.Ori.                              Boolean, True to refine
523Show                                   Boolean, True to refine
524Use                                    Boolean, True to refine
525Scale                                  Phase fraction; Boolean, True to refine
526=============  ==========  ============================================================
527
528------------------------
529Histogram/Phase objects
530------------------------
531Each phase and powder histogram in a :class:`G2Project` object has an associated
532object. Parameters within each individual object can be turned on and off by calling
533:meth:`G2PwdrData.set_refinements` or :meth:`G2PwdrData.clear_refinements`
534for histogram parameters;
535:meth:`G2Phase.set_refinements` or :meth:`G2Phase.clear_refinements`
536for phase parameters; and :meth:`G2Phase.set_HAP_refinements` or
537: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:
538
539.. code-block::  python
540
541    params = { 'Limits': [0.8, 12.0],
542               'Sample Parameters': ['Absorption', 'Contrast', 'DisplaceX'],
543               'Background': {'type': 'chebyschev', 'refine': True}}
544    some_histogram.set_refinements(params)
545
546Likewise to turn refinement flags on, use code such as this:
547
548.. code-block::  python
549
550    params = { 'Instrument Parameters': ['U', 'V', 'W']}
551    some_histogram.set_refinements(params)
552
553and to turn these refinement flags, off use this (Note that the
554``.clear_refinements()`` methods will usually will turn off refinement even
555if a refinement parameter is set in the dict to True.):
556
557.. code-block::  python
558
559    params = { 'Instrument Parameters': ['U', 'V', 'W']}
560    some_histogram.clear_refinements(params)
561
562For phase parameters, use code such as this:
563   
564.. code-block::  python
565
566    params = { 'LeBail': True, 'Cell': True,
567               'Atoms': { 'Mn1': 'X',
568                          'O3': 'XU',
569                          'V4': 'FXU'}}
570    some_histogram.set_refinements(params)
571
572
573and here is an example for HAP parameters:
574
575.. code-block::  python
576
577    params = { 'Babinet': 'BabA',
578               'Extinction': True,
579               'Mustrain': { 'type': 'uniaxial',
580                             'direction': [0, 0, 1],
581                             'refine': True}}
582    some_phase.set_HAP_refinements(params)
583
584Note that the parameters must match the object type and method (phase vs. histogram vs. HAP).
585
586.. _CommandlineInterface:
587
588=======================================
589GSASIIscriptable Command-line Interface
590=======================================
591
592The routines described above are intended to be called from a Python script, but an
593alternate way to access some of the same functionality is to
594invoke the ``GSASIIscriptable.py`` script from
595the command line usually from within a shell script or batch file. This
596will usually be done with a command such as::
597
598       python <path/>GSASIIscriptable.py <subcommand> <file.gpx> <options>
599
600    The following subcommands are defined:
601
602        * create, see :func:`create`
603        * add, see :func:`add`
604        * dump, see :func:`dump`
605        * refine, see :func:`refine`
606        * seqrefine, see :func:`seqrefine`
607        * export, :func:`export`
608        * browse, see :func:`IPyBrowse`
609       
610Run::
611
612   python GSASIIscriptable.py --help
613
614to show the available subcommands, and inspect each subcommand with
615`python GSASIIscriptable.py <subcommand> --help` or see the documentation for each of the above routines.
616
617.. _JsonFormat:
618
619-------------------------
620Parameters in JSON files
621-------------------------
622
623The refine command requires two inputs: an existing GSAS-II project (.gpx) file and
624a JSON format file
625(see `Introducing JSON <http://json.org/>`_) that contains a single dict.
626This dict may have two keys:
627
628refinements:
629  This defines the a set of refinement steps in a JSON representation of a
630  :ref:`Refinement_recipe` list.
631
632code:
633  This optionally defines Python code that will be executed after the project is loaded,
634  but before the refinement is started. This can be used to execute Python code to change
635  parameters that are not accessible via a :ref:`Refinement_recipe` dict (note that the
636  project object is accessed with variable ``proj``) or to define code that will be called
637  later (see key ``call`` in the :ref:`Refinement_recipe` section.)
638   
639JSON website: `Introducing JSON <http://json.org/>`_.
640
641.. _API:
642
643============================================================
644API: Complete Documentation
645============================================================
646
647The large number of classes and modules in this module are described below.
648A script will have one or more G2Project objects using :class:`G2Project` and then
649perform actions such as adding a histogram (method :meth:`G2Project.add_powder_histogram`),
650adding a phase (method :meth:`G2Project.add_phase`),
651or setting parameters and performing a refinement
652(method :meth:`G2Project.do_refinements`).
653
654To change settings within histograms, images and phases, one usually needs to use
655methods inside :class:`G2PwdrData`, :class:`G2Image` or :class:`G2Phase`.
656
657"""
658from __future__ import division, print_function
659import argparse
660import os.path as ospath
661import datetime as dt
662import sys
663import platform
664if '2' in platform.python_version_tuple()[0]:
665    import cPickle
666    strtypes = (str,unicode)
667else:
668    import pickle as cPickle
669    strtypes = (str,bytes)
670import imp
671import copy
672import os
673import random as ran
674
675import numpy.ma as ma
676import scipy.interpolate as si
677import numpy as np
678import scipy as sp
679
680import GSASIIpath
681GSASIIpath.SetBinaryPath(True)  # for now, this is needed before some of these modules can be imported
682import GSASIIobj as G2obj
683import GSASIIpwd as G2pwd
684import GSASIIstrMain as G2strMain
685#import GSASIIIO as G2IO
686import GSASIIstrIO as G2strIO
687import GSASIIspc as G2spc
688import GSASIIElem as G2elem
689import GSASIIfiles as G2fil
690import GSASIIimage as G2img
691
692# Delay imports to not slow down small scripts that don't need them
693Readers = {'Pwdr':[], 'Phase':[], 'Image':[]}
694'''Readers by reader type'''
695exportersByExtension = {}
696'''Specifies the list of extensions that are supported for Powder data export'''
697
698def LoadG2fil():
699    """Setup GSAS-II importers. Delay importing this module, it is slow"""
700    if len(Readers['Pwdr']) > 0: return
701    # initialize imports
702    Readers['Pwdr'] = G2fil.LoadImportRoutines("pwd", "Powder_Data")
703    Readers['Phase'] = G2fil.LoadImportRoutines("phase", "Phase")
704    Readers['Image'] = G2fil.LoadImportRoutines("img", "Image")
705
706    # initialize exports
707    for obj in exportersByExtension:
708        try:
709            obj.Writer
710        except AttributeError:
711            continue
712        for typ in obj.exporttype:
713            if typ not in exportersByExtension:
714                exportersByExtension[typ] = {obj.extension:obj}
715            else:
716                exportersByExtension[typ][obj.extension] = obj
717
718def LoadDictFromProjFile(ProjFile):
719    '''Read a GSAS-II project file and load items to dictionary
720   
721    :param str ProjFile: GSAS-II project (name.gpx) full file name
722    :returns: Project,nameList, where
723
724      * Project (dict) is a representation of gpx file following the GSAS-II tree structure
725        for each item: key = tree name (e.g. 'Controls','Restraints',etc.), data is dict
726        data dict = {'data':item data whch may be list, dict or None,'subitems':subdata (if any)}
727      * nameList (list) has names of main tree entries & subentries used to reconstruct project file
728
729    Example for fap.gpx::
730
731      Project = {                 #NB:dict order is not tree order
732        'Phases':{'data':None,'fap':{phase dict}},
733        'PWDR FAP.XRA Bank 1':{'data':[histogram data list],'Comments':comments,'Limits':limits, etc},
734        'Rigid bodies':{'data': {rigid body dict}},
735        'Covariance':{'data':{covariance data dict}},
736        'Controls':{'data':{controls data dict}},
737        'Notebook':{'data':[notebook list]},
738        'Restraints':{'data':{restraint data dict}},
739        'Constraints':{'data':{constraint data dict}}]
740        }
741      nameList = [                #NB: reproduces tree order
742        ['Notebook',],
743        ['Controls',],
744        ['Covariance',],
745        ['Constraints',],
746        ['Restraints',],
747        ['Rigid bodies',],
748        ['PWDR FAP.XRA Bank 1',
749             'Comments',
750             'Limits',
751             'Background',
752             'Instrument Parameters',
753             'Sample Parameters',
754             'Peak List',
755             'Index Peak List',
756             'Unit Cells List',
757             'Reflection Lists'],
758        ['Phases', 'fap']
759        ]
760    '''
761    # Let IOError be thrown if file does not exist
762    if not ospath.exists(ProjFile):
763        print ('\n*** Error attempt to open project file that does not exist: \n    {}'.
764                   format(ProjFile))
765        raise IOError('GPX file {} does not exist'.format(ProjFile))
766    try:
767        Project, nameList = G2strIO.GetFullGPX(ProjFile)
768    except Exception as msg:
769        raise IOError(msg)
770    return Project,nameList
771
772def SaveDictToProjFile(Project,nameList,ProjFile):
773    '''Save a GSAS-II project file from dictionary/nameList created by LoadDictFromProjFile
774
775    :param dict Project: representation of gpx file following the GSAS-II
776        tree structure as described for LoadDictFromProjFile
777    :param list nameList: names of main tree entries & subentries used to reconstruct project file
778    :param str ProjFile: full file name for output project.gpx file (including extension)
779    '''
780    file = open(ProjFile,'wb')
781    try:
782        for name in nameList:
783            data = []
784            item = Project[name[0]]
785            data.append([name[0],item['data']])
786            for item2 in name[1:]:
787                data.append([item2,item[item2]])
788            cPickle.dump(data,file,1)
789    finally:
790        file.close()
791    print('gpx file saved as %s'%ProjFile)
792
793# def ImportPowder(reader,filename):
794#     '''Use a reader to import a powder diffraction data file
795
796#     :param str reader: a scriptable reader
797#     :param str filename: full name of powder data file; can be "multi-Bank" data
798
799#     :returns: list rdlist: list of reader objects containing powder data, one for each
800#         "Bank" of data encountered in file. Items in reader object of interest are:
801
802#           * rd.comments: list of str: comments found on powder file
803#           * rd.dnames: list of str: data nammes suitable for use in GSASII data tree NB: duplicated in all rd entries in rdlist
804#           * rd.powderdata: list of numpy arrays: pos,int,wt,zeros,zeros,zeros as needed for a PWDR entry in  GSASII data tree.
805#     '''
806#     rdfile,rdpath,descr = imp.find_module(reader)
807#     rdclass = imp.load_module(reader,rdfile,rdpath,descr)
808#     rd = rdclass.GSAS_ReaderClass()
809#     if not rd.scriptable:
810#         print(u'**** ERROR: '+reader+u' is not a scriptable reader')
811#         return None
812#     rdlist = []
813#     if rd.ContentsValidator(filename):
814#         repeat = True
815#         rdbuffer = {} # create temporary storage for file reader
816#         block = 0
817#         while repeat: # loop if the reader asks for another pass on the file
818#             block += 1
819#             repeat = False
820#             rd.objname = ospath.basename(filename)
821#             flag = rd.Reader(filename,None,buffer=rdbuffer,blocknum=block,)
822#             if flag:
823#                 rdlist.append(copy.deepcopy(rd)) # save the result before it is written over
824#                 if rd.repeat:
825#                     repeat = True
826#         return rdlist
827#     print(rd.errors)
828#     return None
829
830def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
831    '''Create an initial Histogram dictionary
832
833    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
834    '''
835    if dType in ['SXC','SNC']:
836        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
837            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
838            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
839            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}],
840            'Flack':[0.0,False]}
841    elif dType == 'SNT':
842        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
843            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
844            'Extinction':['Lorentzian','None', {
845            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
846    elif 'P' in dType:
847        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
848            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{},[],0.1],
849            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
850                [1.,1.,1.,0.,0.,0.],6*[False,]],
851            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
852                NShkl*[0.01,],NShkl*[False,]],
853            'HStrain':[NDij*[0.0,],NDij*[False,]],
854            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
855
856
857def PreSetup(data):
858    '''Create part of an initial (empty) phase dictionary
859
860    from GSASIIphsGUI.py, near end of UpdatePhaseData
861
862    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
863    '''
864    if 'RBModels' not in data:
865        data['RBModels'] = {}
866    if 'MCSA' not in data:
867        data['MCSA'] = {'Models':[{'Type':'MD','Coef':[1.0,False,[.8,1.2],],'axis':[0,0,1]}],'Results':[],'AtInfo':{}}
868    if 'dict' in str(type(data['MCSA']['Results'])):
869        data['MCSA']['Results'] = []
870    if 'Modulated' not in data['General']:
871        data['General']['Modulated'] = False
872#    if 'modulated' in data['General']['Type']:
873#        data['General']['Modulated'] = True
874#        data['General']['Type'] = 'nuclear'
875
876
877def SetupGeneral(data, dirname):
878    """Helps initialize phase data.
879
880    From GSASIIphsGui.py, function of the same name. Minor changes for imports etc.
881
882    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
883    """
884    mapDefault = {'MapType':'','RefList':'','Resolution':0.5,'Show bonds':True,
885                'rho':[],'rhoMax':0.,'mapSize':10.0,'cutOff':50.,'Flip':False}
886    generalData = data['General']
887    atomData = data['Atoms']
888    generalData['AtomTypes'] = []
889    generalData['Isotopes'] = {}
890
891    if 'Isotope' not in generalData:
892        generalData['Isotope'] = {}
893    if 'Data plot type' not in generalData:
894        generalData['Data plot type'] = 'Mustrain'
895    if 'POhkl' not in generalData:
896        generalData['POhkl'] = [0,0,1]
897    if 'Map' not in generalData:
898        generalData['Map'] = mapDefault.copy()
899    if 'Flip' not in generalData:
900        generalData['Flip'] = {'RefList':'','Resolution':0.5,'Norm element':'None',
901            'k-factor':0.1,'k-Max':20.,}
902    if 'testHKL' not in generalData['Flip']:
903        generalData['Flip']['testHKL'] = [[0,0,2],[2,0,0],[1,1,1],[0,2,0],[1,2,3]]
904    if 'doPawley' not in generalData:
905        generalData['doPawley'] = False     #ToDo: change to ''
906    if 'Pawley dmin' not in generalData:
907        generalData['Pawley dmin'] = 1.0
908    if 'Pawley neg wt' not in generalData:
909        generalData['Pawley neg wt'] = 0.0
910    if 'Algolrithm' in generalData.get('MCSA controls',{}) or \
911        'MCSA controls' not in generalData:
912        generalData['MCSA controls'] = {'Data source':'','Annealing':[50.,0.001,50],
913        'dmin':2.0,'Algorithm':'log','Jump coeff':[0.95,0.5],'boltzmann':1.0,
914        'fast parms':[1.0,1.0,1.0],'log slope':0.9,'Cycles':1,'Results':[],'newDmin':True}
915    if 'AtomPtrs' not in generalData:
916        generalData['AtomPtrs'] = [3,1,7,9]
917        if generalData['Type'] == 'macromolecular':
918            generalData['AtomPtrs'] = [6,4,10,12]
919        elif generalData['Type'] == 'magnetic':
920            generalData['AtomPtrs'] = [3,1,10,12]
921    if generalData['Modulated']:
922        generalData['Type'] = 'nuclear'
923        if 'Super' not in generalData:
924            generalData['Super'] = 1
925            generalData['SuperVec'] = [[0,0,.1],False,4]
926            generalData['SSGData'] = {}
927        if '4DmapData' not in generalData:
928            generalData['4DmapData'] = mapDefault.copy()
929            generalData['4DmapData'].update({'MapType':'Fobs'})
930    if 'Modulated' not in generalData:
931        generalData['Modulated'] = False
932    if 'HydIds' not in generalData:
933        generalData['HydIds'] = {}
934    cx,ct,cs,cia = generalData['AtomPtrs']
935    generalData['NoAtoms'] = {}
936    generalData['BondRadii'] = []
937    generalData['AngleRadii'] = []
938    generalData['vdWRadii'] = []
939    generalData['AtomMass'] = []
940    generalData['Color'] = []
941    if generalData['Type'] == 'magnetic':
942        generalData['MagDmin'] = generalData.get('MagDmin',1.0)
943        landeg = generalData.get('Lande g',[])
944    generalData['Mydir'] = dirname
945    badList = {}
946    for iat,atom in enumerate(atomData):
947        atom[ct] = atom[ct].lower().capitalize()              #force to standard form
948        if generalData['AtomTypes'].count(atom[ct]):
949            generalData['NoAtoms'][atom[ct]] += atom[cx+3]*float(atom[cs+1])
950        elif atom[ct] != 'UNK':
951            Info = G2elem.GetAtomInfo(atom[ct])
952            if not Info:
953                if atom[ct] not in badList:
954                    badList[atom[ct]] = 0
955                badList[atom[ct]] += 1
956                atom[ct] = 'UNK'
957                continue
958            atom[ct] = Info['Symbol'] # N.B. symbol might be changed by GetAtomInfo
959            generalData['AtomTypes'].append(atom[ct])
960            generalData['Z'] = Info['Z']
961            generalData['Isotopes'][atom[ct]] = Info['Isotopes']
962            generalData['BondRadii'].append(Info['Drad'])
963            generalData['AngleRadii'].append(Info['Arad'])
964            generalData['vdWRadii'].append(Info['Vdrad'])
965            if atom[ct] in generalData['Isotope']:
966                if generalData['Isotope'][atom[ct]] not in generalData['Isotopes'][atom[ct]]:
967                    isotope = list(generalData['Isotopes'][atom[ct]].keys())[-1]
968                    generalData['Isotope'][atom[ct]] = isotope
969                generalData['AtomMass'].append(Info['Isotopes'][generalData['Isotope'][atom[ct]]]['Mass'])
970            else:
971                generalData['Isotope'][atom[ct]] = 'Nat. Abund.'
972                if 'Nat. Abund.' not in generalData['Isotopes'][atom[ct]]:
973                    isotope = list(generalData['Isotopes'][atom[ct]].keys())[-1]
974                    generalData['Isotope'][atom[ct]] = isotope
975                generalData['AtomMass'].append(Info['Mass'])
976            generalData['NoAtoms'][atom[ct]] = atom[cx+3]*float(atom[cs+1])
977            generalData['Color'].append(Info['Color'])
978            if generalData['Type'] == 'magnetic':
979                if len(landeg) < len(generalData['AtomTypes']):
980                    landeg.append(2.0)
981    if generalData['Type'] == 'magnetic':
982        generalData['Lande g'] = landeg[:len(generalData['AtomTypes'])]
983
984    if badList:
985        msg = 'Warning: element symbol(s) not found:'
986        for key in badList:
987            msg += '\n\t' + key
988            if badList[key] > 1:
989                msg += ' (' + str(badList[key]) + ' times)'
990        raise G2ScriptException("Phase error:\n" + msg)
991        # wx.MessageBox(msg,caption='Element symbol error')
992    F000X = 0.
993    F000N = 0.
994    for i,elem in enumerate(generalData['AtomTypes']):
995        F000X += generalData['NoAtoms'][elem]*generalData['Z']
996        isotope = generalData['Isotope'][elem]
997        F000N += generalData['NoAtoms'][elem]*generalData['Isotopes'][elem][isotope]['SL'][0]
998    generalData['F000X'] = F000X
999    generalData['F000N'] = F000N
1000    import GSASIImath as G2mth
1001    generalData['Mass'] = G2mth.getMass(generalData)
1002
1003
1004def make_empty_project(author=None, filename=None):
1005    """Creates an dictionary in the style of GSASIIscriptable, for an empty
1006    project.
1007
1008    If no author name or filename is supplied, 'no name' and
1009    <current dir>/test_output.gpx are used , respectively.
1010
1011    Returns: project dictionary, name list
1012
1013    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1014    """
1015    if not filename:
1016        filename = 'test_output.gpx'
1017    filename = os.path.abspath(filename)
1018    gsasii_version = str(GSASIIpath.GetVersionNumber())
1019    LoadG2fil()
1020    import matplotlib as mpl
1021    python_library_versions = G2fil.get_python_versions([mpl, np, sp])
1022
1023    controls_data = dict(G2obj.DefaultControls)
1024    controls_data['LastSavedAs'] = filename
1025    controls_data['LastSavedUsing'] = gsasii_version
1026    controls_data['PythonVersions'] = python_library_versions
1027    if author:
1028        controls_data['Author'] = author
1029
1030    output = {'Constraints': {'data': {'HAP': [], 'Hist': [], 'Phase': [],
1031                                       'Global': []}},
1032              'Controls': {'data': controls_data},
1033              u'Covariance': {'data': {}},
1034              u'Notebook': {'data': ['']},
1035              u'Restraints': {'data': {}},
1036              u'Rigid bodies': {'data': {'RBIds': {'Residue': [], 'Vector': []},
1037                                'Residue': {'AtInfo': {}},
1038                                'Vector':  {'AtInfo': {}}}}}
1039
1040    names = [[u'Notebook'], [u'Controls'], [u'Covariance'],
1041             [u'Constraints'], [u'Restraints'], [u'Rigid bodies']]
1042
1043    return output, names
1044
1045
1046class G2ImportException(Exception):
1047    pass
1048
1049class G2ScriptException(Exception):
1050    pass
1051
1052def import_generic(filename, readerlist, fmthint=None, bank=None):
1053    """Attempt to import a filename, using a list of reader objects.
1054
1055    Returns the first reader object which worked."""
1056    # Translated from OnImportGeneric method in GSASII.py
1057    primaryReaders, secondaryReaders = [], []
1058    for reader in readerlist:
1059        if fmthint is not None and fmthint not in reader.formatName: continue
1060        flag = reader.ExtensionValidator(filename)
1061        if flag is None:
1062            secondaryReaders.append(reader)
1063        elif flag:
1064            primaryReaders.append(reader)
1065    if not secondaryReaders and not primaryReaders:
1066        raise G2ImportException("Could not read file: ", filename)
1067
1068    with open(filename, 'Ur') as fp:
1069        rd_list = []
1070
1071        for rd in primaryReaders + secondaryReaders:
1072            # Initialize reader
1073            rd.selections = []
1074            if bank is None:
1075                rd.selections = []
1076            else:
1077                rd.selections = [bank-1]
1078            rd.dnames = []
1079            rd.ReInitialize()
1080            # Rewind file
1081            rd.errors = ""
1082            if not rd.ContentsValidator(filename):
1083                # Report error
1084                print("Warning: File {} has a validation error, continuing".format(filename))
1085            if len(rd.selections) > 1:
1086                raise G2ImportException("File {} has {} banks. Specify which bank to read with databank param."
1087                                .format(filename,len(rd.selections)))
1088
1089            block = 0
1090            rdbuffer = {}
1091            repeat = True
1092            while repeat:
1093                repeat = False
1094                block += 1
1095                rd.objname = os.path.basename(filename)
1096                try:
1097                    flag = rd.Reader(filename,buffer=rdbuffer, blocknum=block)
1098                except:
1099                    flag = False
1100                if flag:
1101                    # Omitting image loading special cases
1102                    rd.readfilename = filename
1103                    rd_list.append(copy.deepcopy(rd))
1104                    repeat = rd.repeat
1105                else:
1106                    if GSASIIpath.GetConfigValue('debug'): print("DBG_{} Reader failed to read {}".format(rd.formatName,filename))
1107            if rd_list:
1108                if rd.warnings:
1109                    print("Read warning by", rd.formatName, "reader:",
1110                          rd.warnings, file=sys.stderr)
1111                elif bank is None:
1112                    print("{} read by Reader {}"
1113                              .format(filename,rd.formatName))
1114                else:
1115                    print("{} block # {} read by Reader {}"
1116                              .format(filename,bank,rd.formatName))
1117                return rd_list
1118    raise G2ImportException("No reader could read file: " + filename)
1119
1120
1121def load_iprms(instfile, reader, bank=None):
1122    """Loads instrument parameters from a file, and edits the
1123    given reader.
1124
1125    Returns a 2-tuple of (Iparm1, Iparm2) parameters
1126    """
1127    LoadG2fil()
1128    ext = os.path.splitext(instfile)[1]
1129
1130    if ext.lower() == '.instprm':
1131        # New GSAS File, load appropriate bank
1132        with open(instfile) as f:
1133            lines = f.readlines()
1134        if bank is None: 
1135            bank = reader.powderentry[2] 
1136        numbanks = reader.numbanks
1137        iparms = G2fil.ReadPowderInstprm(lines, bank, numbanks, reader)
1138        reader.instfile = instfile
1139        reader.instmsg = '{} (G2 fmt) bank {}'.format(instfile,bank)
1140        return iparms
1141    elif ext.lower() not in ('.prm', '.inst', '.ins'):
1142        raise ValueError('Expected .prm file, found: ', instfile)
1143
1144    # It's an old GSAS file, load appropriately
1145    Iparm = {}
1146    with open(instfile, 'Ur') as fp:
1147        for line in fp:
1148            if '#' in line:
1149                continue
1150            Iparm[line[:12]] = line[12:-1]
1151    ibanks = int(Iparm.get('INS   BANK  ', '1').strip())
1152    if bank is not None:
1153        # pull out requested bank # bank from the data, and change the bank to 1
1154        Iparm,IparmC = {},Iparm
1155        for key in IparmC:
1156            if 'INS' not in key[:3]: continue   #skip around rubbish lines in some old iparm
1157            if key[4:6] == "  ":
1158                Iparm[key] = IparmC[key]
1159            elif int(key[4:6].strip()) == bank:
1160                Iparm[key[:4]+' 1'+key[6:]] = IparmC[key]           
1161        reader.instbank = bank
1162    elif ibanks == 1:
1163        reader.instbank = 1
1164    else: 
1165        raise G2ImportException("Instrument parameter file has {} banks, select one with instbank param."
1166                                    .format(ibanks))
1167    reader.powderentry[2] = 1
1168    reader.instfile = instfile
1169    reader.instmsg = '{} bank {}'.format(instfile,reader.instbank)
1170    return G2fil.SetPowderInstParms(Iparm, reader)
1171
1172def load_pwd_from_reader(reader, instprm, existingnames=[],bank=None):
1173    """Loads powder data from a reader object, and assembles it into a GSASII data tree.
1174
1175    :returns: (name, tree) - 2-tuple of the histogram name (str), and data
1176
1177    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1178    """
1179    HistName = 'PWDR ' + G2obj.StripUnicode(reader.idstring, '_')
1180    HistName = G2obj.MakeUniqueLabel(HistName, existingnames)
1181
1182    try:
1183        Iparm1, Iparm2 = instprm
1184    except ValueError:
1185        Iparm1, Iparm2 = load_iprms(instprm, reader, bank=bank)
1186        print('Instrument parameters read:',reader.instmsg)
1187    Ymin = np.min(reader.powderdata[1])
1188    Ymax = np.max(reader.powderdata[1])
1189    valuesdict = {'wtFactor': 1.0,
1190                  'Dummy': False,
1191                  'ranId': ran.randint(0, sys.maxsize),
1192                  'Offset': [0.0, 0.0], 'delOffset': 0.02*Ymax,
1193                  'refOffset': -0.1*Ymax, 'refDelt': 0.1*Ymax,
1194                  'Yminmax': [Ymin, Ymax]}
1195    reader.Sample['ranId'] = valuesdict['ranId']
1196    if 'T' in Iparm1['Type'][0]:
1197        if not reader.clockWd and reader.GSAS:
1198            reader.powderdata[0] *= 100.        #put back the CW centideg correction
1199
1200    # Ending keys:
1201    # [u'Reflection Lists',
1202    #  u'Limits',
1203    #  'data',
1204    #  u'Index Peak List',
1205    #  u'Comments',
1206    #  u'Unit Cells List',
1207    #  u'Sample Parameters',
1208    #  u'Peak List',
1209    #  u'Background',
1210    #  u'Instrument Parameters']
1211    Tmin = np.min(reader.powderdata[0])
1212    Tmax = np.max(reader.powderdata[0])
1213
1214    default_background = [['chebyschev', False, 3, 1.0, 0.0, 0.0],
1215                          {'nDebye': 0, 'debyeTerms': [], 'nPeaks': 0, 'peaksList': []}]
1216
1217    output_dict = {u'Reflection Lists': {},
1218                   u'Limits': reader.pwdparms.get('Limits', [(Tmin, Tmax), [Tmin, Tmax]]),
1219                   u'data': [valuesdict, reader.powderdata, HistName],
1220                   u'Index Peak List': [[], []],
1221                   u'Comments': reader.comments,
1222                   u'Unit Cells List': [],
1223                   u'Sample Parameters': reader.Sample,
1224                   u'Peak List': {'peaks': [], 'sigDict': {}},
1225                   u'Background': reader.pwdparms.get('Background', default_background),
1226                   u'Instrument Parameters': [Iparm1, Iparm2],
1227                   }
1228
1229    names = [u'Comments',
1230             u'Limits',
1231             u'Background',
1232             u'Instrument Parameters',
1233             u'Sample Parameters',
1234             u'Peak List',
1235             u'Index Peak List',
1236             u'Unit Cells List',
1237             u'Reflection Lists']
1238
1239    # TODO controls?? GSASII.py:1664-7
1240
1241    return HistName, [HistName] + names, output_dict
1242
1243
1244def _deep_copy_into(from_, into):
1245    """Helper function for reloading .gpx file. See G2Project.reload()
1246
1247    :author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1248    """
1249    if isinstance(from_, dict) and isinstance(into, dict):
1250        combined_keys = set(from_.keys()).union(into.keys())
1251        for key in combined_keys:
1252            if key in from_ and key in into:
1253                both_dicts = (isinstance(from_[key], dict)
1254                              and isinstance(into[key], dict))
1255                both_lists = (isinstance(from_[key], list)
1256                              and isinstance(into[key], list))
1257                if both_dicts or both_lists:
1258                    _deep_copy_into(from_[key], into[key])
1259                else:
1260                    into[key] = from_[key]
1261            elif key in from_:
1262                into[key] = from_[key]
1263            else:  # key in into
1264                del into[key]
1265    elif isinstance(from_, list) and isinstance(into, list):
1266        if len(from_) == len(into):
1267            for i in range(len(from_)):
1268                both_dicts = (isinstance(from_[i], dict)
1269                              and isinstance(into[i], dict))
1270                both_lists = (isinstance(from_[i], list)
1271                              and isinstance(into[i], list))
1272                if both_dicts or both_lists:
1273                    _deep_copy_into(from_[i], into[i])
1274                else:
1275                    into[i] = from_[i]
1276        else:
1277            into[:] = from_
1278
1279def _getCorrImage(ImageReaderlist,proj,imageRef):
1280    '''Gets image & applies dark, background & flat background corrections.
1281    based on :func:`GSASIIimgGUI.GetImageZ`. Expected to be for internal
1282    use only.
1283
1284    :param list ImageReaderlist: list of Reader objects for images
1285    :param object ImageReaderlist: list of Reader objects for images
1286    :param imageRef: A reference to the desired image. Either the Image
1287      tree name (str), the image's index (int) or
1288      a image object (:class:`G2Image`)
1289
1290    :return: array sumImg: corrected image for background/dark/flat back
1291    '''
1292    ImgObj = proj.image(imageRef)
1293    Controls = ImgObj.data['Image Controls']
1294    formatName = Controls.get('formatName','')
1295    imagefile = ImgObj.data['data'][1]
1296    ImageTag = None # fix this for multiimage files
1297    sumImg = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1298    if sumImg is None:
1299        return []
1300    darkImg = False
1301    if 'dark image' in Controls:
1302        darkImg,darkScale = Controls['dark image']
1303        if darkImg:
1304            dImgObj = proj.image(darkImg)
1305            formatName = dImgObj.data['Image Controls'].get('formatName','')
1306            imagefile = dImgObj.data['data'][1]
1307            ImageTag = None # fix this for multiimage files
1308            darkImg = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1309            if darkImg is None:
1310                raise Exception('Error reading dark image {}'.format(imagefile))
1311            sumImg += np.array(darkImage*darkScale,dtype='int32')
1312    if 'background image' in Controls:
1313        backImg,backScale = Controls['background image']           
1314        if backImg:     #ignores any transmission effect in the background image
1315            bImgObj = proj.image(backImg)
1316            formatName = bImgObj.data['Image Controls'].get('formatName','')
1317            imagefile = bImgObj.data['data'][1]
1318            ImageTag = None # fix this for multiimage files
1319            backImg = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1320            if backImage is None:
1321                raise Exception('Error reading background image {}'.format(imagefile))
1322            if darkImg:
1323                backImage += np.array(darkImage*darkScale/backScale,dtype='int32')
1324            else:
1325                sumImg += np.array(backImage*backScale,dtype='int32')
1326    if 'Gain map' in Controls:
1327        gainMap = Controls['Gain map']
1328        if gainMap:
1329            gImgObj = proj.image(gainMap)
1330            formatName = gImgObj.data['Image Controls'].get('formatName','')
1331            imagefile = gImgObj.data['data'][1]
1332            ImageTag = None # fix this for multiimage files
1333            GMimage = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1334            if GMimage is None:
1335                raise Exception('Error reading Gain map image {}'.format(imagefile))
1336            sumImg = sumImg*GMimage/1000
1337    sumImg -= int(Controls.get('Flat Bkg',0))
1338    Imax = np.max(sumImg)
1339    Controls['range'] = [(0,Imax),[0,Imax]]
1340    return np.asarray(sumImg,dtype='int32')
1341
1342class G2ObjectWrapper(object):
1343    """Base class for all GSAS-II object wrappers.
1344
1345    The underlying GSAS-II format can be accessed as `wrapper.data`. A number
1346    of overrides are implemented so that the wrapper behaves like a dictionary.
1347
1348    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1349    """
1350    def __init__(self, datadict):
1351        self.data = datadict
1352
1353    def __getitem__(self, key):
1354        return self.data[key]
1355
1356    def __setitem__(self, key, value):
1357        self.data[key] = value
1358
1359    def __contains__(self, key):
1360        return key in self.data
1361
1362    def get(self, k, d=None):
1363        return self.data.get(k, d)
1364
1365    def keys(self):
1366        return self.data.keys()
1367
1368    def values(self):
1369        return self.data.values()
1370
1371    def items(self):
1372        return self.data.items()
1373
1374
1375class G2Project(G2ObjectWrapper):   
1376    """Represents an entire GSAS-II project.
1377
1378    :param str gpxfile: Existing .gpx file to be loaded. If nonexistent,
1379            creates an empty project.
1380    :param str author: Author's name (not yet implemented)
1381    :param str newgpx: The filename the project should be saved to in
1382            the future. If both newgpx and gpxfile are present, the project is
1383            loaded from the gpxfile, then when saved will be written to newgpx.
1384    :param str filename: Name to be used to save the project. Has same function as
1385            parameter newgpx (do not use both gpxfile and filename). Use of newgpx
1386            is preferred over filename.
1387
1388    There are two ways to initialize this object:
1389
1390    >>> # Load an existing project file
1391    >>> proj = G2Project('filename.gpx')
1392   
1393    >>> # Create a new project
1394    >>> proj = G2Project(newgpx='new_file.gpx')
1395   
1396    Histograms can be accessed easily.
1397
1398    >>> # By name
1399    >>> hist = proj.histogram('PWDR my-histogram-name')
1400   
1401    >>> # Or by index
1402    >>> hist = proj.histogram(0)
1403    >>> assert hist.id == 0
1404   
1405    >>> # Or by random id
1406    >>> assert hist == proj.histogram(hist.ranId)
1407
1408    Phases can be accessed the same way.
1409
1410    >>> phase = proj.phase('name of phase')
1411
1412    New data can also be loaded via :meth:`~G2Project.add_phase` and
1413    :meth:`~G2Project.add_powder_histogram`.
1414
1415    >>> hist = proj.add_powder_histogram('some_data_file.chi',
1416                                         'instrument_parameters.prm')
1417    >>> phase = proj.add_phase('my_phase.cif', histograms=[hist])
1418
1419    Parameters for Rietveld refinement can be turned on and off as well.
1420    See :meth:`~G2Project.set_refinement`, :meth:`~G2Project.clear_refinements`,
1421    :meth:`~G2Project.iter_refinements`, :meth:`~G2Project.do_refinements`.
1422    """
1423    def __init__(self, gpxfile=None, author=None, filename=None, newgpx=None):
1424        if filename is not None and newgpx is not None:
1425            raise G2ScriptException('Do not use filename and newgpx together')
1426        elif newgpx is not None:
1427            filename = newgpx
1428        if gpxfile is None:
1429            filename = os.path.abspath(os.path.expanduser(filename))
1430            self.filename = filename
1431            self.data, self.names = make_empty_project(author=author, filename=filename)
1432        elif isinstance(gpxfile, str): # TODO: create replacement for isinstance that checks if path exists
1433                                       # filename is valid, etc.
1434            if isinstance(filename, str): 
1435                self.filename = os.path.abspath(os.path.expanduser(filename)) # both are defined
1436            else: 
1437                self.filename = os.path.abspath(os.path.expanduser(gpxfile))
1438            # TODO set author
1439            self.data, self.names = LoadDictFromProjFile(gpxfile)
1440            self.update_ids()
1441        else:
1442            raise ValueError("Not sure what to do with gpxfile")
1443
1444    @classmethod
1445    def from_dict_and_names(cls, gpxdict, names, filename=None):
1446        """Creates a :class:`G2Project` directly from
1447        a dictionary and a list of names. If in doubt, do not use this.
1448
1449        :returns: a :class:`G2Project`
1450        """
1451        out = cls()
1452        if filename:
1453            filename = os.path.abspath(os.path.expanduser(filename))
1454            out.filename = filename
1455            gpxdict['Controls']['data']['LastSavedAs'] = filename
1456        else:
1457            try:
1458                out.filename = gpxdict['Controls']['data']['LastSavedAs']
1459            except KeyError:
1460                out.filename = None
1461        out.data = gpxdict
1462        out.names = names
1463
1464    def save(self, filename=None):
1465        """Saves the project, either to the current filename, or to a new file.
1466
1467        Updates self.filename if a new filename provided"""
1468        # TODO update LastSavedUsing ?
1469        if filename:
1470            filename = os.path.abspath(os.path.expanduser(filename))
1471            self.data['Controls']['data']['LastSavedAs'] = filename
1472            self.filename = filename
1473        elif not self.filename:
1474            raise AttributeError("No file name to save to")
1475        SaveDictToProjFile(self.data, self.names, self.filename)
1476
1477    def add_powder_histogram(self, datafile, iparams, phases=[], fmthint=None,
1478                                 databank=None, instbank=None):
1479        """Loads a powder data histogram into the project.
1480
1481        Automatically checks for an instrument parameter file, or one can be
1482        provided. Note that in unix fashion, "~" can be used to indicate the
1483        home directory (e.g. ~/G2data/data.fxye).
1484
1485        :param str datafile: The powder data file to read, a filename.
1486        :param str iparams: The instrument parameters file, a filename.
1487        :param list phases: Phases to link to the new histogram
1488        :param str fmthint: If specified, only importers where the format name
1489          (reader.formatName, as shown in Import menu) contains the
1490          supplied string will be tried as importers. If not specified, all
1491          importers consistent with the file extension will be tried
1492          (equivalent to "guess format" in menu).
1493        :param int databank: Specifies a dataset number to read, if file contains
1494          more than set of data. This should be 1 to read the first bank in
1495          the file (etc.) regardless of the number on the Bank line, etc.
1496          Default is None which means there should only be one dataset in the
1497          file.
1498        :param int instbank: Specifies an instrument parameter set to read, if
1499          the instrument parameter file contains more than set of parameters.
1500          This will match the INS # in an GSAS type file so it will typically
1501          be 1 to read the first parameter set in the file (etc.)
1502          Default is None which means there should only be one parameter set
1503          in the file.
1504
1505        :returns: A :class:`G2PwdrData` object representing
1506            the histogram
1507        """
1508        LoadG2fil()
1509        datafile = os.path.abspath(os.path.expanduser(datafile))
1510        iparams = os.path.abspath(os.path.expanduser(iparams))
1511        pwdrreaders = import_generic(datafile, Readers['Pwdr'],fmthint=fmthint,bank=databank)
1512        histname, new_names, pwdrdata = load_pwd_from_reader(
1513                                          pwdrreaders[0], iparams,
1514                                          [h.name for h in self.histograms()],bank=instbank)
1515        if histname in self.data:
1516            print("Warning - redefining histogram", histname)
1517        elif self.names[-1][0] == 'Phases':
1518            self.names.insert(-1, new_names)
1519        else:
1520            self.names.append(new_names)
1521        self.data[histname] = pwdrdata
1522        self.update_ids()
1523
1524        for phase in phases:
1525            phase = self.phase(phase)
1526            self.link_histogram_phase(histname, phase)
1527
1528        return self.histogram(histname)
1529
1530    def add_simulated_powder_histogram(self, histname, iparams, Tmin, Tmax, Tstep,
1531                                       wavelength=None, scale=None, phases=[]):
1532        """Loads a powder data histogram into the project.
1533
1534        Requires an instrument parameter file.
1535        Note that in unix fashion, "~" can be used to indicate the
1536        home directory (e.g. ~/G2data/data.prm). The instrument parameter file
1537        will determine if the histogram is x-ray, CW neutron, TOF, etc. as well
1538        as the instrument type.
1539
1540        :param str histname: A name for the histogram to be created.
1541        :param str iparams: The instrument parameters file, a filename.
1542        :param float Tmin: Minimum 2theta or TOF (ms) for dataset to be simulated
1543        :param float Tmax: Maximum 2theta or TOF (ms) for dataset to be simulated
1544        :param float Tstep: Step size in 2theta or TOF (ms) for dataset to be simulated       
1545        :param float wavelength: Wavelength for CW instruments, overriding the value
1546           in the instrument parameters file if specified.
1547        :param float scale: Histogram scale factor which multiplies the pattern. Note that
1548           simulated noise is added to the pattern, so that if the maximum intensity is
1549           small, the noise will mask the computed pattern. The scale
1550           needs to be a large number for CW neutrons.
1551           The default, None, provides a scale of 1 for x-rays and TOF; 10,000 for CW neutrons.
1552        :param list phases: Phases to link to the new histogram. Use proj.phases() to link to
1553           all defined phases.
1554
1555        :returns: A :class:`G2PwdrData` object representing the histogram
1556        """
1557        LoadG2fil()
1558        iparams = os.path.abspath(os.path.expanduser(iparams))
1559        if not os.path.exists(iparams):
1560            raise G2ScriptException("File does not exist:"+iparams)
1561        rd = G2obj.ImportPowderData( # Initialize a base class reader
1562            extensionlist=tuple(),
1563            strictExtension=False,
1564            formatName = 'Simulate dataset',
1565            longFormatName = 'Compute a simulated pattern')
1566        rd.powderentry[0] = '' # no filename
1567        rd.powderentry[2] = 1 # only one bank
1568        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1569        rd.idstring = histname
1570        #Iparm1, Iparm2 = load_iprms(iparams, rd)
1571        if Tmax < Tmin:
1572            Tmin,Tmax = Tmax,Tmin
1573        Tstep = abs(Tstep)
1574        if 'TOF' in rd.idstring:
1575                N = (np.log(Tmax)-np.log(Tmin))/Tstep
1576                x = np.exp((np.arange(0,N))*Tstep+np.log(Tmin*1000.))
1577                N = len(x)
1578        else:           
1579                N = int((Tmax-Tmin)/Tstep)+1
1580                x = np.linspace(Tmin,Tmax,N,True)
1581                N = len(x)
1582        if N < 3:
1583            raise G2ScriptException("Error: Range is too small or step is too large, <3 points")
1584        rd.powderdata = [
1585            np.array(x), # x-axis values
1586            np.zeros_like(x), # powder pattern intensities
1587            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1588            np.zeros_like(x), # calc. intensities (zero)
1589            np.zeros_like(x), # calc. background (zero)
1590            np.zeros_like(x), # obs-calc profiles
1591            ]
1592        Tmin = rd.powderdata[0][0]
1593        Tmax = rd.powderdata[0][-1]
1594        histname, new_names, pwdrdata = load_pwd_from_reader(rd, iparams,
1595                                                            [h.name for h in self.histograms()])
1596        if histname in self.data:
1597            print("Warning - redefining histogram", histname)
1598        elif self.names[-1][0] == 'Phases':
1599            self.names.insert(-1, new_names)
1600        else:
1601            self.names.append(new_names)
1602        if scale is not None:
1603            pwdrdata['Sample Parameters']['Scale'][0] = scale
1604        elif pwdrdata['Instrument Parameters'][0]['Type'][0].startswith('PNC'):
1605            pwdrdata['Sample Parameters']['Scale'][0] = 10000.
1606        self.data[histname] = pwdrdata
1607        self.update_ids()
1608
1609        for phase in phases:
1610            phase = self.phase(phase)
1611            self.link_histogram_phase(histname, phase)
1612
1613        return self.histogram(histname)
1614   
1615    def add_phase(self, phasefile, phasename=None, histograms=[], fmthint=None):
1616        """Loads a phase into the project from a .cif file
1617
1618        :param str phasefile: The CIF file from which to import the phase.
1619        :param str phasename: The name of the new phase, or None for the default
1620        :param list histograms: The names of the histograms to associate with
1621            this phase. Use proj.histograms() to add to all histograms.
1622        :param str fmthint: If specified, only importers where the format name
1623          (reader.formatName, as shown in Import menu) contains the
1624          supplied string will be tried as importers. If not specified, all
1625          importers consistent with the file extension will be tried
1626          (equivalent to "guess format" in menu).
1627
1628        :returns: A :class:`G2Phase` object representing the
1629            new phase.
1630        """
1631        LoadG2fil()
1632        histograms = [self.histogram(h).name for h in histograms]
1633        phasefile = os.path.abspath(os.path.expanduser(phasefile))
1634
1635        # TODO handle multiple phases in a file
1636        phasereaders = import_generic(phasefile, Readers['Phase'], fmthint=fmthint)
1637        phasereader = phasereaders[0]
1638       
1639        phasename = phasename or phasereader.Phase['General']['Name']
1640        phaseNameList = [p.name for p in self.phases()]
1641        phasename = G2obj.MakeUniqueLabel(phasename, phaseNameList)
1642        phasereader.Phase['General']['Name'] = phasename
1643
1644        if 'Phases' not in self.data:
1645            self.data[u'Phases'] = { 'data': None }
1646        assert phasename not in self.data['Phases'], "phase names should be unique"
1647        self.data['Phases'][phasename] = phasereader.Phase
1648
1649        if phasereader.Constraints:
1650            Constraints = self.data['Constraints']
1651            for i in phasereader.Constraints:
1652                if isinstance(i, dict):
1653                    if '_Explain' not in Constraints:
1654                        Constraints['_Explain'] = {}
1655                    Constraints['_Explain'].update(i)
1656                else:
1657                    Constraints['Phase'].append(i)
1658
1659        data = self.data['Phases'][phasename]
1660        generalData = data['General']
1661        SGData = generalData['SGData']
1662        NShkl = len(G2spc.MustrainNames(SGData))
1663        NDij = len(G2spc.HStrainNames(SGData))
1664        Super = generalData.get('Super', 0)
1665        if Super:
1666            SuperVec = np.array(generalData['SuperVec'][0])
1667        else:
1668            SuperVec = []
1669        UseList = data['Histograms']
1670
1671        for hist in histograms:
1672            self.link_histogram_phase(hist, phasename)
1673
1674        for obj in self.names:
1675            if obj[0] == 'Phases':
1676                phasenames = obj
1677                break
1678        else:
1679            phasenames = [u'Phases']
1680            self.names.append(phasenames)
1681        phasenames.append(phasename)
1682
1683        # TODO should it be self.filename, not phasefile?
1684        SetupGeneral(data, os.path.dirname(phasefile))
1685        self.index_ids()
1686
1687        self.update_ids()
1688        return self.phase(phasename)
1689
1690    def link_histogram_phase(self, histogram, phase):
1691        """Associates a given histogram and phase.
1692
1693        .. seealso::
1694
1695            :meth:`G2Project.histogram`
1696            :meth:`G2Project.phase`"""
1697        hist = self.histogram(histogram)
1698        phase = self.phase(phase)
1699
1700        generalData = phase['General']
1701
1702        if hist.name.startswith('HKLF '):
1703            raise NotImplementedError("HKLF not yet supported")
1704        elif hist.name.startswith('PWDR '):
1705            hist['Reflection Lists'][generalData['Name']] = {}
1706            UseList = phase['Histograms']
1707            SGData = generalData['SGData']
1708            NShkl = len(G2spc.MustrainNames(SGData))
1709            NDij = len(G2spc.HStrainNames(SGData))
1710            UseList[hist.name] = SetDefaultDData('PWDR', hist.name, NShkl=NShkl, NDij=NDij)
1711            UseList[hist.name]['hId'] = hist.id
1712            for key, val in [('Use', True), ('LeBail', False),
1713                             ('newLeBail', True),
1714                             ('Babinet', {'BabA': [0.0, False],
1715                                          'BabU': [0.0, False]})]:
1716                if key not in UseList[hist.name]:
1717                    UseList[hist.name][key] = val
1718        else:
1719            raise RuntimeError("Unexpected histogram" + hist.name)
1720
1721
1722    def reload(self):
1723        """Reload self from self.filename"""
1724        data, names = LoadDictFromProjFile(self.filename)
1725        self.names = names
1726        # Need to deep copy the new data file data into the current tree,
1727        # so that any existing G2Phase, or G2PwdrData objects will still be
1728        # valid
1729        _deep_copy_into(from_=data, into=self.data)
1730
1731    def refine(self, newfile=None, printFile=None, makeBack=False):
1732        # TODO migrate to RefineCore
1733        # G2strMain.RefineCore(Controls,Histograms,Phases,restraintDict,rigidbodyDict,parmDict,varyList,
1734        #      calcControls,pawleyLookup,ifPrint,printFile,dlg)
1735        # index_ids will automatically save the project
1736        self.index_ids()
1737        # TODO G2strMain does not properly use printFile
1738        G2strMain.Refine(self.filename, makeBack=makeBack)
1739        # Reload yourself
1740        self.reload()
1741
1742    def histogram(self, histname):
1743        """Returns the histogram named histname, or None if it does not exist.
1744
1745        :param histname: The name of the histogram (str), or ranId or index.
1746        :returns: A :class:`G2PwdrData` object, or None if
1747            the histogram does not exist
1748
1749        .. seealso::
1750            :meth:`G2Project.histograms`
1751            :meth:`G2Project.phase`
1752            :meth:`G2Project.phases`
1753        """
1754        if isinstance(histname, G2PwdrData):
1755            if histname.proj == self:
1756                return histname
1757        if histname in self.data:
1758            return G2PwdrData(self.data[histname], self)
1759        try:
1760            # see if histname is an id or ranId
1761            histname = int(histname)
1762        except ValueError:
1763            return
1764
1765        for histogram in self.histograms():
1766            if histogram.id == histname or histogram.ranId == histname:
1767                return histogram
1768
1769    def histograms(self, typ=None):
1770        """Return a list of all histograms, as :class:`G2PwdrData` objects
1771
1772        For now this only finds Powder/Single Xtal histograms, since that is all that is
1773        currently implemented in this module.
1774
1775        :param ste typ: The prefix (type) the histogram such as 'PWDR '. If None
1776          (the default) all known histograms types are found.
1777        :returns: a list of objects
1778
1779        .. seealso::
1780            :meth:`G2Project.histogram`
1781            :meth:`G2Project.phase`
1782            :meth:`G2Project.phases`
1783        """
1784        output = []
1785        # loop through each tree entry. If it is more than one level (more than one item in the
1786        # list of names). then it must be a histogram, unless labeled Phases or Restraints
1787        if typ is None:
1788            for obj in self.names:
1789                if obj[0].startswith('PWDR ') or obj[0].startswith('HKLF '): 
1790                    output.append(self.histogram(obj[0]))
1791        else:
1792            for obj in self.names:
1793                if len(obj) > 1 and obj[0].startswith(typ): 
1794                    output.append(self.histogram(obj[0]))
1795        return output
1796
1797    def phase(self, phasename):
1798        """
1799        Gives an object representing the specified phase in this project.
1800
1801        :param str phasename: A reference to the desired phase. Either the phase
1802            name (str), the phase's ranId, the phase's index (both int) or
1803            a phase object (:class:`G2Phase`)
1804        :returns: A :class:`G2Phase` object
1805        :raises: KeyError
1806
1807        .. seealso::
1808            :meth:`G2Project.histograms`
1809            :meth:`G2Project.phase`
1810            :meth:`G2Project.phases`
1811            """
1812        if isinstance(phasename, G2Phase):
1813            if phasename.proj == self:
1814                return phasename
1815        phases = self.data['Phases']
1816        if phasename in phases:
1817            return G2Phase(phases[phasename], phasename, self)
1818
1819        try:
1820            # phasename should be phase index or ranId
1821            phasename = int(phasename)
1822        except ValueError:
1823            return
1824
1825        for phase in self.phases():
1826            if phase.id == phasename or phase.ranId == phasename:
1827                return phase
1828
1829    def phases(self):
1830        """
1831        Returns a list of all the phases in the project.
1832
1833        :returns: A list of :class:`G2Phase` objects
1834
1835        .. seealso::
1836            :meth:`G2Project.histogram`
1837            :meth:`G2Project.histograms`
1838            :meth:`G2Project.phase`
1839            """
1840        for obj in self.names:
1841            if obj[0] == 'Phases':
1842                return [self.phase(p) for p in obj[1:]]
1843        return []
1844
1845    def _images(self):
1846        """Returns a list of all the phases in the project.
1847        """
1848        return [i[0] for i in self.names if i[0].startswith('IMG ')]
1849   
1850    def image(self, imageRef):
1851        """
1852        Gives an object representing the specified image in this project.
1853
1854        :param str imageRef: A reference to the desired image. Either the Image
1855          tree name (str), the image's index (int) or
1856          a image object (:class:`G2Image`)
1857        :returns: A :class:`G2Image` object
1858        :raises: KeyError
1859
1860        .. seealso::
1861            :meth:`G2Project.images`
1862        """
1863        if isinstance(imageRef, G2Image):
1864            if imageRef.proj == self:
1865                return imageRef
1866            else:
1867                raise Exception("Image {} not in current selected project".format(imageRef.name))
1868        if imageRef in self._images():
1869            return G2Image(self.data[imageRef], imageRef, self)
1870
1871        try:
1872            # imageRef should be an index
1873            num = int(imageRef)
1874            imageRef = self._images()[num] 
1875            return G2Image(self.data[imageRef], imageRef, self)
1876        except ValueError:
1877            raise Exception("imageRef {} not an object, name or image index in current selected project"
1878                                .format(imageRef))
1879        except IndexError:
1880            raise Exception("imageRef {} out of range (max={}) in current selected project"
1881                                .format(imageRef,len(self._images())-1))
1882           
1883    def images(self):
1884        """
1885        Returns a list of all the images in the project.
1886
1887        :returns: A list of :class:`G2Image` objects
1888        """
1889        return [G2Image(self.data[i],i,self) for i in self._images()]
1890   
1891    def update_ids(self):
1892        """Makes sure all phases and histograms have proper hId and pId"""
1893        # Translated from GetUsedHistogramsAndPhasesfromTree,
1894        #   GSASIIdataGUI.py:4107
1895        for i, h in enumerate(self.histograms()):
1896            h.id = i
1897        for i, p in enumerate(self.phases()):
1898            p.id = i
1899
1900    def do_refinements(self, refinements, histogram='all', phase='all',
1901                       outputnames=None, makeBack=False):
1902        """Conducts one or a series of refinements according to the
1903           input provided in parameter refinements. This is a wrapper
1904           around :meth:`iter_refinements`
1905
1906        :param list refinements: A list of dictionaries specifiying changes to be made to
1907            parameters before refinements are conducted.
1908            See the :ref:`Refinement_recipe` section for how this is defined.
1909        :param str histogram: Name of histogram for refinements to be applied
1910            to, or 'all'; note that this can be overridden for each refinement
1911            step via a "histograms" entry in the dict.
1912        :param str phase: Name of phase for refinements to be applied to, or
1913            'all'; note that this can be overridden for each refinement
1914            step via a "phases" entry in the dict.
1915        :param list outputnames: Provides a list of project (.gpx) file names
1916            to use for each refinement step (specifying None skips the save step).
1917            See :meth:`save`.
1918            Note that this can be overridden using an "output" entry in the dict.
1919        :param bool makeBack: determines if a backup ).bckX.gpx) file is made
1920            before a refinement is performed. The default is False.
1921           
1922        To perform a single refinement without changing any parameters, use this
1923        call:
1924
1925        .. code-block::  python
1926       
1927            my_project.do_refinements([])
1928        """
1929       
1930        for proj in self.iter_refinements(refinements, histogram, phase,
1931                                          outputnames, makeBack):
1932            pass
1933        return self
1934
1935    def iter_refinements(self, refinements, histogram='all', phase='all',
1936                         outputnames=None, makeBack=False):
1937        """Conducts a series of refinements, iteratively. Stops after every
1938        refinement and yields this project, to allow error checking or
1939        logging of intermediate results. Parameter use is the same as for
1940        :meth:`do_refinements` (which calls this method).
1941
1942        >>> def checked_refinements(proj):
1943        ...     for p in proj.iter_refinements(refs):
1944        ...         # Track intermediate results
1945        ...         log(p.histogram('0').residuals)
1946        ...         log(p.phase('0').get_cell())
1947        ...         # Check if parameter diverged, nonsense answer, or whatever
1948        ...         if is_something_wrong(p):
1949        ...             raise Exception("I need a human!")
1950
1951           
1952        """
1953        if outputnames:
1954            if len(refinements) != len(outputnames):
1955                raise ValueError("Should have same number of outputs to"
1956                                 "refinements")
1957        else:
1958            outputnames = [None for r in refinements]
1959
1960        for output, refinedict in zip(outputnames, refinements):
1961            if 'histograms' in refinedict:
1962                hist = refinedict['histograms']
1963            else:
1964                hist = histogram
1965            if 'phases' in refinedict:
1966                ph = refinedict['phases']
1967            else:
1968                ph = phase
1969            if 'output' in refinedict:
1970                output = refinedict['output']
1971            self.set_refinement(refinedict, hist, ph)
1972            # Handle 'once' args - refinements that are disabled after this
1973            # refinement
1974            if 'once' in refinedict:
1975                temp = {'set': refinedict['once']}
1976                self.set_refinement(temp, hist, ph)
1977
1978            if output:
1979                self.save(output)
1980
1981            if 'skip' not in refinedict:
1982                self.refine(makeBack=makeBack)
1983            yield self
1984
1985            # Handle 'once' args - refinements that are disabled after this
1986            # refinement
1987            if 'once' in refinedict:
1988                temp = {'clear': refinedict['once']}
1989                self.set_refinement(temp, hist, ph)
1990            if 'call' in refinedict:
1991                fxn = refinedict['call']
1992                if callable(fxn):
1993                    fxn(*refinedict.get('callargs',[self]))
1994                elif callable(eval(fxn)):
1995                    eval(fxn)(*refinedict.get('callargs',[self]))
1996                else:
1997                    raise G2ScriptException("Error: call value {} is not callable".format(fxn))
1998
1999    def set_refinement(self, refinement, histogram='all', phase='all'):
2000        """Apply specified refinements to a given histogram(s) or phase(s).
2001
2002        :param dict refinement: The refinements to be conducted
2003        :param histogram: Specifies either 'all' (default), a single histogram or
2004          a list of histograms. Histograms may be specified as histogram objects
2005          (see :class:`G2PwdrData`), the histogram name (str) or the index number (int)
2006          of the histogram in the project, numbered starting from 0.
2007          Omitting the parameter or the string 'all' indicates that parameters in
2008          all histograms should be set.
2009        :param phase: Specifies either 'all' (default), a single phase or
2010          a list of phases. Phases may be specified as phase objects
2011          (see :class:`G2Phase`), the phase name (str) or the index number (int)
2012          of the phase in the project, numbered starting from 0.
2013          Omitting the parameter or the string 'all' indicates that parameters in
2014          all phases should be set.
2015
2016        Note that refinement parameters are categorized as one of three types:
2017
2018        1. Histogram parameters
2019        2. Phase parameters
2020        3. Histogram-and-Phase (HAP) parameters
2021       
2022        .. seealso::
2023            :meth:`G2PwdrData.set_refinements`
2024            :meth:`G2PwdrData.clear_refinements`
2025            :meth:`G2Phase.set_refinements`
2026            :meth:`G2Phase.clear_refinements`
2027            :meth:`G2Phase.set_HAP_refinements`
2028            :meth:`G2Phase.clear_HAP_refinements`"""
2029
2030        if histogram == 'all':
2031            hists = self.histograms()
2032        elif isinstance(histogram, list) or isinstance(histogram, tuple):
2033            hists = []
2034            for h in histogram:
2035                if isinstance(h, str) or isinstance(h, int):
2036                    hists.append(self.histogram(h))
2037                else:
2038                    hists.append(h)
2039        elif isinstance(histogram, str) or isinstance(histogram, int):
2040            hists = [self.histogram(histogram)]
2041        else:
2042            hists = [histogram]
2043
2044        if phase == 'all':
2045            phases = self.phases()
2046        elif isinstance(phase, list) or isinstance(phase, tuple):
2047            phases = []
2048            for ph in phase:
2049                if isinstance(ph, str) or isinstance(ph, int):
2050                    phases.append(self.phase(ph))
2051                else:
2052                    phases.append(ph)
2053        elif isinstance(phase, str) or isinstance(phase, int):
2054            phases = [self.phase(phase)]
2055        else:
2056            phases = [phase]
2057
2058        # TODO: HAP parameters:
2059        #   Babinet
2060        #   Extinction
2061        #   HStrain
2062        #   Mustrain
2063        #   Pref. Ori
2064        #   Size
2065
2066        pwdr_set = {}
2067        phase_set = {}
2068        hap_set = {}
2069        for key, val in refinement.get('set', {}).items():
2070            # Apply refinement options
2071            if G2PwdrData.is_valid_refinement_key(key):
2072                pwdr_set[key] = val
2073            elif G2Phase.is_valid_refinement_key(key):
2074                phase_set[key] = val
2075            elif G2Phase.is_valid_HAP_refinement_key(key):
2076                hap_set[key] = val
2077            else:
2078                raise ValueError("Unknown refinement key", key)
2079
2080        for hist in hists:
2081            hist.set_refinements(pwdr_set)
2082        for phase in phases:
2083            phase.set_refinements(phase_set)
2084        for phase in phases:
2085            phase.set_HAP_refinements(hap_set, hists)
2086
2087        pwdr_clear = {}
2088        phase_clear = {}
2089        hap_clear = {}
2090        for key, val in refinement.get('clear', {}).items():
2091            # Clear refinement options
2092            if G2PwdrData.is_valid_refinement_key(key):
2093                pwdr_clear[key] = val
2094            elif G2Phase.is_valid_refinement_key(key):
2095                phase_clear[key] = val
2096            elif G2Phase.is_valid_HAP_refinement_key(key):
2097                hap_set[key] = val
2098            else:
2099                raise ValueError("Unknown refinement key", key)
2100
2101        for hist in hists:
2102            hist.clear_refinements(pwdr_clear)
2103        for phase in phases:
2104            phase.clear_refinements(phase_clear)
2105        for phase in phases:
2106            phase.clear_HAP_refinements(hap_clear, hists)
2107
2108    def index_ids(self):
2109        import GSASIIstrIO as G2strIO
2110        self.save()
2111        return G2strIO.GetUsedHistogramsAndPhases(self.filename)
2112
2113    def add_constraint_raw(self, cons_scope, constr):
2114        """Adds a constraint of type consType to the project.
2115        cons_scope should be one of "Hist", "Phase", "HAP", or "Global".
2116
2117        WARNING it does not check the constraint is well-constructed"""
2118        constrs = self.data['Constraints']['data']
2119        if 'Global' not in constrs:
2120            constrs['Global'] = []
2121        constrs[cons_scope].append(constr)
2122
2123    def hold_many(self, vars, type):
2124        """Apply holds for all the variables in vars, for constraint of a given type.
2125
2126        type is passed directly to add_constraint_raw as consType
2127
2128        :param list vars: A list of variables to hold. Either :class:`GSASIIobj.G2VarObj` objects,
2129            string variable specifiers, or arguments for :meth:`make_var_obj`
2130        :param str type: A string constraint type specifier. See
2131            :class:`G2Project.add_constraint_raw`
2132
2133        """
2134        for var in vars:
2135            if isinstance(var, str):
2136                var = self.make_var_obj(var)
2137            elif not isinstance(var, G2obj.G2VarObj):
2138                var = self.make_var_obj(*var)
2139            self.add_constraint_raw(type, [[1.0, var], None, None, 'h'])
2140
2141    def make_var_obj(self, phase=None, hist=None, varname=None, atomId=None,
2142                     reloadIdx=True):
2143        """Wrapper to create a G2VarObj. Takes either a string representaiton ("p:h:name:a")
2144        or individual names of phase, histogram, varname, and atomId.
2145
2146        Automatically converts string phase, hist, or atom names into the ID required
2147        by G2VarObj."""
2148
2149        if reloadIdx:
2150            self.index_ids()
2151
2152        # If string representation, short circuit
2153        if hist is None and varname is None and atomId is None:
2154            if isinstance(phase, str) and ':' in phase:
2155                return G2obj.G2VarObj(phase)
2156
2157        # Get phase index
2158        phaseObj = None
2159        if isinstance(phase, G2Phase):
2160            phaseObj = phase
2161            phase = G2obj.PhaseRanIdLookup[phase.ranId]
2162        elif phase in self.data['Phases']:
2163            phaseObj = self.phase(phase)
2164            phaseRanId = phaseObj.ranId
2165            phase = G2obj.PhaseRanIdLookup[phaseRanId]
2166
2167        # Get histogram index
2168        if isinstance(hist, G2PwdrData):
2169            hist = G2obj.HistRanIdLookup[hist.ranId]
2170        elif hist in self.data:
2171            histRanId = self.histogram(hist).ranId
2172            hist = G2obj.HistRanIdLookup[histRanId]
2173
2174        # Get atom index (if any)
2175        if isinstance(atomId, G2AtomRecord):
2176            atomId = G2obj.AtomRanIdLookup[phase][atomId.ranId]
2177        elif phaseObj:
2178            atomObj = phaseObj.atom(atomId)
2179            if atomObj:
2180                atomRanId = atomObj.ranId
2181                atomId = G2obj.AtomRanIdLookup[phase][atomRanId]
2182
2183        return G2obj.G2VarObj(phase, hist, varname, atomId)
2184
2185    def add_image(self, imagefile, fmthint=None, defaultImage=None):
2186        """Load an image into a project
2187
2188        :param str imagefile: The image file to read, a filename.
2189        :param str fmthint: If specified, only importers where the format name
2190          (reader.formatName, as shown in Import menu) contains the
2191          supplied string will be tried as importers. If not specified, all
2192          importers consistent with the file extension will be tried
2193          (equivalent to "guess format" in menu).
2194        :param str defaultImage: The name of an image to use as a default for
2195          setting parameters for the image file to read.
2196
2197        :returns: a list of G2Image object for the added image(s) [this routine
2198          has not yet been tested with files with multiple images...]
2199        """
2200        LoadG2fil()
2201        imagefile = os.path.abspath(os.path.expanduser(imagefile))
2202        readers = import_generic(imagefile, Readers['Image'], fmthint=fmthint)
2203        objlist = []
2204        for rd in readers:
2205            if rd.SciPy:        #was default read by scipy; needs 1 time fixes
2206                print('TODO: Image {} read by SciPy. Parameters likely need editing'.format(imagefile))
2207                #see this: G2IO.EditImageParms(self,rd.Data,rd.Comments,rd.Image,imagefile)
2208                rd.SciPy = False
2209            rd.readfilename = imagefile
2210            if rd.repeatcount == 1 and not rd.repeat: # skip image number if only one in set
2211                rd.Data['ImageTag'] = None
2212            else:
2213                rd.Data['ImageTag'] = rd.repeatcount
2214            rd.Data['formatName'] = rd.formatName
2215            if rd.sumfile:
2216                rd.readfilename = rd.sumfile
2217            # Load generic metadata, as configured
2218            G2fil.GetColumnMetadata(rd)
2219            # Code from G2IO.LoadImage2Tree(rd.readfilename,self,rd.Comments,rd.Data,rd.Npix,rd.Image)
2220            Imax = np.amax(rd.Image)
2221            ImgNames = [i[0] for i in self.names if i[0].startswith('IMG ')]
2222            TreeLbl = 'IMG '+os.path.basename(imagefile)
2223            ImageTag = rd.Data.get('ImageTag')
2224            if ImageTag:
2225                TreeLbl += ' #'+'%04d'%(ImageTag)
2226                imageInfo = (imagefile,ImageTag)
2227            else:
2228                imageInfo = imagefile
2229            TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
2230            # MT dict to contain image info
2231            ImgDict = {}
2232            ImgDict['data'] = [rd.Npix,imageInfo]
2233            ImgDict['Comments'] = rd.Comments
2234            if defaultImage:
2235                if isinstance(defaultImage, G2Image):
2236                    if defaultImage.proj == self:
2237                        defaultImage = self.data[defaultImage.name]['data']
2238                    else:
2239                        raise Exception("Image {} not in current selected project".format(defaultImage.name))
2240                elif defaultImage in self._images():
2241                    defaultImage = self.data[defaultImage]['data']
2242                else:
2243                    defaultImage = None
2244            Data = rd.Data
2245            if defaultImage:
2246                Data = copy.copy(defaultImage)
2247                Data['showLines'] = True
2248                Data['ring'] = []
2249                Data['rings'] = []
2250                Data['cutoff'] = 10.
2251                Data['pixLimit'] = 20
2252                Data['edgemin'] = 100000000
2253                Data['calibdmin'] = 0.5
2254                Data['calibskip'] = 0
2255                Data['ellipses'] = []
2256                Data['calibrant'] = ''
2257                Data['GonioAngles'] = [0.,0.,0.]
2258                Data['DetDepthRef'] = False
2259            else:
2260                Data['type'] = 'PWDR'
2261                Data['color'] = GSASIIpath.GetConfigValue('Contour_color','Paired')
2262                if 'tilt' not in Data:          #defaults if not preset in e.g. Bruker importer
2263                    Data['tilt'] = 0.0
2264                    Data['rotation'] = 0.0
2265                    Data['pixLimit'] = 20
2266                    Data['calibdmin'] = 0.5
2267                    Data['cutoff'] = 10.
2268                Data['showLines'] = False
2269                Data['calibskip'] = 0
2270                Data['ring'] = []
2271                Data['rings'] = []
2272                Data['edgemin'] = 100000000
2273                Data['ellipses'] = []
2274                Data['GonioAngles'] = [0.,0.,0.]
2275                Data['DetDepth'] = 0.
2276                Data['DetDepthRef'] = False
2277                Data['calibrant'] = ''
2278                Data['IOtth'] = [5.0,50.0]
2279                Data['LRazimuth'] = [0.,180.]
2280                Data['azmthOff'] = 0.0
2281                Data['outChannels'] = 2500
2282                Data['outAzimuths'] = 1
2283                Data['centerAzm'] = False
2284                Data['fullIntegrate'] = GSASIIpath.GetConfigValue('fullIntegrate',True)
2285                Data['setRings'] = False
2286                Data['background image'] = ['',-1.0]                           
2287                Data['dark image'] = ['',-1.0]
2288                Data['Flat Bkg'] = 0.0
2289                Data['Oblique'] = [0.5,False]
2290            Data['setDefault'] = False
2291            Data['range'] = [(0,Imax),[0,Imax]]
2292            ImgDict['Image Controls'] = Data
2293            ImgDict['Masks'] = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],
2294                                'Frames':[],'Thresholds':[(0,Imax),[0,Imax]]}
2295            ImgDict['Stress/Strain']  = {'Type':'True','d-zero':[],'Sample phi':0.0,
2296                                             'Sample z':0.0,'Sample load':0.0}
2297            self.names.append([TreeName]+['Comments','Image Controls','Masks','Stress/Strain'])
2298            self.data[TreeName] = ImgDict
2299            del rd.Image
2300            objlist.append(G2Image(self.data[TreeName], TreeName, self))
2301        return objlist
2302
2303class G2AtomRecord(G2ObjectWrapper):
2304    """Wrapper for an atom record. Has convenient accessors via @property.
2305
2306
2307    Available accessors: label, type, refinement_flags, coordinates,
2308        occupancy, ranId, id, adp_flag, uiso
2309
2310    Example:
2311
2312    >>> atom = some_phase.atom("O3")
2313    >>> # We can access the underlying data format
2314    >>> atom.data
2315    ['O3', 'O-2', '', ... ]
2316    >>> # We can also use wrapper accessors
2317    >>> atom.coordinates
2318    (0.33, 0.15, 0.5)
2319    >>> atom.refinement_flags
2320    u'FX'
2321    >>> atom.ranId
2322    4615973324315876477
2323    >>> atom.occupancy
2324    1.0
2325    """
2326    def __init__(self, data, indices, proj):
2327        self.data = data
2328        self.cx, self.ct, self.cs, self.cia = indices
2329        self.proj = proj
2330
2331    @property
2332    def label(self):
2333        '''Get the associated atom's label
2334        '''
2335        return self.data[self.ct-1]
2336
2337    @property
2338    def type(self):
2339        '''Get the associated atom's type
2340        '''
2341        return self.data[self.ct]
2342
2343    @property
2344    def refinement_flags(self):
2345        '''Get or set refinement flags for the associated atom
2346        '''
2347        return self.data[self.ct+1]
2348
2349    @refinement_flags.setter
2350    def refinement_flags(self, other):
2351        # Automatically check it is a valid refinement
2352        for c in other:
2353            if c not in ' FXU':
2354                raise ValueError("Invalid atom refinement: ", other)
2355        self.data[self.ct+1] = other
2356
2357    @property
2358    def coordinates(self):
2359        '''Get the associated atom's coordinates
2360        '''
2361        return tuple(self.data[self.cx:self.cx+3])
2362
2363    @property
2364    def occupancy(self):
2365        '''Get or set the associated atom's occupancy fraction
2366        '''
2367        return self.data[self.cx+3]
2368
2369    @occupancy.setter
2370    def occupancy(self, val):
2371        self.data[self.cx+3] = float(val)
2372
2373    @property
2374    def ranId(self):
2375        '''Get the associated atom's Random Id number
2376        '''
2377        return self.data[self.cia+8]
2378
2379    @property
2380    def adp_flag(self):
2381        '''Get the associated atom's iso/aniso setting, 'I' or 'A'
2382        '''
2383        # Either 'I' or 'A'
2384        return self.data[self.cia]
2385
2386    @property
2387    def uiso(self):
2388        '''Get or set the associated atom's Uiso or Uaniso value(s)
2389        '''
2390        if self.adp_flag == 'I':
2391            return self.data[self.cia+1]
2392        else:
2393            return self.data[self.cia+2:self.cia+8]
2394
2395    @uiso.setter
2396    def uiso(self, value):
2397        if self.adp_flag == 'I':
2398            self.data[self.cia+1] = float(value)
2399        else:
2400            assert len(value) == 6
2401            self.data[self.cia+2:self.cia+8] = [float(v) for v in value]
2402
2403
2404class G2PwdrData(G2ObjectWrapper):
2405    """Wraps a Powder Data Histogram."""
2406    def __init__(self, data, proj):
2407        self.data = data
2408        self.proj = proj
2409
2410    @staticmethod
2411    def is_valid_refinement_key(key):
2412        valid_keys = ['Limits', 'Sample Parameters', 'Background',
2413                      'Instrument Parameters']
2414        return key in valid_keys
2415
2416    @property
2417    def name(self):
2418        return self.data['data'][-1]
2419
2420    @property
2421    def ranId(self):
2422        return self.data['data'][0]['ranId']
2423
2424    @property
2425    def residuals(self):
2426        '''Provides a dictionary with with the R-factors for this histogram.
2427        Includes the weighted and unweighted profile terms (R, Rb, wR, wRb, wRmin)
2428        as well as the Bragg R-values for each phase (ph:H:Rf and ph:H:Rf^2).
2429        '''
2430        data = self.data['data'][0]
2431        return {key: data[key] for key in data
2432                if key in ['R', 'Rb', 'wR', 'wRb', 'wRmin']
2433                   or ':' in key}
2434
2435    @property
2436    def InstrumentParameters(self):
2437        '''Provides a dictionary with with the Instrument Parameters
2438        for this histogram.
2439        '''
2440        return self.data['Instrument Parameters'][0]
2441
2442    @property
2443    def SampleParameters(self):
2444        '''Provides a dictionary with with the Sample Parameters
2445        for this histogram.
2446        '''
2447        return self.data['Sample Parameters']
2448
2449    @property
2450    def Background(self):
2451        '''Provides a list with with the Background parameters
2452        for this histogram.
2453
2454        :returns: list containing a list and dict with background values
2455        '''
2456        return self.data['Background']
2457
2458    def add_back_peak(self,pos,int,sig,gam,refflags=[]):
2459        '''Adds a background peak to the Background parameters
2460       
2461        :param float pos: position of peak, a 2theta or TOF value
2462        :param float int: integrated intensity of background peak, usually large
2463        :param float sig: Gaussian width of background peak, usually large
2464        :param float gam: Lorentzian width of background peak, usually unused (small)
2465        :param list refflags: a list of 1 to 4 boolean refinement flags for
2466            pos,int,sig & gam, respectively (use [0,1] to refine int only).
2467            Defaults to [] which means nothing is refined.
2468        '''
2469        if 'peaksList' not in self.Background[1]:
2470            self.Background[1]['peaksList'] = []
2471        flags = 4*[False]
2472        for i,f in enumerate(refflags):
2473            if i>3: break
2474            flags[i] = bool(f)
2475        bpk = []
2476        for i,j in zip((pos,int,sig,gam),flags):
2477            bpk += [float(i),j]
2478        self.Background[1]['peaksList'].append(bpk)
2479        self.Background[1]['nPeaks'] = len(self.Background[1]['peaksList'])
2480
2481    def del_back_peak(self,peaknum):
2482        '''Removes a background peak from the Background parameters
2483       
2484        :param int peaknum: the number of the peak (starting from 0)
2485        '''
2486        npks = self.Background[1].get('nPeaks',0)
2487        if peaknum >= npks:
2488            raise Exception('peak {} not found in histogram {}'.format(peaknum,self.name))
2489        del self.Background[1]['peaksList'][peaknum]
2490        self.Background[1]['nPeaks'] = len(self.Background[1]['peaksList'])
2491       
2492    def ref_back_peak(self,peaknum,refflags=[]):
2493        '''Sets refinement flag for a background peak
2494       
2495        :param int peaknum: the number of the peak (starting from 0)
2496        :param list refflags: a list of 1 to 4 boolean refinement flags for
2497            pos,int,sig & gam, respectively. If a flag is not specified
2498            it defaults to False (use [0,1] to refine int only).
2499            Defaults to [] which means nothing is refined.
2500        '''
2501        npks = self.Background[1].get('nPeaks',0)
2502        if peaknum >= npks:
2503            raise Exception('peak {} not found in histogram {}'.format(peaknum,self.name))
2504        flags = 4*[False]
2505        for i,f in enumerate(refflags):
2506            if i>3: break
2507            flags[i] = bool(f)
2508        for i,f in enumerate(flags):
2509            self.Background[1]['peaksList'][peaknum][2*i+1] = f
2510                   
2511    @property
2512    def id(self):
2513        self.proj.update_ids()
2514        return self.data['data'][0]['hId']
2515
2516    @id.setter
2517    def id(self, val):
2518        self.data['data'][0]['hId'] = val
2519
2520    def fit_fixed_points(self):
2521        """Attempts to apply a background fit to the fixed points currently specified."""
2522        def SetInstParms(Inst):
2523            dataType = Inst['Type'][0]
2524            insVary = []
2525            insNames = []
2526            insVals = []
2527            for parm in Inst:
2528                insNames.append(parm)
2529                insVals.append(Inst[parm][1])
2530                if parm in ['U','V','W','X','Y','Z','SH/L','I(L2)/I(L1)','alpha',
2531                    'beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q',] and Inst[parm][2]:
2532                        Inst[parm][2] = False
2533            instDict = dict(zip(insNames, insVals))
2534            instDict['X'] = max(instDict['X'], 0.01)
2535            instDict['Y'] = max(instDict['Y'], 0.01)
2536            if 'SH/L' in instDict:
2537                instDict['SH/L'] = max(instDict['SH/L'], 0.002)
2538            return dataType, instDict, insVary
2539
2540        bgrnd = self.data['Background']
2541
2542        # Need our fixed points in order
2543        bgrnd[1]['FixedPoints'].sort(key=lambda pair: pair[0])
2544        X = [x for x, y in bgrnd[1]['FixedPoints']]
2545        Y = [y for x, y in bgrnd[1]['FixedPoints']]
2546
2547        limits = self.data['Limits'][1]
2548        if X[0] > limits[0]:
2549            X = [limits[0]] + X
2550            Y = [Y[0]] + Y
2551        if X[-1] < limits[1]:
2552            X += [limits[1]]
2553            Y += [Y[-1]]
2554
2555        # Some simple lookups
2556        controls = self.proj['Controls']['data']
2557        inst, inst2 = self.data['Instrument Parameters']
2558        pwddata = self.data['data'][1]
2559
2560        # Construct the data for background fitting
2561        xBeg = np.searchsorted(pwddata[0], limits[0])
2562        xFin = np.searchsorted(pwddata[0], limits[1])
2563        xdata = pwddata[0][xBeg:xFin]
2564        ydata = si.interp1d(X,Y)(ma.getdata(xdata))
2565
2566        W = [1]*len(xdata)
2567        Z = [0]*len(xdata)
2568
2569        dataType, insDict, insVary = SetInstParms(inst)
2570        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
2571
2572        # Do the fit
2573        data = np.array([xdata, ydata, W, Z, Z, Z])
2574        G2pwd.DoPeakFit('LSQ', [], bgrnd, limits, inst, inst2, data,
2575                        prevVaryList=bakVary, controls=controls)
2576
2577        # Post-fit
2578        parmDict = {}
2579        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
2580        parmDict.update(bakDict)
2581        parmDict.update(insDict)
2582        pwddata[3][xBeg:xFin] *= 0
2583        pwddata[5][xBeg:xFin] *= 0
2584        pwddata[4][xBeg:xFin] = G2pwd.getBackground('', parmDict, bakType, dataType, xdata)[0]
2585
2586        # TODO adjust pwddata? GSASIIpwdGUI.py:1041
2587        # TODO update background
2588        self.proj.save()
2589
2590    def getdata(self,datatype):
2591        '''Provides access to the histogram data of the selected data type
2592
2593        :param str datatype: must be one of the following values (case is ignored)
2594       
2595           * 'X': the 2theta or TOF values for the pattern
2596           * 'Yobs': the observed intensity values
2597           * 'Yweight': the weights for each data point (1/sigma**2)
2598           * 'Ycalc': the computed intensity values
2599           * 'Background': the computed background values
2600           * 'Residual': the difference between Yobs and Ycalc (obs-calc)
2601
2602        :returns: an numpy MaskedArray with data values of the requested type
2603       
2604        '''
2605        enums = ['x', 'yobs', 'yweight', 'ycalc', 'background', 'residual']
2606        if datatype.lower() not in enums:
2607            raise G2ScriptException("Invalid datatype = "+datatype+" must be one of "+str(enums))
2608        return self.data['data'][1][enums.index(datatype.lower())]
2609       
2610    def y_calc(self):
2611        return self.data['data'][1][3]
2612
2613    def Export(self,fileroot,extension):
2614        '''Write the histogram into a file. The path is specified by fileroot and
2615        extension.
2616       
2617        :param str fileroot: name of the file, optionally with a path (extension is
2618           ignored)
2619        :param str extension: includes '.', must match an extension in global
2620           exportersByExtension['powder'] or a Exception is raised.
2621        :returns: name of file that was written
2622        '''
2623        if extension not in exportersByExtension.get('powder',[]):
2624            raise G2ScriptException('No Writer for file type = "'+extension+'"')
2625        fil = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2626        obj = exportersByExtension['powder'][extension]
2627        obj.SetFromArray(hist=self.data,histname=self.name)
2628        obj.Writer(self.name,fil)
2629           
2630    def plot(self, Yobs=True, Ycalc=True, Background=True, Residual=True):
2631        try:
2632            import matplotlib.pyplot as plt
2633            data = self.data['data'][1]
2634            if Yobs:
2635                plt.plot(data[0], data[1], label='Yobs')
2636            if Ycalc:
2637                plt.plot(data[0], data[3], label='Ycalc')
2638            if Background:
2639                plt.plot(data[0], data[4], label='Background')
2640            if Residual:
2641                plt.plot(data[0], data[5], label="Residual")
2642        except ImportError:
2643            pass
2644
2645    def get_wR(self):
2646        """returns the overall weighted profile R factor for a histogram
2647       
2648        :returns: a wR value as a percentage or None if not defined
2649        """
2650        return self['data'][0].get('wR')
2651
2652    def set_refinements(self, refs):
2653        """Sets the histogram refinement parameter 'key' to the specification 'value'
2654
2655        :param dict refs: A dictionary of the parameters to be set. See
2656                          :ref:`Histogram_parameters_table` for a description of
2657                          what these dictionaries should be.
2658
2659        :returns: None
2660
2661        """
2662        do_fit_fixed_points = False
2663        for key, value in refs.items():
2664            if key == 'Limits':
2665                old_limits = self.data['Limits'][1]
2666                new_limits = value
2667                if isinstance(new_limits, dict):
2668                    if 'low' in new_limits:
2669                        old_limits[0] = new_limits['low']
2670                    if 'high' in new_limits:
2671                        old_limits[1] = new_limits['high']
2672                else:
2673                    old_limits[0], old_limits[1] = new_limits
2674            elif key == 'Sample Parameters':
2675                sample = self.data['Sample Parameters']
2676                for sparam in value:
2677                    if sparam not in sample:
2678                        raise ValueError("Unknown refinement parameter, "
2679                                         + str(sparam))
2680                    sample[sparam][1] = True
2681            elif key == 'Background':
2682                bkg, peaks = self.data['Background']
2683
2684                # If True or False, just set the refine parameter
2685                if value in (True, False):
2686                    bkg[1] = value
2687                    return
2688
2689                if 'type' in value:
2690                    bkg[0] = value['type']
2691                if 'refine' in value:
2692                    bkg[1] = value['refine']
2693                if 'no. coeffs' in value:
2694                    cur_coeffs = bkg[2]
2695                    n_coeffs = value['no. coeffs']
2696                    if n_coeffs > cur_coeffs:
2697                        for x in range(n_coeffs - cur_coeffs):
2698                            bkg.append(0.0)
2699                    else:
2700                        for _ in range(cur_coeffs - n_coeffs):
2701                            bkg.pop()
2702                    bkg[2] = n_coeffs
2703                if 'coeffs' in value:
2704                    bkg[3:] = value['coeffs']
2705                if 'FixedPoints' in value:
2706                    peaks['FixedPoints'] = [(float(a), float(b))
2707                                            for a, b in value['FixedPoints']]
2708                if value.get('fit fixed points', False):
2709                    do_fit_fixed_points = True
2710                if 'peaks' in value:
2711                    for i,flags in enumerate(value['peaks']):
2712                        self.ref_back_peak(i,flags)
2713
2714            elif key == 'Instrument Parameters':
2715                instrument, secondary = self.data['Instrument Parameters']
2716                for iparam in value:
2717                    try:
2718                        instrument[iparam][2] = True
2719                    except IndexError:
2720                        raise ValueError("Invalid key:", iparam)
2721            else:
2722                raise ValueError("Unknown key:", key)
2723        # Fit fixed points after the fact - ensure they are after fixed points
2724        # are added
2725        if do_fit_fixed_points:
2726            # Background won't be fit if refinement flag not set
2727            orig = self.data['Background'][0][1]
2728            self.data['Background'][0][1] = True
2729            self.fit_fixed_points()
2730            # Restore the previous value
2731            self.data['Background'][0][1] = orig
2732
2733    def clear_refinements(self, refs):
2734        """Clears the refinement parameter 'key' and its associated value.
2735
2736        :param dict refs: A dictionary of parameters to clear."""
2737        for key, value in refs.items():
2738            if key == 'Limits':
2739                old_limits, cur_limits = self.data['Limits']
2740                cur_limits[0], cur_limits[1] = old_limits
2741            elif key == 'Sample Parameters':
2742                sample = self.data['Sample Parameters']
2743                for sparam in value:
2744                    sample[sparam][1] = False
2745            elif key == 'Background':
2746                bkg, peaks = self.data['Background']
2747
2748                # If True or False, just set the refine parameter
2749                if value in (True, False):
2750                    bkg[1] = False
2751                    return
2752
2753                bkg[1] = False
2754                if 'FixedPoints' in value:
2755                    if 'FixedPoints' in peaks:
2756                        del peaks['FixedPoints']
2757                if 'peaks' in value:
2758                    for i in range(len(self.Background[1]['peaksList'])):
2759                        self.ref_back_peak(i,[])
2760            elif key == 'Instrument Parameters':
2761                instrument, secondary = self.data['Instrument Parameters']
2762                for iparam in value:
2763                    instrument[iparam][2] = False
2764            else:
2765                raise ValueError("Unknown key:", key)
2766
2767    def add_peak(self,area,dspace=None,Q=None,ttheta=None):
2768        '''Adds a single peak to the peak list
2769        :param float area: peak area
2770        :param float dspace: peak position as d-space (A)
2771        :param float Q: peak position as Q (A-1)
2772        :param float ttheta: peak position as 2Theta (deg)
2773
2774        Note: only one of the parameters dspace, Q or ttheta may be specified
2775        '''
2776        import GSASIIlattice as G2lat
2777        import GSASIImath as G2mth
2778        if (not dspace) + (not Q) + (not ttheta) != 2:
2779            print('add_peak error: too many or no peak position(s) specified')
2780            return
2781        pos = ttheta
2782        Parms,Parms2 = self.data['Instrument Parameters']
2783        if Q:
2784            pos = G2lat.Dsp2pos(Parms,2.*np.pi/Q)
2785        elif dspace:
2786            pos = G2lat.Dsp2pos(Parms,dspace)
2787        peaks = self.data['Peak List']
2788        peaks['sigDict'] = {}        #no longer valid
2789        peaks['peaks'].append(G2mth.setPeakparms(Parms,Parms2,pos,area))
2790
2791    def set_peakFlags(self,peaklist=None,area=None,pos=None,sig=None,gam=None):
2792        '''Set refinement flags for peaks
2793       
2794        :param list peaklist: a list of peaks to change flags. If None (default), changes
2795          are made to all peaks.
2796        :param bool area: Sets or clears the refinement flag for the peak area value.
2797          If None (the default), no change is made.
2798        :param bool pos: Sets or clears the refinement flag for the peak position value.
2799          If None (the default), no change is made.
2800        :param bool sig: Sets or clears the refinement flag for the peak sig (Gaussian width) value.
2801          If None (the default), no change is made.
2802        :param bool gam: Sets or clears the refinement flag for the peak sig (Lorentzian width) value.
2803          If None (the default), no change is made.
2804         
2805        Note that when peaks are first created the area flag is on and the other flags are
2806        initially off.
2807
2808        Example::
2809       
2810           set_peakFlags(sig=False,gam=True)
2811
2812        causes the sig refinement flag to be cleared and the gam flag to be set, in both cases for
2813        all peaks. The position and area flags are not changed from their previous values.
2814        '''
2815        peaks = self.data['Peak List']
2816        if peaklist is None:
2817            peaklist = range(len(peaks['peaks']))
2818        for i in peaklist:
2819            for var,j in [(area,3),(pos,1),(sig,5),(gam,7)]:
2820                if var is not None:
2821                    peaks['peaks'][i][j] = var
2822           
2823    def refine_peaks(self):
2824        '''Causes a refinement of peak position, background and instrument parameters
2825        '''
2826        import GSASIIpwd as G2pwd
2827        controls = self.proj.data.get('Controls',{})
2828        controls = controls.get('data',
2829                            {'deriv type':'analytic','min dM/M':0.001,}     #fill in defaults if needed
2830                            )
2831        peaks = self.data['Peak List']
2832        Parms,Parms2 = self.data['Instrument Parameters']
2833        background = self.data['Background']
2834        limits = self.data['Limits'][1]
2835        bxye = np.zeros(len(self.data['data'][1][1]))
2836        peaks['sigDict'] = G2pwd.DoPeakFit('LSQ',peaks['peaks'],background,limits,
2837                                           Parms,Parms2,self.data['data'][1],bxye,[],
2838                                           False,controls,None)[0]
2839
2840    def SaveProfile(self,filename):
2841        '''Writes a GSAS-II (new style) .instprm file
2842        '''
2843        data,Parms2 = self.data['Instrument Parameters']
2844        filename = os.path.splitext(filename)[0]+'.instprm'         # make sure extension is .instprm
2845        File = open(filename,'w')
2846        File.write("#GSAS-II instrument parameter file; do not add/delete items!\n")
2847        for item in data:
2848            File.write(item+':'+str(data[item][1])+'\n')
2849        File.close()
2850        print ('Instrument parameters saved to: '+filename)
2851
2852    def LoadProfile(self,filename):
2853        '''Reads a GSAS-II (new style) .instprm file and overwrites the current parameters
2854        '''
2855        filename = os.path.splitext(filename)[0]+'.instprm'         # make sure extension is .instprm
2856        File = open(filename,'r')
2857        S = File.readline()
2858        newItems = []
2859        newVals = []
2860        Found = False
2861        while S:
2862            if S[0] == '#':
2863                if Found:
2864                    break
2865                if 'Bank' in S:
2866                    if bank == int(S.split(':')[0].split()[1]):
2867                        S = File.readline()
2868                        continue
2869                    else:
2870                        S = File.readline()
2871                        while S and '#Bank' not in S:
2872                            S = File.readline()
2873                        continue
2874                else:   #a non #Bank file
2875                    S = File.readline()
2876                    continue
2877            Found = True
2878            [item,val] = S[:-1].split(':')
2879            newItems.append(item)
2880            try:
2881                newVals.append(float(val))
2882            except ValueError:
2883                newVals.append(val)                       
2884            S = File.readline()               
2885        File.close()
2886        LoadG2fil()
2887        self.data['Instrument Parameters'][0] = G2fil.makeInstDict(newItems,newVals,len(newVals)*[False,])
2888
2889       
2890class G2Phase(G2ObjectWrapper):
2891    """A wrapper object around a given phase.
2892
2893    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
2894    """
2895    def __init__(self, data, name, proj):
2896        self.data = data
2897        self.name = name
2898        self.proj = proj
2899
2900    @staticmethod
2901    def is_valid_refinement_key(key):
2902        valid_keys = ["Cell", "Atoms", "LeBail"]
2903        return key in valid_keys
2904
2905    @staticmethod
2906    def is_valid_HAP_refinement_key(key):
2907        valid_keys = ["Babinet", "Extinction", "HStrain", "Mustrain",
2908                      "Pref.Ori.", "Show", "Size", "Use", "Scale"]
2909        return key in valid_keys
2910
2911    def atom(self, atomlabel):
2912        """Returns the atom specified by atomlabel, or None if it does not
2913        exist.
2914
2915        :param str atomlabel: The name of the atom (e.g. "O2")
2916        :returns: A :class:`G2AtomRecord` object
2917            representing the atom.
2918        """
2919        # Consult GSASIIobj.py for the meaning of this
2920        cx, ct, cs, cia = self.data['General']['AtomPtrs']
2921        ptrs = [cx, ct, cs, cia]
2922        atoms = self.data['Atoms']
2923        for atom in atoms:
2924            if atom[ct-1] == atomlabel:
2925                return G2AtomRecord(atom, ptrs, self.proj)
2926
2927    def atoms(self):
2928        """Returns a list of atoms present in the phase.
2929
2930        :returns: A list of :class:`G2AtomRecord` objects.
2931
2932        .. seealso::
2933            :meth:`G2Phase.atom`
2934            :class:`G2AtomRecord`
2935        """
2936        ptrs = self.data['General']['AtomPtrs']
2937        output = []
2938        atoms = self.data['Atoms']
2939        for atom in atoms:
2940            output.append(G2AtomRecord(atom, ptrs, self.proj))
2941        return output
2942
2943    def histograms(self):
2944        output = []
2945        for hname in self.data.get('Histograms', {}).keys():
2946            output.append(self.proj.histogram(hname))
2947        return output
2948
2949    @property
2950    def ranId(self):
2951        return self.data['ranId']
2952
2953    @property
2954    def id(self):
2955        return self.data['pId']
2956
2957    @id.setter
2958    def id(self, val):
2959        self.data['pId'] = val
2960
2961    def get_cell(self):
2962        """Returns a dictionary of the cell parameters, with keys:
2963            'length_a', 'length_b', 'length_c', 'angle_alpha', 'angle_beta', 'angle_gamma', 'volume'
2964
2965        :returns: a dict
2966
2967        .. seealso::
2968           :meth:`G2Phase.get_cell_and_esd`
2969
2970        """
2971        cell = self.data['General']['Cell']
2972        return {'length_a': cell[1], 'length_b': cell[2], 'length_c': cell[3],
2973                'angle_alpha': cell[4], 'angle_beta': cell[5], 'angle_gamma': cell[6],
2974                'volume': cell[7]}
2975
2976    def get_cell_and_esd(self):
2977        """
2978        Returns a pair of dictionaries, the first representing the unit cell, the second
2979        representing the estimated standard deviations of the unit cell.
2980
2981        :returns: a tuple of two dictionaries
2982
2983        .. seealso::
2984           :meth:`G2Phase.get_cell`
2985
2986        """
2987        # translated from GSASIIstrIO.ExportBaseclass.GetCell
2988        import GSASIIstrIO as G2stIO
2989        import GSASIIlattice as G2lat
2990        import GSASIImapvars as G2mv
2991        try:
2992            pfx = str(self.id) + '::'
2993            sgdata = self['General']['SGData']
2994            covDict = self.proj['Covariance']['data']
2995
2996            parmDict = dict(zip(covDict.get('varyList',[]),
2997                                covDict.get('variables',[])))
2998            sigDict = dict(zip(covDict.get('varyList',[]),
2999                               covDict.get('sig',[])))
3000
3001            if covDict.get('covMatrix') is not None:
3002                sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],
3003                                                  covDict['varyList'],
3004                                                  parmDict))
3005
3006            A, sigA = G2stIO.cellFill(pfx, sgdata, parmDict, sigDict)
3007            cellSig = G2stIO.getCellEsd(pfx, sgdata, A, self.proj['Covariance']['data'])
3008            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
3009            cellDict, cellSigDict = {}, {}
3010            for i, key in enumerate(['length_a', 'length_b', 'length_c',
3011                                     'angle_alpha', 'angle_beta', 'angle_gamma',
3012                                     'volume']):
3013                cellDict[key] = cellList[i]
3014                cellSigDict[key] = cellSig[i]
3015            return cellDict, cellSigDict
3016        except KeyError:
3017            cell = self.get_cell()
3018            return cell, {key: 0.0 for key in cell}
3019
3020    def export_CIF(self, outputname, quickmode=True):
3021        """Write this phase to a .cif file named outputname
3022
3023        :param str outputname: The name of the .cif file to write to
3024        :param bool quickmode: Currently ignored. Carryover from exports.G2export_CIF"""
3025        # This code is all taken from exports/G2export_CIF.py
3026        # Functions copied have the same names
3027        import GSASIImath as G2mth
3028        import GSASIImapvars as G2mv
3029        from exports import G2export_CIF as cif
3030
3031        CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
3032        CIFname = os.path.splitext(self.proj.filename)[0]
3033        CIFname = os.path.split(CIFname)[1]
3034        CIFname = ''.join([c if ord(c) < 128 else ''
3035                           for c in CIFname.replace(' ', '_')])
3036        try:
3037            author = self.proj['Controls']['data'].get('Author','').strip()
3038        except KeyError:
3039            pass
3040        oneblock = True
3041
3042        covDict = self.proj['Covariance']['data']
3043        parmDict = dict(zip(covDict.get('varyList',[]),
3044                            covDict.get('variables',[])))
3045        sigDict = dict(zip(covDict.get('varyList',[]),
3046                           covDict.get('sig',[])))
3047
3048        if covDict.get('covMatrix') is not None:
3049            sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],
3050                                              covDict['varyList'],
3051                                              parmDict))
3052
3053        with open(outputname, 'w') as fp:
3054            fp.write(' \n' + 70*'#' + '\n')
3055            cif.WriteCIFitem(fp, 'data_' + CIFname)
3056            # from exports.G2export_CIF.WritePhaseInfo
3057            cif.WriteCIFitem(fp, '\n# phase info for '+str(self.name) + ' follows')
3058            cif.WriteCIFitem(fp, '_pd_phase_name', self.name)
3059            # TODO get esds
3060            cellDict = self.get_cell()
3061            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
3062            names = ['length_a','length_b','length_c',
3063                     'angle_alpha','angle_beta ','angle_gamma',
3064                     'volume']
3065            for key, val in cellDict.items():
3066                cif.WriteCIFitem(fp, '_cell_' + key, G2mth.ValEsd(val))
3067
3068            cif.WriteCIFitem(fp, '_symmetry_cell_setting',
3069                         self.data['General']['SGData']['SGSys'])
3070
3071            spacegroup = self.data['General']['SGData']['SpGrp'].strip()
3072            # regularize capitalization and remove trailing H/R
3073            spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
3074            cif.WriteCIFitem(fp, '_symmetry_space_group_name_H-M', spacegroup)
3075
3076            # generate symmetry operations including centering and center of symmetry
3077            SymOpList, offsetList, symOpList, G2oprList, G2opcodes = G2spc.AllOps(
3078                self.data['General']['SGData'])
3079            cif.WriteCIFitem(fp, 'loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
3080            for i, op in enumerate(SymOpList,start=1):
3081                cif.WriteCIFitem(fp, '   {:3d}  {:}'.format(i,op.lower()))
3082
3083            # TODO skipped histograms, exports/G2export_CIF.py:880
3084
3085            # report atom params
3086            if self.data['General']['Type'] in ['nuclear','macromolecular']:        #this needs macromolecular variant, etc!
3087                cif.WriteAtomsNuclear(fp, self.data, self.name, parmDict, sigDict, [])
3088                # self._WriteAtomsNuclear(fp, parmDict, sigDict)
3089            else:
3090                raise G2ScriptException("no export for "+str(self.data['General']['Type'])+" coordinates implemented")
3091            # report cell contents
3092            cif.WriteComposition(fp, self.data, self.name, parmDict)
3093            if not quickmode and self.data['General']['Type'] == 'nuclear':      # report distances and angles
3094                # WriteDistances(fp,self.name,SymOpList,offsetList,symOpList,G2oprList)
3095                raise NotImplementedError("only quickmode currently supported")
3096            if 'Map' in self.data['General'] and 'minmax' in self.data['General']['Map']:
3097                cif.WriteCIFitem(fp,'\n# Difference density results')
3098                MinMax = self.data['General']['Map']['minmax']
3099                cif.WriteCIFitem(fp,'_refine_diff_density_max',G2mth.ValEsd(MinMax[0],-0.009))
3100                cif.WriteCIFitem(fp,'_refine_diff_density_min',G2mth.ValEsd(MinMax[1],-0.009))
3101
3102
3103    def set_refinements(self, refs):
3104        """Sets the refinement parameter 'key' to the specification 'value'
3105
3106        :param dict refs: A dictionary of the parameters to be set. See
3107                          :ref:`Phase_parameters_table` for a description of
3108                          this dictionary.
3109
3110        :returns: None"""
3111        for key, value in refs.items():
3112            if key == "Cell":
3113                self.data['General']['Cell'][0] = value
3114
3115            elif key == "Atoms":
3116                for atomlabel, atomrefinement in value.items():
3117                    if atomlabel == 'all':
3118                        for atom in self.atoms():
3119                            atom.refinement_flags = atomrefinement
3120                    else:
3121                        atom = self.atom(atomlabel)
3122                        if atom is None:
3123                            raise ValueError("No such atom: " + atomlabel)
3124                        atom.refinement_flags = atomrefinement
3125
3126            elif key == "LeBail":
3127                hists = self.data['Histograms']
3128                for hname, hoptions in hists.items():
3129                    if 'LeBail' not in hoptions:
3130                        hoptions['newLeBail'] = bool(True)
3131                    hoptions['LeBail'] = bool(value)
3132            else:
3133                raise ValueError("Unknown key:", key)
3134
3135    def clear_refinements(self, refs):
3136        """Clears a given set of parameters.
3137
3138        :param dict refs: The parameters to clear"""
3139        for key, value in refs.items():
3140            if key == "Cell":
3141                self.data['General']['Cell'][0] = False
3142            elif key == "Atoms":
3143                cx, ct, cs, cia = self.data['General']['AtomPtrs']
3144
3145                for atomlabel in value:
3146                    atom = self.atom(atomlabel)
3147                    # Set refinement to none
3148                    atom.refinement_flags = ' '
3149            elif key == "LeBail":
3150                hists = self.data['Histograms']
3151                for hname, hoptions in hists.items():
3152                    if 'LeBail' not in hoptions:
3153                        hoptions['newLeBail'] = True
3154                    hoptions['LeBail'] = False
3155            else:
3156                raise ValueError("Unknown key:", key)
3157
3158    def set_HAP_refinements(self, refs, histograms='all'):
3159        """Sets the given HAP refinement parameters between this phase and
3160        the given histograms
3161
3162        :param dict refs: A dictionary of the parameters to be set. See
3163                          :ref:`HAP_parameters_table` for a description of this
3164                          dictionary.
3165        :param histograms: Either 'all' (default) or a list of the histograms
3166            whose HAP parameters will be set with this phase. Histogram and phase
3167            must already be associated
3168
3169        :returns: None
3170        """
3171        if not self.data['Histograms']:
3172            print("Error likely: Phase {} has no linked histograms".format(self.name))
3173            return
3174           
3175        if histograms == 'all':
3176            histograms = self.data['Histograms'].values()
3177        else:
3178            histograms = [self.data['Histograms'][h.name] for h in histograms
3179                          if h.name in self.data['Histograms']]
3180
3181        if not histograms:
3182            print("Skipping HAP set for phase {}, no selected histograms".format(self.name))
3183            return
3184        for key, val in refs.items():
3185            for h in histograms:
3186                if key == 'Babinet':
3187                    try:
3188                        sets = list(val)
3189                    except ValueError:
3190                        sets = ['BabA', 'BabU']
3191                    for param in sets:
3192                        if param not in ['BabA', 'BabU']:
3193                            raise ValueError("Not sure what to do with" + param)
3194                        for hist in histograms:
3195                            hist['Babinet'][param][1] = True
3196                elif key == 'Extinction':
3197                    for h in histograms:
3198                        h['Extinction'][1] = bool(val)
3199                elif key == 'HStrain':
3200                    for h in histograms:
3201                        h['HStrain'][1] = [bool(val) for p in h['HStrain'][1]]
3202                elif key == 'Mustrain':
3203                    for h in histograms:
3204                        mustrain = h['Mustrain']
3205                        newType = None
3206                        direction = None
3207                        if isinstance(val, strtypes):
3208                            if val in ['isotropic', 'uniaxial', 'generalized']:
3209                                newType = val
3210                            else:
3211                                raise ValueError("Not a Mustrain type: " + val)
3212                        elif isinstance(val, dict):
3213                            newType = val.get('type', None)
3214                            direction = val.get('direction', None)
3215
3216                        if newType:
3217                            mustrain[0] = newType
3218                            if newType == 'isotropic':
3219                                mustrain[2][0] = True == val.get('refine',False)
3220                                mustrain[5] = [False for p in mustrain[4]]
3221                            elif newType == 'uniaxial':
3222                                if 'refine' in val:
3223                                    mustrain[2][0] = False
3224                                    types = val['refine']
3225                                    if isinstance(types, strtypes):
3226                                        types = [types]
3227                                    elif isinstance(types, bool):
3228                                        mustrain[2][1] = types
3229                                        mustrain[2][2] = types
3230                                        types = []
3231                                    else:
3232                                        raise ValueError("Not sure what to do with: "
3233                                                         + str(types))
3234                                else:
3235                                    types = []
3236
3237                                for unitype in types:
3238                                    if unitype == 'equatorial':
3239                                        mustrain[2][0] = True
3240                                    elif unitype == 'axial':
3241                                        mustrain[2][1] = True
3242                                    else:
3243                                        msg = 'Invalid uniaxial mustrain type'
3244                                        raise ValueError(msg + ': ' + unitype)
3245                            else:  # newtype == 'generalized'
3246                                mustrain[2] = [False for p in mustrain[1]]
3247                                if 'refine' in val:
3248                                    mustrain[5] = [True == val['refine']]*len(mustrain[5])
3249
3250                        if direction:
3251                            if len(direction) != 3:
3252                                raise ValueError("Expected hkl, found", direction)
3253                            direction = [int(n) for n in direction]
3254                            mustrain[3] = direction
3255                elif key == 'Size':
3256                    newSize = None
3257                    if 'value' in val:
3258                        newSize = float(val['value'])
3259                    for h in histograms:
3260                        size = h['Size']
3261                        newType = None
3262                        direction = None
3263                        if isinstance(val, strtypes):
3264                            if val in ['isotropic', 'uniaxial', 'ellipsoidal']:
3265                                newType = val
3266                            else:
3267                                raise ValueError("Not a valid Size type: " + val)
3268                        elif isinstance(val, dict):
3269                            newType = val.get('type', None)
3270                            direction = val.get('direction', None)
3271
3272                        if newType:
3273                            size[0] = newType
3274                            refine = True == val.get('refine')
3275                            if newType == 'isotropic' and refine is not None:
3276                                size[2][0] = bool(refine)
3277                                if newSize: size[1][0] = newSize
3278                            elif newType == 'uniaxial' and refine is not None:
3279                                size[2][1] = bool(refine)
3280                                size[2][2] = bool(refine)
3281                                if newSize: size[1][1] = size[1][2] =newSize
3282                            elif newType == 'ellipsoidal' and refine is not None:
3283                                size[5] = [bool(refine) for p in size[5]]
3284                                if newSize: size[4] = [newSize for p in size[4]]
3285
3286                        if direction:
3287                            if len(direction) != 3:
3288                                raise ValueError("Expected hkl, found", direction)
3289                            direction = [int(n) for n in direction]
3290                            size[3] = direction
3291                elif key == 'Pref.Ori.':
3292                    for h in histograms:
3293                        h['Pref.Ori.'][2] = bool(val)
3294                elif key == 'Show':
3295                    for h in histograms:
3296                        h['Show'] = bool(val)
3297                elif key == 'Use':
3298                    for h in histograms:
3299                        h['Use'] = bool(val)
3300                elif key == 'Scale':
3301                    for h in histograms:
3302                        h['Scale'][1] = bool(val)
3303                else:
3304                    print(u'Unknown HAP key: '+key)
3305
3306    def getHAPvalues(self, histname):
3307        """Returns a dict with HAP values for the selected histogram
3308
3309        :param histogram: is a histogram object (:class:`G2PwdrData`) or
3310            a histogram name or the index number of the histogram
3311
3312        :returns: HAP value dict
3313        """
3314        if isinstance(histname, G2PwdrData):
3315            histname = histname.name
3316        elif histname in self.data['Histograms']:
3317            pass
3318        elif type(histname) is int:
3319            histname = self.proj.histograms()[histname].name
3320        else:
3321            raise G2ScriptException("Invalid histogram reference: "+str(histname))
3322        return self.data['Histograms'][histname]
3323                   
3324    def clear_HAP_refinements(self, refs, histograms='all'):
3325        """Clears the given HAP refinement parameters between this phase and
3326        the given histograms
3327
3328        :param dict refs: A dictionary of the parameters to be cleared.
3329        :param histograms: Either 'all' (default) or a list of the histograms
3330            whose HAP parameters will be cleared with this phase. Histogram and
3331            phase must already be associated
3332
3333        :returns: None
3334        """
3335        if histograms == 'all':
3336            histograms = self.data['Histograms'].values()
3337        else:
3338            histograms = [self.data['Histograms'][h.name] for h in histograms
3339                          if h.name in self.data['Histograms']]
3340
3341        for key, val in refs.items():
3342            for h in histograms:
3343                if key == 'Babinet':
3344                    try:
3345                        sets = list(val)
3346                    except ValueError:
3347                        sets = ['BabA', 'BabU']
3348                    for param in sets:
3349                        if param not in ['BabA', 'BabU']:
3350                            raise ValueError("Not sure what to do with" + param)
3351                        for hist in histograms:
3352                            hist['Babinet'][param][1] = False
3353                elif key == 'Extinction':
3354                    for h in histograms:
3355                        h['Extinction'][1] = False
3356                elif key == 'HStrain':
3357                    for h in histograms:
3358                        h['HStrain'][1] = [False for p in h['HStrain'][1]]
3359                elif key == 'Mustrain':
3360                    for h in histograms:
3361                        mustrain = h['Mustrain']
3362                        mustrain[2] = [False for p in mustrain[2]]
3363                        mustrain[5] = [False for p in mustrain[4]]
3364                elif key == 'Pref.Ori.':
3365                    for h in histograms:
3366                        h['Pref.Ori.'][2] = False
3367                elif key == 'Show':
3368                    for h in histograms:
3369                        h['Show'] = False
3370                elif key == 'Size':
3371                    for h in histograms:
3372                        size = h['Size']
3373                        size[2] = [False for p in size[2]]
3374                        size[5] = [False for p in size[5]]
3375                elif key == 'Use':
3376                    for h in histograms:
3377                        h['Use'] = False
3378                elif key == 'Scale':
3379                    for h in histograms:
3380                        h['Scale'][1] = False
3381                else:
3382                    print(u'Unknown HAP key: '+key)
3383
3384class G2Image(G2ObjectWrapper):
3385    """Wrapper for an IMG tree entry, containing an image and various metadata.
3386    Note that in a GSASIIscriptable script, instances of G2Image will be created by
3387    calls to :meth:`G2Project.add_image` or :meth:`G2Project.images`, not via calls
3388    to :meth:`G2Image.__init__`.
3389
3390    Example use of G2Image:
3391
3392    >>> gpx = G2sc.G2Project(filename='itest.gpx')
3393    >>> imlst = gpx.add_image(idata,fmthint="TIF")
3394    >>> imlst[0].loadControls('stdSettings.imctrl')
3395    >>> imlst[0].setCalibrant('Si    SRM640c')
3396    >>> imlst[0].loadMasks('stdMasks.immask')
3397    >>> imlst[0].Recalibrate()
3398    >>> imlst[0].setControl('outAzimuths',3)
3399    >>> pwdrList = imlst[0].Integrate()
3400
3401
3402    Sample script using an image:   
3403
3404.. code-block::  python
3405
3406    import os,sys
3407    sys.path.insert(0,'/Users/toby/software/G2/GSASII')
3408    import GSASIIscriptable as G2sc
3409    datadir = "/tmp/V4343_IntegrationError/"
3410    PathWrap = lambda fil: os.path.join(datadir,fil)
3411
3412    gpx = G2sc.G2Project(filename=PathWrap('inttest.gpx'))
3413    imlst = gpx.add_image(PathWrap('Si_free_dc800_1-00000.tif'),fmthint="TIF")
3414    imlst[0].loadControls(PathWrap('Si_free_dc800_1-00000.imctrl'))
3415    pwdrList = imlst[0].Integrate()
3416    gpx.save()
3417
3418    """
3419    # parameters in that can be accessed via setControl. This may need future attention
3420    ControlList = {
3421        'int': ['calibskip', 'pixLimit', 'edgemin', 'outChannels',
3422                    'outAzimuths'],
3423        'float': ['cutoff', 'setdist', 'wavelength', 'Flat Bkg',
3424                      'azmthOff', 'tilt', 'calibdmin', 'rotation',
3425                      'distance', 'DetDepth'],
3426        'bool': ['setRings', 'setDefault', 'centerAzm', 'fullIntegrate',
3427                     'DetDepthRef', 'showLines'],
3428        'str': ['SampleShape', 'binType', 'formatName', 'color',
3429                    'type', ],
3430        'list': ['GonioAngles', 'IOtth', 'LRazimuth', 'Oblique', 'PolaVal',
3431                   'SampleAbs', 'center', 'ellipses', 'linescan',
3432                    'pixelSize', 'range', 'ring', 'rings', 'size', ],
3433        'dict': ['varyList'],
3434        }
3435    '''Defines the items known to exist in the Image Controls tree section
3436    and the item's data types. A few are not included here
3437    ('background image', 'dark image', 'Gain map', and 'calibrant') because
3438    these items have special set routines,
3439    where references to entries are checked to make sure their values are
3440    correct.
3441    ''' 
3442       
3443    def __init__(self, data, name, proj):
3444        self.data = data
3445        self.name = name
3446        self.proj = proj
3447
3448    def setControl(self,arg,value):
3449        '''Set an Image Controls parameter in the current image.
3450        If the parameter is not found an exception is raised.
3451
3452        :param str arg: the name of a parameter (dict entry) in the
3453          image. The parameter must be found in :data:`ControlList`
3454          or an exception is raised.
3455        :param value: the value to set the parameter. The value is
3456          cast as the appropriate type from :data:`ControlList`.
3457        '''
3458        for typ in self.ControlList:
3459            if arg in self.ControlList[typ]: break
3460        else:
3461            print('Allowed args:\n',[nam for nam,typ in self.findControl('')])
3462            raise Exception('arg {} not defined in G2Image.setControl'
3463                                .format(arg))
3464        try:
3465            if typ == 'int':
3466                self.data['Image Controls'][arg] = int(value)
3467            elif typ == 'float':
3468                self.data['Image Controls'][arg] = float(value)
3469            elif typ == 'bool':
3470                self.data['Image Controls'][arg] = bool(value)
3471            elif typ == 'str':
3472                self.data['Image Controls'][arg] = str(value)
3473            elif typ == 'list':
3474                self.data['Image Controls'][arg] = list(value)
3475            elif typ == 'dict':
3476                self.data['Image Controls'][arg] = dict(value)
3477            else:
3478                raise Exception('Unknown type {} for arg {} in  G2Image.setControl'
3479                                    .format(typ,arg))
3480        except:
3481            raise Exception('Error formatting value {} as type {} for arg {} in  G2Image.setControl'
3482                                    .format(value,typ,arg))
3483
3484    def getControl(self,arg):
3485        '''Return an Image Controls parameter in the current image.
3486        If the parameter is not found an exception is raised.
3487
3488        :param str arg: the name of a parameter (dict entry) in the
3489          image.
3490        :returns: the value as a int, float, list,...
3491        '''
3492        if arg in self.data['Image Controls']:
3493            return self.data['Image Controls'][arg]
3494        print(self.findControl(''))
3495        raise Exception('arg {} not defined in G2Image.getControl'.format(arg))
3496
3497    def findControl(self,arg=''):
3498        '''Finds the Image Controls parameter(s) in the current image
3499        that match the string in arg. Default is '' which returns all
3500        parameters.
3501
3502            Example:
3503
3504            >>> findControl('calib')
3505            [['calibskip', 'int'], ['calibdmin', 'float'], ['calibrant', 'str']]
3506
3507        :param str arg: a string containing part of the name of a
3508          parameter (dict entry) in the image's Image Controls.
3509        :returns: a list of matching entries in form
3510          [['item','type'], ['item','type'],...] where each 'item' string
3511          contains the sting in arg.
3512        '''
3513        matchList = []
3514        for typ in self.ControlList:
3515            for item in self.ControlList[typ]:
3516                if arg in item:
3517                    matchList.append([item,typ])
3518        return matchList
3519
3520    def setCalibrant(self,calib):
3521        '''Set a calibrant for the current image
3522
3523        :param str calib: specifies a calibrant name which must be one of
3524          the entries in file ImageCalibrants.py. This is validated and
3525          an error provides a list of valid choices.
3526        '''
3527        import ImageCalibrants as calFile
3528        if calib in calFile.Calibrants.keys():
3529            self.data['Image Controls']['calibrant'] = calib
3530            return
3531        print('Calibrant {} is not valid. Valid calibrants'.format(calib))
3532        for i in calFile.Calibrants.keys():
3533            if i: print('\t"{}"'.format(i))
3534       
3535    def setControlFile(self,typ,imageRef,mult=None):
3536        '''Set a image to be used as a background/dark/gain map image
3537
3538        :param str typ: specifies image type, which must be one of:
3539           'background image', 'dark image', 'gain map'; N.B. only the first
3540           four characters must be specified and case is ignored.
3541        :param imageRef: A reference to the desired image. Either the Image
3542          tree name (str), the image's index (int) or
3543          a image object (:class:`G2Image`)
3544        :param float mult: a multiplier to be applied to the image (not used
3545          for 'Gain map'; required for 'background image', 'dark image'
3546        '''
3547        if 'back' in typ.lower():
3548            key = 'background image'
3549            mult = float(mult)
3550        elif 'dark' in typ.lower():
3551            key = 'dark image'
3552            mult = float(mult)
3553        elif 'gain' in typ.lower():
3554            #key = 'Gain map'
3555            if mult is not None:
3556                print('Ignoring multiplier for Gain map')
3557            mult = None
3558        else:
3559            raise Exception("Invalid typ {} for setControlFile".format(typ))
3560        imgNam = self.proj.image(imageRef).name
3561        if mult is None:
3562            self.data['Image Controls']['Gain map'] = imgNam
3563        else:
3564            self.data['Image Controls'][key] = [imgNam,mult]
3565
3566    def loadControls(self,filename):
3567        '''load controls from a .imctrl file
3568
3569        :param str filename: specifies a file to be read, which should end
3570          with .imctrl
3571        '''
3572        File = open(filename,'r')
3573        Slines = File.readlines()
3574        File.close()
3575        G2fil.LoadControls(Slines,self.data['Image Controls'])
3576        print('file {} read into {}'.format(filename,self.name))
3577
3578    def saveControls(self,filename):
3579        '''write current controls values to a .imctrl file
3580
3581        :param str filename: specifies a file to write, which should end
3582          with .imctrl
3583        '''
3584        G2fil.WriteControls(filename,self.data['Image Controls'])
3585        print('file {} written from {}'.format(filename,self.name))
3586
3587    def loadMasks(self,filename,ignoreThreshold=False):
3588        '''load masks from a .immask file
3589
3590        :param str filename: specifies a file to be read, which should end
3591          with .immask
3592        :param bool ignoreThreshold: If True, masks are loaded with
3593          threshold masks. Default is False which means any Thresholds
3594          in the file are ignored.
3595        '''
3596        G2fil.readMasks(filename,self.data['Masks'],ignoreThreshold)
3597        print('file {} read into {}'.format(filename,self.name))
3598       
3599    def getVary(self,*args):
3600        '''Return the refinement flag(s) for Image Controls parameter(s)
3601        in the current image.
3602        If the parameter is not found, an exception is raised.
3603
3604        :param str arg: the name of a refinement parameter in the
3605          varyList for the image. The name should be one of
3606          'dep', 'det-X', 'det-Y', 'dist', 'phi', 'tilt', or 'wave'
3607        :param str arg1: the name of a parameter (dict entry) as before,
3608          optional
3609
3610
3611        :returns: a list of bool value(s)
3612        '''
3613        res = []
3614        for arg in args:
3615            if arg in self.data['Image Controls']['varyList']:
3616                res.append(self.data['Image Controls']['varyList'][arg])
3617            else:
3618                raise Exception('arg {} not defined in G2Image.getVary'.format(arg))
3619        return res
3620   
3621    def setVary(self,arg,value):
3622        '''Set a refinement flag for Image Controls parameter in the
3623        current image.
3624        If the parameter is not found an exception is raised.
3625
3626        :param str arg: the name of a refinement parameter in the
3627          varyList for the image. The name should be one of
3628          'dep', 'det-X', 'det-Y', 'dist', 'phi', 'tilt', or 'wave'
3629        :param str arg: the name of a parameter (dict entry) in the
3630          image. The parameter must be found in :data:`ControlList`
3631          or an exception is raised.
3632        :param value: the value to set the parameter. The value is
3633          cast as the appropriate type from :data:`ControlList`.
3634        '''
3635        if arg in self.data['Image Controls']['varyList']:
3636            self.data['Image Controls']['varyList'][arg] = bool(value)
3637        else:
3638            raise Exception('arg {} not defined in G2Image.setVary'.format(arg))
3639
3640    def Recalibrate(self):
3641        '''Invokes a recalibration fit (same as Image Controls/Calibration/Recalibrate
3642        menu command). Note that for this to work properly, the calibration
3643        coefficients (center, wavelength, ditance & tilts) must be fairly close.
3644        This may produce a better result if run more than once.
3645        '''
3646        LoadG2fil()
3647        ImageZ = _getCorrImage(Readers['Image'],self.proj,self)
3648        G2img.ImageRecalibrate(None,ImageZ,self.data['Image Controls'],self.data['Masks'])
3649
3650    def Integrate(self,name=None):
3651        '''Invokes an image integration (same as Image Controls/Integration/Integrate
3652        menu command). All parameters will have previously been set with Image Controls
3653        so no input is needed here. Note that if integration is performed on an
3654        image more than once, histogram entries may be overwritten. Use the name
3655        parameter to prevent this if desired.
3656
3657        :param str name: base name for created histogram(s). If None (default),
3658          the histogram name is taken from the image name.
3659        :returns: a list of created histogram (:class:`G2PwdrData`) objects.
3660        '''
3661        blkSize = 256   #256 seems to be optimal; will break in polymask if >1024
3662        ImageZ = _getCorrImage(Readers['Image'],self.proj,self)
3663        # do integration
3664        ints,azms,Xvals,cancel = G2img.ImageIntegrate(ImageZ,self.data['Image Controls'],self.data['Masks'],blkSize=blkSize)
3665        # code from here on based on G2IO.SaveIntegration, but places results in the current
3666        # project rather than tree
3667        X = Xvals[:-1]
3668        N = len(X)
3669
3670        data = self.data['Image Controls']
3671        Comments = self.data['Comments']
3672        # make name from image, unless overridden
3673        if name:
3674            if not name.startswith(data['type']+' '):
3675                name = data['type']+' '+name
3676        else:
3677            name = self.name.replace('IMG ',data['type']+' ')
3678        if 'PWDR' in name:
3679            if 'target' in data:
3680                names = ['Type','Lam1','Lam2','I(L2)/I(L1)','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
3681                codes = [0 for i in range(14)]
3682            else:
3683                names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
3684                codes = [0 for i in range(12)]
3685        elif 'SASD' in name:
3686            names = ['Type','Lam','Zero','Azimuth'] 
3687            codes = [0 for i in range(4)]
3688            X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
3689        Xminmax = [X[0],X[-1]]
3690        Azms = []
3691        dazm = 0.
3692        if data['fullIntegrate'] and data['outAzimuths'] == 1:
3693            Azms = [45.0,]                              #a poor man's average?
3694        else:
3695            for i,azm in enumerate(azms[:-1]):
3696                if azm > 360. and azms[i+1] > 360.:
3697                    Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
3698                else:   
3699                    Azms.append(G2img.meanAzm(azm,azms[i+1]))
3700            dazm = np.min(np.abs(np.diff(azms)))/2.
3701        # pull out integration results and make histograms for each
3702        IntgOutList = []
3703        for i,azm in enumerate(azms[:-1]):
3704            Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
3705            # MT dict to contain histogram
3706            HistDict = {}
3707            histItems = [Aname]
3708            Sample = G2obj.SetDefaultSample()       #set as Debye-Scherrer
3709            Sample['Gonio. radius'] = data['distance']
3710            Sample['Omega'] = data['GonioAngles'][0]
3711            Sample['Chi'] = data['GonioAngles'][1]
3712            Sample['Phi'] = data['GonioAngles'][2]
3713            Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
3714            polariz = 0.99    #set default polarization for synchrotron radiation!
3715            for item in Comments:
3716                if 'polariz' in item:
3717                    try:
3718                        polariz = float(item.split('=')[1])
3719                    except:
3720                        polariz = 0.99
3721                for key in ('Temperature','Pressure','Time','FreePrm1','FreePrm2','FreePrm3','Omega',
3722                    'Chi','Phi'):
3723                    if key.lower() in item.lower():
3724                        try:
3725                            Sample[key] = float(item.split('=')[1])
3726                        except:
3727                            pass
3728                if 'label_prm' in item.lower():
3729                    for num in ('1','2','3'):
3730                        if 'label_prm'+num in item.lower():
3731                            Controls['FreePrm'+num] = item.split('=')[1].strip()
3732            if 'PWDR' in Aname:
3733                if 'target' in data:    #from lab x-ray 2D imaging data
3734                    wave1,wave2 = waves[data['target']]
3735                    parms = ['PXC',wave1,wave2,0.5,0.0,polariz,290.,-40.,30.,6.,-14.,0.0,0.0001,Azms[i]]
3736                else:
3737                    parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0,0.0001,Azms[i]]
3738            elif 'SASD' in Aname:
3739                Sample['Trans'] = data['SampleAbs'][0]
3740                parms = ['LXC',data['wavelength'],0.0,Azms[i]]
3741            Y = ints[i]
3742            Ymin = np.min(Y)
3743            Ymax = np.max(Y)
3744            W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
3745            section = 'Comments'
3746            histItems += [section]
3747            HistDict[section] = Comments
3748            section = 'Limits'
3749            histItems += [section]
3750            HistDict[section] = copy.deepcopy([tuple(Xminmax),Xminmax])
3751            if 'PWDR' in Aname:
3752                section = 'Background'
3753                histItems += [section]
3754                HistDict[section] = [['chebyschev',1,3,1.0,0.0,0.0],
3755                    {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}]
3756            inst = [dict(zip(names,zip(parms,parms,codes))),{}]
3757            for item in inst[0]:
3758                inst[0][item] = list(inst[0][item])
3759            section = 'Instrument Parameters'
3760            histItems += [section]
3761            HistDict[section] = inst
3762            if 'PWDR' in Aname:
3763                section = 'Sample Parameters'
3764                histItems += [section]
3765                HistDict[section] = Sample
3766                section = 'Peak List'
3767                histItems += [section]
3768                HistDict[section] = {'sigDict':{},'peaks':[]}
3769                section = 'Index Peak List'
3770                histItems += [section]
3771                HistDict[section] = [[],[]]
3772                section = 'Unit Cells List'
3773                histItems += [section]
3774                HistDict[section] = []
3775                section = 'Reflection Lists'
3776                histItems += [section]
3777                HistDict[section] = {}
3778            elif 'SASD' in Aname:             
3779                section = 'Substances'
3780                histItems += [section]
3781                HistDict[section] = G2pdG.SetDefaultSubstances()  # this needs to be moved
3782                section = 'Sample Parameters'
3783                histItems += [section]
3784                HistDict[section] = Sample
3785                section = 'Models'
3786                histItems += [section]
3787                HistDict[section] = G2pdG.SetDefaultSASDModel() # this needs to be moved
3788            valuesdict = {
3789                'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxsize),'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
3790                'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,'Yminmax':[Ymin,Ymax]}
3791            # if Aname is already in the project replace it
3792            for j in self.proj.names:
3793                if j[0] == Aname: 
3794                    print('Replacing "{}" in project'.format(Aname))
3795                    break
3796            else:
3797                print('Adding "{}" to project'.format(Aname))
3798                self.proj.names.append([Aname]+
3799                        [u'Comments',u'Limits',u'Background',u'Instrument Parameters',
3800                         u'Sample Parameters', u'Peak List', u'Index Peak List',
3801                         u'Unit Cells List', u'Reflection Lists'])
3802            HistDict['data'] = [valuesdict,
3803                    [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]]
3804            self.proj.data[Aname] = HistDict
3805            IntgOutList.append(self.proj.histogram(Aname))
3806        return IntgOutList
3807
3808##########################
3809# Command Line Interface #
3810##########################
3811# Each of these takes an argparse.Namespace object as their argument,
3812# representing the parsed command-line arguments for the relevant subcommand.
3813# The argument specification for each is in the subcommands dictionary (see
3814# below)
3815
3816commandhelp={}
3817commandhelp["create"] = "creates a GSAS-II project, optionally adding histograms and/or phases"
3818def create(args):
3819    """Implements the create command-line subcommand. This creates a GSAS-II project, optionally adding histograms and/or phases::
3820
3821  usage: GSASIIscriptable.py create [-h] [-d HISTOGRAMS [HISTOGRAMS ...]]
3822                                  [-i IPARAMS [IPARAMS ...]]
3823                                  [-p PHASES [PHASES ...]]
3824                                  filename
3825                                 
3826positional arguments::
3827
3828  filename              the project file to create. should end in .gpx
3829
3830optional arguments::
3831
3832  -h, --help            show this help message and exit
3833  -d HISTOGRAMS [HISTOGRAMS ...], --histograms HISTOGRAMS [HISTOGRAMS ...]
3834                        list of datafiles to add as histograms
3835  -i IPARAMS [IPARAMS ...], --iparams IPARAMS [IPARAMS ...]
3836                        instrument parameter file, must be one for every
3837                        histogram
3838  -p PHASES [PHASES ...], --phases PHASES [PHASES ...]
3839                        list of phases to add. phases are automatically
3840                        associated with all histograms given.
3841
3842    """
3843    proj = G2Project(gpxname=args.filename)
3844
3845    hist_objs = []
3846    if args.histograms:
3847        for h,i in zip(args.histograms,args.iparams):
3848            print("Adding histogram from",h,"with instparm ",i)
3849            hist_objs.append(proj.add_powder_histogram(h, i))
3850
3851    if args.phases: 
3852        for p in args.phases:
3853            print("Adding phase from",p)
3854            proj.add_phase(p, histograms=hist_objs)
3855        print('Linking phase(s) to histogram(s):')
3856        for h in hist_objs:
3857            print ('   '+h.name)
3858
3859    proj.save()
3860
3861commandhelp["add"] = "adds histograms and/or phases to GSAS-II project"
3862def add(args):
3863    """Implements the add command-line subcommand. This adds histograms and/or phases to GSAS-II project::
3864
3865  usage: GSASIIscriptable.py add [-h] [-d HISTOGRAMS [HISTOGRAMS ...]]
3866                               [-i IPARAMS [IPARAMS ...]]
3867                               [-hf HISTOGRAMFORMAT] [-p PHASES [PHASES ...]]
3868                               [-pf PHASEFORMAT] [-l HISTLIST [HISTLIST ...]]
3869                               filename
3870
3871
3872positional arguments::
3873
3874  filename              the project file to open. Should end in .gpx
3875
3876optional arguments::
3877
3878  -h, --help            show this help message and exit
3879  -d HISTOGRAMS [HISTOGRAMS ...], --histograms HISTOGRAMS [HISTOGRAMS ...]
3880                        list of datafiles to add as histograms
3881  -i IPARAMS [IPARAMS ...], --iparams IPARAMS [IPARAMS ...]
3882                        instrument parameter file, must be one for every
3883                        histogram
3884  -hf HISTOGRAMFORMAT, --histogramformat HISTOGRAMFORMAT
3885                        format hint for histogram import. Applies to all
3886                        histograms
3887  -p PHASES [PHASES ...], --phases PHASES [PHASES ...]
3888                        list of phases to add. phases are automatically
3889                        associated with all histograms given.
3890  -pf PHASEFORMAT, --phaseformat PHASEFORMAT
3891                        format hint for phase import. Applies to all phases.
3892                        Example: -pf CIF
3893  -l HISTLIST [HISTLIST ...], --histlist HISTLIST [HISTLIST ...]
3894                        list of histgram indices to associate with added
3895                        phases. If not specified, phases are associated with
3896                        all previously loaded histograms. Example: -l 2 3 4
3897   
3898    """
3899    proj = G2Project(args.filename)
3900
3901    if args.histograms:
3902        for h,i in zip(args.histograms,args.iparams):
3903            print("Adding histogram from",h,"with instparm ",i)
3904            proj.add_powder_histogram(h, i, fmthint=args.histogramformat)
3905
3906    if args.phases: 
3907        if not args.histlist:
3908            histlist = proj.histograms()
3909        else:
3910            histlist = [proj.histogram(i) for i in args.histlist]
3911
3912        for p in args.phases:
3913            print("Adding phase from",p)
3914            proj.add_phase(p, histograms=histlist, fmthint=args.phaseformat)
3915           
3916        if not args.histlist:
3917            print('Linking phase(s) to all histogram(s)')
3918        else:
3919            print('Linking phase(s) to histogram(s):')
3920            for h in histlist:
3921                print ('   '+h.name)
3922
3923    proj.save()
3924
3925
3926commandhelp["dump"] = "Shows the contents of a GSAS-II project"
3927def dump(args):
3928    """Implements the dump command-line subcommand, which shows the contents of a GSAS-II project::
3929
3930       usage: GSASIIscriptable.py dump [-h] [-d] [-p] [-r] files [files ...]
3931
3932positional arguments::
3933
3934  files
3935
3936optional arguments::
3937
3938  -h, --help        show this help message and exit
3939  -d, --histograms  list histograms in files, overrides --raw
3940  -p, --phases      list phases in files, overrides --raw
3941  -r, --raw         dump raw file contents, default
3942 
3943    """
3944    if not args.histograms and not args.phases:
3945        args.raw = True
3946    if args.raw:
3947        import IPython.lib.pretty as pretty
3948
3949    for fname in args.files:
3950        if args.raw:
3951            proj, nameList = LoadDictFromProjFile(fname)
3952            print("file:", fname)
3953            print("names:", nameList)
3954            for key, val in proj.items():
3955                print(key, ":")
3956                pretty.pprint(val)
3957        else:
3958            proj = G2Project(fname)
3959            if args.histograms:
3960                hists = proj.histograms()
3961                for h in hists:
3962                    print(fname, "hist", h.id, h.name)
3963            if args.phases:
3964                phase_list = proj.phases()
3965                for p in phase_list:
3966                    print(fname, "phase", p.id, p.name)
3967
3968
3969commandhelp["browse"] = "Load a GSAS-II project and then open a IPython shell to browse it"
3970def IPyBrowse(args):
3971    """Load a .gpx file and then open a IPython shell to browse it::
3972
3973  usage: GSASIIscriptable.py browse [-h] files [files ...]
3974
3975positional arguments::
3976
3977  files       list of files to browse
3978
3979optional arguments::
3980
3981  -h, --help  show this help message and exit
3982
3983    """
3984    for fname in args.files:
3985        proj, nameList = LoadDictFromProjFile(fname)
3986        msg = "\nfname {} loaded into proj (dict) with names in nameList".format(fname)
3987        GSASIIpath.IPyBreak_base(msg)
3988        break
3989
3990
3991commandhelp["refine"] = '''
3992Conducts refinements on GSAS-II projects according to a list of refinement
3993steps in a JSON dict
3994'''
3995def refine(args):
3996    """Implements the refine command-line subcommand:
3997    conducts refinements on GSAS-II projects according to a JSON refinement dict::
3998
3999        usage: GSASIIscriptable.py refine [-h] gpxfile [refinements]
4000
4001positional arguments::
4002
4003  gpxfile      the project file to refine
4004  refinements  json file of refinements to apply. if not present refines file
4005               as-is
4006
4007optional arguments::
4008
4009  -h, --help   show this help message and exit
4010 
4011    """
4012    proj = G2Project(args.gpxfile)
4013    if args.refinements is None:
4014        proj.refine()
4015    else:
4016        import json
4017        with open(args.refinements) as refs:
4018            refs = json.load(refs)
4019        if type(refs) is not dict:
4020            raise G2ScriptException("Error: JSON object must be a dict.")
4021        if "code" in refs:
4022            print("executing code:\n|  ",'\n|  '.join(refs['code']))
4023            exec('\n'.join(refs['code']))
4024        proj.do_refinements(refs['refinements'])
4025
4026
4027commandhelp["seqrefine"] = "Not implemented. Placeholder for eventual sequential refinement implementation"
4028def seqrefine(args):
4029    """Future implementation for the seqrefine command-line subcommand """
4030    raise NotImplementedError("seqrefine is not yet implemented")
4031
4032
4033commandhelp["export"] = "Export phase as CIF"
4034def export(args):
4035    """Implements the export command-line subcommand: Exports phase as CIF::
4036
4037      usage: GSASIIscriptable.py export [-h] gpxfile phase exportfile
4038
4039positional arguments::
4040
4041  gpxfile     the project file from which to export
4042  phase       identifier of phase to export
4043  exportfile  the .cif file to export to
4044
4045optional arguments::
4046
4047  -h, --help  show this help message and exit
4048
4049    """
4050    proj = G2Project(args.gpxfile)
4051    phase = proj.phase(args.phase)
4052    phase.export_CIF(args.exportfile)
4053
4054
4055def _args_kwargs(*args, **kwargs):
4056    return args, kwargs
4057
4058# A dictionary of the name of each subcommand, and a tuple
4059# of its associated function and a list of its arguments
4060# The arguments are passed directly to the add_argument() method
4061# of an argparse.ArgumentParser
4062
4063subcommands = {"create":
4064               (create, [_args_kwargs('filename',
4065                                      help='the project file to create. should end in .gpx'),
4066
4067                         _args_kwargs('-d', '--histograms',
4068                                      nargs='+',
4069                                      help='list of datafiles to add as histograms'),
4070                                     
4071                         _args_kwargs('-i', '--iparams',
4072                                      nargs='+',
4073                                      help='instrument parameter file, must be one'
4074                                           ' for every histogram'
4075                                      ),
4076
4077                         _args_kwargs('-p', '--phases',
4078                                      nargs='+',
4079                                      help='list of phases to add. phases are '
4080                                           'automatically associated with all '
4081                                           'histograms given.')]),
4082               "add": (add, [_args_kwargs('filename',
4083                                      help='the project file to open. Should end in .gpx'),
4084
4085                         _args_kwargs('-d', '--histograms',
4086                                      nargs='+',
4087                                      help='list of datafiles to add as histograms'),
4088                                     
4089                         _args_kwargs('-i', '--iparams',
4090                                      nargs='+',
4091                                      help='instrument parameter file, must be one'
4092                                           ' for every histogram'
4093                                      ),
4094                                     
4095                         _args_kwargs('-hf', '--histogramformat',
4096                                      help='format hint for histogram import. Applies to all'
4097                                           ' histograms'
4098                                      ),
4099
4100                         _args_kwargs('-p', '--phases',
4101                                      nargs='+',
4102                                      help='list of phases to add. phases are '
4103                                           'automatically associated with all '
4104                                           'histograms given.'),
4105
4106                         _args_kwargs('-pf', '--phaseformat',
4107                                      help='format hint for phase import. Applies to all'
4108                                           ' phases. Example: -pf CIF'
4109                                      ),
4110                                     
4111                         _args_kwargs('-l', '--histlist',
4112                                      nargs='+',
4113                                      help='list of histgram indices to associate with added'
4114                                           ' phases. If not specified, phases are'
4115                                           ' associated with all previously loaded'
4116                                           ' histograms. Example: -l 2 3 4')]),
4117                                           
4118               "dump": (dump, [_args_kwargs('-d', '--histograms',
4119                                     action='store_true',
4120                                     help='list histograms in files, overrides --raw'),
4121
4122                               _args_kwargs('-p', '--phases',
4123                                            action='store_true',
4124                                            help='list phases in files, overrides --raw'),
4125
4126                               _args_kwargs('-r', '--raw',
4127                                      action='store_true', help='dump raw file contents, default'),
4128
4129                               _args_kwargs('files', nargs='+')]),
4130
4131               "refine":
4132               (refine, [_args_kwargs('gpxfile', help='the project file to refine'),
4133                         _args_kwargs('refinements',
4134                                      help='JSON file of refinements to apply. if not present'
4135                                           ' refines file as-is',
4136                                      default=None,
4137                                      nargs='?')]),
4138
4139               "seqrefine": (seqrefine, []),
4140               "export": (export, [_args_kwargs('gpxfile',
4141                                                help='the project file from which to export'),
4142                                   _args_kwargs('phase', help='identifier of phase to export'),
4143                                   _args_kwargs('exportfile', help='the .cif file to export to')]),
4144               "browse": (IPyBrowse, [_args_kwargs('files', nargs='+',
4145                                                   help='list of files to browse')])}
4146
4147
4148def main():
4149    '''The command-line interface for calling GSASIIscriptable as a shell command,
4150    where it is expected to be called as::
4151
4152       python GSASIIscriptable.py <subcommand> <file.gpx> <options>
4153
4154    The following subcommands are defined:
4155
4156        * create, see :func:`create`
4157        * add, see :func:`add`
4158        * dump, see :func:`dump`
4159        * refine, see :func:`refine`
4160        * seqrefine, see :func:`seqrefine`
4161        * export, :func:`export`
4162        * browse, see :func:`IPyBrowse`
4163
4164    .. seealso::
4165        :func:`create`
4166        :func:`add`
4167        :func:`dump`
4168        :func:`refine`
4169        :func:`seqrefine`
4170        :func:`export`
4171        :func:`IPyBrowse`
4172    '''
4173    parser = argparse.ArgumentParser(description=
4174        "Use of "+os.path.split(__file__)[1]+" Allows GSAS-II actions from command line."
4175        )
4176    subs = parser.add_subparsers()
4177
4178    # Create all of the specified subparsers
4179    for name, (func, args) in subcommands.items():
4180        new_parser = subs.add_parser(name,help=commandhelp.get(name),
4181                                     description='Command "'+name+'" '+commandhelp.get(name))
4182        for listargs, kwds in args:
4183            new_parser.add_argument(*listargs, **kwds)
4184        new_parser.set_defaults(func=func)
4185
4186    # Parse and trigger subcommand
4187    result = parser.parse_args()
4188    result.func(result)
4189
4190if __name__ == '__main__':
4191    #fname='/tmp/corundum-template.gpx'
4192    #prj = G2Project(fname)
4193    main()
Note: See TracBrowser for help on using the repository browser.