Skip to content

Commit

Permalink
make ancillaries sensitive to preprocessing
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmueller committed Jan 11, 2020
1 parent 679343d commit 7fcb5ea
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 73 deletions.
1 change: 1 addition & 0 deletions pyjibe/fd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ def on_params_init(self):
fdist = self.current_curve
idx = self.current_index
self.tab_preprocess.fit_apply_preprocessing(fdist)
self.tab_fit.anc_update_parameters(fdist)
self.tab_fit.fit_approach_retract(fdist)
self.widget_fdist.mpl_curve_update(fdist)
self.curve_list_update(item=idx)
Expand Down
96 changes: 49 additions & 47 deletions pyjibe/fd/tab_fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, *args, **kwargs):
models_av.sort(key=lambda x: nmodel.models_available[x].model_name)
for ii, key in enumerate(models_av):
model = nmodel.models_available[key]
self.cb_model.addItem(model.model_name)
self.cb_model.addItem(model.model_name, key)
if key == "hertz_para":
id_para = ii
# Setz Hertz Parabolic as default
Expand All @@ -39,10 +39,10 @@ def __init__(self, *args, **kwargs):
self.cb_right_individ.setVisible(False)

# signals
self.cb_segment.currentTextChanged.connect(self.on_params_init)
self.cb_xaxis.currentTextChanged.connect(self.on_params_init)
self.cb_yaxis.currentTextChanged.connect(self.on_params_init)
self.cb_model.currentTextChanged.connect(self.on_model)
self.cb_segment.currentIndexChanged.connect(self.on_params_init)
self.cb_xaxis.currentIndexChanged.connect(self.on_params_init)
self.cb_yaxis.currentIndexChanged.connect(self.on_params_init)
self.cb_model.currentIndexChanged.connect(self.on_model)
self.cb_range_type.currentTextChanged.connect(self.on_params_init)
self.table_parameters_anc.itemChanged.connect(self.on_params_anc)
self.table_parameters_initial.itemChanged.connect(self.on_params_init)
Expand All @@ -60,7 +60,48 @@ def fd(self):

@property
def fit_model(self):
return nmodel.get_model_by_name(self.cb_model.currentText())
return nmodel.models_available[self.cb_model.currentData()]

def anc_update_parameters(self, fdist):
model_key = self.fit_model.model_key
# ancillaries
anc = fdist.get_ancillary_parameters(model_key=model_key)
anc_used = [ak for ak in anc if ak in self.fit_model.parameter_keys]
if anc_used:
self.widget_anc.setVisible(True)
itab = self.table_parameters_initial
atab = self.table_parameters_anc
atab.blockSignals(True)
rows_changed = self.assert_parameter_table_rows(atab,
len(anc_used),
cb_first=True,
read_only=True)
row = 0
for ii, ak in enumerate(list(anc.keys())):
if ak not in anc_used:
continue
# Get the human readable name of the parameter
hrname = self.fit_model.parameter_anc_names[ii]
# Determine unit scale, e.g. 1e6 [sic] for µm
si_unit = self.fit_model.parameter_anc_units[ii]
# Determine unit scale, e.g. 1e6 [sic] for µm
scale = units.hrscale(hrname, si_unit=si_unit)
label = units.hrscname(hrname, si_unit=si_unit)
atab.verticalHeaderItem(row).setText(label)
if rows_changed:
atab.item(row, 0).setCheckState(QtCore.Qt.Checked)
atab.item(row, 1).setText("{:.5g}".format(anc[ak]*scale))
# updates initial parameters if "use" is checked
if atab.item(row, 0).checkState() == QtCore.Qt.Checked:
# update initial parameters
idx = self.fit_model.parameter_keys.index(ak)
text = atab.item(row, 1).text()
if text != "nan":
itab.item(idx, 1).setText(text)
row += 1
atab.blockSignals(False)
else:
self.widget_anc.setVisible(False)

def assert_parameter_table_rows(self, table, rows, cb_first=False,
read_only=False):
Expand Down Expand Up @@ -294,36 +335,7 @@ def fit_update_parameters(self, fdist):
self.sp_range_2.setValue(right)

