Hi zusammen,
die n-body Simulation hat mich schon immer interessiert, zufällig hatte die c´t-Redaktion in einer der letzten Ausgaben einen Bericht zum Thema.
Allerdings haben die Jungs dort nicht in AutoIt auf einem Mono-Core getestet, sondern sind mit diversen Compileroptionen einem Xeon E5 2690, einem Opteron 6380 und einer Tesla K20X / Xeon Phi auf die Pelle gerückt 8o, die Dinger haben über den dicken Daumen 60 Prozessoren
Der Bericht ist jedenfalls lesenswert, gerade die C/C++-Fraktion bekommt wieder Prügel....(wem bspw. der Unterschied eines Array of Structures AoS zu einer Structure of Arrays SoA etwas sagt, der weiss auch warum...dafür gibts nämlich keine C/C++-interne Umorganisierung, welche mal eben den Faktor 2 in der Geschwindigkeit ausmacht, egal auf welchem Prozessor)
Genug geschwafelt, zum Script:
Da AutoIt alles andere als schnell ist, habe ich das Script schon für diverse Optimierungen vorgesehen
So werden keine Arrays verwendet, um die Koordinaten und die Geschwindigkeiten der Körper zu speichern, sondern Structs. Auf die kann man dann ohne das Schript umzubauen, bspw mit diversen Dll´s (C/C++ und Basic-Freaks, ich zähl auf euch!!!) oder mit asm-Code oder per OpenCL zugreifen.
Der Code ist recht einfach, es wird die Anziehungskraft (Gravitation) zwischen allen beteiligten Körpern berechnet und daraus für jeden Körper die resultierende Bewegung ermittelt.
Ich habe im Code überall die 3D-Darstellung mitgezogen, für die "Optik" aber nur 2D angezeigt, die z-Ebene ist also 0. Wer das ändern möchte, kommentiert einfach die entsprechende Zeile in der inneren Schleife aus, dann ist es "realistischer"
Da ich die GGEngine-UDF vom Kollegen TheShadowAE viel zu schade finde um sie versauern zu lassen, habe ich damit die "Grafik" erstellt.
Natürlich wäre "richtiges" GDI+ auch gegangen, aber spätestens, wenn jemand per Shaderprogramm und OpenGL loslegen will, ist damit sowieso Schluss^^
Los gehts mit 4 Körpern, bis zu 50 kann man noch mit AutoIt verarbeiten (Variable $particles), sollen es 10000 sein, ist Gehirnschmalz gefragt
Je nach Geschwindigkeit und Ballung der Körper kann es sein, dass einer der Kandidaten aus dem Universum geschossen wird...c´est la vie... bei mehr als 30 Körpern ist das aber auch unerheblich, ggf. einfach nochmal neu starten.
Um nur die Körper, ohne den Kometenschweif, anzeigen zu lassen, muss der Parameter $cleanflag auf 1 gesetzt werden.
Spoiler anzeigen
;Formeln
;Kraft = Masse * Beschleunigung
; F = m * a ; Masse m=1 für alle Partikel gleich
;(1) F = a
;
;Beschleunigung = Geschwindigkeit / Zeit
; a = V / t ;Zeit t = 1 sec Intervall
; a = V
;
;Geschwindigkeit = Weg / Zeit
; V = s / t ;Zeit t = 1 sec Intervall
; V = s
;
; => wenn alle Massen = 1 und das Intervall t=1 dann
; ist der zurückgelegte Weg s so groß wie die
; resultierende aller Anziehungskräfte F
#include <GGEngine.au3>
;#include <assembleit.au3>
Global $w = 800 ;Fenstergröße
Global $h = $w
Global $ausgleich = 1E-3 ;falls mehrere partikel dieselben parameter haben
Global $t = 1 ;Zeit
Global $max = 0
;***********************************************************************
Global $particles = 4 ;anzahl partikel
Global $cleanflag = 0 ;0=weg zeigen, 1= nur pixel
;***********************************************************************
_GGinit()
$gui = _GGGUIcreate("n-body", $w, $h)
$ptr1 = _GGGUIgetIMGptr($gui)
$bmp_struct = DllStructCreate("dword[" & $w * $h & "]", $ptr1) ;bilddaten
$leer_struct = DllStructCreate("char[" & $w * $w * 4 & "]", $ptr1) ;"doublebuffer"
$leerstring = ""
For $i = 1 To $w * $w * 4 ;
$leerstring &= Chr(0)
Next
$col_struct = DllStructCreate("int[" & $particles & "]") ;Farben
[/autoit] [autoit][/autoit] [autoit];struct of arrays ist wesentlich schneller als array of structs (später mit asm-SIMD oder c++)
$x_struct = DllStructCreate("float [" & $particles & "]") ;Koordinaten
$y_struct = DllStructCreate("float [" & $particles & "]") ;
$z_struct = DllStructCreate("float [" & $particles & "]") ;
$vx_struct = DllStructCreate("float [" & $particles & "]") ;Geschwindigkeiten
$vy_struct = DllStructCreate("float [" & $particles & "]") ;
$vz_struct = DllStructCreate("float [" & $particles & "]") ;
For $i = 1 To $particles
DllStructSetData($x_struct, 1, Random($w / 4, $w * 3 / 4), $i) ;koordinaten mit zufälligen floats füllen
DllStructSetData($y_struct, 1, Random($w / 4, $w * 3 / 4), $i) ;
DllStructSetData($z_struct, 1, Random($w / 4, $w * 3 / 4), $i) ;
;~ DllStructSetData($vx_struct, 1, Random(-.051, .051), $i) ;geschwindigkeitsvektoren mit zufälligen floats füllen
;~ DllStructSetData($vy_struct, 1, Random(-.051, .051), $i) ;
;~ DllStructSetData($vz_struct, 1, Random(-.051, .051), $i) ;
DllStructSetData($col_struct, 1, 0xFF000000 + Random(0, 0xFFFFFF, 1), $i) ;zufällige Farben
Next
;vorbelegung als Demo für 2 Partikel
DllStructSetData($vx_struct, 1, 0, 1) ;geschwindigkeitsvektoren mit zufälligen floats füllen
DllStructSetData($vy_struct, 1, -.05, 1) ;
DllStructSetData($vx_struct, 1, 0, 2) ;geschwindigkeitsvektoren mit zufälligen floats füllen
DllStructSetData($vy_struct, 1, .05, 2) ;
While Not _GGGUIclosed($gui)
_nbody($particles) ;koordinaten berechnen
_pic() ;pixel anzeigen
WEnd
_GGGUIdelete($gui)
_GGfree()
Func _nBody($particles)
[/autoit] [autoit][/autoit] [autoit]For $i = 1 To $particles ;jeder partikel
$x = DllStructGetData($x_struct, 1, $i) ;x-koordinate
$y = DllStructGetData($y_struct, 1, $i) ;y-koordinate
$z = DllStructGetData($z_struct, 1, $i) ;z-koordinate
$Fx = 0
$Fy = 0
$Fz = 0
$vxi = 0
$vyi = 0
$vzi = 0
For $j = 1 To $particles ;hat wechselwirkung mit jedem anderen Partikel
$xj = DllStructGetData($x_struct, 1, $j) ;x-koordinate
$yj = DllStructGetData($y_struct, 1, $j) ;y-koordinate
$zj = DllStructGetData($z_struct, 1, $j) ;z-koordinate
$dx = $xj - $x ;abstand zw. den einzelnen Partikeln
$dy = $yj - $y
$dz = $zj - $z
;$sqr = $dx * $dx + $dy * $dy + $dz * $dz + $ausgleich ;ausgleich, um 0 zu vermeiden 3D
$sqr = $dx * $dx + $dy * $dy + $ausgleich ;ausgleich, um 0 zu vermeiden 2D
$1sqr = 1 / ($sqr * Sqrt($sqr))
If $1sqr > .01 Then $1sqr = .01 ;Anziehungskräfte begrenzen
$Fx += $dx * $1sqr ;Gravitationskräfte addieren
$Fy += $dy * $1sqr
$Fz += $dz * $1sqr
Next
$vxi += $Fx * $t ;Geschwindigkeiten addieren
$vyi += $Fy * $t
$vzi += $Fz * $t
$vx = DllStructGetData($vx_struct, 1, $i) ;V=s => s=V
$vy = DllStructGetData($vy_struct, 1, $i) ;
$vz = DllStructGetData($vz_struct, 1, $i) ;
$v1 = $vx + $vxi ;neue Koordinate= alte Koordinate + Weg
$v2 = $vy + $vyi
$v3 = $vz + $vzi
;~ If $v1 > $max Then
;~ ConsoleWrite($v1 & @CRLF)
;~ $max = $v1
;~ EndIf
;~ if abs($v1)>.4 then $v1=$vx
;~ if abs($v2)>.4 then $v2=$vy
;~ if abs($v3)>.4 then $v3=$vz
DllStructSetData($vx_struct, 1, $v1, $i) ;koordinaten eintragen
DllStructSetData($vy_struct, 1, $v2, $i) ;
DllStructSetData($vz_struct, 1, $v3, $i) ;
Next
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]EndFunc ;==>_nBody
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]Func _pic() ;schreibt die koordinaten in die Bitmap
If $cleanflag Then DllStructSetData($leer_struct, 1, $leerstring) ;hintergrund leeren
For $i = 1 To $particles
$x = DllStructGetData($x_struct, 1, $i) ;x-koordinate
$y = DllStructGetData($y_struct, 1, $i) ;y-koordinate
$z = DllStructGetData($z_struct, 1, $i) ;z-koordinate
$vx = DllStructGetData($vx_struct, 1, $i) ;Geschwindigkeit = zurückgelegter Weg
$vy = DllStructGetData($vy_struct, 1, $i) ;vy
$vz = DllStructGetData($vz_struct, 1, $i) ;vz
DllStructSetData($x_struct, 1, $x + $vx, $i);neue Koordinaten
DllStructSetData($y_struct, 1, $y + $vy, $i)
DllStructSetData($z_struct, 1, $z + $vz, $i);
$col = DllStructGetData($col_struct, 1, $i) ;Farbe holen
DllStructSetData($bmp_struct, 1, $col, Int($y) * $w + Int($x)) ;pixel setzen
Next
_GGGUIupdate($gui)
EndFunc ;==>_pic
TheShadowAE´s UDF
autoit.de/wcf/attachment/21537/