nano_exit

基礎的なことこそ、簡単な例が必要だと思うのです。

NiOの反強磁性結晶構造における対称性をspglibを使って求める。

通常の結晶構造であれば、データベースか何かから引っ張って来れるが、それをベースにした超周期構造を作ろうと思うと、対称性(空間群)が変わるため、手でやるには歯が立たない。
そこで、入力した原子座標に対して対称性を見つけてくれる spglib を使って、反強磁性でのNiOの対称性を決定することを試みる。

spglibはC言語FORTRAN, pythonに対応しているが、いずれにおいても「実行ファイル(今風に言えばアプリケーション)」ではなく、(~libとあるように)ライブラリーとして提供されている。そのためGUIのようなものに結晶構造を入れるようなものではないため、多少プログラミングに慣れている必要がある。
Spglib — spglib 1.12.0 documentation

個人的には、pythonが簡便でオススメである。なので、ここではpythonでの例を紹介する。
Spglib for Python — spglib 1.12.0 documentation
pythonバージョンのインストールは、pipですぐ入る。

pip install spglib

これを使って、まずは通常のNiOの結晶構造で試してみる。

import numpy as np
import spglib

# lattice constant
a = 4.19

# structural information ( [lattice constants], [coordination], [atomic numbers] )
# Caution: it should be a tuple
nio = \
    ( [ [ a, 0, 0 ], [ 0, a, 0 ], [ 0, 0, a ] ],
      [ [ 0,   0,   0   ],  # Ni
        [ 0,   0.5, 0.5 ],  # Ni
        [ 0.5, 0,   0.5 ],  # Ni
        [ 0.5, 0.5, 0   ],  # Ni
        [ 0.5, 0,   0   ],  # O
        [ 0,   0.5, 0   ],  # O
        [ 0,   0,   0.5 ],  # O
        [ 0.5, 0.5, 0.5 ]   # O
      ],
      [28,] * 4 + [8,] * 4
    )

# just space group 
print( spglib.get_spacegroup( nio ) )
#Fm-3m (225)

# primitive cell
print( spglib.find_primitive( nio ) )
#(array([[0.   , 2.095, 2.095],
#       [2.095, 0.   , 2.095],
#       [2.095, 2.095, 0.   ]]), array([[0. , 0. , 0. ],
#       [0.5, 0.5, 0.5]]), array([28,  8], dtype=int32))

 2 \times 2 \times 2のsuper cell でもちゃんと元の構造の対称性を返してくれる。

# some coordinates are just added 1 to discribe them in the other unit cell
# therefore, lattice constans and coordinates should be multipled and divided by 2, respectively
nio_sc \
    = [ [ ( a, 0, 0 ), ( 0, a, 0 ), ( 0, 0, a ) ],
        [ [ 0,   0,   0   ],  # Ni 0, 0, 0
          [ 0,   0.5, 0.5 ],
          [ 0.5, 0,   0.5 ],
          [ 0.5, 0.5, 0   ],
          [ 1,   0,   0   ],  # Ni 1, 0, 0
          [ 1,   0.5, 0.5 ],
          [ 1.5, 0,   0.5 ],
          [ 1.5, 0.5, 0   ],
          [ 0,   1,   0   ],  # Ni 0, 1, 0
          [ 0,   1.5, 0.5 ],
          [ 0.5, 1,   0.5 ],
          [ 0.5, 1.5, 0   ],
          [ 0,   0,   1   ],  # Ni 0, 0, 1
          [ 0,   0.5, 1.5 ],
          [ 0.5, 0,   1.5 ],
          [ 0.5, 0.5, 1   ],
          [ 1,   1,   0   ],  # Ni 1, 1, 0
          [ 1,   1.5, 0.5 ],
          [ 1.5, 1,   0.5 ],
          [ 1.5, 1.5, 0   ],
          [ 1,   0,   1   ],  # Ni 1, 0, 1
          [ 1,   0.5, 1.5 ],
          [ 1.5, 0,   1.5 ],
          [ 1.5, 0.5, 1   ],
          [ 0,   1,   1   ],  # Ni 0, 1, 1
          [ 0,   1.5, 1.5 ],
          [ 0.5, 1,   1.5 ],
          [ 0.5, 1.5, 1   ],
          [ 1,   1,   1   ],  # Ni 1, 1, 1
          [ 1,   1.5, 1.5 ],
          [ 1.5, 1,   1.5 ],
          [ 1.5, 1.5, 1   ],
          [ 0.5, 0,   0   ],  # O  0, 0, 0
          [ 0,   0.5, 0   ],
          [ 0,   0,   0.5 ],
          [ 0.5, 0.5, 0.5 ],
          [ 1.5, 0,   0   ],  # O  1, 0, 0
          [ 1,   0.5, 0   ],
          [ 1,   0,   0.5 ],
          [ 1.5, 0.5, 0.5 ],
          [ 0.5, 1,   0   ],  # O  0, 1, 0
          [ 0,   1.5, 0   ],
          [ 0,   1,   0.5 ],
          [ 0.5, 1.5, 0.5 ],
          [ 0.5, 0,   1   ],  # O  0, 0, 1
          [ 0,   0.5, 1   ],
          [ 0,   0,   1.5 ],
          [ 0.5, 0.5, 1.5 ],
          [ 1.5, 1,   0   ],  # O  1, 1, 0
          [ 1,   1.5, 0   ],
          [ 1,   1,   0.5 ],
          [ 1.5, 1.5, 0.5 ],
          [ 1.5, 0,   1   ],  # O  1, 0, 1
          [ 1,   0.5, 1   ],
          [ 1,   0,   1.5 ],
          [ 1.5, 0.5, 1.5 ],
          [ 0.5, 1,   1   ],  # O  0, 1, 1
          [ 0,   1.5, 1   ],
          [ 0,   1,   1.5 ],
          [ 0.5, 1.5, 1.5 ],
          [ 1.5, 1,   1   ],  # O  1, 1, 1
          [ 1,   1.5, 1   ],
          [ 1,   1,   1.5 ],
          [ 1.5, 1.5, 1.5 ] ],
        [28,] * 32 + [8,] * 32
      ]

