/// <summary> /// 最上位ウインドウを作成 /// </summary> public MainWindow(DemComputerCpu computer) { // コンポーネント初期化 InitializeComponent(); // ポイントスプライトプログラムを作成 this.programPoint = new Program( this.Viewport, Properties.Resources.point_vertex, Properties.Resources.point_geometry, Properties.Resources.point_fragment); // ビューポートにプログラムを追加 this.Viewport.Programs.Add(this.programPoint); // 点の大きさを変更可能に設定 this.programPoint.Enable(EnableCap.ProgramPointSize); // ポイントスプライト用 this.programPoint.Enable(EnableCap.PointSprite); // パラメーター再設定 Action setParameters = () => { // 視野変換および投影変換行列を設定 this.programPoint.SetUniform("view", this.Viewport.View); this.programPoint.SetUniform("projection", this.Viewport.Projection); // 表示する大きさ(拡大率)を設定 this.programPoint.SetUniform("size", (float)this.Viewport.Camera.R); // カメラの位置を設定 this.programPoint.SetUniform("cameraPosition", this.Viewport.Camera.Position); }; // ウインドウが読みこまれたら this.Loaded += (sender, e) => { // 真上からの平行光線を設定 this.programPoint.SetUniform("sunLight", new Vector3(0, 0, -0.6f)); // 環境光強度を割り当て this.programPoint.SetUniform("ambient", 0.1f); // カメラからの光源の強度を割り当て this.programPoint.SetUniform("cameraLight", 0.3f); }; // 再描画時に this.Viewport.Invalidated += (sender, e) => { // パラメーター再設定 setParameters(); }; // カメラが動いたら this.Viewport.Camera.Changed += (sender, e) => { // パラメーター再設定 setParameters(); }; // 終了時に this.Closed += (sender, e) => { // プログラムを削除 this.programPoint.Dispose(); }; // 描画中かどうか bool isRendering = false; // 出力用粒子配列を初期化 var outputParticles = new OutputParticle[0]; // 描画処理 Timer renderer = new Timer((TimerCallback)((state) => { // 描画中でなければ if(!isRendering) { // 描画開始 isRendering = true; // 計算空間画面に this.Viewport.Dispatcher.BeginInvoke((Action)(() => { // 現在の粒子を取得 var thisParticles = computer.GetParticles(); // 出力用と粒子数が違えば if(outputParticles.Length != thisParticles.Length) { // 出力用粒子群を再生成 outputParticles = new OutputParticle[thisParticles.Length]; // 動的バッファーを作成 particlesBuffer = Buffer.CreateDynamic<OutputParticle>( this.Viewport, BeginMode.Points, outputParticles.Length, MainWindow.CreatePointsIndices(outputParticles.Length)); // ポイントスプライトプログラムにバッファーを割り当て programPoint.AttachBuffer(particlesBuffer, new[] { new VertexAttribution("particleX", VertexAttribPointerType.Float, 3, 0), new VertexAttribution("particleD", VertexAttribPointerType.Float, 1, Vector3.SizeInBytes), new VertexAttribution("particleColor", VertexAttribPointerType.Float, 4, Vector3.SizeInBytes + sizeof(float)), }); } // すべての粒子を int i = 0; foreach(var particle in thisParticles) { // 出力用粒子に変換 outputParticles[i++] = new OutputParticle((OpenTK.Vector3)particle.X, (float)particle.D, particle.Material.Color); } // 出力粒子を書き込み particlesBuffer.WriteData(outputParticles); // 再描画 this.Viewport.Invalidate(); // 処理完了 isRendering = false; })); } // 時刻表示パネルに this.TimePanel.Dispatcher.BeginInvoke((Action)(() => { // 時刻・タイムステップ数・時間刻みを表示 this.TBox.Text = computer.T.ToString("G5"); this.StepsBox.Text = computer.TimeStep.ToString(); this.DtBox.Text = computer.Dt.ToString("e"); // 粒子数を表示 this.ParticleCountBox.Text = computer.ParticleCount.ToString(); })); })); // 描画を開始 renderer.Change(0, fps); // 前の時刻と時間ステップ double oldT = 0; double oldTimeStep = 0; var oldRealT = DateTime.Now; var initialT = DateTime.Now; // 速度計測 Timer speedMeasure = new Timer((TimerCallback)((state) => { // 速度表示パネルに this.TimePanel.Dispatcher.BeginInvoke((Action)(() => { // 時間ステップを取得 var thisTimeStep = computer.TimeStep; var thisRealT = DateTime.Now; // 時間ステップが進んでいれば if(thisTimeStep != oldTimeStep) { // 時刻を取得 var thisT = computer.T; // 経過時間を秒で計算 double interval = (double)(thisRealT - oldRealT).TotalSeconds; // 各データを表示 this.StepPerRealBox.Text = (interval / (thisTimeStep - oldTimeStep)).ToString("G5"); this.StepPerRealPerParticleBox.Text = (interval / (thisTimeStep - oldTimeStep) / computer.ParticleCount).ToString("G5"); this.ComputationalPerRealBox.Text = (interval / (thisT - oldT)).ToString("G5"); // 前の時刻と時間ステップを設定 oldT = thisT; oldTimeStep = thisTimeStep; oldRealT = thisRealT; } // 現在時刻を表示 this.RealTimeBox.Text = (thisRealT - initialT).ToString(@"d\.hh\:mm\:ss"); })); })); // 1秒感覚で速度計測を開始 speedMeasure.Change(0, 1000); }
/// <summary> /// 最上位ウインドウを作成 /// </summary> public MainWindow(DemComputerCpu computer) { // コンポーネント初期化 InitializeComponent(); // ポイントスプライトプログラムを作成 this.programPoint = new Program( this.Viewport, Properties.Resources.point_vertex, Properties.Resources.point_geometry, Properties.Resources.point_fragment); // ビューポートにプログラムを追加 this.Viewport.Programs.Add(this.programPoint); // 点の大きさを変更可能に設定 this.programPoint.Enable(EnableCap.ProgramPointSize); // ポイントスプライト用 this.programPoint.Enable(EnableCap.PointSprite); // パラメーター再設定 Action setParameters = () => { // 視野変換および投影変換行列を設定 this.programPoint.SetUniform("view", this.Viewport.View); this.programPoint.SetUniform("projection", this.Viewport.Projection); // 表示する大きさ(拡大率)を設定 this.programPoint.SetUniform("size", (float)this.Viewport.Camera.R); // カメラの位置を設定 this.programPoint.SetUniform("cameraPosition", this.Viewport.Camera.Position); }; // ウインドウが読みこまれたら this.Loaded += (sender, e) => { // 真上からの平行光線を設定 this.programPoint.SetUniform("sunLight", new Vector3(0, 0, -0.6f)); // 環境光強度を割り当て this.programPoint.SetUniform("ambient", 0.1f); // カメラからの光源の強度を割り当て this.programPoint.SetUniform("cameraLight", 0.3f); }; // 再描画時に this.Viewport.Invalidated += (sender, e) => { // パラメーター再設定 setParameters(); }; // カメラが動いたら this.Viewport.Camera.Changed += (sender, e) => { // パラメーター再設定 setParameters(); }; // 終了時に this.Closed += (sender, e) => { // プログラムを削除 this.programPoint.Dispose(); }; // 描画中かどうか bool isRendering = false; // 出力用粒子配列を初期化 var outputParticles = new OutputParticle[0]; // 描画処理 Timer renderer = new Timer((TimerCallback)((state) => { // 描画中でなければ if (!isRendering) { // 描画開始 isRendering = true; // 計算空間画面に this.Viewport.Dispatcher.BeginInvoke((Action)(() => { // 現在の粒子を取得 var thisParticles = computer.GetParticles(); // 出力用と粒子数が違えば if (outputParticles.Length != thisParticles.Length) { // 出力用粒子群を再生成 outputParticles = new OutputParticle[thisParticles.Length]; // 動的バッファーを作成 particlesBuffer = Buffer.CreateDynamic <OutputParticle>( this.Viewport, BeginMode.Points, outputParticles.Length, MainWindow.CreatePointsIndices(outputParticles.Length)); // ポイントスプライトプログラムにバッファーを割り当て programPoint.AttachBuffer(particlesBuffer, new[] { new VertexAttribution("particleX", VertexAttribPointerType.Float, 3, 0), new VertexAttribution("particleD", VertexAttribPointerType.Float, 1, Vector3.SizeInBytes), new VertexAttribution("particleColor", VertexAttribPointerType.Float, 4, Vector3.SizeInBytes + sizeof(float)), }); } // すべての粒子を int i = 0; foreach (var particle in thisParticles) { // 出力用粒子に変換 outputParticles[i++] = new OutputParticle((OpenTK.Vector3)particle.X, (float)particle.D, particle.Material.Color); } // 出力粒子を書き込み particlesBuffer.WriteData(outputParticles); // 再描画 this.Viewport.Invalidate(); // 処理完了 isRendering = false; })); } // 時刻表示パネルに this.TimePanel.Dispatcher.BeginInvoke((Action)(() => { // 時刻・タイムステップ数・時間刻みを表示 this.TBox.Text = computer.T.ToString("G5"); this.StepsBox.Text = computer.TimeStep.ToString(); this.DtBox.Text = computer.Dt.ToString("e"); // 粒子数を表示 this.ParticleCountBox.Text = computer.ParticleCount.ToString(); })); })); // 描画を開始 renderer.Change(0, fps); // 前の時刻と時間ステップ double oldT = 0; double oldTimeStep = 0; var oldRealT = DateTime.Now; var initialT = DateTime.Now; // 速度計測 Timer speedMeasure = new Timer((TimerCallback)((state) => { // 速度表示パネルに this.TimePanel.Dispatcher.BeginInvoke((Action)(() => { // 時間ステップを取得 var thisTimeStep = computer.TimeStep; var thisRealT = DateTime.Now; // 時間ステップが進んでいれば if (thisTimeStep != oldTimeStep) { // 時刻を取得 var thisT = computer.T; // 経過時間を秒で計算 double interval = (double)(thisRealT - oldRealT).TotalSeconds; // 各データを表示 this.StepPerRealBox.Text = (interval / (thisTimeStep - oldTimeStep)).ToString("G5"); this.StepPerRealPerParticleBox.Text = (interval / (thisTimeStep - oldTimeStep) / computer.ParticleCount).ToString("G5"); this.ComputationalPerRealBox.Text = (interval / (thisT - oldT)).ToString("G5"); // 前の時刻と時間ステップを設定 oldT = thisT; oldTimeStep = thisTimeStep; oldRealT = thisRealT; } // 現在時刻を表示 this.RealTimeBox.Text = (thisRealT - initialT).ToString(@"d\.hh\:mm\:ss"); })); })); // 1秒感覚で速度計測を開始 speedMeasure.Change(0, 1000); }
/// <summary> /// メインクラスを開始 /// </summary> public CpuDemMain() { // 時間刻み const double maxDt = 0.01; // 粒子の大きさ const double diameter = 50e-3; const double diameterWall = diameter * 0.8; const double length0 = diameter * 1.001; const double length0Wall = diameterWall * 0.75; // 計算領域 const double sizeX = 4500e-3; const double sizeZ = 2200e-3; const double extraSize = diameter; // 物性値 const double rho = 2.7e3; const double E = 30e6; const double nu = 0.2; // 重力加速度 Vector g = new Vector(0, 0, -9.8); // 立方体配列を初期化 var computer = new DemComputerCpu(maxDt, g); // 材質を生成 var materials = new [] { // 移動粒子 new Material(0, rho, E, nu, new Color4(50, 50, 255, 255)), // 壁 new Material(1, rho, E, nu, new Color4(200, 200, 200, 255)) }; ulong particleID = 0; // 領域内に for (double x = 0; x < sizeX; x += length0) { for (double z = 0; z < sizeZ; z += length0) { // 移動粒子作成して追加 computer.AddParticle(new Particle(particleID++, diameter * (0.4 + 0.6 * z / sizeZ), materials[0], ParticleType.FreeMovable) { X = new Vector(x, 0, z) }); } } // 床の部分に for (double x = -extraSize; x < sizeX + extraSize; x += length0Wall) { // 作成して追加 computer.AddParticle(new Particle(particleID++, diameterWall, materials[1], ParticleType.Fixed) { X = new Vector(x, 0, -extraSize) }); } // 壁の部分に for (double z = -extraSize; z < sizeZ + extraSize; z += length0Wall) { // 作成して追加 computer.AddParticle(new Particle(particleID++, diameterWall, materials[1], ParticleType.Fixed) { X = new Vector(-extraSize, 0, z) }); computer.AddParticle(new Particle(particleID++, diameterWall, materials[1], ParticleType.Fixed) { X = new Vector(sizeX + extraSize, 0, z) }); } // 計算中かどうか bool isComputing = true; // 個別要素法の自動実行スレッドを生成 Thread demWorker = new Thread((ThreadStart)(() => { // 計算中の間 while (isComputing) { // 次ステップに進める computer.Next(); } })); // アプリケーションの終了時に this.Exit += (sender, e) => { // 計算終了 isComputing = false; // 実行中断 demWorker.Abort(); }; // 自動実行スレッド開始 demWorker.Start(); // 最上位ウインドウを作成して設定 MainWindow mainWindow = new MainWindow(computer); base.MainWindow = mainWindow; // ウインドウ表示 base.MainWindow.Show(); }
/// <summary> /// メインクラスを開始 /// </summary> public CpuDemMain() { // 時間刻み const double maxDt = 0.01; // 粒子の大きさ const double diameter = 50e-3; const double diameterWall = diameter * 0.8; const double length0 = diameter * 1.001; const double length0Wall = diameterWall * 0.75; // 計算領域 const double sizeX = 4500e-3; const double sizeZ = 2200e-3; const double extraSize = diameter; // 物性値 const double rho = 2.7e3; const double E = 30e6; const double nu = 0.2; // 重力加速度 Vector g = new Vector(0, 0, -9.8); // 立方体配列を初期化 var computer = new DemComputerCpu(maxDt, g); // 材質を生成 var materials = new [] { // 移動粒子 new Material(0, rho, E, nu, new Color4( 50, 50, 255, 255)), // 壁 new Material(1, rho, E, nu, new Color4(200, 200, 200, 255)) }; ulong particleID = 0; // 領域内に for(double x = 0; x < sizeX; x += length0) { for(double z = 0; z < sizeZ; z += length0) { // 移動粒子作成して追加 computer.AddParticle(new Particle(particleID++, diameter * (0.4 + 0.6 * z / sizeZ), materials[0], ParticleType.FreeMovable) { X = new Vector(x, 0, z) }); } } // 床の部分に for(double x = -extraSize; x < sizeX + extraSize; x += length0Wall) { // 作成して追加 computer.AddParticle(new Particle(particleID++, diameterWall, materials[1], ParticleType.Fixed) { X = new Vector(x, 0, -extraSize) }); } // 壁の部分に for(double z = -extraSize; z < sizeZ + extraSize; z += length0Wall) { // 作成して追加 computer.AddParticle(new Particle(particleID++, diameterWall, materials[1], ParticleType.Fixed) { X = new Vector(-extraSize, 0, z) }); computer.AddParticle(new Particle(particleID++, diameterWall, materials[1], ParticleType.Fixed) { X = new Vector(sizeX+extraSize, 0, z) }); } // 計算中かどうか bool isComputing = true; // 個別要素法の自動実行スレッドを生成 Thread demWorker = new Thread((ThreadStart)(() => { // 計算中の間 while(isComputing) { // 次ステップに進める computer.Next(); } })); // アプリケーションの終了時に this.Exit += (sender, e) => { // 計算終了 isComputing = false; // 実行中断 demWorker.Abort(); }; // 自動実行スレッド開始 demWorker.Start(); // 最上位ウインドウを作成して設定 MainWindow mainWindow = new MainWindow(computer); base.MainWindow = mainWindow; // ウインドウ表示 base.MainWindow.Show(); }
/// <summary> /// 計算を1ステップ進める /// </summary> public void Next() { // 準備処理 this.prepare(); // 各粒子について this.EachParticle((particle, i) => { // 重力加速度に設定 particle.A = this.G; }); // 各粒子について this.EachParticle(0, this.ParticleCount - 1, (thisParticle, i) => { // 自分の質量を計算 double thisM = thisParticle.M; // 自分より後ろの番号の粒子について this.EachParticle(i + 1, this.ParticleCount, (anotherParticle, j) => { // 粒子間の距離を計算 double interparticleDistance = (thisParticle.X - anotherParticle.X).Length - (thisParticle.D + anotherParticle.D) / 2; // 距離が負なら if (interparticleDistance < 0) { // 変位を計算 double delta = -interparticleDistance; // 各平均値を計算 // * ヤング係数(調和平均) // * ポアソン比(調和平均) // * 質量(相加平均) double E = DemComputerCpu.HermonicMean(thisParticle.Material.E, anotherParticle.Material.E); double Nu = DemComputerCpu.HermonicMean(thisParticle.Material.Nu, anotherParticle.Material.Nu); double M = (thisParticle.M + anotherParticle.M) / 2; // 逓減率 double s0 = 1.0 / 2 / (1 + Nu); // ヘルツ理論および1次元臨界減衰条件から、バネ・ダッシュポットの係数を設定: // * 法線方向バネ:kn = √δ * 2/3 * E/(1-ν^2)*√(d1*d2/2(d1 + d2)) // * 接線方向バネ:ks = kn・s0 // * 法線方向ダッシュポット:cn = 2√(M・kn) // * 接線方向ダッシュポット:cs = cn/√s0 double k_n = System.Math.Sqrt(System.Math.Abs(delta)) * 2.0f / 3 * E / (1 - Nu * Nu) * System.Math.Sqrt(thisParticle.D * anotherParticle.D / 2 / (thisParticle.D + anotherParticle.D)); double k_s = System.Math.Abs(k_n) * s0; double c_n = 1000 * 2 * System.Math.Sqrt(M * System.Math.Abs(k_n)); double c_s = c_n * System.Math.Sqrt(s0); // 粒子2から粒子1を向く法線方向単位ベクトルを計算 Vector n = (thisParticle.X - anotherParticle.X); // 相対速度を計算 Vector deltaU = anotherParticle.U - thisParticle.U; // +cross(thisOmega, thisD * norm) + cross(anotherOmega, anotherD * norm); Vector u_n = deltaU.Dot(n) * n; // 法線・接線方向の各バネ・ダッシュポット力を計算 // * 法線方向バネ:en = kn・δ // * 接線方向バネ:es = ks・δs // * 法線方向ダッシュポット:dn = cn・法線方向の相対速度 // * 接線方向ダッシュポット:ds = cs・接線方向の相対速度 // * 接線方向バネ: Vector e_n = k_n * delta * n; Vector d_n = c_n * u_n; // 粒子間力を計算 Vector force = e_n + d_n; // 粒子間力を更新 thisParticle.A += force / thisM; anotherParticle.A += -force / anotherParticle.M; } }); }); // 時間刻みを設定 double dt = this.MaxDt; // 各粒子について this.EachParticle((particle, i) => { // 大きさがゼロでなければ if (!particle.U.IsZero) { // クーラン数から計算した時間刻みを設定 dt = System.Math.Min(dt, 0.02 * particle.D / particle.U.Length); } }); // 各粒子について this.EachParticle((particle, i) => { // 移動粒子なら if (particle.Type == ParticleType.FreeMovable) { // 等加速度直線運動 particle.X += particle.U * dt + particle.A * dt * dt / 2; particle.U += particle.A * dt; } }); // 時刻を進める this.Dt = dt; this.T += dt; this.TimeStep++; }