# -*- coding: utf-8 -*-
########### SVN repository information ###################
# $Date: 2019-06-14 14:45:45 -0400 (Fri, 14 Jun 2019) $
# $Author: toby $
# $Revision: 4026 $
# $URL: https://subversion.xray.aps.anl.gov/pyGSAS/trunk/GSASIIfiles.py $
# $Id: GSASIIfiles.py 4026 2019-06-14 18:45:45Z toby $
########### SVN repository information ###################
'''
# Third-party code. No Schrodinger Copyright.
*GSASIIfiles: data (non-GUI) I/O routines*
==========================================
Module with miscellaneous routines for input and output from files.
This module should not contain any references to wxPython so that it
can be imported for scriptable use or potentially on clients where
wx is not installed.
Future refactoring: This module and GSASIIIO.py needs some work to
move non-wx routines here. It may will likely make sense to rename the module(s)
at that point.
'''
# flake8: noqa
import glob
import imp
import inspect
import os
import platform
import sys
import numpy as np
# N.B. This is duplicated in G2IO
[docs]def sfloat(S):
    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
    if S.strip():
        try:
            return float(S)
        except ValueError:
            pass
    return 0.0 
G2printLevel = 'all'
'''This defines the level of output from calls to :func:`G2Print`,
which should  be used in place of print() within this module.
Settings for this are 'all', 'warn', 'error' or 'none'. Also see:
:func:`G2Print` and :func:`G2SetPrintLevel`.
'''
[docs]def G2SetPrintLevel(level):
    '''Set the level of output from calls to :func:`G2Print`, which should
    be used in place of print() within GSASII. Settings for the mode are
    'all', 'warn', 'error' or 'none'
    :param str level: a string used to set the print level, which may be
      'all', 'warn', 'error' or 'none'.
      Note that capitalization and extra letters in level are ignored, so
      'Warn', 'warnings', etc. will all set the mode to 'warn'
    '''
    global G2printLevel
    for mode in 'all', 'warn', 'error', 'none':
        if mode in level.lower():
            G2printLevel = mode
            return
    else:
        G2Print('G2SetPrintLevel Error: level={} cannot be interpreted.',
                'Use all, warn, error or none.') 
[docs]def G2Print(*args, **kwargs):
    '''Print with filtering based level of output (see :func:`G2SetPrintLevel`).
    Use G2Print() as replacement for print().
    :param str mode: if specified, this should contain the mode for printing
      ('error', 'warn' or anything else). If not specified, the first argument
      of the print command (args[0]) should contain the string 'error' for
      error messages and 'warn' for warning messages
      (capitalization and additional letters ignored.)
    '''
    if G2printLevel == 'none':
        return
    if kwargs.get('mode') is None:
        testStr = str(args[0]).lower()
    else:
        testStr = kwargs['mode'][:].lower()
        del kwargs['mode']
    level = 2
    for i, mode in enumerate(('error', 'warn')):
        if mode in testStr:
            level = i
            break
    if G2printLevel == 'error' and level > 0:
        return
    if G2printLevel == 'warn' and level > 1:
        return
    print(*args, **kwargs) 
[docs]def get_python_versions(packagelist):
    versions = [['Python', sys.version.split()[0]]]
    for pack in packagelist:
        try:
            versions.append([pack.__name__, pack.__version__])
        except:
            pass
    versions.append([
        'Platform', sys.platform + ' ' + platform.architecture()[0] + ' ' +
        platform.machine()
    ])
    return versions 
[docs]def makeInstDict(names, data, codes):
    inst = dict(zip(names, zip(data, data, codes)))
    for item in inst:
        inst[item] = list(inst[item])
    return inst 
