バックトレース (data.backtrace) — 実験的

data.backtrace は、EMSES の電磁場に対して粒子の軌道を逆向きに積分する バックトレース機能への入口です。到達確率(get_probabilities)や 個別粒子の軌跡(get_backtrace / get_backtraces)を計算でき、 結果は専用コンテナに包まれて返るので .vxvz.plot() のような短い記法で 可視化まで一直線につなげられます。

要件: バックトレースは外部の vdist-solver-fortran パッケージ (vdsolverf) に依存します。pip install vdist-solver-fortran で インストールしてください。未インストールの環境では data.backtrace.* の 呼び出し時に ImportError が出ます。

いつ使うか

  • ある観測点に粒子が到達する 位相空間分布 を見たい

  • 注目した粒子がどの領域から来たのかを 軌跡として追いたい

  • 到達粒子の エネルギースペクトル を描きたい

バックトレースは data.inp.dt と EMSES の保存済みフィールドを使って 後ろ向きに ODE を解くため、大きな max_step を指定するとそれなりに時間が かかります。HPC ノードに処理を任せたい場合は、リモート実行と 組み合わせてください(後述)。

クイックスタート

import emout

data = emout.Emout("output_dir")

# 単一粒子のバックトレース
position = (20.0, 32.0, 40.0)
velocity = (1.0e5, 0.0, -2.0e5)
bt = data.backtrace.get_backtrace(position, velocity, ispec=0)

bt.tx.plot()      # t vs x の軌跡
bt.xvz.plot()     # x vs vz 位相空間

# 複数粒子を一括処理
import numpy as np
positions = np.array([[20, 32, 40], [21, 32, 40], [22, 32, 40]], dtype=float)
velocities = np.zeros_like(positions)
velocities[:, 0] = 1.0e5
many = data.backtrace.get_backtraces(positions, velocities, ispec=0)

many.xz.plot(alpha=0.5)    # 全軌跡を重ね描き

# 6D 位相空間での到達確率
result = data.backtrace.get_probabilities(
    x=20.0, y=32.0, z=40.0,
    vx=(-3e5, 3e5, 64),
    vy=0.0,
    vz=(-3e5, 3e5, 64),
    ispec=0,
)

result.vxvz.plot(cmap="viridis")      # vx-vz 平面上のヒートマップ
result.plot_energy_spectrum(scale="log")

単一粒子:get_backtrace

get_backtrace(position, velocity, ispec=0, ...) は 1 本の軌跡を計算し、 :class:BacktraceResult を返します。戻り値はタプル展開もできます。

bt = data.backtrace.get_backtrace(position, velocity, ispec=0, max_step=50000)

ts, prob, positions, velocities = bt   # タプル展開
print(bt)                                # <BacktraceResult: n_steps=...>

属性

形状

意味

bt.ts

(N,)

時刻(EMSES 単位)

bt.probability

(N,)

到達確率の時系列

bt.positions

(N, 3)

[x, y, z]

bt.velocities

(N, 3)

[vx, vy, vz]

ショートハンドでの可視化

bt.pair(var1, var2) は 2 変数を切り出して :class:XYData を返します。 var1, var2 には t, x, y, z, vx, vy, vz が使え、連結 属性名 (bt.tx, bt.xvz, bt.yz, …) としても取り出せます。

bt.tx.plot()                 # = bt.pair("t", "x")
bt.xvz.plot()                # = bt.pair("x", "vz")
bt.yz.plot(color="black")    # 軌道の xy 投影

XYData.plot() はデフォルトで SI 単位に変換し、軸ラベルも自動生成します (use_si=False で EMSES 単位のまま表示できます)。gap=... を渡すと、 周期境界のジャンプなど長い飛びを NaN で切って線が繋がらないようにできます。

複数粒子:get_backtraces

get_backtraces(positions, velocities, ispec=0, n_threads=4, ...) は :class:MultiBacktraceResult を返します。positionsvelocities(N, 3) 配列であることが必要です。

ts, probs, pos_list, vel_list, last = many
many.xz.plot(alpha=np.clip(probs, 0, 1))    # 確率に応じた透明度
many.sample(50, random_state=0).tvx.plot()   # ランダムに 50 本だけ表示
many.sample(slice(0, 10)).tx.plot()         # 先頭 10 本を選ぶ

属性

形状

意味

ts_list

(N_traj, N_steps)

probabilities

(N_traj,)

最終到達確率

positions_list

(N_traj, N_steps, 3)

velocities_list

(N_traj, N_steps, 3)

last_indexes

(N_traj,)

有効データの終端 index(パディング用)

many.pair("t", "x") は :class:MultiXYData を返し、.plot() で 全軌跡を重ね描きします。alpha はスカラーでもスカラーの配列 (長さ N_traj)でも渡せるので、確率の重み付けに便利です。

Particle オブジェクトから直接実行

vdsolverf.core.Particle をすでに手元に持っている場合 (例えば ProbabilityResult.particles をそのまま流したい)、 get_backtraces_from_particles(particles, ...) が使えます。

from vdsolverf.core import Particle

