# Simulation einer Magnetischen Flasche



## frostbyte (14. Mrz 2010)

Hallo,

Ich möchte eine Simulation einer Magnetischen Flasche bauen. Ein ähnliches Programm habe ich schon in 2D geschrieben, jedoch ist dort das Magnetfeld homogen.

Jetzt habe ich zwei Fragen: Wie modelliere ich ein inhomogenes Magnetfeld in Java? Die Kraft, die auf das Elektron wirkt ist das Vektorprodukt der Geschwindigkeit v sowie des Magnetfeldes B, also F = v x B. Ist es dann sinnvoll ein 3D Array mit Vektoren zu erzeugen, so dass ich an jedem Punkt, wo sich das Teilchen aufhält, einfach den Kraftvektor ausrechnen kann?

Und dann noch eine Frage:

Das ganze soll eigentlich Kugelsymmetrisch (siehe Magnetfeld hier) werden, da das Erdmagnetfeld dargestellt werden soll. Als Näherung würde ich es so darstellen.

Intern muss das ganze auf jeden Fall in 3D berechnet werden, das ist aber von der Mathematik her kein Problem. Bei der Anzeige weiß ich aber gar nicht, wie ich das machen soll. Bisher habe ich nur 2D Programme geschrieben, in dem ich die paintComponent(Graphics g) im JPanel überschrieben habe und dort mit g.drawLine() gebastelt habe.

Wie schwer ist es, das ganze in 3D darzustellen und am Besten so, dass man die ganze Szene mit der Maus um den Mittelpunkt drehen kann?

Vielen Dank für Hinweise!

Iteranium


----------



## Steev (14. Mrz 2010)

Für 3d benutzt man in der Regel entweder Java3d, JOGL oder LWJGL. Auf jedem Fall müssen externe Bibliotheken eingebunden werden. Auser natürlich, man schreibt sich die Projektion selbst. Je nachdem, wie komplex du das gestalten willst, kann es schon recht komplex werden. Wie gesagt, es kommt dabei darauf an, wie komplex du das darstellen willst. Einige Primitiven in einer 3d-Szene darzustellen ist nicht so das Problem.

Bei dem Magnetfeld gibt es ja verschiedene Möglichkeiten. Ich bin eigendlich nicht dafür, dass man einen 3d-Array mit Kraftvektoren verwendet, da diese Variante dazu noch Recht ungenau ist... Mir fällt allerdings auf Anhieb nicht ein, wie man es besser machen könnte  ...


----------



## Illuvatar (14. Mrz 2010)

Zur Modellierung: Was spricht denn dagegen, dass du dir eben die Funktion überlegst die das Feld berechnet, und dann hast du zB ne Klasse

```
public class MagneticField
{
  public Vector at (Vector x)
  {
    // berechnen
  }
  // andere methoden
}
```
Und dann halt wenn du die Kraft brauchst:

```
Vector B = magneticField.at(currentLocation);
Vector F = velocity.crossProduct(B).multiply(charge);
```

Zur Anzeige hat steev eigentlich schon alles gesagt  Wenn du in der Materie bist, ist eine solche einfache Animation nicht schwierig. Wenn du dich erst reinarbeiten musst, dauert es natürlich bisschen.
In dem Fall würd ich übrigens sogar Java3D für ziemlich geeignet halten.


----------



## frostbyte (14. Mrz 2010)

Natürlich, das Magnetfeld sollte spontan berechnet werden, das ist dann viel genauer und schont den Speicher. Vielen Dank für den Anstoß!

Ich war schon dabei mich ins Unglück zu stürzen …


```
bfeld = new Vektor[10][10][20];

for (int x = 0; x < bfeld.length; x++) {
	for (int y = 0; y < bfeld[x].length; y++) {
		for (int z = 0; z < bfeld[x][y].length; z++) {
			// Berechne den Vektor an dem Punkt (X|Y|Z)
			
		}
	}
}
```

Was 3D betrifft bin ich definitiv nicht in der Materie, dann schreibe ich fürs erste einfach nur eine Z- und eine X-Projektion mit meiner bewährten Technik des JPanel.paintComponent() und kann dann, wenn das Programm an sich läuft mich mal in 3D einarbeiten.


----------



## frostbyte (14. Mrz 2010)