# ancillaries
anc = fdist.get_ancillary_parameters(model_key=model_key)
anc_used = [ak for ak in anc if ak in self.fit_model.parameter_keys]
if anc_used:
self.widget_anc.setVisible(True)
atab = self.table_parameters_anc
atab.blockSignals(True)
rows_changed = self.assert_parameter_table_rows(atab,
len(anc_used),
cb_first=True,
read_only=True)
row = 0
for ii, ak in enumerate(list(anc.keys())):
if ak not in anc_used:
continue
# Get the human readable name of the parameter
hrname = self.fit_model.parameter_anc_names[ii]
# Determine unit scale, e.g. 1e6 [sic] for µm
si_unit = self.fit_model.parameter_anc_units[ii]
# Determine unit scale, e.g. 1e6 [sic] for µm
scale = units.hrscale(hrname, si_unit=si_unit)
label = units.hrscname(hrname, si_unit=si_unit)
atab.verticalHeaderItem(row).setText(label)
if rows_changed:
atab.item(row, 0).setCheckState(QtCore.Qt.Checked)
atab.item(row, 1).setText("{:.5g}".format(anc[ak]*scale))
row += 1
atab.blockSignals(False)
self.on_params_anc() # updates initial parameters if necessary
else:
self.widget_anc.setVisible(False)
self.anc_update_parameters(fdist)

def indentation_depth_setup(self):
"""Initiate ranges (spin/slider) that allow inf values"""
Expand All @@ -341,17 +353,7 @@ def on_model(self):
self.fd.on_model()

def on_params_anc(self):
"""Updates initial parameters if the "use" is checked"""
atab = self.table_parameters_anc
itab = self.table_parameters_initial
anc = self.fit_model.parameter_anc_keys
anc_used = [ak for ak in anc if ak in self.fit_model.parameter_keys]
for row, ak in enumerate(anc_used):
if atab.item(row, 0).checkState() == QtCore.Qt.Checked:
# update initial parameters
idx = self.fit_model.parameter_keys.index(ak)
itab.item(idx, 1).setText(atab.item(row, 1).text())
self.on_params_init()
self.fd.on_params_init()

