指数の正規化、あるいは、asymmetric unit への変換

マージする時や mtz ファイルを解釈する時など、与えられた指数を標準的な asymmetric unit 内の指数に変換する必要が生じる。もっとも単純なアルゴリズムは以下のとおり。

  1. あらかじめ点群における対称操作 S_1, S_2, \dots, S_n を列挙しておく
  2. 与えられた指数 h に、上の対称操作を適用して、S_1h, S_2h, \dots, S_nh を作る
  3. 指数のうち、asymmetric unit 内に入るもの(unique に決まる)を選ぶ

最初のステップは、単位元 E しかもたない点群 (P1) から初めて、生成子を1つずつ足していけばよい。CrystFEL での実装は symmetry.c の expand_ops() 関数 にある。

最後のステップは、愚直に行うと、asymmetric unit を点群ごとに定義しなければならず、煩雑に思える。1つの工夫としては、指数  \mathbf{h} \in \mathbb{I}^3 に順序を入れることができる(単純に、h で大小比較し、h が同じなら k で大小比較し、それでも同じなら l で比較すればよい)ので、S_1h, S_2h, \dots, S_nh の中から最小のものを選ぶことにすれば、それが1つの asymmetric unit となっている。CrystFEL の実装(symmetry.c の get_asymm() 関数) はこうなっている。一方、Clipper は、真面目に asymmetric unit を定義していて、asymmetric unit 内の指数を発見したらループを抜けるようになっている。

このアルゴリズムには O(n) の時間がかかる。気になる場合は、結果をキャッシュしておくとよい。また、フーリエ変換などでは、逆空間で隣接した指数に連続してアクセスすることが多い。この場合、隣り合う指数は同じ対称操作で asymmetric unit に落ちることが多いので、前回採用された操作を覚えておいて、それを優先的に試すようにすると効率的である。Clipper はこのテクニックを使っている(Clipper: Developing using Reflection Data 参照)。