I created a CPU Parallel.For loop in my code as follow:
Vector2[] p;
Vector2[] pnew;
Vector2[] v;
float[] m;
Parallel.For(0, N, i =>
{
double dx;
double dy;
double invr;
double invr3;
double f;
double ax;
double ay;
double eps = 0.02;
ax = 0;
ay = 0;
for (int j = 0; j < N; j++)
{
dx = p[j].X - p[i].X;
dy = p[j].Y - p[i].Y;
invr =1.0 / Math.Sqrt(dx * dx + dy * dy + eps);
invr3 = invr * invr * invr;
f = m[j] * m[i] * invr3;
ax += f * dx;
ay += f * dy;
}
pnew[i].X = (float)(p[i].X + dt * v[i].X + 0.5 * dt * dt * ax); /* save new position of particle "i" */
pnew[i].Y = (float)(p[i].Y + dt * v[i].Y + 0.5 * dt * dt * ay);
v[i].X += dt * (float)ax; /* update velocity of particle "i" */
v[i].Y += dt * (float)ay;
});
The above code works fine and run on my CPU cores.
Want I want to do it to convert this code to a GPU For loop using "Alea GPU" library. So i tried the following:
Vector2[] p;
Vector2[] pnew;
Vector2[] v;
float[] m;
[GpuManaged]
public void calculForceGPU()
{
Gpu.Default.For(0, N + 1, i =>
{
double dx;
double dy;
double invr;
double invr3;
double f;
double ax;
double ay;
double eps = 0.02;
ax = 0;
ay = 0;
for (int j = 0; j < N; j++)
{
dx = p[j].X - p[i].X;
dy = p[j].Y - p[i].Y;
invr = 1.0 / Math.Sqrt(dx * dx + dy * dy + eps);
invr3 = invr * invr * invr;
f = m[j] * m[i] * invr3;
ax += f * dx;
ay += f * dy;
}
pnew[i].X = (float)(p[i].X + dt * v[i].X + 0.5 * dt * dt * ax); /* save new position of particle "i" */
pnew[i].Y = (float)(p[i].Y + dt * v[i].Y + 0.5 * dt * dt * ay);
v[i].X += dt * (float)ax; /* update velocity of particle "i" */
v[i].Y += dt * (float)ay;
});
}
You can see that it is the exact same code as above but with Parallel.For changed for Gpu.Default.For. But when I run it I get the following error:
i32 is not struct type.
Source location stack:
-> in C:\Users\...\Simulation.cs(628,21-628,42)
-> at ....Simulation.[Void <calculForceGPU>b__36_0(Int32)]
-> at Alea.Parallel.Device.DeviceFor.[Void Kernel(Int32, Int32,
System.Action`1[System.Int32])]
-> at defining runtime32 (sm52,32bit)
Loading method as kernel:
-> Method: Alea.Parallel.Device.DeviceFor.[Void Kernel(Int32, Int32,
System.Action`1[System.Int32])]
-> InstanceOpt: <None>
-> Argument.#0: 0
-> Argument.#1: 1025
-> Argument.#2: System.Action`1[System.Int32]
Getting or loading method as kernel:
-> Method: Alea.Parallel.Device.DeviceFor.[Void Kernel(Int32, Int32,
System.Action`1[System.Int32])]
-> InstanceOpt: <None>
-> Argument.#0: 0
-> Argument.#1: 1025
-> Argument.#2: System.Action`1[System.Int32]
I am not sure how to solve this error. Any help would be appreciated.
Update on what I tried after comments by NineBerry:
So it turns out the problem might be the Vector2 type because it might use properties. So I created my own struct which use Fields like so:
struct Vector2Struct
{
public float X;
public float Y;
public Vector2Struct(float x, float y)
{
X = x;
Y = y;
}
}
Vector2Struct[] p;
Vector2Struct[] pnew;
Vector2Struct[] v;
float[] m;
The rest of the code is pretty much the same as before. But I still get the same "i32 is not struct type." error.
Same error if I ditch all struct and use arrays of float instead:
float[] m;
float[] pX;
float[] pY;
float[] pnewX;
float[] pnewY;
float[] vX;
float[] vY;
Code dump as per comment. Creating a new instance of the class should make it run. You will need to install nuget ALEA and ALEA.FODY . Also I think you need FSharp.Core to run Alea
using System;
using Alea;
using Alea.Parallel;
namespace GalaxyTest
{
public class Simulation
{
// param
object[] param;
// masses
float[] m;
float[] pX;
float[] pY;
float[] pnewX;
float[] pnewY;
float[] vX;
float[] vY;
// data
static int N;
double ratioPM;
double ratioMasse;
int Np;
int Nm;
int distribution;
int simulType;
float dt = 0.01F;
double eps = 0.02;
float Rrp = 1;
float Rrm = 10;
public Simulation() //Constructor
{
Initializer();
updatePointGPUAlea();
}
private void Initializer()
{
// Settings
N = 1024;
ratioPM = 0.25;
ratioMasse = 1;
Np = 256;
Nm = 769;
distribution = 0;
simulType = 1;
// vector Initialisation
pX = new float[N];
pY = new float[N];
pnewX = new float[N];
pnewY = new float[N];
vX = new float[N];
vY = new float[N];
m = new float[N];
// compute masses
for (int i = 0; i < Np; i++)
{
m[i] = 1;
}
for (int i = Np; i < Nm; i++)
{
m[i] = -1 * (float)ratioMasse;
}
Random r = new Random();
double R;
double teta;
double Rp;
double Rn;
float signe1, signe2, signe3, signe4;
// Init pos = random shell
for (int i = 0; i < N; i++)
{
Rp = 2.61;
Rn = 45;
teta = r.NextDouble() * 2 * Math.PI;
signe1 = Math.Sign(r.NextDouble() - 0.5);
signe2 = Math.Sign(r.NextDouble() - 0.5);
signe3 = Math.Sign(r.NextDouble() - 0.5);
signe4 = Math.Sign(r.NextDouble() - 0.5);
if (m[i] > 0)
{
pX[i] = (float)(Rp * Math.Cos(teta)) + 400 / 2;
pY[i] = (float)(Rp * Math.Sin(teta)) + 400 / 2;
vX[i] = (float)(r.NextDouble() * Rrp * signe1 + Math.Sqrt(Np) / 12 * 3 * Math.Sin(teta) * (0.4 - 1 / Math.Sqrt(10 + Rp)) * 3);
vY[i] = (float)(r.NextDouble() * Rrp * signe2 - Math.Sqrt(Np) / 12 * 3 * Math.Cos(teta) * (0.4 - 1 / Math.Sqrt(10 + Rp)) * 3);
}
else
{
pX[i] = (float)(Rn * Math.Cos(teta)) + 400 / 2;
pY[i] = (float)(Rn * Math.Sin(teta)) + 400 / 2;
vX[i] = (float)r.NextDouble() * Rrm * signe3;
vY[i] = (float)r.NextDouble() * Rrm * signe4;
}
}
}
public void updatePointGPUAlea()
{
calculForceGPU();
for (int i = 0; i < N; i++)
{
// Update de la position
pX[i] = pnewX[i];
pY[i] = pnewY[i];
}
}
[GpuManaged]
public void calculForceGPU()
{
Gpu.Default.For(0, N + 1, i =>
{
double dx;
double dy;
double invr;
double invr3;
double f;
double ax;
double ay;
ax = 0;
ay = 0;
for (int j = 0; j < N; j++)
{
dx = pX[j] - pX[i];
dy = pY[j] - pY[i];
invr = 1.0 / Math.Sqrt(dx * dx + dy * dy + eps);
invr3 = invr * invr * invr;
f = m[j] * m[i] * invr3;
ax += f * dx;
ay += f * dy;
}
pnewX[i] = (float)(pX[i] + dt * vX[i] + 0.5 * dt * dt * ax); /* save new position of particle "i" */
pnewY[i] = (float)(pY[i] + dt * vY[i] + 0.5 * dt * dt * ay);
vX[i] += dt * (float)ax; /* update velocity of particle "i" */
vY[i] += dt * (float)ay;
});
}
}
}
This is probably because you use the X and Y properties of the Vector2 struct type. Alea does not support properties according to the documentation:
Note that the current version of Alea GPU only supports fields in structs but not properties
See also Alea: "i32 is not struct type
I want to draw sin(θ)*cos(θ), but it doesn't work.
I can draw sin or cos,
but I want to draw sin(θ)*cos(θ) together.
Here is my code
private void button1_Click(object sender, EventArgs e)
{
Graphics drw = this.CreateGraphics();
Pen pen = new Pen(Brushes.Black, 7.0f);
float x1 = 0;
float y1 = 0;
float xoy = 200;
float ef = 20;
for (double i=0;i<40;i+=1)
{
double radi = (float)(i * 180 / Math.PI);
float temp = (float)Math.Cos(radi)*(float)Math.Sin(radi);
drw.DrawLine(pen, x1 * ef, y1 * ef + xoy, ef * (float)i, temp * ef + xoy);
x1 = (float)i;
y1 = temp;
}
}
And I want this result:
You may find it easier to look at the corresponding Parametric Equations.
private void Form1_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
double pi = Math.PI;
int n = 100;
var t = Enumerable.Range(0, n).Select(p => p * 2 * pi / n).ToArray();
var x = t.Select(p => Math.Sin(2 * p) * Math.Cos(p)).ToArray();
var y = t.Select(p => Math.Sin(2 * p) * Math.Sin(p)).ToArray();
Pen pen = new Pen(Brushes.Black, 3);
int scale = 100;
int shift = 100;
for (int i = 0; i < n - 1; i++)
{
g.DrawLine(pen, scale*(float)x[i] + shift,
scale*(float)y[i] + shift,
scale*(float)x[i + 1] + shift,
scale*(float)y[i + 1] + shift);
}
}
Actually, the real function you are looking for is a little bit different... see an example here. Looking at this article about polar flowers, I'm sure it will get pointed to the right direction, and it also contains a full working source code.
Just an example, supposing you use a panel in your form on which to draw the polar flower:
panel.OnPaint += Panel_Paint;
private void Panel_Paint(Object sender, PaintEventArgs e)
{
Double scale = ((Panel)sender).Width / 2.0d;
Double repetitions = Math.Round(scale, 0);
Double basis = (2.0d * Math.PI) / scale;
Double petals = 2.0d;
using (Graphics g = e.Graphics)
{
using (Pen pen = new Pen(Brushes.Red, 2.0f))
{
for (Double i = 0.0f; i < (repetitions - 1); ++i)
{
Double t0 = i*basis;
Double t1 = (i + 1)*basis;
Double x0 = Math.Sin(petals * t0) * Math.Cos(t0);
Double x1 = Math.Sin(petals * t1) * Math.Cos(t1);
Double y0 = Math.Sin(petals * t0) * Math.Sin(t0);
Double y1 = Math.Sin(petals * t1) * Math.Sin(t1);
g.DrawLine
(
pen,
(Single) ((scale*x0) + scale),
(Single) ((scale*y0) + scale),
(Single) ((scale*x1) + scale),
(Single) ((scale*y1) + scale)
);
}
}
}
}
The basic formulation states that if the petals variable value is:
even, then it represents half the amount of petals of the polar flower
odd, then it represents the amount of petals of the polar flower
so if you define Double petals = 2.0d;, you will obtain 4 petals... and if you define Double petals = 5.0d;, you will obtain 5 petals.
I tried to convert a csv file to shapefile with its projection. Conversion works but I can't create the .prj file.
The error says that
"there is no source code available for current location".
My code is as follows:
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
AxMap axMap1 = new AxMap();
Shapefile sf = new Shapefile();
bool result = sf.CreateNewWithShapeID("", ShpfileType.SHP_POLYGON);
if (!result) {
MessageBox.Show(sf.get_ErrorMsg(sf.LastErrorCode));
} else {
double xMin = 0.0;
double yMin = 0.0;
double xMax = 1000.0;
double yMax = 1000.0;
Random rnd = new Random(DateTime.Now.Millisecond);
int fldX = sf.EditAddField("x", FieldType.DOUBLE_FIELD, 9, 12);
int fldY = sf.EditAddField("y", FieldType.DOUBLE_FIELD, 9, 12);
int fldArea = sf.EditAddField("area", FieldType.DOUBLE_FIELD, 9, 12);
// In a loop we are creating 100 different points using the box established above.
for (int i = 0; i < 100; i++) {
if (i % 10 == 0) {
Shape shp1 = new Shape();
shp1.Create(ShpfileType.SHP_POLYGON);
sf.EditInsertShape(shp1, ref i);
} else {
double xCenter = xMin + (xMax - xMin) * rnd.NextDouble();
double yCenter = yMin + (yMax - yMin) * rnd.NextDouble();
// random radius from 10 to 100
double radius = 10 + rnd.NextDouble() * 90;
// polygons must be clockwise
Shape shp = new Shape();
shp.Create(ShpfileType.SHP_POLYGON);
for (int j = 0; j < 37; j++) {
Point pnt = new Point();
pnt.x = xCenter + radius * Math.Cos(j * Math.PI / 18);
pnt.y = yCenter - radius * Math.Sin(j * Math.PI / 18);
shp.InsertPoint(pnt, ref j);
}
sf.EditInsertShape(shp, ref i);
sf.EditCellValue(fldX, i, xCenter.ToString());
sf.EditCellValue(fldY, i, yCenter.ToString());
sf.EditCellValue(fldArea, i, Math.PI * radius * radius);
}
}
axMap1.CreateControl();
axMap1.AddLayer(sf, true);
axMap1.ZoomToLayer(0);
sf.Categories.Generate(fldArea, tkClassificationType.ctNaturalBreaks, 7);
ColorScheme scheme = new ColorScheme();
scheme.SetColors2(tkMapColor.Wheat, tkMapColor.Salmon); sf.Categories.ApplyColorScheme(tkColorSchemeType.ctSchemeGraduated, scheme);
axMap1.Redraw();
sf.SaveAs(#"D:\shp1\polygons.shp", null);
sf.Open("D:\\shp1\\polygons.shp");
sf.Projection = (DotSpatial.Projections.KnownCoordinateSystems.Projected.UtmWgs1984.WGS1984UTMZone32N).ToString();
sf.SaveAs(#"D:\shp1\polygons.prj");
}
}
I am very new to c# form and i would like to write some letters in a specific location. As you can see in the picture i have attached I've drawn a curved Line with a X and Y axis to scale it with. I would like to write the letter X on the edge of the horizontal line and Y on the top Edge of the vertical line. Also is there any possible ways to assign values on the line as well?
protected override void OnPaint(PaintEventArgs e)
{
float a = 1, b = 5, c = -4;
double x1, x2, x3, x4, x5, x6, y1, y2, y3, y4, y5, y6, x7, y7, delta;
delta = (b * b) - (4 * a * c);
x1 = ((b * (-1)) + Math.Sqrt(delta)) / (2 * a);
x6 = ((b * (-1)) - Math.Sqrt(delta)) / (2 * a);
y6 = a * (x6 * x6) + (b * (x6)) + c;
y1 = a * (x1 * x1) + (b * (x1)) + c;
x2 = 3;
y2 = a * (x2 * x2) + (b * (x2)) + c;
x3 = -3;
y3 = a * (x3 * x3) + (b * (x3)) + c;
x4 = 5;
y4 = a * (x4 * x4) + (b * (x4)) + c;
x5 = -10;
y5 = a * (x5 * x5) + (b * (x5)) + c;
x7 = 0;
y7 = a * (x7 * x7) + (b * (x7)) + c;
int cx1 = Convert.ToInt32(x1);
int cx2 = Convert.ToInt32(x2);
int cx3 = Convert.ToInt32(x3);
int cy1 = Convert.ToInt32(y1);
int cy2 = Convert.ToInt32(y2);
int cy3 = Convert.ToInt32(y3);
int cx4 = Convert.ToInt32(x4);
int cy4 = Convert.ToInt32(y4);
int cx5 = Convert.ToInt32(x5);
int cy5 = Convert.ToInt32(y5);
int cx6 = Convert.ToInt32(x6);
int cy6 = Convert.ToInt32(y6);
int cx7 = Convert.ToInt32(x7);
int cy7 = Convert.ToInt32(x7);
Graphics g = e.Graphics;
int deltaX = 300;
int deltaY = 300;
g.TranslateTransform(deltaX, deltaY);
float factor = 2.5f;
Matrix m = new Matrix();
m.Scale(factor, factor);
g.MultiplyTransform(m);
Pen aPen = new Pen(Color.Blue, 1);
aPen.DashStyle = DashStyle.DashDot;
Pen bPen = new Pen(Color.Green, 1);
bPen.EndCap = LineCap.ArrowAnchor;
Pen cPen = new Pen(Color.Green, 1);
cPen.StartCap = LineCap.DiamondAnchor;
Point point1 = new Point(cx1, -cy1);
Point point2 = new Point(cx2, -cy2);
Point point3 = new Point(cx3, -cy3);
Point point4 = new Point(cx4, -cy4);
Point point5 = new Point(cx5, -cy5);
Point point6 = new Point(cx6, -cy6);
Point pointa = new Point(20, -50);
Point pointb = new Point(40, -30);
Point pointc = new Point(60, -70);
Point[] Points = { point5, point3, point1, point2, point4 };
Point[] Pointss = { pointa, pointb,pointc };
g.DrawCurve(new Pen(Color.Red, 1), Pointss);
g.DrawCurve(aPen, Points);
g.DrawLine((cPen), new Point(cx7, -100), new Point(cx7, 100));
g.DrawLine((bPen), -100, 0, 100, 0);
check my sample. I hope it helps.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace GraphicsForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
var w = ClientRectangle.Width;
var h = ClientRectangle.Height;
var midY = h/2;
var midX = w/2;
var linePen = new Pen(Brushes.Red, 1)
{
StartCap = LineCap.DiamondAnchor,
EndCap = LineCap.DiamondAnchor
};
//horizontal line
g.DrawLine(linePen, 0, midY, w, midY);
var font = Font;
var measureStringX = g.MeasureString("x", font);
g.DrawString("x", font, Brushes.Black, w - measureStringX.Width - 2, midY + 2);
//vertical line
g.DrawLine(linePen, midX, 0, midX, h);
g.DrawString("y", font, Brushes.Black, midX + 2, 2);
//horizontals&vertical marks
const float marksCount = 12f;
var wx = w / marksCount;
var hx = h / marksCount;
var markPen = new Pen(Brushes.Red, 1);
for (int i = 1; i < marksCount; i++)
{
g.DrawLine(markPen, i * wx, midY, i * wx, midY + 5);
g.DrawLine(markPen, midX, hx * i, midX + 5, hx * i);
}
}
}
}
"Check out DrawString on the graphics object. https://msdn.microsoft.com/en-us/library/system.drawing.graphics.drawstring.aspx – dbugger"
Answered.
I'm trying to implement an N body simulation in C# using either Runge Kutta 4 or Velocity Verlet integration algorithms.
Before I move to a bigger number of particles, I wanted to test the simulation by modeling the earth's orbit around the sun, however, instead of the elliptical orbit, I get a weird spiral for some reason.
I can't figure out the problem since I made a simpler simulation of the solar system using the same algorithms where the sun was fixed in position and everything worked perfectly. The integrators work perfectly because it doesn't matter which one I use, I get the spiral with both.
Any help would be appreciated.
Here's the code:
class NBODY
{
public static double G = 4 * Math.PI * Math.PI;
class Particle
{
public double[] r; // position vector
public double[] v; // velocity vector
public double mass;
//constructor
public Particle() {}
public Particle(double x, double y, double z, double vx, double vy, double vz, double m)
{
this.r = new double[3];
this.v = new double[3];
this.r[0] = x;
this.r[1] = y;
this.r[2] = z;
this.v[0] = vx;
this.v[1] = vy;
this.v[2] = vz;
this.mass = m;
}
public void Update(Particle[] particles, double t, double h, int particleNumber)
{
RungeKutta4(particles, t, h, particleNumber);
}
private double acc(double r, Particle[] particles, int particleNumber, double[] r_temp, int l)
{
// dv/dt = f(x) = -G * m_i * (x - x_i) / [(x - x_i)^2 + (y - y_i)^2 + (z - z_i)^2]^(3/2)
double sum = 0;
switch (l)
{
case 0:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow( Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[1] - particles[i].r[1], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 1:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 2:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[1] - particles[i].r[1], 2), 1.5);
break;
}
return -G * sum;
}
private void RungeKutta4(Particle[] particles, double t, double h, int particleNumber)
{
//current position of the particle is saved in a vector
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
double[,] k = new double[4, 2];
k[0, 0] = this.v[l]; //k1_r
k[0, 1] = acc(this.r[l], particles, particleNumber, r_temp, l); //k1_v
k[1, 0] = this.v[l] + k[0, 1] * 0.5 * h; //k2_r
k[1, 1] = acc(this.r[l] + k[0, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k2_v
k[2, 0] = this.v[l] + k[1, 1] * 0.5 * h; //k3_r
k[2, 1] = acc(this.r[l] + k[1, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k3_v
k[3, 0] = this.v[l] + k[2, 1] * h; //k4_r
k[3, 1] = acc(this.r[l] + k[2, 0] * h, particles, particleNumber, r_temp, l); //k4_v
this.r[l] += (h / 6.0) * (k[0, 0] + 2 * k[1, 0] + 2 * k[2, 0] + k[3, 0]);
this.v[l] += (h / 6.0) * (k[0, 1] + 2 * k[1, 1] + 2 * k[2, 1] + k[3, 1]);
}
}
/*
Velocity Verlet algorithm:
1. Calculate y(t+h) = y(t) + v(t)h + 0.5a(t)h*h
2. Derive a(t+h) from dv/dt = -y using y(t+h)
3. Calculate v(t+h) = v(t) + 0.5*(a(t) + a(t+h))*h
*/
private void VelocityVerlet(Particle[] particles, double t, double h, int particleNumber)
{
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
//position
this.r[l] += h * this.v[l] + 0.5 * h * h * acc(this.r[l], particles, particleNumber, r_temp, l);
//velocity
this.v[l] += 0.5 * h * (acc(r_temp[l], particles, particleNumber, r_temp,l)
+ acc(this.r[l], particles, particleNumber, r_temp,l));
}
}
}
static void Main(string[] args)
{
//output file
TextWriter output = new StreamWriter("ispis.txt");
// declarations of variables
Particle[] particles = new Particle[2];
particles[0] = new Particle(0, 0, 0, 0, 0, 0, 1); //sun
particles[1] = new Particle(1, 0, 0, 0, 6.28, 0, 3.003467E-06); //earth
int N = 200;
double h, t, tmax;
double[,,] x = new double[particles.Length, N, 3]; //output
// setting initial values, step size and max time tmax
h = 0.01; // the step size in years
tmax = h * N;
// initial time
t = 0;
int i = 0;
while (t <= tmax) {
//updates position of all particles
for (int z = 1; z < particles.Length; z++)
particles[z].Update(particles, t, h, z);
//saves the position for output
for (int j = 1; j < particles.Length ; j++)
for (int z = 0; z < 3; z++ )
x[j,i,z] = particles[j].r[z];
t += h;
i++;
}
//output to file
for (int k = 0; k < particles.Length; k++ )
{
for (int f = 0; f < 3; f++)
{
for (int l = 0; l < N; l++)
output.Write(string.Format("{0,-15:0.########},", x[k,l,f]));
output.Write(string.Format("\n\n"));
}
output.Write(string.Format("\n\n\n\n"));
}
output.Close();
}
}
And here's the plot of the output data for earth's orbit:
Your model calculates the gravity force between two particles twice: for the first particle the force is based on their original coordinates, and for the second particle it is based on an updated position of the first one. This is a clear violation of the Newton's 3rd law. You must precompute all the forces before any update.
Your problem with the orbital of Earth is because the Center of Gravity of the System Earth-Sun, if you want to see the Orbital stay in loops you need to set center Of Gravity In (x,y,z)=(0,0,0) ;
I have a C# code based on your code Above so :
public partial class Form1 : Form
{
static
int
x1,y1,x2,y2, x3, y3;//for the 3th particule
private void timer1_Tick_1(object sender, EventArgs e)
{Moveu();
Invalidate();
}
private void button1_Click_1(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
public Form1()
{
InitializeComponent();
Paint += new PaintEventHandler(paint);
MouseDown += new MouseEventHandler(mouse_Click);
MouseUp += new MouseEventHandler(mouse_up);
MouseMove += new MouseEventHandler(mouse_move);
// x y z vx vy vz m
particles[0] = new Particle( 0, 0, 0, 0, 0, 0, 1 ) ; //sun
particles[1] = new Particle( 1, 0, 0, 0, 6, 0, 0.03 ); //earth
// particles[2] = new Particle( 0, 2, 0, 0, 0, 0, 1 ); //planet
x1 = (int)(100 * particles[0].r[0] + 300);
y1 = (int)(100 * particles[0].r[1] + 300);
x2 = (int)(100 * particles[1].r[0] + 300);
y2 = (int)(100 * particles[1].r[1] + 300);
}
Particle[] particles = new Particle[2];
void Moveu()
{
double h, t;
// setting initial values, step size and max time tmax
h = 0.005; // the step size in years
// initial time
t = 0;
//updates position of --all-- particles ( z=0 not z=1 )
for (int z = 0; z < particles.Length; z++)
particles[z].RungeKutta4(particles, t, h, z);
x1 = (int)(100 * particles[0].r[0] + 300); // +300 just for render it in centre
y1 = (int)(100 * particles[0].r[1] + 300);
x2 = (int)(100 * particles[1].r[0] + 300);
y2 = (int)(100 * particles[1].r[1] + 300);
// x3 = (int)(100 * particles[2].r[0] + 300);
// y3 = (int)(100 * particles[2].r[1] + 300);
}
void paint(object s, PaintEventArgs e)
{
Graphics graf;
graf = CreateGraphics();
graf.FillEllipse(new SolidBrush(Color.AntiqueWhite), x1 + move.X, y1 + move.Y, 50, 50);
graf.FillEllipse(new SolidBrush(Color.Blue), x2 + move.X, y2 + move.Y, 10, 10);
// graf.FillEllipse(new SolidBrush(Color.Yellow), x3, y3, 20, 20);
}
class Particle
{
public double[] r; // position vector
public double[] v; // velocity vector
public double mass;
//constructor
public Particle() { }
public Particle(double x, double y, double z, double vx, double vy, double vz, double m)
{
this.r = new double[3];
this.v = new double[3];
this.r[0] = x;
this.r[1] = y;
this.r[2] = z;
this.v[0] = vx;
this.v[1] = vy;
this.v[2] = vz;
this.mass = m;
}
private double acc(double r, Particle[] particles, int particleNumber, double[] r_temp, int l)
{
// dv/dt = f(x) = -G * m_i * (x - x_i) / [(x - x_i)^2 + (y - y_i)^2 + (z - z_i)^2]^(3/2)
double sum = 0;
switch (l)
{
case 0:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[1] - particles[i].r[1], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 1:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 2:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[1] - particles[i].r[1], 2), 1.5);
break;
}
return -G * sum;
}
public void RungeKutta4(Particle[] particles, double t, double h, int particleNumber)
{
//current position of the particle is saved in a vector
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
double[,] k = new double[4, 2];
k[0, 0] = this.v[l]; //k1_r
k[0, 1] = acc(this.r[l], particles, particleNumber, r_temp, l); //k1_v
k[1, 0] = this.v[l] + k[0, 1] * 0.5 * h; //k2_r
k[1, 1] = acc(this.r[l] + k[0, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k2_v
k[2, 0] = this.v[l] + k[1, 1] * 0.5 * h; //k3_r
k[2, 1] = acc(this.r[l] + k[1, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k3_v
k[3, 0] = this.v[l] + k[2, 1] * h; //k4_r
k[3, 1] = acc(this.r[l] + k[2, 0] * h, particles, particleNumber, r_temp, l); //k4_v
this.r[l] += (h / 6.0) * (k[0, 0] + 2 * k[1, 0] + 2 * k[2, 0] + k[3, 0]);
this.v[l] += (h / 6.0) * (k[0, 1] + 2 * k[1, 1] + 2 * k[2, 1] + k[3, 1]);
}
}
}
public static double G = 4 * Math.PI * Math.PI; //then time unite in years and length unite = distance between Earth and Sun and masse is the sun masse unite
void mouse_Click(object o, MouseEventArgs e)
{
dwn = new Point(e.X, e.Y);
if ("" + e.Button == "Left")
{
pos = move;
clicked = true;
}
}
void mouse_move(object o, MouseEventArgs e)
{
if (clicked)
{
move = new Point(e.X + pos.X - dwn.X, e.Y + pos.Y - dwn.Y);
Invalidate();
}
}
void mouse_up(object o, MouseEventArgs e)
{
clicked = false;
}
Point dwn, pos,move;
bool clicked;
}`you need to create a Timer and a button [![enter image description here][1]][1]