diff --git a/cxxfem/_src/femi.i b/cxxfem/_src/femi.i
index 4cf565837dd454088d3fab725ba92256c5a5a558..1cfe5ad3620df2363001c48d6c4b1724e438a1e2 100644
--- a/cxxfem/_src/femi.i
+++ b/cxxfem/_src/femi.i
@@ -19,6 +19,7 @@ threads="1"
 #include "femExtractor.h"
 
 #include "wCppBuf2Py.h"
+#include "OpenMP.h"
 
 #include <string>
 // #include <sstream>
@@ -77,6 +78,7 @@ namespace std {
 %warnfilter(401); //Nothing known about base class 'std::basic_streambuf< char >'
 %include "wCppBuf2Py.h"
 %warnfilter(+401);
+%include "OpenMP.h"
 
 // ----------- FEM CLASSES ---------------
 %include "fem.h"
diff --git a/cxxfem/src/femSolver.cpp b/cxxfem/src/femSolver.cpp
index 065bc22cc3a325ec45b349925c7cc3790c61e2fc..57f33c6e0363a5eb197dc1c84b2992aef9d0ec8d 100644
--- a/cxxfem/src/femSolver.cpp
+++ b/cxxfem/src/femSolver.cpp
@@ -33,7 +33,7 @@ Solver::Solver(Problem &_pbl) : pbl(_pbl),
     bcs.add("FREE");
     bcs.add("PRESCRIBED");
 
-    // std::cout << "#threads = " << OpenMP::get_max_threads() << '\n';
+    std::cout << "#threads = " << OpenMP::get_max_threads() << '\n';
 }
 
 Solver::~Solver()
diff --git a/cxxfem/tests/beam3d.py b/cxxfem/tests/beam3d.py
index d6de2a7baf4854357806dfd9ec1804d0e1d1e159..6e8f6f14997227909f09ebcb8634c85f218b6243 100644
--- a/cxxfem/tests/beam3d.py
+++ b/cxxfem/tests/beam3d.py
@@ -9,25 +9,25 @@ fem.debug_level(0)
 if __name__ == "__main__":
 
     # fine mesh
-    # pars = {
-    #     "Lx": 5.0, # width
-    #     "Ly": 1.0, # depth
-    #     "Lz": 1.0, # height/thickness
-    #     "nx": 60,         
-    #     "ny": 30,
-    #     "nz": 30
-    # }
-
-    # same as python
     pars = {
         "Lx": 5.0, # width
         "Ly": 1.0, # depth
         "Lz": 1.0, # height/thickness
-        "nx": 15,         
-        "ny": 2,
-        "nz": 10
+        "nx": 60,         
+        "ny": 30,
+        "nz": 30
     }
 
+    # same as python
+    # pars = {
+    #     "Lx": 5.0, # width
+    #     "Ly": 1.0, # depth
+    #     "Lz": 1.0, # height/thickness
+    #     "nx": 15,         
+    #     "ny": 2,
+    #     "nz": 10
+    # }
+
     # mono-element for debug
     # pars = {
     #     "Lx": 5.0, # width
diff --git a/cxxfem/utils.py b/cxxfem/utils.py
index c3ab0dd7768f7880d48853c844994ba1cec0ae02..00a2ae1ed638491017bc3e81b41df61e9171ad11 100644
--- a/cxxfem/utils.py
+++ b/cxxfem/utils.py
@@ -37,13 +37,16 @@ def parseargs():
     """
     parses command line arguments
     """
+    import os
     import argparse
     parser = argparse.ArgumentParser()
-    parser.add_argument(
-        "-v", "--verb", help="increase output verbosity", action="count", default=0)
+    # parser.add_argument(
+    #     "-v", "--verb", help="increase output verbosity", action="count", default=0)
     parser.add_argument(
         "--nogui", help="disable any graphical output", action="store_true")
-    parser.add_argument("-k", help="nb of threads", type=int, default=1)
-    parser.add_argument('file', help='python files')
+    parser.add_argument(
+        "--post", help="display existing results", action="store_true")
+    parser.add_argument("-k", help="nb of threads", type=int, default=os.cpu_count())
+    parser.add_argument('file', help='python file', type=str, nargs='?')
     args = parser.parse_args()
     return args
diff --git a/fossil.png b/fossil.png
new file mode 100644
index 0000000000000000000000000000000000000000..768b48306290e3d19f175a79776389b746d82c97
Binary files /dev/null and b/fossil.png differ
diff --git a/fossils.py b/fossils.py
index 3b0546f6dfa98a181a9a8b2e796f3787bc82b9e0..dda22a3bbb128fff87575e3882e40ebdf49e9df5 100644
--- a/fossils.py
+++ b/fossils.py
@@ -2,9 +2,8 @@
 # -*- coding: utf-8 -*-
 # runs a test as if it was installed
 
-import os
-import platform
-
+# import os
+# import platform
 # if 'Windows' in platform.uname():
 #     # remove tbb from the PATH
 #     #   otherwise: "Intel MKL FATAL ERROR: Cannot load mkl_intel_thread.dll"
