diffpy.srfit 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. diffpy/__init__.py +15 -0
  2. diffpy/srfit/__init__.py +41 -0
  3. diffpy/srfit/equation/__init__.py +31 -0
  4. diffpy/srfit/equation/builder.py +702 -0
  5. diffpy/srfit/equation/equationmod.py +213 -0
  6. diffpy/srfit/equation/literals/__init__.py +71 -0
  7. diffpy/srfit/equation/literals/abcs.py +81 -0
  8. diffpy/srfit/equation/literals/argument.py +76 -0
  9. diffpy/srfit/equation/literals/literal.py +69 -0
  10. diffpy/srfit/equation/literals/operators.py +380 -0
  11. diffpy/srfit/equation/visitors/__init__.py +94 -0
  12. diffpy/srfit/equation/visitors/argfinder.py +62 -0
  13. diffpy/srfit/equation/visitors/printer.py +120 -0
  14. diffpy/srfit/equation/visitors/swapper.py +142 -0
  15. diffpy/srfit/equation/visitors/validator.py +113 -0
  16. diffpy/srfit/equation/visitors/visitor.py +62 -0
  17. diffpy/srfit/exceptions.py +29 -0
  18. diffpy/srfit/fitbase/__init__.py +53 -0
  19. diffpy/srfit/fitbase/calculator.py +110 -0
  20. diffpy/srfit/fitbase/configurable.py +57 -0
  21. diffpy/srfit/fitbase/constraint.py +117 -0
  22. diffpy/srfit/fitbase/fitcontribution.py +351 -0
  23. diffpy/srfit/fitbase/fithook.py +227 -0
  24. diffpy/srfit/fitbase/fitrecipe.py +826 -0
  25. diffpy/srfit/fitbase/fitresults.py +674 -0
  26. diffpy/srfit/fitbase/parameter.py +314 -0
  27. diffpy/srfit/fitbase/parameterset.py +110 -0
  28. diffpy/srfit/fitbase/profile.py +405 -0
  29. diffpy/srfit/fitbase/profilegenerator.py +168 -0
  30. diffpy/srfit/fitbase/profileparser.py +179 -0
  31. diffpy/srfit/fitbase/recipeorganizer.py +1074 -0
  32. diffpy/srfit/fitbase/restraint.py +117 -0
  33. diffpy/srfit/fitbase/simplerecipe.py +230 -0
  34. diffpy/srfit/fitbase/validatable.py +59 -0
  35. diffpy/srfit/interface/__init__.py +33 -0
  36. diffpy/srfit/interface/interface.py +180 -0
  37. diffpy/srfit/pdf/__init__.py +24 -0
  38. diffpy/srfit/pdf/basepdfgenerator.py +296 -0
  39. diffpy/srfit/pdf/characteristicfunctions.py +432 -0
  40. diffpy/srfit/pdf/debyepdfgenerator.py +115 -0
  41. diffpy/srfit/pdf/pdfcontribution.py +311 -0
  42. diffpy/srfit/pdf/pdfgenerator.py +77 -0
  43. diffpy/srfit/pdf/pdfparser.py +226 -0
  44. diffpy/srfit/sas/__init__.py +30 -0
  45. diffpy/srfit/sas/prcalculator.py +139 -0
  46. diffpy/srfit/sas/sasgenerator.py +70 -0
  47. diffpy/srfit/sas/sasimport.py +66 -0
  48. diffpy/srfit/sas/sasparameter.py +70 -0
  49. diffpy/srfit/sas/sasparser.py +133 -0
  50. diffpy/srfit/sas/sasprofile.py +94 -0
  51. diffpy/srfit/structure/__init__.py +60 -0
  52. diffpy/srfit/structure/basestructureparset.py +62 -0
  53. diffpy/srfit/structure/bvsrestraint.py +101 -0
  54. diffpy/srfit/structure/cctbxparset.py +292 -0
  55. diffpy/srfit/structure/diffpyparset.py +303 -0
  56. diffpy/srfit/structure/objcrystparset.py +1552 -0
  57. diffpy/srfit/structure/sgconstraints.py +761 -0
  58. diffpy/srfit/structure/srrealparset.py +92 -0
  59. diffpy/srfit/util/__init__.py +50 -0
  60. diffpy/srfit/util/argbinders.py +29 -0
  61. diffpy/srfit/util/inpututils.py +50 -0
  62. diffpy/srfit/util/nameutils.py +45 -0
  63. diffpy/srfit/util/observable.py +93 -0
  64. diffpy/srfit/util/tagmanager.py +150 -0
  65. diffpy/srfit/util/weakrefcallable.py +170 -0
  66. diffpy/srfit/version.py +26 -0
  67. diffpy_srfit-3.1.0.dist-info/METADATA +216 -0
  68. diffpy_srfit-3.1.0.dist-info/RECORD +73 -0
  69. diffpy_srfit-3.1.0.dist-info/WHEEL +5 -0
  70. diffpy_srfit-3.1.0.dist-info/licenses/AUTHORS.rst +10 -0
  71. diffpy_srfit-3.1.0.dist-info/licenses/LICENSE.rst +138 -0
  72. diffpy_srfit-3.1.0.dist-info/licenses/LICENSE_DANSE.rst +34 -0
  73. diffpy_srfit-3.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,674 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # diffpy.srfit by DANSE Diffraction group
