Pythonで立体骨組構造解析

# 大きな修正
+ (2017.05.18) 座標変換行列修正（部材軸が全体座標系Z軸と平行な場合とそうでない場合に場合分け）
+ (2018.08.08) 最新版プログラムはこちら：[Python：FEMでの疎行列計算利用による高速化（３次元骨組構造解析での事例）](https://qiita.com/damyarou/items/85c7902cc658d748115e)
+ (2018.09.17) 最新版プログラム対応で記事の内容を修正．

# 概要
前から欲しかった立体骨組構造解析プログラムをPythonで作ってみました．
以前に作成した平面骨組解析プログラムにおいて，主に，剛性行列，座標変換行列を変更し，１節点６自由度の梁要素対応にしたものです．

~~まだテストが十分ではありませんが，~~ 下の記事に刺激されて投稿してみました．
[http://qiita.com/sasaco/items/917429a497c6e9a08401](http://qiita.com/sasaco/items/917429a497c6e9a08401)

~~プログラム作成は，Jupyter上で行っていますので，この記事も自分のJupyterの記載に沿って記述しています．
Pythonによる解析プログラムは３分割してあります．~~ 
いつも気になるのですが，ファイル書き出しの部分がごちゃごちゃしすぎている．もっとうまく書けないものか．．．

~~計算はいちおう回っています．．．~~ 

プログラミング環境は
Machine: MacBook Pro (Retina, 13-inch, Mid 2014)
OS: macOS Sierra
Python version: Python 3.6.1



# 立体骨組構造解析の理論
## 基本事項
理論展開およびプログラムには，以下の条件が入っています．

+ 線形弾性材料の微小変形を仮定している
+ 物体力として慣性力を考慮する
+ 初期ひずみとして温度変化によるひずみを考慮する

このプログラムでは，モデルが大規模になることを想定し，連立１次方程式を解く際，疎行列処理を導入しています．



## 要素剛性方程式
要素剛性方程式の一般形は，２次元応力解析におけるものと同一である．


### 変位を求めるための方程式

```math
\begin{equation}
[\boldsymbol{k}]\{\boldsymbol{u_{nd}}\}=\{\boldsymbol{f}\}+\{\boldsymbol{f_t}\}+\{\boldsymbol{f_b}\}
\end{equation}
```

```math
\begin{align}
&[\boldsymbol{k}]=\int_V[\boldsymbol{B}]^T[\boldsymbol{D_e}][\boldsymbol{B}]dV  & & \text{（要素剛性行列）}\\
&\{\boldsymbol{f}\}=\int_A [\boldsymbol{N}]^T\{\boldsymbol{S}\}dA  & & \text{（節点外力ベクトル）}\\
&\{\boldsymbol{f_t}\}=\int_V[\boldsymbol{B}]^T[\boldsymbol{D_e}]\{\boldsymbol{\epsilon_0}\}dV  & & \text{（節点初期歪ベクトル）}\\
&\{\boldsymbol{f_b}\}=\gamma\left(\int_V [\boldsymbol{N}]^T [\boldsymbol{N}]dV\right)\{\boldsymbol{w_{nd}}\} & & \text{（節点慣性力ベクトル）}
\end{align}
```

### 要素内部応力計算式
```math
\begin{equation}
\{\boldsymbol{\sigma}\}=[\boldsymbol{D_e}]\{\boldsymbol{\epsilon}-\boldsymbol{\epsilon_0}\}
\end{equation}
```

```math
\begin{align}
&\{\boldsymbol{\epsilon}\}  & &\text{：剛性方程式を解いて求めた変位から求めた要素のひずみベクトル}\\
&\{\boldsymbol{\epsilon_0}\} & &\text{：温度ひずみなどの初期ひずみ}\\
&[\boldsymbol{D_e}]          & &\text{：応力ーひずみ関係行列}
\end{align}
```

## 立体骨組構造の要素剛性マトリックス
立体骨組構造の要素剛性マトリックスは 12 x 12 の正方行列であり，
これはねじれを考慮すること以外は平面骨組に対するものを拡張することにより導くことができるため，
導出過程は省略し，ここでは結果のみを記載する．


### 要素剛性方程式（最も単純な表現）
```math
\begin{equation}
\boldsymbol{\{f\}}=\boldsymbol{[k]}\boldsymbol{\{u\}}
\end{equation}
```

### 節点変位ベクトル
```math
\begin{equation}
\boldsymbol{\{u\}}=
\begin{Bmatrix}
u_i & v_i & w_i & \theta_{xi} & \theta_{yi} & \theta_{zi} & u_j & v_j & w_j & \theta_{xj} & \theta_{yj} & \theta_{zj}
\end{Bmatrix}^T
\end{equation}
```

```math
\begin{align}
&u & &\text{：x軸方向変位} & &\theta_x& &\text{：x軸回り回転量（ねじり角）}\\
&v & &\text{：y軸方向変位} & &\theta_y& &\text{：y軸回り回転量}\\
&w & &\text{：z軸方向変位} & &\theta_z& &\text{：z軸回り回転量}
\end{align}
```

### 節点力ベクトル
```math
\begin{equation}
\boldsymbol{\{f\}}=
\begin{Bmatrix}
N_i & S_{yi} & S_{zi} & M_{xi} & M_{yi} & M_{zi} & N_j & S_{yj} & S_{zj} & M_{xj} & M_{yj} & M_{zj}
\end{Bmatrix}^T
\end{equation}
```

```math
\begin{align}
&N  & &\text{：軸力（x軸方向力）} & &M_x& &\text{：x軸回りモーメント（ねじりモーメント）}\\
&S_y& &\text{：y軸方向せん断力}   & &M_y& &\text{：y軸回り曲げモーメント}\\
&S_z& &\text{：z軸方向せん断力}   & &M_z& &\text{：z軸回り曲げモーメント}
\end{align}
```

### 要素剛性行列
```math
\begin{equation}
\boldsymbol{[k]}=
\begin{bmatrix}
\cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 & -\cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 \\
0 & \cfrac{12EI_z}{L^3} & 0 & 0 & 0 & \cfrac{6EI_z}{L^2} & 0 & -\cfrac{12EI_z}{L^3} & 0 & 0 & 0 & \cfrac{6EI_z}{L^2} \\
0 & 0 & \cfrac{12EI_y}{L^3} & 0 & -\cfrac{6EI_y}{L^2} & 0 & 0 & 0 & -\cfrac{12EI_y}{L^3} & 0 & -\cfrac{6EI_y}{L^2} & 0 \\
0 & 0 & 0 & \cfrac{GJ}{L} & 0 & 0 & 0 & 0 & 0 & -\cfrac{GJ}{L} & 0 & 0 \\
0 & 0 & -\cfrac{6EI_y}{L^2} & 0 & \cfrac{4EI_y}{L} & 0 & 0 & 0 & \cfrac{6EI_y}{L^2} & 0 & \cfrac{2EI_y}{L} & 0 \\
0 & \cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{4EI_z}{L} & 0 & -\cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{2EI_z}{L} \\
-\cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 & \cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 \\
0 & -\cfrac{12EI_z}{L^3} & 0 & 0 & 0 & -\cfrac{6EI_z}{L^2} & 0 & \cfrac{12EI_z}{L^3} & 0 & 0 & 0 & -\cfrac{6EI_z}{L^2} \\
0 & 0 & -\cfrac{12EI_y}{L^3} & 0 & \cfrac{6EI_y}{L^2} & 0 & 0 & 0 & \cfrac{12EI_y}{L^3} & 0 & \cfrac{6EI_y}{L^2} & 0 \\
0 & 0 & 0 & -\cfrac{GJ}{L} & 0 & 0 & 0 & 0 & 0 & \cfrac{GJ}{L} & 0 & 0 \\
0 & 0 & -\cfrac{6EI_y}{L^2} & 0 & \cfrac{2EI_y}{L} & 0 & 0 & 0 & \cfrac{6EI_y}{L^2} & 0 & \cfrac{4EI_y}{L} & 0 \\
0 & \cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{2EI_z}{L} & 0 & -\cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{4EI_z}{L} \\
\end{bmatrix}
\end{equation}
```

```math
\begin{align}
&L& &\text{：要素長}        & &J& &\text{：ねじり定数}\\
&E& &\text{：弾性係数}      & &I_y& &\text{：y軸回り断面２次モーメント}\\
&G& &\text{：せん断弾性係数}& &I_z& &\text{：z軸回り断面２次モーメント}\\
&A& &\text{：断面積}        & &
\end{align}
```

## 立体骨組要素の節点慣性力ベクトル
物体力として慣性力を考え，節点ベクトルとして「加速度」を与える．
ここでは，最も単純な方法として，要素に作用する慣性力を単純に節点力に配分する．
要素に作用する全慣性力は，全体座標系での値として以下のとおりとなる．

```math
\begin{align}
F_{m(x)}=&\frac{\gamma\cdot A \cdot L}{g}\cdot (k_x \cdot g) \\
F_{m(y)}=&\frac{\gamma\cdot A \cdot L}{g}\cdot (k_y \cdot g) \\
F_{m(z)}=&\frac{\gamma\cdot A \cdot L}{g}\cdot (k_z \cdot g) \\
\end{align}
```

よって，以下のように慣性力による節点力 $\{\boldsymbol{f_{b}}\}$ を定めることができる．

```math
\begin{equation*}
\{\boldsymbol{F_{b}}\}=\{\boldsymbol{f_{b}}\}=\frac{\gamma\cdot A \cdot L}{2}
\begin{Bmatrix}
k_x \\
k_y \\
k_z \\
0   \\
0   \\
0   \\
k_x \\
k_y \\
k_z \\
0   \\
0   \\
0
\end{Bmatrix}
\end{equation*}
```

ここに，$\gamma$ は要素の単位体積重量，$A$ は要素断面積，$L$ は要素長さ，$g$ は重力加速度である．



## 立体骨組要素の節点温度荷重ベクトル
温度変化は部材の軸力のみに影響するとする．
ここで温度上昇を正の温度変化とすれば，温度変化による部材座標系での節点力ベクトルは以下のとおりとなる．

```math
\begin{equation*}
\{\boldsymbol{f_{t}}\}=EA\cdot\alpha\cdot\Delta T
\begin{Bmatrix}
-1 \\
 0 \\
 0 \\
 0 \\
 0 \\
 0 \\
 1 \\
 0 \\
 0 \\
 0 \\
 0 \\
 0
\end{Bmatrix}
\end{equation*}
```

ここに，$EA$ は部材の軸剛性，$\alpha$ は部材の線膨張係数，$\Delta T$ は部材の温度変化量である．

これを座標変換することにより，全体座標系での節点力ベクトルが定まる．

```math
\begin{equation*}
\{\boldsymbol{F_t}\}=[\boldsymbol{T}]^T\{\boldsymbol{f_t}\}
\end{equation*}
```

ここに $[\boldsymbol{T}]^T$ は座標変換マトリックス $[\boldsymbol{T}]$ の転置マトリックス（この場合は逆行列に等しい）である．



## 立体骨組要素の節点外力ベクトル
節点外力ベクトルについては，ここで紹介するプログラムでは，直接，等価節点外力を入力データファイルから入力することとしており，特別な計算処理は行っていない．また荷重は，全体座標系における水平方向力，鉛直方向力，モーメントとしている．



## 座標変換マトリックス
これまでに求めた要素剛性方程式は，要素に固定された「要素座標系」でのものである．
実際の構造物では要素は色々な方向を向いているので，統一的に定義した「全体座標系」との関係を求めておく必要がある．

ここで，全体座標系変位を要素座標系変位に変換する座標変換マトリックスを $[\boldsymbol{T}]$ とおくと，
全体座標系での剛性マトリックス $[\boldsymbol{K}]$ と要素座標系での剛性マトリックス $[\boldsymbol{k}]$ には次の関係がある．

```math
\begin{equation}
[\boldsymbol{K}]=[\boldsymbol{T}]^T[\boldsymbol{k}][\boldsymbol{T}]
\end{equation}
```

この関係は，要素座標系変位 $\{\boldsymbol{u}\}$ と全体座標系変位 $\{\boldsymbol{U}\}$ の関係が，以下の通りとなっていることより理解できる．

```math
\begin{equation}
\{\boldsymbol{u}\}=[\boldsymbol{T}]\{\boldsymbol{U}\} \qquad\qquad \{\boldsymbol{u}\}^T=\{\boldsymbol{U}\}^T [\boldsymbol{T}]^T
\end{equation}
```

ここに $[\boldsymbol{T}]$ は，全体座標系変位・節点力を要素座標系変位・節点力に変換する座標変換マトリックスである．


### 座標変換マトリックスの具体的表現
#### 部材軸（ｘ軸）の方向余弦
要素を構成する節点を $i$ および $j$ とし，その座標を $(x_i,y_i,z_i)$，$(x_j,y_j,z_j)$とする．

```math
\begin{gather}
l=\cfrac{x_j-x_i}{\ell} \qquad m=\cfrac{y_j-y_i}{\ell} \qquad n=\cfrac{z_j-z_i}{\ell} \\
\ell=\sqrt{(x_j-x_i)^2+(y_j-y_i)^2+(z_j-z_i)^2}
\end{gather}
```

#### 座標変換小行列
$\boldsymbol{[t]}$ は，全体座標系を部材座標系に変換する座標変換行列 (12 x 12) の小行列 (3 x 3)．

```math
\begin{equation}
\boldsymbol{\{x\}}=\boldsymbol{[t]}\boldsymbol{\{X\}}
\end{equation}
```

```math
\begin{align}
&\boldsymbol{\{x\}}=\{x,y,z\}^T  & &\text{：部材座標（local coordinate）}\\
&\boldsymbol{\{X\}}＝\{X,Y,Z\}^T & &\text{：全体座標（global coordinate）}
\end{align}
```

+ 部材長軸（部材座標系x軸）が全体座標系Z軸と平行でない場合

```math
\begin{equation}
\boldsymbol{[t]}=
\begin{bmatrix}
1 & 0 & 0 \\
0 & \cos\theta & \sin\theta \\
0 & -\sin\theta & \cos\theta
\end{bmatrix}
\cdot
\begin{bmatrix}
l & m & n \\
-\cfrac{m}{\sqrt{l^2+m^2}} & \cfrac{l}{\sqrt{l^2+m^2}} & 0 \\
-\cfrac{l \cdot n}{\sqrt{l^2+m^2}} & -\cfrac{m \cdot n}{\sqrt{l^2+m^2}} & \sqrt{l^2+m^2}
\end{bmatrix}
\qquad \text{($\theta$ : chord angle)}
\end{equation}
```

+ 部材長軸（部材座標系x軸）が全体座標系Z軸と平行な場合<br>
この時，$x_j-x_i=0$，$y_j-y_i=0$ であり，$l=0$，$m=0$ であることから，$\boldsymbol{[t]}$を書き直す必要がある．

```math
\begin{equation}
\boldsymbol{[t]}=
\begin{bmatrix}
1 & 0 & 0 \\
0 & \cos\theta & \sin\theta \\
0 & -\sin\theta & \cos\theta
\end{bmatrix}
\cdot
\begin{bmatrix}
0 & 0 & n \\
n & 0 & 0 \\
0 & 1 & 0
\end{bmatrix}
\qquad \text{($\theta$ : chord angle)}
\end{equation}
```

#### 座標変換行列
```math
\begin{equation}
\boldsymbol{[T]}=
\begin{bmatrix}
\boldsymbol{[t]} & \boldsymbol{0}   & \boldsymbol{0}   & \boldsymbol{0} \\\
\boldsymbol{0}   & \boldsymbol{[t]} & \boldsymbol{0}   & \boldsymbol{0} \\\
\boldsymbol{0}   & \boldsymbol{0}   & \boldsymbol{[t]} & \boldsymbol{0} \\\
\boldsymbol{0}   & \boldsymbol{0}   & \boldsymbol{0}   &\boldsymbol{[t]}
\end{bmatrix}
\end{equation}
```

#### コードアングルが０となる部材座標系（x-y-z）と全体座標系（X-Y-Z）の関係
<img src="https://qiita-image-store.s3.amazonaws.com/0/129300/89da7161-71e3-a2d9-c9ee-09ccd6b0d2fb.png" width=50%>


## 全体剛性方程式
以上より，全体座標系での剛性方程式は，以下のとおり表現される．

```math
\begin{equation}
[\boldsymbol{K}]\{\boldsymbol{U}\}=\{\boldsymbol{F}\}+\{\boldsymbol{F_b}\}+\{\boldsymbol{F_t}\}
\end{equation}
```

```math
\begin{align}
&\{\boldsymbol{U}\}                          & & \text{全体座標系での節点変位ベクトル}\\
&\{\boldsymbol{F}\}                          & & \text{全体座標系での節点外力ベクトル}\\
&\{\boldsymbol{F_b}\}=\{\boldsymbol{f_b}\}   & & \text{全体座標系での節点慣性力ベクトル}\\
&\{\boldsymbol{F_t}\}=[\boldsymbol{T}]^T\{\boldsymbol{f_t}\}  & & \text{全体座標系での節点温度荷重ベクトル}\\
&[\boldsymbol{K}]=[\boldsymbol{T}]^T[\boldsymbol{k}][\boldsymbol{T}]   & & \text{全体座標系での剛性マトリックス}\\
&[\boldsymbol{k}]                            & & \text{要素座標系での剛性マトリックス}\\
&\{\boldsymbol{f_b}\}                          & & \text{要素座標系での節点慣性力ベクトル}\\
&\{\boldsymbol{f_t}\}                          & & \text{要素座標系での節点温度荷重ベクトル}\\
&[\boldsymbol{T}]                            & & \text{座標変換行列}
\end{align}
```

上式を解くことにより，全体座標系での節点変位 $\{\boldsymbol{U}\}$ を求めることができる．



## 要素断面力
要素断面力は，上記で得られた全体座標系変位 $\{\boldsymbol{U}\}$ を，座標変換マトリックス $[\boldsymbol{T}]$ により要素座標系変位に変換し，これに要素剛性マトリックス $[\boldsymbol{k}]$ を乗じて算定する．すなわち，要素断面力 $\{\boldsymbol{f_{sec}}\}$ は，以下により求める．

```math
\begin{equation}
\{\boldsymbol{f_{sec}}\}=[\boldsymbol{k}]\cdot ([\boldsymbol{T}]\{\boldsymbol{U}\})
\end{equation}
```

上記で計算した計算した軸力は，温度変化による効果が含まれていないため，以下のように軸力の温度効果補正を行う必要が有ることに注意する．

```math
\begin{align}
N_1'=&N_1+EA\cdot\alpha\cdot\Delta T \\
N_2'=&N_2-EA\cdot\alpha\cdot\Delta T
\end{align}
```

ここに，$N_1'$，$N_2'$ は温度変化を考慮した節点 1 および 2 での軸力，$N_1$，$N_2$ は連立方程式の解としての変位より計算した軸力，
$EA$ は部材の軸剛性，$\alpha$ は部材の線膨張係数，$\Delta T$ は部材の温度変化量である．



# 立体骨組構造解析プログラム
## プログラムの概要

+ プログラミング言語はPythonとする
+ 立体骨組弾性解析を行う
+ 要素は，１要素２節点，１節点６自由度の梁要素とする
+ 全体座標システムは，水平面平面上の直行座標系右方向を x 軸正方向，上方向を y 軸正方向とする
+ 荷重として，節点外力，節点強制変位（ゼロ変位含む），節点温度変化，要素への慣性力を扱える
+ 外力は，節点に作用するもののみ扱える．要素中間への荷重は扱えない
+ 連立一次方程式は，その大きさ（１節点自由度ｘ総節点数）を変えずに，節点強制変位導入による処理を行い解いている
+ 連立一次方程式は，CSR 変換後，<var>scipy.sparse.linalg.spsolve()</var> により解く
+ 出力は，節点変位および要素断面力とする
+ 入力データファイル，出力データファイルの文字区切りは空白とする
+ 入力データファイル名，出力データファイル名は，コマンドライン引数として入力する


## 扱える境界条件の説明
<table>
<tr><th>項目</th><th>説明</th></tr>
<tr><td>節点外力    </td><td>載荷節点数，積荷節点番号，荷重値を指定</td></tr>
<tr><td>節点温度変化</td><td>全節点の温度変化量を指定．温度上昇を正値とする</td></tr>
<tr><td>要素慣性力  </td><td>材料特性データの中で x / y / z方向の加速度を重力加速度に対する比で指定</td></tr>
<tr><td>節点強制変位</td><td>強制変位を与える節点数，節点番号，変位値を指定</td></tr>
</table>


## プログラムの構成（プログラム名：py_fem_3dfrmx.py）
```
   1. モジュール読み込み
   2. 関数：データ入力 (def inpdata_3dfrm)
       (1) 基本数値読み込み
       (2) 配列宣言
       (3) 材料特性データ読み込み
       (4) 要素構成節点番号および材料番号読み込み
       (5) 節点座標および節点温度変化量読み込み
       (6) 境界条件読み込み
       (7) 節点荷重データ読み込み
   3. 関数：入力データ書き出し (def prinp_3dfrm)
   4. 関数：計算結果書き出し (def prout_3dfrm)
   5. 関数：要素剛性マトリックス作成 (def sm_3dfrm)
   6. 関数：座標変換マトリックス作成 (def tm_3dfrm)
   7. 関数：節点慣性力ベクトル作成 (def bfvec_3dfrm)
   8. 関数：節点温度荷重ベクトル作成 (def tfvec_3dfrm)
   9. 関数：要素断面力計算 (def calsecf_3dfrm)
  10. 関数：メインルーチン (def main_3dfrm)
       (1) コマンドラインより入出力ファイル名取得
       (2) データ入力
       (3) 入力データ書き出し
       (4) 剛性マトリックス組み立て
       (5) 境界条件処理（変位既知自由度の処理）
       (6) 剛性方程式を解く（変位計算）
       (7) 境界条件処理（既知変位の組み込み）
       (8) 要素断面力計算
       (9) 計算結果書き出し
       (10) 計算時間等情報表示
  11. メインルーチン実行
```


## 実行コマンドと入出力データ
### 解析実行コマンド

```
python3 py_fem_3dfrmx.py inp.txt out.txt
```

<table>
<tr><th>項目</th><th>説明</th></tr>
<tr><td><var>py_fem_3dfrmx.py</var> </td><td> Python による FEM プログラム名 </td></tr>
<tr><td><var>inp.txt</var>         </td><td> 入力データファイル名（空白区切りデータ）</td></tr>
<tr><td><var>out.txt</var>         </td><td> 出力データファイル名（空白区切りデータ） </td></tr>
</table>

### 入力データファイルフォーマット
```
npoin  nele  nsec  npfix  nlod                   # Basic values for analysis
E  po  A  Ix  Iy  Iz  theta  alpha  gamma  gkX  gkY  gkZ
    ..... (1 to nsec) .....                      # Material properties
node_1  node_2  isec                             # Element connectivity, material set number
    ..... (1 to nele) .....                      #
x  y  z  deltaT                                  # Node coordinate, temperature change of node
    ..... (1 to npoin) .....                     #
lp  kox  koy  koz  kmx  kmy  kmz  rdis_x  rdis_y  rdis_z  rrot_x  rrot_y  rrot_z
    ..... (1 to npfix) .....                     # Restricted node and restrict conditions
lp  fp_x  fp_y  fp_z  mp_x  mp_y  mp_z           # Loaded node and loading conditions
    ..... (1 to nlod) .....                      #     (omit data input if nlod=0)
```

<table>
<tr><th>項目</th><th>説明</th></tr>
<tr><td><var>npoin, nele, nsec</var>    </td><td> 節点数，要素数，断面特性数 </td></tr>
<tr><td><var>npfix, nlod</var>          </td><td> 拘束節点数，載荷節点数 </td></tr>
<tr><td><var>E, po, A</var>             </td><td> 要素弾性係数，要素ポアソン比，要素断面積 </td></tr>
<tr><td><var>Ix, Iy, Iz</var>           </td><td> ねじり定数，y軸周り断面２次モーメント，z軸周り断面２次モーメント </td></tr>
<tr><td><var>theta, alpha</var>         </td><td> コードアングル，要素線膨張係数 </td></tr>
<tr><td><var>gamma, gkX, gkY, gkZ</var> </td><td> 単位体積重量，x / y / z方向加速度（gの比） </td></tr>
<tr><td><var>node_1, node_2, isec</var> </td><td> 節点１，節点２，断面特性番号 </td></tr>
<tr><td><var>x, y, z, deltaT</var>         </td><td> 節点x座標，節点y座標，節点z座標，節点温度変化 </td></tr>
<tr><td><var>lp, kox, koy, koz</var></td><td> 拘束節点番号，x / y / z 方向変位拘束の有無（拘束: 1，自由: 0） </td></tr>
<tr><td><var>kmx, kmy, kmz</var></td><td> x / y / z 軸周り回転拘束の有無（拘束: 1，自由: 0） </td></tr>
<tr><td><var>r_disx, r_disy, rdis_z</var> </td><td> x / y / z方向既知変位（無拘束でも0を入力） </td></tr>
<tr><td><var>rrot_x, rrot_y, rrot_z</var> </td><td> x / y / z軸周り既知回転量（無拘束でも0を入力） </td></tr>
<tr><td><var>lp, fp_x, fp_y, fp_z</var>   </td><td> 載荷重節点番号，x方向荷重，y方向荷重，z方向荷重 </td></tr>
<tr><td><var>mp_x, mp_y, mp_z</var>   </td><td> x軸周りモーメント（ねじり），y軸周りモーメント，z軸周りモーメント </td></tr>
</table>

### 出力データファイルフォーマット
```
npoin  nele  nsec npfix  nlod
    (Each value of above)
sec  E      po     A    J    Iy    Iz    theta
sec  alpha  gamma  gkX  gkY  gkZ
    sec   : Material number
    E     : Elastic modulus
    po    : Poisson's ratio
    A     : Section area
    J     ; Torsional constant
    Iy    : Moment of inertia around y-axis
    Iz    : Moment of inertia around z-axis
    theta : Chord angle
    alpha : Thermal expansion coefficient
    gamma : Unit weight
    gkX   : Acceleration in X-direction (ratio to g)
    gkY   : Acceleration in Y-direction (ratio to g)
    gkZ   : Acceleration in Z-direction (ratio to g)
    ..... (1 to nsec) .....
node   x   y   z   fx   fy   fz   mx   my   mz  deltaT
    node   : Node number
    x      : x-coordinate
    y      : y-coordinate
    fx     : Load in X-direction
    fy     : Load in Y-direction
    fz     : Load in Z-direction
    mx     : Moment load around X-axis
    my     : Moment load around Y-axis
    mz     : Moment load around Z-axis
    deltaT : Temperature change of node
    ..... (1 to npoin) .....
node   kox   koy   koz   kmx   kmy   kmz   rdis_x   rdis_y   rdis_z   rrot_x   rrot_y   rrot_z
    node    : Node number
    kox     : Index of restriction in X-direction (0: free, 1: fixed)
    koy     : Index of restriction in Y-direction (0: free, 1: fixed)
    koz     : Index of restriction in Z-direction (0: free, 1: fixed)
    kmx     : Index of restriction in rotation around X-axis  (0: free, 1: fixed)
    kmy     : Index of restriction in rotation around Y-axis  (0: free, 1: fixed)
    kmz     : Index of restriction in rotation around Z-axis  (0: free, 1: fixed)
    rdis_x  : Given displacement in X-direction
    rdis_y  : Given displacement in Y-direction
    rdis_z  : Given displacement in Z-direction
    rrot_x  : Given displacement in rotation around X-axis
    rrot_y  : Given displacement in rotation around Y-axis
    rrot_z  : Given displacement in rotation around Z-axis
    ..... (1 to npfix) .....
elem     i     j   sec
    elem : Element number
    i    : Node number of start point
    j    : Node number of end point
    sec  : Material number
    ..... (1 to nele) .....
node   dis-x   dis-y   dis-z   rot-x   rot-y   rot-z
    node  : Node number
    dis-x : Displacement in X-direction
    dis-y : Displacement in Y-direction
    dis-z : Displacement in Z-direction
    rot-x : Rotation around X-axis
    rot-y : Rotation around Y-axis
    rot-z : Rotation around Z-axis
    ..... (1 to npoin) .....
elem nodei   N_i   Sy_i   Sz_i   Mx_i   My_i   Mz_i
elem nodej   N_j   Sy_j   Sz_j   Mx_j   My_j   Mz_j
    elem  : Element number
    nodei : First node nymber
    nodej : Second node number
    N_i   : Axial force of node-i
    Sy_i  : Shear force of node-i in y-direction
    Sz_i  : Shear force of node-i in z-direction
    Mx_i  : Moment of node-i around x-axis (torsional moment)
    My_i  : Moment of node-i around y-axis (bending moment)
    Mz_i  : Moment of node-i around z-axis (bending moment)
    N_j   : Axial force of node-j
    Sy_j  : Shear force of node-j in y-direction
    Sz_j  : Shear force of node-j in z-direction
    Mx_j  : Moment of node-j around x-axis (torsional moment)
    My_j  : Moment of node-j around y-axis (bending moment)
    Mz_j  : Moment of node-j around z-axis (bending moment)
    ..... (1 to nele) .....
n=(total degrees of freedom)  time=(calculation time)
```

<table>
<tr><th>項目</th><th>説明</th></tr>
<tr><td><var>dis-x, dis-y, dis-z</var> </td><td> x方向変位，y方向変位，z方向変位 </td></tr>
<tr><td><var>rot-x, rot-y, rot-z</var> </td><td> x軸周り回転量，y軸周り回転量，z軸周り回転量 </td></tr>
<tr><td><var>N, Sy, Sz</var>           </td><td> 軸力，y方向せん断力，z方向せん断力 </td></tr>
<tr><td><var>Mx, My, Mz</var>          </td><td> x軸周りモーメント（ねじり），y軸周り曲げモーメント，z軸周り曲げモーメント </td></tr>
</table>


## プログラミング
プログラム本体を，「プログラムの構成」に沿って以下に示します．


### 1. モジュール読み込み

+ numpy：配列演算用
+ scipy.sparse.linalg.spsolve：疎行列連立一次方程式解法
+ scipy.sparse import csr_matrix：疎行列圧縮格納
+ sys：コマンドライン引数使用
+ time：計算時間計測用

```py
# ======================
# 3D Frame Analysis
# ======================
import numpy as np
from scipy.sparse.linalg import spsolve
from scipy.sparse import csr_matrix
import sys
import time
```

### 2. 関数：データ入力 (def inpdata_3dfrm)

+ 基本数値読み込み（<var>npoin, nele, nsec, npfix, nlod</var>）
+ 配列宣言（<var>ae, node, x, deltaT, mpfix, rdis, fp</var>）
+ 材料特性データ読み込み（配列 <var>ae</var>）
+ 要素構成節点番号および材料番号読み込み（配列 <var>node</var>）
+ 節点座標および節点温度変化量読み込み（配列 <var>x, deltaT</var>）
+ 境界条件読み込み（配列 <var>mpfix, rdis</var>）
+ 節点荷重データ読み込み（配列 <var>fp</var>）

```py
def inpdata_3dfrm(fnameR,nod,nfree):
    f=open(fnameR,'r')
    text=f.readline()
    text=text.strip()
    text=text.split()
    npoin=int(text[0]) # Number of nodes
    nele =int(text[1]) # Number of elements
    nsec =int(text[2]) # Number of sections
    npfix=int(text[3]) # Number of restricted nodes
    nlod =int(text[4]) # Number of loaded nodes
    # array declaration
    ae    =np.zeros((12,nsec),dtype=np.float64)      # Section characteristics
    node  =np.zeros((nod+1,nele),dtype=np.int)       # node-element relationship
    x     =np.zeros((3,npoin),dtype=np.float64)      # Coordinates of nodes
    deltaT=np.zeros(npoin,dtype=np.float64)          # Temperature change of nodes
    mpfix =np.zeros((nfree,npoin),dtype=np.int)      # Ristrict conditions
    rdis  =np.zeros((nfree,npoin),dtype=np.float64)  # Ristricted displacement
    fp    =np.zeros(nfree*npoin,dtype=np.float64)    # External force vector
    # section characteristics
    for i in range(0,nsec):
        text=f.readline()
        text=text.strip()
        text=text.split()
        ae[0,i] =float(text[0])  # E     : elastic modulus
        ae[1,i] =float(text[1])  # po    : Poisson's ratio
        ae[2,i] =float(text[2])  # aa    : section area
        ae[3,i] =float(text[3])  # aix   : tortional constant
        ae[4,i] =float(text[4])  # aiy   : moment of inertia aroutd y-axis
        ae[5,i] =float(text[5])  # aiz   : moment of inertia around z-axis
        ae[6,i] =float(text[6])  # theta : chord angle
        ae[7,i] =float(text[7])  # alpha : thermal expansion coefficient
        ae[8,i] =float(text[8])  # gamma : unit weight of material
        ae[9,i] =float(text[9])  # gkX   : acceleration in X-direction
        ae[10,i]=float(text[10]) # gkY   : acceleration in Y-direction
        ae[11,i]=float(text[11]) # gkZ   : acceleration in Z-direction
    # element-node
    for i in range(0,nele):
        text=f.readline()
        text=text.strip()
        text=text.split()
        node[0,i]=int(text[0]) #node_1
        node[1,i]=int(text[1]) #node_2
        node[2,i]=int(text[2]) #section characteristic number
    # node coordinates
    for i in range(0,npoin):
        text=f.readline()
        text=text.strip()
        text=text.split()
        x[0,i]=float(text[0])    # x-coordinate
        x[1,i]=float(text[1])    # y-coordinate
        x[2,i]=float(text[2])    # z-coordinate
        deltaT[i]=float(text[3]) # Temperature change
    # boundary conditions (0:free, 1:restricted)
    for i in range(0,npfix):
        text=f.readline()
        text=text.strip()
        text=text.split()
        lp=int(text[0])              #fixed node
        mpfix[0,lp-1]=int(text[1])   #fixed in x-direction
        mpfix[1,lp-1]=int(text[2])   #fixed in y-direction
        mpfix[2,lp-1]=int(text[3])   #fixed in z-direction
        mpfix[3,lp-1]=int(text[4])   #fixed in rotation around x-axis
        mpfix[4,lp-1]=int(text[5])   #fixed in rotation around y-axis
        mpfix[5,lp-1]=int(text[6])   #fixed in rotation around z-axis
        rdis[0,lp-1]=float(text[7])  #fixed displacement in x-direction
        rdis[1,lp-1]=float(text[8])  #fixed displacement in y-direction
        rdis[2,lp-1]=float(text[9])  #fixed displacement in z-direction
        rdis[3,lp-1]=float(text[10]) #fixed rotation around x-axis
        rdis[4,lp-1]=float(text[11]) #fixed rotation around y-axis
        rdis[5,lp-1]=float(text[12]) #fixed rotation around z-axis
    # load
    if 0&lt;nlod:
        for i in range(0,nlod):
            text=f.readline()
            text=text.strip()
            text=text.split()
            lp=int(text[0])           #loaded node
            fp[6*lp-6]=float(text[1]) #load in x-direction
            fp[6*lp-5]=float(text[2]) #load in y-direction
            fp[6*lp-4]=float(text[3]) #load in z-direction
            fp[6*lp-3]=float(text[4]) #moment around x-axis
            fp[6*lp-2]=float(text[5]) #moment around y-axis
            fp[6*lp-1]=float(text[6]) #moment around z-axis
    f.close()
    return npoin,nele,nsec,npfix,nlod,ae,node,x,deltaT,mpfix,rdis,fp
```

### 3. 関数：入力データ書き出し (def prinp_3dfrm)
```py
def prinp_3dfrm(fnameW,npoin,nele,nsec,npfix,nlod,ae,node,x,deltaT,mpfix,rdis,fp):
    fout=open(fnameW,'w')
    # print out of input data
    print('{0:&gt;5s} {1:&gt;5s} {2:&gt;5s} {3:&gt;5s} {4:&gt;5s}'.format('npoin','nele','nsec','npfix','nlod'),file=fout)
    print('{0:5d} {1:5d} {2:5d} {3:5d} {4:5d}'.format(npoin,nele,nsec,npfix,nlod),file=fout)
    print('{0:&gt;5s} {1:&gt;15s} {2:&gt;15s} {3:&gt;15s} {4:&gt;15s} {5:&gt;15s} {6:&gt;15s} {7:&gt;15s}'
    .format('sec','E','po','A','J','Iy','Iz','theta'),file=fout)
    print('{0:&gt;5s} {1:&gt;15s} {2:&gt;15s} {3:&gt;15s} {4:&gt;15s} {5:&gt;15s}'
    .format('sec','alpha','gamma','gkX','gkY','gkZ'),file=fout)
    for i in range(0,nsec):
        print('{0:5d} {1:15.7e} {2:15.7e} {3:15.7e} {4:15.7e} {5:15.7e} {6:15.7e} {7:15.7e}'
        .format(i+1,ae[0,i],ae[1,i],ae[2,i],ae[3,i],ae[4,i],ae[5,i],ae[6,i]),file=fout)
        print('{0:5d} {1:15.7e} {2:15.7e} {3:15.7e} {4:15.7e} {5:15.7e}'
        .format(i+1,ae[7,i],ae[8,i],ae[9,i],ae[10,i],ae[11,i]),file=fout)
    print('{0:&gt;5s} {1:&gt;15s} {2:&gt;15s} {3:&gt;15s} {4:&gt;15s} {5:&gt;15s} {6:&gt;15s} {7:&gt;15s} {8:&gt;15s} {9:&gt;15s} {10:&gt;15s}'
    .format('node','x','y','z','fx','fy','fz','mx','my','mz','deltaT'),file=fout)
    for i in range(0,npoin):
        lp=i+1
        print('{0:5d} {1:15.7e} {2:15.7e} {3:15.7e} {4:15.7e} {5:15.7e} {6:15.7e} {7:15.7e} {8:15.7e} {9:15.7e} {10:15.7e}'
        .format(lp,x[0,i],x[1,i],x[2,i],fp[6*lp-6],fp[6*lp-5],fp[6*lp-4],fp[6*lp-3],fp[6*lp-2],fp[6*lp-1],deltaT[i]),file=fout)
    print('{0:&gt;5s} {1:&gt;5s} {2:&gt;5s} {3:&gt;5s} {4:&gt;5s} {5:&gt;5s} {6:&gt;5s} {7:&gt;15s} {8:&gt;15s} {9:&gt;15s} {10:&gt;15s} {11:&gt;15s} {12:&gt;15s}'
    .format('node','kox','koy','koz','kmx','kmy','kmz','rdis_x','rdis_y','rdis_z','rrot_x','rrot_y','rrot_z'),file=fout)
    for i in range(0,npoin):
        if 0&lt;mpfix[0,i]+mpfix[1,i]+mpfix[2,i]+mpfix[3,i]+mpfix[4,i]+mpfix[5,i]:
            lp=i+1
            print('{0:5d} {1:5d} {2:5d} {3:5d} {4:5d} {5:5d} {6:5d} {7:15.7e} {8:15.7e} {9:15.7e} {10:15.7e} {11:15.7e} {12:15.7e}'
            .format(lp,mpfix[0,i],mpfix[1,i],mpfix[2,i],mpfix[3,i],mpfix[4,i],mpfix[5,i],rdis[0,i],rdis[1,i],rdis[2,i],rdis[3,i],rdis[4,i],rdis[5,i]),file=fout)
    print('{0:&gt;5s} {1:&gt;5s} {2:&gt;5s} {3:&gt;5s}'.format('elem','i','j','sec'),file=fout)
    for ne in range(0,nele):
        print('{0:5d} {1:5d} {2:5d} {3:5d}'.format(ne+1,node[0,ne],node[1,ne],node[2,ne]),file=fout)
    fout.close()
```

### 4. 関数：計算結果書き出し (def prout_3dfrm)
```py
def prout_3dfrm(fnameW,npoin,nele,node,disg,fsec):
    fout=open(fnameW,'a')
    # displacement
    print('{0:&gt;5s} {1:&gt;15s} {2:&gt;15s} {3:&gt;15s} {4:&gt;15s} {5:&gt;15s} {6:&gt;15s}'
          .format('node','dis-x','dis-y','dis-z','rot-x','rot-y','rot-z'),file=fout)
    for i in range(0,npoin):
        lp=i+1
        print('{0:5d} {1:15.7e} {2:15.7e} {3:15.7e} {4:15.7e} {5:15.7e} {6:15.7e}'
              .format(lp,disg[6*lp-6],disg[6*lp-5],disg[6*lp-4],disg[6*lp-3],disg[6*lp-2],disg[6*lp-1]),file=fout)
    # section force
    print('{0:&gt;5s} {1:&gt;5s} {2:&gt;15s} {3:&gt;15s} {4:&gt;15s} {5:&gt;15s} {6:&gt;15s} {7:&gt;15s}'
    .format('elem','nodei','N_i','Sy_i','Sz_i','Mx_i','My_i','Mz_i'),file=fout)
    print('{0:&gt;5s} {1:&gt;5s} {2:&gt;15s} {3:&gt;15s} {4:&gt;15s} {5:&gt;15s} {6:&gt;15s} {7:&gt;15s}'
    .format('elem','nodej','N_j','Sy_j','Sz_j','Mx_j','My_j','Mz_j'),file=fout)
    for ne in range(0,nele):
        print('{0:5d} {1:5d} {2:15.7e} {3:15.7e} {4:15.7e} {5:15.7e} {6:15.7e} {7:15.7e}'
        .format(ne+1,node[0,ne],fsec[0,ne],fsec[1,ne],fsec[2,ne],fsec[3,ne],fsec[4,ne],fsec[5,ne]),file=fout)
        print('{0:5d} {1:5d} {2:15.7e} {3:15.7e} {4:15.7e} {5:15.7e} {6:15.7e} {7:15.7e}'
        .format(ne+1,node[1,ne],fsec[6,ne],fsec[7,ne],fsec[8,ne],fsec[9,ne],fsec[10,ne],fsec[11,ne]),file=fout)
    fout.close()
```

### 5. 関数：要素剛性マトリックス作成 (def sm_3dfrm)
```math
\begin{equation}
\boldsymbol{[k]}=
\begin{bmatrix}
\cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 & -\cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 \\
0 & \cfrac{12EI_z}{L^3} & 0 & 0 & 0 & \cfrac{6EI_z}{L^2} & 0 & -\cfrac{12EI_z}{L^3} & 0 & 0 & 0 & \cfrac{6EI_z}{L^2} \\
0 & 0 & \cfrac{12EI_y}{L^3} & 0 & -\cfrac{6EI_y}{L^2} & 0 & 0 & 0 & -\cfrac{12EI_y}{L^3} & 0 & -\cfrac{6EI_y}{L^2} & 0 \\
0 & 0 & 0 & \cfrac{GJ}{L} & 0 & 0 & 0 & 0 & 0 & -\cfrac{GJ}{L} & 0 & 0 \\
0 & 0 & -\cfrac{6EI_y}{L^2} & 0 & \cfrac{4EI_y}{L} & 0 & 0 & 0 & \cfrac{6EI_y}{L^2} & 0 & \cfrac{2EI_y}{L} & 0 \\
0 & \cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{4EI_z}{L} & 0 & -\cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{2EI_z}{L} \\
-\cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 & \cfrac{EA}{L} & 0 & 0 & 0 & 0 & 0 \\
0 & -\cfrac{12EI_z}{L^3} & 0 & 0 & 0 & -\cfrac{6EI_z}{L^2} & 0 & \cfrac{12EI_z}{L^3} & 0 & 0 & 0 & -\cfrac{6EI_z}{L^2} \\
0 & 0 & -\cfrac{12EI_y}{L^3} & 0 & \cfrac{6EI_y}{L^2} & 0 & 0 & 0 & \cfrac{12EI_y}{L^3} & 0 & \cfrac{6EI_y}{L^2} & 0 \\
0 & 0 & 0 & -\cfrac{GJ}{L} & 0 & 0 & 0 & 0 & 0 & \cfrac{GJ}{L} & 0 & 0 \\
0 & 0 & -\cfrac{6EI_y}{L^2} & 0 & \cfrac{2EI_y}{L} & 0 & 0 & 0 & \cfrac{6EI_y}{L^2} & 0 & \cfrac{4EI_y}{L} & 0 \\
0 & \cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{2EI_z}{L} & 0 & -\cfrac{6EI_z}{L^2} & 0 & 0 & 0 & \cfrac{4EI_z}{L} \\
\end{bmatrix}
\end{equation}
```

```py
def sm_3dfrm(EA,GJ,EIy,EIz,x1,y1,z1,x2,y2,z2):
    ek=np.zeros((12,12),dtype=np.float64) # local stiffness matrix
    xx=x2-x1
    yy=y2-y1
    zz=z2-z1
    el=np.sqrt(xx**2+yy**2+zz**2)
    ek[ 0, 0]= EA/el
    ek[ 0, 6]=-EA/el
    ek[ 1, 1]= 12*EIz/el**3
    ek[ 1, 5]=  6*EIz/el**2
    ek[ 1, 7]=-12*EIz/el**3
    ek[ 1,11]=  6*EIz/el**2
    ek[ 2, 2]= 12*EIy/el**3
    ek[ 2, 4]= -6*EIy/el**2
    ek[ 2, 8]=-12*EIy/el**3
    ek[ 2,10]= -6*EIy/el**2
    ek[ 3, 3]= GJ/el
    ek[ 3, 9]=-GJ/el
    ek[ 4, 2]= -6*EIy/el**2
    ek[ 4, 4]=  4*EIy/el
    ek[ 4, 8]=  6*EIy/el**2
    ek[ 4,10]=  2*EIy/el
    ek[ 5, 1]=  6*EIz/el**2
    ek[ 5, 5]=  4*EIz/el
    ek[ 5, 7]= -6*EIz/el**2
    ek[ 5,11]=  2*EIz/el
    ek[ 6, 0]=-EA/el
    ek[ 6, 6]= EA/el
    ek[ 7, 1]=-12*EIz/el**3
    ek[ 7, 5]= -6*EIz/el**2
    ek[ 7, 7]= 12*EIz/el**3
    ek[ 7,11]= -6*EIz/el**2
    ek[ 8, 2]=-12*EIy/el**3
    ek[ 8, 4]=  6*EIy/el**2
    ek[ 8, 8]= 12*EIy/el**3
    ek[ 8,10]=  6*EIy/el**2
    ek[ 9, 3]=-GJ/el
    ek[ 9, 9]= GJ/el
    ek[10, 2]= -6*EIy/el**2
    ek[10, 4]=  2*EIy/el
    ek[10, 8]=  6*EIy/el**2
    ek[10,10]=  4*EIy/el
    ek[11, 1]=  6*EIz/el**2
    ek[11, 5]=  2*EIz/el
    ek[11, 7]= -6*EIz/el**2
    ek[11,11]=  4*EIz/el
    return ek
```

### 6. 関数：座標変換マトリックス作成 (def tm_3dfrm)
```math
\begin{gather}
\ell=\sqrt{(x_j-x_i)^2+(y_j-y_i)^2+(z_j-z_i)^2}\\
l=\cfrac{x_j-x_i}{\ell} \qquad m=\cfrac{y_j-y_i}{\ell} \qquad n=\cfrac{z_j-z_i}{\ell}
\end{gather}
```

```math
\begin{equation}
\boldsymbol{[t_1]}=
\begin{bmatrix}
1 & 0 & 0 \\
0 & \cos\theta & \sin\theta \\
0 & -\sin\theta & \cos\theta
\end{bmatrix}
\qquad \text{($\theta$ : chord angle)}
\end{equation}
```

```math
\begin{align}
&\boldsymbol{[t_2]}=
\begin{bmatrix}
0 & 0 & n \\
n & 0 & 0 \\
0 & 1 & 0
\end{bmatrix}
& &\text{部材長軸（部材座標系x軸）が全体座標系Z軸と平行な場合}\\
&\boldsymbol{[t_2]}=
\begin{bmatrix}
l & m & n \\
-\cfrac{m}{\sqrt{l^2+m^2}} & \cfrac{l}{\sqrt{l^2+m^2}} & 0 \\
-\cfrac{l \cdot n}{\sqrt{l^2+m^2}} & -\cfrac{m \cdot n}{\sqrt{l^2+m^2}} & \sqrt{l^2+m^2}
\end{bmatrix}
& &\text{部材長軸（部材座標系x軸）が全体座標系Z軸と平行でない場合}
\end{align}
```

```math
\begin{equation}
\boldsymbol{[t]}=\boldsymbol{[t_1]}\cdot\boldsymbol{[t_2]}
\end{equation}
```

```math
\begin{equation}
\boldsymbol{[T]}=
\begin{bmatrix}
\boldsymbol{[t]} & \boldsymbol{0}   & \boldsymbol{0}   & \boldsymbol{0} \\
\boldsymbol{0}   & \boldsymbol{[t]} & \boldsymbol{0}   & \boldsymbol{0} \\
\boldsymbol{0}   & \boldsymbol{0}   & \boldsymbol{[t]} & \boldsymbol{0} \\
\boldsymbol{0}   & \boldsymbol{0}   & \boldsymbol{0}   &\boldsymbol{[t]}
\end{bmatrix}
\end{equation}
```

```py
def tm_3dfrm(theta,x1,y1,z1,x2,y2,z2):
    tt=np.zeros((12,12),dtype=np.float64) # transformation matrix
    t1=np.zeros((3,3),dtype=np.float64)
    t2=np.zeros((3,3),dtype=np.float64)
    xx=x2-x1
    yy=y2-y1
    zz=z2-z1
    el=np.sqrt(xx**2+yy**2+zz**2)
    t1[0,0]=1
    t1[1,1]= np.cos(theta)
    t1[1,2]= np.sin(theta)
    t1[2,1]=-np.sin(theta)
    t1[2,2]= np.cos(theta)
    ll=(x2-x1)/el
    mm=(y2-y1)/el
    nn=(z2-z1)/el
    if x2-x1==0.0 and y2-y1==0.0:
        t2[0,2]=nn
        t2[1,0]=nn
        t2[2,1]=1.0
    else:
        qq=np.sqrt(ll**2+mm**2)
        t2[0,0]=ll
        t2[0,1]=mm
        t2[0,2]=nn
        t2[1,0]=-mm/qq
        t2[1,1]= ll/qq
        t2[2,0]=-ll*nn/qq
        t2[2,1]=-mm*nn/qq
        t2[2,2]=qq
    t3=np.dot(t1,t2)
    tt[0:3,0:3]  =t3[0:3,0:3]
    tt[3:6,3:6]  =t3[0:3,0:3]
    tt[6:9,6:9]  =t3[0:3,0:3]
    tt[9:12,9:12]=t3[0:3,0:3]
    return tt
```

### 7. 関数：節点慣性力ベクトル作成 (def bfvec_3dfrm)
```math
\begin{equation*}
\{\boldsymbol{F_{b}}\}=\{\boldsymbol{f_{b}}\}=\frac{\gamma\cdot A \cdot L}{2}
\begin{Bmatrix}
k_x \\
k_y \\
k_z \\
0   \\
0   \\
0   \\
k_x \\
k_y \\
k_z \\
0   \\
0   \\
0
\end{Bmatrix}
\end{equation*}
```

```py
def bfvec_3dfrm(A,gamma,gkX,gkY,gkZ,x1,y1,z1,x2,y2,z2):
    # Body force vector in global coordinate system
    bfe_g=np.zeros(12,dtype=np.float64)
    xx=x2-x1
    yy=y2-y1
    zz=z2-z1
    el=np.sqrt(xx**2+yy**2+zz**2)
    bfe_g[0]=0.5*gamma*A*el*gkX
    bfe_g[1]=0.5*gamma*A*el*gkY
    bfe_g[2]=0.5*gamma*A*el*gkZ
    bfe_g[6]=0.5*gamma*A*el*gkX
    bfe_g[7]=0.5*gamma*A*el*gkY
    bfe_g[8]=0.5*gamma*A*el*gkZ
    return bfe_g
```

### 8. 関数：節点温度荷重ベクトル作成 (def tfvec_3dfrm)
```math
\begin{equation*}
\{\boldsymbol{f_{t}}\}=EA\cdot\alpha\cdot\Delta T
\begin{Bmatrix}
-1 \\
 0 \\
 0 \\
 0 \\
 0 \\
 0 \\
 1 \\
 0 \\
 0 \\
 0 \\
 0 \\
 0
\end{Bmatrix}
\end{equation*}
```

```py
def tfvec_3dfrm(EA,alpha,tem):
    # Thermal load vector  in local coordinate system
    tfe_l=np.zeros(12,dtype=np.float64)
    tfe_l[0]=-EA*alpha*tem
    tfe_l[6]= EA*alpha*tem
    return tfe_l
```

### 9. 関数：要素断面力計算 (def calsecf_3dfrm)
$N_1'$ および $N_2'$ は，温度変化による軸力補正である．

```math
\begin{align}
&\{\boldsymbol{f_{sec}}\}=[\boldsymbol{k}]\cdot ([\boldsymbol{T}]\{\boldsymbol{U}\})\\
&N_1'=N_1+EA\cdot\alpha\cdot\Delta T \\
&N_2'=N_2-EA\cdot\alpha\cdot\Delta T
\end{align}
```

```py
def calsecf_3dfrm(EA,GJ,EIy,EIz,theta,alpha,tem,dis,x1,y1,z1,x2,y2,z2):
    ek=sm_3dfrm(EA,GJ,EIy,EIz,x1,y1,z1,x2,y2,z2) # Stiffness matrix in local coordinate
    tt=tm_3dfrm(theta,x1,y1,z1,x2,y2,z2) # Transformation matrix
    secf=np.dot(ek,np.dot(tt,dis))
    secf[0]=secf[0]+EA*alpha*tem
    secf[6]=secf[6]-EA*alpha*tem
    return secf
```

### 10. 関数：メインルーチン
#### メインルーチン処理手順

+ <b>コマンドラインより入出力ファイル名取得</b>：
コマンドライン引数として入出力データファイル名を取得．
入力データファイル名：<var>fnameR</var>，出力データファイル名：<var>fnameW</var>


+ <b>データ入力</b>：
関数 <em>inpdata_3dfrm</em> により入力データ取得


+ <b>入力データ書き出し</b>：
関数 <em>prinp_3dfrm</em> により入力データ書き出し


+ <b>剛性マトリックス組み立て</b>：
各要素毎に，剛性マトリックス <var>ck</var> （座標変換後），温度荷重ベクトル <var>tfe</var> （座標変換後），慣性力ベクトル <var>bfe</var> を計算し，全体剛性方程式に組み込んでいく．
ここで剛性マトリックス，温度荷重ベクトル，慣性力ベクトル計算の関数の引数は，全てスカラーとした．
要素番号が指定されれば，要素を構成する節点座標や要素物性値は特定できるため，他の要素の値を含んで収納する配列よりは，スカラーを送り込んだほうがプログラム的にスッキリするように思う．
全体剛性マトリックスは変数 <var>gk</var> に，荷重ベクトルは変数 <var>fp</var> に加算されていく．
要素剛性マトリックスを全体剛性マトリックスに組み込むための位置情報は，配列 <var>ir[]</var> に格納されている．


+ <b>境界条件処理（変位既知自由度の処理）</b>：
全体剛性マトリックスの大きさは変えず，変位既知節点の処理を行う．

<table>
<tr><th>項目</th><th>説明</th></tr>
<tr><td> <var>npoin</var> </td><td> 総節点数 </td></tr>
<tr><td> <var>nfree</var> </td><td> １節点自由度（ここでは 6） </td></tr>
<tr><td> <var>mpfix</var> </td><td> 各節点毎の拘束条件を格納した配列（=1：拘束，=0：自由） </td></tr>
<tr><td> <var>fp</var>    </td><td> 節点荷重ベクトル </td></tr>
<tr><td> <var>rdis</var>  </td><td> 変位拘束する節点の変位量 </td></tr>
<tr><td> <var>gk</var>    </td><td> 全体剛性マトリックス </td></tr>
<tr><td> <var>disg</var>  </td><td> 全体剛性方程式の解（変位） </td></tr>
</table>


+ <b>剛性方程式を解く（変位計算）</b>：
全体剛性マトリックス <var>gk</var> を CSR 変換後，<var>scipy.sparse.linalg.spsolve()</var> により連立一次方程式を解く．


+ <b>境界条件処理（既知変位の組み込み）</b>：
連立一次方程式を解いた後，既知変位に相当する自由度の項に，既知変位を入れ直すことを忘れないようにする．


+ <b>要素断面力計算</b>：
関数 <em>calsecf_3dfrm</em> による要素毎の断面力計算実施


+ <b>計算結果書き出し</b>：
関数 <em>prout_3dfrm</em> による計算結果出力


+ <b>計算時間等情報表示</b>：
以下により，計算完了時刻とスタート時刻の差分を処理時間として出力する．

```py
import time

start=time.time()
......
dtime=time.time()-start
```


#### メインルーチンプログラム
```py
def main_3dfrm():
    start=time.time()
    args = sys.argv
    fnameR=args[1] # input data file
    fnameW=args[2] # output data file
    nod=2              # Number of nodes per element
    nfree=6            # Degree of freedom per node
    # data input
    npoin,nele,nsec,npfix,nlod,ae,node,x,deltaT,mpfix,rdis,fp=inpdata_3dfrm(fnameR,nod,nfree)
    # print out of input data
    prinp_3dfrm(fnameW,npoin,nele,nsec,npfix,nlod,ae,node,x,deltaT,mpfix,rdis,fp)
    # array declaration
    ir=np.zeros(nod*nfree,dtype=np.int)   # Work vector for matrix assembly
    gk=np.zeros((nfree*npoin,nfree*npoin),dtype=np.float64)   # Global stiffness matrix
    # assembly of stiffness matrix & load vectors
    for ne in range(0,nele):
        i=node[0,ne]-1
        j=node[1,ne]-1
        m=node[2,ne]-1
        x1=x[0,i]; y1=x[1,i]; z1=x[2,i]
        x2=x[0,j]; y2=x[1,j]; z2=x[2,j]
        ee   =ae[0,m]  # elastic modulus
        po   =ae[1,m]  # Poisson's ratio
        aa   =ae[2,m]  # section area
        aix  =ae[3,m] # tortional constant
        aiy  =ae[4,m] # moment of inertia around y-axis
        aiz  =ae[5,m] # moment of inertia around z-axis
        theta=np.radians(ae[6,m]) # chord angle
        alpha=ae[7,m] # thermal expansion coefficient
        gamma=ae[8,m] # unit weight of material
        gkX  =ae[9,m]   # seismic coefficient in X-direction
        gkY  =ae[10,m]  # seismic coefficient in Y-direction
        gkZ  =ae[11,m]  # seismic coefficient in Z-direction
        A=aa  # section area
        EA=ee*aa
        GJ=ee/2/(1+po)*aix
        EIy=ee*aiy
        EIz=ee*aiz
        tem=0.5*(deltaT[i]+deltaT[j])                # average temperature change
        tt   =tm_3dfrm(theta,x1,y1,z1,x2,y2,z2)         # Transformation matrix
        ek   =sm_3dfrm(EA,GJ,EIy,EIz,x1,y1,z1,x2,y2,z2) # Stiffness matrix in local coordinate
        ck   =np.dot(np.dot(tt.T,ek),tt)             # Stiffness matrix in global coordinate
        tfe_l=tfvec_3dfrm(EA,alpha,tem)              # Thermal load vector in local coordinate
        tfe  =np.dot(tt.T,tfe_l)                     # Thermal load vector in global coordinate
        bfe  =bfvec_3dfrm(A,gamma,gkX,gkY,gkZ,x1,y1,z1,x2,y2,z2) # Body force vector in global coordinate
        ir[11]=6*j+5; ir[10]=ir[11]-1; ir[9]=ir[10]-1; ir[8]=ir[9]-1; ir[7]=ir[8]-1; ir[6]=ir[7]-1
        ir[5] =6*i+5; ir[4] =ir[5]-1 ; ir[3]=ir[4]-1 ; ir[2]=ir[3]-1; ir[1]=ir[2]-1; ir[0]=ir[1]-1
        for i in range(0,nod*nfree):
            it=ir[i]
            fp[it]=fp[it]+tfe[i]+bfe[i]
            for j in range(0,nod*nfree):
                jt=ir[j]
                gk[it,jt]=gk[it,jt]+ck[i,j]
    # treatment of boundary conditions
    for i in range(0,npoin):
        for j in range(0,nfree):
            if mpfix[j,i]==1:
                iz=i*nfree+j
                fp[iz]=0.0
    for i in range(0,npoin):
        for j in range(0,nfree):
            if mpfix[j,i]==1:
                iz=i*nfree+j
                fp=fp-rdis[j,i]*gk[:,iz]
                gk[:,iz]=0.0
                gk[iz,iz]=1.0
    # solution of simultaneous linear equations
    #disg = np.linalg.solve(gk, fp)
    gk = csr_matrix(gk)
    disg = spsolve(gk, fp, use_umfpack=True)
    # recovery of restricted displacements
    for i in range(0,npoin):
        for j in range(0,nfree):
            if mpfix[j,i]==1:
                iz=i*nfree+j
                disg[iz]=rdis[j,i]
    # calculation of section force
    dis=np.zeros(12,dtype=np.float64)
    fsec  =np.zeros((nod*nfree,nele),dtype=np.float64)  # Section force vector
    for ne in range(0,nele):
        i=node[0,ne]-1
        j=node[1,ne]-1
        m=node[2,ne]-1
        x1=x[0,i]; y1=x[1,i]; z1=x[2,i]
        x2=x[0,j]; y2=x[1,j]; z2=x[2,j]
        ee   =ae[0,m]  # elastic modulus
        po   =ae[1,m]  # Poisson's ratio
        aa   =ae[2,m]  # section area
        aix  =ae[3,m] # tortional constant
        aiy  =ae[4,m] # moment of inertia around y-axis
        aiz  =ae[5,m] # moment of inertia around z-axis
        theta=np.radians(ae[6,m]) # chord angle
        alpha=ae[7,m] # thermal expansion coefficient
        EA=ee*aa
        GJ=ee/2/(1+po)*aix
        EIy=ee*aiy
        EIz=ee*aiz
        tem=0.5*(deltaT[i]+deltaT[j]) # average temperature change
        dis[0]=disg[6*i]  ; dis[1] =disg[6*i+1]; dis[2]= disg[6*i+2]
        dis[3]=disg[6*i+3]; dis[4] =disg[6*i+4]; dis[5]= disg[6*i+5]
        dis[6]=disg[6*j]  ; dis[7] =disg[6*j+1]; dis[8]= disg[6*j+2]
        dis[9]=disg[6*j+3]; dis[10]=disg[6*j+4]; dis[11]=disg[6*j+5]
        fsec[:,ne]=calsecf_3dfrm(EA,GJ,EIy,EIz,theta,alpha,tem,dis,x1,y1,z1,x2,y2,z2)
    # print out of result
    prout_3dfrm(fnameW,npoin,nele,node,disg,fsec)
    # information
    dtime=time.time()-start
    print('n={0}  time={1:.3f}'.format(nfree*npoin,dtime)+' sec')
    fout=open(fnameW,'a')
    print('n={0}  time={1:.3f}'.format(nfree*npoin,dtime)+' sec',file=fout)
    fout.close()
```

### 11. メインルーチン実行
```py
#==============
# Execution
#==============
if __name__ == '__main__': main_3dfrm()
```

以上