学习自旋电子学的笔记02:OOMMF的报错和部分功能详述



一点浩然气,千里快哉风。——苏轼 《水调歌头·黄州快哉亭赠张偓佺》


前言

又是一个三月=31天=744小时=44640分钟过去了呀,time is going too fast。是该每月总结的时候了,但我也不晓得该写点啥来敷衍一下哈,复制别人文章里面的一些内容,不不不,太明目张胆的抄是不好的,分享看过的文章,不不不,我自己都看不太明白,还是别误导读者了。思索良久之后,还是决定写点关于本人在使用OOMMF软件过程中遇到的各种问题吧,毕竟这些关于软件的问题不涉及高深的理论,是一目了然的,肯定不会误导读者的。同时也包含有些童鞋私信遇到的各种软件问题,这里建议读者有疑惑的在评论区提问,别老是私信,如此让大家都能看到不解的地方,避免后面的人重复踩坑。本文分为4个部分:①OOMMF使用之前就遇到的问题(即主要是编译的问题)②OOMMF软件部分功能的详细描述和使用过程中报错③MIF文件内容即Tcl代码中的一些问题④几个简单的示例。

一、OOMMF使用之前就遇到的问题(即主要是编译的问题)

有两种情况需要用户自己在计算机上重新编译OOMMF软件:①当OOMMF软件不能在计算机上正常运行,特别是用户已经按照第0章描述的那样操作了,还是不能运行该软件的情况。②当用户需要为OOMMF添加额外的第三方扩展模块的情况。下面分别介绍这两种情况:

1.不能运行OOMMF,需要重新编译

