def test_calc_crystal_frame_vectors_single_axis_gonio( test_reflection_table, test_experiment_singleaxisgonio ): """Test the namesake function, to check that the vectors are correctly rotated into the crystal frame.""" rt, exp = test_reflection_table, test_experiment_singleaxisgonio reflection_table = calc_crystal_frame_vectors(rt, exp) # s0c and s1c are normalised. s0c points towards the source. # as the crystal rotates about the x axis, the s0 vector moves in the y-z plane towards -y expected_s0c = [ (0.0, 0.0, -1.0), (0.0, -1.0 / sqrt(2.0), -1.0 / sqrt(2.0)), (0.0, -1.0, 0.0), ] for v1, v2 in zip(reflection_table["s0c"], expected_s0c): assert v1 == pytest.approx(v2) # the s1c vector should have fixed x-component, rotating in the y-z plane towards +y expected_s1c = [ (1.0 / sqrt(2.0), 0.0, 1.0 / sqrt(2.0)), (1.0 / sqrt(2.0), 0.5, 0.5), (1.0 / sqrt(2.0), 1.0 / sqrt(2.0), 0.0), ] for v1, v2 in zip(reflection_table["s1c"], expected_s1c): assert v1 == pytest.approx(v2) # now test redefined coordinates so that the lab x-axis is along the # z-axis in the crystal frame alignment_axis = (1.0, 0.0, 0.0) reflection_table["s1c"] = align_axis_along_z( alignment_axis, reflection_table["s1c"] ) reflection_table["s0c"] = align_axis_along_z( alignment_axis, reflection_table["s0c"] ) expected_s0c_realigned = [ (1.0, 0.0, 0.0), (1.0 / sqrt(2.0), -1.0 / sqrt(2.0), 0.0), (0.0, -1.0, 0.0), ] for v1, v2 in zip(reflection_table["s0c"], expected_s0c_realigned): assert v1 == pytest.approx(v2) expected_s1c_realigned = [ (-1.0 / sqrt(2.0), 0.0, 1.0 / sqrt(2.0)), (-0.5, 0.5, 1.0 / sqrt(2.0)), (0.0, 1.0 / sqrt(2.0), 1.0 / sqrt(2.0)), ] for v1, v2 in zip(reflection_table["s1c"], expected_s1c_realigned): assert v1 == pytest.approx(v2)
def test_create_sph_harm_table(test_reflection_table, test_experiment_singleaxisgonio): """Simple test for the spherical harmonic table, constructing the table step by step, and verifying the values of a few easy-to-calculate entries. This also acts as a test for the calc_theta_phi function as well.""" rt, exp = test_reflection_table, test_experiment_singleaxisgonio reflection_table = calc_crystal_frame_vectors(rt, exp) reflection_table["s0c"] = align_axis_along_z( (1.0, 0.0, 0.0), reflection_table["s0c"] ) reflection_table["s1c"] = align_axis_along_z( (1.0, 0.0, 0.0), reflection_table["s1c"] ) theta_phi = calc_theta_phi(reflection_table["s0c"]) # so s0c vectors realigned in xyz is # (1.0, 0.0, 0.0), # (1.0 / sqrt(2.0), -1.0 / sqrt(2.0), 0.0), # (0.0, -1.0, 0.0), # physics conventions, theta from 0 to pi, phi from 0 to 2pi expected = [ (pi / 2.0, 0.0), (pi / 2.0, 7.0 * pi / 4.0), (pi / 2.0, 3.0 * pi / 2.0), ] for v1, v2 in zip(theta_phi, expected): assert v1 == pytest.approx(v2) theta_phi_2 = calc_theta_phi(reflection_table["s1c"]) expected = [ (pi / 4.0, pi), (pi / 4.0, 3 * pi / 4.0), (pi / 4.0, 1.0 * pi / 2.0), ] for v1, v2 in zip(theta_phi_2, expected): assert v1 == pytest.approx(v2) sph_h_t = create_sph_harm_table(theta_phi, theta_phi_2, 2) Y10 = ((3.0 / (8.0 * pi)) ** 0.5) / 2.0 Y20 = -1.0 * ((5.0 / (256.0 * pi)) ** 0.5) assert sph_h_t[1, 0] == pytest.approx(Y10) assert sph_h_t[1, 1] == pytest.approx(Y10) assert sph_h_t[1, 2] == pytest.approx(Y10) assert sph_h_t[5, 0] == pytest.approx(Y20) assert sph_h_t[5, 1] == pytest.approx(Y20) assert sph_h_t[5, 2] == pytest.approx(Y20)
def create(cls, params, experiment, reflection_table, for_multi=False): """Perform reflection_table preprocessing and create a SingleScaler.""" cls.ensure_experiment_identifier(experiment, reflection_table) logger.info( "The scaling model type being applied is %s. \n", experiment.scaling_model.id_, ) try: reflection_table = cls.filter_bad_reflections( reflection_table, partiality_cutoff=params.cut_data.partiality_cutoff, min_isigi=params.cut_data.min_isigi, intensity_choice=params.reflection_selection.intensity_choice, ) except ValueError: raise BadDatasetForScalingException # combine partial measurements of same reflection, to handle those reflections # that were split by dials.integrate - changes size of reflection table. reflection_table = sum_partial_reflections(reflection_table) if "inverse_scale_factor" not in reflection_table: reflection_table["inverse_scale_factor"] = flex.double( reflection_table.size(), 1.0) elif (reflection_table["inverse_scale_factor"].count(0.0) == reflection_table.size()): reflection_table["inverse_scale_factor"] = flex.double( reflection_table.size(), 1.0) reflection_table = choose_initial_scaling_intensities( reflection_table, params.reflection_selection.intensity_choice) excluded_for_scaling = reflection_table.get_flags( reflection_table.flags.excluded_for_scaling) user_excluded = reflection_table.get_flags( reflection_table.flags.user_excluded_in_scaling) reasons = Reasons() reasons.add_reason("user excluded", user_excluded.count(True)) reasons.add_reason("excluded for scaling", excluded_for_scaling.count(True)) n_excluded = (excluded_for_scaling | user_excluded).count(True) if n_excluded == reflection_table.size(): logger.info( "All reflections were determined to be unsuitable for scaling." ) logger.info(reasons) raise BadDatasetForScalingException( """Unable to use this dataset for scaling""") else: logger.info( "Excluding %s/%s reflections\n%s", n_excluded, reflection_table.size(), reasons, ) if params.reflection_selection.method == "intensity_ranges": reflection_table = quasi_normalisation(reflection_table, experiment) if (params.reflection_selection.method in (None, Auto, "auto", "quasi_random")) or ( experiment.scaling_model.id_ == "physical" and "absorption" in experiment.scaling_model.components): if experiment.scan: reflection_table = calc_crystal_frame_vectors( reflection_table, experiment) alignment_axis = (1.0, 0.0, 0.0) reflection_table["s0c"] = align_axis_along_z( alignment_axis, reflection_table["s0c"]) reflection_table["s1c"] = align_axis_along_z( alignment_axis, reflection_table["s1c"]) try: scaler = SingleScaler(params, experiment, reflection_table, for_multi) except BadDatasetForScalingException as e: raise ValueError(e) else: return scaler
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)
def test_calc_crystal_frame_vectors_multi_axis_gonio(test_reflection_table): """Test the namesake function, to check that the vectors are correctly rotated into the crystal frame.""" experiments = test_experiments_multiaxisgonio() table = generate_reflection_table() # for the first scan, the rotation axis is the (1,0,0) direction, like the # single axis gonio test case above, so check that first. table = calc_crystal_frame_vectors(table, experiments[0]) # s0c and s1c are normalised. s0c points towards the source. # as the crystal rotates about the x axis, the s0 vector moves in the y-z plane towards -y expected_s0c = [ (0.0, 0.0, -1.0), (0.0, -1.0 / sqrt(2.0), -1.0 / sqrt(2.0)), (0.0, -1.0, 0.0), ] for v1, v2 in zip(table["s0c"], expected_s0c): assert v1 == pytest.approx(v2) # the s1c vector should have fixed x-component, rotating in the y-z plane towards +y expected_s1c = [ (1.0 / sqrt(2.0), 0.0, 1.0 / sqrt(2.0)), (1.0 / sqrt(2.0), 0.5, 0.5), (1.0 / sqrt(2.0), 1.0 / sqrt(2.0), 0.0), ] for v1, v2 in zip(table["s1c"], expected_s1c): assert v1 == pytest.approx(v2) # for second scan, the rotation axis is the (1,1,0) direction table = generate_reflection_table().select(flex.bool([True, False, True])) table = calc_crystal_frame_vectors(table, experiments[1]) # s0c and s1c are normalised. s0c points towards the source. # as the crystal rotates about the (1,1,0) axis, the s0 vector rotates towards (1, -sqrt2, -1) # the y-z plane towards -y expected_s0c = [ (0.0, 0.0, -1.0), (0.5, -1.0 / sqrt(2.0), -0.5), ] for v1, v2 in zip(table["s0c"], expected_s0c): assert v1 == pytest.approx(v2) # the s1c vector should rotate to +y expected_s1c = [ (1.0 / sqrt(2.0), 0.0, 1.0 / sqrt(2.0)), (0.0, 1.0, 0.0), ] for v1, v2 in zip(table["s1c"], expected_s1c): assert v1 == pytest.approx(v2) # now test redefined coordinates so that the lab x-axis is along the # z-axis in the crystal frame alignment_axis = (1.0, 0.0, 0.0) table["s1c"] = align_axis_along_z(alignment_axis, table["s1c"]) table["s0c"] = align_axis_along_z(alignment_axis, table["s0c"]) expected_s0c_realigned = [ (1.0, 0.0, 0.0), (0.5, -1.0 / sqrt(2.0), 0.5), ] for v1, v2 in zip(table["s0c"], expected_s0c_realigned): assert v1 == pytest.approx(v2) # the s1c vector should rotate to +y expected_s1c_realigned = [ (-1.0 / sqrt(2.0), 0.0, 1.0 / sqrt(2.0)), (0.0, 1.0, 0.0), ] for v1, v2 in zip(table["s1c"], expected_s1c_realigned): assert v1 == pytest.approx(v2)