@@ -14,6 +13,56 @@ import platform
 #     for p in corr_path:
 #         print(p)
 
+from PyQt5.QtCore import *
+from PyQt5.QtGui import *
+from PyQt5.QtWidgets import *
+from ui_fossils import Ui_Form
+
+class Window(QWidget, Ui_Form):
+    """Minimal GUI asking for a file and running it
+    """
+    def __init__(self, parent=None):
+        super(Window, self).__init__(parent)
+        self.setupUi(self)
+
+        # read Qt settings for the application
+        settings = QSettings()
+        # self.restoreGeometry(settings.value("Geometry", self.saveGeometry()))
+        self.inpFileLineEdit.setText(settings.value("inpFile", ""))
+
+        self.action = 'cancelled'
+
+        iconfile = os.path.join('fossil.png')
+        self.setWindowIcon(QIcon(iconfile))
+
+    def on_runPushButton_pressed(self):
+        # print("runPushButton...")
+        self.action = 'run'
+        self.close()
+
+    def on_viewPushButton_pressed(self):
+        # print("viewPushButton...")
+        self.action = 'post'
+        self.close()
+
+    def on_inpFilePushButton_pressed(self):
+        # print("inpFilePushButton...")
+        fname = QFileDialog.getOpenFileName(
+            None, 'Select input file', '', filter='Python File (*.py)')
+        # print(fname)    
+        pyfile = fname[0]
+        if pyfile:
+            self.inpFileLineEdit.setText(QDir.toNativeSeparators(pyfile))    
+
+    def closeEvent(self, event):
+        """save settings to registry and quit
+        """
+        settings = QSettings()
+        # settings.setValue("Geometry", QVariant(self.saveGeometry()))
+        settings.setValue("inpfile", QVariant(self.inpFileLineEdit.text()))
+        event.accept()
+
+
 
 if __name__ == "__main__":
     import sys
@@ -39,6 +88,18 @@ if __name__ == "__main__":
     args = cxxfem.parseargs()
     # set number of threads from the "-k" argument
     os.environ['OMP_NUM_THREADS'] = str(args.k)
+    cxxfem.set_num_threads(args.k)
+
+    # try:
+    #     ncpus1 = int(os.environ['NUMBER_OF_PROCESSORS'])
+    #     print(f'{ncpus1} CPUs detected')
+    #     import multiprocessing
+    #     ncpus2 = multiprocessing.cpu_count()
+    #     print(f'{ncpus2} CPUs detected')
+    #     ncpus3 = os.cpu_count()
+    #     print(f'{ncpus3} CPUs detected')
+    # except:
+    #     pass
 
     # display env variables
     try:
@@ -46,22 +107,41 @@ if __name__ == "__main__":
     except:
         print(f"OMP_NUM_THREADS=[not set]")
 
