pg_generators.py 技術ドキュメント

プログラムの動作

pg_generators.py は、指定された結晶点群(Hermann-Mauguin記号で表現)の対称操作を解析し、その生成元(ジェネレータ)と、各対称操作を生成元で表現した「語」を特定するPythonプログラムです。

主な機能は以下の通りです。

  • pymatgen ライブラリを使用して、指定された点群のすべての対称操作(回転行列)を取得します。

  • 浮動小数点演算に伴う誤差を考慮し、行列を正規化・安定化します。

  • すべての対称操作から、その点群を構成するための最小限の生成元セットを探索し、抽出します。生成元は、特定の軸(例: \(z\) 軸)に沿った回転や鏡映が優先的に選ばれるようにソートされます。

  • 各生成元が何回の操作で恒等元に戻るか(位数)を推定します。

  • 抽出された生成元を用いて、各対称操作を生成元の組み合わせ(「語」)として表現します。この際、位数を考慮して指数表記(例: \(C_6^2\))を最適化し、最短経路の語(幅優先探索による)を出力します。

  • 生成元には自動でラベルが割り当てられますが、コマンドライン引数でユーザーが任意のラベルを指定することも可能です。

  • 結果は、整形されたテキスト形式で標準出力に表示され、オプションでJSON形式での出力も可能です。

このプログラムは、結晶学や材料科学の分野で、特定の結晶点群の対称性を理解し、その操作を効率的に表現する必要がある場合に有用です。

原理

pg_generators.py は、以下の主要な数学的・アルゴリズム的原理に基づいて動作します。

1. 直交行列の安定化と正規化

対称操作は3次元空間における回転や鏡映を表すため、理論上は直交行列です。しかし、浮動小数点演算には誤差が伴うため、プログラム内で扱われる行列は厳密な直交性を満たさない場合があります。これを補正するため、orthogonalize_preserving_det 関数が使用されます。

この関数は、行列 \(R\) を特異値分解 (SVD) を用いて直交化します。

\[ R = U \Sigma V^T \]

ここで \(U\)\(V\) は直交行列、\(\Sigma\) は対角行列です。直交化された行列 \(R_{sv}\) は以下のように計算されます。

\[ R_{sv} = U V^T \]

このとき、行列式 \(\det(R_{sv})\) の符号が元の行列 \(\det(R)\) の符号と一致しない場合、 \(U\) の最終列の符号を反転させることで、行列式の符号を維持します。これは、回転と回反を正しく区別するために重要です。

canon 関数は、この直交化された行列を丸めてタプルに変換することで、浮動小数点誤差を吸収し、集合や辞書のキーとして使用可能にします。

2. 対称操作の分類と幾何情報

classify_and_geometry 関数は、与えられた対称操作行列 \(R\) を以下の種類に分類し、必要に応じて幾何情報(回転軸や鏡映面法線)を抽出します。

  • 恒等元 (I): \(R = I\) (単位行列)

  • 反転 (Inv): \(R = -I\)

  • 純粋回転 (Rot): \(\det(R) = 1\) かつ \(R \neq I\)

    • 回転次数 \(n\) は、回転角 \(\theta\) から求められます。 $\( \cos \theta = \frac{\text{trace}(R) - 1}{2} \)\( \)\( n = \frac{2\pi}{\theta} \)\( 回転軸は、固有値 \)1$ に対応する固有ベクトルとして求められます。

  • 鏡映 (Mir): \(\det(R) = -1\) かつ \(R^2 = I\)

    • 鏡映面の法線は、固有値 \(-1\) に対応する固有ベクトルとして求められます。

  • 回反操作 (Rinv): \(\det(R) = -1\) かつ \(R^2 \neq I\)

    • 回反次数 \(n\) は、\(-R\) の回転次数として求められます。回反軸は \(-R\) の回転軸と同じです。

eigvec_for_value 関数は、指定された固有値に対応する固有ベクトルを計算します。

3. ジェネレータの抽出