Ich habe jetzt eine Funktion für das Magnetfeld, die liefert in der X-Y Ebene auch brav immer nur B(0, 0, 1), also einfach ein homogenes Magnetfeld nach oben. Das Teilchen bewegt sich momentan auch nur in der X-Y Ebene.

Aber irgendwie wird das Teilchen immer schneller, obwohl die einzige Kraft senkrecht darauf wirkt. Bei einem sehr ähnlichen Programm hatte das auch super geklappt …

Mein Teilchen:

```
public abstract class Teilchen {
	// Die Position des Teilchens
	Vektor position;
	
	// Die Geschwindigkeit des Teilchens
	Vektor v;
	
	// Farbe in der Anzeige
	Color farbe;
	
	// Materialkonstanten
	double masse;
	double ladung;
	
	void bewegen () {
		// Kraft bestimmen
		Vektor f = v.kreuzprodukt(MagnetischeFlascheSimulator.bfeld(position)).skalarmul(ladung);
		
		// Beschleunigung (a = F/m) auf die Geschwindigkeit addieren
		v = v.add(f.skalarmul(1/masse));
		
		// Bewegung aufrechnen
		position = position.add(v);
	}

}
```


Mein Vektor:

```
public class Vektor {
	double x, y, z;
	
	public Vektor kreuzprodukt (Vektor b) {
		Vektor c = new Vektor();
		
		c.x = y*b.z - z*b.y;
		c.y = z*b.x - x*b.z;
		c.z = x*b.y - y*b.x;
		
		return c;
	}
	
	public Vektor add (Vektor b) {
		Vektor c = new Vektor();
		
		c.x = x + b.x;
		c.y = y + b.y;
		c.z = z + b.z;
		
		return c;
	}
	
	public Vektor skalarmul (double b) {
		Vektor c = new Vektor();
		
		c.x = x * b;
		c.y = y * b;
		c.z = z * b;
		
		return c;
	}

}
```

MagnetischeFlascheSimulator.bfeld(position) ist an jeder Position der X-Y Ebene (0, 0, 1), das habe ich überprüft.

Schaut man sich die Z-Projektion an, wandert das Teilchen in einer Spirale ab …


Was ist da los?


----------



## 0x7F800000 (14. Mrz 2010)

Iteranium hat gesagt.:


> I
> 
> ```
> // Beschleunigung (a = F/m) auf die Geschwindigkeit addieren
> ...


Und dir ist nicht aufgefallen, dass es zumindest mal von den einheiten überhaupt nicht passt?
Man kann doch nicht einfach beschleunigungen mit geschwindigkeiten zusammenaddieren, und das ganze dann wieder als geschwindigkeit bezeichnen.

Wenn du das in ganz kleine Zeitschritte unterteilen willst, und dann so tust, als ob die beschleunigung während dieser zeitschritte annähernd konstant ist, dann würde das etwa so aussehen:

```
public void move(double dt){
   a = F(x,v)/m;
   x = x+v*dt+(a*dt^2)/2 
   v = v+a*dt
}
```
wenn du das "dt", also diese kleine Zeitschritte weglässt, dann rechnest du so, als ob du in 1-sekunden schritten iterieren würdest (vorausgesetzt alles andere ist in SI), und das könnte unter umständen etwas grob werden, auch nichtrelativistisch: wenn man unter 10%c ist, kann man auch klassisch rechnen. In einer Sekunde ist da das teilchen schon drei tausend Kilometer weiter, und das Magnetfeld sieht dort evtl. merkbar anders aus. Dann benehmen sich die Teilchen so, als ob sie sich dauernd verlustfrei hin und her beamen würden. Insbesondere kann es dadurch dazu kommen, dass beispielsweise die Energierhaltung nicht mal annähernd gilt, solche Systeme fabrizieren dann nach kürzester zeit totalen Schrott.
=> dt runtersetzen
=> evtl. ein besseres Verfahren anwenden ( -> Runge Kutta)


----------



## frostbyte (14. Mrz 2010)

Die Einheiten stimmen nicht, da das dt=1 war und von daher nicht explizit hingeschrieben worden ist.

Ich habe dt jetzt mal auf 1/1000 gesetzt, jetzt wird das ganze deutlich besser, die Beschleunigung war bei 1/100 minimal, bei 1/1000 sollte es nicht mehr auffallen.

Danke für den Tipp, schließlich müsste man eigentlich das Integral bilden, und nicht einfach nur grob iterieren. Jetzt habe ich eine ausreichend genaue Näherung.


----------



## frostbyte (16. Mrz 2010)

So, jetzt versuche ich mich an einer 3D Darstellung nach der Tutorial-Zusammenschneiden-Methode.


```
Canvas3D canvas = new Canvas3D(config); 
SimpleUniverse universe = new SimpleUniverse(canvas);
BranchGroup group = new BranchGroup();

