File: sim_images.py

package info (click to toggle)
dials 3.25.0%2Bdfsg3-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 20,112 kB
  • sloc: python: 134,740; cpp: 34,526; makefile: 160; sh: 142
file content (142 lines) | stat: -rw-r--r-- 5,373 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""Simulate a rotation dataset with a smoothly-varying beam position for
refinement testing. Script based on tst_nanoBragg_basic.py"""

from __future__ import annotations

import math

from cctbx.eltbx import henke
from dxtbx.model import (
    BeamFactory,
    Crystal,
    DetectorFactory,
    GoniometerFactory,
    ScanFactory,
)
from iotbx import pdb
from scitbx import matrix

_pdb_lines = """HEADER TEST
CRYST1   50.000   60.000   70.000  90.00  90.00  90.00 P 1
ATOM      1  O   HOH A   1      56.829   2.920  55.702  1.00 20.00           O
ATOM      2  O   HOH A   2      49.515  35.149  37.665  1.00 20.00           O
ATOM      3  O   HOH A   3      52.667  17.794  69.925  1.00 20.00           O
ATOM      4  O   HOH A   4      40.986  20.409  18.309  1.00 20.00           O
ATOM      5  O   HOH A   5      46.896  37.790  41.629  1.00 20.00           O
ATOM      6 SED  MSE A   6       1.000   2.000   3.000  1.00 20.00          SE
END
"""


class Simulation:
    def __init__(self, override_fdp=None):
        # Set up detector
        distance = 100
        pixel_size = 0.1
        image_size = (1000, 1000)
        beam_centre_mm = (
            pixel_size * image_size[0] / 2,
            pixel_size * image_size[1] / 2,
        )
        self.detector = DetectorFactory().simple(
            "CCD",
            distance,
            beam_centre_mm,
            "+x",
            "-y",
            (pixel_size, pixel_size),
            image_size,
        )

        # Set up beam
        self.beam = BeamFactory().simple(wavelength=1)

        # Set up scan
        sequence_width = 90.0
        osc_start = 0.0
        image_width = 0.2
        oscillation = (osc_start, image_width)

        nframes = int(math.ceil(sequence_width / image_width))
        image_range = (1, nframes)
        exposure_times = 0.0
        epochs = [0] * nframes
        self.scan = ScanFactory().make_scan(
            image_range, exposure_times, oscillation, epochs, deg=True
        )

        # Set up goniometer
        self.goniometer = GoniometerFactory.known_axis(self.detector[0].get_fast_axis())

        # Set up simulated structure factors
        self.sfall = self.fcalc_from_pdb(
            resolution=1.6, algorithm="direct", override_fdp=override_fdp
        )

        # Set up crystal
        self.crystal = Crystal(
            real_space_a=(50, 0, 0),
            real_space_b=(0, 60, 0),
            real_space_c=(0, 0, 70),
            space_group_symbol="P1",
        )
        axis = matrix.col(
            elems=(-0.14480368275412925, -0.6202131724405818, -0.7709523423610766)
        )
        self.crystal.set_U(
            axis.axis_and_angle_as_r3_rotation_matrix(angle=0.625126343998969)
        )

    def fcalc_from_pdb(self, resolution, algorithm=None, override_fdp=None):
        pdb_inp = pdb.input(source_info=None, lines=_pdb_lines)
        xray_structure = pdb_inp.xray_structure_simple()
        wavelength = self.beam.get_wavelength()
        #
        # take a detour to calculate anomalous contribution of every atom
        scatterers = xray_structure.scatterers()
        for sc in scatterers:
            expected_henke = henke.table(sc.element_symbol()).at_angstrom(wavelength)
            sc.fp = expected_henke.fp()
            sc.fdp = override_fdp if override_fdp is not None else expected_henke.fdp()

        # how do we do bulk solvent?
        primitive_xray_structure = xray_structure.primitive_setting()
        P1_primitive_xray_structure = primitive_xray_structure.expand_to_p1()
        fcalc = P1_primitive_xray_structure.structure_factors(
            d_min=resolution, anomalous_flag=True, algorithm=algorithm
        ).f_calc()
        return fcalc.amplitudes()

    def set_varying_beam(self, along="fast", npixels_drift=5):
        assert along in ["fast", "slow", "both"]
        num_scan_points = self.scan.get_num_images() + 1
        s0 = matrix.col(self.beam.get_s0())
        beam_centre_px = self.detector[0].get_beam_centre_px(s0)
        if along == "fast":
            start_beam_centre = (
                beam_centre_px[0] - npixels_drift / 2,
                beam_centre_px[1],
            )
            end_beam_centre = (beam_centre_px[0] + npixels_drift / 2, beam_centre_px[1])
        elif along == "slow":
            start_beam_centre = (
                beam_centre_px[0],
                beam_centre_px[1] - npixels_drift / 2,
            )
            end_beam_centre = (beam_centre_px[0], beam_centre_px[1] + npixels_drift / 2)
        elif along == "both":
            offset = math.sqrt(2.0) * npixels_drift / 4.0
            start_beam_centre = (beam_centre_px[0] - offset, beam_centre_px[1] - offset)
            end_beam_centre = (beam_centre_px[0] + offset, beam_centre_px[1] + offset)

        start_lab = matrix.col(self.detector[0].get_pixel_lab_coord(start_beam_centre))
        end_lab = matrix.col(self.detector[0].get_pixel_lab_coord(end_beam_centre))
        axis = start_lab.cross(end_lab).normalize()
        full_angle = start_lab.angle(end_lab)
        angle_step = full_angle / self.scan.get_num_images()
        angles = [e * angle_step for e in range(num_scan_points)]

        start_s0 = start_lab.normalize() * s0.length()
        s0_list = [start_s0.rotate_around_origin(axis=axis, angle=e) for e in angles]

        self.beam.set_s0_at_scan_points(s0_list)