/// <summary> /// コピー /// </summary> /// <param name="src"></param> public override void CP(FemElement src) { if (src == this) { return; } // 基本クラスのコピー base.CP(src); }
/// <summary> /// 要素の節点数から該当するFemElementインスタンスを作成する /// </summary> /// <param name="eNodeCnt"></param> /// <returns></returns> public static FemElement CreateFemElementByElementNodeCnt(int eNodeCnt) { FemElement femElement = null; Constants.FemElementShapeDV elemShapeDv; int order; int vertexCnt; GetElementShapeDvAndOrderByElemNodeCnt(eNodeCnt, out elemShapeDv, out order, out vertexCnt); if (vertexCnt == Constants.TriVertexCnt) { femElement = new FemTriElement(); } else { femElement = new FemElement(); } return femElement; }
/// <summary> /// フィールド値等高線図描画 /// </summary> /// <param name="g"></param> /// <param name="panel"></param> public void DrawFieldEx(Graphics g, Panel panel, Rectangle clientRectangle, FemElement.FieldDV fieldDv, FemElement.ValueDV valueDv) { if (!isInputDataReady()) { return; } if (!isOutputDataReady()) { return; } Size delta; Size ofs; Size regionSize; //getFitDrawRegion(panel, out delta, out ofs, out regionSize); getFitDrawRegion(clientRectangle.Width, clientRectangle.Height, out delta, out ofs, out regionSize); ofs.Width += clientRectangle.Left; ofs.Height += clientRectangle.Top; double min = 0.0; double max = 1.0; if (fieldDv == FemElement.FieldDV.Field) { min = MinFValue; max = MaxFValue; } else if (fieldDv == FemElement.FieldDV.RotX || fieldDv == FemElement.FieldDV.RotY) { min = MinRotFValue; max = MaxRotFValue; } else { return; } // カラーマップに最小、最大を設定 if (valueDv == FemElement.ValueDV.Real || valueDv == FemElement.ValueDV.Imaginary) { FValueColorMap.Min = -max; FValueColorMap.Max = max; } else { // 既定値は絶対値で処理する //FValueColorMap.Min = min; FValueColorMap.Min = 0.0; FValueColorMap.Max = max; } foreach (FemElement element in Elements) { // 等高線描画 element.DrawField(g, ofs, delta, regionSize, fieldDv, valueDv, FValueColorMap); } }
/// <summary> /// フィールド値等高線図描画 /// </summary> /// <param name="g"></param> /// <param name="panel"></param> public void DrawEigenField(Graphics g, Panel panel, Rectangle clientRectangle, FemElement.ValueDV valueDv) { if (!isInputDataReady()) { return; } if (!isOutputDataReady()) { return; } Size delta; Size ofs; Size regionSize; //getFitDrawRegion(panel, out delta, out ofs, out regionSize); getFitDrawRegion(clientRectangle.Width, clientRectangle.Height, out delta, out ofs, out regionSize); ofs.Width += clientRectangle.Left; ofs.Height += clientRectangle.Top; double min = MinEigenFValue; double max = MaxEigenFValue; // カラーマップに最小、最大を設定 if (valueDv == FemElement.ValueDV.Real || valueDv == FemElement.ValueDV.Imaginary) { EigenFValueColorMap.Min = -max; EigenFValueColorMap.Max = max; } else { // 既定値は絶対値で処理する EigenFValueColorMap.Min = 0.0; EigenFValueColorMap.Max = max; } foreach (FemElement element in Elements) { // 等高線描画(固有モード分布) element.DrawEigenField(g, ofs, delta, regionSize, valueDv, EigenFValueColorMap); } }
/// <summary> /// 凡例カラーマップ目盛の描画 /// </summary> /// <param name="g"></param> private void drawFValueLegendColorScale(Graphics g, FemElement.FieldDV fieldDv, FemElement.ValueDV valueDv) { const int cnt = LegendColorCnt; const int ofsX = 22; const int ofsY = 0; const int height = 20; // 1目盛の高さ //double divValue = 1.0 / (double)cnt; double min = 0.0; double max = 1.0; if (fieldDv == FemElement.FieldDV.Field) { min = MinFValue; max = MaxFValue; } else if (fieldDv == FemElement.FieldDV.RotX || fieldDv == FemElement.FieldDV.RotY) { min = MinRotFValue; max = MaxRotFValue; } else { return; } if (valueDv == FemElement.ValueDV.Abs) { } else { min = -max; } double divValue = (max - min) / (double)cnt; using (Font font = new Font("MS UI Gothic", 9)) using (Brush brush = new SolidBrush(FValueLegendColorPanel.ForeColor)) { for (int i = 0; i < cnt + 1; i++) { int y = i * height; double value = min + (cnt - i) * divValue; string text = string.Format("{0:E3}", value); g.DrawString(text, font, brush, new Point(ofsX, y + ofsY)); } } }
/// <summary> /// 点が要素内に含まれる? /// </summary> /// <param name="element">要素</param> /// <param name="test_pp">テストする点</param> /// <param name="Nodes">節点リスト(要素は座標を持たないので節点リストが必要)</param> /// <returns></returns> public static bool IsPointInElement(FemElement element, double[] test_pp, IList<FemNode> nodes) { bool hit = false; int eNodeCnt = element.NodeNumbers.Length; Constants.FemElementShapeDV elemShapeDv; int order; int vertexCnt; GetElementShapeDvAndOrderByElemNodeCnt(eNodeCnt, out elemShapeDv, out order, out vertexCnt); if (vertexCnt == Constants.TriVertexCnt) { // 2次/1次三角形要素 hit = TriElement_IsPointInElement(element, test_pp, nodes); } else { System.Diagnostics.Debug.Assert(false); } return hit; }
/// <summary> /// フィールド値を描画する /// </summary> /// <param name="g"></param> /// <param name="ofs"></param> /// <param name="delta"></param> /// <param name="regionSize"></param> /// <param name="fieldDv"></param> /// <param name="valueDv"></param> /// <param name="colorMap"></param> public virtual void DrawField(Graphics g, Size ofs, Size delta, Size regionSize, FemElement.FieldDV fieldDv, FemElement.ValueDV valueDv, ColorMap colorMap) { }
/// <summary> /// Fem入力データの取得 /// </summary> /// <param name="outNodes">節点リスト</param> /// <param name="outElements">要素リスト</param> /// <param name="outMedias">媒質リスト</param> /// <param name="outPorts">ポート節点番号リストのリスト</param> /// <param name="outForceNodes">強制境界節点番号リスト</param> /// <param name="outIncidentPortNo">入射ポート番号</param> /// <param name="outWaveguideWidth">導波路の幅</param> public void GetFemInputInfo( out FemNode[] outNodes, out FemElement[] outElements, out MediaInfo[] outMedias, out IList<int[]> outPorts, out int[] outForceNodes, out int outIncidentPortNo, out double outWaveguideWidth, out double outLatticeA) { outNodes = null; outElements = null; outMedias = null; outPorts = null; outForceNodes = null; outIncidentPortNo = 1; outWaveguideWidth = DefWaveguideWidth; outLatticeA = 1.0; /* データの判定は取得した側が行う(メッシュ表示で、ポートを指定しないとメッシュが表示されないのを解消するためここで判定するのを止める) if (!isInputDataValid()) { return; } */ int nodeCnt = Nodes.Count; outNodes = new FemNode[nodeCnt]; for (int i = 0; i < nodeCnt; i++) { FemNode femNode = new FemNode(); femNode.CP(Nodes[i]); outNodes[i] = femNode; } int elementCnt = Elements.Count; outElements = new FemElement[elementCnt]; for (int i = 0; i < elementCnt; i++) { //FemElement femElement = new FemElement(); FemElement femElement = FemMeshLogic.CreateFemElementByElementNodeCnt(Elements[i].NodeNumbers.Length); femElement.CP(Elements[i]); outElements[i] = femElement; } if (Medias != null) { outMedias = new MediaInfo[Medias.Length]; for (int i = 0; i < Medias.Length; i++) { outMedias[i] = Medias[i].Clone() as MediaInfo; } } int portCnt = Ports.Count; outPorts = new List<int[]>(); foreach (IList<int> portNodes in Ports) { int[] outPortNodes = new int[portNodes.Count]; for (int inoB = 0; inoB < portNodes.Count; inoB++) { outPortNodes[inoB] = portNodes[inoB]; } outPorts.Add(outPortNodes); } outForceNodes = new int[ForceBCNodes.Count]; for (int i = 0; i < ForceBCNodes.Count; i++) { outForceNodes[i] = ForceBCNodes[i]; } outIncidentPortNo = IncidentPortNo; outWaveguideWidth = WaveguideWidth; outLatticeA = LatticeA; }
/// <summary> /// 対象のフィールドを描画する /// </summary> /// <param name="g"></param> /// <param name="ofs"></param> /// <param name="delta"></param> /// <param name="regionSize"></param> /// <param name="tagtValues"></param> /// <param name="valueDv"></param> /// <param name="colorMap"></param> public void doDrawField(Graphics g, Size ofs, Size delta, Size regionSize, Complex[] tagtValues, FemElement.ValueDV valueDv, ColorMap colorMap) { const int ndim = Constants.CoordDim2D; //2; // 座標の次元数 const int vertexCnt = Constants.TriVertexCnt; //3; // 三角形の頂点の数(2次要素でも同じ) //const int nodeCnt = Constants.TriNodeCnt_SecondOrder; //6; // 三角形2次要素 int nodeCnt = NodeNumbers.Length; if (nodeCnt != Constants.TriNodeCnt_SecondOrder && nodeCnt != Constants.TriNodeCnt_FirstOrder) { return; } // 三角形の節点座標を取得 double[][] pp = new double[nodeCnt][]; for (int ino = 0; ino < pp.GetLength(0); ino++) { FemNode node = _Nodes[ino]; System.Diagnostics.Debug.Assert(node.Coord.Length == ndim); pp[ino] = new double[ndim]; pp[ino][0] = node.Coord[0] * delta.Width + ofs.Width; //pp[ino][1] = regionSize.Height - node.Coord[1] * delta.Height + ofs.Height; pp[ino][1] = node.Coord[1] * delta.Height + ofs.Height; } // 長方形描画領域のリスト IList<double[][]> rectLiList = _RectLiList; // 描画ロジック上の原点となる頂点 int orginVertexNo = _OrginVertexNo; // 四角形の頂点 const int rectVCnt = 4; foreach (double[][] rectLi in rectLiList) { double[][] rectpp = new double[rectVCnt][]; for (int ino = 0; ino < rectVCnt; ino++) { double[] vLpp = rectLi[ino]; double xx = 0.0; double yy = 0.0; for (int k = 0; k < vertexCnt; k++) { xx += pp[k][0] * vLpp[(k + orginVertexNo) % vertexCnt]; yy += pp[k][1] * vLpp[(k + orginVertexNo) % vertexCnt]; } rectpp[ino] = new double[] { xx, yy }; } // 表示する位置 double[] vLi = new double[] { (rectLi[0][0] + rectLi[1][0]) * 0.5, (rectLi[0][1] + rectLi[3][1]) * 0.5, 0 }; if (vLi[0] < 0.0) { vLi[0] = 0.0; } if (vLi[0] > 1.0) { vLi[0] = 1.0; } if (vLi[1] < 0.0) { vLi[1] = 0.0; } if (vLi[1] > (1.0 - vLi[0])) { vLi[1] = (1.0 - vLi[0]); } vLi[2] = 1.0 - vLi[0] - vLi[1]; if (vLi[2] < 0.0) { System.Diagnostics.Debug.WriteLine("logic error vLi[2] = {0}", vLi[2]); } // 表示する値 Complex cvalue = new Complex(0.0, 0.0); // 表示する位置の形状関数値 double[] vNi = null; double[] shiftedLi = new double[vertexCnt]; for (int i = 0; i < vertexCnt; i++) { shiftedLi[i] = vLi[(i + orginVertexNo) % vertexCnt]; } if (nodeCnt == Constants.TriNodeCnt_FirstOrder) { vNi = new double[] { shiftedLi[0], shiftedLi[1], shiftedLi[2] }; } else { vNi = new double[] { shiftedLi[0] * (2.0 * shiftedLi[0] - 1.0), shiftedLi[1] * (2.0 * shiftedLi[1] - 1.0), shiftedLi[2] * (2.0 * shiftedLi[2] - 1.0), 4.0 * shiftedLi[0] * shiftedLi[1], 4.0 * shiftedLi[1] * shiftedLi[2], 4.0 * shiftedLi[2] * shiftedLi[0], }; } for (int k = 0; k < nodeCnt; k++) { cvalue += tagtValues[k] * vNi[k]; } // 四角形の頂点(描画用) Point[] rectp = new Point[rectVCnt]; for (int ino = 0; ino < rectVCnt; ino++) { rectp[ino] = new Point((int)rectpp[ino][0], (int)rectpp[ino][1]); } try { // 表示する値 double showValue = 0.0; if (valueDv == ValueDV.Real) { showValue = cvalue.Real; } else if (valueDv == ValueDV.Imaginary) { showValue = cvalue.Imaginary; } else { // 既定値は絶対値 showValue = Complex.Abs(cvalue); } // 塗りつぶし色の取得 Color fillColor = colorMap.GetColor(showValue); // 塗りつぶし using (Brush brush = new SolidBrush(fillColor)) { g.FillPolygon(brush, rectp); } } catch (Exception exception) { System.Diagnostics.Debug.WriteLine(exception.Message + " " + exception.StackTrace); } } }
/// <summary> /// フィールド値の回転を描画する /// </summary> /// <param name="g"></param> /// <param name="ofs"></param> /// <param name="delta"></param> /// <param name="regionSize"></param> /// <param name="drawColor"></param> /// <param name="fieldDv"></param> /// <param name="minRotFValue"></param> /// <param name="maxRotFValue"></param> public override void DrawRotField(Graphics g, Size ofs, Size delta, Size regionSize, Color drawColor, FemElement.FieldDV fieldDv, double minRotFValue, double maxRotFValue) { if (_Nodes == null || _FValues == null || _RotXFValues == null || _RotYFValues == null || _PoyntingXFValues == null || _PoyntingYFValues == null) { return; } Complex[] tagtXValues = null; Complex[] tagtYValues = null; if (fieldDv == FemElement.FieldDV.PoyntingXY) { tagtXValues = _PoyntingXFValues; tagtYValues = _PoyntingYFValues; } else if (fieldDv == FemElement.FieldDV.RotXY) { tagtXValues = _RotXFValues; tagtYValues = _RotYFValues; } else { return; } const int ndim = Constants.CoordDim2D; //2; // 座標の次元数 const int vertexCnt = Constants.TriVertexCnt; //3; // 三角形の頂点の数(2次要素でも同じ) //const int nodeCnt = Constants.TriNodeCnt_SecondOrder; //6; // 三角形2次要素 int nodeCnt = NodeNumbers.Length; if (nodeCnt != Constants.TriNodeCnt_SecondOrder && nodeCnt != Constants.TriNodeCnt_FirstOrder) { return; } // 三角形の節点座標を取得 double[][] pp = new double[nodeCnt][]; for (int ino = 0; ino < pp.GetLength(0); ino++) { FemNode node = _Nodes[ino]; System.Diagnostics.Debug.Assert(node.Coord.Length == ndim); pp[ino] = new double[ndim]; pp[ino][0] = node.Coord[0] * delta.Width + ofs.Width; //pp[ino][1] = regionSize.Height - node.Coord[1] * delta.Height + ofs.Height; pp[ino][1] = node.Coord[1] * delta.Height + ofs.Height; } // 表示する位置の面積座標 double[] Li = new double[vertexCnt] { 1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0 }; // 表示する位置の形状関数 double[] vNi = null; if (nodeCnt == Constants.TriNodeCnt_FirstOrder) { vNi = new double[] { Li[0], Li[1], Li[2] }; } else { vNi = new double[] { Li[0] * (2.0 * Li[0] - 1.0), Li[1] * (2.0 * Li[1] - 1.0), Li[2] * (2.0 * Li[2] - 1.0), 4.0 * Li[0] * Li[1], 4.0 * Li[1] * Li[2], 4.0 * Li[2] * Li[0], }; } // 表示する位置 double showPosX = 0; double showPosY = 0; for (int k = 0; k < nodeCnt; k++) { showPosX += pp[k][0] * vNi[k]; showPosY += pp[k][1] * vNi[k]; } Complex cvalueX = new Complex(0, 0); Complex cvalueY = new Complex(0, 0); for (int k = 0; k < nodeCnt; k++) { cvalueX += tagtXValues[k] * vNi[k]; cvalueY += tagtYValues[k] * vNi[k]; } try { double showScale = ((double)regionSize.Width / DefPanelWidth) * ArrowLength; // 実数部のベクトル表示 int lenX = 0; int lenY = 0; if (Math.Abs(maxRotFValue) >= Constants.PrecisionLowerLimit) { lenX = (int)((double)(cvalueX.Real / maxRotFValue) * showScale); lenY = (int)((double)(cvalueY.Real / maxRotFValue) * showScale); } if (lenX != 0 || lenY != 0) { // Y方向は表示上逆になる lenY = -lenY; using (Pen pen = new Pen(drawColor, 1)) { //pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; //pen.StartCap = System.Drawing.Drawing2D.LineCap.Round; pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; //pen.CustomEndCap = new System.Drawing.Drawing2D.AdjustableArrowCap(3, 3, false); // 重い g.DrawLine(pen, (int)showPosX, (int)showPosY, (int)(showPosX + lenX), (int)(showPosY + lenY)); } } } catch (Exception exception) { System.Diagnostics.Debug.WriteLine(exception.Message + " " + exception.StackTrace); } }
/// <summary> /// フィールド値を描画する /// </summary> /// <param name="g"></param> /// <param name="ofs"></param> /// <param name="delta"></param> /// <param name="regionSize"></param> /// <param name="fieldDv"></param> /// <param name="valueDv"></param> /// <param name="colorMap"></param> public override void DrawField(Graphics g, Size ofs, Size delta, Size regionSize, FemElement.FieldDV fieldDv, FemElement.ValueDV valueDv, ColorMap colorMap) { //base.DrawField(g, ofs, delta, regionSize, colorMap); if (_Nodes == null || _FValues == null || _RotXFValues == null || _RotYFValues == null || _PoyntingXFValues == null || _PoyntingYFValues == null) { return; } Complex[] tagtValues = null; if (fieldDv == FemElement.FieldDV.Field) { tagtValues = _FValues; } else if (fieldDv == FemElement.FieldDV.RotX) { tagtValues = _RotXFValues; } else if (fieldDv == FemElement.FieldDV.RotY) { tagtValues = _RotYFValues; } else { return; } doDrawField(g, ofs, delta, regionSize, tagtValues, valueDv, colorMap); }
/// <summary> /// 周期構造導波路の固有モード分布を描画する /// </summary> /// <param name="g"></param> /// <param name="ofs"></param> /// <param name="delta"></param> /// <param name="regionSize"></param> /// <param name="valueDv"></param> /// <param name="colorMap"></param> public override void DrawEigenField(Graphics g, Size ofs, Size delta, Size regionSize, FemElement.ValueDV valueDv, ColorMap colorMap) { Complex[] tagtValues = _EigenFValues; if (tagtValues != null) { doDrawField(g, ofs, delta, regionSize, tagtValues, valueDv, colorMap); } }
/// <summary> /// ヘルムホルツ方程式に対する有限要素マトリクス作成 /// </summary> /// <param name="waveLength">波長</param> /// <param name="toSorted">ソートされた節点インデックス( 2D節点番号→ソート済みリストインデックスのマップ)</param> /// <param name="element">有限要素</param> /// <param name="Nodes">節点リスト</param> /// <param name="Medias">媒質リスト</param> /// <param name="ForceNodeNumberH">強制境界節点ハッシュ</param> /// <param name="WaveModeDv">計算する波のモード区分</param> /// <param name="mat">マージされる全体行列</param> public static void AddElementMat( double waveLength, Dictionary<int, int> toSorted, FemElement element, IList<FemNode> Nodes, MediaInfo[] Medias, Dictionary<int, bool> ForceNodeNumberH, FemSolver.WaveModeDV WaveModeDv, ref MyComplexMatrix mat) { // 定数 const double pi = Constants.pi; const double c0 = Constants.c0; // 波数 double k0 = 2.0 * pi / waveLength; // 角周波数 double omega = k0 * c0; // 要素頂点数 const int vertexCnt = Constants.TriVertexCnt; //3; // 要素内節点数 const int nno = Constants.TriNodeCnt_SecondOrder; //6; // 2次三角形要素 // 座標次元数 const int ndim = Constants.CoordDim2D; //2; int[] nodeNumbers = element.NodeNumbers; int[] no_c = new int[nno]; MediaInfo media = Medias[element.MediaIndex]; // ver1.1.0.0 媒質情報の取得 double[,] media_P = null; double[,] media_Q = null; // ヘルムホルツ方程式のパラメータP,Qを取得する FemSolver.GetHelmholtzMediaPQ( k0, media, WaveModeDv, out media_P, out media_Q); // 節点座標(IFの都合上配列の配列形式の2次元配列を作成) double[][] pp = new double[nno][]; for (int ino = 0; ino < nno; ino++) { int nodeNumber = nodeNumbers[ino]; int nodeIndex = nodeNumber - 1; FemNode node = Nodes[nodeIndex]; no_c[ino] = nodeNumber; pp[ino] = new double[ndim]; for (int n = 0; n < ndim; n++) { pp[ino][n] = node.Coord[n]; } } // 面積を求める double area = KerEMatTri.TriArea(pp[0], pp[1], pp[2]); //System.Diagnostics.Debug.WriteLine("Elem No {0} area: {1}", element.No, area); System.Diagnostics.Debug.Assert(area >= 0.0); // 面積座標の微分を求める // dldx[k, n] k面積座標Lkのn方向微分 double[,] dldx = null; double[] const_term = null; KerEMatTri.TriDlDx(out dldx, out const_term, pp[0], pp[1], pp[2]); // 形状関数の微分の係数を求める // dndxC[ino,n,k] ino節点のn方向微分のLk(k面積座標)の係数 // dNino/dn = dndxC[ino, n, 0] * L0 + dndxC[ino, n, 1] * L1 + dndxC[ino, n, 2] * L2 + dndxC[ino, n, 3] double[, ,] dndxC = new double[nno, ndim, vertexCnt + 1] { { {4.0 * dldx[0, 0], 0.0, 0.0, -1.0 * dldx[0, 0]}, {4.0 * dldx[0, 1], 0.0, 0.0, -1.0 * dldx[0, 1]}, }, { {0.0, 4.0 * dldx[1, 0], 0.0, -1.0 * dldx[1, 0]}, {0.0, 4.0 * dldx[1, 1], 0.0, -1.0 * dldx[1, 1]}, }, { {0.0, 0.0, 4.0 * dldx[2, 0], -1.0 * dldx[2, 0]}, {0.0, 0.0, 4.0 * dldx[2, 1], -1.0 * dldx[2, 1]}, }, { {4.0 * dldx[1, 0], 4.0 * dldx[0, 0], 0.0, 0.0}, {4.0 * dldx[1, 1], 4.0 * dldx[0, 1], 0.0, 0.0}, }, { {0.0, 4.0 * dldx[2, 0], 4.0 * dldx[1, 0], 0.0}, {0.0, 4.0 * dldx[2, 1], 4.0 * dldx[1, 1], 0.0}, }, { {4.0 * dldx[2, 0], 0.0, 4.0 * dldx[0, 0], 0.0}, {4.0 * dldx[2, 1], 0.0, 4.0 * dldx[0, 1], 0.0}, }, }; // ∫dN/dndN/dn dxdy // integralDNDX[n, ino, jno] n = 0 --> ∫dN/dxdN/dx dxdy // n = 1 --> ∫dN/dydN/dy dxdy double[, ,] integralDNDX = new double[ndim, nno, nno]; for (int n = 0; n < ndim; n++) { for (int ino = 0; ino < nno; ino++) { for (int jno = 0; jno < nno; jno++) { integralDNDX[n, ino, jno] = area / 6.0 * (dndxC[ino, n, 0] * dndxC[jno, n, 0] + dndxC[ino, n, 1] * dndxC[jno, n, 1] + dndxC[ino, n, 2] * dndxC[jno, n, 2]) + area / 12.0 * (dndxC[ino, n, 0] * dndxC[jno, n, 1] + dndxC[ino, n, 0] * dndxC[jno, n, 2] + dndxC[ino, n, 1] * dndxC[jno, n, 0] + dndxC[ino, n, 1] * dndxC[jno, n, 2] + dndxC[ino, n, 2] * dndxC[jno, n, 0] + dndxC[ino, n, 2] * dndxC[jno, n, 1]) + area / 3.0 * (dndxC[ino, n, 0] * dndxC[jno, n, 3] + dndxC[ino, n, 1] * dndxC[jno, n, 3] + dndxC[ino, n, 2] * dndxC[jno, n, 3] + dndxC[ino, n, 3] * dndxC[jno, n, 0] + dndxC[ino, n, 3] * dndxC[jno, n, 1] + dndxC[ino, n, 3] * dndxC[jno, n, 2]) + area * dndxC[ino, n, 3] * dndxC[jno, n, 3]; } } } // ∫N N dxdy double[,] integralN = new double[nno, nno] { { 6.0 * area / 180.0, -1.0 * area / 180.0, -1.0 * area / 180.0, 0.0, -4.0 * area / 180.0, 0.0}, { -1.0 * area / 180.0, 6.0 * area / 180.0, -1.0 * area / 180.0, 0.0, 0.0, -4.0 * area / 180.0}, { -1.0 * area / 180.0, -1.0 * area / 180.0, 6.0 * area / 180.0, -4.0 * area / 180.0, 0.0, 0.0}, { 0.0, 0.0, -4.0 * area / 180.0, 32.0 * area / 180.0, 16.0 * area / 180.0, 16.0 * area / 180.0}, { -4.0 * area / 180.0, 0.0, 0.0, 16.0 * area / 180.0, 32.0 * area / 180.0, 16.0 * area / 180.0}, { 0.0, -4.0 * area / 180.0, 0.0, 16.0 * area / 180.0, 16.0 * area / 180.0, 32.0 * area / 180.0}, }; // 要素剛性行列を作る double[,] emat = new double[nno, nno]; for (int ino = 0; ino < nno; ino++) { for (int jno = 0; jno < nno; jno++) { emat[ino, jno] = media_P[0, 0] * integralDNDX[1, ino, jno] + media_P[1, 1] * integralDNDX[0, ino, jno] - k0 * k0 * media_Q[2, 2] * integralN[ino, jno]; } } // 要素剛性行列にマージする for (int ino = 0; ino < nno; ino++) { int iNodeNumber = no_c[ino]; if (ForceNodeNumberH.ContainsKey(iNodeNumber)) continue; int inoGlobal = toSorted[iNodeNumber]; for (int jno = 0; jno < nno; jno++) { int jNodeNumber = no_c[jno]; if (ForceNodeNumberH.ContainsKey(jNodeNumber)) continue; int jnoGlobal = toSorted[jNodeNumber]; //mat[inoGlobal, jnoGlobal] += emat[ino, jno]; //mat._body[inoGlobal + jnoGlobal * mat.RowSize] += emat[ino, jno]; // 実数部に加算する //mat._body[inoGlobal + jnoGlobal * mat.RowSize].Real += emat[ino, jno]; // バンドマトリクス対応 mat._body[mat.GetBufferIndex(inoGlobal, jnoGlobal)].Real += emat[ino, jno]; } } }
/// <summary> /// 周期構造導波路固有値問題用FEM行列の追加 /// </summary> /// <param name="isYDirectionPeriodic"></param> /// <param name="waveLength"></param> /// <param name="nodeCntPeriodic"></param> /// <param name="freeNodeCntPeriodic_0"></param> /// <param name="toSortedPeriodic"></param> /// <param name="element"></param> /// <param name="Nodes"></param> /// <param name="Medias"></param> /// <param name="ForceNodeNumberH"></param> /// <param name="WaveModeDv"></param> /// <param name="KMat"></param> public static void AddElementMatPeriodic( bool isYDirectionPeriodic, bool isSVEA, double waveLength, int nodeCntPeriodic, int freeNodeCntPeriodic_0, Dictionary<int, int> toSortedPeriodic, FemElement element, IList<FemNode> Nodes, MediaInfo[] Medias, Dictionary<int, bool> ForceNodeNumberH, FemSolver.WaveModeDV WaveModeDv, ref double[] KMat, ref double[] CMat, ref double[] MMat) { // 定数 const double pi = Constants.pi; const double c0 = Constants.c0; // 波数 double k0 = 2.0 * pi / waveLength; // 角周波数 double omega = k0 * c0; // 要素頂点数 const int vertexCnt = Constants.TriVertexCnt; //3; // 要素内節点数 const int nno = Constants.TriNodeCnt_SecondOrder; //6; // 2次三角形要素 // 座標次元数 const int ndim = Constants.CoordDim2D; //2; int[] nodeNumbers = element.NodeNumbers; int[] no_c = new int[nno]; MediaInfo media = Medias[element.MediaIndex]; // ver1.1.0.0 媒質情報の取得 double[,] media_P = null; double[,] media_Q = null; // ヘルムホルツ方程式のパラメータP,Qを取得する FemSolver.GetHelmholtzMediaPQ( k0, media, WaveModeDv, out media_P, out media_Q); // 節点座標(IFの都合上配列の配列形式の2次元配列を作成) double[][] pp = new double[nno][]; for (int ino = 0; ino < nno; ino++) { int nodeNumber = nodeNumbers[ino]; int nodeIndex = nodeNumber - 1; FemNode node = Nodes[nodeIndex]; no_c[ino] = nodeNumber; pp[ino] = new double[ndim]; for (int n = 0; n < ndim; n++) { pp[ino][n] = node.Coord[n]; } } // 面積を求める double area = KerEMatTri.TriArea(pp[0], pp[1], pp[2]); //System.Diagnostics.Debug.WriteLine("Elem No {0} area: {1}", element.No, area); System.Diagnostics.Debug.Assert(area >= 0.0); // 面積座標の微分を求める // dldx[k, n] k面積座標Lkのn方向微分 double[,] dldx = null; double[] const_term = null; KerEMatTri.TriDlDx(out dldx, out const_term, pp[0], pp[1], pp[2]); // 形状関数の微分の係数を求める // dndxC[ino,n,k] ino節点のn方向微分のLk(k面積座標)の係数 // dNino/dn = dndxC[ino, n, 0] * L0 + dndxC[ino, n, 1] * L1 + dndxC[ino, n, 2] * L2 + dndxC[ino, n, 3] double[, ,] dndxC = new double[nno, ndim, vertexCnt + 1] { { {4.0 * dldx[0, 0], 0.0, 0.0, -1.0 * dldx[0, 0]}, {4.0 * dldx[0, 1], 0.0, 0.0, -1.0 * dldx[0, 1]}, }, { {0.0, 4.0 * dldx[1, 0], 0.0, -1.0 * dldx[1, 0]}, {0.0, 4.0 * dldx[1, 1], 0.0, -1.0 * dldx[1, 1]}, }, { {0.0, 0.0, 4.0 * dldx[2, 0], -1.0 * dldx[2, 0]}, {0.0, 0.0, 4.0 * dldx[2, 1], -1.0 * dldx[2, 1]}, }, { {4.0 * dldx[1, 0], 4.0 * dldx[0, 0], 0.0, 0.0}, {4.0 * dldx[1, 1], 4.0 * dldx[0, 1], 0.0, 0.0}, }, { {0.0, 4.0 * dldx[2, 0], 4.0 * dldx[1, 0], 0.0}, {0.0, 4.0 * dldx[2, 1], 4.0 * dldx[1, 1], 0.0}, }, { {4.0 * dldx[2, 0], 0.0, 4.0 * dldx[0, 0], 0.0}, {4.0 * dldx[2, 1], 0.0, 4.0 * dldx[0, 1], 0.0}, }, }; // ∫dN/dndN/dn dxdy // integralDNDX[n, ino, jno] n = 0 --> ∫dN/dxdN/dx dxdy // n = 1 --> ∫dN/dydN/dy dxdy double[, ,] integralDNDX = new double[ndim, nno, nno]; for (int n = 0; n < ndim; n++) { for (int ino = 0; ino < nno; ino++) { for (int jno = 0; jno < nno; jno++) { integralDNDX[n, ino, jno] = area / 6.0 * (dndxC[ino, n, 0] * dndxC[jno, n, 0] + dndxC[ino, n, 1] * dndxC[jno, n, 1] + dndxC[ino, n, 2] * dndxC[jno, n, 2]) + area / 12.0 * (dndxC[ino, n, 0] * dndxC[jno, n, 1] + dndxC[ino, n, 0] * dndxC[jno, n, 2] + dndxC[ino, n, 1] * dndxC[jno, n, 0] + dndxC[ino, n, 1] * dndxC[jno, n, 2] + dndxC[ino, n, 2] * dndxC[jno, n, 0] + dndxC[ino, n, 2] * dndxC[jno, n, 1]) + area / 3.0 * (dndxC[ino, n, 0] * dndxC[jno, n, 3] + dndxC[ino, n, 1] * dndxC[jno, n, 3] + dndxC[ino, n, 2] * dndxC[jno, n, 3] + dndxC[ino, n, 3] * dndxC[jno, n, 0] + dndxC[ino, n, 3] * dndxC[jno, n, 1] + dndxC[ino, n, 3] * dndxC[jno, n, 2]) + area * dndxC[ino, n, 3] * dndxC[jno, n, 3]; } } } // ∫(dNi/dx)Nj dxdy // ∫(dNi/dy)Nj dxdy double[, ,] integralDNDXL = new double[2, nno, nno]; for (int ino = 0; ino < nno; ino++) { for (int n = 0; n < ndim; n++) { integralDNDXL[n, ino, 0] = (area / 30.0) * dndxC[ino, n, 0] - (area / 60.0) * (dndxC[ino, n, 1] + dndxC[ino, n, 2]); integralDNDXL[n, ino, 1] = (area / 30.0) * dndxC[ino, n, 1] - (area / 60.0) * (dndxC[ino, n, 0] + dndxC[ino, n, 2]); integralDNDXL[n, ino, 2] = (area / 30.0) * dndxC[ino, n, 2] - (area / 60.0) * (dndxC[ino, n, 0] + dndxC[ino, n, 1]); integralDNDXL[n, ino, 3] = (area / 15.0) * (2.0 * dndxC[ino, n, 0] + 2.0 * dndxC[ino, n, 1] + dndxC[ino, n, 2] + 5.0 * dndxC[ino, n, 3]); integralDNDXL[n, ino, 4] = (area / 15.0) * (dndxC[ino, n, 0] + 2.0 * dndxC[ino, n, 1] + 2.0 * dndxC[ino, n, 2] + 5.0 * dndxC[ino, n, 3]); integralDNDXL[n, ino, 5] = (area / 15.0) * (2.0 * dndxC[ino, n, 0] + dndxC[ino, n, 1] + 2.0 * dndxC[ino, n, 2] + 5.0 * dndxC[ino, n, 3]); } } // ∫N N dxdy double[,] integralN = new double[nno, nno] { { 6.0 * area / 180.0, -1.0 * area / 180.0, -1.0 * area / 180.0, 0.0, -4.0 * area / 180.0, 0.0}, { -1.0 * area / 180.0, 6.0 * area / 180.0, -1.0 * area / 180.0, 0.0, 0.0, -4.0 * area / 180.0}, { -1.0 * area / 180.0, -1.0 * area / 180.0, 6.0 * area / 180.0, -4.0 * area / 180.0, 0.0, 0.0}, { 0.0, 0.0, -4.0 * area / 180.0, 32.0 * area / 180.0, 16.0 * area / 180.0, 16.0 * area / 180.0}, { -4.0 * area / 180.0, 0.0, 0.0, 16.0 * area / 180.0, 32.0 * area / 180.0, 16.0 * area / 180.0}, { 0.0, -4.0 * area / 180.0, 0.0, 16.0 * area / 180.0, 16.0 * area / 180.0, 32.0 * area / 180.0}, }; // 要素剛性行列を作る double[,] eKMat = new double[nno, nno]; double[,] eCMat = new double[nno, nno]; double[,] eMMat = new double[nno, nno]; for (int ino = 0; ino < nno; ino++) { for (int jno = 0; jno < nno; jno++) { eKMat[ino, jno] = -media_P[0, 0] * integralDNDX[1, ino, jno] - media_P[1, 1] * integralDNDX[0, ino, jno] + k0 * k0 * media_Q[2, 2] * integralN[ino, jno]; if (isSVEA) { if (isYDirectionPeriodic) { eCMat[ino, jno] = -media.P[1, 1] * (integralDNDXL[1, ino, jno] - integralDNDXL[1, jno, ino]); } else { eCMat[ino, jno] = -media.P[1, 1] * (integralDNDXL[0, ino, jno] - integralDNDXL[0, jno, ino]); } // 要素質量行列 eMMat[ino, jno] = media.P[1, 1] * integralN[ino, jno]; } } } // 要素剛性行列にマージする for (int ino = 0; ino < nno; ino++) { int iNodeNumber = no_c[ino]; if (ForceNodeNumberH.ContainsKey(iNodeNumber)) continue; int inoGlobal = toSortedPeriodic[iNodeNumber]; for (int jno = 0; jno < nno; jno++) { int jNodeNumber = no_c[jno]; if (ForceNodeNumberH.ContainsKey(jNodeNumber)) continue; int jnoGlobal = toSortedPeriodic[jNodeNumber]; KMat[inoGlobal + freeNodeCntPeriodic_0 * jnoGlobal] += eKMat[ino, jno]; if (isSVEA) { CMat[inoGlobal + freeNodeCntPeriodic_0 * jnoGlobal] += eCMat[ino, jno]; MMat[inoGlobal + freeNodeCntPeriodic_0 * jnoGlobal] += eMMat[ino, jno]; } } } }
/// <summary> /// フィールド値の回転を描画する /// </summary> /// <param name="g"></param> /// <param name="ofs"></param> /// <param name="delta"></param> /// <param name="regionSize"></param> /// <param name="drawColor"></param> /// <param name="fieldDv"></param> /// <param name="minRotFValue"></param> /// <param name="maxRotFValue"></param> public virtual void DrawRotField(Graphics g, Size ofs, Size delta, Size regionSize, Color drawColor, FemElement.FieldDV fieldDv, double minRotFValue, double maxRotFValue) { }
/// <summary> /// フィールドの回転ベクトル描画 /// </summary> /// <param name="g"></param> /// <param name="panel"></param> public void DrawRotFieldEx(Graphics g, Panel panel, Rectangle clientRectangle, FemElement.FieldDV fieldDv) { if (!isInputDataReady()) { return; } if (!isOutputDataReady()) { return; } Size delta; Size ofs; Size regionSize; //getFitDrawRegion(panel, out delta, out ofs, out regionSize); getFitDrawRegion(clientRectangle.Width, clientRectangle.Height, out delta, out ofs, out regionSize); ofs.Width += clientRectangle.Left; ofs.Height += clientRectangle.Top; Color drawColor = Color.Gray; double min = 0.0; double max = 1.0; if (fieldDv == FemElement.FieldDV.PoyntingXY) { drawColor = Color.Green;//Color.YellowGreen; min = -MaxPoyntingFValue; max = MaxPoyntingFValue; } else if (fieldDv == FemElement.FieldDV.RotXY) { drawColor = Color.Red; { drawColor = (WaveModeDv == FemSolver.WaveModeDV.TM) ? Color.Blue : Color.Red; } min = -MaxRotFValue; max = MaxRotFValue; } else { return; } foreach (FemElement element in Elements) { // 回転ベクトル描画 element.DrawRotField(g, ofs, delta, regionSize, drawColor, fieldDv, min, max); } }
/// <summary> /// 点が要素内に含まれる? /// </summary> /// <param name="element">要素</param> /// <param name="test_pp">テストする点</param> /// <param name="Nodes">節点リスト(要素は座標を持たないので節点リストが必要)</param> /// <returns></returns> public static bool IsPointInElement(FemElement element, double[] test_pp, IList<FemNode> nodes) { return FemMeshLogic.IsPointInElement(element, test_pp, nodes); }
/// <summary> /// 三角形要素:点が要素内に含まれる? /// </summary> /// <param name="element">要素</param> /// <param name="test_pp">テストする点</param> /// <param name="Nodes">節点リスト(要素は座標を持たないので節点リストが必要)</param> /// <returns></returns> public static bool TriElement_IsPointInElement(FemElement element, double[] test_pp, IList<FemNode> nodes) { bool hit = false; // 三角形の頂点数 const int vertexCnt = Constants.TriVertexCnt; double[][] pps = new double[vertexCnt][]; // 2次三角形要素の最初の3点=頂点の座標を取得 for (int ino = 0; ino < vertexCnt; ino++) { int nodeNumber = element.NodeNumbers[ino]; FemNode node = nodes[nodeNumber - 1]; System.Diagnostics.Debug.Assert(node.No == nodeNumber); pps[ino] = node.Coord; } // バウンディングボックス取得 double minX = double.MaxValue; double maxX = double.MinValue; double minY = double.MaxValue; double maxY = double.MinValue; foreach (double[] pp in pps) { double xx = pp[0]; double yy = pp[1]; if (minX > xx) { minX = xx; } if (maxX < xx) { maxX = xx; } if (minY > yy) { minY = yy; } if (maxY < yy) { maxY = yy; } } // バウンディングボックスでチェック if (test_pp[0] < minX || test_pp[0] > maxX) { return hit; } if (test_pp[1] < minY || test_pp[1] > maxY) { return hit; } // 頂点? foreach (double[] pp in pps) { if (Math.Abs(pp[0] - test_pp[0]) < Constants.PrecisionLowerLimit && Math.Abs(pp[1] - test_pp[1]) < Constants.PrecisionLowerLimit) { hit = true; break; } } if (!hit) { // 面積から内部判定する double area = KerEMatTri.TriArea(pps[0], pps[1], pps[2]); double sumOfSubArea = 0.0; for (int ino = 0; ino < vertexCnt; ino++) { double[][] subArea_pp = new double[vertexCnt][]; subArea_pp[0] = pps[ino]; subArea_pp[1] = pps[(ino + 1) % vertexCnt]; subArea_pp[2] = test_pp; //foreach (double[] work_pp in subArea_pp) //{ // System.Diagnostics.Debug.Write("{0},{1} ", work_pp[0], work_pp[1]); //} double subArea = KerEMatTri.TriArea(subArea_pp[0], subArea_pp[1], subArea_pp[2]); //System.Diagnostics.Debug.Write(" subArea = {0}", subArea); //System.Diagnostics.Debug.WriteLine(); //BUGFIX //if (subArea <= 0.0) // 丁度辺上の場合は、サブエリアの1つが0になるのでこれは許可しないといけない if (subArea < -1.0 * Constants.PrecisionLowerLimit) // 0未満 { sumOfSubArea = 0.0; break; // 外側? } sumOfSubArea += Math.Abs(subArea); } if (Math.Abs(area - sumOfSubArea) < Constants.PrecisionLowerLimit) { hit = true; } } return hit; }
/// <summary> /// ヘルムホルツ方程式に対する有限要素マトリクス作成 /// </summary> /// <param name="waveLength">波長</param> /// <param name="toSorted">ソートされた節点インデックス( 2D節点番号→ソート済みリストインデックスのマップ)</param> /// <param name="element">有限要素</param> /// <param name="mat">マージされる全体行列</param> private void addElementMat(double waveLength, Dictionary<int, int> toSorted, FemElement element, ref MyComplexMatrix mat) { Constants.FemElementShapeDV elemShapeDv; int order; int vertexCnt; FemMeshLogic.GetElementShapeDvAndOrderByElemNodeCnt(element.NodeNumbers.Length, out elemShapeDv, out order, out vertexCnt); if (elemShapeDv == Constants.FemElementShapeDV.Triangle && order == Constants.SecondOrder) { // 2次三角形要素の要素行列を全体行列に加算する FemMat_Tri_Second.AddElementMat( waveLength, toSorted, element, Nodes, Medias, ForceNodeNumberH, WaveModeDv, ref mat); } else if (elemShapeDv == Constants.FemElementShapeDV.Triangle && order == Constants.FirstOrder) { // 1次三角形要素の要素行列を全体行列に加算する FemMat_Tri_First.AddElementMat( waveLength, toSorted, element, Nodes, Medias, ForceNodeNumberH, WaveModeDv, ref mat); } else { System.Diagnostics.Debug.Assert(false); } }
/// <summary> /// コピー /// </summary> /// <param name="src"></param> public virtual void CP(FemElement src) { if (src == this) { return; } No = src.No; NodeNumbers = null; if (src.NodeNumbers != null) { NodeNumbers = new int[src.NodeNumbers.Length]; for (int i = 0; i < src.NodeNumbers.Length; i++) { NodeNumbers[i] = src.NodeNumbers[i]; } } MediaIndex = src.MediaIndex; LineColor = src.LineColor; BackColor = src.BackColor; // 内部使用のフィールドはコピーしない _Nodes = null; _FValues = null; _RotXFValues = null; _RotYFValues = null; _PoyntingXFValues = null; _PoyntingYFValues = null; _FactorForRot = 1.0; _EigenFValues = null; _media_Q = new double[,] { {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, }; _WaveModeDv = FemSolver.WaveModeDV.TE; IsCoarseFieldMesh = false; }