20201009のC#に関する記事は5件です。

Unityを1ヶ月間勉強後、アプリを一つ製作したのでまとめ

これは何

ゲーム開発がしたくて、Unityの勉強を一ヶ月間行った後、ゲームを一つ製作した。
iPhoneとAndroidの両方にbuildし、実機で遊べるようにした。
その中で詰まった点や学んだことについてまとめたもの。

何で勉強したか?

・ドットインストール
・Unityの教科書2019

学んだこと

詰まったところ

ここから学んだ事の各詳細

Scriptファイルに元から記述されているメソッド二つの呼び出しタイミング

  • Start()メソッド: 起動時に一度呼ばれる
  • Update(): ワンフレーム毎に呼ばれる

C#のscriptファイル製作時↓

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start() //起動時に一度呼ばれる
    {

    }

    // Update is called once per frame
    void Update() // ワンフレーム毎に呼ばれる
    {

    }
}

publicをつけて変数を宣言すると、Unity上で触れるようになる

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public GameObject Clear; //GameObject型のClear変数が宣言される
}

上記のように記述すると、下記スクショ赤枠のように、Clearという箇所が新しく表示される。
右側の枠にGameObjectをドラッグアンドドロップすると、選択したオブジェクトをscript上で簡単に使えるようになる。

public.png

Clearという名称のゲームオブジェクトを、上記の赤枠内右側にドラッグアンドドロップした場合、下記のように記述するとClearのRigidbodyを簡単に使える。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public GameObject Clear; //GameObject型のClear変数が宣言される

    // Start is called before the first frame update
    void Start()
    {
        Clear.GetComponent<Rigidbody>();
    }
}

Physics(Rigidbody, Collider)を使った物理判定(今回は衝突判定)の実装方法

Physicsとは?

Unityに標準で付属している物理エンジン。
Rigidbodyコンポーネントと、Colliderコンポーネントの二つからなる。今後ゲーム製作する上で滅茶苦茶よく使うと思う。

Rigidbodyを使うと?

ゲームオブジェクトを物理特性によって制御する事ができるようになる。
詳しくはこちら→ Rigidbody -Unity マニュアル

Colliderを使うと?

物体の当たり判定の実装が出来る。


Playerと壁がある場合、下記のようにどちらかにrigidbodyをアタッチし、両方にcolliderをアタッチする必要がある。
Player: rigidbody + colllider
壁: collider

Physicsを使っている場合、onCollisionEnter()メソッドを使うことによって、衝突した時に処理を走らせる事が出来る。

onCollisionEnterやonCollisionExit等、一通りの衝突を検知するメソッドが見れる→ 【Unity】当たり判定を一通り!OnTrigger・OnCollisionをひとまとめ | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト

時間を遅らせてメソッドを呼ぶ方法2つ

1. invoke

書き方

Invoke("呼び出すメソッド名", 秒数);

例: 宝箱のゲームオブジェクトに加えたscriptに記述しているのを想定。

    void OnCollisionEnter(Collision collision) 
    {
        if (collision.gameObject.tag == "Player") { // もし、衝突したゲームオブジェクトにPlayerというタグがついていた場合
            Invoke("open", 1.0f); // openメソッドを一秒後に読み込む
        }
    }

    // 宝箱が開いたときの音
    void open(){
        audSourse.PlayOneShot(openSE); //openSEという名称のaudioファイルを一度鳴らす処理
    }

Invokeの詳しい内容はこちら→
【Unity】Invokeの使い方!実行タイミングを自在に操ろう | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト

※豆知識: ちなみにInvokeは日本語に訳すと呼び出すという意味らしい。

2. Coroutine

書き方

    // 呼び出す時
    StartCoroutine("ClearGame");

    //コルーチン関数を定義
    private IEnumerator ClearGame(){
        yield return new WaitForSeconds(秒数);
        SceneManager.LoadScene(0); //走らせたい処理
    }

例: 宝箱のゲームオブジェクトに加えたscriptに記述しているのを想定。

void OnCollisionEnter(Collision collision) 
    {
        if (collision.gameObject.tag == "Player") {
            StartCoroutine("ClearGame"); //ClearGameというコルーチン関数を呼ぶ
        }
    }

    private IEnumerator ClearGame() //ClearGameという名称のコルーチン関数を定義
    {
        yield return new WaitForSeconds(4.0f);//四秒後に以下に書いた処理が走る
        SceneManager.LoadScene(0); //0番のゲームシーンを読み込む
    }

