Skip to content

Commit

Permalink
Merge pull request #884 from neutrons/add_background_peakshape
Browse files Browse the repository at this point in the history
Add background peakshape
  • Loading branch information
fanchercm authored Oct 4, 2024
2 parents b277f1f + 52bcf3e commit c204bb4
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 86 deletions.
57 changes: 19 additions & 38 deletions pyrs/core/peak_profile_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@


# Effective peak and background parameters
EFFECTIVE_PEAK_PARAMETERS = ['Center', 'Height', 'FWHM', 'Mixing', 'A0', 'A1', 'Intensity']
EFFECTIVE_PEAK_PARAMETERS = ['Center', 'Height', 'FWHM', 'Mixing', 'Intensity', 'A0', 'A1', 'A2']


class PeakShape(Enum):
GAUSSIAN = 'Gaussian'
PSEUDOVOIGT = 'PseudoVoigt'
VOIGT = 'Voigt'

def __str__(self):
return self.value
Expand All @@ -35,14 +34,14 @@ def getShape(shape):
def native_parameters(self):
# Native peak parameters in Mantid naming convention
NATIVE_PEAK_PARAMETERS = {'Gaussian': ['Height', 'PeakCentre', 'Sigma'],
'PseudoVoigt': ['Mixing', 'Intensity', 'PeakCentre', 'FWHM'],
'Voigt': ['LorentzAmp', 'LorentzPos', 'LorentzFWHM', 'GaussianFWHM']}
'PseudoVoigt': ['Mixing', 'Intensity', 'PeakCentre', 'FWHM']}

return NATIVE_PEAK_PARAMETERS[self.value][:]


class BackgroundFunction(Enum):
LINEAR = 'Linear' # so far, one and only supported
QUADRATIC = 'Quadratic' # so far, one and only supported

def __str__(self):
return self.value
Expand All @@ -64,7 +63,8 @@ def getFunction(function):
@property
def native_parameters(self):
# Native background parameters in Mantid naming convention
NATIVE_BACKGROUND_PARAMETERS = {'Linear': ['A0', 'A1']}
NATIVE_BACKGROUND_PARAMETERS = {'Linear': ['A0', 'A1'],
'Quadratic': ['A0', 'A1', 'A2']}

return NATIVE_BACKGROUND_PARAMETERS[self.value][:]

Expand Down Expand Up @@ -106,8 +106,6 @@ def get_effective_parameters_converter(peak_profile):
converter = Gaussian()
elif peak_profile == PeakShape.PSEUDOVOIGT:
converter = PseudoVoigt()
elif peak_profile == PeakShape.VOIGT:
converter = Voigt()
else:
raise RuntimeError('if/else tree is incomplete')

Expand Down Expand Up @@ -223,6 +221,13 @@ def calculate_effective_parameters(self, param_value_array, param_error_array):
eff_error_array['A1'] = param_error_array['A1'] # A1
eff_error_array['Intensity'] = intensity_error_array[:] # intensity

try:
eff_value_array['A2'] = param_value_array['A2'] # A2
eff_error_array['A2'] = param_error_array['A2'] # A2
except ValueError:
eff_value_array['A2'] = np.zeros_like(param_value_array['A1']) # A2
eff_error_array['A2'] = np.zeros_like(param_value_array['A1']) + 0.01 # A2

return eff_value_array, eff_error_array

@staticmethod
Expand Down Expand Up @@ -396,6 +401,13 @@ def calculate_effective_parameters(self, param_value_array, param_error_array):
eff_error_array['A1'] = param_error_array['A1'] # A1
eff_error_array['Intensity'] = param_error_array['Intensity'] # intensity

try:
eff_value_array['A2'] = param_value_array['A2'] # A2
eff_error_array['A2'] = param_error_array['A2'] # A2
except ValueError:
eff_value_array['A2'] = np.zeros_like(param_value_array['A1']) # A2
eff_error_array['A2'] = np.zeros_like(param_value_array['A1']) + 0.01 # A2

return eff_value_array, eff_error_array

@staticmethod
Expand Down Expand Up @@ -496,37 +508,6 @@ def cal_intensity(height, fwhm, mixing):
return intensity


class Voigt(PeakParametersConverter):
"""
class for handling peak profile parameters' conversion
"""
def __init__(self):
super(Voigt, self).__init__(PeakShape.VOIGT)

