def theta(gravity: sc.Variable, wavelength: sc.Variable, incident_beam: sc.Variable, scattered_beam: sc.Variable, sample_rotation: sc.Variable) -> sc.Variable: """ Compute the theta angle, including gravity correction, This is similar to the theta calculation in SANS (see https://docs.mantidproject.org/nightly/algorithms/Q1D-v2.html#q-unit-conversion), but we ignore the horizontal `x` component. See the schematic in Fig 5 of doi: 10.1016/j.nima.2016.03.007. """ grav = sc.norm(gravity) L2 = sc.norm(scattered_beam) y = sc.dot(scattered_beam, gravity) / grav y_correction = sc.to_unit(wavelength, _elem_unit(L2), copy=True) y_correction *= y_correction drop = L2**2 drop *= grav * (m_n**2 / (2 * h**2)) # Optimization when handling either the dense or the event coord of binned data: # - For the event coord, both operands have same dims, and we can multiply in place # - For the dense coord, we need to broadcast using non in-place operation if set(drop.dims).issubset(set(y_correction.dims)): y_correction *= drop else: y_correction = y_correction * drop y_correction += y out = sc.abs(y_correction, out=y_correction) out /= L2 out = sc.asin(out, out=out) out -= sc.to_unit(sample_rotation, 'rad') return out
def test_abs_out(): var = sc.Variable([Dim.X], values=np.array([0.1, -0.2]), unit=sc.units.m) expected = sc.Variable([Dim.X], values=np.array([0.1, 0.2]), unit=sc.units.m) out = sc.abs(x=var, out=var) assert var == expected assert out == expected
def _to_spherical(pos, output): output["r"] = sc.sqrt(sc.dot(pos, pos)) output["t"] = sc.acos(pos.fields.z / output["r"].data) signed_phi = sc.atan2(y=pos.fields.y, x=pos.fields.x) abs_phi = sc.abs(signed_phi) output["p-delta"] = ( np.pi * sc.units.rad) - abs_phi # angular delta (magnitude) from pole output['p-sign'] = signed_phi # weighted sign of phi return output
def test_abs(): var = sc.Variable([Dim.X], values=np.array([0.1, -0.2]), unit=sc.units.m) expected = sc.Variable([Dim.X], values=np.array([0.1, 0.2]), unit=sc.units.m) assert sc.abs(var) == expected
def get_detector_properties(ws, source_pos, sample_pos, spectrum_dim, advanced_geometry=False): if not advanced_geometry: return (get_detector_pos(ws, spectrum_dim), None, None) spec_info = ws.spectrumInfo() det_info = ws.detectorInfo() comp_info = ws.componentInfo() nspec = len(spec_info) det_rot = np.zeros([nspec, 3, 3]) det_bbox = np.zeros([nspec, 3]) if sample_pos is not None and source_pos is not None: total_detectors = spec_info.detectorCount() act_beam = (sample_pos - source_pos) rot = _rot_from_vectors(act_beam, sc.vector(value=[0, 0, 1])) inv_rot = _rot_from_vectors(sc.vector(value=[0, 0, 1]), act_beam) pos_d = sc.Dataset() # Create empty to hold position info for all spectra detectors pos_d["x"] = sc.zeros(dims=["detector"], shape=[total_detectors], unit=sc.units.m) pos_d["y"] = sc.zeros_like(pos_d["x"]) pos_d["z"] = sc.zeros_like(pos_d["x"]) pos_d.coords[spectrum_dim] = sc.array(dims=["detector"], values=np.empty(total_detectors)) spectrum_values = pos_d.coords[spectrum_dim].values x_values = pos_d["x"].values y_values = pos_d["y"].values z_values = pos_d["z"].values idx = 0 for i, spec in enumerate(spec_info): if spec.hasDetectors: definition = spec_info.getSpectrumDefinition(i) n_dets = len(definition) quats = [] bboxes = [] for j in range(n_dets): det_idx = definition[j][0] p = det_info.position(det_idx) r = det_info.rotation(det_idx) spectrum_values[idx] = i x_values[idx] = p.X() y_values[idx] = p.Y() z_values[idx] = p.Z() idx += 1 quats.append( np.array([r.imagI(), r.imagJ(), r.imagK(), r.real()])) if comp_info.hasValidShape(det_idx): s = comp_info.shape(det_idx) bboxes.append(s.getBoundingBox().width()) det_rot[ i, :] = sc.geometry.rotation_matrix_from_quaternion_coeffs( np.mean(quats, axis=0)) det_bbox[i, :] = np.sum(bboxes, axis=0) rot_pos = rot * sc.geometry.position(pos_d["x"].data, pos_d["y"].data, pos_d["z"].data) _to_spherical(rot_pos, pos_d) averaged = sc.groupby(pos_d, spectrum_dim, bins=sc.Variable(dims=[spectrum_dim], values=np.arange( -0.5, len(spec_info) + 0.5, 1.0))).mean("detector") sign = averaged["p-sign"].data / sc.abs(averaged["p-sign"].data) averaged["p"] = sign * ( (np.pi * sc.units.rad) - averaged["p-delta"].data) averaged["x"] = averaged["r"].data * sc.sin( averaged["t"].data) * sc.cos(averaged["p"].data) averaged["y"] = averaged["r"].data * sc.sin( averaged["t"].data) * sc.sin(averaged["p"].data) averaged["z"] = averaged["r"].data * sc.cos(averaged["t"].data) pos = sc.geometry.position(averaged["x"].data, averaged["y"].data, averaged["z"].data) return (inv_rot * pos, sc.spatial.linear_transforms(dims=[spectrum_dim], values=det_rot), sc.vectors(dims=[spectrum_dim], values=det_bbox, unit=sc.units.m)) else: pos = np.zeros([nspec, 3]) for i, spec in enumerate(spec_info): if spec.hasDetectors: definition = spec_info.getSpectrumDefinition(i) n_dets = len(definition) vec3s = [] quats = [] bboxes = [] for j in range(n_dets): det_idx = definition[j][0] p = det_info.position(det_idx) r = det_info.rotation(det_idx) vec3s.append([p.X(), p.Y(), p.Z()]) quats.append( np.array([r.imagI(), r.imagJ(), r.imagK(), r.real()])) if comp_info.hasValidShape(det_idx): s = comp_info.shape(det_idx) bboxes.append(s.getBoundingBox().width()) pos[i, :] = np.mean(vec3s, axis=0) det_rot[ i, :] = sc.geometry.rotation_matrix_from_quaternion_coeffs( np.mean(quats, axis=0)) det_bbox[i, :] = np.sum(bboxes, axis=0) else: pos[i, :] = [np.nan, np.nan, np.nan] det_rot[i, :] = [np.nan, np.nan, np.nan, np.nan] det_bbox[i, :] = [np.nan, np.nan, np.nan] return (sc.vectors(dims=[spectrum_dim], values=pos, unit=sc.units.m), sc.spatial.linear_transforms(dims=[spectrum_dim], values=det_rot), sc.vectors( dims=[spectrum_dim], values=det_bbox, unit=sc.units.m, ))