Index: src/sas/qtgui/GUITests.py
===================================================================
--- src/sas/qtgui/GUITests.py (revision 7dd309a3e471bf4759e1678ada578fb8c73d7007)
+++ src/sas/qtgui/GUITests.py (revision ccd2b87ed4046bfd64dc45b580c48032d91271b8)
@@ -180,5 +180,5 @@
unittest.makeSuite(InvariantPerspectiveTest.InvariantPerspectiveTest, 'test'),
# Inversion
- #unittest.makeSuite(InversionPerspectiveTest.InversionTest, 'test'),
+ unittest.makeSuite(InversionPerspectiveTest.InversionTest, 'test'),
)
return unittest.TestSuite(suites)
Index: src/sas/qtgui/MainWindow/GuiManager.py
===================================================================
--- src/sas/qtgui/MainWindow/GuiManager.py (revision fc5d2d7fa9cd52ca6b9a8d7082da6eddf35b1d34)
+++ src/sas/qtgui/MainWindow/GuiManager.py (revision dee9e5f369951654b0c4ceb6506475a3cd8ed1f9)
@@ -992,4 +992,5 @@
When setting a perspective, sets up the menu bar
"""
+ self._workspace.actionReport.setEnabled(False)
if isinstance(perspective, Perspectives.PERSPECTIVES["Fitting"]):
self.checkAnalysisOption(self._workspace.actionFitting)
@@ -1001,4 +1002,5 @@
self._workspace.menubar.addAction(self._workspace.menuWindow.menuAction())
self._workspace.menubar.addAction(self._workspace.menuHelp.menuAction())
+ self._workspace.actionReport.setEnabled(True)
elif isinstance(perspective, Perspectives.PERSPECTIVES["Invariant"]):
Index: src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py (revision b0ba43ee4b198d4f5a834c93888985b18c20c071)
+++ src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py (revision 9f2f713821e7ee39c15ba219bdc789d46dd08254)
@@ -42,5 +42,5 @@
self.parent = parent
- self.setWindowTitle("Dââ Explorer")
+ self.setWindowTitle("Dmax Explorer")
self.pr_state = pr_state
@@ -116,5 +116,5 @@
bck = []
chi2 = []
-
+ plotable_xs = [] #Introducing this to make sure size of x and y for plotting is the same.8
try:
dmin = float(self.model.item(W.DMIN).text())
@@ -128,4 +128,5 @@
original = self.pr_state.d_max
+
for x in xs:
self.pr_state.d_max = x
@@ -140,4 +141,5 @@
bck.append(self.pr_state.background)
chi2.append(self.pr_state.chi2)
+ plotable_xs.append(x)
except Exception as ex:
# This inversion failed, skip this D_max value
@@ -155,6 +157,5 @@
logger.error(msg)
- plotter = self.model.item(W.VARIABLE).text()
- y_label = y_unit = ""
+ plotter = self.dependentVariable.currentText()
x_label = "D_{max}"
x_unit = "A"
@@ -188,7 +189,7 @@
y_unit = "a.u."
- data = Data1D(xs, ys)
+ data = Data1D(plotable_xs, ys)
if self.hasPlot:
- self.plot.removePlot(None)
+ self.plot.removePlot(data.name)
self.hasPlot = True
data.title = plotter
Index: src/sas/qtgui/Perspectives/Inversion/InversionLogic.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/InversionLogic.py (revision 6da860acc5670055935e7bdc8678d3827d1a057f)
+++ src/sas/qtgui/Perspectives/Inversion/InversionLogic.py (revision 3c4f02ecca04eb32797cdd49ced17d153b1d653f)
@@ -111,4 +111,7 @@
new_plot.title = title
+ new_plot.symbol = 'Line'
+ new_plot.hide_error = True
+
return new_plot
Index: src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py (revision 9ce69ec3193ed870f2129f02525b0537ca161f76)
+++ src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py (revision dee9e5f369951654b0c4ceb6506475a3cd8ed1f9)
@@ -44,4 +44,6 @@
estimateSignal = QtCore.pyqtSignal(tuple)
estimateNTSignal = QtCore.pyqtSignal(tuple)
+ estimateDynamicNTSignal = QtCore.pyqtSignal(tuple)
+ estimateDynamicSignal = QtCore.pyqtSignal(tuple)
calculateSignal = QtCore.pyqtSignal(tuple)
@@ -53,4 +55,6 @@
self._manager = parent
+ #Needed for Batch fitting
+ self._parent = parent
self.communicate = parent.communicator()
self.communicate.dataDeletedSignal.connect(self.removeData)
@@ -110,4 +114,7 @@
# Set up the Widget Map
self.setupMapper()
+
+ #Hidding calculate all buton
+ self.calculateAllButton.setVisible(False)
# Set base window state
self.setupWindow()
@@ -120,5 +127,5 @@
def allowBatch(self):
- return True
+ return False
def setClosable(self, value=True):
@@ -195,6 +202,10 @@
self.model.itemChanged.connect(self.model_changed)
self.estimateNTSignal.connect(self._estimateNTUpdate)
+ self.estimateDynamicNTSignal.connect(self._estimateDynamicNTUpdate)
+ self.estimateDynamicSignal.connect(self._estimateDynamicUpdate)
self.estimateSignal.connect(self._estimateUpdate)
self.calculateSignal.connect(self._calculateUpdate)
+
+ self.maxDistanceInput.textEdited.connect(self.performEstimateDynamic)
def setupMapper(self):
@@ -310,6 +321,5 @@
and not self.isCalculating)
self.removeButton.setEnabled(self.logic.data_is_loaded)
- self.explorerButton.setEnabled(self.logic.data_is_loaded
- and np.all(self.logic.data.dy != 0))
+ self.explorerButton.setEnabled(self.logic.data_is_loaded)
self.stopButton.setVisible(self.isCalculating)
self.regConstantSuggestionButton.setEnabled(
@@ -454,12 +464,20 @@
# Create initial internal mappings
self.logic.data = GuiUtils.dataFromItem(data)
+ if not isinstance(self.logic.data, Data1D):
+ msg = "P(r) perspective works for 1D data only"
+ logger.warning(msg)
+ continue
# Estimate q range
qmin, qmax = self.logic.computeDataRange()
self._calculator.set_qmin(qmin)
self._calculator.set_qmax(qmax)
+ if np.size(self.logic.data.dy) == 0 or np.all(self.logic.data.dy) == 0:
+ self.logic.data.dy = self._calculator.add_errors(self.logic.data.y)
self.updateDataList(data)
self.populateDataComboBox(self.logic.data.filename, data)
self.dataList.setCurrentIndex(len(self.dataList) - 1)
- self.setCurrentData(data)
+ #Checking for 1D again to mitigate the case when 2D data is last on the data list
+ if isinstance(self.logic.data, Data1D):
+ self.setCurrentData(data)
def updateDataList(self, dataRef):
@@ -502,4 +520,15 @@
self.dataPlot = self._dataList[data_ref].get(DICT_KEYS[2])
self.performEstimate()
+
+ def updateDynamicGuiValues(self):
+ pr = self._calculator
+ alpha = self._calculator.suggested_alpha
+ self.model.setItem(WIDGETS.W_MAX_DIST,
+ QtGui.QStandardItem("{:.4g}".format(pr.get_dmax())))
+ self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha))
+ self.noOfTermsSuggestionButton.setText(
+ "{:n}".format(self.nTermsSuggested))
+
+ self.enableButtons()
def updateGuiValues(self):
@@ -521,7 +550,4 @@
self.model.setItem(WIDGETS.W_MAX_DIST,
QtGui.QStandardItem("{:.4g}".format(pr.get_dmax())))
- self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha))
- self.noOfTermsSuggestionButton.setText(
- "{:n}".format(self.nTermsSuggested))
if isinstance(pr.chi2, np.ndarray):
@@ -638,6 +664,7 @@
pr = self._calculator.clone()
- nfunc = self.getNFunc()
- self.calcThread = CalcPr(pr, nfunc,
+ #Making sure that nfunc and alpha parameters are correctly initialized
+ pr.suggested_alpha = self._calculator.alpha
+ self.calcThread = CalcPr(pr, self.nTermsSuggested,
error_func=self._threadError,
completefn=self._calculateCompleted,
@@ -672,4 +699,29 @@
error_func=self._threadError,
completefn=self._estimateNTCompleted,
+ updatefn=None)
+ self.estimationThreadNT.queue()
+ self.estimationThreadNT.ready(2.5)
+
+ def performEstimateDynamicNT(self):
+ """
+ Perform parameter estimation
+ """
+ from .Thread import EstimateNT
+
+ self.updateCalculator()
+
+ # If a thread is already started, stop it
+ self.stopEstimateNTThread()
+
+ pr = self._calculator.clone()
+ # Skip the slit settings for the estimation
+ # It slows down the application and it doesn't change the estimates
+ pr.slit_height = 0.0
+ pr.slit_width = 0.0
+ nfunc = self.getNFunc()
+
+ self.estimationThreadNT = EstimateNT(pr, nfunc,
+ error_func=self._threadError,
+ completefn=self._estimateDynamicNTCompleted,
updatefn=None)
self.estimationThreadNT.queue()
@@ -698,4 +750,21 @@
self.estimationThread.ready(2.5)
+ def performEstimateDynamic(self):
+ """
+ Perform parameter estimation
+ """
+ from .Thread import EstimatePr
+
+ # If a thread is already started, stop it
+ self.stopEstimationThread()
+
+ self.estimationThread = EstimatePr(self._calculator.clone(),
+ self.getNFunc(),
+ error_func=self._threadError,
+ completefn=self._estimateDynamicCompleted,
+ updatefn=None)
+ self.estimationThread.queue()
+ self.estimationThread.ready(2.5)
+
def stopEstimationThread(self):
""" Stop the estimation thread if it exists and is running """
@@ -710,4 +779,8 @@
''' Send a signal to the main thread for model update'''
self.estimateSignal.emit((alpha, message, elapsed))
+
+ def _estimateDynamicCompleted(self, alpha, message, elapsed):
+ ''' Send a signal to the main thread for model update'''
+ self.estimateDynamicSignal.emit((alpha, message, elapsed))
def _estimateUpdate(self, output_tuple):
@@ -725,8 +798,28 @@
logger.info(message)
self.performEstimateNT()
+ self.performEstimateDynamicNT()
+
+ def _estimateDynamicUpdate(self, output_tuple):
+ """
+ Parameter estimation completed,
+ display the results to the user
+
+ :param alpha: estimated best alpha
+ :param elapsed: computation time
+ """
+ alpha, message, elapsed = output_tuple
+ self._calculator.alpha = alpha
+ self._calculator.elapsed += self._calculator.elapsed
+ if message:
+ logger.info(message)
+ self.performEstimateDynamicNT()
def _estimateNTCompleted(self, nterms, alpha, message, elapsed):
''' Send a signal to the main thread for model update'''
self.estimateNTSignal.emit((nterms, alpha, message, elapsed))
+
+ def _estimateDynamicNTCompleted(self, nterms, alpha, message, elapsed):
+ ''' Send a signal to the main thread for model update'''
+ self.estimateDynamicNTSignal.emit((nterms, alpha, message, elapsed))
def _estimateNTUpdate(self, output_tuple):
@@ -752,4 +845,26 @@
self.startThread()
+ def _estimateDynamicNTUpdate(self, output_tuple):
+ """
+ Parameter estimation completed,
+ display the results to the user
+
+ :param alpha: estimated best alpha
+ :param nterms: estimated number of terms
+ :param elapsed: computation time
+ """
+ nterms, alpha, message, elapsed = output_tuple
+ self._calculator.elapsed += elapsed
+ self._calculator.suggested_alpha = alpha
+ self.nTermsSuggested = nterms
+ # Save useful info
+ self.updateDynamicGuiValues()
+ if message:
+ logger.info(message)
+ if self.isBatch:
+ self.acceptAlpha()
+ self.acceptNoTerms()
+ self.startThread()
+
def _calculateCompleted(self, out, cov, pr, elapsed):
''' Send a signal to the main thread for model update'''
Index: src/sas/qtgui/Perspectives/Inversion/UI/DMaxExplorer.ui
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/UI/DMaxExplorer.ui (revision 8f83719fd67e6a72acdcd666e7bf1e2326b52cd0)
+++ src/sas/qtgui/Perspectives/Inversion/UI/DMaxExplorer.ui (revision cfd61f23d91bd633d84aa6a5cfb03c97e0512189)
@@ -7,6 +7,6 @@
0
0
- 394
- 426
+ 586
+ 538
@@ -21,10 +21,4 @@
Qt::Vertical
-
-
-
- 20
- 305
-
Index: src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui (revision 72ecbdf22c121ae7a0f43b886ce56a9b503fed04)
+++ src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui (revision 68dc287336e67e72e8509168de3021b5af3676ab)
@@ -7,6 +7,6 @@
0
0
- 390
- 409
+ 446
+ 480
@@ -740,5 +740,5 @@
- true
+ false
Index: src/sas/qtgui/Perspectives/Inversion/UnitTesting/InversionPerspectiveTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/UnitTesting/InversionPerspectiveTest.py (revision 144fe21c4befe53963fef06a9091c7d69d0b3b41)
+++ src/sas/qtgui/Perspectives/Inversion/UnitTesting/InversionPerspectiveTest.py (revision ccd2b87ed4046bfd64dc45b580c48032d91271b8)
@@ -99,5 +99,5 @@
def baseBatchState(self):
""" Testing the base batch fitting state """
- self.assertTrue(self.widget.allowBatch())
+ self.assertFalse(self.widget.allowBatch())
self.assertFalse(self.widget.isBatch)
self.assertFalse(self.widget.calculateAllButton.isEnabled())
@@ -304,5 +304,5 @@
self.assertIsNotNone(self.widget.dmaxWindow)
self.assertTrue(self.widget.dmaxWindow.isVisible())
- self.assertTrue(self.widget.dmaxWindow.windowTitle() == "Dââ Explorer")
+ self.assertTrue(self.widget.dmaxWindow.windowTitle() == "Dmax Explorer")
Index: src/sas/qtgui/Plotting/Plotter.py
===================================================================
--- src/sas/qtgui/Plotting/Plotter.py (revision 5b144c6da3772c7f3a243d7540d60d506f7f931e)
+++ src/sas/qtgui/Plotting/Plotter.py (revision d0952de6a781359a354ba76157ffb2a89e3e588d)
@@ -84,5 +84,7 @@
if self.data.ytransform is None:
self.data.ytransform = 'log10(y)'
-
+ #Added condition to Dmax explorer from P(r) perspective
+ if self.data._xaxis == 'D_{max}':
+ self.xscale = 'linear'
# Transform data if required.
if transform and (self.data.xtransform is not None or self.data.ytransform is not None):
Index: src/sas/qtgui/Utilities/GridPanel.py
===================================================================
--- src/sas/qtgui/Utilities/GridPanel.py (revision aed053275d5f293f1f6495be4c78e4b18fc52b41)
+++ src/sas/qtgui/Utilities/GridPanel.py (revision 4fbf0db038252084f77261644233fd494886a284)
@@ -54,7 +54,8 @@
# Fill in the table from input data
self.setupTable(widget=self.tblParams, data=output_data)
+ #TODO: This is not what was inteded to be.
if output_data is not None:
# Set a table tooltip describing the model
- model_name = output_data[0][0].model.id
+ model_name = list(output_data.keys())[0]
self.tabWidget.setTabToolTip(0, model_name)
@@ -418,9 +419,9 @@
def __init__(self, parent = None, output_data=None):
- super(BatchInversionOutputPanel, self).__init__(parent, output_data)
+ super(BatchInversionOutputPanel, self).__init__(parent._parent, output_data)
_translate = QtCore.QCoreApplication.translate
self.setWindowTitle(_translate("GridPanelUI", "Batch P(r) Results"))
- def setupTable(self, data):
+ def setupTable(self, widget=None, data=None):
"""
Create tablewidget items and show them, based on params
@@ -433,4 +434,6 @@
'Calc. Time [sec]']
+ if data is None:
+ return
keys = data.keys()
rows = len(keys)
Index: src/sas/sascalc/pr/invertor.py
===================================================================
--- src/sas/sascalc/pr/invertor.py (revision b8080e1affadb04559f0597dfddc5506d2044e9d)
+++ src/sas/sascalc/pr/invertor.py (revision eeea6a3d116f83d83e3b689d46542bf558d77e09)
@@ -71,5 +71,5 @@
A[j][i] = (Fourier transformed base function for point j)
- We them choose a number of r-points, n_r, to evaluate the second
+ We then choose a number of r-points, n_r, to evaluate the second
derivative of P(r) at. This is used as our regularization term.
For a vector r of length n_r, the following n_r rows are set to ::
@@ -144,5 +144,5 @@
x, y, err, d_max, q_min, q_max and alpha
"""
- if name == 'x':
+ if name == 'x':
if 0.0 in value:
msg = "Invertor: one of your q-values is zero. "
@@ -227,4 +227,18 @@
return None
+ def add_errors(self, yvalues):
+ """
+ Adds errors to data set is they are not avaialble
+ :return:
+ """
+ stats_errors = np.zeros(len(yvalues))
+ for i in range(len(yvalues)):
+ # Scale the error so that we can fit over several decades of Q
+ scale = 0.05 * np.sqrt(yvalues[i])
+ min_err = 0.01 * yvalues[i]
+ stats_errors[i] = scale * np.sqrt(np.fabs(yvalues[i])) + min_err
+ logger.warning("Simulated errors have been added to the data set\n")
+ return stats_errors
+
def clone(self):
"""
@@ -268,5 +282,5 @@
A[i][j] = (Fourier transformed base function for point j)
- We them choose a number of r-points, n_r, to evaluate the second
+ We then choose a number of r-points, n_r, to evaluate the second
derivative of P(r) at. This is used as our regularization term.
For a vector r of length n_r, the following n_r rows are set to ::