Plotting the solution to a differential equation solved in a for loop

49 views
Skip to first unread message

Michael DeFeo

unread,
Mar 28, 2019, 1:48:03 PM3/28/19
to pyqtgraph
I am trying to write a program that will solve a differential equation for several values of one of the equations parameters in a for loop. After each iteration of the loop I would like to plot the mean value of the solution. What happens in practice is the the plot will only be generated after exiting the for loop. A minimal working example demonstrating this issue is pasted below and attached. Any advice on how to get this working is appreciated. 

Cheers!



# gui
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np

# ODE
from scipy.integrate import RK45

win = pg.GraphicsWindow()
p1 = win.addPlot()
data = np.empty([100000,2])     # Holds time dep. solution

g = 0.5
cntr = 0

def EOM(t,y):
    global g
    return -g*y

def update():
    p1.plot(tcData[:cntr,:])

timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

tC = np.linspace(-0.5,0.5,20)   # Sweep parameter
tcData = np.empty([len(tC),2])  # Holds mean solution (plot this data)
for idx,item in enumerate(tC):
    ode = RK45(EOM, 0, [5],t_bound=10,max_step=0.001)
    cntr = 0
    g = item
    while ode.status == 'running':
        ode.step()
        data[cntr,0] = ode.t
        data[cntr,1] = ode.y
        cntr+=1
    tcData[idx,0] = item
    tcData[idx,1] = np.mean(data[:,1])
    print( idx )


if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

diffEqGraphDemo.py

Jim Crowell

unread,
Mar 29, 2019, 9:56:27 AM3/29/19
to pyqtgraph
The easy way to do it is to break your loop into smaller chunks (one or more steps) that get run periodically by a timer. See the scrollingPlots example.

The hard way would be to run your ODE solver in a separate thread and post the results back to the main thread using a signal/slot pair. In either case you should use setData() as in the example to update the plot.

Michael DeFeo

unread,
Mar 30, 2019, 3:59:45 PM3/30/19
to pyqtgraph
Thank you very much for your reply. I was able to get it working by using QTimers to call the functions that evaluate the ODE as you suggested.

Cheers,
Mike

Patrick

unread,
Mar 31, 2019, 8:30:37 PM3/31/19
to pyqtgraph
Hi,

I thought an explanation of why your original code didn't work might be helpful. Since pyqtgraph is based off Qt, then understanding the Qt event loop is important - all of the GUI updates must be done from within the Qt event loop. Data acquisition or processing can be done on separate threads, but should be communicated back to the GUI through Qt signal/slot style methods.

In your code, your program starts up and does the DE solving code immediately. Interestingly, your timer.start() call doesn't actually start the timer yet! Your program then hits the __name__ == '__main__' part, where the .exec_() line starts the Qt event loop thread. This then begins triggering events, such as displaying the GUI and starting the timer calls. So by the time your GUI is drawn, the DE solving is done.

Jim's answer above is correct. If you're interested in doing this the alternative way, processing on a separate thread (and doing the signal/slots thing), then the code from this answer might help explain how to do that: https://groups.google.com/forum/#!msg/pyqtgraph/ajykxBvysEc/e1V8lvZSCgAJ

Patrick

Michael DeFeo

unread,
Apr 2, 2019, 9:14:24 AM4/2/19
to pyqtgraph
Hi Patrick,

Thank you for the explanation. I will read through the thread you linked to. I have been using pyqtgraph for various things for a couple of years--it is probably time I learn about the Qt event loop. If I were to make my script into a class and instance the class after 'if __name__ == '__main__': would the plotting have worked as expected since the timer would start prior to solve the DE?

Cheers,
Mike

Patrick

unread,
Apr 2, 2019, 8:25:14 PM4/2/19
to pyqtgraph
Hi,

Yes and no. If you just copied your entire code into the __init__ method of a Widget, it would behave exactly the same (the DE solving loop would block up the init, so UI wouldn't be completed until after it finished). You would still need to either trigger single iterations of the loop with a timer as I think you have done, which lets the UI update work proceed in between, or do it multithreaded as per the link I posted, where your DE solving loop would be doing its own thing in the separate generate_data() thread, but you do the callback(data) once each time the solution updates, which will then trigger the redraw of the UI in its own thread.

Patrick
Reply all
Reply to author
Forward
0 new messages