source: sasview/src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py @ 8f83719f

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 8f83719f was 8f83719f, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

More inversion work on details in validation, UI design and such

  • Property mode set to 100644
File size: 6.1 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3Dialog panel to explore the P(r) inversion results for a range
4of D_max value. User picks a number of points and a range of
5distances, then can toggle between inversion outputs and see
6their distribution as a function of D_max.
7"""
8
9# global
10import sys
11import os
12import logging
13import numpy as np
14from PyQt5 import QtCore
15from PyQt5 import QtGui
16from PyQt5 import QtWidgets
17
18# sas-global
19from sas.qtgui.Plotting.PlotterData import Data1D
20from sas.qtgui.Plotting.Plotter import PlotterWidget
21import sas.qtgui.Utilities.GuiUtils as GuiUtils
22
23# local
24from .UI.DMaxExplorer import Ui_DmaxExplorer
25
26logger = logging.getLogger(__name__)
27
28from sas.qtgui.Utilities.GuiUtils import enum
29
30W = enum( 'NPTS',      #0
31          'DMIN',      #1
32          'DMAX',      #2
33          'VARIABLE',  #3
34)
35
36class DmaxWindow(QtWidgets.QDialog, Ui_DmaxExplorer):
37    # The controller which is responsible for managing signal slots connections
38    # for the gui and providing an interface to the data model.
39    name = "Dmax Explorer"  # For displaying in the combo box
40
41    def __init__(self, pr_state, nfunc, parent=None):
42        super(DmaxWindow, self).__init__()
43        self.setupUi(self)
44
45        self.setWindowTitle("Dₐₓ Explorer")
46
47        self.pr_state = pr_state
48        self.nfunc = nfunc
49        self.communicator = GuiUtils.Communicate()
50
51        self.plot = PlotterWidget(self, self)
52        self.hasPlot = None
53        self.verticalLayout.insertWidget(0, self.plot)
54
55        # Let's choose the Standard Item Model.
56        self.model = QtGui.QStandardItemModel(self)
57        self.mapper = None
58
59        # Add validators on line edits
60        self.setupValidators()
61
62        # # Connect buttons to slots.
63        # # Needs to be done early so default values propagate properly.
64        self.setupSlots()
65
66        # Set up the model.
67        self.setupModel()
68
69        # # Set up the mapper
70        self.setupMapper()
71
72    def setupValidators(self):
73        """Add validators on relevant line edits"""
74        self.Npts.setValidator(QtGui.QIntValidator())
75        self.minDist.setValidator(GuiUtils.DoubleValidator())
76        self.maxDist.setValidator(GuiUtils.DoubleValidator())
77
78    def setupSlots(self):
79        self.closeButton.clicked.connect(self.close)
80        self.model.itemChanged.connect(self.modelChanged)
81        self.dependentVariable.currentIndexChanged.connect(lambda:self.modelChanged(None))
82
83    def setupModel(self):
84        self.model.setItem(W.NPTS, QtGui.QStandardItem(str(self.nfunc)))
85        self.model.setItem(W.DMIN, QtGui.QStandardItem("{:.1f}".format(0.9*self.pr_state.d_max)))
86        self.model.setItem(W.DMAX, QtGui.QStandardItem("{:.1f}".format(1.1*self.pr_state.d_max)))
87        self.model.setItem(W.VARIABLE, QtGui.QStandardItem( "χ²/dof"))
88
89    def setupMapper(self):
90        self.mapper = QtWidgets.QDataWidgetMapper(self)
91        self.mapper.setOrientation(QtCore.Qt.Vertical)
92        self.mapper.setModel(self.model)
93
94        self.mapper.addMapping(self.Npts, W.NPTS)
95        self.mapper.addMapping(self.minDist, W.DMIN)
96        self.mapper.addMapping(self.maxDist, W.DMAX)
97        self.mapper.addMapping(self.dependentVariable, W.VARIABLE)
98
99        self.mapper.toFirst()
100
101    def variableChanged(self, index):
102        """
103        Respond to combobox update
104        """
105        # Just fire the model change signal, mate
106        pass
107
108    def modelChanged(self, item):
109        if not self.mapper:
110            return
111
112        iq0 = []
113        rg = []
114        pos = []
115        pos_err = []
116        osc = []
117        bck = []
118        chi2 = []
119
120        xs = np.linspace(float(self.model.item(W.DMIN).text()),
121                         float(self.model.item(W.DMAX).text()),
122                         float(self.model.item(W.NPTS).text()))
123
124        original = self.pr_state.d_max
125        for x in xs:
126            self.pr_state.d_max = x
127            try:
128                out, cov = self.pr_state.invert(self.pr_state.nfunc)
129
130                iq0.append(self.pr_state.iq0(out))
131                rg.append(self.pr_state.rg(out))
132                pos.append(self.pr_state.get_positive(out))
133                pos_err.append(self.pr_state.get_pos_err(out, cov))
134                osc.append(self.pr_state.oscillations(out))
135                bck.append(self.pr_state.background)
136                chi2.append(self.pr_state.chi2)
137            except Exception as ex:
138                # This inversion failed, skip this D_max value
139                msg = "ExploreDialog: inversion failed "
140                msg += "for D_max=%s\n%s" % (str(x), ex)
141                print(msg)
142                logger.error(msg)
143
144        #Return the invertor to its original state
145        self.pr_state.d_max = original
146        try:
147            self.pr_state.invert(self.nfunc)
148        except RuntimeError as ex:
149            msg = "ExploreDialog: inversion failed "
150            msg += "for D_max=%s\n%s" % (str(x), ex)
151            print(msg)
152            logger.error(msg)
153
154        plotter = self.model.item(W.VARIABLE).text()
155        y_label = y_unit = ""
156        x_label = "D_{max}"
157        x_unit = "A"
158        if plotter == "χ²/dof":
159            ys = chi2
160            y_label = "\chi^2/dof"
161            y_unit = "a.u."
162        elif plotter == "I(Q=0)":
163            ys = iq0
164            y_label = "I(q=0)"
165            y_unit = "\AA^{-1}"
166        elif plotter == "Rg":
167            ys = rg
168            y_label = "R_g"
169            y_unit = "\AA"
170        elif plotter == "Oscillation parameter":
171            ys = osc
172            y_label = "Osc"
173            y_unit = "a.u."
174        elif plotter == "Background":
175            ys = bck
176            y_label = "Bckg"
177            y_unit = "\AA^{-1}"
178        elif plotter == "Positive Fraction":
179            ys = pos
180            y_label = "P^+"
181            y_unit = "a.u."
182        else:
183            ys = pos_err
184            y_label = "P^{+}_{1\sigma}"
185            y_unit = "a.u."
186
187        data = Data1D(xs, ys)
188        if self.hasPlot:
189            self.plot.removePlot(None)
190        self.hasPlot = True
191        data.title = plotter
192        data._xaxis= x_label
193        data._xunit = x_unit
194        data._yaxis = y_label
195        data._yunit = y_unit
196        self.plot.plot(data=data, marker="-")
Note: See TracBrowser for help on using the repository browser.