有的用户按照第0章描述的的步骤,在正确安装Tcl/Tk环境后,并且下载OOMMF软件包之后,双击oommf.tcl想运行,却弹出一个错误弹窗(错误类型包括文件缺失,平台识别错误,通信出错等,若用户实在是自己排除不了问题,则建议重新在自己的计算机上编译一遍OOMMF。
虽然在教程手册里清晰明白的给出了编译流程,但为了凑够本文的篇幅,我还是手把手演示一遍吧。
在这里插入图片描述
正如手册所述的那样,编译OOMMF需要三样东西,缺一不可:正确的Tcl/Tk环境,c++编译器,OOMMF软件包。

(一)
首先检查自己是否已经正确安装了Tcl/Tk环境,在任意目录打开Windows的命令窗口,输入命令“tclsh”,如下图:
在这里插入图片描述
看到命令提示符从“>”变成"%",就说明正确安装了Tcl/Tk环境,若提示识别不了命令,在确定不是由于环境变量Path导致的问题后,用户仍然解决不了的,则建议按照第0章描述的的步骤重装ActiveTcl。

(二)
其次是计算机上要有c++编译器,手册里提到了在Windows平台的好几个c++编译器,但目前为止我亲手使用过的就只有两个:cl和g++,我相信大多数读者也是只对这两个程序比较熟悉。cl是VS系列IDE自带的c++编译器,g++是CodeBlocks自带的c++编译器。 本人按照手册的说明,使用VS2019自带的cl程序来编译OOMMF软件,但是当编译到oommf/pkg目录时总是提示找不到“error.h”头文件,在尝试了各种方法后依然不知怎么解决,所以换了g++来编译oommf,很快啊,一会儿就编译好了。 所以接下来就按照使用g++来编译一步一步的讲解:
①用户若是第一次安装OOMMF,在OOMMF安装目录下打开命令窗口,输入命令“tclsh oommf.tcl +platform”,一般都是这种情况:
在这里插入图片描述
它提示用户找不到c++编译器“cl”程序,这说明你计算机上还没有c++编译器,需要去下载一个c++编译器。由于本人使用VS2019自带的cl编译失败,所以下载CodeBlocks安装包的同时把附带的g++也给下载了,点击下载CodeBlocks最新版,注意选择下载名称中带“mingw”的版本。安装很简单,这里省略安装过程,安装完成后需要为环境变量Path添加g++程序所在的目录,如下图:
在这里插入图片描述
在任意目录打开命令窗口,输入命令“g++ -v”,出现类似下图的输出就说明已经设置好g++程序了:
在这里插入图片描述
②接着,按照手册所述的那样,修改配置文件来选择使用“g++”来编译oommf,而不是使用默认的“cl”程序编译。即需要在oommf的配置文件oommf\config\platforms\windows-x86_64.tcl中,将第103行注释掉,并把第106行取消注释,如下图:
在这里插入图片描述
③如此,重新在OOMMF安装目录下打开命令窗口,输入命令“tclsh oommf.tcl +platform”,于是输出如下:
在这里插入图片描述
出现这种样式的内容,就说明OOMMF已经能正确识别并使用你的c++编译器了。

(三)
最后按照手册所述的那样,按顺序输入命令调用pimake即可。即先输入命令“tclsh oommf.tcl pimake distclean -d”,等待清除所有旧的已编译的无用文件:
在这里插入图片描述
接着输入命令“tclsh oommf.tcl pimake -d”开始重新编译OOMMF,此过程需要十多分钟完成,整个编译过程不会报错,编译完成效果如下图:
在这里插入图片描述
注意:使用自己编译的OOMMF软件会出现一些小问题,比如:①在3D求解器Osxii中单击“Reload”按钮来重新加载问题时会报错并退出Osxii,原因尚不可知,不过并不影响使用。②当mif文件中有语法错误时,Osxii虽然会加载错误,但并不会出现错误提示框来显示代码出错的位置,Osxii只会瞬间异常退出,这种情况就稍微有点影响使用了。
在这里插入图片描述

2.添加额外的第三方模块,需要重新编译

浏览过OOMMF官网的扩展模块介绍页面,或者看过OOMMF视频教程,或者看过示例文件的人,估计都接触到了一些第三方模块,这些模块(或者类)在手册中并没有具体介绍。手册16.17 Oxs包管理:oxspkg,详细介绍了如何管理第三方模块,使用命令“tclsh oommf.tcl oxspkg list”列出所有的第三方模块的信息:
在这里插入图片描述
可以看到有什么周期边界啊,DMI啊,电流在薄膜的STT啊等等模块,每个模块的具体用法都可以在对应目录中找到源代码和示例文件,以上Installed模块都是OOMMF已经集成了的,可以直接在MIF文件中直接使用的。若用户需要添加自定义的模块,只需在oommf\app\oxs\local中新建模块目录,并把自定义模块的.c和.h文件复制进去,并重新编译OOMMF即可在MIF文件中使用新增的模块。
具体的如何自定义模块参见OOMMF视频教程

二、OOMMF软件部分功能的详细描述和使用过程中报错

涉及OOMMF的两个子程序:Oxsii和mmDisp,之所以单独拎出它们的部分功能来讲解,是因为我觉得手册上关于部分功能的描述太令人费解了。接着是关于使用过程中关于检查点文件的错误。

1.Oxsii和mmDisp部分功能

(一)在子程序Oxsii界面中,首先是File菜单打开的加载问题界面:
在这里插入图片描述
在这个界面中,需要补充说明的只有红框中的三项:
Restart框不能勾选,否则文件会加载错误,提示找不到检查点文件:
在这里插入图片描述
勾选菜单Options中的Restart flag,并单击Reload重新加载文件也会出现同样的问题。
解决方法就是不勾选Restart框,从而禁用Oxsii的重启功能。关于检查点文件的说明后面还会提到。
Threads框中是Oxsii使用的线程数量,一般它有默认值,不必修改它。经过亲手测试,运行同一个MIF文件,对于把线程数量设置为超过默认值的情况,模拟过程中占用的CPU会增加,但是完成模拟所需的时间竟然增加了!! 若疑惑的用户可以自己测试验证一下。
在这里插入图片描述

Params框可以为MIF文件中Parameter命令定义的变量设置新的值。在每次加载MIF文件之前,在这个框中按照类似“变量名1 值1 变量名2 值2”的形式为变量设置值,当然也可按照手册所述的那样较为麻烦的在命令行设置变量的值。

(二)在子程序Oxsii界面中,File菜单的Show Console会调出Tcl的命令窗口,在这里可以看到MIF文件中使用Report命令输出的字符串,它对于用户调试程序很有用。如下图:
在这里插入图片描述
MIF文件中有三个Parameter命令定义的变量:xcell,ycell zcell,并使用Report命令输出它们的值,在加载此文件之前,在Params框中为变量xcell,ycell重新设置新值,如此,加载此文件后,它们的值就变成了控制台中显示的新值了。

(三)在子程序Oxsii界面中,“Run”,“Relax”,“Step”三个按钮用于控制模拟的进度,分别是:完成整个模拟,完成一个阶段的模拟,完成一个步进的模拟。在手册中关于“阶段stage”和“步进step”的解释很清晰,但又稍微有点抽象。我觉得作者在视频教程中关于这三者的比喻很生动形象:想象把完成一个模拟看做是阅读一本书,“Step”即一个步进,就代表阅读书籍的一页内容,“Relax”即一个阶段,就代表阅读书籍的一整章,而“Run”则表示不停地,从头到尾阅读整本书。 仔细想一哈,我觉得这个比喻确实是十分恰当的。

(四)在子程序Oxsii界面中,关于输出计划Schedule的内容。在手册中,提到了在运行模拟之前可以先向数据接收子程序发送一个dummy数据,让数据接收子程序依照收到的这个数据来进一步规划显示界面。首先此处的dummy数据是MIF文件加载后,各个类的可以输出的参数的初始数据,其次数据接收子程序有了这个初始值就可以选择如何处理显示数据了。举个例子:比如说,先打开一个mmGraph子程序,可以看到它的X,Y1,Y2菜单是没有任何可选的数据的。在Oxsii中加载MIF文件但并不运行,并在Destination里选中数据输出到mmGraph,这时再单击Send按钮,就可以把初始数据发送给mmGraph了,然后在mmGraph的X,Y1,Y2菜单选择所需的数据来显示即可。设置好mmGraph之后,再在Oxsii中,设置好输出频率,然后运行模拟,这样就可以很正确的显示所需数据了。
在这里插入图片描述

(五)在子程序mmDisp,菜单Configure界面中,手册里对于颜色的描述我觉得也不是很清楚,特别是如下图中对像素着色时,颜色量xy/xz/yz-angle表示的意思:
在这里插入图片描述
首先众所周知的是,mmDisp是用来显示矢量场的程序,矢量场中每个位置点都有一个矢量,而矢量是有大小有方向的量,所以一般情况下用箭头的方向表示矢量的方向,用箭头的颜色表示矢量的大小。 当然, 手册里很清晰的举了例子来说明如何对Arrow着色,比如上图中颜色量Color Quantity选中z,Colormap选中Red-Black-Blue,那么箭头的颜色表示的是矢量在z方向的分量大小,比如:该矢量的z分量为负的最小值,即指向-z,那么该箭头就是红色;z分量为0,即该矢量平躺在xy平面,那么该箭头为黑色;z分量为正的最大值,即指向+z,那么该箭头就是蓝色。
其次,需要记住对像素着色时,若颜色量选择xy/xz/yz-angle时,那么颜色映射必须要选择一个“色环”。 可以看到Colormap有两种色环:Red-Green-Blue-Red(即RGB色环)和Cyan-Magenta-Yellow-Cyan(即CMYK色环),它们如下:
在这里插入图片描述
有关色环的知识可以自己去了解一下,通常RGB色环用于计算机上的颜色显示,而CMYK色环用于印刷等实物的颜色显示。为了更加方便的解释这个功能,我就自定义一个磁体系来说明:假设有一个沿着+y延伸的纳米条带,沿着+y方向平分为8个子区域,每个子区域的初始磁化分布规律是这样的:所有区域的磁矩都在yz面内,磁矩从+z方向按照顺时针旋转45度的规律分布在360/45=8个子区域,那么具体的MIF文件内容如下:

# MIF 2.1

#################
#author:YQYUN
#date:22/3/4
#desc:验证yz-angle颜色量的使用
#平均分为8个区域,每个区域的初始磁化方向分别在yz平面内,并按45度递增
#################

set pi [expr 4*atan(1.0)]
set mu0 [expr 4*$pi*1e-7]

#饱和磁化强度
Parameter Ms  860e3

#平均分为8个区域,总尺寸40*40*1nm
Parameter regionNum 8
Parameter regionTotal_x 40e-9
Parameter regionTotal_y 40e-9
Parameter regionTotal_z 1e-9
set cellsize 1e-9



#采用相对坐标系平均分8个区域
proc averageRegion { rel_x rel_y rel_z } {
	global regionNum
	if {$rel_y <= 1 * (1.0 / $regionNum)} {
	return 1
	} elseif {$rel_y <= 2 * (1.0 / $regionNum)} {
	return 2       
	} elseif {$rel_y <= 3 * (1.0 / $regionNum)} {
	return 3       
	} elseif {$rel_y <= 4 * (1.0 / $regionNum)} {
	return 4       
	} elseif {$rel_y <= 5 * (1.0 / $regionNum)} {
	return 5       
	} elseif {$rel_y <= 6 * (1.0 / $regionNum)} {
	return 6       
	} elseif {$rel_y <= 7 * (1.0 / $regionNum)} {
	return 7
	} 
	return 8
	}
	

#使用TCL脚本的函数定义容器
Specify Oxs_ScriptAtlas:atlas [subst {
  xrange {0 $regionTotal_x}
  yrange {0 $regionTotal_y}
  zrange {0 $regionTotal_z}
  regions {region1 region2 region3 region4 region5 region6 region7 region8}
  comment {"采用的是相对坐标系"}
  script_args relpt
  script averageRegion
}]

#定义网格
Specify Oxs_RectangularMesh:mesh [subst {
  cellsize {$cellsize $cellsize $cellsize}
  atlas :atlas
}]


#定义退磁项
Specify Oxs_Demag {}

#8个区域的初始化磁化方向都在yz平面内(x分量始终为0),且按45度递增(cos(pi/4)=0.707)。
Specify Oxs_AtlasVectorField:m0 { 
  atlas :atlas
	values { 
	region1 {0 0 1}
	region2 {0 0.707 0.707}
	region3 {0 1 0}
	region4 {0 0.707 -0.707}
	region5 {0 0 -1}
	region6 {0 -0.707 -0.707}
	region7 {0 -1 0}
	region8 {0 -0.707 0.707}
}
}

#定义演化器(龙格-库塔时间演化器)
Specify Oxs_RungeKuttaEvolve:evolver {}

#定义时间驱动器
Specify Oxs_TimeDriver [subst {
 evolver :evolver
 stopping_dm_dt  1e-9
 stopping_time   1.0
 mesh :mesh
 Ms $Ms
 m0 :m0
}]

加载此MIF文件后但是并不运行,而是选择输出初始数据到mmDisp,效果如下图:
在这里插入图片描述
接着为像素Pixel着色,颜色映射选择RGB色环,颜色量选择yz-angle,勾选Reverse,最终效果如下:
在这里插入图片描述
结合左上图,估计大家一眼就看出来像素是怎么着色了的吧!没错,在上图中,颜色量yz-angle就表示,色环放在yz平面,yz平面的箭头方向正是色环颜色对应的切线。 比如说第3区域里的磁矩指向+y方向,那么在色环上,它是红色位置处的切线,其他颜色解释相同。

2.检查点文件

估计部分用户在运行自己的MIF文件时,大概15分钟左右就突然就报错,提示checkpoint检查点文件(xxx.restart文件)保存错误,或者找不到,报错框类似下图:
在这里插入图片描述
首先,需要知道检查点文件是什么。按照手册所述,检查点文件是用于定时保存求解器状态的,在求解器异常退出时用于恢复求解状态,所以对于正常按规范使用软件的用户来说是没有啥用的。若用户没有在MIF文件或者通过求解器菜单显式设置保存时间的话,求解器一般会默认每15分钟保存一次状态,但由于检查点文件找不到或者写入失败,那么求解器就报错。但是我也发现MIF文件所在目录中新生成了一个.temp的文件,这也是我觉得比较迷惑的地方。
但是,解决方法很简单,既然这个错误是由于检查点文件引起的,那把求解器的检查点功能给禁用了,不就从源头给解决了吗?
在这里插入图片描述
按照手册所述,将驱动器的checkpoint_interval参数设置为-1,从而禁用检查点功能,于是求解器无论运行多长时间都不会报错了。

三、MIF文件内容即Tcl代码中的一些问题

1.时间演化器和能量最小化演化器的区别

众所周知,演化器和驱动器要配对使用!但什么时候选择时间演化器,什么时候选择使用能量最小化演化器呢?按照作者在视频教程所说的那样:对于准静态模拟(即磁体系不关心时间),需要快速达到稳态的情况使用能量最小化演化器。对于磁体系是按照LLG方程来演化的,需要观察磁体系随时间而变化的情况则使用时间演化器。其实看一下在MIF文件中这两种演化器需要设置的参数:时间演化器需要的参数是LLG方程中的参数,而能量最小化演化器的参数则没有,而且不同类型演化器/驱动器的输出项也不同。

2.塞曼能(即外加磁场)的设置

对于MIF文件中的塞曼能,手册里介绍了Oxs_FixedZeeman , Oxs_UZeeman,Oxs_ScriptUZeeman,Oxs_StageZeeman,Oxs_TransformZeeman共5个类。可以在MIF文件中添加多个塞曼项
按照我目前所知的外加磁场类型:对整个磁体系施加均匀的磁场,对整个磁体系施加时变的磁场,对整个磁体系的部分区域施加均匀的磁场,对整个磁体系的部分区域施加时变的磁场,利用上面的类都能实现。比如最后一种情况:可以使用Oxs_StageZeeman ,结合时间驱动器,结合矢量场Oxs_ScriptVectorField等就可以实现。

3.驱动器的report_wall_time和m0参数

将驱动器的report_wall_time设置为1,表示启用外界时间输出,它是从计算机开机到此时刻的总时间(以秒为单位)。
磁体系的初始磁化是由驱动器的m0参数设置的,需要注意的是,m0要求用户设置磁化矢量的方向即可,它会自动调整矢量大小为单位磁矩(即矢量模为1)。比如(0.1 0.1 0.1)和(0.2 0.2 0.2)和(1 1 1)等效果都是相同的。

四、几个简单的示例

1.宏自旋模型

读者们应该对宏自旋模型十分的熟悉了,如下图:
在这里插入图片描述
在总有效磁场H的作用下,单独的磁矩M会绕着H进动(逆时针转动的同时,向H靠近)。若在外加极化电流,那么由于STT效应,提供了两个额外的力矩项:类场项和类阻尼项。则分为三种情况:电流促进阻尼过程导致磁矩更快收敛到有效场方向;电流适当,类阻尼项和阻尼项抵消,导致磁矩在固定轨道旋转;电流的类阻尼项克服阻尼项,导致磁矩反向进动。至于导致磁矩翻转的具体电流大小,应该是有公式计算的,但我忘了,所以下面代码里关于电流大小的设置都是随便设置的。。。
恰好OOMMF自带了宏自旋模型的例程,文件位置:oommf\app\oxs\local\spinxfer-onespin.mif。
为它添加一些注释,并少许修改:

# MIF 2.1

#################
#author:YQYUN
#date:22/3/6
#desc:模拟宏自旋模型的STT效应
#################

set pi [expr 4*atan(1.0)]
set mu0 [expr 4*$pi*1e-7]

#使用时间驱动器的总共运行时间为80ns,每个阶段0.5ns
Parameter stage_time 5e-10
Parameter run_time  80e-9
set number_of_stages [expr {int(ceil($run_time/double($stage_time)))}]

#电流大小
Parameter I 1e-9   
#外加磁场
Parameter Happ 10 
#饱和磁化强度
Parameter Ms  860e3
#电流极化率
Parameter Polarization 0.4
#电流极化方向(即固定层的磁矩方向),采用球坐标系,theta是与z轴的夹角,phi是矢量投影在xy平面上与x轴的夹角
Parameter theta 0  
Parameter phi   0  
set theta [expr {$theta*$pi/180}]
set phi   [expr {$phi*$pi/180}]
#宏自旋模型尺寸100*100*5nm
Parameter length 100e-9
Parameter width  100e-9
Parameter thick    5e-9
#计算通过模型的电流密度
set current_density [expr {$I/($length*$width)}]

#定义容器
Specify Oxs_BoxAtlas:atlas [subst {
  xrange {0 $length}
  yrange {0 $width}
  zrange {0 $thick}
}]

#定义网格
Specify Oxs_RectangularMesh:mesh [subst {
  cellsize {$length $width $thick}
  atlas :atlas
}]

#外加磁场
Specify Oxs_UZeeman [subst {
	comment {"将A/m转化为mT"}
    multiplier [expr {0.001/$mu0}]
	Hrange {
	 { 0 0 $Happ 0 0 0  0 }
  }
}]

# #使用能量最小化演化器
# Specify Oxs_CGEvolve:evolver {}
# #使用驱动器一直推进到稳态
# Specify Oxs_MinDriver [subst {
	# evolver :evolver
	# mesh :mesh
	# stopping_mxHxm 0.1
	# Ms $Ms
	# m0 { 1 1 1}
	# }]

#使用函数定义随阶段数变化的电流,
#该函数的返回值会乘以初始电流密度得到该时刻的电流密度
proc Jprofile { stageOfTime } {
	global number_of_stages
	#把总的阶段数平分为5个阶梯,每个阶梯内的电流相等
    if {$stageOfTime < $number_of_stages / 5 * 1} {#第1个阶梯
	#电流密度小,且为正,加速阻尼过程
       return 1
    } elseif {$stageOfTime < $number_of_stages / 5 * 2} {#第2个阶梯
        return 0
    } elseif {$stageOfTime < $number_of_stages / 5 * 3} {#第3个阶梯
    #电流密度大,且为负,会克服阻尼过程
		return -2.5e5
    } elseif {$stageOfTime < $number_of_stages / 5 * 4} {#第4个阶梯
        return 0
    } elseif {$stageOfTime < $number_of_stages / 5 * 5} {#第5个阶梯    
		return 2.5e5
    }
}

#使用带自旋转移矩的演化器
Specify Oxs_SpinXferEvolve:evolve [subst {
  alpha 0.014
  start_dm 0.01
  comment {"使用的是球坐标系"}
  mp {[expr {sin($theta)*cos($phi)}]
      [expr {sin($theta)*sin($phi)}]
      [expr {cos($theta)}]}
  J $current_density
  J_profile Jprofile
  comment {"参数为当前时间所处的阶段数"}
  J_profile_args stage
  P $Polarization
}]

#定义时间驱动器
Specify Oxs_TimeDriver [subst {
	evolver :evolve
	stopping_dm_dt  1e-9
	stopping_time $stage_time
	stage_count $number_of_stages
	mesh :mesh
	Ms $Ms
	m0 { 1 1 1}
}]


(1)首先看一下时间演化器和能量最小化演化器的区别,两者的参数都设置为:电流为0,极化方向为+z方向,外加磁场为(0 0 1)方向,大小10mT。
在这里插入图片描述
可以看到使用最小化演化器是看不到磁矩的具体动态变化过程,只看到了最终稳态:
在这里插入图片描述
而使用时间演化器,则磁矩正如LLG方程描述的那样进行变化:磁矩会绕Z轴逆时针进动,接着由于阻尼作用,一段时间后达到稳态。

在这里插入图片描述

(2)接着是施加电流的情况,这里使用一个函数Jprofile 来定义随着阶段数而变化的电流脉冲,根据不同阶段数,得到不同的电流大小。按照手册所述,电流值为正的话,会使磁矩倾向电流极化方向,此处的电流极化方向(即极化层的磁矩方向)为+z方向,和外加场方向相同。那么当电流:①为一定大小的正值,则磁矩越快达到稳态。②为一定大小的负值,使单个磁矩倾向于和电流极化方向反平行,由电流产生的类阻尼矩可以克服阻尼矩,于是磁矩会翻转。

在这里插入图片描述

2.不同核壳模型的反磁化

这是去年的时候看到的一篇知乎文章,本来那个时候就想模仿文章来做一下的,但奈何那个时候啥都不太懂,加上人又太懒,所以直到现在才动手模拟了一下。
在这里插入图片描述
这里给出文章原文,是中文的,很好理解。文章图10描述的几个核壳模型如下:在这里插入图片描述
按照文章中给定的的参数和模型,并参照OOMMF自带例程oommf\app\oxs\examples\stdprob1.mif,那么就很容易写出:

# MIF 2.1

#################
#author:YQYUN
#date:22/3/7
#desc:模拟不同核壳模型的磁滞回线
#################

set pi [expr {4*atan(1.0)}]
set mu0 [expr {4*$pi*1e-7}]

# #声明均匀包裹的核壳模型尺寸:核的尺寸为20*20*20,壳的厚度为6,于是每个方向上的边长:20+(6+6)=32
# #经过模拟得到矫顽场大小为-6220mT
# #定义单元格尺寸
# Parameter cellsize 2e-9
# Parameter shell_x 32e-9
# Parameter shell_y 32e-9
# Parameter shell_z 32e-9
# Parameter core_x 20e-9
# Parameter core_y 20e-9
# Parameter core_z 20e-9

# #均匀包裹的核壳模型的位置点分配函数,采用原始坐标系
# proc shell_core_shape_uniform { x y z } {
	# global shell_x shell_y shell_z core_x core_y core_z
	# #分配核的范围
	# if {$x >= ($shell_x - $core_x) / 2 && $x <= ($shell_x - $core_x) / 2 + $core_x
	# && $y >= ($shell_y - $core_y) / 2 && $y <= ($shell_y - $core_y) / 2 + $core_y
	# && $z >= ($shell_z - $core_z) / 2 && $z <=($shell_z - $core_z) / 2 + $core_z} {
	# return 1
	# } 
	# #分配壳的范围
	# return 2
# }




#定义单元格尺寸
Parameter cellsize 2e-9

#声明“只在核的x面分布的壳”模型,即ax型的尺寸:核的尺寸为40*40*40,一边的壳的厚度为20,于是x方向的边长:40+(20+20)=80
#经过模拟得到矫顽场大小为-5170mT
Parameter core_x 40e-9
Parameter core_y 40e-9
Parameter core_z 40e-9
Parameter shell_x 80e-9
Parameter shell_y 40e-9
Parameter shell_z 40e-9

#ax型核壳模型的位置点分配函数,采用原始坐标系
proc shell_core_shape_ax { x y z } {
	global shell_x shell_y shell_z core_x core_y core_z
	#分配核的范围
	if {$x >= ($shell_x - $core_x) / 2 && $x <= ($shell_x - $core_x) / 2 + $core_x} {
	return 1
	} 
	#分配壳的范围
	return 2
}



#使用TCL脚本的函数定义一个容器
Specify Oxs_ScriptAtlas:atlas [subst {
  xrange {0 $shell_x}
  yrange {0 $shell_y}
  zrange {0 $shell_z}
  comment {"core在列表中的位置为“1”,shell在列表中的位置为“2” "}
  regions {core shell}
  comment {"采用的是原始坐标系"}
  script_args rawpt
  script shell_core_shape_ax
}]


#定义一个网格
Specify Oxs_RectangularMesh:mesh [subst {
  cellsize {$cellsize $cellsize $cellsize}
  atlas :atlas
}]

#定义一个标量场对象来为不同区域分配标量值(单轴各向异性常数)
Specify Oxs_AtlasScalarField:anisotropy_K1 { 
  atlas :atlas
  default_value 0
	values { 
	core 3.6e6 
	shell 4.5e6
}
}

#定义一个矢量场对象来为不同区域分配矢量值(各向异性的方向)
Specify Oxs_AtlasVectorField:anisotropy_axis { 
  atlas :atlas
	values { 
	core {0 0 1}
	shell {0 0 1}
}
}

#定义单轴各向异性项
Specify Oxs_UniaxialAnisotropy {
  K1 :anisotropy_K1
  axis :anisotropy_axis
}

#定义一个标量场对象来为不同区域分配标量值(交换系数)
Specify Oxs_AtlasScalarField:exchange { 
  atlas :atlas
	values { 
	core 10.25e-12
	shell 12.5e-12
}
}

#定义交换项,其中的交换系数是逐单元格指定的
Specify Oxs_ExchangePtwise { 
  A :exchange
}

#定义退磁项
Specify Oxs_Demag {}

#定义外加磁场项
Specify Oxs_UZeeman:extfield [subst {
  comment {"将默认单位A/m换算为特斯拉T"}
  multiplier [expr {1/$mu0}]
  comment {"外加磁场从0到-10T,步进数量为200次,每次0.05"}
  Hrange {
    {0 0 0 0 0 -10 200}
  }
}]

#定义演化器(龙格-库塔时间演化器)
Specify Oxs_RungeKuttaEvolve:evolver {}

#定义时间驱动器
Specify Oxs_TimeDriver {
  evolver :evolver
  mesh :mesh
  comment {"分别为核壳分配饱和磁化强度"}
  Ms {
  Oxs_AtlasScalarField { 
  atlas :atlas
	values { 
	core 1176e3
	shell 1281.5e3
}
}
}
comment {"分别为核壳分配初始磁化配置"}
  m0 {
  Oxs_AtlasVectorField { 
  atlas :atlas
	values { 
	core {0 0 1}
	shell {0 0 1}
}
}
}
  stopping_dm_dt 0.01
}

这里只定义了两种模型:“壳是均匀包裹核”(核的尺寸为202020,壳的厚度为6)和“只在核的x面分布的壳”(即ax型的尺寸:核的尺寸为404040,一边的壳的厚度为20),其余模型的定义方式是类似的。此外,施加的外加磁场的顺序和原文有一点点区别,原文是在+z方向以0.5 kOe的梯度从 0 kOe均匀地增加到 100 kOe,然后再降到-100KOe。本文就直接从0KOe以0.5KOe的梯度降到-100KOe。
(1)首先,对于“ax型”的模型,模拟得到它的磁化曲线如图:
在这里插入图片描述
原文图12中描述的ax型的矫顽力是51.25KOe,模拟得到的结果也是在这个范围内。接下来对比一下原文和模拟的“ax型”反磁化过程:
原文图12(f):
在这里插入图片描述
反磁化的模拟结果:
在这里插入图片描述
可以看到模拟的反磁化过程和原文描述是一致的。
(2)接着,来看一下均匀分布的壳的磁化曲线吧:
在这里插入图片描述
对比原文,虽然文中图2(a)中“核:202020,壳:6”只有大概的数值范围,没有写出具体的数值,但也能看出模拟得到6220mT的矫顽力是正确的。它的反磁化过程也和原文描述的一致,这里就不再赘述了。

3.外加磁场驱动180度畴壁运动

这个例子参考文章如下:
在这里插入图片描述
按照文章中的第四章 “场驱动下的磁畴壁在纳米带中的输运”所述的内容设置参数并进行模拟。
原文中参数描述如下,其他参数读者自己去看:
在这里插入图片描述
由第四章的所有内容,可以得到的代码如下:(懒得分段张贴代码,所以一股脑整出来)

# MIF 2.1

#################
#author:YQYUN
#date:22/3/10
#desc:外加磁场驱动磁畴壁
#################

set pi [expr {4*atan(1.0)}]
set mu0 [expr {4*$pi*1e-7}]

#使用时间驱动器的总共运行时间为1e-12s,每个阶段1e-14s
Parameter stage_time 1e-14
Parameter run_time  1e-12
set number_of_stages [expr {int(ceil($run_time/double($stage_time)))}]

#单元格尺寸:4*4*4nm
Parameter xcell 4 
Parameter ycell 4
Parameter zcell 4
set xcell [expr {$xcell*1e-9}]
set ycell [expr {$ycell*1e-9}]
set zcell [expr {$zcell*1e-9}]

#纳米条带:4000*20*4nm
Specify Oxs_BoxAtlas:atlas {
  xrange {0 4000e-9}
  yrange {0  20e-9}
  zrange {0  4e-9}
}

#定义网格
Specify Oxs_RectangularMesh:mesh [subst {
  cellsize {$xcell $ycell $zcell}
  atlas :atlas
}]

#定义交换能
Specify Oxs_UniformExchange {
  A  13e-12
}

#定义单轴各向异性能
Specify Oxs_UniaxialAnisotropy {
   K1 1.4999e4
   axis {1 0 0}
}

#定义退磁能
Specify Oxs_Demag {}

###########得到稳态180度畴壁###########
# # 先使用能量最小化演化器弛豫到180头对头稳态畴壁,作为初始态并保存
# Specify Oxs_CGEvolve:evolver {}

# # 在纳米条x方向的中心30nm范围,设置畴壁的方向,两侧设为头对头的方向
# proc head2head_180_wall { x y z } { 
# if {$x <= 1985e-9} {return "1 0 0"}
# if {$x >= 2015e-9} {return "-1 0 0"}
# return  "0 1 0"
# }

# # 使用最小化驱动器快速推进到稳态
# Specify Oxs_MinDriver [subst {
	# evolver :evolver
	# mesh :mesh
	# stopping_mxHxm 0.1
	# Ms 1.94e5
	# m0 { Oxs_ScriptVectorField { 
	# atlas :atlas
	# norm 1
	# script head2head_180_wall
	# script_args rawpt
# }
	# }
# }]
###########得到稳态180度畴壁###########

###########施加静态的外加磁场###########
# #沿x轴方向施加 1mT=10Oe的磁场
# Specify Oxs_UZeeman [subst { 
# multiplier [expr {0.001/$mu0}]
# Hrange { 
# { 1 0 0 1 0 0 0 }
# }
# }]

#沿x轴方向施加 0.05mT=0.5Oe的磁场
# Specify Oxs_UZeeman [subst { 
# multiplier [expr {0.001/$mu0}]
# Hrange { 
# { 0.05 0 0 0.05 0 0 0 }
# }
# }]
###########施加静态的外加磁场###########


###########施加圆偏振的外加磁场###########
#圆偏振微波磁场的振幅,这里的单位为mT,表示40*pi Oe
Parameter Hc        [expr {4*$pi}] 

#圆偏振微波磁场的频率,这里的单位为GHz
Parameter frequency         1

#Y-Z平面的圆偏振微波磁场:Hm= Hc(ycoswt  + zsinwt)=yHc*coswt + zHc*sinwt
proc circularPolarization { total_time } {
   global Hc frequency pi
   
   #计算w=2*pi*f,并把单位顺便转化为GHz
   set w [expr {2 * $pi * $frequency * 1e9}]
   #分别计算y和z方向的磁场分量及其对时间的导数
   #即y方向Hc*coswt,z方向Hc*sinwt
   set Hy [expr {$Hc * cos($w * $total_time)}]
   set dHy [expr {$Hc * (-1) *sin($w  * $total_time) * $w}]
   set Hz [expr {$Hc * sin($w  * $total_time)}]
   set dHz [expr {$Hc * cos($w  * $total_time) * $w}]
  
   return [list 0 $Hy $Hz 0 $dHy $dHz]
}

#使用脚本定义外加磁场
Specify Oxs_ScriptUZeeman [subst {
   comment {"将默认单位A/m换算为mT"}
   multiplier [expr {0.001/$mu0}]
   script circularPolarization
   script_args total_time
}]
###########施加圆偏振的外加磁场###########


#定义时间演化器
Specify Oxs_RungeKuttaEvolve:evolver [subst {
   alpha 0.001
   comment "按照文中的描述将gamma_G进行单位换算"
   gamma_G [expr {1.76e7 * 1e3 / (4 * $pi)}]
}]

#定义驱动器
Specify Oxs_TimeDriver [subst {
	evolver :evolver
	mesh :mesh
	stopping_time $stage_time
    stage_count $number_of_stages
	Ms 1.94e5
	comment "读取稳态畴壁的文件作为初始态,并按时间演化"
	m0 { Oxs_FileVectorField { 
    file "initial_state.omf"
	spatial_scaling { 1 1 1 }
	spatial_offset { 0 0 0 }
	norm 1
	}
	}
	comment "将checkpoint_interval设为-1,从而禁用检查点功能"
    checkpoint_interval  -1
}]

(1)首先,使用最小化演化器达到头对头180度畴壁的稳态,并保存为文件“initial_state.omf”:
在这里插入图片描述
模拟得到稳态的180度畴壁的图像看起来也和原文中图4-2一样,所以应该这一步是对了的。

(2)接着,对整个纳米条带施加静态的沿着x轴的,大小为1mT=10Oe的外加磁场。此时畴壁的运动是向+x方向的,如下图:
在这里插入图片描述
移动一定距离后,畴壁里的磁矩翻转,于是畴壁向-x方向的移动,如下图:
在这里插入图片描述
总共花费了1个多小时,但是居然才模拟了17皮秒左右,得到x方向的相对磁化强度随着模拟时间的变化:
在这里插入图片描述
显然畴壁是来回移动的,在计算畴壁速度时,又遇到了问题,按照原文给出的公式v=L/2*dmx/dt计算畴壁的速度,但在上图中无论怎么选取点也不对啊,无论是曲线的哪两个顶点,还是曲线同一边的随机两个点,反正得到的结果就是离谱。我也尝试在磁化矢量图上找磁畴壁结构中相同的两个点,看一下经过了多少时间和距离,然后用距离除以时间,得到的畴壁速度也是错的。后来我也将磁场设为0.5mT,得到的图像也是类似的,畴壁速度还是算不出来。我是真不知道到底是哪里出问题了,仿真时间太短了吗?还是文章理解错了吗?哪里的参数设置错了吗?有大佬帮忙解惑吗?

(3)对整个纳米条带施加yz平面内的圆偏振微波磁场,首先需要验证施加的场是正确的,但由于将它直接施加给这个磁体系,模拟速度实在是太慢了,所以我就将它施加到宏自旋模型中,得到的磁场随时间的变化如图:
在这里插入图片描述
磁场的y和z方向的分量确实是频率为1GHz,振幅为12.6mT=40π Oe,然后将这个磁场施加到文章中的磁体系。
到这一步,我觉得这些步骤都没错吧,但是运行模拟之后,等了半个小时,仿真时间才过了5.8e-12秒,x方向的相对磁化强度才变化了0.023,我都傻眼了:
在这里插入图片描述
看都不用看,又错了,唉,真的是搞不懂。。。
在这里插入图片描述


总结

希望早点毕业!另外有大佬帮忙看一下第三个示例吗?到底是哪里弄错了,搞不懂。。
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 68
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 68
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖工人_0803号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值