5
+ # Simon J. L. Billinge
6
+ # (c) 2008 The Trustees of Columbia University
7
+ # in the City of New York. All rights reserved.
8
+ #
9
+ # File coded by: Chris Farrow, Pavol Juhas
10
+ #
11
+ # See AUTHORS.txt for a list of people who contributed.
12
+ # See LICENSE_DANSE.txt for license information.
13
+ #
14
+ ##############################################################################
15
+ """The FitResults and ContributionResults classes for storing results of a fit.
16
+
17
+ The FitResults class is used to display the current state of a
18
+ FitRecipe. It stores the state, and uses it to calculate useful
19
+ statistics, which can be displayed on screen or saved to file.
20
+ """
21
+
22
+ from __future__ import print_function
23
+
24
+ __all__ = ["FitResults", "ContributionResults", "initializeRecipe"]
25
+
26
+ import re
27
+ from collections import OrderedDict
28
+
29
+ import numpy
30
+
31
+ from diffpy.srfit.util import _DASHEDLINE
32
+ from diffpy.srfit.util import sortKeyForNumericString as numstr
33
+ from diffpy.srfit.util.inpututils import inputToString
34
+
35
+
36
+ class FitResults(object):
37
+ """Class for processing, presenting and storing results of a fit.
38
+
39
+ Attributes
40
+ recipe -- The recipe containing the results.
41
+ cov -- The covariance matrix from the recipe.
42
+ conresults -- An ordered dictionary of ContributionResults for each
43
+ FitContribution, indexed by the FitContribution name.
44
+ derivstep -- The fractional step size for calculating numeric
45
+ derivatives. Default 1e-8.
46
+ varnames -- Names of the variables in the recipe.
47
+ varvals -- Values of the variables in the recipe.
48
+ varunc -- Uncertainties in the variable values.
49
+ showfixed -- Show fixed variables (default True).
50
+ fixednames -- Names of the fixed variables of the recipe.
51
+ fixedvals -- Values of the fixed variables of the recipe.
52
+ showcon -- Show constraint values in the output (default False).
53
+ connames -- Names of the constrained parameters.
54
+ convals -- Values of the constrained parameters.
55
+ conunc -- Uncertainties in the constraint values.
56
+ residual -- The scalar residual of the recipe.
57
+ penalty -- The penalty to residual from the restraints.
58
+ chi2 -- The chi2 of the recipe.
59
+ cumchi2 -- The cumulative chi2 of the recipe.
60
+ rchi2 -- The reduced chi2 of the recipe.
61
+ rw -- The Rw of the recipe.
62
+ cumrw -- The cumulative Rw of the recipe.
63
+ messages -- A list of messages about the results.
64
+ precision -- The precision of numeric output (default 8).
65
+ _dcon -- The derivatives of the constraint equations with respect to
66
+ the variables. This is used internally.
67
+
68
+ Each of these attributes, except the recipe, are created or updated when
69
+ the update method is called.
70
+ """
71
+
72
+ def __init__(self, recipe, update=True, showfixed=True, showcon=False):
73
+ """Initialize the attributes.
74
+
75
+ recipe -- The recipe containing the results
76
+ update -- Flag indicating whether to do an immediate update (default
77
+ True).
78
+ showcon -- Show fixed variables in the output (default True).
79
+ showcon -- Show constraint values in the output (default False).
80
+ """
81
+ self.recipe = recipe
82
+ self.conresults = OrderedDict()
83
+ self.derivstep = 1e-8
84
+ self.varnames = []
85
+ self.varvals = []
86
+ self.varunc = []
87
+ self.fixednames = []
88
+ self.fixedvals = []
89
+ self.connames = []
90
+ self.convals = []
91
+ self.conunc = []
92
+ self.cov = None
93
+ self.residual = 0
94
+ self.penalty = 0
95
+ self.chi2 = 0
96
+ self.rchi2 = 0
97
+ self.rw = 0
98
+ self.precision = 8
99
+ self._dcon = []
100
+ self.messages = []
101
+
102
+ self.showfixed = bool(showfixed)
103
+ self.showcon = bool(showcon)
104
+
105
+ if update:
106
+ self.update()
107
+ return
108
+
109
+ def update(self):
110
+ """Update the results according to the current state of the recipe."""
111
+ # Note that the order of these operations are chosen to reduce
112
+ # computation time.
113
+
114
+ recipe = self.recipe
115
+
116
+ if not recipe._contributions:
117
+ return
118
+
119
+ # Make sure everything is ready for calculation
120
+ recipe._prepare()
121
+
122
+ # Store the variable names and values
123
+ self.varnames = recipe.getNames()
124
+ self.varvals = recipe.getValues()
125
+ fixedpars = recipe._tagmanager.union(recipe._fixedtag)
126
+ fixedpars = [p for p in fixedpars if not p.constrained]
127
+ self.fixednames = [p.name for p in fixedpars]
128
+ self.fixedvals = [p.value for p in fixedpars]
129
+
130
+ # Store the constraint information
131
+ self.connames = [con.par.name for con in recipe._oconstraints]
132
+ self.convals = [con.par.getValue() for con in recipe._oconstraints]
133
+
134
+ if self.varnames:
135
+ # Calculate the covariance
136
+ self._calculateCovariance()
137
+
138
+ # Get the variable uncertainties
139
+ self.varunc = [
140
+ self.cov[i, i] ** 0.5 for i in range(len(self.varnames))
141
+ ]
142
+
143
+ # Get the constraint uncertainties
144
+ self._calculateConstraintUncertainties()
145
+
146
+ # Store the fitting arrays and metrics for each FitContribution.
147
+ self.conresults = OrderedDict()
148
+ for con, weight in zip(
149
+ recipe._contributions.values(), recipe._weights
150
+ ):
151
+ self.conresults[con.name] = ContributionResults(con, weight, self)
152
+
153
+ # Calculate the metrics
154
+ res = recipe.residual()
155
+ self.residual = numpy.dot(res, res)
156
+ self._calculateMetrics()
157
+
158
+ # Calculate the restraints penalty
159
+ w = self.chi2 / len(res)
160
+ self.penalty = sum([r.penalty(w) for r in recipe._restraintlist])
161
+
162
+ return
163
+
164
+ def _calculateCovariance(self):
165
+ """Calculate the covariance matrix. This is called by update.
166
+
167
+ This code borrowed from PARK. It finds the pseudo-inverse of the
168
+ Jacobian using the singular value decomposition.
169
+ """
170
+ try:
171
+ J = self._calculateJacobian()
172
+ u, s, vh = numpy.linalg.svd(J, 0)
173
+ self.cov = numpy.dot(vh.T.conj() / s**2, vh)
174
+ except numpy.linalg.LinAlgError:
175
+ self.messages.append("Cannot compute covariance matrix.")
176
+ lvarvals = len(self.varvals)
177
+ self.cov = numpy.zeros((lvarvals, lvarvals), dtype=float)
178
+ return
179
+
180
+ def _calculateJacobian(self):
181
+ """Calculate the Jacobian for the fitting.
182
+
183
+ Adapted from PARK. Returns the derivative wrt the fit variables
184
+ at point p.
185
+
186
+ This also calculates the derivatives of the constrained
187
+ parameters while we're at it.
188
+
189
+ Numeric derivatives are calculated based on step, where step is
190
+ the portion of variable value. E.g. step = dv/v.
191
+ """
192
+ recipe = self.recipe
193
+ step = self.derivstep
194
+
195
+ # Make sure the input vector is an array
196
+ pvals = numpy.asarray(self.varvals)
197
+ # Compute the numeric derivative using the center point formula.
198
+ delta = step * pvals
199
+
200
+ # Center point formula:
201
+ # df/dv = lim_{h->0} ( f(v+h)-f(v-h) ) / ( 2h )
202
+ #
203
+
204
+ r = []
205
+ # The list of constraint derivatives with respect to variables
206
+ # The forward difference would be faster, but perhaps not as accurate.
207
+ conr = []
208
+ for k, v in enumerate(pvals):
209
+ h = delta[k]
210
+ pvals[k] = v + h
211
+ rk = self.recipe.residual(pvals)
212
+
213
+ # The constraints derivatives
214
+ cond = []
215
+ for con in recipe._oconstraints:
216
+ con.update()
217
+ cond.append(con.par.getValue())
218
+
219
+ pvals[k] = v - h
220
+ rk -= self.recipe.residual(pvals)
221
+
222
+ # FIXME - constraints are used for vectors as well!
223
+ for i, con in enumerate(recipe._oconstraints):
224
+ con.update()
225
+ val = con.par.getValue()
226
+ if numpy.isscalar(val):
227
+ cond[i] -= con.par.getValue()
228
+ cond[i] /= 2 * h
229
+ else:
230
+ cond[i] = 0.0
231
+
232
+ conr.append(cond)
233
+
234
+ pvals[k] = v
235
+ r.append(rk / (2 * h))
236
+
237
+ # Reset the constrained parameters to their original values
238
+ for con in recipe._oconstraints:
239
+ con.update()
240
+
241
+ self._dcon = numpy.vstack(conr).T
242
+
243
+ # return the jacobian
244
+ jac = numpy.vstack(r).T
245
+ return jac
246
+
247
+ def _calculateMetrics(self):
248
+ """Calculate chi2, cumchi2, rchi2, rw and cumrw for the recipe."""
249
+ cumchi2 = numpy.array([], dtype=float)
250
+ # total weighed denominator for the ratio in the Rw formula
251
+ yw2tot = 0.0
252
+ numpoints = 0
253
+ for con in self.conresults.values():
254
+ cc2w = con.weight * con.cumchi2
255
+ c2last = cumchi2[-1:].sum()
256
+ cumchi2 = numpy.concatenate([cumchi2, c2last + cc2w])
257
+ yw2tot += con.weight * (con.chi2 / con.rw**2)
258
+ numpoints += len(con.x)
259
+
260
+ chi2 = cumchi2[-1:].sum()
261
+ cumrw = numpy.sqrt(cumchi2 / yw2tot)
262
+ rw = cumrw[-1:].sum()
263
+
264
+ numpoints += len(self.recipe._restraintlist)
265
+ rchi2 = chi2 / (numpoints - len(self.varnames))
266
+
267
+ self.chi2 = chi2
268
+ self.rchi2 = rchi2
269
+ self.rw = rw
270
+ self.cumchi2 = cumchi2
271
+ self.cumrw = cumrw
272
+ return
273
+
274
+ def _calculateConstraintUncertainties(self):
275
+ """Calculate the uncertainty on the constrained parameters."""
276
+ vu = self.varunc
277
+
278
+ # sig^2(c) = sum_i sum_j sig(v_i) sig(v_j) (dc/dv_i)(dc/dv_j)
279
+ # sig^2(c) = sum_i sum_j [sig(v_i)(dc/dv_i)][sig(v_j)(dc/dv_j)]
280
+ # sig^2(c) = sum_i sum_j u_i u_j
281
+ self.conunc = []
282
+ for dci in self._dcon:
283
+
284
+ # Create sig(v_i) (dc/dv_i) array.
285
+ u = dci * vu
286
+ # The outer product is all possible pairings of u_i and u_j
287
+ # uu_ij = u_i u_j
288
+ uu = numpy.outer(u, u)
289
+ # Sum these pairings to get sig^2(c)
290
+ sig2c = sum(uu.flatten())
291
+
292
+ self.conunc.append(sig2c**0.5)
293
+ return
294
+
295
+ def formatResults(self, header="", footer="", update=False):
296
+ """Format the results and return them in a string.
297
+
298
+ This function is called by printResults and saveResults. Overloading
299
+ the formatting here will change all three functions.
300
+
301
+ header -- A header to add to the output (default "")
302
+ footer -- A footer to add to the output (default "")
303
+ update -- Flag indicating whether to call update() (default False).
304
+
305
+ Returns a string containing the formatted results.
306
+ """
307
+ if update:
308
+ self.update()
309
+
310
+ lines = []
311
+ corrmin = 0.25
312
+ p = self.precision
313
+ pe = "%-" + "%i.%ie" % (p + 6, p)
314
+ pet = "%" + ".%ie" % (p,)
315
+ # Check to see if the uncertainty values are reliable.
316
+ certain = True
317
+ for con in self.conresults.values():
318
+ if (con.dy == 1).all():
319
+ certain = False
320
+ break
321
+
322
+ # User-defined header
323
+ if header:
324
+ lines.append(header)
325
+
326
+ if not certain:
327
+ err_msg = (
328
+ "Some quantities invalid due to missing profile uncertainty"
329
+ )
330
+ if err_msg not in self.messages:
331
+ self.messages.append(err_msg)
332
+
333
+ lines.extend(self.messages)
334
+
335
+ # Overall results
336
+ err_msg = "Overall"
337
+ if not certain:
338
+ err_msg += " (Chi2 and Reduced Chi2 invalid)"
339
+ lines.append(err_msg)
340
+ lines.append(_DASHEDLINE)
341
+ formatstr = "%-14s %.8f"
342
+ lines.append(formatstr % ("Residual", self.residual))
343
+ lines.append(
344
+ formatstr % ("Contributions", self.residual - self.penalty)
345
+ )
346
+ lines.append(formatstr % ("Restraints", self.penalty))
347
+ lines.append(formatstr % ("Chi2", self.chi2))
348
+ lines.append(formatstr % ("Reduced Chi2", self.rchi2))
349
+ lines.append(formatstr % ("Rw", self.rw))
350
+
351
+ # Per-FitContribution results
352
+ if len(self.conresults) > 1:
353
+ keys = list(self.conresults.keys())
354
+ keys.sort(key=numstr)
355
+
356
+ lines.append("")
357
+ err_msg = "Contributions"
358
+ if not certain:
359
+ err_msg += " (Chi2 and Reduced Chi2 invalid)"
360
+ lines.append(err_msg)
361
+ lines.append(_DASHEDLINE)
362
+ formatstr = "%-10s %-42.8f"
363
+ for name in keys:
364
+ res = self.conresults[name]
365
+ lines.append("")
366
+ namestr = name + " (%f)" % res.weight
367
+ lines.append(namestr)
368
+ lines.append("-" * len(namestr))
369
+ lines.append(formatstr % ("Residual", res.residual))
370
+ lines.append(formatstr % ("Chi2", res.chi2))
371
+ lines.append(formatstr % ("Rw", res.rw))
372
+
373
+ # The variables
374
+ if self.varnames:
375
+ lines.append("")
376
+ err_msg = "Variables"
377
+ if not certain:
378
+ err_msg2 = "Uncertainties invalid"
379
+ err_msg += " (%s)" % err_msg2
380
+ lines.append(err_msg)
381
+ lines.append(_DASHEDLINE)
382
+
383
+ varnames = self.varnames
384
+ varvals = self.varvals
385
+ varunc = self.varunc
386
+ varlines = []
387
+
388
+ w = max(map(len, varnames))
389
+ w = str(w + 1)
390
+ # Format the lines
391
+ formatstr = "%-" + w + "s " + pe + " +/- " + pet
392
+ for name, val, unc in zip(varnames, varvals, varunc):
393
+ varlines.append(formatstr % (name, val, unc))
394
+
395
+ varlines.sort()
396
+ lines.extend(varlines)
397
+
398
+ # Fixed variables
399
+ if self.showfixed and self.fixednames:
400
+ varlines = []
401
+ lines.append("")
402
+ lines.append("Fixed Variables")
403
+ lines.append(_DASHEDLINE)
404
+ w = max(map(len, self.fixednames))
405
+ w = str(w + 1)
406
+ formatstr = "%-" + w + "s " + pet
407
+ for name, val in zip(self.fixednames, self.fixedvals):
408
+ varlines.append(formatstr % (name, val))
409
+ varlines.sort()
410
+ lines.extend(varlines)
411
+
412
+ # The constraints
413
+ if self.connames and self.showcon:
414
+ lines.append("")
415
+ err_msg = "Constrained Parameters"
416
+ if not certain:
417
+ err_msg += " (Uncertainties invalid)"
418
+ lines.append(err_msg)
419
+ lines.append(_DASHEDLINE)
420
+
421
+ w = 0
422
+ keys = []
423
+ vals = {}
424
+ for con in self.conresults.values():
425
+ for i, loc in enumerate(con.conlocs):
426
+ names = [obj.name for obj in loc]
427
+ name = ".".join(names)
428
+ w = max(w, len(name))
429
+ val = con.convals[i]
430
+ unc = con.conunc[i]
431
+ keys.append(name)
432
+ vals[name] = (val, unc)
433
+
434
+ keys.sort(key=numstr)
435
+ w = str(w + 1)
436
+ formatstr = "%-" + w + "s %- 15f +/- %-15f"
437
+ for name in keys:
438
+ val, unc = vals[name]
439
+ lines.append(formatstr % (name, val, unc))
440
+
441
+ # Variable correlations
442
+ lines.append("")
443
+ corint = int(corrmin * 100)
444
+ err_msg = "Variable Correlations greater than %i%%" % corint
445
+ if not certain:
446
+ err_msg += " (Correlations invalid)"
447
+ lines.append(err_msg)
448
+ lines.append(_DASHEDLINE)
449
+ tup = []
450
+ cornames = []
451
+ n = len(self.varnames)
452
+ for i in range(n):
453
+ for j in range(i + 1, n):
454
+ name = "corr(%s, %s)" % (varnames[i], varnames[j])
455
+ val = self.cov[i, j] / (self.cov[i, i] * self.cov[j, j]) ** 0.5
456
+ if abs(val) > corrmin:
457
+ cornames.append(name)
458
+ tup.append((val, name))
459
+
460
+ tup.sort(key=lambda vn: abs(vn[0]))
461
+ tup.reverse()
462
+
463
+ if cornames:
464
+ w = max(map(len, cornames))
465
+ w = str(w + 1)
466
+ formatstr = "%-" + w + "s %.4f"
467
+ for val, name in tup:
468
+ lines.append(formatstr % (name, val))
469
+ else:
470
+ lines.append("No correlations greater than %i%%" % corint)
471
+
472
+ # User-defined footer
473
+ if footer:
474
+ lines.append(footer)
475
+
476
+ out = "\n".join(lines) + "\n"
477
+ return out
478
+
479
+ def printResults(self, header="", footer="", update=False):
480
+ """Format and print the results.
481
+
482
+ header -- A header to add to the output (default "")
483
+ footer -- A footer to add to the output (default "")
484
+ update -- Flag indicating whether to call update() (default False).
485
+ """
486
+ print(self.formatResults(header, footer, update).rstrip())
487
+ return
488
+
489
+ def __str__(self):
490
+ return self.formatResults()
491
+
492
+ def saveResults(self, filename, header="", footer="", update=False):
493
+ """Format and save the results.
494
+
495
+ filename - Name of the save file.
496
+ header -- A header to add to the output (default "")
497
+ footer -- A footer to add to the output (default "")
498
+ update -- Flag indicating whether to call update() (default False).
499
+ """
500
+ # Save the time and user
501
+ from getpass import getuser
502
+ from time import ctime
503
+
504
+ myheader = "Results written: " + ctime() + "\n"
505
+ myheader += "produced by " + getuser() + "\n"
506
+ header = myheader + header
507
+
508
+ res = self.formatResults(header, footer, update)
509
+ f = open(filename, "w")
510
+ f.write(res)
511
+ f.close()
512
+ return
513
+
514
+
515
+ # End class FitResults
516
+
517
+
518
+ class ContributionResults(object):
519
+ """Class for processing, storing FitContribution results.
520
+
521
+ This does not store the FitContribution.
522
+
523
+ Attributes
524
+ y -- The FitContribution's profile over the calculation range
525
+ (default None).
526
+ dy -- The uncertainty in the FitContribution's profile over the
527
+ calculation range (default None).
528
+ x -- A numpy array of the calculated independent variable for the
529
+ FitContribution (default None).
530
+ ycalc -- A numpy array of the calculated signal for the FitContribution
531
+ (default None).
532
+ residual -- The scalar residual of the FitContribution.
533
+ chi2 -- The chi2 of the FitContribution.
534
+ cumchi2 -- The cumulative chi2 of the FitContribution.
535
+ rw -- The Rw of the FitContribution.
536
+ cumrw -- The cumulative Rw of the FitContribution.
537
+ weight -- The weight of the FitContribution in the recipe.
538
+ conlocs -- The location of the constrained parameters in the
539
+ FitContribution (see the
540
+ RecipeContainer._locateManagedObject method).
541
+ convals -- Values of the constrained parameters.
542
+ conunc -- Uncertainties in the constraint values.
543
+ """
544
+
545
+ def __init__(self, con, weight, fitres):
546
+ """Initialize the attributes.
547
+
548
+ con -- The FitContribution
549
+ weight -- The weight of the FitContribution in the recipe
550
+ fitres -- The FitResults instance to contain this ContributionResults
551
+ """
552
+ self.x = None
553
+ self.y = None
554
+ self.dy = None
555
+ self.ycalc = None
556
+ self.residual = 0
557
+ self.chi2 = 0
558
+ self.rw = 0
559
+ self.weight = 0
560
+ self.conlocs = []
561
+ self.convals = []
562
+ self.conunc = []
563
+ self._init(con, weight, fitres)
564
+ return
565
+
566
+ def _init(self, con, weight, fitres):
567
+ """Initialize the attributes, for real."""
568
+ # Note that the order of these operations is chosen to reduce
569
+ # computation time.
570
+
571
+ if con.profile is None:
572
+ return
573
+
574
+ recipe = fitres.recipe
575
+
576
+ # Store the weight
577
+ self.weight = weight
578
+
579
+ # First the residual
580
+ res = con.residual()
581
+ self.residual = numpy.dot(res, res)
582
+
583
+ # The arrays
584
+ self.x = numpy.array(con.profile.x)
585
+ self.y = numpy.array(con.profile.y)
586
+ self.dy = numpy.array(con.profile.dy)
587
+ self.ycalc = numpy.array(con.profile.ycalc)
588
+
589
+ # The other metrics
590
+ self._calculateMetrics()
591
+
592
+ # Find the parameters
593
+ for i, constraint in enumerate(recipe._oconstraints):
594
+ par = constraint.par
595
+ loc = con._locateManagedObject(par)
596
+ if loc:
597
+ self.conlocs.append(loc)
598
+ self.convals.append(fitres.convals[i])
599
+ self.conunc.append(fitres.conunc[i])
600
+
601
+ return
602
+
603
+ # FIXME: factor rw, chi2, cumrw, cumchi2 to separate functions.
604
+ def _calculateMetrics(self):
605
+ """Calculate chi2 and Rw of the recipe."""
606
+ # We take absolute values in case the signal is complex
607
+ num = numpy.abs(self.y - self.ycalc)
608
+ y = numpy.abs(self.y)
609
+ chiv = num / self.dy
610
+ self.cumchi2 = numpy.cumsum(chiv**2)
611
+ # avoid index error for empty array
612
+ self.chi2 = self.cumchi2[-1:].sum()
613
+ yw = y / self.dy
614
+ yw2tot = numpy.dot(yw, yw)
615
+ if yw2tot == 0.0:
616
+ yw2tot = 1.0
617
+ self.cumrw = numpy.sqrt(self.cumchi2 / yw2tot)
618
+ # avoid index error for empty array
619
+ self.rw = self.cumrw[-1:].sum()
620
+ return
621
+
622
+
623
+ # End class ContributionResults
624
+
625
+
626
+ def resultsDictionary(results):
627
+ """Get dictionary of results from file.
628
+
629
+ This reads the results from file and stores them in a dictionary to be
630
+ returned to the caller. The dictionary may contain non-result entries.
631
+
632
+ results -- An open file-like object, name of a file that contains
633
+ results from FitResults or a string containing fit results.
634
+ """
635
+ resstr = inputToString(results)
636
+
637
+ rx = {
638
+ "f": r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?",
639
+ "n": r"[a-zA-Z_]\w*",
640
+ }
641
+ pat = r"(%(n)s)\s+(%(f)s)" % rx
642
+
643
+ matches = re.findall(pat, resstr)
644
+ # Prefer the first match
645
+ matches.reverse()
646
+ mpairs = dict(matches)
647
+ return mpairs
648
+
649
+
650
+ def initializeRecipe(recipe, results):
651
+ """Initialize the variables of a recipe from a results file.
652
+
653
+ This reads the results from file and initializes any variables (fixed or
654
+ free) in the recipe to the results values. Note that the recipe has to be
655
+ configured, with variables. This does not reconstruct a FitRecipe.
656
+
657
+ recipe -- A configured recipe with variables
658
+ results -- An open file-like object, name of a file that contains
659
+ results from FitResults or a string containing fit results.
660
+ """
661
+
662
+ mpairs = resultsDictionary(results)
663
+ if not mpairs:
664
+ raise AttributeError("Cannot find results")
665
+
666
+ # Get variable names
667
+ names = recipe._parameters.keys()
668
+ for vname in names:
669
+ value = mpairs.get(vname)
670
+ if value is not None:
671
+ var = recipe.get(vname)
672
+ var.value = float(value)
673
+
674
+ return