[docs]def SetPowderInstParms(Iparm, rd):
    '''extracts values from instrument parameters in rd.instdict
    or in array Iparm.
    Create and return the contents of the instrument parameter tree entry.
    '''
    Irads = {
        0: ' ',
        1: 'CrKa',
        2: 'FeKa',
        3: 'CuKa',
        4: 'MoKa',
        5: 'AgKa',
        6: 'TiKa',
        7: 'CoKa'
    }
    DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
    # override inst values with values read from data file
    Bank = rd.powderentry[2]  #should be used in multibank iparm files
    if rd.instdict.get('type'):
        DataType = rd.instdict.get('type')
    data = [
        DataType,
    ]
    instname = Iparm.get('INS  1INAME ')
    irad = int(Iparm.get('INS  1 IRAD ', '0'))
    if instname:
        rd.Sample['InstrName'] = instname.strip()
    if 'C' in DataType:
        wave1 = None
        wave2 = 0.0
        if rd.instdict.get('wave'):
            wl = rd.instdict.get('wave')
            wave1 = wl[0]
            if len(wl) > 1:
                wave2 = wl[1]
        s = Iparm['INS  1 ICONS']
        if not wave1:
            wave1 = sfloat(s[:10])
            wave2 = sfloat(s[10:20])
        v = (wave1, wave2, sfloat(s[20:30]) / 100., sfloat(s[55:65]),
             sfloat(s[40:50]))  #get lam1, lam2, zero, pola & ratio
        if not v[1]:
            names = [
                'Type', 'Lam', 'Zero', 'Polariz.', 'U', 'V', 'W', 'X', 'Y', 'Z',
                'SH/L', 'Azimuth'
            ]
            v = (v[0], v[2], v[4])
            codes = [0, 0, 0, 0, 0]
            rd.Sample.update({
                'Type': 'Debye-Scherrer',
                'Absorption': [0., False],
                'DisplaceX': [0., False],
                'DisplaceY': [0., False]
            })
        else:
            names = [
                'Type', 'Lam1', 'Lam2', 'Zero', 'I(L2)/I(L1)', 'Polariz.', 'U',
                'V', 'W', 'X', 'Y', 'Z', 'SH/L', 'Azimuth'
            ]
            codes = [0, 0, 0, 0, 0, 0, 0]
            rd.Sample.update({
                'Type': 'Bragg-Brentano',
                'Shift': [0., False],
                'Transparency': [0., False],
                'SurfRoughA': [0., False],
                'SurfRoughB': [0., False]
            })
        data.extend(v)
        if 'INS  1PRCF  ' in Iparm:
            v1 = Iparm['INS  1PRCF  '].split()
            v = Iparm['INS  1PRCF 1'].split()
            data.extend([float(v[0]), float(v[1]),
                         float(v[2])])  #get GU, GV & GW - always here
            azm = float(Iparm.get('INS  1DETAZM', '0.0'))
            v = Iparm['INS  1PRCF 2'].split()
            if v1[0] == 3:
                data.extend([
                    float(v[0]),
                    float(v[1]), 0.0,
                    float(v[2]) + float(v[3], azm)
                ])  #get LX, LY, Z, S+H/L & azimuth
            else:
                data.extend([0.0, 0.0, 0.0, 0.002,
                             azm])  #OK defaults if fxn #3 not 1st in iprm file
        else:
            v1 = Iparm['INS  1PRCF1 '].split()
            v = Iparm['INS  1PRCF11'].split()
            data.extend([float(v[0]), float(v[1]),
                         float(v[2])])  #get GU, GV & GW - always here
            azm = float(Iparm.get('INS  1DETAZM', '0.0'))
            v = Iparm['INS  1PRCF12'].split()
            if v1[0] == 3:
                data.extend([
                    float(v[0]),
                    float(v[1]), 0.0,
                    float(v[2]) + float(v[3], azm)
                ])  #get LX, LY, Z, S+H/L & azimuth
            else:
                data.extend([0.0, 0.0, 0.0, 0.002,
                             azm])  #OK defaults if fxn #3 not 1st in iprm file
        codes.extend([0, 0, 0, 0, 0, 0, 0])
        Iparm1 = makeInstDict(names, data, codes)
        Iparm1['Source'] = [Irads[irad], Irads[irad]]
        Iparm1['Bank'] = [Bank, Bank, 0]
        return [Iparm1, {}]
    elif 'T' in DataType:
        names = [
            'Type',
            'fltPath',
            '2-theta',
            'difC',
            'difA',
            'difB',
            'Zero',
            'alpha',
            'beta-0',
            'beta-1',
            'beta-q',
            'sig-0',
            'sig-1',
            'sig-2',
            'sig-q',
            'X',
            'Y',
            'Z',
            'Azimuth',
        ]
        codes = [
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
        ]
        azm = 0.
        if 'INS  1DETAZM' in Iparm:
            azm = float(Iparm['INS  1DETAZM'])
        rd.Sample['Azimuth'] = azm
        fltPath0 = 20.  #arbitrary
        if 'INS   FPATH1' in Iparm:
            s = Iparm['INS   FPATH1'].split()
            fltPath0 = sfloat(s[0])
        if 'INS  1BNKPAR' not in Iparm:  #bank missing from Iparm file
            return []
        s = Iparm['INS  1BNKPAR'].split()
        fltPath1 = sfloat(s[0])
        data.extend([
            fltPath0 + fltPath1,
        ])  #Flight path source-sample-detector
        data.extend([
            sfloat(s[1]),
        ])  #2-theta for bank
        s = Iparm['INS  1 ICONS'].split()
        data.extend([sfloat(s[0]),
                     sfloat(s[1]), 0.0,
                     sfloat(s[2])])  #difC,difA,difB,Zero
        if 'INS  1PRCF  ' in Iparm:
            s = Iparm['INS  1PRCF  '].split()
            pfType = int(s[0])
            s = Iparm['INS  1PRCF 1'].split()
            if abs(pfType) == 1:
                data.extend([sfloat(s[1]),
                             sfloat(s[2]),
                             sfloat(s[3])])  #alpha, beta-0, beta-1
                s = Iparm['INS  1PRCF 2'].split()
                data.extend([
                    0.0, 0.0,
                    sfloat(s[1]),
                    sfloat(s[2]), 0.0, 0.0, 0.0, 0.0, azm
                ])  #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
            elif abs(pfType) in [3, 4, 5]:
                data.extend([sfloat(s[0]),
                             sfloat(s[1]),
                             sfloat(s[2])])  #alpha, beta-0, beta-1
                if abs(pfType) == 4:
                    data.extend(
                        [0.0, 0.0,
                         sfloat(s[3]), 0.0, 0.0, 0.0, 0.0, 0.0,
                         azm])  #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
                else:
                    s = Iparm['INS  1PRCF 2'].split()
                    data.extend([
                        0.0, 0.0,
                        sfloat(s[0]),
                        sfloat(s[1]), 0.0, 0.0, 0.0, 0.0, azm
                    ])  #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
            elif abs(pfType) == 2:
                data.extend([sfloat(s[1]), 0.0,
                             1. / sfloat(s[3])])  #alpha, beta-0, beta-1
                data.extend(
                    [0.0, 0.0,
                     sfloat(s[1]), 0.0, 0.0, 0.0, 0.0, 0.0,
                     azm])  #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
        else:
            s = Iparm['INS  1PRCF1 '].split()
            pfType = int(s[0])
            s = Iparm['INS  1PRCF11'].split()
            if abs(pfType) == 1:
                data.extend([sfloat(s[1]),
                             sfloat(s[2]),
                             sfloat(s[3])])  #alpha, beta-0, beta-1
                s = Iparm['INS  1PRCF12'].split()
                data.extend([
                    0.0, 0.0,
                    sfloat(s[1]),
                    sfloat(s[2]), 0.0, 0.0, 0.0, 0.0, 0.0, azm
                ])  #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
            elif abs(pfType) in [3, 4, 5]:
                data.extend([sfloat(s[0]),
                             sfloat(s[1]),
                             sfloat(s[2])])  #alpha, beta-0, beta-1
                if abs(pfType) == 4:
                    data.extend(
                        [0.0, 0.0,
                         sfloat(s[3]), 0.0, 0.0, 0.0, 0.0, 0.0,
                         azm])  #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
                else:
                    s = Iparm['INS  1PRCF12'].split()
                    data.extend([
                        0.0, 0.0,
                        sfloat(s[0]),
                        sfloat(s[1]), 0.0, 0.0, 0.0, 0.0, azm
                    ])  #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y, Z
        Inst1 = makeInstDict(names, data, codes)
        Inst1['Bank'] = [Bank, Bank, 0]
        Inst2 = {}
        if pfType < 0:
            Ipab = 'INS  1PAB' + str(-pfType)
            Npab = int(Iparm[Ipab + '  '].strip())
            Inst2['Pdabc'] = []
            for i in range(Npab):
                k = Ipab + str(i + 1).rjust(2)
                s = Iparm[k].split()
                Inst2['Pdabc'].append([float(t) for t in s])
            Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
            Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0] * Inst1['difC'][
                0]  #turn 3rd col into TOF
        if 'INS  1I ITYP' in Iparm:
            s = Iparm['INS  1I ITYP'].split()
            Ityp = int(s[0])
            Tminmax = [float(s[1]) * 1000., float(s[2]) * 1000.]
            Itypes = [
                'Exponential', 'Maxwell/Exponential', '', 'Maxwell/Chebyschev',
                ''
            ]
            if Ityp in [1, 2, 4]:
                Inst2['Itype'] = Itypes[Ityp - 1]
                Inst2['Tminmax'] = Tminmax
                Icoeff = []
                Iesd = []
                Icovar = []
                for i in range(3):
                    s = Iparm['INS  1ICOFF' + str(i + 1)].split()
                    Icoeff += [float(S) for S in s]
                    s = Iparm['INS  1IECOF' + str(i + 1)].split()
                    Iesd += [float(S) for S in s]
                NT = 10
                for i in range(8):
                    s = Iparm['INS  1IECOR' + str(i + 1)]
                    if i == 7:
                        NT = 8
                    Icovar += [float(s[6 * j:6 * j + 6]) for j in range(NT)]
                Inst2['Icoeff'] = Icoeff
                Inst2['Iesd'] = Iesd
                Inst2['Icovar'] = Icovar
        return [Inst1, Inst2] 
