Пример #1
0
def test_calc_lookup_index():
    pi = 3.141
    theta_phi = flex.vec2_double([(0.001, -pi), (pi, pi), (0.001, pi), (pi, -pi)])
    indices = calc_lookup_index(theta_phi, 1)
    assert list(indices) == [0, 64799, 359, 64440]
    indices = calc_lookup_index(theta_phi, 2)
    assert list(indices) == [0, 259199, 719, 258480]
Пример #2
0
 def configure_components(self, reflection_table, experiment, params):
     """Add the required reflection table data to the model components."""
     if "scale" in self.components:
         norm = (reflection_table["xyzobs.px.value"].parts()[2] *
                 self._configdict["s_norm_fac"])
         self.components["scale"].data = {"x": norm}
     if "decay" in self.components:
         norm = (reflection_table["xyzobs.px.value"].parts()[2] *
                 self._configdict["d_norm_fac"])
         self.components["decay"].parameter_restraints = flex.double(
             self.components["decay"].parameters.size(),
             params.physical.decay_restraint,
         )
         self.components["decay"].data = {
             "x": norm,
             "d": reflection_table["d"]
         }
     if "absorption" in self.components:
         lmax = self._configdict["lmax"]
         if reflection_table.size() > 100000:
             assert "s0c" in reflection_table
             assert "s1c" in reflection_table
             theta_phi_0 = calc_theta_phi(
                 reflection_table["s0c"])  # array of tuples in radians
             theta_phi_1 = calc_theta_phi(reflection_table["s1c"])
             s0_lookup_index = calc_lookup_index(theta_phi_0,
                                                 points_per_degree=2)
             s1_lookup_index = calc_lookup_index(theta_phi_1,
                                                 points_per_degree=2)
             if SHScaleComponent.coefficients_list is None:
                 SHScaleComponent.coefficients_list = create_sph_harm_lookup_table(
                     lmax, points_per_degree=2
                 )  # set the class variable and share
             elif len(SHScaleComponent.coefficients_list) < (lmax *
                                                             (2.0 + lmax)):
                 # this (rare) case can happen if adding a new dataset with a larger lmax!
                 SHScaleComponent.coefficients_list = create_sph_harm_lookup_table(
                     lmax, points_per_degree=2
                 )  # set the class variable and share
             self.components["absorption"].data = {
                 "s0_lookup": s0_lookup_index,
                 "s1_lookup": s1_lookup_index,
             }
         # here just pass in good reflections
         else:
             self.components["absorption"].data = {
                 "sph_harm_table": sph_harm_table(reflection_table, lmax)
             }
         surface_weight = self._configdict["abs_surface_weight"]
         parameter_restraints = flex.double([])
         for i in range(1, lmax + 1):
             parameter_restraints.extend(flex.double([1.0] * ((2 * i) + 1)))
         parameter_restraints *= surface_weight
         self.components[
             "absorption"].parameter_restraints = parameter_restraints
