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

ESS_GUI_opencl
Last change on this file since e0d5b63 was e0d5b63, checked in by wojciech, 5 years ago

Fixing opencl driver issue

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