[docs]def ReadPowderInstprm(instLines, bank, databanks, rd):
    '''Read lines from a GSAS-II (new) instrument parameter file
    similar to G2pwdGUI.OnLoad
    If instprm file has multiple banks each with header #Bank n: ..., this
    finds matching bank no. to load - problem with nonmatches?
    Note that this routine performs a similar role to :meth:`GSASIIdataGUI.GSASII.ReadPowderInstprm`,
    but that will call a GUI routine for selection when needed. This routine will raise exceptions
    on errors and will select the first bank when a choice might be appropriate.
    TODO: refactor to combine the two routines.
    :param list instLines: strings from GSAS-II parameter file; can be concatenated with ';'
    :param int  bank: bank number to check when instprm file has '#BANK n:...' strings
         when bank = n then use parameters; otherwise skip that set. Ignored if BANK n:
         not present. NB: this kind of instprm file made by a Save all profile command in Instrument Par     ameters
    :return dict: Inst  instrument parameter dict if OK, or
             str: Error message if failed
    (transliterated from GSASIIdataGUI.py:1235 (rev 3008), function of the same name)
     '''
    if 'GSAS-II' not in instLines[0]:
        raise ValueError("Not a valid GSAS-II instprm file")
    newItems = []
    newVals = []
    Found = False
    il = 0
    if bank is None:
        banklist = set()
        for S in instLines:
            if S[0] == '#' and 'Bank' in S:
                banklist.add(int(S.split(':')[0].split()[1]))
        # Picks the first bank by default
        if len(banklist) > 1:
            bank = sorted(banklist)[0]
        else:
            bank = 1
        rd.powderentry[2] = bank
    while il < len(instLines):
        S = instLines[il]
        if S[0] == '#':
            if Found:
                break
            if 'Bank' in S:
                if bank == int(S.split(':')[0].split()[1]):
                    il += 1
                    S = instLines[il]
                else:
                    il += 1
                    S = instLines[il]
                    while il < len(instLines) and '#Bank' not in S:
                        il += 1
                        if il == len(instLines):
                            raise ValueError(
                                "Bank {} not found in instprm file".format(
                                    bank))
                        S = instLines[il]
                    continue
            else:
                il += 1
                S = instLines[il]
        Found = True
        if '"""' in S:
            delim = '"""'
        elif "'''" in S:
            delim = "'''"
        else:
            S = S.replace(' ', '')
            SS = S.strip().split(';')
            for s in SS:
                item, val = s.split(':', 1)
                newItems.append(item)
                try:
                    newVals.append(float(val))
                except ValueError:
                    newVals.append(val)
            il += 1
            continue
        # read multiline values, delimited by ''' or """
        item, val = S.strip().split(':', 1)
        val = val.replace(delim, '').rstrip()
        val += '\n'
        while True:
            il += 1
            if il >= len(instLines):
                break
            S = instLines[il]
            if delim in S:
                val += S.replace(delim, '').rstrip()
                val += '\n'
                break
            else:
                val += S.rstrip()
                val += '\n'
        newItems.append(item)
        newVals.append(val)
        il += 1
    if 'Lam1' in newItems:
        rd.Sample.update({
            'Type': 'Bragg-Brentano',
            'Shift': [0., False],
            'Transparency': [0., False],
            'SurfRoughA': [0., False],
            'SurfRoughB': [0., False]
        })
    else:
        rd.Sample.update({
            'Type': 'Debye-Scherrer',
            'Absorption': [0., False],
            'DisplaceX': [0., False],
            'DisplaceY': [0., False]
        })
    return [makeInstDict(newItems, newVals, len(newVals) * [False]), {}] 
