自动化与脚本
用户通常会搭建一个仿真结构来代表真实器件,但随后会提出这样的问题:当我改变有源层的迁移率时,我的太阳能电池效率会发生什么?或者当我改变量子阱厚度时,我的激光输出波长会发生什么?要回答这类问题,就必须在一定范围内改变一个或多个材料参数,然后检查仿真结果。显然这可以手工完成,但也有更好的方式来自动化该过程。
自动化 OghamNano 仿真主要有三种方式:
-
第一种方法是使用参数扫描窗口,下面在 17.1 节中进行了说明。参数扫描窗口允许用户通过图形界面以步进方式改变一个参数(或多个参数)。这种方法不需要任何编程知识。如果你想快速查看某个参数如何影响结果,并且所研究的情景并不复杂,那么参数扫描窗口非常有用。大多数情况下,扫描窗口都能满足大多数用户的大多数需求。
-
若需要对参数变化方式进行更细粒度的控制,下一种方法是使用 Python 脚本,这在 17.4 节中进行了说明。Python 脚本为用户在调整全部仿真参数并运行仿真方面提供了终极灵活性;Python 广泛可用,使得该方法非常有吸引力。
-
第三种方式是通过 MATLAB 脚本,这在 17.4.3 节中进行了说明。MATLAB 脚本的优点是很多人会用 MATLAB 编程,因此使得自动化 OghmaNano 非常易用。使用 MATLAB 的缺点是它相当昂贵,并非所有人都能使用。MATLAB 的替代方案是 Octave,但在写作时它还没有 json 读写器。
或选项 4:上述所有方法都依赖相同的原理:系统性地编辑 OghmaNano 仿真保存文件,然后运行软件后端 \(oghma\_core.exe\) 对 sim.oghma 文件执行计算以生成新结果。理解脚本工作方式的关键在于认识到 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。
复制参数——更改有源层厚度
很多时候你希望更改一个参数,然后将另一个参数设置为与被更改参数相同的值。一个例子是在模拟具有对称迁移率的器件时,希望同时改变电子与空穴迁移率。这可以使用扫描窗口的复制功能实现,如图 17.5 所示。在本例中,我们处理一个比同步改变迁移率稍微更棘手的问题:我们将改变有源层的物理厚度,同时调整电学网格以使其匹配。如在 8 节所讨论,有源层厚度必须始终与电学网格厚度匹配。当你在图层编辑器中手动更改图层厚度时,OghmaNano 会为你更新电学网格厚度。但在脚本方式下模型不会为你完成该更新。因此在下面示例中,我们将通过扫描以下参数来设置有源层厚度:
epitaxy\(\rightarrow\)PM6:Y6\(\rightarrow\)dy
of the object
然后我们将在其下再添加一行,并在要扫描的参数中选择
mesh\(\rightarrow\)mesh_y\(\rightarrow\)segment0\(\rightarrow\)len
并在操作下拉框中将其设为
epitaxy\(\rightarrow\)PM6:Y6\(\rightarrow\)dy of
the object
你会看到在 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 变到 400,步长为 1,可以输入
[100 400 1]
扫描窗口的局限性
尽管扫描窗口提供了快速扫描仿真参数的便捷方式,但在灵活性方面它天生较为有限。如果你想进行复杂扫描(例如同时改变多个参数),或以编程方式从每次仿真中收集数据,那么你可以使用 Python 或 MATLAB 接口来驱动 OghmaNano。这些将在后续章节中描述。
多参数器件优化器
相关 YouTube 视频:
![]() |
优化钙钛矿太阳能电池的层结构 |
![]() |
将 OPV 器件优化到最大光子收集。 |
![]() |
搜索有机太阳能电池的最优层结构。 |
在优化器件时,工程师或科学家常常想知道器件的最优结构是什么。例如,钙钛矿太阳能电池由多层组成,但每一层的最优厚度是多少?如果钙钛矿层做得很厚,将会吸收大量光,但缺点是电荷载流子需要更长时间才能从器件中逸出,因此复合会很高。相反,如果该层做得很薄,载流子在器件中停留时间很短,复合机会较少,但缺点是一开始吸收的光子就不多,因为该层很薄。若再考虑光会在器件界面多次反射形成驻波图样,这将进一步使优化问题复杂化,因为你不仅需要优化钙钛矿层厚度,还需要同时优化所有其他层的厚度。要解决这个多参数优化问题,可以使用扫描窗口中的 Fast optimizer。
使用多参数优化器
在新建仿真窗口的子主题 Scripting and fitting 下,有若干多参数优化器示例:
-
电学层优化器:将改变有机太阳能电池仿真中两个有源层的厚度,并绘制 PEC/FF/Voc 随这些层厚度变化的关系。
-
光学层优化器(perovskite):将改变钙钛矿太阳能电池中两层的厚度,并绘制器件中每一层产生的电流。
-
光学层优化器(OPV):将改变钙钛矿太阳能电池中两层的厚度,并绘制器件中每一层产生的电流。
本文将使用 Optical layer optimizer (perovskite)。如果你打开该仿真并进入扫描窗口,你会看到一个已设置好的扫描,名为 optimizer。打开它会得到图 17.7 所示的窗口。该扫描窗口看起来与上一节描述的扫描窗口类似,但关键区别在于 Fast optimizer 按钮被按下。当该按钮被按下时,扫描结果不会写入磁盘;相反,关键仿真参数会被制表并在仿真结束时保存到磁盘。注意在此示例中,我们把 Perovskite 层厚度(dy)在 300nm 到 500 nm 之间以 10 nm 步长变化,并把 TiO2 层厚度(dy)在 100 nm 到 300 nm 之间也以 10 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 仿真存储在 zip 归档内的 .json 文件中。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\) 为 1 |
| 小于 | \(<\) | \(2 < 5\) 为 1 |
| 大于或等于 | \(>=\) | \(5 >= 5\) 为 1 |
| 小于或等于 | \(<=\) | \(3 <= 4\) 为 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\) 之间的对数随机数 |
