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,405 @@
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
10
+ #
11
+ # See AUTHORS.txt for a list of people who contributed.
12
+ # See LICENSE_DANSE.txt for license information.
13
+ #
14
+ ##############################################################################
15
+ """The Profile class containing the physical and calculated data.
16
+
17
+ Profile holds the arrays representing an observed profile, a selected
18
+ subset of the observed profile and a calculated profile. Profiles are
19
+ used by Calculators to store a calculated signal, and by
20
+ FitContributions to help calculate a residual equation.
21
+ """
22
+
23
+ __all__ = ["Parameter", "Profile"]
24
+
25
+ import numpy
26
+ import six
27
+
28
+ from diffpy.srfit.exceptions import SrFitError
29
+ from diffpy.srfit.fitbase.parameter import Parameter
30
+ from diffpy.srfit.fitbase.validatable import Validatable
31
+ from diffpy.srfit.util.observable import Observable
32
+
33
+ # This is the roundoff tolerance for selecting bounds on arrays.
34
+ epsilon = 1e-8
35
+
36
+
37
+ class Profile(Observable, Validatable):
38
+ """Observed and calculated profile container.
39
+
40
+ Profile is an Observable. The xpar, ypar and dypar attributes are observed
41
+ by the Profile, which can in turn be observed by some other object.
42
+
43
+ Attributes
44
+
45
+ _xobs -- A numpy array of the observed independent variable (default
46
+ None)
47
+ xobs -- Read-only property of _xobs.
48
+ _yobs -- A numpy array of the observed signal (default None)
49
+ yobs -- Read-only property of _yobs.
50
+ _dyobs -- A numpy array of the uncertainty of the observed signal
51
+ (default None, optional).
52
+ dyobs -- Read-only property of _dyobs.
53
+ x -- A numpy array of the calculated independent variable (default
54
+ None, property for xpar accessors).
55
+ y -- The profile over the calculation range (default None, property
56
+ for ypar accessors).
57
+ dy -- The uncertainty in the profile over the calculation range
58
+ (default None, property for dypar accessors).
59
+ ycalc -- A numpy array of the calculated signal (default None).
60
+ xpar -- A Parameter that stores x (named "x").
61
+ ypar -- A Parameter that stores y (named "y").
62
+ dypar -- A Parameter that stores dy (named "dy").
63
+ ycpar -- A Parameter that stores ycalc (named "ycalc"). This is
64
+ not observed by the profile, but it is present so it can be
65
+ constrained to.
66
+ meta -- A dictionary of metadata. This is only set if provided by a
67
+ parser.
68
+ """
69
+
70
+ def __init__(self):
71
+ """Initialize the attributes."""
72
+ Observable.__init__(self)
73
+ self._xobs = None
74
+ self._yobs = None
75
+ self._dyobs = None
76
+ self.xpar = Parameter("x")
77
+ self.ypar = Parameter("y")
78
+ self.dypar = Parameter("dy")
79
+ self.ycpar = Parameter("ycalc")
80
+ self.meta = {}
81
+
82
+ # Observable
83
+ self.xpar.addObserver(self._flush)
84
+ self.ypar.addObserver(self._flush)
85
+ self.dypar.addObserver(self._flush)
86
+ return
87
+
88
+ # We want x, y, ycalc and dy to stay in-sync with xpar, ypar and dypar
89
+ x = property(
90
+ lambda self: self.xpar.getValue(),
91
+ lambda self, val: self.xpar.setValue(val),
92
+ )
93
+ y = property(
94
+ lambda self: self.ypar.getValue(),
95
+ lambda self, val: self.ypar.setValue(val),
96
+ )
97
+ dy = property(
98
+ lambda self: self.dypar.getValue(),
99
+ lambda self, val: self.dypar.setValue(val),
100
+ )
101
+ ycalc = property(
102
+ lambda self: self.ycpar.getValue(),
103
+ lambda self, val: self.ycpar.setValue(val),
104
+ )
105
+
106
+ # We want xobs, yobs and dyobs to be read-only
107
+ xobs = property(lambda self: self._xobs)
108
+ yobs = property(lambda self: self._yobs)
109
+ dyobs = property(lambda self: self._dyobs)
110
+
111
+ def loadParsedData(self, parser):
112
+ """Load parsed data from a ProfileParser.
113
+
114
+ This sets the xobs, yobs, dyobs arrays as well as the metadata.
115
+ """
116
+ x, y, junk, dy = parser.getData()
117
+ self.meta = dict(parser.getMetaData())
118
+ self.setObservedProfile(x, y, dy)
119
+ return
120
+
121
+ def setObservedProfile(self, xobs, yobs, dyobs=None):
122
+ """Set the observed profile.
123
+
124
+ Arguments
125
+ xobs -- Numpy array of the independent variable
126
+ yobs -- Numpy array of the observed signal.
127
+ dyobs -- Numpy array of the uncertainty in the observed signal. If
128
+ dyobs is None (default), it will be set to 1 at each
129
+ observed xobs.
130
+
131
+ Raises ValueError if len(yobs) != len(xobs)
132
+ Raises ValueError if dyobs != None and len(dyobs) != len(xobs)
133
+ """
134
+ if len(yobs) != len(xobs):
135
+ raise ValueError("xobs and yobs are different lengths")
136
+ if dyobs is not None and len(dyobs) != len(xobs):
137
+ raise ValueError("xobs and dyobs are different lengths")
138
+
139
+ self._xobs = numpy.asarray(xobs, dtype=float)
140
+ self._yobs = numpy.asarray(yobs, dtype=float)
141
+
142
+ if dyobs is None:
143
+ self._dyobs = numpy.ones_like(xobs)
144
+ else:
145
+ self._dyobs = numpy.asarray(dyobs, dtype=float)
146
+
147
+ # Set the default calculation points
148
+ if self.x is None:
149
+ self.setCalculationPoints(self._xobs)
150
+ else:
151
+ self.setCalculationPoints(self.x)
152
+
153
+ return
154
+
155
+ def setCalculationRange(self, xmin=None, xmax=None, dx=None):
156
+ """Set epsilon-inclusive calculation range.
157
+
158
+ Adhere to the observed ``xobs`` points when ``dx`` is the same
159
+ as in the data. ``xmin`` and ``xmax`` are clipped at the bounds
160
+ of the observed data.
161
+
162
+ Parameters
163
+ ----------
164
+
165
+ xmin : float or "obs", optional
166
+ The minimum value of the independent variable. Keep the
167
+ current minimum when not specified. If specified as "obs"
168
+ reset to the minimum observed value.
169
+ xmax : float or "obs", optional
170
+ The maximum value of the independent variable. Keep the
171
+ current maximum when not specified. If specified as "obs"
172
+ reset to the maximum observed value.
173
+ dx : float or "obs", optional
174
+ The sample spacing in the independent variable. When different
175
+ from the data, resample the ``x`` as anchored at ``xmin``.
176
+
177
+ Note that xmin is always inclusive (unless clipped). xmax is inclusive
178
+ if it is within the bounds of the observed data.
179
+
180
+ Raises
181
+ ------
182
+ AttributeError
183
+ If there is no observed data.
184
+ ValueError
185
+ When xmin > xmax or if dx <= 0. Also if dx > xmax - xmin.
186
+ """
187
+ if self.xobs is None:
188
+ raise AttributeError("No observed profile")
189
+
190
+ # local helper function
191
+ def _isobs(a):
192
+ if not isinstance(a, six.string_types):
193
+ return False
194
+ if a != "obs":
195
+ raise ValueError('Must be either float or "obs".')
196
+ return True
197
+
198
+ # resolve new low and high bounds for x
199
+ lo = (
200
+ self.x[0]
201
+ if xmin is None
202
+ else self.xobs[0] if _isobs(xmin) else float(xmin)
203
+ )
204
+ lo = max(lo, self.xobs[0])
205
+ hi = (
206
+ self.x[-1]
207
+ if xmax is None
208
+ else self.xobs[-1] if _isobs(xmax) else float(xmax)
209
+ )
210
+ hi = min(hi, self.xobs[-1])
211
+ # determine if we need to clip the original grid
212
+ clip = True
213
+ step = None
214
+ ncur = len(self.x)
215
+ stepcur = 1 if ncur < 2 else (self.x[-1] - self.x[0]) / (ncur - 1.0)
216
+ nobs = len(self.xobs)
217
+ stepobs = (
218
+ 1 if nobs < 2 else (self.xobs[-1] - self.xobs[0]) / (nobs - 1.0)
219
+ )
220
+ if dx is None:
221
+ # check if xobs overlaps with x
222
+ i0 = numpy.fabs(self.xobs - self.x[0]).argmin()
223
+ n0 = min(len(self.x), len(self.xobs) - i0)
224
+ if not numpy.allclose(self.xobs[i0 : i0 + n0], self.x[:n0]):
225
+ clip = False
226
+ step = stepcur if ncur > 1 else stepobs
227
+ elif _isobs(dx):
228
+ assert clip and step is None
229
+ elif numpy.allclose(stepobs, dx):
230
+ assert clip and step is None
231
+ else:
232
+ clip = False
233
+ step = float(dx)
234
+ # verify that we either clip or have the step defined.
235
+ assert clip or step is not None
236
+ # hi, lo, step, clip all resolved here.
237
+ # validate arguments
238
+ if lo > hi:
239
+ raise ValueError("xmax must be greater than xmin.")
240
+ if not clip:
241
+ if step > hi - lo:
242
+ raise ValueError("dx must be less than (xmax - xmin).")
243
+ if step <= 0:
244
+ raise ValueError("dx must be positive.")
245
+ # determine epsilon extensions to the lower and upper bounds.
246
+ epslo = abs(lo) * epsilon + epsilon
247
+ epshi = abs(hi) * epsilon + epsilon
248
+ # process the new grid.
249
+ if clip:
250
+ indices = (lo - epslo <= self.xobs) & (self.xobs <= hi + epshi)
251
+ self.x = self.xobs[indices]
252
+ self.y = self.yobs[indices]
253
+ self.dy = self.dyobs[indices]
254
+ else:
255
+ x1 = numpy.arange(lo, hi + epshi, step)
256
+ self.setCalculationPoints(x1)
257
+ return
258
+
259
+ def setCalculationPoints(self, x):
260
+ """Set the calculation points.
261
+
262
+ Arguments
263
+ x -- A non-empty numpy array containing the calculation points. If
264
+ xobs exists, the bounds of x will be limited to its bounds.
265
+
266
+ This will create y and dy on the specified grid if xobs, yobs and
267
+ dyobs exist.
268
+ """
269
+ x = numpy.asarray(x)
270
+ if self.xobs is not None:
271
+ x = x[x >= self.xobs[0] - epsilon]
272
+ x = x[x <= self.xobs[-1] + epsilon]
273
+ self.x = x
274
+ if self.yobs is not None:
275
+ self.y = rebinArray(self.yobs, self.xobs, self.x)
276
+ if self.dyobs is not None:
277
+ # work around for interpolation issue making some of these non-1
278
+ if (self.dyobs == 1).all():
279
+ self.dy = numpy.ones_like(self.x)
280
+ else:
281
+ # FIXME - This does not follow error propagation rules and it
282
+ # introduces (more) correlation between the data points.
283
+ self.dy = rebinArray(self.dyobs, self.xobs, self.x)
284
+
285
+ return
286
+
287
+ def loadtxt(self, *args, **kw):
288
+ """Use numpy.loadtxt to load data.
289
+
290
+ Arguments are passed to numpy.loadtxt. unpack = True is
291
+ enforced. The first two arrays returned by numpy.loadtxt are
292
+ assumed to be x and y. If there is a third array, it is assumed
293
+ to by dy. Any other arrays are ignored. These are passed to
294
+ setObservedProfile.
295
+
296
+ Raises ValueError if the call to numpy.loadtxt returns fewer
297
+ than 2 arrays.
298
+
299
+ Returns the x, y and dy arrays loaded from the file
300
+ """
301
+ if len(args) == 8 and not args[-1]:
302
+ args = list(args)
303
+ args[-1] = True
304
+ else:
305
+ kw["unpack"] = True
306
+ cols = numpy.loadtxt(*args, **kw)
307
+
308
+ x = y = dy = None
309
+ # Due to using 'unpack', a single column will come out as a single
310
+ # array, thus the second check.
311
+ if len(cols) < 2 or not isinstance(cols[0], numpy.ndarray):
312
+ raise ValueError("numpy.loadtxt returned fewer than 2 arrays")
313
+ x = cols[0]
314
+ y = cols[1]
315
+ if len(cols) > 2:
316
+ dy = cols[2]
317
+
318
+ self.setObservedProfile(x, y, dy)
319
+ return x, y, dy
320
+
321
+ def savetxt(self, fname, **kwargs):
322
+ """Call `numpy.savetxt` with x, ycalc, y, dy.
323
+
324
+ Parameters
325
+ ----------
326
+ fname : filename or file handle
327
+ This is passed to `numpy.savetxt`.
328
+ **kwargs
329
+ The keyword arguments that are passed to `numpy.savetxt`.
330
+ We preset file header "x ycalc y dy". Use ``header=''``
331
+ to save data without any header.
332
+
333
+ Raises
334
+ ------
335
+ SrFitError
336
+ When `self.ycalc` has not been set.
337
+
338
+ See also
339
+ --------
340
+ numpy.savetxt
341
+ """
342
+ x = self.x
343
+ ycalc = self.ycalc
344
+ if ycalc is None:
345
+ raise SrFitError("ycalc is None")
346
+ y = self.y
347
+ dy = self.dy
348
+ kwargs.setdefault("header", "x ycalc y dy")
349
+ data = numpy.transpose([x, ycalc, y, dy])
350
+ numpy.savetxt(fname, data, **kwargs)
351
+ return
352
+
353
+ def _flush(self, other):
354
+ """Invalidate cached state.
355
+
356
+ This will force any observer to invalidate its state.
357
+ """
358
+ self.ycalc = None
359
+ self.notify(other)
360
+ return
361
+
362
+ def _validate(self):
363
+ """Validate my state.
364
+
365
+ This validates that x, y, dy, xobx, yobs and dyobs are not None.
366
+ This validates that x, y, and dy are the same length.
367
+
368
+ Raises SrFitError if validation fails.
369
+ """
370
+ datanotset = any(
371
+ v is None
372
+ for v in [
373
+ self.x,
374
+ self.y,
375
+ self.dy,
376
+ self.xobs,
377
+ self.yobs,
378
+ self.dyobs,
379
+ ]
380
+ )
381
+ if datanotset:
382
+ raise SrFitError("Missing data")
383
+ if len(self.x) != len(self.y) or len(self.x) != len(self.dy):
384
+ raise SrFitError("Data are different lengths")
385
+ return
386
+
387
+
388
+ # End class Profile
389
+
390
+
391
+ def rebinArray(A, xold, xnew):
392
+ """Rebin the an array by interpolating over the new x range.
393
+
394
+ Arguments:
395
+ A -- Array to interpolate
396
+ xold -- Old sampling array
397
+ xnew -- New sampling array
398
+
399
+ This uses cubic spline interpolation.
400
+
401
+ Returns: A new array over the new sampling array.
402
+ """
403
+ if numpy.array_equal(xold, xnew):
404
+ return A
405
+ return numpy.interp(xnew, xold, A)
@@ -0,0 +1,168 @@
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
10
+ #
11
+ # See AUTHORS.txt for a list of people who contributed.
12
+ # See LICENSE_DANSE.txt for license information.
13
+ #
14
+ ##############################################################################
15
+ """The ProfileGenerator class for generating a profile.
16
+
17
+ ProfileGenerators encapsulate the evaluation and required Parameters and
18
+ ParameterSets of a profile calculator. The ProfileGenerator class can
19
+ be associated with a FitContribution to help calculate a profile.
20
+
21
+ To define a ProfileGenerator, one must implement the required Parameters
22
+ and ParameterSets as well as overload the __call__ method with the
23
+ calculation. A very simple example is > class
24
+ Gaussian(ProfileGenerator): > > def __init__(self): > #
25
+ Initialize and give this a name > ProfileGenerator.__init__(self,
26
+ "g") > # Add amplitude, center and width parameters >
27
+ self.newParameter("amp", 0) > self.newParameter("center", 0) >
28
+ self.newParameter("width", 0) > > def __call__(self, x): > a =
29
+ self.amp.getValue() > x0 = self.center.getValue() > w =
30
+ self.width.getValue() > return a * exp(-0.5*((x-x0)/w)**2)
31
+
32
+ More examples can be found in the example directory of the
33
+ documentation.
34
+ """
35
+
36
+ __all__ = ["ProfileGenerator"]
37
+
38
+
39
+ from diffpy.srfit.equation.literals.operators import Operator
40
+ from diffpy.srfit.exceptions import SrFitError
41
+ from diffpy.srfit.fitbase.parameterset import ParameterSet
42
+
43
+
44
+ class ProfileGenerator(Operator, ParameterSet):
45
+ """Base class for profile generators.
46
+
47
+ A ProfileGenerator organizes Parameters and has a __call__ method that can
48
+ generate a profile. ProfileGenerator is also an Operator
49
+ (diffpy.srfit.equation.literals.operators), so it can be used directly in
50
+ an evaluation network.
51
+
52
+ Attributes
53
+ name -- A name for this organizer.
54
+ profile -- A Profile instance that contains the calculation range
55
+ and will contain the generated profile.
56
+ meta -- A dictionary of metadata needed by the generator.
57
+ eq -- The Equation object used to wrap this ProfileGenerator.
58
+ This is set when the ProfileGenerator is added to a
59
+ FitContribution.
60
+ _calculators -- A managed dictionary of Calculators, indexed by name.
61
+ _constraints -- A set of constrained Parameters. Constraints can be
62
+ added using the 'constrain' methods.
63
+ _parameters -- A managed OrderedDict of contained Parameters.
64
+ _parsets -- A managed dictionary of ParameterSets.
65
+ _restraints -- A set of Restraints. Restraints can be added using the
66
+ 'restrain' or 'confine' methods.
67
+ _eqfactory -- A diffpy.srfit.equation.builder.EquationFactory
68
+ instance that is used create Equations from string.
69
+
70
+ Operator Attributes
71
+ args -- List of Literal arguments, set with 'addLiteral'
72
+ name -- A name for this operator. e.g. "add" or "sin"
73
+ nin -- Number of inputs (<1 means this is variable)
74
+ nout -- Number of outputs
75
+ operation -- Function that performs the operation. e.g. numpy.add. In
76
+ this case, operation is an instance method.
77
+ symbol -- The symbolic representation. e.g. "+" or "sin"
78
+ _value -- The value of the Operator.
79
+ value -- Property for 'getValue'.
80
+
81
+ Properties
82
+ names -- Variable names (read only). See getNames.
83
+ values -- Variable values (read only). See getValues.
84
+ """
85
+
86
+ # define abstract attributes from the Operator base.
87
+ nin = 0
88
+ nout = 1
89
+
90
+ def __init__(self, name):
91
+ """Initialize the attributes."""
92
+ Operator.__init__(self)
93
+ ParameterSet.__init__(self, name)
94
+ self.profile = None
95
+ self.meta = {}
96
+ return
97
+
98
+ @property
99
+ def symbol(self):
100
+ return self.name
101
+
102
+ # Overload me!
103
+
104
+ def __call__(self, x):
105
+ """Evaluate the profile.
106
+
107
+ This method must be overloaded.
108
+
109
+ This method only takes the independent variables to calculate
110
+ over.
111
+ """
112
+ return x
113
+
114
+ # No need to overload anything below here
115
+
116
+ def operation(self):
117
+ """Evaluate the profile.
118
+
119
+ Return the result of __call__(profile.x).
120
+ """
121
+ y = self.__call__(self.profile.x)
122
+ return y
123
+
124
+ def setProfile(self, profile):
125
+ """Assign the profile.
126
+
127
+ profile -- A Profile that specifies the calculation points and which
128
+ will store the calculated signal.
129
+ """
130
+ if self.profile is not None:
131
+ self.profile.removeObserver(self._flush)
132
+
133
+ self.profile = profile
134
+ self.profile.addObserver(self._flush)
135
+ self._flush(other=(self,))
136
+
137
+ # Merge the profiles metadata with our own
138
+ self.meta.update(self.profile.meta)
139
+ self.processMetaData()
140
+ return
141
+
142
+ def processMetaData(self):
143
+ """Process the metadata.
144
+
145
+ This can be used to configure a ProfileGenerator upon a change
146
+ in the metadata. This method gets called whenever the Profile is
147
+ set.
148
+ """
149
+ return
150
+
151
+ def _validate(self):
152
+ """Validate my state.
153
+
154
+ This performs profile validations. This performs ParameterSet
155
+ validations. This does not validate the operation, since this
156
+ could be costly. The operation should be validated with a
157
+ containing equation.
158
+
159
+ Raises SrFitError if validation fails.
160
+ """
161
+ if self.profile is None:
162
+ raise SrFitError("profile is None")
163
+ self.profile._validate()
164
+ ParameterSet._validate(self)
165
+ return
166
+
167
+
168
+ # End class ProfileGenerator