用.NET模擬天體運動

2021-02-22 dotNET跨平臺

用.NET模擬天體運動

這將是一篇罕見而偏極客的文章。

我上大學時就見過一些模擬太陽系等天體運動的軟體和網站,覺得非常酷炫,比如這個(http://www.astronoo.com/en/articles/positions-of-the-planets.html): 

其酷炫之處不僅在於天體運動軌跡能畫出美妙的弧線,更重要的是其運動規律完全由萬有引力定律產生,不需要對其運動軌跡進行編程。所有天體受其它天體的合力,然後按照其加速度運行。只需一個起始坐標和起始速度,就能坐下來欣賞畫面。

我從大學畢業後就一直對這個抱有深厚興趣,工作第一年時我就用 C++做過一版;後來我負責公司前端工作,又用 js/ canvas又做了一個重製版;由於近期爆發的 .NET「革命」,我近期又用 C#/ .NET再次重製了一版。

需要的數學知識

由於是程序看數學知識,此處我將使用代碼來表示公式。

萬有引力,該力 F與兩個天體的質量 m1, m2成正比,如距離 r的平方成反比,用代碼表示為: F=m1*m2*G/r^2;

牛頓第二定律,加速度 a等於合力 F除以質量 m,用代碼表示為: a=F/m;

速度 v與加速度 a以及時間 t的關係,用代碼表示為: v=a*t;

距離 s與速度 v以及時間 t的關係,用代碼表示為: s=v*t。

這裡面的所有知識都已經在高中物理提過了,但有兩點需要注意:

核心代碼天體類:

class Star

{

public LinkedList<Vector2> PositionTrack = new LinkedList<SharpDX.Vector2>();

public double Px, Py, Vx, Vy;

public double Mass;

public float Size => (float)Math.Log(Mass) * 2;

public Color Color = Color.Black;

public void Move(double step)

{

Px += Vx * step;

Py += Vy * step;

PositionTrack.AddFirst(new Vector2((float)Px, (float)Py));

if (PositionTrack.Count > 1000)

{

PositionTrack.RemoveLast();

}

}

}

注意,我沒指定大小 Size,直接給值為其質量的對數乘 2,另外注意我使用了一個 PositionTrack的 鍊表來存儲其運動軌跡。

萬有引力、加速度、速度計算

void Step()

{

foreach (var s1 in Stars)

{

// star velocity

// F = G * m1 * m2 / r^2

// F has a direction:

double Fdx = 0;

double Fdy = 0;

const double Gm1 = 100.0f; // G*s1.m

var ttm = StepDt * StepDt; // t*t/s1.m

foreach (var s2 in Stars)

{

if (s1 == s2) continue;

var rx = s2.Px - s1.Px;

var ry = s2.Py - s1.Py;

var rr = rx * rx + ry * ry;

var r = Math.Sqrt(rr);

var f = Gm1 * s2.Mass / rr;

var fdx = f * rx / r;

var fdy = f * ry / r;

Fdx += fdx;

Fdy += fdy;

}

// Ft = ma -> a = Ft/m

// v = at -> v = Ftt/m

var dvx = Fdx * ttm;

var dvy = Fdy * ttm;

s1.Vx += dvx;

s1.Vy += dvy;

}

foreach (var star in Stars)

{

star.Move(StepDt);

}

}

注意其中有個 foreach循環,它將一一計算每個天體對某天體的力,然後通過累加的方式求出合力,最後依照合力計算加速度和速度。如果使用 gmp等高精度計算庫,該循環將存在性能熱點,因此可以將這個 foreach改成 Parallel.For加 lock的方式修改合力 Fdx和 Fdy,可以提高性能( C++的代碼就是這樣寫的)。

繪圖代碼

public void Draw(DeviceContext ctx)

{

ctx.Clear(Color.DarkGray);

using var solidBrash = new SolidColorBrush(ctx, Color.White);

float allHeight = ctx.Size.Height;

float allWidth = ctx.Size.Width;

float scale = allHeight / 100.0f;

foreach (var star in Stars)

{

using var radialBrush = new RadialGradientBrush(ctx, new RadialGradientBrushProperties

{

Center = Vector2.Zero,

RadiusX = 1.0f,

RadiusY = 1.0f,

}, new SharpDX.Direct2D1.GradientStopCollection(ctx, new[]

{

new GradientStop{ Color = Color.White, Position = 0f},

new GradientStop{ Color = star.Color, Position = 1.0f},

}));

ctx.Transform =

Matrix3x2.Scaling(star.Size) *

Matrix3x2.Translation(((float)star.Px + 50) * scale + (allWidth - allHeight) / 2, ((float)star.Py + 50) * scale);

ctx.FillEllipse(new Ellipse(Vector2.Zero, 1, 1), radialBrush);

ctx.Transform =

Matrix3x2.Translation(allHeight / 2 + (allWidth - allHeight) / 2, allHeight / 2);

foreach (var line in star.PositionTrack.Zip(star.PositionTrack.Skip(1)))

{

ctx.DrawLine(line.First * scale, line.Second * scale, solidBrash, 1.0f);

}

}

ctx.Transform = Matrix3x2.Identity;

}

注意我在繪圖代碼邏輯中做了一些矩陣變換,我把所有邏輯做成了窗體解析度無關的,假定屏幕長和寬的較小值為 100,然後左上角坐標為 -50,-50,右下角坐標為 50,50。

星系模擬太陽、地球和月亮

這是最容易想到了,地球繞太陽轉,月亮繞地球轉,創建代碼如下:

public static StarSystem CreateSolarEarthMoon()

{

var solar = new Star

{

Px = 0, Py = 0,

Vx = 0.6, Vy = 0,

Mass = 1000,

Color = Color.Red,

};

// Earth

var earth = new Star

{

Px = 0, Py = -41,

Vx = -5, Vy = 0,

Mass = 100,

Color = Color.Blue,

};

// Moon

var moon = new Star

{

Px = 0, Py = -45,

Vx = -10, Vy = 0,

Mass = 10,

};

return new StarSystem(new List<Star> { solar, earth, moon });

}

注意所有數據都沒使用真實數字模擬(不然地球繞太陽轉一圈需要 365天才能看完😂),運行效果如下: 

從軌跡可以看出,由於太陽引力的作用,地球會轉著太陽轉,但也同樣由於地球和月球引力的作用,太陽也在以微小的角度在「公轉」。

擴展

如果將太陽質量翻倍( 1000-> 2000),會是何種效果呢?

可見這樣一來,由於引力太大,導致地球速度變快,月亮就被地球「甩」出去了,然後地球軌道也變成了實實在在的橢圓。

雙子星

宇宙中存在這樣一種星系,它的兩顆恆星互相圍繞對方轉,也可以模擬出來: 

注意兩個天體在接近時速度會變快,遠離時速度會變慢,這是由於萬有引力與距離平方成反比決定的。

擴展N星系統

static IEnumerable<Star> CreateStars(int N)

{

for (var i = 0; i < N; ++i)

{

double angle = 1.0f * i / N * Math.PI * 2;

double R = 45;

double M = 10000 * 2 / (N * Math.Sqrt(N) * Math.Log(N));

double v = 5;

double px = R * Math.Sin(angle);

double py = R * -Math.Cos(angle);

double vx = v * Math.Cos(angle);

double vy = v * Math.Sin(angle);

yield return new Star

{

Px = px,

Py = py,

Vx = vx,

Vy = vy,

Mass = M,

};

}

}

通過精密的數學計算,可以讓任意多的天體組織為系統,如將 3當作 N傳入函數,即可組織為「三星系統」,運行效果如下: 

注意,超過 2星的系統都不穩定(因此「三星系統」也不穩定),轉過兩圈之後所有天體由於 double類型的誤差已經累積到不可逆轉的程度,「三星系統」會慢慢崩潰解體。

看看四星系統,命運也差不多(又比「三星」稍穩定,需要等待好幾圈才崩潰): 

展望與總結

由於誤差是 double類型的精度限制而累積的,在 C++中我可以使用 gmp、 mpir、 mpfr等高精度計算庫來模擬計算,性能也非常高。我之前使用 C++和 mpir/ boost配合,可以讓四星系統穩定運行長達 15分鐘不崩潰,還能在我的 WindowsPhone(😂)上流暢運行。

之前有人將 mpir移植到了 .NET,但不支持 .NETCore(https://github.com/wezeku/Mpir.NET),有人將 mpfr移植到了 .NET(https://github.com/emphasis87/mpfr.NET/pull/5), .NETCore可以運行,但有坑爹的 bug,我提了 PR,但作者似乎沒心思Merge😂。

大小數計算在天文、地震、天氣、海洋等科研領域有不可取代的作用,我挺希望 .NET能提供一個高性能、高精度的小數計算庫,如 BigFloat。有人會問 .NET4.0不是提供了 BigInteger嗎?難道不夠?是真不夠!整數計算和小數計算不完全一樣,性能這一關就過不去。但在 .NETCore中這個問題官方似乎沒有太大動力去做,我在 github上找到了幾個相關 issue,都是 open狀態:

本文中涉及的所有完整、可運行代碼都已經上傳到我的 github博客,各位可以自行下載:https://github.com/sdcb/blog-data/tree/master/2019/20191214-simulate-planet-movement-using-dotnet

喜歡的朋友 請關注我的微信公眾號:【DotNet騷操作】

相關焦點

  • 《宇宙沙盤:平方》可模擬三體運動
    遊戲中模擬了重力、氣候、碰撞、物質的相互作用等等現象,也揭示著地球的美麗和脆弱。三體運動在遊戲中的演示視頻:沙盤宇宙一代中,三體運動的演示視頻
  • 高中物理:天體運動要怎麼分析計算?用這些方法!
    ②分析計算天體運動的兩種基本方法。常規的天體運動常規的天體運動,也即是某個天體繞著某個中心天體的運動。特別的天體運動特別的天體運動,簡單來說有兩種基本類型:雙星模型兩個行星互相繞著對方做圓周運動而天體運動的分析與計算,會涉及到兩個基本的情況:在天體表面或者天體表面附近。如果一個物體剛好在某個天體的表面,並且知道它的重力。
  • 什麼力量在維持天體的運動?
    宇宙中的每個天體都在太空中不斷運動,例如,地球自從45億年前誕生以來,它一直在環繞太陽運動,並且自身還一直在自轉;就像地球一樣,太陽本身也會繞軸自轉,並且它還會環繞銀河系中心旋轉。那麼,宇宙中的天體是永動機嗎?為什麼它們一直在運動而不會停下來?
  • 天體運動的原力是什麼?晚年牛頓痴迷神學,認為是上帝踢了一腳
    在地球上、宇宙中,運動是絕對的,靜止是相對的,這一點從天體之間的運轉情況就可以看得出來。地球作為太陽系八大行星之一,無時無刻不在圍繞著太陽運動,因此從地球的角度來看太陽,太陽就是每天從東邊升起西邊落下。那麼問題來了,促使地球產生運動的力從何而來呢?
  • 快速流動的電子可能模擬天體物理髮電機?
    這在一定程度上是因為,將流體運動的複雜方程與控制電場和磁場彎曲、扭曲、相互作用和傳播的方程結合起來,計算起來會加倍困難。但這也因為實驗室的發電機,試圖模擬天體物理版本,是昂貴的,危險的,並沒有可靠地產生籤名自維持的磁場的真正發電機。
  • 為什麼用都卜勒效應可以測得天體的運動速度
    1842年,奧地利物理學家都卜勒發現,運動物體發出的聲音在靜止的觀測者聽起來會發生變化。當發聲物體遠離觀測者運動時,觀測者聽到的聲波波長就會比靜止波長更長,而聲源朝向觀測者運動時,聽到的聲波波長就會比靜止波長更短。速度越高,波長變化越大。
  • 萬有引力與天體運動備考指導
    一、概念理解1.克卜勒運動三大定律(1)克卜勒第一定律:所有的行星繞太陽運動的軌道都是橢圓,太陽處在所有橢圓的一個焦點上;(2)克卜勒第二定律:對於每一個行星而言,太陽和行星的連線在相等的時間內掃過的面積相等;(3)克卜勒第三定律:所有行星的軌道的半長軸的三次方跟公轉周期的二次方的比值都相等。
  • 為什麼用都卜勒效應可以測得天體的運動速度?
    現在,我們就來聊一下,為什麼用都卜勒效應能測得天體的運行速度!&nbsp&nbsp&nbsp&nbsp1842年,奧地利物理學家都卜勒發現,運動物體發出的聲音在靜止的觀測者聽起來會發生變化。當發聲物體遠離觀測者運動時,觀測者聽到的聲波波長就會比靜止波長更長,而聲源朝向觀測者運動時,聽到的聲波波長就會比靜止波長更短。速度越高,波長變化越大。
  • 高一物理:《3.1 天體運動》學案
    【學習方法】觀察法、探究法、討論法、分析法、實物法【學習過程】一、日心說地心說認為:__________是宇宙的中心,它是___________的,太陽、月亮及其他天體都繞______________做圓周運動;日心說認為:___________是宇宙的中心,它是________________的,地球和所有的行星都繞______________做圓周運動。
  • 迄今為止最詳細的大規模宇宙演化模擬
    通過模擬物質、恆星、宇宙氣體、磁場和超大質量黑洞的200億多個粒子做到了這一點,這個計算本身需要斯圖加特Hazel Hen超級計算機上的16000個內核,協同工作一年多,相當於在一個處理器上工作一萬五千年,使其成為迄今為止要求最高的天體物理計算之一。
  • 古人對天體運動的看法和克卜勒行星運動定律
    人類自古就常常仰望星空,思考天上的太陽、月亮、星星等天體與我們的關係,歷代先哲對天體現象提出了很多看法。眾說紛紜,我們今天主要介紹西方自然科學的看法。一、古代人們對天體運動的認識:1.「地心說」:古代人們認為,地球是宇宙的中心,是靜止不動的,太陽、月亮等各星體都圍繞地球做簡單而完美的圓周運動。代表人物:希臘人亞里斯多德(提出)、託勒密。2.
  • 如何用COMSOL變形網格接口模擬平移運動?
    如何用COMSOL變形網格接口模擬平移運動?本篇文章中,我們將介紹應何時使用這些接口,以及如何通過它們來高效模擬平移運動。 使用變形網格的優勢 假設我們希望通過 COMSOL Multiphysics 模型來描述一個在較大域內移動的固體對象,域內充滿諸如空氣等的流體,甚至可以是真空環境。首先假定對象隨時間的移動路徑已知,我們不必擔心需要使用哪個物理場求解模型,但將假定希望求解移動域及周圍域中的一些場。
  • 克卜勒在牛頓提出萬有引力之前,已經歸納出成熟的天體運動規律
    你相不相信,在牛頓提出萬有引力之前,就有人進行觀測和猜測,歸納出了至今仍然適用的天體運行的規律。這是怎麼一回事呢?之前小編為大家介紹了彗星這種看上去很不起眼的小天體,其實卻給咱們人類文明社會行為帶來很大的影響。那從科學上來講,這背後其實隱藏著太陽系的起源以及生命起源的奧秘。
  • 高考物理天體運動過程基本參量
    網絡資源 2019-05-09 19:34:42   人類行為學意義上的天體運動
  • 我的物質基理(21)——天體運動
    天體運動是自然現象中最難合理解釋的自然現象,此前並沒有任何一種力學認識對天體運動是作出了合理解釋的,至少都沒有能夠從力學上真正建立起可以相對穩定的運動軌道。  一、牛頓力學的萬在引力定律與慣性定律下的圓周運動只能建立起極極不穩定的天體運動,牛頓力學中的天體運動要麼以圓周運動的半徑加速增加地進行;要麼以圓周運動的半徑加速減少地進行。
  • 天體運動中的雙星與多星問題
    天體運動中的雙星與多星問題,是天體運動中較為抽象的一個問題,對該問題的認識,抓住以下幾點:1、萬有引力公式中的r的物理含義為質心距(質量中心距離),圓周運動需要的向心力中的r則為圓運動的軌道半徑且對於雙星模型,有質心距等於兩個星球圓運動軌道半徑之和。2、要形成穩定的雙星系統,則兩星球做圓運動的周期和角速度一定相等,否則則會因為一個轉得快,一個轉得慢而改變兩者之間的距離,進而改變兩者之間的萬有引力。依據以上兩點可以列出方程,然後得出相應的一些關係或者物理量。
  • 宇宙的天體為什麼都在轉動或運動
    宇宙中的天體,都不可能靜止不動,因為它們不是受引力促使運動,就是時空波推動它們運動,這種時空波就像扔進水塘的石頭激起的波浪。那麼彗星是怎樣運動的呢?其實彗星要麼是兩塊隕石碰撞,而產生的一種推力使它快速在宇宙中飛翔。
  • 能精確計算天體運動的 古代金屬計算器
    卡地夫大學的天體物理學教授愛德蒙茲表示,它可以被稱作是已知的第一臺計算器。我們近來的研究使用了非常現代化的技術,我們相信已經揭示了它的真實用途。安提凱希拉能夠進行加、減、乘、除運算,能夠制定日曆,顯示太陽和月亮的位置。  愛德蒙茲和他的同事發現,安提凱希拉有一個刻度盤,能夠預測何時可能會發生日食或月食。安提凱希拉還考慮了月亮的橢圓形軌道。
  • 宇宙中有沒有不轉的天體?宇宙本身又是怎麼運動的?
    中學的科學書上就明確告訴大家,宇宙中所有天體都在不停的運動,地球在自轉的同時本身還以30千米/秒的速度繞著太陽公轉,太陽則帶著一幫小弟以120千米/秒的速度繞著銀心公轉,而銀河係數千億顆恆星卻正朝著一個叫做巨引源的神秘區域以超過600千米/秒的速度前進!更大範圍呢是怎麼運動呢?宇宙又是咋個運動呢?
  • 古柏帶常現天體排成直線運行,天文學家模擬得出有巨行星隱藏其內
    天文學家在觀測古柏帶(即柯伊柏帶)區域時,發現了幾種奇怪的現象:現象一,柯伊伯帶有十幾個天體經常排成縱一字型沿各自的軌道圍繞著太陽旋轉。而天文學家在最近幾年卻觀測到,柯伊伯帶有十幾個天體經常發生連珠的現象,且周期性非常之短,有時候在一個地球年裡可以發生幾十次連珠的現象,這種現象令科學家們困惑不已。現象二,柯伊柏帶這十幾個經常連珠的天體的運行軌道都不在同一平面內,如同孔雀開屏般,呈360度分布。