particles = [Particle(p, v) for p, v in zip(positions, velocities)]
many = data.backtrace.get_backtraces_from_particles(particles, ispec=0)

ProbabilityResult から得られた粒子リストをそのまま渡して、 確率が高い粒子だけ軌跡を描く、といった連結が可能です:

result = data.backtrace.get_probabilities(...)
bt = data.backtrace.get_backtraces_from_particles(result.particles, ispec=0)
bt.xz.plot(alpha=np.clip(result.probabilities, 0, 1))

到達確率:get_probabilities

get_probabilities(x, y, z, vx, vy, vz, ispec=0, ...) は 6 次元の位相空間 グリッドを切って、各グリッド点から粒子をバックトレースし、到達確率を :class:ProbabilityResult として返します。

各軸は次のいずれかで指定できます:

  • (start, stop, n) のタプル — 等間隔グリッド

  • 明示的な配列やリスト — 任意の値

  • スカラー — サイズ 1 の軸(後で pair() すると自動的にスクイーズされます)

result = data.backtrace.get_probabilities(
    x=20.0, y=32.0, z=40.0,     # 位置は固定点
    vx=(-3e5, 3e5, 64),         # vx を 64 点で走査
    vy=0.0,
    vz=(-3e5, 3e5, 64),         # vz を 64 点で走査
    ispec=0,
    max_step=10000,
    n_threads=8,
)

2D ヒートマップへの射影

result.pair(var1, var2) は選ばれていない 4 軸を台形則で積分し、 :class:HeatmapData を返します。bt と同じく連結属性でもアクセスできます。

result.vxvz.plot(cmap="viridis")   # = result.pair("vx", "vz")
result.xvx.plot()                  # x-vx 平面
result.yz.plot(cmap="plasma")      # y-z 平面

HeatmapData.plot() はカラーバー付きの pcolormesh を描き、軸ラベルには SI 単位が付きます(use_si=False でグリッド単位に戻せます)。 追加のキーワード引数はすべて pcolormesh にそのまま渡されるので、 vmin / vmaxnorm=LogNorm(...) で配色を制御できます。 offsets=("center", 0) のように渡すと、軸をセンタリングしたり 数値シフトを掛けたりもできます。

エネルギースペクトル

plot_energy_spectrum(energy_bins=None, scale="log") は到達粒子の エネルギーフラックスヒストグラムを描きます。ビンは整数(本数)、 配列(ビン境界)のどちらも受け付けます。

result.plot_energy_spectrum(scale="log", energy_bins=80)

内部で plasma.inpwp や光電流設定から基準数密度 n0 を計算し、 各位相点の確率に掛けてから積分しています(nflag_emit == 2 の光電子は pathcurf から導出)。

ヒストグラム配列だけ欲しい場合

result.energy_spectrum(energy_bins=...)(hist, bin_edges) を返すので、 独自の加工や別ライブラリ(例:matplotlib の ax.step、seaborn)への 受け渡しに使えます。

リモート実行との連携

data.backtraceEmout ファサード経由で remote_open_kwargs を持ち、 emout server が動いていれば自動的にワーカー上で実行されます (結果は RemoteProbabilityResult / RemoteBacktraceResult プロキシとして返ります)。 ワーカー上にキャッシュされているので、プロットパラメータを変えても 再計算は走りません。

from emout.distributed import remote_figure

result = data.backtrace.get_probabilities(...)   # ワーカー上で 1 回だけ計算

with remote_figure():
    result.vxvz.plot(cmap="viridis")

with remote_figure():
    result.plot_energy_spectrum(scale="log")

result.drop()   # 不要になったらワーカーメモリを解放

明示的な remote 記法を使いたいときは data.remote().backtrace... に 切り替えます。返り値は同じ専用プロキシです:

from emout.distributed import remote_scope, remote_figure

with remote_scope():
    rdata = data.remote()
    bt = rdata.backtrace.get_backtrace(position, velocity, ispec=0)
    result = rdata.backtrace.get_probabilities(...)

    with remote_figure():
        bt.tx.plot()
        result.vxvz.plot()

リモート実行全体の仕組み・環境変数・サーバー起動については リモート実行ガイド を参照してください。

fetch() でローカルに取り出す

matplotlib で細かくカスタマイズしたい(独自アノテーション、共通カラーバー、 subplot への差し込み、など)場合は、fetch() で結果を小さなローカル オブジェクトに変換できます:

heatmap = result.vxvz.fetch()      # ローカルの HeatmapData
fig, ax = plt.subplots()
heatmap.plot(ax=ax, cmap="plasma")
ax.axhline(y=0, color="red", linestyle="--")

関連クラス

詳細なシグネチャは API リファレンス(emout.core.backtrace パッケージ)を参照してください。

  • BacktraceWrapperdata.backtrace の実体

  • BacktraceResult / MultiBacktraceResult — 軌跡コンテナ

  • ProbabilityResult — 6D 確率グリッドとヒートマップ射影

  • XYData / MultiXYData / HeatmapData — 可視化用の軽量コンテナ