source: trunk/GSASIIscriptable.py @ 3841

Last change on this file since 3841 was 3841, checked in by vondreele, 3 years ago

fix problem if image calibrant isn't in local calibration files
fix future problem with a logical '-' line 878 og G2image.py
show image intensity range at start of integration
change id to pid in G2plot in places (id is a special fxn in python)
add print of saved gpx file name to SaveDictToProjFile?
set image integration blkSize=1028 (default was 128)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 178.0 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3########### SVN repository information ###################
4# $Date: 2019-03-07 19:50:32 +0000 (Thu, 07 Mar 2019) $
5# $Author: vondreele $
6# $Revision: 3841 $
7# $URL: trunk/GSASIIscriptable.py $
8# $Id: GSASIIscriptable.py 3841 2019-03-07 19:50:32Z vondreele $
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`. Likely for internal use only.
1282
1283    :param list ImageReaderlist: list of Reader objects for images
1284    :param object ImageReaderlist: list of Reader objects for images
1285    :param imageRef: A reference to the desired image. Either the Image
1286      tree name (str), the image's index (int) or
1287      a image object (:class:`G2Image`)
1288
1289    :return: array sumImg: corrected image for background/dark/flat back
1290    '''
1291    ImgObj = proj.image(imageRef)
1292    Controls = ImgObj.data['Image Controls']
1293    formatName = Controls.get('formatName','')
1294    imagefile = ImgObj.data['data'][1]
1295    ImageTag = None # fix this for multiimage files
1296    sumImg = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1297    if sumImg is None:
1298        return []
1299    darkImg = False
1300    if 'dark image' in Controls:
1301        darkImg,darkScale = Controls['dark image']
1302        if darkImg:
1303            dImgObj = proj.image(darkImg)
1304            formatName = dImgObj.data['Image Controls'].get('formatName','')
1305            imagefile = dImgObj.data['data'][1]
1306            ImageTag = None # fix this for multiimage files
1307            darkImg = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1308            if darkImg is None:
1309                raise Exception('Error reading dark image {}'.format(imagefile))
1310            sumImg += np.array(darkImage*darkScale,dtype='int32')
1311    if 'background image' in Controls:
1312        backImg,backScale = Controls['background image']           
1313        if backImg:     #ignores any transmission effect in the background image
1314            bImgObj = proj.image(backImg)
1315            formatName = bImgObj.data['Image Controls'].get('formatName','')
1316            imagefile = bImgObj.data['data'][1]
1317            ImageTag = None # fix this for multiimage files
1318            backImg = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1319            if backImage is None:
1320                raise Exception('Error reading background image {}'.format(imagefile))
1321            if darkImg:
1322                backImage += np.array(darkImage*darkScale/backScale,dtype='int32')
1323            else:
1324                sumImg += np.array(backImage*backScale,dtype='int32')
1325    if 'Gain map' in Controls:
1326        gainMap = Controls['Gain map']
1327        if gainMap:
1328            gImgObj = proj.image(gainMap)
1329            formatName = gImgObj.data['Image Controls'].get('formatName','')
1330            imagefile = gImgObj.data['data'][1]
1331            ImageTag = None # fix this for multiimage files
1332            GMimage = G2fil.RereadImageData(ImageReaderlist,imagefile,ImageTag=ImageTag,FormatName=formatName)
1333            if GMimage is None:
1334                raise Exception('Error reading Gain map image {}'.format(imagefile))
1335            sumImg = sumImg*GMimage/1000
1336    sumImg -= int(Controls.get('Flat Bkg',0))
1337    Imax = np.max(sumImg)
1338    Controls['range'] = [(0,Imax),[0,Imax]]
1339    return np.asarray(sumImg,dtype='int32')
1340
1341class G2ObjectWrapper(object):
1342    """Base class for all GSAS-II object wrappers.
1343
1344    The underlying GSAS-II format can be accessed as `wrapper.data`. A number
1345    of overrides are implemented so that the wrapper behaves like a dictionary.
1346
1347    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
1348    """
1349    def __init__(self, datadict):
1350        self.data = datadict
1351
1352    def __getitem__(self, key):
1353        return self.data[key]
1354
1355    def __setitem__(self, key, value):
1356        self.data[key] = value
1357
1358    def __contains__(self, key):
1359        return key in self.data
1360
1361    def get(self, k, d=None):
1362        return self.data.get(k, d)
1363
1364    def keys(self):
1365        return self.data.keys()
1366
1367    def values(self):
1368        return self.data.values()
1369
1370    def items(self):
1371        return self.data.items()
1372
1373
1374class G2Project(G2ObjectWrapper):   
1375    """Represents an entire GSAS-II project.
1376
1377    :param str gpxfile: Existing .gpx file to be loaded. If nonexistent,
1378            creates an empty project.
1379    :param str author: Author's name (not yet implemented)
1380    :param str newgpx: The filename the project should be saved to in
1381            the future. If both newgpx and gpxfile are present, the project is
1382            loaded from the gpxfile, then when saved will be written to newgpx.
1383    :param str filename: Name to be used to save the project. Has same function as
1384            parameter newgpx (do not use both gpxfile and filename). Use of newgpx
1385            is preferred over filename.
1386
1387    There are two ways to initialize this object:
1388
1389    >>> # Load an existing project file
1390    >>> proj = G2Project('filename.gpx')
1391   
1392    >>> # Create a new project
1393    >>> proj = G2Project(newgpx='new_file.gpx')
1394   
1395    Histograms can be accessed easily.
1396
1397    >>> # By name
1398    >>> hist = proj.histogram('PWDR my-histogram-name')
1399   
1400    >>> # Or by index
1401    >>> hist = proj.histogram(0)
1402    >>> assert hist.id == 0
1403   
1404    >>> # Or by random id
1405    >>> assert hist == proj.histogram(hist.ranId)
1406
1407    Phases can be accessed the same way.
1408
1409    >>> phase = proj.phase('name of phase')
1410
1411    New data can also be loaded via :meth:`~G2Project.add_phase` and
1412    :meth:`~G2Project.add_powder_histogram`.
1413
1414    >>> hist = proj.add_powder_histogram('some_data_file.chi',
1415                                         'instrument_parameters.prm')
1416    >>> phase = proj.add_phase('my_phase.cif', histograms=[hist])
1417
1418    Parameters for Rietveld refinement can be turned on and off as well.
1419    See :meth:`~G2Project.set_refinement`, :meth:`~G2Project.clear_refinements`,
1420    :meth:`~G2Project.iter_refinements`, :meth:`~G2Project.do_refinements`.
1421    """
1422    def __init__(self, gpxfile=None, author=None, filename=None, newgpx=None):
1423        if filename is not None and newgpx is not None:
1424            raise G2ScriptException('Do not use filename and newgpx together')
1425        elif newgpx is not None:
1426            filename = newgpx
1427        if gpxfile is None:
1428            filename = os.path.abspath(os.path.expanduser(filename))
1429            self.filename = filename
1430            self.data, self.names = make_empty_project(author=author, filename=filename)
1431        elif isinstance(gpxfile, str): # TODO: create replacement for isinstance that checks if path exists
1432                                       # filename is valid, etc.
1433            if isinstance(filename, str): 
1434                self.filename = os.path.abspath(os.path.expanduser(filename)) # both are defined
1435            else: 
1436                self.filename = os.path.abspath(os.path.expanduser(gpxfile))
1437            # TODO set author
1438            self.data, self.names = LoadDictFromProjFile(gpxfile)
1439            self.update_ids()
1440        else:
1441            raise ValueError("Not sure what to do with gpxfile")
1442
1443    @classmethod
1444    def from_dict_and_names(cls, gpxdict, names, filename=None):
1445        """Creates a :class:`G2Project` directly from
1446        a dictionary and a list of names. If in doubt, do not use this.
1447
1448        :returns: a :class:`G2Project`
1449        """
1450        out = cls()
1451        if filename:
1452            filename = os.path.abspath(os.path.expanduser(filename))
1453            out.filename = filename
1454            gpxdict['Controls']['data']['LastSavedAs'] = filename
1455        else:
1456            try:
1457                out.filename = gpxdict['Controls']['data']['LastSavedAs']
1458            except KeyError:
1459                out.filename = None
1460        out.data = gpxdict
1461        out.names = names
1462
1463    def save(self, filename=None):
1464        """Saves the project, either to the current filename, or to a new file.
1465
1466        Updates self.filename if a new filename provided"""
1467        # TODO update LastSavedUsing ?
1468        if filename:
1469            filename = os.path.abspath(os.path.expanduser(filename))
1470            self.data['Controls']['data']['LastSavedAs'] = filename
1471            self.filename = filename
1472        elif not self.filename:
1473            raise AttributeError("No file name to save to")
1474        SaveDictToProjFile(self.data, self.names, self.filename)
1475
1476    def add_powder_histogram(self, datafile, iparams, phases=[], fmthint=None,
1477                                 databank=None, instbank=None):
1478        """Loads a powder data histogram into the project.
1479
1480        Automatically checks for an instrument parameter file, or one can be
1481        provided. Note that in unix fashion, "~" can be used to indicate the
1482        home directory (e.g. ~/G2data/data.fxye).
1483
1484        :param str datafile: The powder data file to read, a filename.
1485        :param str iparams: The instrument parameters file, a filename.
1486        :param list phases: Phases to link to the new histogram
1487        :param str fmthint: If specified, only importers where the format name
1488          (reader.formatName, as shown in Import menu) contains the
1489          supplied string will be tried as importers. If not specified, all
1490          importers consistent with the file extension will be tried
1491          (equivalent to "guess format" in menu).
1492        :param int databank: Specifies a dataset number to read, if file contains
1493          more than set of data. This should be 1 to read the first bank in
1494          the file (etc.) regardless of the number on the Bank line, etc.
1495          Default is None which means there should only be one dataset in the
1496          file.
1497        :param int instbank: Specifies an instrument parameter set to read, if
1498          the instrument parameter file contains more than set of parameters.
1499          This will match the INS # in an GSAS type file so it will typically
1500          be 1 to read the first parameter set in the file (etc.)
1501          Default is None which means there should only be one parameter set
1502          in the file.
1503
1504        :returns: A :class:`G2PwdrData` object representing
1505            the histogram
1506        """
1507        LoadG2fil()
1508        datafile = os.path.abspath(os.path.expanduser(datafile))
1509        iparams = os.path.abspath(os.path.expanduser(iparams))
1510        pwdrreaders = import_generic(datafile, Readers['Pwdr'],fmthint=fmthint,bank=databank)
1511        histname, new_names, pwdrdata = load_pwd_from_reader(
1512                                          pwdrreaders[0], iparams,
1513                                          [h.name for h in self.histograms()],bank=instbank)
1514        if histname in self.data:
1515            print("Warning - redefining histogram", histname)
1516        elif self.names[-1][0] == 'Phases':
1517            self.names.insert(-1, new_names)
1518        else:
1519            self.names.append(new_names)
1520        self.data[histname] = pwdrdata
1521        self.update_ids()
1522
1523        for phase in phases:
1524            phase = self.phase(phase)
1525            self.link_histogram_phase(histname, phase)
1526
1527        return self.histogram(histname)
1528
1529    def add_simulated_powder_histogram(self, histname, iparams, Tmin, Tmax, Tstep,
1530                                       wavelength=None, scale=None, phases=[]):
1531        """Loads a powder data histogram into the project.
1532
1533        Requires an instrument parameter file.
1534        Note that in unix fashion, "~" can be used to indicate the
1535        home directory (e.g. ~/G2data/data.prm). The instrument parameter file
1536        will determine if the histogram is x-ray, CW neutron, TOF, etc. as well
1537        as the instrument type.
1538
1539        :param str histname: A name for the histogram to be created.
1540        :param str iparams: The instrument parameters file, a filename.
1541        :param float Tmin: Minimum 2theta or TOF (ms) for dataset to be simulated
1542        :param float Tmax: Maximum 2theta or TOF (ms) for dataset to be simulated
1543        :param float Tstep: Step size in 2theta or TOF (ms) for dataset to be simulated       
1544        :param float wavelength: Wavelength for CW instruments, overriding the value
1545           in the instrument parameters file if specified.
1546        :param float scale: Histogram scale factor which multiplies the pattern. Note that
1547           simulated noise is added to the pattern, so that if the maximum intensity is
1548           small, the noise will mask the computed pattern. The scale
1549           needs to be a large number for CW neutrons.
1550           The default, None, provides a scale of 1 for x-rays and TOF; 10,000 for CW neutrons.
1551        :param list phases: Phases to link to the new histogram. Use proj.phases() to link to
1552           all defined phases.
1553
1554        :returns: A :class:`G2PwdrData` object representing the histogram
1555        """
1556        LoadG2fil()
1557        iparams = os.path.abspath(os.path.expanduser(iparams))
1558        if not os.path.exists(iparams):
1559            raise G2ScriptException("File does not exist:"+iparams)
1560        rd = G2obj.ImportPowderData( # Initialize a base class reader
1561            extensionlist=tuple(),
1562            strictExtension=False,
1563            formatName = 'Simulate dataset',
1564            longFormatName = 'Compute a simulated pattern')
1565        rd.powderentry[0] = '' # no filename
1566        rd.powderentry[2] = 1 # only one bank
1567        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1568        rd.idstring = histname
1569        #Iparm1, Iparm2 = load_iprms(iparams, rd)
1570        if Tmax < Tmin:
1571            Tmin,Tmax = Tmax,Tmin
1572        Tstep = abs(Tstep)
1573        if 'TOF' in rd.idstring:
1574                N = (np.log(Tmax)-np.log(Tmin))/Tstep
1575                x = np.exp((np.arange(0,N))*Tstep+np.log(Tmin*1000.))
1576                N = len(x)
1577        else:           
1578                N = int((Tmax-Tmin)/Tstep)+1
1579                x = np.linspace(Tmin,Tmax,N,True)
1580                N = len(x)
1581        if N < 3:
1582            raise G2ScriptException("Error: Range is too small or step is too large, <3 points")
1583        rd.powderdata = [
1584            np.array(x), # x-axis values
1585            np.zeros_like(x), # powder pattern intensities
1586            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1587            np.zeros_like(x), # calc. intensities (zero)
1588            np.zeros_like(x), # calc. background (zero)
1589            np.zeros_like(x), # obs-calc profiles
1590            ]
1591        Tmin = rd.powderdata[0][0]
1592        Tmax = rd.powderdata[0][-1]
1593        histname, new_names, pwdrdata = load_pwd_from_reader(rd, iparams,
1594                                                            [h.name for h in self.histograms()])
1595        if histname in self.data:
1596            print("Warning - redefining histogram", histname)
1597        elif self.names[-1][0] == 'Phases':
1598            self.names.insert(-1, new_names)
1599        else:
1600            self.names.append(new_names)
1601        if scale is not None:
1602            pwdrdata['Sample Parameters']['Scale'][0] = scale
1603        elif pwdrdata['Instrument Parameters'][0]['Type'][0].startswith('PNC'):
1604            pwdrdata['Sample Parameters']['Scale'][0] = 10000.
1605        self.data[histname] = pwdrdata
1606        self.update_ids()
1607
1608        for phase in phases:
1609            phase = self.phase(phase)
1610            self.link_histogram_phase(histname, phase)
1611
1612        return self.histogram(histname)
1613   
1614    def add_phase(self, phasefile, phasename=None, histograms=[], fmthint=None):
1615        """Loads a phase into the project from a .cif file
1616
1617        :param str phasefile: The CIF file from which to import the phase.
1618        :param str phasename: The name of the new phase, or None for the default
1619        :param list histograms: The names of the histograms to associate with
1620            this phase. Use proj.histograms() to add to all histograms.
1621        :param str fmthint: If specified, only importers where the format name
1622          (reader.formatName, as shown in Import menu) contains the
1623          supplied string will be tried as importers. If not specified, all
1624          importers consistent with the file extension will be tried
1625          (equivalent to "guess format" in menu).
1626
1627        :returns: A :class:`G2Phase` object representing the
1628            new phase.
1629        """
1630        LoadG2fil()
1631        histograms = [self.histogram(h).name for h in histograms]
1632        phasefile = os.path.abspath(os.path.expanduser(phasefile))
1633
1634        # TODO handle multiple phases in a file
1635        phasereaders = import_generic(phasefile, Readers['Phase'], fmthint=fmthint)
1636        phasereader = phasereaders[0]
1637       
1638        phasename = phasename or phasereader.Phase['General']['Name']
1639        phaseNameList = [p.name for p in self.phases()]
1640        phasename = G2obj.MakeUniqueLabel(phasename, phaseNameList)
1641        phasereader.Phase['General']['Name'] = phasename
1642
1643        if 'Phases' not in self.data:
1644            self.data[u'Phases'] = { 'data': None }
1645        assert phasename not in self.data['Phases'], "phase names should be unique"
1646        self.data['Phases'][phasename] = phasereader.Phase
1647
1648        if phasereader.Constraints:
1649            Constraints = self.data['Constraints']
1650            for i in phasereader.Constraints:
1651                if isinstance(i, dict):
1652                    if '_Explain' not in Constraints:
1653                        Constraints['_Explain'] = {}
1654                    Constraints['_Explain'].update(i)
1655                else:
1656                    Constraints['Phase'].append(i)
1657
1658        data = self.data['Phases'][phasename]
1659        generalData = data['General']
1660        SGData = generalData['SGData']
1661        NShkl = len(G2spc.MustrainNames(SGData))
1662        NDij = len(G2spc.HStrainNames(SGData))
1663        Super = generalData.get('Super', 0)
1664        if Super:
1665            SuperVec = np.array(generalData['SuperVec'][0])
1666        else:
1667            SuperVec = []
1668        UseList = data['Histograms']
1669
1670        for hist in histograms:
1671            self.link_histogram_phase(hist, phasename)
1672
1673        for obj in self.names:
1674            if obj[0] == 'Phases':
1675                phasenames = obj
1676                break
1677        else:
1678            phasenames = [u'Phases']
1679            self.names.append(phasenames)
1680        phasenames.append(phasename)
1681
1682        # TODO should it be self.filename, not phasefile?
1683        SetupGeneral(data, os.path.dirname(phasefile))
1684        self.index_ids()
1685
1686        self.update_ids()
1687        return self.phase(phasename)
1688
1689    def link_histogram_phase(self, histogram, phase):
1690        """Associates a given histogram and phase.
1691
1692        .. seealso::
1693
1694            :meth:`G2Project.histogram`
1695            :meth:`G2Project.phase`"""
1696        hist = self.histogram(histogram)
1697        phase = self.phase(phase)
1698
1699        generalData = phase['General']
1700
1701        if hist.name.startswith('HKLF '):
1702            raise NotImplementedError("HKLF not yet supported")
1703        elif hist.name.startswith('PWDR '):
1704            hist['Reflection Lists'][generalData['Name']] = {}
1705            UseList = phase['Histograms']
1706            SGData = generalData['SGData']
1707            NShkl = len(G2spc.MustrainNames(SGData))
1708            NDij = len(G2spc.HStrainNames(SGData))
1709            UseList[hist.name] = SetDefaultDData('PWDR', hist.name, NShkl=NShkl, NDij=NDij)
1710            UseList[hist.name]['hId'] = hist.id
1711            for key, val in [('Use', True), ('LeBail', False),
1712                             ('newLeBail', True),
1713                             ('Babinet', {'BabA': [0.0, False],
1714                                          'BabU': [0.0, False]})]:
1715                if key not in UseList[hist.name]:
1716                    UseList[hist.name][key] = val
1717        else:
1718            raise RuntimeError("Unexpected histogram" + hist.name)
1719
1720
1721    def reload(self):
1722        """Reload self from self.filename"""
1723        data, names = LoadDictFromProjFile(self.filename)
1724        self.names = names
1725        # Need to deep copy the new data file data into the current tree,
1726        # so that any existing G2Phase, or G2PwdrData objects will still be
1727        # valid
1728        _deep_copy_into(from_=data, into=self.data)
1729
1730    def refine(self, newfile=None, printFile=None, makeBack=False):
1731        # TODO migrate to RefineCore
1732        # G2strMain.RefineCore(Controls,Histograms,Phases,restraintDict,rigidbodyDict,parmDict,varyList,
1733        #      calcControls,pawleyLookup,ifPrint,printFile,dlg)
1734        # index_ids will automatically save the project
1735        self.index_ids()
1736        # TODO G2strMain does not properly use printFile
1737        G2strMain.Refine(self.filename, makeBack=makeBack)
1738        # Reload yourself
1739        self.reload()
1740
1741    def histogram(self, histname):
1742        """Returns the histogram named histname, or None if it does not exist.
1743
1744        :param histname: The name of the histogram (str), or ranId or index.
1745        :returns: A :class:`G2PwdrData` object, or None if
1746            the histogram does not exist
1747
1748        .. seealso::
1749            :meth:`G2Project.histograms`
1750            :meth:`G2Project.phase`
1751            :meth:`G2Project.phases`
1752        """
1753        if isinstance(histname, G2PwdrData):
1754            if histname.proj == self:
1755                return histname
1756        if histname in self.data:
1757            return G2PwdrData(self.data[histname], self)
1758        try:
1759            # see if histname is an id or ranId
1760            histname = int(histname)
1761        except ValueError:
1762            return
1763
1764        for histogram in self.histograms():
1765            if histogram.id == histname or histogram.ranId == histname:
1766                return histogram
1767
1768    def histograms(self, typ=None):
1769        """Return a list of all histograms, as :class:`G2PwdrData` objects
1770
1771        For now this only finds Powder/Single Xtal histograms, since that is all that is
1772        currently implemented in this module.
1773
1774        :param ste typ: The prefix (type) the histogram such as 'PWDR '. If None
1775          (the default) all known histograms types are found.
1776        :returns: a list of objects
1777
1778        .. seealso::
1779            :meth:`G2Project.histogram`
1780            :meth:`G2Project.phase`
1781            :meth:`G2Project.phases`
1782        """
1783        output = []
1784        # loop through each tree entry. If it is more than one level (more than one item in the
1785        # list of names). then it must be a histogram, unless labeled Phases or Restraints
1786        if typ is None:
1787            for obj in self.names:
1788                if obj[0].startswith('PWDR ') or obj[0].startswith('HKLF '): 
1789                    output.append(self.histogram(obj[0]))
1790        else:
1791            for obj in self.names:
1792                if len(obj) > 1 and obj[0].startswith(typ): 
1793                    output.append(self.histogram(obj[0]))
1794        return output
1795
1796    def phase(self, phasename):
1797        """
1798        Gives an object representing the specified phase in this project.
1799
1800        :param str phasename: A reference to the desired phase. Either the phase
1801            name (str), the phase's ranId, the phase's index (both int) or
1802            a phase object (:class:`G2Phase`)
1803        :returns: A :class:`G2Phase` object
1804        :raises: KeyError
1805
1806        .. seealso::
1807            :meth:`G2Project.histograms`
1808            :meth:`G2Project.phase`
1809            :meth:`G2Project.phases`
1810            """
1811        if isinstance(phasename, G2Phase):
1812            if phasename.proj == self:
1813                return phasename
1814        phases = self.data['Phases']
1815        if phasename in phases:
1816            return G2Phase(phases[phasename], phasename, self)
1817
1818        try:
1819            # phasename should be phase index or ranId
1820            phasename = int(phasename)
1821        except ValueError:
1822            return
1823
1824        for phase in self.phases():
1825            if phase.id == phasename or phase.ranId == phasename:
1826                return phase
1827
1828    def phases(self):
1829        """
1830        Returns a list of all the phases in the project.
1831
1832        :returns: A list of :class:`G2Phase` objects
1833
1834        .. seealso::
1835            :meth:`G2Project.histogram`
1836            :meth:`G2Project.histograms`
1837            :meth:`G2Project.phase`
1838            """
1839        for obj in self.names:
1840            if obj[0] == 'Phases':
1841                return [self.phase(p) for p in obj[1:]]
1842        return []
1843
1844    def _images(self):
1845        """Returns a list of all the phases in the project.
1846        """
1847        return [i[0] for i in self.names if i[0].startswith('IMG ')]
1848   
1849    def image(self, imageRef):
1850        """
1851        Gives an object representing the specified image in this project.
1852
1853        :param str imageRef: A reference to the desired image. Either the Image
1854          tree name (str), the image's index (int) or
1855          a image object (:class:`G2Image`)
1856        :returns: A :class:`G2Image` object
1857        :raises: KeyError
1858
1859        .. seealso::
1860            :meth:`G2Project.images`
1861            """
1862        if isinstance(imageRef, G2Image):
1863            if imageRef.proj == self:
1864                return imageRef
1865            else:
1866                raise Exception("Image {} not in current selected project".format(imageRef.name))
1867        if imageRef in self._images():
1868            return G2Image(self.data[imageRef], imageRef, self)
1869
1870        try:
1871            # imageRef should be an index
1872            num = int(imageRef)
1873            imageRef = self._images()[num] 
1874            return G2Image(self.data[imageRef], imageRef, self)
1875        except ValueError:
1876            raise Exception("imageRef {} not an object, name or image index in current selected project"
1877                                .format(imageRef))
1878        except IndexError:
1879            raise Exception("imageRef {} out of range (max={}) in current selected project"
1880                                .format(imageRef,len(self._images())-1))
1881           
1882    def images(self):
1883        """
1884        Returns a list of all the images in the project.
1885
1886        :returns: A list of :class:`G2Image` objects
1887        """
1888        return [G2Image(self.data[i],i,self) for i in self._images()]
1889   
1890    def update_ids(self):
1891        """Makes sure all phases and histograms have proper hId and pId"""
1892        # Translated from GetUsedHistogramsAndPhasesfromTree,
1893        #   GSASIIdataGUI.py:4107
1894        for i, h in enumerate(self.histograms()):
1895            h.id = i
1896        for i, p in enumerate(self.phases()):
1897            p.id = i
1898
1899    def do_refinements(self, refinements, histogram='all', phase='all',
1900                       outputnames=None, makeBack=False):
1901        """Conducts one or a series of refinements according to the
1902           input provided in parameter refinements. This is a wrapper
1903           around :meth:`iter_refinements`
1904
1905        :param list refinements: A list of dictionaries specifiying changes to be made to
1906            parameters before refinements are conducted.
1907            See the :ref:`Refinement_recipe` section for how this is defined.
1908        :param str histogram: Name of histogram for refinements to be applied
1909            to, or 'all'; note that this can be overridden for each refinement
1910            step via a "histograms" entry in the dict.
1911        :param str phase: Name of phase for refinements to be applied to, or
1912            'all'; note that this can be overridden for each refinement
1913            step via a "phases" entry in the dict.
1914        :param list outputnames: Provides a list of project (.gpx) file names
1915            to use for each refinement step (specifying None skips the save step).
1916            See :meth:`save`.
1917            Note that this can be overridden using an "output" entry in the dict.
1918        :param bool makeBack: determines if a backup ).bckX.gpx) file is made
1919            before a refinement is performed. The default is False.
1920           
1921        To perform a single refinement without changing any parameters, use this
1922        call:
1923
1924        .. code-block::  python
1925       
1926            my_project.do_refinements([])
1927        """
1928       
1929        for proj in self.iter_refinements(refinements, histogram, phase,
1930                                          outputnames, makeBack):
1931            pass
1932        return self
1933
1934    def iter_refinements(self, refinements, histogram='all', phase='all',
1935                         outputnames=None, makeBack=False):
1936        """Conducts a series of refinements, iteratively. Stops after every
1937        refinement and yields this project, to allow error checking or
1938        logging of intermediate results. Parameter use is the same as for
1939        :meth:`do_refinements` (which calls this method).
1940
1941        >>> def checked_refinements(proj):
1942        ...     for p in proj.iter_refinements(refs):
1943        ...         # Track intermediate results
1944        ...         log(p.histogram('0').residuals)
1945        ...         log(p.phase('0').get_cell())
1946        ...         # Check if parameter diverged, nonsense answer, or whatever
1947        ...         if is_something_wrong(p):
1948        ...             raise Exception("I need a human!")
1949
1950           
1951        """
1952        if outputnames:
1953            if len(refinements) != len(outputnames):
1954                raise ValueError("Should have same number of outputs to"
1955                                 "refinements")
1956        else:
1957            outputnames = [None for r in refinements]
1958
1959        for output, refinedict in zip(outputnames, refinements):
1960            if 'histograms' in refinedict:
1961                hist = refinedict['histograms']
1962            else:
1963                hist = histogram
1964            if 'phases' in refinedict:
1965                ph = refinedict['phases']
1966            else:
1967                ph = phase
1968            if 'output' in refinedict:
1969                output = refinedict['output']
1970            self.set_refinement(refinedict, hist, ph)
1971            # Handle 'once' args - refinements that are disabled after this
1972            # refinement
1973            if 'once' in refinedict:
1974                temp = {'set': refinedict['once']}
1975                self.set_refinement(temp, hist, ph)
1976
1977            if output:
1978                self.save(output)
1979
1980            if 'skip' not in refinedict:
1981                self.refine(makeBack=makeBack)
1982            yield self
1983
1984            # Handle 'once' args - refinements that are disabled after this
1985            # refinement
1986            if 'once' in refinedict:
1987                temp = {'clear': refinedict['once']}
1988                self.set_refinement(temp, hist, ph)
1989            if 'call' in refinedict:
1990                fxn = refinedict['call']
1991                if callable(fxn):
1992                    fxn(*refinedict.get('callargs',[self]))
1993                elif callable(eval(fxn)):
1994                    eval(fxn)(*refinedict.get('callargs',[self]))
1995                else:
1996                    raise G2ScriptException("Error: call value {} is not callable".format(fxn))
1997
1998    def set_refinement(self, refinement, histogram='all', phase='all'):
1999        """Apply specified refinements to a given histogram(s) or phase(s).
2000
2001        :param dict refinement: The refinements to be conducted
2002        :param histogram: Specifies either 'all' (default), a single histogram or
2003          a list of histograms. Histograms may be specified as histogram objects
2004          (see :class:`G2PwdrData`), the histogram name (str) or the index number (int)
2005          of the histogram in the project, numbered starting from 0.
2006          Omitting the parameter or the string 'all' indicates that parameters in
2007          all histograms should be set.
2008        :param phase: Specifies either 'all' (default), a single phase or
2009          a list of phases. Phases may be specified as phase objects
2010          (see :class:`G2Phase`), the phase name (str) or the index number (int)
2011          of the phase in the project, numbered starting from 0.
2012          Omitting the parameter or the string 'all' indicates that parameters in
2013          all phases should be set.
2014
2015        Note that refinement parameters are categorized as one of three types:
2016
2017        1. Histogram parameters
2018        2. Phase parameters
2019        3. Histogram-and-Phase (HAP) parameters
2020       
2021        .. seealso::
2022            :meth:`G2PwdrData.set_refinements`
2023            :meth:`G2PwdrData.clear_refinements`
2024            :meth:`G2Phase.set_refinements`
2025            :meth:`G2Phase.clear_refinements`
2026            :meth:`G2Phase.set_HAP_refinements`
2027            :meth:`G2Phase.clear_HAP_refinements`"""
2028
2029        if histogram == 'all':
2030            hists = self.histograms()
2031        elif isinstance(histogram, list) or isinstance(histogram, tuple):
2032            hists = []
2033            for h in histogram:
2034                if isinstance(h, str) or isinstance(h, int):
2035                    hists.append(self.histogram(h))
2036                else:
2037                    hists.append(h)
2038        elif isinstance(histogram, str) or isinstance(histogram, int):
2039            hists = [self.histogram(histogram)]
2040        else:
2041            hists = [histogram]
2042
2043        if phase == 'all':
2044            phases = self.phases()
2045        elif isinstance(phase, list) or isinstance(phase, tuple):
2046            phases = []
2047            for ph in phase:
2048                if isinstance(ph, str) or isinstance(ph, int):
2049                    phases.append(self.phase(ph))
2050                else:
2051                    phases.append(ph)
2052        elif isinstance(phase, str) or isinstance(phase, int):
2053            phases = [self.phase(phase)]
2054        else:
2055            phases = [phase]
2056
2057        # TODO: HAP parameters:
2058        #   Babinet
2059        #   Extinction
2060        #   HStrain
2061        #   Mustrain
2062        #   Pref. Ori
2063        #   Size
2064
2065        pwdr_set = {}
2066        phase_set = {}
2067        hap_set = {}
2068        for key, val in refinement.get('set', {}).items():
2069            # Apply refinement options
2070            if G2PwdrData.is_valid_refinement_key(key):
2071                pwdr_set[key] = val
2072            elif G2Phase.is_valid_refinement_key(key):
2073                phase_set[key] = val
2074            elif G2Phase.is_valid_HAP_refinement_key(key):
2075                hap_set[key] = val
2076            else:
2077                raise ValueError("Unknown refinement key", key)
2078
2079        for hist in hists:
2080            hist.set_refinements(pwdr_set)
2081        for phase in phases:
2082            phase.set_refinements(phase_set)
2083        for phase in phases:
2084            phase.set_HAP_refinements(hap_set, hists)
2085
2086        pwdr_clear = {}
2087        phase_clear = {}
2088        hap_clear = {}
2089        for key, val in refinement.get('clear', {}).items():
2090            # Clear refinement options
2091            if G2PwdrData.is_valid_refinement_key(key):
2092                pwdr_clear[key] = val
2093            elif G2Phase.is_valid_refinement_key(key):
2094                phase_clear[key] = val
2095            elif G2Phase.is_valid_HAP_refinement_key(key):
2096                hap_set[key] = val
2097            else:
2098                raise ValueError("Unknown refinement key", key)
2099
2100        for hist in hists:
2101            hist.clear_refinements(pwdr_clear)
2102        for phase in phases:
2103            phase.clear_refinements(phase_clear)
2104        for phase in phases:
2105            phase.clear_HAP_refinements(hap_clear, hists)
2106
2107    def index_ids(self):
2108        import GSASIIstrIO as G2strIO
2109        self.save()
2110        return G2strIO.GetUsedHistogramsAndPhases(self.filename)
2111
2112    def add_constraint_raw(self, cons_scope, constr):
2113        """Adds a constraint of type consType to the project.
2114        cons_scope should be one of "Hist", "Phase", "HAP", or "Global".
2115
2116        WARNING it does not check the constraint is well-constructed"""
2117        constrs = self.data['Constraints']['data']
2118        if 'Global' not in constrs:
2119            constrs['Global'] = []
2120        constrs[cons_scope].append(constr)
2121
2122    def hold_many(self, vars, type):
2123        """Apply holds for all the variables in vars, for constraint of a given type.
2124
2125        type is passed directly to add_constraint_raw as consType
2126
2127        :param list vars: A list of variables to hold. Either :class:`GSASIIobj.G2VarObj` objects,
2128            string variable specifiers, or arguments for :meth:`make_var_obj`
2129        :param str type: A string constraint type specifier. See
2130            :class:`G2Project.add_constraint_raw`
2131
2132        """
2133        for var in vars:
2134            if isinstance(var, str):
2135                var = self.make_var_obj(var)
2136            elif not isinstance(var, G2obj.G2VarObj):
2137                var = self.make_var_obj(*var)
2138            self.add_constraint_raw(type, [[1.0, var], None, None, 'h'])
2139
2140    def make_var_obj(self, phase=None, hist=None, varname=None, atomId=None,
2141                     reloadIdx=True):
2142        """Wrapper to create a G2VarObj. Takes either a string representaiton ("p:h:name:a")
2143        or individual names of phase, histogram, varname, and atomId.
2144
2145        Automatically converts string phase, hist, or atom names into the ID required
2146        by G2VarObj."""
2147
2148        if reloadIdx:
2149            self.index_ids()
2150
2151        # If string representation, short circuit
2152        if hist is None and varname is None and atomId is None:
2153            if isinstance(phase, str) and ':' in phase:
2154                return G2obj.G2VarObj(phase)
2155
2156        # Get phase index
2157        phaseObj = None
2158        if isinstance(phase, G2Phase):
2159            phaseObj = phase
2160            phase = G2obj.PhaseRanIdLookup[phase.ranId]
2161        elif phase in self.data['Phases']:
2162            phaseObj = self.phase(phase)
2163            phaseRanId = phaseObj.ranId
2164            phase = G2obj.PhaseRanIdLookup[phaseRanId]
2165
2166        # Get histogram index
2167        if isinstance(hist, G2PwdrData):
2168            hist = G2obj.HistRanIdLookup[hist.ranId]
2169        elif hist in self.data:
2170            histRanId = self.histogram(hist).ranId
2171            hist = G2obj.HistRanIdLookup[histRanId]
2172
2173        # Get atom index (if any)
2174        if isinstance(atomId, G2AtomRecord):
2175            atomId = G2obj.AtomRanIdLookup[phase][atomId.ranId]
2176        elif phaseObj:
2177            atomObj = phaseObj.atom(atomId)
2178            if atomObj:
2179                atomRanId = atomObj.ranId
2180                atomId = G2obj.AtomRanIdLookup[phase][atomRanId]
2181
2182        return G2obj.G2VarObj(phase, hist, varname, atomId)
2183
2184    def add_image(self, imagefile, fmthint=None, defaultImage=None):
2185        """Load an image into a project
2186
2187        :param str imagefile: The image file to read, a filename.
2188        :param str fmthint: If specified, only importers where the format name
2189          (reader.formatName, as shown in Import menu) contains the
2190          supplied string will be tried as importers. If not specified, all
2191          importers consistent with the file extension will be tried
2192          (equivalent to "guess format" in menu).
2193        :param str defaultImage: The name of an image to use as a default for
2194          setting parameters for the image file to read.
2195
2196        :returns: a list of G2Image object for the added image(s) [this routine
2197          has not yet been tested with files with multiple images...]
2198        """
2199        LoadG2fil()
2200        imagefile = os.path.abspath(os.path.expanduser(imagefile))
2201        readers = import_generic(imagefile, Readers['Image'], fmthint=fmthint)
2202        objlist = []
2203        for rd in readers:
2204            if rd.SciPy:        #was default read by scipy; needs 1 time fixes
2205                print('TODO: Image {} read by SciPy. Parameters likely need editing'.format(imagefile))
2206                #see this: G2IO.EditImageParms(self,rd.Data,rd.Comments,rd.Image,imagefile)
2207                rd.SciPy = False
2208            rd.readfilename = imagefile
2209            if rd.repeatcount == 1 and not rd.repeat: # skip image number if only one in set
2210                rd.Data['ImageTag'] = None
2211            else:
2212                rd.Data['ImageTag'] = rd.repeatcount
2213            rd.Data['formatName'] = rd.formatName
2214            if rd.sumfile:
2215                rd.readfilename = rd.sumfile
2216            # Load generic metadata, as configured
2217            G2fil.GetColumnMetadata(rd)
2218            # Code from G2IO.LoadImage2Tree(rd.readfilename,self,rd.Comments,rd.Data,rd.Npix,rd.Image)
2219            Imax = np.amax(rd.Image)
2220            ImgNames = [i[0] for i in self.names if i[0].startswith('IMG ')]
2221            TreeLbl = 'IMG '+os.path.basename(imagefile)
2222            ImageTag = rd.Data.get('ImageTag')
2223            if ImageTag:
2224                TreeLbl += ' #'+'%04d'%(ImageTag)
2225                imageInfo = (imagefile,ImageTag)
2226            else:
2227                imageInfo = imagefile
2228            TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
2229            # MT dict to contain image info
2230            ImgDict = {}
2231            ImgDict['data'] = [rd.Npix,imageInfo]
2232            ImgDict['Comments'] = rd.Comments
2233            if defaultImage:
2234                if isinstance(defaultImage, G2Image):
2235                    if defaultImage.proj == self:
2236                        defaultImage = self.data[defaultImage.name]['data']
2237                    else:
2238                        raise Exception("Image {} not in current selected project".format(defaultImage.name))
2239                elif defaultImage in self._images():
2240                    defaultImage = self.data[defaultImage]['data']
2241                else:
2242                    defaultImage = None
2243            Data = rd.Data
2244            if defaultImage:
2245                Data = copy.copy(defaultImage)
2246                Data['showLines'] = True
2247                Data['ring'] = []
2248                Data['rings'] = []
2249                Data['cutoff'] = 10.
2250                Data['pixLimit'] = 20
2251                Data['edgemin'] = 100000000
2252                Data['calibdmin'] = 0.5
2253                Data['calibskip'] = 0
2254                Data['ellipses'] = []
2255                Data['calibrant'] = ''
2256                Data['GonioAngles'] = [0.,0.,0.]
2257                Data['DetDepthRef'] = False
2258            else:
2259                Data['type'] = 'PWDR'
2260                Data['color'] = GSASIIpath.GetConfigValue('Contour_color','Paired')
2261                if 'tilt' not in Data:          #defaults if not preset in e.g. Bruker importer
2262                    Data['tilt'] = 0.0
2263                    Data['rotation'] = 0.0
2264                    Data['pixLimit'] = 20
2265                    Data['calibdmin'] = 0.5
2266                    Data['cutoff'] = 10.
2267                Data['showLines'] = False
2268                Data['calibskip'] = 0
2269                Data['ring'] = []
2270                Data['rings'] = []
2271                Data['edgemin'] = 100000000
2272                Data['ellipses'] = []
2273                Data['GonioAngles'] = [0.,0.,0.]
2274                Data['DetDepth'] = 0.
2275                Data['DetDepthRef'] = False
2276                Data['calibrant'] = ''
2277                Data['IOtth'] = [5.0,50.0]
2278                Data['LRazimuth'] = [0.,180.]
2279                Data['azmthOff'] = 0.0
2280                Data['outChannels'] = 2250
2281                Data['outAzimuths'] = 1
2282                Data['centerAzm'] = False
2283                Data['fullIntegrate'] = GSASIIpath.GetConfigValue('fullIntegrate',True)
2284                Data['setRings'] = False
2285                Data['background image'] = ['',-1.0]                           
2286                Data['dark image'] = ['',-1.0]
2287                Data['Flat Bkg'] = 0.0
2288                Data['Oblique'] = [0.5,False]
2289            Data['setDefault'] = False
2290            Data['range'] = [(0,Imax),[0,Imax]]
2291            ImgDict['Image Controls'] = Data
2292            ImgDict['Masks'] = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],
2293                                'Frames':[],'Thresholds':[(0,Imax),[0,Imax]]}
2294            ImgDict['Stress/Strain']  = {'Type':'True','d-zero':[],'Sample phi':0.0,
2295                                             'Sample z':0.0,'Sample load':0.0}
2296            self.names.append([TreeName]+['Comments','Image Controls','Masks','Stress/Strain'])
2297            self.data[TreeName] = ImgDict
2298            del rd.Image
2299            objlist.append(G2Image(self.data[TreeName], TreeName, self))
2300        return objlist
2301
2302class G2AtomRecord(G2ObjectWrapper):
2303    """Wrapper for an atom record. Has convenient accessors via @property.
2304
2305
2306    Available accessors: label, type, refinement_flags, coordinates,
2307        occupancy, ranId, id, adp_flag, uiso
2308
2309    Example:
2310
2311    >>> atom = some_phase.atom("O3")
2312    >>> # We can access the underlying data format
2313    >>> atom.data
2314    ['O3', 'O-2', '', ... ]
2315    >>> # We can also use wrapper accessors
2316    >>> atom.coordinates
2317    (0.33, 0.15, 0.5)
2318    >>> atom.refinement_flags
2319    u'FX'
2320    >>> atom.ranId
2321    4615973324315876477
2322    >>> atom.occupancy
2323    1.0
2324    """
2325    def __init__(self, data, indices, proj):
2326        self.data = data
2327        self.cx, self.ct, self.cs, self.cia = indices
2328        self.proj = proj
2329
2330    @property
2331    def label(self):
2332        '''Get the associated atom's label
2333        '''
2334        return self.data[self.ct-1]
2335
2336    @property
2337    def type(self):
2338        '''Get the associated atom's type
2339        '''
2340        return self.data[self.ct]
2341
2342    @property
2343    def refinement_flags(self):
2344        '''Get or set refinement flags for the associated atom
2345        '''
2346        return self.data[self.ct+1]
2347
2348    @refinement_flags.setter
2349    def refinement_flags(self, other):
2350        # Automatically check it is a valid refinement
2351        for c in other:
2352            if c not in ' FXU':
2353                raise ValueError("Invalid atom refinement: ", other)
2354        self.data[self.ct+1] = other
2355
2356    @property
2357    def coordinates(self):
2358        '''Get the associated atom's coordinates
2359        '''
2360        return tuple(self.data[self.cx:self.cx+3])
2361
2362    @property
2363    def occupancy(self):
2364        '''Get or set the associated atom's occupancy fraction
2365        '''
2366        return self.data[self.cx+3]
2367
2368    @occupancy.setter
2369    def occupancy(self, val):
2370        self.data[self.cx+3] = float(val)
2371
2372    @property
2373    def ranId(self):
2374        '''Get the associated atom's Random Id number
2375        '''
2376        return self.data[self.cia+8]
2377
2378    @property
2379    def adp_flag(self):
2380        '''Get the associated atom's iso/aniso setting, 'I' or 'A'
2381        '''
2382        # Either 'I' or 'A'
2383        return self.data[self.cia]
2384
2385    @property
2386    def uiso(self):
2387        '''Get or set the associated atom's Uiso or Uaniso value(s)
2388        '''
2389        if self.adp_flag == 'I':
2390            return self.data[self.cia+1]
2391        else:
2392            return self.data[self.cia+2:self.cia+8]
2393
2394    @uiso.setter
2395    def uiso(self, value):
2396        if self.adp_flag == 'I':
2397            self.data[self.cia+1] = float(value)
2398        else:
2399            assert len(value) == 6
2400            self.data[self.cia+2:self.cia+8] = [float(v) for v in value]
2401
2402
2403class G2PwdrData(G2ObjectWrapper):
2404    """Wraps a Powder Data Histogram."""
2405    def __init__(self, data, proj):
2406        self.data = data
2407        self.proj = proj
2408
2409    @staticmethod
2410    def is_valid_refinement_key(key):
2411        valid_keys = ['Limits', 'Sample Parameters', 'Background',
2412                      'Instrument Parameters']
2413        return key in valid_keys
2414
2415    @property
2416    def name(self):
2417        return self.data['data'][-1]
2418
2419    @property
2420    def ranId(self):
2421        return self.data['data'][0]['ranId']
2422
2423    @property
2424    def residuals(self):
2425        '''Provides a dictionary with with the R-factors for this histogram.
2426        Includes the weighted and unweighted profile terms (R, Rb, wR, wRb, wRmin)
2427        as well as the Bragg R-values for each phase (ph:H:Rf and ph:H:Rf^2).
2428        '''
2429        data = self.data['data'][0]
2430        return {key: data[key] for key in data
2431                if key in ['R', 'Rb', 'wR', 'wRb', 'wRmin']
2432                   or ':' in key}
2433
2434    @property
2435    def InstrumentParameters(self):
2436        '''Provides a dictionary with with the Instrument Parameters
2437        for this histogram.
2438        '''
2439        return self.data['Instrument Parameters'][0]
2440
2441    @property
2442    def SampleParameters(self):
2443        '''Provides a dictionary with with the Sample Parameters
2444        for this histogram.
2445        '''
2446        return self.data['Sample Parameters']
2447
2448    @property
2449    def Background(self):
2450        '''Provides a list with with the Background parameters
2451        for this histogram.
2452
2453        :returns: list containing a list and dict with background values
2454        '''
2455        return self.data['Background']
2456
2457    def add_back_peak(self,pos,int,sig,gam,refflags=[]):
2458        '''Adds a background peak to the Background parameters
2459       
2460        :param float pos: position of peak, a 2theta or TOF value
2461        :param float int: integrated intensity of background peak, usually large
2462        :param float sig: Gaussian width of background peak, usually large
2463        :param float gam: Lorentzian width of background peak, usually unused (small)
2464        :param list refflags: a list of 1 to 4 boolean refinement flags for
2465            pos,int,sig & gam, respectively (use [0,1] to refine int only).
2466            Defaults to [] which means nothing is refined.
2467        '''
2468        if 'peaksList' not in self.Background[1]:
2469            self.Background[1]['peaksList'] = []
2470        flags = 4*[False]
2471        for i,f in enumerate(refflags):
2472            if i>3: break
2473            flags[i] = bool(f)
2474        bpk = []
2475        for i,j in zip((pos,int,sig,gam),flags):
2476            bpk += [float(i),j]
2477        self.Background[1]['peaksList'].append(bpk)
2478        self.Background[1]['nPeaks'] = len(self.Background[1]['peaksList'])
2479
2480    def del_back_peak(self,peaknum):
2481        '''Removes a background peak from the Background parameters
2482       
2483        :param int peaknum: the number of the peak (starting from 0)
2484        '''
2485        npks = self.Background[1].get('nPeaks',0)
2486        if peaknum >= npks:
2487            raise Exception('peak {} not found in histogram {}'.format(peaknum,self.name))
2488        del self.Background[1]['peaksList'][peaknum]
2489        self.Background[1]['nPeaks'] = len(self.Background[1]['peaksList'])
2490       
2491    def ref_back_peak(self,peaknum,refflags=[]):
2492        '''Sets refinement flag for a background peak
2493       
2494        :param int peaknum: the number of the peak (starting from 0)
2495        :param list refflags: a list of 1 to 4 boolean refinement flags for
2496            pos,int,sig & gam, respectively. If a flag is not specified
2497            it defaults to False (use [0,1] to refine int only).
2498            Defaults to [] which means nothing is refined.
2499        '''
2500        npks = self.Background[1].get('nPeaks',0)
2501        if peaknum >= npks:
2502            raise Exception('peak {} not found in histogram {}'.format(peaknum,self.name))
2503        flags = 4*[False]
2504        for i,f in enumerate(refflags):
2505            if i>3: break
2506            flags[i] = bool(f)
2507        for i,f in enumerate(flags):
2508            self.Background[1]['peaksList'][peaknum][2*i+1] = f
2509                   
2510    @property
2511    def id(self):
2512        self.proj.update_ids()
2513        return self.data['data'][0]['hId']
2514
2515    @id.setter
2516    def id(self, val):
2517        self.data['data'][0]['hId'] = val
2518
2519    def fit_fixed_points(self):
2520        """Attempts to apply a background fit to the fixed points currently specified."""
2521        def SetInstParms(Inst):
2522            dataType = Inst['Type'][0]
2523            insVary = []
2524            insNames = []
2525            insVals = []
2526            for parm in Inst:
2527                insNames.append(parm)
2528                insVals.append(Inst[parm][1])
2529                if parm in ['U','V','W','X','Y','Z','SH/L','I(L2)/I(L1)','alpha',
2530                    'beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q',] and Inst[parm][2]:
2531                        Inst[parm][2] = False
2532            instDict = dict(zip(insNames, insVals))
2533            instDict['X'] = max(instDict['X'], 0.01)
2534            instDict['Y'] = max(instDict['Y'], 0.01)
2535            if 'SH/L' in instDict:
2536                instDict['SH/L'] = max(instDict['SH/L'], 0.002)
2537            return dataType, instDict, insVary
2538
2539        bgrnd = self.data['Background']
2540
2541        # Need our fixed points in order
2542        bgrnd[1]['FixedPoints'].sort(key=lambda pair: pair[0])
2543        X = [x for x, y in bgrnd[1]['FixedPoints']]
2544        Y = [y for x, y in bgrnd[1]['FixedPoints']]
2545
2546        limits = self.data['Limits'][1]
2547        if X[0] > limits[0]:
2548            X = [limits[0]] + X
2549            Y = [Y[0]] + Y
2550        if X[-1] < limits[1]:
2551            X += [limits[1]]
2552            Y += [Y[-1]]
2553
2554        # Some simple lookups
2555        controls = self.proj['Controls']['data']
2556        inst, inst2 = self.data['Instrument Parameters']
2557        pwddata = self.data['data'][1]
2558
2559        # Construct the data for background fitting
2560        xBeg = np.searchsorted(pwddata[0], limits[0])
2561        xFin = np.searchsorted(pwddata[0], limits[1])
2562        xdata = pwddata[0][xBeg:xFin]
2563        ydata = si.interp1d(X,Y)(ma.getdata(xdata))
2564
2565        W = [1]*len(xdata)
2566        Z = [0]*len(xdata)
2567
2568        dataType, insDict, insVary = SetInstParms(inst)
2569        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
2570
2571        # Do the fit
2572        data = np.array([xdata, ydata, W, Z, Z, Z])
2573        G2pwd.DoPeakFit('LSQ', [], bgrnd, limits, inst, inst2, data,
2574                        prevVaryList=bakVary, controls=controls)
2575
2576        # Post-fit
2577        parmDict = {}
2578        bakType, bakDict, bakVary = G2pwd.SetBackgroundParms(bgrnd)
2579        parmDict.update(bakDict)
2580        parmDict.update(insDict)
2581        pwddata[3][xBeg:xFin] *= 0
2582        pwddata[5][xBeg:xFin] *= 0
2583        pwddata[4][xBeg:xFin] = G2pwd.getBackground('', parmDict, bakType, dataType, xdata)[0]
2584
2585        # TODO adjust pwddata? GSASIIpwdGUI.py:1041
2586        # TODO update background
2587        self.proj.save()
2588
2589    def getdata(self,datatype):
2590        '''Provides access to the histogram data of the selected data type
2591
2592        :param str datatype: must be one of the following values (case is ignored)
2593       
2594           * 'X': the 2theta or TOF values for the pattern
2595           * 'Yobs': the observed intensity values
2596           * 'Yweight': the weights for each data point (1/sigma**2)
2597           * 'Ycalc': the computed intensity values
2598           * 'Background': the computed background values
2599           * 'Residual': the difference between Yobs and Ycalc (obs-calc)
2600
2601        :returns: an numpy MaskedArray with data values of the requested type
2602       
2603        '''
2604        enums = ['x', 'yobs', 'yweight', 'ycalc', 'background', 'residual']
2605        if datatype.lower() not in enums:
2606            raise G2ScriptException("Invalid datatype = "+datatype+" must be one of "+str(enums))
2607        return self.data['data'][1][enums.index(datatype.lower())]
2608       
2609    def y_calc(self):
2610        return self.data['data'][1][3]
2611
2612    def Export(self,fileroot,extension):
2613        '''Write the histogram into a file. The path is specified by fileroot and
2614        extension.
2615       
2616        :param str fileroot: name of the file, optionally with a path (extension is
2617           ignored)
2618        :param str extension: includes '.', must match an extension in global
2619           exportersByExtension['powder'] or a Exception is raised.
2620        :returns: name of file that was written
2621        '''
2622        if extension not in exportersByExtension.get('powder',[]):
2623            raise G2ScriptException('No Writer for file type = "'+extension+'"')
2624        fil = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2625        obj = exportersByExtension['powder'][extension]
2626        obj.SetFromArray(hist=self.data,histname=self.name)
2627        obj.Writer(self.name,fil)
2628           
2629    def plot(self, Yobs=True, Ycalc=True, Background=True, Residual=True):
2630        try:
2631            import matplotlib.pyplot as plt
2632            data = self.data['data'][1]
2633            if Yobs:
2634                plt.plot(data[0], data[1], label='Yobs')
2635            if Ycalc:
2636                plt.plot(data[0], data[3], label='Ycalc')
2637            if Background:
2638                plt.plot(data[0], data[4], label='Background')
2639            if Residual:
2640                plt.plot(data[0], data[5], label="Residual")
2641        except ImportError:
2642            pass
2643
2644    def get_wR(self):
2645        """returns the overall weighted profile R factor for a histogram
2646       
2647        :returns: a wR value as a percentage or None if not defined
2648        """
2649        return self['data'][0].get('wR')
2650
2651    def set_refinements(self, refs):
2652        """Sets the histogram refinement parameter 'key' to the specification 'value'
2653
2654        :param dict refs: A dictionary of the parameters to be set. See
2655                          :ref:`Histogram_parameters_table` for a description of
2656                          what these dictionaries should be.
2657
2658        :returns: None
2659
2660        """
2661        do_fit_fixed_points = False
2662        for key, value in refs.items():
2663            if key == 'Limits':
2664                old_limits = self.data['Limits'][1]
2665                new_limits = value
2666                if isinstance(new_limits, dict):
2667                    if 'low' in new_limits:
2668                        old_limits[0] = new_limits['low']
2669                    if 'high' in new_limits:
2670                        old_limits[1] = new_limits['high']
2671                else:
2672                    old_limits[0], old_limits[1] = new_limits
2673            elif key == 'Sample Parameters':
2674                sample = self.data['Sample Parameters']
2675                for sparam in value:
2676                    if sparam not in sample:
2677                        raise ValueError("Unknown refinement parameter, "
2678                                         + str(sparam))
2679                    sample[sparam][1] = True
2680            elif key == 'Background':
2681                bkg, peaks = self.data['Background']
2682
2683                # If True or False, just set the refine parameter
2684                if value in (True, False):
2685                    bkg[1] = value
2686                    return
2687
2688                if 'type' in value:
2689                    bkg[0] = value['type']
2690                if 'refine' in value:
2691                    bkg[1] = value['refine']
2692                if 'no. coeffs' in value:
2693                    cur_coeffs = bkg[2]
2694                    n_coeffs = value['no. coeffs']
2695                    if n_coeffs > cur_coeffs:
2696                        for x in range(n_coeffs - cur_coeffs):
2697                            bkg.append(0.0)
2698                    else:
2699                        for _ in range(cur_coeffs - n_coeffs):
2700                            bkg.pop()
2701                    bkg[2] = n_coeffs
2702                if 'coeffs' in value:
2703                    bkg[3:] = value['coeffs']
2704                if 'FixedPoints' in value:
2705                    peaks['FixedPoints'] = [(float(a), float(b))
2706                                            for a, b in value['FixedPoints']]
2707                if value.get('fit fixed points', False):
2708                    do_fit_fixed_points = True
2709                if 'peaks' in value:
2710                    for i,flags in enumerate(value['peaks']):
2711                        self.ref_back_peak(i,flags)
2712
2713            elif key == 'Instrument Parameters':
2714                instrument, secondary = self.data['Instrument Parameters']
2715                for iparam in value:
2716                    try:
2717                        instrument[iparam][2] = True
2718                    except IndexError:
2719                        raise ValueError("Invalid key:", iparam)
2720            else:
2721                raise ValueError("Unknown key:", key)
2722        # Fit fixed points after the fact - ensure they are after fixed points
2723        # are added
2724        if do_fit_fixed_points:
2725            # Background won't be fit if refinement flag not set
2726            orig = self.data['Background'][0][1]
2727            self.data['Background'][0][1] = True
2728            self.fit_fixed_points()
2729            # Restore the previous value
2730            self.data['Background'][0][1] = orig
2731
2732    def clear_refinements(self, refs):
2733        """Clears the refinement parameter 'key' and its associated value.
2734
2735        :param dict refs: A dictionary of parameters to clear."""
2736        for key, value in refs.items():
2737            if key == 'Limits':
2738                old_limits, cur_limits = self.data['Limits']
2739                cur_limits[0], cur_limits[1] = old_limits
2740            elif key == 'Sample Parameters':
2741                sample = self.data['Sample Parameters']
2742                for sparam in value:
2743                    sample[sparam][1] = False
2744            elif key == 'Background':
2745                bkg, peaks = self.data['Background']
2746
2747                # If True or False, just set the refine parameter
2748                if value in (True, False):
2749                    bkg[1] = False
2750                    return
2751
2752                bkg[1] = False
2753                if 'FixedPoints' in value:
2754                    if 'FixedPoints' in peaks:
2755                        del peaks['FixedPoints']
2756                if 'peaks' in value:
2757                    for i in range(len(self.Background[1]['peaksList'])):
2758                        self.ref_back_peak(i,[])
2759            elif key == 'Instrument Parameters':
2760                instrument, secondary = self.data['Instrument Parameters']
2761                for iparam in value:
2762                    instrument[iparam][2] = False
2763            else:
2764                raise ValueError("Unknown key:", key)
2765
2766    def add_peak(self,area,dspace=None,Q=None,ttheta=None):
2767        '''Adds a single peak to the peak list
2768        :param float area: peak area
2769        :param float dspace: peak position as d-space (A)
2770        :param float Q: peak position as Q (A-1)
2771        :param float ttheta: peak position as 2Theta (deg)
2772
2773        Note: only one of the parameters dspace, Q or ttheta may be specified
2774        '''
2775        import GSASIIlattice as G2lat
2776        import GSASIImath as G2mth
2777        if (not dspace) + (not Q) + (not ttheta) != 2:
2778            print('add_peak error: too many or no peak position(s) specified')
2779            return
2780        pos = ttheta
2781        Parms,Parms2 = self.data['Instrument Parameters']
2782        if Q:
2783            pos = G2lat.Dsp2pos(Parms,2.*np.pi/Q)
2784        elif dspace:
2785            pos = G2lat.Dsp2pos(Parms,dspace)
2786        peaks = self.data['Peak List']
2787        peaks['sigDict'] = {}        #no longer valid
2788        peaks['peaks'].append(G2mth.setPeakparms(Parms,Parms2,pos,area))
2789
2790    def set_peakFlags(self,peaklist=None,area=None,pos=None,sig=None,gam=None):
2791        '''Set refinement flags for peaks
2792       
2793        :param list peaklist: a list of peaks to change flags. If None (default), changes
2794          are made to all peaks.
2795        :param bool area: Sets or clears the refinement flag for the peak area value.
2796          If None (the default), no change is made.
2797        :param bool pos: Sets or clears the refinement flag for the peak position value.
2798          If None (the default), no change is made.
2799        :param bool sig: Sets or clears the refinement flag for the peak sig (Gaussian width) value.
2800          If None (the default), no change is made.
2801        :param bool gam: Sets or clears the refinement flag for the peak sig (Lorentzian width) value.
2802          If None (the default), no change is made.
2803         
2804        Note that when peaks are first created the area flag is on and the other flags are
2805        initially off.
2806
2807        Example::
2808       
2809           set_peakFlags(sig=False,gam=True)
2810
2811        causes the sig refinement flag to be cleared and the gam flag to be set, in both cases for
2812        all peaks. The position and area flags are not changed from their previous values.
2813        '''
2814        peaks = self.data['Peak List']
2815        if peaklist is None:
2816            peaklist = range(len(peaks['peaks']))
2817        for i in peaklist:
2818            for var,j in [(area,3),(pos,1),(sig,5),(gam,7)]:
2819                if var is not None:
2820                    peaks['peaks'][i][j] = var
2821           
2822    def refine_peaks(self):
2823        '''Causes a refinement of peak position, background and instrument parameters
2824        '''
2825        import GSASIIpwd as G2pwd
2826        controls = self.proj.data.get('Controls',{})
2827        controls = controls.get('data',
2828                            {'deriv type':'analytic','min dM/M':0.001,}     #fill in defaults if needed
2829                            )
2830        peaks = self.data['Peak List']
2831        Parms,Parms2 = self.data['Instrument Parameters']
2832        background = self.data['Background']
2833        limits = self.data['Limits'][1]
2834        bxye = np.zeros(len(self.data['data'][1][1]))
2835        peaks['sigDict'] = G2pwd.DoPeakFit('LSQ',peaks['peaks'],background,limits,
2836                                           Parms,Parms2,self.data['data'][1],bxye,[],
2837                                           False,controls,None)[0]
2838
2839    def SaveProfile(self,filename):
2840        '''Writes a GSAS-II (new style) .instprm file
2841        '''
2842        data,Parms2 = self.data['Instrument Parameters']
2843        filename = os.path.splitext(filename)[0]+'.instprm'         # make sure extension is .instprm
2844        File = open(filename,'w')
2845        File.write("#GSAS-II instrument parameter file; do not add/delete items!\n")
2846        for item in data:
2847            File.write(item+':'+str(data[item][1])+'\n')
2848        File.close()
2849        print ('Instrument parameters saved to: '+filename)
2850
2851    def LoadProfile(self,filename):
2852        '''Reads a GSAS-II (new style) .instprm file and overwrites the current parameters
2853        '''
2854        filename = os.path.splitext(filename)[0]+'.instprm'         # make sure extension is .instprm
2855        File = open(filename,'r')
2856        S = File.readline()
2857        newItems = []
2858        newVals = []
2859        Found = False
2860        while S:
2861            if S[0] == '#':
2862                if Found:
2863                    break
2864                if 'Bank' in S:
2865                    if bank == int(S.split(':')[0].split()[1]):
2866                        S = File.readline()
2867                        continue
2868                    else:
2869                        S = File.readline()
2870                        while S and '#Bank' not in S:
2871                            S = File.readline()
2872                        continue
2873                else:   #a non #Bank file
2874                    S = File.readline()
2875                    continue
2876            Found = True
2877            [item,val] = S[:-1].split(':')
2878            newItems.append(item)
2879            try:
2880                newVals.append(float(val))
2881            except ValueError:
2882                newVals.append(val)                       
2883            S = File.readline()               
2884        File.close()
2885        LoadG2fil()
2886        self.data['Instrument Parameters'][0] = G2fil.makeInstDict(newItems,newVals,len(newVals)*[False,])
2887
2888       
2889class G2Phase(G2ObjectWrapper):
2890    """A wrapper object around a given phase.
2891
2892    Author: Jackson O'Donnell (jacksonhodonnell .at. gmail.com)
2893    """
2894    def __init__(self, data, name, proj):
2895        self.data = data
2896        self.name = name
2897        self.proj = proj
2898
2899    @staticmethod
2900    def is_valid_refinement_key(key):
2901        valid_keys = ["Cell", "Atoms", "LeBail"]
2902        return key in valid_keys
2903
2904    @staticmethod
2905    def is_valid_HAP_refinement_key(key):
2906        valid_keys = ["Babinet", "Extinction", "HStrain", "Mustrain",
2907                      "Pref.Ori.", "Show", "Size", "Use", "Scale"]
2908        return key in valid_keys
2909
2910    def atom(self, atomlabel):
2911        """Returns the atom specified by atomlabel, or None if it does not
2912        exist.
2913
2914        :param str atomlabel: The name of the atom (e.g. "O2")
2915        :returns: A :class:`G2AtomRecord` object
2916            representing the atom.
2917        """
2918        # Consult GSASIIobj.py for the meaning of this
2919        cx, ct, cs, cia = self.data['General']['AtomPtrs']
2920        ptrs = [cx, ct, cs, cia]
2921        atoms = self.data['Atoms']
2922        for atom in atoms:
2923            if atom[ct-1] == atomlabel:
2924                return G2AtomRecord(atom, ptrs, self.proj)
2925
2926    def atoms(self):
2927        """Returns a list of atoms present in the phase.
2928
2929        :returns: A list of :class:`G2AtomRecord` objects.
2930
2931        .. seealso::
2932            :meth:`G2Phase.atom`
2933            :class:`G2AtomRecord`
2934        """
2935        ptrs = self.data['General']['AtomPtrs']
2936        output = []
2937        atoms = self.data['Atoms']
2938        for atom in atoms:
2939            output.append(G2AtomRecord(atom, ptrs, self.proj))
2940        return output
2941
2942    def histograms(self):
2943        output = []
2944        for hname in self.data.get('Histograms', {}).keys():
2945            output.append(self.proj.histogram(hname))
2946        return output
2947
2948    @property
2949    def ranId(self):
2950        return self.data['ranId']
2951
2952    @property
2953    def id(self):
2954        return self.data['pId']
2955
2956    @id.setter
2957    def id(self, val):
2958        self.data['pId'] = val
2959
2960    def get_cell(self):
2961        """Returns a dictionary of the cell parameters, with keys:
2962            'length_a', 'length_b', 'length_c', 'angle_alpha', 'angle_beta', 'angle_gamma', 'volume'
2963
2964        :returns: a dict
2965
2966        .. seealso::
2967           :meth:`G2Phase.get_cell_and_esd`
2968
2969        """
2970        cell = self.data['General']['Cell']
2971        return {'length_a': cell[1], 'length_b': cell[2], 'length_c': cell[3],
2972                'angle_alpha': cell[4], 'angle_beta': cell[5], 'angle_gamma': cell[6],
2973                'volume': cell[7]}
2974
2975    def get_cell_and_esd(self):
2976        """
2977        Returns a pair of dictionaries, the first representing the unit cell, the second
2978        representing the estimated standard deviations of the unit cell.
2979
2980        :returns: a tuple of two dictionaries
2981
2982        .. seealso::
2983           :meth:`G2Phase.get_cell`
2984
2985        """
2986        # translated from GSASIIstrIO.ExportBaseclass.GetCell
2987        import GSASIIstrIO as G2stIO
2988        import GSASIIlattice as G2lat
2989        import GSASIImapvars as G2mv
2990        try:
2991            pfx = str(self.id) + '::'
2992            sgdata = self['General']['SGData']
2993            covDict = self.proj['Covariance']['data']
2994
2995            parmDict = dict(zip(covDict.get('varyList',[]),
2996                                covDict.get('variables',[])))
2997            sigDict = dict(zip(covDict.get('varyList',[]),
2998                               covDict.get('sig',[])))
2999
3000            if covDict.get('covMatrix') is not None:
3001                sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],
3002                                                  covDict['varyList'],
3003                                                  parmDict))
3004
3005            A, sigA = G2stIO.cellFill(pfx, sgdata, parmDict, sigDict)
3006            cellSig = G2stIO.getCellEsd(pfx, sgdata, A, self.proj['Covariance']['data'])
3007            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
3008            cellDict, cellSigDict = {}, {}
3009            for i, key in enumerate(['length_a', 'length_b', 'length_c',
3010                                     'angle_alpha', 'angle_beta', 'angle_gamma',
3011                                     'volume']):
3012                cellDict[key] = cellList[i]
3013                cellSigDict[key] = cellSig[i]
3014            return cellDict, cellSigDict
3015        except KeyError:
3016            cell = self.get_cell()
3017            return cell, {key: 0.0 for key in cell}
3018
3019    def export_CIF(self, outputname, quickmode=True):
3020        """Write this phase to a .cif file named outputname
3021
3022        :param str outputname: The name of the .cif file to write to
3023        :param bool quickmode: Currently ignored. Carryover from exports.G2export_CIF"""
3024        # This code is all taken from exports/G2export_CIF.py
3025        # Functions copied have the same names
3026        import GSASIImath as G2mth
3027        import GSASIImapvars as G2mv
3028        from exports import G2export_CIF as cif
3029
3030        CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
3031        CIFname = os.path.splitext(self.proj.filename)[0]
3032        CIFname = os.path.split(CIFname)[1]
3033        CIFname = ''.join([c if ord(c) < 128 else ''
3034                           for c in CIFname.replace(' ', '_')])
3035        try:
3036            author = self.proj['Controls']['data'].get('Author','').strip()
3037        except KeyError:
3038            pass
3039        oneblock = True
3040
3041        covDict = self.proj['Covariance']['data']
3042        parmDict = dict(zip(covDict.get('varyList',[]),
3043                            covDict.get('variables',[])))
3044        sigDict = dict(zip(covDict.get('varyList',[]),
3045                           covDict.get('sig',[])))
3046
3047        if covDict.get('covMatrix') is not None:
3048            sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],
3049                                              covDict['varyList'],
3050                                              parmDict))
3051
3052        with open(outputname, 'w') as fp:
3053            fp.write(' \n' + 70*'#' + '\n')
3054            cif.WriteCIFitem(fp, 'data_' + CIFname)
3055            # from exports.G2export_CIF.WritePhaseInfo
3056            cif.WriteCIFitem(fp, '\n# phase info for '+str(self.name) + ' follows')
3057            cif.WriteCIFitem(fp, '_pd_phase_name', self.name)
3058            # TODO get esds
3059            cellDict = self.get_cell()
3060            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
3061            names = ['length_a','length_b','length_c',
3062                     'angle_alpha','angle_beta ','angle_gamma',
3063                     'volume']
3064            for key, val in cellDict.items():
3065                cif.WriteCIFitem(fp, '_cell_' + key, G2mth.ValEsd(val))
3066
3067            cif.WriteCIFitem(fp, '_symmetry_cell_setting',
3068                         self.data['General']['SGData']['SGSys'])
3069
3070            spacegroup = self.data['General']['SGData']['SpGrp'].strip()
3071            # regularize capitalization and remove trailing H/R
3072            spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
3073            cif.WriteCIFitem(fp, '_symmetry_space_group_name_H-M', spacegroup)
3074
3075            # generate symmetry operations including centering and center of symmetry
3076            SymOpList, offsetList, symOpList, G2oprList, G2opcodes = G2spc.AllOps(
3077                self.data['General']['SGData'])
3078            cif.WriteCIFitem(fp, 'loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
3079            for i, op in enumerate(SymOpList,start=1):
3080                cif.WriteCIFitem(fp, '   {:3d}  {:}'.format(i,op.lower()))
3081
3082            # TODO skipped histograms, exports/G2export_CIF.py:880
3083
3084            # report atom params
3085            if self.data['General']['Type'] in ['nuclear','macromolecular']:        #this needs macromolecular variant, etc!
3086                cif.WriteAtomsNuclear(fp, self.data, self.name, parmDict, sigDict, [])
3087                # self._WriteAtomsNuclear(fp, parmDict, sigDict)
3088            else:
3089                raise G2ScriptException("no export for "+str(self.data['General']['Type'])+" coordinates implemented")
3090            # report cell contents
3091            cif.WriteComposition(fp, self.data, self.name, parmDict)
3092            if not quickmode and self.data['General']['Type'] == 'nuclear':      # report distances and angles
3093                # WriteDistances(fp,self.name,SymOpList,offsetList,symOpList,G2oprList)
3094                raise NotImplementedError("only quickmode currently supported")
3095            if 'Map' in self.data['General'] and 'minmax' in self.data['General']['Map']:
3096                cif.WriteCIFitem(fp,'\n# Difference density results')
3097                MinMax = self.data['General']['Map']['minmax']
3098                cif.WriteCIFitem(fp,'_refine_diff_density_max',G2mth.ValEsd(MinMax[0],-0.009))
3099                cif.WriteCIFitem(fp,'_refine_diff_density_min',G2mth.ValEsd(MinMax[1],-0.009))
3100
3101
3102    def set_refinements(self, refs):
3103        """Sets the refinement parameter 'key' to the specification 'value'
3104
3105        :param dict refs: A dictionary of the parameters to be set. See
3106                          :ref:`Phase_parameters_table` for a description of
3107                          this dictionary.
3108
3109        :returns: None"""
3110        for key, value in refs.items():
3111            if key == "Cell":
3112                self.data['General']['Cell'][0] = value
3113
3114            elif key == "Atoms":
3115                for atomlabel, atomrefinement in value.items():
3116                    if atomlabel == 'all':
3117                        for atom in self.atoms():
3118                            atom.refinement_flags = atomrefinement
3119                    else:
3120                        atom = self.atom(atomlabel)
3121                        if atom is None:
3122                            raise ValueError("No such atom: " + atomlabel)
3123                        atom.refinement_flags = atomrefinement
3124
3125            elif key == "LeBail":
3126                hists = self.data['Histograms']
3127                for hname, hoptions in hists.items():
3128                    if 'LeBail' not in hoptions:
3129                        hoptions['newLeBail'] = bool(True)
3130                    hoptions['LeBail'] = bool(value)
3131            else:
3132                raise ValueError("Unknown key:", key)
3133
3134    def clear_refinements(self, refs):
3135        """Clears a given set of parameters.
3136
3137        :param dict refs: The parameters to clear"""
3138        for key, value in refs.items():
3139            if key == "Cell":
3140                self.data['General']['Cell'][0] = False
3141            elif key == "Atoms":
3142                cx, ct, cs, cia = self.data['General']['AtomPtrs']
3143
3144                for atomlabel in value:
3145                    atom = self.atom(atomlabel)
3146                    # Set refinement to none
3147                    atom.refinement_flags = ' '
3148            elif key == "LeBail":
3149                hists = self.data['Histograms']
3150                for hname, hoptions in hists.items():
3151                    if 'LeBail' not in hoptions:
3152                        hoptions['newLeBail'] = True
3153                    hoptions['LeBail'] = False
3154            else:
3155                raise ValueError("Unknown key:", key)
3156
3157    def set_HAP_refinements(self, refs, histograms='all'):
3158        """Sets the given HAP refinement parameters between this phase and
3159        the given histograms
3160
3161        :param dict refs: A dictionary of the parameters to be set. See
3162                          :ref:`HAP_parameters_table` for a description of this
3163                          dictionary.
3164        :param histograms: Either 'all' (default) or a list of the histograms
3165            whose HAP parameters will be set with this phase. Histogram and phase
3166            must already be associated
3167
3168        :returns: None
3169        """
3170        if not self.data['Histograms']:
3171            print("Error likely: Phase {} has no linked histograms".format(self.name))
3172            return
3173           
3174        if histograms == 'all':
3175            histograms = self.data['Histograms'].values()
3176        else:
3177            histograms = [self.data['Histograms'][h.name] for h in histograms
3178                          if h.name in self.data['Histograms']]
3179
3180        if not histograms:
3181            print("Skipping HAP set for phase {}, no selected histograms".format(self.name))
3182            return
3183        for key, val in refs.items():
3184            for h in histograms:
3185                if key == 'Babinet':
3186                    try:
3187                        sets = list(val)
3188                    except ValueError:
3189                        sets = ['BabA', 'BabU']
3190                    for param in sets:
3191                        if param not in ['BabA', 'BabU']:
3192                            raise ValueError("Not sure what to do with" + param)
3193                        for hist in histograms:
3194                            hist['Babinet'][param][1] = True
3195                elif key == 'Extinction':
3196                    for h in histograms:
3197                        h['Extinction'][1] = bool(val)
3198                elif key == 'HStrain':
3199                    for h in histograms:
3200                        h['HStrain'][1] = [bool(val) for p in h['HStrain'][1]]
3201                elif key == 'Mustrain':
3202                    for h in histograms:
3203                        mustrain = h['Mustrain']
3204                        newType = None
3205                        direction = None
3206                        if isinstance(val, strtypes):
3207                            if val in ['isotropic', 'uniaxial', 'generalized']:
3208                                newType = val
3209                            else:
3210                                raise ValueError("Not a Mustrain type: " + val)
3211                        elif isinstance(val, dict):
3212                            newType = val.get('type', None)
3213                            direction = val.get('direction', None)
3214
3215                        if newType:
3216                            mustrain[0] = newType
3217                            if newType == 'isotropic':
3218                                mustrain[2][0] = True == val.get('refine',False)
3219                                mustrain[5] = [False for p in mustrain[4]]
3220                            elif newType == 'uniaxial':
3221                                if 'refine' in val:
3222                                    mustrain[2][0] = False
3223                                    types = val['refine']
3224                                    if isinstance(types, strtypes):
3225                                        types = [types]
3226                                    elif isinstance(types, bool):
3227                                        mustrain[2][1] = types
3228                                        mustrain[2][2] = types
3229                                        types = []
3230                                    else:
3231                                        raise ValueError("Not sure what to do with: "
3232                                                         + str(types))
3233                                else:
3234                                    types = []
3235
3236                                for unitype in types:
3237                                    if unitype == 'equatorial':
3238                                        mustrain[2][0] = True
3239                                    elif unitype == 'axial':
3240                                        mustrain[2][1] = True
3241                                    else:
3242                                        msg = 'Invalid uniaxial mustrain type'
3243                                        raise ValueError(msg + ': ' + unitype)
3244                            else:  # newtype == 'generalized'
3245                                mustrain[2] = [False for p in mustrain[1]]
3246                                if 'refine' in val:
3247                                    mustrain[5] = [True == val['refine']]*len(mustrain[5])
3248
3249                        if direction:
3250                            if len(direction) != 3:
3251                                raise ValueError("Expected hkl, found", direction)
3252                            direction = [int(n) for n in direction]
3253                            mustrain[3] = direction
3254                elif key == 'Size':
3255                    newSize = None
3256                    if 'value' in val:
3257                        newSize = float(val['value'])
3258                    for h in histograms:
3259                        size = h['Size']
3260                        newType = None
3261                        direction = None
3262                        if isinstance(val, strtypes):
3263                            if val in ['isotropic', 'uniaxial', 'ellipsoidal']:
3264                                newType = val
3265                            else:
3266                                raise ValueError("Not a valid Size type: " + val)
3267                        elif isinstance(val, dict):
3268                            newType = val.get('type', None)
3269                            direction = val.get('direction', None)
3270
3271                        if newType:
3272                            size[0] = newType
3273                            refine = True == val.get('refine')
3274                            if newType == 'isotropic' and refine is not None:
3275                                size[2][0] = bool(refine)
3276                                if newSize: size[1][0] = newSize
3277                            elif newType == 'uniaxial' and refine is not None:
3278                                size[2][1] = bool(refine)
3279                                size[2][2] = bool(refine)
3280                                if newSize: size[1][1] = size[1][2] =newSize
3281                            elif newType == 'ellipsoidal' and refine is not None:
3282                                size[5] = [bool(refine) for p in size[5]]
3283                                if newSize: size[4] = [newSize for p in size[4]]
3284
3285                        if direction:
3286                            if len(direction) != 3:
3287                                raise ValueError("Expected hkl, found", direction)
3288                            direction = [int(n) for n in direction]
3289                            size[3] = direction
3290                elif key == 'Pref.Ori.':
3291                    for h in histograms:
3292                        h['Pref.Ori.'][2] = bool(val)
3293                elif key == 'Show':
3294                    for h in histograms:
3295                        h['Show'] = bool(val)
3296                elif key == 'Use':
3297                    for h in histograms:
3298                        h['Use'] = bool(val)
3299                elif key == 'Scale':
3300                    for h in histograms:
3301                        h['Scale'][1] = bool(val)
3302                else:
3303                    print(u'Unknown HAP key: '+key)
3304
3305    def getHAPvalues(self, histname):
3306        """Returns a dict with HAP values for the selected histogram
3307
3308        :param histogram: is a histogram object (:class:`G2PwdrData`) or
3309            a histogram name or the index number of the histogram
3310
3311        :returns: HAP value dict
3312        """
3313        if isinstance(histname, G2PwdrData):
3314            histname = histname.name
3315        elif histname in self.data['Histograms']:
3316            pass
3317        elif type(histname) is int:
3318            histname = self.proj.histograms()[histname].name
3319        else:
3320            raise G2ScriptException("Invalid histogram reference: "+str(histname))
3321        return self.data['Histograms'][histname]
3322                   
3323    def clear_HAP_refinements(self, refs, histograms='all'):
3324        """Clears the given HAP refinement parameters between this phase and
3325        the given histograms
3326
3327        :param dict refs: A dictionary of the parameters to be cleared.
3328        :param histograms: Either 'all' (default) or a list of the histograms
3329            whose HAP parameters will be cleared with this phase. Histogram and
3330            phase must already be associated
3331
3332        :returns: None
3333        """
3334        if histograms == 'all':
3335            histograms = self.data['Histograms'].values()
3336        else:
3337            histograms = [self.data['Histograms'][h.name] for h in histograms
3338                          if h.name in self.data['Histograms']]
3339
3340        for key, val in refs.items():
3341            for h in histograms:
3342                if key == 'Babinet':
3343                    try:
3344                        sets = list(val)
3345                    except ValueError:
3346                        sets = ['BabA', 'BabU']
3347                    for param in sets:
3348                        if param not in ['BabA', 'BabU']:
3349                            raise ValueError("Not sure what to do with" + param)
3350                        for hist in histograms:
3351                            hist['Babinet'][param][1] = False
3352                elif key == 'Extinction':
3353                    for h in histograms:
3354                        h['Extinction'][1] = False
3355                elif key == 'HStrain':
3356                    for h in histograms:
3357                        h['HStrain'][1] = [False for p in h['HStrain'][1]]
3358                elif key == 'Mustrain':
3359                    for h in histograms:
3360                        mustrain = h['Mustrain']
3361                        mustrain[2] = [False for p in mustrain[2]]
3362                        mustrain[5] = [False for p in mustrain[4]]
3363                elif key == 'Pref.Ori.':
3364                    for h in histograms:
3365                        h['Pref.Ori.'][2] = False
3366                elif key == 'Show':
3367                    for h in histograms:
3368                        h['Show'] = False
3369                elif key == 'Size':
3370                    for h in histograms:
3371                        size = h['Size']
3372                        size[2] = [False for p in size[2]]
3373                        size[5] = [False for p in size[5]]
3374                elif key == 'Use':
3375                    for h in histograms:
3376                        h['Use'] = False
3377                elif key == 'Scale':
3378                    for h in histograms:
3379                        h['Scale'][1] = False
3380                else:
3381                    print(u'Unknown HAP key: '+key)
3382
3383class G2Image(G2ObjectWrapper):
3384    """Wrapper for an IMG tree entry, containing an image and various metadata.
3385
3386    Example:
3387
3388    >>> gpx = G2sc.G2Project(filename='itest.gpx')
3389    >>> img2 = gpx.add_image(idata,fmthint="TIF")
3390    >>> img2[0].loadControls('stdSettings.imctrl')
3391    >>> img2[0].setCalibrant('Si    SRM640c')
3392    >>> img2[0].loadMasks('stdMasks.immask')
3393    >>> img2[0].Recalibrate()
3394    >>> img2[0].setControl('outAzimuths',3)
3395    >>> pwdrList = img2[0].Integrate()
3396
3397    """
3398    # parameters in that can be accessed via setControl
3399    ControlList = {
3400        'int': ['calibskip', 'pixLimit', 'edgemin', 'outChannels',
3401                    'outAzimuths'],
3402        'float': ['cutoff', 'setdist', 'wavelength', 'Flat Bkg',
3403                      'azmthOff', 'tilt', 'calibdmin', 'rotation',
3404                      'distance', 'DetDepth'],
3405        'bool': ['setRings', 'setDefault', 'centerAzm', 'fullIntegrate',
3406                     'DetDepthRef', 'showLines'],
3407        'str': ['SampleShape', 'binType', 'formatName', 'color',
3408                    'type', ],
3409        'list': ['GonioAngles', 'IOtth', 'LRazimuth', 'Oblique', 'PolaVal',
3410                   'SampleAbs', 'center', 'ellipses', 'linescan',
3411                    'pixelSize', 'range', 'ring', 'rings', 'size', ],
3412        'dict': ['varyList'],
3413        }
3414    '''Defines the items known to exist in the Image Controls tree section
3415    and the item's data types. A few are not included
3416    ('background image', 'dark image', 'Gain map', and 'calibrant') because
3417    these items have special set routines,
3418    where references to entries are checked to make sure their values are
3419    correct.
3420    ''' 
3421    # this may need future attention
3422       
3423    def __init__(self, data, name, proj):
3424        self.data = data
3425        self.name = name
3426        self.proj = proj
3427
3428    def setControl(self,arg,value):
3429        '''Set an Image Controls parameter in the current image.
3430        If the parameter is not found an exception is raised.
3431
3432        :param str arg: the name of a parameter (dict entry) in the
3433          image. The parameter must be found in :data:`ControlList`
3434          or an exception is raised.
3435        :param value: the value to set the parameter. The value is
3436          cast as the appropriate type from :data:`ControlList`.
3437        '''
3438        for typ in self.ControlList:
3439            if arg in self.ControlList[typ]: break
3440        else:
3441            print('Allowed args:\n',[nam for nam,typ in self.findControl('')])
3442            raise Exception('arg {} not defined in G2Image.setControl'
3443                                .format(arg))
3444        try:
3445            if typ == 'int':
3446                self.data['Image Controls'][arg] = int(value)
3447            elif typ == 'float':
3448                self.data['Image Controls'][arg] = float(value)
3449            elif typ == 'bool':
3450                self.data['Image Controls'][arg] = bool(value)
3451            elif typ == 'str':
3452                self.data['Image Controls'][arg] = str(value)
3453            elif typ == 'list':
3454                self.data['Image Controls'][arg] = list(value)
3455            elif typ == 'dict':
3456                self.data['Image Controls'][arg] = dict(value)
3457            else:
3458                raise Exception('Unknown type {} for arg {} in  G2Image.setControl'
3459                                    .format(typ,arg))
3460        except:
3461            raise Exception('Error formatting value {} as type {} for arg {} in  G2Image.setControl'
3462                                    .format(value,typ,arg))
3463
3464    def getControl(self,arg):
3465        '''Return an Image Controls parameter in the current image.
3466        If the parameter is not found an exception is raised.
3467
3468        :param str arg: the name of a parameter (dict entry) in the
3469          image.
3470        :returns: the value as a int, float, list,...
3471        '''
3472        if arg in self.data['Image Controls']:
3473            return self.data['Image Controls'][arg]
3474        print(self.findControl(''))
3475        raise Exception('arg {} not defined in G2Image.getControl'.format(arg))
3476
3477    def findControl(self,arg=''):
3478        '''Finds the Image Controls parameter(s) in the current image
3479        that match the string in arg. Default is '' which returns all
3480        parameters.
3481
3482            Example:
3483
3484            >>> findControl('calib')
3485            [['calibskip', 'int'], ['calibdmin', 'float'], ['calibrant', 'str']]
3486
3487        :param str arg: a string containing part of the name of a
3488          parameter (dict entry) in the image's Image Controls.
3489        :returns: a list of matching entries in form
3490          [['item','type'], ['item','type'],...] where each 'item' string
3491          contains the sting in arg.
3492        '''
3493        matchList = []
3494        for typ in self.ControlList:
3495            for item in self.ControlList[typ]:
3496                if arg in item:
3497                    matchList.append([item,typ])
3498        return matchList
3499
3500    def setCalibrant(self,calib):
3501        '''Set a calibrant for the current image
3502
3503        :param str calib: specifies a calibrant name which must be one of
3504          the entries in file ImageCalibrants.py. This is validated and
3505          an error provides a list of valid choices.
3506        '''
3507        import ImageCalibrants as calFile
3508        if calib in calFile.Calibrants.keys():
3509            self.data['Image Controls']['calibrant'] = calib
3510            return
3511        print('Calibrant {} is not valid. Valid calibrants'.format(calib))
3512        for i in calFile.Calibrants.keys():
3513            if i: print('\t"{}"'.format(i))
3514       
3515    def setControlFile(self,typ,imageRef,mult=None):
3516        '''Set a image to be used as a background/dark/gain map image
3517
3518        :param str typ: specifies image type, which must be one of:
3519           'background image', 'dark image', 'gain map'; N.B. only the first
3520           four characters must be specified and case is ignored.
3521        :param imageRef: A reference to the desired image. Either the Image
3522          tree name (str), the image's index (int) or
3523          a image object (:class:`G2Image`)
3524        :param float mult: a multiplier to be applied to the image (not used
3525          for 'Gain map'; required for 'background image', 'dark image'
3526        '''
3527        if 'back' in typ.lower():
3528            key = 'background image'
3529            mult = float(mult)
3530        elif 'dark' in typ.lower():
3531            key = 'dark image'
3532            mult = float(mult)
3533        elif 'gain' in typ.lower():
3534            #key = 'Gain map'
3535            if mult is not None:
3536                print('Ignoring multiplier for Gain map')
3537            mult = None
3538        else:
3539            raise Exception("Invalid typ {} for setControlFile".format(typ))
3540        imgNam = self.proj.image(imageRef).name
3541        if mult is None:
3542            self.data['Image Controls']['Gain map'] = imgNam
3543        else:
3544            self.data['Image Controls'][key] = [imgNam,mult]
3545
3546    def loadControls(self,filename):
3547        '''load controls from a .imctrl file
3548
3549        :param str filename: specifies a file to be read, which should end
3550          with .imctrl
3551        '''
3552        File = open(filename,'r')
3553        Slines = File.readlines()
3554        File.close()
3555        G2fil.LoadControls(Slines,self.data['Image Controls'])
3556        print('file {} read into {}'.format(filename,self.name))
3557
3558    def saveControls(self,filename):
3559        '''write current controls values to a .imctrl file
3560
3561        :param str filename: specifies a file to write, which should end
3562          with .imctrl
3563        '''
3564        G2fil.WriteControls(filename,self.data['Image Controls'])
3565        print('file {} written from {}'.format(filename,self.name))
3566
3567    def loadMasks(self,filename,ignoreThreshold=False):
3568        '''load masks from a .immask file
3569
3570        :param str filename: specifies a file to be read, which should end
3571          with .immask
3572        :param bool ignoreThreshold: If True, masks are loaded with
3573          threshold masks. Default is False which means any Thresholds
3574          in the file are ignored.
3575        '''
3576        G2fil.readMasks(filename,self.data['Masks'],ignoreThreshold)
3577        print('file {} read into {}'.format(filename,self.name))
3578       
3579    def getVary(self,*args):
3580        '''Return the refinement flag(s) for Image Controls parameter(s)
3581        in the current image.
3582        If the parameter is not found, an exception is raised.
3583
3584        :param str arg: the name of a refinement parameter in the
3585          varyList for the image. The name should be one of
3586          'dep', 'det-X', 'det-Y', 'dist', 'phi', 'tilt', or 'wave'
3587        :param str arg1: the name of a parameter (dict entry) as before,
3588          optional
3589
3590
3591        :returns: a list of bool value(s)
3592        '''
3593        res = []
3594        for arg in args:
3595            if arg in self.data['Image Controls']['varyList']:
3596                res.append(self.data['Image Controls']['varyList'][arg])
3597            else:
3598                raise Exception('arg {} not defined in G2Image.getVary'.format(arg))
3599        return res
3600   
3601    def setVary(self,arg,value):
3602        '''Set a refinement flag for Image Controls parameter in the
3603        current image.
3604        If the parameter is not found an exception is raised.
3605
3606        :param str arg: the name of a refinement parameter in the
3607          varyList for the image. The name should be one of
3608          'dep', 'det-X', 'det-Y', 'dist', 'phi', 'tilt', or 'wave'
3609        :param str arg: the name of a parameter (dict entry) in the
3610          image. The parameter must be found in :data:`ControlList`
3611          or an exception is raised.
3612        :param value: the value to set the parameter. The value is
3613          cast as the appropriate type from :data:`ControlList`.
3614        '''
3615        if arg in self.data['Image Controls']['varyList']:
3616            self.data['Image Controls']['varyList'][arg] = bool(value)
3617        else:
3618            raise Exception('arg {} not defined in G2Image.setVary'.format(arg))
3619
3620    def Recalibrate(self):
3621        '''Invokes a recalibration fit (same as Image Controls/Calibration/Recalibrate
3622        menu command). Note that for this to work properly, the calibration
3623        coefficients (center, wavelength, ditance & tilts) must be fairly close.
3624        This may produce a better result if run more than once.
3625        '''
3626        LoadG2fil()
3627        ImageZ = GetCorrImage(Readers['Image'],self.proj,self)
3628        G2img.ImageRecalibrate(None,ImageZ,self.data['Image Controls'],self.data['Masks'])
3629
3630    def Integrate(self,name=None):
3631        '''Invokes an image integration (same as Image Controls/Integration/Integrate
3632        menu command). All parameters will have previously been set with Image Controls
3633        so no input is needed here. Note that if integration is performed on an
3634        image more than once, histogram entries may be overwritten. Use the name
3635        parameter to prevent this if desired.
3636
3637        :param str name: base name for created histogram(s). If None (default),
3638          the histogram name is taken from the image name.
3639        :returns: a list of created histogram (:class:`G2PwdrData`) objects.
3640        '''
3641        blkSize = 1024   #this seems to be optimal; will break in polymask if >1024
3642        ImageZ = GetCorrImage(Readers['Image'],self.proj,self)
3643        # do integration
3644        ints,azms,Xvals,cancel = G2img.ImageIntegrate(ImageZ,self.data['Image Controls'],self.data['Masks'],blkSize=blkSize)
3645        # code from here on based on G2IO.SaveIntegration, but places results in the current
3646        # project rather than tree
3647        X = Xvals[:-1]
3648        N = len(X)
3649
3650        data = self.data['Image Controls']
3651        Comments = self.data['Comments']
3652        # make name from image, unless overridden
3653        if name:
3654            if not name.startswith(data['type']+' '):
3655                name = data['type']+' '+name
3656        else:
3657            name = self.name.replace('IMG ',data['type']+' ')
3658        if 'PWDR' in name:
3659            if 'target' in data:
3660                names = ['Type','Lam1','Lam2','I(L2)/I(L1)','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
3661                codes = [0 for i in range(14)]
3662            else:
3663                names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
3664                codes = [0 for i in range(12)]
3665        elif 'SASD' in name:
3666            names = ['Type','Lam','Zero','Azimuth'] 
3667            codes = [0 for i in range(4)]
3668            X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
3669        Xminmax = [X[0],X[-1]]
3670        Azms = []
3671        dazm = 0.
3672        if data['fullIntegrate'] and data['outAzimuths'] == 1:
3673            Azms = [45.0,]                              #a poor man's average?
3674        else:
3675            for i,azm in enumerate(azms[:-1]):
3676                if azm > 360. and azms[i+1] > 360.:
3677                    Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
3678                else:   
3679                    Azms.append(G2img.meanAzm(azm,azms[i+1]))
3680            dazm = np.min(np.abs(np.diff(azms)))/2.
3681        # pull out integration results and make histograms for each
3682        IntgOutList = []
3683        for i,azm in enumerate(azms[:-1]):
3684            Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
3685            # MT dict to contain histogram
3686            HistDict = {}
3687            histItems = [Aname]
3688            Sample = G2obj.SetDefaultSample()       #set as Debye-Scherrer
3689            Sample['Gonio. radius'] = data['distance']
3690            Sample['Omega'] = data['GonioAngles'][0]
3691            Sample['Chi'] = data['GonioAngles'][1]
3692            Sample['Phi'] = data['GonioAngles'][2]
3693            Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
3694            polariz = 0.99    #set default polarization for synchrotron radiation!
3695            for item in Comments:
3696                if 'polariz' in item:
3697                    try:
3698                        polariz = float(item.split('=')[1])
3699                    except:
3700                        polariz = 0.99
3701                for key in ('Temperature','Pressure','Time','FreePrm1','FreePrm2','FreePrm3','Omega',
3702                    'Chi','Phi'):
3703                    if key.lower() in item.lower():
3704                        try:
3705                            Sample[key] = float(item.split('=')[1])
3706                        except:
3707                            pass
3708                if 'label_prm' in item.lower():
3709                    for num in ('1','2','3'):
3710                        if 'label_prm'+num in item.lower():
3711                            Controls['FreePrm'+num] = item.split('=')[1].strip()
3712            if 'PWDR' in Aname:
3713                if 'target' in data:    #from lab x-ray 2D imaging data
3714                    wave1,wave2 = waves[data['target']]
3715                    parms = ['PXC',wave1,wave2,0.5,0.0,polariz,290.,-40.,30.,6.,-14.,0.0,0.0001,Azms[i]]
3716                else:
3717                    parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0,0.0001,Azms[i]]
3718            elif 'SASD' in Aname:
3719                Sample['Trans'] = data['SampleAbs'][0]
3720                parms = ['LXC',data['wavelength'],0.0,Azms[i]]
3721            Y = ints[i]
3722            Ymin = np.min(Y)
3723            Ymax = np.max(Y)
3724            W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
3725            section = 'Comments'
3726            histItems += [section]
3727            HistDict[section] = Comments
3728            section = 'Limits'
3729            histItems += [section]
3730            HistDict[section] = copy.deepcopy([tuple(Xminmax),Xminmax])
3731            if 'PWDR' in Aname:
3732                section = 'Background'
3733                histItems += [section]
3734                HistDict[section] = [['chebyschev',1,3,1.0,0.0,0.0],
3735                    {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}]
3736            inst = [dict(zip(names,zip(parms,parms,codes))),{}]
3737            for item in inst[0]:
3738                inst[0][item] = list(inst[0][item])
3739            section = 'Instrument Parameters'
3740            histItems += [section]
3741            HistDict[section] = inst
3742            if 'PWDR' in Aname:
3743                section = 'Sample Parameters'
3744                histItems += [section]
3745                HistDict[section] = Sample
3746                section = 'Peak List'
3747                histItems += [section]
3748                HistDict[section] = {'sigDict':{},'peaks':[]}
3749                section = 'Index Peak List'
3750                histItems += [section]
3751                HistDict[section] = [[],[]]
3752                section = 'Unit Cells List'
3753                histItems += [section]
3754                HistDict[section] = []
3755                section = 'Reflection Lists'
3756                histItems += [section]
3757                HistDict[section] = {}
3758            elif 'SASD' in Aname:             
3759                section = 'Substances'
3760                histItems += [section]
3761                HistDict[section] = G2pdG.SetDefaultSubstances()  # this needs to be moved
3762                section = 'Sample Parameters'
3763                histItems += [section]
3764                HistDict[section] = Sample
3765                section = 'Models'
3766                histItems += [section]
3767                HistDict[section] = G2pdG.SetDefaultSASDModel() # this needs to be moved
3768            valuesdict = {
3769                'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxsize),'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
3770                'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,'Yminmax':[Ymin,Ymax]}
3771            # if Aname is already in the project replace it
3772            for j in self.proj.names:
3773                if j[0] == Aname: 
3774                    print('Replacing "{}" in project'.format(Aname))
3775                    break
3776            else:
3777                print('Adding "{}" to project'.format(Aname))
3778                self.proj.names.append([Aname]+
3779                        [u'Comments',u'Limits',u'Background',u'Instrument Parameters',
3780                         u'Sample Parameters', u'Peak List', u'Index Peak List',
3781                         u'Unit Cells List', u'Reflection Lists'])
3782            HistDict['data'] = [valuesdict,
3783                    [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]]
3784            self.proj.data[Aname] = HistDict
3785            IntgOutList.append(self.proj.histogram(Aname))
3786        return IntgOutList
3787
3788##########################
3789# Command Line Interface #
3790##########################
3791# Each of these takes an argparse.Namespace object as their argument,
3792# representing the parsed command-line arguments for the relevant subcommand.
3793# The argument specification for each is in the subcommands dictionary (see
3794# below)
3795
3796commandhelp={}
3797commandhelp["create"] = "creates a GSAS-II project, optionally adding histograms and/or phases"
3798def create(args):
3799    """Implements the create command-line subcommand. This creates a GSAS-II project, optionally adding histograms and/or phases::
3800
3801  usage: GSASIIscriptable.py create [-h] [-d HISTOGRAMS [HISTOGRAMS ...]]
3802                                  [-i IPARAMS [IPARAMS ...]]
3803                                  [-p PHASES [PHASES ...]]
3804                                  filename
3805                                 
3806positional arguments::
3807
3808  filename              the project file to create. should end in .gpx
3809
3810optional arguments::
3811
3812  -h, --help            show this help message and exit
3813  -d HISTOGRAMS [HISTOGRAMS ...], --histograms HISTOGRAMS [HISTOGRAMS ...]
3814                        list of datafiles to add as histograms
3815  -i IPARAMS [IPARAMS ...], --iparams IPARAMS [IPARAMS ...]
3816                        instrument parameter file, must be one for every
3817                        histogram
3818  -p PHASES [PHASES ...], --phases PHASES [PHASES ...]
3819                        list of phases to add. phases are automatically
3820                        associated with all histograms given.
3821
3822    """
3823    proj = G2Project(gpxname=args.filename)
3824
3825    hist_objs = []
3826    if args.histograms:
3827        for h,i in zip(args.histograms,args.iparams):
3828            print("Adding histogram from",h,"with instparm ",i)
3829            hist_objs.append(proj.add_powder_histogram(h, i))
3830
3831    if args.phases: 
3832        for p in args.phases:
3833            print("Adding phase from",p)
3834            proj.add_phase(p, histograms=hist_objs)
3835        print('Linking phase(s) to histogram(s):')
3836        for h in hist_objs:
3837            print ('   '+h.name)
3838
3839    proj.save()
3840
3841commandhelp["add"] = "adds histograms and/or phases to GSAS-II project"
3842def add(args):
3843    """Implements the add command-line subcommand. This adds histograms and/or phases to GSAS-II project::
3844
3845  usage: GSASIIscriptable.py add [-h] [-d HISTOGRAMS [HISTOGRAMS ...]]
3846                               [-i IPARAMS [IPARAMS ...]]
3847                               [-hf HISTOGRAMFORMAT] [-p PHASES [PHASES ...]]
3848                               [-pf PHASEFORMAT] [-l HISTLIST [HISTLIST ...]]
3849                               filename
3850
3851
3852positional arguments::
3853
3854  filename              the project file to open. Should end in .gpx
3855
3856optional arguments::
3857
3858  -h, --help            show this help message and exit
3859  -d HISTOGRAMS [HISTOGRAMS ...], --histograms HISTOGRAMS [HISTOGRAMS ...]
3860                        list of datafiles to add as histograms
3861  -i IPARAMS [IPARAMS ...], --iparams IPARAMS [IPARAMS ...]
3862                        instrument parameter file, must be one for every
3863                        histogram
3864  -hf HISTOGRAMFORMAT, --histogramformat HISTOGRAMFORMAT
3865                        format hint for histogram import. Applies to all
3866                        histograms
3867  -p PHASES [PHASES ...], --phases PHASES [PHASES ...]
3868                        list of phases to add. phases are automatically
3869                        associated with all histograms given.
3870  -pf PHASEFORMAT, --phaseformat PHASEFORMAT
3871                        format hint for phase import. Applies to all phases.
3872                        Example: -pf CIF
3873  -l HISTLIST [HISTLIST ...], --histlist HISTLIST [HISTLIST ...]
3874                        list of histgram indices to associate with added
3875                        phases. If not specified, phases are associated with
3876                        all previously loaded histograms. Example: -l 2 3 4
3877   
3878    """
3879    proj = G2Project(args.filename)
3880
3881    if args.histograms:
3882        for h,i in zip(args.histograms,args.iparams):
3883            print("Adding histogram from",h,"with instparm ",i)
3884            proj.add_powder_histogram(h, i, fmthint=args.histogramformat)
3885
3886    if args.phases: 
3887        if not args.histlist:
3888            histlist = proj.histograms()
3889        else:
3890            histlist = [proj.histogram(i) for i in args.histlist]
3891
3892        for p in args.phases:
3893            print("Adding phase from",p)
3894            proj.add_phase(p, histograms=histlist, fmthint=args.phaseformat)
3895           
3896        if not args.histlist:
3897            print('Linking phase(s) to all histogram(s)')
3898        else:
3899            print('Linking phase(s) to histogram(s):')
3900            for h in histlist:
3901                print ('   '+h.name)
3902
3903    proj.save()
3904
3905
3906commandhelp["dump"] = "Shows the contents of a GSAS-II project"
3907def dump(args):
3908    """Implements the dump command-line subcommand, which shows the contents of a GSAS-II project::
3909
3910       usage: GSASIIscriptable.py dump [-h] [-d] [-p] [-r] files [files ...]
3911
3912positional arguments::
3913
3914  files
3915
3916optional arguments::
3917
3918  -h, --help        show this help message and exit
3919  -d, --histograms  list histograms in files, overrides --raw
3920  -p, --phases      list phases in files, overrides --raw
3921  -r, --raw         dump raw file contents, default
3922 
3923    """
3924    if not args.histograms and not args.phases:
3925        args.raw = True
3926    if args.raw:
3927        import IPython.lib.pretty as pretty
3928
3929    for fname in args.files:
3930        if args.raw:
3931            proj, nameList = LoadDictFromProjFile(fname)
3932            print("file:", fname)
3933            print("names:", nameList)
3934            for key, val in proj.items():
3935                print(key, ":")
3936                pretty.pprint(val)
3937        else:
3938            proj = G2Project(fname)
3939            if args.histograms:
3940                hists = proj.histograms()
3941                for h in hists:
3942                    print(fname, "hist", h.id, h.name)
3943            if args.phases:
3944                phase_list = proj.phases()
3945                for p in phase_list:
3946                    print(fname, "phase", p.id, p.name)
3947
3948
3949commandhelp["browse"] = "Load a GSAS-II project and then open a IPython shell to browse it"
3950def IPyBrowse(args):
3951    """Load a .gpx file and then open a IPython shell to browse it::
3952
3953  usage: GSASIIscriptable.py browse [-h] files [files ...]
3954
3955positional arguments::
3956
3957  files       list of files to browse
3958
3959optional arguments::
3960
3961  -h, --help  show this help message and exit
3962
3963    """
3964    for fname in args.files:
3965        proj, nameList = LoadDictFromProjFile(fname)
3966        msg = "\nfname {} loaded into proj (dict) with names in nameList".format(fname)
3967        GSASIIpath.IPyBreak_base(msg)
3968        break
3969
3970
3971commandhelp["refine"] = '''
3972Conducts refinements on GSAS-II projects according to a list of refinement
3973steps in a JSON dict
3974'''
3975def refine(args):
3976    """Implements the refine command-line subcommand:
3977    conducts refinements on GSAS-II projects according to a JSON refinement dict::
3978
3979        usage: GSASIIscriptable.py refine [-h] gpxfile [refinements]
3980
3981positional arguments::
3982
3983  gpxfile      the project file to refine
3984  refinements  json file of refinements to apply. if not present refines file
3985               as-is
3986
3987optional arguments::
3988
3989  -h, --help   show this help message and exit
3990 
3991    """
3992    proj = G2Project(args.gpxfile)
3993    if args.refinements is None:
3994        proj.refine()
3995    else:
3996        import json
3997        with open(args.refinements) as refs:
3998            refs = json.load(refs)
3999        if type(refs) is not dict:
4000            raise G2ScriptException("Error: JSON object must be a dict.")
4001        if "code" in refs:
4002            print("executing code:\n|  ",'\n|  '.join(refs['code']))
4003            exec('\n'.join(refs['code']))
4004        proj.do_refinements(refs['refinements'])
4005
4006
4007commandhelp["seqrefine"] = "Not implemented. Placeholder for eventual sequential refinement implementation"
4008def seqrefine(args):
4009    """Future implementation for the seqrefine command-line subcommand """
4010    raise NotImplementedError("seqrefine is not yet implemented")
4011
4012
4013commandhelp["export"] = "Export phase as CIF"
4014def export(args):
4015    """Implements the export command-line subcommand: Exports phase as CIF::
4016
4017      usage: GSASIIscriptable.py export [-h] gpxfile phase exportfile
4018
4019positional arguments::
4020
4021  gpxfile     the project file from which to export
4022  phase       identifier of phase to export
4023  exportfile  the .cif file to export to
4024
4025optional arguments::
4026
4027  -h, --help  show this help message and exit
4028
4029    """
4030    proj = G2Project(args.gpxfile)
4031    phase = proj.phase(args.phase)
4032    phase.export_CIF(args.exportfile)
4033
4034
4035def _args_kwargs(*args, **kwargs):
4036    return args, kwargs
4037
4038# A dictionary of the name of each subcommand, and a tuple
4039# of its associated function and a list of its arguments
4040# The arguments are passed directly to the add_argument() method
4041# of an argparse.ArgumentParser
4042
4043subcommands = {"create":
4044               (create, [_args_kwargs('filename',
4045                                      help='the project file to create. should end in .gpx'),
4046
4047                         _args_kwargs('-d', '--histograms',
4048                                      nargs='+',
4049                                      help='list of datafiles to add as histograms'),
4050                                     
4051                         _args_kwargs('-i', '--iparams',
4052                                      nargs='+',
4053                                      help='instrument parameter file, must be one'
4054                                           ' for every histogram'
4055                                      ),
4056
4057                         _args_kwargs('-p', '--phases',
4058                                      nargs='+',
4059                                      help='list of phases to add. phases are '
4060                                           'automatically associated with all '
4061                                           'histograms given.')]),
4062               "add": (add, [_args_kwargs('filename',
4063                                      help='the project file to open. Should end in .gpx'),
4064
4065                         _args_kwargs('-d', '--histograms',
4066                                      nargs='+',
4067                                      help='list of datafiles to add as histograms'),
4068                                     
4069                         _args_kwargs('-i', '--iparams',
4070                                      nargs='+',
4071                                      help='instrument parameter file, must be one'
4072                                           ' for every histogram'
4073                                      ),
4074                                     
4075                         _args_kwargs('-hf', '--histogramformat',
4076                                      help='format hint for histogram import. Applies to all'
4077                                           ' histograms'
4078                                      ),
4079
4080                         _args_kwargs('-p', '--phases',
4081                                      nargs='+',
4082                                      help='list of phases to add. phases are '
4083                                           'automatically associated with all '
4084                                           'histograms given.'),
4085
4086                         _args_kwargs('-pf', '--phaseformat',
4087                                      help='format hint for phase import. Applies to all'
4088                                           ' phases. Example: -pf CIF'
4089                                      ),
4090                                     
4091                         _args_kwargs('-l', '--histlist',
4092                                      nargs='+',
4093                                      help='list of histgram indices to associate with added'
4094                                           ' phases. If not specified, phases are'
4095                                           ' associated with all previously loaded'
4096                                           ' histograms. Example: -l 2 3 4')]),
4097                                           
4098               "dump": (dump, [_args_kwargs('-d', '--histograms',
4099                                     action='store_true',
4100                                     help='list histograms in files, overrides --raw'),
4101
4102                               _args_kwargs('-p', '--phases',
4103                                            action='store_true',
4104                                            help='list phases in files, overrides --raw'),
4105
4106                               _args_kwargs('-r', '--raw',
4107                                      action='store_true', help='dump raw file contents, default'),
4108
4109                               _args_kwargs('files', nargs='+')]),
4110
4111               "refine":
4112               (refine, [_args_kwargs('gpxfile', help='the project file to refine'),
4113                         _args_kwargs('refinements',
4114                                      help='JSON file of refinements to apply. if not present'
4115                                           ' refines file as-is',
4116                                      default=None,
4117                                      nargs='?')]),
4118
4119               "seqrefine": (seqrefine, []),
4120               "export": (export, [_args_kwargs('gpxfile',
4121                                                help='the project file from which to export'),
4122                                   _args_kwargs('phase', help='identifier of phase to export'),
4123                                   _args_kwargs('exportfile', help='the .cif file to export to')]),
4124               "browse": (IPyBrowse, [_args_kwargs('files', nargs='+',
4125                                                   help='list of files to browse')])}
4126
4127
4128def main():
4129    '''The command-line interface for calling GSASIIscriptable as a shell command,
4130    where it is expected to be called as::
4131
4132       python GSASIIscriptable.py <subcommand> <file.gpx> <options>
4133
4134    The following subcommands are defined:
4135
4136        * create, see :func:`create`
4137        * add, see :func:`add`
4138        * dump, see :func:`dump`
4139        * refine, see :func:`refine`
4140        * seqrefine, see :func:`seqrefine`
4141        * export, :func:`export`
4142        * browse, see :func:`IPyBrowse`
4143
4144    .. seealso::
4145        :func:`create`
4146        :func:`add`
4147        :func:`dump`
4148        :func:`refine`
4149        :func:`seqrefine`
4150        :func:`export`
4151        :func:`IPyBrowse`
4152    '''
4153    parser = argparse.ArgumentParser(description=
4154        "Use of "+os.path.split(__file__)[1]+" Allows GSAS-II actions from command line."
4155        )
4156    subs = parser.add_subparsers()
4157
4158    # Create all of the specified subparsers
4159    for name, (func, args) in subcommands.items():
4160        new_parser = subs.add_parser(name,help=commandhelp.get(name),
4161                                     description='Command "'+name+'" '+commandhelp.get(name))
4162        for listargs, kwds in args:
4163            new_parser.add_argument(*listargs, **kwds)
4164        new_parser.set_defaults(func=func)
4165
4166    # Parse and trigger subcommand
4167    result = parser.parse_args()
4168    result.func(result)
4169
4170if __name__ == '__main__':
4171    #fname='/tmp/corundum-template.gpx'
4172    #prj = G2Project(fname)
4173    main()
Note: See TracBrowser for help on using the repository browser.