/// <summary> /// CPUでの計算プログラムを作成する /// </summary> /// <param name="maxDt">初期時間刻み</param> /// <param name="a">振幅</param> /// <param name="omega">角速度</param> public ComputerCpu(double maxDt, double a, double omega) : base(maxDt, a, omega) { // 粒子群を初期化 this.particles = new Particle[0]; // 準備処理は何もしない this.prepare = () => { }; // 粒子が追加された時に base.ParticleAdded += (sender, e) => { // 準備処理の時の処理を実装 this.prepare = () => { // 新しい粒子群配列を生成 var newParticles = new Particle[this.particles.Length + this.inputParticles.Count]; // 古い粒子群を新しい粒子群に複製 this.particles.CopyTo(newParticles, 0); // 入力粒子群を新しい粒子群に複製 this.inputParticles.CopyTo(newParticles, this.particles.Length); // 新しい粒子群を今の粒子群とする this.particles = newParticles; // 入力粒子群を空にする this.inputParticles.Clear(); // 準備処理は空 this.prepare = () => { }; }; }; }
/// <summary> /// 粒子を追加する /// </summary> /// <param name="particle">追加する粒子</param> public void AddParticle(Particle particle) { // 入力粒子に追加 this.inputParticles.Add(particle); // 粒子が追加されたことを通知 this.OnParticleAdded(); }
/// <summary> /// メインクラスを開始 /// </summary> public SimpleClooMain() { // 時間刻み 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 = 45000e-3; const double sizeZ = 2200e-3; const double extraSize = diameter; // 物性値 const double rho = 2.7e3; const double E = 30e6; const double nu = 0.2; // 振幅と角速度 double A = 0.5; double omega = 2.0; // 計算プログラムを初期化 var computerCpu = new ComputerCpu(maxDt, A, omega); var computerCL = new ComputerCL(maxDt, A, omega); // 最上位ウインドウを作成して設定 MainWindow mainWindow = new MainWindow(computerCpu, computerCL); base.MainWindow = mainWindow; mainWindow.Log("データ初期化中..."); // ウインドウ表示 base.MainWindow.Show(); mainWindow.LogLine("完了"); mainWindow.Log("データ作成中..."); // 材質を生成 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) { // 移動粒子を作成 var particle = new Particle(particleID++, diameter * (0.4 + 0.6 * z / sizeZ), materials[0], ParticleType.FreeMovable) { X = new Vector(x, 0, z) }; // 追加 computerCpu.AddParticle(particle); computerCL.AddParticle(particle); } } // 床の部分に for(double x = -extraSize; x < sizeX + extraSize; x += length0Wall) { // 固定粒子を作成 var particle = new Particle(particleID++, diameterWall, materials[1], ParticleType.Fixed) { X = new Vector(x, 0, -extraSize) }; // 追加 computerCpu.AddParticle(particle); computerCL.AddParticle(particle); } mainWindow.LogLine("完了"); // 計算中かどうか bool isComputing = false; // 計算中かどうかを変更する Action<bool> changeIsComputing = (isComputingNow) => { // 計算状態を設定 isComputing = isComputingNow; // ウインドウで mainWindow.Dispatcher.BeginInvoke((Action)(() => { // 1ステップ計算のボタンの有効化および無効化 mainWindow.ComputeOneButton.IsEnabled = !isComputingNow; // 計算状態に合わせて自動実行ボタンの表示を変える mainWindow.ComputeAllButton.Content = (isComputingNow) ? "自動実行を停止" : "自動実行を開始"; // 計算開始および終了を通知 mainWindow.LogLine((isComputingNow) ? "計算を開始" : "計算を終了"); })); }; // 1ステップ実行がクリックされたら mainWindow.ComputeOneButton.Click += (sender, e) => { // 計算中でなければ if(!isComputing) { // 計算開始 changeIsComputing(true); // 1ステップだけ進める mainWindow.computer.Next(); // 計算終了 changeIsComputing(false); } }; // 自動実行がクリックされたら mainWindow.ComputeAllButton.Click += (sender, e) => { // 計算中でなければ if(!isComputing) { // 計算開始 changeIsComputing(true); // 自動実行スレッド開始 new Task(() => { // 計算中の間 while(isComputing) { // 次ステップに進める mainWindow.computer.Next(); } // 計算終了 changeIsComputing(false); }, TaskCreationOptions.LongRunning).Start(); } // 計算中なら else { // 計算中断 changeIsComputing(false); } }; // プログラムが切り替わったら System.Windows.RoutedEventHandler stopWhenSwitchWith = (sender, e) => { // 計算終了 changeIsComputing(false); }; // プログラムが切り替わった時の処理 mainWindow.WithCpuButton.Checked += stopWhenSwitchWith; mainWindow.WithCLButton.Checked += stopWhenSwitchWith; // アプリケーションの終了時に this.Exit += (sender, e) => { // 計算終了 changeIsComputing(false); }; }
/// <summary> /// 現在の粒子を取得する /// </summary> /// <returns>現在の粒子の複製</returns> public override Particle[] GetParticles() { // 準備が出来ていなかったら if(this.bufferX != null) { // 出力データを確保 var particlesX = new Vector4[this.particleCount]; var particlesU = new Vector4[this.particleCount]; var particlesA = new Vector4[this.particleCount]; // バッファーから転送 this.queue.ReadFromBuffer(this.bufferX, ref particlesX, false, null); this.queue.ReadFromBuffer(this.bufferU, ref particlesU, false, null); this.queue.ReadFromBuffer(this.bufferA, ref particlesA, false, null); // 出力粒子を作製 var particles = new Particle[this.particleCount]; // ここまで待機 this.queue.Finish(); // 全粒子について for(ulong i = 0; i < (ulong)this.particleCount; i++) { particles[i] = new Particle(i, this.particlesD[i], this.particlesMaterial[i], this.particlesType[i]) { X = new Vector(particlesX[i].X, particlesX[i].Y, particlesX[i].Z), U = new Vector(particlesU[i].X, particlesU[i].Y, particlesU[i].Z), A = new Vector(particlesA[i].X, particlesA[i].Y, particlesA[i].Z), }; }; // 粒子群を返す return particles; } // 空の配列を返す return new Particle[0]; }
/// <summary> /// 現在の粒子を取得する /// </summary> /// <returns>現在の粒子の複製</returns> public override Particle[] GetParticles() { // 複製配列を作成して、粒子群を複製 var clone = new Particle[this.particles.Length]; this.particles.CopyTo(clone, 0); // 複製したものを返す return clone; }