def on_params_init(self):
self.fd.on_params_init()
Expand Down
155 changes: 129 additions & 26 deletions tests/test_fd_fit.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Test of data set functionalities"""
import pathlib

import numpy as np
from PyQt5 import QtWidgets
import pytest

import nanite.model as nmodel
import pyjibe.head
Expand All @@ -11,6 +13,23 @@
jpkfile = here / "data" / "spot3-0192.jpk-force"


class MockModel():
def __init__(self, model_key, **kwargs):
# rebase on hertz model
md = nmodel.models_available["hertz_para"]
for key in dir(md):
setattr(self, key, getattr(md, key))
for kw in kwargs:
setattr(self, kw, kwargs[kw])
self.model_key = model_key

def __enter__(self):
nmodel.register_model(self, self.__repr__())

def __exit__(self, a, b, c):
nmodel.models_available.pop(self.model_key)


def cleanup_autosave(jpkfile):
"""Remove autosave files"""
path = jpkfile.parent
Expand All @@ -19,8 +38,17 @@ def cleanup_autosave(jpkfile):
[f.unlink() for f in files]


def test_remember_initial_params(qtbot):
@pytest.fixture(autouse=True)
def run_around_tests():
# Code that will run before your test, for example:
cleanup_autosave(jpkfile)
# A test function will be run at this point
yield
# Code that will run after your test, for example:
cleanup_autosave(jpkfile)


def test_change_model_keep_parms(qtbot):
main_window = pyjibe.head.PyJibe()
main_window.load_data(files=[jpkfile, jpkfile])
war = main_window.subwindows[0].widget()
Expand All @@ -34,24 +62,17 @@ def test_remember_initial_params(qtbot):
# perform fitting with standard parameters
# set initial parameters in user interface
itab = war.tab_fit.table_parameters_initial
# disable weighting
war.tab_fit.cb_weight_cp.setCheckState(0)
# enable fitting of force offset
itab.item(4, 0).setCheckState(0)
# set better value for contact point
itab.item(3, 1).setText(str(18000))
# change standard tip radius from 10 to 5
assert float(itab.item(1, 1).text()) == 10
itab.item(1, 1).setText(str(5))
cl1 = war.list_curves.currentItem()
cl2 = war.list_curves.itemBelow(cl1)
war.list_curves.setCurrentItem(cl2)
assert float(itab.item(1, 1).text()) == 5
cleanup_autosave(jpkfile)
# set value for contact point
itab.item(3, 1).setText(str(12345))
# change the model to pyramidal
pyr_name = nmodel.model_hertz_three_sided_pyramid.model_name
pyr_idx = war.tab_fit.cb_model.findText(pyr_name)
war.tab_fit.cb_model.setCurrentIndex(pyr_idx)
# check that contact point is still the same
assert float(itab.item(3, 1).text()) == 12345


def test_change_model_keep_parms(qtbot):
cleanup_autosave(jpkfile)
def test_remember_initial_params(qtbot):
main_window = pyjibe.head.PyJibe()
main_window.load_data(files=[jpkfile, jpkfile])
war = main_window.subwindows[0].widget()
Expand All @@ -65,12 +86,94 @@ def test_change_model_keep_parms(qtbot):
# perform fitting with standard parameters
# set initial parameters in user interface
itab = war.tab_fit.table_parameters_initial
# set value for contact point
itab.item(3, 1).setText(str(12345))
# change the model to pyramidal
pyr_name = nmodel.model_hertz_three_sided_pyramid.model_name
pyr_idx = war.tab_fit.cb_model.findText(pyr_name)
war.tab_fit.cb_model.setCurrentIndex(pyr_idx)
# check that contact point is still the same
assert float(itab.item(3, 1).text()) == 12345
cleanup_autosave(jpkfile)
# disable weighting
war.tab_fit.cb_weight_cp.setCheckState(0)
# enable fitting of force offset
itab.item(4, 0).setCheckState(0)
# set better value for contact point
itab.item(3, 1).setText(str(18000))
# change standard tip radius from 10 to 5
assert float(itab.item(1, 1).text()) == 10
itab.item(1, 1).setText(str(5))
cl1 = war.list_curves.currentItem()
cl2 = war.list_curves.itemBelow(cl1)
war.list_curves.setCurrentItem(cl2)
assert float(itab.item(1, 1).text()) == 5


def test_update_ancillary_nan(qtbot):
with MockModel(
compute_ancillaries=lambda x: {"E": np.nan},
parameter_anc_keys=["E"],
parameter_anc_names=["ancillary E guess"],
parameter_anc_units=["Pa"],
model_key="test1"):

main_window = pyjibe.head.PyJibe()
main_window.load_data(files=[jpkfile, jpkfile])
war = main_window.subwindows[0].widget()
# clear data
war.tab_preprocess.list_preproc_applied.clear()
war.cb_autosave.setChecked(0)
# perform simple filter
item = QtWidgets.QListWidgetItem()
item.setText("compute_tip_position")
war.tab_preprocess.list_preproc_applied.addItem(item)
# disable weighting
war.tab_fit.cb_weight_cp.setCheckState(0)
# set mock model
idx = war.tab_fit.cb_model.findData("test1")
war.tab_fit.cb_model.setCurrentIndex(idx)
# perform fitting with standard parameters
# set initial parameters in user interface
itab = war.tab_fit.table_parameters_initial
atab = war.tab_fit.table_parameters_anc
assert atab.item(0, 1).text() == "nan"
assert itab.item(0, 1).text() == "3000"


def test_update_ancillary_preproc_change(qtbot):
with MockModel(
compute_ancillaries=lambda x: {
# i.e. model works only if there are multiple preproc steps
"E": np.nan if len(x.preprocessing) == 1 else 2345},
parameter_anc_keys=["E"],
parameter_anc_names=["ancillary E guess"],
parameter_anc_units=["Pa"],
model_key="test1"):

main_window = pyjibe.head.PyJibe()
main_window.load_data(files=[jpkfile, jpkfile])
war = main_window.subwindows[0].widget()
# clear data
war.tab_preprocess.list_preproc_applied.clear()
war.cb_autosave.setChecked(0)
# perform simple filter
item = QtWidgets.QListWidgetItem()
item.setText("compute_tip_position")
war.tab_preprocess.list_preproc_applied.addItem(item)
# disable weighting
war.tab_fit.cb_weight_cp.setCheckState(0)
# set mock model
idx = war.tab_fit.cb_model.findData("test1")
war.tab_fit.cb_model.setCurrentIndex(idx)
# perform fitting with standard parameters
# set initial parameters in user interface
itab = war.tab_fit.table_parameters_initial
atab = war.tab_fit.table_parameters_anc
war.on_tab_changed(1)
assert len(war.data_set[0].preprocessing) == 1
assert war.tab_preprocess.list_preproc_applied.count() == 1
assert atab.item(0, 1).text() == "nan"
assert itab.item(0, 1).text() == "3000"
# up until here this is the same as `test_update_ancillary_nan`
# now change preprocessing
war.tabs.setCurrentIndex(0) # actually switch tabs like a user
item = QtWidgets.QListWidgetItem()
item.setText("correct_tip_offset")
war.tab_preprocess.list_preproc_applied.addItem(item)
war.tabs.setCurrentIndex(1) # triggers recomputation of anc
assert len(war.data_set[0].preprocessing) == 2
assert war.tab_preprocess.list_preproc_applied.count() == 2
assert atab.item(0, 1).text() == "2345"
assert itab.item(0, 1).text() == "2345"

0 comments on commit 7fcb5ea

Please sign in to comment.