Пример #3
0
def test_equality_of_two_harmonic_table_methods(dials_regression, run_in_tmpdir):
    from dials_scaling_ext import calc_theta_phi, calc_lookup_index
    from dxtbx.serialize import load
    from dials.util.options import OptionParser
    from libtbx import phil
    from dials.algorithms.scaling.scaling_library import create_scaling_model

    data_dir = os.path.join(dials_regression, "xia2-28")
    pickle_path = os.path.join(data_dir, "20_integrated.pickle")
    sequence_path = os.path.join(data_dir, "20_integrated_experiments.json")

    phil_scope = phil.parse(
        """
      include scope dials.command_line.scale.phil_scope
    """,
        process_includes=True,
    )
    optionparser = OptionParser(phil=phil_scope, check_format=False)
    params, _ = optionparser.parse_args(args=[], quick_parse=True)
    params.model = "physical"
    lmax = 2
    params.physical.lmax = lmax

    reflection_table = flex.reflection_table.from_file(pickle_path)
    experiments = load.experiment_list(sequence_path, check_format=False)
    experiments = create_scaling_model(params, experiments, [reflection_table])

    experiment = experiments[0]
    # New method

    reflection_table["phi"] = (
        reflection_table["xyzobs.px.value"].parts()[2]
        * experiment.scan.get_oscillation()[1]
    )
    reflection_table = calc_crystal_frame_vectors(reflection_table, experiment)
    theta_phi_0 = calc_theta_phi(reflection_table["s0c"])  # array of tuples in radians
    theta_phi_1 = calc_theta_phi(reflection_table["s1c"])
    points_per_degree = 2
    s0_lookup_index = calc_lookup_index(theta_phi_0, points_per_degree)
    s1_lookup_index = calc_lookup_index(theta_phi_1, points_per_degree)
    print(list(s0_lookup_index[0:20]))
    print(list(s1_lookup_index[0:20]))
    coefficients_list = create_sph_harm_lookup_table(lmax, points_per_degree)
    experiment.scaling_model.components["absorption"].data = {
        "s0_lookup": s0_lookup_index,
        "s1_lookup": s1_lookup_index,
    }
    experiment.scaling_model.components[
        "absorption"
    ].coefficients_list = coefficients_list
    assert experiment.scaling_model.components["absorption"]._mode == "memory"
    experiment.scaling_model.components["absorption"].update_reflection_data()
    absorption = experiment.scaling_model.components["absorption"]
    harmonic_values_list = absorption.harmonic_values[0]

    experiment.scaling_model.components["absorption"].parameters = flex.double(
        [0.1, -0.1, 0.05, 0.02, 0.01, -0.05, 0.12, -0.035]
    )
    scales, derivatives = experiment.scaling_model.components[
        "absorption"
    ].calculate_scales_and_derivatives()

    # Old method:

    old_data = {"sph_harm_table": create_sph_harm_table(theta_phi_0, theta_phi_1, lmax)}
    experiment.scaling_model.components["absorption"].data = old_data
    assert experiment.scaling_model.components["absorption"]._mode == "speed"
    experiment.scaling_model.components["absorption"].update_reflection_data()
    old_harmonic_values = absorption.harmonic_values[0]
    for i in range(0, 8):
        print(i)
        assert list(harmonic_values_list[i]) == pytest.approx(
            list(old_harmonic_values.col(i).as_dense_vector()), abs=0.01
        )

    experiment.scaling_model.components["absorption"].parameters = flex.double(
        [0.1, -0.1, 0.05, 0.02, 0.01, -0.05, 0.12, -0.035]
    )
    scales_1, derivatives_1 = experiment.scaling_model.components[
        "absorption"
    ].calculate_scales_and_derivatives()

    assert list(scales_1) == pytest.approx(list(scales), abs=0.001)
    assert list(scales_1) != [1.0] * len(scales_1)