コルーチンについての詳しい内容はこちら→ Unityのコルーチンの使い方をまとめてみた - WonderPlanet Developers’ Blog

詰まった所の、解決策や学んだことについてはこれから記載

ここから詰まった所の各詳細

フリックした際に、フリック方向を上下左右の四方向で取得する方法

3Dのゲームを製作したのですが、右斜め上や、左斜め下などの斜め方向のスワイプを上下左右のどれかに分類して取得するのにかなり詰まりました。
悩んみに悩み、ぐぐり倒して下記の記事に辿り着きかなり参考にさせていただきました。

参考にさせていただいた記事↓
【Unity】フリックとスワイプ入力の同時取得方法【C#】

onCollisionEnter内でinvokeを呼び出して、particleを発火させる方法

playerを操作して、宝箱に当たったらゲームクリアという内容で、宝箱に当たったら二秒後に紙吹雪のパーティクルが発生するように処理を書いていました。

何が原因だったのか?

particleにアタッチされているscriptに、下記のコードが記述されている事が原因だった。
ゲームスタートと同時に、三秒後に自分自身を破棄するメソッドが走っていた。

[SerializeField] private float timer = 3f;

    private void Start()
    {
        Destroy(gameObject, timer);
    }

このスクリプトのtimerの値を1000に変更することによって解決。

ハマった理由

たまに宝箱に当たると、ちゃんと二秒後にパーティクルが発火する事があり、それにより原因の追求に紆余曲折時間がかかった。

悩んだ流れ

たまにパーティクル発火する事に気づき、発火する条件探しが始まる

Clear(ゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト)

パーティクルは上記のようなヒエラルキーの構成にしており、clearParticleをforeachで取得する事によりパーティクルを発火させていた。foreachの既述の仕方がおかしいのかと思い探る。
Start()メソッドの中に既述するちゃんとパーティクルが発火したため、取得するためのコードに問題がない事を確認する。

壁に一回当たった後だとパーティクルが発火し、二回当たるとパーティクルが発火しないという事に気づく。壁に当たった際にisKinematicをtrueにし、その後falseにする処理を書いていたのでそれが原因かと思い、playerのisKinematicのステータスをDebug.Logで確認しながらゲームをテストしたが問題なさそう。
ここでまず数時間溶けていた。
その後、壁に一回当たった後でもパーティクルが発火しない事がある事に気づく。

壁に当たった回数ではなく、時間ではないか?と思い当たる。
5秒位経ってから宝箱に当たるとパーティクルが発火しない事に気づく。
ここら辺で色々いじっていたら新しくエラーログが出る。
ログの内容は、DestroyされているGameObjectを呼び出そうとしているよという内容だった。そこから、clearParticleオブジェクトor親オブジェクトのClearがDestroyされていると確認する。
しかしどこでパーティクルがDestroyされているのか分からず時間が溶けていく。
色々探った結果、clearParticleにscriptがアタッチされているのに気づき、中を見るとビンゴだった。

複数ある子オブジェクトを取得する方法

Clear(ゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト )

上記のようなヒエラルキーの構成で、clearParticleを取得したい場合

foreach文を使う

// Clear配下の、clearParticleにアタッチされたパーティクルを発火させたい場合のコード↓
Transform parentTransform = Clear.transform;
foreach (Transform child in parentTransform)
{
  child.GetComponent<ParticleSystem>().Play(); 
}

AddForce()で横方向にPlayerを動かしたいのに、上下にブレる

これから記述

Playerが氷の上を滑っているような動きの実装

これから記述

iPhoneにbuildしようとした所、2つTheredが表示されできなかった

これから記述

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NET Framework 4.5.2でAzure Blob Storageを利用する

はじめに