tgalles = new TransformGroup();

tgalles.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
tgalles.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );


Sphere erde = new Sphere((float)VanAllenGuertel.ERDRADIUS);

tgalles.addChild(erde);

for (Teilchen t : VanAllenGuertel.teilchen) {
	tgalles.addChild(t.tg);
}

group.addChild(tgalles);

BoundingSphere bounds = new BoundingSphere( new Point3d(0.0,0.0,0.0), 100.0 );

universe.getViewingPlatform().setNominalViewingTransform();

universe.addBranchGraph(group);
```

Die Teilchen haben jeweils eine Sphere und eine TransformationGroup:


```
public abstract class Teilchen {


	TransformGroup tg = new TransformGroup();
	Transform3D transform = new Transform3D();
	Sphere sphere = new Sphere(0.05f);
	Vector3f position3f;
	
	public Teilchen () {
		position3f = new Vector3f();
	
		tg.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
		tg.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
		tg.addChild(sphere);
	}


	void bewegen () {
		for (int i = 0; i < iter/4; i++) {
			// ... Position in Vektor "position" schreiben ...

			position3f = new Vector3f((float)position.x, (float)position.y, (float)position.z);

			transform.setTranslation(position3f);

			tg.setTransform(transform);
		}
	}
}
```

Die Kugeln kreisen jetzt auch wunderbar hin und her, aber wenn ich eine weitere hinzufügen möchte mit:


```
Teilchen t = new Elektron()

VanAllenGuertel.frame3d.tgalles.addChild(t.tg);
```

Dann kommt 
	
	
	
	





```
Exception in thread "Timer-0" javax.media.j3d.RestrictedAccessException: Group: only a BranchGroup node may be added
	at javax.media.j3d.Group.addChild(Group.java:260)
	at erdmagnetfeld.TeilchenBeweger.run(TeilchenBeweger.java:42)
```

Ich mache doch letztlich nichts anderes, als in der Methode, wo alle Teilchen in die 3D Welt hinzugefügt werden, warum ist es jetzt ein Problem von außerhalb noch welche hinzuzufügen?


----------



## Illuvatar (16. Mrz 2010)

Wenn der SceneGraph schon _live_ ist, darfst du nur noch BranchGroups hinzufügen.
Das ist aber kein Problem, einfach so in der Art:

```
BranchGroup bg = new BranchGroup();
bg.addChild(t.tg);
VanAllenGuertel.frame3d.tgalles.addChild(t.tg);
```


----------



## frostbyte (16. Mrz 2010)

Ich musste den entsprechenden Knoten im Baum noch sagen, dass man sie verändern darf. Jetzt klappt das alles.

Und inzwischen habe ich das Licht auch mit in den Baum eingehängt, jetzt dreht sich das Licht mit, man dreht nicht mehr die Erde unter einer Lampe, sondern jetzt eher das ganze Sonnensystem. Wunderbar.


Jetzt würde ich gerne noch die Magnetfeldlinien da reinpacken, ich kann nacheinander einen Haufen 3D Punkte ausrechnen, jedoch weiß ich nicht, wie ich diese dann als feine Linie darstelle … Wie mache ich das am Besten?


----------



## Illuvatar (16. Mrz 2010)

Vermutlich am besten ein Shape3D mit einem LineStripArray.

Edit: das hier erklärt eigentlich ganz gut die Verwendung. Wenn du es farbig haben willst: entweder über die Appearance des Shape3D, oder punktweise im LineStripArray (im Konstruktor das Flag COLOR_3 setzen, und dann funktioniert das Setzen der Farben für die einzelnen Punkte wie das Setzen der Koordinaten).


----------



## frostbyte (19. Mrz 2010)

Vielen Dank, jetzt sieht das ganze verständlicher aus.


----------



## Illuvatar (19. Mrz 2010)

Das Programm find ich (Physikstudent... ) interessant, meinst du du kannst das fertige Programm mal hochladen?


----------



## frostbyte (19. Mrz 2010)

Kann man die Linien denn auch noch so beleuchten, die die Kugeln? Die sind bisher aus jedem Winkel immer die gleiche Farbe.

Auf die Erdkugel habe ich jetzt eine Textur gesetzt:


```
Appearance ap = new Appearance();

