Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP. DONT MERGE. Allow polydisperse parameters to be constrained #1373

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2a2fabe
Initial commit. Not yet functional.
rozyczko Jul 19, 2019
c88153e
Use Qt.UserRole to store actual polydisp param name.
rozyczko Jul 19, 2019
bd200d2
More changes and fixes for mutual constraints, deleting and editing.
rozyczko Jul 31, 2019
40f827a
Show status bar info about the polydisp. constraint.
rozyczko Aug 1, 2019
b8843ef
Merge branch 'ESS_GUI' into ESS_GUI_constrain_poly
rozyczko Aug 7, 2019
7b9a446
Generalized constraint removal for certain cases, fixed complex
rozyczko Aug 8, 2019
4bc1fdd
Remove potential empty tabs on project load
rozyczko Aug 14, 2019
6f1c433
Minor stability fixes
rozyczko Sep 23, 2019
e0fd41b
Merge remote-tracking branch 'origin/main' into ESS_GUI_constrain_poly
rozyczko Feb 4, 2021
17c2ed7
Added proper filling of the first parameter list.
rozyczko Aug 20, 2021
2ffef79
don't update non-existing GUI elements
rozyczko Aug 30, 2021
754fc29
polydisperse parameters now appear as options for m2
Caddy-Jones Sep 7, 2021
f2cd591
Model encoded as a dictionary
Caddy-Jones Oct 7, 2021
1b67e06
Streamlined how model_key is determined, and fixed an issue when cons…
Caddy-Jones Oct 7, 2021
3c5e9f9
Fix TypeError when constraining two polidysperse parameters
Caddy-Jones Oct 29, 2021
aadaf70
Fixed the issue with adding multiple constraints
Caddy-Jones Oct 29, 2021
8cbe68f
Check for model is None
lucas-wilkins Jun 16, 2022
470f4d9
comment on a comment
lucas-wilkins Jun 16, 2022
24e4f51
Merge branch 'main' into ESS_GUI_constrain_poly
lucas-wilkins Jun 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/sas/qtgui/Perspectives/Fitting/ComplexConstraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ def setupWidgets(self):

self.setupParamWidgets()


self.setupMenu()

def setupMenu(self):
Expand All @@ -110,10 +109,10 @@ def setupParamWidgets(self):
# Populate the left combobox parameter arbitrarily with the parameters
# from the first tab if `All` option is selected
if self.cbModel1.currentText() == "All":
items1 = self.tabs[1].main_params_to_fit
items1 = self.tabs[1].main_params_to_fit + self.tabs[1].poly_params_to_fit
else:
tab_index1 = self.cbModel1.currentIndex()
items1 = self.tabs[tab_index1].main_params_to_fit
items1 = self.tabs[tab_index1].main_params_to_fit + self.tabs[tab_index1].poly_params_to_fit
self.cbParam1.addItems(items1)
# Show the previously selected parameter if available
if previous_param1 in items1:
Expand All @@ -126,6 +125,7 @@ def setupParamWidgets(self):
self.cbParam2.clear()
tab_index2 = self.cbModel2.currentIndex()
items2 = [param for param in self.params[tab_index2] if not self.tabs[tab_index2].paramHasConstraint(param)]

self.cbParam2.addItems(items2)
# Show the previously selected parameter if available
if previous_param2 in items2:
Expand Down
9 changes: 9 additions & 0 deletions src/sas/qtgui/Perspectives/Fitting/Constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self, parent=None, param=None, value=0.0,
self._min = min
self._max = max
self._operator = operator
self._model = None
self.validate = True
self.active = True

Expand Down Expand Up @@ -81,3 +82,11 @@ def operator(self):
def operator(self, val):
self._operator = val

@property
def model(self):
# model this constraint originates from
return self._model

@model.setter
def model(self, val):
self._model = val
53 changes: 37 additions & 16 deletions src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def initializeWidgets(self):
# Single Fit is the default, so disable chainfit
self.chkChain.setVisible(False)