[docs]def LoadImportRoutines(prefix, errprefix=None, traceback=False):
    '''Routine to locate GSASII importers matching a prefix string
    '''
    if errprefix is None:
        errprefix = prefix
    readerlist = []
    pathlist = sys.path[:]
    if '.' not in pathlist:
        pathlist.append('.')
    potential_files = []
    for path in pathlist:
        for filename in glob.iglob(os.path.join(path, 'G2' + prefix + '*.py')):
            potential_files.append(filename)
    potential_files = sorted(list(set(potential_files)))
    for filename in potential_files:
        path, rootname = os.path.split(filename)
        pkg = os.path.splitext(rootname)[0]
        try:
            fp = None
            fp, fppath, desc = imp.find_module(pkg, [path])
            pkg = imp.load_module(pkg, fp, fppath, desc)
            for name, value in inspect.getmembers(pkg):
                if name.startswith('_'):
                    continue
                if inspect.isclass(value):
                    for method in 'Reader', 'ExtensionValidator', 'ContentsValidator':
                        if not hasattr(value, method):
                            break
                        if not callable(getattr(value, method)):
                            break
                    else:
                        reader = value()
                        if reader.UseReader:
                            readerlist.append(reader)
        except AttributeError:
            G2Print('Import_' + errprefix + ': Attribute Error ' + filename)
            if traceback:
                traceback.print_exc(file=sys.stdout)
        except Exception as exc:
            G2Print('\nImport_' + errprefix + ': Error importing file ' +
                    filename)
            G2Print(u'Error message: {}\n'.format(exc))
            if traceback:
                traceback.print_exc(file=sys.stdout)
        finally:
            if fp:
                fp.close()
    return readerlist 
