def _start(self): import h5py self._h5_handle = h5py.File(self.get_image_file(), 'r') # names change as to what the sample positioner is called - originally was # 'pose' now is 'transformations' - FIXME I should be using the depends_on # attribute then I would not care what this is called... # compute coordinate frame transformation to imgCIF frame, just for kicks entry = self._h5_handle['entry'] sample = entry['sample'] if 'pose' in sample: self._pose_name = 'pose' elif 'transformations' in sample: self._pose_name = 'transformations' else: raise RuntimeError, 'cannot find pose or transformations' axis = tuple(sample[self._pose_name]['CBF_axis_omega'].attrs['vector']) # NeXus coordinate frame: Z is canonical from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame self._R = align_reference_frame(axis, (1, 0, 0), (0, 0, -1), (0, 0, 1)) return
def derive_reindex_matrix(self, handle): """Derive a reindexing matrix to go from the orientation matrix used for XDS integration to the one used for DIALS integration.""" from scitbx import matrix dA = matrix.sqr(self._experiment.crystal.get_A()) dbeam = matrix.col( self._experiment.beam.get_sample_to_source_direction()) daxis = matrix.col(self._experiment.goniometer.get_rotation_axis()) n = dbeam.cross(daxis) xbeam = matrix.col(handle.beam_vector).normalize() xaxis = matrix.col(handle.rotation_axis).normalize() # want to align XDS -s0 vector... from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(-xbeam, dbeam, xaxis, n.cross(dbeam)) xA = matrix.sqr(handle.unit_cell_a_axis + handle.unit_cell_b_axis + handle.unit_cell_c_axis).inverse() xA = R * xA # assert that this should just be a simple integer rotation matrix # i.e. reassignment of a, b, c so... return matrix.sqr([int(round(e)) for e in (dA.inverse() * xA).elems])
def _reorient_coordinate_frame(self): """Align a DIALS experiment and data in a reflection table to the PETS coordinate system. In that system, the s0 vector is aligned with -Z, while the rotation axis is in the X-Z plane, close to +X""" axis = matrix.col(self.experiment.goniometer.get_rotation_axis()) us0 = matrix.col(self.experiment.beam.get_unit_s0()) normal = us0.cross(-axis).normalize() orthogonalised_axis = us0.cross(normal).normalize() # Keep track of s1.us0 values to check reorientation s1_dot_us0 = self.reflections["s1"].dot(us0) R = align_reference_frame(us0, (0, 0, -1), orthogonalised_axis, (1, 0, 0)) axis_angle = r3_rotation_axis_and_angle_from_matrix(R) axis = axis_angle.axis angle = axis_angle.angle(deg=False) logger.info( "Rotating experiment about axis ({:.4f}, {:.4f}, {:.4f}) by {:.3f}°" .format(*axis, np.degrees(angle))) self.experiment.detector.rotate_around_origin(axis, angle, deg=False) self.experiment.crystal = rotate_crystal(self.experiment.crystal, R, axis, angle) # Following does not work (https://github.com/cctbx/dxtbx/issues/454) # self.experiment.beam.rotate_around_origin(axis, angle, deg=False) # Set unit s0 and polarization normal directly instead (preserving inconsistent # beam, if that's what we have). new_us0 = (R * matrix.col(self.experiment.beam.get_unit_s0())).normalize() new_p_norm = (R * matrix.col( self.experiment.beam.get_polarization_normal())).normalize() self.experiment.beam.set_unit_s0(new_us0) self.experiment.beam.set_polarization_normal(new_p_norm) # Rotating the goniometer is also complicated. See https://github.com/cctbx/dxtbx/pull/451 new_datum = (R * matrix.col( self.experiment.goniometer.get_rotation_axis_datum())).normalize() new_F = (R * matrix.sqr(self.experiment.goniometer.get_fixed_rotation()) * R.transpose()) new_S = ( R * matrix.sqr(self.experiment.goniometer.get_setting_rotation()) * R.transpose()) self.experiment.goniometer.set_rotation_axis_datum(new_datum) self.experiment.goniometer.set_setting_rotation(new_S) self.experiment.goniometer.set_fixed_rotation(new_F) # Re-calculate s1 vectors and reciprocal lattice points with new geometry el = ExperimentList() el.append(self.experiment) self.reflections.map_centroids_to_reciprocal_space(el, calculated=True) error = flex.abs(s1_dot_us0 - self.reflections["s1"].dot(new_us0)) if flex.max(error) > 1e-10: raise RuntimeError("Failed to rotate experiment correctly")
def __init__(self, sweep): self._sweep = sweep # detector dimensions in pixels assert(len(self.get_detector()) == 1) self.detector_size = map(int, self.get_detector()[0].get_image_size()) self.fast, self.slow = self.detector_size R = align_reference_frame( self.get_detector()[0].get_fast_axis(), (1,0,0), self.get_detector()[0].get_slow_axis(), (0,1,0)) self.imagecif_to_xds_transformation_matrix = R self.detector_x_axis = ( R * matrix.col(self.get_detector()[0].get_fast_axis())).elems self.detector_y_axis = ( R * matrix.col(self.get_detector()[0].get_slow_axis())).elems F = R * matrix.col(self.get_detector()[0].get_fast_axis()) S = R * matrix.col(self.get_detector()[0].get_slow_axis()) N = F.cross(S) self.detector_normal = N.elems origin = R * matrix.col(self.get_detector()[0].get_origin()) centre = -(origin - origin.dot(N) * N) x = centre.dot(F) y = centre.dot(S) self.pixel_size = self.get_detector()[0].get_pixel_size() f, s = self.pixel_size self.detector_distance = origin.dot(N) # Need to add 0.5 because XDS seems to do centroids in fortran coords self.detector_origin = x/f + 0.5, y/f + 0.5 # Beam stuff self.wavelength = self.get_beam().get_wavelength() self.beam_vector = R * matrix.col(self.get_beam().get_direction()) # just to make sure it is the correct length self.beam_vector = self.beam_vector.normalize() / self.wavelength self.beam_vector = (- self.beam_vector).elems # Scan and goniometer stuff self.starting_frame = self.get_scan().get_image_range()[0] self.starting_angle = self.get_scan().get_oscillation()[0] self.oscillation_range = self.get_scan().get_oscillation()[1] self.rotation_axis = ( R * matrix.col(self.get_goniometer().get_rotation_axis())).elems return
def derive_reindex_matrix(integrate_hkl, integrate_mtz): '''Derive a reindexing matrix to go from the orientation matrix used for MOSFLM integration to the one used for XDS integration.''' dA = integrate_hkl_to_A_matrix(integrate_hkl) dbeam, daxis = get_xds_coordinate_frame(integrate_hkl) mbeam, maxis = get_mosflm_coordinate_frame(integrate_mtz) from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(mbeam, -dbeam, maxis, daxis) mA = R * integrate_mtz_to_A_matrix(integrate_mtz) # assert that this should just be a simple integer rotation matrix # i.e. reassignment of a, b, c so... return matrix.sqr(map(int, map(round, (dA.inverse() * mA).elems)))
def derive_reindex_matrix(integrate_hkl, integrate_mtz): '''Derive a reindexing matrix to go from the orientation matrix used for MOSFLM integration to the one used for XDS integration.''' dA = integrate_hkl_to_A_matrix(integrate_hkl) dbeam, daxis = get_xds_coordinate_frame(integrate_hkl) mbeam, maxis = get_mosflm_coordinate_frame(integrate_mtz) from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(mbeam, - dbeam, maxis, daxis) mA = R * integrate_mtz_to_A_matrix(integrate_mtz) # assert that this should just be a simple integer rotation matrix # i.e. reassignment of a, b, c so... from scitbx import matrix return matrix.sqr(map(int, map(round, (dA.inverse() * mA).elems)))
def derive_reindex_matrix(experiments_json, integrate_hkl): '''Derive a reindexing matrix to go from the orientation matrix used for XDS integration to the one used for DIALS integration.''' dA = get_dials_matrix(experiments_json) dbeam, daxis = get_dials_coordinate_frame(experiments_json) xbeam, xaxis = get_xds_coordinate_frame(integrate_hkl) # want to align XDS -s0 vector... from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(- xbeam, dbeam, xaxis, daxis) xA = R * integrate_hkl_to_A_matrix(integrate_hkl) # assert that this should just be a simple integer rotation matrix # i.e. reassignment of a, b, c so... from scitbx import matrix return matrix.sqr(map(int, map(round, (dA.inverse() * xA).elems)))
def derive_reindex_matrix(crystal_json, sweep_json, integrate_mtz): """Derive a reindexing matrix to go from the orientation matrix used for MOSFLM integration to the one used for DIALS integration.""" dA = get_dials_matrix(crystal_json) dbeam, daxis = get_dials_coordinate_frame(sweep_json) mbeam, maxis = get_mosflm_coordinate_frame(integrate_mtz) from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(mbeam, dbeam, maxis, daxis) mA = R * integrate_mtz_to_A_matrix(integrate_mtz) # assert that this should just be a simple integer rotation matrix # i.e. reassignment of a, b, c so... from scitbx import matrix return matrix.sqr(map(int, map(round, (dA.inverse() * mA).elems)))
def derive_reindex_matrix(crystal_json, sweep_json, integrate_hkl): """Derive a reindexing matrix to go from the orientation matrix used for XDS integration to the one used for DIALS integration.""" dA = get_dials_matrix(crystal_json) dbeam, daxis = get_dials_coordinate_frame(sweep_json) xbeam, xaxis = get_xds_coordinate_frame(integrate_hkl) # want to align XDS -s0 vector... from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(-xbeam, dbeam, xaxis, daxis) xA = R * integrate_hkl_to_A_matrix(integrate_hkl) # assert that this should just be a simple integer rotation matrix # i.e. reassignment of a, b, c so... from scitbx import matrix return matrix.sqr(map(int, map(round, (dA.inverse() * xA).elems)))
def align_experiments( experiments: ExperimentList, params: libtbx.phil.scope_extract, ) -> ExperimentList: if len(experiments) > 1: logger.info( "Only the first experiment will be used to determine the detector axes" ) expt = experiments[0] detector = expt.detector if len(detector) > 1: logger.info( "Only the first panel will be used to determine the detector axes") panel = detector[0] xds_x, xds_y = read_xds_inp(params.input.xds_inp) R = align_reference_frame(panel.get_fast_axis(), xds_x, panel.get_slow_axis(), xds_y) axis_angle = r3_rotation_axis_and_angle_from_matrix(R) axis = axis_angle.axis angle = axis_angle.angle() logger.info( f"Rotating experiment{'s' if len(experiments) else ''} about axis {axis} by {np.degrees(angle)}°" ) for expt in experiments: expt.detector.rotate_around_origin(axis, angle, deg=False) expt.beam.rotate_around_origin(axis, angle, deg=False) # https://github.com/cctbx/dxtbx/issues/447 # expt.goniometer.rotate_around_origin(axis, angle, deg=False) rotation_axis = matrix.col(expt.goniometer.get_rotation_axis()) expt.goniometer.set_rotation_axis(R * rotation_axis) if expt.crystal is not None: expt.crystal = rotate_crystal(expt.crystal, R, axis, angle) return experiments
def derive_reindex_matrix(self, handle): '''Derive a reindexing matrix to go from the orientation matrix used for XDS integration to the one used for DIALS integration.''' from scitbx import matrix dA = self._experiment.crystal.get_A() dbeam = matrix.col(self._experiment.beam.get_direction()) daxis = matrix.col(self._experiment.goniometer.get_rotation_axis()) xbeam = matrix.col(handle.beam_vector).normalize() xaxis = matrix.col(handle.rotation_axis).normalize() # want to align XDS -s0 vector... from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(- xbeam, dbeam, xaxis, daxis) xA = matrix.sqr( handle.unit_cell_a_axis + handle.unit_cell_b_axis + handle.unit_cell_c_axis).inverse() xA = R * xA # assert that this should just be a simple integer rotation matrix # i.e. reassignment of a, b, c so... return matrix.sqr(map(int, map(round, (dA.inverse() * xA).elems)))
def export_xds_ascii(integrated_data, experiment_list, params, var_model=(1, 0)): """Export data from integrated_data corresponding to experiment_list to an XDS_ASCII.HKL formatted text file.""" from dials.array_family import flex # for the moment assume (and assert) that we will convert data from exactly # one lattice... assert len(experiment_list) == 1 # select reflections that are assigned to an experiment (i.e. non-negative id) integrated_data = integrated_data.select(integrated_data["id"] >= 0) assert max(integrated_data["id"]) == 0 # export for xds_ascii should only be for non-scaled reflections assert any([ i in integrated_data for i in ["intensity.sum.value", "intensity.prf.value"] ]) integrated_data = filter_reflection_table( integrated_data, intensity_choice=params.intensity, partiality_threshold=params.mtz.partiality_threshold, combine_partials=params.mtz.combine_partials, min_isigi=params.mtz.min_isigi, filter_ice_rings=params.mtz.filter_ice_rings, d_min=params.mtz.d_min, ) # calculate the scl = lp/dqe correction for outputting but don't apply it as # it has already been applied in filter_reflection_table integrated_data, scl = FilteringReductionMethods.calculate_lp_qe_correction_and_filter( integrated_data) experiment = experiment_list[0] # sort data before output nref = len(integrated_data["miller_index"]) indices = flex.size_t_range(nref) import copy unique = copy.deepcopy(integrated_data["miller_index"]) from cctbx.miller import map_to_asu map_to_asu(experiment.crystal.get_space_group().type(), False, unique) perm = sorted(indices, key=lambda k: unique[k]) integrated_data = integrated_data.select(flex.size_t(perm)) from scitbx import matrix from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame assert not experiment.goniometer is None unit_cell = experiment.crystal.get_unit_cell() from scitbx.array_family import flex assert not experiment.scan is None image_range = experiment.scan.get_image_range() phi_start, phi_range = experiment.scan.get_image_oscillation( image_range[0]) # gather the required information for the reflection file nref = len(integrated_data["miller_index"]) zdet = flex.double(integrated_data["xyzcal.px"].parts()[2]) miller_index = integrated_data["miller_index"] # profile correlation if "profile.correlation" in integrated_data: prof_corr = 100.0 * integrated_data["profile.correlation"] else: prof_corr = flex.double(nref, 100.0) # partiality if "partiality" in integrated_data: partiality = 100 * integrated_data["partiality"] else: prof_corr = flex.double(nref, 100.0) if "intensity.sum.value" in integrated_data: I = integrated_data["intensity.sum.value"] V = integrated_data["intensity.sum.variance"] assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) else: I = integrated_data["intensity.prf.value"] V = integrated_data["intensity.prf.variance"] assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) fout = open(params.xds_ascii.hklout, "w") # first write the header - in the "standard" coordinate frame... panel = experiment.detector[0] fast = panel.get_fast_axis() slow = panel.get_slow_axis() Rd = align_reference_frame(fast, (1, 0, 0), slow, (0, 1, 0)) print("Coordinate change:") print("%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n" % Rd.elems) fast = Rd * fast slow = Rd * slow qx, qy = panel.get_pixel_size() nx, ny = panel.get_image_size() distance = matrix.col(Rd * panel.get_origin()).dot( matrix.col(Rd * panel.get_normal())) org = Rd * (matrix.col(panel.get_origin()) - distance * matrix.col(panel.get_normal())) orgx = -org.dot(fast) / qx orgy = -org.dot(slow) / qy UB = Rd * matrix.sqr(experiment.crystal.get_A()) real_space_ABC = UB.inverse().elems axis = Rd * experiment.goniometer.get_rotation_axis() beam = Rd * experiment.beam.get_s0() cell_fmt = "%9.3f %9.3f %9.3f %7.3f %7.3f %7.3f" axis_fmt = "%9.3f %9.3f %9.3f" fout.write("\n".join([ "!FORMAT=XDS_ASCII MERGE=FALSE FRIEDEL'S_LAW=TRUE", "!Generated by dials.export", "!DATA_RANGE= %d %d" % image_range, "!ROTATION_AXIS= %9.6f %9.6f %9.6f" % axis.elems, "!OSCILLATION_RANGE= %f" % phi_range, "!STARTING_ANGLE= %f" % phi_start, "!STARTING_FRAME= %d" % image_range[0], "!SPACE_GROUP_NUMBER= %d" % experiment.crystal.get_space_group().type().number(), "!UNIT_CELL_CONSTANTS= %s" % (cell_fmt % unit_cell.parameters()), "!UNIT_CELL_A-AXIS= %s" % (axis_fmt % real_space_ABC[0:3]), "!UNIT_CELL_B-AXIS= %s" % (axis_fmt % real_space_ABC[3:6]), "!UNIT_CELL_C-AXIS= %s" % (axis_fmt % real_space_ABC[6:9]), "!X-RAY_WAVELENGTH= %f" % experiment.beam.get_wavelength(), "!INCIDENT_BEAM_DIRECTION= %f %f %f" % beam.elems, "!NX= %d NY= %d QX= %f QY= %f" % (nx, ny, qx, qy), "!ORGX= %9.2f ORGY= %9.2f" % (orgx, orgy), "!DETECTOR_DISTANCE= %8.3f" % distance, "!DIRECTION_OF_DETECTOR_X-AXIS= %9.5f %9.5f %9.5f" % fast.elems, "!DIRECTION_OF_DETECTOR_Y-AXIS= %9.5f %9.5f %9.5f" % slow.elems, "!VARIANCE_MODEL= %7.3e %7.3e" % var_model, "!NUMBER_OF_ITEMS_IN_EACH_DATA_RECORD=12", "!ITEM_H=1", "!ITEM_K=2", "!ITEM_L=3", "!ITEM_IOBS=4", "!ITEM_SIGMA(IOBS)=5", "!ITEM_XD=6", "!ITEM_YD=7", "!ITEM_ZD=8", "!ITEM_RLP=9", "!ITEM_PEAK=10", "!ITEM_CORR=11", "!ITEM_PSI=12", "!END_OF_HEADER", "", ])) # then write the data records s0 = Rd * matrix.col(experiment.beam.get_s0()) for j in range(nref): x, y, z = integrated_data["xyzcal.px"][j] phi = phi_start + z * phi_range h, k, l = miller_index[j] X = (UB * (h, k, l)).rotate(axis, phi, deg=True) s = s0 + X g = s.cross(s0).normalize() f = (s - s0).normalize() # find component of beam perpendicular to f, e e = -(s + s0).normalize() if h == k and k == l: u = (h, -h, 0) else: u = (k - l, l - h, h - k) q = ((matrix.col(u).transpose() * UB.inverse()).normalize().transpose().rotate(axis, phi, deg=True)) psi = q.angle(g, deg=True) if q.dot(e) < 0: psi *= -1 fout.write("%d %d %d %f %f %f %f %f %f %.1f %.1f %f\n" % ( h, k, l, I[j], sigI[j], x, y, z, scl[j], partiality[j], prof_corr[j], psi, )) fout.write("!END_OF_DATA\n") fout.close() logger.info("Output %d reflections to %s" % (nref, params.xds_ascii.hklout))
def get_goniometer_shadow_masker(self, goniometer=None): from dials.util.masking import GoniometerShadowMaskGenerator from scitbx.array_family import flex import math coords = flex.vec3_double(((0, 0, 0), )) alpha = flex.double_range(0, 190, step=10) * math.pi / 180 r = flex.double(alpha.size(), 40) x = flex.double(r.size(), 107.61) y = -r * flex.sin(alpha) z = -r * flex.cos(alpha) coords.extend(flex.vec3_double(x, y, z)) coords.extend( flex.vec3_double(( # fixed (107.49, 7.84, 39.49), (107.39, 15.69, 38.97), (107.27, 23.53, 38.46), (107.16, 31.37, 37.94), (101.76, 33.99, 36.25), (96.37, 36.63, 34.56), (90.98, 39.25, 33.00), (85.58, 41.88, 31.18), (80.89, 47.06, 31.00), (76.55, 51.51, 31.03), (72.90, 55.04, 31.18), (66.86, 60.46, 31.67), (62.10, 64.41, 32.25), ))) alpha = flex.double_range(180, 370, step=10) * math.pi / 180 r = flex.double(alpha.size(), 33) x = (flex.sqrt(flex.pow2(r * flex.sin(alpha)) + 89.02**2) * flex.cos((50 * math.pi / 180) - flex.atan(r / 89.02 * flex.sin(alpha)))) y = (flex.sqrt(flex.pow2(r * flex.sin(alpha)) + 89.02**2) * flex.sin((50 * math.pi / 180) - flex.atan(r / 89.02 * flex.sin(alpha)))) z = -r * flex.cos(alpha) coords.extend(flex.vec3_double(x, y, z)) coords.extend( flex.vec3_double(( # fixed (62.10, 64.41, -32.25), (66.86, 60.46, -31.67), (72.90, 55.04, -31.18), (76.55, 51.51, -31.03), (80.89, 47.06, -31.00), (85.58, 41.88, -31.18), (90.98, 39.25, -33.00), (96.37, 36.63, -34.56), (101.76, 33.99, -36.25), (107.16, 31.37, -37.94), (107.27, 23.53, -38.46), (107.39, 15.69, -38.97), (107.49, 7.84, -39.49), (107.61, 0.00, -40.00)))) # I23 end station coordinate system: # X-axis: positive direction is facing away from the storage ring (from # sample towards goniometer) # Y-axis: positive direction is vertically up # Z-axis: positive direction is in the direction of the beam (from # sample towards detector) # K-axis (kappa): at an angle of +50 degrees from the X-axis # K & phi rotation axes: clockwise rotation is positive (right hand # thumb rule) # Omega-axis: along the X-axis; clockwise rotation is positive # End station x-axis is parallel to ImgCIF x-axis # End station z-axis points in opposite direction to ImgCIF definition # (ImgCIF: The Z-axis is derived from the source axis which goes from # the sample to the source) # Consequently end station y-axis (to complete set following right hand # rule) points in opposite direction to ImgCIF y-axis. # Kappa arm aligned with -y in ImgCIF convention from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix R = align_reference_frame(matrix.col((1, 0, 0)), matrix.col((1, 0, 0)), matrix.col((0, 1, 0)), matrix.col( (0, -1, 0))) coords = R.elems * coords if goniometer is None: goniometer = self.get_goniometer() return GoniometerShadowMaskGenerator(goniometer, coords, flex.size_t(len(coords), 1))
def extrema_at_scan_angle(self, scan_angle): from scitbx.array_family import flex # Align end station coordinate system with ImgCIF coordinate system from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix R = align_reference_frame( matrix.col((-1, 0, 0)), matrix.col((1, 0, 0)), matrix.col((0, -1, 0)), matrix.col((0, 1, 0)), ) faceA = R.elems * self.faceA faceE = R.elems * self.faceE axes = self.goniometer.get_axes() angles = self.goniometer.get_angles() scan_axis = self.goniometer.get_scan_axis() angles[scan_axis] = scan_angle extrema = flex.vec3_double() for coords in (faceA, faceE): coords = coords.deep_copy() for i, axis in enumerate(axes): if i == 0: continue # shadow doesn't change with phi setting sel = flex.bool(len(coords), True) rotation = matrix.col( axis).axis_and_angle_as_r3_rotation_matrix(angles[i], deg=True) coords.set_selected(sel, rotation.elems * coords.select(sel)) extrema.extend(coords) s = matrix.col(self.faceB[0]) mx, my, _ = self.faceB[1] nx, ny, _ = self.faceB[2] px, py, _ = self.faceB[3] Rchi = (R.inverse() * matrix.col(axes[1])).axis_and_angle_as_r3_rotation_matrix( angles[1], deg=True) sk = Rchi * s sxk, syk, szk = sk.elems coords = flex.vec3_double(( (sxk, syk, 0), (sxk, syk, szk), (sxk + mx / 2, syk + my / 2, szk), (sxk + mx, syk + my, szk), (sxk + (mx + nx) / 2, syk + (my + ny) / 2, szk), (sxk + nx, syk + ny, szk), (sxk + (nx + px) / 2, syk + (ny + py) / 2, szk), (sxk + px, syk + py, szk), (sxk + px, syk + py, 0), (sxk + px, syk + py, -szk), (sxk + (nx + px) / 2, syk + (ny + py) / 2, -szk), (sxk + nx, syk + ny, -szk), (sxk + (mx + nx) / 2, syk + (my + ny) / 2, -szk), (sxk + mx, syk + my, -szk), (sxk + mx / 2, syk + my / 2, -szk), (sxk, syk, -szk), )) coords = R.elems * coords Romega = matrix.col(axes[2]).axis_and_angle_as_r3_rotation_matrix( angles[2], deg=True) coords = Romega.elems * coords extrema.extend(coords) return extrema
def extrema_at_scan_angle(self, scan_angle): from scitbx.array_family import flex # Align end station coordinate system with ImgCIF coordinate system from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix R = align_reference_frame(matrix.col((1,0,0)), matrix.col((-1,0,0)), matrix.col((0,1,0)), matrix.col((0,1,0))) faceA = R.elems * self.faceA faceE = R.elems * self.faceE axes = self.goniometer.get_axes() angles = self.goniometer.get_angles() scan_axis = self.goniometer.get_scan_axis() angles[scan_axis] = scan_angle extrema = flex.vec3_double() for coords in (faceA, faceE): coords = coords.deep_copy() for i, axis in enumerate(axes): sel = flex.bool(len(coords), True) rotation = matrix.col( axis).axis_and_angle_as_r3_rotation_matrix(angles[i], deg=True) coords.set_selected(sel, rotation.elems * coords.select(sel)) extrema.extend(coords) s = matrix.col(self.faceB[0]) mx, my, _ = self.faceB[1] nx, ny, _ = self.faceB[2] px, py, _ = self.faceB[3] Rchi = (R.inverse() * matrix.col(axes[1])).axis_and_angle_as_r3_rotation_matrix(angles[1], deg=True) sk = Rchi * s sxk, syk, szk = sk.elems coords = flex.vec3_double(( (sxk, syk, 0), (sxk, syk, szk), (sxk+mx/2, syk+my/2, szk), (sxk+mx, syk+my, szk), (sxk+(mx+nx)/2, syk+(my+ny)/2, szk), (sxk+nx, syk+ny, szk), (sxk+(nx+px)/2, syk+(ny+py)/2, szk), (sxk+px, syk+py, szk), (sxk+px, syk+py, 0), (sxk+px, syk+py, -szk), (sxk+(nx+px)/2, syk+(ny+py)/2, -szk), (sxk+nx, syk+ny, -szk), (sxk+(mx+nx)/2, syk+(my+ny)/2, -szk), (sxk+mx, syk+my, -szk), (sxk+mx/2, syk+my/2, -szk), (sxk, syk, -szk), )) coords = R.elems * coords Romega = matrix.col(axes[2]).axis_and_angle_as_r3_rotation_matrix(angles[2], deg=True) coords = Romega.elems * coords extrema.extend(coords) return extrema
def __init__(self, experiment, vectors, frame='reciprocal', mode='main'): from libtbx.utils import Sorry self.experiment = experiment self.vectors = vectors self.frame = frame self.mode = mode gonio = experiment.goniometer scan = experiment.scan self.s0 = matrix.col(self.experiment.beam.get_s0()) self.rotation_axis = matrix.col(gonio.get_rotation_axis()) from dxtbx.model import MultiAxisGoniometer if not isinstance(gonio, MultiAxisGoniometer): raise Sorry('Only MultiAxisGoniometer models supported') axes = gonio.get_axes() if len(axes) != 3: raise Sorry('Only 3-axis goniometers supported') e1, e2, e3 = (matrix.col(e) for e in axes) fixed_rotation = matrix.sqr(gonio.get_fixed_rotation()) setting_rotation = matrix.sqr(gonio.get_setting_rotation()) rotation_axis = matrix.col(gonio.get_rotation_axis_datum()) rotation_matrix = rotation_axis.axis_and_angle_as_r3_rotation_matrix( experiment.scan.get_oscillation()[0], deg=True) from dials.algorithms.refinement import rotation_decomposition results = {} for (v1_, v2_) in self.vectors: results[(v1_, v2_)] = {} crystal = copy.deepcopy(self.experiment.crystal) for smx in list(crystal.get_space_group().smx())[:]: cb_op = sgtbx.change_of_basis_op(smx) crystal = crystal.change_basis(cb_op) # Goniometer datum setting [D] at which the orientation was determined D = (setting_rotation * rotation_matrix * fixed_rotation).inverse() # The setting matrix [U] will vary with the datum setting according to # [U] = [D] [U0] U = crystal.get_U() # XXX In DIALS recorded U is equivalent to U0 - D is applied to U inside # prediction U0 = U B = crystal.get_B() if self.frame == 'direct': B = B.inverse().transpose() v1_0 = U0 * B * v1_ v2_0 = U0 * B * v2_ #c (b) The laboratory frame vectors l1 & l2 are normally specified with the #c MODE command: MODE MAIN (the default) sets l1 (along which v1 will be #c placed) along the principle goniostat axis e1 (Omega), and l2 along #c the beam s0. This allows rotation for instance around a principle axis. #c The other mode is MODE CUSP, which puts l1 (v1) perpendicular to the #c beam (s0) and the e1 (Omega) axis, and l2 (v2) in the plane containing #c l1 & e1 (ie l1 = e1 x s0, l2 = e1). if self.mode == 'cusp': l1 = self.rotation_axis.cross(s0) l2 = self.rotation_axis else: l1 = self.rotation_axis.normalize() l3 = l1.cross(self.s0).normalize() l2 = l1.cross(l3) from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(v1_0, l1, v2_0, l2) solutions = rotation_decomposition.solve_r3_rotation_for_angles_given_axes( R, e1, e2, e3, return_both_solutions=True, deg=True) if solutions is None: continue results[(v1_, v2_)][smx] = solutions self.all_solutions = results self.unique_solutions = {} for (v1, v2), result in results.iteritems(): for solutions in result.itervalues(): for solution in solutions: k = tuple(round(a, 2) for a in solution[1:]) self.unique_solutions.setdefault(k, set()) self.unique_solutions[k].add((v1, v2))
def extract_varying_crystal(integrate_lp, experiments): """Extract a varying crystal model from an INTEGRATE.LP file (static in blocks with step changes) and write it to the provided experiments """ if len(experiments) > 1: print( "Can only read a varying crystal model for a single " + "experiment. Skipping." ) return experiment = experiments[0] # read required records from the file. Relies on them being in the # right order as we read through once xds_axis = None xds_beam = None blocks, a_axis, b_axis, c_axis = [], [], [], [] with open(integrate_lp) as f: for record in f: if record.lstrip().startswith("ROTATION_AXIS="): xds_axis = record.split("ROTATION_AXIS=")[1].split() break for record in f: if record.lstrip().startswith("INCIDENT_BEAM_DIRECTION="): xds_beam = record.split("INCIDENT_BEAM_DIRECTION=")[1].split() break for record in f: if record.lstrip().startswith("PROCESSING OF IMAGES"): blocks.append(record.split("PROCESSING OF IMAGES")[1]) continue if record.lstrip().startswith("COORDINATES OF UNIT CELL A-AXIS"): a_axis.append(record.split("COORDINATES OF UNIT CELL A-AXIS")[1]) continue if record.lstrip().startswith("COORDINATES OF UNIT CELL B-AXIS"): b_axis.append(record.split("COORDINATES OF UNIT CELL B-AXIS")[1]) continue if record.lstrip().startswith("COORDINATES OF UNIT CELL C-AXIS"): c_axis.append(record.split("COORDINATES OF UNIT CELL C-AXIS")[1]) continue # sanity checks msg = "INTEGRATE.LP is not in the expected format" nblocks = len(blocks) try: assert len(a_axis) == len(b_axis) == len(c_axis) == nblocks assert (xds_axis, xds_beam).count(None) == 0 except AssertionError: print(msg) return # conversions to numeric try: blocks = [[int(b) for b in block.split("...")] for block in blocks] a_axis = [[float(a) for a in axis.split()] for axis in a_axis] b_axis = [[float(b) for b in axis.split()] for axis in b_axis] c_axis = [[float(c) for c in axis.split()] for axis in c_axis] xds_beam = [float(e) for e in xds_beam] xds_axis = [float(e) for e in xds_axis] except ValueError: print(msg) return # coordinate frame conversions dbeam = matrix.col(experiment.beam.get_sample_to_source_direction()) daxis = matrix.col(experiment.goniometer.get_rotation_axis()) xbeam = matrix.col(xds_beam).normalize() xaxis = matrix.col(xds_axis).normalize() # want to align XDS -s0 vector... R = align_reference_frame(-xbeam, dbeam, xaxis, daxis) # Make a static crystal for each block crystals = [] sg = experiment.crystal.get_space_group() for a, b, c in zip(a_axis, b_axis, c_axis): a = R * matrix.col(a) b = R * matrix.col(b) c = R * matrix.col(c) crystals.append(Crystal(a, b, c, space_group=sg)) # construct a list of scan points A_list = [] for block, crystal in zip(blocks, crystals): A = crystal.get_A() for im in range(block[0], block[1] + 1): A_list.append(A) # Need a final scan point at the end of the final image A_list.append(A) # set the scan-varying crystal experiment.crystal.set_A_at_scan_points(A_list)
def write_par_file(file_name, experiment): from dxtbx.model import Crystal from iotbx.mtz.extract_from_symmetry_lib import ccp4_symbol from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix imageset = experiment.imageset detector = imageset.get_detector() goniometer = imageset.get_goniometer() beam = imageset.get_beam() scan = imageset.get_scan() R_to_mosflm = align_reference_frame(beam.get_s0(), (1.0, 0.0, 0.0), goniometer.get_rotation_axis(), (0.0, 0.0, 1.0)) cryst = experiment.crystal cryst = cryst.change_basis(cryst.get_space_group().info(). change_of_basis_op_to_reference_setting()) A = matrix.sqr(cryst.get_A()) A_inv = A.inverse() real_space_a = R_to_mosflm * A_inv.elems[:3] real_space_b = R_to_mosflm * A_inv.elems[3:6] real_space_c = R_to_mosflm * A_inv.elems[6:9] cryst_mosflm = Crystal(real_space_a, real_space_b, real_space_c, space_group=cryst.get_space_group()) U_mosflm = matrix.sqr(cryst_mosflm.get_U()) B_mosflm = matrix.sqr(cryst_mosflm.get_B()) UB_mosflm = U_mosflm * B_mosflm uc_params = cryst_mosflm.get_unit_cell().parameters() assert U_mosflm.is_r3_rotation_matrix(), U_mosflm beam_centre = tuple(reversed(detector[0].get_beam_centre(beam.get_s0()))) distance = detector[0].get_directed_distance() polarization = R_to_mosflm * matrix.col(beam.get_polarization_normal()) rotation = matrix.col(goniometer.get_rotation_axis()) if rotation.angle(matrix.col( detector[0].get_fast_axis())) < rotation.angle( matrix.col(detector[0].get_slow_axis())): direction = "FAST" else: direction = "SLOW" rotation = R_to_mosflm * rotation # Calculate average spot diameter for SEPARATION parameter # http://xds.mpimf-heidelberg.mpg.de/html_doc/xds_parameters.html # BEAM_DIVERGENCE= # This value is approximately arctan(spot diameter/DETECTOR_DISTANCE) profile = experiment.profile spot_diameter = math.tan(profile.delta_b() * math.pi / 180) * distance spot_diameter_px = spot_diameter * detector[0].get_pixel_size()[0] # determine parameters for RASTER keyword # http://www.mrc-lmb.cam.ac.uk/harry/cgi-bin/keyword2.cgi?RASTER # NXS, NYS (odd integers) define the overall dimensions of the rectangular array of pixels for each spot # NXS and NYS are set to twice the spot size plus 5 pixels nxs = 2 * int(math.ceil(spot_diameter_px)) + 5 nys = nxs # NRX, NRY are the number of columns or rows of points in the background rim # NRX and NRY are set to half the spot size plus 2 pixels nrx = int(math.ceil(0.5 * spot_diameter_px)) + 2 nry = nrx # NC the corner background cut-off which corresponds to a half-square of side NC points # NC is set to the mean of the spot size in X and Y plus 4 nc = int(math.ceil(spot_diameter_px)) + 4 def space_group_symbol(space_group): symbol = ccp4_symbol(space_group.info(), lib_name="syminfo.lib", require_at_least_one_lib=False) if symbol != "P 1": symbol = symbol.replace(" 1", "") symbol = symbol.replace(" ", "") return symbol logger.info("Saving BEST parameter file to %s", file_name) with open(file_name, "w") as f: print("# parameter file for BEST", file=f) print("TITLE From DIALS", file=f) print("DETECTOR PILA", file=f) print("SITE Not set", file=f) print( "DIAMETER %6.2f" % (max(detector[0].get_image_size()) * detector[0].get_pixel_size()[0]), file=f, ) print(f"PIXEL {round(detector[0].get_pixel_size()[0], 10)}", file=f) print("ROTAXIS %4.2f %4.2f %4.2f" % rotation.elems, direction, file=f) print("POLAXIS %4.2f %4.2f %4.2f" % polarization.elems, file=f) print("GAIN 1.00", file=f) # correct for Pilatus images # http://strucbio.biologie.uni-konstanz.de/xdswiki/index.php/FAQ#You_said_that_the_XDS_deals_with_high_mosaicity._How_high_mosaicity_is_still_manageable.3F # http://journals.iucr.org/d/issues/2012/01/00/wd5161/index.html # Transform from XDS definition of sigma_m to FWHM (MOSFLM mosaicity definition) print(f"CMOSAIC {experiment.profile.sigma_m() * 2.355:.2f}", file=f) print(f"PHISTART {scan.get_oscillation_range()[0]:.2f}", file=f) print(f"PHIWIDTH {scan.get_oscillation()[1]:.2f}", file=f) print(f"DISTANCE {distance:7.2f}", file=f) print(f"WAVELENGTH {beam.get_wavelength():.5f}", file=f) print(f"POLARISATION {beam.get_polarization_fraction():7.5f}", file=f) print(f"SYMMETRY {space_group_symbol(cryst.get_space_group())}", file=f) print("UB %9.6f %9.6f %9.6f" % UB_mosflm[:3], file=f) print(" %9.6f %9.6f %9.6f" % UB_mosflm[3:6], file=f) print(" %9.6f %9.6f %9.6f" % UB_mosflm[6:], file=f) print("CELL %8.2f %8.2f %8.2f %6.2f %6.2f %6.2f" % uc_params, file=f) print("RASTER %i %i %i %i %i" % (nxs, nys, nc, nrx, nry), file=f) print(f"SEPARATION {spot_diameter:.3f} {spot_diameter:.3f}", file=f) print("BEAM %8.3f %8.3f" % beam_centre, file=f) print("# end of parameter file for BEST", file=f)
def write_par_file(file_name, experiment): from scitbx import matrix from dxtbx.model.crystal import crystal_model from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from dials.command_line.refine_bravais_settings import short_space_group_name imageset = experiment.imageset detector = imageset.get_detector() goniometer = imageset.get_goniometer() beam = imageset.get_beam() scan = imageset.get_scan() R_to_mosflm = align_reference_frame(beam.get_s0(), (1.0, 0.0, 0.0), goniometer.get_rotation_axis(), (0.0, 0.0, 1.0)) cryst = experiment.crystal cryst = cryst.change_basis( cryst.get_space_group().info()\ .change_of_basis_op_to_reference_setting()) A = cryst.get_A() A_inv = A.inverse() real_space_a = R_to_mosflm * A_inv.elems[:3] real_space_b = R_to_mosflm * A_inv.elems[3:6] real_space_c = R_to_mosflm * A_inv.elems[6:9] cryst_mosflm = crystal_model(real_space_a, real_space_b, real_space_c, space_group=cryst.get_space_group(), mosaicity=cryst.get_mosaicity()) A_mosflm = cryst_mosflm.get_A() U_mosflm = cryst_mosflm.get_U() B_mosflm = cryst_mosflm.get_B() UB_mosflm = U_mosflm * B_mosflm uc_params = cryst_mosflm.get_unit_cell().parameters() assert U_mosflm.is_r3_rotation_matrix(), U_mosflm symmetry = cryst_mosflm.get_space_group().type().number() beam_centre = tuple(reversed(detector[0].get_beam_centre(beam.get_s0()))) distance = detector[0].get_directed_distance() polarization = R_to_mosflm * matrix.col(beam.get_polarization_normal()) rotation = matrix.col(goniometer.get_rotation_axis()) if (rotation.angle(matrix.col(detector[0].get_fast_axis())) < rotation.angle(matrix.col(detector[0].get_slow_axis()))): direction = 'FAST' else: direction = 'SLOW' rotation = R_to_mosflm * rotation with open(file_name, 'wb') as f: # print >> f, '# parameter file for BEST' print >> f, 'TITLE From DIALS' print >> f, 'DETECTOR PILA' print >> f, 'SITE Not set' print >> f, 'DIAMETER %6.2f' % (max( detector[0].get_image_size()) * detector[0].get_pixel_size()[0]) print >> f, 'PIXEL %s' % detector[0].get_pixel_size()[0] print >> f, 'ROTAXIS %4.2f %4.2f %4.2f' % rotation.elems, direction print >> f, 'POLAXIS %4.2f %4.2f %4.2f' % polarization.elems print >> f, 'GAIN 1.00' # correct for Pilatus images print >> f, 'CMOSAIC %.2f' % experiment.profile.sigma_m() print >> f, 'PHISTART %.2f' % scan.get_oscillation_range()[0] print >> f, 'PHIWIDTH %.2f' % scan.get_oscillation()[1] print >> f, 'DISTANCE %7.2f' % distance print >> f, 'WAVELENGTH %.5f' % beam.get_wavelength() print >> f, 'POLARISATION %7.5f' % beam.get_polarization_fraction() print >> f, 'SYMMETRY %s' % short_space_group_name( cryst.get_space_group()) print >> f, 'UB %9.2f %9.2f %9.2f' % UB_mosflm[:3] print >> f, ' %9.2f %9.2f %9.2f' % UB_mosflm[3:6] print >> f, ' %9.2f %9.2f %9.2f' % UB_mosflm[6:] print >> f, 'CELL %8.2f %8.2f %8.2f %6.2f %6.2f %6.2f' % uc_params print >> f, 'RASTER 13 13 7 3 4' print >> f, 'SEPARATION 2.960 2.960' print >> f, 'BEAM %8.3f %8.3f' % beam_centre print >> f, '# end of parameter file for BEST'
def __init__(self, sweep): self._sweep = sweep # detector dimensions in pixels self.detector_size = map( int, (max(panel.get_raw_image_offset()[0]+panel.get_image_size()[0] for panel in self.get_detector()), max(panel.get_raw_image_offset()[1]+panel.get_image_size()[1] for panel in self.get_detector()))) self.fast, self.slow = self.detector_size if len(self.get_detector()) > 1: fast = self.get_detector()[0].get_parent_fast_axis() slow = self.get_detector()[0].get_parent_slow_axis() Rd = align_reference_frame(fast, (1,0,0), slow, (0,1,0)) origin = Rd * matrix.col(self.get_detector()[0].get_parent_origin()) else: fast = self.get_detector()[0].get_fast_axis() slow = self.get_detector()[0].get_slow_axis() Rd = align_reference_frame(fast, (1,0,0), slow, (0,1,0)) origin = Rd * matrix.col(self.get_detector()[0].get_origin()) self.detector_x_axis = (Rd * matrix.col(fast)).elems self.detector_y_axis = (Rd * matrix.col(slow)).elems F = Rd * matrix.col(fast) S = Rd * matrix.col(slow) N = F.cross(S) self.detector_normal = N.elems self.pixel_size = self.get_detector()[0].get_pixel_size() # assume all panels same pixel size centre = -(origin - origin.dot(N) * N) x = centre.dot(F) y = centre.dot(S) f, s = self.pixel_size self.detector_distance = origin.dot(N) # Need to add 0.5 because XDS seems to do centroids in fortran coords self.detector_origin = (x/f + 0.5, y/f + 0.5) self.imagecif_to_xds_transformation_matrix = Rd self.panel_limits = [] self.panel_x_axis = [] self.panel_y_axis = [] self.panel_origin = [] self.panel_distance = [] self.panel_normal = [] for panel_id, panel in enumerate(self.get_detector()): f = Rd * matrix.col(panel.get_fast_axis()) s = Rd * matrix.col(panel.get_slow_axis()) n = f.cross(s) xmin, ymin = panel.get_raw_image_offset() xmax = xmin + panel.get_image_size()[0] ymax = ymin + panel.get_image_size()[1] self.panel_limits.append((xmin+1, xmax, ymin+1, ymax)) o = Rd * matrix.col(panel.get_origin()) op = o.dot(n) * n d0 = matrix.col((-x, -y, self.detector_distance)) orgsx = (op - o + d0).dot(f) / self.pixel_size[0] + xmin orgsy = (op - o + d0).dot(s) / self.pixel_size[1] + ymin panel_distance = op.dot(n) - d0.dot(n) # axes in local (i.e. detector) frame fl = matrix.col(panel.get_local_fast_axis()) sl = matrix.col(panel.get_local_slow_axis()) nl = fl.cross(sl) self.panel_x_axis.append(fl.elems) self.panel_y_axis.append(sl.elems) self.panel_normal.append(nl.elems) self.panel_origin.append((orgsx, orgsy)) self.panel_distance.append(panel_distance) # Beam stuff self.wavelength = self.get_beam().get_wavelength() self.beam_vector = Rd * matrix.col(self.get_beam().get_direction()) # just to make sure it is the correct length self.beam_vector = self.beam_vector.normalize() #/ self.wavelength self.beam_vector = (- self.beam_vector).elems # Scan and goniometer stuff self.starting_frame = self.get_scan().get_image_range()[0] self.starting_angle = self.get_scan().get_oscillation()[0] self.oscillation_range = self.get_scan().get_oscillation()[1] self.rotation_axis = ( Rd * matrix.col(self.get_goniometer().get_rotation_axis())).elems return
def _export_experiment(filename, integrated_data, experiment, params, var_model=(1, 0)): # type: (str, flex.reflection_table, dxtbx.model.Experiment, libtbx.phil.scope_extract, Tuple) """Export a single experiment to an XDS_ASCII.HKL format file. Args: filename: The file to write to integrated_data: The reflection table, pre-selected to one experiment experiment: The experiment list entry to export params: The PHIL configuration object var_model: """ # export for xds_ascii should only be for non-scaled reflections assert any(i in integrated_data for i in ["intensity.sum.value", "intensity.prf.value"]) # Handle requesting profile intensities (default via auto) but no column if "profile" in params.intensity and "intensity.prf.value" not in integrated_data: raise Sorry( "Requested profile intensity data but only summed present. Use intensity=sum." ) integrated_data = filter_reflection_table( integrated_data, intensity_choice=params.intensity, partiality_threshold=params.mtz.partiality_threshold, combine_partials=params.mtz.combine_partials, min_isigi=params.mtz.min_isigi, filter_ice_rings=params.mtz.filter_ice_rings, d_min=params.mtz.d_min, ) # calculate the scl = lp/dqe correction for outputting but don't apply it as # it has already been applied in filter_reflection_table ( integrated_data, scl, ) = FilteringReductionMethods.calculate_lp_qe_correction_and_filter( integrated_data) # sort data before output nref = len(integrated_data["miller_index"]) indices = flex.size_t_range(nref) unique = copy.deepcopy(integrated_data["miller_index"]) map_to_asu(experiment.crystal.get_space_group().type(), False, unique) perm = sorted(indices, key=lambda k: unique[k]) integrated_data = integrated_data.select(flex.size_t(perm)) if experiment.goniometer is None: print( "Warning: No goniometer. Experimentally exporting with (1 0 0) axis" ) unit_cell = experiment.crystal.get_unit_cell() if experiment.scan is None: print( "Warning: No Scan. Experimentally exporting no-oscillation values") image_range = (1, 1) phi_start, phi_range = 0.0, 0.0 else: image_range = experiment.scan.get_image_range() phi_start, phi_range = experiment.scan.get_image_oscillation( image_range[0]) # gather the required information for the reflection file nref = len(integrated_data["miller_index"]) miller_index = integrated_data["miller_index"] # profile correlation if "profile.correlation" in integrated_data: prof_corr = 100.0 * integrated_data["profile.correlation"] else: prof_corr = flex.double(nref, 100.0) # partiality if "partiality" in integrated_data: partiality = 100 * integrated_data["partiality"] else: prof_corr = flex.double(nref, 100.0) if "intensity.sum.value" in integrated_data: I = integrated_data["intensity.sum.value"] V = integrated_data["intensity.sum.variance"] assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) else: I = integrated_data["intensity.prf.value"] V = integrated_data["intensity.prf.variance"] assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) fout = open(filename, "w") # first write the header - in the "standard" coordinate frame... panel = experiment.detector[0] fast = panel.get_fast_axis() slow = panel.get_slow_axis() Rd = align_reference_frame(fast, (1, 0, 0), slow, (0, 1, 0)) print("Coordinate change:") print("%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n" % Rd.elems) fast = Rd * fast slow = Rd * slow qx, qy = panel.get_pixel_size() nx, ny = panel.get_image_size() distance = matrix.col(Rd * panel.get_origin()).dot( matrix.col(Rd * panel.get_normal())) org = Rd * (matrix.col(panel.get_origin()) - distance * matrix.col(panel.get_normal())) orgx = -org.dot(fast) / qx orgy = -org.dot(slow) / qy UB = Rd * matrix.sqr(experiment.crystal.get_A()) real_space_ABC = UB.inverse().elems if experiment.goniometer is not None: axis = Rd * experiment.goniometer.get_rotation_axis() else: axis = Rd * (1, 0, 0) beam = Rd * experiment.beam.get_s0() cell_fmt = "%9.3f %9.3f %9.3f %7.3f %7.3f %7.3f" axis_fmt = "%9.3f %9.3f %9.3f" fout.write("\n".join([ "!FORMAT=XDS_ASCII MERGE=FALSE FRIEDEL'S_LAW=TRUE", "!Generated by dials.export", "!DATA_RANGE= %d %d" % image_range, "!ROTATION_AXIS= %9.6f %9.6f %9.6f" % axis.elems, "!OSCILLATION_RANGE= %f" % phi_range, "!STARTING_ANGLE= %f" % phi_start, "!STARTING_FRAME= %d" % image_range[0], "!SPACE_GROUP_NUMBER= %d" % experiment.crystal.get_space_group().type().number(), "!UNIT_CELL_CONSTANTS= %s" % (cell_fmt % unit_cell.parameters()), "!UNIT_CELL_A-AXIS= %s" % (axis_fmt % real_space_ABC[0:3]), "!UNIT_CELL_B-AXIS= %s" % (axis_fmt % real_space_ABC[3:6]), "!UNIT_CELL_C-AXIS= %s" % (axis_fmt % real_space_ABC[6:9]), "!X-RAY_WAVELENGTH= %f" % experiment.beam.get_wavelength(), "!INCIDENT_BEAM_DIRECTION= %f %f %f" % beam.elems, "!NX= %d NY= %d QX= %f QY= %f" % (nx, ny, qx, qy), "!ORGX= %9.2f ORGY= %9.2f" % (orgx, orgy), "!DETECTOR_DISTANCE= %8.3f" % distance, "!DIRECTION_OF_DETECTOR_X-AXIS= %9.5f %9.5f %9.5f" % fast.elems, "!DIRECTION_OF_DETECTOR_Y-AXIS= %9.5f %9.5f %9.5f" % slow.elems, "!VARIANCE_MODEL= %7.3e %7.3e" % var_model, "!NUMBER_OF_ITEMS_IN_EACH_DATA_RECORD=12", "!ITEM_H=1", "!ITEM_K=2", "!ITEM_L=3", "!ITEM_IOBS=4", "!ITEM_SIGMA(IOBS)=5", "!ITEM_XD=6", "!ITEM_YD=7", "!ITEM_ZD=8", "!ITEM_RLP=9", "!ITEM_PEAK=10", "!ITEM_CORR=11", "!ITEM_PSI=12", "!END_OF_HEADER", "", ])) # then write the data records s0 = Rd * matrix.col(experiment.beam.get_s0()) for j in range(nref): x, y, z = integrated_data["xyzcal.px"][j] phi = phi_start + z * phi_range h, k, l = miller_index[j] X = (UB * (h, k, l)).rotate(axis, phi, deg=True) s = s0 + X g = s.cross(s0).normalize() # find component of beam perpendicular to f, e e = -(s + s0).normalize() if h == k and k == l: u = (h, -h, 0) else: u = (k - l, l - h, h - k) q = ((matrix.col(u).transpose() * UB.inverse()).normalize().transpose().rotate(axis, phi, deg=True)) psi = q.angle(g, deg=True) if q.dot(e) < 0: psi *= -1 fout.write("%d %d %d %f %f %f %f %f %f %.1f %.1f %f\n" % ( h, k, l, I[j], sigI[j], x, y, z, scl[j], partiality[j], prof_corr[j], psi, )) fout.write("!END_OF_DATA\n") fout.close() logger.info("Output %d reflections to %s" % (nref, filename))
def run(args): from dials.util.options import OptionParser from dials.util.options import flatten_experiments from libtbx.utils import Sorry import libtbx.load_env usage = "%s [options] experiments.json" %libtbx.env.dispatcher_name parser = OptionParser( usage=usage, phil=phil_scope, read_experiments=True, check_format=False, epilog=help_message) params, options = parser.parse_args(show_diff_phil=True) experiments = flatten_experiments(params.input.experiments) if len(experiments) <= 1: parser.print_help() return from dials.algorithms.indexing.compare_orientation_matrices import \ difference_rotation_matrix_axis_angle from scitbx import matrix crystals = [] for experiment in experiments: crystal = experiment.crystal gonio = experiment.goniometer assert len(experiments) == (len(gonio.get_axes())+1) scan = experiment.scan fixed_rotation = matrix.sqr(gonio.get_fixed_rotation()) setting_rotation = matrix.sqr(gonio.get_setting_rotation()) rotation_axis = matrix.col(gonio.get_rotation_axis_datum()) rotation_matrix = rotation_axis.axis_and_angle_as_r3_rotation_matrix( scan.get_oscillation()[0], deg=True) U = matrix.sqr(crystal.get_U()) U = setting_rotation * rotation_matrix * fixed_rotation * U crystal.set_U(U) if params.space_group is not None: crystal.set_space_group(params.space_group.group()) rows = [] from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix R_to_mosflm = align_reference_frame( experiments[0].beam.get_s0(), (1.0, 0.0, 0.0), experiments[0].goniometer.get_rotation_axis(), (0.0, 0.0, 1.0)) axes = [] angles = [] for i in range(len(experiments) - 1): target_angle = experiments[i+1].goniometer.get_angles()[i] R_ij, axis, angle, cb_op = difference_rotation_matrix_axis_angle( experiments[i].crystal, experiments[i+1].crystal, target_angle=target_angle) gonio = experiments[i+1].goniometer axis_names = gonio.get_names() axes.append(axis) angles.append(angle) depends_on = '.' if i+1 < len(axis_names): depends_on = axis_names[i+1] rows.insert(0, ( axis_names[i], 'rotation', 'goniometer', depends_on, '%.4f' %axis[0], '%.4f' %axis[1], '%.4f' %axis[2], '.', '.', '.')) axis_names = experiments[0].goniometer.get_names() print "Goniometer axes and angles (ImgCIF coordinate system):" for axis, angle, name in zip(axes, angles, axis_names): print "%s: " %name, "rotation of %.3f degrees" %angle, "about axis (%.5f, %.5f, %.5f)" %axis print print "Goniometer axes and angles (MOSFLM coordinate system):" for axis, angle, name in zip(axes, angles, axis_names): print "%s: " %name, "rotation of %.3f degrees" %angle, "about axis (%.5f, %.5f, %.5f)" %( R_to_mosflm * matrix.col(axis)).elems print print "ImgCIF _axis loop template:" from iotbx import cif loop = cif.model.loop( header=['_axis.id', '_axis.type', '_axis.equipment', '_axis.depends_on', '_axis.vector[1]', '_axis.vector[2]', '_axis.vector[3]', '_axis.offset[1]', '_axis.offset[2]', '_axis.offset[3]']) for row in reversed(rows): loop.add_row(row) print loop if params.output.xoalign is not None: write_xoalign_config(params.output.xoalign, axes, axis_names)
def extract_varying_crystal(integrate_lp, experiments): '''Extract a varying crystal model from an INTEGRATE.LP file (static in blocks with step changes) and write it to the provided experiments ''' if len(experiments) > 1: print "Can only read a varying crystal model for a single " +\ "experiment. Skipping." return experiment = experiments[0] # read required records from the file. Relies on them being in the # right order as we read through once xds_axis = None xds_beam = None blocks, a_axis, b_axis, c_axis = [], [], [], [] with open(integrate_lp) as f: for record in f: if record.lstrip().startswith("ROTATION_AXIS="): xds_axis = record.split("ROTATION_AXIS=")[1].split() break for record in f: if record.lstrip().startswith("INCIDENT_BEAM_DIRECTION="): xds_beam = record.split("INCIDENT_BEAM_DIRECTION=")[1].split() break for record in f: if record.lstrip().startswith("PROCESSING OF IMAGES"): blocks.append(record.split("PROCESSING OF IMAGES")[1]) continue if record.lstrip().startswith("COORDINATES OF UNIT CELL A-AXIS"): a_axis.append(record.split("COORDINATES OF UNIT CELL A-AXIS")[1]) continue if record.lstrip().startswith("COORDINATES OF UNIT CELL B-AXIS"): b_axis.append(record.split("COORDINATES OF UNIT CELL B-AXIS")[1]) continue if record.lstrip().startswith("COORDINATES OF UNIT CELL C-AXIS"): c_axis.append(record.split("COORDINATES OF UNIT CELL C-AXIS")[1]) continue # sanity checks msg = "INTEGRATE.LP is not in the expected format" nblocks = len(blocks) try: assert len(a_axis) == len(b_axis) == len(c_axis) == nblocks assert (xds_axis, xds_beam).count(None) == 0 except AssertionError: print msg return # conversions to numeric try: blocks = [map(int, block.split("...")) for block in blocks] a_axis = [map(float, axis.split()) for axis in a_axis] b_axis = [map(float, axis.split()) for axis in b_axis] c_axis = [map(float, axis.split()) for axis in c_axis] xds_beam = [float(e) for e in xds_beam] xds_axis = [float(e) for e in xds_axis] except ValueError: print msg return # coordinate frame conversions from scitbx import matrix dbeam = matrix.col(experiment.beam.get_direction()) daxis = matrix.col(experiment.goniometer.get_rotation_axis()) xbeam = matrix.col(xds_beam).normalize() xaxis = matrix.col(xds_axis).normalize() # want to align XDS -s0 vector... from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(- xbeam, dbeam, xaxis, daxis) # Make a static crystal for each block from dxtbx.model.crystal import crystal_model crystals = [] sg = experiment.crystal.get_space_group() for a, b, c in zip(a_axis, b_axis, c_axis): a = R * matrix.col(a) b = R * matrix.col(b) c = R * matrix.col(c) crystals.append(crystal_model(a, b, c, space_group=sg)) # construct a list of scan points A_list = [] for block, crystal in zip(blocks, crystals): A = crystal.get_A() for im in range(block[0], block[1] + 1): A_list.append(A) # Need a final scan point at the end of the final image A_list.append(A) # set the scan-varying crystal experiment.crystal.set_A_at_scan_points(A_list) return
def run(args): from dials.util.options import OptionParser from dials.util.options import flatten_experiments from dials.util.options import flatten_reflections import libtbx.load_env usage = "%s [options] datablock.json" % (libtbx.env.dispatcher_name) parser = OptionParser( usage=usage, phil=phil_scope, read_experiments=True, read_reflections=True, check_format=True, epilog=help_message, ) params, options = parser.parse_args(show_diff_phil=True) experiments = flatten_experiments(params.input.experiments) reflections = flatten_reflections(params.input.reflections) if len(reflections) == 0 or len(experiments) == 0: parser.print_help() exit(0) reflections = reflections[0] assert len(experiments) == 1 experiment = experiments[0] from dials.command_line.check_strategy import filter_shadowed_reflections sel = filter_shadowed_reflections(experiments, reflections) print( "%i/%i (%.2f%%) shadowed reflections" % (sel.count(True), sel.size(), 100 * sel.count(True) / sel.size()) ) if params.negate: sel = ~sel shadowed = reflections.select(sel) shadowed.as_pickle(params.output.reflections) if params.output.filter_hkl is not None: from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix detector = experiment.detector if len(detector) > 1: fast = detector[0].get_parent_fast_axis() slow = detector[0].get_parent_slow_axis() Rd = align_reference_frame(fast, (1, 0, 0), slow, (0, 1, 0)) origin = Rd * matrix.col(detector[0].get_parent_origin()) else: fast = detector[0].get_fast_axis() slow = detector[0].get_slow_axis() Rd = align_reference_frame(fast, (1, 0, 0), slow, (0, 1, 0)) origin = Rd * matrix.col(detector[0].get_origin()) with open(params.output.filter_hkl, "wb") as f: for ref in shadowed: p = detector[ref["panel"]] ox, oy = p.get_raw_image_offset() h, k, l = ref["miller_index"] x, y, z = ref["xyzcal.px"] dx, dy, dz = (2, 2, 2) print( "%i %i %i %.1f %.1f %.1f %.1f %.1f %.1f" % (h, k, l, x + ox, y + oy, z, dx, dy, dz), file=f, )
def export_xds_ascii(integrated_data, experiment_list, hklout, summation=False, include_partials=False, keep_partials=False, var_model=(1,0)): '''Export data from integrated_data corresponding to experiment_list to an XDS_ASCII.HKL formatted text file.''' from dials.array_family import flex import math # for the moment assume (and assert) that we will convert data from exactly # one lattice... assert(len(experiment_list) == 1) # select reflections that are assigned to an experiment (i.e. non-negative id) integrated_data = integrated_data.select(integrated_data['id'] >= 0) assert max(integrated_data['id']) == 0 if not summation: assert('intensity.prf.value' in integrated_data) if 'intensity.prf.variance' in integrated_data: selection = integrated_data.get_flags( integrated_data.flags.integrated, all=True) else: selection = integrated_data.get_flags( integrated_data.flags.integrated_sum) integrated_data = integrated_data.select(selection) selection = integrated_data['intensity.sum.variance'] <= 0 if selection.count(True) > 0: integrated_data.del_selected(selection) logger.info('Removing %d reflections with negative variance' % \ selection.count(True)) if 'intensity.prf.variance' in integrated_data: selection = integrated_data['intensity.prf.variance'] <= 0 if selection.count(True) > 0: integrated_data.del_selected(selection) logger.info('Removing %d profile reflections with negative variance' % \ selection.count(True)) if include_partials: integrated_data = sum_partial_reflections(integrated_data) integrated_data = scale_partial_reflections(integrated_data) if 'partiality' in integrated_data: selection = integrated_data['partiality'] < 0.99 if selection.count(True) > 0 and not keep_partials: integrated_data.del_selected(selection) logger.info('Removing %d incomplete reflections' % \ selection.count(True)) experiment = experiment_list[0] # sort data before output nref = len(integrated_data['miller_index']) indices = flex.size_t_range(nref) import copy unique = copy.deepcopy(integrated_data['miller_index']) from cctbx.miller import map_to_asu map_to_asu(experiment.crystal.get_space_group().type(), False, unique) perm = sorted(indices, key=lambda k: unique[k]) integrated_data = integrated_data.select(flex.size_t(perm)) from scitbx import matrix from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame assert (not experiment.goniometer is None) unit_cell = experiment.crystal.get_unit_cell() from scitbx.array_family import flex from math import floor, sqrt assert(not experiment.scan is None) image_range = experiment.scan.get_image_range() phi_start, phi_range = experiment.scan.get_image_oscillation(image_range[0]) # gather the required information for the reflection file nref = len(integrated_data['miller_index']) zdet = flex.double(integrated_data['xyzcal.px'].parts()[2]) miller_index = integrated_data['miller_index'] I = None sigI = None # export including scale factors if 'lp' in integrated_data: lp = integrated_data['lp'] else: lp = flex.double(nref, 1.0) if 'dqe' in integrated_data: dqe = integrated_data['dqe'] else: dqe = flex.double(nref, 1.0) scl = lp / dqe # profile correlation if 'profile.correlation' in integrated_data: prof_corr = 100.0 * integrated_data['profile.correlation'] else: prof_corr = flex.double(nref, 100.0) # partiality if 'partiality' in integrated_data: partiality = 100 * integrated_data['partiality'] else: prof_corr = flex.double(nref, 100.0) if summation: I = integrated_data['intensity.sum.value'] * scl V = integrated_data['intensity.sum.variance'] * scl * scl assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) else: I = integrated_data['intensity.prf.value'] * scl V = integrated_data['intensity.prf.variance'] * scl * scl assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) fout = open(hklout, 'w') # first write the header - in the "standard" coordinate frame... panel = experiment.detector[0] fast = panel.get_fast_axis() slow = panel.get_slow_axis() Rd = align_reference_frame(fast, (1,0,0), slow, (0,1,0)) print 'Coordinate change:' print '%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n' % Rd.elems fast = Rd * fast slow = Rd * slow qx, qy = panel.get_pixel_size() nx, ny = panel.get_image_size() distance = matrix.col(Rd * panel.get_origin()).dot( matrix.col(Rd * panel.get_normal())) org = Rd * (matrix.col(panel.get_origin()) - distance * matrix.col( panel.get_normal())) orgx = - org.dot(fast) / qx orgy = - org.dot(slow) / qy UB = Rd * matrix.sqr(experiment.crystal.get_A()) real_space_ABC = UB.inverse().elems axis = Rd * experiment.goniometer.get_rotation_axis() beam = Rd * experiment.beam.get_s0() cell_fmt = '%9.3f %9.3f %9.3f %7.3f %7.3f %7.3f' axis_fmt = '%9.3f %9.3f %9.3f' fout.write('\n'.join([ '!FORMAT=XDS_ASCII MERGE=FALSE FRIEDEL\'S_LAW=TRUE', '!Generated by dials.export', '!DATA_RANGE= %d %d' % image_range, '!ROTATION_AXIS= %9.6f %9.6f %9.6f' % axis.elems, '!OSCILLATION_RANGE= %f' % phi_range, '!STARTING_ANGLE= %f' % phi_start, '!STARTING_FRAME= %d' % image_range[0], '!SPACE_GROUP_NUMBER= %d' % experiment.crystal.get_space_group().type().number(), '!UNIT_CELL_CONSTANTS= %s' % (cell_fmt % unit_cell.parameters()), '!UNIT_CELL_A-AXIS= %s' % (axis_fmt % real_space_ABC[0:3]), '!UNIT_CELL_B-AXIS= %s' % (axis_fmt % real_space_ABC[3:6]), '!UNIT_CELL_C-AXIS= %s' % (axis_fmt % real_space_ABC[6:9]), '!X-RAY_WAVELENGTH= %f' % experiment.beam.get_wavelength(), '!INCIDENT_BEAM_DIRECTION= %f %f %f' % beam.elems, '!NX= %d NY= %d QX= %f QY= %f' % (nx, ny, qx, qy), '!ORGX= %9.2f ORGY= %9.2f' % (orgx, orgy), '!DETECTOR_DISTANCE= %8.3f' % distance, '!DIRECTION_OF_DETECTOR_X-AXIS= %9.5f %9.5f %9.5f' % fast.elems, '!DIRECTION_OF_DETECTOR_Y-AXIS= %9.5f %9.5f %9.5f' % slow.elems, '!VARIANCE_MODEL= %7.3e %7.3e' % var_model, '!NUMBER_OF_ITEMS_IN_EACH_DATA_RECORD=12', '!ITEM_H=1', '!ITEM_K=2', '!ITEM_L=3', '!ITEM_IOBS=4', '!ITEM_SIGMA(IOBS)=5', '!ITEM_XD=6', '!ITEM_YD=7', '!ITEM_ZD=8', '!ITEM_RLP=9', '!ITEM_PEAK=10', '!ITEM_CORR=11', '!ITEM_PSI=12', '!END_OF_HEADER', ''])) # then write the data records s0 = Rd * matrix.col(experiment.beam.get_s0()) for j in range(nref): x, y, z = integrated_data['xyzcal.px'][j] phi = phi_start + z * phi_range h, k, l = miller_index[j] X = (UB * (h, k, l)).rotate(axis, phi, deg=True) s = s0 + X g = s.cross(s0).normalize() f = (s - s0).normalize() # find component of beam perpendicular to f, e e = - (s + s0).normalize() if h == k and k == l: u = (h, -h, 0) else: u = (k - l, l - h, h - k) q = (matrix.col(u).transpose() * UB.inverse()).normalize( ).transpose().rotate(axis, phi, deg=True) psi = q.angle(g, deg=True) if q.dot(e) < 0: psi *= -1 fout.write('%d %d %d %f %f %f %f %f %f %.1f %.1f %f\n' % (h, k, l, I[j], sigI[j], x, y, z, scl[j], partiality[j], prof_corr[j], psi)) fout.write('!END_OF_DATA\n') fout.close() logger.info('Output %d reflections to %s' % (nref, hklout)) return
def dump(experiments, directory): """ Dump the experiments in mosflm format :param experiments: The experiments to dump :param directory: The directory to write to """ for i, experiment in enumerate(experiments): suffix = "" if len(experiments) > 1: suffix = "_%i" % (i + 1) sub_dir = "%s%s" % (directory, suffix) if not os.path.isdir(sub_dir): os.makedirs(sub_dir) detector = experiment.detector beam = experiment.beam goniometer = experiment.goniometer # XXX imageset is getting the experimental geometry from the image files # rather than the input models.expt file imageset = experiment.imageset R_to_mosflm = align_reference_frame( beam.get_s0(), (1.0, 0.0, 0.0), goniometer.get_rotation_axis(), (0.0, 0.0, 1.0), ) cryst = experiment.crystal cryst = cryst.change_basis(cryst.get_space_group().info(). change_of_basis_op_to_reference_setting()) A = matrix.sqr(cryst.get_A()) A_inv = A.inverse() real_space_a = R_to_mosflm * A_inv.elems[:3] real_space_b = R_to_mosflm * A_inv.elems[3:6] real_space_c = R_to_mosflm * A_inv.elems[6:9] cryst_mosflm = Crystal( real_space_a, real_space_b, real_space_c, space_group=cryst.get_space_group(), ) A_mosflm = matrix.sqr(cryst_mosflm.get_A()) U_mosflm = matrix.sqr(cryst_mosflm.get_U()) assert U_mosflm.is_r3_rotation_matrix(), U_mosflm w = beam.get_wavelength() index_mat = os.path.join(sub_dir, "index.mat") mosflm_in = os.path.join(sub_dir, "mosflm.in") print("Exporting experiment to %s and %s" % (index_mat, mosflm_in)) with open(index_mat, "w") as f: f.write( format_mosflm_mat(w * A_mosflm, U_mosflm, cryst.get_unit_cell())) img_dir, template = os.path.split(imageset.get_template()) symmetry = cryst_mosflm.get_space_group().type().number() beam_centre = tuple( reversed(detector[0].get_beam_centre(beam.get_s0()))) distance = detector[0].get_directed_distance() with open(mosflm_in, "w") as f: f.write( write_mosflm_input( directory=img_dir, template=template, symmetry=symmetry, beam_centre=beam_centre, distance=distance, mat_file="index.mat", ))
def __init__(self, sweep): self._sweep = sweep # detector dimensions in pixels self.detector_size = map( int, ( max(panel.get_raw_image_offset()[0] + panel.get_image_size()[0] for panel in self.get_detector()), max(panel.get_raw_image_offset()[1] + panel.get_image_size()[1] for panel in self.get_detector()), ), ) self.fast, self.slow = self.detector_size if len(self.get_detector()) > 1: fast = self.get_detector()[0].get_parent_fast_axis() slow = self.get_detector()[0].get_parent_slow_axis() Rd = align_reference_frame(fast, (1, 0, 0), slow, (0, 1, 0)) origin = Rd * matrix.col( self.get_detector()[0].get_parent_origin()) else: fast = self.get_detector()[0].get_fast_axis() slow = self.get_detector()[0].get_slow_axis() Rd = align_reference_frame(fast, (1, 0, 0), slow, (0, 1, 0)) origin = Rd * matrix.col(self.get_detector()[0].get_origin()) self.detector_x_axis = (Rd * matrix.col(fast)).elems self.detector_y_axis = (Rd * matrix.col(slow)).elems F = Rd * matrix.col(fast) S = Rd * matrix.col(slow) N = F.cross(S) self.detector_normal = N.elems # assume all panels same pixel size self.pixel_size = self.get_detector()[0].get_pixel_size() centre = -(origin - origin.dot(N) * N) x = centre.dot(F) y = centre.dot(S) f, s = self.pixel_size self.detector_distance = origin.dot(N) # Need to add 0.5 because XDS seems to do centroids in fortran coords self.detector_origin = (x / f + 0.5, y / f + 0.5) self.imagecif_to_xds_transformation_matrix = Rd self.panel_limits = [] self.panel_x_axis = [] self.panel_y_axis = [] self.panel_origin = [] self.panel_distance = [] self.panel_normal = [] for panel_id, panel in enumerate(self.get_detector()): f = Rd * matrix.col(panel.get_fast_axis()) s = Rd * matrix.col(panel.get_slow_axis()) n = f.cross(s) xmin, ymin = panel.get_raw_image_offset() xmax = xmin + panel.get_image_size()[0] ymax = ymin + panel.get_image_size()[1] self.panel_limits.append((xmin + 1, xmax, ymin + 1, ymax)) o = Rd * matrix.col(panel.get_origin()) op = o.dot(n) * n d0 = matrix.col((-x, -y, self.detector_distance)) orgsx = (op - o + d0).dot(f) / self.pixel_size[0] + xmin orgsy = (op - o + d0).dot(s) / self.pixel_size[1] + ymin panel_distance = op.dot(n) - d0.dot(n) # axes in local (i.e. detector) frame fl = matrix.col(panel.get_local_fast_axis()) sl = matrix.col(panel.get_local_slow_axis()) nl = fl.cross(sl) self.panel_x_axis.append(fl.elems) self.panel_y_axis.append(sl.elems) self.panel_normal.append(nl.elems) self.panel_origin.append((orgsx, orgsy)) self.panel_distance.append(panel_distance) # Beam stuff self.wavelength = self.get_beam().get_wavelength() self.beam_vector = Rd * matrix.col(self.get_beam().get_direction()) # just to make sure it is the correct length self.beam_vector = self.beam_vector.normalize() # / self.wavelength self.beam_vector = (-self.beam_vector).elems # Scan and goniometer stuff self.starting_frame = self.get_scan().get_image_range()[0] self.starting_angle = self.get_scan().get_oscillation()[0] self.oscillation_range = self.get_scan().get_oscillation()[1] self.rotation_axis = ( Rd * matrix.col(self.get_goniometer().get_rotation_axis())).elems
def run(args): from dials.util.options import OptionParser from dials.util.options import flatten_experiments import libtbx.load_env usage = "%s [options] experiments.json" % libtbx.env.dispatcher_name parser = OptionParser(usage=usage, phil=phil_scope, read_experiments=True, check_format=False, epilog=help_message) params, options = parser.parse_args(show_diff_phil=True) experiments = flatten_experiments(params.input.experiments) if len(experiments) <= 1: parser.print_help() return from dials.algorithms.indexing.compare_orientation_matrices import \ difference_rotation_matrix_axis_angle from scitbx import matrix crystals = [] for experiment in experiments: crystal = experiment.crystal gonio = experiment.goniometer assert len(experiments) == (len(gonio.get_axes()) + 1) scan = experiment.scan fixed_rotation = matrix.sqr(gonio.get_fixed_rotation()) setting_rotation = matrix.sqr(gonio.get_setting_rotation()) rotation_axis = matrix.col(gonio.get_rotation_axis_datum()) rotation_matrix = rotation_axis.axis_and_angle_as_r3_rotation_matrix( scan.get_oscillation()[0], deg=True) U = matrix.sqr(crystal.get_U()) U = setting_rotation * rotation_matrix * fixed_rotation * U crystal.set_U(U) if params.space_group is not None: crystal.set_space_group(params.space_group.group()) rows = [] from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix R_to_mosflm = align_reference_frame( experiments[0].beam.get_s0(), (1.0, 0.0, 0.0), experiments[0].goniometer.get_rotation_axis(), (0.0, 0.0, 1.0)) axes = [] angles = [] for i in range(len(experiments) - 1): target_angle = experiments[i + 1].goniometer.get_angles()[i] if i == experiments[i].goniometer.get_scan_axis(): # rotation axis is canonical in our coordinate system axis = experiments[i].goniometer.get_axes()[i] angle = target_angle else: R_ij, axis, angle, cb_op = difference_rotation_matrix_axis_angle( experiments[i].crystal, experiments[i + 1].crystal, target_angle=target_angle) gonio = experiments[i + 1].goniometer axis_names = gonio.get_names() axes.append(axis) angles.append(angle) depends_on = '.' if i + 1 < len(axis_names): depends_on = axis_names[i + 1] rows.insert( 0, (axis_names[i], 'rotation', 'goniometer', depends_on, '%.4f' % axis[0], '%.4f' % axis[1], '%.4f' % axis[2], '.', '.', '.')) axis_names = experiments[0].goniometer.get_names() print "Goniometer axes and angles (ImgCIF coordinate system):" for axis, angle, name in zip(axes, angles, axis_names): print "%s: " % name, "rotation of %.3f degrees" % angle, "about axis (%.5f,%.5f,%.5f)" % axis print print "Goniometer axes and angles (MOSFLM coordinate system):" for axis, angle, name in zip(axes, angles, axis_names): print "%s: " % name, "rotation of %.3f degrees" % angle, "about axis (%.5f,%.5f,%.5f)" % ( R_to_mosflm * matrix.col(axis)).elems print print "ImgCIF _axis loop template:" from iotbx import cif loop = cif.model.loop(header=[ '_axis.id', '_axis.type', '_axis.equipment', '_axis.depends_on', '_axis.vector[1]', '_axis.vector[2]', '_axis.vector[3]', '_axis.offset[1]', '_axis.offset[2]', '_axis.offset[3]' ]) for row in rows: loop.add_row(row) print loop if params.output.xoalign is not None: axes_mosflm = [(R_to_mosflm * matrix.col(axis)).elems for axis in axes] write_xoalign_config(params.output.xoalign, reversed(axes_mosflm), reversed(axis_names))
def dump(experiments, directory): ''' Dump the experiments in mosflm format :param experiments: The experiments to dump :param directory: The directory to write to ''' for i in range(len(experiments)): suffix = "" if len(experiments) > 1: suffix = "_%i" %(i+1) sub_dir = "%s%s" % (directory, suffix) if not os.path.isdir(sub_dir): os.makedirs(sub_dir) detector = experiments[i].detector beam = experiments[i].beam scan = experiments[i].scan goniometer = experiments[i].goniometer # XXX imageset is getting the experimental geometry from the image files # rather than the input experiments.json file imageset = experiments[i].imageset from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R_to_mosflm = align_reference_frame( beam.get_s0(), (1.0, 0.0, 0.0), goniometer.get_rotation_axis(), (0.0, 0.0, 1.0)) #print R_to_mosflm cryst = experiments[i].crystal cryst = cryst.change_basis( cryst.get_space_group().info()\ .change_of_basis_op_to_reference_setting()) A = cryst.get_A() A_inv = A.inverse() real_space_a = R_to_mosflm * A_inv.elems[:3] real_space_b = R_to_mosflm * A_inv.elems[3:6] real_space_c = R_to_mosflm * A_inv.elems[6:9] cryst_mosflm = crystal_model( real_space_a, real_space_b, real_space_c, space_group=cryst.get_space_group(), mosaicity=cryst.get_mosaicity()) A_mosflm = cryst_mosflm.get_A() U_mosflm = cryst_mosflm.get_U() assert U_mosflm.is_r3_rotation_matrix(), U_mosflm w = beam.get_wavelength() index_mat = os.path.join(sub_dir, "index.mat") mosflm_in = os.path.join(sub_dir, "mosflm.in") print "Exporting experiment to %s and %s" %(index_mat, mosflm_in) with open(index_mat, "wb") as f: print >> f, format_mosflm_mat(w*A_mosflm, U_mosflm, cryst.get_unit_cell()) directory, template = os.path.split(imageset.get_template()) symmetry = cryst_mosflm.get_space_group().type().number() beam_centre = tuple(reversed(detector[0].get_beam_centre(beam.get_s0()))) distance = detector[0].get_distance() with open(mosflm_in, "wb") as f: print >> f, write_mosflm_input(directory=directory, template=template, symmetry=symmetry, beam_centre=beam_centre, distance=distance, mat_file="index.mat") return
def run(args): from dials.util.options import OptionParser from dials.util.options import flatten_experiments from dials.util.options import flatten_reflections import libtbx.load_env usage = "%s [options] datablock.json" %( libtbx.env.dispatcher_name) parser = OptionParser( usage=usage, phil=phil_scope, read_experiments=True, read_reflections=True, check_format=True, epilog=help_message) params, options = parser.parse_args(show_diff_phil=True) experiments = flatten_experiments(params.input.experiments) reflections = flatten_reflections(params.input.reflections) if (len(reflections) == 0 or len(experiments) == 0): parser.print_help() exit(0) reflections = reflections[0] assert len(experiments) == 1 experiment = experiments[0] from dials.command_line.check_strategy import filter_shadowed_reflections sel = filter_shadowed_reflections(experiments, reflections) print "%i/%i (%.2f%%) shadowed reflections" %( sel.count(True), sel.size(), 100*sel.count(True)/sel.size()) if params.negate: sel = ~sel shadowed = reflections.select(sel) shadowed.as_pickle(params.output.reflections) if params.output.filter_hkl is not None: from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix detector = experiment.detector if len(detector) > 1: fast = detector[0].get_parent_fast_axis() slow = detector[0].get_parent_slow_axis() Rd = align_reference_frame(fast, (1,0,0), slow, (0,1,0)) origin = Rd * matrix.col(detector[0].get_parent_origin()) else: fast = detector[0].get_fast_axis() slow = detector[0].get_slow_axis() Rd = align_reference_frame(fast, (1,0,0), slow, (0,1,0)) origin = Rd * matrix.col(detector[0].get_origin()) with open(params.output.filter_hkl, 'wb') as f: for ref in shadowed: p = detector[ref['panel']] ox, oy = p.get_raw_image_offset() h, k, l = ref['miller_index'] x, y, z = ref['xyzcal.px'] dx, dy, dz = (2, 2, 2) print >> f, "%i %i %i %.1f %.1f %.1f %.1f %.1f %.1f" %( h, k, l, x+ox, y+oy, z, dx, dy, dz)
def export_xds_ascii(integrated_data, experiment_list, hklout, summation=False, include_partials=False, keep_partials=False, var_model=(1, 0)): '''Export data from integrated_data corresponding to experiment_list to an XDS_ASCII.HKL formatted text file.''' from dials.array_family import flex # for the moment assume (and assert) that we will convert data from exactly # one lattice... assert (len(experiment_list) == 1) # select reflections that are assigned to an experiment (i.e. non-negative id) integrated_data = integrated_data.select(integrated_data['id'] >= 0) assert max(integrated_data['id']) == 0 if not summation: assert ('intensity.prf.value' in integrated_data) if 'intensity.prf.variance' in integrated_data: selection = integrated_data.get_flags(integrated_data.flags.integrated, all=True) else: selection = integrated_data.get_flags( integrated_data.flags.integrated_sum) integrated_data = integrated_data.select(selection) selection = integrated_data['intensity.sum.variance'] <= 0 if selection.count(True) > 0: integrated_data.del_selected(selection) logger.info('Removing %d reflections with negative variance' % \ selection.count(True)) if 'intensity.prf.variance' in integrated_data: selection = integrated_data['intensity.prf.variance'] <= 0 if selection.count(True) > 0: integrated_data.del_selected(selection) logger.info('Removing %d profile reflections with negative variance' % \ selection.count(True)) if include_partials: integrated_data = sum_partial_reflections(integrated_data) integrated_data = scale_partial_reflections(integrated_data) if 'partiality' in integrated_data: selection = integrated_data['partiality'] < 0.99 if selection.count(True) > 0 and not keep_partials: integrated_data.del_selected(selection) logger.info('Removing %d incomplete reflections' % \ selection.count(True)) experiment = experiment_list[0] # sort data before output nref = len(integrated_data['miller_index']) indices = flex.size_t_range(nref) import copy unique = copy.deepcopy(integrated_data['miller_index']) from cctbx.miller import map_to_asu map_to_asu(experiment.crystal.get_space_group().type(), False, unique) perm = sorted(indices, key=lambda k: unique[k]) integrated_data = integrated_data.select(flex.size_t(perm)) from scitbx import matrix from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame assert (not experiment.goniometer is None) unit_cell = experiment.crystal.get_unit_cell() from scitbx.array_family import flex from math import sqrt assert (not experiment.scan is None) image_range = experiment.scan.get_image_range() phi_start, phi_range = experiment.scan.get_image_oscillation( image_range[0]) # gather the required information for the reflection file nref = len(integrated_data['miller_index']) zdet = flex.double(integrated_data['xyzcal.px'].parts()[2]) miller_index = integrated_data['miller_index'] I = None sigI = None # export including scale factors if 'lp' in integrated_data: lp = integrated_data['lp'] else: lp = flex.double(nref, 1.0) if 'dqe' in integrated_data: dqe = integrated_data['dqe'] else: dqe = flex.double(nref, 1.0) scl = lp / dqe # profile correlation if 'profile.correlation' in integrated_data: prof_corr = 100.0 * integrated_data['profile.correlation'] else: prof_corr = flex.double(nref, 100.0) # partiality if 'partiality' in integrated_data: partiality = 100 * integrated_data['partiality'] else: prof_corr = flex.double(nref, 100.0) if summation: I = integrated_data['intensity.sum.value'] * scl V = integrated_data['intensity.sum.variance'] * scl * scl assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) else: I = integrated_data['intensity.prf.value'] * scl V = integrated_data['intensity.prf.variance'] * scl * scl assert V.all_gt(0) V = var_model[0] * (V + var_model[1] * I * I) sigI = flex.sqrt(V) fout = open(hklout, 'w') # first write the header - in the "standard" coordinate frame... panel = experiment.detector[0] fast = panel.get_fast_axis() slow = panel.get_slow_axis() Rd = align_reference_frame(fast, (1, 0, 0), slow, (0, 1, 0)) print 'Coordinate change:' print '%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n%5.2f %5.2f %5.2f\n' % Rd.elems fast = Rd * fast slow = Rd * slow qx, qy = panel.get_pixel_size() nx, ny = panel.get_image_size() distance = matrix.col(Rd * panel.get_origin()).dot( matrix.col(Rd * panel.get_normal())) org = Rd * (matrix.col(panel.get_origin()) - distance * matrix.col(panel.get_normal())) orgx = -org.dot(fast) / qx orgy = -org.dot(slow) / qy UB = Rd * matrix.sqr(experiment.crystal.get_A()) real_space_ABC = UB.inverse().elems axis = Rd * experiment.goniometer.get_rotation_axis() beam = Rd * experiment.beam.get_s0() cell_fmt = '%9.3f %9.3f %9.3f %7.3f %7.3f %7.3f' axis_fmt = '%9.3f %9.3f %9.3f' fout.write('\n'.join([ '!FORMAT=XDS_ASCII MERGE=FALSE FRIEDEL\'S_LAW=TRUE', '!Generated by dials.export', '!DATA_RANGE= %d %d' % image_range, '!ROTATION_AXIS= %9.6f %9.6f %9.6f' % axis.elems, '!OSCILLATION_RANGE= %f' % phi_range, '!STARTING_ANGLE= %f' % phi_start, '!STARTING_FRAME= %d' % image_range[0], '!SPACE_GROUP_NUMBER= %d' % experiment.crystal.get_space_group().type().number(), '!UNIT_CELL_CONSTANTS= %s' % (cell_fmt % unit_cell.parameters()), '!UNIT_CELL_A-AXIS= %s' % (axis_fmt % real_space_ABC[0:3]), '!UNIT_CELL_B-AXIS= %s' % (axis_fmt % real_space_ABC[3:6]), '!UNIT_CELL_C-AXIS= %s' % (axis_fmt % real_space_ABC[6:9]), '!X-RAY_WAVELENGTH= %f' % experiment.beam.get_wavelength(), '!INCIDENT_BEAM_DIRECTION= %f %f %f' % beam.elems, '!NX= %d NY= %d QX= %f QY= %f' % (nx, ny, qx, qy), '!ORGX= %9.2f ORGY= %9.2f' % (orgx, orgy), '!DETECTOR_DISTANCE= %8.3f' % distance, '!DIRECTION_OF_DETECTOR_X-AXIS= %9.5f %9.5f %9.5f' % fast.elems, '!DIRECTION_OF_DETECTOR_Y-AXIS= %9.5f %9.5f %9.5f' % slow.elems, '!VARIANCE_MODEL= %7.3e %7.3e' % var_model, '!NUMBER_OF_ITEMS_IN_EACH_DATA_RECORD=12', '!ITEM_H=1', '!ITEM_K=2', '!ITEM_L=3', '!ITEM_IOBS=4', '!ITEM_SIGMA(IOBS)=5', '!ITEM_XD=6', '!ITEM_YD=7', '!ITEM_ZD=8', '!ITEM_RLP=9', '!ITEM_PEAK=10', '!ITEM_CORR=11', '!ITEM_PSI=12', '!END_OF_HEADER', '' ])) # then write the data records s0 = Rd * matrix.col(experiment.beam.get_s0()) for j in range(nref): x, y, z = integrated_data['xyzcal.px'][j] phi = phi_start + z * phi_range h, k, l = miller_index[j] X = (UB * (h, k, l)).rotate(axis, phi, deg=True) s = s0 + X g = s.cross(s0).normalize() f = (s - s0).normalize() # find component of beam perpendicular to f, e e = -(s + s0).normalize() if h == k and k == l: u = (h, -h, 0) else: u = (k - l, l - h, h - k) q = (matrix.col(u).transpose() * UB.inverse()).normalize().transpose().rotate(axis, phi, deg=True) psi = q.angle(g, deg=True) if q.dot(e) < 0: psi *= -1 fout.write('%d %d %d %f %f %f %f %f %f %.1f %.1f %f\n' % (h, k, l, I[j], sigI[j], x, y, z, scl[j], partiality[j], prof_corr[j], psi)) fout.write('!END_OF_DATA\n') fout.close() logger.info('Output %d reflections to %s' % (nref, hklout))
def write_par_file(file_name, experiment): from scitbx import matrix from dxtbx.model.crystal import crystal_model from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from iotbx.mtz.extract_from_symmetry_lib import ccp4_symbol imageset = experiment.imageset detector = imageset.get_detector() goniometer = imageset.get_goniometer() beam = imageset.get_beam() scan = imageset.get_scan() R_to_mosflm = align_reference_frame( beam.get_s0(), (1.0, 0.0, 0.0), goniometer.get_rotation_axis(), (0.0, 0.0, 1.0)) cryst = experiment.crystal cryst = cryst.change_basis( cryst.get_space_group().info()\ .change_of_basis_op_to_reference_setting()) A = cryst.get_A() A_inv = A.inverse() real_space_a = R_to_mosflm * A_inv.elems[:3] real_space_b = R_to_mosflm * A_inv.elems[3:6] real_space_c = R_to_mosflm * A_inv.elems[6:9] cryst_mosflm = crystal_model( real_space_a, real_space_b, real_space_c, space_group=cryst.get_space_group(), mosaicity=cryst.get_mosaicity()) A_mosflm = cryst_mosflm.get_A() U_mosflm = cryst_mosflm.get_U() B_mosflm = cryst_mosflm.get_B() UB_mosflm = U_mosflm * B_mosflm uc_params = cryst_mosflm.get_unit_cell().parameters() assert U_mosflm.is_r3_rotation_matrix(), U_mosflm symmetry = cryst_mosflm.get_space_group().type().number() beam_centre = tuple(reversed(detector[0].get_beam_centre(beam.get_s0()))) distance = detector[0].get_directed_distance() polarization = R_to_mosflm * matrix.col(beam.get_polarization_normal()) rotation = matrix.col(goniometer.get_rotation_axis()) if (rotation.angle(matrix.col(detector[0].get_fast_axis())) < rotation.angle(matrix.col(detector[0].get_slow_axis()))): direction = 'FAST' else: direction = 'SLOW' rotation = R_to_mosflm * rotation with open(file_name, 'wb') as f:# print >> f, '# parameter file for BEST' print >> f, 'TITLE From DIALS' print >> f, 'DETECTOR PILA' print >> f, 'SITE Not set' print >> f, 'DIAMETER %6.2f' %(max(detector[0].get_image_size()) * detector[0].get_pixel_size()[0]) print >> f, 'PIXEL %s' %detector[0].get_pixel_size()[0] print >> f, 'ROTAXIS %4.2f %4.2f %4.2f' %rotation.elems, direction print >> f, 'POLAXIS %4.2f %4.2f %4.2f' %polarization.elems print >> f, 'GAIN 1.00' # correct for Pilatus images print >> f, 'CMOSAIC %.2f' %experiment.profile.sigma_m() print >> f, 'PHISTART %.2f' %scan.get_oscillation_range()[0] print >> f, 'PHIWIDTH %.2f' %scan.get_oscillation()[1] print >> f, 'DISTANCE %7.2f' %distance print >> f, 'WAVELENGTH %.5f' %beam.get_wavelength() print >> f, 'POLARISATION %7.5f' %beam.get_polarization_fraction() print >> f, 'SYMMETRY %s' %ccp4_symbol( cryst.get_space_group().info(), lib_name='syminfo.lib', require_at_least_one_lib=False).replace(' ', '') print >> f, 'UB %9.6f %9.6f %9.6f' %UB_mosflm[:3] print >> f, ' %9.6f %9.6f %9.6f' %UB_mosflm[3:6] print >> f, ' %9.6f %9.6f %9.6f' %UB_mosflm[6:] print >> f, 'CELL %8.2f %8.2f %8.2f %6.2f %6.2f %6.2f' %uc_params print >> f, 'RASTER 13 13 7 3 4' print >> f, 'SEPARATION 2.960 2.960' print >> f, 'BEAM %8.3f %8.3f' %beam_centre print >> f, '# end of parameter file for BEST'
def run(args=None): from dials.util.options import OptionParser, flatten_experiments usage = "dials.goniometer_calibration [options] models.expt" parser = OptionParser( usage=usage, phil=phil_scope, read_experiments=True, check_format=False, epilog=help_message, ) params, options = parser.parse_args(args, show_diff_phil=True) if not params.use_space_group_from_experiments and params.space_group is None: parser.print_help() return experiments = flatten_experiments(params.input.experiments) if len(experiments) <= 1: parser.print_help() return from dials.algorithms.indexing.compare_orientation_matrices import ( difference_rotation_matrix_axis_angle, ) for experiment in experiments: crystal = experiment.crystal gonio = experiment.goniometer assert len(experiments) == (len(gonio.get_axes()) + 1) scan = experiment.scan fixed_rotation = matrix.sqr(gonio.get_fixed_rotation()) setting_rotation = matrix.sqr(gonio.get_setting_rotation()) rotation_axis = matrix.col(gonio.get_rotation_axis_datum()) rotation_matrix = rotation_axis.axis_and_angle_as_r3_rotation_matrix( scan.get_oscillation()[0], deg=True) U = matrix.sqr(crystal.get_U()) U = setting_rotation * rotation_matrix * fixed_rotation * U crystal.set_U(U) if params.space_group is not None: crystal.set_space_group(params.space_group.group()) rows = [] from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R_to_mosflm = align_reference_frame( experiments[0].beam.get_s0(), (1.0, 0.0, 0.0), experiments[0].goniometer.get_rotation_axis(), (0.0, 0.0, 1.0), ) axes = [] angles = [] for i in range(len(experiments) - 1): target_angle = experiments[i + 1].goniometer.get_angles()[i] if i == experiments[i].goniometer.get_scan_axis(): # rotation axis is canonical in our coordinate system axis = experiments[i].goniometer.get_axes()[i] angle = target_angle else: R_ij, axis, angle, cb_op = difference_rotation_matrix_axis_angle( experiments[i].crystal, experiments[i + 1].crystal, target_angle=target_angle, ) gonio = experiments[i + 1].goniometer axis_names = gonio.get_names() axes.append(axis) angles.append(angle) depends_on = "." if i + 1 < len(axis_names): depends_on = axis_names[i + 1] rows.insert( 0, ( axis_names[i], "rotation", "goniometer", depends_on, f"{axis[0]:.4f}", f"{axis[1]:.4f}", f"{axis[2]:.4f}", ".", ".", ".", ), ) axis_names = experiments[0].goniometer.get_names() print("Goniometer axes and angles (ImgCIF coordinate system):") for axis, angle, name in zip(axes, angles, axis_names): print( f"{name}: ", f"rotation of {angle:.3f} degrees", "about axis (%.5f,%.5f,%.5f)" % axis, ) print() print("Goniometer axes and angles (MOSFLM coordinate system):") for axis, angle, name in zip(axes, angles, axis_names): print( f"{name}: ", f"rotation of {angle:.3f} degrees", "about axis (%.5f,%.5f,%.5f)" % (R_to_mosflm * matrix.col(axis)).elems, ) print() print("ImgCIF _axis loop template:") from iotbx import cif loop = cif.model.loop(header=[ "_axis.id", "_axis.type", "_axis.equipment", "_axis.depends_on", "_axis.vector[1]", "_axis.vector[2]", "_axis.vector[3]", "_axis.offset[1]", "_axis.offset[2]", "_axis.offset[3]", ]) for row in rows: loop.add_row(row) print(loop) if params.output.xoalign is not None: axes_mosflm = [(R_to_mosflm * matrix.col(axis)).elems for axis in axes] write_xoalign_config(params.output.xoalign, reversed(axes_mosflm), reversed(axis_names))
def __init__(self, experiment, vectors, frame="reciprocal", mode="main"): from dials.util import Sorry self.experiment = experiment self.vectors = vectors self.frame = frame self.mode = mode gonio = experiment.goniometer self.s0 = matrix.col(self.experiment.beam.get_s0()) self.rotation_axis = matrix.col(gonio.get_rotation_axis()) from dxtbx.model import MultiAxisGoniometer if not isinstance(gonio, MultiAxisGoniometer): raise Sorry("Only MultiAxisGoniometer models supported") axes = gonio.get_axes() if len(axes) != 3: raise Sorry("Only 3-axis goniometers supported") e1, e2, e3 = (matrix.col(e) for e in reversed(axes)) # fixed_rotation = matrix.sqr(gonio.get_fixed_rotation()) # setting_rotation = matrix.sqr(gonio.get_setting_rotation()) # rotation_axis = matrix.col(gonio.get_rotation_axis_datum()) # rotation_matrix = rotation_axis.axis_and_angle_as_r3_rotation_matrix( # experiment.scan.get_oscillation()[0], deg=True # ) from dials.algorithms.refinement import rotation_decomposition results = [] # from https://github.com/legrandp/xdsme/blob/master/XOalign/XOalign.py#L427 # referential_permutations sign permutations for four permutations of # parallel/antiparallel (rotation axis & beam) # y1 // e1, y2 // beamVector; y1 anti// e1, y2 // beamVector # y1 // e1, y2 anti// beamVector; y1 anti// e1, y2 anti// beamVector ex = matrix.col((1, 0, 0)) ey = matrix.col((0, 1, 0)) ez = matrix.col((0, 0, 1)) referential_permutations = ( [ex, ey, ez], [-ex, -ey, ez], [ex, -ey, -ez], [-ex, ey, -ez], ) for (v1_, v2_) in self.vectors: result_dictionary = collections.OrderedDict() results.append((v1_, v2_, result_dictionary)) space_group = self.experiment.crystal.get_space_group() for smx in list(space_group.smx())[:]: result_dictionary[smx] = [] crystal = copy.deepcopy(self.experiment.crystal) cb_op = sgtbx.change_of_basis_op(smx) crystal = crystal.change_basis(cb_op) # Goniometer datum setting [D] at which the orientation was determined # D = (setting_rotation * rotation_matrix * fixed_rotation).inverse() # The setting matrix [U] will vary with the datum setting according to # [U] = [D] [U0] U = matrix.sqr(crystal.get_U()) # XXX In DIALS recorded U is equivalent to U0 - D is applied to U inside # prediction U0 = U B = matrix.sqr(crystal.get_B()) if self.frame == "direct": B = B.inverse().transpose() v1_0 = U0 * B * v1_ v2_0 = U0 * B * v2_ # c (b) The laboratory frame vectors l1 & l2 are normally specified with the # c MODE command: MODE MAIN (the default) sets l1 (along which v1 will be # c placed) along the principle goniostat axis e1 (Omega), and l2 along # c the beam s0. This allows rotation for instance around a principle axis. # c The other mode is MODE CUSP, which puts l1 (v1) perpendicular to the # c beam (s0) and the e1 (Omega) axis, and l2 (v2) in the plane containing # c l1 & e1 (ie l1 = e1 x s0, l2 = e1). if self.mode == "cusp": l1 = self.rotation_axis.cross(self.s0) l2 = self.rotation_axis else: l1 = self.rotation_axis.normalize() l3 = l1.cross(self.s0).normalize() l2 = l1.cross(l3) for perm in referential_permutations: S = matrix.sqr(perm[0].elems + perm[1].elems + perm[2].elems) from rstbx.cftbx.coordinate_frame_helpers import ( align_reference_frame, ) R = align_reference_frame(v1_0, S * l1, v2_0, S * l2) solutions = rotation_decomposition.solve_r3_rotation_for_angles_given_axes( R, e1, e2, e3, return_both_solutions=True, deg=True) if solutions is None: continue result_dictionary[smx].extend(solutions) self.all_solutions = results self.unique_solutions = collections.OrderedDict() for v1, v2, result in results: for solutions in result.values(): for solution in solutions: k = tuple(round(a, 3) for a in solution[1:]) self.unique_solutions.setdefault(k, []) if all(v1 != z1 or v2 != z2 for z1, z2 in self.unique_solutions[k]): self.unique_solutions[k].append((v1, v2))
def get_goniometer_shadow_masker(self, goniometer=None): from dials.util.masking import GoniometerShadowMaskGenerator from scitbx.array_family import flex import math coords = flex.vec3_double(( (0,0,0), )) alpha = flex.double_range(0, 190, step=10) * math.pi / 180 r = flex.double(alpha.size(), 40) x = flex.double(r.size(), 107.61) y = -r*flex.sin(alpha) z = -r*flex.cos(alpha) coords.extend(flex.vec3_double(x, y, z)) coords.extend(flex.vec3_double(( # fixed (107.49, 7.84, 39.49), (107.39, 15.69, 38.97), (107.27, 23.53, 38.46), (107.16, 31.37, 37.94), (101.76, 33.99, 36.25), (96.37, 36.63, 34.56), (90.98, 39.25, 33.00), (85.58, 41.88, 31.18), (80.89, 47.06, 31.00), (76.55, 51.51, 31.03), (72.90, 55.04, 31.18), (66.86, 60.46, 31.67), (62.10, 64.41, 32.25), ))) alpha = flex.double_range(180, 370, step=10) * math.pi / 180 r = flex.double(alpha.size(), 33) x = (flex.sqrt(flex.pow2(r * flex.sin(alpha)) + 89.02**2) * flex.cos((50 * math.pi/180) - flex.atan(r/89.02 * flex.sin(alpha)))) y = (flex.sqrt(flex.pow2(r * flex.sin(alpha)) + 89.02**2) * flex.sin((50 * math.pi/180) - flex.atan(r/89.02 * flex.sin(alpha)))) z = -r*flex.cos(alpha) coords.extend(flex.vec3_double(x, y, z)) coords.extend(flex.vec3_double(( # fixed (62.10, 64.41, -32.25), (66.86, 60.46, -31.67), (72.90, 55.04, -31.18), (76.55, 51.51, -31.03), (80.89, 47.06, -31.00), (85.58, 41.88, -31.18), (90.98, 39.25, -33.00), (96.37, 36.63, -34.56), (101.76, 33.99, -36.25), (107.16, 31.37, -37.94), (107.27, 23.53, -38.46), (107.39, 15.69, -38.97), (107.49, 7.84, -39.49), (107.61, 0.00, -40.00) ))) # I23 end station coordinate system: # X-axis: positive direction is facing away from the storage ring (from # sample towards goniometer) # Y-axis: positive direction is vertically up # Z-axis: positive direction is in the direction of the beam (from # sample towards detector) # K-axis (kappa): at an angle of +50 degrees from the X-axis # K & phi rotation axes: clockwise rotation is positive (right hand # thumb rule) # Omega-axis: along the X-axis; clockwise rotation is positive # End station x-axis is parallel to ImgCIF x-axis # End station z-axis points in opposite direction to ImgCIF definition # (ImgCIF: The Z-axis is derived from the source axis which goes from # the sample to the source) # Consequently end station y-axis (to complete set following right hand # rule) points in opposite direction to ImgCIF y-axis. # Kappa arm aligned with -y in ImgCIF convention from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame from scitbx import matrix R = align_reference_frame(matrix.col((1,0,0)), matrix.col((1,0,0)), matrix.col((0,1,0)), matrix.col((0,-1,0))) coords = R.elems * coords if goniometer is None: goniometer = self.get_goniometer() return GoniometerShadowMaskGenerator( goniometer, coords, flex.size_t(len(coords), 1))