# disabled constraint
# disabled constraint
labels = ['Constraint']
self.tblConstraints.setColumnCount(len(labels))
self.tblConstraints.setHorizontalHeaderLabels(labels)
Expand Down Expand Up @@ -399,6 +399,10 @@ def onHelp(self):
help_location = tree_location + helpfile

# OMG, really? Crawling up the object hierarchy...
#
# It's the top level that needs to do the show help.
# Perhaps better to address directly, but it does need to
# be that object. I don't like that the type is hidden. :LW
self.parent.parent.showHelp(help_location)

def onTabCellEdit(self, row, column):
Expand Down Expand Up @@ -530,6 +534,7 @@ def onConstraintChange(self, row, column):
return
# Then check if the parameter is correctly defined with colons
# separating model and parameter name

lhs, rhs = re.split(" *= *", item.data(0).strip(), 1)
if ":" not in lhs:
msg = ("Incorrect constrained parameter definition. Please use "
Expand Down Expand Up @@ -565,6 +570,7 @@ def onConstraintChange(self, row, column):
return
new_function = rhs
new_tab = self.available_tabs[new_model]
model_key = tab.getModelKey(param)
# Make sure we are dealing with fit tabs
assert isinstance(tab, FittingWidget)
assert isinstance(new_tab, FittingWidget)
Expand All @@ -573,12 +579,14 @@ def onConstraintChange(self, row, column):
# Apply the new constraint
constraint = Constraint(param=new_param, func=new_function,
value_ex=new_model + "." + new_param)
model_key = tab.getModelKey(new_param)
new_tab.addConstraintToRow(constraint=constraint,
row=tab.getRowFromName(new_param))
row=tab.getRowFromName(new_param), model_key=model_key)
# If the constraint is valid and we are changing model or
# parameter, delete the old constraint
if (self.constraint_accepted and new_model != model or
new_param != param):
print(1, param)
tab.deleteConstraintOnParameter(param)
# Reload the view
self.initializeFitList()
Expand All @@ -593,9 +601,9 @@ def onConstraintChange(self, row, column):
font.setItalic(True)
brush = QtGui.QBrush(QtGui.QColor('blue'))
tab.modifyViewOnRow(tab.getRowFromName(new_param), font=font,
brush=brush)
brush=brush, model_key=model_key)
else:
tab.modifyViewOnRow(tab.getRowFromName(new_param))
tab.modifyViewOnRow(tab.getRowFromName(new_param), model_key=model_key)
# reload the view so the user gets a consistent feedback on the
# constraints
self.initializeFitList()
Expand Down Expand Up @@ -890,6 +898,7 @@ def deleteConstraint(self):#, row):
moniker = constraint[:constraint.index(':')]
param = constraint[constraint.index(':')+1:constraint.index('=')].strip()
tab = self.available_tabs[moniker]
print(2, param)
tab.deleteConstraintOnParameter(param)

# Constraints removed - refresh the table widget
Expand All @@ -903,14 +912,17 @@ def uneditableItem(self, data=""):
item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled )
return item

def updateFitLine(self, tab):
def updateFitLine(self, tab, model_key="standard"):
"""
Update a single line of the table widget with tab info
"""
fit_page = ObjectLibrary.getObject(tab)
model = fit_page.kernel_module

if model is None:
logging.warning("No model selected")
return

tab_name = tab
model_name = model.id
moniker = model.name
Expand Down Expand Up @@ -947,19 +959,25 @@ def updateFitLine(self, tab):
self.tblTabList.blockSignals(False)

# Check if any constraints present in tab
active_constraint_names = fit_page.getComplexConstraintsForModel()
constraint_names = fit_page.getFullConstraintNameListForModel()
constraints = fit_page.getConstraintObjectsForModel()
constraint_names = fit_page.getComplexConstraintsForAllModels()
constraints = fit_page.getConstraintObjectsForAllModels()

# these three assignments need proper extension to all models, not just main model
active_constraint_names = fit_page.getComplexConstraintsForModel(model_key=model_key)
constraint_names = fit_page.getFullConstraintNameListForModel(model_key=model_key)
constraints = fit_page.getConstraintObjectsForModel(model_key=model_key)

