#!/usr/bin/env python
##############################################################################
#
# diffpy.srfit by DANSE Diffraction group
# Simon J. L. Billinge
# (c) 2008 The Trustees of Columbia University
# in the City of New York. All rights reserved.
#
# File coded by: Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
##############################################################################
"""The ProfileGenerator class for generating a profile.
ProfileGenerators encapsulate the evaluation and required Parameters and
ParameterSets of a profile calculator. The ProfileGenerator class can
be associated with a FitContribution to help calculate a profile.
To define a ProfileGenerator, one must implement the required Parameters
and ParameterSets as well as overload the __call__ method with the
calculation. A very simple example is
.. code:: python
class Gaussian(ProfileGenerator):
def __init__(self):
# Initialize and give this a name
ProfileGenerator.__init__(self, "g")
# Add amplitude, center and width parameters
self.new_parameter("amp", 0)
self.new_parameter("center", 0)
self.new_parameter("width", 0)
def __call__(self, x):
a = self.amp.get_value()
x0 = self.center.get_value()
w = self.width.get_value()
return a * exp(-0.5*((x-x0)/w)**2)
More examples can be found in the example directory of the
documentation.
"""
__all__ = ["ProfileGenerator"]
from diffpy.srfit.equation.literals.operators import Operator
from diffpy.srfit.exceptions import SrFitError
from diffpy.srfit.fitbase.parameterset import ParameterSet
[docs]
class ProfileGenerator(Operator, ParameterSet):
"""Base class for profile generators.
A ProfileGenerator organizes Parameters and has a ``__call__`` method
that can generate a profile. ProfileGenerator is also an Operator
(diffpy.srfit.equation.literals.operators), so it can be used directly
in an evaluation network.
Parameters
----------
name : str
The name of the ProfileGenerator. It must be a valid attribute
name.
Attributes
----------
name : str
The name of the ProfileGenerator.
profile : Profile or None
The Profile that contains the calculation range and will contain
the generated profile. It is None until ``set_profile`` is called.
meta : dict
The metadata needed by the generator. It is updated from the
Profile metadata whenever the Profile is set or changes.
names : list of str
The names of the top-level Parameters (read only). See
``get_names``.
values : list
The values of the top-level Parameters (read only). See
``get_values``.
args : list
The Literal arguments of the operator, added with
``add_literal``.
nin : int
The number of inputs, which is 0. The independent variable is
taken from ``profile``.
nout : int
The number of outputs, which is 1.
symbol : str
The symbolic representation of the operator, which is the same as
``name`` (read only).
value : object
The value of the operator (read only). See ``get_value``.
_value : object
The cached value of the operator, or None if it must be
recomputed.
_calculators : dict
The managed dictionary of Calculators, indexed by name.
_constraints : dict
The dictionary of Constraints, indexed by the constrained
Parameter. Constraints can be added using the ``add_constraint``
method.
_parameters : OrderedDict
The managed OrderedDict of contained Parameters.
_parsets : OrderedDict
The managed OrderedDict of contained ParameterSets.
_restraints : set
The set of Restraints. Restraints can be added using the
``add_soft_bounds`` method.
_eqfactory : diffpy.srfit.equation.builder.EquationFactory
The factory that is used to create Equations from strings.
"""
# define abstract attributes from the Operator base.
nin = 0
nout = 1
def __init__(self, name):
Operator.__init__(self)
ParameterSet.__init__(self, name)
self.profile = None
self.meta = {}
return
@property
def symbol(self):
"""The symbolic representation, which is the generator name."""
return self.name
# Overload me!
def __call__(self, x):
"""Evaluate the profile.
This method must be overloaded. It only takes the independent
variable to calculate over.
Parameters
----------
x : ndarray
The independent variable over which to calculate.
Returns
-------
ndarray
The calculated profile.
"""
return x
# No need to overload anything below here
[docs]
def operation(self):
"""Evaluate the profile over the calculation points.
Returns
-------
ndarray
The profile calculated by ``__call__`` at ``profile.x``.
"""
y = self.__call__(self.profile.x)
return y
[docs]
def set_profile(self, profile):
"""Assign the profile.
The generator stops observing any previously assigned Profile and
starts observing the new one, so that changes to the Profile
update ``meta`` and invalidate the cached value.
Parameters
----------
profile : Profile
The Profile that specifies the calculation points and which
will store the calculated signal.
"""
if self.profile is not None:
self.profile.remove_observer(self._on_profile_update)
self.profile = profile
self.profile.add_observer(self._on_profile_update)
self._on_profile_update(other=(self,))
return
def _on_profile_update(self, other=()):
if self.profile is not None:
self.meta.update(self.profile.meta)
self._process_metadata()
self._flush(other=other)
return
def _process_metadata(self):
"""Process the metadata.
This can be used to configure a ProfileGenerator upon a change
in the metadata. This method gets called whenever the Profile is
set.
"""
return
def _validate(self):
"""Validate my state.
This performs profile validations. This performs ParameterSet
validations. This does not validate the operation, since this
could be costly. The operation should be validated with a
containing equation.
Raises
------
SrFitError
If validation fails.
"""
if self.profile is None:
raise SrFitError("profile is None")
self.profile._validate()
ParameterSet._validate(self)
return
# End class ProfileGenerator