def _prepare_for_compose(self, reflections, skip_derivatives=False): """Add columns to the reflection table to hold the varying state matrices or vectors for the experimental models, if required. Also prepare the cache for the derivatives of states that are scan-varying""" nref = len(reflections) # set columns if needed if 'u_matrix' not in reflections: reflections['u_matrix'] = flex.mat3_double(nref) if 'b_matrix' not in reflections: reflections['b_matrix'] = flex.mat3_double(nref) if 's0_vector' not in reflections: reflections['s0_vector'] = flex.vec3_double(nref) if 'd_matrix' not in reflections: reflections['d_matrix'] = flex.mat3_double(nref) if 'D_matrix' not in reflections: reflections['D_matrix'] = flex.mat3_double(nref) if 'S_matrix' not in reflections: reflections['S_matrix'] = flex.mat3_double(nref) # Clear the state derivative cache and set the number of reflections needed # to reconstruct the derivative arrays later self._derivative_cache.clear() self._derivative_cache.nref = nref return
def test_scan_varying_results_are_close_to_static_prediction_when_model_is_static( static_test, # noqa: F811, not a redefinition ): """Test that various modes of scan-varying prediction produce results close to static prediction when the supplied models are indeed static""" # Get static predictor results scan = static_test.experiments[0].scan crystal = static_test.experiments[0].crystal beam = static_test.experiments[0].beam goniometer = static_test.experiments[0].goniometer n_scan_points = scan.get_num_images() + 1 static_preds = static_test.predict_new() static_preds.sort("miller_index") # Set up scan-varying predictor from dials.algorithms.spot_prediction import ScanVaryingReflectionPredictor from dials.array_family import flex predict = ScanVaryingReflectionPredictor(static_test.experiments[0]) def compare(refs1, refs2): assert len(refs1) == len(refs2) for r1, r2 in zip(refs1.rows(), refs2.rows()): assert r1["miller_index"] == r2["miller_index"] # differences less than one hundredth of a pixel/image for e1, e2 in zip(r1["xyzcal.px"], r2["xyzcal.px"]): assert e1 == pytest.approx(e2, abs=0.01) # Prediction for UB matrix expressed as array of static UB A = [crystal.get_A() for i in range(n_scan_points)] result1 = predict.for_ub(flex.mat3_double(A)) result1.sort("miller_index") compare(static_preds, result1) # Prediction for UB matrix, s0 vectors and goniometer setting rotation # matrices expressed as arrays of static model states s0 = [beam.get_s0() for i in range(n_scan_points)] S = [goniometer.get_setting_rotation() for i in range(n_scan_points)] result2 = predict.for_varying_models(flex.mat3_double(A), flex.vec3_double(s0), flex.mat3_double(S)) result2.sort("miller_index") compare(static_preds, result2) # First frame only, start and end UB _, _, z = static_preds["xyzcal.px"].parts() static_preds_frame0 = static_preds.select((z >= 0) & (z < 1)) A = crystal.get_A() result3 = predict.for_ub_on_single_image(0, A, A) result3.sort("miller_index") compare(static_preds_frame0, result3) # First frame only, start and end UB, s0 and S s0 = beam.get_s0() S = goniometer.get_setting_rotation() result4 = predict.for_varying_models_on_single_image(0, A, A, s0, s0, S, S) result4.sort("miller_index") compare(static_preds_frame0, result4)
def test_for_reflection_table(data): from dials.algorithms.spot_prediction import ( ScanStaticReflectionPredictor, ScanVaryingReflectionPredictor, ) from dials.array_family import flex predict = ScanStaticReflectionPredictor(data.experiments[0]) preds = predict.for_ub(data.experiments[0].crystal.get_A()) preds["ub_matrix"] = flex.mat3_double(len(preds), data.experiments[0].crystal.get_A()) preds["s0"] = flex.vec3_double(len(preds), data.experiments[0].beam.get_s0()) preds["d_matrix"] = flex.mat3_double(len(preds)) preds["S_matrix"] = flex.mat3_double( len(preds), data.experiments[0].goniometer.get_setting_rotation()) for ipanel, panel in enumerate(data.experiments[0].detector): sel = preds["panel"] == ipanel D = panel.get_d_matrix() preds["d_matrix"].set_selected(sel, D) predict = ScanVaryingReflectionPredictor(data.experiments[0]) old_preds = copy.deepcopy(preds) predict.for_reflection_table(preds, preds["ub_matrix"], preds["s0"], preds["d_matrix"], preds["S_matrix"]) # Because UB, s0, d and S values are the same for all reflections, the new # reflections should be approx equal to those produced by the scan static # predictor old_x, old_y, old_z = old_preds["xyzcal.px"].parts() new_x, new_y, new_z = preds["xyzcal.px"].parts() assert old_x.all_approx_equal(new_x) assert old_y.all_approx_equal(new_y) assert old_z.all_approx_equal(new_z)
def jacobian_callable(self,values): PB = self.get_partiality_array(values) EXP = flex.exp(-2.*values.BFACTOR*self.DSSQ) G_terms = (EXP * PB * self.ICALCVEC) B_terms = (values.G * EXP * PB * self.ICALCVEC)*(-2.*self.DSSQ) P_terms = (values.G * EXP * self.ICALCVEC) thetax = values.thetax; thetay = values.thetay; Rx = matrix.col((1,0,0)).axis_and_angle_as_r3_rotation_matrix(thetax) dRx_dthetax = matrix.col((1,0,0)).axis_and_angle_as_r3_derivative_wrt_angle(thetax) Ry = matrix.col((0,1,0)).axis_and_angle_as_r3_rotation_matrix(thetay) dRy_dthetay = matrix.col((0,1,0)).axis_and_angle_as_r3_derivative_wrt_angle(thetay) ref_ori = matrix.sqr(self.ORI.reciprocal_matrix()) miller_vec = self.MILLER.as_vec3_double() ds1_dthetax = flex.mat3_double(len(self.MILLER),Ry * dRx_dthetax * ref_ori) * miller_vec ds1_dthetay = flex.mat3_double(len(self.MILLER),dRy_dthetay * Rx * ref_ori) * miller_vec s1vec = self.get_s1_array(values) s1lenvec = flex.sqrt(s1vec.dot(s1vec)) dRh_dthetax = s1vec.dot(ds1_dthetax)/s1lenvec dRh_dthetay = s1vec.dot(ds1_dthetay)/s1lenvec rs = values.RS Rh = self.get_Rh_array(values) rs_sq = rs*rs denomin = (2. * Rh * Rh + rs_sq) dPB_dRh = -PB * 4. * Rh / denomin dPB_dthetax = dPB_dRh * dRh_dthetax dPB_dthetay = dPB_dRh * dRh_dthetay Px_terms = P_terms * dPB_dthetax; Py_terms = P_terms * dPB_dthetay dPB_drs = 4 * rs * Rh * Rh / (denomin * denomin) Prs_terms = P_terms * dPB_drs return [G_terms,B_terms,Prs_terms,Px_terms,Py_terms]
def jacobian_callable(self,values): PB = self.get_partiality_array(values) EXP = flex.exp(-2.*values.BFACTOR*self.DSSQ) G_terms = (EXP * PB * self.ICALCVEC) B_terms = (values.G * EXP * PB * self.ICALCVEC)*(-2.*self.DSSQ) P_terms = (values.G * EXP * self.ICALCVEC) thetax = values.thetax; thetay = values.thetay; Rx = matrix.col((1,0,0)).axis_and_angle_as_r3_rotation_matrix(thetax) dRx_dthetax = matrix.col((1,0,0)).axis_and_angle_as_r3_derivative_wrt_angle(thetax) Ry = matrix.col((0,1,0)).axis_and_angle_as_r3_rotation_matrix(thetay) dRy_dthetay = matrix.col((0,1,0)).axis_and_angle_as_r3_derivative_wrt_angle(thetay) ref_ori = matrix.sqr(self.ORI.reciprocal_matrix()) miller_vec = self.MILLER.as_vec3_double() ds1_dthetax = flex.mat3_double(len(self.MILLER),Ry * dRx_dthetax * ref_ori) * miller_vec ds1_dthetay = flex.mat3_double(len(self.MILLER),dRy_dthetay * Rx * ref_ori) * miller_vec s1vec = self.get_s1_array(values) s1lenvec = flex.sqrt(s1vec.dot(s1vec)) dRh_dthetax = s1vec.dot(ds1_dthetax)/s1lenvec dRh_dthetay = s1vec.dot(ds1_dthetay)/s1lenvec rs = values.RS Rh = self.get_Rh_array(values) rs_sq = rs*rs denomin = (2. * Rh * Rh + rs_sq) dPB_dRh = { "lorentzian": -PB * 4. * Rh / denomin, "gaussian": -PB * 4. * math.log(2) * Rh / rs_sq }[self.profile_shape] dPB_dthetax = dPB_dRh * dRh_dthetax dPB_dthetay = dPB_dRh * dRh_dthetay Px_terms = P_terms * dPB_dthetax; Py_terms = P_terms * dPB_dthetay return [G_terms,B_terms,0,Px_terms,Py_terms]
def merging_crystal_table(postrefinement_columns=False): table = flex.reflection_table() table['u_matrix'] = flex.mat3_double() table['b_matrix'] = flex.mat3_double() table['wavelength'] = flex.double() if postrefinement_columns: table['G'] = flex.double() table['B'] = flex.double() table['RS'] = flex.double() table['thetax'] = flex.double() table['thetay'] = flex.double() table['ETA'] = flex.double() table['DEFF'] = flex.double() table['n_refl'] = flex.size_t() return table
def get_gradients(self, reflections, callback=None): """ Calculate gradients of the prediction formula with respect to each of the parameters of the contained models, for all of the reflections. To be implemented by a derived class, which determines the space of the prediction formula (e.g. we calculate dX/dp, dY/dp, dphi/dp for the prediction formula for a rotation scan expressed in detector space, but components of d\vec{r}/dp for the prediction formula in reciprocal space """ ### Calculate various quantities of interest for the reflections # Set up arrays of values for each reflection n = len(reflections) D = flex.mat3_double(n) s0 = flex.vec3_double(n) U = flex.mat3_double(n) B = flex.mat3_double(n) axis = flex.vec3_double(n) fixed_rotation = flex.mat3_double(n) for iexp, exp in enumerate(self._experiments): sel = reflections['id'] == iexp isel = sel.iselection() # D matrix array panels = reflections['panel'].select(isel) for ipanel, D_mat in enumerate([p.get_D_matrix() for p in exp.detector]): subsel = isel.select(panels == ipanel) D.set_selected(subsel, D_mat) # s0 array s0.set_selected(isel, exp.beam.get_s0()) # U and B arrays exp_U, exp_B = self._get_U_B_for_experiment(exp.crystal, reflections, isel) U.set_selected(isel, exp_U) B.set_selected(isel, exp_B) # axis array if exp.goniometer: axis.set_selected(isel, exp.goniometer.get_rotation_axis()) fixed_rotation.set_selected(isel, exp.goniometer.get_fixed_rotation()) return self._get_gradients_core(reflections, D, s0, U, B, axis, fixed_rotation, callback)
def get_Rh_array(self, values): eff_Astar = self.get_eff_Astar(values) h = self.MILLER.as_vec3_double() x = flex.mat3_double(len(self.MILLER), eff_Astar) * h Svec = x + self.BEAM Rh = Svec.norms() - (1. / self.WAVE) return Rh
def _detector_derivatives(self, isel, panel_id, parameterisation=None, dd_ddet_p=None, reflections=None): """Helper function to convert derivatives of the detector state to derivatives of the vector pv. Derivatives that would all be null vectors are replaced with None""" # Get required data pv = self._pv.select(isel) D = self._D.select(isel) if dd_ddet_p is None: # get the derivatives of detector d matrix for this panel dd_ddet_p = parameterisation.get_ds_dp(multi_state_elt=panel_id, use_none_as_null=True) # replace explicit null derivatives with None dd_ddet_p = [None if e is None else \ flex.mat3_double(len(D), e.elems) for e in dd_ddet_p] # calculate the derivative of pv for this parameter dpv_ddet_p = [ der if der is None else (D * (der * -1.)) * pv for der in dd_ddet_p ] return dpv_ddet_p
def _xl_orientation_derivatives(self, xlop, axis, fixed_rotation, phi_calc, h, s1, e_X_r, e_r_s0, B, D): """helper function to extend the derivatives lists by derivatives of the crystal orientation parameterisations""" # get derivatives of the U matrix wrt the parameters dU_dxlo_p = xlop.get_ds_dp() dphi_dp = [] dpv_dp = [] # loop through the parameters for der in dU_dxlo_p: der_mat = flex.mat3_double(len(B), der.elems) # calculate the derivative of r for this parameter # FIXME COULD DO THIS BETTER WITH __rmul__?! tmp = fixed_rotation * (der_mat * B * h) dr = tmp.rotate_around_origin(axis, phi_calc) # calculate the derivative of phi for this parameter dphi = -1.0 * dr.dot(s1) / e_r_s0 dphi_dp.append(dphi) # calculate the derivative of pv for this parameter dpv_dp.append(D * (dr + e_X_r * dphi)) return dpv_dp, dphi_dp
def _xl_unit_cell_derivatives(self, xlucp, axis, fixed_rotation, phi_calc, h, s1, e_X_r, e_r_s0, U, D): """helper function to extend the derivatives lists by derivatives of the crystal unit cell parameterisations""" # get derivatives of the B matrix wrt the parameters dB_dxluc_p = xlucp.get_ds_dp() dphi_dp = [] dpv_dp = [] # loop through the parameters for der in dB_dxluc_p: der_mat = flex.mat3_double(len(U), der.elems) # calculate the derivative of r for this parameter tmp = fixed_rotation * (U * der_mat * h) dr = tmp.rotate_around_origin(axis, phi_calc) # calculate the derivative of phi for this parameter dphi = -1.0 * dr.dot(s1) / e_r_s0 dphi_dp.append(dphi) # calculate the derivative of pv for this parameter dpv_dp.append(D * (dr + e_X_r * dphi)) return dpv_dp, dphi_dp
def predict_reflections(self, experiment, dmin=None, dmax=None, **kwargs): """Predict the reflections.""" from dials.algorithms.spot_prediction import ScanVaryingReflectionPredictor from dials.array_family import flex # Create the scan varying reflection predictor predictor = ScanVaryingReflectionPredictor(experiment, dmin=dmin, margin=1) # Get length of scan scan_len = experiment.scan.get_num_images() crystal = experiment.crystal # Get the A matrices if crystal.num_scan_points == 0: A = [crystal.get_A() for i in range(scan_len + 1)] else: assert (crystal.num_scan_points) == scan_len + 1 A = [crystal.get_A_at_scan_point(i) for i in range(scan_len + 1)] # Do the prediction result = predictor.for_ub(flex.mat3_double(A)) if dmax is not None: assert dmax > 0 result.compute_d_single(experiment) mask = result["d"] > dmax result.del_selected(mask) # Return the reflections return result
def _xl_unit_cell_derivatives(self, isel, parameterisation=None, reflections=None): # Get required data h = self._h.select(isel) B = self._B.select(isel) wl = self._wavelength.select(isel) # get derivatives of the B matrix wrt the parameters dB_dxluc_p = [None if der is None else flex.mat3_double(len(isel), der.elems) \ for der in parameterisation.get_ds_dp(use_none_as_null=True)] d2theta_dp = [] # loop through the parameters for der in dB_dxluc_p: if der is None: d2theta_dp.append(None) continue r0 = B * h dr0 = der * h r0len = r0.norms() dr0len = dr0.dot(r0) / r0len # 2theta = 2 * arcsin( |r0| / (2 * |s0| ) ) sintheta = 0.5 * r0len * wl fac = 1.0 / flex.sqrt(flex.double(len(wl), 1.0) - sintheta**2) val = fac * wl * dr0len d2theta_dp.append(val) return d2theta_dp
def _xl_unit_cell_derivatives(self, isel, parameterisation=None, reflections=None): # Get required data h = self._h.select(isel) B = self._B.select(isel) wl = self._wavelength.select(isel) # get derivatives of the B matrix wrt the parameters dB_dxluc_p = [ None if der is None else flex.mat3_double(len(isel), der.elems) for der in parameterisation.get_ds_dp(use_none_as_null=True) ] d2theta_dp = [] # loop through the parameters for der in dB_dxluc_p: if der is None: d2theta_dp.append(None) continue r0 = B * h dr0 = der * h r0len = r0.norms() dr0len = dr0.dot(r0) / r0len # 2theta = 2 * arcsin( |r0| / (2 * |s0| ) ) sintheta = 0.5 * r0len * wl fac = 1.0 / flex.sqrt(flex.double(len(wl), 1.0) - flex.pow2(sintheta)) val = fac * wl * dr0len d2theta_dp.append(val) return d2theta_dp
def run(self, experiments, reflections): result = flex.reflection_table() for expt_id, experiment in enumerate(experiments): refls = reflections.select(reflections['id'] == expt_id) A = flex.mat3_double(len(refls), experiment.crystal.get_A()) s0 = flex.vec3_double(len(refls), experiment.beam.get_s0()) q = A * refls['miller_index'].as_vec3_double() rh = (q + s0).norms() - 1/experiment.beam.get_wavelength() eta = 2*math.pi/180 * experiment.crystal.get_half_mosaicity_deg() rs = (1/experiment.crystal.get_domain_size_ang()) + (eta/2/refls['d']) p_G = flex.exp(-2*ln2*rh**2/rs**2) dp_G_drs = p_G * 4 * ln2 * rh**2 / rs**2 refls['rh'] = rh refls['rs'] = rs refls['p_G'] = p_G refls['dp_G_drs'] = dp_G_drs result.extend(refls) return experiments, result
def _get_model_data_for_experiment(self, experiment, reflections): """Helper function to return model data s0, U, B, D and S for a particular experiment. D is always returned as an array the same length as the reflections for the experiment, whereas here s0, U, B and S are returned as single matrices or vectors. In the scan-varying overload these will all be arrays.""" # D matrix array D = flex.mat3_double(len(reflections)) panels = reflections["panel"] for ipanel, D_mat in enumerate( [p.get_D_matrix() for p in experiment.detector]): sel = panels == ipanel D.set_selected(sel, D_mat) result = { "s0": experiment.beam.get_s0(), "U": matrix.sqr(experiment.crystal.get_U()), "B": matrix.sqr(experiment.crystal.get_B()), "D": D, } # If a goniometer is present, get the setting matrix too if experiment.goniometer: result["S"] = matrix.sqr( experiment.goniometer.get_setting_rotation()) return result
def mosaic_blocks(mos_spread_deg, mos_domains, twister_seed=None, random_seed=None): """ Code from LS49 for adjusting mosaicity of simulation :param mos_spread_deg: spread in degrees :param mos_domains: number of mosaic domains :param twister_seed: default from ls49 code :param random_seed: default from ls49 code :return: """ UMAT_nm = flex.mat3_double() if twister_seed is None: twister_seed = 777 if random_seed is None: random_seed = 777 mersenne_twister = flex.mersenne_twister(seed=twister_seed) scitbx.random.set_random_seed(random_seed) rand_norm = scitbx.random.normal_distribution(mean=0, sigma=mos_spread_deg * np.pi / 180.) g = scitbx.random.variate(rand_norm) mosaic_rotation = g(mos_domains) for m in mosaic_rotation: site = col(mersenne_twister.random_double_point_on_sphere()) UMAT_nm.append(site.axis_and_angle_as_r3_rotation_matrix(m, deg=False)) UMAT_nm.append(site.axis_and_angle_as_r3_rotation_matrix( -m, deg=False)) # NOTE: make symmetric dist return UMAT_nm
def test_auto_reduction_parameter_extension_modules_part2(setup_test_sorting): # Cut-down original algorithm for AutoReduce._unit_cell_surplus_reflections from dials_refinement_helpers_ext import uc_surpl_iter as uc_surpl r, r_sorted, exp_ids = setup_test_sorting isel = flex.size_t() for exp_id in exp_ids: isel.extend((r["id"] == exp_id).iselection()) ref = r.select(isel) h = ref["miller_index"].as_vec3_double() dB_dp = flex.mat3_double([(1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 0, 1, 0, 1, 0, 1, 0)]) nref_each_param = [] for der in dB_dp: tst = (der * h).norms() nref_each_param.append((tst > 0.0).count(True)) res0 = min(nref_each_param) # Updated algorithm for _unit_cell_surplus_reflections res1_unsrt_int = uc_surpl(r["id"], r["miller_index"], exp_ids, dB_dp).result res1_int = uc_surpl(r_sorted["id"], r_sorted["miller_index"], exp_ids, dB_dp).result res1_sizet = uc_surpl(flex.size_t(list(r_sorted["id"])), r_sorted["miller_index"], exp_ids, dB_dp).result assert res0 != res1_unsrt_int assert res0 == res1_int assert res0 == res1_sizet
def get_beam_gradients(self, reflections): ds0_dbeam_p = self.beam_parameterisation.get_ds_dp() p_names = self.beam_parameterisation.get_param_names() n = len(reflections) U = flex.mat3_double(n, self.U) B = flex.mat3_double(n, self.B) UB = U * B # q is the reciprocal lattice vector, in the lab frame h = reflections['miller_index'].as_vec3_double() q = (UB * h) qlen = q.norms() qlen2 = q.dot(q) q_s0 = q + self.s0 s1 = reflections['s1'] ss = qlen2 + 2 * q.dot(self.s0) + self.s0len2 assert (ss > 0.0).all_eq(True) s = flex.sqrt(ss) sss = s * ss inv_s = 1.0 / s inv_sss = 1.0 / sss # check equation 10 tmp = self.s0len * (q_s0) / s for a, b in zip(s1, tmp): assert a == pytest.approx(b, abs=1e-7) ds1_dp = {} # loop through the parameters for name, der in zip(p_names, ds0_dbeam_p): # term1 term1 = self.us0.dot(der) * q_s0 + self.s0len * (der) term1 = term1 * inv_s # term2 term2 = self.s0len * q_s0 * q_s0.dot(der) term2 = term2 * inv_sss name = 'Beam1' + name # XXXX Hack to get matching keys ds1_dp[name] = {'ds1': (term1 - term2)} return ds1_dp
def get_beam_gradients(self, reflections): ds0_dbeam_p = self.beam_parameterisation.get_ds_dp() p_names = self.beam_parameterisation.get_param_names() n = len(reflections) U = flex.mat3_double(n, self.U) B = flex.mat3_double(n, self.B) UB = U*B # q is the reciprocal lattice vector, in the lab frame h = reflections['miller_index'].as_vec3_double() q = (UB * h) qlen = q.norms() qlen2 = q.dot(q) q_s0 = q + self.s0 s1 = reflections['s1'] ss = qlen2 + 2 * q.dot(self.s0) + self.s0len2 assert (ss > 0.0).all_eq(True) s = flex.sqrt(ss) sss = s * ss inv_s = 1.0 / s inv_sss = 1.0 / sss # check equation 10 tmp = self.s0len * (q_s0) / s for a, b in zip(s1, tmp): assert approx_equal(a, b) ds1_dp = {} # loop through the parameters for name, der in zip(p_names, ds0_dbeam_p): # term1 term1 = self.us0.dot(der) * q_s0 + self.s0len * (der) term1 = term1 * inv_s # term2 term2 = self.s0len * q_s0 * q_s0.dot(der) term2 = term2 * inv_sss name = 'Beam1' + name # XXXX Hack to get matching keys ds1_dp[name] = {'ds1':(term1 - term2)} return ds1_dp
def get_crystal_unit_cell_gradients(self, reflections): # get derivatives of the B matrix wrt the parameters dB_dxluc_p = self.xl_unit_cell_parameterisation.get_ds_dp() p_names = self.xl_unit_cell_parameterisation.get_param_names() n = len(reflections) U = flex.mat3_double(n, self.U) B = flex.mat3_double(n, self.B) UB = U * B # q is the reciprocal lattice vector, in the lab frame h = reflections['miller_index'].as_vec3_double() q = (UB * h) qlen = q.norms() qlen2 = q.dot(q) q_s0 = q + self.s0 s1 = reflections['s1'] ss = qlen2 + 2 * q.dot(self.s0) + self.s0len2 assert (ss > 0.0).all_eq(True) s = flex.sqrt(ss) sss = s * ss inv_s = 1.0 / s inv_sss = 1.0 / sss ds1_dp = {} # loop through the parameters for name, der in zip(p_names, dB_dxluc_p): # calculate the derivative of q for this parameter dq = U * flex.mat3_double(n, der.elems) * h # term1 term1 = self.s0len * dq term1 = term1 * inv_s # term2 term2 = self.s0len * q_s0 * q_s0.dot(dq) term2 = term2 * inv_sss name = 'Crystal1' + name # XXXX Hack to get matching keys ds1_dp[name] = {'ds1': (term1 - term2)} return ds1_dp
def get_s1_array(self, values): miller_vec = self.MILLER.as_vec3_double() ref_ori = matrix.sqr(self.ORI.reciprocal_matrix()) Rx = matrix.col((1,0,0)).axis_and_angle_as_r3_rotation_matrix(values.thetax) Ry = matrix.col((0,1,0)).axis_and_angle_as_r3_rotation_matrix(values.thetay) s_array = flex.mat3_double(len(self.MILLER),Ry * Rx * ref_ori) * miller_vec s1_array = s_array + flex.vec3_double(len(self.MILLER), self.BEAM) return s1_array
def get_crystal_unit_cell_gradients(self, reflections): # get derivatives of the B matrix wrt the parameters dB_dxluc_p = self.xl_unit_cell_parameterisation.get_ds_dp() p_names = self.xl_unit_cell_parameterisation.get_param_names() n = len(reflections) U = flex.mat3_double(n, self.U) B = flex.mat3_double(n, self.B) UB = U*B # q is the reciprocal lattice vector, in the lab frame h = reflections['miller_index'].as_vec3_double() q = (UB * h) qlen = q.norms() qlen2 = q.dot(q) q_s0 = q + self.s0 s1 = reflections['s1'] ss = qlen2 + 2 * q.dot(self.s0) + self.s0len2 assert (ss > 0.0).all_eq(True) s = flex.sqrt(ss) sss = s * ss inv_s = 1.0 / s inv_sss = 1.0 / sss ds1_dp = {} # loop through the parameters for name, der in zip(p_names, dB_dxluc_p): # calculate the derivative of q for this parameter dq = U * flex.mat3_double(n, der.elems) * h # term1 term1 = self.s0len * dq term1 = term1 * inv_s # term2 term2 = self.s0len * q_s0 * q_s0.dot(dq) term2 = term2 * inv_sss name = 'Crystal1' + name # XXXX Hack to get matching keys ds1_dp[name] = {'ds1':(term1 - term2)} return ds1_dp
def _xl_unit_cell_derivatives(self, isel, parameterisation=None, reflections=None): """helper function to extend the derivatives lists by derivatives of the crystal unit cell parameterisations""" # Get required data U = self._U.select(isel) h = self._h.select(isel) e1 = self._e1.select(isel) DeltaPsi = self._DeltaPsi.select(isel) s1 = self._s1.select(isel) nu = self._nu.select(isel) q_s0 = self._q_s0.select(isel) inv_s = self._inv_s.select(isel) inv_sss = self._inv_sss.select(isel) r = self._r.select(isel) s0 = self._s0.select(isel) D = self._D.select(isel) # get derivatives of the B matrix wrt the parameters dB_dxluc_p = parameterisation.get_ds_dp(use_none_as_null=True) dDeltaPsi_dp = [] dpv_dp = [] # loop through the parameters for der in dB_dxluc_p: if der is None: dpv_dp.append(None) dDeltaPsi_dp.append(None) continue der_mat = flex.mat3_double(len(U), der.elems) # calculate the derivative of q for this parameter dq = U * der_mat * h # calculate the derivative of r for this parameter dr = dq.rotate_around_origin(e1, DeltaPsi) # calculate the derivative of DeltaPsi for this parameter dDeltaPsi = -1.0 * (dr.dot(s1)) / (e1.cross(r).dot(s0)) dDeltaPsi_dp.append(dDeltaPsi) # term 1 term1 = (nu * dq) * inv_s # term 2 term2 = (nu * q_s0 * q_s0.dot(dq)) * inv_sss # calculate the derivative of pv for this parameter dpv = D * (term1 - term2) dpv_dp.append(dpv) return dpv_dp, dDeltaPsi_dp
def rotate_mat3_double(R, A): """ Helper function to rotate a flex.mat3_double array of matrices """ accessor = A.accessor() RAR = flex.mat3_double([R * matrix.sqr(a) * R.transpose() for a in A]) RAR.reshape(accessor) return RAR
def _xl_derivatives(self, isel, derivatives, b_matrix, parameterisation=None): """helper function to extend the derivatives lists by derivatives of generic parameterisations.""" # Get required data axis = self._axis.select(isel) fixed_rotation = self._fixed_rotation.select(isel) setting_rotation = self._setting_rotation.select(isel) phi_calc = self._phi_calc.select(isel) h = self._h.select(isel) s1 = self._s1.select(isel) e_X_r = self._e_X_r.select(isel) e_r_s0 = self._e_r_s0.select(isel) if b_matrix: B = self._B.select(isel) else: U = self._U.select(isel) D = self._D.select(isel) if derivatives is None: # get derivatives of the B/U matrix wrt the parameters derivatives = [ None if der is None else flex.mat3_double( len(isel), der.elems) for der in parameterisation.get_ds_dp(use_none_as_null=True) ] dphi_dp = [] dpv_dp = [] # loop through the parameters for der in derivatives: if der is None: dphi_dp.append(None) dpv_dp.append(None) continue # calculate the derivative of r for this parameter if b_matrix: tmp = fixed_rotation * (der * B * h) else: tmp = fixed_rotation * (U * der * h) dr = setting_rotation * tmp.rotate_around_origin(axis, phi_calc) # calculate the derivative of phi for this parameter dphi = -1.0 * dr.dot(s1) / e_r_s0 dphi_dp.append(dphi) # calculate the derivative of pv for this parameter dpv_dp.append(D * (dr + e_X_r * dphi)) return dpv_dp, dphi_dp
def first_derivatives(self): """ Compute the first derivatives of Sigma w.r.t the parameters """ (b1, ) = self.params dSdb1 = (2 * b1, 0, 0, 0, 2 * b1, 0, 0, 0, 2 * b1) return flex.mat3_double([dSdb1])
def first_derivatives(self): """ Compute the first derivatives of Sigma w.r.t the parameters """ l1 = self.params[0] dSdl1 = (0, 0, 0, 0, 0, 0, 0, 0, 2 * l1) return flex.mat3_double([dSdl1])
def first_derivatives(self): """ Compute the first derivatives of Sigma w.r.t the parameters """ b1, b2 = self.params d1 = (2 * b1, 0, 0, 0, 2 * b1, 0, 0, 0, 0) d2 = (0, 0, 0, 0, 0, 0, 0, 0, 2 * b2) return flex.mat3_double([d1, d2])
def _xl_orientation_derivatives(self, isel, parameterisation=None, dU_dxlo_p=None, reflections=None): """helper function to extend the derivatives lists by derivatives of the crystal orientation parameterisations""" # Get required data axis = self._axis.select(isel) fixed_rotation = self._fixed_rotation.select(isel) setting_rotation = self._setting_rotation.select(isel) phi_calc = self._phi_calc.select(isel) h = self._h.select(isel) s1 = self._s1.select(isel) e_X_r = self._e_X_r.select(isel) e_r_s0 = self._e_r_s0.select(isel) B = self._B.select(isel) D = self._D.select(isel) if dU_dxlo_p is None: # get derivatives of the U matrix wrt the parameters dU_dxlo_p = [ None if der is None else flex.mat3_double( len(isel), der.elems) for der in parameterisation.get_ds_dp(use_none_as_null=True) ] dphi_dp = [] dpv_dp = [] # loop through the parameters for der in dU_dxlo_p: if der is None: dphi_dp.append(None) dpv_dp.append(None) continue # calculate the derivative of r for this parameter # FIXME COULD DO THIS BETTER WITH __rmul__?! tmp = fixed_rotation * (der * B * h) dr = setting_rotation * tmp.rotate_around_origin(axis, phi_calc) # calculate the derivative of phi for this parameter dphi = -1.0 * dr.dot(s1) / e_r_s0 dphi_dp.append(dphi) # calculate the derivative of pv for this parameter dpv_dp.append(D * (dr + e_X_r * dphi)) return dpv_dp, dphi_dp
def _predict_one_experiment(self, experiment, reflections): B = flex.mat3_double(len(reflections), experiment.crystal.get_B()) r0 = B * reflections["miller_index"].as_vec3_double() r0len = r0.norms() wl = experiment.beam.get_wavelength() # 2theta = 2 * arcsin( |r0| / (2 * |s0| ) ) reflections["2theta_cal.rad"] = 2.0 * flex.asin(0.5 * r0len * wl) reflections.set_flags(flex.size_t(len(reflections)), reflections.flags.predicted)
def second_derivatives(self): """ Compute the second derivatives of Sigma w.r.t the parameters """ l1 = self.params[0] d11 = (0, 0, 0, 0, 0, 0, 0, 0, 2) d2 = flex.mat3_double([d11]) d2.reshape(flex.grid(1, 1)) return d2
def tst_for_reflection_table(self): from libtbx.test_utils import approx_equal from dials.algorithms.spot_prediction import \ ScanVaryingReflectionPredictor, ScanStaticReflectionPredictor from dials.array_family import flex predict = ScanStaticReflectionPredictor(self.experiments[0]) preds = predict.for_ub(self.experiments[0].crystal.get_A()) preds['ub_matrix'] = flex.mat3_double(len(preds), self.experiments[0].crystal.get_A()) preds['s0'] = flex.vec3_double(len(preds), self.experiments[0].beam.get_s0()) preds['d_matrix'] = flex.mat3_double(len(preds)) preds['S_matrix'] = flex.mat3_double(len(preds), self.experiments[0].goniometer.get_setting_rotation()) for ipanel, panel in enumerate(self.experiments[0].detector): sel = preds['panel'] == ipanel D = panel.get_d_matrix() preds['d_matrix'].set_selected(sel, D) predict = ScanVaryingReflectionPredictor(self.experiments[0]) from copy import deepcopy old_preds = deepcopy(preds) predict.for_reflection_table(preds, preds['ub_matrix'], preds['s0'], preds['d_matrix'], preds['S_matrix']) # Because UB, s0, d and S values are the same for all reflections, the new # reflections should be approx equal to those produced by the scan static # predictor old_x, old_y, old_z = old_preds['xyzcal.px'].parts() new_x, new_y, new_z = preds['xyzcal.px'].parts() assert old_x.all_approx_equal(new_x) assert old_y.all_approx_equal(new_y) assert old_z.all_approx_equal(new_z) print "OK" return
def _goniometer_derivatives(self, isel, parameterisation=None, dS_dgon_p=None, reflections=None): """helper function to extend the derivatives lists by derivatives of the goniometer parameterisations""" # Get required data axis = self._axis.select(isel) fixed_rotation = self._fixed_rotation.select(isel) phi_calc = self._phi_calc.select(isel) h = self._h.select(isel) s1 = self._s1.select(isel) e_X_r = self._e_X_r.select(isel) e_r_s0 = self._e_r_s0.select(isel) UB = self._UB.select(isel) D = self._D.select(isel) if dS_dgon_p is None: # get derivatives of the setting matrix S wrt the parameters dS_dgon_p = [ None if der is None else flex.mat3_double( len(isel), der.elems) for der in parameterisation.get_ds_dp(use_none_as_null=True) ] dphi_dp = [] dpv_dp = [] # loop through the parameters for der in dS_dgon_p: if der is None: dphi_dp.append(None) dpv_dp.append(None) continue # calculate the derivative of r for this parameter tmp = fixed_rotation * (UB * h) dr = der * tmp.rotate_around_origin(axis, phi_calc) # calculate the derivative of phi for this parameter dphi = -1.0 * dr.dot(s1) / e_r_s0 dphi_dp.append(dphi) # calculate the derivative of pv for this parameter dpv_dp.append(D * (dr + e_X_r * dphi)) return dpv_dp, dphi_dp
def _prepare_for_compose(self, reflections, skip_derivatives=False): """Add columns to the reflection table to hold the varying state matrices or vectors for the experimental models, if required. Also add columns for the derivatives of states that are scan-varying""" nref = len(reflections) # set columns if needed if 'u_matrix' not in reflections: reflections['u_matrix'] = flex.mat3_double(nref) if 'b_matrix' not in reflections: reflections['b_matrix'] = flex.mat3_double(nref) if 's0_vector' not in reflections: reflections['s0_vector'] = flex.vec3_double(nref) if 'd_matrix' not in reflections: reflections['d_matrix'] = flex.mat3_double(nref) if 'D_matrix' not in reflections: reflections['D_matrix'] = flex.mat3_double(nref) # set columns in the reflection table to store the derivative of state for # each reflection, if needed if not skip_derivatives: null9 = (0., 0., 0., 0., 0., 0., 0., 0., 0.) null3 = (0., 0., 0.) if self._varying_xl_orientations and "dU_dp0" not in reflections: max_free_params = max([ e.num_free() for e in self._xl_orientation_parameterisations ]) for i in range(max_free_params): colname = "dU_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null9) if self._varying_xl_unit_cells and "dB_dp0" not in reflections: max_free_params = max([ e.num_free() for e in self._xl_unit_cell_parameterisations ]) for i in range(max_free_params): colname = "dB_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null9) if self._varying_detectors and "dd_dp0" not in reflections: max_free_params = max( [e.num_free() for e in self._detector_parameterisations]) for i in range(max_free_params): colname = "dd_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null9) if self._varying_beams and "ds0_dp0" not in reflections: max_free_params = max( [e.num_free() for e in self._beam_parameterisations]) for i in range(max_free_params): colname = "ds0_dp{0}".format(i) reflections[colname] = flex.vec3_double(nref, null3) return
def tst_for_reflection_table(self): from libtbx.test_utils import approx_equal from dials.algorithms.spot_prediction import \ ScanVaryingReflectionPredictor, ScanStaticReflectionPredictor from dials.array_family import flex predict = ScanStaticReflectionPredictor(self.experiments[0]) preds = predict.for_ub(self.experiments[0].crystal.get_A()) preds['ub_matrix'] = flex.mat3_double(len(preds), self.experiments[0].crystal.get_A()) preds['s0'] = flex.vec3_double(len(preds), self.experiments[0].beam.get_s0()) preds['d_matrix'] = flex.mat3_double(len(preds)) for ipanel, panel in enumerate(self.experiments[0].detector): sel = preds['panel'] == ipanel D = panel.get_d_matrix() preds['d_matrix'].set_selected(sel, D) predict = ScanVaryingReflectionPredictor(self.experiments[0]) from copy import deepcopy old_preds = deepcopy(preds) predict.for_reflection_table(preds, preds['ub_matrix'], preds['s0'], preds['d_matrix']) # Because UB, s0 and d values are the same for all reflections, the new # reflections should be approx equal to those produced by the scan static # predictor old_x, old_y, old_z = old_preds['xyzcal.px'].parts() new_x, new_y, new_z = preds['xyzcal.px'].parts() assert old_x.all_approx_equal(new_x) assert old_y.all_approx_equal(new_y) assert old_z.all_approx_equal(new_z) print "OK" return
def first_derivatives(self): """ Compute the first derivatives of Sigma w.r.t the parameters """ b1, b2, b3, b4 = self.params d1 = (2 * b1, b2, 0, b2, 0, 0, 0, 0, 0) d2 = (0, b1, 0, b1, 2 * b2, 0, 0, 0, 0) d3 = (0, 0, 0, 0, 2 * b3, 0, 0, 0, 0) d4 = (0, 0, 0, 0, 0, 0, 0, 0, 2 * b4) return flex.mat3_double([d1, d2, d3, d4])
def _prepare_for_compose(self, reflections): nref = len(reflections) # set columns for U and B if needed if not reflections.has_key('u_matrix'): reflections['u_matrix'] = flex.mat3_double(nref) if not reflections.has_key('b_matrix'): reflections['b_matrix'] = flex.mat3_double(nref) # set columns in the reflection table to store the derivative of state for # each reflection, if needed null = (0., 0., 0., 0., 0., 0., 0., 0., 0.) if self._xl_orientation_parameterisations and not reflections.has_key("dU_dp0"): max_free_U_params = max([e.num_free() for e in self._xl_orientation_parameterisations]) for i in range(max_free_U_params): colname = "dU_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null) if self._xl_unit_cell_parameterisations and not reflections.has_key("dB_dp0"): max_free_B_params = max([e.num_free() for e in self._xl_unit_cell_parameterisations]) for i in range(max_free_B_params): colname = "dB_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null) return
def _predict_new(self, hkl=None, frame=None, panel=None): from dials.algorithms.spot_prediction import ScanVaryingReflectionPredictor from dials.array_family import flex predict = ScanVaryingReflectionPredictor(self.experiments[0]) #if hkl is None: A = [ self.experiments[0].crystal.get_A_at_scan_point(i) for i in range(self.experiments[0].crystal.num_scan_points) ] result = predict.for_ub(flex.mat3_double(A)) #else: #if panel is None: #result = predict(hkl, frame) #else: #result = predict(hkl, frame, panel) return result
def _xl_orientation_derivatives(self, isel, parameterisation=None, dU_dxlo_p=None, reflections=None): """helper function to extend the derivatives lists by derivatives of the crystal orientation parameterisations""" # Get required data axis = self._axis.select(isel) fixed_rotation = self._fixed_rotation.select(isel) setting_rotation = self._setting_rotation.select(isel) phi_calc = self._phi_calc.select(isel) h = self._h.select(isel) s1 = self._s1.select(isel) e_X_r = self._e_X_r.select(isel) e_r_s0 = self._e_r_s0.select(isel) B = self._B.select(isel) D = self._D.select(isel) if dU_dxlo_p is None: # get derivatives of the U matrix wrt the parameters dU_dxlo_p = [None if der is None else flex.mat3_double(len(isel), der.elems) \ for der in parameterisation.get_ds_dp(use_none_as_null=True)] dphi_dp = [] dpv_dp = [] # loop through the parameters for der in dU_dxlo_p: if der is None: dphi_dp.append(None) dpv_dp.append(None) continue # calculate the derivative of r for this parameter # FIXME COULD DO THIS BETTER WITH __rmul__?! tmp = fixed_rotation * (der * B * h) dr = setting_rotation * tmp.rotate_around_origin(axis, phi_calc) # calculate the derivative of phi for this parameter dphi = -1.0 * dr.dot(s1) / e_r_s0 dphi_dp.append(dphi) # calculate the derivative of pv for this parameter dpv_dp.append(D * (dr + e_X_r * dphi)) return dpv_dp, dphi_dp
def predict_new(self, hkl=None, frame=None, panel=None): from dials.algorithms.spot_prediction import ScanVaryingReflectionPredictor from time import time from dials.array_family import flex st = time() predict = ScanVaryingReflectionPredictor(self.experiments[0]) #if hkl is None: A = [self.experiments[0].crystal.get_A_at_scan_point(i) for i in range(self.experiments[0].crystal.num_scan_points)] result = predict.for_ub(flex.mat3_double(A)) #else: #if panel is None: #result = predict(hkl, frame) #else: #result = predict(hkl, frame, panel) #print "New Time: ", time() - st return result
def _get_model_data_for_experiment(self, experiment, reflections): """Helper function to return model data s0, U, B and D for a particular experiment. D is always returned as an array the same length as the reflections for the experiment, whereas here U, B and s0 are returned as single matrices or vectors. In the scan-varying overload these will all be arrays.""" # D matrix array D = flex.mat3_double(len(reflections)) panels = reflections['panel'] for ipanel, D_mat in enumerate([p.get_D_matrix() for p in experiment.detector]): sel = panels == ipanel D.set_selected(sel, D_mat) return {'s0':experiment.beam.get_s0(), 'U':experiment.crystal.get_U(), 'B':experiment.crystal.get_B(), 'D':D}
def _prepare_for_compose(self, reflections, skip_derivatives=False): """Add columns to the reflection table to hold the varying state matrices or vectors for the experimental models, if required. Also add columns for the derivatives of states that are scan-varying""" nref = len(reflections) # set columns if needed if 'u_matrix' not in reflections: reflections['u_matrix'] = flex.mat3_double(nref) if 'b_matrix' not in reflections: reflections['b_matrix'] = flex.mat3_double(nref) if 's0_vector' not in reflections: reflections['s0_vector'] = flex.vec3_double(nref) if 'd_matrix' not in reflections: reflections['d_matrix'] = flex.mat3_double(nref) if 'D_matrix' not in reflections: reflections['D_matrix'] = flex.mat3_double(nref) # set columns in the reflection table to store the derivative of state for # each reflection, if needed if not skip_derivatives: null9 = (0., 0., 0., 0., 0., 0., 0., 0., 0.) null3 = (0., 0., 0.) if self._varying_xl_orientations and "dU_dp0" not in reflections: max_free_params = max([e.num_free() for e in self._xl_orientation_parameterisations]) for i in range(max_free_params): colname = "dU_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null9) if self._varying_xl_unit_cells and "dB_dp0" not in reflections: max_free_params = max([e.num_free() for e in self._xl_unit_cell_parameterisations]) for i in range(max_free_params): colname = "dB_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null9) if self._varying_detectors and "dd_dp0" not in reflections: max_free_params = max([e.num_free() for e in self._detector_parameterisations]) for i in range(max_free_params): colname = "dd_dp{0}".format(i) reflections[colname] = flex.mat3_double(nref, null9) if self._varying_beams and "ds0_dp0" not in reflections: max_free_params = max([e.num_free() for e in self._beam_parameterisations]) for i in range(max_free_params): colname = "ds0_dp{0}".format(i) reflections[colname] = flex.vec3_double(nref, null3) return
def get_gradients(self, reflections, callback=None): """ Calculate gradients of the prediction formula with respect to each of the parameters of the detector, for all of the reflections. """ ### Calculate various quantities of interest for the reflections # Set up arrays of values for each reflection n = len(reflections) D = flex.mat3_double(n) #s0 = flex.vec3_double(n) #U = flex.mat3_double(n) #B = flex.mat3_double(n) #axis = flex.vec3_double(n) for iexp, exp in enumerate(self._experiments): sel = reflections['id'] == iexp isel = sel.iselection() # D matrix array panels = reflections['panel'].select(isel) for ipanel, D_mat in enumerate([p.get_D_matrix() for p in exp.detector]): subsel = isel.select(panels == ipanel) D.set_selected(subsel, D_mat) # s0 array #s0.set_selected(isel, exp.beam.get_s0()) # U and B arrays #exp_U, exp_B = self._get_U_B_for_experiment(exp.crystal, reflections, isel) #U.set_selected(isel, exp_U) #B.set_selected(isel, exp_B) # axis array #if exp.goniometer: # axis.set_selected(isel, exp.goniometer.get_rotation_axis()) return self._get_gradients_core(reflections, D, callback)
def __call__(self, reflections): """Predict 2theta angles for all reflections at the current model geometry""" for iexp, e in enumerate(self._experiments): # select the reflections for this experiment only sel = reflections['id'] == iexp refs = reflections.select(sel) B = flex.mat3_double(len(reflections), e.crystal.get_B()) r0 = B * reflections['miller_index'].as_vec3_double() r0len = r0.norms() wl = e.beam.get_wavelength() # 2theta = 2 * arcsin( |r0| / (2 * |s0| ) ) twotheta = 2.0 * flex.asin(0.5 * r0len * wl) # write predictions back to overall reflections reflections['2theta_cal.rad'].set_selected(sel, twotheta) # set predicted flag reflections.set_flags(sel, reflections.flags.predicted) return reflections
def _detector_derivatives(self, isel, panel_id, parameterisation=None, dd_ddet_p=None, reflections=None): """Helper function to convert derivatives of the detector state to derivatives of the vector pv. Derivatives that would all be null vectors are replaced with None""" # Get required data pv = self._pv.select(isel) D = self._D.select(isel) if dd_ddet_p is None: # get the derivatives of detector d matrix for this panel dd_ddet_p = parameterisation.get_ds_dp(multi_state_elt=panel_id, use_none_as_null=True) # replace explicit null derivatives with None dd_ddet_p = [None if e is None else \ flex.mat3_double(len(D), e.elems) for e in dd_ddet_p] # calculate the derivative of pv for this parameter dpv_ddet_p = [der if der is None else (D * (der * -1.)) * pv for der in dd_ddet_p] return dpv_ddet_p
def _xl_unit_cell_derivatives(self, isel, parameterisation=None, reflections=None): """helper function to extend the derivatives lists by derivatives of the crystal unit cell parameterisations""" # Get required data U = self._U.select(isel) h = self._h.select(isel) e1 = self._e1.select(isel) DeltaPsi = self._DeltaPsi.select(isel) s1 = self._s1.select(isel) q = self._q.select(isel) q_scalar = self._q_scalar.select(isel) qq = self._qq.select(isel) q0 = self._q0.select(isel) r = self._r.select(isel) s0 = self._s0.select(isel) s0u = self._s0u.select(isel) D = self._D.select(isel) # get derivatives of the B matrix wrt the parameters dB_dxluc_p = parameterisation.get_ds_dp(use_none_as_null=True) dDeltaPsi_dp = [] dpv_dp = [] # loop through the parameters for der in dB_dxluc_p: if der is None: dpv_dp.append(None) dDeltaPsi_dp.append(None) continue der_mat = flex.mat3_double(len(U), der.elems) # calculate the derivative of q for this parameter dq = U * der_mat * h # calculate the derivative of r for this parameter dr = dq.rotate_around_origin(e1, DeltaPsi) # calculate the derivative of DeltaPsi for this parameter dDeltaPsi = -1.0 * (dr.dot(s1)) / (e1.cross(r).dot(s0)) dDeltaPsi_dp.append(dDeltaPsi) # derivative of the axis e1 q_dot_dq = q.dot(dq) dq0 = (q_scalar * dq - (q_dot_dq * q0)) / qq de1_dp = dq0.cross(s0u) # calculate (d[r]/d[e1])(d[e1]/dp) dr_de1 = dRq_de(DeltaPsi, e1, q) drde_dedp = dr_de1 * de1_dp # calculate the partial derivative of r wrt change in rotation # axis e1 by finite differences dp = 1.e-8 del_e1 = de1_dp * dp e1f = e1 + del_e1 * 0.5 rfwd = q.rotate_around_origin(e1f , DeltaPsi) e1r = e1 - del_e1 * 0.5 rrev = q.rotate_around_origin(e1r , DeltaPsi) drde_dedp = (rfwd - rrev) * (1 / dp) # calculate the derivative of pv for this parameter dpv = D * (dr + e1.cross(r) * dDeltaPsi + drde_dedp) dpv_dp.append(dpv) return dpv_dp, dDeltaPsi_dp
def __init__(self, experiment, dmin=None, dmax=None, margin=1, force_static=False): ''' Initialise a predictor for each experiment. :param experiment: The experiment to predict for :param dmin: The maximum resolution :param dmax: The minimum resolution :param margin: The margin of hkl to predict :param force_static: force scan varying prediction to be static ''' from dials.algorithms.spot_prediction import ScanStaticReflectionPredictor from dials.algorithms.spot_prediction import ScanVaryingReflectionPredictor from dials.algorithms.spot_prediction import StillsReflectionPredictor from dxtbx.imageset import ImageSweep from dials.array_family import flex class Predictor(object): def __init__(self, name, func): self.name = name self.func = func def __call__(self): result = self.func() if dmax is not None: assert(dmax > 0) result.compute_d_single(experiment) mask = result['d'] > dmax result.del_selected(mask) return result # Select the predictor class if isinstance(experiment.imageset, ImageSweep): nsp = experiment.crystal.num_scan_points nim = experiment.scan.get_num_images() if not force_static and nsp == nim + 1: predictor = ScanVaryingReflectionPredictor( experiment, dmin=dmin, margin=margin) A = [experiment.crystal.get_A_at_scan_point(i) for i in range(experiment.crystal.num_scan_points)] predict = Predictor( "scan varying prediction", lambda: predictor.for_ub(flex.mat3_double(A))) else: predictor = ScanStaticReflectionPredictor( experiment, dmin=dmin) predict = Predictor( "scan static prediction", lambda: predictor.for_ub(experiment.crystal.get_A())) else: predictor = StillsReflectionPredictor( experiment, dmin=dmin) predict = Predictor( "stills prediction", lambda: predictor.for_ub(experiment.crystal.get_A())) # Create and add the predictor class self._predict = predict
def get_gradients(self, reflections, callback=None): """Calculate gradients of the prediction formula with respect to each of the parameters of the contained models, for all of the reflections. This method sets up required quantities relevant to the current step of refinement and then loops over the parameterisations of each type extending a results list each time.""" # Set up arrays of quantities of interest for each reflection self._nref = len(reflections) self._D = flex.mat3_double(self._nref) self._s0 = flex.vec3_double(self._nref) self._U = flex.mat3_double(self._nref) self._B = flex.mat3_double(self._nref) self._axis = flex.vec3_double(self._nref) self._fixed_rotation = flex.mat3_double(self._nref) self._setting_rotation = flex.mat3_double(self._nref) # Set up experiment to index mapping self._experiment_to_idx = [] # Populate values in these arrays for iexp, exp in enumerate(self._experiments): sel = reflections['id'] == iexp isel = sel.iselection() self._experiment_to_idx.append(isel) subref = reflections.select(sel) states = self._get_model_data_for_experiment(exp, subref) self._D.set_selected(sel, states['D']) self._s0.set_selected(sel, states['s0']) self._U.set_selected(sel, states['U']) self._B.set_selected(sel, states['B']) if exp.goniometer: self._axis.set_selected(sel, exp.goniometer.get_rotation_axis_datum()) self._fixed_rotation.set_selected(sel, exp.goniometer.get_fixed_rotation()) self._setting_rotation.set_selected(sel, exp.goniometer.get_setting_rotation()) # Other derived values self._h = reflections['miller_index'].as_vec3_double() self._UB = self._U * self._B self._s1 = reflections['s1'] self._pv = self._D * self._s1 # 'projection vector' for the ray along s1. # Quantities derived from pv, precalculated for efficiency u, v, w = self._pv.parts() self._w_inv = 1./w self._u_w_inv = u * self._w_inv self._v_w_inv = v * self._w_inv # Reset a pointer to the parameter number self._iparam = 0 # Do additional setup specified by derived classes self._local_setup(reflections) # Set up empty list in which to store gradients results = [] # loop over detector parameterisations and extend results results = self._grads_detector_loop(reflections, results, callback) # loop over the beam parameterisations and extend results results = self._grads_beam_loop(reflections, results, callback) # loop over the crystal orientation parameterisations and extend results results = self._grads_xl_orientation_loop(reflections, results, callback) # loop over the crystal unit cell parameterisations and extend results results = self._grads_xl_unit_cell_loop(reflections, results, callback) return results