nxs-analysis-tools 0.1.5__tar.gz → 0.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nxs-analysis-tools might be problematic. Click here for more details.

Files changed (28) hide show
  1. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/PKG-INFO +1 -1
  2. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/pyproject.toml +2 -2
  3. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/src/_meta/__init__.py +1 -1
  4. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/src/nxs_analysis_tools/chess.py +105 -6
  5. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/src/nxs_analysis_tools/fitting.py +51 -9
  6. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/LICENSE +0 -0
  7. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/MANIFEST.in +0 -0
  8. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/README.md +0 -0
  9. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/setup.cfg +0 -0
  10. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/setup.py +0 -0
  11. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/src/nxs_analysis_tools/__init__.py +0 -0
  12. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/src/nxs_analysis_tools/datareduction.py +0 -0
  13. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/src/nxs_analysis_tools/pairdistribution.py +0 -0
  14. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/src/nxs_analysis_tools.egg-info/SOURCES.txt +0 -0
  15. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_accurate_highlight.py +0 -0
  16. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_chess.py +0 -0
  17. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_chess_fitting.py +0 -0
  18. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_datareduction.py +0 -0
  19. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_ellipsoidal_window.py +0 -0
  20. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_fitting.py +0 -0
  21. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_lmfit.py +0 -0
  22. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_mask_plotting.py +0 -0
  23. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_pairdistribution.py +0 -0
  24. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_plot_slice_axes_types.py +0 -0
  25. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_plot_slice_with_ndarray.py +0 -0
  26. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_rotate_data.py +0 -0
  27. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_sum_axis.py +0 -0
  28. {nxs_analysis_tools-0.1.5 → nxs_analysis_tools-0.1.6}/tests/test_symmetrizer_rectangular_plane.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nxs-analysis-tools
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Reduce and transform nexus format (.nxs) scattering data.
5
5
  Author-email: "Steven J. Gomez Alvarado" <stevenjgomez@ucsb.edu>
6
6
  License-Expression: MIT
@@ -6,7 +6,7 @@ build-backend = 'setuptools.build_meta'
6
6
 
7
7
  [project]
8
8
  name = 'nxs-analysis-tools'
9
- version = '0.1.5'
9
+ version = '0.1.6'
10
10
  description = 'Reduce and transform nexus format (.nxs) scattering data.'
11
11
  readme = 'README.md'
12
12
  requires-python = '>=3.7'
