Changeset 1074 for trunk/exports


Ignore:
Timestamp:
Oct 2, 2013 9:27:17 AM (8 years ago)
Author:
toby
Message:

more CIF work

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/exports/G2cif.py

    r1069 r1074  
    1010########### SVN repository information ###################
    1111'''Code to export a GSAS-II project as a CIF
    12 The heavy lifting is done in method export
     12This builds on the data extraction done in method export in the base class
     13
     14TODO: set bond pub flags?
     15TODO: progress bar
     16TODO: cleanup routine import
     17
    1318'''
    1419
    15 # TODO: set bond pub flags?
    1620
    1721import datetime as dt
     
    4852CIFdic = None
    4953
    50 def getCallerDocString(): # for development
    51     "Return the calling function's doc string"
    52     import inspect as ins
    53     for item in ins.stack()[1][0].f_code.co_consts:
    54         if type(item) is str:
    55             return item
    56     else:
    57         return '?'
     54class ExportCIF(G2IO.ExportBaseclass):
     55    '''Used to create a CIF of an entire project
     56    '''
     57    def __init__(self,G2frame):
     58        super(self.__class__,self).__init__( # fancy way to say <parentclass>.__init__
     59            G2frame=G2frame,
     60            formatName = 'full CIF',
     61            extension='.cif',
     62            longFormatName = 'Export project as CIF'
     63            )
     64        self.author = ''
     65
     66    def export(self,mode='full'):
     67        '''Export a CIF
     68
     69        :param str mode: "full" (default) to create a complete CIF of project,
     70          "simple" for a simple CIF with only coordinates
     71        '''
     72   
     73# ===== define functions for export method =======================================
     74        def openCIF(filnam):
     75            'opens the output file'
     76            if DEBUG:
     77                self.fp = sys.stdout
     78            else:
     79                self.fp = open(filnam,'w')
     80
     81        def closeCIF():
     82            'close the output file'
     83            if not DEBUG:
     84                self.fp.close()
     85           
     86        def WriteCIFitem(name,value=''):
     87            '''Write CIF data items to the file. Formats values as needed.
     88            Also used without a value for loops, comments, loop headers, etc.
     89            '''           
     90            if value:
     91                if "\n" in value or len(value)> 70:
     92                    if name.strip(): self.fp.write(name+'\n')
     93                    self.fp.write('; '+value+'\n')
     94                    self.fp.write('; '+'\n')
     95                elif " " in value:
     96                    if len(name)+len(value) > 65:
     97                        self.fp.write(name + '\n   ' + '"' + str(value) + '"'+'\n')
     98                    else:
     99                        self.fp.write(name + '  ' + '"' + str(value) + '"'+'\n')
     100                else:
     101                    if len(name)+len(value) > 65:
     102                        self.fp.write(name+'\n   ' + value+'\n')
     103                    else:
     104                        self.fp.write(name+'  ' + value+'\n')
     105            else:
     106                self.fp.write(name+'\n')
     107
     108        def WriteAudit():
     109            'Write the CIF audit values. Perhaps should be in a single element loop.'
     110            WriteCIFitem('_audit_creation_method',
     111                         'created in GSAS-II')
     112            WriteCIFitem('_audit_creation_date',self.CIFdate)
     113            if self.author:
     114                WriteCIFitem('_audit_author_name',self.author)
     115            WriteCIFitem('_audit_update_record',
     116                         self.CIFdate+'  Initial software-generated CIF')
     117
     118        def WriteOverall():
     119            '''Write out overall refinement information.
     120
     121            More could be done here, but this is a good start.
     122            '''
     123            WriteCIFitem('_pd_proc_info_datetime', self.CIFdate)
     124            WriteCIFitem('_pd_calc_method', 'Rietveld Refinement')
     125            #WriteCIFitem('_refine_ls_shift/su_max',DAT1)
     126            #WriteCIFitem('_refine_ls_shift/su_mean',DAT2)
     127            #WriteCIFitem('_refine_diff_density_max',rhomax)    #these need to be defined for each phase!
     128            #WriteCIFitem('_refine_diff_density_min',rhomin)
     129            WriteCIFitem('_computing_structure_refinement','GSAS-II (Toby & Von Dreele, J. Appl. Cryst. 46, 544-549, 2013)')
     130            try:
     131                vars = str(len(self.OverallParms['Covariance']['varyList']))
     132            except:
     133                vars = '?'
     134            WriteCIFitem('_refine_ls_number_parameters',vars)
     135            try:
     136                GOF = G2mth.ValEsd(self.OverallParms['Covariance']['Rvals']['GOF'],-0.009)
     137            except:
     138                GOF = '?'
     139            WriteCIFitem('_refine_ls_goodness_of_fit_all',GOF)
     140
     141            # get restraint info
     142            # restraintDict = self.OverallParms.get('Restraints',{})
     143            # for i in  self.OverallParms['Constraints']:
     144            #     print i
     145            #     for j in self.OverallParms['Constraints'][i]:
     146            #         print j
     147            #WriteCIFitem('_refine_ls_number_restraints',TEXT)
     148            # other things to consider reporting
     149            # _refine_ls_number_reflns
     150            # _refine_ls_goodness_of_fit_obs
     151            # _refine_ls_wR_factor_obs
     152            # _refine_ls_restrained_S_all
     153            # _refine_ls_restrained_S_obs
     154
     155            # include an overall profile r-factor, if there is more than one powder histogram
     156            R = '%.5f'%(self.OverallParms['Covariance']['Rvals']['Rwp']/100.)
     157            WriteCIFitem('\n# OVERALL WEIGHTED R-FACTOR')
     158            WriteCIFitem('_refine_ls_wR_factor_obs',R)
     159                # _refine_ls_R_factor_all
     160                # _refine_ls_R_factor_obs               
     161            WriteCIFitem('_refine_ls_matrix_type','full')
     162            #WriteCIFitem('_refine_ls_matrix_type','userblocks')
     163
     164        def writeCIFtemplate(G2dict,tmplate,defaultname=''):
     165            '''Write out the selected or edited CIF template
     166            An unedited CIF template file is copied, comments intact; an edited
     167            CIF template is written out from PyCifRW which of course strips comments.
     168            In all cases the initial data_ header is stripped (there should only be one!)
     169            '''
     170            CIFobj = G2dict.get("CIF_template")
     171            if defaultname:
     172                defaultname = defaultname.encode('ascii','replace').strip().replace(' ','_')
     173                defaultname = re.sub(r'[^a-zA-Z0-9_-]','',defaultname)
     174                defaultname = tmplate + "_" + defaultname + ".cif"
     175            else:
     176                defaultname = ''
     177            templateDefName = 'template_'+tmplate+'.cif'
     178            if not CIFobj: # copying a template
     179                for pth in [os.getcwd()]+sys.path:
     180                    fil = os.path.join(pth,defaultname)
     181                    if os.path.exists(fil) and defaultname: break
     182                else:
     183                    for pth in sys.path:
     184                        fil = os.path.join(pth,templateDefName)
     185                        if os.path.exists(fil): break
     186                    else:
     187                        print(CIFobj+' not found in path!')
     188                        return
     189                fp = open(fil,'r')
     190                txt = fp.read()
     191                fp.close()
     192            elif type(CIFobj) is not list and type(CIFobj) is not tuple:
     193                if not os.path.exists(CIFobj):
     194                    print("Error: requested template file has disappeared: "+CIFobj)
     195                    return
     196                fp = open(CIFobj,'r')
     197                txt = fp.read()
     198                fp.close()
     199            else:
     200                txt = dict2CIF(CIFobj[0],CIFobj[1]).WriteOut()
     201            # remove the PyCifRW header, if present
     202            #if txt.find('PyCifRW') > -1 and txt.find('data_') > -1:
     203            txt = "# GSAS-II edited template follows "+txt[txt.index("data_")+5:]
     204            #txt = txt.replace('data_','#')
     205            WriteCIFitem(txt)
     206
     207        def FormatSH(phasenam):
     208            'Format a full spherical harmonics texture description as a string'
     209            phasedict = self.Phases[phasenam] # pointer to current phase info           
     210            pfx = str(phasedict['pId'])+'::'
     211            s = ""
     212            textureData = phasedict['General']['SH Texture']   
     213            if textureData.get('Order'):
     214                s += "Spherical Harmonics correction. Order = "+str(textureData['Order'])
     215                s += " Model: " + str(textureData['Model']) + "\n    Orientation angles: "
     216                for name in ['omega','chi','phi']:
     217                    aname = pfx+'SH '+name
     218                    s += name + " = "
     219                    sig = self.sigDict.get(aname,-0.09)
     220                    s += G2mth.ValEsd(self.parmDict[aname],sig)
     221                    s += "; "
     222                s += "\n"
     223                s1 = "    Coefficients:  "
     224                for name in textureData['SH Coeff'][1]:
     225                    aname = pfx+name
     226                    if len(s1) > 60:
     227                        s += s1 + "\n"
     228                        s1 = "    "
     229                    s1 += aname + ' = '
     230                    sig = self.sigDict.get(aname,-0.0009)
     231                    s1 += G2mth.ValEsd(self.parmDict[aname],sig)
     232                    s1 += "; "
     233                s += s1
     234            return s
     235
     236        def FormatHAPpo(phasenam):
     237            '''return the March-Dollase/SH correction for every
     238            histogram in the current phase formatted into a
     239            character string
     240            '''
     241            phasedict = self.Phases[phasenam] # pointer to current phase info           
     242            s = ''
     243            for histogram in sorted(phasedict['Histograms']):
     244                if histogram.startswith("HKLF"): continue # powder only
     245                Histogram = self.Histograms.get(histogram)
     246                if not Histogram: continue
     247                hapData = phasedict['Histograms'][histogram]
     248                if hapData['Pref.Ori.'][0] == 'MD':
     249                    aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':MD'
     250                    if self.parmDict.get(aname,1.0) != 1.0: continue
     251                    sig = self.sigDict.get(aname,-0.009)
     252                    if s != "": s += '\n'
     253                    s += 'March-Dollase correction'
     254                    if len(self.powderDict) > 1:
     255                        s += ', histogram '+str(Histogram['hId']+1)
     256                    s += ' coef. = ' + G2mth.ValEsd(self.parmDict[aname],sig)
     257                    s += ' axis = ' + str(hapData['Pref.Ori.'][3])
     258                else: # must be SH
     259                    if s != "": s += '\n'
     260                    s += 'Simple spherical harmonic correction'
     261                    if len(self.powderDict) > 1:
     262                        s += ', histogram '+str(Histogram['hId']+1)
     263                    s += ' Order = '+str(hapData['Pref.Ori.'][4])+'\n'
     264                    s1 = "    Coefficients:  "
     265                    for item in hapData['Pref.Ori.'][5]:
     266                        aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':'+item
     267                        if len(s1) > 60:
     268                            s += s1 + "\n"
     269                            s1 = "    "
     270                        s1 += aname + ' = '
     271                        sig = self.sigDict.get(aname,-0.0009)
     272                        s1 += G2mth.ValEsd(self.parmDict[aname],sig)
     273                        s1 += "; "
     274                    s += s1
     275            return s
     276        def FormatBackground(bkg,hId):
     277            '''Display the Background information as a descriptive text string.
     278           
     279            TODO: this needs to be expanded to show the diffuse peak and
     280            Debye term information as well. (Bob)
     281
     282            :returns: the text description (str)
     283            '''
     284            hfx = ':'+str(hId)+':'
     285            fxn, bkgdict = bkg
     286            terms = fxn[2]
     287            txt = 'Background function: "'+fxn[0]+'" function with '+str(terms)+' terms:\n'
     288            l = "    "
     289            for i,v in enumerate(fxn[3:]):
     290                name = '%sBack:%d'%(hfx,i)
     291                sig = self.sigDict.get(name,-0.009)
     292                if len(l) > 60:
     293                    txt += l + '\n'
     294                    l = '    '
     295                l += G2mth.ValEsd(v,sig)+', '
     296            txt += l
     297            if bkgdict['nDebye']:
     298                txt += '\n  Background Debye function parameters: A, R, U:'
     299                names = ['A:','R:','U:']
     300                for i in range(bkgdict['nDebye']):
     301                    txt += '\n    '
     302                    for j in range(3):
     303                        name = hfx+'Debye'+names[j]+str(i)
     304                        sig = self.sigDict.get(name,-0.009)
     305                        txt += G2mth.ValEsd(bkgdict['debyeTerms'][i][2*j],sig)+', '
     306            if bkgdict['nPeaks']:
     307                txt += '\n  Background peak parameters: pos, int, sig, gam:'
     308                names = ['pos:','int:','sig:','gam:']
     309                for i in range(bkgdict['nPeaks']):
     310                    txt += '\n    '
     311                    for j in range(4):
     312                        name = hfx+'BkPk'+names[j]+str(i)
     313                        sig = self.sigDict.get(name,-0.009)
     314                        txt += G2mth.ValEsd(bkgdict['peaksList'][i][2*j],sig)+', '
     315            return txt
     316
     317        def FormatInstProfile(instparmdict,hId):
     318            '''Format the instrumental profile parameters with a
     319            string description. Will only be called on PWDR histograms
     320            '''
     321            s = ''
     322            inst = instparmdict[0]
     323            hfx = ':'+str(hId)+':'
     324            if 'C' in inst['Type'][0]:
     325                s = 'Finger-Cox-Jephcoat function parameters U, V, W, X, Y, SH/L:\n'
     326                s += '  peak variance(Gauss) = Utan(Th)^2^+Vtan(Th)+W:\n'
     327                s += '  peak HW(Lorentz) = X/cos(Th)+Ytan(Th); SH/L = S/L+H/L\n'
     328                s += '  U, V, W in (centideg)^2^, X & Y in centideg\n    '
     329                for item in ['U','V','W','X','Y','SH/L']:
     330                    name = hfx+item
     331                    sig = self.sigDict.get(name,-0.009)
     332                    s += G2mth.ValEsd(inst[item][1],sig)+', '                   
     333            elif 'T' in inst['Type'][0]:    #to be tested after TOF Rietveld done
     334                s = 'Von Dreele-Jorgenson-Windsor function parameters\n'+ \
     335                    '   alpha, beta-0, beta-1, beta-q, sig-0, sig-1, sig-q, X, Y:\n    '
     336                for item in ['alpha','bet-0','bet-1','bet-q','sig-0','sig-1','sig-q','X','Y']:
     337                    name = hfx+item
     338                    sig = self.sigDict.get(name,-0.009)
     339                    s += G2mth.ValEsd(inst[item][1],sig)+', '
     340            return s
     341
     342        def FormatPhaseProfile(phasenam):
     343            '''Format the phase-related profile parameters (size/strain)
     344            with a string description.
     345            return an empty string or None if there are no
     346            powder histograms for this phase.
     347            '''
     348            s = ''
     349            phasedict = self.Phases[phasenam] # pointer to current phase info
     350            SGData = phasedict['General'] ['SGData']         
     351            for histogram in sorted(phasedict['Histograms']):
     352                if histogram.startswith("HKLF"): continue # powder only
     353                Histogram = self.Histograms.get(histogram)
     354                if not Histogram: continue
     355                hapData = phasedict['Histograms'][histogram]
     356                pId = phasedict['pId']
     357                hId = Histogram['hId']
     358                phfx = '%d:%d:'%(pId,hId)
     359                size = hapData['Size']
     360                mustrain = hapData['Mustrain']
     361                hstrain = hapData['HStrain']
     362                s = '  Crystallite size model "%s" for %s (microns)\n  '%(size[0],phasenam)
     363                names = ['Size;i','Size;mx']
     364                if 'uniax' in size[0]:
     365                    names = ['Size;i','Size;a','Size;mx']
     366                    s += 'anisotropic axis is %s\n  '%(str(size[3]))
     367                    s += 'parameters: equatorial size, axial size, G/L mix\n    '
     368                    for i,item in enumerate(names):
     369                        name = phfx+item
     370                        sig = self.sigDict.get(name,-0.009)
     371                        s += G2mth.ValEsd(size[1][i],sig)+', '
     372                elif 'ellip' in size[0]:
     373                    s += 'parameters: S11, S22, S33, S12, S13, S23, G/L mix\n    '
     374                    for i in range(6):
     375                        name = phfx+'Size:'+str(i)
     376                        sig = self.sigDict.get(name,-0.009)
     377                        s += G2mth.ValEsd(size[4][i],sig)+', '
     378                    sig = self.sigDict.get(phfx+'Size;mx',-0.009)
     379                    s += G2mth.ValEsd(size[1][2],sig)+', '                                           
     380                else:       #isotropic
     381                    s += 'parameters: Size, G/L mix\n    '
     382                    i = 0
     383                    for item in names:
     384                        name = phfx+item
     385                        sig = self.sigDict.get(name,-0.009)
     386                        s += G2mth.ValEsd(size[1][i],sig)+', '
     387                        i = 2    #skip the aniso value               
     388                s += '\n  Mustrain model "%s" for %s (10^6^)\n  '%(mustrain[0],phasenam)
     389                names = ['Mustrain;i','Mustrain;mx']
     390                if 'uniax' in mustrain[0]:
     391                    names = ['Mustrain;i','Mustrain;a','Mustrain;mx']
     392                    s += 'anisotropic axis is %s\n  '%(str(size[3]))
     393                    s += 'parameters: equatorial mustrain, axial mustrain, G/L mix\n    '
     394                    for i,item in enumerate(names):
     395                        name = phfx+item
     396                        sig = self.sigDict.get(name,-0.009)
     397                        s += G2mth.ValEsd(mustrain[1][i],sig)+', '
     398                elif 'general' in mustrain[0]:
     399                    names = 'parameters: '
     400                    for i,name in enumerate(G2spc.MustrainNames(SGData)):
     401                        names += name+', '
     402                        if i == 9:
     403                            names += '\n  '
     404                    names += 'G/L mix\n    '
     405                    s += names
     406                    txt = ''
     407                    for i in range(len(mustrain[4])):
     408                        name = phfx+'Mustrain:'+str(i)
     409                        sig = self.sigDict.get(name,-0.009)
     410                        if len(txt) > 60:
     411                            s += txt+'\n    '
     412                            txt = ''
     413                        txt += G2mth.ValEsd(mustrain[4][i],sig)+', '
     414                    s += txt                                           
     415                    sig = self.sigDict.get(phfx+'Mustrain;mx',-0.009)
     416                    s += G2mth.ValEsd(mustrain[1][2],sig)+', '
     417                   
     418                else:       #isotropic
     419                    s += '  parameters: Mustrain, G/L mix\n    '
     420                    i = 0
     421                    for item in names:
     422                        name = phfx+item
     423                        sig = self.sigDict.get(name,-0.009)
     424                        s += G2mth.ValEsd(mustrain[1][i],sig)+', '
     425                        i = 2    #skip the aniso value               
     426                s += '\n  Macrostrain for %s\n'%(phasenam)
     427                txt = '  parameters: '
     428                names = G2spc.HStrainNames(SGData)
     429                for name in names:
     430                    txt += name+', '
     431                s += txt+'\n    '
     432                for i in range(len(names)):
     433                    name = phfx+name[i]
     434                    sig = self.sigDict.get(name,-0.009)
     435                    s += G2mth.ValEsd(hstrain[0][i],sig)+', '
     436            return s
     437       
     438        def FmtAtomType(sym):
     439            'Reformat a GSAS-II atom type symbol to match CIF rules'
     440            sym = sym.replace('_','') # underscores are not allowed: no isotope designation?
     441            # in CIF, oxidation state sign symbols come after, not before
     442            if '+' in sym:
     443                sym = sym.replace('+','') + '+'
     444            elif '-' in sym:
     445                sym = sym.replace('-','') + '-'
     446            return sym
     447           
     448        def PutInCol(val,wid):
     449            '''Pad a value to >=wid+1 columns by adding spaces at the end. Always
     450            adds at least one space
     451            '''
     452            val = str(val).replace(' ','')
     453            if not val: val = '?'
     454            fmt = '{:' + str(wid) + '} '
     455            return fmt.format(val)
     456
     457        def MakeUniqueLabel(lbl,labellist):
     458            'Make sure that every atom label is unique'
     459            lbl = lbl.strip()
     460            if not lbl: # deal with a blank label
     461                lbl = 'A_1'
     462            if lbl not in labellist:
     463                labellist.append(lbl)
     464                return lbl
     465            i = 1
     466            prefix = lbl
     467            if '_' in lbl:
     468                prefix = lbl[:lbl.rfind('_')]
     469                suffix = lbl[lbl.rfind('_')+1:]
     470                try:
     471                    i = int(suffix)+1
     472                except:
     473                    pass
     474            while prefix+'_'+str(i) in labellist:
     475                i += 1
     476            else:
     477                lbl = prefix+'_'+str(i)
     478                labellist.append(lbl)
     479
     480        def WriteAtomsNuclear(phasenam):
     481            'Write atom positions to CIF'
     482            phasedict = self.Phases[phasenam] # pointer to current phase info
     483            General = phasedict['General']
     484            cx,ct,cs,cia = General['AtomPtrs']
     485            Atoms = phasedict['Atoms']
     486            cfrac = cx+3
     487            fpfx = str(phasedict['pId'])+'::Afrac:'       
     488            for i,at in enumerate(Atoms):
     489                fval = self.parmDict.get(fpfx+str(i),at[cfrac])
     490                if fval != 0.0:
     491                    break
     492            else:
     493                WriteCIFitem('\n# PHASE HAS NO ATOMS!')
     494                return
     495               
     496            WriteCIFitem('\n# ATOMIC COORDINATES AND DISPLACEMENT PARAMETERS')
     497            WriteCIFitem('loop_ '+
     498                         '\n\t_atom_site_label'+
     499                         '\n\t_atom_site_type_symbol'+
     500                         '\n\t_atom_site_fract_x'+
     501                         '\n\t_atom_site_fract_y'+
     502                         '\n\t_atom_site_fract_z'+
     503                         '\n\t_atom_site_occupancy'+
     504                         '\n\t_atom_site_adp_type'+
     505                         '\n\t_atom_site_U_iso_or_equiv'+
     506                         '\n\t_atom_site_symmetry_multiplicity')
     507
     508            varnames = {cx:'Ax',cx+1:'Ay',cx+2:'Az',cx+3:'Afrac',
     509                        cia+1:'AUiso',cia+2:'AU11',cia+3:'AU22',cia+4:'AU33',
     510                        cia+5:'AU12',cia+6:'AU13',cia+7:'AU23'}
     511            self.labellist = []
     512           
     513            pfx = str(phasedict['pId'])+'::'
     514            # loop over all atoms
     515            naniso = 0
     516            for i,at in enumerate(Atoms):
     517                s = PutInCol(MakeUniqueLabel(at[ct-1],self.labellist),6) # label
     518                fval = self.parmDict.get(fpfx+str(i),at[cfrac])
     519                if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
     520                s += PutInCol(FmtAtomType(at[ct]),4) # type
     521                if at[cia] == 'I':
     522                    adp = 'Uiso '
     523                else:
     524                    adp = 'Uani '
     525                    naniso += 1
     526                    # compute Uequiv crudely
     527                    # correct: Defined as "1/3 trace of diagonalized U matrix".
     528                    # SEE cell2GS & Uij2Ueqv to GSASIIlattice. Former is needed to make the GS matrix used by the latter.
     529                    t = 0.0
     530                    for j in (2,3,4):
     531                        var = pfx+varnames[cia+j]+":"+str(i)
     532                        t += self.parmDict.get(var,at[cia+j])
     533                for j in (cx,cx+1,cx+2,cx+3,cia,cia+1):
     534                    if j in (cx,cx+1,cx+2):
     535                        dig = 11
     536                        sigdig = -0.00009
     537                    else:
     538                        dig = 10
     539                        sigdig = -0.009
     540                    if j == cia:
     541                        s += adp
     542                    else:
     543                        var = pfx+varnames[j]+":"+str(i)
     544                        dvar = pfx+"d"+varnames[j]+":"+str(i)
     545                        if dvar not in self.sigDict:
     546                            dvar = var
     547                        if j == cia+1 and adp == 'Uani ':
     548                            val = t/3.
     549                            sig = sigdig
     550                        else:
     551                            #print var,(var in self.parmDict),(var in self.sigDict)
     552                            val = self.parmDict.get(var,at[j])
     553                            sig = self.sigDict.get(dvar,sigdig)
     554                        s += PutInCol(G2mth.ValEsd(val,sig),dig)
     555                s += PutInCol(at[cs+1],3)
     556                WriteCIFitem(s)
     557            if naniso == 0: return
     558            # now loop over aniso atoms
     559            WriteCIFitem('\nloop_' + '\n\t_atom_site_aniso_label' +
     560                         '\n\t_atom_site_aniso_U_11' + '\n\t_atom_site_aniso_U_12' +
     561                         '\n\t_atom_site_aniso_U_13' + '\n\t_atom_site_aniso_U_22' +
     562                         '\n\t_atom_site_aniso_U_23' + '\n\t_atom_site_aniso_U_33')
     563            for i,at in enumerate(Atoms):
     564                fval = self.parmDict.get(fpfx+str(i),at[cfrac])
     565                if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
     566                if at[cia] == 'I': continue
     567                s = PutInCol(self.labellist[i],6) # label
     568                for j in (2,3,4,5,6,7):
     569                    sigdig = -0.0009
     570                    var = pfx+varnames[cia+j]+":"+str(i)
     571                    val = self.parmDict.get(var,at[cia+j])
     572                    sig = self.sigDict.get(var,sigdig)
     573                    s += PutInCol(G2mth.ValEsd(val,sig),11)
     574                WriteCIFitem(s)
     575
     576        def HillSortElements(elmlist):
     577            '''Sort elements in "Hill" order: C, H, others, (where others
     578            are alphabetical).
     579
     580            :params list elmlist: a list of element strings
     581
     582            :returns: a sorted list of element strings
     583            '''
     584            newlist = []
     585            oldlist = elmlist[:]
     586            for elm in ('C','H'):
     587                if elm in elmlist:
     588                    newlist.append(elm)
     589                    oldlist.pop(oldlist.index(elm))
     590            return newlist+sorted(oldlist)
     591
     592        def WriteComposition(phasenam):
     593            '''determine the composition for the unit cell, crudely determine Z and
     594            then compute the composition in formula units
     595            '''
     596            phasedict = self.Phases[phasenam] # pointer to current phase info
     597            General = phasedict['General']
     598            Z = General.get('cellZ',0.0)
     599            cx,ct,cs,cia = General['AtomPtrs']
     600            Atoms = phasedict['Atoms']
     601            fpfx = str(phasedict['pId'])+'::Afrac:'       
     602            cfrac = cx+3
     603            cmult = cs+1
     604            compDict = {} # combines H,D & T
     605            sitemultlist = []
     606            massDict = dict(zip(General['AtomTypes'],General['AtomMass']))
     607            cellmass = 0
     608            for i,at in enumerate(Atoms):
     609                atype = at[ct].strip()
     610                if atype.find('-') != -1: atype = atype.split('-')[0]
     611                if atype.find('+') != -1: atype = atype.split('+')[0]
     612                atype = atype[0].upper()+atype[1:2].lower() # force case conversion
     613                if atype == "D" or atype == "D": atype = "H"
     614                fvar = fpfx+str(i)
     615                fval = self.parmDict.get(fvar,at[cfrac])
     616                mult = at[cmult]
     617                if not massDict.get(at[ct]):
     618                    print('Error: No mass found for atom type '+at[ct])
     619                    print('Will not compute cell contents for phase '+phasenam)
     620                    return
     621                cellmass += massDict[at[ct]]*mult*fval
     622                compDict[atype] = compDict.get(atype,0.0) + mult*fval
     623                if fval == 1: sitemultlist.append(mult)
     624            if len(compDict.keys()) == 0: return # no elements!
     625            if Z < 1: # Z has not been computed or set by user
     626                Z = 1
     627                for i in range(2,min(sitemultlist)+1):
     628                    for m in sitemultlist:
     629                        if m % i != 0:
     630                            break
     631                        else:
     632                            Z = i
     633                General['cellZ'] = Z # save it
     634
     635            # when scattering factors are included in the CIF, this needs to be
     636            # added to the loop here but only in the one-block case.
     637            # For multiblock CIFs, scattering factors go in the histogram
     638            # blocks  (for all atoms in all appropriate phases) - an example?:
     639#loop_
     640#    _atom_type_symbol
     641#    _atom_type_description
     642#    _atom_type_scat_dispersion_real
     643#    _atom_type_scat_dispersion_imag
     644#    _atom_type_scat_source
     645#    'C' 'C' 0.0033 0.0016
     646#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
     647#    'H' 'H' 0.0000 0.0000
     648#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
     649#    'P' 'P' 0.1023 0.0942
     650#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
     651#    'Cl' 'Cl' 0.1484 0.1585
     652#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
     653#    'Cu' 'Cu' 0.3201 1.2651
     654#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
     655
     656            #if oneblock: # add scattering factors for current phase here
     657            WriteCIFitem('\nloop_  _atom_type_symbol _atom_type_number_in_cell')
     658            formula = ''
     659            reload(G2mth)
     660            for elem in HillSortElements(compDict.keys()):
     661                WriteCIFitem('  ' + PutInCol(elem,4) +
     662                             G2mth.ValEsd(compDict[elem],-0.009,True))
     663                if formula: formula += " "
     664                formula += elem
     665                if compDict[elem] == Z: continue
     666                formula += G2mth.ValEsd(compDict[elem]/Z,-0.009,True)
     667            WriteCIFitem( '\n# Note that Z affects _cell_formula_sum and _weight')
     668            WriteCIFitem( '_cell_formula_units_Z',str(Z))
     669            WriteCIFitem( '_chemical_formula_sum',formula)
     670            WriteCIFitem( '_chemical_formula_weight',
     671                          G2mth.ValEsd(cellmass/Z,-0.09,True))
     672
     673        def WriteDistances(phasenam,SymOpList,offsetList,symOpList,G2oprList):
     674            '''Report bond distances and angles for the CIF
     675
     676            Note that _geom_*_symmetry_* fields are values of form
     677            n_klm where n is the symmetry operation in SymOpList (counted
     678            starting with 1) and (k-5, l-5, m-5) are translations to add
     679            to (x,y,z). See
     680            http://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Igeom_angle_site_symmetry_.html
     681
     682            TODO: need a method to select publication flags for distances/angles
     683            '''
     684            phasedict = self.Phases[phasenam] # pointer to current phase info           
     685            Atoms = phasedict['Atoms']
     686            generalData = phasedict['General']
     687            cx,ct,cs,cia = phasedict['General']['AtomPtrs']
     688            cn = ct-1
     689            fpfx = str(phasedict['pId'])+'::Afrac:'       
     690            cfrac = cx+3
     691            DisAglData = {}
     692            DisAglCtls = {}
     693            # create a list of atoms, but skip atoms with zero occupancy
     694            xyz = []
     695            fpfx = str(phasedict['pId'])+'::Afrac:'       
     696            for i,atom in enumerate(Atoms):
     697                if self.parmDict.get(fpfx+str(i),atom[cfrac]) == 0.0: continue
     698                xyz.append([i,]+atom[cn:cn+2]+atom[cx:cx+3])
     699            if 'DisAglCtls' in generalData:
     700                DisAglCtls = generalData['DisAglCtls']
     701            else: # should not happen, since DisAglDialog should be called for all
     702                # phases before getting here
     703                dlg = G2gd.DisAglDialog(self.G2frame,DisAglCtls,generalData)
     704                if dlg.ShowModal() == wx.ID_OK:
     705                    DisAglCtls = dlg.GetData()
     706                    generalData['DisAglCtls'] = DisAglCtls
     707                else:
     708                    dlg.Destroy()
     709                    return
     710                dlg.Destroy()
     711            DisAglData['OrigAtoms'] = xyz
     712            DisAglData['TargAtoms'] = xyz
     713            SymOpList,offsetList,symOpList,G2oprList = G2spc.AllOps(
     714                generalData['SGData'])
     715
     716            xpandSGdata = generalData['SGData'].copy()
     717            xpandSGdata.update({'SGOps':symOpList,
     718                                'SGInv':False,
     719                                'SGLatt':'P',
     720                                'SGCen':np.array([[0, 0, 0]]),})
     721            DisAglData['SGData'] = xpandSGdata
     722
     723            DisAglData['Cell'] = generalData['Cell'][1:] #+ volume
     724            if 'pId' in phasedict:
     725                DisAglData['pId'] = phasedict['pId']
     726                DisAglData['covData'] = self.OverallParms['Covariance']
     727            try:
     728                AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(DisAglCtls,DisAglData)
     729            except KeyError:        # inside DistAngle for missing atom types in DisAglCtls
     730                print('**** ERROR - try again but do "Reset" to fill in missing atom types ****')
     731                   
     732            # loop over interatomic distances for this phase
     733            WriteCIFitem('\n# MOLECULAR GEOMETRY')
     734            WriteCIFitem('loop_' +
     735                         '\n\t_geom_bond_atom_site_label_1' +
     736                         '\n\t_geom_bond_atom_site_label_2' +
     737                         '\n\t_geom_bond_distance' +
     738                         '\n\t_geom_bond_site_symmetry_1' +
     739                         '\n\t_geom_bond_site_symmetry_2' +
     740                         '\n\t_geom_bond_publ_flag')
     741
     742            for i in sorted(AtomLabels.keys()):
     743                Dist = DistArray[i]
     744                for D in Dist:
     745                    line = '  '+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[D[0]],6)
     746                    sig = D[4]
     747                    if sig == 0: sig = -0.00009
     748                    line += PutInCol(G2mth.ValEsd(D[3],sig,True),10)
     749                    line += "  1_555 "
     750                    line += " {:3d}_".format(D[2])
     751                    for d in D[1]:
     752                        line += "{:1d}".format(d+5)
     753                    line += " yes"
     754                    WriteCIFitem(line)
     755
     756            # loop over interatomic angles for this phase
     757            WriteCIFitem('\nloop_' +
     758                         '\n\t_geom_angle_atom_site_label_1' +
     759                         '\n\t_geom_angle_atom_site_label_2' +
     760                         '\n\t_geom_angle_atom_site_label_3' +
     761                         '\n\t_geom_angle' +
     762                         '\n\t_geom_angle_site_symmetry_1' +
     763                         '\n\t_geom_angle_site_symmetry_2' +
     764                         '\n\t_geom_angle_site_symmetry_3' +
     765                         '\n\t_geom_angle_publ_flag')
     766
     767            for i in sorted(AtomLabels.keys()):
     768                Dist = DistArray[i]
     769                for k,j,tup in AngArray[i]:
     770                    Dj = Dist[j]
     771                    Dk = Dist[k]
     772                    line = '  '+PutInCol(AtomLabels[Dj[0]],6)+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[Dk[0]],6)
     773                    sig = tup[1]
     774                    if sig == 0: sig = -0.009
     775                    line += PutInCol(G2mth.ValEsd(tup[0],sig,True),10)
     776                    line += " {:3d}_".format(Dj[2])
     777                    for d in Dj[1]:
     778                        line += "{:1d}".format(d+5)
     779                    line += "  1_555 "
     780                    line += " {:3d}_".format(Dk[2])
     781                    for d in Dk[1]:
     782                        line += "{:1d}".format(d+5)
     783                    line += " yes"
     784                    WriteCIFitem(line)
     785
     786        def WritePhaseInfo(phasenam):
     787            'Write out the phase information for the selected phase'
     788            WriteCIFitem('\n# phase info for '+str(phasenam) + ' follows')
     789            phasedict = self.Phases[phasenam] # pointer to current phase info           
     790            WriteCIFitem('_pd_phase_name', phasenam)
     791            pfx = str(phasedict['pId'])+'::'
     792            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
     793            cellSig = G2stIO.getCellEsd(pfx,
     794                                       phasedict['General']['SGData'],A,
     795                                       self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
     796            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
     797            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
     798            names = ['length_a','length_b','length_c',
     799                     'angle_alpha','angle_beta ','angle_gamma',
     800                     'volume']
     801            prevsig = 0
     802            for lbl,defsig,val,sig in zip(names,defsigL,cellList,cellSig):
     803                if sig:
     804                    txt = G2mth.ValEsd(val,sig)
     805                    prevsig = -sig # use this as the significance for next value
     806                else:
     807                    txt = G2mth.ValEsd(val,min(defsig,prevsig),True)
     808                WriteCIFitem('_cell_'+lbl,txt)
     809                   
     810            WriteCIFitem('_symmetry_cell_setting',
     811                         phasedict['General']['SGData']['SGSys'])
     812
     813            spacegroup = phasedict['General']['SGData']['SpGrp'].strip()
     814            # regularize capitalization and remove trailing H/R
     815            spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
     816            WriteCIFitem('_symmetry_space_group_name_H-M',spacegroup)
     817
     818            # generate symmetry operations including centering and center of symmetry
     819            SymOpList,offsetList,symOpList,G2oprList = G2spc.AllOps(
     820                phasedict['General']['SGData'])
     821            WriteCIFitem('loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
     822            for i,op in enumerate(SymOpList,start=1):
     823                WriteCIFitem('   {:3d}  {:}'.format(i,op.lower()))
     824
     825            # loop over histogram(s) used in this phase
     826            if not oneblock and not self.quickmode:
     827                # report pointers to the histograms used in this phase
     828                histlist = []
     829                for hist in self.Phases[phasenam]['Histograms']:
     830                    if self.Phases[phasenam]['Histograms'][hist]['Use']:
     831                        if phasebyhistDict.get(hist):
     832                            phasebyhistDict[hist].append(phasenam)
     833                        else:
     834                            phasebyhistDict[hist] = [phasenam,]
     835                        blockid = datablockidDict.get(hist)
     836                        if not blockid:
     837                            print("Internal error: no block for data. Phase "+str(
     838                                phasenam)+" histogram "+str(hist))
     839                            histlist = []
     840                            break
     841                        histlist.append(blockid)
     842
     843                if len(histlist) == 0:
     844                    WriteCIFitem('# Note: phase has no associated data')
     845
     846            # report atom params
     847            if phasedict['General']['Type'] == 'nuclear':        #this needs macromolecular variant, etc!
     848                WriteAtomsNuclear(phasenam)
     849            else:
     850                raise Exception,"no export for mm coordinates implemented"
     851            # report cell contents
     852            WriteComposition(phasenam)
     853            if not self.quickmode:      # report distances and angles
     854                WriteDistances(phasenam,SymOpList,offsetList,symOpList,G2oprList)
     855               
     856        def Yfmt(ndec,val):
     857            'Format intensity values'
     858            out = ("{:."+str(ndec)+"f}").format(val)
     859            out = out.rstrip('0')  # strip zeros to right of decimal
     860            return out.rstrip('.')  # and decimal place when not needed
     861           
     862        def WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,nRefSets=1):
     863            'Write reflection statistics'
     864            WriteCIFitem('_reflns_number_total', str(refcount))
     865            if hklmin is not None and nRefSets == 1: # hkl range has no meaning with multiple phases
     866                WriteCIFitem('_reflns_limit_h_min', str(int(hklmin[0])))
     867                WriteCIFitem('_reflns_limit_h_max', str(int(hklmax[0])))
     868                WriteCIFitem('_reflns_limit_k_min', str(int(hklmin[1])))
     869                WriteCIFitem('_reflns_limit_k_max', str(int(hklmax[1])))
     870                WriteCIFitem('_reflns_limit_l_min', str(int(hklmin[2])))
     871                WriteCIFitem('_reflns_limit_l_max', str(int(hklmax[2])))
     872            if hklmin is not None:
     873                WriteCIFitem('_reflns_d_resolution_low  ', G2mth.ValEsd(dmax,-0.009))
     874                WriteCIFitem('_reflns_d_resolution_high ', G2mth.ValEsd(dmin,-0.009))
     875
     876        def WritePowderData(histlbl):
     877            'Write out the selected powder diffraction histogram info'
     878            histblk = self.Histograms[histlbl]
     879            inst = histblk['Instrument Parameters'][0]
     880            hId = histblk['hId']
     881            pfx = ':' + str(hId) + ':'
     882           
     883            if 'Lam1' in inst:
     884                ratio = self.parmDict.get('I(L2)/I(L1)',inst['I(L2)/I(L1)'][1])
     885                sratio = self.sigDict.get('I(L2)/I(L1)',-0.0009)
     886                lam1 = self.parmDict.get('Lam1',inst['Lam1'][1])
     887                slam1 = self.sigDict.get('Lam1',-0.00009)
     888                lam2 = self.parmDict.get('Lam2',inst['Lam2'][1])
     889                slam2 = self.sigDict.get('Lam2',-0.00009)
     890                # always assume Ka1 & Ka2 if two wavelengths are present
     891                WriteCIFitem('_diffrn_radiation_type','K\\a~1,2~')
     892                WriteCIFitem('loop_' +
     893                             '\n\t_diffrn_radiation_wavelength' +
     894                             '\n\t_diffrn_radiation_wavelength_wt' +
     895                             '\n\t_diffrn_radiation_wavelength_id')
     896                WriteCIFitem('  ' + PutInCol(G2mth.ValEsd(lam1,slam1),15)+
     897                             PutInCol('1.0',15) +
     898                             PutInCol('1',5))
     899                WriteCIFitem('  ' + PutInCol(G2mth.ValEsd(lam2,slam2),15)+
     900                             PutInCol(G2mth.ValEsd(ratio,sratio),15)+
     901                             PutInCol('2',5))               
     902            else:
     903                lam1 = self.parmDict.get('Lam',inst['Lam'][1])
     904                slam1 = self.sigDict.get('Lam',-0.00009)
     905                WriteCIFitem('_diffrn_radiation_wavelength',G2mth.ValEsd(lam1,slam1))
     906
     907            if not oneblock:
     908                if not phasebyhistDict.get(histlbl):
     909                    WriteCIFitem('\n# No phases associated with this data set')
     910                else:
     911                    WriteCIFitem('\n# PHASE TABLE')
     912                    WriteCIFitem('loop_' +
     913                                 '\n\t_pd_phase_id' +
     914                                 '\n\t_pd_phase_block_id' +
     915                                 '\n\t_pd_phase_mass_%')
     916                    wtFrSum = 0.
     917                    for phasenam in phasebyhistDict.get(histlbl):
     918                        hapData = self.Phases[phasenam]['Histograms'][histlbl]
     919                        General = self.Phases[phasenam]['General']
     920                        wtFrSum += hapData['Scale'][0]*General['Mass']
     921
     922                    for phasenam in phasebyhistDict.get(histlbl):
     923                        hapData = self.Phases[phasenam]['Histograms'][histlbl]
     924                        General = self.Phases[phasenam]['General']
     925                        wtFr = hapData['Scale'][0]*General['Mass']/wtFrSum
     926                        pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
     927                        if pfx+'Scale' in self.sigDict:
     928                            sig = self.sigDict[pfx+'Scale']*wtFr/hapData['Scale'][0]
     929                        else:
     930                            sig = -0.0001
     931                        WriteCIFitem(
     932                            '  '+
     933                            str(self.Phases[phasenam]['pId']) +
     934                            '  '+datablockidDict[phasenam]+
     935                            '  '+G2mth.ValEsd(wtFr,sig)
     936                            )
     937                    WriteCIFitem('loop_' +
     938                                 '\n\t_gsas_proc_phase_R_F_factor' +
     939                                 '\n\t_gsas_proc_phase_R_Fsqd_factor' +
     940                                 '\n\t_gsas_proc_phase_id' +
     941                                 '\n\t_gsas_proc_phase_block_id')
     942                    for phasenam in phasebyhistDict.get(histlbl):
     943                        pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
     944                        WriteCIFitem(
     945                            '  '+
     946                            '  '+G2mth.ValEsd(histblk[pfx+'Rf']/100.,-.00009) +
     947                            '  '+G2mth.ValEsd(histblk[pfx+'Rf^2']/100.,-.00009)+
     948                            '  '+str(self.Phases[phasenam]['pId'])+
     949                            '  '+datablockidDict[phasenam]
     950                            )
     951            else:
     952                # single phase in this histogram
     953                pfx = '0:'+str(hId)+':'
     954                WriteCIFitem('_refine_ls_R_F_factor      ','%.5f'%(histblk[pfx+'Rf']/100.))
     955                WriteCIFitem('_refine_ls_R_Fsqd_factor   ','%.5f'%(histblk[pfx+'Rf^2']/100.))
     956               
     957            WriteCIFitem('_pd_proc_ls_prof_R_factor   ','%.5f'%(histblk['R']/100.))
     958            WriteCIFitem('_pd_proc_ls_prof_wR_factor  ','%.5f'%(histblk['wR']/100.))
     959            WriteCIFitem('_gsas_proc_ls_prof_R_B_factor ','%.5f'%(histblk['Rb']/100.))
     960            WriteCIFitem('_gsas_proc_ls_prof_wR_B_factor','%.5f'%(histblk['wRb']/100.))
     961            WriteCIFitem('_pd_proc_ls_prof_wR_expected','%.5f'%(histblk['wRmin']/100.))
     962
     963            if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
     964                WriteCIFitem('_diffrn_radiation_probe','x-ray')
     965                pola = histblk['Instrument Parameters'][0].get('Polariz.')
     966                if pola:
     967                    pfx = ':' + str(hId) + ':'
     968                    sig = self.sigDict.get(pfx+'Polariz.',-0.0009)
     969                    txt = G2mth.ValEsd(pola[1],sig)
     970                    WriteCIFitem('_diffrn_radiation_polarisn_ratio',txt)
     971            elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
     972                WriteCIFitem('_diffrn_radiation_probe','neutron')
     973            # TOF (note that this may not be defined)
     974            #if histblk['Instrument Parameters'][0]['Type'][1][2] == 'T':
     975            #    WriteCIFitem('_pd_meas_2theta_fixed',text)
     976           
     977
     978            # TODO: this will need help from Bob
     979            #if not oneblock:
     980            #WriteCIFitem('\n# SCATTERING FACTOR INFO')
     981            #WriteCIFitem('loop_  _atom_type_symbol')
     982            #if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
     983            #    WriteCIFitem('      _atom_type_scat_dispersion_real')
     984            #    WriteCIFitem('      _atom_type_scat_dispersion_imag')
     985            #    for lbl in ('a1','a2','a3', 'a4', 'b1', 'b2', 'b3', 'b4', 'c'):
     986            #        WriteCIFitem('      _atom_type_scat_Cromer_Mann_'+lbl)
     987            #elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
     988            #    WriteCIFitem('      _atom_type_scat_length_neutron')
     989            #WriteCIFitem('      _atom_type_scat_source')
     990
     991            WriteCIFitem('_pd_proc_ls_background_function',FormatBackground(histblk['Background'],histblk['hId']))
     992
     993            # TODO: this will need help from Bob
     994            #WriteCIFitem('_exptl_absorpt_process_details','?')
     995            #WriteCIFitem('_exptl_absorpt_correction_T_min','?')
     996            #WriteCIFitem('_exptl_absorpt_correction_T_max','?')
     997            #C extinction
     998            #WRITE(IUCIF,'(A)') '# Extinction correction'
     999            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_min',TEXT(1:10))
     1000            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_max',TEXT(11:20))
     1001
     1002            if not oneblock:                 # instrumental profile terms go here
     1003                WriteCIFitem('_pd_proc_ls_profile_function',
     1004                    FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
     1005
     1006            #refprx = '_refln.' # mm
     1007            refprx = '_refln_' # normal
     1008            WriteCIFitem('\n# STRUCTURE FACTOR TABLE')           
     1009            # compute maximum intensity reflection
     1010            Imax = 0
     1011            for phasenam in histblk['Reflection Lists']:
     1012                scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
     1013                Icorr = np.array([refl[13] for refl in histblk['Reflection Lists'][phasenam]])[0]
     1014                FO2 = np.array([refl[8] for refl in histblk['Reflection Lists'][phasenam]])
     1015                I100 = scale*FO2*Icorr
     1016                Imax = max(Imax,max(I100))
     1017
     1018            WriteCIFitem('loop_')
     1019            if len(histblk['Reflection Lists'].keys()) > 1:
     1020                WriteCIFitem('\t_pd_refln_phase_id')
     1021            WriteCIFitem('\t' + refprx + 'index_h' +
     1022                         '\n\t' + refprx + 'index_k' +
     1023                         '\n\t' + refprx + 'index_l' +
     1024                         '\n\t' + refprx + 'F_squared_meas' +
     1025                         '\n\t' + refprx + 'F_squared_calc' +
     1026                         '\n\t' + refprx + 'phase_calc' +
     1027                         '\n\t_pd_refln_d_spacing')
     1028            if Imax > 0:
     1029                WriteCIFitem('\t_gsas_i100_meas')
     1030
     1031            refcount = 0
     1032            hklmin = None
     1033            hklmax = None
     1034            dmax = None
     1035            dmin = None
     1036            for phasenam in histblk['Reflection Lists']:
     1037                scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
     1038                phaseid = self.Phases[phasenam]['pId']
     1039                refcount += len(histblk['Reflection Lists'][phasenam])
     1040                for ref in histblk['Reflection Lists'][phasenam]:
     1041                    if DEBUG:
     1042                        print('DEBUG: skipping reflection list')
     1043                        break
     1044                    if hklmin is None:
     1045                        hklmin = ref[0:3]
     1046                        hklmax = ref[0:3]
     1047                        dmax = dmin = ref[4]
     1048                    if len(histblk['Reflection Lists'].keys()) > 1:
     1049                        s = PutInCol(phaseid,2)
     1050                    else:
     1051                        s = ""
     1052                    for i,hkl in enumerate(ref[0:3]):
     1053                        hklmax[i] = max(hkl,hklmax[i])
     1054                        hklmin[i] = min(hkl,hklmin[i])
     1055                        s += PutInCol(int(hkl),4)
     1056                    for I in ref[8:10]:
     1057                        s += PutInCol(G2mth.ValEsd(I,-0.0009),10)
     1058                    s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
     1059                    dmax = max(dmax,ref[4])
     1060                    dmin = min(dmin,ref[4])
     1061                    s += PutInCol(G2mth.ValEsd(ref[4],-0.009),8)
     1062                    if Imax > 0:
     1063                        I100 = 100.*scale*ref[8]*ref[13]/Imax
     1064                        s += PutInCol(G2mth.ValEsd(I100,-0.09),6)
     1065                    WriteCIFitem("  "+s)
     1066
     1067            WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,len(histblk['Reflection Lists']))
     1068            WriteCIFitem('\n# POWDER DATA TABLE')
     1069            # is data fixed step? If the step varies by <0.01% treat as fixed step
     1070            steps = histblk['Data'][0][1:] - histblk['Data'][0][:-1]
     1071            if abs(max(steps)-min(steps)) > abs(max(steps))/10000.:
     1072                fixedstep = False
     1073            else:
     1074                fixedstep = True
     1075
     1076            if fixedstep: # and not TOF
     1077                WriteCIFitem('_pd_meas_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0],-0.00009))
     1078                WriteCIFitem('_pd_meas_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1],-0.00009))
     1079                WriteCIFitem('_pd_meas_2theta_range_inc', G2mth.ValEsd(steps.sum()/len(steps),-0.00009))
     1080                # zero correct, if defined
     1081                zero = None
     1082                zerolst = histblk['Instrument Parameters'][0].get('Zero')
     1083                if zerolst: zero = zerolst[1]
     1084                zero = self.parmDict.get('Zero',zero)
     1085                if zero:
     1086                    WriteCIFitem('_pd_proc_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0]-zero,-0.00009))
     1087                    WriteCIFitem('_pd_proc_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1]-zero,-0.00009))
     1088                    WriteCIFitem('_pd_proc_2theta_range_inc', G2mth.ValEsd(steps.sum()/len(steps),-0.00009))
     1089               
     1090            if zero:
     1091                WriteCIFitem('_pd_proc_number_of_points', str(len(histblk['Data'][0])))
     1092            else:
     1093                WriteCIFitem('_pd_meas_number_of_points', str(len(histblk['Data'][0])))
     1094            WriteCIFitem('\nloop_')
     1095            #            WriteCIFitem('\t_pd_proc_d_spacing') # need easy way to get this
     1096            if not fixedstep:
     1097                if zero:
     1098                    WriteCIFitem('\t_pd_proc_2theta_corrected')
     1099                else:
     1100                    WriteCIFitem('\t_pd_meas_2theta_scan')
     1101            # at least for now, always report weights.
     1102            #if countsdata:
     1103            #    WriteCIFitem('\t_pd_meas_counts_total')
     1104            #else:
     1105            WriteCIFitem('\t_pd_meas_intensity_total')
     1106            WriteCIFitem('\t_pd_calc_intensity_total')
     1107            WriteCIFitem('\t_pd_proc_intensity_bkg_calc')
     1108            WriteCIFitem('\t_pd_proc_ls_weight')
     1109            maxY = max(histblk['Data'][1].max(),histblk['Data'][3].max())
     1110            if maxY < 0: maxY *= -10 # this should never happen, but...
     1111            ndec = max(0,10-int(np.log10(maxY))-1) # 10 sig figs should be enough
     1112            maxSU = histblk['Data'][2].max()
     1113            if maxSU < 0: maxSU *= -1 # this should never happen, but...
     1114            ndecSU = max(0,8-int(np.log10(maxSU))-1) # 8 sig figs should be enough
     1115            lowlim,highlim = histblk['Limits'][1]
     1116
     1117            if DEBUG:
     1118                print('DEBUG: skipping profile list')
     1119            else:   
     1120                for x,yobs,yw,ycalc,ybkg in zip(histblk['Data'][0],
     1121                                                histblk['Data'][1],
     1122                                                histblk['Data'][2],
     1123                                                histblk['Data'][3],
     1124                                                histblk['Data'][4]):
     1125                    if lowlim <= x <= highlim:
     1126                        pass
     1127                    else:
     1128                        yw = 0.0 # show the point is not in use
     1129   
     1130                    if fixedstep:
     1131                        s = ""
     1132                    else:
     1133                        s = PutInCol(G2mth.ValEsd(x-zero,-0.00009),10)
     1134                    s += PutInCol(Yfmt(ndec,yobs),12)
     1135                    s += PutInCol(Yfmt(ndec,ycalc),12)
     1136                    s += PutInCol(Yfmt(ndec,ybkg),11)
     1137                    s += PutInCol(Yfmt(ndecSU,yw),9)
     1138                    WriteCIFitem("  "+s)
     1139
     1140        def WriteSingleXtalData(histlbl):
     1141            'Write out the selected single crystal histogram info'
     1142            histblk = self.Histograms[histlbl]
     1143            #refprx = '_refln.' # mm
     1144            refprx = '_refln_' # normal
     1145
     1146            WriteCIFitem('\n# STRUCTURE FACTOR TABLE')           
     1147            WriteCIFitem('loop_' +
     1148                         '\n\t' + refprx + 'index_h' +
     1149                         '\n\t' + refprx + 'index_k' +
     1150                         '\n\t' + refprx + 'index_l' +
     1151                         '\n\t' + refprx + 'F_squared_meas' +
     1152                         '\n\t' + refprx + 'F_squared_sigma' +
     1153                         '\n\t' + refprx + 'F_squared_calc' +
     1154                         '\n\t' + refprx + 'phase_calc'
     1155                         )
     1156
     1157            hklmin = None
     1158            hklmax = None
     1159            dmax = None
     1160            dmin = None
     1161            refcount = len(histblk['Data'])
     1162            for ref in histblk['Data']:
     1163                s = "  "
     1164                if hklmin is None:
     1165                    hklmin = ref[0:3]
     1166                    hklmax = ref[0:3]
     1167                    dmax = dmin = ref[4]
     1168                for i,hkl in enumerate(ref[0:3]):
     1169                    hklmax[i] = max(hkl,hklmax[i])
     1170                    hklmin[i] = min(hkl,hklmin[i])
     1171                    s += PutInCol(int(hkl),4)
     1172                import sys
     1173                if ref[5] == 0.0:
     1174                    s += PutInCol(G2mth.ValEsd(ref[8],0),12)
     1175                    s += PutInCol('.',10)
     1176                    s += PutInCol(G2mth.ValEsd(ref[9],0),12)
     1177                    s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
     1178                else:
     1179                    sig = ref[6] * ref[8] / ref[5]
     1180                    s += PutInCol(G2mth.ValEsd(ref[8],-abs(sig/10)),12)
     1181                    s += PutInCol(G2mth.ValEsd(sig,-abs(sig)/10.),10)
     1182                    s += PutInCol(G2mth.ValEsd(ref[9],-abs(sig/10)),12)
     1183                s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
     1184                dmax = max(dmax,ref[4])
     1185                dmin = min(dmin,ref[4])
     1186                WriteCIFitem(s)
     1187            WriteReflStat(refcount,hklmin,hklmax,dmin,dmax)
     1188            hId = histblk['hId']
     1189            pfx = '0:'+str(hId)+':'
     1190            WriteCIFitem('_reflns_wR_factor_obs    ','%.4f'%(histblk['wR']/100.))
     1191            WriteCIFitem('_reflns_R_F_factor_obs   ','%.4f'%(histblk[pfx+'Rf']/100.))
     1192            WriteCIFitem('_reflns_R_Fsqd_factor_obs','%.4f'%(histblk[pfx+'Rf^2']/100.))
     1193        def EditAuthor(event=None):
     1194            'dialog to edit the CIF author info'
     1195            'Edit the CIF author name'
     1196            dlg = G2gd.SingleStringDialog(self.G2frame,
     1197                                          'Get CIF Author',
     1198                                          'Provide CIF Author name (Last, First)',
     1199                                          value=self.author)
     1200            if not dlg.Show():
     1201                dlg.Destroy()
     1202                return False  # cancel was pressed
     1203            self.author = dlg.GetValue()
     1204            dlg.Destroy()
     1205            try:
     1206                self.OverallParms['Controls']["Author"] = self.author # save for future
     1207            except KeyError:
     1208                pass
     1209            return True
     1210        def EditInstNames(event=None):
     1211            'Provide a dialog for editing instrument names'
     1212            dictlist = []
     1213            keylist = []
     1214            lbllist = []
     1215            for hist in self.Histograms:
     1216                if hist.startswith("PWDR"):
     1217                    key2 = "Sample Parameters"
     1218                    d = self.Histograms[hist][key2]
     1219                elif hist.startswith("HKLF"):
     1220                    key2 = "Instrument Parameters"
     1221                    d = self.Histograms[hist][key2][0]
     1222                   
     1223                lbllist.append(hist)
     1224                dictlist.append(d)
     1225                keylist.append('InstrName')
     1226                instrname = d.get('InstrName')
     1227                if instrname is None:
     1228                    d['InstrName'] = ''
     1229            return G2gd.CallScrolledMultiEditor(
     1230                self.G2frame,dictlist,keylist,
     1231                prelbl=range(1,len(dictlist)+1),
     1232                postlbl=lbllist,
     1233                title='Instrument names',
     1234                header="Edit instrument names. Note that a non-blank\nname is required for all histograms",
     1235                CopyButton=True)
     1236           
     1237        def EditRanges(event):
     1238            '''Edit the bond distance/angle search range; phase is determined from
     1239            a pointer placed in the button object (.phasedict) that references the
     1240            phase dictionary
     1241            '''
     1242            but = event.GetEventObject()
     1243            phasedict = but.phasedict
     1244            dlg = G2gd.DisAglDialog(self.G2frame,{},phasedict['General'])
     1245            if dlg.ShowModal() == wx.ID_OK:
     1246                phasedict['General']['DisAglCtls'] = dlg.GetData()
     1247            dlg.Destroy()
     1248           
     1249        def EditCIFDefaults():
     1250            '''Fills the CIF Defaults window with controls for editing various CIF export
     1251            parameters (mostly related to templates).
     1252            '''
     1253            import wx.lib.scrolledpanel as wxscroll
     1254            self.cifdefs.DestroyChildren()
     1255            self.cifdefs.SetTitle('Edit CIF settings')
     1256            vbox = wx.BoxSizer(wx.VERTICAL)
     1257            but = wx.Button(self.cifdefs, wx.ID_ANY,'Edit CIF Author')
     1258            but.Bind(wx.EVT_BUTTON,EditAuthor)
     1259            vbox.Add(but,0,wx.ALIGN_CENTER,3)
     1260            but = wx.Button(self.cifdefs, wx.ID_ANY,'Edit Instrument Name(s)')
     1261            but.Bind(wx.EVT_BUTTON,EditInstNames)
     1262            vbox.Add(but,0,wx.ALIGN_CENTER,3)
     1263            cpnl = wxscroll.ScrolledPanel(self.cifdefs,size=(300,300))
     1264            cbox = wx.BoxSizer(wx.VERTICAL)
     1265            G2gd.HorizontalLine(cbox,cpnl)         
     1266            cbox.Add(
     1267                CIFtemplateSelect(self.cifdefs,
     1268                                  cpnl,'publ',self.OverallParms['Controls'],
     1269                                  EditCIFDefaults,
     1270                                  "Publication (overall) template",
     1271                                  ),
     1272                0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
     1273            for phasenam in sorted(self.Phases.keys()):
     1274                G2gd.HorizontalLine(cbox,cpnl)         
     1275                title = 'Phase '+phasenam
     1276                phasedict = self.Phases[phasenam] # pointer to current phase info           
     1277                cbox.Add(
     1278                    CIFtemplateSelect(self.cifdefs,
     1279                                      cpnl,'phase',phasedict['General'],
     1280                                      EditCIFDefaults,
     1281                                      title,
     1282                                      phasenam),
     1283                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
     1284                cpnl.SetSizer(cbox)
     1285                but = wx.Button(cpnl, wx.ID_ANY,'Edit distance/angle ranges')
     1286                #cbox.Add(but,0,wx.ALIGN_CENTER,3)
     1287                cbox.Add((-1,2))
     1288                cbox.Add(but,0,wx.ALIGN_LEFT,0)
     1289                but.phasedict = self.Phases[phasenam]  # set a pointer to current phase info     
     1290                but.Bind(wx.EVT_BUTTON,EditRanges)     # phase bond/angle ranges
     1291            for i in sorted(self.powderDict.keys()):
     1292                G2gd.HorizontalLine(cbox,cpnl)         
     1293                hist = self.powderDict[i]
     1294                histblk = self.Histograms[hist]
     1295                title = 'Powder dataset '+hist[5:]
     1296                cbox.Add(
     1297                    CIFtemplateSelect(self.cifdefs,
     1298                                      cpnl,'powder',histblk["Sample Parameters"],
     1299                                      EditCIFDefaults,
     1300                                      title,
     1301                                      histblk["Sample Parameters"]['InstrName']),
     1302                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
     1303                cpnl.SetSizer(cbox)
     1304            for i in sorted(self.xtalDict.keys()):
     1305                G2gd.HorizontalLine(cbox,cpnl)         
     1306                hist = self.xtalDict[i]
     1307                histblk = self.Histograms[hist]
     1308                title = 'Single Xtal dataset '+hist[5:]
     1309                cbox.Add(
     1310                    CIFtemplateSelect(self.cifdefs,
     1311                                      cpnl,'single',histblk["Instrument Parameters"][0],
     1312                                      EditCIFDefaults,
     1313                                      title,
     1314                                      histblk["Instrument Parameters"][0]['InstrName']),
     1315                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
     1316                cpnl.SetSizer(cbox)
     1317
     1318            cpnl.SetAutoLayout(1)
     1319            cpnl.SetupScrolling()
     1320            #cpnl.Bind(rw.EVT_RW_LAYOUT_NEEDED, self.OnLayoutNeeded) # needed if sizes change
     1321            cpnl.Layout()
     1322
     1323            vbox.Add(cpnl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 0)
     1324            btnsizer = wx.StdDialogButtonSizer()
     1325            btn = wx.Button(self.cifdefs, wx.ID_OK, "Create CIF")
     1326            btn.SetDefault()
     1327            btnsizer.AddButton(btn)
     1328            btn = wx.Button(self.cifdefs, wx.ID_CANCEL)
     1329            btnsizer.AddButton(btn)
     1330            btnsizer.Realize()
     1331            vbox.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
     1332            self.cifdefs.SetSizer(vbox)
     1333            vbox.Fit(self.cifdefs)
     1334            self.cifdefs.Layout()
     1335
     1336# ===== end of functions for export method =======================================
     1337#=================================================================================
     1338
     1339        # the export process starts here
     1340        # load all of the tree into a set of dicts
     1341        self.loadTree()
     1342        # create a dict with refined values and their uncertainties
     1343        self.loadParmDict()
     1344
     1345        # Someday: get restraint & constraint info
     1346        #restraintDict = self.OverallParms.get('Restraints',{})
     1347        #for i in  self.OverallParms['Constraints']:
     1348        #    print i
     1349        #    for j in self.OverallParms['Constraints'][i]:
     1350        #        print j
     1351
     1352        self.CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
     1353        # index powder and single crystal histograms
     1354        self.powderDict = {}
     1355        self.xtalDict = {}
     1356        for hist in self.Histograms:
     1357            i = self.Histograms[hist]['hId']
     1358            if hist.startswith("PWDR"):
     1359                self.powderDict[i] = hist
     1360            elif hist.startswith("HKLF"):
     1361                self.xtalDict[i] = hist
     1362        # is there anything to export?
     1363        if len(self.Phases) == len(self.powderDict) == len(self.xtalDict) == 0:
     1364           self.G2frame.ErrorDialog(
     1365               'Empty project',
     1366               'Project does not contain interconnected data & phase(s)')
     1367           return
     1368        # get the project file name
     1369        self.CIFname = os.path.splitext(
     1370            os.path.split(self.G2frame.GSASprojectfile)[1]
     1371            )[0]
     1372        self.CIFname = self.CIFname.replace(' ','')
     1373        if not self.CIFname: # none defined & needed, save as GPX to get one
     1374            self.G2frame.OnFileSaveas(None)
     1375            if not self.G2frame.GSASprojectfile: return
     1376            self.CIFname = os.path.splitext(
     1377                os.path.split(self.G2frame.GSASprojectfile)[1]
     1378                )[0]
     1379            self.CIFname = self.CIFname.replace(' ','')
     1380        # test for quick CIF mode or no data
     1381        self.quickmode = False
     1382        phasenam = phasenum = None # include all phases
     1383        if mode != "full" or len(self.powderDict) + len(self.xtalDict) == 0:
     1384            self.quickmode = True
     1385            oneblock = True
     1386            if len(self.Phases) == 0:
     1387                self.G2frame.ErrorDialog(
     1388                    'No phase present',
     1389                    'Cannot create a coordinates CIF with no phases')
     1390                return
     1391            elif len(self.Phases) > 1: # quick mode: choose one phase
     1392                choices = sorted(self.Phases.keys())
     1393                phasenum = G2gd.ItemSelector(choices,self.G2frame)
     1394                if phasenum is None: return
     1395                phasenam = choices[phasenum]
     1396        # will this require a multiblock CIF?
     1397        elif len(self.Phases) > 1:
     1398            oneblock = False
     1399        elif len(self.powderDict) + len(self.xtalDict) > 1:
     1400            oneblock = False
     1401        else: # one phase, one dataset, Full CIF
     1402            oneblock = True
     1403
     1404        # make sure needed infomation is present
     1405        # get CIF author name -- required for full CIFs
     1406        try:
     1407            self.author = self.OverallParms['Controls'].get("Author",'').strip()
     1408        except KeyError:
     1409            pass
     1410        while not (self.author or self.quickmode):
     1411            if not EditAuthor(): return
     1412        self.shortauthorname = self.author.replace(',','').replace(' ','')[:20]
     1413
     1414        # check there is an instrument name for every histogram
     1415        if not self.quickmode:
     1416            invalid = 0
     1417            key3 = 'InstrName'
     1418            for hist in self.Histograms:
     1419                if hist.startswith("PWDR"):
     1420                    key2 = "Sample Parameters"
     1421                    d = self.Histograms[hist][key2]
     1422                elif hist.startswith("HKLF"):
     1423                    key2 = "Instrument Parameters"
     1424                    d = self.Histograms[hist][key2][0]                   
     1425                instrname = d.get(key3)
     1426                if instrname is None:
     1427                    d[key3] = ''
     1428                    invalid += 1
     1429                elif instrname.strip() == '':
     1430                    invalid += 1
     1431            if invalid:
     1432                msg = ""
     1433                if invalid > 3: msg = (
     1434                    "\n\nNote: it may be faster to set the name for\n"
     1435                    "one histogram for each instrument and use the\n"
     1436                    "File/Copy option to duplicate the name"
     1437                    )
     1438                if not EditInstNames(): return
     1439        # check for a distance-angle range search range for each phase
     1440        if not self.quickmode:
     1441            for phasenam in sorted(self.Phases.keys()):
     1442                #i = self.Phases[phasenam]['pId']
     1443                phasedict = self.Phases[phasenam] # pointer to current phase info           
     1444                if 'DisAglCtls' not in phasedict['General']:
     1445                    dlg = G2gd.DisAglDialog(self.G2frame,{},phasedict['General'])
     1446                    if dlg.ShowModal() == wx.ID_OK:
     1447                        phasedict['General']['DisAglCtls'] = dlg.GetData()
     1448                    else:
     1449                        dlg.Destroy()
     1450                        return
     1451                    dlg.Destroy()
     1452
     1453        if oneblock and not self.quickmode:
     1454            # select a dataset to use (there should only be one set in one block,
     1455            # but take whatever comes 1st)
     1456            for hist in self.Histograms:
     1457                histblk = self.Histograms[hist]
     1458                if hist.startswith("PWDR"):
     1459                    instnam = histblk["Sample Parameters"]['InstrName']
     1460                    break # ignore all but 1st data histogram
     1461                elif hist.startswith("HKLF"):
     1462                    instnam = histblk["Instrument Parameters"][0]['InstrName']
     1463                    break # ignore all but 1st data histogram
     1464        if self.quickmode:
     1465            fil = self.askSaveFile()
     1466        else:
     1467            fil = self.defSaveFile()
     1468        if not fil: return
     1469        if not self.quickmode: # give the user a chance to edit all defaults
     1470            self.cifdefs = wx.Dialog(
     1471                self.G2frame,style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
     1472            EditCIFDefaults()
     1473            val = self.cifdefs.ShowModal()
     1474            self.cifdefs.Destroy()
     1475            if val != wx.ID_OK:
     1476                return
     1477        #======================================================================
     1478        # Start writing the CIF - single block
     1479        #======================================================================
     1480        print('Writing CIF output to file '+fil+"...")
     1481        openCIF(fil)
     1482        if oneblock:
     1483            WriteCIFitem('data_'+self.CIFname)
     1484            if phasenam is None: # if not already selected, select the first phase (should be one)
     1485                phasenam = self.Phases.keys()[0]
     1486            #print 'phasenam',phasenam
     1487            phaseblk = self.Phases[phasenam] # pointer to current phase info
     1488            if not self.quickmode:
     1489                instnam = instnam.replace(' ','')
     1490                WriteCIFitem('_pd_block_id',
     1491                             str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
     1492                             str(self.shortauthorname) + "|" + instnam)
     1493                WriteAudit()
     1494                writeCIFtemplate(self.OverallParms['Controls'],'publ') # overall (publication) template
     1495                WriteOverall()
     1496                writeCIFtemplate(self.Phases[phasenam]['General'],'phase',phasenam) # write phase template
     1497            # report the phase info
     1498            WritePhaseInfo(phasenam)
     1499            if hist.startswith("PWDR") and not self.quickmode:
     1500                # preferred orientation
     1501                SH = FormatSH(phasenam)
     1502                MD = FormatHAPpo(phasenam)
     1503                if SH and MD:
     1504                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
     1505                elif SH or MD:
     1506                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + MD)
     1507                else:
     1508                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', 'none')
     1509                    # report profile, since one-block: include both histogram and phase info
     1510                WriteCIFitem('_pd_proc_ls_profile_function',
     1511                    FormatInstProfile(histblk["Instrument Parameters"],histblk['hId'])
     1512                    +'\n'+FormatPhaseProfile(phasenam))
     1513                histblk = self.Histograms[hist]["Sample Parameters"]
     1514                writeCIFtemplate(histblk,'powder',histblk['InstrName']) # write powder template
     1515                WritePowderData(hist)
     1516            elif hist.startswith("HKLF") and not self.quickmode:
     1517                histprm = self.Histograms[hist]["Instrument Parameters"][0]
     1518                writeCIFtemplate(histprm,'single',histprm['InstrName']) # single crystal template
     1519                WriteSingleXtalData(hist)
     1520        else:
     1521        #======================================================================
     1522        # Start writing the CIF - multiblock
     1523        #======================================================================
     1524            # publication info
     1525            WriteCIFitem('\ndata_'+self.CIFname+'_publ')
     1526            WriteAudit()
     1527            WriteCIFitem('_pd_block_id',
     1528                         str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
     1529                         str(self.shortauthorname) + "|Overall")
     1530            writeCIFtemplate(self.OverallParms['Controls'],'publ') #insert the publication template
     1531            # ``template_publ.cif`` or a modified version
     1532            # overall info
     1533            WriteCIFitem('data_'+str(self.CIFname)+'_overall')
     1534            WriteOverall()
     1535            #============================================================
     1536            WriteCIFitem('# POINTERS TO PHASE AND HISTOGRAM BLOCKS')
     1537            datablockidDict = {} # save block names here -- N.B. check for conflicts between phase & hist names (unlikely!)
     1538            # loop over phase blocks
     1539            if len(self.Phases) > 1:
     1540                loopprefix = ''
     1541                WriteCIFitem('loop_   _pd_phase_block_id')
     1542            else:
     1543                loopprefix = '_pd_phase_block_id'
     1544           
     1545            for phasenam in sorted(self.Phases.keys()):
     1546                i = self.Phases[phasenam]['pId']
     1547                datablockidDict[phasenam] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
     1548                             'phase_'+ str(i) + '|' + str(self.shortauthorname))
     1549                WriteCIFitem(loopprefix,datablockidDict[phasenam])
     1550            # loop over data blocks
     1551            if len(self.powderDict) + len(self.xtalDict) > 1:
     1552                loopprefix = ''
     1553                WriteCIFitem('loop_   _pd_block_diffractogram_id')
     1554            else:
     1555                loopprefix = '_pd_block_diffractogram_id'
     1556            for i in sorted(self.powderDict.keys()):
     1557                hist = self.powderDict[i]
     1558                histblk = self.Histograms[hist]
     1559                instnam = histblk["Sample Parameters"]['InstrName']
     1560                instnam = instnam.replace(' ','')
     1561                i = histblk['hId']
     1562                datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
     1563                                         str(self.shortauthorname) + "|" +
     1564                                         instnam + "_hist_"+str(i))
     1565                WriteCIFitem(loopprefix,datablockidDict[hist])
     1566            for i in sorted(self.xtalDict.keys()):
     1567                hist = self.xtalDict[i]
     1568                histblk = self.Histograms[hist]
     1569                instnam = histblk["Instrument Parameters"][0]['InstrName']
     1570                instnam = instnam.replace(' ','')
     1571                i = histblk['hId']
     1572                datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
     1573                                         str(self.shortauthorname) + "|" +
     1574                                         instnam + "_hist_"+str(i))
     1575                WriteCIFitem(loopprefix,datablockidDict[hist])
     1576            #============================================================
     1577            # loop over phases, exporting them
     1578            phasebyhistDict = {} # create a cross-reference to phases by histogram
     1579            for j,phasenam in enumerate(sorted(self.Phases.keys())):
     1580                i = self.Phases[phasenam]['pId']
     1581                WriteCIFitem('\ndata_'+self.CIFname+"_phase_"+str(i))
     1582                WriteCIFitem('# Information for phase '+str(i))
     1583                WriteCIFitem('_pd_block_id',datablockidDict[phasenam])
     1584                # report the phase
     1585                writeCIFtemplate(self.Phases[phasenam]['General'],'phase',phasenam) # write phase template
     1586                WritePhaseInfo(phasenam)
     1587                # preferred orientation
     1588                SH = FormatSH(phasenam)
     1589                MD = FormatHAPpo(phasenam)
     1590                if SH and MD:
     1591                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
     1592                elif SH or MD:
     1593                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + MD)
     1594                else:
     1595                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', 'none')
     1596                # report sample profile terms
     1597                PP = FormatPhaseProfile(phasenam)
     1598                if PP:
     1599                    WriteCIFitem('_pd_proc_ls_profile_function',PP)
     1600                   
     1601            #============================================================
     1602            # loop over histograms, exporting them
     1603            for i in sorted(self.powderDict.keys()):
     1604                hist = self.powderDict[i]
     1605                histblk = self.Histograms[hist]
     1606                if hist.startswith("PWDR"):
     1607                    WriteCIFitem('\ndata_'+self.CIFname+"_pwd_"+str(i))
     1608                    #instnam = histblk["Sample Parameters"]['InstrName']
     1609                    # report instrumental profile terms
     1610                    WriteCIFitem('_pd_proc_ls_profile_function',
     1611                        FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
     1612                    WriteCIFitem('# Information for histogram '+str(i)+': '+hist)
     1613                    WriteCIFitem('_pd_block_id',datablockidDict[hist])
     1614                    histprm = self.Histograms[hist]["Sample Parameters"]
     1615                    writeCIFtemplate(histprm,'powder',histprm['InstrName']) # powder template
     1616                    WritePowderData(hist)
     1617            for i in sorted(self.xtalDict.keys()):
     1618                hist = self.xtalDict[i]
     1619                histblk = self.Histograms[hist]
     1620                if hist.startswith("HKLF"):
     1621                    WriteCIFitem('\ndata_'+self.CIFname+"_sx_"+str(i))
     1622                    #instnam = histblk["Instrument Parameters"][0]['InstrName']
     1623                    WriteCIFitem('# Information for histogram '+str(i)+': '+hist)
     1624                    WriteCIFitem('_pd_block_id',datablockidDict[hist])
     1625                    histprm = self.Histograms[hist]["Instrument Parameters"][0]
     1626                    writeCIFtemplate(histprm,'single',histprm['InstrName']) # single crystal template
     1627                    WriteSingleXtalData(hist)
     1628
     1629        WriteCIFitem('#--' + 15*'eof--' + '#')
     1630        closeCIF()
     1631        print("...export complete")
     1632# end of CIF export
    581633
    591634#===============================================================================
     
    731648    import CifFile as cif # PyCifRW from James Hester
    741649    cifdic = {}
    75     #dictobj = cif.CifDic(fil)
    76     fp = open(fil,'r')             # patch: open file to avoid windows bug
    77     dictobj = cif.CifDic(fp)
    78     fp.close()
     1650    try:
     1651        fp = open(fil,'r')             # patch: open file to avoid windows bug
     1652        dictobj = cif.CifDic(fp)
     1653        fp.close()
     1654    except IOError:
     1655        dictobj = cif.CifDic(fil)
    791656    if DEBUG: print('loaded '+str(fil))
    801657    for item in dictobj.keys():
     
    6062183        self.Add(hbox)
    6072184    def _onGetTemplateFile(self,event):
     2185        'select a template file'
    6082186        dlg = wx.FileDialog(
    6092187            self.cifdefs, message="Read CIF template file",
     
    6172195        dlg.Destroy()
    6182196        if ret == wx.ID_OK:
    619             import CifFile as cif # PyCifRW from James Hester
    6202197            try:
    621                 #cf = cif.ReadCif(fil)
    622                 fp = open(fil,'r')             # patch: open file to avoid windows bug
    623                 cf = cif.ReadCif(fp)
    624                 fp.close()
     2198                cf = G2IO.ReadCIF(fil)
    6252199                if len(cf.keys()) == 0: raise Exception,"No CIF data_ blocks found"
    6262200                if len(cf.keys()) != 1:
    627                     print('\nWarning: CIF has more than one block, '+fil)
     2201                    raise Exception, 'Error, CIF Template has more than one block: '+fil
    6282202                self.dict["CIF_template"] = fil
    6292203            except Exception as err:
     
    6402214
    6412215    def _onEditTemplateContents(self,event):
    642         import CifFile as cif # PyCifRW from James Hester
     2216        'Called to edit the contents of a CIF template'
    6432217        if type(self.CIF) is list or  type(self.CIF) is tuple:
    6442218            dblk,loopstructure = copy.deepcopy(self.CIF) # don't modify original
    6452219        else:
    646             #dblk,loopstructure = CIF2dict(cif.ReadCif(self.CIF))
    647             fp = open(self.CIF,'r')             # patch: open file to avoid windows bug
    648             dblk,loopstructure = CIF2dict(cif.ReadCif(fp))
    649             fp.close()
     2220            cf = G2IO.ReadCIF(self.CIF)
     2221            dblk,loopstructure = CIF2dict(cf)
    6502222        dlg = EditCIFtemplate(self.cifdefs,dblk,loopstructure,self.defaultname)
    6512223        val = dlg.Post()
     
    6622234# end of misc CIF utilities
    6632235#===============================================================================
    664 
    665 class ExportCIF(G2IO.ExportBaseclass):
    666     '''Used to create a CIF of an entire project
    667     '''
    668     def __init__(self,G2frame):
    669         super(self.__class__,self).__init__( # fancy way to say <parentclass>.__init__
    670             G2frame=G2frame,
    671             formatName = 'full CIF',
    672             extension='.cif',
    673             longFormatName = 'Export project as CIF'
    674             )
    675         self.author = ''
    676 
    677     def export(self,mode='full'):
    678         '''Export a CIF
    679 
    680         :param str mode: "full" (default) to create a complete CIF of project,
    681           "simple" for a simple CIF with only coordinates
    682         '''
    683    
    684 # ===== define functions for export method =======================================
    685         def openCIF(filnam):
    686             if DEBUG:
    687                 self.fp = sys.stdout
    688             else:
    689                 self.fp = open(filnam,'w')
    690 
    691         def closeCIF():
    692             if not DEBUG:
    693                 self.fp.close()
    694            
    695         def WriteCIFitem(name,value=''):
    696             if value:
    697                 if "\n" in value or len(value)> 70:
    698                     if name.strip(): self.fp.write(name+'\n')
    699                     self.fp.write('; '+value+'\n')
    700                     self.fp.write('; '+'\n')
    701                 elif " " in value:
    702                     if len(name)+len(value) > 65:
    703                         self.fp.write(name + '\n   ' + '"' + str(value) + '"'+'\n')
    704                     else:
    705                         self.fp.write(name + '  ' + '"' + str(value) + '"'+'\n')
    706                 else:
    707                     if len(name)+len(value) > 65:
    708                         self.fp.write(name+'\n   ' + value+'\n')
    709                     else:
    710                         self.fp.write(name+'  ' + value+'\n')
    711             else:
    712                 self.fp.write(name+'\n')
    713 
    714         def WriteAudit():
    715             WriteCIFitem('_audit_creation_method',
    716                          'created in GSAS-II')
    717             WriteCIFitem('_audit_creation_date',self.CIFdate)
    718             if self.author:
    719                 WriteCIFitem('_audit_author_name',self.author)
    720             WriteCIFitem('_audit_update_record',
    721                          self.CIFdate+'  Initial software-generated CIF')
    722 
    723         def WriteOverall():
    724             '''Write out overall refinement information.
    725 
    726             More could be done here, but this is a good start.
    727             '''
    728             WriteCIFitem('_pd_proc_info_datetime', self.CIFdate)
    729             WriteCIFitem('_pd_calc_method', 'Rietveld Refinement')
    730             #WriteCIFitem('_refine_ls_shift/su_max',DAT1)
    731             #WriteCIFitem('_refine_ls_shift/su_mean',DAT2)
    732             #WriteCIFitem('_refine_diff_density_max',rhomax)    #these need to be defined for each phase!
    733             #WriteCIFitem('_refine_diff_density_min',rhomin)
    734             WriteCIFitem('_computing_structure_refinement','GSAS-II (Toby & Von Dreele, J. Appl. Cryst. 46, 544-549, 2013)')
    735             try:
    736                 vars = str(len(self.OverallParms['Covariance']['varyList']))
    737             except:
    738                 vars = '?'
    739             WriteCIFitem('_refine_ls_number_parameters',vars)
    740             try:
    741                 GOF = G2mth.ValEsd(self.OverallParms['Covariance']['Rvals']['GOF'],-0.009)
    742             except:
    743                 GOF = '?'
    744             WriteCIFitem('_refine_ls_goodness_of_fit_all',GOF)
    745 
    746             # get restraint info
    747             # restraintDict = self.OverallParms.get('Restraints',{})
    748             # for i in  self.OverallParms['Constraints']:
    749             #     print i
    750             #     for j in self.OverallParms['Constraints'][i]:
    751             #         print j
    752             #WriteCIFitem('_refine_ls_number_restraints',TEXT)
    753             # other things to consider reporting
    754             # _refine_ls_number_reflns
    755             # _refine_ls_goodness_of_fit_obs
    756             # _refine_ls_wR_factor_obs
    757             # _refine_ls_restrained_S_all
    758             # _refine_ls_restrained_S_obs
    759 
    760             # include an overall profile r-factor, if there is more than one powder histogram
    761             R = '%.5f'%(self.OverallParms['Covariance']['Rvals']['Rwp']/100.)
    762             WriteCIFitem('\n# OVERALL WEIGHTED R-FACTOR')
    763             WriteCIFitem('_refine_ls_wR_factor_obs',R)
    764                 # _refine_ls_R_factor_all
    765                 # _refine_ls_R_factor_obs               
    766             WriteCIFitem('_refine_ls_matrix_type','full')
    767             #WriteCIFitem('_refine_ls_matrix_type','userblocks')
    768 
    769         def GetCIF(G2dict,tmplate,defaultname=''):
    770             CIFobj = G2dict.get("CIF_template")
    771             if defaultname:
    772                 defaultname = defaultname.encode('ascii','replace').strip().replace(' ','_')
    773                 defaultname = re.sub(r'[^a-zA-Z0-9_-]','',defaultname)
    774                 defaultname = tmplate + "_" + defaultname + ".cif"
    775             else:
    776                 defaultname = ''
    777             templateDefName = 'template_'+tmplate+'.cif'
    778             if not CIFobj: # copying a template
    779                 for pth in [os.getcwd()]+sys.path:
    780                     fil = os.path.join(pth,defaultname)
    781                     if os.path.exists(fil) and defaultname: break
    782                 else:
    783                     for pth in sys.path:
    784                         fil = os.path.join(pth,templateDefName)
    785                         if os.path.exists(fil): break
    786                     else:
    787                         print(CIFobj+' not found in path!')
    788                         return
    789                 fp = open(fil,'r')
    790                 txt = fp.read()
    791                 fp.close()
    792             elif type(CIFobj) is not list and type(CIFobj) is not tuple:
    793                 if not os.path.exists(CIFobj):
    794                     print("Error: requested template file has disappeared: "+CIFobj)
    795                     return
    796                 fp = open(CIFobj,'r')
    797                 txt = fp.read()
    798                 fp.close()
    799             else:
    800                 txt = dict2CIF(CIFobj[0],CIFobj[1]).WriteOut()
    801             # remove the PyCifRW header, if present
    802             #if txt.find('PyCifRW') > -1 and txt.find('data_') > -1:
    803             txt = "# GSAS-II edited template follows "+txt[txt.index("data_")+5:]
    804             #txt = txt.replace('data_','#')
    805             WriteCIFitem(txt)
    806 
    807         def WritePubTemplate():
    808             '''insert the publication template ``template_publ.cif`` or a modified
    809             version
    810             '''
    811             GetCIF(self.OverallParms['Controls'],'publ')
    812 
    813         def WritePhaseTemplate(phasenam):
    814             '''insert the phase template ``template_phase.cif`` or a modified
    815             version for this project
    816             '''
    817             GetCIF(self.Phases[phasenam]['General'],'phase',phasenam)
    818 
    819         def WritePowderTemplate(hist):
    820             '''insert the powder histogram phase template
    821             for this project
    822             '''
    823             histblk = self.Histograms[hist]["Sample Parameters"]
    824             GetCIF(histblk,'powder',histblk['InstrName'])
    825 
    826         def WriteSnglXtalTemplate(hist):
    827             '''insert the single-crystal histogram template
    828             for this project
    829             '''
    830             histblk = self.Histograms[hist]["Instrument Parameters"][0]
    831             GetCIF(histblk,'single',histblk['InstrName'])
    832 
    833         def FormatSH(phasenam):
    834             'Format a full spherical harmonics texture description as a string'
    835             phasedict = self.Phases[phasenam] # pointer to current phase info           
    836             pfx = str(phasedict['pId'])+'::'
    837             s = ""
    838             textureData = phasedict['General']['SH Texture']   
    839             if textureData.get('Order'):
    840                 s += "Spherical Harmonics correction. Order = "+str(textureData['Order'])
    841                 s += " Model: " + str(textureData['Model']) + "\n    Orientation angles: "
    842                 for name in ['omega','chi','phi']:
    843                     aname = pfx+'SH '+name
    844                     s += name + " = "
    845                     sig = self.sigDict.get(aname,-0.09)
    846                     s += G2mth.ValEsd(self.parmDict[aname],sig)
    847                     s += "; "
    848                 s += "\n"
    849                 s1 = "    Coefficients:  "
    850                 for name in textureData['SH Coeff'][1]:
    851                     aname = pfx+name
    852                     if len(s1) > 60:
    853                         s += s1 + "\n"
    854                         s1 = "    "
    855                     s1 += aname + ' = '
    856                     sig = self.sigDict.get(aname,-0.0009)
    857                     s1 += G2mth.ValEsd(self.parmDict[aname],sig)
    858                     s1 += "; "
    859                 s += s1
    860             return s
    861 
    862         def FormatHAPpo(phasenam):
    863             '''return the March-Dollase/SH correction for every
    864             histogram in the current phase formatted into a
    865             character string
    866             '''
    867             phasedict = self.Phases[phasenam] # pointer to current phase info           
    868             s = ''
    869             for histogram in sorted(phasedict['Histograms']):
    870                 if histogram.startswith("HKLF"): continue # powder only
    871                 Histogram = self.Histograms.get(histogram)
    872                 if not Histogram: continue
    873                 hapData = phasedict['Histograms'][histogram]
    874                 if hapData['Pref.Ori.'][0] == 'MD':
    875                     aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':MD'
    876                     if self.parmDict.get(aname,1.0) != 1.0: continue
    877                     sig = self.sigDict.get(aname,-0.009)
    878                     if s != "": s += '\n'
    879                     s += 'March-Dollase correction'
    880                     if len(self.powderDict) > 1:
    881                         s += ', histogram '+str(Histogram['hId']+1)
    882                     s += ' coef. = ' + G2mth.ValEsd(self.parmDict[aname],sig)
    883                     s += ' axis = ' + str(hapData['Pref.Ori.'][3])
    884                 else: # must be SH
    885                     if s != "": s += '\n'
    886                     s += 'Simple spherical harmonic correction'
    887                     if len(self.powderDict) > 1:
    888                         s += ', histogram '+str(Histogram['hId']+1)
    889                     s += ' Order = '+str(hapData['Pref.Ori.'][4])+'\n'
    890                     s1 = "    Coefficients:  "
    891                     for item in hapData['Pref.Ori.'][5]:
    892                         aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':'+item
    893                         if len(s1) > 60:
    894                             s += s1 + "\n"
    895                             s1 = "    "
    896                         s1 += aname + ' = '
    897                         sig = self.sigDict.get(aname,-0.0009)
    898                         s1 += G2mth.ValEsd(self.parmDict[aname],sig)
    899                         s1 += "; "
    900                     s += s1
    901             return s
    902         def FormatBackground(bkg,hId):
    903             '''Display the Background information as a descriptive text string.
    904            
    905             TODO: this needs to be expanded to show the diffuse peak and
    906             Debye term information as well. (Bob)
    907 
    908             :returns: the text description (str)
    909             '''
    910             hfx = ':'+str(hId)+':'
    911             fxn, bkgdict = bkg
    912             terms = fxn[2]
    913             txt = 'Background function: "'+fxn[0]+'" function with '+str(terms)+' terms:\n'
    914             l = "    "
    915             for i,v in enumerate(fxn[3:]):
    916                 name = '%sBack:%d'%(hfx,i)
    917                 sig = self.sigDict.get(name,-0.009)
    918                 if len(l) > 60:
    919                     txt += l + '\n'
    920                     l = '    '
    921                 l += G2mth.ValEsd(v,sig)+', '
    922             txt += l
    923             if bkgdict['nDebye']:
    924                 txt += '\n  Background Debye function parameters: A, R, U:'
    925                 names = ['A:','R:','U:']
    926                 for i in range(bkgdict['nDebye']):
    927                     txt += '\n    '
    928                     for j in range(3):
    929                         name = hfx+'Debye'+names[j]+str(i)
    930                         sig = self.sigDict.get(name,-0.009)
    931                         txt += G2mth.ValEsd(bkgdict['debyeTerms'][i][2*j],sig)+', '
    932             if bkgdict['nPeaks']:
    933                 txt += '\n  Background peak parameters: pos, int, sig, gam:'
    934                 names = ['pos:','int:','sig:','gam:']
    935                 for i in range(bkgdict['nPeaks']):
    936                     txt += '\n    '
    937                     for j in range(4):
    938                         name = hfx+'BkPk'+names[j]+str(i)
    939                         sig = self.sigDict.get(name,-0.009)
    940                         txt += G2mth.ValEsd(bkgdict['peaksList'][i][2*j],sig)+', '
    941             return txt
    942 
    943         def FormatInstProfile(instparmdict,hId):
    944             '''Format the instrumental profile parameters with a
    945             string description. Will only be called on PWDR histograms
    946             '''
    947             s = ''
    948             inst = instparmdict[0]
    949             hfx = ':'+str(hId)+':'
    950             if 'C' in inst['Type'][0]:
    951                 s = 'Finger-Cox-Jephcoat function parameters U, V, W, X, Y, SH/L:\n'
    952                 s += '  peak variance(Gauss) = Utan(Th)^2^+Vtan(Th)+W:\n'
    953                 s += '  peak HW(Lorentz) = X/cos(Th)+Ytan(Th); SH/L = S/L+H/L\n'
    954                 s += '  U, V, W in (centideg)^2^, X & Y in centideg\n    '
    955                 for item in ['U','V','W','X','Y','SH/L']:
    956                     name = hfx+item
    957                     sig = self.sigDict.get(name,-0.009)
    958                     s += G2mth.ValEsd(inst[item][1],sig)+', '                   
    959             elif 'T' in inst['Type'][0]:    #to be tested after TOF Rietveld done
    960                 s = 'Von Dreele-Jorgenson-Windsor function parameters\n'+ \
    961                     '   alpha, beta-0, beta-1, beta-q, sig-0, sig-1, sig-q, X, Y:\n    '
    962                 for item in ['alpha','bet-0','bet-1','bet-q','sig-0','sig-1','sig-q','X','Y']:
    963                     name = hfx+item
    964                     sig = self.sigDict.get(name,-0.009)
    965                     s += G2mth.ValEsd(inst[item][1],sig)+', '
    966             return s
    967 
    968         def FormatPhaseProfile(phasenam):
    969             '''Format the phase-related profile parameters (size/strain)
    970             with a string description.
    971             return an empty string or None if there are no
    972             powder histograms for this phase.
    973             '''
    974             s = ''
    975             phasedict = self.Phases[phasenam] # pointer to current phase info
    976             SGData = phasedict['General'] ['SGData']         
    977             for histogram in sorted(phasedict['Histograms']):
    978                 if histogram.startswith("HKLF"): continue # powder only
    979                 Histogram = self.Histograms.get(histogram)
    980                 if not Histogram: continue
    981                 hapData = phasedict['Histograms'][histogram]
    982                 pId = phasedict['pId']
    983                 hId = Histogram['hId']
    984                 phfx = '%d:%d:'%(pId,hId)
    985                 size = hapData['Size']
    986                 mustrain = hapData['Mustrain']
    987                 hstrain = hapData['HStrain']
    988                 s = '  Crystallite size model "%s" for %s (microns)\n  '%(size[0],phasenam)
    989                 names = ['Size;i','Size;mx']
    990                 if 'uniax' in size[0]:
    991                     names = ['Size;i','Size;a','Size;mx']
    992                     s += 'anisotropic axis is %s\n  '%(str(size[3]))
    993                     s += 'parameters: equatorial size, axial size, G/L mix\n    '
    994                     for i,item in enumerate(names):
    995                         name = phfx+item
    996                         sig = self.sigDict.get(name,-0.009)
    997                         s += G2mth.ValEsd(size[1][i],sig)+', '
    998                 elif 'ellip' in size[0]:
    999                     s += 'parameters: S11, S22, S33, S12, S13, S23, G/L mix\n    '
    1000                     for i in range(6):
    1001                         name = phfx+'Size:'+str(i)
    1002                         sig = self.sigDict.get(name,-0.009)
    1003                         s += G2mth.ValEsd(size[4][i],sig)+', '
    1004                     sig = self.sigDict.get(phfx+'Size;mx',-0.009)
    1005                     s += G2mth.ValEsd(size[1][2],sig)+', '                                           
    1006                 else:       #isotropic
    1007                     s += 'parameters: Size, G/L mix\n    '
    1008                     i = 0
    1009                     for item in names:
    1010                         name = phfx+item
    1011                         sig = self.sigDict.get(name,-0.009)
    1012                         s += G2mth.ValEsd(size[1][i],sig)+', '
    1013                         i = 2    #skip the aniso value               
    1014                 s += '\n  Mustrain model "%s" for %s (10^6^)\n  '%(mustrain[0],phasenam)
    1015                 names = ['Mustrain;i','Mustrain;mx']
    1016                 if 'uniax' in mustrain[0]:
    1017                     names = ['Mustrain;i','Mustrain;a','Mustrain;mx']
    1018                     s += 'anisotropic axis is %s\n  '%(str(size[3]))
    1019                     s += 'parameters: equatorial mustrain, axial mustrain, G/L mix\n    '
    1020                     for i,item in enumerate(names):
    1021                         name = phfx+item
    1022                         sig = self.sigDict.get(name,-0.009)
    1023                         s += G2mth.ValEsd(mustrain[1][i],sig)+', '
    1024                 elif 'general' in mustrain[0]:
    1025                     names = 'parameters: '
    1026                     for i,name in enumerate(G2spc.MustrainNames(SGData)):
    1027                         names += name+', '
    1028                         if i == 9:
    1029                             names += '\n  '
    1030                     names += 'G/L mix\n    '
    1031                     s += names
    1032                     txt = ''
    1033                     for i in range(len(mustrain[4])):
    1034                         name = phfx+'Mustrain:'+str(i)
    1035                         sig = self.sigDict.get(name,-0.009)
    1036                         if len(txt) > 60:
    1037                             s += txt+'\n    '
    1038                             txt = ''
    1039                         txt += G2mth.ValEsd(mustrain[4][i],sig)+', '
    1040                     s += txt                                           
    1041                     sig = self.sigDict.get(phfx+'Mustrain;mx',-0.009)
    1042                     s += G2mth.ValEsd(mustrain[1][2],sig)+', '
    1043                    
    1044                 else:       #isotropic
    1045                     s += '  parameters: Mustrain, G/L mix\n    '
    1046                     i = 0
    1047                     for item in names:
    1048                         name = phfx+item
    1049                         sig = self.sigDict.get(name,-0.009)
    1050                         s += G2mth.ValEsd(mustrain[1][i],sig)+', '
    1051                         i = 2    #skip the aniso value               
    1052                 s += '\n  Macrostrain for %s\n'%(phasenam)
    1053                 txt = '  parameters: '
    1054                 names = G2spc.HStrainNames(SGData)
    1055                 for name in names:
    1056                     txt += name+', '
    1057                 s += txt+'\n    '
    1058                 for i in range(len(names)):
    1059                     name = phfx+name[i]
    1060                     sig = self.sigDict.get(name,-0.009)
    1061                     s += G2mth.ValEsd(hstrain[0][i],sig)+', '
    1062             return s
    1063        
    1064         def FmtAtomType(sym):
    1065             'Reformat a GSAS-II atom type symbol to match CIF rules'
    1066             sym = sym.replace('_','') # underscores are not allowed: no isotope designation?
    1067             # in CIF, oxidation state sign symbols come after, not before
    1068             if '+' in sym:
    1069                 sym = sym.replace('+','') + '+'
    1070             elif '-' in sym:
    1071                 sym = sym.replace('-','') + '-'
    1072             return sym
    1073            
    1074         def PutInCol(val,wid):
    1075             '''Pad a value to >=wid+1 columns by adding spaces at the end. Always
    1076             adds at least one space
    1077             '''
    1078             val = str(val).replace(' ','')
    1079             if not val: val = '?'
    1080             fmt = '{:' + str(wid) + '} '
    1081             return fmt.format(val)
    1082 
    1083         def MakeUniqueLabel(lbl,labellist):
    1084             'Make sure that every atom label is unique'
    1085             lbl = lbl.strip()
    1086             if not lbl: # deal with a blank label
    1087                 lbl = 'A_1'
    1088             if lbl not in labellist:
    1089                 labellist.append(lbl)
    1090                 return lbl
    1091             i = 1
    1092             prefix = lbl
    1093             if '_' in lbl:
    1094                 prefix = lbl[:lbl.rfind('_')]
    1095                 suffix = lbl[lbl.rfind('_')+1:]
    1096                 try:
    1097                     i = int(suffix)+1
    1098                 except:
    1099                     pass
    1100             while prefix+'_'+str(i) in labellist:
    1101                 i += 1
    1102             else:
    1103                 lbl = prefix+'_'+str(i)
    1104                 labellist.append(lbl)
    1105 
    1106         def WriteAtomsNuclear(phasenam):
    1107             'Write atom positions to CIF'
    1108             phasedict = self.Phases[phasenam] # pointer to current phase info
    1109             General = phasedict['General']
    1110             cx,ct,cs,cia = General['AtomPtrs']
    1111             Atoms = phasedict['Atoms']
    1112             cfrac = cx+3
    1113             fpfx = str(phasedict['pId'])+'::Afrac:'       
    1114             for i,at in enumerate(Atoms):
    1115                 fval = self.parmDict.get(fpfx+str(i),at[cfrac])
    1116                 if fval != 0.0:
    1117                     break
    1118             else:
    1119                 WriteCIFitem('\n# PHASE HAS NO ATOMS!')
    1120                 return
    1121                
    1122             WriteCIFitem('\n# ATOMIC COORDINATES AND DISPLACEMENT PARAMETERS')
    1123             WriteCIFitem('loop_ '+
    1124                          '\n\t_atom_site_label'+
    1125                          '\n\t_atom_site_type_symbol'+
    1126                          '\n\t_atom_site_fract_x'+
    1127                          '\n\t_atom_site_fract_y'+
    1128                          '\n\t_atom_site_fract_z'+
    1129                          '\n\t_atom_site_occupancy'+
    1130                          '\n\t_atom_site_adp_type'+
    1131                          '\n\t_atom_site_U_iso_or_equiv'+
    1132                          '\n\t_atom_site_symmetry_multiplicity')
    1133 
    1134             varnames = {cx:'Ax',cx+1:'Ay',cx+2:'Az',cx+3:'Afrac',
    1135                         cia+1:'AUiso',cia+2:'AU11',cia+3:'AU22',cia+4:'AU33',
    1136                         cia+5:'AU12',cia+6:'AU13',cia+7:'AU23'}
    1137             self.labellist = []
    1138            
    1139             pfx = str(phasedict['pId'])+'::'
    1140             # loop over all atoms
    1141             naniso = 0
    1142             for i,at in enumerate(Atoms):
    1143                 s = PutInCol(MakeUniqueLabel(at[ct-1],self.labellist),6) # label
    1144                 fval = self.parmDict.get(fpfx+str(i),at[cfrac])
    1145                 if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
    1146                 s += PutInCol(FmtAtomType(at[ct]),4) # type
    1147                 if at[cia] == 'I':
    1148                     adp = 'Uiso '
    1149                 else:
    1150                     adp = 'Uani '
    1151                     naniso += 1
    1152                     # compute Uequiv crudely
    1153                     # correct: Defined as "1/3 trace of diagonalized U matrix".
    1154                     # SEE cell2GS & Uij2Ueqv to GSASIIlattice. Former is needed to make the GS matrix used by the latter.
    1155                     t = 0.0
    1156                     for j in (2,3,4):
    1157                         var = pfx+varnames[cia+j]+":"+str(i)
    1158                         t += self.parmDict.get(var,at[cia+j])
    1159                 for j in (cx,cx+1,cx+2,cx+3,cia,cia+1):
    1160                     if j in (cx,cx+1,cx+2):
    1161                         dig = 11
    1162                         sigdig = -0.00009
    1163                     else:
    1164                         dig = 10
    1165                         sigdig = -0.009
    1166                     if j == cia:
    1167                         s += adp
    1168                     else:
    1169                         var = pfx+varnames[j]+":"+str(i)
    1170                         dvar = pfx+"d"+varnames[j]+":"+str(i)
    1171                         if dvar not in self.sigDict:
    1172                             dvar = var
    1173                         if j == cia+1 and adp == 'Uani ':
    1174                             val = t/3.
    1175                             sig = sigdig
    1176                         else:
    1177                             #print var,(var in self.parmDict),(var in self.sigDict)
    1178                             val = self.parmDict.get(var,at[j])
    1179                             sig = self.sigDict.get(dvar,sigdig)
    1180                         s += PutInCol(G2mth.ValEsd(val,sig),dig)
    1181                 s += PutInCol(at[cs+1],3)
    1182                 WriteCIFitem(s)
    1183             if naniso == 0: return
    1184             # now loop over aniso atoms
    1185             WriteCIFitem('\nloop_' + '\n\t_atom_site_aniso_label' +
    1186                          '\n\t_atom_site_aniso_U_11' + '\n\t_atom_site_aniso_U_12' +
    1187                          '\n\t_atom_site_aniso_U_13' + '\n\t_atom_site_aniso_U_22' +
    1188                          '\n\t_atom_site_aniso_U_23' + '\n\t_atom_site_aniso_U_33')
    1189             for i,at in enumerate(Atoms):
    1190                 fval = self.parmDict.get(fpfx+str(i),at[cfrac])
    1191                 if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
    1192                 if at[cia] == 'I': continue
    1193                 s = PutInCol(self.labellist[i],6) # label
    1194                 for j in (2,3,4,5,6,7):
    1195                     sigdig = -0.0009
    1196                     var = pfx+varnames[cia+j]+":"+str(i)
    1197                     val = self.parmDict.get(var,at[cia+j])
    1198                     sig = self.sigDict.get(var,sigdig)
    1199                     s += PutInCol(G2mth.ValEsd(val,sig),11)
    1200                 WriteCIFitem(s)
    1201 
    1202         def HillSortElements(elmlist):
    1203             '''Sort elements in "Hill" order: C, H, others, (where others
    1204             are alphabetical).
    1205 
    1206             :params list elmlist: a list of element strings
    1207 
    1208             :returns: a sorted list of element strings
    1209             '''
    1210             newlist = []
    1211             oldlist = elmlist[:]
    1212             for elm in ('C','H'):
    1213                 if elm in elmlist:
    1214                     newlist.append(elm)
    1215                     oldlist.pop(oldlist.index(elm))
    1216             return newlist+sorted(oldlist)
    1217 
    1218         def WriteComposition(phasenam):
    1219             '''determine the composition for the unit cell, crudely determine Z and
    1220             then compute the composition in formula units
    1221             '''
    1222             phasedict = self.Phases[phasenam] # pointer to current phase info
    1223             General = phasedict['General']
    1224             Z = General.get('cellZ',0.0)
    1225             cx,ct,cs,cia = General['AtomPtrs']
    1226             Atoms = phasedict['Atoms']
    1227             fpfx = str(phasedict['pId'])+'::Afrac:'       
    1228             cfrac = cx+3
    1229             cmult = cs+1
    1230             compDict = {} # combines H,D & T
    1231             sitemultlist = []
    1232             massDict = dict(zip(General['AtomTypes'],General['AtomMass']))
    1233             cellmass = 0
    1234             for i,at in enumerate(Atoms):
    1235                 atype = at[ct].strip()
    1236                 if atype.find('-') != -1: atype = atype.split('-')[0]
    1237                 if atype.find('+') != -1: atype = atype.split('+')[0]
    1238                 atype = atype[0].upper()+atype[1:2].lower() # force case conversion
    1239                 if atype == "D" or atype == "D": atype = "H"
    1240                 fvar = fpfx+str(i)
    1241                 fval = self.parmDict.get(fvar,at[cfrac])
    1242                 mult = at[cmult]
    1243                 if not massDict.get(at[ct]):
    1244                     print('Error: No mass found for atom type '+at[ct])
    1245                     print('Will not compute cell contents for phase '+phasenam)
    1246                     return
    1247                 cellmass += massDict[at[ct]]*mult*fval
    1248                 compDict[atype] = compDict.get(atype,0.0) + mult*fval
    1249                 if fval == 1: sitemultlist.append(mult)
    1250             if len(compDict.keys()) == 0: return # no elements!
    1251             if Z < 1: # Z has not been computed or set by user
    1252                 Z = 1
    1253                 for i in range(2,min(sitemultlist)+1):
    1254                     for m in sitemultlist:
    1255                         if m % i != 0:
    1256                             break
    1257                         else:
    1258                             Z = i
    1259                 General['cellZ'] = Z # save it
    1260 
    1261             # when scattering factors are included in the CIF, this needs to be
    1262             # added to the loop here but only in the one-block case.
    1263             # For multiblock CIFs, scattering factors go in the histogram
    1264             # blocks  (for all atoms in all appropriate phases) - an example?:
    1265 #loop_
    1266 #    _atom_type_symbol
    1267 #    _atom_type_description
    1268 #    _atom_type_scat_dispersion_real
    1269 #    _atom_type_scat_dispersion_imag
    1270 #    _atom_type_scat_source
    1271 #    'C' 'C' 0.0033 0.0016
    1272 #                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
    1273 #    'H' 'H' 0.0000 0.0000
    1274 #                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
    1275 #    'P' 'P' 0.1023 0.0942
    1276 #                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
    1277 #    'Cl' 'Cl' 0.1484 0.1585
    1278 #                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
    1279 #    'Cu' 'Cu' 0.3201 1.2651
    1280 #                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
    1281 
    1282             #if oneblock: # add scattering factors for current phase here
    1283             WriteCIFitem('\nloop_  _atom_type_symbol _atom_type_number_in_cell')
    1284             formula = ''
    1285             reload(G2mth)
    1286             for elem in HillSortElements(compDict.keys()):
    1287                 WriteCIFitem('  ' + PutInCol(elem,4) +
    1288                              G2mth.ValEsd(compDict[elem],-0.009,True))
    1289                 if formula: formula += " "
    1290                 formula += elem
    1291                 if compDict[elem] == Z: continue
    1292                 formula += G2mth.ValEsd(compDict[elem]/Z,-0.009,True)
    1293             WriteCIFitem( '\n# Note that Z affects _cell_formula_sum and _weight')
    1294             WriteCIFitem( '_cell_formula_units_Z',str(Z))
    1295             WriteCIFitem( '_chemical_formula_sum',formula)
    1296             WriteCIFitem( '_chemical_formula_weight',
    1297                           G2mth.ValEsd(cellmass/Z,-0.09,True))
    1298 
    1299         def WriteDistances(phasenam,SymOpList,offsetList,symOpList,G2oprList):
    1300             '''Report bond distances and angles for the CIF
    1301 
    1302             Note that _geom_*_symmetry_* fields are values of form
    1303             n_klm where n is the symmetry operation in SymOpList (counted
    1304             starting with 1) and (k-5, l-5, m-5) are translations to add
    1305             to (x,y,z). See
    1306             http://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Igeom_angle_site_symmetry_.html
    1307 
    1308             TODO: need a method to select publication flags for distances/angles
    1309             '''
    1310             phasedict = self.Phases[phasenam] # pointer to current phase info           
    1311             Atoms = phasedict['Atoms']
    1312             generalData = phasedict['General']
    1313             cx,ct,cs,cia = phasedict['General']['AtomPtrs']
    1314             cn = ct-1
    1315             fpfx = str(phasedict['pId'])+'::Afrac:'       
    1316             cfrac = cx+3
    1317             DisAglData = {}
    1318             DisAglCtls = {}
    1319             # create a list of atoms, but skip atoms with zero occupancy
    1320             xyz = []
    1321             fpfx = str(phasedict['pId'])+'::Afrac:'       
    1322             for i,atom in enumerate(Atoms):
    1323                 if self.parmDict.get(fpfx+str(i),atom[cfrac]) == 0.0: continue
    1324                 xyz.append([i,]+atom[cn:cn+2]+atom[cx:cx+3])
    1325             if 'DisAglCtls' in generalData:
    1326                 DisAglCtls = generalData['DisAglCtls']
    1327             else:
    1328                 dlg = G2gd.DisAglDialog(self.G2frame,DisAglCtls,generalData)
    1329                 if dlg.ShowModal() == wx.ID_OK:
    1330                     DisAglCtls = dlg.GetData()
    1331                     generalData['DisAglCtls'] = DisAglCtls
    1332                 else:
    1333                     dlg.Destroy()
    1334                     return
    1335                 dlg.Destroy()
    1336             DisAglData['OrigAtoms'] = xyz
    1337             DisAglData['TargAtoms'] = xyz
    1338             SymOpList,offsetList,symOpList,G2oprList = G2spc.AllOps(
    1339                 generalData['SGData'])
    1340 
    1341             xpandSGdata = generalData['SGData'].copy()
    1342             xpandSGdata.update({'SGOps':symOpList,
    1343                                 'SGInv':False,
    1344                                 'SGLatt':'P',
    1345                                 'SGCen':np.array([[0, 0, 0]]),})
    1346             DisAglData['SGData'] = xpandSGdata
    1347 
    1348             DisAglData['Cell'] = generalData['Cell'][1:] #+ volume
    1349             if 'pId' in phasedict:
    1350                 DisAglData['pId'] = phasedict['pId']
    1351                 DisAglData['covData'] = self.OverallParms['Covariance']
    1352             try:
    1353                 AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(DisAglCtls,DisAglData)
    1354             except KeyError:        # inside DistAngle for missing atom types in DisAglCtls
    1355                 print('**** ERROR - try again but do "Reset" to fill in missing atom types ****')
    1356                    
    1357             # loop over interatomic distances for this phase
    1358             WriteCIFitem('\n# MOLECULAR GEOMETRY')
    1359             WriteCIFitem('loop_' +
    1360                          '\n\t_geom_bond_atom_site_label_1' +
    1361                          '\n\t_geom_bond_atom_site_label_2' +
    1362                          '\n\t_geom_bond_distance' +
    1363                          '\n\t_geom_bond_site_symmetry_1' +
    1364                          '\n\t_geom_bond_site_symmetry_2' +
    1365                          '\n\t_geom_bond_publ_flag')
    1366 
    1367             for i in sorted(AtomLabels.keys()):
    1368                 Dist = DistArray[i]
    1369                 for D in Dist:
    1370                     line = '  '+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[D[0]],6)
    1371                     sig = D[4]
    1372                     if sig == 0: sig = -0.00009
    1373                     line += PutInCol(G2mth.ValEsd(D[3],sig,True),10)
    1374                     line += "  1_555 "
    1375                     line += " {:3d}_".format(D[2])
    1376                     for d in D[1]:
    1377                         line += "{:1d}".format(d+5)
    1378                     line += " yes"
    1379                     WriteCIFitem(line)
    1380 
    1381             # loop over interatomic angles for this phase
    1382             WriteCIFitem('\nloop_' +
    1383                          '\n\t_geom_angle_atom_site_label_1' +
    1384                          '\n\t_geom_angle_atom_site_label_2' +
    1385                          '\n\t_geom_angle_atom_site_label_3' +
    1386                          '\n\t_geom_angle' +
    1387                          '\n\t_geom_angle_site_symmetry_1' +
    1388                          '\n\t_geom_angle_site_symmetry_2' +
    1389                          '\n\t_geom_angle_site_symmetry_3' +
    1390                          '\n\t_geom_angle_publ_flag')
    1391 
    1392             for i in sorted(AtomLabels.keys()):
    1393                 Dist = DistArray[i]
    1394                 for k,j,tup in AngArray[i]:
    1395                     Dj = Dist[j]
    1396                     Dk = Dist[k]
    1397                     line = '  '+PutInCol(AtomLabels[Dj[0]],6)+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[Dk[0]],6)
    1398                     sig = tup[1]
    1399                     if sig == 0: sig = -0.009
    1400                     line += PutInCol(G2mth.ValEsd(tup[0],sig,True),10)
    1401                     line += " {:3d}_".format(Dj[2])
    1402                     for d in Dj[1]:
    1403                         line += "{:1d}".format(d+5)
    1404                     line += "  1_555 "
    1405                     line += " {:3d}_".format(Dk[2])
    1406                     for d in Dk[1]:
    1407                         line += "{:1d}".format(d+5)
    1408                     line += " yes"
    1409                     WriteCIFitem(line)
    1410 
    1411         def WritePhaseInfo(phasenam):
    1412             WriteCIFitem('\n# phase info for '+str(phasenam) + ' follows')
    1413             phasedict = self.Phases[phasenam] # pointer to current phase info           
    1414             WriteCIFitem('_pd_phase_name', phasenam)
    1415             pfx = str(phasedict['pId'])+'::'
    1416             A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
    1417             cellSig = G2stIO.getCellEsd(pfx,
    1418                                        phasedict['General']['SGData'],A,
    1419                                        self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
    1420             cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
    1421             defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
    1422             names = ['length_a','length_b','length_c',
    1423                      'angle_alpha','angle_beta ','angle_gamma',
    1424                      'volume']
    1425             prevsig = 0
    1426             for lbl,defsig,val,sig in zip(names,defsigL,cellList,cellSig):
    1427                 if sig:
    1428                     txt = G2mth.ValEsd(val,sig)
    1429                     prevsig = -sig # use this as the significance for next value
    1430                 else:
    1431                     txt = G2mth.ValEsd(val,min(defsig,prevsig),True)
    1432                 WriteCIFitem('_cell_'+lbl,txt)
    1433                    
    1434             WriteCIFitem('_symmetry_cell_setting',
    1435                          phasedict['General']['SGData']['SGSys'])
    1436 
    1437             spacegroup = phasedict['General']['SGData']['SpGrp'].strip()
    1438             # regularize capitalization and remove trailing H/R
    1439             spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
    1440             WriteCIFitem('_symmetry_space_group_name_H-M',spacegroup)
    1441 
    1442             # generate symmetry operations including centering and center of symmetry
    1443             SymOpList,offsetList,symOpList,G2oprList = G2spc.AllOps(
    1444                 phasedict['General']['SGData'])
    1445             WriteCIFitem('loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
    1446             for i,op in enumerate(SymOpList,start=1):
    1447                 WriteCIFitem('   {:3d}  {:}'.format(i,op.lower()))
    1448 
    1449             # loop over histogram(s) used in this phase
    1450             if not oneblock and not self.quickmode:
    1451                 # report pointers to the histograms used in this phase
    1452                 histlist = []
    1453                 for hist in self.Phases[phasenam]['Histograms']:
    1454                     if self.Phases[phasenam]['Histograms'][hist]['Use']:
    1455                         if phasebyhistDict.get(hist):
    1456                             phasebyhistDict[hist].append(phasenam)
    1457                         else:
    1458                             phasebyhistDict[hist] = [phasenam,]
    1459                         blockid = datablockidDict.get(hist)
    1460                         if not blockid:
    1461                             print("Internal error: no block for data. Phase "+str(
    1462                                 phasenam)+" histogram "+str(hist))
    1463                             histlist = []
    1464                             break
    1465                         histlist.append(blockid)
    1466 
    1467                 if len(histlist) == 0:
    1468                     WriteCIFitem('# Note: phase has no associated data')
    1469 
    1470             # report atom params
    1471             if phasedict['General']['Type'] == 'nuclear':        #this needs macromolecular variant, etc!
    1472                 WriteAtomsNuclear(phasenam)
    1473             else:
    1474                 raise Exception,"no export for mm coordinates implemented"
    1475             # report cell contents
    1476             WriteComposition(phasenam)
    1477             if not self.quickmode:      # report distances and angles
    1478                 WriteDistances(phasenam,SymOpList,offsetList,symOpList,G2oprList)
    1479                
    1480         def Yfmt(ndec,val):
    1481             'Format intensity values'
    1482             out = ("{:."+str(ndec)+"f}").format(val)
    1483             out = out.rstrip('0')  # strip zeros to right of decimal
    1484             return out.rstrip('.')  # and decimal place when not needed
    1485            
    1486         def WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,nRefSets=1):
    1487             WriteCIFitem('_reflns_number_total', str(refcount))
    1488             if hklmin is not None and nRefSets == 1: # hkl range has no meaning with multiple phases
    1489                 WriteCIFitem('_reflns_limit_h_min', str(int(hklmin[0])))
    1490                 WriteCIFitem('_reflns_limit_h_max', str(int(hklmax[0])))
    1491                 WriteCIFitem('_reflns_limit_k_min', str(int(hklmin[1])))
    1492                 WriteCIFitem('_reflns_limit_k_max', str(int(hklmax[1])))
    1493                 WriteCIFitem('_reflns_limit_l_min', str(int(hklmin[2])))
    1494                 WriteCIFitem('_reflns_limit_l_max', str(int(hklmax[2])))
    1495             if hklmin is not None:
    1496                 WriteCIFitem('_reflns_d_resolution_low  ', G2mth.ValEsd(dmax,-0.009))
    1497                 WriteCIFitem('_reflns_d_resolution_high ', G2mth.ValEsd(dmin,-0.009))
    1498 
    1499         def WritePowderData(histlbl):
    1500             histblk = self.Histograms[histlbl]
    1501             inst = histblk['Instrument Parameters'][0]
    1502             hId = histblk['hId']
    1503             pfx = ':' + str(hId) + ':'
    1504            
    1505             if 'Lam1' in inst:
    1506                 ratio = self.parmDict.get('I(L2)/I(L1)',inst['I(L2)/I(L1)'][1])
    1507                 sratio = self.sigDict.get('I(L2)/I(L1)',-0.0009)
    1508                 lam1 = self.parmDict.get('Lam1',inst['Lam1'][1])
    1509                 slam1 = self.sigDict.get('Lam1',-0.00009)
    1510                 lam2 = self.parmDict.get('Lam2',inst['Lam2'][1])
    1511                 slam2 = self.sigDict.get('Lam2',-0.00009)
    1512                 # always assume Ka1 & Ka2 if two wavelengths are present
    1513                 WriteCIFitem('_diffrn_radiation_type','K\\a~1,2~')
    1514                 WriteCIFitem('loop_' +
    1515                              '\n\t_diffrn_radiation_wavelength' +
    1516                              '\n\t_diffrn_radiation_wavelength_wt' +
    1517                              '\n\t_diffrn_radiation_wavelength_id')
    1518                 WriteCIFitem('  ' + PutInCol(G2mth.ValEsd(lam1,slam1),15)+
    1519                              PutInCol('1.0',15) +
    1520                              PutInCol('1',5))
    1521                 WriteCIFitem('  ' + PutInCol(G2mth.ValEsd(lam2,slam2),15)+
    1522                              PutInCol(G2mth.ValEsd(ratio,sratio),15)+
    1523                              PutInCol('2',5))               
    1524             else:
    1525                 lam1 = self.parmDict.get('Lam',inst['Lam'][1])
    1526                 slam1 = self.sigDict.get('Lam',-0.00009)
    1527                 WriteCIFitem('_diffrn_radiation_wavelength',G2mth.ValEsd(lam1,slam1))
    1528 
    1529             if not oneblock:
    1530                 if not phasebyhistDict.get(histlbl):
    1531                     WriteCIFitem('\n# No phases associated with this data set')
    1532                 else:
    1533                     WriteCIFitem('\n# PHASE TABLE')
    1534                     WriteCIFitem('loop_' +
    1535                                  '\n\t_pd_phase_id' +
    1536                                  '\n\t_pd_phase_block_id' +
    1537                                  '\n\t_pd_phase_mass_%')
    1538                     wtFrSum = 0.
    1539                     for phasenam in phasebyhistDict.get(histlbl):
    1540                         hapData = self.Phases[phasenam]['Histograms'][histlbl]
    1541                         General = self.Phases[phasenam]['General']
    1542                         wtFrSum += hapData['Scale'][0]*General['Mass']
    1543 
    1544                     for phasenam in phasebyhistDict.get(histlbl):
    1545                         hapData = self.Phases[phasenam]['Histograms'][histlbl]
    1546                         General = self.Phases[phasenam]['General']
    1547                         wtFr = hapData['Scale'][0]*General['Mass']/wtFrSum
    1548                         pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
    1549                         if pfx+'Scale' in self.sigDict:
    1550                             sig = self.sigDict[pfx+'Scale']*wtFr/hapData['Scale'][0]
    1551                         else:
    1552                             sig = -0.0001
    1553                         WriteCIFitem(
    1554                             '  '+
    1555                             str(self.Phases[phasenam]['pId']) +
    1556                             '  '+datablockidDict[phasenam]+
    1557                             '  '+G2mth.ValEsd(wtFr,sig)
    1558                             )
    1559                     WriteCIFitem('loop_' +
    1560                                  '\n\t_gsas_proc_phase_R_F_factor' +
    1561                                  '\n\t_gsas_proc_phase_R_Fsqd_factor' +
    1562                                  '\n\t_gsas_proc_phase_id' +
    1563                                  '\n\t_gsas_proc_phase_block_id')
    1564                     for phasenam in phasebyhistDict.get(histlbl):
    1565                         pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
    1566                         WriteCIFitem(
    1567                             '  '+
    1568                             '  '+G2mth.ValEsd(histblk[pfx+'Rf']/100.,-.00009) +
    1569                             '  '+G2mth.ValEsd(histblk[pfx+'Rf^2']/100.,-.00009)+
    1570                             '  '+str(self.Phases[phasenam]['pId'])+
    1571                             '  '+datablockidDict[phasenam]
    1572                             )
    1573             else:
    1574                 # single phase in this histogram
    1575                 pfx = '0:'+str(hId)+':'
    1576                 WriteCIFitem('_refine_ls_R_F_factor      ','%.5f'%(histblk[pfx+'Rf']/100.))
    1577                 WriteCIFitem('_refine_ls_R_Fsqd_factor   ','%.5f'%(histblk[pfx+'Rf^2']/100.))
    1578                
    1579             WriteCIFitem('_pd_proc_ls_prof_R_factor   ','%.5f'%(histblk['R']/100.))
    1580             WriteCIFitem('_pd_proc_ls_prof_wR_factor  ','%.5f'%(histblk['wR']/100.))
    1581             WriteCIFitem('_gsas_proc_ls_prof_R_B_factor ','%.5f'%(histblk['Rb']/100.))
    1582             WriteCIFitem('_gsas_proc_ls_prof_wR_B_factor','%.5f'%(histblk['wRb']/100.))
    1583             WriteCIFitem('_pd_proc_ls_prof_wR_expected','%.5f'%(histblk['wRmin']/100.))
    1584 
    1585             if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
    1586                 WriteCIFitem('_diffrn_radiation_probe','x-ray')
    1587                 pola = histblk['Instrument Parameters'][0].get('Polariz.')
    1588                 if pola:
    1589                     pfx = ':' + str(hId) + ':'
    1590                     sig = self.sigDict.get(pfx+'Polariz.',-0.0009)
    1591                     txt = G2mth.ValEsd(pola[1],sig)
    1592                     WriteCIFitem('_diffrn_radiation_polarisn_ratio',txt)
    1593             elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
    1594                 WriteCIFitem('_diffrn_radiation_probe','neutron')
    1595             # TOF (note that this may not be defined)
    1596             #if histblk['Instrument Parameters'][0]['Type'][1][2] == 'T':
    1597             #    WriteCIFitem('_pd_meas_2theta_fixed',text)
    1598            
    1599 
    1600             # TODO: this will need help from Bob
    1601             #if not oneblock:
    1602             #WriteCIFitem('\n# SCATTERING FACTOR INFO')
    1603             #WriteCIFitem('loop_  _atom_type_symbol')
    1604             #if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
    1605             #    WriteCIFitem('      _atom_type_scat_dispersion_real')
    1606             #    WriteCIFitem('      _atom_type_scat_dispersion_imag')
    1607             #    for lbl in ('a1','a2','a3', 'a4', 'b1', 'b2', 'b3', 'b4', 'c'):
    1608             #        WriteCIFitem('      _atom_type_scat_Cromer_Mann_'+lbl)
    1609             #elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
    1610             #    WriteCIFitem('      _atom_type_scat_length_neutron')
    1611             #WriteCIFitem('      _atom_type_scat_source')
    1612 
    1613             WriteCIFitem('_pd_proc_ls_background_function',FormatBackground(histblk['Background'],histblk['hId']))
    1614 
    1615             # TODO: this will need help from Bob
    1616             #WriteCIFitem('_exptl_absorpt_process_details','?')
    1617             #WriteCIFitem('_exptl_absorpt_correction_T_min','?')
    1618             #WriteCIFitem('_exptl_absorpt_correction_T_max','?')
    1619             #C extinction
    1620             #WRITE(IUCIF,'(A)') '# Extinction correction'
    1621             #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_min',TEXT(1:10))
    1622             #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_max',TEXT(11:20))
    1623 
    1624             if not oneblock:                 # instrumental profile terms go here
    1625                 WriteCIFitem('_pd_proc_ls_profile_function',
    1626                     FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
    1627 
    1628             #refprx = '_refln.' # mm
    1629             refprx = '_refln_' # normal
    1630             WriteCIFitem('\n# STRUCTURE FACTOR TABLE')           
    1631             # compute maximum intensity reflection
    1632             Imax = 0
    1633             for phasenam in histblk['Reflection Lists']:
    1634                 scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
    1635                 Icorr = np.array([refl[13] for refl in histblk['Reflection Lists'][phasenam]])[0]
    1636                 FO2 = np.array([refl[8] for refl in histblk['Reflection Lists'][phasenam]])
    1637                 I100 = scale*FO2*Icorr
    1638                 Imax = max(Imax,max(I100))
    1639 
    1640             WriteCIFitem('loop_')
    1641             if len(histblk['Reflection Lists'].keys()) > 1:
    1642                 WriteCIFitem('\t_pd_refln_phase_id')
    1643             WriteCIFitem('\t' + refprx + 'index_h' +
    1644                          '\n\t' + refprx + 'index_k' +
    1645                          '\n\t' + refprx + 'index_l' +
    1646                          '\n\t' + refprx + 'F_squared_meas' +
    1647                          '\n\t' + refprx + 'F_squared_calc' +
    1648                          '\n\t' + refprx + 'phase_calc' +
    1649                          '\n\t_pd_refln_d_spacing')
    1650             if Imax > 0:
    1651                 WriteCIFitem('\t_gsas_i100_meas')
    1652 
    1653             refcount = 0
    1654             hklmin = None
    1655             hklmax = None
    1656             dmax = None
    1657             dmin = None
    1658             for phasenam in histblk['Reflection Lists']:
    1659                 scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
    1660                 phaseid = self.Phases[phasenam]['pId']
    1661                 refcount += len(histblk['Reflection Lists'][phasenam])
    1662                 for ref in histblk['Reflection Lists'][phasenam]:
    1663                     if DEBUG:
    1664                         print('DEBUG: skipping reflection list')
    1665                         break
    1666                     if hklmin is None:
    1667                         hklmin = ref[0:3]
    1668                         hklmax = ref[0:3]
    1669                         dmax = dmin = ref[4]
    1670                     if len(histblk['Reflection Lists'].keys()) > 1:
    1671                         s = PutInCol(phaseid,2)
    1672                     else:
    1673                         s = ""
    1674                     for i,hkl in enumerate(ref[0:3]):
    1675                         hklmax[i] = max(hkl,hklmax[i])
    1676                         hklmin[i] = min(hkl,hklmin[i])
    1677                         s += PutInCol(int(hkl),4)
    1678                     for I in ref[8:10]:
    1679                         s += PutInCol(G2mth.ValEsd(I,-0.0009),10)
    1680                     s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
    1681                     dmax = max(dmax,ref[4])
    1682                     dmin = min(dmin,ref[4])
    1683                     s += PutInCol(G2mth.ValEsd(ref[4],-0.009),8)
    1684                     if Imax > 0:
    1685                         I100 = 100.*scale*ref[8]*ref[13]/Imax
    1686                         s += PutInCol(G2mth.ValEsd(I100,-0.09),6)
    1687                     WriteCIFitem("  "+s)
    1688 
    1689             WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,len(histblk['Reflection Lists']))
    1690             WriteCIFitem('\n# POWDER DATA TABLE')
    1691             # is data fixed step? If the step varies by <0.01% treat as fixed step
    1692             steps = histblk['Data'][0][1:] - histblk['Data'][0][:-1]
    1693             if abs(max(steps)-min(steps)) > abs(max(steps))/10000.:
    1694                 fixedstep = False
    1695             else:
    1696                 fixedstep = True
    1697 
    1698             if fixedstep: # and not TOF
    1699                 WriteCIFitem('_pd_meas_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0],-0.00009))
    1700                 WriteCIFitem('_pd_meas_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1],-0.00009))
    1701                 WriteCIFitem('_pd_meas_2theta_range_inc', G2mth.ValEsd(steps.sum()/len(steps),-0.00009))
    1702                 # zero correct, if defined
    1703                 zero = None
    1704                 zerolst = histblk['Instrument Parameters'][0].get('Zero')
    1705                 if zerolst: zero = zerolst[1]
    1706                 zero = self.parmDict.get('Zero',zero)
    1707                 if zero:
    1708                     WriteCIFitem('_pd_proc_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0]-zero,-0.00009))
    1709                     WriteCIFitem('_pd_proc_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1]-zero,-0.00009))
    1710                     WriteCIFitem('_pd_proc_2theta_range_inc', G2mth.ValEsd(steps.sum()/len(steps),-0.00009))
    1711                
    1712             if zero:
    1713                 WriteCIFitem('_pd_proc_number_of_points', str(len(histblk['Data'][0])))
    1714             else:
    1715                 WriteCIFitem('_pd_meas_number_of_points', str(len(histblk['Data'][0])))
    1716             WriteCIFitem('\nloop_')
    1717             #            WriteCIFitem('\t_pd_proc_d_spacing') # need easy way to get this
    1718             if not fixedstep:
    1719                 if zero:
    1720                     WriteCIFitem('\t_pd_proc_2theta_corrected')
    1721                 else:
    1722                     WriteCIFitem('\t_pd_meas_2theta_scan')
    1723             # at least for now, always report weights.
    1724             #if countsdata:
    1725             #    WriteCIFitem('\t_pd_meas_counts_total')
    1726             #else:
    1727             WriteCIFitem('\t_pd_meas_intensity_total')
    1728             WriteCIFitem('\t_pd_calc_intensity_total')
    1729             WriteCIFitem('\t_pd_proc_intensity_bkg_calc')
    1730             WriteCIFitem('\t_pd_proc_ls_weight')
    1731             maxY = max(histblk['Data'][1].max(),histblk['Data'][3].max())
    1732             if maxY < 0: maxY *= -10 # this should never happen, but...
    1733             ndec = max(0,10-int(np.log10(maxY))-1) # 10 sig figs should be enough
    1734             maxSU = histblk['Data'][2].max()
    1735             if maxSU < 0: maxSU *= -1 # this should never happen, but...
    1736             ndecSU = max(0,8-int(np.log10(maxSU))-1) # 8 sig figs should be enough
    1737             lowlim,highlim = histblk['Limits'][1]
    1738 
    1739             if DEBUG:
    1740                 print('DEBUG: skipping profile list')
    1741             else:   
    1742                 for x,yobs,yw,ycalc,ybkg in zip(histblk['Data'][0],
    1743                                                 histblk['Data'][1],
    1744                                                 histblk['Data'][2],
    1745                                                 histblk['Data'][3],
    1746                                                 histblk['Data'][4]):
    1747                     if lowlim <= x <= highlim:
    1748                         pass
    1749                     else:
    1750                         yw = 0.0 # show the point is not in use
    1751    
    1752                     if fixedstep:
    1753                         s = ""
    1754                     else:
    1755                         s = PutInCol(G2mth.ValEsd(x-zero,-0.00009),10)
    1756                     s += PutInCol(Yfmt(ndec,yobs),12)
    1757                     s += PutInCol(Yfmt(ndec,ycalc),12)
    1758                     s += PutInCol(Yfmt(ndec,ybkg),11)
    1759                     s += PutInCol(Yfmt(ndecSU,yw),9)
    1760                     WriteCIFitem("  "+s)
    1761 
    1762         def WriteSingleXtalData(histlbl):
    1763             histblk = self.Histograms[histlbl]
    1764             #refprx = '_refln.' # mm
    1765             refprx = '_refln_' # normal
    1766 
    1767             WriteCIFitem('\n# STRUCTURE FACTOR TABLE')           
    1768             WriteCIFitem('loop_' +
    1769                          '\n\t' + refprx + 'index_h' +
    1770                          '\n\t' + refprx + 'index_k' +
    1771                          '\n\t' + refprx + 'index_l' +
    1772                          '\n\t' + refprx + 'F_squared_meas' +
    1773                          '\n\t' + refprx + 'F_squared_sigma' +
    1774                          '\n\t' + refprx + 'F_squared_calc' +
    1775                          '\n\t' + refprx + 'phase_calc'
    1776                          )
    1777 
    1778             hklmin = None
    1779             hklmax = None
    1780             dmax = None
    1781             dmin = None
    1782             refcount = len(histblk['Data'])
    1783             for ref in histblk['Data']:
    1784                 s = "  "
    1785                 if hklmin is None:
    1786                     hklmin = ref[0:3]
    1787                     hklmax = ref[0:3]
    1788                     dmax = dmin = ref[4]
    1789                 for i,hkl in enumerate(ref[0:3]):
    1790                     hklmax[i] = max(hkl,hklmax[i])
    1791                     hklmin[i] = min(hkl,hklmin[i])
    1792                     s += PutInCol(int(hkl),4)
    1793                 sig = ref[6] * ref[8] / ref[5]
    1794                 s += PutInCol(G2mth.ValEsd(ref[8],-abs(sig/10)),12)
    1795                 s += PutInCol(G2mth.ValEsd(sig,-abs(sig)/10.),10)
    1796                 s += PutInCol(G2mth.ValEsd(ref[9],-abs(sig/10)),12)
    1797                 s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
    1798                 dmax = max(dmax,ref[4])
    1799                 dmin = min(dmin,ref[4])
    1800                 WriteCIFitem(s)
    1801             WriteReflStat(refcount,hklmin,hklmax,dmin,dmax)
    1802             hId = histblk['hId']
    1803             pfx = '0:'+str(hId)+':'
    1804             WriteCIFitem('_reflns_wR_factor_obs    ','%.4f'%(histblk['wR']/100.))
    1805             WriteCIFitem('_reflns_R_F_factor_obs   ','%.4f'%(histblk[pfx+'Rf']/100.))
    1806             WriteCIFitem('_reflns_R_Fsqd_factor_obs','%.4f'%(histblk[pfx+'Rf^2']/100.))
    1807         def EditAuthor(event=None):
    1808             'Edit the CIF author name'
    1809             dlg = G2gd.SingleStringDialog(self.G2frame,
    1810                                           'Get CIF Author',
    1811                                           'Provide CIF Author name (Last, First)',
    1812                                           value=self.author)
    1813             if not dlg.Show():
    1814                 dlg.Destroy()
    1815                 return False  # cancel was pressed
    1816             self.author = dlg.GetValue()
    1817             dlg.Destroy()
    1818             try:
    1819                 self.OverallParms['Controls']["Author"] = self.author # save for future
    1820             except KeyError:
    1821                 pass
    1822             return True
    1823         def EditInstNames(event=None):
    1824             'Provide a dialog for editing instrument names'
    1825             dictlist = []
    1826             keylist = []
    1827             lbllist = []
    1828             for hist in self.Histograms:
    1829                 if hist.startswith("PWDR"):
    1830                     key2 = "Sample Parameters"
    1831                     d = self.Histograms[hist][key2]
    1832                 elif hist.startswith("HKLF"):
    1833                     key2 = "Instrument Parameters"
    1834                     d = self.Histograms[hist][key2][0]
    1835                    
    1836                 lbllist.append(hist)
    1837                 dictlist.append(d)
    1838                 keylist.append('InstrName')
    1839                 instrname = d.get('InstrName')
    1840                 if instrname is None:
    1841                     d['InstrName'] = ''
    1842             return G2gd.CallScrolledMultiEditor(
    1843                 self.G2frame,dictlist,keylist,
    1844                 prelbl=range(1,len(dictlist)+1),
    1845                 postlbl=lbllist,
    1846                 title='Instrument names',
    1847                 header="Edit instrument names. Note that a non-blank\nname is required for all histograms",
    1848                 CopyButton=True)
    1849            
    1850         def EditRanges(event):
    1851             but = event.GetEventObject()
    1852             phasedict = but.phasedict
    1853             dlg = G2gd.DisAglDialog(self.G2frame,{},phasedict['General'])
    1854             if dlg.ShowModal() == wx.ID_OK:
    1855                 phasedict['General']['DisAglCtls'] = dlg.GetData()
    1856             dlg.Destroy()
    1857            
    1858         def EditCIFDefaults():
    1859             'Fills the CIF Defaults window'
    1860             import wx.lib.scrolledpanel as wxscroll
    1861             self.cifdefs.DestroyChildren()
    1862             self.cifdefs.SetTitle('Edit CIF settings')
    1863             vbox = wx.BoxSizer(wx.VERTICAL)
    1864             but = wx.Button(self.cifdefs, wx.ID_ANY,'Edit CIF Author')
    1865             but.Bind(wx.EVT_BUTTON,EditAuthor)
    1866             vbox.Add(but,0,wx.ALIGN_CENTER,3)
    1867             but = wx.Button(self.cifdefs, wx.ID_ANY,'Edit Instrument Name(s)')
    1868             but.Bind(wx.EVT_BUTTON,EditInstNames)
    1869             vbox.Add(but,0,wx.ALIGN_CENTER,3)
    1870             cpnl = wxscroll.ScrolledPanel(self.cifdefs,size=(300,300))
    1871             cbox = wx.BoxSizer(wx.VERTICAL)
    1872             G2gd.HorizontalLine(cbox,cpnl)         
    1873             cbox.Add(
    1874                 CIFtemplateSelect(self.cifdefs,
    1875                                   cpnl,'publ',self.OverallParms['Controls'],
    1876                                   EditCIFDefaults,
    1877                                   "Publication (overall) template",
    1878                                   ),
    1879                 0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
    1880             for phasenam in sorted(self.Phases.keys()):
    1881                 G2gd.HorizontalLine(cbox,cpnl)         
    1882                 title = 'Phase '+phasenam
    1883                 phasedict = self.Phases[phasenam] # pointer to current phase info           
    1884                 cbox.Add(
    1885                     CIFtemplateSelect(self.cifdefs,
    1886                                       cpnl,'phase',phasedict['General'],
    1887                                       EditCIFDefaults,
    1888                                       title,
    1889                                       phasenam),
    1890                     0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
    1891                 cpnl.SetSizer(cbox)
    1892                 but = wx.Button(cpnl, wx.ID_ANY,'Edit distance/angle ranges')
    1893                 #cbox.Add(but,0,wx.ALIGN_CENTER,3)
    1894                 cbox.Add((-1,2))
    1895                 cbox.Add(but,0,wx.ALIGN_LEFT,0)
    1896                 but.phasedict = self.Phases[phasenam]  # set a pointer to current phase info     
    1897                 but.Bind(wx.EVT_BUTTON,EditRanges)     # phase bond/angle ranges
    1898             for i in sorted(self.powderDict.keys()):
    1899                 G2gd.HorizontalLine(cbox,cpnl)         
    1900                 hist = self.powderDict[i]
    1901                 histblk = self.Histograms[hist]
    1902                 title = 'Powder dataset '+hist[5:]
    1903                 cbox.Add(
    1904                     CIFtemplateSelect(self.cifdefs,
    1905                                       cpnl,'powder',histblk["Sample Parameters"],
    1906                                       EditCIFDefaults,
    1907                                       title,
    1908                                       histblk["Sample Parameters"]['InstrName']),
    1909                     0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
    1910                 cpnl.SetSizer(cbox)
    1911             for i in sorted(self.xtalDict.keys()):
    1912                 G2gd.HorizontalLine(cbox,cpnl)         
    1913                 hist = self.xtalDict[i]
    1914                 histblk = self.Histograms[hist]
    1915                 title = 'Single Xtal dataset '+hist[5:]
    1916                 cbox.Add(
    1917                     CIFtemplateSelect(self.cifdefs,
    1918                                       cpnl,'single',histblk["Instrument Parameters"][0],
    1919                                       EditCIFDefaults,
    1920                                       title,
    1921                                       histblk["Instrument Parameters"][0]['InstrName']),
    1922                     0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
    1923                 cpnl.SetSizer(cbox)
    1924 
    1925             cpnl.SetAutoLayout(1)
    1926             cpnl.SetupScrolling()
    1927             #cpnl.Bind(rw.EVT_RW_LAYOUT_NEEDED, self.OnLayoutNeeded) # needed if sizes change
    1928             cpnl.Layout()
    1929 
    1930             vbox.Add(cpnl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 0)
    1931             btnsizer = wx.StdDialogButtonSizer()
    1932             btn = wx.Button(self.cifdefs, wx.ID_OK, "Create CIF")
    1933             btn.SetDefault()
    1934             btnsizer.AddButton(btn)
    1935             btn = wx.Button(self.cifdefs, wx.ID_CANCEL)
    1936             btnsizer.AddButton(btn)
    1937             btnsizer.Realize()
    1938             vbox.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
    1939             self.cifdefs.SetSizer(vbox)
    1940             vbox.Fit(self.cifdefs)
    1941             self.cifdefs.Layout()
    1942 
    1943 # ===== end of functions for export method =======================================
    1944 #=================================================================================
    1945 
    1946         # the export process starts here
    1947         # load all of the tree into a set of dicts
    1948         self.loadTree()
    1949         # create a dict with refined values and their uncertainties
    1950         self.loadParmDict()
    1951 
    1952         # Someday: get restraint & constraint info
    1953         #restraintDict = self.OverallParms.get('Restraints',{})
    1954         #for i in  self.OverallParms['Constraints']:
    1955         #    print i
    1956         #    for j in self.OverallParms['Constraints'][i]:
    1957         #        print j
    1958 
    1959         self.CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
    1960         # index powder and single crystal histograms
    1961         self.powderDict = {}
    1962         self.xtalDict = {}
    1963         for hist in self.Histograms:
    1964             i = self.Histograms[hist]['hId']
    1965             if hist.startswith("PWDR"):
    1966                 self.powderDict[i] = hist
    1967             elif hist.startswith("HKLF"):
    1968                 self.xtalDict[i] = hist
    1969         # is there anything to export?
    1970         if len(self.Phases) == len(self.powderDict) == len(self.xtalDict) == 0:
    1971            self.G2frame.ErrorDialog(
    1972                'Empty project',
    1973                'Project does not contain interconnected data & phase(s)')
    1974            return
    1975         # get the project file name
    1976         self.CIFname = os.path.splitext(
    1977             os.path.split(self.G2frame.GSASprojectfile)[1]
    1978             )[0]
    1979         self.CIFname = self.CIFname.replace(' ','')
    1980         if not self.CIFname: # none defined & needed, save as GPX to get one
    1981             self.G2frame.OnFileSaveas(None)
    1982             if not self.G2frame.GSASprojectfile: return
    1983             self.CIFname = os.path.splitext(
    1984                 os.path.split(self.G2frame.GSASprojectfile)[1]
    1985                 )[0]
    1986             self.CIFname = self.CIFname.replace(' ','')
    1987         # test for quick CIF mode or no data
    1988         self.quickmode = False
    1989         phasenam = phasenum = None # include all phases
    1990         if mode != "full" or len(self.powderDict) + len(self.xtalDict) == 0:
    1991             self.quickmode = True
    1992             oneblock = True
    1993             if len(self.Phases) == 0:
    1994                 self.G2frame.ErrorDialog(
    1995                     'No phase present',
    1996                     'Cannot create a coordinates CIF with no phases')
    1997                 return
    1998             elif len(self.Phases) > 1: # quick mode: choose one phase
    1999                 choices = sorted(self.Phases.keys())
    2000                 phasenum = G2gd.ItemSelector(choices,self.G2frame)
    2001                 if phasenum is None: return
    2002                 phasenam = choices[phasenum]
    2003         # will this require a multiblock CIF?
    2004         elif len(self.Phases) > 1:
    2005             oneblock = False
    2006         elif len(self.powderDict) + len(self.xtalDict) > 1:
    2007             oneblock = False
    2008         else: # one phase, one dataset, Full CIF
    2009             oneblock = True
    2010 
    2011         # make sure needed infomation is present
    2012         # get CIF author name -- required for full CIFs
    2013         try:
    2014             self.author = self.OverallParms['Controls'].get("Author",'').strip()
    2015         except KeyError:
    2016             pass
    2017         while not (self.author or self.quickmode):
    2018             if not EditAuthor(): return
    2019         self.shortauthorname = self.author.replace(',','').replace(' ','')[:20]
    2020 
    2021         # check there is an instrument name for every histogram
    2022         if not self.quickmode:
    2023             invalid = 0
    2024             key3 = 'InstrName'
    2025             for hist in self.Histograms:
    2026                 if hist.startswith("PWDR"):
    2027                     key2 = "Sample Parameters"
    2028                     d = self.Histograms[hist][key2]
    2029                 elif hist.startswith("HKLF"):
    2030                     key2 = "Instrument Parameters"
    2031                     d = self.Histograms[hist][key2][0]                   
    2032                 instrname = d.get(key3)
    2033                 if instrname is None:
    2034                     d[key3] = ''
    2035                     invalid += 1
    2036                 elif instrname.strip() == '':
    2037                     invalid += 1
    2038             if invalid:
    2039                 msg = ""
    2040                 if invalid > 3: msg = (
    2041                     "\n\nNote: it may be faster to set the name for\n"
    2042                     "one histogram for each instrument and use the\n"
    2043                     "File/Copy option to duplicate the name"
    2044                     )
    2045                 if not EditInstNames(): return
    2046         # check for a distance-angle range search range for each phase
    2047         if not self.quickmode:
    2048             for phasenam in sorted(self.Phases.keys()):
    2049                 #i = self.Phases[phasenam]['pId']
    2050                 phasedict = self.Phases[phasenam] # pointer to current phase info           
    2051                 if 'DisAglCtls' not in phasedict['General']:
    2052                     dlg = G2gd.DisAglDialog(self.G2frame,{},phasedict['General'])
    2053                     if dlg.ShowModal() == wx.ID_OK:
    2054                         phasedict['General']['DisAglCtls'] = dlg.GetData()
    2055                     else:
    2056                         dlg.Destroy()
    2057                         return
    2058                     dlg.Destroy()
    2059 
    2060         if oneblock and not self.quickmode:
    2061             # select a dataset to use (there should only be one set in one block,
    2062             # but take whatever comes 1st)
    2063             for hist in self.Histograms:
    2064                 histblk = self.Histograms[hist]
    2065                 if hist.startswith("PWDR"):
    2066                     instnam = histblk["Sample Parameters"]['InstrName']
    2067                     break # ignore all but 1st data histogram
    2068                 elif hist.startswith("HKLF"):
    2069                     instnam = histblk["Instrument Parameters"][0]['InstrName']
    2070                     break # ignore all but 1st data histogram
    2071         if self.quickmode:
    2072             fil = self.askSaveFile()
    2073         else:
    2074             fil = self.defSaveFile()
    2075         if not fil: return
    2076         if not self.quickmode: # give the user a chance to edit all defaults
    2077             self.cifdefs = wx.Dialog(
    2078                 self.G2frame,style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
    2079             EditCIFDefaults()
    2080             val = self.cifdefs.ShowModal()
    2081             self.cifdefs.Destroy()
    2082             if val != wx.ID_OK:
    2083                 return
    2084         #======================================================================
    2085         # Start writing the CIF - single block
    2086         #======================================================================
    2087         print('Writing CIF output to file '+fil+"...")
    2088         openCIF(fil)
    2089         if oneblock:
    2090             WriteCIFitem('data_'+self.CIFname)
    2091             if phasenam is None: # if not already selected, select the first phase (should be one)
    2092                 phasenam = self.Phases.keys()[0]
    2093             #print 'phasenam',phasenam
    2094             phaseblk = self.Phases[phasenam] # pointer to current phase info
    2095             if not self.quickmode:
    2096                 instnam = instnam.replace(' ','')
    2097                 WriteCIFitem('_pd_block_id',
    2098                              str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
    2099                              str(self.shortauthorname) + "|" + instnam)
    2100                 WriteAudit()
    2101                 WritePubTemplate()
    2102                 WriteOverall()
    2103                 WritePhaseTemplate(phasenam)
    2104             # report the phase info
    2105             WritePhaseInfo(phasenam)
    2106             if hist.startswith("PWDR") and not self.quickmode:
    2107                 # preferred orientation
    2108                 SH = FormatSH(phasenam)
    2109                 MD = FormatHAPpo(phasenam)
    2110                 if SH and MD:
    2111                     WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
    2112                 elif SH or MD:
    2113                     WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + MD)
    2114                 else:
    2115                     WriteCIFitem('_pd_proc_ls_pref_orient_corr', 'none')
    2116                     # report profile, since one-block: include both histogram and phase info
    2117                 WriteCIFitem('_pd_proc_ls_profile_function',
    2118                     FormatInstProfile(histblk["Instrument Parameters"],histblk['hId'])
    2119                     +'\n'+FormatPhaseProfile(phasenam))
    2120                 WritePowderTemplate(hist)
    2121                 WritePowderData(hist)
    2122             elif hist.startswith("HKLF") and not self.quickmode:
    2123                 WriteSnglXtalTemplate(hist)
    2124                 WriteSingleXtalData(hist)
    2125         else:
    2126         #======================================================================
    2127         # Start writing the CIF - multiblock
    2128         #======================================================================
    2129             # publication info
    2130             WriteCIFitem('\ndata_'+self.CIFname+'_publ')
    2131             WriteAudit()
    2132             WriteCIFitem('_pd_block_id',
    2133                          str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
    2134                          str(self.shortauthorname) + "|Overall")
    2135             WritePubTemplate()
    2136             # overall info
    2137             WriteCIFitem('data_'+str(self.CIFname)+'_overall')
    2138             WriteOverall()
    2139             #============================================================
    2140             WriteCIFitem('# POINTERS TO PHASE AND HISTOGRAM BLOCKS')
    2141             datablockidDict = {} # save block names here -- N.B. check for conflicts between phase & hist names (unlikely!)
    2142             # loop over phase blocks
    2143             if len(self.Phases) > 1:
    2144                 loopprefix = ''
    2145                 WriteCIFitem('loop_   _pd_phase_block_id')
    2146             else:
    2147                 loopprefix = '_pd_phase_block_id'
    2148            
    2149             for phasenam in sorted(self.Phases.keys()):
    2150                 i = self.Phases[phasenam]['pId']
    2151                 datablockidDict[phasenam] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
    2152                              'phase_'+ str(i) + '|' + str(self.shortauthorname))
    2153                 WriteCIFitem(loopprefix,datablockidDict[phasenam])
    2154             # loop over data blocks
    2155             if len(self.powderDict) + len(self.xtalDict) > 1:
    2156                 loopprefix = ''
    2157                 WriteCIFitem('loop_   _pd_block_diffractogram_id')
    2158             else:
    2159                 loopprefix = '_pd_block_diffractogram_id'
    2160             for i in sorted(self.powderDict.keys()):
    2161                 hist = self.powderDict[i]
    2162                 histblk = self.Histograms[hist]
    2163                 instnam = histblk["Sample Parameters"]['InstrName']
    2164                 instnam = instnam.replace(' ','')
    2165                 i = histblk['hId']
    2166                 datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
    2167                                          str(self.shortauthorname) + "|" +
    2168                                          instnam + "_hist_"+str(i))
    2169                 WriteCIFitem(loopprefix,datablockidDict[hist])
    2170             for i in sorted(self.xtalDict.keys()):
    2171                 hist = self.xtalDict[i]
    2172                 histblk = self.Histograms[hist]
    2173                 instnam = histblk["Instrument Parameters"][0]['InstrName']
    2174                 instnam = instnam.replace(' ','')
    2175                 i = histblk['hId']
    2176                 datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
    2177                                          str(self.shortauthorname) + "|" +
    2178                                          instnam + "_hist_"+str(i))
    2179                 WriteCIFitem(loopprefix,datablockidDict[hist])
    2180             #============================================================
    2181             # loop over phases, exporting them
    2182             phasebyhistDict = {} # create a cross-reference to phases by histogram
    2183             for j,phasenam in enumerate(sorted(self.Phases.keys())):
    2184                 i = self.Phases[phasenam]['pId']
    2185                 WriteCIFitem('\ndata_'+self.CIFname+"_phase_"+str(i))
    2186                 WriteCIFitem('# Information for phase '+str(i))
    2187                 WriteCIFitem('_pd_block_id',datablockidDict[phasenam])
    2188                 # report the phase
    2189                 WritePhaseTemplate(phasenam)
    2190                 WritePhaseInfo(phasenam)
    2191                 # preferred orientation
    2192                 SH = FormatSH(phasenam)
    2193                 MD = FormatHAPpo(phasenam)
    2194                 if SH and MD:
    2195                     WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
    2196                 elif SH or MD:
    2197                     WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + MD)
    2198                 else:
    2199                     WriteCIFitem('_pd_proc_ls_pref_orient_corr', 'none')
    2200                 # report sample profile terms
    2201                 PP = FormatPhaseProfile(phasenam)
    2202                 if PP:
    2203                     WriteCIFitem('_pd_proc_ls_profile_function',PP)
    2204                    
    2205             #============================================================
    2206             # loop over histograms, exporting them
    2207             for i in sorted(self.powderDict.keys()):
    2208                 hist = self.powderDict[i]
    2209                 histblk = self.Histograms[hist]
    2210                 if hist.startswith("PWDR"):
    2211                     WriteCIFitem('\ndata_'+self.CIFname+"_pwd_"+str(i))
    2212                     #instnam = histblk["Sample Parameters"]['InstrName']
    2213                     # report instrumental profile terms
    2214                     WriteCIFitem('_pd_proc_ls_profile_function',
    2215                         FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
    2216                     WriteCIFitem('# Information for histogram '+str(i)+': '+hist)
    2217                     WriteCIFitem('_pd_block_id',datablockidDict[hist])
    2218                     WritePowderTemplate(hist)
    2219                     WritePowderData(hist)
    2220             for i in sorted(self.xtalDict.keys()):
    2221                 hist = self.xtalDict[i]
    2222                 histblk = self.Histograms[hist]
    2223                 if hist.startswith("HKLF"):
    2224                     WriteCIFitem('\ndata_'+self.CIFname+"_sx_"+str(i))
    2225                     #instnam = histblk["Instrument Parameters"][0]['InstrName']
    2226                     WriteCIFitem('# Information for histogram '+str(i)+': '+hist)
    2227                     WriteCIFitem('_pd_block_id',datablockidDict[hist])
    2228                     WriteSnglXtalTemplate(hist)
    2229                     WriteSingleXtalData(hist)
    2230 
    2231         WriteCIFitem('#--' + 15*'eof--' + '#')
    2232         closeCIF()
    2233         print("...export complete")
Note: See TracChangeset for help on using the changeset viewer.