1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 # The author may be contacted at <wking@drexel.edu> on the Internet, or
17 # write to Trevor King, Drexel University, Physics Dept., 3141 Chestnut St.,
18 # Philadelphia PA 19104, USA.
20 """Experiment vs. simulation comparison error bar calculation.
25 from .histogram import Histogram
26 from .parameter_scan import (
27 EXAMPLE_HISTOGRAM_FILE_CONTENTS, HistogramMatcher,
28 parse_param_ranges_string)
29 from .sawsim_histogram import sawsim_histogram
30 from .sawsim import SawsimRunner
33 def find_error_bounds(histogram_matcher, param_range, log_scale, threshold):
34 if log_scale == False:
35 ps = numpy.linspace(*param_range)
38 ps = numpy.exp(numpy.linspace(numpy.log(m), numpy.log(M), n))
41 for i,p in enumerate(ps):
42 residual[p] = histogram_matcher.residual([p])
45 r_best = min(residual.itervalues())
46 p_best = min([p for p,r in residual.iteritems() if r == r_best])
47 point['best fit'] = (p_best, r_best)
48 assert r_best <= threshold, (
49 'min residual %g (at %g) is over the threshold of %g'
50 % (r_best, p_best, threshold))
53 if residual[p] < threshold:
54 assert p_last != None, (
55 'range did not bracket lower error bound: residual(%g) = %g < %g'
56 % (p, residual[p], threshold))
57 point['error (low)'] = p_last, residual[p_last]
61 for p in reversed(ps):
62 if residual[p] < threshold:
63 assert p_last != None, (
64 'range did not bracket upper error bound: residual(%g) = %g < %g'
65 % (p, residual[p], threshold))
66 point['error (high)'] = p_last, residual[p_last]
69 return point, residual
75 >>> f = tempfile.NamedTemporaryFile()
76 >>> f.write(EXAMPLE_HISTOGRAM_FILE_CONTENTS)
78 >>> main(['-s', 'bin/sawsim',
79 ... '-r', '[1e-6,1e-4,3]', '--log',
80 ... '-N', '4', '-t', '0.8',
82 ... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_UDIFF
83 #parameter value\tresidual\tdescription
84 #1e-05\t0...\tbest fit
85 #0.0001\t1...\terror (high)
86 #1e-06\t1...\terror (low)
92 from optparse import OptionParser
100 usage = '%prog [options] histogram_file'
102 'Compare simulated results against experimental over a sweep of',
103 'a single parameter. Prints `(<paramter value>, <residual>)`',
104 'pairs for every tested point, with comments listing the',
105 'best-fit, + error bound, and - error bound. The histogram file',
106 'should look something like:',
108 EXAMPLE_HISTOGRAM_FILE_CONTENTS,
110 '`#HISTOGRAM: <params>` lines start each histogram. `params`',
111 'lists the `sawsim` parameters that are unique to that',
114 'Each histogram line is of the format:',
116 '<bin_edge><whitespace><count>',
118 '`<bin_edge>` should mark the left-hand side of the bin, and',
119 'all bins should be of equal width (so we know where the last',
122 parser = OptionParser(usage, epilog=epilog)
123 for option in sr.optparse_options:
124 if option.dest == 'param_string':
126 parser.add_option(option)
127 parser.add_option('-f','--param-format', dest='param_format',
129 help='Convert params to sawsim options (%default).',
130 default=('-s cantilever,hooke,0.05 -N1 -s folded,null -N8 -s "unfolded,wlc,{0.39e-9,28e-9}" -k "folded,unfolded,bell,{%g,0.25e-9}" -q folded'))
131 parser.add_option('-r','--param-range', dest='param_range',
133 help='Param range for plotting (%default).',
134 default='[1e-5,1e-3,30]')
135 parser.add_option('--log', dest='log',
136 help='Use a log scale for the parameter range.',
137 default=False, action='store_true')
138 parser.add_option('-R','--residual', dest='residual',
140 help='Residual type (from %s; default: %%default).'
141 % ', '.join(Histogram().types()),
142 default='jensen-shannon')
143 parser.add_option('-P','--plot-residuals', dest='plot_residuals',
144 help='Generate residual difference plots for each point in the plot range.',
145 default=False, action='store_true')
146 parser.add_option('-t', '--threshold', dest='threshold',
147 metavar='FLOAT', type='float', default='0.2',
148 help='Residual value that defines the location of the error bar (%default).')
150 options,args = parser.parse_args(argv)
152 param_ranges = parse_param_ranges_string(options.param_range)
153 assert len(param_ranges) == 1, param_ranges
154 param_range = param_ranges[0]
156 histogram_file = args[0]
157 sr_call_params = sr.initialize_from_options(options)
160 hm = HistogramMatcher(
161 file(histogram_file, 'r'),
162 param_format_string=options.param_format,
163 sawsim_runner=sr, residual_type=options.residual,
164 plot=options.plot_residuals, **sr_call_params)
165 points,residuals = find_error_bounds(hm, param_range, options.log, options.threshold)
169 print '#%s' % '\t'.join(['parameter value', 'residual', 'description'])
170 for key,value in sorted(points.iteritems()):
171 print '#%s' % '\t'.join([str(x) for x in [value[0], value[1], key]])
172 for key,value in sorted(residuals.iteritems()):
173 print '%s' % '\t'.join([str(x) for x in [key, value]])