0

I know this is an old question and have already found answers in other questions like this thread here. However, I have some problems applying it in my case.

The way I have things right now are the following: I have my MainWindow class where I can input some data. Then I have a Worker class which is a PySide2.QtCore.QThread object. To this class I pass some input data from the MainWindow. Inside this Worker class I have a method which sets up some ODEs, which in another method of the Worker class are being solved by scipy.integrate.solve_ivp. When the integration is done, I send the results via a signal back to the MainWindow. So the code roughly looks like this:

import PySide2
from scipy.integrate import solve_ivp

class Worker(QtCore.QThread):

    def __init__(self,*args,**kwargs):
        super(Worker,self).__init__()
        "Here I collect input parameters"

    def run(self):
        "Here I call solve_ivp for the integration and send a signal with the 
        solution when it is done"

    def ode_fun(self,t,c):
        "Function where the ode equations are set up"


class Ui_MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        "set up the GUI"
        self.btnStartSimulation.clicked.connect(self.start_simulation) #button to start the integration

    def start_simulation(self):
        self.watchthread(Worker)
        self.thread.start()

    def watchthread(self,worker):
        self.thread = worker("input values")
        "connect to signals from the thread"

Now I understand, that using the multiprocessing module I should be able to run the thread with the integration on another processor core to make it faster and make the GUI less laggy. However, from the link above I am not sure how I should apply this module or even how to restructure my code. Do I have to put the code that I now have in my Worker class into another class or am I somehow able to apply the multiprocessing module on my existing thread? Any help is greatly appreciated!

Edit: The new code looks like this:

class Worker(QtCore.QThread):

    def __init__(self,*args,**kwargs):
        super(Worker,self).__init__()
        self.operation_parameters = args[0]
        self.growth_parameters = args[1]
        self.osmolality_parameters = args[2]
        self.controller_parameters = args[3]
        self.c_zero = args[4]

    def run(self):
        data = multiprocessing.Queue()
        input_dict = {"function": self.ode_fun_vrabel_rushton_scaba_cont_co2_oxygen_biomass_metabol,
                      "time": [0, self.t_final],
                      "initial values": self.c_zero}
        data.put(input_dict)
        self.ode_process = multiprocessing.Process(target=self.multi_process_function, args=(data,))
        self.ode_process.start()

        self.solution = data.get()

    def multi_process_function(self,data):
        self.message_signal = True
        input_dict = data.get()
        solution = solve_ivp(input_dict["function"], input_dict["time"],
                      input_dict["initial values"], method="BDF")
        data.put(solution)

    def ode_fun(self,t,c):
        "Function where the ode equations are set up"
        (...) = self.operation_parameters
        (...) = self.growth_parameters
        (...) = self.osmolality_parameters
        (...) = self.controller_parameters

Is it okay if I access the parameters in the ode_fun function via self."parameter_name"? Or do I also have to pass them with the data-parameter?
With the current code I receive the following error: TypeError: can't pickle Worker objects

1 Answers1

0

You could call it from your worker like this:

import PySide2
from scipy.integrate import solve_ivp
import multiprocessing

class Worker(QtCore.QThread):

    def __init__(self,*args,**kwargs):
        super(Worker, self).__init__()
        self.ode_process = None

        "Here I collect input parameters"

    def run(self):
        "Here I call solve_ivp for the integration and send a signal with the solution when it is done"
        data = multiprocessing.Queue()
        data.put("all objects needed in the process, i would suggest a single dict from which you extract all data")
        self.ode_process = multiprocessing.Process(target="your heavy duty function", args=(data,))
        self.ode_process.start()  # this is non blocking

        # if you want it to block:
        self.ode_process.join()
        # make sure you remove all input data from the queue and fill it with the result, then to get it back:
        results = data.get()

        print(results)  # or do with it what you want to do...

    def ode_fun(self, t, c):
        "Function where the ode equations are set up"


class Ui_MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        "set up the GUI"
        self.btnStartSimulation.clicked.connect(self.start_simulation) #button to start the integration

    def start_simulation(self):
        self.watchthread(Worker)
        self.thread.start()

    def watchthread(self,worker):
        self.thread = worker("input values")
        "connect to signals from the thread"

Also beware that you would overwrite the running process now every time you press to start the simulation. You may want to use some sort of lock for that.

Marc
  • 1,539
  • 8
  • 14
  • Thanks for your help! Take a look at my edit for the current code. I retrieve an error when I want to start the process: "TypeError: can't pickle Worker objects" – Dominik Schieman Oct 07 '19 at 08:04
  • Hmm what if you try to move the multi_process_function outside of your class, it seems to me that it does not need anything from the self? There seems to be something in your Worker class it is not able to pickle and send to the other process. The self.message_signal can also be set in the run() function right? – Marc Oct 07 '19 at 08:33
  • I think the problem is my ode_fun i want to pass. According to this thread ([link](https://stackoverflow.com/a/8805244/8800551)) I think I also have to move my ode_fun out of the class and not pass it via the dict. What do you think? – Dominik Schieman Oct 07 '19 at 08:54
  • Definitely worth a shot! With these things I tend to test where exactly things are going wrong, tryout what object exactly is causing this error and then see how that can be moved to a place where it does not cause a problem. – Marc Oct 07 '19 at 08:58