📏

ソリッド表面シェルを取得する(C#)

2024/06/19に公開

ソリッド表面が取得できると何がうれしいのか

  • これは、ソリッドの接触や面圧をかける解析をやったことある人なら
    すぐわかると思いますが、Nullシェルを作る時に使います。
  • 1つ2つなら大したことないですが、数が多いとめんどくさいです。

今回やること

  • 基本的にフリーエッジの時と考え方は、一緒です。
    https://zenn.dev/cae_taka/articles/9122e4e070d76d
  • 1:メッシュモデルファイル(今回は、keywordの標準表記)を読み込み、
    節点、ソリッドの内容を記録する。
  • 2:指定したPIDのすべてのソリッドから構成するシェルを作成する。
  • 3:シェルの内一つも重複していないシェル(表面シェル)を取り出す

プログラムの説明

  • まず節点、エッジ、はフリーエッジの時と同じなので割愛します。
  • シェルとデータを保存するクラスは以下のように変更しソリッドクラスを追加します。
    class SHELL
    {
        public int Pid { get; set; }
        public List<int> Nid { get; set; }
        public SHELL(int _Pid, List<int> _Nid)
        {
            this.Pid = _Pid;
            this.Nid = _Nid;
        }

        //ここから
        public override bool Equals(object other)
        {
            if (other == null || this.GetType() != other.GetType())
            {
                return false;
            }
            var _other = other as SHELL;
            return !this.Nid.Select(i => _other.Nid.Contains(i)).Contains(false);
        }
        public override int GetHashCode()
        {
            var d = this.Nid[0].GetHashCode();
            for (var i=1;i<this.Nid.Count;i++)
            {
                d ^= this.Nid[i];
            }
            return d;
        }
        public static bool operator ==(SHELL lhs, SHELL rhs)
        {
            if (lhs is null)
            {
                if (rhs is null)
                {
                    return true;
                }

                // Only the left side is null.
                return false;
            }
            // Equals handles case of null on right side.
            return lhs.Equals(rhs);
        }

        public static bool operator !=(SHELL lhs, SHELL rhs) => !(lhs == rhs);
        //ここまで追加
    }
    class SOLID//ソリッドクラス
    {
        public int Pid { get; set; }
        public List<int> Nid { get; set; }
        public SOLID(int _Pid, List<int> _Nid)
        {
            this.Pid = _Pid;
            this.Nid = _Nid;
        }
    }
    class CAE_DATA
    {
        public Dictionary<int, NODE>  Node { get; set; }
        public Dictionary<int, SHELL> Shell { get; set; }
        public Dictionary<int, SOLID>  Solid { get; set; }//ここ追加
        public CAE_DATA()
        {
            this.Node  = new Dictionary<int, NODE>();
            this.Shell = new Dictionary<int, SHELL>();
            this.Solid  = new Dictionary<int, SOLID>();//ここ追加
        }

    }
  • メッシュファイルを読み込んで保存する箇所は、from_Key_Fileを変更し、
    SolidList_makeを追加
   class FILE_CAE
    {
    public CAE_DATA from_Key_File(string FileName)
        {
            var DATA = new CAE_DATA();
            //ファイルの内容で特定のキーワード(今回は"$")が書かれていない行を一行ずつリストにする
            var Base = Read_File_Make_All_Line_without_keyword(FileName, "$");

            var NodeLine = Sepalete_Line(Base, "*NODE");//節点の行だけにする
            var ShellLine = Sepalete_Line(Base, "*ELEMENT_SHELL");//シェルだけの行にする
            var SolidLine = Sepalete_Line(Base, "*ELEMENT_SOLID");//ソリッドだけの行にする <=ここ追加

            DATA.Node = NodeList_make(NodeLine);//節点を保存する
            DATA.Shell = ShellList_make(ShellLine);//シェルを保存する
            DATA.Solid = SolidList_make(SolidLine);//ソリッドを保存する <=ここ追加
            return DATA;
        }
    }
    private Dictionary<int, SOLID> SolidList_make(List<string> ORG)
    {
        var temp = new Dictionary<int, SOLID>();
        foreach (var i in Sepalete_Cloum(ORG, 8))
        {
            var ID = int.Parse(i[0]);
            var PID = int.Parse(i[1]);
            i.RemoveRange(0, 2);

            var NID = i.Select(j => int.Parse(j)).ToList();
            temp.Add(ID, new SOLID(PID, NID));
        }
        return temp;
    }
  • 保存したデータから表面シェルを取得する。(フリーエッジとほとんど同じ)
  class FreeShell
    {
        public List<SHELL> FreeShell_Make(CAE_DATA DATA, int TargetPID)
        {
            var ShellList = ShellList_make(DATA, TargetPID);//TargetPIDのソリッドからシェルを作成する
            //処理速度を上げるため、重複したシェルを検索する範囲を分割する
            var sepalete_List = Sepalete_Area(ShellList, 1000,DATA);

            return Get_Free_Shell(sepalete_List);//表面シェルを取得する
        }
        private List<SHELL> ShellList_make(CAE_DATA DATA, int TargetPID)
        {
            return DATA.Solid.Where(i => i.Value.Pid == TargetPID).SelectMany(i => Shell_Nid_Make(DATA, i.Value)).ToList();
        }
        private List<SHELL> Shell_Nid_Make(CAE_DATA DATA, SOLID E)
        {
            var temp = new List<SHELL>();
            //keyword形式では、テトラソリッドの節点IDの4番目は、5番目と同じ値になっているため
            //下の方法でソリッドがテトラかヘキサのどちらかが判別できる
            var N = new List<List<int>>();
            if (E.Nid[3] == E.Nid[4])
            {
                //234  235 345 245
                N.AddRange(new List<List<int>> { new List<int> { E.Nid[0], E.Nid[1], E.Nid[2] } ,
                                                 new List<int> { E.Nid[0], E.Nid[1], E.Nid[3] } ,
                                                 new List<int> { E.Nid[1], E.Nid[2], E.Nid[3] } ,
                                                 new List<int> { E.Nid[0], E.Nid[2], E.Nid[3] } });
            }
            else
            {
                //2,3,4,5  2,3,6,7   4,5,8,9  2,5,6,9   3,4,7,8  6,7,8,9
                N.AddRange( new List<List<int>> { new List<int> { E.Nid[0], E.Nid[1], E.Nid[2], E.Nid[3] } ,
                                              new List<int> { E.Nid[0], E.Nid[1], E.Nid[4], E.Nid[5] } ,
                                              new List<int> { E.Nid[2], E.Nid[3], E.Nid[6], E.Nid[7] } ,
                                              new List<int> { E.Nid[0], E.Nid[3], E.Nid[4], E.Nid[7] } ,
                                              new List<int> { E.Nid[1], E.Nid[2], E.Nid[5], E.Nid[6] } ,
                                              new List<int> { E.Nid[4], E.Nid[5], E.Nid[6], E.Nid[7] } });
                
            }
            N.ForEach(i => temp.Add(new SHELL(E.Pid, i)));
            return temp;
        }
        private List<SHELL> Get_Free_Shell(List<List<SHELL>> temp)
        {
            var Free_Shell = new List<SHELL>();
            foreach (var i in temp)
            {
                var distinct = i.Distinct();
                foreach (var q in distinct)
                {
                    var temp_List = i.Where(j => j == q).ToList();
                    if (temp_List.Count() == 1)
                    {
                        Free_Shell.Add(temp_List[0]);
                    }
                }
            }

            return Free_Shell;
        }
        private List<List<SHELL>> Sepalete_Area(List<SHELL> temp, int num, CAE_DATA DATA)
        {
            var Max = temp.Select(i => DATA.Node[i.Nid[0]]).Max(i => i.Crd.Y) + 1;
            var Min = temp.Select(i => DATA.Node[i.Nid[0]]).Min(i => i.Crd.Y) - 1;
            var dis = (Max - Min) / num;
            var temp2 = new List<List<SHELL>>();

            for (var i = 0; i <= num; i++)
            {
                temp2.Add(temp.Where(j =>DATA.Node[j.Nid[0]].Crd.Y >= Min + i * dis &&
                DATA.Node[j.Nid[0]].Crd.Y < Min + (i + 1) * dis).ToList());
            }
            return temp2;
        }

使い方

static void Main(string[] args)
        {
            var Infile = @"C:/Users/test.key";
            var DATA=new FILE_CAE().from_Key_File(Infile);
            var TargetPID=1;
                //List<Shell>で表面シェルが取得できます
            var FreeShell=new FreeShell().FreeShell_Make(DATA,TargetPID);
        }

今後

  • 表面シェルを作るところは、フリーエッジの時と同じなので親クラス作って共通化した方がよいかもしれません。
  • 今後もモデル作成に使える小技ツールを作っていきたいです。

Discussion