昔,ANODEで計算する異常分散差フーリエが,他のプログラム(phenixやRefmac)と比べてもS/Nが良いことが気になり,調査したときのメモを発掘したのでここに清書. ANODEが良いのはその前処理に使っているSHELXCによるoutlier rejectionが大いに効いていることが分かった.
SHELXCは以下の2つの条件に基づいてrejectionを行う模様.
条件1: $$ \sigma^2(I^+)>6.25\sigma^2(I^-) {\rm ~or~ } \sigma^2(I^-)>6.25\sigma^2(I^+) $$ 条件2: $$ \min(\sqrt{I^+}, \sqrt{I^-}) \le 4\sqrt{ (\sqrt{I^+ +\sigma(I^+)} - \sqrt{I^+})^2 + (\sqrt{I^- +\sigma(I^-)} - \sqrt{I^-})^2 } $$ ただし\(I < 0\)のときは\(I = 0\)とする.
いかにもSHELXらしい,empiricalな方法という感じ. このrejection後,SHELXCは\(e^{-2.5s^2/4}\)を掛けて出力している.さらにANODEは\(B=10\)を掛けて計算しているようだ.
unmerged intensityを与えた場合の挙動は調査しきれていない.昔は重みなしで平均を計算してしまっていたが,ある時点から重み付けするようにはなったらしい(10年前に聞いた話). あとは\(\sigma(\langle I \rangle)\)はinternal/external varianceの最大値として計算されているようだ.
上記rejectionを確認するために書いたと思われるコードも発掘したので,動くかも分からないがいちおう載せておく.Python2だ…
import iotbx.file_reader
from cctbx.array_family import flex
import math
if __name__ == "__main__":
f1 = "xscale.sca"
fano = "anode_fa.hkl"
i_org = iotbx.file_reader.any_file(f1).file_server.miller_arrays[0]
i_org.show_summary()
print
fa = iotbx.file_reader.any_file(fano+"=amplitudes").file_server.miller_arrays[0]
fa.show_summary()
fa = fa.customized_copy(crystal_symmetry=i_org.crystal_symmetry())
i_org_fa = i_org.anomalous_differences()
matches = i_org_fa.match_indices(fa)
flags = flex.bool(i_org.size(), False)
flags.set_selected(matches.pairs().column(0), True)
ofs = open("sca_common.dat", "w")
print >>ofs, "h k l d I sig common"
for hkl, r, d, s, f in zip(i_org_fa.indices(), i_org_fa.d_spacings().data(), i_org_fa.data(), i_org_fa.sigmas(), flags):
print >>ofs, "% 4d % 4d % 4d" % hkl,
print >>ofs, r, d,s,f
asu, matches = i_org.match_bijvoet_mates()
ip = i_org.select(matches.pairs().column(0))
im = i_org.select(matches.pairs().column(1))
# flag is True when not rejected
ofs = open("test_reject.dat", "w")
print >>ofs, "h k l Ip Im SA flag"
for hkl, Ip, SigIp, Im, SigIm in zip(ip.indices(),
ip.data(), ip.sigmas(),
im.data(), im.sigmas()):
print >>ofs, "% 4d % 4d % 4d" % hkl,
print >>ofs, Ip, Im,
S, T = SigIp**2, SigIm**2
flag = not (S>6.25*T or T>6.25*S)
if flag:
Fp, Fm = math.sqrt(max(Ip, 0)), math.sqrt(max(Im, 0))
sa = math.sqrt( (math.sqrt(Fp**2+SigIp)-Fp)**2 + (math.sqrt(Fm**2+SigIm)-Fm)**2 )
flag = min(Fp,Fm) > 4*sa
print >>ofs, sa, flag
当時,かなりがっつり反射数が減ることに驚いたような記憶がある. 適切なweightingを考えるよりも経験的な方法でrejectしてしまう方がいいというのはちょっと残念な感じがするが,もっと良い方法にたどり着けたらいいですね.