Пример #4
0
def plot_absorption_plots(physical_model, reflection_table=None):
    """Make a number of plots to help with the interpretation of the
    absorption correction."""
    # First plot the absorption surface

    d = {
        "absorption_surface": {
            "data": [],
            "layout": {
                "title": "Absorption correction surface",
                "xaxis": {
                    "domain": [0, 1],
                    "anchor": "y",
                    "title": "azimuthal angle (degrees)",
                },
                "yaxis": {
                    "domain": [0, 1],
                    "anchor": "x",
                    "title": "polar angle (degrees)",
                },
            },
            "help": absorption_help_msg,
        }
    }

    params = physical_model.components["absorption"].parameters

    order = int(-1.0 + ((1.0 + len(params)) ** 0.5))
    lfg = scitbxmath.log_factorial_generator(2 * order + 1)
    STEPS = 50
    azimuth_ = np.linspace(0, 2 * np.pi, 2 * STEPS)
    polar_ = np.linspace(0, np.pi, STEPS)
    THETA, _ = np.meshgrid(azimuth_, polar_, indexing="ij")
    lmax = int(-1.0 + ((1.0 + len(params)) ** 0.5))
    Intensity = np.ones(THETA.shape)
    undiffracted_intensity = np.ones(THETA.shape)
    counter = 0
    sqrt2 = math.sqrt(2)
    nsssphe = scitbxmath.nss_spherical_harmonics(order, 50000, lfg)
    for l in range(1, lmax + 1):
        for m in range(-l, l + 1):
            for it, t in enumerate(polar_):
                for ip, p in enumerate(azimuth_):
                    Ylm = nsssphe.spherical_harmonic(l, abs(m), t, p)
                    if m < 0:
                        r = sqrt2 * ((-1) ** m) * Ylm.imag
                    elif m == 0:
                        assert Ylm.imag == 0.0
                        r = Ylm.real
                    else:
                        r = sqrt2 * ((-1) ** m) * Ylm.real
                    Intensity[ip, it] += params[counter] * r
                    # for the undiffracted intensity, we want to add the correction
                    # at each point to the parity conjugate. We can use the fact
                    # that the odd l terms are parity odd, and even are even, to
                    # just calculate the even terms as follows
                    if l % 2 == 0:
                        undiffracted_intensity[ip, it] += params[counter] * r
            counter += 1
    d["absorption_surface"]["data"].append(
        {
            "x": list(azimuth_ * 180.0 / np.pi),
            "y": list(polar_ * 180.0 / np.pi),
            "z": list(Intensity.T.tolist()),
            "type": "heatmap",
            "colorscale": "Viridis",
            "colorbar": {"title": "inverse <br>scale factor"},
            "name": "absorption surface",
            "xaxis": "x",
            "yaxis": "y",
        }
    )

    d["undiffracted_absorption_surface"] = {
        "data": [],
        "layout": {
            "title": "Undiffracted absorption correction",
            "xaxis": {
                "domain": [0, 1],
                "anchor": "y",
                "title": "azimuthal angle (degrees)",
            },
            "yaxis": {
                "domain": [0, 1],
                "anchor": "x",
                "title": "polar angle (degrees)",
            },
        },
        "help": """
This plot shows the calculated relative absorption for a paths travelling
straight through the crystal at a given direction in a crystal-fixed frame of
reference (in spherical coordinates). This gives an indication of the effective
shape of the crystal for absorbing x-rays. In this plot, the pole (polar angle 0)
corresponds to the laboratory x-axis.
""",
    }

    d["undiffracted_absorption_surface"]["data"].append(
        {
            "x": list(azimuth_ * 180.0 / np.pi),
            "y": list(polar_ * 180.0 / np.pi),
            "z": list(undiffracted_intensity.T.tolist()),
            "type": "heatmap",
            "colorscale": "Viridis",
            "colorbar": {"title": "inverse <br>scale factor"},
            "name": "Undiffracted absorption correction",
            "xaxis": "x",
            "yaxis": "y",
        }
    )

    if not reflection_table:
        return d

    # now plot the directions of the scattering vectors

    d["vector_directions"] = {
        "data": [],
        "layout": {
            "title": "Scattering vectors in crystal frame",
            "xaxis": {
                "domain": [0, 1],
                "anchor": "y",
                "title": "azimuthal angle (degrees)",
                "range": [0, 360],
            },
            "yaxis": {
                "domain": [0, 1],
                "anchor": "x",
                "title": "polar angle (degrees)",
                "range": [0, 180],
            },
            "coloraxis": {
                "showscale": False,
            },
        },
        "help": """
This plot shows the scattering vector directions in the crystal reference frame
used to determine the absorption correction. The s0 vectors are plotted in yellow,
the s1 vectors are plotted in teal. This gives an indication of which parts of
the absorption correction surface are sampled when determining the absorption
correction. In this plot, the pole (polar angle 0) corresponds to the laboratory
x-axis.""",
    }

    STEPS = 180  # do one point per degree
    azimuth_ = np.linspace(0, 2 * np.pi, 2 * STEPS)
    polar_ = np.linspace(0, np.pi, STEPS)
    THETA, _ = np.meshgrid(azimuth_, polar_, indexing="ij")
    Intensity = np.full(THETA.shape, np.NAN)

    # note, the s1_lookup, s0_lookup is only calculated for large datasets, so
    # for small datasets we need to calculate again.
    if "s1_lookup" not in physical_model.components["absorption"].data:
        s1_lookup = calc_lookup_index(
            calc_theta_phi(reflection_table["s1c"]), points_per_degree=1
        )
        idx_polar, idx_azimuth = np.divmod(np.unique(s1_lookup), 360)
        Intensity[idx_azimuth, idx_polar] = 1
    else:
        s1_lookup = np.unique(physical_model.components["absorption"].data["s1_lookup"])
        # x is phi, y is theta
        idx_polar, idx_azimuth = np.divmod(s1_lookup, 720)
        idx_polar = idx_polar // 2  # convert from two points per degree to one
        idx_azimuth = idx_azimuth // 2
        Intensity[idx_azimuth, idx_polar] = 1

    d["vector_directions"]["data"].append(
        {
            "x": list(azimuth_ * 180.0 / np.pi),
            "y": list(polar_ * 180.0 / np.pi),
            "z": list(Intensity.T.tolist()),
            "type": "heatmap",
            "colorscale": "Viridis",
            "showscale": False,
            "xaxis": "x",
            "yaxis": "y",
            "zmin": 0,
            "zmax": 2,
        }
    )

    Intensity = np.full(THETA.shape, np.NAN)

    if "s0_lookup" not in physical_model.components["absorption"].data:
        s0_lookup = calc_lookup_index(
            calc_theta_phi(reflection_table["s0c"]), points_per_degree=1
        )
        idx_polar, idx_azimuth = np.divmod(np.unique(s0_lookup), 360)
        Intensity[idx_azimuth, idx_polar] = 2
    else:
        s0_lookup = np.unique(physical_model.components["absorption"].data["s0_lookup"])
        # x is phi, y is theta
        idx_polar, idx_azimuth = np.divmod(s0_lookup, 720)
        idx_polar = idx_polar // 2  # convert from two points per degree to one
        idx_azimuth = idx_azimuth // 2
        Intensity[idx_azimuth, idx_polar] = 2

    d["vector_directions"]["data"].append(
        {
            "x": list(azimuth_ * 180.0 / np.pi),
            "y": list(polar_ * 180.0 / np.pi),
            "z": list(Intensity.T.tolist()),
            "type": "heatmap",
            "colorscale": "Viridis",
            "showscale": False,
            "xaxis": "x",
            "yaxis": "y",
            "zmin": 0,
            "zmax": 2,
        }
    )

    scales = physical_model.components["absorption"].calculate_scales()
    hist = flex.histogram(scales, n_slots=min(100, int(scales.size() * 10)))

    d["absorption_corrections"] = {
        "data": [
            {
                "x": list(hist.slot_centers()),
                "y": list(hist.slots()),
                "type": "bar",
                "name": "Applied absorption corrections",
            },
        ],
        "layout": {
            "title": "Applied absorption corrections",
            "xaxis": {"anchor": "y", "title": "Inverse scale factor"},
            "yaxis": {"anchor": "x", "title": "Number of reflections"},
        },
    }

    return d