[docs]def LoadExportRoutines(parent, traceback=False):
    '''Routine to locate GSASII exporters
    '''
    exporterlist = []
    pathlist = sys.path
    filelist = []
    for path in pathlist:
        for filename in glob.iglob(os.path.join(path, "G2export*.py")):
            filelist.append(filename)
    filelist = sorted(list(set(filelist)))  # remove duplicates
    # go through the routines and import them, saving objects that
    # have export routines (method Exporter)
    for filename in filelist:
        path, rootname = os.path.split(filename)
        pkg = os.path.splitext(rootname)[0]
        try:
            fp = None
            fp, fppath, desc = imp.find_module(pkg, [
                path,
            ])
            pkg = imp.load_module(pkg, fp, fppath, desc)
            for clss in inspect.getmembers(
                    pkg):  # find classes defined in package
                if clss[0].startswith('_'):
                    continue
                if not inspect.isclass(clss[1]):
                    continue
                # check if we have the required methods
                if not hasattr(clss[1], 'Exporter'):
                    continue
                if not callable(getattr(clss[1], 'Exporter')):
                    continue
                if parent is None:
                    if not hasattr(clss[1], 'Writer'):
                        continue
                else:
                    if not hasattr(clss[1], 'loadParmDict'):
                        continue
                    if not callable(getattr(clss[1], 'loadParmDict')):
                        continue
                try:
                    exporter = clss[1](parent)  # create an export instance
                except AttributeError:
                    pass
                except Exception as exc:
                    G2Print('\nExport init: Error substantiating class ' +
                            clss[0])
                    G2Print(u'Error message: {}\n'.format(exc))
                    if traceback:
                        traceback.print_exc(file=sys.stdout)
                    continue
                exporterlist.append(exporter)
        except AttributeError:
            G2Print('Export Attribute Error ' + filename)
            if traceback:
                traceback.print_exc(file=sys.stdout)
        except Exception as exc:
            G2Print('\nExport init: Error importing file ' + filename)
            G2Print(u'Error message: {}\n'.format(exc))
            if traceback:
                traceback.print_exc(file=sys.stdout)
        finally:
            if fp:
                fp.close()
    return exporterlist 