if not constraints:
return
self.tblConstraints.setEnabled(True)
self.tblConstraints.blockSignals(True)
for constraint, constraint_name in zip(constraints, constraint_names):
# Ignore constraints that have no *func* attribute defined
if constraint.func is None:
if not constraint_name and len(constraint_name) < 2:
continue
if constraint_name[0] is None or constraint_name[1] is None:
continue
# Create the text for widget item
label = moniker + ":"+ constraint_name[0] + " = " + constraint_name[1]
label = moniker + ":" + constraint_name[0] + " = " + constraint_name[1]
pos = self.tblConstraints.rowCount()
self.available_constraints[pos] = constraint

Expand All @@ -978,7 +996,7 @@ def updateFitLine(self, tab):
self.tblConstraints.setItem(pos, 0, item)
self.tblConstraints.blockSignals(False)

def initializeFitList(self):
def initializeFitList(self, row=0, model_key="standard"):
"""
Fill the list of model/data sets for fitting/constraining
"""
Expand Down Expand Up @@ -1017,7 +1035,7 @@ def initializeFitList(self):
self._row_order = tabs

for tab in tabs:
self.updateFitLine(tab)
self.updateFitLine(tab, model_key=model_key)
self.updateSignalsFromTab(tab)
# We have at least 1 fit page, allow fitting
self.cmdFit.setEnabled(True)
Expand Down Expand Up @@ -1084,14 +1102,16 @@ def onAcceptConstraint(self, con_tuple):

# Find the constrained parameter row
constrained_row = constrained_tab.getRowFromName(constraint.param)
model_key = constrained_tab.getModelKey(constraint.param)

# Update the tab
constrained_tab.addConstraintToRow(constraint, constrained_row)
constrained_tab.addConstraintToRow(constraint, constrained_row, model_key=model_key)
if not self.constraint_accepted:
return

# Select this parameter for adjusting/fitting
constrained_tab.changeCheckboxStatus(constrained_row, True)
# constrained_tab.selectCheckbox(constrained_row, model=model)


def showMultiConstraint(self):
"""
Expand Down Expand Up @@ -1211,4 +1231,5 @@ def uncheckConstraint(self, name):
# deactivate the constraint
tab = self.parent.getTabByName(name[:name.index(":")])
row = tab.getRowFromName(name[name.index(":") + 1:])
tab.getConstraintForRow(row).active = False
model_key = tab.getModelKey(constraint)
tab.getConstraintForRow(row, model_key=model_key).active = False
10 changes: 6 additions & 4 deletions src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,11 @@ def updateFromConstraints(self, constraint_dict):
constraint.param = constraint_param[1]
constraint.value_ex = constraint_param[2]
constraint.validate = constraint_param[3]
model_key = tab.getModelKey(constraint)
tab.addConstraintToRow(constraint=constraint,
row=tab.getRowFromName(
constraint_param[1]))
constraint_param[1]),
model_key=model_key)

def onParamSave(self):
self.currentTab.onCopyToClipboard("Save")
Expand Down Expand Up @@ -519,9 +521,9 @@ def getActiveConstraintList(self):
constraints = []
for tab in self.getFitTabs():
tab_name = tab.modelName()
tab_constraints = tab.getConstraintsForModel()
constraints.extend((tab_name + "." + par, expr)
for par, expr in tab_constraints)
tab_constraints = tab.getConstraintsForAllModels()
constraints.extend((tab_name + "." + par, expr) for par, expr in tab_constraints)

return constraints

def getSymbolDictForConstraints(self):
Expand Down
6 changes: 6 additions & 0 deletions src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,9 +972,15 @@ def isParamPolydisperse(param_name, kernel_params, is2D=False):
"""
Simple lookup for polydispersity for the given param name
"""
# First, check if this is a polydisperse parameter directly
if '.width' in param_name:
return True

parameters = kernel_params.form_volume_parameters
if is2D:
parameters += kernel_params.orientation_parameters

# Next, check if the parameter is included in para.polydisperse
has_poly = False
for param in parameters:
if param.name==param_name and param.polydisperse:
Expand Down
Loading