Plotly.pyで3次元のムービーを作成

# 概要
前回([PythonコードをCythonで高速化してFortranに近づける](https://qiita.com/Hirotoshi-Uchino/items/fc93a732dd970834d965 "PythonコードをCythonで高速化してFortranに近づける"))からの続きで、今回は可視化の話。
せっかくなので、前回の計算結果のプラズマの運動をPythonでかっこよく可視化しようと思い、[Plotly.py](https://plot.ly/python/)に行き着いた。
試行錯誤で3次元のアニメーションを作ってみたので紹介。

# Plotly.pyを用いたアニメーション
まずは結果の動画から　→　https://youtu.be/1OFHpvSOsUM

Plotly.pyのofflineモードでは、html形式のファイルでリンクのようなアニメーションが作成できる。
また、Play&Pauseボタンを設置できたり、Pause中に図をグリグリ動かすことが可能。
(実際は動画よりもプラズマは速く動くのだが、動画撮影による処理落ち(?)によってスピードが落ちている)

### パラメータ設定
リンクの動画は、[前回](https://qiita.com/Hirotoshi-Uchino/items/fc93a732dd970834d965)の数値計算において物理パラメータを以下のように設定した場合の結果。

|N(反復回数)|Δt|m|q|E|B|
|:-------:|:------------:|:----------:|:-----------:|:-------------:|:----------:|
|100,000| 0.001|1 |-1|(0, 0, 0.005)|(0, 0, 1)|
電場と磁場は、空間3次元(x, y, z)に対応。qのマイナスは、プラズマが電子(物理の定義では電子の電荷がマイナスなので)ということを意味している。パラメータの絶対量は基本的に適当だが、厳密なシミュレーションではないのでOK。
10万の計算反復の、100ステップごとにプラズマの位置データを保存し、100ステップごとの図をつなげることでリンクのアニメーションが作成されている。


### 可視化方法
今回の動画の生成用コードを以下に示す。

```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import plotly.offline as offline
import plotly.graph_objs as go
import numpy as np


if __name__ == '__main__':

    load_data = np.loadtxt('gyro_motion_EparaB.dat', delimiter=',', skiprows=1)

    x = load_data[:, 0]
    y = load_data[:, 1]
    z = load_data[:, 2]

    scatter = go.Scatter3d(x=x, y=y, z=z, mode='lines', line=dict(width=2, color='blue'))

    data = [scatter, scatter]

    layout = dict(xaxis=dict(range=[x.min(), x.max()], autorange=False, zeroline=False),
                  yaxis=dict(range=[y.min(), y.max()], autorange=False, zeroline=False),
                  zaxis=dict(range=[z.min(), z.max()], autorange=False, zeroline=False),
                  title='Gyro Motion of Electron', hovermode='closest',
                  autosize=True,
                  scene=dict(
                            aspectratio=dict(x=0.4, y=0.4, z=1.6),
                            # aspectratio=dict(x=2, y=0.4, z=0.4), # ExB用
                            ),
                  updatemenus=[{'type': 'buttons',
                                'buttons': [{'label': 'Play',
                                             'method': 'animate',
                                             'args':[None, dict(frame=dict(duration=0, redraw=False),
                                                     transition=dict(duration=0),
                                                     fromcurrent=True,
                                                     mode='immediate')]

                                             },
                                            {
                                                'args': [[None], {'frame': {'duration': 0, 'redraw': False},
                                                                  'mode': 'immediate',
                                                                  'transition': {'duration': 0}}],
                                                'label': 'Pause',
                                                'method': 'animate'
                                            }
                                            ]}])

    frames = [dict(data=[dict(x=[x[k]],
                              y=[y[k]],
                              z=[z[k]],
                              mode='markers',
                              marker=dict(color='red', size=4)
                              )
                         ]) for k in range(len(x))]
    figure = dict(data=data, layout=layout, frames=frames)
    offline.plot(figure, filename='EparaB.html')


```

基本的な利用方法は、[公式サイト](https://plot.ly/python/)や他のページにも紹介されているので割愛させていただき、ここでは3次元アニメーションの作成方法の要点を(コードの逆順で)紹介する。

最終的に、`offline.plot(figure, filename='EparaB.html')`の部分で動画を作成しているため、ここの引数`figure`を準備する必要がある。そして、`figure`は`data, layout, frames`という情報をディクショナリ形式で持つようにする。

- `frames`には、プラズマのステップごとの位置データをリスト形式で溜め込んでおく。`mode='markers'`は、プラズマを点として描くという意味。
- `layout`は、図の設定情報をコードのように入れておく。軸の設定や、動画の再生・停止ボタンを設置することなどが可能。
- `data`には、プラズマの3次元の軌跡を、plotly.graph_objsのScatter3dメソッド用いて保存しておく。その際、コードのように`data = [scatter, scatter]`と2要素にしておく必要がある(`data = [scatter]`では不十分だった。軌跡として認められないため？）。プラズマの3次元の軌跡の画像を準備しておいて、そこに`frames`でプラズマを描いていくというイメージだと思われる。

# まとめ
簡単にだが、Plotly.pyを用いて3次元の動画を描けるようになった。まだまだPlotly初心者なので、さらにかっこいい動画を作れるようになりたい。コメントや質問等頂ければ幸いです。

# 補足 ~プラズマ物理~
上記のプラズマの運動は、磁場に沿った方向にかかる電場によって、電子が(電場と逆方向に)加速されつつ、円運動(ジャイロ運動)をするという基本的な物理過程を表している(高校の物理を思い出してください)。

では、電場が磁場と垂直にかかるような場合はどうなるのであろうか。
例えば、電場を以下のように、y成分のみ持つようにした場合について。

|N(反復回数)|Δt|m|q|E|B|
|:-------:|:------------:|:----------:|:-----------:|:-------------:|:----------:|
|100,000| 0.001|1 |-1|(0, 0.1, 0)|(0, 0, 1)|

答えはこちら　→　https://youtu.be/GNTKQ3iMcZg

この運動は、電場と磁場の外積の方向に、プラズマがジャイロ運動しながら動いていくので、プラズマ物理学分野で__ExB drift__ (イークロスビードリフト)と呼ばれる。