@@ -71,7 +71,7 @@ dev = [
71
71
  'DOI' = 'https://doi.org/10.5281/zenodo.15186359'
72
72
 
73
73
  [tool.bumpver]
74
- current_version = "0.1.5"
74
+ current_version = "0.1.6"
75
75
  version_pattern = "MAJOR.MINOR.PATCH[-TAG]"
76
76
  tag_pattern = "vMAJOR.MINOR.PATCH[-TAG]"
77
77
  commit_message = "Bump version {old_version} -> {new_version}"
@@ -6,5 +6,5 @@ __author__ = 'Steven J. Gomez Alvarado'
6
6
  __email__ = 'stevenjgomez@ucsb.edu'
7
7
  __copyright__ = f"2023-2025, {__author__}"
8
8
  __license__ = 'MIT'
9
- __version__ = '0.1.5'
9
+ __version__ = '0.1.6'
10
10
  __repo_url__ = 'https://github.com/stevenjgomez/nxs_analysis_tools'
@@ -14,6 +14,7 @@ from IPython.display import display, Markdown
14
14
  from nxs_analysis_tools import load_data, Scissors
15
15
  from nxs_analysis_tools.fitting import LinecutModel
16
16
  from nxs_analysis_tools.datareduction import load_transform, reciprocal_lattice_params
17
+ from lmfit.models import PseudoVoigtModel, LinearModel
17
18
 
18
19
 
19
20
  class TempDependence:
@@ -98,7 +99,11 @@ class TempDependence:
98
99
  Fit the line cut models for each temperature.
99
100
  plot_fit(mdheadings=False, **kwargs):
100
101
  Plot the fit results for each temperature.
101
- plot_order_parameter(self):
102
+ overlay_fits(numpoints=None, vertical_offset=0, cmap='viridis', ax=ax):
103
+ Plot raw data and fitted models for each temperature.
104
+ fit_peak_simple():
105
+ Perform a basic fit using a pseudo-Voigt peak shape, linear background, and no constraints.
106
+ plot_order_parameter():
102
107
  Plot the temperature dependence of the peakheight parameter.
103
108
  print_fit_report():
104
109
  Print the fit report for each temperature.
@@ -469,7 +474,7 @@ class TempDependence:
469
474
  y = np.array([int(t) for t in self.temperatures])
470
475
 
471
476
  # Collect counts from each temperature and ensure they are numpy arrays
472
- v = [self.linecuts[T].counts.nxdata for T in self.temperatures]
477
+ v = [self.linecuts[T].nxsignal.nxdata for T in self.temperatures]
473
478
 
474
479
  # Convert list of arrays to a 2D array for the heatmap
475
480
  v_2d = np.array(v)
@@ -548,7 +553,7 @@ class TempDependence:
548
553
 
549
554
  Parameters
550
555
  ----------
551
- model_components : Model or iterable of Model
556
+ model_components : Model, CompositeModel, or iterable of Model
552
557
  The model components to set for all line cut models.
553
558
 
554
559
  """
@@ -561,7 +566,8 @@ class TempDependence:
561
566
 
562
567
  This method sets the parameter hints for all line cut models in the analysis.
563
568
  It iterates over each line cut model and calls their respective `set_param_hint` method
564
- with the provided arguments and keyword arguments.
569
+ with the provided arguments and keyword arguments. These are implemented when the
570
+ .make_params() method is called.
565
571
 
566
572
  Parameters
567
573
  ----------
@@ -573,10 +579,40 @@ class TempDependence:
573
579
  """
574
580
  [linecutmodel.set_param_hint(*args, **kwargs)
575
581
  for linecutmodel in self.linecutmodels.values()]
582
+
583
+ def params_set(self, name, **kwargs):
584
+ """
585
+ Set constraints on a parameter for all line cut models.
586
+
587
+ This method updates the specified parameter across all models in
588
+ `self.linecutmodels` using the keyword arguments provided. These
589
+ keyword arguments are passed to the `set()` method of the parameter,
590
+ which comes from a `lmfit.Parameters` object.
591
+
592
+ Parameters
593
+ ----------
594
+ name : str
595
+ Name of the parameter to modify (must exist in each model).
596
+ **kwargs
597
+ Constraint arguments passed to `Parameter.set()`, such as `value`,
598
+ `min`, `max`, `vary`, etc.
599
+
600
+ Raises
601
+ ------
602
+ KeyError
603
+ If the parameter `name` does not exist in one of the models.
604
+
605
+ Example
606
+ -------
607
+ >>> sample.params_set('peakamplitude', value=5, min=0, vary=True)
608
+ """
609
+
610
+ for linecutmodel in self.linecutmodels.values():
611
+ linecutmodel.params[name].set(**kwargs)
576
612
 
577
613
  def make_params(self):
578
614
  """
579
- Make parameters for all line cut models.
615
+ Create and initialize the parameters for all models.
580
616
 
581
617
  This method creates the parameters for all line cut models in the analysis.
582
618
  It iterates over each line cut model and calls their respective `make_params` method.
@@ -585,7 +621,8 @@ class TempDependence:
585
621
 
586
622
  def guess(self):
587
623
  """
588
- Make initial parameter guesses for all line cut models.
624
+ Make initial parameter guesses for all line cut models. This overwrites any prior initial
625
+ values and constraints.
589
626
 
590
627
  This method generates initial parameter guesses for all line cut models in the analysis.
591
628
  It iterates over each line cut model and calls their respective `guess` method.
@@ -657,6 +694,68 @@ class TempDependence:
657
694
  title=f"{T} K",
658
695
  **kwargs)
659
696
 
697
+ def overlay_fits(self, numpoints=None, vertical_offset=0, cmap='viridis', ax=None):
698
+ """
699
+ Plot raw data and fitted models for each temperature with optional vertical offsets.
700
+
701
+ Parameters:
702
+ -----------
703
+ numpoints : int or None, default=None
704
+ Number of points to evaluate for the fitted model curves.
705
+ If None, uses the number of raw data points for each linecut.
706
+ vertical_offset : float, default=0
707
+ Amount to vertically offset each linecut for clarity.
708
+ cmap : str, default='viridis'
709
+ Name of the matplotlib colormap used to distinguish different temperatures.
710
+ ax : matplotlib.axes.Axes or None, default=None
711
+ Axis object to plot on. If None, a new figure and axis are created.
712
+
713
+ The function:
714
+ - Uses a colormap to assign unique colors to each temperature.
715
+ - Plots raw data alongside evaluated fit models for each linecut.
716
+ - Vertically offsets each trace by a constant value for visual separation.
717
+ - Displays a legend in reverse order to match top-to-bottom visual stacking.
718
+ - Automatically labels the x- and y-axes based on NeXus-style data metadata.
719
+ """
720
+
721
+ # Create a figure and axes if an axis is not already provided
722
+ _, ax = plt.subplots() if ax is None else (None, ax)
723
+
724
+ # Generate a color palette for the various temperatures
725
+ cmap = plt.get_cmap(cmap)
726
+ colors = [cmap(i / len(self.temperatures)) for i, _ in enumerate(self.temperatures)]
727
+
728
+ for i, lm in enumerate(self.linecutmodels.values()):
729
+ # Plot the raw data
730
+ ax.plot(lm.x, lm.y + vertical_offset * i, '.', c=colors[i])
731
+
732
+ # Evaluate the fit
733
+ numpoints = len(lm.x) if numpoints is None else numpoints
734
+ x_eval = np.linspace(lm.x.min(), lm.x.max(), numpoints)
735
+ y_eval = lm.modelresult.eval(x=x_eval)
736
+ ax.plot(x_eval, y_eval + vertical_offset * i, '-', c=colors[i], label=self.temperatures[i])
737
+
738
+ # Reverse legend entries to match top-to-bottom stacking
739
+ handles, labels = ax.get_legend_handles_labels()
740
+ ax.legend(handles[::-1], labels[::-1])
741
+
742
+ # Add axis labels
743
+ ax.set(xlabel=lm.data.nxaxes[0].nxname, ylabel=lm.data.nxsignal.nxname)
744
+
745
+ def fit_peak_simple(self):
746
+ """
747
+ Fit all linecuts in the temperature series using a pseudo-Voigt peak shape and linear
748
+ background, with no constraints.
749
+ """
750
+
751
+ for T in self.temperatures:
752
+ linecutmodel = self.linecutmodels[T]
753
+ linecutmodel.set_model_components([PseudoVoigtModel(prefix='peak'),
754
+ LinearModel(prefix='background')])
755
+ linecutmodel.make_params()
756
+ linecutmodel.guess()
757
+ linecutmodel.fit()
758
+
660
759
  def plot_order_parameter(self):