C#からAzure Blob Storageを利用する記事を探すと、v12のAPIを提供するNuGetパッケージ(Azure Storage Blobsなど)を利用するサンプルが多く表示されるが、v12は.NETStandar 2.0以上をターゲットしているため、.NET Framework 4.5.2からは利用することができません。
BtoB製品の場合、導入先のお客様が現在運用している可能性のあるWindows Server全てに対応する必要があるため、現在利用可能な.NET Frameworkの最低バージョン4.5.2(それ以前はMSのサポート対象外)をターゲットと考えなければなりません。
「Azure Web Appsで運用するのだったら4.7以降をターゲットとすればいいのでは?」と考えるかもしれませんが、オンプレとWeb Appsで同じ製品のターゲットフレームワークを変えるというのも構成管理やテストのコストがかかる場合があるのでできれば避けたいところです。
(※.NETStandardの実装とサポートする.NET Frameworkのバージョンに関しては、MSページ(https://docs.microsoft.com/ja-jp/dotnet/standard/net-standard) を参考にしてください。)
本記事ではそのような制約下の開発でAuzre Blob Storageを利用するには、どのNuGetパッケージを用いて、どのようなコーディングを行えばいいのかを見ていこうと思います。

利用するNuGetパッケージ

Azure Blob Storageを操作するためのパッケージはいくつか公開されていますが、.NET Framework 4.5.2で利用可能で、かつ2020年10月時点でサポート切れが見込まれていないものは以下のパッケージになります。
・Microsoft.Azure.Storage.Blob

Blob Storageにファイルをアップロードする

Azure Storage Blobを利用するには、Blobやファイルなどの各種ストレージオブジェクトへのアクセスを提供するストレージアカウントというサービスの単位があり、ストレージアカウントに対して認証を受けたうえで、ストーレージアカウントの管理する各種ストレージにアクセスをするという流れになります。
Blobに対してのアクセスは以下のようなコードになります

Upload.ashx.cs
using System.Configuration;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;

// Web Appsの場合、接続文字列はApplication Settingsから取るのがおそらく一般的。
string connectionstring = ConfigurationManager.AppSettings["connectionstring"];
// blobが保存されるコンテナ名
string containername = ConfigurationManager.AppSettings["containername"];

// アップロードされたファイルを取得
HttpPostedFile file = HttpContext.Current.Request.Files["userfile"];
if (!string.IsNullOrEmpty(file.FileName))
{
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionstring);
    CloudBlobClient client = storageAccount.CreateCloudBlobClient();

    CloudBlobContainer container = client.GetContainerReference(containername);
    // もしコンテナが存在しなかった場合は作成
    container.CreateIfNotExists();

    // 一意となるファイル名でblobに対しての参照を取得する
    CloudBlockBlob blob = container.GetBlockBlobReference($"{file.FileName}");

    using (var fileStream = file.InputStream)
    {
        // ファイルをアップロードする
        blob.UploadFromStream(fileStream);
    }
}

Blobからファイルをダウンロードする

ダウンロードの場合は、Responseに対してのファイルの書き出し方によっていくつか利用するメソッドが異なりますが、本記事ではbyteの配列として書き出すやり方を採用します。

Download.ashx.cs
using System.Configuration;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;

// Web Appsの場合、接続文字列はApplication Settingsから取るのがおそらく一般的。
string connectionstring = ConfigurationManager.AppSettings["connectionstring"];
// blobが保存されるコンテナ名
string containername = ConfigurationManager.AppSettings["containername"];
// ダウンロードするファイル名
string filename = HttpContext.Current.Request["filename"];
if (filename != null)
{
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionstring);
    CloudBlobClient client = storageAccount.CreateCloudBlobClient();
    CloudBlobContainer container = client.GetContainerReference(containername );
    CloudBlockBlob blob = container.GetBlockBlobReference(fileName);

    // 必要なbyte配列の長さを取得するためには、取得したblobの参照に対してFetchAttributesを行う必要がある
    blob.FetchAttributes();
    long fileByteLength = blob.Properties.Length;
    byte[] filecontent = new byte[fileByteLength];
    blob.DownloadToByteArray(filecontent, 0);
    if (filecontent.Length != 0)
    {
        // ここでもちいてるcontextは利用しているクラス(HandlerとかPageとか)に応じて適当なものを利用してください
        context.Response.BinaryWrite(filecontent);
    }
    context.Response.Flush();
}

結論

上記の通り、比較的簡単なコードでBlob Storageを利用することができた。
ネット上の資料だと、FetchAttributesをせずにProperties.Lengthを取得している記事が見つかるが、私の環境ではFetchAttributesを行わないとLength=-1が返ってきてしまいbyte配列の宣言時にエラーが返ってきてしまった。
これが単純なネット記事の間違いなのか、なにかしらの環境に依存する際なのかは不明だが、とりあえずはこのコーディングで動いています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#でHeaderとBodyを指定したAPIを実行する

