自动化与脚本
用户通常会搭建一个仿真结构来代表真实器件,但随后会提出这样的问题:当我改变有源层的迁移率时,我的太阳能电池效率会发生什么?或者当我改变量子阱的厚度时,我的激光器输出波长会发生什么?要回答这类问题,就必须在一定范围内改变一个或多个材料参数,然后检查仿真结果。显然这可以手工完成,但有更好的方式来自动化这个过程。
有三种主要方式可以自动化 OghamNano 仿真:
-
第一种方法是使用参数扫描窗口,如下在 17.1 节所述。参数扫描窗口允许用户通过图形用户界面以步进方式改变一个参数(或多个参数)。这种方法不需要任何编码知识。如果你想快速考察某个参数如何影响结果,并且所研究的场景并不复杂,那么参数扫描窗口就很有用。大多数时候,扫描窗口能够满足大多数用户的需求。
-
为了对参数如何变化获得更细粒度的控制,下一种方法是使用 Python 脚本,如 17.4 节所述。Python 脚本为用户提供了在调整所有仿真参数并运行仿真方面的终极灵活性,而且 Python 广泛可用,使这种方法非常有吸引力。
-
第三种方式是通过 MATLAB 脚本,如 17.4.3 节所述。MATLAB 脚本的优势是很多人会用 MATLAB 编码,因此使 OghmaNano 的自动化非常容易上手。使用 MATLAB 的缺点是它相当昂贵,并且并非所有人都能使用。MATLAB 的替代方案是 Octave,不过在撰写本文时它还没有 json 读写器。
或者选项 4:以上所有方法都依赖相同的原理:系统性地编辑 OghmaNano 的仿真保存文件,并在 sim.oghma 文件上运行软件后端 \(oghma\_core.exe\) 以生成新的结果。理解脚本工作方式的关键在于认识到 sim.oghma 只是一个 zip 文件(见 14.1),其中包含一个 json 文件(sim.json)。只要你能编辑 json 文件(使用任何你想要的语言:ActionScript、C、C++、C#、Cold Fusion、Java、Lisp、Perl、Objective-C、OCAML、PHP、Python、Ruby 等……),你就可以自动化 OghmaNano。
参数扫描窗口
相关 YouTube 视频:
![]() |
在 OghmaNano 中使用参数扫描工具 |
系统性地改变仿真参数最直接的方法是使用扫描窗口。在这个示例中,我们将系统性地改变 PM6:Y6 太阳能电池有源层的迁移率,你可以在示例仿真中的 Scripting and fitting/Scan demo (PMY:Y6 OPV) 下找到该示例。定位并打开该仿真后,你需要调出参数扫描窗口,这可以通过点击自动化功能区中的 Parameter scan 图标来完成(见图 [fig:parameter_scan_icon])。然后通过点击 new scan 按钮(1)创建一个新的扫描(在示例仿真中,这一步已经为你完成)。通过双击表示该扫描的图标(2)打开新扫描,见图 [fig:newscan]。这将弹出扫描窗口,见图 17.1。
[fig:parameter_scan_icon]
[fig:newscan]
改变一个材料参数
当 扫描窗口 打开后,通过点击图 17.1 中的加号图标(1)新建一条扫描线,然后选择该行使其高亮(2),再点击三个点(3)来选择要扫描的参数。如果你使用示例仿真,这些也已经为你完成。
在此示例中,我们将选择 PM6:Y6 太阳能电池的电子迁移率。请导航到 epitaxy\(\rightarrow\) PM6:Y6\(\rightarrow\) Drift diffusion\(\rightarrow\) Electron mobility y。高亮该参数并点击 OK。随后它应当出现在扫描线中。epitaxy\(\rightarrow\) PM6:Y6\(\rightarrow\) Drift diffusion\(\rightarrow\)Electron mobility y 的含义如下:
-
epitaxy:.oghma 文件中的所有参数都通过参数选择窗口暴露出来,见 [fig:scanselect]。该文件是树状结构,见 14.1。器件结构在 epitaxy 标题下定义。
-
PM6:Y6:在 epitaxy 之下,器件的每一层都以其名称给出。该器件中的有源层称为 PM6:Y6;如果你的有源层叫 Perovskite 或 P3HT:PCBM,你就应当选择相应名称。
-
Drift diffusion:所有电学参数都存储在子标题 drift diffusion 之下。
[fig:scanselect]
-
Electron mobility y:可以在 z、x 和 y 方向定义各向异性迁移率——这对 OFET 仿真很有用。然而默认情况下,模型假设迁移率各向同性,即在所有方向相同。该数值由 Electron mobility y 定义。
接下来输入你要扫描的迁移率取值,在此我们输入 1e-5 1-6 1e-7 1e-8 1e-9(见图 17.2 1),然后点击 run scan(见图 17.2 2)。OghmaNano 将在你的计算机每个核心上运行一个仿真,直到所有仿真完成。
要查看仿真结果,点击 output 选项卡,这将显示仿真输出,见图 17.3。你可以看到为我们扫描的每个变量值都创建了一个目录,即 1e-5、1e-6、1e-7、1e-8 和 1e-9。如果你查看每个目录内部,它将是基础仿真目录的精确副本。如果你双击包含多色 JV 曲线的文件(见图 17.3 中红色框),OghmaNano 将自动把每次仿真的所有曲线绘制在同一张图中,见图 17.4。
复制参数——改变有源层厚度
很多时候我们希望改变一个参数,然后把另一个参数设置为与被改变参数相同的值。一个例子是当模拟具有对称迁移率的器件时,可能希望同时改变电子与空穴迁移率。这可以通过扫描窗口的 duplicate 功能实现,如图 17.5 所示。在此示例中,我们处理一个比同时改变迁移率更棘手的问题:我们将改变有源层的物理厚度,同时调整电学网格使其匹配。正如在 8 节讨论的那样,有源层的厚度必须始终与电学网格的厚度一致。当你在层编辑器中手动改变层厚度时,OghmaNano 会为你更新电学网格的厚度。但当你通过脚本驱动模型时,它不会为你进行该更新。因此在下面示例中,我们将通过扫描以下参数来设置有源层厚度:
epitaxy\(\rightarrow\)PM6:Y6\(\rightarrow\)dy
of the object
然后我们将在其下再添加一行,并在 parameter to scan 选择
mesh\(\rightarrow\)mesh_y\(\rightarrow\)segment0\(\rightarrow\)len
并将其设置为
epitaxy\(\rightarrow\)PM6:Y6\(\rightarrow\)dy of
the object
在 operation 下拉框中。你会看到在 values 下出现 duplicate 一词。
如果你现在运行仿真,“epitaxy\(\rightarrow\)PM6:Y6\(\rightarrow\)dy of the object” 将被改变,而 “mesh\(\rightarrow\)mesh_y\(\rightarrow\)segment0\(\rightarrow\)len” 将随之变化。
旁注:具有多个有源层的器件
有源层厚度之和(在层编辑器中定义)必须等于电学网格厚度(网格更多内容见 8)。例如,若有三个有源层 TiO2(100 nm)/Perovskite(200 nm)/Spiro(100 nm),总厚度为 400 nm,则网格总长度也必须为 400 nm。因此,若要像 17.5 那样改变钙钛矿层厚度,就必须把电学网格分成三段,并确保你更新的是仅对应钙钛矿层的网格段。
设置常数
在运行参数扫描时,经常需要设置一个常数值,这可以通过 Operations 下拉菜单中的 “constant” 选项完成。见图 17.6
循环的等价物
在扫描一个参数范围时,有时可能需要模拟的参数数量太多而不便手工输入。在这种情况下,OghmaNano 提供了等价于循环的机制。例如,如果想把一个值从 100 以步长 1 变化到 400,可以输入
[100 400 1]
扫描窗口的局限性
尽管扫描窗口方便,能够快速扫描仿真参数,但其灵活性在本质上是有限的。如果你想进行复杂扫描(同时改变多个参数),或以编程方式从每次仿真中收集数据,那么你可以使用 OghmaNano 的 python 或 matlab 接口。这些将在后续章节中介绍。
多参数器件优化器
相关 YouTube 视频:
![]() |
优化钙钛矿太阳能电池的层结构 |
![]() |
为最大化光子收集而优化 OPV 器件。 |
![]() |
搜索有机太阳能电池的最优层结构。 |
在优化器件时,工程师或科学家经常想知道器件的最优结构是什么。例如,钙钛矿太阳能电池由多层构成,那么每一层的最优厚度是多少?如果钙钛矿层做得很厚,会吸收更多光,但缺点是载流子逃逸出器件所需时间更长,因此复合会更高。相反,如果该层做得很薄,载流子在器件中停留时间短,发生复合的机会就更少,但缺点是由于层太薄,最初吸收的光子数量也会更少。若进一步考虑光会在器件界面处多次反射并形成驻波图样,这将使优化问题更为复杂,因为不仅需要优化钙钛矿层厚度,还需要同时优化所有其他层的厚度。为解决这个多参数优化问题,可以使用扫描窗口中的 Fast optimizer。
使用多参数优化器
在新建仿真窗口的子主题 Scripting and fitting 下,有若干多参数优化器示例:
-
Electrical layer optimizer:这将改变有机太阳能电池仿真中两个有源层的层厚,并将 PEC/FF/Voc 作为这些层厚的函数绘制出来。
-
Optical layer optimizer (perovskite):这将改变钙钛矿太阳能电池中两层的厚度,并绘制器件中每一层产生的电流。
-
Optical layer optimizer (OPV):这将改变钙钛矿太阳能电池中两层的厚度,并绘制器件中每一层产生的电流。
在本文中我们将使用 Optical layer optimizer (perovskite)。打开该仿真并进入扫描窗口,你会看到一个已设置好的扫描,名为 optimizer。打开后将得到图 17.7 所示窗口。该扫描窗口看起来与上一节描述的扫描窗口相同,但关键差异是 Fast optimizer 按钮被按下。当该按钮被按下时,扫描结果不会写入磁盘,而是将关键仿真参数制表并在仿真结束时保存到磁盘。注意此示例中我们以 10 nm 的步长把钙钛矿层厚度(dy)在 300nm 到 500 nm 之间变化,并以 10 nm 的步长把 TiO2 层厚度(dy)在 100 nm 到 300 nm 之间变化。尝试运行该仿真,然后使用 Windows 资源管理器进入你的仿真目录,打开名为 optimize 的文件夹,在其中你会找到一个名为 optimizer_output.csv 的 csv 文件。用 Excel 或 LibreOffice 打开它,会呈现为图 17.8 的样子。
如果你仔细查看图 17.8,可以看到前两列标记为 epitaxy.layer2.dy 和 epitaxy.layer1.dy。这些就是我们在扫描窗口中决定改变的层厚。对于器件中每个后续层,都有两列,标记为 layerX/light_frac_photon_generation 和 layerX/J。它们分别表示该层内吸收的光的比例以及如果该层内吸收的所有光都转化为电流时该层能够产生的最大电流。显然,如果光在有源层内被吸收,它有很大机会转化为电流;但如果光在后金属电极中被吸收,那么该光转化为电流的机会很小。使用 Excel/LibreOffice 自带的排序工具,你可以找出哪些器件结构产生的电流最大。
在 LibreOffice 中打开(你也可以使用 Excel)。 [fig:device_optimizer_output]
OghmaNano 的 Python/MATLAB 脚本
脚本提供了一种与 gvpdm 交互的更强大方式。你可以不使用图形用户界面,而使用你最喜欢的编程语言与 OghmaNano 交互。这样你就可以以比仅靠图形界面更强大的方式驱动仿真。下面给出了使用 MATLAB 和 python 驱动 OghmaNano 的示例,但你也可以使用任何你想要的、具备 json 读写器的语言。Pearl 和 Java 是两个很容易想到的语言。
在开始编写 OghmaNano 脚本之前,你需要告诉 Windows OghmaNano 安装在哪里。默认情况下, OghmaNano 会安装到 C:\Program files x86 \OghmaNano,在该目录中你会看到两个 Windows 可执行文件:一个叫 oghma.exe,这是图形用户界面;另一个叫 oghma_core.exe。你可以在没有 oghma.exe 的情况下从命令行运行 oghma_core.exe。你只需进入包含 sim.oghma 文件夹的目录并调用 oghma_core.exe,这可以从 Windows 命令行、matlab、python 或任何其他脚本语言完成。然而在 Windows 上这么做之前,你需要将 C:\Program files x86 \OghmaNano 添加到 Windows 的 PATH 中,使 Windows 知道 OghmaNano 安装在哪里。现代版本 Windows 的一个示例做法见链接 https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee537574(v=office.14)
Windows 的每个新版本似乎都会移动配置选项,因此你可能需要为你的 Windows 版本寻找对应的说明。
Python 脚本
相关 YouTube 视频:
![]() |
Python 脚本:钙钛矿太阳能电池仿真 |
通过 python 与 .oghmafiles 交互有两种方式:使用原生 python 命令,或使用 OghmaNano 的类结构。下面给出了两者的示例。
原生 python 方法
如 14.1 节所述,.oghmafiles 本质上是将 json 文件打包压缩在归档中。如果你从 sim.oghmafile 中提取出 sim.json 文件,就可以使用 Python 的 json 读写代码直接编辑 .json 配置文件,这是一种快速而粗糙、但可行的方法。然后你可以用 \(os.system\) 调用运行 oghma_core 来执行 OghmaNano。
例如,如果想把第 1 个器件层的迁移率改为 1.0 并运行仿真,可以使用列表 [python-example] 中给出的代码。
import json
import os
import sys
f=open('sim.json') #open the sim.json file
lines=f.readlines()
f.close()
lines="".join(lines) #convert the text to a python json object
data = json.loads(lines)
#Edit a value (use firefox as a json viewer
# to help you figure out which value to edit)
# this time we are editing the mobility of layer 1
data['epitaxy']['layer1']['shape_dos']['mue_y']=1.0
#convert the json object back to a string
jstr = json.dumps(data, sort_keys=False, indent='\t')
#write it back to disk
f=open('sim.json',"w")
f.write(jstr)
f.close()
#run the simulation using oghma_core
os.system("oghma_core.exe")
如果 sim.json 中的仿真被设置为运行 JV 曲线,则会在仿真目录中写出一个名为 sim_data.dat 的文件,其中包含 PCE、填充因子、\(J_{sc}\) 与 \(V_{oc}\) 等参数。该文件同样是一个原始 json 文件。要用 python 读取该文件,并把 \(V_oc\) 的值写到另一个文件中,可使用列表 [python-example2] 中给出的代码。
f=open('sim_info.dat')
lines=f.readlines()
f.close()
lines="".join(lines)
data = json.loads(lines)
f=open('out.dat',"a")
f.write(str(data["Voc"])+"\n");
f.close()
PyOghma
如 17.4.1 节所述,通过 python 直接操作 OghmaNano 的 json 文件可以走得很远。然而,用这种方法并不能(很容易)同时运行多个仿真。并且由于大多数现代 CPU 有 8 个或更多核心,不在生成大型数据集时同时运行多个仿真似乎是一种浪费。此外,在 Python 中操作 json 文件并不直观,也不太“Python”。因此 Cai Williams 编写了一个名为 PyOghma 的 API,可用于操作 OghmaNano 的 json 文件并运行仿真。这是一个独立于 OhgmaNano 的项目,因此任何问题请直接向他咨询!
PyOghma 可在 GitHub 上获得,也可通过 pip 安装:
python -m pip install PyOghma
下面给出一个使用 PyOghma 的示例 [python-example3]:
import PyOghma as po
Oghma = po.OghmaNano()
Results = po.Results()
source_simulation = "\exapmle\pm6y6\"
Oghma.set_source_simulation(source_simulation)
experiment_name = 'NewExperiment'
Oghma.set_experiment_name(experiment_name)
mobility = 1e-5
trap_desnsity = 1e-18
trapping_crosssection = 1e-20
recombination_crosssection = 1e-20
urbach_energy = 40e-3
temperature = 300
intensity = 0.5
experiment_name = 'NewExperiment' + str(1)
Oghma.clone('NewExperiment0')
Oghma.Optical.Light.set_light_Intensity(intensity)
Oghma.Optical.Light.update()
Oghma.Thermal.set_temperature(temperature)
Oghma.Thermal.update()
Oghma.Epitaxy.load_existing()
Oghma.Epitaxy.pm6y6.dos.mobility('both', mobility)
Oghma.Epitaxy.pm6y6.dos.trap_density('both', trap_desnsity)
Oghma.Epitaxy.pm6y6.dos.trapping_rate('both', 'free to trap',..
trapping_crosssection)
Oghma.Epitaxy.pm6y6.dos.trapping_rate('both', 'trap to free',..
recombination_crosssection)
Oghma.Epitaxy.pm6y6.dos.urbach_energy('both', urbach_energy)
Oghma.Epitaxy.update()
Oghma.add_job(experiment_name)
Oghma.run_jobs()
在该示例中,PyOghma 以 po 导入,并通过改变迁移率、陷阱密度、俘获速率与 Urbach 能量来操作源 OghmaNano json 文件。原始文件通过以下语句被克隆:
Oghma.clone('NewExperiment0')
然后在代码末尾有如下两行。第一行将该任务添加到 PyOghma 的任务列表中;第二行让 PyOghma 执行所有任务。若任务不止一个,PyOghma 会在所有 CPU 上并行执行多个任务,直到全部完成。例如,如果想运行不同迁移率取值的一系列仿真,你会把每个仿真添加到任务列表中,然后只调用一次 \(run\_jobs\),即可高效地在所有核心上运行这些任务。
Oghma.add_job(experiment_name)
Oghma.run_jobs()
关于 PyOghma 的更多信息可在 GitHub 页面上获得。
MATLAB 脚本
如 14.1 节所述,OghmaNano 仿真以 .json 文件形式存储,并在 zip 归档中压缩。Matlab 既有 zip 解压器也有 json 解码器。因此,在 MATLAB 中编辑与读取 .oghma 文件是很直接的。你可以使用 MATLAB 执行相当复杂的参数扫描。下面在列表 [matlab-example] 中给出的示例脚本演示了如何运行多个仿真,使迁移率从 1e-7 变化到 1e-5 \(m^{2}V^{-1}s^{-1})\)。脚本首先解压 sim.json 文件;如果你已经从 sim.oghma 文件中提取出 sim.json,则不需要这些行。随后代码使用 MATLAB 的 json 解码器 \(jsondecode\) 读取 sim.json。接着创建一个与迁移率值对应的新目录,并将 sim.oghma 文件复制到该目录中。然后将 \(json\_data.epitaxy.layer0.shape\_dos.mue_y\) 设置为所需迁移率,并使用 \(jsonencode\) 与 \(fopen,fprintf,fclose\) 保存仿真。随后使用 \(system\) 调用运行 \(oghma\_core.exe\) 以执行仿真。输出参数如 \(J_{sc}\) 会存储在 sim_data.dat 中,同样为 json 格式,见 4.1.4 节,尽管这个简单脚本并未读取该文件。
if exist("sim.oghma", 'file')==false
sprintf("No sim.oghma file found"); %Check if we have a sim.oghma file
end
if exist("sim.json", 'file')==false
unzip("sim.oghma") %if we don't have a sim.json file
%try to extract it
end
A = fileread("sim.json"); %Read the json file.
json_data=jsondecode(A); %Decode the json file
mobility=1e-7 %Start mobility
origonal_path=pwd %working with the current dir
base_dir="mobility_scan" %output directory name
while(mobility<1e-5)
dir_name=sprintf("%e",mobility);
full_path=fullfile(origonal_path,base_dir,dir_name) %join paths
mkdir(full_path) %make the dir
cd(full_path) %cd to the dir
%Update the json mobility
json_data.epitaxy.layer0.shape_dos.mue_y=mobility %Change mobility
%of layer0
copyfile(fullfile(origonal_path,"sim.oghma"),\\
fullfile(origonal_path,base_dir,dir_name,"sim.oghma"))
%now write the json file back to disk
out=jsonencode(json_data);
json_data
fid = fopen("sim.json",'w');
fprintf(fid, '%s', out);
fclose(fid);
%run oghma - This won't work if you have not added the oghma
%install directory to your windows paths
system("oghma_core.exe")
%Multiply mobility by 10
mobility=mobility*10;
end
%Move back to the original dir
cd(origonal_path)
内置计算器
OghmaNano 具有一个内置计算器,可用于执行简单的数学运算。该功能对于设置拟合流程以及在代码各处定义方程很有用。该计算器基于逆波兰表示法(RPN)。
支持的运算
| 运算 | 运算符 | 示例 |
|---|---|---|
| 乘方 | ^ | 2 ^3 = 8 |
| 乘法 | \(*\) | \(2 * 3 = 6\) |
| 除法 | \(/\) | \(6 / 2 = 3\) |
| 加法 | \(+\) | \(2 + 3 = 5\) |
| 减法 | \(-\) | \(5 - 3 = 2\) |
| 大于 | \(>\) | \(5 > 3\) is 1 |
| 小于 | \(<\) | \(2 < 5\) is 1 |
| 大于或等于 | \(>=\) | \(5 >= 5\) is 1 |
| 小于或等于 | \(<=\) | \(3 <= 4\) is 1 |
支持的函数
| 函数名称 | 函数 | 示例 |
|---|---|---|
| 正弦 | sin | \(\sin(\pi/2) = 1\) |
| 余弦 | cos | \(\cos(0) = 1\) |
| 绝对值 | abs | \(\text{abs}(-3) = 3\) |
| 正值 | pos | \(\text{pos}(-3) = 0, \text{pos}(3) = 3\) |
| 对数(以 10 为底) | log | \(\log(100) = 2\) |
| 指数 | exp | \(\exp(2) = e^2\) |
| 平方根 | sqrt | \(sqrt(9) = 3\) |
| 最小值 | min | \(\min(2, 3) = 2\) |
| 最大值 | max | \(\max(2, 3) = 3\) |
| 随机数 | rand | \(\text{rand}(a, b)\) 生成介于 \(a\) 与 \(b\) 之间的随机数 |
| 对数随机数 | randlog | \(\text{randlog}(a, b)\) 生成介于 \(a\) 与 \(b\) 之间的对数随机数 |
