Hallo liebes Forum,
ich habe ein Programm entworfen, welches die Turing-Patterns simuliert. Dieses berechnet über eine gewisse Anzahl von Schritten, die Reaktions-Diffusionsgleichungen neu. Da das ganze bei Simulationen mit 200.000 Schritten eine sehr langwierige Sache ist, wollte ich nachfragen ob es möglich ist das Programm auf mehreren Cpu-Kernen laufen zu lassen und wie ich so etwas implementieren könnte. (Nutze aktuell Jupyter-Notebook)
Vielen Dank schon mal für jegliche Hilfe
liebe Grüße
CaptainNeemo
Multi-Core-Processing
-
- User
- Beiträge: 14
- Registriert: Sonntag 6. Dezember 2020, 20:50
@sirius3: Hier der Code
Code: Alles auswählen
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
%matplotlib inline
class basement:
"""
Das Basissystem
"""
def start_fig(self): #Erstellen der Figur und der Achse
fig, ax = plt.subplots()
return fig, ax
def final_figure(self, filename, timesteps): #Kreieren der Figur und speichern dieser
self.initialise()
fig, ax = self.start_fig()
for i in range(timesteps):
self.next()
self.create(ax)
fig.savefig(filename, dpi = 300)
plt.close()
def create_gif(self, filename, timesteps=20): #Gif kreieren
self.initialise()
fig, ax = self.start_fig()
def step(t):
self.next()
self.create(ax)
simu = animation.FuncAnimation(fig, step, frames=np.arange(timesteps), interval=20)
simu.save(filename=filename, dpi=300, fps=20)
plt.close()
def start(shape): #Grundumgebung schaffen
return (
np.random.normal(loc=0, scale=1.0, size=shape),
np.random.normal(loc=0, scale=1.0, size=shape)
)
def derivate(a, dx):
return (
- 4 * a
+ np.roll(a,1,axis=0)
+ np.roll(a,-1,axis=0)
+ np.roll(a,+1,axis=1)
+ np.roll(a,-1,axis=1)
) / (dx ** 2)
def Ra(a,b): return a - a ** 3 - b + alpha #Berechnen der Reaktion der beiden Stoffe
def Rb(a,b): return (a - b) * beta
#Paramter zum Einstellen der Png und der eigentlichen Funktionsparameter
Da, Db, alpha, beta = 1, 100, 0.01, 10
width = 200
dx = 1
dt = 0.001
class Main(basement):
def __init__(self, Da, Db, Ra, Rb, #Initialisieren der Variablen
width=1000, height=1000,
dx=1, dt=0.1, steps=1, initialiser=start):
self.Da = Da
self.Db = Db
self.Ra = Ra
self.Rb = Rb
self.initialiser = initialiser
self.width = width
self.height = height
self.shape = (width, height)
self.dx = dx
self.dt = dt
self.steps = steps
def initialise(self): #Initialisieren der Form
self.t = 0
self.a, self.b = self.initialiser(self.shape)
def next(self): #Ein weiterer Zeitschritt dt und nachladen aufrufen
for i in range(self.steps):
self.t += self.dt
self.reload()
def reload(self): #Neuladen und berechnen der Diffusionsgleichungen
der_a = derivate(self.a, self.dx)
d_A = self.dt * (self.Da * der_a + Ra(self.a,self.b))
self.a += d_A
der_b = derivate(self.b, self.dx)
d_B = self.dt * (self.Db * der_b + Rb(self.a,self.b))
self.b += d_B
def create(self, ax): #Löschen des vorangegangenen Plots, neue Zeit als Legende hinzufügen und Plot zeigen
ax[0].clear()
ax[0].imshow(self.a, cmap = "coolwarm")
ax[0].set_title("A, t = {0:.1f}".format(self.t))
ax[0].grid(b=False)
ax[1].clear()
ax[1].imshow(self.b, cmap = "Spectral")
ax[1].set_title("B, t = {0:.1f}".format(self.t))
ax[1].grid(b=False)
def start_fig(self): #Figur entwickeln im Basement System
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(12,6))
return fig, ax
#Aufrufen der Main Funktion mit den entsprechenden Variablen und der Möglichkeit die Zeitschritte zu wählen
Main(
Da, Db, Ra, Rb,
width=width, height=width,
dx=dx, dt=dt, steps=100
).final_figure("Turing-Muster2.pdf", timesteps=200)
@CaptainNeemo: Das ist auf den ersten Blick kein Problem, das embarrassingly parallel ist, da muss man beim Parallelisieren also zumindest mal nachdenken, wenn es überhaupt möglich ist.
Erster Schritt sollte immer sein, dass man mit Messen anfängt. Wo ist es langsam, kann ich an den Stellen was machen? Gibt es vielleicht bessere Algorithmen? Es gibt dafür `cProfile` in der Standardbibliothek, und das kann man dann mit verschiedenen Werkzeugen visualisieren, zum Beispiel `gprof2dot` oder einem der vielen Flamegraph-Tools.
Ich habe den Code nur überflogen, aber du hast da ein `np.roll` in der inneren Schleife. Das sieht so aus, als wenn es das Array komplett neu anlegen müsste. Das bedeutet also, dass du mit jedem `derivate`-Aufruf 10 Mal ein Array mit einer Million Elementen erzeugst. Da würde ich also (nach Gefühl) ansetzen.
Erster Schritt sollte immer sein, dass man mit Messen anfängt. Wo ist es langsam, kann ich an den Stellen was machen? Gibt es vielleicht bessere Algorithmen? Es gibt dafür `cProfile` in der Standardbibliothek, und das kann man dann mit verschiedenen Werkzeugen visualisieren, zum Beispiel `gprof2dot` oder einem der vielen Flamegraph-Tools.
Ich habe den Code nur überflogen, aber du hast da ein `np.roll` in der inneren Schleife. Das sieht so aus, als wenn es das Array komplett neu anlegen müsste. Das bedeutet also, dass du mit jedem `derivate`-Aufruf 10 Mal ein Array mit einer Million Elementen erzeugst. Da würde ich also (nach Gefühl) ansetzen.