はじめに

この記事ではC#でAPIを実行するときにHeaderとBodyの両方を指定する方法を紹介します。

結論

サンプルコードは以下の通りです。

C#
string url = "URL";
string body = "内容";
string headerKey1 = "ContentType";
string headerValue1 = "application/json";
string headerKey2 = "Authorization";
string headerValue2 = "Bearer " + accessToken;

HttpClient client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, url);

# Headerの指定
request.Headers.Add(headerKey1, headerValue1);
request.Headers.Add(headerKey2, headerValue2);

# Bodyの指定
HttpContent httpContent = new StringContent(body, Encoding.UTF8);
request.Content = httpContent;

# APIの実行
var response = await client.SendAsync(request);

HeaderはHttpRequestMessageHttpRequestMessage.Headers.Addすることで指定することができます。本方法で複数のHeaderを指定することもできます。

BodyはHttpRequestMessageにHttpContent型で格納したStringContentHttpRequestMessageのContentに追加することで指定することができます。

躓きポイント

C#でAPIを実行するときの躓きポイントを紹介いたします。

StringContentを利用する際の注意点

APIを実行する際に、Bodyしか指定していないサンプルコードにはStringContent + PostAsyncを使った方法がよく利用され、以下のように記載されます。

C#
string url = "URL";                
string body = "内容";
                    
var client = new HttpClient()
var content = new StringContent(body, Encoding.UTF8);
var response = await client.PostAsync(url, content);

また、本記事のサンプルコードのようにHttpRequestMessage + SendAsyncを使った方法もあります。

C#
string url = "URL";
string body = "内容";

HttpClient client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, url);

# Bodyの指定
HttpContent httpContent = new StringContent(body, Encoding.UTF8);
request.Content = httpContent;

# APIの実行
var response = await client.SendAsync(request);

実は、StringContentの第三引数にはコンストラクタ (constructor)を指定することができるのですが、ここが詰まりポイントだと思います。
StringContentについてはこちらを参照ください

つまり、StringContent作成の際にこういう書き方ができてしまいます。
var content = new StringContent(requestBody, Encoding.UTF8, "application/json");

ここだけ見れば「StringContentの第三引数にはHeaderが指定できる」
などの勘違いが起こるのは必然だと思います。

実は、.NETのStringContentは自動でcharsetを付けるみたいで、Headerが指定できているワケではありません。ざっくり言えば、StringContentを使用したリクエストを作ると、デフォルトでContent-Type:application/json;が送られます。

以下の記事に詳しく記載されておりますので、興味がある方は是非ご確認ください。
HttpClientのStringContentはcharsetを付ける

その他

本サンプルプログラムはHttpClientの実装に問題がございます。HttpClientの仕様、実装例については以下のページが非常に参考になりますので実務でご利用の場合には是非ともご確認ください。
.NET(Framework)のHttpClientの取り扱いには要注意という話

まとめ

・HeaderはHttpRequestMessageHttpRequestMessage.Headers.Addすることで指定できます。
・BodyはHttpRequestMessageにHttpContent型で格納したStringContentHttpRequestMessageのContentに追加することで指定することができます。
・StringContentを利用する際は、第三引数が何なのかきちんと理解した上で実行してください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「Javaによるはじめての有限要素法」を読んでみた

概要

Javaによるはじめての有限要素法(長岐滋 著)
https://www.coronasha.co.jp/np/isbn/9784339046069/
03269907_1.png

有限要素法の入門に当たり、上記の書籍を参考に勉強してみました。
その内容を当記事でまとめてみます。

特徴

この書籍では1次元、2次元有限要素法を取り扱っており、非常に分かり易く
原理を説明しているという印象を受けました。
また、実際にプログラミングするところまで説明されており、1次元に関しては
ソースコードまで丁寧に解説されています。

内容

第2章

有限要素法に必要な1次元での仮想仕事の原理と材料力学の基礎知識を解説しています。
図は少ないですが1次元なので数式がすっきりしていて分かりやすいです。

第3章 ~ 第4章

Javaに関する基本的な知識について書かれており、Javaを初めて扱う人向けに書かれている印象でした。逆にJavaが分かっている人は読み飛ばしても構わない項目だと思います。(私はほぼ読み飛ばしました)

第5章