# check whether there is dupulication
for i, p1 in enumerate( nio_sc[1] ):
    for p2 in nio_sc[1][ i+1 : ]:
        if p1 == p2:
            print( p1 )

# lattice constants should be twice in 2*2*2 super cell
nio_sc[0] = [ list( a_ ) for a_ in np.array( nio_sc[0] ) * 2 ]

# coordinate should be divided by 2 
nio_sc[1] = [ list( p_ ) for p_ in np.array( nio_sc[1] ) / 2 ]

nio_sc = tuple( nio_sc )

print( spglib.get_spacegroup( nio_sc ) )
print( spglib.find_primitive( nio_sc ) )

座標の重複があると、'None'が返って来るため、チェックする必要がある。目で見て一生懸命探すのには限界がある。

最後に反強磁性だが、格子定数と座標はsuper cellのままでよく、原子番号で反強磁性秩序の情報を入れる。仮に磁化軸方向と平行な原子をNi(28)として、反平行なものを仮にPd(46)とする。酸素はそのままで良い。
NiOの反強磁性磁化軸は[111]方向なので、それを考慮して原子番号を対応させていく。

nio_af \
    = [ [ ( a, 0, 0 ), ( 0, a, 0 ), ( 0, 0, a ) ],
        [ [ 0,   0,   0   ],  # Ni 0, 0, 0
          [ 0,   0.5, 0.5 ],
          [ 0.5, 0,   0.5 ],
          [ 0.5, 0.5, 0   ],
          [ 1,   0,   0   ],  # Ni 1, 0, 0
          [ 1,   0.5, 0.5 ],
          [ 1.5, 0,   0.5 ],
          [ 1.5, 0.5, 0   ],
          [ 0,   1,   0   ],  # Ni 0, 1, 0
          [ 0,   1.5, 0.5 ],
          [ 0.5, 1,   0.5 ],
          [ 0.5, 1.5, 0   ],
          [ 0,   0,   1   ],  # Ni 0, 0, 1
          [ 0,   0.5, 1.5 ],
          [ 0.5, 0,   1.5 ],
          [ 0.5, 0.5, 1   ],
          [ 1,   1,   0   ],  # Ni 1, 1, 0
          [ 1,   1.5, 0.5 ],
          [ 1.5, 1,   0.5 ],
          [ 1.5, 1.5, 0   ],
          [ 1,   0,   1   ],  # Ni 1, 0, 1
          [ 1,   0.5, 1.5 ],
          [ 1.5, 0,   1.5 ],
          [ 1.5, 0.5, 1   ],
          [ 0,   1,   1   ],  # Ni 0, 1, 1
          [ 0,   1.5, 1.5 ],
          [ 0.5, 1,   1.5 ],
          [ 0.5, 1.5, 1   ],
          [ 1,   1,   1   ],  # Ni 1, 1, 1
          [ 1,   1.5, 1.5 ],
          [ 1.5, 1,   1.5 ],
          [ 1.5, 1.5, 1   ],
          [ 0.5, 0,   0   ],  # O  0, 0, 0
          [ 0,   0.5, 0   ],
          [ 0,   0,   0.5 ],
          [ 0.5, 0.5, 0.5 ],
          [ 1.5, 0,   0   ],  # O  1, 0, 0
          [ 1,   0.5, 0   ],
          [ 1,   0,   0.5 ],
          [ 1.5, 0.5, 0.5 ],
          [ 0.5, 1,   0   ],  # O  0, 1, 0
          [ 0,   1.5, 0   ],
          [ 0,   1,   0.5 ],
          [ 0.5, 1.5, 0.5 ],
          [ 0.5, 0,   1   ],  # O  0, 0, 1
          [ 0,   0.5, 1   ],
          [ 0,   0,   1.5 ],
          [ 0.5, 0.5, 1.5 ],
          [ 1.5, 1,   0   ],  # O  1, 1, 0
          [ 1,   1.5, 0   ],
          [ 1,   1,   0.5 ],
          [ 1.5, 1.5, 0.5 ],
          [ 1.5, 0,   1   ],  # O  1, 0, 1
          [ 1,   0.5, 1   ],
          [ 1,   0,   1.5 ],
          [ 1.5, 0.5, 1.5 ],
          [ 0.5, 1,   1   ],  # O  0, 1, 1
          [ 0,   1.5, 1   ],
          [ 0,   1,   1.5 ],
          [ 0.5, 1.5, 1.5 ],
          [ 1.5, 1,   1   ],  # O  1, 1, 1
          [ 1,   1.5, 1   ],
          [ 1,   1,   1.5 ],
          [ 1.5, 1.5, 1.5 ] ],
        [ 28, 46, 46, 46 ]
      + [ 46, 28, 28, 28 ] * 3
      + [ 28, 46, 46, 46 ] * 3
      + [ 46, 28, 28, 28 ]
      + [ 8, ] * 32
      ]