[docs]def LoadControls(Slines, data):
    'Read values from a .imctrl (Image Controls) file'
    cntlList = [
        'color', 'wavelength', 'distance', 'tilt', 'invert_x', 'invert_y',
        'type', 'Oblique', 'fullIntegrate', 'outChannels', 'outAzimuths',
        'LRazimuth', 'IOtth', 'azmthOff', 'DetDepth', 'calibskip', 'pixLimit',
        'cutoff', 'calibdmin', 'Flat Bkg', 'varyList', 'setdist', 'PolaVal',
        'SampleAbs', 'dark image', 'background image', 'twoth'
    ]
    save = {}
    for S in Slines:
        if S[0] == '#':
            continue
        [key, val] = S.strip().split(':', 1)
        if key in [
                'type',
                'calibrant',
                'binType',
                'SampleShape',
                'color',
        ]:  #strings
            save[key] = val
        elif key in [
                'varyList',
        ]:
            save[key] = eval(val)  #dictionary
        elif key in ['rotation']:
            save[key] = float(val)
        elif key in [
                'center',
        ]:
            if ',' in val:
                save[key] = eval(val)
            else:
                vals = val.strip('[] ').split()
                save[key] = [float(vals[0]), float(vals[1])]
        elif key in cntlList:
            save[key] = eval(val)
    data.update(save) 
[docs]def WriteControls(filename, data):
    'Write current values to a .imctrl (Image Controls) file'
    File = open(filename, 'w')
    keys = [
        'type', 'color', 'wavelength', 'calibrant', 'distance', 'center',
        'Oblique', 'tilt', 'rotation', 'azmthOff', 'fullIntegrate', 'LRazimuth',
        'setdist', 'IOtth', 'outChannels', 'outAzimuths', 'invert_x',
        'invert_y', 'DetDepth', 'calibskip', 'pixLimit', 'cutoff', 'calibdmin',
        'Flat Bkg', 'varyList', 'binType', 'SampleShape', 'PolaVal',
        'SampleAbs', 'dark image', 'background image', 'twoth'
    ]
    for key in keys:
        if key not in data:  #uncalibrated!
            continue
        File.write(key + ':' + str(data[key]) + '\n')
    File.close() 
[docs]def RereadImageData(ImageReaderlist, imagefile, ImageTag=None, FormatName=''):
    '''Read a single image with an image importer. This is called to
    reread an image after it has already been imported, so it is not
    necessary to reload metadata.
    Based on :func:`GetImageData.GetImageData` which this can replace
    where imageOnly=True
    :param list ImageReaderlist: list of Reader objects for images
    :param str imagefile: name of image file
    :param int/str ImageTag: specifies a particular image to be read from a file.
      First image is read if None (default).
    :param str formatName: the image reader formatName
    :returns: an image as a numpy array
    '''
    # determine which formats are compatible with this file
    primaryReaders = []
    secondaryReaders = []
    for rd in ImageReaderlist:
        flag = rd.ExtensionValidator(imagefile)
        if flag is None:
            secondaryReaders.append(rd)
        elif flag:
            if not FormatName:
                primaryReaders.append(rd)
            elif FormatName == rd.formatName:
                primaryReaders.append(rd)
    if len(secondaryReaders) + len(primaryReaders) == 0:
        G2Print('Error: No matching format for file ' + imagefile)
        raise Exception('No image read')
    errorReport = ''
    if not imagefile:
        return
    for rd in primaryReaders + secondaryReaders:
        rd.ReInitialize()  # purge anything from a previous read
        rd.errors = ""  # clear out any old errors
        if not rd.ContentsValidator(imagefile):  # rejected on cursory check
            errorReport += "\n  " + rd.formatName + ' validator error'
            if rd.errors:
                errorReport += ': ' + rd.errors
                continue
        flag = rd.Reader(imagefile, None, blocknum=ImageTag)
        if flag:  # this read succeeded
            if rd.Image is None:
                raise Exception('No image read. Strange!')
            if GSASIIpath.GetConfigValue('Transpose'):
                G2Print('Warning: Transposing Image!')
                rd.Image = rd.Image.T
            #rd.readfilename = imagefile
            return rd.Image
    else:
        G2Print('Error reading file ' + imagefile)
        G2Print('Error messages(s)\n' + errorReport)
        raise Exception('No image read') 
