/// <summary> /// 1次線要素を作成する(固有値問題用) /// </summary> /// <param name="nodes"></param> /// <param name="EdgeToElementNoH"></param> /// <param name="Elements"></param> /// <param name="elements"></param> public static void MkElements( IList <int> nodes, Dictionary <string, IList <int> > EdgeToElementNoH, IList <FemElement> Elements, ref IList <FemLineElement> elements) { // 要素内節点数 const int nno = Constants.LineNodeCnt_FirstOrder; //2; // 1次線要素 // 要素リスト作成 int elemCnt = (nodes.Count - 1); // 1次線要素 for (int elemIndex = 0; elemIndex < elemCnt; elemIndex++) { // 線要素の要素内節点 // 節点番号はポート上の1D節点番号(1起点の番号) // 1 2 // +---+ FemLineElement element = new FemLineElement(); element.No = elemIndex + 1; element.NodeNumbers = new int[nno]; element.NodeNumbers[0] = elemIndex + 1; element.NodeNumbers[1] = elemIndex + 1 + 1; element.MediaIndex = 0; elements.Add(element); } // 要素を1辺とする2D領域の要素番号を取得 for (int elemIndex = 0; elemIndex < elements.Count; elemIndex++) { FemElement element = elements[elemIndex]; int[] nodeNumbers = element.NodeNumbers; // 1辺だけ調べればよい(1-2をチェック) int stNodeNumber = nodes[nodeNumbers[0] - 1]; int edNodeNumber = nodes[nodeNumbers[1] - 1]; string edgeKey = ""; if (stNodeNumber < edNodeNumber) { edgeKey = string.Format("{0}_{1}", stNodeNumber, edNodeNumber); } else { edgeKey = string.Format("{0}_{1}", edNodeNumber, stNodeNumber); } if (!EdgeToElementNoH.ContainsKey(edgeKey)) { System.Diagnostics.Debug.WriteLine("logical error: Not find edge {0}", edgeKey); } else { // 隣接する2Dの要素を1つ取得する int elemNo2d = EdgeToElementNoH[edgeKey][0]; FemElement element2d = Elements[elemNo2d - 1]; // 媒質インデックスをセット element.MediaIndex = element2d.MediaIndex; } } }
/// <summary> /// 1Dヘルムホルツ方程式固有値問題の要素行列を加算する /// </summary> /// <param name="waveLength">波長(E面の場合のみ使用する)</param> /// <param name="element">線要素</param> /// <param name="coords">座標リスト</param> /// <param name="toSorted">節点番号→ソート済み節点インデックスマップ</param> /// <param name="Medias">媒質情報リスト</param> /// <param name="WaveModeDv">計算する波のモード区分</param> /// <param name="txx_1d">txx行列</param> /// <param name="ryy_1d">ryy行列</param> /// <param name="uzz_1d">uzz行列</param> public static void AddElementMatOf1dEigenValueProblem( double waveLength, FemLineElement element, IList<double> coords, Dictionary<int, int> toSorted, MediaInfo[] Medias, FemSolver.WaveModeDV WaveModeDv, ref MyDoubleMatrix txx_1d, ref MyDoubleMatrix ryy_1d, ref MyDoubleMatrix uzz_1d) { // 定数 const double pi = Constants.pi; const double c0 = Constants.c0; // 波数 double k0 = 2.0 * pi / waveLength; // 角周波数 double omega = k0 * c0; // 2次線要素 const int nno = Constants.LineNodeCnt_SecondOrder; // 3; int[] nodeNumbers = element.NodeNumbers; System.Diagnostics.Debug.Assert(nno == nodeNumbers.Length); // 座標の取得 double[] elementCoords = new double[nno]; for (int n = 0; n < nno; n++) { int nodeIndex = nodeNumbers[n] - 1; elementCoords[n] = coords[nodeIndex]; } // 線要素の長さ double elen = Math.Abs(elementCoords[1] - elementCoords[0]); // 媒質インデックス int mediaIndex = element.MediaIndex; // 媒質 MediaInfo media = Medias[mediaIndex]; double[,] media_P = null; double[,] media_Q = null; // ヘルムホルツ方程式のパラメータP,Qを取得する FemSolver.GetHelmholtzMediaPQ( k0, media, WaveModeDv, out media_P, out media_Q); double[,] integralN = new double[nno, nno] { { 4.0 / 30.0 * elen, -1.0 / 30.0 * elen, 2.0 / 30.0 * elen }, { -1.0 / 30.0 * elen, 4.0 / 30.0 * elen, 2.0 / 30.0 * elen }, { 2.0 / 30.0 * elen, 2.0 / 30.0 * elen, 16.0 / 30.0 * elen }, }; double[,] integralDNDY = new double[nno, nno] { { 7.0 / (3.0 * elen), 1.0 / (3.0 * elen), -8.0 / (3.0 * elen) }, { 1.0 / (3.0 * elen), 7.0 / (3.0 * elen), -8.0 / (3.0 * elen) }, { -8.0 / (3.0 * elen), -8.0 / (3.0 * elen), 16.0 / (3.0 * elen) }, }; for (int ino = 0; ino < nno; ino++) { int inoBoundary = nodeNumbers[ino]; int inoSorted; if (!toSorted.ContainsKey(inoBoundary)) continue; inoSorted = toSorted[inoBoundary]; for (int jno = 0; jno < nno; jno++) { int jnoBoundary = nodeNumbers[jno]; int jnoSorted; if (!toSorted.ContainsKey(jnoBoundary)) continue; jnoSorted = toSorted[jnoBoundary]; // 対称バンド行列対応 if (ryy_1d is MyDoubleSymmetricBandMatrix && jnoSorted < inoSorted) { continue; } double e_txx_1d_inojno = media_P[0, 0] * integralDNDY[ino, jno]; double e_ryy_1d_inojno = media_P[1, 1] * integralN[ino, jno]; double e_uzz_1d_inojno = media_Q[2, 2] * integralN[ino, jno]; //txx_1d[inoSorted, jnoSorted] += e_txx_1d_inojno; //ryy_1d[inoSorted, jnoSorted] += e_ryy_1d_inojno; //uzz_1d[inoSorted, jnoSorted] += e_uzz_1d_inojno; txx_1d._body[txx_1d.GetBufferIndex(inoSorted, jnoSorted)] += e_txx_1d_inojno; ryy_1d._body[ryy_1d.GetBufferIndex(inoSorted, jnoSorted)] += e_ryy_1d_inojno; uzz_1d._body[uzz_1d.GetBufferIndex(inoSorted, jnoSorted)] += e_uzz_1d_inojno; } } }
/// <summary> /// 2次線要素を作成する(固有値問題用) /// </summary> /// <param name="nodes"></param> /// <param name="EdgeToElementNoH"></param> /// <param name="Elements"></param> /// <param name="elements"></param> public static void MkElements( IList<int> nodes, Dictionary<string, IList<int>> EdgeToElementNoH, IList<FemElement> Elements, ref IList<FemLineElement> elements) { // 要素内節点数 const int nno = Constants.LineNodeCnt_SecondOrder; //3; // 2次線要素 // 要素リスト作成 int elemCnt = (nodes.Count - 1) / 2; // 2次線要素 for (int elemIndex = 0; elemIndex < elemCnt; elemIndex++) { // 線要素の要素内節点 // 節点番号はポート上の1D節点番号(1起点の番号) // 1 3 2 // +---+---+ 並びに注意:頂点→内部の点 FemLineElement element = new FemLineElement(); element.No = elemIndex + 1; element.NodeNumbers = new int[nno]; element.NodeNumbers[0] = 2 * elemIndex + 1; element.NodeNumbers[1] = 2 * elemIndex + 2 + 1; element.NodeNumbers[2] = 2 * elemIndex + 1 + 1; element.MediaIndex = 0; elements.Add(element); } // 要素を1辺とする2D領域の要素番号を取得 for (int elemIndex = 0; elemIndex < elements.Count; elemIndex++) { FemElement element = elements[elemIndex]; int[] nodeNumbers = element.NodeNumbers; // 1辺だけ調べればよい(1-3をチェック) int stNodeNumber = nodes[nodeNumbers[0] - 1]; int edNodeNumber = nodes[nodeNumbers[2] - 1]; string edgeKey = ""; if (stNodeNumber < edNodeNumber) { edgeKey = string.Format("{0}_{1}", stNodeNumber, edNodeNumber); } else { edgeKey = string.Format("{0}_{1}", edNodeNumber, stNodeNumber); } if (!EdgeToElementNoH.ContainsKey(edgeKey)) { System.Diagnostics.Debug.WriteLine("logical error: Not find edge {0}", edgeKey); } else { // 隣接する2Dの要素を1つ取得する int elemNo2d = EdgeToElementNoH[edgeKey][0]; FemElement element2d = Elements[elemNo2d - 1]; // 媒質インデックスをセット element.MediaIndex = element2d.MediaIndex; } } }
/// <summary> /// ポート固有値解析 /// </summary> public static void SolvePortWaveguideEigen( FemSolver.WaveModeDV WaveModeDv, double waveLength, int maxModeSpecified, IList <FemNode> Nodes, Dictionary <string, IList <int> > EdgeToElementNoH, IList <FemElement> Elements, MediaInfo[] Medias, Dictionary <int, bool> ForceNodeNumberH, IList <int> portNodes, out int[] nodesBoundary, out MyDoubleMatrix ryy_1d, out Complex[] eigenValues, out Complex[,] eigenVecs) { //System.Diagnostics.Debug.WriteLine("solvePortWaveguideEigen: {0},{1}", waveLength, portNo); nodesBoundary = null; ryy_1d = null; eigenValues = null; eigenVecs = null; // 2D次元数 const int ndim2d = Constants.CoordDim2D; //2; // 波数 double k0 = 2.0 * pi / waveLength; // 角周波数 double omega = k0 * c0; // 節点番号リスト(要素インデックス: 1D節点番号 - 1 要素:2D節点番号) IList <int> nodes = portNodes; // 2D→1D節点番号マップ Dictionary <int, int> to1dNodes = new Dictionary <int, int>(); // 節点座標リスト IList <double> coords = new List <double>(); // 要素リスト IList <FemLineElement> elements = new List <FemLineElement>(); // 1D節点番号リスト(ソート済み) IList <int> sortedNodes = new List <int>(); // 1D節点番号→ソート済みリストインデックスのマップ Dictionary <int, int> toSorted = new Dictionary <int, int>(); // 2Dの要素から次数を取得する Constants.FemElementShapeDV elemShapeDv2d; int order; int vertexCnt2d; FemMeshLogic.GetElementShapeDvAndOrderByElemNodeCnt(Elements[0].NodeNumbers.Length, out elemShapeDv2d, out order, out vertexCnt2d); // 2D→1D節点番号マップ作成 for (int i = 0; i < nodes.Count; i++) { int nodeNumber2d = nodes[i]; if (!to1dNodes.ContainsKey(nodeNumber2d)) { to1dNodes.Add(nodeNumber2d, i + 1); } } // 原点 int nodeNumber0 = nodes[0]; int nodeIndex0 = nodeNumber0 - 1; FemNode node0 = Nodes[nodeIndex0]; double[] coord0 = new double[ndim2d]; coord0[0] = node0.Coord[0]; coord0[1] = node0.Coord[1]; // 座標リスト作成 double[] coord = new double[ndim2d]; foreach (int nodeNumber in nodes) { int nodeIndex = nodeNumber - 1; FemNode node = Nodes[nodeIndex]; coord[0] = node.Coord[0]; coord[1] = node.Coord[1]; double x = FemMeshLogic.GetDistance(coord, coord0); //System.Diagnostics.Debug.WriteLine("{0},{1},{2},{3}", nodeIndex, coord[0], coord[1], x); coords.Add(x); } // 線要素を作成する if (order == Constants.FirstOrder) { // 1次線要素 FemMat_Line_First.MkElements( nodes, EdgeToElementNoH, Elements, ref elements); } else { // 2次線要素 FemMat_Line_Second.MkElements( nodes, EdgeToElementNoH, Elements, ref elements); } // 強制境界節点と内部領域節点を分離 foreach (int nodeNumber2d in nodes) { int nodeNumber = to1dNodes[nodeNumber2d]; if (ForceNodeNumberH.ContainsKey(nodeNumber2d)) { System.Diagnostics.Debug.WriteLine("{0}: {1} {2}", nodeNumber, Nodes[nodeNumber2d - 1].Coord[0], Nodes[nodeNumber2d - 1].Coord[1]); } else { sortedNodes.Add(nodeNumber); toSorted.Add(nodeNumber, sortedNodes.Count - 1); } } // 対称バンド行列のパラメータを取得する int rowcolSize = 0; int subdiaSize = 0; int superdiaSize = 0; { bool[,] matPattern = null; GetMatNonzeroPatternForEigen(elements, toSorted, out matPattern); GetBandMatrixSubDiaSizeAndSuperDiaSizeForEigen(matPattern, out rowcolSize, out subdiaSize, out superdiaSize); } // ソート済み1D節点インデックス→2D節点番号マップ nodesBoundary = new int[sortedNodes.Count]; for (int i = 0; i < sortedNodes.Count; i++) { int nodeNumber = sortedNodes[i]; int nodeIndex = nodeNumber - 1; int nodeNumber2d = nodes[nodeIndex]; nodesBoundary[i] = nodeNumber2d; } // 節点数 int nodeCnt = sortedNodes.Count; // 固有値、固有ベクトル int maxMode = maxModeSpecified; if (maxMode > nodeCnt) { maxMode = nodeCnt; } eigenValues = new Complex[maxMode]; eigenVecs = new Complex[maxMode, nodeCnt]; // 固有モード解析でのみ使用するuzz_1d, txx_1d MyDoubleMatrix txx_1d = new MyDoubleSymmetricBandMatrix(nodeCnt, subdiaSize, superdiaSize); MyDoubleMatrix uzz_1d = new MyDoubleSymmetricBandMatrix(nodeCnt, subdiaSize, superdiaSize); // ryy_1dマトリクス (線要素) ryy_1d = new MyDoubleSymmetricBandMatrix(nodeCnt, subdiaSize, superdiaSize); for (int elemIndex = 0; elemIndex < elements.Count; elemIndex++) { // 線要素 FemLineElement element = elements[elemIndex]; // 1Dヘルムホルツ方程式固有値問題の要素行列を加算する if (order == Constants.FirstOrder) { // 1次線要素 FemMat_Line_First.AddElementMatOf1dEigenValueProblem( waveLength, // E面の場合のみ使用 element, coords, toSorted, Medias, WaveModeDv, ref txx_1d, ref ryy_1d, ref uzz_1d); } else { // 2次線要素 FemMat_Line_Second.AddElementMatOf1dEigenValueProblem( waveLength, // E面の場合のみ使用 element, coords, toSorted, Medias, WaveModeDv, ref txx_1d, ref ryy_1d, ref uzz_1d); } } // [A] = [Txx] - k0 * k0 *[Uzz] //メモリ節約 //MyDoubleMatrix matA = new MyDoubleMatrix(nodeCnt, nodeCnt); MyDoubleSymmetricBandMatrix matA = new MyDoubleSymmetricBandMatrix(nodeCnt, subdiaSize, superdiaSize); for (int ino = 0; ino < nodeCnt; ino++) { for (int jno = 0; jno < nodeCnt; jno++) { // 対称バンド行列対応 if (matA is MyDoubleSymmetricBandMatrix && ino > jno) { continue; } // 剛性行列 //matA[ino, jno] = txx_1d[ino, jno] - (k0 * k0) * uzz_1d[ino, jno]; // 質量行列matBが正定値行列となるように剛性行列matAの方の符号を反転する matA[ino, jno] = -(txx_1d[ino, jno] - (k0 * k0) * uzz_1d[ino, jno]); } } // ( [txx] - k0^2[uzz] + β^2[ryy]){Ez} = {0}より // [A]{x} = λ[B]{x}としたとき、λ = β^2 とすると[B] = -[ryy] //MyDoubleMatrix matB = MyMatrixUtil.product(-1.0, ryy_1d); // 質量行列が正定値となるようにするため、上記符号反転を剛性行列の方に反映し、質量行列はryy_1dをそのまま使用する //MyDoubleMatrix matB = new MyDoubleMatrix(ryy_1d); MyDoubleSymmetricBandMatrix matB = new MyDoubleSymmetricBandMatrix((MyDoubleSymmetricBandMatrix)ryy_1d); // 一般化固有値問題を解く Complex[] evals = null; Complex[,] evecs = null; try { // 固有値、固有ベクトルを求める solveEigen(matA, matB, out evals, out evecs); // 固有値のソート Sort1DEigenMode(k0, evals, evecs); } catch (Exception exception) { System.Diagnostics.Debug.WriteLine(exception.Message + " " + exception.StackTrace); System.Diagnostics.Debug.Assert(false); } for (int imode = 0; imode < evecs.GetLength(0); imode++) { KrdLab.clapack.Complex phaseShift = 1.0; double maxAbs = double.MinValue; KrdLab.clapack.Complex fValueAtMaxAbs = 0.0; { // 境界上で位相調整する for (int ino = 0; ino < evecs.GetLength(1); ino++) { KrdLab.clapack.Complex cvalue = evecs[imode, ino]; double abs = KrdLab.clapack.Complex.Abs(cvalue); if (abs > maxAbs) { maxAbs = abs; fValueAtMaxAbs = cvalue; } } } if (maxAbs >= MyUtilLib.Matrix.Constants.PrecisionLowerLimit) { phaseShift = fValueAtMaxAbs / maxAbs; } //System.Diagnostics.Debug.WriteLine("phaseShift: {0} (°)", Math.Atan2(phaseShift.Imaginary, phaseShift.Real) * 180.0 / pi); for (int ino = 0; ino < evecs.GetLength(1); ino++) { evecs[imode, ino] /= phaseShift; } } for (int imode = 0; imode < maxMode; imode++) { eigenValues[imode] = 0; } for (int tagtModeIdx = evals.Length - 1, imode = 0; tagtModeIdx >= 0 && imode < maxMode; tagtModeIdx--) { // 伝搬定数は固有値のsqrt Complex betam = Complex.Sqrt(evals[tagtModeIdx]); // 定式化BUGFIX // 減衰定数は符号がマイナス(β = -jα) bool isConjugateMode = false; if (betam.Imaginary >= 0.0) { betam = new Complex(betam.Real, -betam.Imaginary); isConjugateMode = true; } // 固有ベクトル Complex[] evec = MyMatrixUtil.matrix_GetRowVec(evecs, tagtModeIdx); if (isConjugateMode) { evec = MyMatrixUtil.vector_Conjugate(evec); } // 規格化定数を求める // 実数の場合 [ryy]*t = [ryy]t ryyは対称行列より[ryy]t = [ryy] Complex[] workVec = MyMatrixUtil.product(ryy_1d, evec); Complex dm = MyMatrixUtil.vector_Dot(MyMatrixUtil.vector_Conjugate(evec), workVec); { // H面、平行平板 if (WaveModeDv == FemSolver.WaveModeDV.TM) { dm = Complex.Sqrt(omega * eps0 / Complex.Abs(betam) / dm); } else { dm = Complex.Sqrt(omega * mu0 / Complex.Abs(betam) / dm); } } //System.Diagnostics.Debug.WriteLine("dm = " + dm); // 伝搬定数の格納 eigenValues[imode] = betam; // check if (imode < 5) { //System.Diagnostics.Debug.WriteLine("eigenValues [ " + imode + "] = " + betam.Real + " + " + betam.Imaginary + " i " + " tagtModeIdx :" + tagtModeIdx + " " ); System.Diagnostics.Debug.WriteLine("β/k0 [ " + imode + "] = " + betam.Real / k0 + " + " + betam.Imaginary / k0 + " i " + " tagtModeIdx :" + tagtModeIdx + " "); } // 固有ベクトルの格納(規格化定数を掛ける) for (int inoSorted = 0; inoSorted < nodeCnt; inoSorted++) { Complex fm = dm * evec[inoSorted]; eigenVecs[imode, inoSorted] = fm; //System.Diagnostics.Debug.WriteLine("eigenVecs [ " + imode + ", " + inoSorted + "] = " + fm.Real + " + " + fm.Imaginary + " i Abs:" + Complex.Abs(fm)); } imode++; } }
/// <summary> /// 1Dヘルムホルツ方程式固有値問題の要素行列を加算する /// </summary> /// <param name="waveLength">波長(E面の場合のみ使用する)</param> /// <param name="element">線要素</param> /// <param name="coords">座標リスト</param> /// <param name="toSorted">節点番号→ソート済み節点インデックスマップ</param> /// <param name="Medias">媒質情報リスト</param> /// <param name="WaveModeDv">計算する波のモード区分</param> /// <param name="txx_1d">txx行列</param> /// <param name="ryy_1d">ryy行列</param> /// <param name="uzz_1d">uzz行列</param> public static void AddElementMatOf1dEigenValueProblem( double waveLength, FemLineElement element, IList <double> coords, Dictionary <int, int> toSorted, MediaInfo[] Medias, FemSolver.WaveModeDV WaveModeDv, ref MyDoubleMatrix txx_1d, ref MyDoubleMatrix ryy_1d, ref MyDoubleMatrix uzz_1d) { // 定数 const double pi = Constants.pi; const double c0 = Constants.c0; // 波数 double k0 = 2.0 * pi / waveLength; // 角周波数 double omega = k0 * c0; // 2次線要素 const int nno = Constants.LineNodeCnt_SecondOrder; // 3; int[] nodeNumbers = element.NodeNumbers; System.Diagnostics.Debug.Assert(nno == nodeNumbers.Length); // 座標の取得 double[] elementCoords = new double[nno]; for (int n = 0; n < nno; n++) { int nodeIndex = nodeNumbers[n] - 1; elementCoords[n] = coords[nodeIndex]; } // 線要素の長さ double elen = Math.Abs(elementCoords[1] - elementCoords[0]); // 媒質インデックス int mediaIndex = element.MediaIndex; // 媒質 MediaInfo media = Medias[mediaIndex]; double[,] media_P = null; double[,] media_Q = null; // ヘルムホルツ方程式のパラメータP,Qを取得する FemSolver.GetHelmholtzMediaPQ( k0, media, WaveModeDv, out media_P, out media_Q); double[,] integralN = new double[nno, nno] { { 4.0 / 30.0 * elen, -1.0 / 30.0 * elen, 2.0 / 30.0 * elen }, { -1.0 / 30.0 * elen, 4.0 / 30.0 * elen, 2.0 / 30.0 * elen }, { 2.0 / 30.0 * elen, 2.0 / 30.0 * elen, 16.0 / 30.0 * elen }, }; double[,] integralDNDY = new double[nno, nno] { { 7.0 / (3.0 * elen), 1.0 / (3.0 * elen), -8.0 / (3.0 * elen) }, { 1.0 / (3.0 * elen), 7.0 / (3.0 * elen), -8.0 / (3.0 * elen) }, { -8.0 / (3.0 * elen), -8.0 / (3.0 * elen), 16.0 / (3.0 * elen) }, }; for (int ino = 0; ino < nno; ino++) { int inoBoundary = nodeNumbers[ino]; int inoSorted; if (!toSorted.ContainsKey(inoBoundary)) { continue; } inoSorted = toSorted[inoBoundary]; for (int jno = 0; jno < nno; jno++) { int jnoBoundary = nodeNumbers[jno]; int jnoSorted; if (!toSorted.ContainsKey(jnoBoundary)) { continue; } jnoSorted = toSorted[jnoBoundary]; // 対称バンド行列対応 if (ryy_1d is MyDoubleSymmetricBandMatrix && jnoSorted < inoSorted) { continue; } double e_txx_1d_inojno = media_P[0, 0] * integralDNDY[ino, jno]; double e_ryy_1d_inojno = media_P[1, 1] * integralN[ino, jno]; double e_uzz_1d_inojno = media_Q[2, 2] * integralN[ino, jno]; //txx_1d[inoSorted, jnoSorted] += e_txx_1d_inojno; //ryy_1d[inoSorted, jnoSorted] += e_ryy_1d_inojno; //uzz_1d[inoSorted, jnoSorted] += e_uzz_1d_inojno; txx_1d._body[txx_1d.GetBufferIndex(inoSorted, jnoSorted)] += e_txx_1d_inojno; ryy_1d._body[ryy_1d.GetBufferIndex(inoSorted, jnoSorted)] += e_ryy_1d_inojno; uzz_1d._body[uzz_1d.GetBufferIndex(inoSorted, jnoSorted)] += e_uzz_1d_inojno; } } }