Пример #5
0
def test_equality_of_two_harmonic_table_methods(dials_data):
    location = dials_data("l_cysteine_dials_output", pathlib=True)
    refl = location / "20_integrated.pickle"
    expt = location / "20_integrated_experiments.json"

    phil_scope = phil.parse(
        """
      include scope dials.command_line.scale.phil_scope
    """,
        process_includes=True,
    )
    parser = ArgumentParser(phil=phil_scope, check_format=False)
    params, _ = parser.parse_args(args=[], quick_parse=True)
    params.model = "physical"
    lmax = 2
    params.physical.lmax = lmax

    reflection_table = flex.reflection_table.from_file(refl)
    experiments = load.experiment_list(expt, check_format=False)
    experiments = create_scaling_model(params, experiments, [reflection_table])

    experiment = experiments[0]
    # New method

    reflection_table["phi"] = (reflection_table["xyzobs.px.value"].parts()[2] *
                               experiment.scan.get_oscillation()[1])
    reflection_table = calc_crystal_frame_vectors(reflection_table, experiment)
    reflection_table["s1c"] = align_axis_along_z((1.0, 0.0, 0.0),
                                                 reflection_table["s1c"])
    reflection_table["s0c"] = align_axis_along_z((1.0, 0.0, 0.0),
                                                 reflection_table["s0c"])
    theta_phi_0 = calc_theta_phi(
        reflection_table["s0c"])  # array of tuples in radians
    theta_phi_1 = calc_theta_phi(reflection_table["s1c"])
    points_per_degree = 4
    s0_lookup_index = calc_lookup_index(theta_phi_0, points_per_degree)
    s1_lookup_index = calc_lookup_index(theta_phi_1, points_per_degree)
    print(list(s0_lookup_index[0:20]))
    print(list(s1_lookup_index[0:20]))
    coefficients_list = create_sph_harm_lookup_table(lmax, points_per_degree)
    experiment.scaling_model.components["absorption"].data = {
        "s0_lookup": s0_lookup_index,
        "s1_lookup": s1_lookup_index,
    }
    experiment.scaling_model.components[
        "absorption"].coefficients_list = coefficients_list
    assert experiment.scaling_model.components["absorption"]._mode == "memory"
    experiment.scaling_model.components["absorption"].update_reflection_data()
    absorption = experiment.scaling_model.components["absorption"]
    harmonic_values_list = absorption.harmonic_values[0]

    experiment.scaling_model.components["absorption"].parameters = flex.double(
        [0.1, -0.1, 0.05, 0.02, 0.01, -0.05, 0.12, -0.035])
    scales, derivatives = experiment.scaling_model.components[
        "absorption"].calculate_scales_and_derivatives()

    # Old method:

    old_data = {
        "sph_harm_table": create_sph_harm_table(theta_phi_0, theta_phi_1, lmax)
    }
    experiment.scaling_model.components["absorption"].data = old_data
    assert experiment.scaling_model.components["absorption"]._mode == "speed"
    experiment.scaling_model.components["absorption"].update_reflection_data()
    old_harmonic_values = absorption.harmonic_values[0]
    for i in range(0, 8):
        print(i)
        assert list(harmonic_values_list[i]) == pytest.approx(list(
            old_harmonic_values.col(i).as_dense_vector()),
                                                              abs=0.01)

    experiment.scaling_model.components["absorption"].parameters = flex.double(
        [0.1, -0.1, 0.05, 0.02, 0.01, -0.05, 0.12, -0.035])
    scales_1, derivatives_1 = experiment.scaling_model.components[
        "absorption"].calculate_scales_and_derivatives()

    assert list(scales_1) == pytest.approx(list(scales), abs=0.001)
    assert list(scales_1) != [1.0] * len(scales_1)