[docs]def readMasks(filename, masks, ignoreThreshold):
    '''Read a GSAS-II masks file'''
    File = open(filename, 'r')
    save = {}
    oldThreshold = masks['Thresholds'][0]
    S = File.readline()
    while S:
        if S[0] == '#':
            S = File.readline()
            continue
        [key, val] = S.strip().split(':', 1)
        if key in [
                'Points', 'Rings', 'Arcs', 'Polygons', 'Frames', 'Thresholds'
        ]:
            if ignoreThreshold and key == 'Thresholds':
                S = File.readline()
                continue
            save[key] = eval(val)
            if key == 'Thresholds':
                save[key][0] = oldThreshold
                save[key][1][1] = min(oldThreshold[1], save[key][1][1])
        S = File.readline()
    File.close()
    masks.update(save)
    # CleanupMasks
    for key in ['Points', 'Rings', 'Arcs', 'Polygons']:
        masks[key] = masks.get(key, [])
        masks[key] = [i for i in masks[key] if len(i)] 
[docs]def PDFWrite(PDFentry, fileroot, PDFsaves, PDFControls, Inst={}, Limits=[]):
    '''Write PDF-related data (G(r), S(Q),...) into files, as
    selected.
    :param str PDFentry: name of the PDF entry in the tree. This is
      used for comments in the file specifying where it came from;
      it can be arbitrary
    :param str fileroot: name of file(s) to be written. The extension
      will be ignored.
    :param list PDFsaves: flags that determine what type of file will be
      written:
      PDFsaves[0], if True writes a I(Q) file with a .iq extension
      PDFsaves[1], if True writes a S(Q) file with a .sq extension
      PDFsaves[2], if True writes a F(Q) file with a .fq extension
      PDFsaves[3], if True writes a G(r) file with a .gr extension
      PDFsaves[4], if True writes G(r) in a pdfGUI input file with
      a .gr extension. Note that if PDFsaves[3] and PDFsaves[4] are
      both True, the pdfGUI overwrites the G(r) file.
    :param dict PDFControls: The PDF parameters and computed results
    :param dict Inst: Instrument parameters from the PDWR entry used
      to compute the PDF. Needed only when PDFsaves[4] is True.
    :param list Limits: Computation limits from the PDWR entry used
      to compute the PDF. Needed only when PDFsaves[4] is True.
    '''
    import scipy.interpolate as scintp
    fileroot = os.path.splitext(fileroot)[0]
    if PDFsaves[0]:  #I(Q)
        iqfilename = fileroot + '.iq'
        iqdata = PDFControls['I(Q)'][1]
        iqfxn = scintp.interp1d(iqdata[0], iqdata[1], kind='linear')
        iqfile = open(iqfilename, 'w')
        iqfile.write('#T I(Q) %s\n' % (PDFentry))
        iqfile.write('#L Q     I(Q)\n')
        qnew = np.arange(iqdata[0][0], iqdata[0][-1], 0.005)
        iqnew = zip(qnew, iqfxn(qnew))
        for q, iq in iqnew:
            iqfile.write("%15.6g %15.6g\n" % (q, iq))
        iqfile.close()
        G2Print(' I(Q) saved to: ' + iqfilename)
    if PDFsaves[1]:  #S(Q)
        sqfilename = fileroot + '.sq'
        sqdata = PDFControls['S(Q)'][1]
        sqfxn = scintp.interp1d(sqdata[0], sqdata[1], kind='linear')
        sqfile = open(sqfilename, 'w')
        sqfile.write('#T S(Q) %s\n' % (PDFentry))
        sqfile.write('#L Q     S(Q)\n')
        qnew = np.arange(sqdata[0][0], sqdata[0][-1], 0.005)
        sqnew = zip(qnew, sqfxn(qnew))
        for q, sq in sqnew:
            sqfile.write("%15.6g %15.6g\n" % (q, sq))
        sqfile.close()
        G2Print(' S(Q) saved to: ' + sqfilename)
    if PDFsaves[2]:  #F(Q)
        fqfilename = fileroot + '.fq'
        fqdata = PDFControls['F(Q)'][1]
        fqfxn = scintp.interp1d(fqdata[0], fqdata[1], kind='linear')
        fqfile = open(fqfilename, 'w')
        fqfile.write('#T F(Q) %s\n' % (PDFentry))
        fqfile.write('#L Q     F(Q)\n')
        qnew = np.arange(fqdata[0][0], fqdata[0][-1], 0.005)
        fqnew = zip(qnew, fqfxn(qnew))
        for q, fq in fqnew:
            fqfile.write("%15.6g %15.6g\n" % (q, fq))
        fqfile.close()
        G2Print(' F(Q) saved to: ' + fqfilename)
    if PDFsaves[3]:  #G(R)
        grfilename = fileroot + '.gr'
        grdata = PDFControls['G(R)'][1]
        grfxn = scintp.interp1d(grdata[0], grdata[1], kind='linear')
        grfile = open(grfilename, 'w')
        grfile.write('#T G(R) %s\n' % (PDFentry))
        grfile.write('#L R     G(R)\n')
        rnew = np.arange(grdata[0][0], grdata[0][-1], 0.010)
        grnew = zip(rnew, grfxn(rnew))
        for r, gr in grnew:
            grfile.write("%15.6g %15.6g\n" % (r, gr))
        grfile.close()
        G2Print(' G(R) saved to: ' + grfilename)
    if PDFsaves[4]:  #pdfGUI file for G(R)
        import GSASIIlattice as G2lat
        import GSASIImath as G2mth
        grfilename = fileroot + '.gr'
        grdata = PDFControls['G(R)'][1]
        qdata = PDFControls['I(Q)'][1][0]
        grfxn = scintp.interp1d(grdata[0], grdata[1], kind='linear')
        grfile = open(grfilename, 'w')
        rnew = np.arange(grdata[0][0], grdata[0][-1], 0.010)
        grnew = zip(rnew, grfxn(rnew))
        grfile.write('[DEFAULT]\n')
        grfile.write('\n')
        grfile.write('version = GSAS-II-v' +
                     str(GSASIIpath.GetVersionNumber()) + '\n')
        grfile.write('\n')
        grfile.write('# input and output specifications\n')
        grfile.write('dataformat = Qnm\n')
        grfile.write('inputfile = %s\n' % (PDFControls['Sample']['Name']))
        grfile.write('backgroundfile = %s\n' %
                     (PDFControls['Sample Bkg.']['Name']))
        grfile.write('outputtype = gr\n')
        grfile.write('\n')
        grfile.write('# PDF calculation setup\n')
        if 'x' in Inst['Type']:
            grfile.write('mode = %s\n' % ('xray'))
        elif 'N' in Inst['Type']:
            grfile.write('mode = %s\n' % ('neutron'))
        wave = G2mth.getMeanWave(Inst)
        grfile.write('wavelength = %.5f\n' % (wave))
        formula = ''
        for el in PDFControls['ElList']:
            formula += el
            num = PDFControls['ElList'][el]['FormulaNo']
            if num == round(num):
                formula += '%d' % (int(num))
            else:
                formula += '%.2f' % (num)
        grfile.write('composition = %s\n' % (formula))
        grfile.write('bgscale = %.3f\n' % (-PDFControls['Sample Bkg.']['Mult']))
        highQ = 2. * np.pi / G2lat.Pos2dsp(Inst, Limits[1][1])
        grfile.write('qmaxinst = %.2f\n' % (highQ))
        grfile.write('qmin = %.5f\n' % (qdata[0]))
        grfile.write('qmax = %.4f\n' % (qdata[-1]))
        grfile.write('rmin = %.2f\n' % (PDFControls['Rmin']))
        grfile.write('rmax = %.2f\n' % (PDFControls['Rmax']))
        grfile.write('rstep = 0.01\n')
        grfile.write('\n')
        grfile.write('# End of config ' + 63 * '-')
        grfile.write('\n')
        grfile.write('#### start data\n')
        grfile.write('#S 1\n')
        grfile.write(r'#L r($\AA$)  G($\AA^{-2}$)\n')
        for r, gr in grnew:
            grfile.write("%15.2F %15.6F\n" % (r, gr))
        grfile.close()
        G2Print(' G(R) saved to: ' + grfilename)