source: sasview/src/sas/qtgui/Perspectives/Fitting/GPUOptions.py @ 54398d5

ESS_GUIESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_sync_sascalc
Last change on this file since 54398d5 was 8479735, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 6 years ago

Updates to the GPU options panel after PR 191 in master.

  • Property mode set to 100644
File size: 8.4 KB
Line 
1# global
2import os
3import sys
4import json
5import platform
6import webbrowser
7import logging
8
9import sasmodels
10import sasmodels.model_test
11import sasmodels.kernelcl
12
13import sas.qtgui.Utilities.GuiUtils as GuiUtils
14from PyQt5 import QtGui, QtCore, QtWidgets
15from sas.qtgui.Perspectives.Fitting.UI.GPUOptionsUI import Ui_GPUOptions
16from sas.qtgui.Perspectives.Fitting.UI.GPUTestResultsUI import Ui_GPUTestResults
17
18try:
19    _fromUtf8 = QtCore.QString.fromUtf8
20except AttributeError:
21    def _fromUtf8(s):
22        return s
23
24try:
25    _encoding = QtWidgets.QApplication.UnicodeUTF8
26    def _translate(context, text, disambig):
27        return QtWidgets.QApplication.translate(context, text, disambig, _encoding)
28except AttributeError:
29    def _translate(context, text, disambig):
30        return QtWidgets.QApplication.translate(context, text, disambig)
31
32logger = logging.getLogger(__name__)
33
34class GPUOptions(QtWidgets.QDialog, Ui_GPUOptions):
35    """
36    OpenCL Dialog to select the desired OpenCL driver
37    """
38
39    clicked = False
40    sas_open_cl = None
41    cl_options = None
42
43    def __init__(self, parent=None):
44        super(GPUOptions, self).__init__(parent)
45        self.parent = parent
46        self.setupUi(self)
47        self.addOpenCLOptions()
48        self.createLinks()
49
50    def addOpenCLOptions(self):
51        """
52        Populate the window with a list of OpenCL options
53        """
54        # Get list of openCL options and add to GUI
55        cl_tuple = _get_clinfo()
56        self.sas_open_cl = os.environ.get("SAS_OPENCL", "")
57
58        # Keys are the names in the form "platform: device". Corresponding values are the combined indices, e.g.
59        # "0:1", for setting the SAS_OPENCL env.
60        self.cl_options = {}
61
62        for title, descr in cl_tuple:
63            # Create an item for each openCL option
64            check_box = QtWidgets.QCheckBox()
65            check_box.setObjectName(_fromUtf8(descr))
66            check_box.setText(_translate("GPUOptions", descr, None))
67            self.optionsLayout.addWidget(check_box)
68            if (title == self.sas_open_cl) or (
69                            title == "None" and not self.clicked):
70                check_box.click()
71                self.clicked = True
72            self.cl_options[descr] = title
73        self.openCLCheckBoxGroup.setMinimumWidth(self.optionsLayout.sizeHint().width()+10)
74
75    def createLinks(self):
76        """
77        Link user interactions to function calls
78        """
79        self.testButton.clicked.connect(self.testButtonClicked)
80        self.helpButton.clicked.connect(self.helpButtonClicked)
81        for item in self.openCLCheckBoxGroup.findChildren(QtWidgets.QCheckBox):
82            item.clicked.connect(self.checked)
83
84    def checked(self):
85        """
86        Only allow a single check box to be selected. Uncheck others.
87        """
88        checked = None
89        for box in self.openCLCheckBoxGroup.findChildren(QtWidgets.QCheckBox):
90            if box.isChecked() and (self.cl_options[str(box.text())] == self.sas_open_cl or (
91                    str(box.text()) == "No OpenCL" and self.sas_open_cl == "")):
92                box.setChecked(False)
93            elif box.isChecked():
94                checked = box
95        if hasattr(checked, "text"):
96            self.sas_open_cl = self.cl_options[str(checked.text())]
97        else:
98            self.sas_open_cl = None
99
100    def set_sas_open_cl(self):
101        """
102        Set SAS_OPENCL value when tests run or OK button clicked
103        """
104        no_opencl_msg = False
105        if self.sas_open_cl:
106            os.environ["SAS_OPENCL"] = self.sas_open_cl
107            if self.sas_open_cl.lower() == "none":
108                no_opencl_msg = True
109        else:
110            if "SAS_OPENCL" in os.environ:
111                del os.environ["SAS_OPENCL"]
112        # Sasmodels kernelcl doesn't exist when initiated with None
113        sasmodels.kernelcl.reset_environment()
114        return no_opencl_msg
115
116    def testButtonClicked(self):
117        """
118        Run sasmodels check from here and report results from
119        """
120
121        no_opencl_msg = self.set_sas_open_cl()
122
123        # Only import when tests are run
124        from sasmodels.model_test import model_tests
125
126        try:
127            env = sasmodels.kernelcl.environment()
128            clinfo = [(ctx.devices[0].platform.vendor,
129                       ctx.devices[0].platform.version,
130                       ctx.devices[0].vendor,
131                       ctx.devices[0].name,
132                       ctx.devices[0].version)
133                      for ctx in env.context]
134        except Exception:
135            clinfo = None
136
137        failures = []
138        tests_completed = 0
139        for test in model_tests():
140            try:
141                test()
142            except Exception:
143                failures.append(test.description)
144
145            tests_completed += 1
146
147        info = {
148            'version': sasmodels.__version__,
149            'platform': platform.uname(),
150            'opencl': clinfo,
151            'failing tests': failures,
152        }
153
154        msg = str(tests_completed) + ' tests completed.\n'
155        if len(failures) > 0:
156            msg += str(len(failures)) + ' tests failed.\n'
157            msg += 'Failing tests: '
158            msg += json.dumps(info['failing tests'])
159            msg += "\n"
160        else:
161            msg += "All tests passed!\n"
162
163        msg += "\nPlatform Details:\n\n"
164        msg += "Sasmodels version: "
165        msg += info['version'] + "\n"
166        msg += "\nPlatform used: "
167        msg += json.dumps(info['platform']) + "\n"
168        if no_opencl_msg:
169            msg += "\nOpenCL driver: None"
170        else:
171            msg += "\nOpenCL driver: "
172            msg += json.dumps(info['opencl']) + "\n"
173        GPUTestResults(self, msg)
174
175    def helpButtonClicked(self):
176        """
177        Open the help menu when the help button is clicked
178        """
179        help_location = GuiUtils.HELP_DIRECTORY_LOCATION
180        help_location += "/user/qtgui/Perspectives/Fitting/gpu_setup.html"
181        help_location += "#device-selection"
182        # Display the page in default browser
183        webbrowser.open('file://' + os.path.realpath(help_location))
184
185    def reject(self):
186        """
187        Close the window without modifying SAS_OPENCL
188        """
189        self.closeEvent(None)
190        self.parent.gpu_options_widget.open()
191
192    def accept(self):
193        """
194        Close the window after modifying the SAS_OPENCL value
195        """
196        self.set_sas_open_cl()
197        self.closeEvent(None)
198
199    def closeEvent(self, event):
200        """
201        Overwrite QDialog close method to allow for custom widget close
202        """
203        self.close()
204        self.parent.gpu_options_widget = GPUOptions(self.parent)
205
206
207class GPUTestResults(QtWidgets.QDialog, Ui_GPUTestResults):
208    """
209    OpenCL Dialog to modify the OpenCL options
210    """
211    def __init__(self, parent, msg):
212        super(GPUTestResults, self).__init__(parent)
213        self.setupUi(self)
214        self.resultsText.setText(_translate("GPUTestResults", msg, None))
215        #self.setFixedSize(self.size())
216        self.open()
217
218
219def _get_clinfo():
220    """
221    Read in information about available OpenCL infrastructure
222    """
223    clinfo = []
224    cl_platforms = []
225
226    try:
227        import pyopencl as cl
228    except ImportError:
229        cl = None
230
231    if cl is None:
232        logger.warn("Unable to import the pyopencl package.  It may not "
233                    "have been installed.  If you wish to use OpenCL, try "
234                    "running pip install --user pyopencl")
235    else:
236        try:
237            cl_platforms = cl.get_platforms()
238        except cl.LogicError as err:
239            logger.warn("Unable to fetch the OpenCL platforms.  This likely "
240                        "means that the opencl drivers for your system are "
241                        "not installed.")
242            logger.warn(err)
243
244    p_index = 0
245    for cl_platform in cl_platforms:
246        d_index = 0
247        cl_devices = cl_platform.get_devices()
248        for cl_device in cl_devices:
249            if len(cl_platforms) > 1 and len(cl_devices) > 1:
250                combined_index = ":".join([str(p_index), str(d_index)])
251            elif len(cl_platforms) > 1:
252                combined_index = str(p_index)
253            else:
254                combined_index = str(d_index)
255            clinfo.append((combined_index, ": ".join([cl_platform.name,
256                                                     cl_device.name])))
257            d_index += 1
258        p_index += 1
259
260    clinfo.append(("None", "No OpenCL"))
261    return clinfo
Note: See TracBrowser for help on using the repository browser.