104 lines
2.6 KiB
Python
104 lines
2.6 KiB
Python
import gemmi
|
||
import numpy as np
|
||
import matplotlib.pyplot as plt
|
||
# -----------------------------
|
||
# User inputs
|
||
# -----------------------------
|
||
apo_pdb = "../data/hewl_apo.pdb"
|
||
occ100_pdb = "../data/hewl_1.0-I.pdb"
|
||
occb_pdb = "../data/hewl_0.5-I.pdb"
|
||
|
||
b = 0.5
|
||
a = 1.0 - b
|
||
dmin = 2.0 # resolution cutoff (Å)
|
||
|
||
# -----------------------------
|
||
# Compute structure factors
|
||
# -----------------------------
|
||
def compute_sf(pdb_file, dmin):
|
||
# Read structure
|
||
st = gemmi.read_structure(pdb_file)
|
||
st.setup_entities()
|
||
st.assign_label_seq_id()
|
||
st.remove_alternative_conformations()
|
||
|
||
model = st[0]
|
||
cell = st.cell
|
||
|
||
# Space group
|
||
sg = st.find_spacegroup()
|
||
if sg is None:
|
||
if st.spacegroup_hm:
|
||
sg = gemmi.SpaceGroup(st.spacegroup_hm)
|
||
else:
|
||
raise RuntimeError("No space group found")
|
||
|
||
# Generate HKLs
|
||
hkl = gemmi.make_miller_array(
|
||
cell=cell,
|
||
spacegroup=sg,
|
||
dmin=dmin,
|
||
unique=True
|
||
)
|
||
|
||
# Structure factor calculator
|
||
calc = gemmi.StructureFactorCalculatorX(cell)
|
||
|
||
# Compute SFs ONE reflection at a time (this is required)
|
||
F = np.array([
|
||
calc.calculate_sf_from_model(model, [int(h), int(k), int(l)])
|
||
for h, k, l in hkl
|
||
], dtype=np.complex128)
|
||
|
||
return hkl, F
|
||
|
||
|
||
hkl, Fa = compute_sf(apo_pdb, dmin)
|
||
_, F100 = compute_sf(occ100_pdb, dmin)
|
||
_, Fb_model = compute_sf(occb_pdb, dmin)
|
||
|
||
# -----------------------------
|
||
# Sanity: same HKLs?
|
||
# -----------------------------
|
||
assert len(Fa) == len(F100) == len(Fb_model)
|
||
|
||
# -----------------------------
|
||
# Test forward relation:
|
||
# Fab = a Fa + b F100
|
||
# -----------------------------
|
||
Fab = a * Fa + b * F100
|
||
print(Fab)
|
||
|
||
# Compare to model b-occupancy SFs
|
||
delta_forward = Fab - Fb_model
|
||
|
||
print("FORWARD TEST: Fab = aFa + bF100")
|
||
print("Mean |ΔF|:", np.mean(np.abs(delta_forward)))
|
||
print("Mean phase Δ (deg):",
|
||
np.mean(np.abs(np.angle(Fab / Fb_model, deg=True))))
|
||
|
||
# -----------------------------
|
||
# Test inverse relation:
|
||
# F100 = (Fab − Fa) / b
|
||
# -----------------------------
|
||
F100_recon = (Fb_model - Fa) / b + Fa
|
||
|
||
delta_inverse = F100_recon - F100
|
||
|
||
print("\nINVERSE TEST: F100 = (Fab − Fa)/b + Fa")
|
||
print("Mean |ΔF|:", np.mean(np.abs(delta_inverse)))
|
||
print("Mean phase Δ (deg):",
|
||
np.mean(np.abs(np.angle(F100_recon / F100, deg=True))))
|
||
|
||
# -----------------------------
|
||
# Optional: inspect worst reflections
|
||
# -----------------------------
|
||
idx = np.argsort(np.abs(delta_inverse))[-5:]
|
||
|
||
print("\nWorst reflections (inverse):")
|
||
for i in idx:
|
||
print(
|
||
f"|F100|={abs(F100[i]):.2f} "
|
||
f"|ΔF|={abs(delta_inverse[i]):.2f} "
|
||
f"Δφ={np.angle(F100_recon[i]/F100[i],deg=True):.1f}°"
|
||
) |