MATLAB スクリプティング
1. イントロダクション
OghmaNano では、MATLAB スクリプティングはディスク上のシミュレーション設定ファイルを直接編集し、
その後シミュレーションエンジン(oghma_core.exe)を呼び出してモデルを実行することで機能します。
この節では、このワークフローについて詳しく説明します。
2. MATLAB でシミュレーションファイルを実行する
OghmaNano シミュレーションは、単一の JSON 設定ファイル
sim.json によって完全に定義されます。
このファイルには、デバイス構造、材料パラメータ、数値設定、および出力設定を含む、
シミュレーションの完全な状態が含まれています。
多くの場合、sim.json はすでにシミュレーションディレクトリ内の通常のファイルとして存在します。
MATLAB から OghmaNano を駆動する場合、スクリプトは MATLAB の
JSON サポートを用いて sim.json をメモリに読み込み、
1 つまたは複数のパラメータを変更し、更新された JSON を
ディスクへ書き戻します。
重要なのは、MATLAB が oghma_core.exe を、その作業ディレクトリを
シミュレーションディレクトリに設定した状態で実行しなければならないことです。
つまり、sim.json(および
sim.oghma)を含むディレクトリです。
シミュレーションエンジンは常に現在の作業ディレクトリから入力ファイルを読み込むため、
作業ディレクトリが誤っていると、シミュレーションは失敗するか、
誤ったモデルを実行してしまいます。
この役割分担は意図的かつ明示的です: MATLAB はシミュレーションの中で 何を 変更するかを定義し、 OghmaNano は現在のディレクトリに存在するファイルを用いて物理を 実行する 役割を担います。
以下の例はこのプロセスを示しています。sim.json を読み込み、
最初のデバイス層のキャリア移動度を変更し、
更新した設定をディスクへ書き戻し、
その後シミュレーションディレクトリからシミュレーションを実行します。
% Set this to your simulation folder (must contain sim.json / sim.oghma)
sim_dir = "C:\path\to\your\simulation";
% Read sim.json
sim_path = fullfile(sim_dir, "sim.json");
txt = fileread(sim_path);
data = jsondecode(txt);
% Edit a value: mobility of segment1 (same JSON path as the Python page)
data.epitaxy.segment1.shape_dos.mue_y = 1.0;
% Write sim.json back to disk
out = jsonencode(data);
fid = fopen(sim_path, "w");
fprintf(fid, "%s", out);
fclose(fid);
% Run the simulation (working directory must be the simulation folder)
orig_dir = pwd;
cd(sim_dir);
system("oghma_core.exe");
cd(orig_dir);
sim.json 内のシミュレーションが J–V 曲線を実行するよう設定されている場合、
OghmaNano は PCE、フィルファクタ、\(J_{sc}\)、
および \(V_{oc}\) などの量を含む出力ファイルを
シミュレーションディレクトリへ書き込みます。
3. 結果の抽出
多くの OghmaNano 出力ファイルは JSON 形式で書き込まれます。
一般的なパターンとして、sim_info.dat を読み込み、
スカラーの性能指標(例えば Voc)を抽出し、
後の解析のためにテキストファイルへ追記します。
以下の例では、sim_info.dat を読み込み、
\(V_{oc}\) の値を別のテキストファイルへ追記します。
% Must be run from (or pointed at) a completed simulation directory
sim_dir = "C:\path\to\your\simulation";
info_path = fullfile(sim_dir, "sim_info.dat");
txt = fileread(info_path);
info = jsondecode(txt);
voc = info.Voc;
% Append to a text file (one Voc per line)
out_path = fullfile(sim_dir, "out.dat");
fid = fopen(out_path, "a");
fprintf(fid, "%g\n", voc);
fclose(fid);
3. より複雑なシミュレーション
多くのスクリプティングワークフローでは、同じ基本シミュレーションを異なるパラメータ値で複数回実行し、 各実行を独自のディレクトリ内に分離して保持したい場合があります。 これは出力を整理し、結果を誤って上書きするのを避ける最も簡単な方法です。
以下の例では、4 つの移動度
(1e-5、1e-6、1e-7、1e-8)
に対応するディレクトリ群を作成します。各ディレクトリに対して以下を行います:
- ディレクトリを作成する(まだ存在しない場合)。
- 現在の
sim.jsonをそのディレクトリへコピーする。 - コピーした
sim.jsonを編集して、目標移動度を設定する。 - そのディレクトリへ作業ディレクトリを変更する。
- そのディレクトリ内でソルバーを実行し、その実行に固有の出力を生成する。
このパターンは OghmaNano におけるバッチスクリプティングの基礎です: 1 実行につき 1 ディレクトリ、1 実行につき 1 設定ファイル、 そして各パラメータ値に対して整理された出力フォルダです。
% The script should be started in the base simulation directory
% (i.e. the directory containing the reference sim.json).
base_dir = pwd;
mobilities = [1e-5, 1e-6, 1e-7, 1e-8];
src_sim = fullfile(base_dir, "sim.json");
for k = 1:numel(mobilities)
mu = mobilities(k);
run_name = sprintf("mu_%.0e", mu);
run_dir = fullfile(base_dir, run_name);
if ~exist(run_dir, "dir")
mkdir(run_dir);
end
% Copy the base sim.json into the run directory
dst_sim = fullfile(run_dir, "sim.json");
copyfile(src_sim, dst_sim);
% Load the copied sim.json and edit the mobility in THAT copy
txt = fileread(dst_sim);
data = jsondecode(txt);
data.epitaxy.segment1.shape_dos.mue_y = mu;
out = jsonencode(data);
fid = fopen(dst_sim, "w");
fprintf(fid, "%s", out);
fclose(fid);
% Change into the run directory and execute the solver there
cd(run_dir);
system("oghma_core.exe");
cd(base_dir);
end
4. 結果の抽出
別々の実行ディレクトリ(例えば mu_1e-05、
mu_1e-06、...)でシミュレーション群を実行した後、
次のステップは各実行から主要な性能指標を抽出し、その傾向を可視化することです。
一般的な例として、各ディレクトリ内の sim_info.dat から
開放電圧 \(V_{oc}\) を読み取り、
逆移動度 \(1/\mu\) に対してプロットします。
以下のスクリプトは、
移動度 1e-5、1e-6、1e-7、1e-8
に対応する固定の実行ディレクトリ群を走査し、各ディレクトリに対して以下を行います:
sim_info.datを JSON として読み込む。Vocを抽出する。- そのディレクトリに対応する移動度を用いて \(1/\mu\) を計算する。
- 小さな要約ファイル(
voc_vs_inv_mobility.dat)を書き出す。 - \(V_{oc}\) を \(1/\mu\) に対してプロットする。
% Run this from the BASE directory that contains the run folders
% (mu_1e-05, mu_1e-06, ...). If not, set base_dir explicitly.
base_dir = pwd;
% Mobilities must match the run directories you generated earlier
mobilities = [1e-5, 1e-6, 1e-7, 1e-8];
inv_mu = zeros(size(mobilities));
vocs = zeros(size(mobilities));
for k = 1:numel(mobilities)
mu = mobilities(k);
run_dir = fullfile(base_dir, sprintf("mu_%.0e", mu));
info_path = fullfile(run_dir, "sim_info.dat");
if ~exist(info_path, "file")
error("Missing sim_info.dat in: %s", run_dir);
end
txt = fileread(info_path);
info = jsondecode(txt);
vocs(k) = info.Voc;
inv_mu(k) = 1.0 / mu;
end
% Write a small summary table to disk
out_path = fullfile(base_dir, "voc_vs_inv_mobility.dat");
fid = fopen(out_path, "w");
fprintf(fid, "# inv_mobility(1/mu) Voc(V)\n");
for k = 1:numel(inv_mu)
fprintf(fid, "%.6e %g\n", inv_mu(k), vocs(k));
end
fclose(fid);
% Plot Voc against inverse mobility
figure;
plot(inv_mu, vocs, "o-");
xlabel("Inverse mobility (1/\mu)");
ylabel("Open-circuit voltage V_{oc} (V)");
title("V_{oc} vs inverse mobility");
grid on;