find_generators 関数は、与えられた対称操作の集合から、その点群を生成する最小の生成元セットを決定します。

  1. 対称性優先スコア: symmetry_priority_score 関数は、各対称操作に対して優先順位を決定するためのスコアを割り当てます。

    • \(z\) 軸周りの回転や \(xy\) 平面鏡映など、結晶学的に重要な方向に関連する操作に高いスコアを与えます。

    • 回転次数が高いほどスコアが高くなります。

    • これにより、より直感的で理解しやすい生成元セットが選ばれる傾向があります。

  2. 貪欲法と閉包生成:

    • すべての対称操作を、このスコアに基づいて降順にソートします。

    • ソートされた操作を一つずつ候補ジェネレータに追加し、現在のジェネレータセットで生成される閉包(グループ全体)が、目的の全対称操作集合をカバーするかを検証します。閉包のサイズが大きくなる場合のみ、その操作をジェネレータとして採用します。

    • この閉包生成には、後述の closure_with_words 関数が使用されます。

  3. 冗長性の削除: 最終的なジェネレータセットから、削除しても群全体を生成できる冗長なジェネレータを削除します。

  4. 順序付け: 最終的に抽出されたジェネレータは、再度 prefer_key に基づいてソートされ、特定の対称操作(例: \(z\) 軸回転、\(xy\) 面鏡映)が先頭に来るように並べられます。

4. 閉包生成と「語」の構成

closure_with_words 関数は、与えられた生成元セットから、それらによって生成されるすべての対称操作と、その操作に至るまでの「語」(生成元のシーケンス)を幅優先探索 (BFS) を用いて見つけます。

  • スナップ機能: 浮動小数点誤差による判定の問題を解決するため、build_snapper 関数によって作成されたスナップ機能が使用されます。これは、生成された新しい操作が、すでに知られている最も近い既存の操作に「スナップ」させることで、わずかな誤差を無視し、同一の操作として扱います。

  • 語の保持: BFSは最短経路を見つけるため、各操作に到達するまでの最短の「語」(ジェネレータ番号のリスト)が記録されます。

  • 右掛け: 新しい操作 \(B\) は、既存の操作 \(A\) にジェネレータ \(G_j\) を右から掛けることで生成されます (\(B = A \cdot G_j\))。これにより、語 \(w_B = w_A + [j]\) となります。

5. 位数の推定と語の整形

estimate_orders 関数は、各生成元 \(G\) について、その位数 \(n\)(すなわち \(G^n = E\) となる最小の正整数)を探索します。結晶点群の操作は周期性が低いため、最大12回程度の繰り返しで十分です。

format_word 関数は、生成元の語(ジェネレータ番号のシーケンス)を人間が読みやすい文字列形式に整形します。

  • 連続する同じジェネレータは指数表記にまとめられます(例: \(g_1, g_1, g_1 \rightarrow g_1^3\))。

  • 生成元の位数 \(n\) が分かっている場合、指数は \(n\) を法として正規化されます(例: 位数6の \(C_6\) なら \(C_6^7 \rightarrow C_6^1\))。指数が \(0\) になる場合は、その部分は語から削除されます。

必要な非標準ライブラリとインストール方法

pg_generators.py を実行するためには、以下の非標準ライブラリが必要です。

  • numpy: 数値計算、特に配列操作や線形代数演算に使用されます。

  • pymatgen: 結晶学的なデータ構造や対称性操作の取得に使用されます。

これらのライブラリは pip コマンドでインストールできます。

pip install numpy pymatgen

必要な入力ファイル

pg_generators.py は、入力ファイルは必要としません。すべての情報はコマンドライン引数として提供されます。

生成される出力ファイル

pg_generators.py は、出力ファイルを生成しません。すべての結果は標準出力 (STDOUT) に表示されます。

  • テキスト形式: プログラムの実行結果は、点群の名前、総操作数、生成元のリスト(名前、ラベル、位数、行列)、および生成された各対称操作(名前、語、分類ラベル、行列)を含む整形されたテキスト形式で標準出力に表示されます。

  • JSON形式 (オプション): --json オプションが指定された場合、上記の情報に加えて、構造化されたJSON形式のデータが標準出力に追加で出力されます。これは他のプログラムとの連携やデータ処理に便利です。

コマンドラインでの使用例 (Usage)

基本的なコマンドラインでの使用法は以下の通りです。

python pg_generators.py --pg <point_group_symbol> [--gen-labels <labels>] [--json]