661
760
  """
662
761
  Plot the temperature dependence of the peak height (order parameter).
@@ -3,8 +3,9 @@ Module for fitting of linecuts using the lmfit package.
3
3
  """
4
4
 
5
5
  import operator
6
- from lmfit.model import Model
7
- from lmfit.model import CompositeModel
6
+ from lmfit import Parameters
7
+ from lmfit.model import Model, CompositeModel
8
+ from lmfit.models import PseudoVoigtModel, LinearModel
8
9
  import matplotlib.pyplot as plt
9
10
  import numpy as np
10
11
 
@@ -66,6 +67,8 @@ class LinecutModel:
66
67
  Fit the model to the data.
67
68
  plot_fit(self, numpoints=None, fit_report=True, **kwargs)
68
69
  Plot the fitted model.
70
+ fit_peak_simple():
71
+ Perform a basic fit using a pseudo-Voigt peak shape, linear background, and no constraints.
69
72
  print_fit_report(self)
70
73
  Print the fit report.
71
74
  """
@@ -110,15 +113,25 @@ class LinecutModel:
110
113
 
111
114
  Parameters
112
115
  ----------
113
- model_components : Model or list of Models
114
- The model component(s) to be used for fitting,
115
- which will be combined into a CompositeModel.
116
+ model_components : Model, CompositeModel, or iterable of Model
117
+ The model component(s) to be used for fitting.
116
118
  """
117
119
 
118
120
  # If the model only has one component, then use it as the model
119
121
  if isinstance(model_components, Model):
120
122
  self.model = model_components
121
- # Else, combine the components into a composite model and use that as the
123
+ self.params = self.model.make_params()
124
+
125
+ # If the model is a composite model, then use it as the model
126
+ elif isinstance(model_components, CompositeModel):
127
+ self.model = model_components
128
+ self.model_components = self.model.components
129
+ # Make params for each component of the model
130
+ self.params = Parameters()
131
+ for component in self.model.components:
132
+ self.params.update(component.make_params())
133
+
134
+ # Else, combine the components into a composite model and use that as the model
122
135
  else:
123
136
  self.model_components = model_components
124
137
  self.model = model_components[0]
@@ -127,9 +140,15 @@ class LinecutModel:
127
140
  for component in model_components[1:]:
128
141
  self.model = CompositeModel(self.model, component, operator.add)
129
142
 
143
+ # Make params for each component of the model
144
+ self.params = Parameters()
145
+ for component in self.model.components:
146
+ self.params.update(component.make_params())
147
+
130
148
  def set_param_hint(self, *args, **kwargs):
131
149
  """
132
- Set parameter hints for the model.
150
+ Set parameter hints for the model. These are implemented when the .make_params() method
151
+ is called.
133
152
 
134
153
  Parameters
135
154
  ----------
@@ -159,10 +178,22 @@ class LinecutModel:
159
178
 
160
179
  def guess(self):
161
180
  """
162
- Perform initial guesses for each model component.
181
+ Perform initial guesses for each model component and update params. This overwrites any
182
+ prior initial values and constraints.
183
+
184
+ Returns
185
+ -------
186
+ components_params : list
187
+ A list containing params objects for each component of the model.
163
188
  """
164
- for model_component in list(self.model_components):
189
+
190
+ components_params = []
191
+
192
+ for model_component in self.model.components:
165
193
  self.params.update(model_component.guess(self.y, x=self.x))
194
+ components_params.append(model_component.guess(self.y, x=self.x))
195
+
196
+ return components_params
166
197
 
167
198
  def print_initial_params(self):
168
199
  """
@@ -251,6 +282,17 @@ class LinecutModel:
251
282
  if fit_report:
252
283
  print(self.modelresult.fit_report())
253
284
  return ax
285
+
286
+ def fit_peak_simple(self):
287
+ """
288
+ Fit all linecuts in the temperature series using a pseudo-Voigt peak shape and linear
289
+ background, with no constraints.
290
+ """
291
+ self.set_model_components([PseudoVoigtModel(prefix='peak'),
292
+ LinearModel(prefix='background')])
293
+ self.make_params()
294
+ self.guess()
295
+ self.fit()
254
296
 
255
297
  def print_fit_report(self):
256
298
  """