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/__init__.py +2 -0
- CHAP/common/__init__.py +7 -2
- CHAP/common/models/map.py +95 -70
- CHAP/common/processor.py +844 -153
- CHAP/common/reader.py +168 -131
- CHAP/common/writer.py +166 -96
- CHAP/edd/__init__.py +2 -0
- CHAP/edd/models.py +94 -48
- CHAP/edd/processor.py +625 -169
- CHAP/edd/utils.py +186 -6
- CHAP/pipeline.py +35 -3
- CHAP/runner.py +40 -13
- CHAP/tomo/models.py +18 -9
- CHAP/tomo/processor.py +1134 -902
- CHAP/utils/fit.py +98 -45
- CHAP/utils/general.py +196 -63
- CHAP/utils/scanparsers.py +403 -94
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/METADATA +1 -1
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/RECORD +23 -23
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/WHEEL +1 -1
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/LICENSE +0 -0
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/entry_points.txt +0 -0
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/top_level.txt +0 -0
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=[],
|
|
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(
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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(**
|
|
239
|
+
item.execute(**args)[0]
|
|
208
240
|
else:
|
|
209
|
-
data.append(item.execute(**
|
|
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
|
-
'
|
|
20
|
+
'inputdir': '.',
|
|
21
|
+
'outputdir': '.',
|
|
21
22
|
'interactive': False,
|
|
22
23
|
'log_level': 'INFO',
|
|
23
|
-
'
|
|
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
|
|
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
|
|
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(
|
|
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 = {'
|
|
147
|
-
'
|
|
148
|
-
'
|
|
149
|
+
kwargs = {'inputdir': inputdir,
|
|
150
|
+
'outputdir': outputdir,
|
|
151
|
+
'interactive': interactive}
|
|
149
152
|
if isinstance(item, dict):
|
|
150
153
|
name = list(item.keys())[0]
|
|
151
|
-
|
|
152
|
-
#
|
|
153
|
-
|
|
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
|
|
74
|
-
in pixels
|
|
75
|
-
:type
|
|
76
|
-
:ivar
|
|
77
|
-
in pixels
|
|
78
|
-
:type
|
|
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
|
-
|
|
94
|
-
|
|
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
|
|