# check whether there is dupulication
for i, p1 in enumerate( nio_af[1] ):
    for p2 in nio_af[1][ i+1 : ]:
        if p1 == p2:
            print( p1 )

# lattice constants should be twice in 2*2*2 super cell
nio_af[0] = [ list( a_ ) for a_ in np.array( nio_af[0] ) * 2 ]

# coordinate should be divided by 2 
nio_af[1] = [ list( p_ ) for p_ in np.array( nio_af[1] ) / 2 ]

nio_af = tuple( nio_af )

print( spglib.get_spacegroup( nio_af ) )
#R-3m (166)

print( spglib.find_primitive( nio_af ) )
#(array([[ 1.48138871,  0.85528017,  4.83819526],
#       [-1.48138871,  0.85528017,  4.83819526],
#       [ 0.        , -1.71056034,  4.83819526]]), array([[-3.70074342e-17,  0.00000000e+00,  0.00000000e+00],
#       [ 5.00000000e-01,  5.00000000e-01,  5.00000000e-01],
#       [ 7.50000000e-01,  7.50000000e-01,  7.50000000e-01],
#       [ 2.50000000e-01,  2.50000000e-01,  2.50000000e-01]]), array([28, 46,  8,  8], dtype=int32))

対称性がFm-3mからR-3mに落ちているのが分かる。

ここで、結晶軸がベクトル表記のままだと結晶構造作成プログラムでは不便な場合があるので、格子定数の形式に変換すると、

prim_nio_af = spglib.find_primitive( nio_af )
axes = prim_nio_af[0]

abc = [ np.linalg.norm( axis ) for axis in axes ]
print( abc )
#[5.131681011130759, 5.131681011130759, 5.131681011130759]

# alpha beta gamma
abg =[]
abg.append( np.degrees( np.arccos( np.dot( axes[1], axes[2] ) / np.linalg.norm(axes[1]) / np.linalg.norm(axes[2]) ) ) )
abg.append( np.degrees( np.arccos( np.dot( axes[2], axes[0] ) / np.linalg.norm(axes[2]) / np.linalg.norm(axes[0]) ) ) )
abg.append( np.degrees( np.arccos( np.dot( axes[0], axes[1] ) / np.linalg.norm(axes[0]) / np.linalg.norm(axes[1]) ) ) )
print( abg )
#[33.55730976192072, 33.55730976192072, 33.55730976192072]

これで、第一原理計算プログラムで反強磁性が計算できるようになった。