def calculate_effective_parameters(self, param_value_array, param_error_array):
"""Calculate effective peak parameter values
If input parameter values include fitting error, then this method will calculate
the propagation of error
Native PseudoVoigt: ['Mixing', 'Intensity', 'PeakCentre', 'FWHM']
Parameters
----------
native_param_names: list or None
param_value_array : numpy.ndarray
(p, n, 1) or (p, n, 2) vector for parameter values and optionally fitting error
p = number of native parameters , n = number of sub runs
param_error_array : numpy.ndarray
Returns
-------
np.ndarray
(p', n, 1) or (p', n, 2) array for parameter values and optionally fitting error
p' = number of effective parameters , n = number of sub runs
"""
raise NotImplementedError('Somebody should write this')


"""
From here are a list of static method of peak profiles
"""
Expand Down
12 changes: 1 addition & 11 deletions pyrs/interface/designer/peakfitwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,6 @@
<string>Gaussian</string>
</property>
</item>
<item>
<property name="text">
<string>Voigt</string>
</property>
</item>
</widget>
</item>
<item>
Expand All @@ -412,11 +407,6 @@
<string>Linear</string>
</property>
</item>
<item>
<property name="text">
<string>Flat</string>
</property>
</item>
<item>
<property name="text">
<string>Quadratic</string>
Expand Down Expand Up @@ -942,7 +932,7 @@
<x>0</x>
<y>0</y>
<width>1536</width>
<height>23</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
Expand Down
4 changes: 3 additions & 1 deletion pyrs/interface/peak_fitting/fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,18 @@ def fit_multi_peaks(self):
_peak_center_list = [np.mean([left, right]) for (left, right) in _peak_range_list]
_peak_tag_list = ["peak{}".format(_index) for _index, _ in enumerate(_peak_center_list)]
_peak_function_name = str(self.parent.ui.comboBox_peakType.currentText())
_peak_background_name = str(self.parent.ui.comboBox_backgroundType.currentText())

_peak_xmin_list = [left for (left, _) in _peak_range_list]
_peak_xmax_list = [right for (_, right) in _peak_range_list]

# Fit peak
hd_ws = self.parent.hidra_workspace

print(_peak_background_name)
_wavelength = hd_ws.get_wavelength(True, True)
fit_engine = PeakFitEngineFactory.getInstance(hd_ws,
_peak_function_name, 'Linear',
_peak_function_name, _peak_background_name,
wavelength=_wavelength)
fit_result = fit_engine.fit_multiple_peaks(_peak_tag_list,
_peak_xmin_list,
Expand Down
1 change: 1 addition & 0 deletions pyrs/interface/texture_fitting/texture_fitting_crtl.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def plot_2D_params(self, ax_object, xlabel, ylabel, peak_number, fit_object, out
fit_object=fit_object,
out_of_plane=out_of_plane)

print(xdata, ydata)
if isinstance(ydata[0], np.ndarray):
yerr = ydata[1]
ydata = ydata[0]
Expand Down
2 changes: 1 addition & 1 deletion pyrs/peaks/fit_factory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Peak fitting engine
SupportedPeakProfiles = ['Gaussian', 'PseudoVoigt', 'Voigt']
SupportedPeakProfiles = ['Gaussian', 'PseudoVoigt']
SupportedBackgroundTypes = ['Flat', 'Linear', 'Quadratic']

__all__ = ['FitEngineFactory', 'SupportedPeakProfiles', 'SupportedBackgroundTypes']
Expand Down
Empty file.
18 changes: 3 additions & 15 deletions tests/integration/test_peak_fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,7 @@ def test_retrieve_fit_metadata(source_project_file, output_project_file, peak_ty
[('data/Hidra_16-1_cor_log.h5', 'Hidra_16-1_cor_log_peak.h5', 'Gaussian',
PeakInfo(94.5, 91, 97, 'Fe111')), # NSFR2 peak
('data/HB2B_938.h5', 'HB2B_938_peak.h5', 'PseudoVoigt',
PeakInfo(95.5, 91, 97, 'Si111'))],
ids=('FakeHB2B', 'HB2B_938'))
PeakInfo(95.5, 91, 97, 'Si111'))], ids=('FakeHB2B', 'HB2B_938'))
def xtest_main(project_file_name, peak_file_name, peak_type, peak_info):
"""Test peak fitting
Expand Down Expand Up @@ -247,17 +246,6 @@ def xtest_main(project_file_name, peak_file_name, peak_type, peak_info):
os.remove(peak_file_name)


# TODO - MAKE IT WORK!
def test_calculating_com():
# calculate_center_of_mass(self, peak_tag, peak_range):
pass


def test_convert_peaks_centers_to_dspacing():
#
pass


def test_improve_quality():
"""This is a test to improve the quality of peak fitting.
Expand Down Expand Up @@ -372,7 +360,7 @@ def test_write_csv():
# verify that the number of columns is correct
# columns are (subruns, one log, parameter values, uncertainties, chisq)
for line in contents[len(EXPECTED_HEADER) + 1:]: # skip past header and constant log
assert len(line.split(',')) == 1 + 1 + 9 * 2 + 1
assert len(line.split(',')) == 1 + 1 + 10 * 2 + 1

