From 709152b064157242950b8d660b7eb2471c564831 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 11:00:34 -0400 Subject: [PATCH 01/11] Update docstrings in fitbase to numpy/group standards --- src/diffpy/srfit/fitbase/calculator.py | 29 +- src/diffpy/srfit/fitbase/configurable.py | 12 +- src/diffpy/srfit/fitbase/constraint.py | 15 +- src/diffpy/srfit/fitbase/fitcontribution.py | 151 ++++++---- src/diffpy/srfit/fitbase/fithook.py | 55 ++-- src/diffpy/srfit/fitbase/fitrecipe.py | 132 ++++++--- src/diffpy/srfit/fitbase/fitresults.py | 106 ++++--- src/diffpy/srfit/fitbase/parameter.py | 127 ++++---- src/diffpy/srfit/fitbase/parameterset.py | 29 +- src/diffpy/srfit/fitbase/profile.py | 149 +++++----- src/diffpy/srfit/fitbase/profilegenerator.py | 28 +- src/diffpy/srfit/fitbase/profileparser.py | 56 ++-- src/diffpy/srfit/fitbase/recipeorganizer.py | 295 ++++++++++++++----- src/diffpy/srfit/fitbase/restraint.py | 56 ++-- src/diffpy/srfit/fitbase/simplerecipe.py | 116 +++++--- src/diffpy/srfit/fitbase/validatable.py | 16 +- 16 files changed, 860 insertions(+), 512 deletions(-) diff --git a/src/diffpy/srfit/fitbase/calculator.py b/src/diffpy/srfit/fitbase/calculator.py index 41ebde60..c0afc776 100644 --- a/src/diffpy/srfit/fitbase/calculator.py +++ b/src/diffpy/srfit/fitbase/calculator.py @@ -101,15 +101,37 @@ def symbol(self): # Overload me! def __call__(self, *args): - """Calculate something. + """Calculate the signal produced by this Calculator. This method must be overloaded. When overloading, you should specify the arguments explicitly, otherwise the parameters must be specified when adding the Calculator to a RecipeOrganizer. + + Parameters + ---------- + *args + The arguments needed to calculate the signal. + + Returns + ------- + object + The calculated signal. """ return 0 def operation(self, *args): + """Calculate and cache the signal produced by this Calculator. + + Parameters + ---------- + *args + The arguments needed to calculate the signal. + + Returns + ------- + object + The calculated signal. + """ self._value = self.__call__(*args) return self._value @@ -120,7 +142,10 @@ def _validate(self): the operation, since this could be costly. The operation should be validated with a containing equation. - Raises AttributeError if validation fails. + Raises + ------ + AttributeError + If validation fails. """ ParameterSet._validate(self) diff --git a/src/diffpy/srfit/fitbase/configurable.py b/src/diffpy/srfit/fitbase/configurable.py index 8fb275bc..7c0ba19d 100644 --- a/src/diffpy/srfit/fitbase/configurable.py +++ b/src/diffpy/srfit/fitbase/configurable.py @@ -21,15 +21,13 @@ class Configurable(object): - """Configurable class. - - A Configurable has state of which a FitRecipe must be aware. + """Base class for objects with state a FitRecipe must be aware of. Attributes ---------- _configobjs - Set of Configureables in a hierarchy or instances. - Messages get passed up the hierarchy to a FitReciple + The set of Configurables in a hierarchy of instances. + Messages get passed up the hierarchy to a FitRecipe via these objects. """ @@ -46,8 +44,8 @@ def _update_configuration(self): def _store_configurable(self, obj): """Store a Configurable. - The passed obj is only stored if it is a a Configurable, - otherwise this method quietly exits. + The passed obj is only stored if it is a Configurable, otherwise + this method quietly exits. """ if isinstance(obj, Configurable): self._configobjs.add(obj) diff --git a/src/diffpy/srfit/fitbase/constraint.py b/src/diffpy/srfit/fitbase/constraint.py index 8119c820..f78d1de0 100644 --- a/src/diffpy/srfit/fitbase/constraint.py +++ b/src/diffpy/srfit/fitbase/constraint.py @@ -45,7 +45,7 @@ class Constraint(Validatable): - """Constraint class. + """Associate a Parameter with an equation that determines its value. Constraints are designed to be stored in only one place. (The holder of the constraint owns it). @@ -53,14 +53,14 @@ class Constraint(Validatable): Attributes ---------- par - A Parameter that is the subject of the constraint. + The Parameter that is the subject of the constraint. eq - An equation whose evaluation is used to set the value of the + The equation whose evaluation is used to set the value of the constraint. """ def __init__(self): - """Initialization.""" + """Initialize an empty constraint.""" self.par = None self.eq = None return @@ -139,9 +139,12 @@ def update(self): def _validate(self): """Validate my state. - This validates that par is not None. This validates eq. + This validates that ``par`` is not None. This validates ``eq``. - Raises SrFitError if validation fails. + Raises + ------ + SrFitError + If validation fails. """ if self.par is None: raise SrFitError("par is None") diff --git a/src/diffpy/srfit/fitbase/fitcontribution.py b/src/diffpy/srfit/fitbase/fitcontribution.py index e1c29f7f..d896e86e 100644 --- a/src/diffpy/srfit/fitbase/fitcontribution.py +++ b/src/diffpy/srfit/fitbase/fitcontribution.py @@ -79,7 +79,7 @@ class FitContribution(ParameterSet): - """FitContribution class. + """Organize an Equation, a Profile, and their supporting objects. FitContributions organize an Equation that calculates the signal, and a Profile that holds the signal. ProfileGenerators and Calculators can be @@ -97,14 +97,14 @@ class FitContribution(ParameterSet): A managed dictionary of Calculators, indexed by name. _constraints A set of constrained Parameters. Constraints can be - added using the 'constrain' methods. + added using the ``constrain`` methods. _generators A managed dictionary of ProfileGenerators. _parameters A managed OrderedDict of parameters. _restraints A set of Restraints. Restraints can be added using the - 'restrain' method. + ``restrain`` method. _parsets A managed dictionary of ParameterSets. _eqfactory @@ -131,7 +131,13 @@ class FitContribution(ParameterSet): """ def __init__(self, name): - """Initialization.""" + """Initialize the FitContribution. + + Parameters + ---------- + name : str + The name of this FitContribution. + """ ParameterSet.__init__(self, name) self._eq = None self._reseq = None @@ -149,20 +155,20 @@ def set_profile(self, profile, xname=None, yname=None, dyname=None): Parameters ---------- - profile - A Profile that specifies the calculation points and that + profile : Profile + The Profile that specifies the calculation points and that will store the calculated signal. - xname + xname : str, optional The name of the independent variable from the Profile. If this is None (default), then the name specified by the Profile for this parameter will be used. This variable is usable within string equations with the specified name. - yname + yname : str, optional The name of the observed Profile. If this is None (default), then the name specified by the Profile for this parameter will be used. This variable is usable within string equations with the specified name. - dyname + dyname : str, optional The name of the uncertainty in the observed Profile. If this is None (default), then the name specified by the Profile for this parameter will be used. This variable is @@ -219,26 +225,28 @@ def add_profile_generator(self, gen, name=None): """Add a ProfileGenerator to be used by this FitContribution. The ProfileGenerator is given a name so that it can be used as part of - the profile equation (see setEquation). This can be different from the - name of the ProfileGenerator used for attribute access. + the profile equation (see ``set_equation``). This can be different + from the name of the ProfileGenerator used for attribute access. FitContributions should not share ProfileGenerator instances. Different ProfileGenerators can share Parameters and ParameterSets, however. - Calling addProfileGenerator sets the profile equation to call the - calculator and if there is not a profile equation already. + Calling ``add_profile_generator`` sets the profile equation to call + the calculator if there is not a profile equation already. Parameters ---------- - gen - A ProfileGenerator instance - name + gen : ProfileGenerator + The ProfileGenerator instance to add. + name : str, optional A name for the calculator. If name is None (default), then the ProfileGenerator's name attribute will be used. - - Raises ValueError if the ProfileGenerator has no name. - Raises ValueError if the ProfileGenerator has the same name as some - other managed object. + Raises + ------ + ValueError + If the ProfileGenerator has no name, or if the + ProfileGenerator has the same name as some other managed + object. """ if name is None: name = gen.name @@ -276,24 +284,25 @@ def set_equation(self, eqstr, ns={}): This sets the equation that will be used when generating the residual for this FitContribution. The equation will be usable within - set_residual_equation as "eq", and it takes no arguments. + ``set_residual_equation`` as ``"eq"``, and it takes no arguments. Parameters ---------- - eqstr + eqstr : str A string representation of the equation. Any Parameter - registered by addParameter or setProfile, or function - registered by setCalculator, register_function or - register_string_function can be can be used in the equation + registered by ``addParameter`` or ``set_profile``, or function + registered by ``register_calculator``, ``register_function`` or + ``register_string_function`` can be used in the equation by name. Other names will be turned into Parameters of this FitContribution. - ns + ns : dict, optional A dictionary of Parameters, indexed by name, that are used in the eqstr, but not registered (default {}). - - Raises ValueError if ns uses a name that is already used for a - variable. + Raises + ------ + ValueError + If ns uses a name that is already used for a variable. """ # Build the equation instance. eq = get_equation_from_string( @@ -328,10 +337,14 @@ def setEquation(self, eqstr, ns={}): return def get_equation(self): - """Get math expression string for the active profile equation. - - Return normalized math expression or an empty string if profile - equation has not been set yet. + """Get the math expression string for the active profile + equation. + + Returns + ------- + str + The normalized math expression, or an empty string if the + profile equation has not been set yet. """ from diffpy.srfit.equation.visitors import getExpression @@ -353,26 +366,29 @@ def getEquation(self): def set_residual_equation(self, eqstr): """Set the residual equation for the FitContribution. + Two residuals are preset for convenience, ``"chiv"`` and ``"resv"``. + ``chiv`` is defined such that ``dot(chiv, chiv) = chi^2``. + ``resv`` is defined such that ``dot(resv, resv) = Rw^2``. + You can call on these in your residual equation. Note that the quantity + that will be optimized is the summed square of the residual equation. + Keep that in mind when defining a new residual or using the built-in + ones. + Parameters ---------- - eqstr + eqstr : str A string representation of the residual. If eqstr is None (default), then the previous residual equation will be used, or the chi2 residual will be used if that does not exist. - - Two residuals are preset for convenience, "chiv" and "resv". - chiv is defined such that dot(chiv, chiv) = chi^2. - resv is defined such that dot(resv, resv) = Rw^2. - You can call on these in your residual equation. Note that the quantity - that will be optimized is the summed square of the residual equation. - Keep that in mind when defining a new residual or using the built-in - ones. - - Raises SrFitError if the Profile is not yet defined. - Raises ValueError if eqstr depends on a Parameter that is not part of - the FitContribution. + Raises + ------ + SrFitError + If the Profile is not yet defined. + ValueError + If eqstr depends on a Parameter that is not part of the + FitContribution. """ if self.profile is None: raise SrFitError("Assign the Profile first") @@ -407,10 +423,14 @@ def setResidualEquation(self, eqstr): return def get_residual_equation(self): - """Get math expression string for the active residual equation. - - Return normalized math formula or an empty string if residual - equation has not been configured yet. + """Get the math expression string for the active residual + equation. + + Returns + ------- + str + The normalized math formula, or an empty string if the + residual equation has not been configured yet. """ from diffpy.srfit.equation.visitors import getExpression @@ -431,18 +451,23 @@ def getResidualEquation(self): return self.get_residual_equation() def residual(self): - """Calculate the residual for this fitcontribution. + """Calculate the residual for this FitContribution. When this method is called, it is assumed that all parameters have been assigned their most current values by the FitRecipe. This will be the case when being called as part of a FitRecipe refinement. - The residual is by default an array chiv: - chiv = (eq() - self.profile.y) / self.profile.dy - The value that is optimized is dot(chiv, chiv). + The residual is by default an array ``chiv``: + ``chiv = (eq() - self.profile.y) / self.profile.dy``. + The value that is optimized is ``dot(chiv, chiv)``. + + The residual equation can be changed with the + ``set_residual_equation`` method. - The residual equation can be changed with the set_residual_equation - method. + Returns + ------- + numpy.ndarray + The array of residual values. """ # Assign the calculated profile self.profile.ycalc = self._eq() @@ -451,8 +476,13 @@ def residual(self): return self._reseq() def evaluate(self): - """Evaluate the contribution equation and update - profile.ycalc.""" + """Evaluate the contribution equation and update profile.ycalc. + + Returns + ------- + numpy.ndarray + The calculated signal. + """ yc = self._eq() if self.profile is not None: self.profile.ycalc = yc @@ -465,7 +495,10 @@ def _validate(self): ProfileGenerator validations. This validates _eq. This validates _reseq and residual. - Raises SrFitError if validation fails. + Raises + ------ + SrFitError + If validation fails. """ self.profile._validate() ParameterSet._validate(self) diff --git a/src/diffpy/srfit/fitbase/fithook.py b/src/diffpy/srfit/fitbase/fithook.py index 43e2c84e..6edae312 100644 --- a/src/diffpy/srfit/fitbase/fithook.py +++ b/src/diffpy/srfit/fitbase/fithook.py @@ -22,7 +22,7 @@ and the current variable values. Custom FitHooks can be added to a FitRecipe with the -FitRecipe.setFitHook method. +FitRecipe.push_fit_hook method. """ from __future__ import print_function @@ -60,8 +60,8 @@ def precall(self, recipe): Parameters ---------- - recipe - The FitRecipe instance + recipe : FitRecipe + The FitRecipe instance. """ return @@ -71,10 +71,10 @@ def postcall(self, recipe, chiv): Parameters ---------- - recipe - The FitRecipe instance - chiv - The residual vector + recipe : FitRecipe + The FitRecipe instance. + chiv : ndarray + The residual vector. """ return @@ -83,7 +83,7 @@ def postcall(self, recipe, chiv): class PrintFitHook(FitHook): - """Base class for inspecting the progress of a FitRecipe refinement. + """Print the progress of a FitRecipe refinement. This FitHook prints out a running count of the number of times the residual has been called, or other information, based on the verbosity. @@ -94,14 +94,15 @@ class PrintFitHook(FitHook): The number of times the residual has been called (default 0). verbose An integer telling how verbose to be (default 1). - 0 - print nothing - 1 - print the count during the precall - 2 - print the residual during the postcall - >=3 - print the variables during the postcall + + 0 + Print nothing. + 1 + Print the count during the precall. + 2 + Print the residual during the postcall. + >=3 + Print the variables during the postcall. """ def __init__(self): @@ -127,8 +128,8 @@ def precall(self, recipe): Parameters ---------- - recipe - The FitRecipe instance + recipe : FitRecipe + The FitRecipe instance. """ self.count += 1 if self.verbose > 0: @@ -141,10 +142,10 @@ def postcall(self, recipe, chiv): Parameters ---------- - recipe - The FitRecipe instance - chiv - The residual vector + recipe : FitRecipe + The FitRecipe instance. + chiv : ndarray + The residual vector. """ if self.verbose < 2: return @@ -185,7 +186,7 @@ def _byname(nv): # TODO - Display the chi^2 on the plot during refinement. class PlotFitHook(FitHook): - """This FitHook has live plotting of whatever is being refined.""" + """Live-plot the progress of a FitRecipe refinement.""" def reset(self, recipe): """Set up the plot.""" @@ -237,10 +238,10 @@ def postcall(self, recipe, chiv): Parameters ---------- - recipe - The FitRecipe instance - chiv - The residual vector + recipe : FitRecipe + The FitRecipe instance. + chiv : ndarray + The residual vector. """ FitHook.postcall(self, recipe, chiv) import pylab diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 101c88b8..9bdd8d72 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -26,7 +26,7 @@ Variables added to a FitRecipe can be tagged with string identifiers. Variables can be later retrieved or manipulated by tag. The tag name -"__fixed" is reserved. +``__fixed`` is reserved. See the examples in the documentation for how to create an optimization problem using FitRecipe. @@ -135,7 +135,8 @@ class FitRecipe(_fitrecipe_interface, RecipeOrganizer): - """FitRecipe class. + """Organize FitContributions, variables, restraints, and constraints + into a refinable recipe. Attributes ---------- @@ -148,7 +149,7 @@ class FitRecipe(_fitrecipe_interface, RecipeOrganizer): _constraints : dict The dictionary of Constraints, indexed by the constrained Parameter. Constraints can be added using the - 'constrain' method. + `add_constraint` method. _oconstraints : list The ordered list of the constraints from this and all sub-components. @@ -180,7 +181,7 @@ class FitRecipe(_fitrecipe_interface, RecipeOrganizer): weights are multiplied by the residual of the FitContribution when determining the overall residual. _fixedtag : str - "__fixed", used for tagging variables as fixed. Don't + ``__fixed``, used for tagging variables as fixed. Don't use this tag unless you want issues. Properties @@ -221,7 +222,13 @@ class FitRecipe(_fitrecipe_interface, RecipeOrganizer): bounds2 = property(lambda self: self.get_bounds_array()) def __init__(self, name="fit"): - """Initialization.""" + """Initialize the FitRecipe. + + Parameters + ---------- + name : str, optional + The name for this FitRecipe. Default is "fit". + """ RecipeOrganizer.__init__(self, name) self.fithooks = [] self.pushFitHook(PrintFitHook()) @@ -337,7 +344,13 @@ def popFitHook(self, fithook=None, index=-1): return def get_fit_hooks(self): - """Get the sequence of FitHook instances.""" + """Get the sequence of FitHook instances. + + Returns + ------- + list + The list of FitHook instances registered with this FitRecipe. + """ return self.fithooks[:] @deprecated(getfithooks_dep_msg) @@ -454,13 +467,13 @@ def remove_parameter_set(self, parset): hierarchy of managed ParameterSets. If the provided ParameterSet is not currently managed by this object, a ValueError will be raised. - Parameters: - ----------- + Parameters + ---------- parset : ParameterSet The ParameterSet instance to be removed from the hierarchy. - Raises: - ------- + Raises + ------ ValueError If the provided ParameterSet is not managed by this object. """ @@ -482,8 +495,8 @@ def residual(self, p=[]): The residual is by default the weighted concatenation of each FitContribution's residual, plus the value of each restraint. The array - returned, denoted chiv, is such that - dot(chiv, chiv) = chi^2 + restraints. + returned, denoted ``chiv``, is such that + ``dot(chiv, chiv) = chi^2 + restraints``. Parameters ---------- @@ -494,11 +507,11 @@ def residual(self, p=[]): been updated in some other way, and the explicit update within this function is skipped. - Return - ------ + Returns + ------- chiv : numpy.ndarray The array of residuals to be optimized. The array is such that - dot(chiv, chiv) = chi^2 + restraints. + ``dot(chiv, chiv) = chi^2 + restraints``. """ # Prepare, if necessary @@ -546,6 +559,12 @@ def scalar_residual(self, p=[]): been updated in some other way, and the explicit update within this function is skipped. + Returns + ------- + float + The scalar residual, ``dot(chiv, chiv)``, where ``chiv`` is + the vector residual returned by `residual`. + Notes ----- The residual is by default the weighted concatenation of each @@ -567,7 +586,19 @@ def scalarResidual(self, p=[]): return self.scalar_residual(p) def __call__(self, p=[]): - """Same as scalar_residual method.""" + """Compute the scalar residual, same as `scalar_residual`. + + Parameters + ---------- + p : list or numpy.ndarray, optional + The list of current variable values, provided in the same order + as the ``_parameters`` list. Default is an empty list. + + Returns + ------- + float + The scalar residual, ``dot(chiv, chiv)``. + """ return self.scalar_residual(p) def _prepare(self): @@ -754,7 +785,7 @@ def add_variable( Returns ------- ParameterProxy - ParameterProxy (variable) for the passed Parameter. + The ParameterProxy (variable) for the passed Parameter. Raises ------ @@ -827,6 +858,8 @@ def delVar(self, var): return def __delattr__(self, name): + """Delete a variable if name refers to one, otherwise defer to + the base class.""" if name in self._parameters: self.delete_variable(self._parameters[name]) return @@ -910,8 +943,15 @@ def __get_var_and_check(self, var): var A variable of the FitRecipe, or the name of a variable. - Returns the variable or None if the variable cannot be found in the - _parameters list. + Returns + ------- + object + The variable. + + Raises + ------ + ValueError + If the variable is not part of the FitRecipe. """ if isinstance(var, str): var = self._parameters.get(var) @@ -973,24 +1013,24 @@ def fix(self, *args, **kw): Parameters ---------- - *args : str or Parameter - The positional arguments specifying the parameters to fix. - These can be parameter objects, their names as strings, or - tags. The special string "all" can be used to select all - parameters. - **kw : dict - The keyword arguments where the keys are parameter names and - the values are the values to assign to the corresponding - fixed parameters. + *args : str or Parameter + The positional arguments specifying the parameters to fix. + These can be parameter objects, their names as strings, or + tags. The special string "all" can be used to select all + parameters. + **kw : dict + The keyword arguments where the keys are parameter names and + the values are the values to assign to the corresponding + fixed parameters. Raises ------ - ValueError: - If an unknown parameter, name, or tag is passed, or if a - tag is passed as a keyword argument. + ValueError + If an unknown parameter, name, or tag is passed, or if a + tag is passed as a keyword argument. - Example - ------- + Examples + -------- :: @@ -1044,6 +1084,10 @@ def free(self, *args, **kw): their values to assign after freeing. This is useful for setting the value of a parameter while marking it as free. + Returns + ------- + None + Raises ------ ValueError @@ -1057,10 +1101,6 @@ def free(self, *args, **kw): are freed. - If keyword arguments are provided, the corresponding parameter values will be updated after freeing. - - Returns - ------- - None """ # Check the inputs and get the variables from them varargs = self.__get_vars_from_args(*args, **kw) @@ -1238,7 +1278,6 @@ def get_values(self): Returns ------- - values_array : numpy.ndarray The array containing the current values of all free variables in the fit recipe. @@ -1264,7 +1303,7 @@ def get_names(self): Returns ------- - parameter_names :list of str + parameter_names : list of str The list containing the names of free variables. """ parameter_names = [ @@ -1518,11 +1557,6 @@ def set_plot_defaults(self, **kwargs): Default is 1.0. show : bool, optional The plot is displayed using `plt.show()` if True. Default is True. - ax : matplotlib.axes.Axes or None, optional - The axes object to plot on. If None, creates a new figure. - Default is None. - return_fig : bool, optional - The figure and axes objects are returned if True. Default is False. Examples -------- @@ -1577,9 +1611,12 @@ def plot_recipe(self, ax=None, return_fig=False, **kwargs): Returns ------- - fig, axes : tuple of (mpl.figure.Figure, list of mpl.axes.Axes) - The figure object and a list of axes objects (one per contribution) - are returned if return_fig=True. + fig, axes : tuple + The figure and axes objects, returned only if + ``return_fig=True``. If the recipe has a single contribution, + a single ``mpl.figure.Figure`` and ``mpl.axes.Axes`` are + returned. If it has multiple contributions, a list of figures + and a list of axes (one per contribution) are returned instead. Examples -------- @@ -1744,7 +1781,6 @@ def convert_bounds_to_restraints(self, sig=1, scaled=False): Smaller values produce stronger restraints. If a scalar is given, the same value is applied to all parameters. If an iterable is provided, it must match the number of parameters. Default is 1. - scaled : bool, optional If True, scale each restraint by the magnitude of the corresponding parameter, consistent with the behavior of :meth:`restrain`. diff --git a/src/diffpy/srfit/fitbase/fitresults.py b/src/diffpy/srfit/fitbase/fitresults.py index f3a48f23..4f7fa5d8 100644 --- a/src/diffpy/srfit/fitbase/fitresults.py +++ b/src/diffpy/srfit/fitbase/fitresults.py @@ -102,8 +102,8 @@ class FitResults(object): The estimated standard uncertainties of the variables. None if invalid. showfixed : bool - Show the fixed variables in the formatted output - (default True). + The flag indicating whether to show the fixed variables in the + formatted output (default True). fixednames : list[str] The names of variables held fixed during refinement. @@ -112,8 +112,8 @@ class FitResults(object): The values of the fixed variables. showcon : bool - show the constrained parameters in the formatted output - (default False). + The flag indicating whether to show the constrained parameters + in the formatted output (default False). connames : list[str] The names of constrained parameters. @@ -170,9 +170,11 @@ def __init__(self, recipe, update=True, showfixed=True, showcon=False): The flag indicating whether to do an immediate update (default True). showfixed : bool - Show fixed variables in the output (default True). + The flag indicating whether to show fixed variables in the + output (default True). showcon : bool - Show constraint values in the output (default False). + The flag indicating whether to show constraint values in + the output (default False). """ self.recipe = recipe self.conresults = OrderedDict() @@ -394,8 +396,9 @@ def _calculate_constraint_uncertainties(self): def get_results_string(self, header="", footer="", update=False): """Format the results and return them in a string. - This function is called by print_results and save_results. Overloading - the formatting here will change all three functions. + This function is called by ``print_results`` and + ``save_results``. Overloading the formatting here will change + all three functions. Parameters ---------- @@ -404,12 +407,13 @@ def get_results_string(self, header="", footer="", update=False): footer : str The footer to add to the output (default "") update : bool - The flag indicating whether to call update() (default False). + The flag indicating whether to call ``update()`` (default + False). Returns ------- - out : str - a string containing the formatted results. + str + The string containing the formatted results. """ if update: self.update() @@ -598,12 +602,13 @@ def print_results(self, header="", footer="", update=False): Parameters ---------- - header + header : str The header to add to the output (default "") - footer + footer : str The footer to add to the output (default "") - update - The flag indicating whether to call update() (default False). + update : bool + The flag indicating whether to call ``update()`` (default + False). """ print(self.get_results_string(header, footer, update).rstrip()) return @@ -620,21 +625,23 @@ def printResults(self, header="", footer="", update=False): return def __str__(self): + """Return the formatted results string.""" return self.get_results_string() def save_results(self, filename, header="", footer="", update=False): """Format and save the results. Parameters - ---------------------------------- - filename + ---------- + filename : str The name of the save file. - header + header : str The header to add to the output (default "") - footer + footer : str The footer to add to the output (default "") - update - The flag indicating whether to call update() (default False). + update : bool + The flag indicating whether to call ``update()`` (default + False). """ # Save the time and user from getpass import getuser @@ -736,12 +743,13 @@ def __init__(self, con, weight, fitres): Parameters ---------- - con - The FitContribution - weight - The weight of the FitContribution in the recipe - fitres - The FitResults instance to contain this ContributionResults + con : FitContribution + The FitContribution to summarize. + weight : float + The weight of the FitContribution in the recipe. + fitres : FitResults + The FitResults instance containing this + ContributionResults. """ self.x = None self.y = None @@ -819,11 +827,11 @@ def _calculate_metrics(self): @deprecated(resultsDictionary_dep_msg) def resultsDictionary(results): - """**This function has been deprecated and will be** **removed in version - 4.0.0.** + """This function has been deprecated and will be removed in version + 4.0.0. - **Please use** - **diffpy.srfit.fitbase.FitResults.get_results_dictionary instead.** + Please use + diffpy.srfit.fitbase.FitResults.get_results_dictionary instead. Get dictionary of results from file. @@ -832,9 +840,14 @@ def resultsDictionary(results): Parameters ---------- - results - An open file-like object, name of a file that contains - results from FitResults or a string containing fit results. + results : str or file-like + The open file-like object, name of a file that contains + results from FitResults, or a string containing fit results. + + Returns + ------- + dict + The mapping of result names to their string values. """ resstr = inputToString(results) @@ -853,12 +866,12 @@ def resultsDictionary(results): @deprecated(initializeRecipe_dep_msg) def initializeRecipe(recipe, results): - """**This function has been deprecated and will be** **removed in - version 4.0.0.** + """This function has been deprecated and will be removed in version + 4.0.0. - **Please use** - **diffpy.srfit.fitbase.FitRecipe.initialize_recipe_with_results** - **instead.** + Please use + diffpy.srfit.fitbase.FitRecipe.initialize_recipe_with_results + instead. Initialize the variables of a recipe from a results file. @@ -868,11 +881,16 @@ def initializeRecipe(recipe, results): Parameters ---------- - recipe - A configured recipe with variables - results - An open file-like object, name of a file that contains - results from FitResults or a string containing fit results. + recipe : FitRecipe + The configured recipe with variables. + results : str or file-like + The open file-like object, name of a file that contains + results from FitResults, or a string containing fit results. + + Raises + ------ + AttributeError + If no results can be found in ``results``. """ mpairs = resultsDictionary(results) if not mpairs: diff --git a/src/diffpy/srfit/fitbase/parameter.py b/src/diffpy/srfit/fitbase/parameter.py index 4fa2c2b6..d6a52d1a 100644 --- a/src/diffpy/srfit/fitbase/parameter.py +++ b/src/diffpy/srfit/fitbase/parameter.py @@ -56,7 +56,7 @@ class Parameter(_parameter_interface, Argument, Validatable): - """Parameter class. + """Encapsulate an adjustable parameter within SrFit. Attributes ---------- @@ -65,9 +65,9 @@ class Parameter(_parameter_interface, Argument, Validatable): const A flag indicating whether this is considered a constant. _value - The value of the Parameter. Modified with 'set_value'. + The value of the Parameter. Modified with ``set_value``. value - Property for 'getValue' and 'set_value'. + Property for ``getValue`` and ``set_value``. constrained A flag indicating if the Parameter is constrained (default False). @@ -78,21 +78,23 @@ class Parameter(_parameter_interface, Argument, Validatable): """ def __init__(self, name, value=None, const=False): - """Initialization. + """Initialize the Parameter. Parameters ---------- - name + name : str The name of this Parameter (must be a valid attribute - identifier) - value + identifier). + value : float, optional The initial value of this Parameter (default 0). - const - A flag inticating whether the Parameter is a constant (like + const : bool, optional + A flag indicating whether the Parameter is a constant (like pi). - - Raises ValueError if the name is not a valid attribute identifier + Raises + ------ + ValueError + If the name is not a valid attribute identifier. """ self.constrained = False self.bounds = [-numpy.inf, +numpy.inf] @@ -101,23 +103,17 @@ def __init__(self, name, value=None, const=False): return def set_value(self, val): - """Set the value of the Parameter and the bounds. + """Set the value of the Parameter. Parameters ---------- - val + val : float The value to assign. - lower_bound : float - The lower bounds for the bounds list. If this is None - (default), then the lower bound will not be alterered. - upper_bound : float - The upper bounds for the bounds list. If this is None - (default), then the upper bound will not be alterered. Returns ------- - self - Returns self so that mutators can be chained. + Parameter + Return self so that mutators can be chained. """ Argument.set_value(self, val) return self @@ -140,14 +136,14 @@ def set_constant(self, is_constant=True, value=None): The flag indicating if the parameter is constant (default True). value : float, optional - The value value for the parameter to be set to (default None). - If this is not None, then the parameter will get a new value, - constant or otherwise. + The value to set the parameter to (default None). If this is + not None, then the parameter will get a new value, constant + or otherwise. Returns ------- - self - Returns self so that mutators can be chained. + Parameter + Return self so that mutators can be chained. """ self.const = bool(is_constant) if value is not None: @@ -176,8 +172,8 @@ def bound_range(self, lower_bound=None, upper_bound=None): Returns ------- - self - Returns self so that mutators can be chained. + Parameter + Return self so that mutators can be chained. """ if lower_bound is not None: self.bounds[0] = lower_bound @@ -210,8 +206,8 @@ def bound_window(self, lower_radius=0, upper_radius=None): Returns ------- - self - Returns self so that mutators can be chained. + Parameter + Return self so that mutators can be chained. """ val = self.getValue() lower_bound = val - lower_radius @@ -236,7 +232,10 @@ def _validate(self): This validates that value is not None. - Raises SrFitError if validation fails. + Raises + ------ + SrFitError + If validation fails. """ if self.value is None: raise SrFitError("value of '%s' is None" % self.name) @@ -261,17 +260,19 @@ class ParameterProxy(Parameter): """ def __init__(self, name, par): - """Initialization. + """Initialize the ParameterProxy. Parameters ---------- - name + name : str The name of this ParameterProxy. - par + par : Parameter The Parameter this is a proxy for. - - Raises ValueError if the name is not a valid attribute identifier + Raises + ------ + ValueError + If the name is not a valid attribute identifier. """ validateName(name) @@ -337,7 +338,10 @@ def _validate(self): This validates that value and par are not None. - Raises SrFitError if validation fails. + Raises + ------ + SrFitError + If validation fails. """ if self.par is None: raise SrFitError("par is None") @@ -360,33 +364,35 @@ def __init__(self, name, obj, getter=None, setter=None, attr=None): Parameters ---------- - name + name : str The name of this Parameter. - obj + obj : object The object to be wrapped. - getter + getter : callable, optional The unbound function that can be used to access the - attribute containing the parameter value. getter(obj) - should return the Parameter value. If getter is None + attribute containing the parameter value. ``getter(obj)`` + should return the Parameter value. If getter is None (default), it is assumed that an attribute is accessed via attr. If attr is also specified, then the Parameter - value will be accessed via getter(obj, attr). - setter + value will be accessed via ``getter(obj, attr)``. + setter : callable, optional The unbound function that can be used to modify the attribute containing the parameter value. - setter(obj, value) should set the attribute to the + ``setter(obj, value)`` should set the attribute to the passed value. If setter is None (default), it is assumed that an attribute is accessed via attr. If attr is also specified, then the Parameter value will be set via - setter(obj, attr, value). - attr + ``setter(obj, attr, value)``. + attr : str, optional The name of the attribute that contains the value of the parameter. If attr is None (default), then both getter and setter must be specified. - - Raises ValueError if exactly one of getter or setter is not None, or if - getter, setter and attr are all None. + Raises + ------ + ValueError + If exactly one of getter or setter is not None, or if + getter, setter and attr are all None. """ if getter is None and setter is None and attr is None: raise ValueError("Specify attribute access") @@ -414,11 +420,28 @@ def __init__(self, name, obj, getter=None, setter=None, attr=None): return def getValue(self): - """Get the value of the Parameter.""" + """Get the value of the Parameter. + + Returns + ------- + object + The current value of the wrapped attribute. + """ return self.getter(self.obj) def set_value(self, value): - """Set the value of the Parameter.""" + """Set the value of the Parameter. + + Parameters + ---------- + value : object + The value to assign. + + Returns + ------- + ParameterAdapter + Return self so that mutators can be chained. + """ if value != self.getValue(): self.setter(self.obj, value) self.notify() diff --git a/src/diffpy/srfit/fitbase/parameterset.py b/src/diffpy/srfit/fitbase/parameterset.py index edaac6f5..21bb2dac 100644 --- a/src/diffpy/srfit/fitbase/parameterset.py +++ b/src/diffpy/srfit/fitbase/parameterset.py @@ -44,7 +44,7 @@ class ParameterSet(RecipeOrganizer): - """Class for organizing Parameters and other ParameterSets. + """Organize Parameters and other ParameterSets in a hierarchy. ParameterSets are hierarchical organizations of Parameters, Constraints, Restraints and other ParameterSets. @@ -52,8 +52,8 @@ class ParameterSet(RecipeOrganizer): Contained Parameters and other ParameterSets can be accessed by name as attributes in order to facilitate multi-level constraints and restraints. These constraints and restraints can be placed at any level and a flattened - list of them can be retrieved with the getConstraints and getRestraints - methods. + list of them can be retrieved with the '_get_constraints' and + '_get_restraints' methods. Attributes ---------- @@ -89,7 +89,7 @@ def __init__(self, name): Parameters ---------- - name + name : str The name of this ParameterSet. """ RecipeOrganizer.__init__(self, name) @@ -108,13 +108,14 @@ def add_parameter_set(self, parset): Parameters ---------- - parset + parset : ParameterSet The ParameterSet to be stored. - - Raises ValueError if the ParameterSet has no name. - Raises ValueError if the ParameterSet has the same name as some other - managed object. + Raises + ------ + ValueError + If the ParameterSet has no name, or if it has the same name + as some other managed object. """ self._add_object(parset, self._parsets, True) return @@ -134,7 +135,15 @@ def addParameterSet(self, parset): def remove_parameter_set(self, parset): """Remove a ParameterSet from the hierarchy. - Raises ValueError if parset is not managed by this object. + Parameters + ---------- + parset : ParameterSet + The ParameterSet to remove. + + Raises + ------ + ValueError + If parset is not managed by this object. """ self._remove_object(parset, self._parsets) return diff --git a/src/diffpy/srfit/fitbase/profile.py b/src/diffpy/srfit/fitbase/profile.py index 3718e907..45a295b7 100644 --- a/src/diffpy/srfit/fitbase/profile.py +++ b/src/diffpy/srfit/fitbase/profile.py @@ -89,13 +89,13 @@ class Profile(Observable, Validatable): Read-only property of _dyobs. x A numpy array of the calculated independent variable (default - None, property for xpar accessors). + None, property for ``xpar`` accessors). y The profile over the calculation range (default None, property - for ypar accessors). + for ``ypar`` accessors). dy The uncertainty in the profile over the calculation range - (default None, property for dypar accessors). + (default None, property for ``dypar`` accessors). ycalc A numpy array of the calculated signal (default None). xpar @@ -157,7 +157,13 @@ def __init__(self): def load_parsed_data(self, parser): """Load parsed data from a ProfileParser. - This sets the xobs, yobs, dyobs arrays as well as the metadata. + This sets the ``xobs``, ``yobs``, ``dyobs`` arrays as well as + the metadata. + + Parameters + ---------- + parser : ProfileParser + The parser holding the observed profile data and metadata. """ x, y, dx, dy = parser.get_data() self.meta = dict(parser.get_metadata()) @@ -180,23 +186,22 @@ def set_observed_profile(self, xobs, yobs, dyobs=None): Parameters ---------- - xobs - Numpy array of the independent variable - yobs - Numpy array of the observed signal. - dyobs - Numpy array of the uncertainty in the observed signal. If - `dyobs` is None (default), `dyobs` stays None to indicate - no uncertainty was observed, and the calculated `dy` will - be set to 1 at each calculation point instead. - + xobs : numpy.ndarray + The array of the independent variable. + yobs : numpy.ndarray + The array of the observed signal. + dyobs : numpy.ndarray, optional + The array of the uncertainty in the observed signal. If + ``dyobs`` is None (default), ``dyobs`` stays None to + indicate no uncertainty was observed, and the calculated + ``dy`` will be set to 1 at each calculation point instead. Raises - ----------- + ------ ValueError - if len(yobs) != len(xobs) + If ``len(yobs) != len(xobs)``. ValueError - if dyobs != None and len(dyobs) != len(xobs) + If ``dyobs`` is not None and ``len(dyobs) != len(xobs)``. """ if len(yobs) != len(xobs): raise ValueError("xobs and yobs are different lengths") @@ -240,7 +245,6 @@ def set_calculation_range(self, xmin=None, xmax=None, dx=None): Parameters ---------- - xmin : float or "obs", optional The minimum value of the independent variable. Keep the current minimum when not specified. If specified as "obs" @@ -354,14 +358,14 @@ def setCalculationRange(self, xmin=None, xmax=None, dx=None): def set_calculation_points(self, x): """Set the calculation points. + This creates ``y`` and ``dy`` on the specified grid if + ``xobs``, ``yobs`` and ``dyobs`` exist. + Parameters ---------- - x - A non-empty numpy array containing the calculation points. If - xobs exists, the bounds of x will be limited to its bounds. - - This will create y and dy on the specified grid if xobs, yobs and - dyobs exist. + x : numpy.ndarray + The non-empty array of calculation points. If ``xobs`` + exists, the bounds of ``x`` will be limited to its bounds. """ x = numpy.asarray(x) if self.xobs is not None: @@ -385,40 +389,48 @@ def set_calculation_points(self, x): @deprecated(setCalculationPoints_dep_msg) def setCalculationPoints(self, x): - """Set the calculation points. - - Parameters - ---------- - x - A non-empty numpy array containing the calculation points. If - xobs exists, the bounds of x will be limited to its bounds. + """This function has been deprecated and will be removed in version + 4.0.0. - This will create y and dy on the specified grid if xobs, yobs and - dyobs exist. + Please use + diffpy.srfit.fitbase.profile.Profile.set_calculation_points + instead. """ self.set_calculation_points(x) return def loadtxt(self, *args, **kw): - """Use numpy.loadtxt to load data. + """Load data using ``numpy.loadtxt``. - Arguments are passed to numpy.loadtxt. unpack = True is - enforced. The first two arrays returned by numpy.loadtxt are - assumed to be x and y. If there is a third array, it is assumed - to by dy. Any other arrays are ignored. These are passed to - set_observed_profile. + Arguments are passed to ``numpy.loadtxt``. ``unpack=True`` is + enforced. The first two arrays returned by ``numpy.loadtxt`` + are assumed to be x and y. If there is a third array, it is + assumed to be dy. Any other arrays are ignored. The loaded + arrays are passed to ``set_observed_profile``. - Raises ValueError if the call to numpy.loadtxt returns fewer - than 2 arrays. + Parameters + ---------- + *args + The positional arguments passed to ``numpy.loadtxt``. + **kw + The keyword arguments passed to ``numpy.loadtxt``. Returns ------- - x - x array loaded from the file. - y - y array loaded from the file. - dy - dy array loaded from the file. + x : numpy.ndarray + The array of the independent variable loaded from the + file. + y : numpy.ndarray + The array of the observed signal loaded from the file. + dy : numpy.ndarray or None + The array of the uncertainty loaded from the file, or None + if no third column is present. + + Raises + ------ + ValueError + If the call to ``numpy.loadtxt`` returns fewer than 2 + arrays. """ if len(args) == 8 and not args[-1]: args = list(args) @@ -441,21 +453,21 @@ def loadtxt(self, *args, **kw): return x, y, dy def savetxt(self, fname, **kwargs): - """Call `numpy.savetxt` with x, ycalc, y, dy. + """Call ``numpy.savetxt`` with x, ycalc, y, dy. Parameters ---------- fname : filename or file handle - This is passed to `numpy.savetxt`. + The filename or file handle passed to ``numpy.savetxt``. **kwargs - The keyword arguments that are passed to `numpy.savetxt`. + The keyword arguments that are passed to ``numpy.savetxt``. We preset file header "x ycalc y dy". Use ``header=''`` to save data without any header. Raises ------ SrFitError - When `self.ycalc` has not been set. + When ``self.ycalc`` has not been set. See also -------- @@ -484,11 +496,15 @@ def _flush(self, other): def _validate(self): """Validate my state. - This validates that x, y, dy, xobs and yobs are not None. dyobs - may be None, since observed uncertainties are optional. This - validates that x, y, and dy are the same length. + This validates that ``x``, ``y``, ``dy``, ``xobs`` and + ``yobs`` are not None. ``dyobs`` may be None, since observed + uncertainties are optional. This also validates that ``x``, + ``y``, and ``dy`` are the same length. - Raises SrFitError if validation fails. + Raises + ------ + SrFitError + If validation fails. """ datanotset = any( v is None @@ -511,24 +527,23 @@ def _validate(self): def _rebin_array(A, xold, xnew): - """Rebin the an array by interpolating over the new x range. + """Rebin an array by interpolating over a new sampling grid. + + This uses linear interpolation via ``numpy.interp``. Parameters ---------- - A - Array to interpolate - xold - Old sampling array - xnew - New sampling array - - - This uses cubic spline interpolation. + A : numpy.ndarray + The array to interpolate. + xold : numpy.ndarray + The old sampling array. + xnew : numpy.ndarray + The new sampling array. Returns ------- - array - A new array over the new sampling array. + numpy.ndarray + The array ``A`` resampled onto ``xnew``. """ if numpy.array_equal(xold, xnew): return A diff --git a/src/diffpy/srfit/fitbase/profilegenerator.py b/src/diffpy/srfit/fitbase/profilegenerator.py index e0206084..402b3916 100644 --- a/src/diffpy/srfit/fitbase/profilegenerator.py +++ b/src/diffpy/srfit/fitbase/profilegenerator.py @@ -136,10 +136,18 @@ def symbol(self): def __call__(self, x): """Evaluate the profile. - This method must be overloaded. + This method must be overloaded. It only takes the independent + variable to calculate over. - This method only takes the independent variables to calculate - over. + Parameters + ---------- + x : ndarray + The independent variable over which to calculate. + + Returns + ------- + ndarray + The calculated profile. """ return x @@ -148,7 +156,10 @@ def __call__(self, x): def operation(self): """Evaluate the profile. - Return the result of __call__(profile.x). + Returns + ------- + ndarray + The result of ``__call__(profile.x)``. """ y = self.__call__(self.profile.x) return y @@ -158,8 +169,8 @@ def set_profile(self, profile): Parameters ---------- - profile - A Profile that specifies the calculation points and which + profile : Profile + The Profile that specifies the calculation points and which will store the calculated signal. """ if self.profile is not None: @@ -191,7 +202,10 @@ def _validate(self): could be costly. The operation should be validated with a containing equation. - Raises SrFitError if validation fails. + Raises + ------ + SrFitError + If validation fails. """ if self.profile is None: raise SrFitError("profile is None") diff --git a/src/diffpy/srfit/fitbase/profileparser.py b/src/diffpy/srfit/fitbase/profileparser.py index 32bf2fba..7dd8ca4e 100644 --- a/src/diffpy/srfit/fitbase/profileparser.py +++ b/src/diffpy/srfit/fitbase/profileparser.py @@ -77,13 +77,13 @@ class ProfileParser(object): - """Class for parsing data from a or string. + """Base class for parsing profile data from a file. Attributes ---------- _format : str, optional The name of the data format that this parses (string, default - `""`). The format string is a unique identifier for the data + ``""``). The format string is a unique identifier for the data format handled by the parser. _banks : list of tuples The data from each bank. Each bank contains a (x, y, dx, @@ -177,15 +177,15 @@ def parse_file( automatic handling of uncertainties. This is a template method. Subclasses customize a format by - overriding the `_parse_metadata` and `_parse_data` hooks rather + overriding the ``_parse_metadata`` and ``_parse_data`` hooks rather than this method. - The default `_parse_data` reads a single bank: + The default ``_parse_data`` reads a single bank: - For files with 2 columns: assumes (x, y) and sets dx, dy to None. - For files with 3 columns: assumes (x, y, dy) and sets dx to None. - For files with 4 columns: assumes (x, y, dx, dy). - - For other cases: `column_format` must be explicitly specified. + - For other cases: ``column_format`` must be explicitly specified. Uncertainty columns (dx, dy) are only considered valid if all values are positive and not NaN/Inf. Otherwise they are set to None. @@ -201,31 +201,31 @@ def parse_file( If None, the format is auto-detected based on the number of columns. - Valid labels: `"x"`, `"y"`, `"dx"`, `"dy"` + Valid labels: ``"x"``, ``"y"``, ``"dx"``, ``"dy"`` Examples: - - `("x", "y")` - - `("x", "y", "dy")` - - `("x", "y", "dx", "dy")` - - `("x", "dx", "y", "dy")` + - ``("x", "y")`` + - ``("x", "y", "dy")`` + - ``("x", "y", "dx", "dy")`` + - ``("x", "dx", "y", "dy")`` metadata : dict, optional Additional metadata to merge into the metadata parsed from the file. Keys must be strings. A key that collides with one already present in the parsed metadata overrides the - parsed value. A key that collides with `"filename"`, - `"bank"`, or `"nbanks"`, which `parse_file` sets itself, + parsed value. A key that collides with ``"filename"``, + ``"bank"``, or ``"nbanks"``, which ``parse_file`` sets itself, also overrides the automatically set value, but raises a - `UserWarning` since it may affect other code that relies + ``UserWarning`` since it may affect other code that relies on the automatically set value. kwargs The keyword arguments passed on to - `diffpy.utils.parsers.load_data`, such as `usecols`, - `delimiter`, `comments` and `minrows`. Use `usecols` to + ``diffpy.utils.parsers.load_data``, such as ``usecols``, + ``delimiter``, ``comments`` and ``minrows``. Use ``usecols`` to select four columns out of a wider file, then label them - with `column_format`. + with ``column_format``. Raises ------ @@ -263,7 +263,8 @@ def _validate_metadata(metadata): return dict(metadata) def _apply_extra_metadata(self, metadata): - """Merge validated user-supplied metadata into `self._meta`.""" + """Merge validated user-supplied metadata into + ``self._meta``.""" if not metadata: return for key in metadata: @@ -385,13 +386,13 @@ def select_bank(self, index): Parameters ---------- - index - index of bank (integer, starting at 0). + index : int + The index of the bank (integer, starting at 0). Raises - ---------- + ------ IndexError - if requesting a bank that does not exist + If requesting a bank that does not exist. """ if index is None: index = self._meta.get("bank", 0) @@ -432,14 +433,15 @@ def get_data(self, index=None): Parameters ---------- - index - index of bank (integer, starting at 0, default None). If - index is None then the currently selected bank is used. + index : int, optional + The index of the bank (integer, starting at 0, default None). + If index is None then the currently selected bank is used. Returns - ---------- - This returns (x, y, dx, dy) tuple for the bank. dx is None if it - cannot be determined from the data format. + ------- + tuple + The ``(x, y, dx, dy)`` tuple for the bank. ``dx`` and ``dy`` + are None if they cannot be determined from the data format. """ self.select_bank(index) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index e329dfb2..0e7e54cd 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -174,10 +174,10 @@ class RecipeContainer(Observable, Configurable, Validatable): RecipeContainers are hierarchical organizations of Parameters and other RecipeContainers. This class provides attribute-access to these contained objects. Parameters and other RecipeContainers can be found within the - hierarchy with the _locate_managed_object method. + hierarchy with the `_locate_managed_object` method. A RecipeContainer can manage dictionaries for that store various objects. - These dictionaries can be added to the RecipeContainer using the _manage + These dictionaries can be added to the RecipeContainer using the `_manage` method. RecipeContainer methods that add, remove or retrieve objects will work with any managed dictionary. This makes it easy to add new types of objects to be contained by a RecipeContainer. By default, the @@ -260,11 +260,16 @@ def iterate_over_parameters( top-level parameters will be iterated over. fullnames : bool, optional The flag indicating whether to match against hierarchical - dotted namesrelative to this object. + dotted names relative to this object. If False (default), match only leaf parameter names. - Example - ------- + Yields + ------ + Parameter + The next Parameter whose name matches `pattern`. + + Examples + -------- .. for param in recipe.iterate_over_parameters(pattern="scale_"): @@ -346,20 +351,59 @@ def iterPars(self, pattern="", recurse=True): return self.iterate_over_parameters(pattern=pattern, recurse=recurse) def __iter__(self): - """Iterate over top-level parameters.""" + """Iterate over top-level parameters. + + Returns + ------- + iterator + The iterator over the top-level Parameters. + """ return iter(self._parameters.values()) def __len__(self): - """Get number of top-level parameters.""" + """Get number of top-level parameters. + + Returns + ------- + int + The number of top-level Parameters. + """ return len(self._parameters) def __getitem__(self, idx): - """Get top-level parameters by index.""" + """Get top-level parameters by index. + + Parameters + ---------- + idx : int or slice + The index, or slice, of the top-level Parameters to get. + + Returns + ------- + Parameter or list of Parameter + The Parameter, or list of Parameters, at `idx`. + """ # need to wrap this in a list for python 3 compatibility. return list(self._parameters.values())[idx] def __getattr__(self, name): - """Gives access to the contained objects as attributes.""" + """Give access to the contained objects as attributes. + + Parameters + ---------- + name : str + The name of the managed object to retrieve. + + Returns + ------- + object + The managed object registered under `name`. + + Raises + ------ + AttributeError + If no managed object is registered under `name`. + """ arg = self.get(name) if arg is None: raise AttributeError(name) @@ -374,7 +418,14 @@ def __getattr__(self, name): ) def __dir__(self): - """Return sorted list of attributes for this object.""" + """Return sorted list of attributes for this object. + + Returns + ------- + list of str + The sorted list of attribute names, including managed + objects. + """ rv = set(dir(type(self))) rv.update(self.__dict__) # self.get fetches looks up for items in all managed dictionaries. @@ -388,7 +439,28 @@ def __dir__(self): __managed = [] def __setattr__(self, name, value): - """Parameter access and object checking.""" + """Set an attribute, routing Parameter names to Parameter + values. + + If `name` matches a managed Parameter, the Parameter's value is + set rather than replacing the Parameter itself. Otherwise this + behaves like normal attribute assignment, except that a managed + non-Parameter object of that name may not be overwritten. + + Parameters + ---------- + name : str + The name of the attribute to set. + value + The value to assign. If `name` refers to a managed + Parameter, this may be a plain value or a Parameter, whose + value will be copied. + + Raises + ------ + AttributeError + If `name` refers to a managed, non-Parameter object. + """ if name in self._parameters: parameter = self._parameters[name] if isinstance(value, Parameter): @@ -405,11 +477,21 @@ def __setattr__(self, name, value): return def __delattr__(self, name): - """Delete parameters with del. + """Delete parameters with ``del``. This does not allow deletion of non-parameters, as this may require configuration changes that are not yet handled in a general way. + + Parameters + ---------- + name : str + The name of the Parameter to delete. + + Raises + ------ + AttributeError + If `name` refers to a managed, non-Parameter object. """ if name in self._parameters: self._remove_parameter(self._parameters[name]) @@ -423,7 +505,22 @@ def __delattr__(self, name): return def get(self, name, default=None): - """Get a managed object.""" + """Get a managed object. + + Parameters + ---------- + name : str + The name of the managed object to retrieve. + default : optional + The value to return if no managed object is found under + `name` (default None). + + Returns + ------- + object + The managed object registered under `name`, or `default` + if no such object exists. + """ for d in self.__managed: arg = d.get(name) if arg is not None: @@ -432,7 +529,13 @@ def get(self, name, default=None): return default def get_names(self): - """Get the names of managed parameters.""" + """Get the names of managed parameters. + + Returns + ------- + list of str + The names of the managed Parameters. + """ return [p.name for p in self._parameters.values()] @deprecated(getNames_deprecation_msg) @@ -447,7 +550,13 @@ def getNames(self): return self.get_names() def get_values(self): - """Get the values of managed parameters.""" + """Get the values of managed parameters. + + Returns + ------- + list + The values of the managed Parameters. + """ return [p.value for p in self._parameters.values()] @deprecated(getValues_deprecation_msg) @@ -471,13 +580,16 @@ def _add_object(self, obj, d, check=True): d The managed dictionary to store the object in. check - If True (default), a ValueError is raised an object of the - given name already exists. - + If True (default), a ValueError is raised if an object of + the given name already exists. - Raises ValueError if the object has no name. - Raises ValueError if the object has the same name as some other managed - object. + Raises + ------ + ValueError + If the object has no name. + ValueError + If the object has the same name as some other managed + object. """ # Check name if not obj.name: @@ -518,7 +630,10 @@ def _add_object(self, obj, d, check=True): def _remove_object(self, obj, d): """Remove an object from a managed dictionary. - Raises ValueError if obj is not part of the dictionary. + Raises + ------ + ValueError + If `obj` is not part of the dictionary. """ if obj not in d.values(): m = "'%s' is not part of the %s" % (obj, self.__class__.__name__) @@ -537,11 +652,13 @@ def _locate_managed_object(self, obj): obj The object to find. - - Returns a list of objects. The first member of the list is this object, - and each subsequent member is a sub-object of the previous one. The - last entry in the list is obj. If obj cannot be found, the list is - empty. + Returns + ------- + list + The list of objects. The first member of the list is this + object, and each subsequent member is a sub-object of the + previous one. The last entry in the list is `obj`. If `obj` + cannot be found, the list is empty. """ loc = [self] @@ -580,7 +697,10 @@ def _validate(self): This validates that contained Parameters and managed objects are valid. - Raises AttributeError if validation fails. + Raises + ------ + AttributeError + If validation fails. """ iterable = chain(self.__iter__(), self._iter_managed()) self._validate_others(iterable) @@ -597,7 +717,7 @@ class RecipeOrganizer(_recipeorganizer_interface, RecipeContainer): Restraints, as well as Equations that can be used in Constraint and Restraint equations. These constraints and Restraints can be placed at any level and a flattened list of them can be retrieved with the - _get_constraints and _get_restraints methods. + `_get_constraints` and `_get_restraints` methods. Attributes ---------- @@ -627,8 +747,10 @@ class RecipeOrganizer(_recipeorganizer_interface, RecipeContainer): values Variable values (read only). See get_values. - - Raises ValueError if the name is not a valid attribute identifier + Raises + ------ + ValueError + If the name is not a valid attribute identifier. """ def __init__(self, name): @@ -647,9 +769,12 @@ def _new_parameter(self, name, value, check=True): """Add a new Parameter to the container. This creates a new Parameter and adds it to the container using - the _add_parameter method. + the `_add_parameter` method. - Returns the Parameter. + Returns + ------- + Parameter + The newly created Parameter. """ p = Parameter(name, value) self._add_parameter(p, check) @@ -665,13 +790,16 @@ def _add_parameter(self, parameter, check=True): parameter The Parameter to be stored. check - If True (default), a ValueError is raised a Parameter of + If True (default), a ValueError is raised if a Parameter of the specified name has already been inserted. - - Raises ValueError if the Parameter has no name. - Raises ValueError if the Parameter has the same name as a contained - RecipeContainer. + Raises + ------ + ValueError + If the Parameter has no name. + ValueError + If the Parameter has the same name as a contained + RecipeContainer. """ # Store the Parameter RecipeContainer._add_object(self, parameter, self._parameters, check) @@ -683,14 +811,16 @@ def _add_parameter(self, parameter, check=True): def _remove_parameter(self, parameter): """Remove a parameter. - This de-registers the Parameter with the _eqfactory. The + This de-registers the Parameter with the `_eqfactory`. The Parameter will remain part of built equations. Note that constraints and restraints involving the Parameter are not modified. - Raises ValueError if parameter is not part of the - RecipeOrganizer. + Raises + ------ + ValueError + If `parameter` is not part of the RecipeOrganizer. """ self._remove_object(parameter, self._parameters) self._eqfactory.deRegisterBuilder(parameter.name) @@ -714,6 +844,11 @@ def register_calculator(self, calculator, argnames=None): The names of the arguments to `calculator` (list or None). If this is None, then the argument names will be extracted from the function. + + Returns + ------- + Equation + The callable Equation object wrapping `calculator`. """ self._eqfactory.registerOperator(calculator.name, calculator) self._add_object(calculator, self._calculators) @@ -767,11 +902,10 @@ def register_function(self, function, name=None, argnames=None): If this is None (default), then the argument names will be extracted from the function. - Note - ---- - The `name` and `argnames` args can be extracted from regular Python - functions (of type ), bound class methods, and callable - classes. + Returns + ------- + equation_object : Equation + The callable Equation object. Raises ------ @@ -782,10 +916,11 @@ def register_function(self, function, name=None, argnames=None): ValueError If function is an Equation object and name is None. - Returns - ------- - equation_object : Equation - The callable Equation object. + Notes + ----- + The `name` and `argnames` args can be extracted from regular Python + functions (of type ), bound class methods, and callable + classes. """ # If the function is an equation, we treat it specially. This is # required so that the objects observed by the root get observed if the @@ -891,6 +1026,11 @@ def register_string_function(self, function_str, name, func_params={}): A dictionary of Parameters, indexed by name, that are used in `function_str`, but not part of the FitRecipe (default {}). + Returns + ------- + equation_object : Equation + The callable Equation object. + Raises ------ ValueError @@ -898,11 +1038,6 @@ def register_string_function(self, function_str, name, func_params={}): managed object. ValueError If the function name is the name of another managed object. - - Returns - ------- - equation_object : Equation - The callable Equation object. """ # Build the equation instance. eq = get_equation_from_string( @@ -1099,8 +1234,10 @@ def remove_constraint(self, *pars): *pars : str or Parameter The names of Parameters or Parameters to unconstrain. - - Raises ValueError if the Parameter is not constrained. + Raises + ------ + ValueError + If the Parameter is not constrained. """ update = False for parameter in pars: @@ -1148,10 +1285,10 @@ def get_constrained_parmeters(self, recurse=False): this object are returned. If True, constrained Parameters in managed sub-objects are also included. - Return - ------ + Returns + ------- constrained_params : list of Parameter - A list of constrained managed Parameters in this object. + The list of constrained managed Parameters in this object. """ const = self._get_constraints(recurse) constrained_params = const.keys() @@ -1159,13 +1296,12 @@ def get_constrained_parmeters(self, recurse=False): @deprecated(getConstrainedPars_deprecation_msg) def getConstrainedPars(self, recurse=False): - """Get a list of constrained managed Parameters in this object. + """This function has been deprecated and will be removed in + version 4.0.0. - Parameters - ---------- - recurse - Recurse into managed objects and retrieve their constrained - Parameters as well (default False). + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.get_constrained_parmeters + instead. """ return self.get_constrained_parmeters(recurse=recurse) @@ -1377,9 +1513,10 @@ def clear_all_soft_bounds(self, recurse=False): Parameters ---------- - recurse - Recurse into managed objects and clear all restraints - found there as well. + recurse : bool, optional + If False (default), only restraints in this object are + cleared. If True, restraints in managed sub-objects are + also cleared. """ self.remove_soft_bounds(*self._restraints) if recurse: @@ -1429,7 +1566,10 @@ def _validate(self): This performs RecipeContainer validations. This validates contained Restraints and Constraints. - Raises AttributeError if validation fails. + Raises + ------ + AttributeError + If validation fails. """ RecipeContainer._validate(self) iterable = chain(self._restraints, self._constraints.values()) @@ -1449,7 +1589,7 @@ def _format_managed(self, prefix=""): Returns ------- list - List of formatted lines, one per each Parameter. + The list of formatted lines, one per each Parameter. """ lines = [] formatstr = "{: Date: Thu, 6 Aug 2026 11:01:03 -0400 Subject: [PATCH 02/11] update docstrings in pdf to np/group standards --- src/diffpy/srfit/pdf/basepdfgenerator.py | 98 ++++++++++++++++------ src/diffpy/srfit/pdf/debyepdfgenerator.py | 22 +++-- src/diffpy/srfit/pdf/pdfcontribution.py | 99 ++++++++++++++++------- src/diffpy/srfit/pdf/pdfgenerator.py | 8 +- src/diffpy/srfit/pdf/pdfparser.py | 30 ++++--- 5 files changed, 178 insertions(+), 79 deletions(-) diff --git a/src/diffpy/srfit/pdf/basepdfgenerator.py b/src/diffpy/srfit/pdf/basepdfgenerator.py index 837dce98..0c4d7341 100644 --- a/src/diffpy/srfit/pdf/basepdfgenerator.py +++ b/src/diffpy/srfit/pdf/basepdfgenerator.py @@ -93,7 +93,13 @@ class BasePDFGenerator(ProfileGenerator): """ def __init__(self, name="pdf"): - """Initialize the generator.""" + """Initialize the generator. + + Parameters + ---------- + name : str, optional + The name for this generator (default "pdf"). + """ ProfileGenerator.__init__(self, name) self._phase = None @@ -125,13 +131,12 @@ def parallel(self, ncpu, mapfunc=None): Parameters ---------- - ncpu - Number of parallel processes. Revert to serial mode when 1. - mapfunc - A mapping function to use. If this is None (default), - multiprocessing.Pool.imap_unordered will be used. - - No return value. + ncpu : int + The number of parallel processes. Revert to serial mode + when 1. + mapfunc : callable, optional + The mapping function to use. If this is None (default), + ``multiprocessing.Pool.imap_unordered`` will be used. """ from diffpy.srreal.parallel import createParallelCalculator @@ -183,12 +188,15 @@ def setScatteringType(self, stype="X"): Parameters ---------- - stype + stype : str, optional "X" for x-ray, "N" for neutron, "E" for electrons, or any registered type from diffpy.srreal from - ScatteringFactorTable.getRegisteredTypes(). + ScatteringFactorTable.getRegisteredTypes() (default "X"). - Raises ValueError for unknown scattering type. + Raises + ------ + ValueError + If stype is not a recognized scattering type. """ self._calc.setScatteringFactorTableByType(stype) # update the meta dictionary only if there was no exception @@ -198,28 +206,57 @@ def setScatteringType(self, stype="X"): def getScatteringType(self): """Get the scattering type. - See 'setScatteringType'. + See ``setScatteringType``. + + Returns + ------- + str + The scattering type used to calculate the PDF. """ return self._calc.getRadiationType() def setQmax(self, qmax): - """Set the qmax value.""" + """Set the qmax value. + + Parameters + ---------- + qmax : float + The maximum scattering vector used to generate the PDF. + """ self._calc.qmax = qmax self.meta["qmax"] = self.getQmax() return def getQmax(self): - """Get the qmax value.""" + """Get the qmax value. + + Returns + ------- + float + The maximum scattering vector used to generate the PDF. + """ return self._calc.qmax def setQmin(self, qmin): - """Set the qmin value.""" + """Set the qmin value. + + Parameters + ---------- + qmin : float + The minimum scattering vector used to generate the PDF. + """ self._calc.qmin = qmin self.meta["qmin"] = self.getQmin() return def getQmin(self): - """Get the qmin value.""" + """Get the qmin value. + + Returns + ------- + float + The minimum scattering vector used to generate the PDF. + """ return self._calc.qmin def setStructure(self, stru, name="phase", periodic=True): @@ -233,12 +270,12 @@ def setStructure(self, stru, name="phase", periodic=True): Parameters ---------- stru - diffpy.structure.Structure, pyobjcryst.crystal.Crystal or - pyobjcryst.molecule.Molecule instance. Default None. - name - A name to give to the managed ParameterSet that adapts stru + The diffpy.structure.Structure, pyobjcryst.crystal.Crystal or + pyobjcryst.molecule.Molecule instance to adapt (default None). + name : str, optional + The name to give to the managed ParameterSet that adapts stru (default "phase"). - periodic + periodic : bool, optional The structure should be treated as periodic (default True). Note that some structures do not support periodicity, in which case this will have no effect on the @@ -262,11 +299,11 @@ def setPhase(self, parset, periodic=True): Parameters ---------- parset - A SrRealParSet that holds the structural information. + The SrRealParSet that holds the structural information. This can be used to share the phase between multiple BasePDFGenerators, and have the changes in one reflect in another. - periodic + periodic : bool, optional The structure should be treated as periodic (default True). Note that some structures do not support periodicity, in which case this will be ignored. @@ -298,7 +335,10 @@ def _validate(self): This validates that the phase is not None. This performs ProfileGenerator validations. - Raises SrFitError if validation fails. + Raises + ------ + SrFitError + If validation fails. """ if self._calc is None: raise SrFitError("_calc is None") @@ -315,6 +355,16 @@ def __call__(self, r): evaluated, the crystal has been updated by the optimizer via the ObjCrystParSet created in setCrystal. Thus, we need only call pdf with the internal structure object. + + Parameters + ---------- + r : np.ndarray + The independent variable over which to calculate the PDF. + + Returns + ------- + np.ndarray + The calculated PDF, evaluated over r. """ if not numpy.array_equal(r, self._lastr): self._prepare(r) diff --git a/src/diffpy/srfit/pdf/debyepdfgenerator.py b/src/diffpy/srfit/pdf/debyepdfgenerator.py index ba84faac..7861bcd4 100644 --- a/src/diffpy/srfit/pdf/debyepdfgenerator.py +++ b/src/diffpy/srfit/pdf/debyepdfgenerator.py @@ -95,12 +95,12 @@ def setStructure(self, stru, name="phase", periodic=False): Parameters ---------- stru - diffpy.structure.Structure, pyobjcryst.crystal.Crystal or - pyobjcryst.molecule.Molecule instance. Default None. - name - A name to give to the managed ParameterSet that adapts stru + The diffpy.structure.Structure, pyobjcryst.crystal.Crystal or + pyobjcryst.molecule.Molecule instance to adapt (default None). + name : str, optional + The name to give to the managed ParameterSet that adapts stru (default "phase"). - periodic + periodic : bool, optional The structure should be treated as periodic (default False). Note that some structures do not support periodicity, in which case this will have no effect on the @@ -119,11 +119,11 @@ def setPhase(self, parset, periodic=False): Parameters ---------- parset - A SrRealParSet that holds the structural information. + The SrRealParSet that holds the structural information. This can be used to share the phase between multiple BasePDFGenerators, and have the changes in one reflect in another. - periodic + periodic : bool, optional The structure should be treated as periodic (default True). Note that some structures do not support periodicity, in which case this will be ignored. @@ -131,7 +131,13 @@ def setPhase(self, parset, periodic=False): return BasePDFGenerator.setPhase(self, parset, periodic) def __init__(self, name="pdf"): - """Initialize the generator.""" + """Initialize the generator. + + Parameters + ---------- + name : str, optional + The name for this generator (default "pdf"). + """ from diffpy.srreal.pdfcalculator import DebyePDFCalculator BasePDFGenerator.__init__(self, name) diff --git a/src/diffpy/srfit/pdf/pdfcontribution.py b/src/diffpy/srfit/pdf/pdfcontribution.py index 207d82d1..b103a414 100644 --- a/src/diffpy/srfit/pdf/pdfcontribution.py +++ b/src/diffpy/srfit/pdf/pdfcontribution.py @@ -24,10 +24,9 @@ class PDFContribution(FitContribution): - """PDFContribution class. + """A FitContribution that is customized for PDF fits. - PDFContribution is a FitContribution that is customized for PDF fits. Data - and phases can be added directly to the PDFContribution. Setup of + Data and phases can be added directly to the PDFContribution. Setup of constraints and restraints requires direct interaction with the generator attributes (see setPhase). @@ -86,7 +85,7 @@ def __init__(self, name): Parameters ---------- - name + name : str The name of the contribution. """ FitContribution.__init__(self, name) @@ -134,16 +133,15 @@ def setCalculationRange(self, xmin=None, xmax=None, dx=None): Parameters ---------- - - xmin : float or `obs`, optional + xmin : float or ``obs``, optional The minimum value of the independent variable. Keep the current minimum when not specified. If specified as "obs" reset to the minimum observed value. - xmax : float or `obs`, optional + xmax : float or ``obs``, optional The maximum value of the independent variable. Keep the current maximum when not specified. If specified as "obs" reset to the maximum observed value. - dx : float or `obs`, optional + dx : float or ``obs``, optional The sample spacing in the independent variable. When different from the data, resample the ``x`` as anchored at ``xmin``. @@ -164,7 +162,12 @@ def savetxt(self, fname, **kwargs): This calls on the built-in Profile. - Arguments are passed to numpy.savetxt. + Parameters + ---------- + fname : str or Path + The file or filename to which the data is saved. + **kwargs + Additional arguments passed to numpy.savetxt. """ return self.profile.savetxt(fname, **kwargs) @@ -175,7 +178,7 @@ def addStructure(self, name, stru, periodic=True): Parameters ---------- - name + name : str A name to give the generator that will manage the PDF calculation from the passed structure. The adapted structure will be accessible via the name "phase" as an @@ -184,9 +187,9 @@ def addStructure(self, name, stru, periodic=True): contribution and 'name' is passed name. (default), then the name will be set as "phase". stru - diffpy.structure.Structure, pyobjcryst.crystal.Crystal or - pyobjcryst.molecule.Molecule instance. Default None. - periodic + The diffpy.structure.Structure, pyobjcryst.crystal.Crystal or + pyobjcryst.molecule.Molecule instance to adapt (default None). + periodic : bool, optional The structure should be treated as periodic. If this is True (default), then a PDFGenerator will be used to calculate the PDF from the phase. Otherwise, a @@ -194,9 +197,11 @@ def addStructure(self, name, stru, periodic=True): do not support periodicity, in which case this may be ignored. - - Returns the new phase (ParameterSet appropriate for what was passed in - stru.) + Returns + ------- + ParameterSet + The new phase, of the type appropriate for what was passed + in stru. """ # Based on periodic, create the proper generator. if periodic: @@ -219,7 +224,7 @@ def addPhase(self, name, parset, periodic=True): Parameters ---------- - name + name : str A name to give the generator that will manage the PDF calculation from the passed parameter phase. The parset will be accessible via the name "phase" as an attribute @@ -227,11 +232,11 @@ def addPhase(self, name, parset, periodic=True): 'contribution' is this contribution and 'name' is passed name. parset - A SrRealParSet that holds the structural information. + The SrRealParSet that holds the structural information. This can be used to share the phase between multiple BasePDFGenerators, and have the changes in one reflect in another. - periodic + periodic : bool, optional The structure should be treated as periodic. If this is True (default), then a PDFGenerator will be used to calculate the PDF from the phase. Otherwise, a @@ -239,9 +244,11 @@ def addPhase(self, name, parset, periodic=True): do not support periodicity, in which case this may be ignored. - - Returns the new phase (ParameterSet appropriate for what was passed in - stru.) + Returns + ------- + ParameterSet + The new phase, of the type appropriate for what was passed + in parset. """ # Based on periodic, create the proper generator. if periodic: @@ -302,10 +309,13 @@ def setScatteringType(self, type="X"): Parameters ---------- - type - "X" for x-ray or "N" for neutron + type : str, optional + "X" for x-ray or "N" for neutron (default "X"). - Raises ValueError if type is not "X" or "N" + Raises + ------ + ValueError + If type is not "X" or "N". """ self._meta["stype"] = type for gen in self._generators.values(): @@ -315,30 +325,59 @@ def setScatteringType(self, type="X"): def getScatteringType(self): """Get the scattering type. - See 'setScatteringType'. + See ``setScatteringType``. + + Returns + ------- + str + The scattering type used to calculate the PDF. """ return self._get_meta_value("stype") def setQmax(self, qmax): - """Set the qmax value.""" + """Set the qmax value. + + Parameters + ---------- + qmax : float + The maximum scattering vector used to generate the PDF. + """ self._meta["qmax"] = qmax for gen in self._generators.values(): gen.setQmax(qmax) return def getQmax(self): - """Get the qmax value.""" + """Get the qmax value. + + Returns + ------- + float + The maximum scattering vector used to generate the PDF. + """ return self._get_meta_value("qmax") def setQmin(self, qmin): - """Set the qmin value.""" + """Set the qmin value. + + Parameters + ---------- + qmin : float + The minimum scattering vector used to generate the PDF. + """ self._meta["qmin"] = qmin for gen in self._generators.values(): gen.setQmin(qmin) return def getQmin(self): - """Get the qmin value.""" + """Get the qmin value. + + Returns + ------- + float + The minimum scattering vector used to generate the PDF. + """ return self._get_meta_value("qmin") diff --git a/src/diffpy/srfit/pdf/pdfgenerator.py b/src/diffpy/srfit/pdf/pdfgenerator.py index 3a2f919b..7dea458f 100644 --- a/src/diffpy/srfit/pdf/pdfgenerator.py +++ b/src/diffpy/srfit/pdf/pdfgenerator.py @@ -84,7 +84,13 @@ class PDFGenerator(BasePDFGenerator): """ def __init__(self, name="pdf"): - """Initialize the generator.""" + """Initialize the generator. + + Parameters + ---------- + name : str, optional + The name for this generator (default "pdf"). + """ from diffpy.srreal.pdfcalculator import PDFCalculator BasePDFGenerator.__init__(self, name) diff --git a/src/diffpy/srfit/pdf/pdfparser.py b/src/diffpy/srfit/pdf/pdfparser.py index 147efdb1..26f5c584 100644 --- a/src/diffpy/srfit/pdf/pdfparser.py +++ b/src/diffpy/srfit/pdf/pdfparser.py @@ -26,18 +26,18 @@ class PDFParser(ProfileParser): - """Class for holding a diffraction pattern. + """Parser for PDF diffraction pattern data. PDFgetX and PDFgetN write their header as plain ``name = value`` pairs, including ``stype = X`` or ``stype = N`` for the scattering - type, so this class parses files identically to `ProfileParser` - and only sets `_format` to identify PDF data. + type, so this class parses files identically to ``ProfileParser`` + and only sets ``_format`` to identify PDF data. Attributes ---------- _format - Name of the data format that this parses (string, default - ""). The format string is a unique identifier for the data + The name of the data format that this parses (string, default + ``""``). The format string is a unique identifier for the data format handled by the parser. _banks The data from each bank. Each bank contains a @@ -57,20 +57,18 @@ class PDFParser(ProfileParser): from the file. This is None if the uncertainty cannot be read. _x - Independent variable from the chosen bank + The independent variable from the chosen bank. _y - Profile from the chosen bank + The profile from the chosen bank. _dx - Uncertainty in independent variable from the chosen bank + The uncertainty in independent variable from the chosen bank. _dy - Uncertainty in profile from the chosen bank + The uncertainty in profile from the chosen bank. _meta A dictionary containing metadata read from the file. General Metadata - - Attributes - ---------- + ----------------- filename The name of the file from which data was parsed. This key will not exist if data was not read from file. @@ -80,13 +78,13 @@ class PDFParser(ProfileParser): The chosen bank number. Metadata - ---------- + -------- stype - The scattering type ("X", "N") + The scattering type ("X", "N"). qmin - Minimum scattering vector (float) + The minimum scattering vector (float). qmax - Maximum scattering vector (float) + The maximum scattering vector (float). These, along with any other ``name = value`` pairs in the header, may appear in the metadata dictionary. From 4b88be3bf904ad4a258a31ed9b7454fcb5e776f4 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 11:01:29 -0400 Subject: [PATCH 03/11] update docstrings in interface and structure to group standards --- src/diffpy/srfit/interface/interface.py | 95 +++++++++++++++----- src/diffpy/srfit/structure/cctbxparset.py | 8 +- src/diffpy/srfit/structure/diffpyparset.py | 8 +- src/diffpy/srfit/structure/objcrystparset.py | 40 +++++---- 4 files changed, 104 insertions(+), 47 deletions(-) diff --git a/src/diffpy/srfit/interface/interface.py b/src/diffpy/srfit/interface/interface.py index 31fef6dd..fc0b78e9 100644 --- a/src/diffpy/srfit/interface/interface.py +++ b/src/diffpy/srfit/interface/interface.py @@ -36,14 +36,19 @@ class ParameterInterface(object): """Mix-in class for enhancing the Parameter interface.""" def __lshift__(self, v): - """set_value with << + """Set the value with ``<<``. - Think of '<<' as injecting a value + Think of ``<<`` as injecting a value. - Attributes + Parameters ---------- - v - value or Argument derivative + v : float or ArgumentABC + The value, or Argument derivative, to assign. + + Returns + ------- + ParameterInterface + Return self after the value has been set. """ if isinstance(v, ArgumentABC): self.value = v.value @@ -61,31 +66,60 @@ class RecipeOrganizerInterface(object): """Mix-in class for enhancing the RecipeOrganizer interface.""" def __imul__(self, args): - """Constrain with *= + """Constrain with ``*=``. + + Think of ``*`` as a push-pin. This accepts arguments for a + single constraint. - Think of '*' as a push-pin. + Parameters + ---------- + args : tuple + The arguments to pass to `constrain`. - This accepts arguments for a single constraint. + Returns + ------- + RecipeOrganizerInterface + Return self after applying the constraint. """ _applyargs(args, self.constrain) return self def __imod__(self, args): - """Restrain with %= + """Restrain with ``%=``. - This of '%' as a loose rope. + Think of ``%`` as a loose rope. This accepts arguments for a + single restraint. + + Parameters + ---------- + args : tuple + The arguments to pass to `restrain`. - This accepts arguments for a single restraint. + Returns + ------- + RecipeOrganizerInterface + Return self after applying the restraint. """ _applyargs(args, self.restrain) return self def __iadd__(self, args): - """_new_parameter or _add_parameter with += + """Add a parameter with ``+=``. - Think of "+" as addition of a Parameter. + Think of ``+`` as addition of a Parameter. This accepts + arguments for a single call to `_new_parameter` or + `_add_parameter`. - This accepts arguments for a single function call. + Parameters + ---------- + args : tuple + The arguments to pass to `_new_parameter` or + `_add_parameter`. + + Returns + ------- + RecipeOrganizerInterface + Return self after adding the parameter. """ # Want to detect _add_parameter or _new_parameter @@ -109,22 +143,41 @@ class FitRecipeInterface(object): """Mix-in class for enhancing the FitRecipe interface.""" def __ior__(self, args): - """AddContribution with |= + """Add a contribution with ``|=``. + + Think of ``|`` as the union of components. This accepts a + single argument. - Think of "|" as the union of components. + Parameters + ---------- + args : FitContribution + The contribution to add. - This accepts a single argument. + Returns + ------- + FitRecipeInterface + Return self after adding the contribution. """ self.add_contribution(args) return self def __iadd__(self, args): - """add_variable or create_new_variable with += + """Add a variable with ``+=``. - Think of "+" as addition of a variable. + Think of ``+`` as addition of a variable. This accepts a + single argument or an iterable of single arguments or + argument tuples. - This accepts a single argument or an iterable of single - arguments or argument tuples. + Parameters + ---------- + args + The arguments to pass to `add_variable` or + `create_new_variable`. + + Returns + ------- + FitRecipeInterface + Return self after adding the variable. """ # Want to detect add_variable or create_new_variable diff --git a/src/diffpy/srfit/structure/cctbxparset.py b/src/diffpy/srfit/structure/cctbxparset.py index 544d1f3f..99711c07 100644 --- a/src/diffpy/srfit/structure/cctbxparset.py +++ b/src/diffpy/srfit/structure/cctbxparset.py @@ -20,11 +20,11 @@ object after wrapping may not be reflected within the wrapper, which can have unpredictable results during a structure refinement. -Classes: +The following classes are adapted: -CCTBXCrystalParSet -- Wrapper for cctbx.crystal -CCTBXUnitCellParSet -- Wrapper for the unit cell of cctbx.crystal -CCTBXScattererParSet -- Wrapper for cctbx.xray.scatterer +- `CCTBXCrystalParSet`: wrapper for `cctbx.crystal`. +- `CCTBXUnitCellParSet`: wrapper for the unit cell of `cctbx.crystal`. +- `CCTBXScattererParSet`: wrapper for `cctbx.xray.scatterer`. """ from diffpy.srfit.fitbase.parameter import ParameterAdapter diff --git a/src/diffpy/srfit/structure/diffpyparset.py b/src/diffpy/srfit/structure/diffpyparset.py index 452dd6ed..545cb976 100644 --- a/src/diffpy/srfit/structure/diffpyparset.py +++ b/src/diffpy/srfit/structure/diffpyparset.py @@ -22,9 +22,11 @@ the diffpy.structure.Structure object should be fully configured before passing it to DiffpyStructureParSet. -DiffpyStructureParSet -- Adapter for diffpy.structure.Structure -DiffpyLatticeParSet -- Adapter for diffpy.structure.Lattice -DiffpyAtomParSet -- Adapter for diffpy.structure.Atom +The following classes are adapted: + +- `DiffpyStructureParSet`: adapter for `diffpy.structure.Structure`. +- `DiffpyLatticeParSet`: adapter for `diffpy.structure.Lattice`. +- `DiffpyAtomParSet`: adapter for `diffpy.structure.Atom`. """ __all__ = ["DiffpyStructureParSet"] diff --git a/src/diffpy/srfit/structure/objcrystparset.py b/src/diffpy/srfit/structure/objcrystparset.py index 0739f8d9..755f818e 100644 --- a/src/diffpy/srfit/structure/objcrystparset.py +++ b/src/diffpy/srfit/structure/objcrystparset.py @@ -16,25 +16,27 @@ ParameterSet. This will adapt a Crystal or Molecule object from pyobjcryst into the -ParameterSet interface. The following classes are adapted. - -ObjCrystCrystalParSet -- Adapter for pyobjcryst.crystal.Crystal -ObjCrystAtomParSet -- Adapter for pyobjcryst.atom.Atom -ObjCrystMoleculeParSet -- Adapter for pyobjcryst.molecule.Molecule -ObjCrystMolAtomParSet -- Adapter for pyobjcryst.molecule.MolAtom - -Related to the adaptation of Molecule and MolAtom, there are adaptors for -specifying molecule restraints. -ObjCrystBondLengthRestraint -ObjCrystBondAngleRestraint -ObjCrystDihedralAngleRestraint - -There are also Parameters for encapsulating and modifying atoms via their -relative positions. These Parameters can also act like constraints, and can -modify the positions of multiple MolAtoms. -ObjCrystBondLengthParameter -ObjCrystBondAngleParameter -ObjCrystDihedralAngleParameter +ParameterSet interface. The following classes are adapted: + +- `ObjCrystCrystalParSet`: adapter for `pyobjcryst.crystal.Crystal`. +- `ObjCrystAtomParSet`: adapter for `pyobjcryst.atom.Atom`. +- `ObjCrystMoleculeParSet`: adapter for `pyobjcryst.molecule.Molecule`. +- `ObjCrystMolAtomParSet`: adapter for `pyobjcryst.molecule.MolAtom`. + +Related to the adaptation of Molecule and MolAtom, there are adaptors +for specifying molecule restraints: + +- `ObjCrystBondLengthRestraint` +- `ObjCrystBondAngleRestraint` +- `ObjCrystDihedralAngleRestraint` + +There are also Parameters for encapsulating and modifying atoms via +their relative positions. These Parameters can also act like +constraints, and can modify the positions of multiple MolAtoms: + +- `ObjCrystBondLengthParameter` +- `ObjCrystBondAngleParameter` +- `ObjCrystDihedralAngleParameter` """ __all__ = ["ObjCrystMoleculeParSet", "ObjCrystCrystalParSet"] From 14ddab2cc16ee65a0e5893ecd0b43edfdf532b0f Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 11:02:09 -0400 Subject: [PATCH 04/11] news --- news/np-docstrings.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 news/np-docstrings.rst diff --git a/news/np-docstrings.rst b/news/np-docstrings.rst new file mode 100644 index 00000000..a123ce9e --- /dev/null +++ b/news/np-docstrings.rst @@ -0,0 +1,23 @@ +**Added:** + +* No news needed: converted docstrings in `fitbase/` and `pdf/` to NumPy style + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* From 0cab9d1390910d3309f482e8c42c6a3d86a44d7f Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 11:32:45 -0400 Subject: [PATCH 05/11] use recipe.plot_recipe() to plot fits in examples --- docs/examples/coreshellnp.py | 33 +++++------- docs/examples/crystalpdf.py | 33 +++++------- docs/examples/crystalpdfall.py | 83 ++++++----------------------- docs/examples/crystalpdfobjcryst.py | 4 +- docs/examples/crystalpdftwodata.py | 53 ++++++------------ docs/examples/crystalpdftwophase.py | 33 +++++------- docs/examples/debyemodel.py | 26 ++++----- docs/examples/debyemodelII.py | 51 ++++++++---------- docs/examples/ellipsoidsas.py | 41 +++++++------- docs/examples/gaussianrecipe.py | 37 ++++++------- docs/examples/interface.py | 4 +- docs/examples/npintensity.py | 39 +++++++------- docs/examples/npintensityII.py | 67 +++++++++++------------ docs/examples/nppdfcrystal.py | 42 ++++++++------- docs/examples/nppdfobjcryst.py | 34 +++++------- docs/examples/nppdfsas.py | 50 +++++++++-------- docs/examples/simplepdf.py | 4 +- docs/examples/simplepdftwophase.py | 4 +- docs/examples/threedoublepeaks.py | 37 ++++++------- 19 files changed, 277 insertions(+), 398 deletions(-) diff --git a/docs/examples/coreshellnp.py b/docs/examples/coreshellnp.py index fd54a50f..61550c56 100644 --- a/docs/examples/coreshellnp.py +++ b/docs/examples/coreshellnp.py @@ -20,7 +20,6 @@ different phases, each with an appropriate characteristic function. """ -import numpy from pyobjcryst import loadCrystal from scipy.optimize import leastsq @@ -134,26 +133,18 @@ def makeRecipe(stru1, stru2, datname): return recipe -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. - r = recipe.cdszns.profile.x - g = recipe.cdszns.profile.y - gcalc = recipe.cdszns.profile.ycalc - diffzero = -0.8 * max(g) * numpy.ones_like(g) - diff = g - gcalc + diffzero - - import pylab - - pylab.plot(r, g, "bo", label="G(r) Data") - pylab.plot(r, gcalc, "r-", label="G(r) Fit") - pylab.plot(r, diff, "g-", label="G(r) diff") - pylab.plot(r, diffzero, "k-") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.show() + recipe.plot_recipe( + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) return @@ -206,7 +197,7 @@ def main(): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) return diff --git a/docs/examples/crystalpdf.py b/docs/examples/crystalpdf.py index bb6ad3cf..71319b9c 100644 --- a/docs/examples/crystalpdf.py +++ b/docs/examples/crystalpdf.py @@ -24,7 +24,6 @@ demonstrates only the basic configuration. """ -import numpy from gaussianrecipe import scipyOptimize from diffpy.srfit.fitbase import ( @@ -129,26 +128,18 @@ def makeRecipe(ciffile, datname): return recipe -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. - r = recipe.nickel.profile.x - g = recipe.nickel.profile.y - gcalc = recipe.nickel.profile.ycalc - diffzero = -0.8 * max(g) * numpy.ones_like(g) - diff = g - gcalc + diffzero - - import pylab - - pylab.plot(r, g, "bo", label="G(r) Data") - pylab.plot(r, gcalc, "r-", label="G(r) Fit") - pylab.plot(r, diff, "g-", label="G(r) diff") - pylab.plot(r, diffzero, "k-") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.show() + recipe.plot_recipe( + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) return @@ -170,6 +161,6 @@ def plotResults(recipe): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/crystalpdfall.py b/docs/examples/crystalpdfall.py index dc2da591..87250053 100644 --- a/docs/examples/crystalpdfall.py +++ b/docs/examples/crystalpdfall.py @@ -18,7 +18,6 @@ structure to all the available data. """ -import numpy from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -143,70 +142,22 @@ def makeRecipe( return recipe -def plotResults(recipe): - """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. - xnickel = recipe.xnickel - xr_ni = xnickel.profile.x - xg_ni = xnickel.profile.y - xgcalc_ni = xnickel.profile.ycalc - xdiffzero_ni = -0.8 * max(xg_ni) * numpy.ones_like(xg_ni) - xdiff_ni = xg_ni - xgcalc_ni + xdiffzero_ni - - xsilicon = recipe.xsilicon - xr_si = xsilicon.profile.x - xg_si = xsilicon.profile.y - xgcalc_si = xsilicon.profile.ycalc - xdiffzero_si = -0.8 * max(xg_si) * numpy.ones_like(xg_si) - xdiff_si = xg_si - xgcalc_si + xdiffzero_si - - nnickel = recipe.nnickel - nr_ni = nnickel.profile.x - ng_ni = nnickel.profile.y - ngcalc_ni = nnickel.profile.ycalc - ndiffzero_ni = -0.8 * max(ng_ni) * numpy.ones_like(ng_ni) - ndiff_ni = ng_ni - ngcalc_ni + ndiffzero_ni - - xsini = recipe.xsini - xr_sini = xsini.profile.x - xg_sini = xsini.profile.y - xgcalc_sini = xsini.profile.ycalc - xdiffzero_sini = -0.8 * max(xg_sini) * numpy.ones_like(xg_sini) - xdiff_sini = xg_sini - xgcalc_sini + xdiffzero_sini - - import pylab - - pylab.subplot(2, 2, 1) - pylab.plot(xr_ni, xg_ni, "bo", label="G(r) x-ray nickel Data") - pylab.plot(xr_ni, xgcalc_ni, "r-", label="G(r) x-ray nickel Fit") - pylab.plot(xr_ni, xdiff_ni, "g-", label="G(r) x-ray nickel diff") - pylab.plot(xr_ni, xdiffzero_ni, "k-") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.subplot(2, 2, 2) - pylab.plot(xr_si, xg_si, "bo", label="G(r) x-ray silicon Data") - pylab.plot(xr_si, xgcalc_si, "r-", label="G(r) x-ray silicon Fit") - pylab.plot(xr_si, xdiff_si, "g-", label="G(r) x-ray silicon diff") - pylab.plot(xr_si, xdiffzero_si, "k-") - pylab.legend(loc=1) - - pylab.subplot(2, 2, 3) - pylab.plot(nr_ni, ng_ni, "bo", label="G(r) neutron nickel Data") - pylab.plot(nr_ni, ngcalc_ni, "r-", label="G(r) neutron nickel Fit") - pylab.plot(nr_ni, ndiff_ni, "g-", label="G(r) neutron nickel diff") - pylab.plot(nr_ni, ndiffzero_ni, "k-") - pylab.legend(loc=1) - - pylab.subplot(2, 2, 4) - pylab.plot(xr_sini, xg_sini, "bo", label="G(r) x-ray sini Data") - pylab.plot(xr_sini, xgcalc_sini, "r-", label="G(r) x-ray sini Fit") - pylab.plot(xr_sini, xdiff_sini, "g-", label="G(r) x-ray sini diff") - pylab.plot(xr_sini, xdiffzero_sini, "k-") - pylab.legend(loc=1) - - pylab.show() +def plot_results(recipe): + """Plot the results contained within a refined FitRecipe. + + The recipe has four contributions ("xnickel", "xsilicon", "nnickel", + "xsini"), so plot_recipe produces one figure per contribution. + """ + recipe.plot_recipe( + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) return @@ -233,6 +184,6 @@ def plotResults(recipe): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/crystalpdfobjcryst.py b/docs/examples/crystalpdfobjcryst.py index 0d427d04..27a1bef0 100644 --- a/docs/examples/crystalpdfobjcryst.py +++ b/docs/examples/crystalpdfobjcryst.py @@ -19,7 +19,7 @@ provided by the ObjCrystCrystalParSet structure adapter. """ -from crystalpdf import plotResults +from crystalpdf import plot_results from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -123,6 +123,6 @@ def makeRecipe(ciffile, datname): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/crystalpdftwodata.py b/docs/examples/crystalpdftwodata.py index 8c9eafe6..3bb9103b 100644 --- a/docs/examples/crystalpdftwodata.py +++ b/docs/examples/crystalpdftwodata.py @@ -20,7 +20,6 @@ underlying ObjCrystCrystalParSet. """ -import numpy from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -134,40 +133,22 @@ def makeRecipe(ciffile, xdatname, ndatname): return recipe -def plotResults(recipe): - """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. - xr = recipe.xnickel.profile.x - xg = recipe.xnickel.profile.y - xgcalc = recipe.xnickel.profile.ycalc - xdiffzero = -0.8 * max(xg) * numpy.ones_like(xg) - xdiff = xg - xgcalc + xdiffzero - - nr = recipe.nnickel.profile.x - ng = recipe.nnickel.profile.y - ngcalc = recipe.nnickel.profile.ycalc - ndiffzero = -0.8 * max(ng) * numpy.ones_like(ng) - ndiff = ng - ngcalc + ndiffzero - - import pylab - - pylab.subplot(2, 1, 1) - pylab.plot(xr, xg, "bo", label="G(r) x-ray Data") - pylab.plot(xr, xgcalc, "r-", label="G(r) x-ray Fit") - pylab.plot(xr, xdiff, "g-", label="G(r) x-ray diff") - pylab.plot(xr, xdiffzero, "k-") - pylab.legend(loc=1) - - pylab.subplot(2, 1, 2) - pylab.plot(nr, ng, "bo", label="G(r) neutron Data") - pylab.plot(nr, ngcalc, "r-", label="G(r) neutron Fit") - pylab.plot(nr, ndiff, "g-", label="G(r) neutron diff") - pylab.plot(nr, ndiffzero, "k-") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.show() +def plot_results(recipe): + """Plot the results contained within a refined FitRecipe. + + The recipe has two contributions ("xnickel" for x-ray, "nnickel" for + neutron), so plot_recipe produces one figure per contribution. + """ + recipe.plot_recipe( + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) return @@ -189,6 +170,6 @@ def plotResults(recipe): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index 9407d028..184114f6 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -20,7 +20,6 @@ nickel and silicon to find the structures and phase fractions. """ -import numpy from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -151,26 +150,18 @@ def makeRecipe(niciffile, siciffile, datname): return recipe -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. - r = recipe.nisi.profile.x - g = recipe.nisi.profile.y - gcalc = recipe.nisi.profile.ycalc - diffzero = -0.8 * max(g) * numpy.ones_like(g) - diff = g - gcalc + diffzero - - import pylab - - pylab.plot(r, g, "bo", label="G(r) Data") - pylab.plot(r, gcalc, "r-", label="G(r) Fit") - pylab.plot(r, diff, "g-", label="G(r) diff") - pylab.plot(r, diffzero, "k-") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.show() + recipe.plot_recipe( + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) return @@ -192,6 +183,6 @@ def plotResults(recipe): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/debyemodel.py b/docs/examples/debyemodel.py index 549b9b09..1529d848 100644 --- a/docs/examples/debyemodel.py +++ b/docs/examples/debyemodel.py @@ -157,25 +157,19 @@ def makeRecipe(): return recipe -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # Plot this. # Note that since the contribution was given the name "pb", it is # accessible from the recipe with this name. This is a useful way to # organize multiple contributions to a fit. - T = recipe.pb.profile.x - U = recipe.pb.profile.y - Ucalc = recipe.pb.profile.ycalc - - import pylab - - pylab.plot(T, U, "o", label="Pb $U_{iso}$ Data") - pylab.plot(T, Ucalc) - pylab.xlabel("T (K)") - pylab.ylabel(r"$U_{iso} (\AA^2)$") - pylab.legend(loc=(0.0, 0.8)) - - pylab.show() + recipe.plot_recipe( + show_diff=False, + data_label=r"Pb $U_{iso}$ Data", + fit_label="Calculated", + xlabel="T (K)", + ylabel=r"$U_{iso} (\AA^2)$", + legend_loc=(0.0, 0.8), + ) return @@ -194,7 +188,7 @@ def main(): res.print_results() # Plot the results - plotResults(recipe) + plot_results(recipe) return diff --git a/docs/examples/debyemodelII.py b/docs/examples/debyemodelII.py index 400daa6f..c75d9cf7 100644 --- a/docs/examples/debyemodelII.py +++ b/docs/examples/debyemodelII.py @@ -93,43 +93,36 @@ def makeRecipeII(): return recipe -def plotResults(recipe): +def plot_results(recipe): """Display the results contained within a refined FitRecipe.""" # The variable values are returned in the order in which the variables were # added to the FitRecipe. lowToffset, highToffset, thetaD = recipe.get_values() + print( + r"lowT: $T_d$=%3.1f K, offset=%1.5f $\AA^2$" + % (abs(thetaD), lowToffset) + ) + print( + r"highT: $T_d$=%3.1f K, offset=%1.5f $\AA^2$" + % (abs(thetaD), highToffset) + ) # We want to extend the fitting range to its full extent so we can get a - # nice full plot. + # nice full plot. Since the calculated profile is only valid for the + # calculation range that was used during the fit, we need to trigger a + # recalculation over the widened range before plotting. recipe.lowT.profile.set_calculation_range(xmin="obs", xmax="obs") recipe.highT.profile.set_calculation_range(xmin="obs", xmax="obs") - T = recipe.lowT.profile.x - U = recipe.lowT.profile.y - # We can use a FitContribution's 'evaluate_equation' method to evaluate - # expressions involving the Parameters and other aspects of the - # FitContribution. Here we evaluate the fitting equation, which is always - # accessed using the name "eq". We access it this way (rather than through - # the Profile's ycalc attribute) because we changed the calculation range - # above, and we therefore need to recalculate the profile. - lowU = recipe.lowT.evaluate_equation("eq") - highU = recipe.highT.evaluate_equation("eq") - - # Now we can plot this. - import pylab - - pylab.plot(T, U, "o", label="Pb $U_{iso}$ Data") - lbl1 = r"$T_d$=%3.1f K, lowToff=%1.5f $\AA^2$" % (abs(thetaD), lowToffset) - lbl2 = r"$T_d$=%3.1f K, highToff=%1.5f $\AA^2$" % ( - abs(thetaD), - highToffset, + recipe.residual() + + recipe.plot_recipe( + show_diff=False, + data_label=r"Pb $U_{iso}$ Data", + fit_label="Calculated", + xlabel="T (K)", + ylabel=r"$U_{iso} (\AA^2)$", + legend_loc=(0.0, 0.8), ) - pylab.plot(T, lowU, label=lbl1) - pylab.plot(T, highU, label=lbl2) - pylab.xlabel("T (K)") - pylab.ylabel(r"$U_{iso} (\AA^2)$") - pylab.legend(loc=(0.0, 0.8)) - - pylab.show() return @@ -148,7 +141,7 @@ def main(): res.print_results() # Plot the results - plotResults(recipe) + plot_results(recipe) return diff --git a/docs/examples/ellipsoidsas.py b/docs/examples/ellipsoidsas.py index b1fa1aa3..9c4fb283 100644 --- a/docs/examples/ellipsoidsas.py +++ b/docs/examples/ellipsoidsas.py @@ -14,6 +14,7 @@ ######################################################################## """Example of a refinement of SAS I(Q) data to an ellipsoidal model.""" +import matplotlib.pyplot as plt from gaussianrecipe import scipyOptimize from diffpy.srfit.fitbase import ( @@ -86,24 +87,28 @@ def makeRecipe(datname): return recipe -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. - r = recipe.ellipsoid.profile.x - y = recipe.ellipsoid.profile.y - ycalc = recipe.ellipsoid.profile.ycalc - diff = y - ycalc + min(y) - - import pylab - - pylab.loglog(r, y, "bo", label="I(Q) Data") - pylab.loglog(r, ycalc, "r-", label="I(Q) Fit") - pylab.loglog(r, diff, "g-", label="I(Q) diff") - pylab.xlabel(r"$Q (\AA^{-1})$") - pylab.ylabel("$I (arb. units)$") - pylab.legend(loc=1) - - pylab.show() + # I(Q) SAS data is best viewed on a log-log scale, so we prepare the + # axes ourselves and hand them to plot_recipe. The residual difference + # curve is not shown since it can go negative and is not meaningful on + # a log scale. + fig = plt.figure() + ax = fig.add_subplot(111) + ax.set_xscale("log") + ax.set_yscale("log") + recipe.plot_recipe( + ax=ax, + show=False, + show_diff=False, + data_color="b", + fit_color="r", + data_label="I(Q) Data", + fit_label="I(Q) Fit", + xlabel=r"$Q (\AA^{-1})$", + ylabel="$I (arb. units)$", + ) + plt.show() return @@ -123,6 +128,6 @@ def plotResults(recipe): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/gaussianrecipe.py b/docs/examples/gaussianrecipe.py index 93e3ff60..bd73a65b 100644 --- a/docs/examples/gaussianrecipe.py +++ b/docs/examples/gaussianrecipe.py @@ -26,8 +26,8 @@ to get an understanding of how a fit recipe can be used once created. After that, read the 'makeRecipe' code to see what goes into a fit recipe. After that, read the 'scipyOptimize' code to see how the refinement is executed. -Finally, read the 'plotResults' code to see how to extracts the refined profile -and plot it. +Finally, read the 'plot_results' code to see how to extracts the +refined profile and plot it. Extensions @@ -75,7 +75,7 @@ def main(): res.print_results() # Plot the results. - plotResults(recipe) + plot_results(recipe) return @@ -178,28 +178,21 @@ def scipyOptimize(recipe): return -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" # We can access the data and fit profile through the Profile we created # above. We get to it through our FitContribution, which we named "g1". - # - # The independent variable. This is always under the "x" attribute. - x = recipe.g1.profile.x - # The observed profile that we loaded earlier, the "y" attribute. - y = recipe.g1.profile.y - # The calculated profile, the "ycalc" attribute. - ycalc = recipe.g1.profile.ycalc - - # This stuff is specific to pylab from the matplotlib distribution. - import pylab - - pylab.plot(x, y, "b.", label="observed Gaussian") - pylab.plot(x, ycalc, "g-", label="calculated Gaussian") - pylab.legend(loc=(0.0, 0.8)) - pylab.xlabel("x") - pylab.ylabel("y") - - pylab.show() + recipe.plot_recipe( + show_diff=False, + data_style=".", + data_color="b", + fit_color="g", + data_label="observed Gaussian", + fit_label="calculated Gaussian", + xlabel="x", + ylabel="y", + legend_loc=(0.0, 0.8), + ) return diff --git a/docs/examples/interface.py b/docs/examples/interface.py index 03bb498a..f3f2b416 100644 --- a/docs/examples/interface.py +++ b/docs/examples/interface.py @@ -65,9 +65,9 @@ def main(): # Print the results. res.print_results() # Plot the results. - from gaussianrecipe import plotResults + from gaussianrecipe import plot_results - plotResults(r) + plot_results(r) return diff --git a/docs/examples/npintensity.py b/docs/examples/npintensity.py index 586683bc..3afbf901 100644 --- a/docs/examples/npintensity.py +++ b/docs/examples/npintensity.py @@ -43,6 +43,7 @@ from __future__ import print_function +import matplotlib.pyplot as plt import numpy from gaussianrecipe import scipyOptimize @@ -328,32 +329,34 @@ def main(): res.print_results(footer=footer) # Plot! - plotResults(recipe) + plot_results(recipe) return -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. + # The background is not part of the standard observed/fit/diff plot + # that plot_recipe produces, so we overlay it afterwards. q = recipe.bucky.profile.x - - Imeas = recipe.bucky.profile.y - Icalc = recipe.bucky.profile.ycalc bkgd = recipe.bucky.evaluate_equation("bkgd") - diff = Imeas - Icalc - - import pylab - - pylab.plot(q, Imeas, "ob", label="I(Q) Data") - pylab.plot(q, Icalc, "r-", label="I(Q) Fit") - pylab.plot(q, diff, "g-", label="I(Q) diff") - pylab.plot(q, bkgd, "c-", label="Bkgd. Fit") - pylab.xlabel(r"$Q (\AA^{-1})$") - pylab.ylabel("Intensity (arb. units)") - pylab.legend(loc=1) - pylab.show() + fig, ax = recipe.plot_recipe( + show=False, + return_fig=True, + data_color="b", + fit_color="r", + diff_color="g", + data_label="I(Q) Data", + fit_label="I(Q) Fit", + diff_label="I(Q) diff", + xlabel=r"$Q (\AA^{-1})$", + ylabel="Intensity (arb. units)", + ) + ax.plot(q, bkgd, "c-", label="Bkgd. Fit") + ax.legend(loc=1) + + plt.show() return diff --git a/docs/examples/npintensityII.py b/docs/examples/npintensityII.py index ff0d6bcd..abfc2e16 100644 --- a/docs/examples/npintensityII.py +++ b/docs/examples/npintensityII.py @@ -34,6 +34,7 @@ first step towards writing a user interface. """ +import matplotlib.pyplot as plt import numpy from gaussianrecipe import scipyOptimize from npintensity import IntensityGenerator, makeData @@ -186,45 +187,37 @@ def gaussian(q, q0, width): return recipe -def plotResults(recipe): - """Plot the results contained within a refined FitRecipe.""" - # plotting song and dance - q = recipe.bucky1.profile.x +def plot_results(recipe): + """Plot the results contained within a refined FitRecipe. - # Plot this for fun. - I1 = recipe.bucky1.profile.y - Icalc1 = recipe.bucky1.profile.ycalc + The recipe has two contributions ("bucky1" and "bucky2"), so + plot_recipe produces one figure per contribution. The backgrounds + are not part of the standard observed/fit/diff plot, so they are + overlaid on each figure afterwards. + """ + q = recipe.bucky1.profile.x bkgd1 = recipe.bucky1.evaluate_equation("bkgd") - diff1 = I1 - Icalc1 - I2 = recipe.bucky2.profile.y - Icalc2 = recipe.bucky2.profile.ycalc bkgd2 = recipe.bucky2.evaluate_equation("bkgd") - diff2 = I2 - Icalc2 - offset = 1.2 * max(I2) * numpy.ones_like(I2) - I1 += offset - Icalc1 += offset - bkgd1 += offset - diff1 += offset - - import pylab - - pylab.subplot(2, 1, 1) - pylab.plot(q, I1, "bo", label="I1(Q) Data") - pylab.plot(q, Icalc1, "r-", label="I1(Q) Fit") - pylab.plot(q, diff1, "g-", label="I1(Q) diff") - pylab.plot(q, bkgd1, "c-", label="Bkgd1 Fit") - pylab.legend(loc=1) - - pylab.subplot(2, 1, 2) - pylab.plot(q, I2, "bo", label="I2(Q) Data") - pylab.plot(q, Icalc2, "r-", label="I2(Q) Fit") - pylab.plot(q, diff2, "g-", label="I2(Q) diff") - pylab.plot(q, bkgd2, "c-", label="Bkgd2 Fit") - pylab.xlabel(r"$Q (\AA^{-1})$") - pylab.ylabel("Intensity (arb. units)") - pylab.legend(loc=1) - - pylab.show() + + figs, axes = recipe.plot_recipe( + show=False, + return_fig=True, + data_color="b", + fit_color="r", + diff_color="g", + data_label="I(Q) Data", + fit_label="I(Q) Fit", + diff_label="I(Q) diff", + xlabel=r"$Q (\AA^{-1})$", + ylabel="Intensity (arb. units)", + ) + # "bucky1" was added to the recipe first, so its axes come first. + axes[0].plot(q, bkgd1, "c-", label="Bkgd1 Fit") + axes[0].legend(loc=1) + axes[1].plot(q, bkgd2, "c-", label="Bkgd2 Fit") + axes[1].legend(loc=1) + + plt.show() return @@ -266,7 +259,7 @@ def main(): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) return diff --git a/docs/examples/nppdfcrystal.py b/docs/examples/nppdfcrystal.py index 033a7162..d932f2a8 100644 --- a/docs/examples/nppdfcrystal.py +++ b/docs/examples/nppdfcrystal.py @@ -23,7 +23,7 @@ diffpy.srfit.pdf.characteristicfunctions module. """ -import numpy +import matplotlib.pyplot as plt from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -80,34 +80,36 @@ def makeRecipe(ciffile, grdata): return recipe -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. r = recipe.pdf.profile.x g = recipe.pdf.profile.y - gcalc = recipe.pdf.profile.ycalc - diffzero = -0.8 * max(g) * numpy.ones_like(g) - diff = g - gcalc + diffzero + # These two curves are not part of the standard observed/fit/diff plot + # that plot_recipe produces, so we overlay them afterwards. gcryst = recipe.pdf.evaluate_equation("G") gcryst /= recipe.scale.value fr = recipe.pdf.evaluate_equation("f") fr *= max(g) / fr[0] - import pylab - - pylab.plot(r, g, "bo", label="G(r) Data") - pylab.plot(r, gcryst, "y--", label="G(r) Crystal") - pylab.plot(r, fr, "k--", label="f(r) calculated (scaled)") - pylab.plot(r, gcalc, "r-", label="G(r) Fit") - pylab.plot(r, diff, "g-", label="G(r) diff") - pylab.plot(r, diffzero, "k-") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.show() + fig, ax = recipe.plot_recipe( + show=False, + return_fig=True, + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) + ax.plot(r, gcryst, "y--", label="G(r) Crystal") + ax.plot(r, fr, "k--", label="f(r) calculated (scaled)") + ax.legend(loc=1) + + plt.show() return @@ -122,6 +124,6 @@ def plotResults(recipe): res = FitResults(recipe) res.print_results() - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/nppdfobjcryst.py b/docs/examples/nppdfobjcryst.py index 6c702172..dfac5855 100644 --- a/docs/examples/nppdfobjcryst.py +++ b/docs/examples/nppdfobjcryst.py @@ -18,8 +18,6 @@ the DebyePDFGenerator from SrReal to refine a pyobjcryst Molecule. """ -import numpy - from diffpy.srfit.fitbase import ( FitContribution, FitRecipe, @@ -107,26 +105,18 @@ def makeRecipe(molecule, datname): return recipe -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" - # Plot this. - r = recipe.bucky.profile.x - g = recipe.bucky.profile.y - gcalc = recipe.bucky.profile.ycalc - diffzero = -0.8 * max(g) * numpy.ones_like(g) - diff = g - gcalc + diffzero - - import pylab - - pylab.plot(r, g, "ob", label="G(r) Data") - pylab.plot(r, gcalc, "-r", label="G(r) Fit") - pylab.plot(r, diff, "-g", label="G(r) diff") - pylab.plot(r, diffzero, "-k") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.show() + recipe.plot_recipe( + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) return @@ -148,7 +138,7 @@ def main(): res.print_results() # Plot results - plotResults(recipe) + plot_results(recipe) return diff --git a/docs/examples/nppdfsas.py b/docs/examples/nppdfsas.py index 863fea7a..07c8ca47 100644 --- a/docs/examples/nppdfsas.py +++ b/docs/examples/nppdfsas.py @@ -22,7 +22,7 @@ of the nanoparticle that agrees best with both the PDF and SAS data. """ -import numpy +import matplotlib.pyplot as plt from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -144,14 +144,16 @@ def fitRecipe(recipe): return -def plotResults(recipe): - """Plot the results contained within a refined FitRecipe.""" - # All this should be pretty familiar by now. +def plot_results(recipe): + """Plot the results contained within a refined FitRecipe. + + The recipe has two contributions ("pdf" and "sas"), so plot_recipe + produces one figure per contribution. The G(r) crystal and shape + curves are not part of the standard observed/fit/diff plot, so they + are overlaid on the "pdf" figure afterwards. + """ r = recipe.pdf.profile.x g = recipe.pdf.profile.y - gcalc = recipe.pdf.profile.ycalc - diffzero = -0.8 * max(g) * numpy.ones_like(g) - diff = g - gcalc + diffzero gcryst = recipe.pdf.evaluate_equation("G") gcryst /= recipe.scale.value @@ -159,19 +161,25 @@ def plotResults(recipe): fr = recipe.pdf.evaluate_equation("f") fr *= max(g) / fr[0] - import pylab - - pylab.plot(r, g, "bo", label="G(r) Data") - pylab.plot(r, gcryst, "y--", label="G(r) Crystal") - pylab.plot(r, fr, "k--", label="f(r) calculated (scaled)") - pylab.plot(r, gcalc, "r-", label="G(r) Fit") - pylab.plot(r, diff, "g-", label="G(r) diff") - pylab.plot(r, diffzero, "k-") - pylab.xlabel(r"$r (\AA)$") - pylab.ylabel(r"$G (\AA^{-2})$") - pylab.legend(loc=1) - - pylab.show() + figs, axes = recipe.plot_recipe( + show=False, + return_fig=True, + data_color="b", + fit_color="r", + diff_color="g", + data_label="G(r) Data", + fit_label="G(r) Fit", + diff_label="G(r) diff", + xlabel=r"$r (\AA)$", + ylabel=r"$G (\AA^{-2})$", + ) + # "pdf" was added to the recipe first, so its axes come first. + ax = axes[0] + ax.plot(r, gcryst, "y--", label="G(r) Crystal") + ax.plot(r, fr, "k--", label="f(r) calculated (scaled)") + ax.legend(loc=1) + + plt.show() return @@ -188,6 +196,6 @@ def plotResults(recipe): res = FitResults(recipe) res.print_results() - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/simplepdf.py b/docs/examples/simplepdf.py index 0611dc08..5c07c9b3 100644 --- a/docs/examples/simplepdf.py +++ b/docs/examples/simplepdf.py @@ -18,7 +18,7 @@ data. It uses the PDFContribution class to simplify fit setup. """ -from crystalpdf import plotResults +from crystalpdf import plot_results from gaussianrecipe import scipyOptimize from diffpy.srfit.fitbase import FitRecipe, FitResults @@ -85,6 +85,6 @@ def makeRecipe(ciffile, datname): res.save_results("nickel_example.res") # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index 9d7202b9..8283deec 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -14,7 +14,7 @@ ######################################################################## """Example of a simplified PDF refinement of two-phase structure.""" -from crystalpdftwophase import plotResults +from crystalpdftwophase import plot_results from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -128,6 +128,6 @@ def makeRecipe(niciffile, siciffile, datname): res.print_results() # Plot! - plotResults(recipe) + plot_results(recipe) # End of file diff --git a/docs/examples/threedoublepeaks.py b/docs/examples/threedoublepeaks.py index 7802b222..a17b0725 100644 --- a/docs/examples/threedoublepeaks.py +++ b/docs/examples/threedoublepeaks.py @@ -188,29 +188,22 @@ def scipyOptimize(recipe): return -def plotResults(recipe): +def plot_results(recipe): """Plot the results contained within a refined FitRecipe.""" # We can access the data and fit profile through the Profile we created - # above. We get to it through our FitContribution, which we named "g1". - # - # The independent variable. This is always under the "x" attribute. - x = recipe.peaks.profile.x - # The observed profile that we loaded earlier, the "y" attribute. - y = recipe.peaks.profile.y - # The calculated profile, the "ycalc" attribute. - ycalc = recipe.peaks.profile.ycalc - - # This stuff is specific to pylab from the matplotlib distribution. - import pylab - - pylab.plot(x, y, "b.", label="observed profile") - pylab.plot(x, ycalc, "r-", label="calculated profile") - pylab.plot(x, y - ycalc - 0.1 * max(y), "g-", label="difference") - pylab.legend(loc=(0.0, 0.8)) - pylab.xlabel("x") - pylab.ylabel("y") - - pylab.show() + # above. We get to it through our FitContribution, which we named "peaks". + recipe.plot_recipe( + data_style=".", + data_color="b", + fit_color="r", + diff_color="g", + data_label="observed profile", + fit_label="calculated profile", + diff_label="difference", + xlabel="x", + ylabel="y", + legend_loc=(0.0, 0.8), + ) return @@ -253,7 +246,7 @@ def steerFit(recipe): res.print_results() # Plot the results - plotResults(recipe) + plot_results(recipe) # End of file From f44576f0b7b913ac54bd3e78056be18caefd8d46 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 14:27:01 -0400 Subject: [PATCH 06/11] remove plot_results in favor of recipe.plot_recipe() method --- docs/examples/C60.iq | 380 ++++++++++++++++++++++++++++ docs/examples/coreshellnp.py | 27 +- docs/examples/crystalpdf.py | 27 +- docs/examples/crystalpdfall.py | 42 ++- docs/examples/crystalpdfobjcryst.py | 18 +- docs/examples/crystalpdftwodata.py | 35 +-- docs/examples/crystalpdftwophase.py | 27 +- docs/examples/ellipsoidsas.py | 4 +- docs/examples/gaussiangenerator.py | 4 +- docs/examples/gaussianrecipe.py | 30 +-- docs/examples/interface.py | 12 +- docs/examples/npintensity.py | 8 +- docs/examples/npintensityII.py | 7 +- docs/examples/nppdfcrystal.py | 12 +- docs/examples/nppdfobjcryst.py | 23 +- docs/examples/nppdfsas.py | 14 +- docs/examples/simplepdf.py | 17 +- docs/examples/simplepdftwophase.py | 17 +- docs/examples/simplerecipe.py | 4 +- docs/examples/threedoublepeaks.py | 29 +-- 20 files changed, 536 insertions(+), 201 deletions(-) create mode 100644 docs/examples/C60.iq diff --git a/docs/examples/C60.iq b/docs/examples/C60.iq new file mode 100644 index 00000000..5ab145f5 --- /dev/null +++ b/docs/examples/C60.iq @@ -0,0 +1,380 @@ +1.000000000000000000e+00 4.100000000000000000e+01 7.110034071726496485e+00 +1.050000000000000044e+00 6.100000000000000000e+01 8.394511215438845042e+00 +1.100000000000000089e+00 8.900000000000000000e+01 9.614581256271836907e+00 +1.150000000000000133e+00 9.400000000000000000e+01 1.066952084213099461e+01 +1.200000000000000178e+00 1.150000000000000000e+02 1.148570297676609719e+01 +1.250000000000000222e+00 1.520000000000000000e+02 1.201930040395864197e+01 +1.300000000000000266e+00 1.390000000000000000e+02 1.225310840582604577e+01 +1.350000000000000311e+00 1.180000000000000000e+02 1.219109445355365295e+01 +1.400000000000000355e+00 1.670000000000000000e+02 1.185346751873712456e+01 +1.450000000000000400e+00 1.370000000000000000e+02 1.127341865731435888e+01 +1.500000000000000444e+00 1.090000000000000000e+02 1.049553382730363182e+01 +1.550000000000000488e+00 7.500000000000000000e+01 9.575614813179214480e+00 +1.600000000000000533e+00 6.500000000000000000e+01 8.581852178359065775e+00 +1.650000000000000577e+00 6.200000000000000000e+01 7.597231598891897697e+00 +1.700000000000000622e+00 4.600000000000000000e+01 6.721462898823489240e+00 +1.750000000000000666e+00 3.700000000000000000e+01 6.065468561148816384e+00 +1.800000000000000711e+00 3.600000000000000000e+01 5.724586352107412957e+00 +1.850000000000000755e+00 3.400000000000000000e+01 5.729721877353608939e+00 +1.900000000000000799e+00 3.500000000000000000e+01 6.021186349459231479e+00 +1.950000000000000844e+00 4.400000000000000000e+01 6.483815947427138404e+00 +2.000000000000000888e+00 4.400000000000000000e+01 7.002239222540367791e+00 +2.050000000000000711e+00 5.500000000000000000e+01 7.488355864944134410e+00 +2.100000000000000977e+00 5.200000000000000000e+01 7.883677244766420955e+00 +2.150000000000001243e+00 5.800000000000000000e+01 8.153614043016924384e+00 +2.200000000000001066e+00 6.100000000000000000e+01 8.281875115567967072e+00 +2.250000000000000888e+00 6.200000000000000000e+01 8.266644375798138711e+00 +2.300000000000001155e+00 6.000000000000000000e+01 8.118269608547297622e+00 +2.350000000000001421e+00 5.900000000000000000e+01 7.857966080848713730e+00 +2.400000000000001243e+00 5.600000000000000000e+01 7.517110700664150436e+00 +2.450000000000001066e+00 4.400000000000000000e+01 7.136638569231891438e+00 +2.500000000000001332e+00 6.100000000000000000e+01 6.765682108597169453e+00 +2.550000000000001599e+00 4.400000000000000000e+01 6.457931897248158748e+00 +2.600000000000001421e+00 4.900000000000000000e+01 6.263923506264947427e+00 +2.650000000000001243e+00 4.900000000000000000e+01 6.219316976871393621e+00 +2.700000000000001510e+00 5.000000000000000000e+01 6.333975657986292696e+00 +2.750000000000001776e+00 4.400000000000000000e+01 6.589463084173096341e+00 +2.800000000000001599e+00 4.000000000000000000e+01 6.947213664064986638e+00 +2.850000000000001421e+00 6.200000000000000000e+01 7.361260323774981629e+00 +2.900000000000001688e+00 6.800000000000000000e+01 7.788341999336049426e+00 +2.950000000000001954e+00 6.700000000000000000e+01 8.193080954789397907e+00 +3.000000000000001776e+00 6.800000000000000000e+01 8.549520234649873984e+00 +3.050000000000001599e+00 9.000000000000000000e+01 8.840859099287747824e+00 +3.100000000000001865e+00 9.200000000000000000e+01 9.058525627326627472e+00 +3.150000000000002132e+00 8.400000000000000000e+01 9.201068065912670235e+00 +3.200000000000001954e+00 8.900000000000000000e+01 9.272993454368318567e+00 +3.250000000000001776e+00 8.300000000000000000e+01 9.283539275304724114e+00 +3.300000000000002043e+00 8.800000000000000000e+01 9.245326311618564219e+00 +3.350000000000002309e+00 9.700000000000000000e+01 9.172856016833904391e+00 +3.400000000000002132e+00 7.800000000000000000e+01 9.080866437502484345e+00 +3.450000000000001954e+00 7.100000000000000000e+01 8.982640387682405247e+00 +3.500000000000002220e+00 6.800000000000000000e+01 8.888451354172726582e+00 +3.550000000000002487e+00 8.300000000000000000e+01 8.804398845976720622e+00 +3.600000000000002309e+00 7.200000000000000000e+01 8.731877217811153002e+00 +3.650000000000002132e+00 7.400000000000000000e+01 8.667814859318555776e+00 +3.700000000000002398e+00 8.600000000000000000e+01 8.605643849638678233e+00 +3.750000000000002665e+00 8.100000000000000000e+01 8.536791772433714343e+00 +3.800000000000002487e+00 6.900000000000000000e+01 8.452401862569086433e+00 +3.850000000000002309e+00 6.900000000000000000e+01 8.345005535250718864e+00 +3.900000000000002576e+00 5.200000000000000000e+01 8.209955683194356979e+00 +3.950000000000002842e+00 7.200000000000000000e+01 8.046521905852104695e+00 +4.000000000000002665e+00 5.500000000000000000e+01 7.858606567364106787e+00 +4.050000000000002487e+00 5.000000000000000000e+01 7.655045495714588810e+00 +4.100000000000003197e+00 5.800000000000000000e+01 7.449412380847344473e+00 +4.150000000000003020e+00 5.200000000000000000e+01 7.259175061828185171e+00 +4.200000000000002842e+00 6.000000000000000000e+01 7.104013309679550581e+00 +4.250000000000002665e+00 3.200000000000000000e+01 7.003207308137216813e+00 +4.300000000000002487e+00 5.300000000000000000e+01 6.972348801934296958e+00 +4.350000000000003197e+00 4.900000000000000000e+01 7.020153409024082691e+00 +4.400000000000003020e+00 5.300000000000000000e+01 7.146492100893967248e+00 +4.450000000000002842e+00 6.100000000000000000e+01 7.342457215186272812e+00 +4.500000000000003553e+00 6.400000000000000000e+01 7.592380394981023350e+00 +4.550000000000003375e+00 8.300000000000000000e+01 7.876901901638250436e+00 +4.600000000000003197e+00 7.200000000000000000e+01 8.176030947489463685e+00 +4.650000000000003020e+00 8.600000000000000000e+01 8.471537656601991984e+00 +4.700000000000002842e+00 6.500000000000000000e+01 8.748510791443548484e+00 +4.750000000000003553e+00 8.000000000000000000e+01 8.996203868783760882e+00 +4.800000000000003375e+00 7.500000000000000000e+01 9.208363789799006938e+00 +4.850000000000003197e+00 1.110000000000000000e+02 9.383194733358182660e+00 +4.900000000000003908e+00 9.200000000000000000e+01 9.523041962636108693e+00 +4.950000000000003730e+00 9.700000000000000000e+01 9.633825800463693412e+00 +5.000000000000003553e+00 9.400000000000000000e+01 9.724228177807262341e+00 +5.050000000000003375e+00 7.800000000000000000e+01 9.804636369833021448e+00 +5.100000000000003197e+00 1.050000000000000000e+02 9.885881519525440808e+00 +5.150000000000003908e+00 9.300000000000000000e+01 9.977868101282044933e+00 +5.200000000000003730e+00 9.100000000000000000e+01 1.008825679835690892e+01 +5.250000000000003553e+00 1.080000000000000000e+02 1.022140576118761324e+01 +5.300000000000004263e+00 1.200000000000000000e+02 1.037776088970294630e+01 +5.350000000000004086e+00 1.210000000000000000e+02 1.055380342457576148e+01 +5.400000000000003908e+00 1.090000000000000000e+02 1.074253855346414355e+01 +5.450000000000003730e+00 1.390000000000000000e+02 1.093439337469931871e+01 +5.500000000000003553e+00 1.160000000000000000e+02 1.111833081572526183e+01 +5.550000000000004263e+00 1.330000000000000000e+02 1.128298953538036820e+01 +5.600000000000004086e+00 1.190000000000000000e+02 1.141770825687207314e+01 +5.650000000000003908e+00 1.610000000000000000e+02 1.151335497518072160e+01 +5.700000000000004619e+00 1.490000000000000000e+02 1.156293357952795198e+01 +5.750000000000004441e+00 1.450000000000000000e+02 1.156197344178840147e+01 +5.800000000000004263e+00 1.470000000000000000e+02 1.150872284964192005e+01 +5.850000000000004086e+00 1.270000000000000000e+02 1.140417052050952762e+01 +5.900000000000003908e+00 1.280000000000000000e+02 1.125191654884030257e+01 +5.950000000000004619e+00 1.200000000000000000e+02 1.105790918681919521e+01 +6.000000000000004441e+00 1.090000000000000000e+02 1.083005955981421309e+01 +6.050000000000004263e+00 1.200000000000000000e+02 1.057774473732988341e+01 +6.100000000000004974e+00 9.900000000000000000e+01 1.031121229885946278e+01 +6.150000000000004796e+00 1.080000000000000000e+02 1.004090829663322637e+01 +6.200000000000004619e+00 8.600000000000000000e+01 9.776766079110393193e+00 +6.250000000000004441e+00 8.900000000000000000e+01 9.527514108236475820e+00 +6.300000000000004263e+00 8.300000000000000000e+01 9.300080872727065184e+00 +6.350000000000004974e+00 8.300000000000000000e+01 9.099184249267430857e+00 +6.400000000000004796e+00 8.000000000000000000e+01 8.927180343679047780e+00 +6.450000000000004619e+00 6.400000000000000000e+01 8.784208031611530743e+00 +6.500000000000005329e+00 7.200000000000000000e+01 8.668607219973745615e+00 +6.550000000000005151e+00 6.500000000000000000e+01 8.577529867563132626e+00 +6.600000000000004974e+00 7.100000000000000000e+01 8.507624103272689808e+00 +6.650000000000004796e+00 7.100000000000000000e+01 8.455665328034214667e+00 +6.700000000000004619e+00 8.400000000000000000e+01 8.419031656868744662e+00 +6.750000000000005329e+00 7.600000000000000000e+01 8.395962095144447801e+00 +6.800000000000005151e+00 7.100000000000000000e+01 8.385580743558554317e+00 +6.850000000000004974e+00 7.700000000000000000e+01 8.387710091873067597e+00 +6.900000000000005684e+00 7.000000000000000000e+01 8.402527436468801625e+00 +6.950000000000005507e+00 6.600000000000000000e+01 8.430139507416994249e+00 +7.000000000000005329e+00 7.800000000000000000e+01 8.470160140601601384e+00 +7.050000000000005151e+00 7.000000000000000000e+01 8.521372205036955805e+00 +7.100000000000004974e+00 6.600000000000000000e+01 8.581537097690857152e+00 +7.150000000000005684e+00 8.000000000000000000e+01 8.647385606863707608e+00 +7.200000000000005507e+00 9.100000000000000000e+01 8.714789602889016606e+00 +7.250000000000005329e+00 7.200000000000000000e+01 8.779083526869889909e+00 +7.300000000000006040e+00 7.900000000000000000e+01 8.835484886425980733e+00 +7.350000000000005862e+00 8.900000000000000000e+01 8.879556286081761840e+00 +7.400000000000005684e+00 6.900000000000000000e+01 8.907655642122886519e+00 +7.450000000000005507e+00 6.900000000000000000e+01 8.917331288011601131e+00 +7.500000000000005329e+00 7.600000000000000000e+01 8.907629739617604514e+00 +7.550000000000006040e+00 7.200000000000000000e+01 8.879292657499760821e+00 +7.600000000000005862e+00 8.600000000000000000e+01 8.834824869103542255e+00 +7.650000000000005684e+00 8.500000000000000000e+01 8.778418030801720562e+00 +7.700000000000006395e+00 6.500000000000000000e+01 8.715717081035128544e+00 +7.750000000000006217e+00 7.900000000000000000e+01 8.653422760898482835e+00 +7.800000000000006040e+00 6.000000000000000000e+01 8.598737299006906198e+00 +7.850000000000005862e+00 8.100000000000000000e+01 8.558684648900005243e+00 +7.900000000000005684e+00 6.800000000000000000e+01 8.539369864632448071e+00 +7.950000000000006395e+00 8.000000000000000000e+01 8.545275608545845003e+00 +8.000000000000007105e+00 8.000000000000000000e+01 8.578712136687379086e+00 +8.050000000000006040e+00 7.200000000000000000e+01 8.639525240410916851e+00 +8.100000000000006750e+00 6.700000000000000000e+01 8.725120279835021364e+00 +8.150000000000005684e+00 7.500000000000000000e+01 8.830792934611320533e+00 +8.200000000000006395e+00 8.600000000000000000e+01 8.950294179439390874e+00 +8.250000000000007105e+00 9.300000000000000000e+01 9.076521062334110823e+00 +8.300000000000006040e+00 7.200000000000000000e+01 9.202223480522381038e+00 +8.350000000000006750e+00 8.500000000000000000e+01 9.320641432123547787e+00 +8.400000000000005684e+00 9.000000000000000000e+01 9.426021121578955331e+00 +8.450000000000006395e+00 8.900000000000000000e+01 9.513988450109815531e+00 +8.500000000000007105e+00 8.000000000000000000e+01 9.581778548664207307e+00 +8.550000000000007816e+00 1.030000000000000000e+02 9.628329978361691133e+00 +8.600000000000006750e+00 8.600000000000000000e+01 9.654255057679581142e+00 +8.650000000000005684e+00 1.000000000000000000e+02 9.661697181893927677e+00 +8.700000000000006395e+00 8.200000000000000000e+01 9.654085151079360827e+00 +8.750000000000007105e+00 8.800000000000000000e+01 9.635795730852684926e+00 +8.800000000000007816e+00 1.000000000000000000e+02 9.611740328487787366e+00 +8.850000000000006750e+00 9.600000000000000000e+01 9.586900055198276149e+00 +8.900000000000007461e+00 8.000000000000000000e+01 9.565844345425249529e+00 +8.950000000000006395e+00 8.700000000000000000e+01 9.552278741984800092e+00 +9.000000000000007105e+00 9.300000000000000000e+01 9.548673107122208847e+00 +9.050000000000007816e+00 7.700000000000000000e+01 9.556018159368159459e+00 +9.100000000000006750e+00 8.700000000000000000e+01 9.573743921776756594e+00 +9.150000000000007461e+00 9.800000000000000000e+01 9.599810448230144289e+00 +9.200000000000006395e+00 9.100000000000000000e+01 9.630954948462534304e+00 +9.250000000000007105e+00 9.800000000000000000e+01 9.663057475129420482e+00 +9.300000000000007816e+00 1.050000000000000000e+02 9.691575119828224061e+00 +9.350000000000006750e+00 9.400000000000000000e+01 9.711993623533592412e+00 +9.400000000000007461e+00 8.200000000000000000e+01 9.720252989863215731e+00 +9.450000000000008171e+00 8.800000000000000000e+01 9.713115648560902926e+00 +9.500000000000007105e+00 9.500000000000000000e+01 9.688457682829707096e+00 +9.550000000000007816e+00 9.600000000000000000e+01 9.645472849804859194e+00 +9.600000000000008527e+00 9.600000000000000000e+01 9.584784599637655944e+00 +9.650000000000007461e+00 9.100000000000000000e+01 9.508463420899012419e+00 +9.700000000000008171e+00 1.010000000000000000e+02 9.419946847482300711e+00 +9.750000000000007105e+00 7.700000000000000000e+01 9.323859095257963858e+00 +9.800000000000007816e+00 8.300000000000000000e+01 9.225728614844570075e+00 +9.850000000000008527e+00 9.100000000000000000e+01 9.131607004916789450e+00 +9.900000000000007461e+00 7.700000000000000000e+01 9.047603415953551220e+00 +9.950000000000008171e+00 7.800000000000000000e+01 8.979364928521878397e+00 +1.000000000000000711e+01 8.700000000000000000e+01 8.931552680443118675e+00 +1.005000000000000782e+01 8.000000000000000000e+01 8.907379524368012724e+00 +1.010000000000000853e+01 6.500000000000000000e+01 8.908279460570181385e+00 +1.015000000000000746e+01 8.700000000000000000e+01 8.933765675745906520e+00 +1.020000000000000817e+01 5.600000000000000000e+01 8.981503000963613204e+00 +1.025000000000000888e+01 7.400000000000000000e+01 9.047580596390391250e+00 +1.030000000000000782e+01 1.000000000000000000e+02 9.126935048637218273e+00 +1.035000000000000853e+01 7.700000000000000000e+01 9.213853947580945558e+00 +1.040000000000000924e+01 9.600000000000000000e+01 9.302488879269672495e+00 +1.045000000000000817e+01 8.400000000000000000e+01 9.387320271775937641e+00 +1.050000000000000888e+01 8.000000000000000000e+01 9.463536501592827221e+00 +1.055000000000000782e+01 8.800000000000000000e+01 9.527308789536903078e+00 +1.060000000000000853e+01 1.080000000000000000e+02 9.575957677129201429e+00 +1.065000000000000924e+01 8.300000000000000000e+01 9.608015565181519335e+00 +1.070000000000000817e+01 7.900000000000000000e+01 9.623194216759152653e+00 +1.075000000000000888e+01 7.600000000000000000e+01 9.622268245622437988e+00 +1.080000000000000782e+01 8.500000000000000000e+01 9.606887201257027442e+00 +1.085000000000000853e+01 9.000000000000000000e+01 9.579331104248058892e+00 +1.090000000000000924e+01 1.060000000000000000e+02 9.542227638819575475e+00 +1.095000000000000817e+01 8.500000000000000000e+01 9.498253378491876120e+00 +1.100000000000000888e+01 9.400000000000000000e+01 9.449845407854379431e+00 +1.105000000000000959e+01 7.500000000000000000e+01 9.398952003246249021e+00 +1.110000000000000853e+01 1.010000000000000000e+02 9.346850057037913828e+00 +1.115000000000000924e+01 9.900000000000000000e+01 9.294051608561360922e+00 +1.120000000000000995e+01 8.700000000000000000e+01 9.240312224591288981e+00 +1.125000000000000888e+01 6.600000000000000000e+01 9.184741447173394135e+00 +1.130000000000000959e+01 6.900000000000000000e+01 9.126002502838224117e+00 +1.135000000000000853e+01 8.300000000000000000e+01 9.062577533224876802e+00 +1.140000000000000924e+01 7.700000000000000000e+01 8.993067601007187051e+00 +1.145000000000000995e+01 7.400000000000000000e+01 8.916494171353958720e+00 +1.150000000000000888e+01 8.500000000000000000e+01 8.832569929412615650e+00 +1.155000000000000959e+01 7.600000000000000000e+01 8.741910228941105032e+00 +1.160000000000000853e+01 8.000000000000000000e+01 8.646160792090730851e+00 +1.165000000000000924e+01 8.000000000000000000e+01 8.548021811113509116e+00 +1.170000000000000995e+01 7.200000000000000000e+01 8.451153715058385529e+00 +1.175000000000000888e+01 7.000000000000000000e+01 8.359956988057456684e+00 +1.180000000000000959e+01 7.900000000000000000e+01 8.279229566668032447e+00 +1.185000000000001030e+01 4.900000000000000000e+01 8.213721992110649239e+00 +1.190000000000000924e+01 7.400000000000000000e+01 8.167631967157074513e+00 +1.195000000000000995e+01 5.200000000000000000e+01 8.144101858626340729e+00 +1.200000000000001066e+01 8.100000000000000000e+01 8.144796853164912420e+00 +1.205000000000000959e+01 7.500000000000000000e+01 8.169638978489189185e+00 +1.210000000000001030e+01 6.800000000000000000e+01 8.216748706501141086e+00 +1.215000000000000924e+01 6.200000000000000000e+01 8.282606051894481070e+00 +1.220000000000000995e+01 7.900000000000000000e+01 8.362399934800148316e+00 +1.225000000000001066e+01 6.200000000000000000e+01 8.450502742672531653e+00 +1.230000000000000959e+01 6.800000000000000000e+01 8.540995032240447316e+00 +1.235000000000001030e+01 8.700000000000000000e+01 8.628172253171534578e+00 +1.240000000000000924e+01 6.600000000000000000e+01 8.706983631617474018e+00 +1.245000000000000995e+01 7.000000000000000000e+01 8.773374033277576700e+00 +1.250000000000001066e+01 6.300000000000000000e+01 8.824516633148817846e+00 +1.255000000000000959e+01 7.400000000000000000e+01 8.858935332352718461e+00 +1.260000000000001030e+01 8.100000000000000000e+01 8.876521735591397899e+00 +1.265000000000001101e+01 7.900000000000000000e+01 8.878454121635543927e+00 +1.270000000000000995e+01 7.000000000000000000e+01 8.867027373514122957e+00 +1.275000000000001066e+01 8.900000000000000000e+01 8.845405047470958237e+00 +1.280000000000001137e+01 9.300000000000000000e+01 8.817308742966060819e+00 +1.285000000000001030e+01 8.900000000000000000e+01 8.786665927272407473e+00 +1.290000000000001101e+01 7.300000000000000000e+01 8.757244558044158467e+00 +1.295000000000000995e+01 6.800000000000000000e+01 8.732309310579621453e+00 +1.300000000000001066e+01 7.100000000000000000e+01 8.714337229203110269e+00 +1.305000000000001137e+01 7.500000000000000000e+01 8.704827545980174719e+00 +1.310000000000001030e+01 8.400000000000000000e+01 8.704229973402965115e+00 +1.315000000000001101e+01 7.700000000000000000e+01 8.711999121178282124e+00 +1.320000000000000995e+01 7.000000000000000000e+01 8.726763399736139348e+00 +1.325000000000001066e+01 9.000000000000000000e+01 8.746579637700319765e+00 +1.330000000000001137e+01 7.800000000000000000e+01 8.769233684973752219e+00 +1.335000000000001030e+01 7.900000000000000000e+01 8.792544252806974825e+00 +1.340000000000001101e+01 7.900000000000000000e+01 8.814631370278997125e+00 +1.345000000000001172e+01 8.700000000000000000e+01 8.834119694396816769e+00 +1.350000000000001066e+01 9.200000000000000000e+01 8.850257795045594733e+00 +1.355000000000001137e+01 8.200000000000000000e+01 8.862945462663342155e+00 +1.360000000000001208e+01 7.400000000000000000e+01 8.872671109107121978e+00 +1.365000000000001101e+01 9.000000000000000000e+01 8.880370275169243044e+00 +1.370000000000001172e+01 7.300000000000000000e+01 8.887224180763482195e+00 +1.375000000000001066e+01 7.400000000000000000e+01 8.894423947880628845e+00 +1.380000000000001137e+01 9.900000000000000000e+01 8.902930864370043551e+00 +1.385000000000001208e+01 7.300000000000000000e+01 8.913264702447145638e+00 +1.390000000000001101e+01 5.500000000000000000e+01 8.925349577665157241e+00 +1.395000000000001172e+01 7.300000000000000000e+01 8.938439737497418491e+00 +1.400000000000001066e+01 8.600000000000000000e+01 8.951136775080847485e+00 +1.405000000000001137e+01 7.800000000000000000e+01 8.961497014404971040e+00 +1.410000000000001208e+01 7.100000000000000000e+01 8.967215757788139285e+00 +1.415000000000001101e+01 6.700000000000000000e+01 8.965866001120533113e+00 +1.420000000000001172e+01 8.700000000000000000e+01 8.955164358982925066e+00 +1.425000000000001243e+01 7.400000000000000000e+01 8.933236265672745091e+00 +1.430000000000001137e+01 9.000000000000000000e+01 8.898854986156370828e+00 +1.435000000000001208e+01 8.100000000000000000e+01 8.851633119159556529e+00 +1.440000000000001279e+01 7.800000000000000000e+01 8.792149805850542066e+00 +1.445000000000001172e+01 8.600000000000000000e+01 8.722001027115990013e+00 +1.450000000000001243e+01 6.800000000000000000e+01 8.643764131952053731e+00 +1.455000000000001137e+01 7.100000000000000000e+01 8.560871690138158741e+00 +1.460000000000001208e+01 6.900000000000000000e+01 8.477394956421136385e+00 +1.465000000000001279e+01 5.900000000000000000e+01 8.397744799564367923e+00 +1.470000000000001172e+01 7.200000000000000000e+01 8.326308489039982774e+00 +1.475000000000001243e+01 5.900000000000000000e+01 8.267053539449024768e+00 +1.480000000000001137e+01 7.200000000000000000e+01 8.223142248411562605e+00 +1.485000000000001208e+01 6.500000000000000000e+01 8.196608198079069041e+00 +1.490000000000001279e+01 7.100000000000000000e+01 8.188144068958164823e+00 +1.495000000000001172e+01 8.600000000000000000e+01 8.197036118232988500e+00 +1.500000000000001243e+01 7.300000000000000000e+01 8.221256566568779789e+00 +1.505000000000001315e+01 5.300000000000000000e+01 8.257697432954218542e+00 +1.510000000000001208e+01 8.800000000000000000e+01 8.302506382866150503e+00 +1.515000000000001279e+01 7.300000000000000000e+01 8.351473069632898927e+00 +1.520000000000001350e+01 6.100000000000000000e+01 8.400414678766409793e+00 +1.525000000000001243e+01 6.400000000000000000e+01 8.445518925934434762e+00 +1.530000000000001315e+01 6.200000000000000000e+01 8.483616572756162100e+00 +1.535000000000001208e+01 8.200000000000000000e+01 8.512369112243002078e+00 +1.540000000000001279e+01 8.500000000000000000e+01 8.530368061403875046e+00 +1.545000000000001350e+01 8.200000000000000000e+01 8.537149669128595519e+00 +1.550000000000001243e+01 7.400000000000000000e+01 8.533133455003650170e+00 +1.555000000000001315e+01 6.200000000000000000e+01 8.519495959118085437e+00 +1.560000000000001208e+01 8.600000000000000000e+01 8.497993418217598460e+00 +1.565000000000001279e+01 6.100000000000000000e+01 8.470749388802742530e+00 +1.570000000000001350e+01 7.000000000000000000e+01 8.440025680544451347e+00 +1.575000000000001243e+01 5.800000000000000000e+01 8.407996898107732875e+00 +1.580000000000001315e+01 5.400000000000000000e+01 8.376549603014964873e+00 +1.585000000000001386e+01 6.800000000000000000e+01 8.347125701768829842e+00 +1.590000000000001279e+01 8.200000000000000000e+01 8.320625543276104708e+00 +1.595000000000001350e+01 8.500000000000000000e+01 8.297379429179359889e+00 +1.600000000000001421e+01 6.800000000000000000e+01 8.277187686741289241e+00 +1.605000000000001137e+01 7.100000000000000000e+01 8.259420655653059384e+00 +1.610000000000001563e+01 5.300000000000000000e+01 8.243162603784146114e+00 +1.615000000000001279e+01 6.600000000000000000e+01 8.227379030012345495e+00 +1.620000000000001350e+01 7.400000000000000000e+01 8.211085576781343320e+00 +1.625000000000001421e+01 6.000000000000000000e+01 8.193498580831027667e+00 +1.630000000000001137e+01 6.600000000000000000e+01 8.174151297132215888e+00 +1.635000000000001563e+01 5.500000000000000000e+01 8.152965028991568275e+00 +1.640000000000001279e+01 7.000000000000000000e+01 8.130269934275649035e+00 +1.645000000000001350e+01 5.700000000000000000e+01 8.106775609030409058e+00 +1.650000000000001421e+01 5.400000000000000000e+01 8.083496410236294949e+00 +1.655000000000001137e+01 6.500000000000000000e+01 8.061640743783430096e+00 +1.660000000000001563e+01 6.700000000000000000e+01 8.042477064031196221e+00 +1.665000000000001279e+01 7.500000000000000000e+01 8.027191829442710258e+00 +1.670000000000001350e+01 8.000000000000000000e+01 8.016755727074528437e+00 +1.675000000000001421e+01 6.500000000000000000e+01 8.011813702718709962e+00 +1.680000000000001492e+01 5.800000000000000000e+01 8.012611498800907839e+00 +1.685000000000001563e+01 5.100000000000000000e+01 8.018966693391679001e+00 +1.690000000000001279e+01 6.500000000000000000e+01 8.030286313410492482e+00 +1.695000000000001350e+01 5.300000000000000000e+01 8.045626990275708934e+00 +1.700000000000001421e+01 6.200000000000000000e+01 8.063788446461787274e+00 +1.705000000000001492e+01 5.900000000000000000e+01 8.083427707923073413e+00 +1.710000000000001563e+01 5.500000000000000000e+01 8.103180212163584528e+00 +1.715000000000001279e+01 5.700000000000000000e+01 8.121774787737169987e+00 +1.720000000000001350e+01 5.800000000000000000e+01 8.138131800275919758e+00 +1.725000000000001421e+01 6.400000000000000000e+01 8.151436919186039631e+00 +1.730000000000001492e+01 5.900000000000000000e+01 8.161186334365957862e+00 +1.735000000000001563e+01 6.400000000000000000e+01 8.167202391196038747e+00 +1.740000000000001279e+01 5.500000000000000000e+01 8.169621259994075402e+00 +1.745000000000001350e+01 7.700000000000000000e+01 8.168856317691430391e+00 +1.750000000000001421e+01 6.700000000000000000e+01 8.165542393935401932e+00 +1.755000000000001492e+01 6.500000000000000000e+01 8.160466956272754757e+00 +1.760000000000001563e+01 5.800000000000000000e+01 8.154494713901854652e+00 +1.765000000000001634e+01 6.100000000000000000e+01 8.148492029864767616e+00 +1.770000000000001350e+01 6.100000000000000000e+01 8.143256970106071080e+00 +1.775000000000001421e+01 6.600000000000000000e+01 8.139459828533286867e+00 +1.780000000000001492e+01 7.200000000000000000e+01 8.137597636501475051e+00 +1.785000000000001563e+01 8.500000000000000000e+01 8.137964630880878403e+00 +1.790000000000001634e+01 6.400000000000000000e+01 8.140639096320997581e+00 +1.795000000000001350e+01 6.600000000000000000e+01 8.145485606347829588e+00 +1.800000000000001421e+01 5.100000000000000000e+01 8.152170629474078112e+00 +1.805000000000001492e+01 6.400000000000000000e+01 8.160188841602350251e+00 +1.810000000000001563e+01 6.900000000000000000e+01 8.168897310024872738e+00 +1.815000000000001634e+01 6.000000000000000000e+01 8.177554917570132531e+00 +1.820000000000001705e+01 7.200000000000000000e+01 8.185364844971692477e+00 +1.825000000000001421e+01 6.900000000000000000e+01 8.191518464074396988e+00 +1.830000000000001492e+01 7.600000000000000000e+01 8.195239461444527862e+00 +1.835000000000001563e+01 5.800000000000000000e+01 8.195827296816128538e+00 +1.840000000000001634e+01 5.800000000000000000e+01 8.192699143815733720e+00 +1.845000000000001705e+01 6.100000000000000000e+01 8.185429259836304539e+00 +1.850000000000001421e+01 7.300000000000000000e+01 8.173784337201112749e+00 +1.855000000000001492e+01 7.100000000000000000e+01 8.157752887025354838e+00 +1.860000000000001563e+01 7.400000000000000000e+01 8.137566213812911897e+00 +1.865000000000001634e+01 6.300000000000000000e+01 8.113708179554294020e+00 +1.870000000000001705e+01 5.400000000000000000e+01 8.086910861287075036e+00 +1.875000000000001421e+01 6.900000000000000000e+01 8.058133500003398453e+00 +1.880000000000001492e+01 5.300000000000000000e+01 8.028522925840768210e+00 +1.885000000000001563e+01 5.700000000000000000e+01 7.999354987087202495e+00 +1.890000000000001634e+01 7.400000000000000000e+01 7.971958396790059354e+00 +1.895000000000001705e+01 5.900000000000000000e+01 7.947624716446139104e+00 +1.900000000000001421e+01 6.800000000000000000e+01 7.927510654738507689e+00 +1.905000000000001492e+01 4.900000000000000000e+01 7.912541061789085006e+00 +1.910000000000001563e+01 6.800000000000000000e+01 7.903322448335442729e+00 +1.915000000000001634e+01 7.800000000000000000e+01 7.900077082789176863e+00 +1.920000000000001705e+01 7.200000000000000000e+01 7.902606424450356215e+00 +1.925000000000001776e+01 6.400000000000000000e+01 7.910289859490984732e+00 +1.930000000000001492e+01 6.200000000000000000e+01 7.922120794074880124e+00 +1.935000000000001563e+01 7.500000000000000000e+01 7.936777717671542831e+00 +1.940000000000001634e+01 5.500000000000000000e+01 7.952723163952562402e+00 +1.945000000000001705e+01 7.200000000000000000e+01 7.968316917582213499e+00 +1.950000000000001776e+01 6.200000000000000000e+01 7.981913200810064168e+00 +1.955000000000001492e+01 5.700000000000000000e+01 7.991861469542171470e+00 +1.960000000000001563e+01 5.200000000000000000e+01 7.996204911052299025e+00 +1.965000000000001634e+01 6.500000000000000000e+01 7.991653990818260667e+00 +1.970000000000001705e+01 7.500000000000000000e+01 7.971264500629853700e+00 +1.975000000000001776e+01 7.000000000000000000e+01 7.920666082970229560e+00 +1.980000000000001847e+01 6.500000000000000000e+01 7.814205482931140700e+00 +1.985000000000001563e+01 5.400000000000000000e+01 7.614470181335118326e+00 +1.990000000000001634e+01 5.500000000000000000e+01 7.279075440319572543e+00 +1.995000000000001705e+01 4.600000000000000000e+01 6.775257558953786230e+00 diff --git a/docs/examples/coreshellnp.py b/docs/examples/coreshellnp.py index 61550c56..34f12e72 100644 --- a/docs/examples/coreshellnp.py +++ b/docs/examples/coreshellnp.py @@ -20,6 +20,8 @@ different phases, each with an appropriate characteristic function. """ +from pathlib import Path + from pyobjcryst import loadCrystal from scipy.optimize import leastsq @@ -133,27 +135,18 @@ def makeRecipe(stru1, stru2, datname): return recipe -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe.""" - recipe.plot_recipe( - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", - xlabel=r"$r (\AA)$", - ylabel=r"$G (\AA^{-2})$", - ) - return +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} def main(): """Set up and refine the recipe.""" # Make the data and the recipe - cdsciffile = "data/CdS.cif" - znsciffile = "data/ZnS.cif" - data = "data/CdS_ZnS_nano.gr" + cdsciffile = Path(__file__).parent / "data/CdS.cif" + znsciffile = Path(__file__).parent / "data/ZnS.cif" + data = Path(__file__).parent / "data/CdS_ZnS_nano.gr" # Make the recipe stru1 = loadCrystal(cdsciffile) @@ -197,7 +190,7 @@ def main(): res.print_results() # Plot! - plot_results(recipe) + recipe.plot_recipe(**plot_styles) return diff --git a/docs/examples/crystalpdf.py b/docs/examples/crystalpdf.py index 71319b9c..a5d4e61e 100644 --- a/docs/examples/crystalpdf.py +++ b/docs/examples/crystalpdf.py @@ -24,6 +24,8 @@ demonstrates only the basic configuration. """ +from pathlib import Path + from gaussianrecipe import scipyOptimize from diffpy.srfit.fitbase import ( @@ -66,7 +68,7 @@ def makeRecipe(ciffile, datname): # Qmax value, as well as initial values for the non-structural Parameters. generator = PDFGenerator("G") stru = Structure() - stru.read(ciffile) + stru.read(str(ciffile)) generator.setStructure(stru) # The FitContribution @@ -128,26 +130,17 @@ def makeRecipe(ciffile, datname): return recipe -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe.""" - recipe.plot_recipe( - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", - xlabel=r"$r (\AA)$", - ylabel=r"$G (\AA^{-2})$", - ) - return +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} if __name__ == "__main__": # Make the data and the recipe - ciffile = "data/ni.cif" - data = "data/ni-q27r100-neutron.gr" + ciffile = Path(__file__).parent / "data/ni.cif" + data = Path(__file__).parent / "data/ni-q27r100-neutron.gr" # Make the recipe recipe = makeRecipe(ciffile, data) @@ -161,6 +154,6 @@ def plot_results(recipe): res.print_results() # Plot! - plot_results(recipe) + recipe.plot_recipe(**plot_styles) # End of file diff --git a/docs/examples/crystalpdfall.py b/docs/examples/crystalpdfall.py index 87250053..e851b050 100644 --- a/docs/examples/crystalpdfall.py +++ b/docs/examples/crystalpdfall.py @@ -18,6 +18,8 @@ structure to all the available data. """ +from pathlib import Path + from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -142,34 +144,20 @@ def makeRecipe( return recipe -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe. - - The recipe has four contributions ("xnickel", "xsilicon", "nnickel", - "xsini"), so plot_recipe produces one figure per contribution. - """ - recipe.plot_recipe( - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", - xlabel=r"$r (\AA)$", - ylabel=r"$G (\AA^{-2})$", - ) - return - +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} if __name__ == "__main__": # Make the data and the recipe - ciffile_ni = "data/ni.cif" - ciffile_si = "data/si.cif" - xdata_ni = "data/ni-q27r60-xray.gr" - ndata_ni = "data/ni-q27r100-neutron.gr" - xdata_si = "data/si-q27r60-xray.gr" - xdata_sini = "data/si90ni10-q27r60-xray.gr" + ciffile_ni = Path(__file__).parent / "data/ni.cif" + ciffile_si = Path(__file__).parent / "data/si.cif" + xdata_ni = Path(__file__).parent / "data/ni-q27r60-xray.gr" + ndata_ni = Path(__file__).parent / "data/ni-q27r100-neutron.gr" + xdata_si = Path(__file__).parent / "data/si-q27r60-xray.gr" + xdata_sini = Path(__file__).parent / "data/si90ni10-q27r60-xray.gr" # Make the recipe recipe = makeRecipe( @@ -183,7 +171,9 @@ def plot_results(recipe): res = FitResults(recipe) res.print_results() - # Plot! - plot_results(recipe) + # Plot! The recipe has four contributions ("xnickel", "xsilicon", + # "nnickel", "xsini"), so plot_recipe produces one figure per + # contribution. + recipe.plot_recipe(**plot_styles) # End of file diff --git a/docs/examples/crystalpdfobjcryst.py b/docs/examples/crystalpdfobjcryst.py index 27a1bef0..3fd889f6 100644 --- a/docs/examples/crystalpdfobjcryst.py +++ b/docs/examples/crystalpdfobjcryst.py @@ -19,7 +19,8 @@ provided by the ObjCrystCrystalParSet structure adapter. """ -from crystalpdf import plot_results +from pathlib import Path + from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -90,9 +91,6 @@ def makeRecipe(ciffile, datname): # things by iterating through all the sgpars. for par in phase.sgpars: recipe.add_variable(par) - # set the initial thermal factor to a non-zero value - assert hasattr(recipe, "B11_0") - recipe.B11_0 = 0.1 # We now select non-structural parameters to refine. # This controls the scaling of the PDF. @@ -106,11 +104,17 @@ def makeRecipe(ciffile, datname): return recipe +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} + + if __name__ == "__main__": # Make the data and the recipe - ciffile = "data/si.cif" - data = "data/si-q27r60-xray.gr" + ciffile = Path(__file__).parent / "data/si.cif" + data = Path(__file__).parent / "data/si-q27r60-xray.gr" # Make the recipe recipe = makeRecipe(ciffile, data) @@ -123,6 +127,6 @@ def makeRecipe(ciffile, datname): res.print_results() # Plot! - plot_results(recipe) + recipe.plot_recipe(**plot_styles) # End of file diff --git a/docs/examples/crystalpdftwodata.py b/docs/examples/crystalpdftwodata.py index 3bb9103b..cb6b3091 100644 --- a/docs/examples/crystalpdftwodata.py +++ b/docs/examples/crystalpdftwodata.py @@ -20,6 +20,8 @@ underlying ObjCrystCrystalParSet. """ +from pathlib import Path + from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -133,31 +135,18 @@ def makeRecipe(ciffile, xdatname, ndatname): return recipe -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe. - - The recipe has two contributions ("xnickel" for x-ray, "nnickel" for - neutron), so plot_recipe produces one figure per contribution. - """ - recipe.plot_recipe( - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", - xlabel=r"$r (\AA)$", - ylabel=r"$G (\AA^{-2})$", - ) - return +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} if __name__ == "__main__": # Make the data and the recipe - ciffile = "data/ni.cif" - xdata = "data/ni-q27r60nodg-xray.gr" - ndata = "data/ni-q27r100-neutron.gr" + ciffile = Path(__file__).parent / "data/ni.cif" + xdata = Path(__file__).parent / "data/ni-q27r60nodg-xray.gr" + ndata = Path(__file__).parent / "data/ni-q27r100-neutron.gr" # Make the recipe recipe = makeRecipe(ciffile, xdata, ndata) @@ -169,7 +158,9 @@ def plot_results(recipe): res = FitResults(recipe) res.print_results() - # Plot! - plot_results(recipe) + # Plot! The recipe has two contributions ("xnickel" for x-ray, + # "nnickel" for neutron), so plot_recipe produces one figure per + # contribution. + recipe.plot_recipe(**plot_styles) # End of file diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index 184114f6..af6d1df7 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -20,6 +20,8 @@ nickel and silicon to find the structures and phase fractions. """ +from pathlib import Path + from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -150,27 +152,18 @@ def makeRecipe(niciffile, siciffile, datname): return recipe -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe.""" - recipe.plot_recipe( - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", - xlabel=r"$r (\AA)$", - ylabel=r"$G (\AA^{-2})$", - ) - return +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} if __name__ == "__main__": # Make the data and the recipe - niciffile = "data/ni.cif" - siciffile = "data/si.cif" - data = "data/si90ni10-q27r60-xray.gr" + niciffile = Path(__file__).parent / "data/ni.cif" + siciffile = Path(__file__).parent / "data/si.cif" + data = Path(__file__).parent / "data/si90ni10-q27r60-xray.gr" # Make the recipe recipe = makeRecipe(niciffile, siciffile, data) @@ -183,6 +176,6 @@ def plot_results(recipe): res.print_results() # Plot! - plot_results(recipe) + recipe.plot_recipe(**plot_styles) # End of file diff --git a/docs/examples/ellipsoidsas.py b/docs/examples/ellipsoidsas.py index 9c4fb283..ad9a2c86 100644 --- a/docs/examples/ellipsoidsas.py +++ b/docs/examples/ellipsoidsas.py @@ -14,6 +14,8 @@ ######################################################################## """Example of a refinement of SAS I(Q) data to an ellipsoidal model.""" +from pathlib import Path + import matplotlib.pyplot as plt from gaussianrecipe import scipyOptimize @@ -115,7 +117,7 @@ def plot_results(recipe): if __name__ == "__main__": # Make the data and the recipe - data = "data/sas_ellipsoid_testdata.txt" + data = Path(__file__).parent / "data/sas_ellipsoid_testdata.txt" # Make the recipe recipe = makeRecipe(data) diff --git a/docs/examples/gaussiangenerator.py b/docs/examples/gaussiangenerator.py index fc41c6fb..b7d3e007 100644 --- a/docs/examples/gaussiangenerator.py +++ b/docs/examples/gaussiangenerator.py @@ -39,6 +39,8 @@ GaussianGenerator will be accessible by its name, "g". """ +from pathlib import Path + from numpy import exp from diffpy.srfit.fitbase import ( @@ -131,7 +133,7 @@ def makeRecipe(): # Load data and add it to the profile. This uses the loadtxt function from # numpy. - profile.loadtxt("data/gaussian.dat") + profile.loadtxt(Path(__file__).parent / "data/gaussian.dat") # The ProfileGenerator # Create a GaussianGenerator named "g". This will be the name we use to diff --git a/docs/examples/gaussianrecipe.py b/docs/examples/gaussianrecipe.py index bd73a65b..61f00384 100644 --- a/docs/examples/gaussianrecipe.py +++ b/docs/examples/gaussianrecipe.py @@ -26,8 +26,8 @@ to get an understanding of how a fit recipe can be used once created. After that, read the 'makeRecipe' code to see what goes into a fit recipe. After that, read the 'scipyOptimize' code to see how the refinement is executed. -Finally, read the 'plot_results' code to see how to extracts the -refined profile and plot it. +Finally, look at the 'plot_styles' dict and the 'recipe.plot_recipe' call +to see how the refined profile is plotted. Extensions @@ -45,6 +45,8 @@ from __future__ import print_function +from pathlib import Path + from diffpy.srfit.fitbase import ( FitContribution, FitRecipe, @@ -75,7 +77,7 @@ def main(): res.print_results() # Plot the results. - plot_results(recipe) + recipe.plot_recipe(**plot_styles) return @@ -100,7 +102,7 @@ def makeRecipe(): # Load data and add it to the profile. This uses the loadtxt function from # numpy. - profile.loadtxt("data/gaussian.dat") + profile.loadtxt(Path(__file__).parent / "data/gaussian.dat") # The FitContribution # The FitContribution associates the Profile with a fitting equation. The @@ -178,22 +180,10 @@ def scipyOptimize(recipe): return -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe.""" - # We can access the data and fit profile through the Profile we created - # above. We get to it through our FitContribution, which we named "g1". - recipe.plot_recipe( - show_diff=False, - data_style=".", - data_color="b", - fit_color="g", - data_label="observed Gaussian", - fit_label="calculated Gaussian", - xlabel="x", - ylabel="y", - legend_loc=(0.0, 0.8), - ) - return +plot_styles = { + "xlabel": "x", + "ylabel": "y", +} if __name__ == "__main__": diff --git a/docs/examples/interface.py b/docs/examples/interface.py index f3f2b416..96466808 100644 --- a/docs/examples/interface.py +++ b/docs/examples/interface.py @@ -18,6 +18,8 @@ defined in the diffpy.srfit.interface.interface.py module. """ +from pathlib import Path + from diffpy.srfit.fitbase import ( FitContribution, FitRecipe, @@ -32,7 +34,7 @@ def main(): p = Profile() - p.loadtxt("data/gaussian.dat") + p.loadtxt(Path(__file__).parent / "data/gaussian.dat") # FitContribution operations # "<<" - Inject a parameter value @@ -65,9 +67,11 @@ def main(): # Print the results. res.print_results() # Plot the results. - from gaussianrecipe import plot_results - - plot_results(r) + plot_styles = { + "xlabel": "x", + "ylabel": "y", + } + r.plot_recipe(**plot_styles) return diff --git a/docs/examples/npintensity.py b/docs/examples/npintensity.py index 3afbf901..09a0cb26 100644 --- a/docs/examples/npintensity.py +++ b/docs/examples/npintensity.py @@ -43,6 +43,8 @@ from __future__ import print_function +from pathlib import Path + import matplotlib.pyplot as plt import numpy from gaussianrecipe import scipyOptimize @@ -142,7 +144,7 @@ def setStructure(self, strufile): from diffpy.structure import Structure stru = Structure() - stru.read(strufile) + stru.read(str(strufile)) # Create a ParameterSet designed to interface with # diffpy.structure.Structure objects that organizes the Parameter @@ -307,7 +309,7 @@ def gaussian(q, q0, width): def main(): # Make the data and the recipe - strufile = "data/C60.stru" + strufile = Path(__file__).parent / "data/C60.stru" q = numpy.arange(1, 20, 0.05) makeData(strufile, q, "C60.iq", 1.0, 100.68, 0.005, 0.13, 2) @@ -488,7 +490,7 @@ def makeData(strufile, q, datname, scale, a, Uiso, sig, bkgc, nl=1): from diffpy.structure import Structure S = Structure() - S.read(strufile) + S.read(str(strufile)) # Set the lattice parameters S.lattice.setLatPar(a, a, a) diff --git a/docs/examples/npintensityII.py b/docs/examples/npintensityII.py index abfc2e16..62b3e8c7 100644 --- a/docs/examples/npintensityII.py +++ b/docs/examples/npintensityII.py @@ -34,6 +34,8 @@ first step towards writing a user interface. """ +from pathlib import Path + import matplotlib.pyplot as plt import numpy from gaussianrecipe import scipyOptimize @@ -202,9 +204,6 @@ def plot_results(recipe): figs, axes = recipe.plot_recipe( show=False, return_fig=True, - data_color="b", - fit_color="r", - diff_color="g", data_label="I(Q) Data", fit_label="I(Q) Fit", diff_label="I(Q) diff", @@ -225,7 +224,7 @@ def main(): # Make two different data sets, each from the same structure, but with # different scale, noise, broadening and background. - strufile = "data/C60.stru" + strufile = Path(__file__).parent / "data/C60.stru" q = numpy.arange(1, 20, 0.05) makeData(strufile, q, "C60_1.iq", 8.1, 101.68, 0.008, 0.12, 2, 0.01) makeData(strufile, q, "C60_2.iq", 3.2, 101.68, 0.02, 0.003, 0, 1) diff --git a/docs/examples/nppdfcrystal.py b/docs/examples/nppdfcrystal.py index d932f2a8..a07be4e4 100644 --- a/docs/examples/nppdfcrystal.py +++ b/docs/examples/nppdfcrystal.py @@ -23,6 +23,8 @@ diffpy.srfit.pdf.characteristicfunctions module. """ +from pathlib import Path + import matplotlib.pyplot as plt from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -96,12 +98,6 @@ def plot_results(recipe): fig, ax = recipe.plot_recipe( show=False, return_fig=True, - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", xlabel=r"$r (\AA)$", ylabel=r"$G (\AA^{-2})$", ) @@ -115,8 +111,8 @@ def plot_results(recipe): if __name__ == "__main__": - ciffile = "data/pb.cif" - grdata = "data/pb_100_qmin1.gr" + ciffile = Path(__file__).parent / "data/pb.cif" + grdata = Path(__file__).parent / "data/pb_100_qmin1.gr" recipe = makeRecipe(ciffile, grdata) scipyOptimize(recipe) diff --git a/docs/examples/nppdfobjcryst.py b/docs/examples/nppdfobjcryst.py index dfac5855..8367d1ca 100644 --- a/docs/examples/nppdfobjcryst.py +++ b/docs/examples/nppdfobjcryst.py @@ -18,6 +18,8 @@ the DebyePDFGenerator from SrReal to refine a pyobjcryst Molecule. """ +from pathlib import Path + from diffpy.srfit.fitbase import ( FitContribution, FitRecipe, @@ -105,26 +107,17 @@ def makeRecipe(molecule, datname): return recipe -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe.""" - recipe.plot_recipe( - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", - xlabel=r"$r (\AA)$", - ylabel=r"$G (\AA^{-2})$", - ) - return +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} def main(): molecule = makeC60() # Make the data and the recipe - recipe = makeRecipe(molecule, "data/C60.gr") + recipe = makeRecipe(molecule, Path(__file__).parent / "data/C60.gr") # Tell the fithook that we want very verbose output. recipe.fithooks[0].verbose = 3 @@ -138,7 +131,7 @@ def main(): res.print_results() # Plot results - plot_results(recipe) + recipe.plot_recipe(**plot_styles) return diff --git a/docs/examples/nppdfsas.py b/docs/examples/nppdfsas.py index 07c8ca47..91983bdf 100644 --- a/docs/examples/nppdfsas.py +++ b/docs/examples/nppdfsas.py @@ -22,6 +22,8 @@ of the nanoparticle that agrees best with both the PDF and SAS data. """ +from pathlib import Path + import matplotlib.pyplot as plt from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -164,12 +166,6 @@ def plot_results(recipe): figs, axes = recipe.plot_recipe( show=False, return_fig=True, - data_color="b", - fit_color="r", - diff_color="g", - data_label="G(r) Data", - fit_label="G(r) Fit", - diff_label="G(r) diff", xlabel=r"$r (\AA)$", ylabel=r"$G (\AA^{-2})$", ) @@ -185,9 +181,9 @@ def plot_results(recipe): if __name__ == "__main__": - ciffile = "data/pb.cif" - grdata = "data/pb_100_qmin1.gr" - iqdata = "data/pb_100_qmax1.iq" + ciffile = Path(__file__).parent / "data/pb.cif" + grdata = Path(__file__).parent / "data/pb_100_qmin1.gr" + iqdata = Path(__file__).parent / "data/pb_100_qmax1.iq" recipe = makeRecipe(ciffile, grdata, iqdata) recipe.fithooks[0].verbose = 3 diff --git a/docs/examples/simplepdf.py b/docs/examples/simplepdf.py index 5c07c9b3..143ee38e 100644 --- a/docs/examples/simplepdf.py +++ b/docs/examples/simplepdf.py @@ -18,7 +18,8 @@ data. It uses the PDFContribution class to simplify fit setup. """ -from crystalpdf import plot_results +from pathlib import Path + from gaussianrecipe import scipyOptimize from diffpy.srfit.fitbase import FitRecipe, FitResults @@ -38,7 +39,7 @@ def makeRecipe(ciffile, datname): # and the phase stru = Structure() - stru.read(ciffile) + stru.read(str(ciffile)) contribution.addStructure("nickel", stru) # Make the FitRecipe and add the FitContribution. @@ -65,11 +66,17 @@ def makeRecipe(ciffile, datname): return recipe +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} + + if __name__ == "__main__": # Make the data and the recipe - ciffile = "data/ni.cif" - data = "data/ni-q27r100-neutron.gr" + ciffile = Path(__file__).parent / "data/ni.cif" + data = Path(__file__).parent / "data/ni-q27r100-neutron.gr" # Make the recipe recipe = makeRecipe(ciffile, data) @@ -85,6 +92,6 @@ def makeRecipe(ciffile, datname): res.save_results("nickel_example.res") # Plot! - plot_results(recipe) + recipe.plot_recipe(**plot_styles) # End of file diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index 8283deec..520b734c 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -14,7 +14,8 @@ ######################################################################## """Example of a simplified PDF refinement of two-phase structure.""" -from crystalpdftwophase import plot_results +from pathlib import Path + from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal @@ -110,12 +111,18 @@ def makeRecipe(niciffile, siciffile, datname): return recipe +plot_styles = { + "xlabel": r"$r (\AA)$", + "ylabel": r"$G (\AA^{-2})$", +} + + if __name__ == "__main__": # Make the data and the recipe - niciffile = "data/ni.cif" - siciffile = "data/si.cif" - data = "data/si90ni10-q27r60-xray.gr" + niciffile = Path(__file__).parent / "data/ni.cif" + siciffile = Path(__file__).parent / "data/si.cif" + data = Path(__file__).parent / "data/si90ni10-q27r60-xray.gr" # Make the recipe recipe = makeRecipe(niciffile, siciffile, data) @@ -128,6 +135,6 @@ def makeRecipe(niciffile, siciffile, datname): res.print_results() # Plot! - plot_results(recipe) + recipe.plot_recipe(**plot_styles) # End of file diff --git a/docs/examples/simplerecipe.py b/docs/examples/simplerecipe.py index d0244d92..0ff981f4 100644 --- a/docs/examples/simplerecipe.py +++ b/docs/examples/simplerecipe.py @@ -19,6 +19,8 @@ creation. """ +from pathlib import Path + from diffpy.srfit.fitbase import SimpleRecipe ###### @@ -32,7 +34,7 @@ def main(): recipe = SimpleRecipe() # Load text from file. - recipe.loadtxt("data/gaussian.dat") + recipe.loadtxt(Path(__file__).parent / "data/gaussian.dat") # Set the equation. The variable "x" is taken from the data that was just # loaded. The other variables, "A", "x0" and "sigma" are turned into diff --git a/docs/examples/threedoublepeaks.py b/docs/examples/threedoublepeaks.py index a17b0725..935aa715 100644 --- a/docs/examples/threedoublepeaks.py +++ b/docs/examples/threedoublepeaks.py @@ -16,6 +16,8 @@ from __future__ import print_function +from pathlib import Path + import numpy from diffpy.srfit.fitbase import ( @@ -47,7 +49,9 @@ def makeRecipe(): # The Profile # Create a Profile to hold the experimental and calculated signal. profile = Profile() - x, y, dy = profile.loadtxt("data/threedoublepeaks.dat") + x, y, dy = profile.loadtxt( + Path(__file__).parent / "data/threedoublepeaks.dat" + ) # Create the contribution contribution = FitContribution("peaks") @@ -188,23 +192,10 @@ def scipyOptimize(recipe): return -def plot_results(recipe): - """Plot the results contained within a refined FitRecipe.""" - # We can access the data and fit profile through the Profile we created - # above. We get to it through our FitContribution, which we named "peaks". - recipe.plot_recipe( - data_style=".", - data_color="b", - fit_color="r", - diff_color="g", - data_label="observed profile", - fit_label="calculated profile", - diff_label="difference", - xlabel="x", - ylabel="y", - legend_loc=(0.0, 0.8), - ) - return +plot_styles = { + "xlabel": "x", + "ylabel": "y", +} def steerFit(recipe): @@ -246,7 +237,7 @@ def steerFit(recipe): res.print_results() # Plot the results - plot_results(recipe) + recipe.plot_recipe(**plot_styles) # End of file From 6e4fbc06edc2f787e2afbf2bb66141c65d9780b5 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 14:27:57 -0400 Subject: [PATCH 07/11] Change plot_recipe to display the contribution name as the title --- src/diffpy/srfit/fitbase/fitrecipe.py | 53 ++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 9bdd8d72..efa1aa64 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -1541,7 +1541,9 @@ def set_plot_defaults(self, **kwargs): ylabel : str, optional The label for the y-axis. title : str or None, optional - The plot title. Default is no title. + The plot title. If None (default), each figure created by + `plot_recipe` is titled with the name of the contribution it + shows. A title is not added to a user-supplied axes. legend : bool, optional The legend is shown if True. Default is True. legend_loc : str, optional @@ -1558,6 +1560,16 @@ def set_plot_defaults(self, **kwargs): show : bool, optional The plot is displayed using `plt.show()` if True. Default is True. + Notes + ----- + The `data_label`, `fit_label`, `diff_label` and `title` options accept + a ``{contribution}`` placeholder that is replaced by the name of the + FitContribution being plotted, e.g. + ``fit_label="{contribution} calculated"``. When several contributions + are drawn on a shared axes, labels without the placeholder are + prefixed with the contribution name so the legend entries stay + distinguishable. + Examples -------- >>> recipe.set_plot_defaults( @@ -1575,6 +1587,14 @@ def set_plot_defaults(self, **kwargs): ) self.plot_options.update(kwargs) + def _format_plot_label(self, label, contribution_name, add_prefix): + """Insert the contribution name into a legend label.""" + if "{contribution}" in label: + return label.format(contribution=contribution_name) + if add_prefix: + return f"{contribution_name}: {label}" + return label + def _set_axes_labels_from_metadata(self, meta, plot_params): """Set axes labels based on filename suffix in profile metadata if not already set.""" @@ -1677,20 +1697,23 @@ def plot_recipe(self, ax=None, return_fig=False, **kwargs): ) figures = [] axes_list = [] + shared_axes = ax is not None and len(self._contributions) > 1 for name, contrib in self._contributions.items(): profile = contrib.profile x = profile.x yobs = profile.y ycalc = profile.ycalc + show_fit = plot_params["show_fit"] + show_diff = plot_params["show_diff"] if ycalc is None: - if plot_params["show_fit"] or plot_params["show_diff"]: + if show_fit or show_diff: print( f"Contribution '{name}' has no calculated values " "(ycalc is None). " "Only observed data will be plotted." ) - plot_params["show_fit"] = False - plot_params["show_diff"] = False + show_fit = False + show_diff = False else: diff = yobs - ycalc y_min = min(yobs.min(), ycalc.min()) @@ -1709,27 +1732,33 @@ def plot_recipe(self, ax=None, return_fig=False, **kwargs): x, yobs, plot_params["data_style"], - label=plot_params["data_label"], + label=self._format_plot_label( + plot_params["data_label"], name, shared_axes + ), color=plot_params["data_color"], markersize=plot_params["markersize"], alpha=plot_params["alpha"], ) - if plot_params["show_fit"]: + if show_fit: current_ax.plot( x, ycalc, plot_params["fit_style"], - label=plot_params["fit_label"], + label=self._format_plot_label( + plot_params["fit_label"], name, shared_axes + ), color=plot_params["fit_color"], linewidth=plot_params["linewidth"], alpha=plot_params["alpha"], ) - if plot_params["show_diff"]: + if show_diff: current_ax.plot( x, diff + offset, plot_params["diff_style"], - label=plot_params["diff_label"], + label=self._format_plot_label( + plot_params["diff_label"], name, shared_axes + ), color=plot_params["diff_color"], linewidth=plot_params["linewidth"], alpha=plot_params["alpha"], @@ -1746,7 +1775,11 @@ def plot_recipe(self, ax=None, return_fig=False, **kwargs): if plot_params["ylabel"] is not None: current_ax.set_ylabel(plot_params["ylabel"]) if plot_params["title"] is not None: - current_ax.set_title(plot_params["title"]) + current_ax.set_title( + self._format_plot_label(plot_params["title"], name, False) + ) + elif ax is None: + current_ax.set_title(name) if plot_params["legend"]: current_ax.legend(loc=plot_params["legend_loc"], frameon=True) if plot_params["grid"]: From 18488bffa49cb6228324fc11b368a936d2b78b62 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 14:34:17 -0400 Subject: [PATCH 08/11] add tests for the new plot_recipe behavior --- tests/test_fitrecipe.py | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index d24ccd2b..6101ed5d 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -860,6 +860,96 @@ def test_plot_recipe_set_title(build_recipes_one_contribution): assert actual_title == expected_title +def test_plot_recipe_default_title(build_recipes_one_contribution): + # Case: A single contribution is plotted with no title given. + # Expected: The figure is titled with the name of the contribution. + recipe, _ = build_recipes_one_contribution + optimize_recipe(recipe) + plt.close("all") + _, ax = recipe.plot_recipe(show=False, return_fig=True) + actual_title = ax.get_title() + expected_title = "c1" + assert actual_title == expected_title + + +def test_plot_recipe_titles_two_contributions(build_recipe_two_contributions): + # Case: Two contributions are plotted on separate figures with no + # title given. + # Expected: Each figure is titled with the name of its contribution. + recipe = build_recipe_two_contributions + optimize_recipe(recipe) + plt.close("all") + _, axes = recipe.plot_recipe(show=False, return_fig=True) + actual_titles = [ax.get_title() for ax in axes] + expected_titles = ["c1", "c2"] + assert actual_titles == expected_titles + + +def test_plot_recipe_labels_shared_axes(build_recipe_two_contributions): + # Case: Two contributions are plotted on a single user-supplied axes. + # Expected: Legend labels are prefixed with the contribution name so + # that the curves of each contribution can be told apart. + recipe = build_recipe_two_contributions + optimize_recipe(recipe) + plt.close("all") + _, ax = plt.subplots() + recipe.plot_recipe(ax=ax, show=False) + actual_labels, _ = get_labels_and_linecount(ax) + expected_labels = [ + "c1: Observed", + "c1: Calculated", + "c1: Difference", + "c2: Observed", + "c2: Calculated", + "c2: Difference", + ] + assert actual_labels == expected_labels + + +# The legend of each figure describes the curves of the contribution it +# shows. The cases below cover the labels a recipe of two contributions +# produces when it is plotted on one figure per contribution. +@pytest.mark.parametrize( + "contributions_without_ycalc, plot_kwargs, expected_labels", + [ + # C1: A label contains the {contribution} placeholder. + # Expected: The placeholder is replaced by the contribution name. + ( + [], + { + "fit_label": "{contribution} calculated", + "show_observed": False, + "show_diff": False, + }, + [["c1 calculated"], ["c2 calculated"]], + ), + # C2: Only the second contribution has been evaluated, so the ycalc + # of the first one is None. + # Expected: The unevaluated contribution shows observed data only + # while the other one is still plotted in full. + ( + ["c1"], + {}, + [["Observed"], ["Observed", "Calculated", "Difference"]], + ), + ], +) +def test_plot_recipe_labels( + build_recipe_two_contributions, + contributions_without_ycalc, + plot_kwargs, + expected_labels, +): + recipe = build_recipe_two_contributions + optimize_recipe(recipe) + plt.close("all") + for name in contributions_without_ycalc: + recipe._contributions[name].profile.ycalc = None + _, axes = recipe.plot_recipe(show=False, return_fig=True, **plot_kwargs) + actual_labels = [get_labels_and_linecount(ax)[0] for ax in axes] + assert actual_labels == expected_labels + + def test_plot_recipe_set_defaults(build_recipes_one_contribution): # Case: user sets default plot options with set_plot_defaults # Expected: plot_recipe uses the default options for all calls From 0366c57561b611f938bd31155380625fe1ad7f18 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 14:37:29 -0400 Subject: [PATCH 09/11] rm accidentally commited output file --- docs/examples/C60.iq | 380 ------------------------------------------- 1 file changed, 380 deletions(-) delete mode 100644 docs/examples/C60.iq diff --git a/docs/examples/C60.iq b/docs/examples/C60.iq deleted file mode 100644 index 5ab145f5..00000000 --- a/docs/examples/C60.iq +++ /dev/null @@ -1,380 +0,0 @@ -1.000000000000000000e+00 4.100000000000000000e+01 7.110034071726496485e+00 -1.050000000000000044e+00 6.100000000000000000e+01 8.394511215438845042e+00 -1.100000000000000089e+00 8.900000000000000000e+01 9.614581256271836907e+00 -1.150000000000000133e+00 9.400000000000000000e+01 1.066952084213099461e+01 -1.200000000000000178e+00 1.150000000000000000e+02 1.148570297676609719e+01 -1.250000000000000222e+00 1.520000000000000000e+02 1.201930040395864197e+01 -1.300000000000000266e+00 1.390000000000000000e+02 1.225310840582604577e+01 -1.350000000000000311e+00 1.180000000000000000e+02 1.219109445355365295e+01 -1.400000000000000355e+00 1.670000000000000000e+02 1.185346751873712456e+01 -1.450000000000000400e+00 1.370000000000000000e+02 1.127341865731435888e+01 -1.500000000000000444e+00 1.090000000000000000e+02 1.049553382730363182e+01 -1.550000000000000488e+00 7.500000000000000000e+01 9.575614813179214480e+00 -1.600000000000000533e+00 6.500000000000000000e+01 8.581852178359065775e+00 -1.650000000000000577e+00 6.200000000000000000e+01 7.597231598891897697e+00 -1.700000000000000622e+00 4.600000000000000000e+01 6.721462898823489240e+00 -1.750000000000000666e+00 3.700000000000000000e+01 6.065468561148816384e+00 -1.800000000000000711e+00 3.600000000000000000e+01 5.724586352107412957e+00 -1.850000000000000755e+00 3.400000000000000000e+01 5.729721877353608939e+00 -1.900000000000000799e+00 3.500000000000000000e+01 6.021186349459231479e+00 -1.950000000000000844e+00 4.400000000000000000e+01 6.483815947427138404e+00 -2.000000000000000888e+00 4.400000000000000000e+01 7.002239222540367791e+00 -2.050000000000000711e+00 5.500000000000000000e+01 7.488355864944134410e+00 -2.100000000000000977e+00 5.200000000000000000e+01 7.883677244766420955e+00 -2.150000000000001243e+00 5.800000000000000000e+01 8.153614043016924384e+00 -2.200000000000001066e+00 6.100000000000000000e+01 8.281875115567967072e+00 -2.250000000000000888e+00 6.200000000000000000e+01 8.266644375798138711e+00 -2.300000000000001155e+00 6.000000000000000000e+01 8.118269608547297622e+00 -2.350000000000001421e+00 5.900000000000000000e+01 7.857966080848713730e+00 -2.400000000000001243e+00 5.600000000000000000e+01 7.517110700664150436e+00 -2.450000000000001066e+00 4.400000000000000000e+01 7.136638569231891438e+00 -2.500000000000001332e+00 6.100000000000000000e+01 6.765682108597169453e+00 -2.550000000000001599e+00 4.400000000000000000e+01 6.457931897248158748e+00 -2.600000000000001421e+00 4.900000000000000000e+01 6.263923506264947427e+00 -2.650000000000001243e+00 4.900000000000000000e+01 6.219316976871393621e+00 -2.700000000000001510e+00 5.000000000000000000e+01 6.333975657986292696e+00 -2.750000000000001776e+00 4.400000000000000000e+01 6.589463084173096341e+00 -2.800000000000001599e+00 4.000000000000000000e+01 6.947213664064986638e+00 -2.850000000000001421e+00 6.200000000000000000e+01 7.361260323774981629e+00 -2.900000000000001688e+00 6.800000000000000000e+01 7.788341999336049426e+00 -2.950000000000001954e+00 6.700000000000000000e+01 8.193080954789397907e+00 -3.000000000000001776e+00 6.800000000000000000e+01 8.549520234649873984e+00 -3.050000000000001599e+00 9.000000000000000000e+01 8.840859099287747824e+00 -3.100000000000001865e+00 9.200000000000000000e+01 9.058525627326627472e+00 -3.150000000000002132e+00 8.400000000000000000e+01 9.201068065912670235e+00 -3.200000000000001954e+00 8.900000000000000000e+01 9.272993454368318567e+00 -3.250000000000001776e+00 8.300000000000000000e+01 9.283539275304724114e+00 -3.300000000000002043e+00 8.800000000000000000e+01 9.245326311618564219e+00 -3.350000000000002309e+00 9.700000000000000000e+01 9.172856016833904391e+00 -3.400000000000002132e+00 7.800000000000000000e+01 9.080866437502484345e+00 -3.450000000000001954e+00 7.100000000000000000e+01 8.982640387682405247e+00 -3.500000000000002220e+00 6.800000000000000000e+01 8.888451354172726582e+00 -3.550000000000002487e+00 8.300000000000000000e+01 8.804398845976720622e+00 -3.600000000000002309e+00 7.200000000000000000e+01 8.731877217811153002e+00 -3.650000000000002132e+00 7.400000000000000000e+01 8.667814859318555776e+00 -3.700000000000002398e+00 8.600000000000000000e+01 8.605643849638678233e+00 -3.750000000000002665e+00 8.100000000000000000e+01 8.536791772433714343e+00 -3.800000000000002487e+00 6.900000000000000000e+01 8.452401862569086433e+00 -3.850000000000002309e+00 6.900000000000000000e+01 8.345005535250718864e+00 -3.900000000000002576e+00 5.200000000000000000e+01 8.209955683194356979e+00 -3.950000000000002842e+00 7.200000000000000000e+01 8.046521905852104695e+00 -4.000000000000002665e+00 5.500000000000000000e+01 7.858606567364106787e+00 -4.050000000000002487e+00 5.000000000000000000e+01 7.655045495714588810e+00 -4.100000000000003197e+00 5.800000000000000000e+01 7.449412380847344473e+00 -4.150000000000003020e+00 5.200000000000000000e+01 7.259175061828185171e+00 -4.200000000000002842e+00 6.000000000000000000e+01 7.104013309679550581e+00 -4.250000000000002665e+00 3.200000000000000000e+01 7.003207308137216813e+00 -4.300000000000002487e+00 5.300000000000000000e+01 6.972348801934296958e+00 -4.350000000000003197e+00 4.900000000000000000e+01 7.020153409024082691e+00 -4.400000000000003020e+00 5.300000000000000000e+01 7.146492100893967248e+00 -4.450000000000002842e+00 6.100000000000000000e+01 7.342457215186272812e+00 -4.500000000000003553e+00 6.400000000000000000e+01 7.592380394981023350e+00 -4.550000000000003375e+00 8.300000000000000000e+01 7.876901901638250436e+00 -4.600000000000003197e+00 7.200000000000000000e+01 8.176030947489463685e+00 -4.650000000000003020e+00 8.600000000000000000e+01 8.471537656601991984e+00 -4.700000000000002842e+00 6.500000000000000000e+01 8.748510791443548484e+00 -4.750000000000003553e+00 8.000000000000000000e+01 8.996203868783760882e+00 -4.800000000000003375e+00 7.500000000000000000e+01 9.208363789799006938e+00 -4.850000000000003197e+00 1.110000000000000000e+02 9.383194733358182660e+00 -4.900000000000003908e+00 9.200000000000000000e+01 9.523041962636108693e+00 -4.950000000000003730e+00 9.700000000000000000e+01 9.633825800463693412e+00 -5.000000000000003553e+00 9.400000000000000000e+01 9.724228177807262341e+00 -5.050000000000003375e+00 7.800000000000000000e+01 9.804636369833021448e+00 -5.100000000000003197e+00 1.050000000000000000e+02 9.885881519525440808e+00 -5.150000000000003908e+00 9.300000000000000000e+01 9.977868101282044933e+00 -5.200000000000003730e+00 9.100000000000000000e+01 1.008825679835690892e+01 -5.250000000000003553e+00 1.080000000000000000e+02 1.022140576118761324e+01 -5.300000000000004263e+00 1.200000000000000000e+02 1.037776088970294630e+01 -5.350000000000004086e+00 1.210000000000000000e+02 1.055380342457576148e+01 -5.400000000000003908e+00 1.090000000000000000e+02 1.074253855346414355e+01 -5.450000000000003730e+00 1.390000000000000000e+02 1.093439337469931871e+01 -5.500000000000003553e+00 1.160000000000000000e+02 1.111833081572526183e+01 -5.550000000000004263e+00 1.330000000000000000e+02 1.128298953538036820e+01 -5.600000000000004086e+00 1.190000000000000000e+02 1.141770825687207314e+01 -5.650000000000003908e+00 1.610000000000000000e+02 1.151335497518072160e+01 -5.700000000000004619e+00 1.490000000000000000e+02 1.156293357952795198e+01 -5.750000000000004441e+00 1.450000000000000000e+02 1.156197344178840147e+01 -5.800000000000004263e+00 1.470000000000000000e+02 1.150872284964192005e+01 -5.850000000000004086e+00 1.270000000000000000e+02 1.140417052050952762e+01 -5.900000000000003908e+00 1.280000000000000000e+02 1.125191654884030257e+01 -5.950000000000004619e+00 1.200000000000000000e+02 1.105790918681919521e+01 -6.000000000000004441e+00 1.090000000000000000e+02 1.083005955981421309e+01 -6.050000000000004263e+00 1.200000000000000000e+02 1.057774473732988341e+01 -6.100000000000004974e+00 9.900000000000000000e+01 1.031121229885946278e+01 -6.150000000000004796e+00 1.080000000000000000e+02 1.004090829663322637e+01 -6.200000000000004619e+00 8.600000000000000000e+01 9.776766079110393193e+00 -6.250000000000004441e+00 8.900000000000000000e+01 9.527514108236475820e+00 -6.300000000000004263e+00 8.300000000000000000e+01 9.300080872727065184e+00 -6.350000000000004974e+00 8.300000000000000000e+01 9.099184249267430857e+00 -6.400000000000004796e+00 8.000000000000000000e+01 8.927180343679047780e+00 -6.450000000000004619e+00 6.400000000000000000e+01 8.784208031611530743e+00 -6.500000000000005329e+00 7.200000000000000000e+01 8.668607219973745615e+00 -6.550000000000005151e+00 6.500000000000000000e+01 8.577529867563132626e+00 -6.600000000000004974e+00 7.100000000000000000e+01 8.507624103272689808e+00 -6.650000000000004796e+00 7.100000000000000000e+01 8.455665328034214667e+00 -6.700000000000004619e+00 8.400000000000000000e+01 8.419031656868744662e+00 -6.750000000000005329e+00 7.600000000000000000e+01 8.395962095144447801e+00 -6.800000000000005151e+00 7.100000000000000000e+01 8.385580743558554317e+00 -6.850000000000004974e+00 7.700000000000000000e+01 8.387710091873067597e+00 -6.900000000000005684e+00 7.000000000000000000e+01 8.402527436468801625e+00 -6.950000000000005507e+00 6.600000000000000000e+01 8.430139507416994249e+00 -7.000000000000005329e+00 7.800000000000000000e+01 8.470160140601601384e+00 -7.050000000000005151e+00 7.000000000000000000e+01 8.521372205036955805e+00 -7.100000000000004974e+00 6.600000000000000000e+01 8.581537097690857152e+00 -7.150000000000005684e+00 8.000000000000000000e+01 8.647385606863707608e+00 -7.200000000000005507e+00 9.100000000000000000e+01 8.714789602889016606e+00 -7.250000000000005329e+00 7.200000000000000000e+01 8.779083526869889909e+00 -7.300000000000006040e+00 7.900000000000000000e+01 8.835484886425980733e+00 -7.350000000000005862e+00 8.900000000000000000e+01 8.879556286081761840e+00 -7.400000000000005684e+00 6.900000000000000000e+01 8.907655642122886519e+00 -7.450000000000005507e+00 6.900000000000000000e+01 8.917331288011601131e+00 -7.500000000000005329e+00 7.600000000000000000e+01 8.907629739617604514e+00 -7.550000000000006040e+00 7.200000000000000000e+01 8.879292657499760821e+00 -7.600000000000005862e+00 8.600000000000000000e+01 8.834824869103542255e+00 -7.650000000000005684e+00 8.500000000000000000e+01 8.778418030801720562e+00 -7.700000000000006395e+00 6.500000000000000000e+01 8.715717081035128544e+00 -7.750000000000006217e+00 7.900000000000000000e+01 8.653422760898482835e+00 -7.800000000000006040e+00 6.000000000000000000e+01 8.598737299006906198e+00 -7.850000000000005862e+00 8.100000000000000000e+01 8.558684648900005243e+00 -7.900000000000005684e+00 6.800000000000000000e+01 8.539369864632448071e+00 -7.950000000000006395e+00 8.000000000000000000e+01 8.545275608545845003e+00 -8.000000000000007105e+00 8.000000000000000000e+01 8.578712136687379086e+00 -8.050000000000006040e+00 7.200000000000000000e+01 8.639525240410916851e+00 -8.100000000000006750e+00 6.700000000000000000e+01 8.725120279835021364e+00 -8.150000000000005684e+00 7.500000000000000000e+01 8.830792934611320533e+00 -8.200000000000006395e+00 8.600000000000000000e+01 8.950294179439390874e+00 -8.250000000000007105e+00 9.300000000000000000e+01 9.076521062334110823e+00 -8.300000000000006040e+00 7.200000000000000000e+01 9.202223480522381038e+00 -8.350000000000006750e+00 8.500000000000000000e+01 9.320641432123547787e+00 -8.400000000000005684e+00 9.000000000000000000e+01 9.426021121578955331e+00 -8.450000000000006395e+00 8.900000000000000000e+01 9.513988450109815531e+00 -8.500000000000007105e+00 8.000000000000000000e+01 9.581778548664207307e+00 -8.550000000000007816e+00 1.030000000000000000e+02 9.628329978361691133e+00 -8.600000000000006750e+00 8.600000000000000000e+01 9.654255057679581142e+00 -8.650000000000005684e+00 1.000000000000000000e+02 9.661697181893927677e+00 -8.700000000000006395e+00 8.200000000000000000e+01 9.654085151079360827e+00 -8.750000000000007105e+00 8.800000000000000000e+01 9.635795730852684926e+00 -8.800000000000007816e+00 1.000000000000000000e+02 9.611740328487787366e+00 -8.850000000000006750e+00 9.600000000000000000e+01 9.586900055198276149e+00 -8.900000000000007461e+00 8.000000000000000000e+01 9.565844345425249529e+00 -8.950000000000006395e+00 8.700000000000000000e+01 9.552278741984800092e+00 -9.000000000000007105e+00 9.300000000000000000e+01 9.548673107122208847e+00 -9.050000000000007816e+00 7.700000000000000000e+01 9.556018159368159459e+00 -9.100000000000006750e+00 8.700000000000000000e+01 9.573743921776756594e+00 -9.150000000000007461e+00 9.800000000000000000e+01 9.599810448230144289e+00 -9.200000000000006395e+00 9.100000000000000000e+01 9.630954948462534304e+00 -9.250000000000007105e+00 9.800000000000000000e+01 9.663057475129420482e+00 -9.300000000000007816e+00 1.050000000000000000e+02 9.691575119828224061e+00 -9.350000000000006750e+00 9.400000000000000000e+01 9.711993623533592412e+00 -9.400000000000007461e+00 8.200000000000000000e+01 9.720252989863215731e+00 -9.450000000000008171e+00 8.800000000000000000e+01 9.713115648560902926e+00 -9.500000000000007105e+00 9.500000000000000000e+01 9.688457682829707096e+00 -9.550000000000007816e+00 9.600000000000000000e+01 9.645472849804859194e+00 -9.600000000000008527e+00 9.600000000000000000e+01 9.584784599637655944e+00 -9.650000000000007461e+00 9.100000000000000000e+01 9.508463420899012419e+00 -9.700000000000008171e+00 1.010000000000000000e+02 9.419946847482300711e+00 -9.750000000000007105e+00 7.700000000000000000e+01 9.323859095257963858e+00 -9.800000000000007816e+00 8.300000000000000000e+01 9.225728614844570075e+00 -9.850000000000008527e+00 9.100000000000000000e+01 9.131607004916789450e+00 -9.900000000000007461e+00 7.700000000000000000e+01 9.047603415953551220e+00 -9.950000000000008171e+00 7.800000000000000000e+01 8.979364928521878397e+00 -1.000000000000000711e+01 8.700000000000000000e+01 8.931552680443118675e+00 -1.005000000000000782e+01 8.000000000000000000e+01 8.907379524368012724e+00 -1.010000000000000853e+01 6.500000000000000000e+01 8.908279460570181385e+00 -1.015000000000000746e+01 8.700000000000000000e+01 8.933765675745906520e+00 -1.020000000000000817e+01 5.600000000000000000e+01 8.981503000963613204e+00 -1.025000000000000888e+01 7.400000000000000000e+01 9.047580596390391250e+00 -1.030000000000000782e+01 1.000000000000000000e+02 9.126935048637218273e+00 -1.035000000000000853e+01 7.700000000000000000e+01 9.213853947580945558e+00 -1.040000000000000924e+01 9.600000000000000000e+01 9.302488879269672495e+00 -1.045000000000000817e+01 8.400000000000000000e+01 9.387320271775937641e+00 -1.050000000000000888e+01 8.000000000000000000e+01 9.463536501592827221e+00 -1.055000000000000782e+01 8.800000000000000000e+01 9.527308789536903078e+00 -1.060000000000000853e+01 1.080000000000000000e+02 9.575957677129201429e+00 -1.065000000000000924e+01 8.300000000000000000e+01 9.608015565181519335e+00 -1.070000000000000817e+01 7.900000000000000000e+01 9.623194216759152653e+00 -1.075000000000000888e+01 7.600000000000000000e+01 9.622268245622437988e+00 -1.080000000000000782e+01 8.500000000000000000e+01 9.606887201257027442e+00 -1.085000000000000853e+01 9.000000000000000000e+01 9.579331104248058892e+00 -1.090000000000000924e+01 1.060000000000000000e+02 9.542227638819575475e+00 -1.095000000000000817e+01 8.500000000000000000e+01 9.498253378491876120e+00 -1.100000000000000888e+01 9.400000000000000000e+01 9.449845407854379431e+00 -1.105000000000000959e+01 7.500000000000000000e+01 9.398952003246249021e+00 -1.110000000000000853e+01 1.010000000000000000e+02 9.346850057037913828e+00 -1.115000000000000924e+01 9.900000000000000000e+01 9.294051608561360922e+00 -1.120000000000000995e+01 8.700000000000000000e+01 9.240312224591288981e+00 -1.125000000000000888e+01 6.600000000000000000e+01 9.184741447173394135e+00 -1.130000000000000959e+01 6.900000000000000000e+01 9.126002502838224117e+00 -1.135000000000000853e+01 8.300000000000000000e+01 9.062577533224876802e+00 -1.140000000000000924e+01 7.700000000000000000e+01 8.993067601007187051e+00 -1.145000000000000995e+01 7.400000000000000000e+01 8.916494171353958720e+00 -1.150000000000000888e+01 8.500000000000000000e+01 8.832569929412615650e+00 -1.155000000000000959e+01 7.600000000000000000e+01 8.741910228941105032e+00 -1.160000000000000853e+01 8.000000000000000000e+01 8.646160792090730851e+00 -1.165000000000000924e+01 8.000000000000000000e+01 8.548021811113509116e+00 -1.170000000000000995e+01 7.200000000000000000e+01 8.451153715058385529e+00 -1.175000000000000888e+01 7.000000000000000000e+01 8.359956988057456684e+00 -1.180000000000000959e+01 7.900000000000000000e+01 8.279229566668032447e+00 -1.185000000000001030e+01 4.900000000000000000e+01 8.213721992110649239e+00 -1.190000000000000924e+01 7.400000000000000000e+01 8.167631967157074513e+00 -1.195000000000000995e+01 5.200000000000000000e+01 8.144101858626340729e+00 -1.200000000000001066e+01 8.100000000000000000e+01 8.144796853164912420e+00 -1.205000000000000959e+01 7.500000000000000000e+01 8.169638978489189185e+00 -1.210000000000001030e+01 6.800000000000000000e+01 8.216748706501141086e+00 -1.215000000000000924e+01 6.200000000000000000e+01 8.282606051894481070e+00 -1.220000000000000995e+01 7.900000000000000000e+01 8.362399934800148316e+00 -1.225000000000001066e+01 6.200000000000000000e+01 8.450502742672531653e+00 -1.230000000000000959e+01 6.800000000000000000e+01 8.540995032240447316e+00 -1.235000000000001030e+01 8.700000000000000000e+01 8.628172253171534578e+00 -1.240000000000000924e+01 6.600000000000000000e+01 8.706983631617474018e+00 -1.245000000000000995e+01 7.000000000000000000e+01 8.773374033277576700e+00 -1.250000000000001066e+01 6.300000000000000000e+01 8.824516633148817846e+00 -1.255000000000000959e+01 7.400000000000000000e+01 8.858935332352718461e+00 -1.260000000000001030e+01 8.100000000000000000e+01 8.876521735591397899e+00 -1.265000000000001101e+01 7.900000000000000000e+01 8.878454121635543927e+00 -1.270000000000000995e+01 7.000000000000000000e+01 8.867027373514122957e+00 -1.275000000000001066e+01 8.900000000000000000e+01 8.845405047470958237e+00 -1.280000000000001137e+01 9.300000000000000000e+01 8.817308742966060819e+00 -1.285000000000001030e+01 8.900000000000000000e+01 8.786665927272407473e+00 -1.290000000000001101e+01 7.300000000000000000e+01 8.757244558044158467e+00 -1.295000000000000995e+01 6.800000000000000000e+01 8.732309310579621453e+00 -1.300000000000001066e+01 7.100000000000000000e+01 8.714337229203110269e+00 -1.305000000000001137e+01 7.500000000000000000e+01 8.704827545980174719e+00 -1.310000000000001030e+01 8.400000000000000000e+01 8.704229973402965115e+00 -1.315000000000001101e+01 7.700000000000000000e+01 8.711999121178282124e+00 -1.320000000000000995e+01 7.000000000000000000e+01 8.726763399736139348e+00 -1.325000000000001066e+01 9.000000000000000000e+01 8.746579637700319765e+00 -1.330000000000001137e+01 7.800000000000000000e+01 8.769233684973752219e+00 -1.335000000000001030e+01 7.900000000000000000e+01 8.792544252806974825e+00 -1.340000000000001101e+01 7.900000000000000000e+01 8.814631370278997125e+00 -1.345000000000001172e+01 8.700000000000000000e+01 8.834119694396816769e+00 -1.350000000000001066e+01 9.200000000000000000e+01 8.850257795045594733e+00 -1.355000000000001137e+01 8.200000000000000000e+01 8.862945462663342155e+00 -1.360000000000001208e+01 7.400000000000000000e+01 8.872671109107121978e+00 -1.365000000000001101e+01 9.000000000000000000e+01 8.880370275169243044e+00 -1.370000000000001172e+01 7.300000000000000000e+01 8.887224180763482195e+00 -1.375000000000001066e+01 7.400000000000000000e+01 8.894423947880628845e+00 -1.380000000000001137e+01 9.900000000000000000e+01 8.902930864370043551e+00 -1.385000000000001208e+01 7.300000000000000000e+01 8.913264702447145638e+00 -1.390000000000001101e+01 5.500000000000000000e+01 8.925349577665157241e+00 -1.395000000000001172e+01 7.300000000000000000e+01 8.938439737497418491e+00 -1.400000000000001066e+01 8.600000000000000000e+01 8.951136775080847485e+00 -1.405000000000001137e+01 7.800000000000000000e+01 8.961497014404971040e+00 -1.410000000000001208e+01 7.100000000000000000e+01 8.967215757788139285e+00 -1.415000000000001101e+01 6.700000000000000000e+01 8.965866001120533113e+00 -1.420000000000001172e+01 8.700000000000000000e+01 8.955164358982925066e+00 -1.425000000000001243e+01 7.400000000000000000e+01 8.933236265672745091e+00 -1.430000000000001137e+01 9.000000000000000000e+01 8.898854986156370828e+00 -1.435000000000001208e+01 8.100000000000000000e+01 8.851633119159556529e+00 -1.440000000000001279e+01 7.800000000000000000e+01 8.792149805850542066e+00 -1.445000000000001172e+01 8.600000000000000000e+01 8.722001027115990013e+00 -1.450000000000001243e+01 6.800000000000000000e+01 8.643764131952053731e+00 -1.455000000000001137e+01 7.100000000000000000e+01 8.560871690138158741e+00 -1.460000000000001208e+01 6.900000000000000000e+01 8.477394956421136385e+00 -1.465000000000001279e+01 5.900000000000000000e+01 8.397744799564367923e+00 -1.470000000000001172e+01 7.200000000000000000e+01 8.326308489039982774e+00 -1.475000000000001243e+01 5.900000000000000000e+01 8.267053539449024768e+00 -1.480000000000001137e+01 7.200000000000000000e+01 8.223142248411562605e+00 -1.485000000000001208e+01 6.500000000000000000e+01 8.196608198079069041e+00 -1.490000000000001279e+01 7.100000000000000000e+01 8.188144068958164823e+00 -1.495000000000001172e+01 8.600000000000000000e+01 8.197036118232988500e+00 -1.500000000000001243e+01 7.300000000000000000e+01 8.221256566568779789e+00 -1.505000000000001315e+01 5.300000000000000000e+01 8.257697432954218542e+00 -1.510000000000001208e+01 8.800000000000000000e+01 8.302506382866150503e+00 -1.515000000000001279e+01 7.300000000000000000e+01 8.351473069632898927e+00 -1.520000000000001350e+01 6.100000000000000000e+01 8.400414678766409793e+00 -1.525000000000001243e+01 6.400000000000000000e+01 8.445518925934434762e+00 -1.530000000000001315e+01 6.200000000000000000e+01 8.483616572756162100e+00 -1.535000000000001208e+01 8.200000000000000000e+01 8.512369112243002078e+00 -1.540000000000001279e+01 8.500000000000000000e+01 8.530368061403875046e+00 -1.545000000000001350e+01 8.200000000000000000e+01 8.537149669128595519e+00 -1.550000000000001243e+01 7.400000000000000000e+01 8.533133455003650170e+00 -1.555000000000001315e+01 6.200000000000000000e+01 8.519495959118085437e+00 -1.560000000000001208e+01 8.600000000000000000e+01 8.497993418217598460e+00 -1.565000000000001279e+01 6.100000000000000000e+01 8.470749388802742530e+00 -1.570000000000001350e+01 7.000000000000000000e+01 8.440025680544451347e+00 -1.575000000000001243e+01 5.800000000000000000e+01 8.407996898107732875e+00 -1.580000000000001315e+01 5.400000000000000000e+01 8.376549603014964873e+00 -1.585000000000001386e+01 6.800000000000000000e+01 8.347125701768829842e+00 -1.590000000000001279e+01 8.200000000000000000e+01 8.320625543276104708e+00 -1.595000000000001350e+01 8.500000000000000000e+01 8.297379429179359889e+00 -1.600000000000001421e+01 6.800000000000000000e+01 8.277187686741289241e+00 -1.605000000000001137e+01 7.100000000000000000e+01 8.259420655653059384e+00 -1.610000000000001563e+01 5.300000000000000000e+01 8.243162603784146114e+00 -1.615000000000001279e+01 6.600000000000000000e+01 8.227379030012345495e+00 -1.620000000000001350e+01 7.400000000000000000e+01 8.211085576781343320e+00 -1.625000000000001421e+01 6.000000000000000000e+01 8.193498580831027667e+00 -1.630000000000001137e+01 6.600000000000000000e+01 8.174151297132215888e+00 -1.635000000000001563e+01 5.500000000000000000e+01 8.152965028991568275e+00 -1.640000000000001279e+01 7.000000000000000000e+01 8.130269934275649035e+00 -1.645000000000001350e+01 5.700000000000000000e+01 8.106775609030409058e+00 -1.650000000000001421e+01 5.400000000000000000e+01 8.083496410236294949e+00 -1.655000000000001137e+01 6.500000000000000000e+01 8.061640743783430096e+00 -1.660000000000001563e+01 6.700000000000000000e+01 8.042477064031196221e+00 -1.665000000000001279e+01 7.500000000000000000e+01 8.027191829442710258e+00 -1.670000000000001350e+01 8.000000000000000000e+01 8.016755727074528437e+00 -1.675000000000001421e+01 6.500000000000000000e+01 8.011813702718709962e+00 -1.680000000000001492e+01 5.800000000000000000e+01 8.012611498800907839e+00 -1.685000000000001563e+01 5.100000000000000000e+01 8.018966693391679001e+00 -1.690000000000001279e+01 6.500000000000000000e+01 8.030286313410492482e+00 -1.695000000000001350e+01 5.300000000000000000e+01 8.045626990275708934e+00 -1.700000000000001421e+01 6.200000000000000000e+01 8.063788446461787274e+00 -1.705000000000001492e+01 5.900000000000000000e+01 8.083427707923073413e+00 -1.710000000000001563e+01 5.500000000000000000e+01 8.103180212163584528e+00 -1.715000000000001279e+01 5.700000000000000000e+01 8.121774787737169987e+00 -1.720000000000001350e+01 5.800000000000000000e+01 8.138131800275919758e+00 -1.725000000000001421e+01 6.400000000000000000e+01 8.151436919186039631e+00 -1.730000000000001492e+01 5.900000000000000000e+01 8.161186334365957862e+00 -1.735000000000001563e+01 6.400000000000000000e+01 8.167202391196038747e+00 -1.740000000000001279e+01 5.500000000000000000e+01 8.169621259994075402e+00 -1.745000000000001350e+01 7.700000000000000000e+01 8.168856317691430391e+00 -1.750000000000001421e+01 6.700000000000000000e+01 8.165542393935401932e+00 -1.755000000000001492e+01 6.500000000000000000e+01 8.160466956272754757e+00 -1.760000000000001563e+01 5.800000000000000000e+01 8.154494713901854652e+00 -1.765000000000001634e+01 6.100000000000000000e+01 8.148492029864767616e+00 -1.770000000000001350e+01 6.100000000000000000e+01 8.143256970106071080e+00 -1.775000000000001421e+01 6.600000000000000000e+01 8.139459828533286867e+00 -1.780000000000001492e+01 7.200000000000000000e+01 8.137597636501475051e+00 -1.785000000000001563e+01 8.500000000000000000e+01 8.137964630880878403e+00 -1.790000000000001634e+01 6.400000000000000000e+01 8.140639096320997581e+00 -1.795000000000001350e+01 6.600000000000000000e+01 8.145485606347829588e+00 -1.800000000000001421e+01 5.100000000000000000e+01 8.152170629474078112e+00 -1.805000000000001492e+01 6.400000000000000000e+01 8.160188841602350251e+00 -1.810000000000001563e+01 6.900000000000000000e+01 8.168897310024872738e+00 -1.815000000000001634e+01 6.000000000000000000e+01 8.177554917570132531e+00 -1.820000000000001705e+01 7.200000000000000000e+01 8.185364844971692477e+00 -1.825000000000001421e+01 6.900000000000000000e+01 8.191518464074396988e+00 -1.830000000000001492e+01 7.600000000000000000e+01 8.195239461444527862e+00 -1.835000000000001563e+01 5.800000000000000000e+01 8.195827296816128538e+00 -1.840000000000001634e+01 5.800000000000000000e+01 8.192699143815733720e+00 -1.845000000000001705e+01 6.100000000000000000e+01 8.185429259836304539e+00 -1.850000000000001421e+01 7.300000000000000000e+01 8.173784337201112749e+00 -1.855000000000001492e+01 7.100000000000000000e+01 8.157752887025354838e+00 -1.860000000000001563e+01 7.400000000000000000e+01 8.137566213812911897e+00 -1.865000000000001634e+01 6.300000000000000000e+01 8.113708179554294020e+00 -1.870000000000001705e+01 5.400000000000000000e+01 8.086910861287075036e+00 -1.875000000000001421e+01 6.900000000000000000e+01 8.058133500003398453e+00 -1.880000000000001492e+01 5.300000000000000000e+01 8.028522925840768210e+00 -1.885000000000001563e+01 5.700000000000000000e+01 7.999354987087202495e+00 -1.890000000000001634e+01 7.400000000000000000e+01 7.971958396790059354e+00 -1.895000000000001705e+01 5.900000000000000000e+01 7.947624716446139104e+00 -1.900000000000001421e+01 6.800000000000000000e+01 7.927510654738507689e+00 -1.905000000000001492e+01 4.900000000000000000e+01 7.912541061789085006e+00 -1.910000000000001563e+01 6.800000000000000000e+01 7.903322448335442729e+00 -1.915000000000001634e+01 7.800000000000000000e+01 7.900077082789176863e+00 -1.920000000000001705e+01 7.200000000000000000e+01 7.902606424450356215e+00 -1.925000000000001776e+01 6.400000000000000000e+01 7.910289859490984732e+00 -1.930000000000001492e+01 6.200000000000000000e+01 7.922120794074880124e+00 -1.935000000000001563e+01 7.500000000000000000e+01 7.936777717671542831e+00 -1.940000000000001634e+01 5.500000000000000000e+01 7.952723163952562402e+00 -1.945000000000001705e+01 7.200000000000000000e+01 7.968316917582213499e+00 -1.950000000000001776e+01 6.200000000000000000e+01 7.981913200810064168e+00 -1.955000000000001492e+01 5.700000000000000000e+01 7.991861469542171470e+00 -1.960000000000001563e+01 5.200000000000000000e+01 7.996204911052299025e+00 -1.965000000000001634e+01 6.500000000000000000e+01 7.991653990818260667e+00 -1.970000000000001705e+01 7.500000000000000000e+01 7.971264500629853700e+00 -1.975000000000001776e+01 7.000000000000000000e+01 7.920666082970229560e+00 -1.980000000000001847e+01 6.500000000000000000e+01 7.814205482931140700e+00 -1.985000000000001563e+01 5.400000000000000000e+01 7.614470181335118326e+00 -1.990000000000001634e+01 5.500000000000000000e+01 7.279075440319572543e+00 -1.995000000000001705e+01 4.600000000000000000e+01 6.775257558953786230e+00 From ea58205e0d703029489545d93070ca45e17a4cc0 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 6 Aug 2026 14:46:53 -0400 Subject: [PATCH 10/11] tidy a test up --- tests/test_fitrecipe.py | 68 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 6101ed5d..3b51b857 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -906,47 +906,45 @@ def test_plot_recipe_labels_shared_axes(build_recipe_two_contributions): assert actual_labels == expected_labels -# The legend of each figure describes the curves of the contribution it -# shows. The cases below cover the labels a recipe of two contributions -# produces when it is plotted on one figure per contribution. -@pytest.mark.parametrize( - "contributions_without_ycalc, plot_kwargs, expected_labels", - [ - # C1: A label contains the {contribution} placeholder. - # Expected: The placeholder is replaced by the contribution name. - ( - [], - { - "fit_label": "{contribution} calculated", - "show_observed": False, - "show_diff": False, - }, - [["c1 calculated"], ["c2 calculated"]], - ), - # C2: Only the second contribution has been evaluated, so the ycalc - # of the first one is None. - # Expected: The unevaluated contribution shows observed data only - # while the other one is still plotted in full. - ( - ["c1"], - {}, - [["Observed"], ["Observed", "Calculated", "Difference"]], - ), - ], -) -def test_plot_recipe_labels( +def test_plot_recipe_label_contribution_placeholder( build_recipe_two_contributions, - contributions_without_ycalc, - plot_kwargs, - expected_labels, ): + # Case: User passes a label containing the {contribution} placeholder. + # Expected: The placeholder is replaced by the contribution name and no + # prefix is added. recipe = build_recipe_two_contributions optimize_recipe(recipe) plt.close("all") - for name in contributions_without_ycalc: - recipe._contributions[name].profile.ycalc = None - _, axes = recipe.plot_recipe(show=False, return_fig=True, **plot_kwargs) + _, axes = recipe.plot_recipe( + fit_label="{contribution} calculated", + show_observed=False, + show_diff=False, + show=False, + return_fig=True, + ) actual_labels = [get_labels_and_linecount(ax)[0] for ax in axes] + expected_labels = [["c1 calculated"], ["c2 calculated"]] + assert actual_labels == expected_labels + + +def test_plot_recipe_missing_ycalc_one_contribution( + build_recipe_two_contributions, +): + # Case: Only the second contribution has been evaluated, so the ycalc of + # the first one is None. + # Expected: The unevaluated contribution shows observed data only while + # the other one is still plotted in full. + recipe = build_recipe_two_contributions + optimize_recipe(recipe) + plt.close("all") + # manually set ycalc to None + recipe._contributions["c1"].profile.ycalc = None + _, axes = recipe.plot_recipe(show=False, return_fig=True) + actual_labels = [get_labels_and_linecount(ax)[0] for ax in axes] + expected_labels = [ + ["Observed"], + ["Observed", "Calculated", "Difference"], + ] assert actual_labels == expected_labels From 1ec0fb7873641d0e376ebff4713b3b3d24abd859 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 7 Aug 2026 08:51:39 -0400 Subject: [PATCH 11/11] empty commit for CI