ChessAnalysisPipeline 0.0.12__py3-none-any.whl → 0.0.14__py3-none-any.whl

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

Potentially problematic release.


This version of ChessAnalysisPipeline might be problematic. Click here for more details.

CHAP/edd/utils.py CHANGED
@@ -258,7 +258,8 @@ def select_tth_initial_guess(x, y, hkls, ds, tth_initial_guess=5.0,
258
258
 
259
259
  return fig, tth_new_guess
260
260
 
261
- def select_material_params(x, y, tth, materials=[], interactive=False):
261
+ def select_material_params(x, y, tth, materials=[], label='Reference Data',
262
+ interactive=False):
262
263
  """Interactively select the lattice parameters and space group for
263
264
  a list of materials. A matplotlib figure will be shown with a plot
264
265
  of the reference data (`x` and `y`). The figure will contain
@@ -276,6 +277,9 @@ def select_material_params(x, y, tth, materials=[], interactive=False):
276
277
  :type tth: float
277
278
  :param materials: Materials to get HKLs and lattice spacings for.
278
279
  :type materials: list[hexrd.material.Material]
280
+ :param label: Legend label for the 1D plot of reference MCA data
281
+ from the parameters `x`, `y`, defaults to `"Reference Data"`
282
+ :type label: str, optional
279
283
  :param interactive: Allows for user interactions, defaults to
280
284
  `False`.
281
285
  :type interactive: bool, optional
@@ -306,7 +310,7 @@ def select_material_params(x, y, tth, materials=[], interactive=False):
306
310
  ax.set_xlabel('MCA channel energy (keV)')
307
311
  ax.set_ylabel('MCA intensity (counts)')
308
312
  ax.set_xlim(x[0], x[-1])
309
- ax.plot(x, y)
313
+ ax.plot(x, y, label=label)
310
314
  for i, material in enumerate(_materials):
311
315
  hkls, ds = get_unique_hkls_ds([material])
312
316
  E0s = get_peak_locations(ds, tth)
@@ -316,6 +320,7 @@ def select_material_params(x, y, tth, materials=[], interactive=False):
316
320
  ax.text(E0, 1, str(hkl)[1:-1], c=f'C{i}',
317
321
  ha='right', va='top', rotation=90,
318
322
  transform=ax.get_xaxis_transform())
323
+ ax.legend()
319
324
  ax.get_figure().canvas.draw()
320
325
 
321
326
  def add_material(*args, material=None, new=True):
@@ -477,6 +482,8 @@ def select_material_params(x, y, tth, materials=[], interactive=False):
477
482
  for widget, callback in group:
478
483
  widget.disconnect(callback)
479
484
  widget.ax.remove()
485
+ else:
486
+ draw_plot()
480
487
 
481
488
  fig.tight_layout()
482
489
 
@@ -492,7 +499,7 @@ def select_material_params(x, y, tth, materials=[], interactive=False):
492
499
  def select_mask_and_hkls(x, y, hkls, ds, tth, preselected_bin_ranges=[],
493
500
  preselected_hkl_indices=[], detector_name=None, ref_map=None,
494
501
  flux_energy_range=None, calibration_bin_ranges=None,
495
- interactive=False):
502
+ label='Reference Data', interactive=False):
496
503
  """Return a matplotlib figure to indicate data ranges and HKLs to
497
504
  include for fitting in EDD Ceria calibration and/or strain
498
505
  analysis.
@@ -530,6 +537,9 @@ def select_mask_and_hkls(x, y, hkls, ds, tth, preselected_bin_ranges=[],
530
537
  :param interactive: Allows for user interactions, defaults to
531
538
  `False`.
532
539
  :type interactive: bool, optional
540
+ :param label: Legend label for the 1D plot of reference MCA data
541
+ from the parameters `x`, `y`, defaults to `"Reference Data"`
542
+ :type label: str, optional
533
543
  :return: A saveable matplotlib figure, the list of selected data
534
544
  index ranges to include, and the list of HKL indices to
535
545
  include
@@ -628,8 +638,27 @@ def select_mask_and_hkls(x, y, hkls, ds, tth, preselected_bin_ranges=[],
628
638
  elif removed_hkls:
629
639
  change_error_text(
630
640
  'Removed HKL(s) outside the selected energy mask')
641
+ # If using ref_map, update the colorbar range to min / max of
642
+ # the selected data only
643
+ if ref_map is not None:
644
+ selected_data = ref_map[:,get_mask()]
645
+ _min, _max = np.argmin(selected_data), np.argmax(selected_data)
646
+ ref_map_mappable = ax_map.pcolormesh(
647
+ x, np.arange(ref_map.shape[0]), ref_map, vmin=_min, vmax=_max)
648
+ fig.colorbar(ref_map_mappable, cax=cax)
631
649
  plt.draw()
632
650
 
651
+ def get_mask():
652
+ """Return a boolean array that acts as the mask corresponding
653
+ to the currently-selected index ranges"""
654
+ mask = np.full(x.shape[0], False)
655
+ bin_indices = np.arange(x.shape[0])
656
+ for span in spans:
657
+ _min, _max = span.extents
658
+ mask = np.logical_or(
659
+ mask, np.logical_and(bin_indices >= _min, bin_indices <= _max))
660
+ return mask
661
+
633
662
  def add_span(event, xrange_init=None):
634
663
  """Callback function for the "Add span" button."""
635
664
  spans.append(
@@ -693,6 +722,12 @@ def select_mask_and_hkls(x, y, hkls, ds, tth, preselected_bin_ranges=[],
693
722
  'extend or add spans before adding this value')
694
723
  plt.draw()
695
724
 
725
+ def position_cax():
726
+ """Reposition the colorbar axes according to the axes of the
727
+ reference map"""
728
+ ((left, bottom), (right, top)) = ax_map.get_position().get_points()
729
+ cax.set_position([right + 0.01, bottom, 0.01, top - bottom])
730
+
696
731
  def reset(event):
697
732
  """Callback function for the "Confirm" button."""
698
733
  for hkl_index in deepcopy(selected_hkl_indices):
@@ -755,14 +790,30 @@ def select_mask_and_hkls(x, y, hkls, ds, tth, preselected_bin_ranges=[],
755
790
  fig, ax = plt.subplots(figsize=(11, 8.5))
756
791
  ax.set(xlabel='Energy (keV)', ylabel='Intensity (counts)')
757
792
  else:
793
+ # Ensure ref_map is 2D
794
+ if ref_map.ndim > 2:
795
+ ref_map = np.reshape(
796
+ ref_map, (np.prod(ref_map.shape[:-1]), ref_map.shape[-1]))
797
+ # If needed, abbreviate ref_map to <= 50 spectra to keep
798
+ # response time of mouse interactions quick.
799
+ max_ref_spectra = 50
800
+ if ref_map.shape[0] > max_ref_spectra:
801
+ choose_i = np.sort(
802
+ np.random.choice(
803
+ ref_map.shape[0], max_ref_spectra, replace=False))
804
+ ref_map = ref_map[choose_i]
758
805
  fig, (ax, ax_map) = plt.subplots(
759
806
  2, sharex=True, figsize=(11, 8.5), height_ratios=[2, 1])
760
807
  ax.set(ylabel='Intensity (counts)')
761
- ax_map.pcolormesh(x, np.arange(ref_map.shape[0]), ref_map)
808
+ ref_map_mappable = ax_map.pcolormesh(
809
+ x, np.arange(ref_map.shape[0]), ref_map)
762
810
  ax_map.set_yticks([])
763
811
  ax_map.set_xlabel('Energy (keV)')
764
812
  ax_map.set_xlim(x[0], x[-1])
765
- handles = ax.plot(x, y, color='k', label='Reference Data')
813
+ ((left, bottom), (right, top)) = ax_map.get_position().get_points()
814
+ cax = plt.axes([right + 0.01, bottom, 0.01, top - bottom])
815
+ fig.colorbar(ref_map_mappable, cax=cax)
816
+ handles = ax.plot(x, y, color='k', label=label)
766
817
  if calibration_bin_ranges is not None:
767
818
  ylow = ax.get_ylim()[0]
768
819
  for low, upp in calibration_bin_ranges:
@@ -828,6 +879,8 @@ def select_mask_and_hkls(x, y, hkls, ds, tth, preselected_bin_ranges=[],
828
879
  change_fig_title(
829
880
  f'Select data and HKLs to use in fitting {detector_name}')
830
881
  fig.subplots_adjust(bottom=0.2)
882
+ if ref_map is not None:
883
+ position_cax()
831
884
 
832
885
  # Setup "Add span" button
833
886
  add_span_btn = Button(plt.axes([0.125, 0.05, 0.15, 0.075]), 'Add span')
@@ -870,6 +923,133 @@ def select_mask_and_hkls(x, y, hkls, ds, tth, preselected_bin_ranges=[],
870
923
  selected_hkl_indices = None
871
924
 
872
925
  fig_title[0].set_in_layout(True)
873
- fig.tight_layout(rect=(0, 0, 1, 0.95))
926
+ fig.tight_layout(rect=(0, 0, 0.9, 0.9))
927
+ if ref_map is not None:
928
+ position_cax()
874
929
 
875
930
  return fig, selected_bin_ranges, selected_hkl_indices
931
+
932
+
933
+ def get_spectra_fits(spectra, energies, peak_locations, fit_params):
934
+ """Return twenty arrays of fit results for the map of spectra
935
+ provided: uniform centers, uniform center errors, uniform
936
+ amplitudes, uniform amplitude errors, uniform sigmas, uniform
937
+ sigma errors, uniform best fit, uniform residuals, uniform reduced
938
+ chi, uniform success codes, unconstrained centers, unconstrained
939
+ center errors, unconstrained amplitudes, unconstrained amplitude
940
+ errors, unconstrained sigmas, unconstrained sigma errors,
941
+ unconstrained best fit, unconstrained residuals, unconstrained
942
+ reduced chi, and unconstrained success codes.
943
+
944
+ :param spectra: Array of intensity spectra to fit.
945
+ :type spectra: numpy.ndarray
946
+ :param energies: Bin energies for the spectra provided.
947
+ :type energies: numpy.ndarray
948
+ :param peak_locations: Initial guesses for peak ceneters to use
949
+ for the uniform fit.
950
+ :type peak_locations: list[float]
951
+ :param fit_params: Detector element fit parameters.
952
+ :type fit_params: CHAP.edd.models.MCAElementStrainAnalysisConfig
953
+ :returns: Uniform and unconstrained centers, amplitdues, sigmas
954
+ (and errors for all three), best fits, residuals between the
955
+ best fits and the input spectra, reduced chi, and fit success
956
+ statuses.
957
+ :rtype: tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray,
958
+ numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray,
959
+ numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray,
960
+ numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray,
961
+ numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray,
962
+ numpy.ndarray]
963
+ """
964
+ from CHAP.utils.fit import FitMap
965
+
966
+ # Perform fit to get measured peak positions
967
+ fit = FitMap(spectra, x=energies)
968
+ num_peak = len(peak_locations)
969
+ delta = 0.1 * (energies[-1]-energies[0])
970
+ centers_range = (
971
+ max(0.0, energies[0]-delta), energies[-1]+delta)
972
+ fit.create_multipeak_model(
973
+ peak_locations,
974
+ fit_type='uniform',
975
+ peak_models=fit_params.peak_models,
976
+ background=fit_params.background,
977
+ fwhm_min=fit_params.fwhm_min,
978
+ fwhm_max=fit_params.fwhm_max,
979
+ centers_range=centers_range)
980
+ fit.fit(num_proc=fit_params.num_proc)
981
+ uniform_fit_centers = [
982
+ fit.best_values[
983
+ fit.best_parameters().index(f'peak{i+1}_center')]
984
+ for i in range(num_peak)]
985
+ uniform_fit_centers_errors = [
986
+ fit.best_errors[
987
+ fit.best_parameters().index(f'peak{i+1}_center')]
988
+ for i in range(num_peak)]
989
+ uniform_fit_amplitudes = [
990
+ fit.best_values[
991
+ fit.best_parameters().index(f'peak{i+1}_amplitude')]
992
+ for i in range(num_peak)]
993
+ uniform_fit_amplitudes_errors = [
994
+ fit.best_errors[
995
+ fit.best_parameters().index(f'peak{i+1}_amplitude')]
996
+ for i in range(num_peak)]
997
+ uniform_fit_sigmas = [
998
+ fit.best_values[
999
+ fit.best_parameters().index(f'peak{i+1}_sigma')]
1000
+ for i in range(num_peak)]
1001
+ uniform_fit_sigmas_errors = [
1002
+ fit.best_errors[
1003
+ fit.best_parameters().index(f'peak{i+1}_sigma')]
1004
+ for i in range(num_peak)]
1005
+ uniform_best_fit = fit.best_fit
1006
+ uniform_residuals = fit.residual
1007
+ uniform_redchi = fit.redchi
1008
+ uniform_success = fit.success
1009
+
1010
+ # Perform unconstrained fit
1011
+ fit.create_multipeak_model(fit_type='unconstrained')
1012
+ fit.fit(num_proc=fit_params.num_proc,
1013
+ rel_amplitude_cutoff=fit_params.rel_amplitude_cutoff)
1014
+ unconstrained_fit_centers = np.array(
1015
+ [fit.best_values[
1016
+ fit.best_parameters()\
1017
+ .index(f'peak{i+1}_center')]
1018
+ for i in range(num_peak)])
1019
+ unconstrained_fit_centers_errors = np.array(
1020
+ [fit.best_errors[
1021
+ fit.best_parameters()\
1022
+ .index(f'peak{i+1}_center')]
1023
+ for i in range(num_peak)])
1024
+ unconstrained_fit_amplitudes = [
1025
+ fit.best_values[
1026
+ fit.best_parameters().index(f'peak{i+1}_amplitude')]
1027
+ for i in range(num_peak)]
1028
+ unconstrained_fit_amplitudes_errors = [
1029
+ fit.best_errors[
1030
+ fit.best_parameters().index(f'peak{i+1}_amplitude')]
1031
+ for i in range(num_peak)]
1032
+ unconstrained_fit_sigmas = [
1033
+ fit.best_values[
1034
+ fit.best_parameters().index(f'peak{i+1}_sigma')]
1035
+ for i in range(num_peak)]
1036
+ unconstrained_fit_sigmas_errors = [
1037
+ fit.best_errors[
1038
+ fit.best_parameters().index(f'peak{i+1}_sigma')]
1039
+ for i in range(num_peak)]
1040
+ unconstrained_best_fit = fit.best_fit
1041
+ unconstrained_residuals = fit.residual
1042
+ unconstrained_redchi = fit.redchi
1043
+ unconstrained_success = fit.success
1044
+
1045
+ return (
1046
+ uniform_fit_centers, uniform_fit_centers_errors,
1047
+ uniform_fit_amplitudes, uniform_fit_amplitudes_errors,
1048
+ uniform_fit_sigmas, uniform_fit_sigmas_errors,
1049
+ uniform_best_fit, uniform_residuals, uniform_redchi, uniform_success,
1050
+ unconstrained_fit_centers, unconstrained_fit_centers_errors,
1051
+ unconstrained_fit_amplitudes, unconstrained_fit_amplitudes_errors,
1052
+ unconstrained_fit_sigmas, unconstrained_fit_sigmas_errors,
1053
+ unconstrained_best_fit, unconstrained_residuals,
1054
+ unconstrained_redchi, unconstrained_success
1055
+ )
CHAP/pipeline.py CHANGED
@@ -113,7 +113,10 @@ class PipelineItem():
113
113
 
114
114
  mod_name, cls_name = schema.rsplit('.', 1)
115
115
  module = __import__(f'CHAP.{mod_name}', fromlist=cls_name)
116
- model_config = getattr(module, cls_name)(**matching_config, **kwargs)
116
+ model_kwargs = {k: v for k, v in kwargs.items() \
117
+ if k not in matching_config}
118
+ model_config = getattr(module, cls_name)(**matching_config,
119
+ **model_kwargs)
117
120
 
118
121
  self.logger.debug(
119
122
  f'Got {schema} configuration in {time()-t0:.3f} seconds')
@@ -184,6 +187,8 @@ class MultiplePipelineItem(PipelineItem):
184
187
  :type items: list
185
188
  :rtype: list[PipelineData]
186
189
  """
190
+ # System modules
191
+ from tempfile import NamedTemporaryFile
187
192
 
188
193
  t0 = time()
189
194
  self.logger.info(f'Executing {len(items)} PipelineItems')
@@ -203,10 +208,37 @@ class MultiplePipelineItem(PipelineItem):
203
208
  mod_name, cls_name = item_name.rsplit('.', 1)
204
209
  module = __import__(f'CHAP.{mod_name}', fromlist=cls_name)
205
210
  item = getattr(module, cls_name)()
211
+ # Combine the command line arguments "inputdir",
212
+ # "outputdir" and "interactive" with the item's arguments
213
+ # joining "inputdir" and "outputdir" and giving precedence
214
+ # for "interactive" in the latter
215
+ args = {**kwargs}
216
+ if 'inputdir' in item_args:
217
+ inputdir = os.path.normpath(os.path.join(
218
+ args['inputdir'], item_args.pop('inputdir')))
219
+ if not os.path.isdir(inputdir):
220
+ raise OSError(
221
+ f'input directory does not exist ({inputdir})')
222
+ if not os.access(inputdir, os.R_OK):
223
+ raise OSError('input directory is not accessible for '
224
+ f'reading ({inputdir})')
225
+ args['inputdir'] = inputdir
226
+ if 'outputdir' in item_args:
227
+ outputdir = os.path.normpath(os.path.join(
228
+ args['outputdir'], item_args.pop('outputdir')))
229
+ if not os.path.isdir(outputdir):
230
+ os.mkdir(outputdir)
231
+ try:
232
+ tmpfile = NamedTemporaryFile(dir=outputdir)
233
+ except:
234
+ raise OSError('output directory is not accessible for '
235
+ f'writing ({outputdir})')
236
+ args['outputdir'] = outputdir
237
+ args = {**args, **item_args}
206
238
  if hasattr(item, 'write'):
207
- item.execute(**item_args, **kwargs)[0]
239
+ item.execute(**args)[0]
208
240
  else:
209
- data.append(item.execute(**item_args, **kwargs)[0])
241
+ data.append(item.execute(**args)[0])
210
242
 
211
243
  self.logger.info(
212
244
  f'Finished executing {len(items)} PipelineItems in {time()-t0:.0f}'
CHAP/runner.py CHANGED
@@ -17,11 +17,11 @@ from CHAP.pipeline import Pipeline
17
17
  class RunConfig():
18
18
  """Representation of Pipeline run configuration."""
19
19
  opts = {'root': os.getcwd(),
20
- 'profile': False,
20
+ 'inputdir': '.',
21
+ 'outputdir': '.',
21
22
  'interactive': False,
22
23
  'log_level': 'INFO',
23
- 'inputdir': '.',
24
- 'outputdir': '.'}
24
+ 'profile': False}
25
25
 
26
26
  def __init__(self, config={}):
27
27
  """RunConfig constructor
@@ -42,7 +42,7 @@ class RunConfig():
42
42
  raise OSError('root directory is not accessible for reading '
43
43
  f'({self.root})')
44
44
 
45
- # Check if input exists and is readable
45
+ # Check if inputdir exists and is readable
46
46
  if not os.path.isabs(self.inputdir):
47
47
  self.inputdir = os.path.realpath(
48
48
  os.path.join(self.root, self.inputdir))
@@ -52,7 +52,7 @@ class RunConfig():
52
52
  raise OSError('input directory is not accessible for reading '
53
53
  f'({self.inputdir})')
54
54
 
55
- # Check if output exists (create it if not) and is writable
55
+ # Check if outputdir exists (create it if not) and is writable
56
56
  if not os.path.isabs(self.outputdir):
57
57
  self.outputdir = os.path.realpath(
58
58
  os.path.join(self.root, self.outputdir))
@@ -131,26 +131,53 @@ def setLogger(log_level="INFO"):
131
131
  logger.addHandler(log_handler)
132
132
  return logger, log_handler
133
133
 
134
- def run(pipeline_config,
135
- inputdir=None, outputdir=None, interactive=False,
134
+ def run(
135
+ pipeline_config, inputdir=None, outputdir=None, interactive=False,
136
136
  logger=None, log_level=None, log_handler=None):
137
137
  """
138
138
  Run given pipeline_config
139
139
 
140
140
  :param pipeline_config: CHAP pipeline config
141
141
  """
142
+ # System modules
143
+ from tempfile import NamedTemporaryFile
144
+
142
145
  objects = []
143
146
  kwds = []
144
147
  for item in pipeline_config:
145
148
  # load individual object with given name from its module
146
- kwargs = {'interactive': interactive,
147
- 'inputdir': inputdir,
148
- 'outputdir': outputdir}
149
+ kwargs = {'inputdir': inputdir,
150
+ 'outputdir': outputdir,
151
+ 'interactive': interactive}
149
152
  if isinstance(item, dict):
150
153
  name = list(item.keys())[0]
151
- # Combine the "interactive" command line argument with the object's keywords
152
- # giving precedence of "interactive" in the latter
153
- kwargs = {**kwargs, **item[name]}
154
+ item_args = item[name]
155
+ # Combine the function's input arguments "inputdir",
156
+ # "outputdir" and "interactive" with the item's arguments
157
+ # joining "inputdir" and "outputdir" and giving precedence
158
+ # for "interactive" in the latter
159
+ if 'inputdir' in item_args:
160
+ newinputdir = os.path.normpath(os.path.join(
161
+ kwargs['inputdir'], item_args.pop('inputdir')))
162
+ if not os.path.isdir(newinputdir):
163
+ raise OSError(
164
+ f'input directory does not exist ({newinputdir})')
165
+ if not os.access(newinputdir, os.R_OK):
166
+ raise OSError('input directory is not accessible for '
167
+ f'reading ({newinputdir})')
168
+ kwargs['inputdir'] = newinputdir
169
+ if 'outputdir' in item_args:
170
+ newoutputdir = os.path.normpath(os.path.join(
171
+ kwargs['outputdir'], item_args.pop('outputdir')))
172
+ if not os.path.isdir(newoutputdir):
173
+ os.mkdir(newoutputdir)
174
+ try:
175
+ tmpfile = NamedTemporaryFile(dir=newoutputdir)
176
+ except:
177
+ raise OSError('output directory is not accessible for '
178
+ f'writing ({newoutputdir})')
179
+ kwargs['outputdir'] = newoutputdir
180
+ kwargs = {**kwargs, **item_args}
154
181
  else:
155
182
  name = item
156
183
  if "users" in name:
CHAP/tomo/models.py CHANGED
@@ -46,7 +46,8 @@ class TomoReduceConfig(BaseModel):
46
46
  Class representing the configuration for the tomography image
47
47
  reduction processor.
48
48
 
49
- :ivar img_row_bounds: Detector image bounds in the row-direction.
49
+ :ivar img_row_bounds: Detector image bounds in the row-direction
50
+ (ignored for id1a3 and id3a).
50
51
  :type img_row_bounds: list[int], optional
51
52
  :ivar delta_theta: Rotation angle increment in image reduction
52
53
  in degrees.
@@ -70,12 +71,12 @@ class TomoFindCenterConfig(BaseModel):
70
71
  :ivar center_offsets: Centers at the center finding row indices in
71
72
  pixels.
72
73
  :type center_offsets: list[float, float], optional
73
- :ivar search_range: Search range to perform center finding search
74
- in pixels
75
- :type search_range: float, optional
76
- :ivar search_step: Search step size in the center finding search
77
- in pixels
78
- :type search_step: float, optional
74
+ :ivar center_offset_min: Minimum value of center_offset in center
75
+ axis finding search in pixels.
76
+ :type center_offset_min: float, optional
77
+ :ivar center_offset_max: Maximum value of center_offset in center
78
+ axis finding search in pixels.
79
+ :type center_offset_max: float, optional
79
80
  :ivar gaussian_sigma: Standard deviation for the Gaussian filter
80
81
  applied to image reconstruction visualizations, defaults to no
81
82
  filtering performed.
@@ -90,8 +91,11 @@ class TomoFindCenterConfig(BaseModel):
90
91
  center_offsets: Optional[conlist(
91
92
  item_type=confloat(allow_inf_nan=False),
92
93
  min_items=2, max_items=2)]
93
- search_range: Optional[confloat(ge=0, allow_inf_nan=False)]
94
- search_step: Optional[confloat(ge=0, allow_inf_nan=False)]
94
+ center_offset_min: Optional[confloat(allow_inf_nan=False)]
95
+ center_offset_max: Optional[confloat(allow_inf_nan=False)]
96
+ center_search_range: Optional[conlist(
97
+ item_type=confloat(allow_inf_nan=False),
98
+ min_items=1, max_items=3)]
95
99
  gaussian_sigma: Optional[confloat(ge=0, allow_inf_nan=False)]
96
100
  ring_width: Optional[confloat(ge=0, allow_inf_nan=False)]
97
101
 
@@ -110,6 +114,10 @@ class TomoReconstructConfig(BaseModel):
110
114
  :ivar secondary_iters: Number of secondary iterations in the tomopy
111
115
  image reconstruction algorithm, defaults to 0.
112
116
  :type secondary_iters: int, optional
117
+ :ivar gaussian_sigma: Standard deviation for the Gaussian filter
118
+ applied to image reconstruction visualizations, defaults to no
119
+ filtering performed.
120
+ :type gaussian_sigma: float, optional
113
121
  :ivar remove_stripe_sigma: Damping parameter in Fourier space in
114
122
  tomopy's horizontal stripe removal tool, defaults to no
115
123
  correction performed.
@@ -125,6 +133,7 @@ class TomoReconstructConfig(BaseModel):
125
133
  z_bounds: Optional[
126
134
  conlist(item_type=conint(ge=-1), min_items=2, max_items=2)]
127
135
  secondary_iters: conint(ge=0) = 0
136
+ gaussian_sigma: Optional[confloat(ge=0, allow_inf_nan=False)]
128
137
  remove_stripe_sigma: Optional[confloat(ge=0, allow_inf_nan=False)]
129
138
  ring_width: Optional[confloat(ge=0, allow_inf_nan=False)]
130
139