空洞共振器の共振周波数の計算をするプログラムをJSで作ってみる。

こんにちは、
[div inc.(TECH::CAMP) Advent Calendar 2018 - Qiita](https://qiita.com/advent-calendar/2018/div-inc)２つめの投稿です。
今年のクリスマスは研究室で過ごすことになりそうです。

諸事情あって空洞共振器の共振周波数の計算をしなければいけなくなってしまいました。
しかし手計算でやるのは少しめんどくさい。
ということで、勝手に計算してくれるプログラムを作ってしまおうと思います。

## 前提条件
1. 計算の精度はそこまで求めない。
2. 環境依存がなく誰でも使えるように、ブラウザで動くようにする。
3. せっかくなので、使ったことのないフレームワーク(Vue.js)を使ってみる。
4. デザインにはこだわらず、まずは計算結果がでればよい。


## 前提知識
そもそも今回求めようとしているものは空洞共振器の周波数です。
空洞共振器は、特定の周波数の電磁波を入力すると特定の共振モードが得られ、
任意の電磁場を得ることができることから、様々な研究に用いられるものです。
形は一般的には「方形（≒長方形）」「円筒形」です。
今回はまず、「方形空洞共振器」について考えていきます。

詳細は色々省いて、今回求めたい共振周波数は以下の式で導出することができます。ここで特定の共振モードの名前をTEnmpのようにつけます。（n,m,pは整数で可変）

```math

\frac{1}{ \lambda} = (\frac{m}{2a})^2 + (\frac{n}{2b})^2 + (\frac{p}{2l})^2
```

１つの共振器で振動数を変えると、複数の共振モードが現れます。
今回この計算で明らかにしたいことは

 **どのような形状の空洞共振器で、どの共振モードが
どんな共振周波数を持ち、どんな順番で出現するか**

という情報です。
それを踏まえて書いていきます。

## 作成開始
ここから実際どんなコードを書くかを考えていきます。
今回は実際に動けばそれでいいので、綺麗なコードは意識していません。

### Vueの導入
まず、今回ついでに使用するJSフレームワークであるVueを使えるようにします。
今回はVueの勉強も兼ねているため、開発者用のCDNを導入します。

`index.html`に以下の記述を追加。

```html
  <head>
    <meta charset="utf-8">
    <title>cavity_calc</title>
		(中略)
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
```

これで準備完了です。


### 計算するための変数の用意
JSの方に必要な変数を定義しておきます。
必要なのは、３方向の長さと、結果を格納するための配列です。
配列の中身に関してはのちほど触れます。

```javascript
var rectangularCavityResonator = new Vue({
  el: '#rectangularCavityResonator',
  data:{
    x_Length: 1,
    y_Length: 1,
    z_Length: 1,
    modeResultList:[
      {m:0,n:0,p:0,lambda:0,f:0},
    ],
  },
});
```


### 変数の入力を受ける
変数の入力を受けて、計算するボタンまでつけてみます。
今回は、きちんとデータが受け渡されているか確認するため、入力値を出力させています。

```html
<h2>方形空洞共振器</h2>
      <div id="rectangularCavityResonator">
          <p>x_Length: {{ x_Length }} [mm]</p>
          <input v-model="x_Length">
          <p>y_Length: {{ y_Length }} [mm]</p>
          <input v-model="y_Length">
          <p>z_Length: {{ z_Length }} [mm]</p>
          <input v-model="z_Length">
          <br>
          <button v-on:click="calcTEmode">Calc TE Mode</button>
      </div>
```


### 計算結果の表示場所を作る。
本来ならコンポーネントを使ってやりたいところですが、あまり理解が進んでおらず、どうやって値を引き渡すのかがよくわからなかったので、格納されている配列をそのまま出力させました。

```html
 		 <ol id="TEmodeResult">
          <li v-for="modeResult in modeResultList">
            TE
            <span class="small">{{modeResult.m}}
            {{modeResult.n}}
            {{modeResult.p}}</span>
            : {{ modeResult.f }} [GHz]
          </li>
       </ol>
```


ここまでのhtmlのコードは以下のような形です。
見た目は悪いですが、とりあえずこれで目的を果たすことができるので、まぁよしとします。

```html
      <h2>方形空洞共振器</h2>
      <div id="rectangularCavityResonator">
          <p>x_Length: {{ x_Length }} [mm]</p>
          <input v-model="x_Length">
          <p>y_Length: {{ y_Length }} [mm]</p>
          <input v-model="y_Length">
          <p>z_Length: {{ z_Length }} [mm]</p>
          <input v-model="z_Length">
          <br>
          <button v-on:click="calcTEmode">Calc TE Mode</button>

        <ol id="TEmodeResult">
          <li v-for="modeResult in modeResultList">
            TE
            <span class="small">{{modeResult.m}}
            {{modeResult.n}}
            {{modeResult.p}}</span>
            : {{ modeResult.f }} [GHz]
          </li>
        </ol>
      </div>

```



### 変数定義
実際に計算させる部分を作ります。
まずは変数を定義します。
* 共振モードを表す添字mnp、波長λ、共振周波数fを格納したハッシュTEmode
* 後々計算の手間を省くため、あらかじめ大きさを２倍して格納したa,b,l。
* 光速C（本当は定数）
* 計算結果を格納するための変数を再度初期化し、入っていた値を削除


```javascript
 methods:{
    calcTEmode: function() {
        console.log('done'); //ボタンが反応したか確認
        var m = 1,n = 0,p = 1,lambda = 0,f = 0;
        var TEmode = {m:m,n:n,p:p,lambda:lambda,f:f};
        var a = 2 * this.x_Length, b = 2 * this.y_Length, l = 2 * this.z_Length;
        var c = 299792458; //[m/s]
        this.modeResultList = [];
          }
        }
```


### 連続計算
mnpの値を順番に変え計算して、波長λを計算する。
その結果をもとに共振周波数fに変換し、有効桁数小数点以下５桁にして再代入を行う。
それらの結果を計算しているモードの情報とともに、１つのハッシュとして格納する。

```javascript
        for(m=1;m<6;m++){
          for(n=0;n<6;n++){
            for(p=0;p<6;p++){
              lambda = 1/Math.sqrt(
                Math.pow(m/a, 2) +
                Math.pow(n/b, 2) +
                Math.pow(p/l, 2)
              );
              f = c/lambda;
              f = f/1000000;
              f = Math.round(f*10000)/10000;
              TEmode = {m:m,n:n,p:p,lambda:lambda,f:f};
              this.modeResultList.push(TEmode);
            }
          }
        }
```


### 計算部分終わり

以下のコードが完成したコードです。

```javascript
  methods:{
    calcTEmode: function() {
        console.log('done'); //ボタンが反応したか確認
        var m = 1,n = 0,p = 1,lambda = 0,f = 0;
        var TEmode = {m:m,n:n,p:p,lambda:lambda,f:f};
        var a = 2 * this.x_Length, b = 2 * this.y_Length, l = 2 * this.z_Length;
        var c = 299792458; //[m/s]
        this.modeResultList = [];

        for(m=1;m<6;m++){
          for(n=0;n<6;n++){
            for(p=0;p<6;p++){
              lambda = 1/Math.sqrt(
                Math.pow(m/a, 2) +
                Math.pow(n/b, 2) +
                Math.pow(p/l, 2)
              );
              f = c/lambda;
              f = f/1000000;
              f = Math.round(f*10000)/10000;
              TEmode = {m:m,n:n,p:p,lambda:lambda,f:f};
              this.modeResultList.push(TEmode);
            }
          }
        }
    }
  }
```


### 結果をソートする。
これでは、計算した順番に出てくるだけで、非常に醜いので、結果を共振周波数の少ない方から順にソートして配列の中身をならべかえる。

```javascript
        this.modeResultList.sort(function(a,b){
          if(a.f < b.f) return -1;
          if(a.f > b.f) return 1;
          return 0;
        });
```

## 完成
以上で完成しました。
最終的なコードを載せておきます。
今後は

- 円筒形も作成する
- 電磁界分布も描画する

ことをしてみようかと思います。

```javascript
var rectangularCavityResonator = new Vue({
  el: '#rectangularCavityResonator',
  data:{
    x_Length: 1,
    y_Length: 1,
    z_Length: 1,
    modeResultList:[
      {m:0,n:0,p:0,lambda:0,f:0},
    ],
  },
  methods:{
    calcTEmode: function() {
        var m = 1,n = 0,p = 1,lambda = 0,f = 0;
        var TEmode = {m:m,n:n,p:p,lambda:lambda,f:f};
        var a = 2 * this.x_Length, b = 2 * this.y_Length, l = 2 * this.z_Length;
        var c = 299792458; //[m/s]
        this.modeResultList = [];

        for(m=1;m<6;m++){
          for(n=0;n<6;n++){
            for(p=0;p<6;p++){
              lambda = 1/Math.sqrt(
                Math.pow(m/a, 2) +
                Math.pow(n/b, 2) +
                Math.pow(p/l, 2)
              );
              f = c/lambda;
              f = f/1000000;
              f = Math.round(f*10000)/10000;
              TEmode = {m:m,n:n,p:p,lambda:lambda,f:f};
              this.modeResultList.push(TEmode);
            }
          }
        }
        this.modeResultList.sort(function(a,b){
          if(a.f < b.f) return -1;
          if(a.f > b.f) return 1;
          return 0;
        });
    }
  }
});

```