行列とベクトルの基礎知識に関して書かれています。基本的な演算のみなので、知識がある方は読み飛ばしても構わない項目です。

第6章

いよいよ、有限要素法の原理の解説に入ります。ここでは、1次元でプログラムを組み、課題を解くところまで取り扱っています。
javaによる初めての有限要素法_例題.png
6章で扱っている単純な引張り変形モデル

形状関数から要素剛性方程式の解説まで多くの図で丁寧に記載されており、
具体例により解説が進んでいくのでイメージが付きやすいです。
また、境界条件をどうプログラミングで扱うか等細かく記載されており、
この内容は重宝しました。

第7章~第8章

ここで2次元の有限要素法の原理について解説になります。
ここでは、原理のみの解説でソースコードの解説は無しでした。
また、形状は三角形のみですが、付録のCDにプログラムがあり、
そちらを使ってCAE解析を体験することが可能です。
実際にメッシュを切るところまで用意されており、その辺りは非常に丁寧な印象を受けました。ただ、2次元の三角形要素に関しては有限要素法のつくり方!(石川博幸、青木伸輔、日比学 著)でより詳しくソースコード付きで書かれているので、そちらの参照をお勧めします。

実践

第6章で作られていたJavaのプログラムを参考にC#(WPF)で実際に作ってみました。
C#で作ったのは可視化がJavaに比べて容易だったためです。
入力にcsvファイルを読み込む形式にしたので、色々な1次元の解析に使えると思います。

ソースコードは下記に置きました。
https://github.com/Altaka4128/Simple1DFEM
使用には入力csvファイルを読み込んで、解析開始ボタンを押すと、
exeの場所にoutput.csvファイルが出力され、その中に結果が書き込まれています。
SampleDataにcsvファイルのサンプルをいくつか置いておきます。(実際のテキストの課題)

また、有限要素法の計算部のソースコードをこちらに記載しておきます。