引数の説明:

  • --pg, -p (必須): 解析対象の点群のHermann-Mauguin記号を指定します。例: 6, 4mm, 6mm, -3m

  • --gen-labels (オプション): 生成元にカスタムラベルを割り当てるための文字列です。カンマ区切りで 'g1=Label1,g2=Label2' の形式で指定します。例えば、'g1=C6(z),g2=m(⊥z)' のように記述します。g1 は1番目の生成元、g2 は2番目の生成元に対応します。

  • --json (オプション): このフラグを指定すると、テキスト出力に加えてJSON形式のデータも標準出力に出力されます。

コマンドラインでの具体的な使用例

例1: 点群 6mm の生成元と操作を解析する

python pg_generators.py -p 6mm

実行結果の説明: このコマンドは、点群 6mm の生成元と、それらを用いて表現されるすべての対称操作を出力します。出力には、合計操作数、生成元のリスト(行列、自動生成されたラベル、位数)、および各操作の行列と生成元の語が含まれます。

出力の一部(抜粋):

--- Point Group 6mm ---
Total operations: 12
Verification (⟨gens⟩ == group): OK

Generators (preferred order):
  g1 = C6(z)  (order=6)
[[-0.500000 -0.866025  0.000000]
 [ 0.866025 -0.500000  0.000000]
 [ 0.000000  0.000000  1.000000]]

  g2 = m(⊥x)  (order=2)
[[ 1.000000  0.000000  0.000000]
 [ 0.000000 -1.000000  0.000000]
 [ 0.000000  0.000000 -1.000000]]

Generated elements (excluding identity):
  O01: g1   =>  C6(z)
[[-0.500000 -0.866025  0.000000]
 [ 0.866025 -0.500000  0.000000]
 [ 0.000000  0.000000  1.000000]]

  O02: g1^2   =>  C3(z)
[[-0.500000  0.866025  0.000000]
 [-0.866025 -0.500000  0.000000]
 [ 0.000000  0.000000  1.000000]]
...

例2: 点群 -3m の生成元にカスタムラベルを付け、JSON形式で出力する

python pg_generators.py -p -3m --gen-labels "g1=C3(z),g2=m(x-y)" --json

実行結果の説明: このコマンドは、点群 -3m の解析結果を、g1\(z\) 軸周りの \(C_3\) 回転、g2\(x-y\) 方向の鏡映としてカスタムラベルを付けて出力します。さらに、結果全体がJSON形式でも出力されるため、機械可読なデータとして利用できます。標準のテキスト出力の後にJSONが出力されます。

出力の一部(抜粋):

--- Point Group -3m ---
Total operations: 12
Verification (⟨gens⟩ == group): OK

Generators (preferred order):
  g1 = C3(z)  (order=3)
[[-0.500000 -0.866025  0.000000]
 [ 0.866025 -0.500000  0.000000]
 [ 0.000000  0.000000  1.000000]]

  g2 = m(x-y)  (order=2)
[[ 0.000000  1.000000  0.000000]
 [ 1.000000  0.000000  0.000000]
 [ 0.000000  0.000000 -1.000000]]

Generated elements (excluding identity):
  O01: g1   =>  C3(z)
[[-0.500000 -0.866025  0.000000]
 [ 0.866025 -0.500000  0.000000]
 [ 0.000000  0.000000  1.000000]]
...
{
  "point_group": "-3m",
  "total_ops": 12,
  "verified": true,
  "generators": [
    {
      "name": "g1",
      "label": "C3(z)",
      "order": 3,
      "matrix": [
        [-0.5000000000, -0.8660254038, 0.0000000000],
        [0.8660254038, -0.5000000000, 0.0000000000],
        [0.0000000000, 0.0000000000, 1.0000000000]
      ]
    },
    {
      "name": "g2",
      "label": "m(x-y)",
      "order": 2,
      "matrix": [
        [0.0000000000, 1.0000000000, 0.0000000000],
        [1.0000000000, 0.0000000000, 0.0000000000],
        [0.0000000000, 0.0000000000, -1.0000000000]
      ]
    }
  ],
  "elements": [
    {
      "name": "O1",
      "word": "g1",
      "class": "C3(z)",
      "matrix": [
        [-0.5000000000, -0.8660254038, 0.0000000000],
        [0.8660254038, -0.5000000000, 0.0000000000],
        [0.0000000000, 0.0000000000, 1.0000000000]
      ]
    },
...
  ]
}