source: trunk/GSASIIpy3.py @ 5352

Last change on this file since 5352 was 5352, checked in by toby, 7 months ago

add ability to merge chem and magnetic phases in a quick CIF

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 8.4 KB
Line 
1'''
2*GSASIIpy3: Python 3.x Routines*
3================================
4
5Module to hold python 3-compatible code, to keep it separate from
6code that will break with __future__ options.
7
8'''
9from __future__ import division, print_function
10import numpy as np
11import GSASIIpath
12GSASIIpath.SetVersionNumber("$Revision: 5352 $")
13# declare symbol (pi) and functions allowed in expressions
14sind = sin = s = lambda x: np.sin(x*np.pi/180.)
15cosd = cos = c = lambda x: np.cos(x*np.pi/180.)
16tand = tan = t = lambda x: np.tan(x*np.pi/180.)
17sqrt = sq = lambda x: np.sqrt(x)
18pi = np.pi
19
20# formatting for unique cell parameters by Laue type
21cellGUIlist = [
22    [['m3','m3m'],4,[" Unit cell: a = "],["{:.5f}"],[True],[0]],
23    [['3R','3mR'],6,[" a = ",u" \u03B1 = "],["{:.5f}","{:.3f}"],[True,True],[0,3]],
24    [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],6,[" a = "," c = "],["{:.5f}","{:.5f}"],[True,True],[0,2]],
25    [['mmm'],8,[" a = "," b = "," c = "],["{:.5f}","{:.5f}","{:.5f}"],
26        [True,True,True],[0,1,2]],
27    [['2/m'+'a'],10,[" a = "," b = "," c = ",u" \u03B1 = "],
28        ["{:.5f}","{:.5f}","{:.5f}","{:.3f}"],[True,True,True,True,],[0,1,2,3]],
29    [['2/m'+'b'],10,[" a = "," b = "," c = ",u" \u03B2 = "],
30        ["{:.5f}","{:.5f}","{:.5f}","{:.3f}"],[True,True,True,True,],[0,1,2,4]],
31    [['2/m'+'c'],10,[" a = "," b = "," c = ",u" \u03B3 = "],
32        ["{:.5f}","{:.5f}","{:.5f}","{:.3f}"],[True,True,True,True,],[0,1,2,5]],
33    [['-1'],7,[" a = "," b = "," c = ",u" \u03B1 = ",u" \u03B2 = ",u" \u03B3 = "],
34         ["{:.5f}","{:.5f}","{:.5f}","{:.3f}","{:.3f}","{:.3f}"],
35         [True,True,True,True,True,True],[0,1,2,3,4,5]]
36    ]
37
38def FormulaEval(string):
39    '''Evaluates a algebraic formula into a float, if possible. Works
40    properly on fractions e.g. 2/3 only with python 3.0+ division.
41
42    Expressions such as 2/3, 3*pi, sin(45)/2, 2*sqrt(2), 2**10 can all
43    be evaluated.
44
45    :param str string: Character string containing a Python expression
46      to be evaluated.
47
48    :returns: the value for the expression as a float or None if the expression does not
49      evaluate to a valid number.
50   
51    '''
52    try:
53        val = float(eval(string))
54        if np.isnan(val) or np.isinf(val): return None
55    except:
56        return None
57    return val
58
59def FormatPadValue(val,maxdigits=None):
60    '''Format a float to fit in ``maxdigits[0]`` spaces with maxdigits[1] after decimal.
61
62    :param float val: number to be formatted.
63
64    :param list maxdigits: the number of digits & places after decimal to be used for display of the
65      number (defaults to [10,2]).
66
67    :returns: a string with exactly maxdigits[0] characters (except under error conditions),
68      but last character will always be a space
69    '''
70    if maxdigits is None:
71        digits = [10,2]
72    else:
73        digits = list(maxdigits)
74    fmt = '{:'+str(digits[0])+'}'
75    s = fmt.format(FormatValue(val,digits))
76    if s[-1] == ' ':
77        return s
78    else:
79        return s+' '
80   
81
82def FormatValue(val,maxdigits=None):
83    '''Format a float to fit in at most ``maxdigits[0]`` spaces with maxdigits[1] after decimal.
84    Note that this code has been hacked from FormatSigFigs and may have unused sections.
85
86    :param float val: number to be formatted.
87
88    :param list maxdigits: the number of digits, places after decimal and 'f' or 'g' to be used for display of the
89      number (defaults to [10,2,'f']).
90
91    :returns: a string with <= maxdigits characters (usually). 
92    '''
93    if 'str' in str(type(val)) and (val == '?' or val == '.'):
94        return val       
95    if maxdigits is None:
96        digits = [10,2,'f']
97    else:
98        digits = list(maxdigits)
99    if len(digits) == 2:
100        digits.append('f')
101    if not val:
102        digits[2] = 'f'
103    fmt="{:"+str(digits[0])+"."+str(digits[1])+digits[2]+"}"
104    string = fmt.format(float(val)).strip() # will standard .f formatting work?
105    if len(string) <= digits[0]:
106        if ':' in string: # deal with weird bug where a colon pops up in a number when formatting (EPD 7.3.2!)
107            string = str(val)
108        if digits[1] > 0 and not 'e' in string.lower(): # strip off extra zeros on right side
109            string = string.rstrip('0')
110            if string[-1] == '.': string += "0"
111        return string
112    if val < 0: digits[0] -= 1 # negative numbers, reserve space for the sign
113    decimals = digits[0] - digits[1]
114    if abs(val) > 1e99: # for very large numbers, use scientific notation and use all digits
115        fmt = "{" + (":{:d}.{:d}g".format(digits[0],digits[0]-6))+"}"
116    elif abs(val) > 1e9:
117        fmt = "{" + (":{:d}.{:d}g".format(digits[0],digits[0]-5))+"}"
118    elif abs(val) < 10**(4-decimals): # make sure at least 4 decimals show
119        # this clause is probably no longer needed since the number probably shows as 0.0
120        decimals = min(digits[0]-5,digits[1])
121        fmt = "{" + (":{:d}.{:d}g".format(digits[0],decimals))+"}"
122    elif abs(val) >= 10**(decimals-1): # deal with large numbers in smaller spaces
123        decimals = max(0,digits[0]-5)
124        fmt = "{" + (":{:d}.{:d}g".format(digits[0],decimals))+"}"
125    elif abs(val) < 1: # use f format for small numbers
126        # this clause is probably no longer needed since the number probably shows as 0.0
127        decimals = min(digits[0]-3,digits[1])
128        fmt = "{" + (":{:d}.{:d}f".format(digits[0],decimals))+"}"
129    else: # in range where g formatting should do what I want
130        # used?
131        decimals = digits[0] - 6
132        fmt = "{" + (":{:d}.{:d}g".format(digits[0],decimals))+"}"
133    try:
134        return fmt.format(float(val)).strip()
135    except ValueError:
136        print ('FormatValue Error with val,maxdigits,fmt= %f %d %s'%(val,maxdigits,fmt))
137        return str(val)
138
139def FormatSigFigs(val, maxdigits=10, sigfigs=5, treatAsZero=1e-20):
140    '''Format a float to use ``maxdigits`` or fewer digits with ``sigfigs``
141    significant digits showing (if room allows).
142
143    :param float val: number to be formatted.
144
145    :param int maxdigits: the number of digits to be used for display of the
146       number (defaults to 10).
147
148    :param int sigfigs: the number of significant figures to use, if room allows
149
150    :param float treatAsZero: numbers that are less than this in magnitude
151      are treated as zero. Defaults to 1.0e-20, but this can be disabled
152      if set to None.
153
154    :returns: a string with <= maxdigits characters (I hope). 
155    '''
156    if 'str' in str(type(val)) and (val == '?' or val == '.'):
157        return val       
158    if treatAsZero is not None:
159        if abs(val) < treatAsZero:
160            return '0.0'
161    # negative numbers, leave room for a sign
162    if np.isnan(val):
163        return str(val)
164    if val < 0: maxdigits -= 1       
165    if abs(val) < 1e-99 or abs(val) > 9.999e99:
166        decimals = min(maxdigits-6,sigfigs)
167        fmt = "{" + (":{:d}.{:d}g".format(maxdigits,decimals))+"}" # create format string
168    elif abs(val) < 1e-9 or abs(val) > 9.999e9:
169        decimals = min(maxdigits-5,sigfigs)
170        fmt = "{" + (":{:d}.{:d}g".format(maxdigits,decimals))+"}"
171    elif abs(val) < 9.9999999*10**(sigfigs-maxdigits):
172        decimals = min(maxdigits-5,sigfigs)
173        fmt = "{" + (":{:d}.{:d}g".format(maxdigits,decimals))+"}"
174    elif abs(val) >= 10**sigfigs: # deal with large numbers in smaller spaces
175        decimals = min(maxdigits-5,sigfigs)
176        fmt = "{" + (":{:d}.{:d}g".format(maxdigits,decimals))+"}"
177    elif abs(val) < 1: # small numbers, add to decimal places
178        decimals = sigfigs - int(np.log10(np.abs(val)))
179        fmt = "{" + (":{:d}.{:d}f".format(maxdigits,decimals))+"}"
180    else: # larger numbers, remove decimal places
181        decimals = sigfigs - 1 - int(np.log10(np.abs(val)))
182        if decimals <= 0: 
183            fmt = "{" + (":{:d}.0f".format(maxdigits))+"}."
184        else:
185            fmt = "{" + (":{:d}.{:d}f".format(maxdigits,decimals))+"}"
186    try:
187        return fmt.format(float(val)).strip()
188    except ValueError:
189        print ('FormatValue Error with val,maxdigits, sigfigs, fmt=%f %d %d %s'%(val, maxdigits,sigfigs, fmt))
190        return str(val)
191
192if __name__ == '__main__':
193    for i in (1.23456789e-129,1.23456789e129,1.23456789e-99,1.23456789e99,-1.23456789e-99,-1.23456789e99):
194        print (FormatSigFigs(i),i)
195    for i in (1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000):
196        print (FormatSigFigs(1.23456789e-9*i),1.23456789e-9*i)
197    for i in (1,10,100,1000,10000,100000,1000000,10000000,100000000):
198        print (FormatSigFigs(1.23456789e9/i),1.23456789e9/i)
199
200    print (FormatSigFigs(200,10,3))
Note: See TracBrowser for help on using the repository browser.