Beam1DFEM.cs
using MathNet.Numerics.LinearAlgebra.Double;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Simple1DFEM
{

    public struct InputData
    {

        public List<double> Node;
        public List<BeamElement> Elem;
    }

    public class Beam1DFEM
    {
        private int NodeNum = 0;   // 節点数
        public List<BeamElement> BeamElems   // 要素の集合
        {
            get;
            private set;
        }
        public DenseVector DispVector   // 変位の境界条件
        {
            get;
            private set;
        }

        public DenseVector ForceVector   // 荷重の境界条件
        {
            get;
            private set;
        }

        public List<bool> Rest   // 拘束の境界条件
        {
            get;
            private set;
        }
        bool AnalysisFlg = false;

        public Beam1DFEM(int nodenum, List<BeamElement> beamelems)
        {
            NodeNum = nodenum;
            BeamElems = beamelems;
        }

        public Beam1DFEM(_1DFEMData data)
        {
            // 要素の形式を変換して格納する
            List<BeamElement> elems = new List<BeamElement>();
            {
                for (int i = 0; i < data.elems.Count; i++)
                {
                    Node[] nodes = new Node[2];
                    nodes[0].No = data.elems[i].NodeNo1;
                    nodes[0].Point = data.nodes[nodes[0].No - 1].Point;
                    nodes[1].No = data.elems[i].NodeNo2;
                    nodes[1].Point = data.nodes[nodes[1].No - 1].Point;

                    int materialNo = data.elems[i].MaterialNo - 1;
                    double area = data.materials[materialNo].Area;
                    double young = data.materials[materialNo].Young;

                    elems.Add(new BeamElement(nodes, area, young));
                }
            }
            NodeNum = data.nodes.Count;
            BeamElems = elems;

            // 拘束条件の形式を変換して格納する
            // 変位
            List<double> disp = new List<double>();
            List<double> force = new List<double>();
            List<bool> constraint = new List<bool>();
            for (int i = 0; i < (data.nodes.Count * 1); i++)
            {
                disp.Add(data.nodes[i].Displacement);
                force.Add(data.nodes[i].Force);
                constraint.Add(data.nodes[i].Constraint);
            }
            DenseVector dispVector = DenseVector.OfArray(disp.ToArray());
            DenseVector forceVector = DenseVector.OfArray(force.ToArray());
            setBoundaryCondition(dispVector, forceVector, constraint);
        }

        // Kマトリックスを作成する
        private DenseMatrix makeKMatrix()
        {
            // 例外処理
            if (Rest == null)
            {
                return null;
            }
            if (NodeNum <= 0 || BeamElems == null || Rest.Count != NodeNum)
            {
                return null;
            }

            DenseMatrix kMatrix = DenseMatrix.Create(NodeNum, NodeNum, 0.0);

            // 各要素のKeマトリックスを計算し、Kマトリックスに統合する
            for (int i = 0; i < BeamElems.Count; i++)
            {
                Console.WriteLine("要素" + (i + 1).ToString());
                DenseMatrix keMatrix = BeamElems[i].makeKeMatrix();

                for (int r = 0; r < 2; r++)
                {
                    int rt = BeamElems[i].Nodes[r].No - 1;
                    for (int c = 0; c < 2; c++)
                    {
                        int ct = BeamElems[i].Nodes[c].No - 1;
                        kMatrix[rt, ct] += keMatrix[r, c];
                    }
                }
            }

            Console.WriteLine("Kマトリックス");
            Console.WriteLine(kMatrix);

            // 境界条件を考慮して修正する
            ForceVector = ForceVector - kMatrix * DispVector;
            for (int i = 0; i < Rest.Count; i++)
            {
                if (Rest[i] == true)
                {
                    for (int j = 0; j < kMatrix.ColumnCount; j++)
                    {
                        kMatrix[i, j] = 0.0;
                    }
                    for (int k = 0; k < kMatrix.RowCount; k++)
                    {
                        kMatrix[k, i] = 0.0;
                    }
                    kMatrix[i, i] = 1.0;

                    ForceVector[i] = DispVector[i];
                }
            }

            Console.WriteLine("Kマトリックス(境界条件考慮)");
            Console.WriteLine(kMatrix);
            Console.WriteLine("荷重ベクトル(境界条件考慮)");
            Console.WriteLine(ForceVector);

            return kMatrix;
        }

        // 境界条件を設定する
        public void setBoundaryCondition(DenseVector dispvector, DenseVector forcevector, List<bool> rest)
        {
            DispVector = dispvector;
            ForceVector = forcevector;
            Rest = rest;
        }

        public void Analysis()
        {
            DenseMatrix kMatrix = makeKMatrix();

            if(kMatrix == null)
            {
                return;
            }

            // 変位を計算する
            DispVector = (DenseVector)(kMatrix.Inverse().Multiply(ForceVector));
            Console.WriteLine("変位ベクトル");
            Console.WriteLine(DispVector);

            // 各要素の応力を計算する
            DenseVector dispElemVector = DenseVector.Create(2, 0.0);
            for (int i = 0; i < BeamElems.Count; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    dispElemVector[j] = DispVector[BeamElems[i].Nodes[j].No - 1];
                }

                Console.WriteLine("要素" + (i + 1).ToString());
                BeamElems[i].makeStrainVector(dispElemVector);
                BeamElems[i].makeStressVector();
            }

            AnalysisFlg = true;
        }

        // 結果を出力する
        public void outputReport()
        {
            if (AnalysisFlg != true)
            {
                return;
            }

            StreamWriter sw = new StreamWriter("output.csv");

            // 節点変位を書き込む
            List<Node> nodes = new List<Node>();
            for(int i = 0; i < BeamElems.Count; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    nodes.Add(BeamElems[i].Nodes[j]);
                }
            }
            nodes = nodes.Distinct().ToList();
            nodes.Sort((a,b) => a.No - b.No);

            sw.WriteLine("Node, Coordinate, Ux");
            for (int i = 0; i < NodeNum; i++)
            {
                sw.WriteLine((i + 1).ToString() + ", " + nodes[i].Point + ", " + DispVector[i]);
            }

            // 要素情報を書き込む
            sw.WriteLine("Element, X-Stress");
            for(int i = 0; i < BeamElems.Count; i++)
            {
                sw.WriteLine((i + 1).ToString() + "," + BeamElems[i].StressVector[0]);
            }
            sw.Close();
        }
    }
}