TextureLoader loader = new TextureLoader("bin/erde.jpg", "RGB", new Container());
Texture texture = loader.getTexture();
texture.setBoundaryModeS(Texture.WRAP);
texture.setBoundaryModeT(Texture.WRAP);
texture.setBoundaryColor( new Color4f( 0.0f, 1.0f, 0.0f, 0.0f ) );

int primflags = Primitive.GENERATE_NORMALS + Primitive.GENERATE_TEXTURE_COORDS; 
		
TransformGroup erdeGruppe = new TransformGroup();
Transform3D erdeTransform = new Transform3D();
erdeTransform.setRotation(new AxisAngle4d(1, 0, 0, Math.PI/2));
erdeGruppe.setTransform(erdeTransform);

Sphere erde = new Sphere((float)VanAllenGuertel.ERDRADIUS, primflags, ap);

erdeGruppe.addChild(erde);


// Set up the texture attributes
TextureAttributes texAttr = new TextureAttributes();
texAttr.setTextureMode(TextureAttributes.REPLACE);
ap.setTexture(texture);
ap.setTextureAttributes(texAttr);

erde.setAppearance(ap);

tgalles.addChild(erdeGruppe);
```

Jedoch sieht man die Textur auch, wenn die Erde gar nicht beleuchtet wird, die sollte dann schwarz sein. Wie stelle ich das dann an?


----------



## frostbyte (22. Mrz 2010)

Ich bin jetzt auf ein ganz anderes Problem gestoßen:

Auf meine Mac mit Eclipse, auf dem ich das Programm entwickelt habe, klappt das Programm super. Auch auf einem anderen Mac. Jedoch habe ich es jetzt unter Windows aufrufen wollen, da meckerte der, dass er die Klasse "Canvas3D" nicht finden kann.

Muss ich die noch in meinem .jar Archiv mitliefern? Und woher bekomme ich ein Java3D Paket, das plattformunabhängig ist?


----------



## Illuvatar (22. Mrz 2010)

Also, wenn Canvas3D nicht gefunden wird ist vermutlich einfach kein Java3D installiert, was anderes kann ich mir eigentlich nicht vorstellen...
Und Java3D ist eben per se etwas plattformabhängiges, da es auf plattformspezifische Funktionen zurückgreift um das 3D darzustellen (auch wenn es normalerweise immer auf OGL rausläuft). Die Frage nach einem plattformunabhängigen Java3D ist meiner Meinung nach ein bisschen wie die Frage nach einer plattformunabhängigen JVM 

Das mit der Textur weiß ich grad nicht auswendig, da müsste ich mit rumspielen  Schau doch einfach mal die ganzen Parameter von den ColoringAttributes, den TextureAttributes, der Texture und vielleicht dem Material an.

Die Linien mit dem LineStripArray wird man eher nicht beleuchten können: Das Problem ist hier, dass es bei einer unendlich dünnen Linie nicht möglich ist, einen Normalenvektor zu definieren, und dann kann man auch keine Beleuchtungseffekte machen. Dafür müsstest du soweit ich weiß die Linie aus echten Polygonen zusammensetzen, möglicherweise aus Cylinder-Objekten.


----------



## guni (23. Mrz 2010)

wow. interessantes programm.
ich schließe mich Illuvatar an. Bin zwar kein Physikstudent aber es wäre ein netter Einstieg in Java3d - also wenn's geht: bitte uppen ;-)

thx. guni


----------



## frostbyte (23. Mrz 2010)

Hier erstmal die Programme:
Magnetische Flasche
Van-Allen-Gürtel

Dass man Linien nicht beleuchten kann erscheint logisch. Jedoch würde ich annehmen, dass man eine Linie nicht sehen kann, wenn man sie ausschließlich im Gegenlicht betrachtet, dann nur noch als Schatten. Aber okay, das kann ich mit Leben.

Mit den Texturen werde ich dann mal ein wenig herumspielen, vielleicht bekomme ich das noch hin.


Aber wenn Java3D nicht plattformunabhängig ist, bedeutet das, dass also jeder, der das Programm unter Windows oder Linux benutzen möchte erst Java3D installieren muss? Das wäre mir doch etwas unangenehm, weil die Programme direkt laufen sollten …


----------