-    # run test file
-    testname = os.path.abspath(args.file)
-    testname = os.path.normcase(testname)  # F:/ => f:/ on Windows
-    # create workspace
-    common = os.path.commonprefix((testname, thisdir + os.sep))
-    resdir = testname[len(common):].replace(os.sep, "_")
-    resdir, ext = os.path.splitext(resdir)
-    wdir = os.path.join('workspace', resdir)
-    print('workspace=', wdir)
-    if not os.path.isdir(wdir):
-        os.makedirs(wdir)
-    os.chdir(wdir)
-
-    tee = cxxfem.Tee('stdout.txt')  # split streams
-
-    if ext == '.py':
-        # run python script
-        __file__ = testname
-        exec(open(testname, encoding='utf-8').read())
+    
+    # ask for a file if not given
+    if not args.file:
+        app = QApplication(sys.argv)
+        app.setOrganizationName("ULiege")
+        app.setApplicationName("fossils")
+        win = Window()
+        win.setWindowTitle("Fossils")
+        win.show()
+        app.lastWindowClosed.connect(app.quit)
+        app.exec_() 
+        action = win.action
+        testname = win.inpFileLineEdit.text()
+    else:
+        action = 'run'
+        testname = os.path.abspath(args.file)
+
+    print(f'action={action}')
+    if action=='run':
+        testname = os.path.normcase(testname)  # F:/ => f:/ on Windows
+        print(f'testname = {testname}')
+        
+        # create workspace
+        common = os.path.commonprefix((testname, thisdir + os.sep))
+        resdir = testname[len(common):].replace(os.sep, "_")
+        resdir, ext = os.path.splitext(resdir)
+        wdir = os.path.join('workspace', resdir)
+        print('workspace=', wdir)
+        if not os.path.isdir(wdir):
+            os.makedirs(wdir)
+        os.chdir(wdir)
+
+        tee = cxxfem.Tee('stdout.txt')  # split streams
+
+        if ext == '.py':
+            # run python script
+            __file__ = testname
+            exec(open(testname, encoding='utf-8').read())
diff --git a/fossils.ui b/fossils.ui
new file mode 100644
index 0000000000000000000000000000000000000000..6d8ab5a32d04d868f8165d5ced92cf30079ea717
--- /dev/null
+++ b/fossils.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>546</width>
+    <height>104</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="inpFilelabel">
+       <property name="text">
+        <string>input file</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="inpFileLineEdit"/>
+     </item>
+     <item>
+      <widget class="QPushButton" name="inpFilePushButton">
+       <property name="text">
+        <string>...</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="runPushButton">
+       <property name="text">
+        <string>Run</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="viewPushButton">
+       <property name="text">
+        <string>View</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gen_ui.py b/gen_ui.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f71975f6e072ebd45e3ddfda4943ba96868f718
--- /dev/null
+++ b/gen_ui.py
@@ -0,0 +1,65 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Execute "pyuic". 
+# Ask for the location of it if not found.
+# Store result in QSettings
+
+import sys
+import subprocess
+import os
+import platform
+from PyQt5.QtCore import *
+from PyQt5.QtGui import *
+from PyQt5.QtWidgets import *
+
+def tryEXE(exe):
+    try:
+        print("dry run of '%s'..." % exe, end=' ')
+        with open(os.devnull, 'w') as FNULL:
+            subprocess.call([exe], stdout=FNULL, stderr=subprocess.STDOUT)
+        print("OK!")
+        return exe
+    except OSError:
+        print("failure!")
+        return ""
+
+def askUser():
+    app = QApplication(sys.argv)
+    ext = '.*' if 'Windows' in platform.uname() else ''
+    fname = QFileDialog.getOpenFileName(
+        None, 'Select pyuic5', '', filter='Executable (pyuic5%s)' % ext)
+    exe = fname[0]
+    if not exe:
+        print('cancelled by user')
+        sys.exit(1)
+    print("user selected '%s'" % exe)
+    return exe
+
+if __name__ == "__main__":
+
+    exe = 'pyuic5'
+    if tryEXE(exe):  # try exe
+        pass
+    else:
+        # read settings
+        settings = QSettings("rboman", "progs")
+        #settings.remove("pyuic5") # clear stored value
+        exe = settings.value("pyuic5", "")
+        print("using value stored in settings: '%s'" % exe)
+
+        if exe and tryEXE(exe):
+            pass
+        else:
+            exe = askUser()
+            if tryEXE(exe):
+                settings = QSettings("rboman", "progs")
+                settings.setValue("pyuic5", QVariant(exe))
+            else:
+                raise Exception("'%s' does not work!" % exe)
+
+    # run cmd
+    cmd = [exe, 'fossils.ui', '-o', 'ui_fossils.py']
+    print("=> running", ' '.join(cmd))
+    iop = subprocess.call(cmd)
+    print('iop =', iop)
diff --git a/ui_fossils.py b/ui_fossils.py
new file mode 100644
index 0000000000000000000000000000000000000000..8df8ef6f161ca5e693d5f3deb47829fc719f13d7
--- /dev/null
+++ b/ui_fossils.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'fossils.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again.  Do not edit this file unless you know what you are doing.
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+    def setupUi(self, Form):
+        Form.setObjectName("Form")
+        Form.resize(546, 104)
+        self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+        self.verticalLayout.setObjectName("verticalLayout")
+        self.horizontalLayout = QtWidgets.QHBoxLayout()
+        self.horizontalLayout.setObjectName("horizontalLayout")
+        self.inpFilelabel = QtWidgets.QLabel(Form)
+        self.inpFilelabel.setObjectName("inpFilelabel")
+        self.horizontalLayout.addWidget(self.inpFilelabel)
+        self.inpFileLineEdit = QtWidgets.QLineEdit(Form)
+        self.inpFileLineEdit.setObjectName("inpFileLineEdit")
+        self.horizontalLayout.addWidget(self.inpFileLineEdit)
+        self.inpFilePushButton = QtWidgets.QPushButton(Form)
+        self.inpFilePushButton.setObjectName("inpFilePushButton")
+        self.horizontalLayout.addWidget(self.inpFilePushButton)
+        self.verticalLayout.addLayout(self.horizontalLayout)
+        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_2.addItem(spacerItem)
+        self.runPushButton = QtWidgets.QPushButton(Form)
+        self.runPushButton.setObjectName("runPushButton")
+        self.horizontalLayout_2.addWidget(self.runPushButton)
+        self.viewPushButton = QtWidgets.QPushButton(Form)
+        self.viewPushButton.setObjectName("viewPushButton")
+        self.horizontalLayout_2.addWidget(self.viewPushButton)
+        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_2.addItem(spacerItem1)
+        self.verticalLayout.addLayout(self.horizontalLayout_2)
+
+        self.retranslateUi(Form)
+        QtCore.QMetaObject.connectSlotsByName(Form)
+
+    def retranslateUi(self, Form):
+        _translate = QtCore.QCoreApplication.translate
+        Form.setWindowTitle(_translate("Form", "Form"))
+        self.inpFilelabel.setText(_translate("Form", "input file"))
+        self.inpFilePushButton.setText(_translate("Form", "..."))
+        self.runPushButton.setText(_translate("Form", "Run"))
+        self.viewPushButton.setText(_translate("Form", "View"))