BeamElement.cs
using MathNet.Numerics.LinearAlgebra.Double;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Simple1DFEM
{
    public class BeamElement
    {
        public Node[] Nodes
        {
            get;
            private set;
        }
        private double Area;
        private double Young;
        private DenseMatrix KeMatrix;
        public DenseVector StrainVector
        {
            get;
            private set;
        }
        public DenseVector StressVector
        {
            get;
            private set;
        }

        public BeamElement()
        {
        }

        public BeamElement(
            Node[] nodes,
            double area,
            double young)
        {

            if (nodes.Length != 2)
            {
                return;
            }

            Nodes = nodes;
            Area = area;
            Young = young;

        }

        // Bマトリックスを計算する
        private DenseMatrix makeBMatirx()
        {
            // 例外処理
            if (Area <= 0)
            {
                return null;
            }

            double length = Nodes[1].Point - Nodes[0].Point;
            double[,] bmatrixArray = new double[1, 2];
            bmatrixArray[0, 0] = -1.0 / length;
            bmatrixArray[0, 1] = 1.0 / length;

            return DenseMatrix.OfArray(bmatrixArray);
        }

        // Keマトリックスを計算する
        public DenseMatrix makeKeMatrix()
        {
            // Bマトリックスを計算する
            DenseMatrix BMatrix = makeBMatirx();
            Console.WriteLine("Bマトリックス");
            Console.WriteLine(BMatrix);

            // 例外処理
            if (BMatrix == null || Young <= 0)
            {
                return null;
            }

            double Volume = Area * (Nodes[1].Point - Nodes[0].Point);
            var keMatrix = Young * Volume * BMatrix.Transpose() * BMatrix;
            DenseMatrix KeMatrix = DenseMatrix.OfColumnArrays(keMatrix.ToColumnArrays());
            Console.WriteLine("Keマトリックス");
            Console.WriteLine(KeMatrix);

            return KeMatrix;
        }

        // ひずみベクトルを計算する
        public void makeStrainVector(DenseVector dispvector)
        {
            DenseMatrix bMatrix = makeBMatirx();
            StrainVector = (DenseVector)bMatrix.Multiply(dispvector);
            Console.WriteLine("ひずみベクトル");
            Console.WriteLine(StrainVector);
        }

        // 応力ベクトルを計算する
        public void makeStressVector()
        {
            if (StrainVector == null || Young <= 0)
            {
                return;
            }

            StressVector = Young * StrainVector;
            Console.WriteLine("応力ベクトル");
            Console.WriteLine(StressVector);
        }

        public BeamElement ShallowCopy()
        {
            return (BeamElement)MemberwiseClone();
        }
    }
}

計算の流れ
要素のデータを入力(座標、ヤング率など)

Dマトリックスを計算する

Bマトリックスを計算する

D、Bマトリックスから各要素のKマトリックス(ソースコード内のKeマトリックス)を計算する

各要素のKマトリックスを計算し、全体のKマトリックスに足し合わせる(重ね合わせの原理)

全体剛性方程式ができるが、Kの逆行列がこのままだと計算できないので、境界条件を加味したKマトリックス及び荷重ベクトルに修正する

剛性方程式から変位を求める

K * \vec{u} = \vec{f} \\
\vec{u} = K^{-1} * \vec{f}


変位から各要素のひずみ、応力が求まる

\vec{\epsilon} = B * \vec{u} \\
\vec{\sigma} = D * \vec{\epsilon}

参考

[1]Javaによるはじめての有限要素法(長岐滋 著)
[2]<解析塾秘伝>有限要素法のつくり方!(石川博幸、青木伸輔、日比学 著)
[3]有限要素法入門(三好俊郎 著)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dotnet core(C#言語)で、コンソールの多重起動制御(Mutexを使う方法)をLinuxでも行う

Mutexクラスを使って多重起動制御を行う

Windowsだと、普通にnew Mutex(false, "hogehoge");とすれば、多重制御処理を書けてしまいますが、Linuxだと発動したり発動しなかったり。

多重制御が効く

/usr/local/bin/Hoge/Hoge &
/usr/local/bin/Hoge/Hoge &

多重制御が効かない

0 0 * * * root /usr/local/bin/Hoge/Hoge
5 0 * * * root /usr/local/bin/Hoge/Hoge

Mutexクラスで多重起動制御できるようにする

Global\ 文字列を付けるだけみたいです。

var mutex = new System.Threading.Mutex(false, @"Global\\Hoge");
try {
    handle = mutex.WaitOne(0, false);
}
catch( System.Threading.AbandonedMutexException) {
    handle = true
}

if( !handle ) {
    // 多重起動
    return;
}

cronでもちゃんと制御してくれました!!

  • dotnet core 3.1 / centos7, centos8 で確認
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む