# cleanup
os.remove(csv_filename)
Expand Down Expand Up @@ -465,7 +453,7 @@ def test_write_csv_from_project(project_file_name, csv_filename, expected_header
# columns are (subruns, seven logs, parameter values, uncertainties, d_spacing values,
# strain values and uncertainties, chisq)
for line in contents[len(expected_header) + 1:]: # skip past header and constant log
assert len(line.split(',')) == 1 + num_logs + 7 * 2 + (2*2) + 1
assert len(line.split(',')) == 1 + num_logs + 8 * 2 + (2*2) + 1

# cleanup
os.remove(csv_filename)
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/test_peak_fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def handle_dialog(filename):
# check number of lines
assert len(file_contents) == 127
# check number of values in line
assert len(np.fromstring(file_contents[-1], dtype=np.float64, sep=',')) == 33
assert len(np.fromstring(file_contents[-1], dtype=np.float64, sep=',')) == 35

# look at 1D results plot
line = window.ui.graphicsView_fitResult.canvas().get_axis().lines[0]
Expand Down Expand Up @@ -134,7 +134,7 @@ def handle_dialog(filename):
# check number of lines
assert len(file_contents) == 127
# check number of values in line
assert len(np.fromstring(file_contents[-1], dtype=np.float64, sep=',')) == 33
assert len(np.fromstring(file_contents[-1], dtype=np.float64, sep=',')) == 35

# look at 1D results plot
line = window.ui.graphicsView_fitResult.canvas().get_axis().lines[0]
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/pyrs/core/test_stress_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,8 @@ def test_set_d_reference(self, strain_stress_object_0, strain_stress_object_1):

def test_peak_parameters(self, strain_stress_object_1):
facade = StressFacade(strain_stress_object_1['stresses']['diagonal'])
assert set(facade.peak_parameters) == {'d', 'Center', 'Height', 'FWHM', 'Mixing',
'A0', 'A1', 'Intensity'}
assert set(facade.peak_parameters) == {'d', 'Center', 'Height', 'FWHM', 'Mixing', 'Intensity',
'A0', 'A1', 'A2'}

def test_peak_parameter_field(self, strain_stress_object_1):
r"""Retrieve the effective peak parameters for a particular run, or for a particular direction"""
Expand Down
67 changes: 54 additions & 13 deletions tests/unit/pyrs/peaks/test_peak_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_peak_collection_init():
np.testing.assert_equal(d_ref_err, np.asarray((0.,)))


def check_peak_collection(peak_shape, NUM_SUBRUN, target_errors,
def check_peak_collection(peak_shape, background, NUM_SUBRUN, target_errors,
wavelength=None, d_reference=None,
target_d_spacing_center=np.nan,
target_d_spacing_center_error=np.asarray([0., 0.]),
Expand Down Expand Up @@ -85,7 +85,7 @@ def check_peak_collection(peak_shape, NUM_SUBRUN, target_errors,
"""
subruns = np.arange(NUM_SUBRUN) + 1
chisq = np.array([42., 43.])
raw_peaks_array = np.zeros(NUM_SUBRUN, dtype=get_parameter_dtype(peak_shape, 'Linear'))
raw_peaks_array = np.zeros(NUM_SUBRUN, dtype=get_parameter_dtype(peak_shape, background))
if peak_shape == 'PseudoVoigt':
raw_peaks_array['Intensity'] = [1, 2]
raw_peaks_array['FWHM'] = np.array([4, 5], dtype=float)
Expand All @@ -96,11 +96,11 @@ def check_peak_collection(peak_shape, NUM_SUBRUN, target_errors,
raw_peaks_array['PeakCentre'] = [90., 91.]

# background terms are both zeros
raw_peaks_errors = np.zeros(NUM_SUBRUN, dtype=get_parameter_dtype(peak_shape, 'Linear'))
raw_peaks_errors = np.zeros(NUM_SUBRUN, dtype=get_parameter_dtype(peak_shape, background))
if wavelength is None:
peaks = PeakCollection('testing', peak_shape, 'Linear')
peaks = PeakCollection('testing', peak_shape, background)
else:
peaks = PeakCollection('testing', peak_shape, 'Linear', wavelength=wavelength)
peaks = PeakCollection('testing', peak_shape, background, wavelength=wavelength)

# uncertainties are being set to zero
peaks.set_peak_fitting_values(subruns, raw_peaks_array,
Expand All @@ -118,6 +118,7 @@ def check_peak_collection(peak_shape, NUM_SUBRUN, target_errors,
obs_raw_peaks, obs_raw_errors = peaks.get_native_params()
np.testing.assert_equal(obs_raw_peaks, raw_peaks_array)
np.testing.assert_equal(obs_raw_errors, raw_peaks_errors)

# check effective parameters
obs_eff_peaks, obs_eff_errors = peaks.get_effective_params()
assert obs_eff_peaks.size == NUM_SUBRUN
Expand Down Expand Up @@ -156,9 +157,15 @@ def check_peak_collection(peak_shape, NUM_SUBRUN, target_errors,
def test_peak_collection_Gaussian():
NUM_SUBRUN = 2
# without wavelength
check_peak_collection('Gaussian', NUM_SUBRUN, np.zeros(NUM_SUBRUN, dtype=get_parameter_dtype(effective=True)))
check_peak_collection('Gaussian', 'Linear', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01)],
dtype=get_parameter_dtype(effective=True))) # Error array
# with wavelength
check_peak_collection('Gaussian', NUM_SUBRUN, np.zeros(NUM_SUBRUN, dtype=get_parameter_dtype(effective=True)),
check_peak_collection('Gaussian', 'Linear', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01)],
dtype=get_parameter_dtype(effective=True)),
wavelength=1.53229, d_reference=1.08, target_d_spacing_center=[1.08, 1.07],
target_d_spacing_center_error=[0.0, 0.0], target_strain=[3234., -5408.],
target_strain_error=[0.0, 0.0])
Expand All @@ -167,14 +174,48 @@ def test_peak_collection_Gaussian():
def test_peak_collection_PseudoVoigt():
NUM_SUBRUN = 2
# without wavelength
check_peak_collection('PseudoVoigt', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)],
check_peak_collection('PseudoVoigt', 'Linear', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01)],
dtype=get_parameter_dtype(effective=True)))
# with wavelength
check_peak_collection('PseudoVoigt', 'Linear', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01)],
dtype=get_parameter_dtype(effective=True)),
wavelength=1.53229, d_reference=1.08, target_d_spacing_center=[1.08, 1.07],
target_d_spacing_center_error=[0.0, 0.0], target_strain=[3234., -5408.],
target_strain_error=[0.0, 0.0])


def test_peak_collection_Gaussian_Quadratic():
NUM_SUBRUN = 2
# without wavelength
check_peak_collection('Gaussian', 'Quadratic', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)],
dtype=get_parameter_dtype(effective=True))) # Error array
# with wavelength
check_peak_collection('Gaussian', 'Quadratic', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)],
dtype=get_parameter_dtype(effective=True)),
wavelength=1.53229, d_reference=1.08, target_d_spacing_center=[1.08, 1.07],
target_d_spacing_center_error=[0.0, 0.0], target_strain=[3234., -5408.],
target_strain_error=[0.0, 0.0])


def test_peak_collection_PseudoVoigt_Quadratic():
NUM_SUBRUN = 2
# without wavelength
check_peak_collection('PseudoVoigt', 'Quadratic', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)],
dtype=get_parameter_dtype(effective=True)))
# with wavelength
check_peak_collection('PseudoVoigt', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)],
check_peak_collection('PseudoVoigt', 'Quadratic', NUM_SUBRUN,
np.array([(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)],
dtype=get_parameter_dtype(effective=True)),
wavelength=1.53229, d_reference=1.08, target_d_spacing_center=[1.08, 1.07],
target_d_spacing_center_error=[0.0, 0.0], target_strain=[3234., -5408.],
Expand Down
Loading

1 comment on commit c204bb4

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitLab pipeline for pyrs-dev has been submitted for this commit: "https://code.ornl.gov/sns-hfir-scse/deployments/pyrs-deploy/-/pipelines/621889"

Please sign in to comment.