CPUでの個別要素法プログラム
Пример #1
0
        /// <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);
        }
Пример #2
0
        /// <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);
        }
Пример #3
0
        /// <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();
        }
Пример #4
0
        /// <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();
        }
Пример #5
0
        /// <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++;
        }