From 13c5a0efc690b9233b7ec77efc736bcdc6d16c1b Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Wed, 28 Jan 2026 13:27:41 -0500 Subject: [PATCH 01/28] Start documentation + typing --- hexrd/core/instrument/detector.py | 190 ++++---- hexrd/core/instrument/hedm_instrument.py | 436 ++++++++++-------- hexrd/core/material/crystallography.py | 4 +- hexrd/core/transforms/new_capi/xf_new_capi.py | 2 +- mypy.ini | 17 + 5 files changed, 361 insertions(+), 288 deletions(-) create mode 100644 mypy.ini diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index f363d0415..650e38170 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -1,8 +1,10 @@ +from __future__ import annotations + from abc import abstractmethod import copy import logging import os -from typing import Optional +from typing import Optional, TYPE_CHECKING from hexrd.core.instrument.constants import ( COATING_DEFAULT, @@ -12,11 +14,16 @@ from hexrd.core.instrument.physics_package import AbstractPhysicsPackage import numba import numpy as np +from numpy.typing import NDArray from hexrd.core import constants as ct -from hexrd.core import distortion as distortion_pkg +from hexrd.core.distortion import Registry +from hexrd.core.distortion.distortionabc import DistortionABC from hexrd.core import matrixutil as mutil +if TYPE_CHECKING: + from hexrd.hed.xrdutil.phutil import JHEPinholeDistortion, LayerDistortion, \ + RyggPinholeDistortion # TODO: Resolve extra-core-dependency from hexrd.hedm import xrdutil from hexrd.hed.xrdutil import _project_on_detector_plane @@ -44,7 +51,7 @@ ) logger = logging.getLogger(__name__) -distortion_registry = distortion_pkg.Registry() +distortion_registry = Registry() max_workers_DFLT = max(1, os.cpu_count() - 1) @@ -157,7 +164,8 @@ def cart_to_dvecs(self, xy_data): raise NotImplementedError @abstractmethod - def pixel_angles(self, origin=ct.zeros_3): + def pixel_angles(self, origin: NDArray[np.float64]=ct.zeros_3, + bvec: Optional[NDArray[np.float64]] = None): raise NotImplementedError @abstractmethod @@ -664,10 +672,10 @@ def pixel_compton_attenuation_length( def compute_compton_scattering_intensity( self, energy: np.floating, - rMat_s: np.array, + rMat_s: NDArray[np.float64], physics_package: AbstractPhysicsPackage, - origin: np.array = ct.zeros_3, - ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + origin: NDArray[np.float64] = ct.zeros_3, + ) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]: '''compute the theoretical compton scattering signal on the detector. this value is corrected for the transmission of compton scattered photons @@ -1044,17 +1052,6 @@ def clip_to_panel(self, xy, buffer_edges=True): """ xy = np.atleast_2d(xy) - ''' - # !!! THIS LOGIC IS OBSOLETE - if self.roi is not None: - ij_crds = self.cartToPixel(xy, pixels=True) - ii, jj = polygon(self.roi[:, 0], self.roi[:, 1], - shape=(self.rows, self.cols)) - on_panel_rows = [i in ii for i in ij_crds[:, 0]] - on_panel_cols = [j in jj for j in ij_crds[:, 1]] - on_panel = np.logical_and(on_panel_rows, on_panel_cols) - else: - ''' xlim = 0.5 * self.col_dim ylim = 0.5 * self.row_dim if buffer_edges and self.panel_buffer is not None: @@ -1210,65 +1207,95 @@ def _generate_bilinear_interp_dict( def make_powder_rings( self, - pd, - merge_hkls=False, - delta_tth=None, - delta_eta=10.0, - eta_period=None, - eta_list=None, - rmat_s=ct.identity_3x3, - tvec_s=ct.zeros_3, - tvec_c=ct.zeros_3, - full_output=False, - tth_distortion=None, - ): - """ - Generate points on Debye_Scherrer rings over the detector. - - !!! it is assuming that rmat_s is built from (chi, ome) as it the case - for HEDM! + pd: PlaneData | np.ndarray, # TODO: Make a different function for array input + merge_hkls: bool = False, + delta_tth: Optional[float] = None, + delta_eta: float = 10.0, + eta_period: list[float] | np.ndarray = [-np.pi, np.pi], + eta_list: Optional[list[float] | np.ndarray] = None, + rmat_s: NDArray[np.float64] = ct.identity_3x3, + tvec_s: NDArray[np.float64] = ct.zeros_3, + tvec_c: NDArray[np.float64] = ct.zeros_3, + full_output: bool = False, # TODO: Remove this option completely + tth_distortion: Optional[RyggPinholeDistortion | JHEPinholeDistortion | + LayerDistortion] = None, + ) -> tuple[ + list[np.ndarray], + list[np.ndarray], + np.ndarray, + list[np.ndarray], + NDArray[np.float64] + ]: + """ Generate points on Debye Scherrer rings over the detector. + + Assumes that `rmat_s` is built from (chi, omega), as it the case for HEDM. Parameters ---------- - pd : TYPE - DESCRIPTION. - merge_hkls : TYPE, optional - DESCRIPTION. The default is False. - delta_tth : TYPE, optional - DESCRIPTION. The default is None. - delta_eta : TYPE, optional - DESCRIPTION. The default is 10.. - eta_period : TYPE, optional - DESCRIPTION. The default is None. - eta_list : TYPE, optional - DESCRIPTION. The default is None. - rmat_s : TYPE, optional - DESCRIPTION. The default is ct.identity_3x3. - tvec_s : TYPE, optional - DESCRIPTION. The default is ct.zeros_3. - tvec_c : TYPE, optional - DESCRIPTION. The default is ct.zeros_3. - full_output : TYPE, optional - DESCRIPTION. The default is False. - tth_distortion : special class, optional - Special distortion class. The default is None. + `pd` : PlaneData | np.ndarray + Either a `PlaneData` object or an array of two-theta values in degrees. + `merge_hkls` : bool, optional + Merge hkls when `pd` is a `PlaneData` type. Defaults to `False`. + `delta_tth` : float, optional + The two-theta width of each ring in degrees. Required if `pd` is an array + of two-theta values. Defaults to `None`. + + If supplied, this value overwrites the `tThWidth` attribute of `pd` if it + is a `PlaneData` type. + `delta_eta` : float, optional + The azimuthal width of each ring sector in degrees. Defaults to `10.0`. + `eta_period` : list[float] | np.ndarray, optional + The azimuthal period over which to generate rings in radians. Defaults to + `[-pi, pi]`. + `eta_list` : list[float] | np.ndarray, optional + Azimuthal angles in degrees at which to generate rings. If supplied, this + overrides `delta_eta`. Defaults to `None`. + `rmat_s` : np.ndarray, optional + The (3, 3) rotation matrix of the sample orientation. Defaults to the + identity matrix. + `tvec_s` : np.ndarray, optional + The (3,) translation vector from the lab origin to the center of the + sample. Defaults to `<0, 0, 0>`. + `tvec_c` : np.ndarray, optional + The (3,) translation vector from the center of the sample to the center of + the crystal or grain. Defaults to `<0, 0, 0>`. + `full_output` : bool, optional + If `True`, also return the mapping indices of each ring point to the + detector pixels. Defaults to `False`. + `tth_distortion` : DistortionABC, optional + A distortion function to apply to the two-theta angles. If supplied, + the translation vectors `tvec_s` and `tvec_c` must both be zero. + Defaults to `None`. Raises ------ - RuntimeError - DESCRIPTION. + `ValueError` + If `tth_distortion` is supplied and either `tvec_s` or `tvec_c` is + non-zero. Returns ------- - TYPE - DESCRIPTION. + `valid_ang`: list[np.ndarray] + A list of arrays of shape `(neta, 2)` containing the valid ring sector + angles (two-theta, eta) that fall on the detector for each ring. + `valid_xy`: list[np.ndarray] + A list of arrays of shape `(neta, 2)` containing the valid ring sector + cartesian coordinates (x, y) on the detector for each ring. + `tth_ranges`: np.ndarray + An array of shape `(n_rings, 2)` containing the two-theta ranges (in + radians) for each ring. + `map_indices`: list[np.ndarray] + A list of arrays of shape `(neta,)` indicating whether each ring point + maps to a valid pixel on the panel. Returns if `full_output` is `True`. + `eta_edges`: np.ndarray + An array of shape `(neta + 1,)` containing the azimuthal edges (in + radians) of the ring sectors. Returns if `full_output` is `True`. """ if tth_distortion is not None: tnorms = mutil.rowNorm(np.vstack([tvec_s, tvec_c])) - assert ( - np.all(tnorms) < ct.sqrt_epsf - ), "If using distrotion function, translations must be zero" + if not np.all(tnorms) < ct.sqrt_epsf: + raise ValueError("If using distortion, translations must be 0") # in case you want to give it tth angles directly if isinstance(pd, PlaneData): @@ -1278,7 +1305,6 @@ def make_powder_rings( else: delta_tth = np.degrees(pd.tThWidth) - # !!! conversions, meh... del_eta = np.radians(delta_eta) # do merging if asked @@ -1338,10 +1364,6 @@ def make_powder_rings( tth = np.radians(tth) del_eta = np.radians(delta_eta) - # for generating rings, make eta vector in correct period - if eta_period is None: - eta_period = (-np.pi, np.pi) - if eta_list is None: neta = int(360.0 / float(delta_eta)) # this is the vector of ETA EDGES @@ -1806,8 +1828,8 @@ def increase_memoization_sizes(funcs, min_size): def calc_physics_package_transmission( self, - energy: np.floating, - rMat_s: np.array, + energy: float, + rMat_s: NDArray[np.float64], physics_package: AbstractPhysicsPackage, ) -> np.float64: """get the transmission from the physics package @@ -1849,7 +1871,7 @@ def calc_physics_package_transmission( def calc_compton_physics_package_transmission( self, energy: np.floating, - rMat_s: np.array, + rMat_s: NDArray[np.float64], physics_package: AbstractPhysicsPackage, ) -> np.ndarray: '''calculate the attenuation of inelastically @@ -1939,11 +1961,11 @@ def calc_compton_window_transmission( def calc_transmission_sample( self, - seca: np.array, - secb: np.array, - energy: np.floating, + seca: NDArray[np.float64], + secb: NDArray[np.float64], + energy: float, physics_package: AbstractPhysicsPackage, - ) -> np.array: + ) -> NDArray[np.float64]: thickness_s = physics_package.sample_thickness # in microns if np.isclose(thickness_s, 0): return np.ones(self.shape) @@ -1957,10 +1979,10 @@ def calc_transmission_sample( def calc_transmission_window( self, - secb: np.array, - energy: np.floating, + secb: NDArray[np.float64], + energy: float, physics_package: AbstractPhysicsPackage, - ) -> np.array: + ) -> NDArray[np.float64]: material_w = physics_package.window_material thickness_w = physics_package.window_thickness # in microns if material_w is None or np.isclose(thickness_w, 0): @@ -2039,7 +2061,7 @@ def calc_compton_transmission_window( def calc_effective_pinhole_area( self, physics_package: AbstractPhysicsPackage - ) -> np.array: + ) -> NDArray[np.float64]: '''get the effective pinhole area correction @SS 04/01/25 a modification was made based on the CeO2 data recorded on NIF. An extra factor of sec(beta) @@ -2079,10 +2101,10 @@ def calc_effective_pinhole_area( def calc_transmission_generic( self, - secb: np.array, + secb: NDArray[np.float64], thickness: np.floating, absorption_length: np.floating, - ) -> np.array: + ) -> NDArray[np.float64]: if np.isclose(thickness, 0): return np.ones(self.shape) @@ -2091,13 +2113,13 @@ def calc_transmission_generic( def calc_transmission_phosphor( self, - secb: np.array, + secb: NDArray[np.float64], thickness: np.floating, readout_length: np.floating, absorption_length: np.floating, energy: np.floating, pre_U0: np.floating, - ) -> np.array: + ) -> NDArray[np.float64]: if np.isclose(thickness, 0): return np.ones(self.shape) diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index c44bb1ed6..332162edb 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -30,15 +30,15 @@ @author: bernier2 """ +from collections.abc import Mapping, Sequence from contextlib import contextmanager import copy import logging -import os from collections import defaultdict from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor from functools import partial -from typing import Any, Literal, Optional, Union - +from pathlib import Path +from typing import Any, Literal, Optional, Union, TypedDict, NotRequired from tqdm import tqdm import yaml @@ -46,6 +46,7 @@ import h5py import numpy as np +from numpy.typing import NDArray from io import IOBase @@ -505,6 +506,17 @@ def _fwhm_to_sigma(fwhm): # ============================================================================= +class OscillationStageConfig(TypedDict): + translation: Sequence[float] + chi: float + +class InstrumentConfig(TypedDict): + detectors: dict[str, dict] + beam: Mapping[str, dict] + oscillation_stage: OscillationStageConfig + id: NotRequired[str] + physics_package: NotRequired[object] + class HEDMInstrument(object): """ Abstraction of XRD instrument. @@ -515,7 +527,7 @@ class HEDMInstrument(object): def __init__( self, - instrument_config=None, + instrument_config: Optional[InstrumentConfig] = None, image_series=None, eta_vector=None, instrument_name=None, @@ -537,37 +549,13 @@ def __init__( self.max_workers = max_workers self.physics_package = physics_package + self._tvec: NDArray[np.float64] = t_vec_s_DFLT + self._chi = chi_DFLT + self._detectors: dict[str, PlanarDetector | CylindricalDetector] = {} - if instrument_config is None: - # Default instrument - if instrument_name is not None: - self._id = instrument_name - self._num_panels = 1 - self._create_default_beam() - - # FIXME: must add cylindrical - self._detectors = dict( - panel_id_DFLT=PlanarDetector( - rows=nrows_DFLT, - cols=ncols_DFLT, - pixel_size=pixel_size_DFLT, - tvec=t_vec_d_DFLT, - tilt=tilt_params_DFLT, - bvec=self.beam_vector, - xrs_dist=self.source_distance, - evec=self._eta_vector, - distortion=None, - roi=None, - group=None, - max_workers=self.max_workers, - ), - ) - - self._tvec = t_vec_s_DFLT - self._chi = chi_DFLT - else: + if instrument_config is not None: if isinstance(instrument_config, h5py.File): - tmp = {} + tmp: dict[Any, Any] = {} unwrap_h5_to_dict(instrument_config, tmp) instrument_config = tmp['instrument'] elif not isinstance(instrument_config, dict): @@ -575,6 +563,7 @@ def __init__( "instrument_config must be either an HDF5 file object" + "or a dictionary. You gave a %s" % type(instrument_config) ) + assert instrument_config is not None if instrument_name is None: if 'id' in instrument_config: self._id = instrument_config['id'] @@ -587,32 +576,38 @@ def __init__( self.physics_package = instrument_config['physics_package'] xrs_config = instrument_config['beam'] - is_single_beam = 'energy' in xrs_config and 'vector' in xrs_config - if is_single_beam: + if 'energy' in xrs_config and 'vector' in xrs_config: # Assume single beam. Load the same way as multibeam self._create_default_beam() - xrs_config = {self.active_beam_name: xrs_config} - - # Multi beam load - for beam_name, beam in xrs_config.items(): - self._beam_dict[beam_name] = { - 'energy': beam['energy'], + self._beam_dict[self.active_beam_name] = { + 'energy': xrs_config['energy'], 'vector': calc_beam_vec( - beam['vector']['azimuth'], - beam['vector']['polar_angle'], + xrs_config['vector']['azimuth'], + xrs_config['vector']['polar_angle'], ), - 'distance': beam.get('source_distance', np.inf), - 'energy_correction': beam.get('energy_correction', None), + 'distance': xrs_config.get('source_distance', np.inf), + 'energy_correction': xrs_config.get('energy_correction', None), } + else: + # Multi beam load + for beam_name, beam in xrs_config.items(): + self._beam_dict[beam_name] = { + 'energy': beam['energy'], + 'vector': calc_beam_vec( + beam['vector']['azimuth'], + beam['vector']['polar_angle'], + ), + 'distance': beam.get('source_distance', np.inf), + 'energy_correction': beam.get('energy_correction', None), + } + # Set the active beam name if not set already if self._active_beam_name is None: self._active_beam_name = next(iter(self._beam_dict)) # now build detector dict - detectors_config = instrument_config['detectors'] - det_dict = dict.fromkeys(detectors_config) - for det_id, det_info in detectors_config.items(): + for det_id, det_info in instrument_config['detectors'].items(): det_group = det_info.get('group') # optional detector group pixel_info = det_info['pixels'] affine_info = det_info['transform'] @@ -626,7 +621,7 @@ def __init__( saturation_level = 2**16 shape = (pixel_info['rows'], pixel_info['columns']) - panel_buffer = None + panel_buffer: NDArray[np.float64] | str | None = None if buffer_key in det_info: det_buffer = det_info[buffer_key] if det_buffer is not None: @@ -650,7 +645,7 @@ def __init__( elif isinstance(det_buffer, str): panel_buffer = det_buffer elif np.isscalar(det_buffer): - panel_buffer = det_buffer * np.ones(2) + panel_buffer = np.asarray(det_buffer) * np.ones(2) else: raise RuntimeError( "panel buffer spec invalid for %s" % det_id @@ -700,17 +695,35 @@ def __init__( # Add cylindrical detector kwargs kwargs['radius'] = det_info.get('radius', 49.51) - det_dict[det_id] = DetectorClass(**kwargs) - - self._detectors = det_dict + self._detectors[det_id] = DetectorClass(**kwargs) + assert instrument_config is not None self._tvec = np.r_[instrument_config['oscillation_stage']['translation']] self._chi = instrument_config['oscillation_stage']['chi'] + else: + # Default instrument + if instrument_name is not None: + self._id = instrument_name + self._num_panels = 1 + self._create_default_beam() - # grab angles from beam vec - # !!! these are in DEGREES! - azim, pola = calc_angles_from_beam_vec(self.beam_vector) - + # FIXME: must add cylindrical + self._detectors = dict( + panel_id_DFLT=PlanarDetector( + rows=nrows_DFLT, + cols=ncols_DFLT, + pixel_size=pixel_size_DFLT, + tvec=t_vec_d_DFLT, + tilt=tilt_params_DFLT, + bvec=self.beam_vector, + xrs_dist=self.source_distance, + evec=self._eta_vector, + distortion=None, + roi=None, + group=None, + max_workers=self.max_workers, + ), + ) self.update_memoization_sizes() @property @@ -747,7 +760,7 @@ def num_panels(self): return self._num_panels @property - def detectors(self): + def detectors(self) -> Mapping[str, Detector]: return self._detectors @property @@ -825,7 +838,7 @@ def xrs_beam_energy(self, beam_name: Optional[str]) -> float: return self.beam_dict[beam_name]['energy'] @property - def active_beam_name(self) -> str: + def active_beam_name(self) -> str | None: return self._active_beam_name @active_beam_name.setter @@ -921,7 +934,7 @@ def energy_correction(self, v: Union[dict, None]): self.active_beam['energy_correction'] = v @staticmethod - def create_default_energy_correction() -> dict[str, float]: + def create_default_energy_correction() -> dict[str, float | str]: return { 'intercept': 0.0, # in mm 'slope': 0.0, # eV/mm @@ -1027,19 +1040,46 @@ def _write_group(file): def extract_polar_maps( self, - plane_data, - imgser_dict, - active_hkls=None, - threshold=None, - tth_tol=None, - eta_tol=0.25, - ): + plane_data: PlaneData, + imgser_dict: dict[str, OmegaImageSeries], + active_hkls: Optional[list[int]]=None, + threshold: Optional[float]=None, + tth_tol: Optional[float]=None, + eta_tol: Optional[float]=0.25, + ) -> tuple[dict[str, np.ndarray], np.ndarray]: """ Extract eta-omega maps from an imageseries. Quick and dirty way to histogram angular patch data for make pole figures suitable for fiber generation + Parameters + ---------- + + plane_data : hexrd.crystallography.PlaneData object + imgser_dict : dict[str, OmegaImageSeries] object + Dictionary of imageseries, one for each panel. + active_hkls : list[int], optional + List of active HKL IDs to extract. If None (default), all non-excluded + reflections in plane_data are used. + threshold : float, optional + Intensity threshold for pixel inclusion. If None (default), no + thresholding is applied. + tth_tol : float, optional + Tolerance for merging reflections in degrees. If None (default), + falls back to `plane_data.tThWidth`. + eta_tol : float, optional + Eta bin width in degrees. If None (default), 0.25 degrees is used. + + Returns + ------- + + ring_maps_panel : dict[str, np.ndarray] + Dictionary of eta-omega maps for each detector panel. Each map has + shape (n_rings, n_omegas, n_eta_bins). + eta_edges : np.ndarray + The edges of the eta bins used in the histograms. + TODO: streamline projection code TODO: normalization !!!: images must be non-negative! @@ -1057,7 +1097,7 @@ def extract_polar_maps( # detectors, so calculate it once # !!! grab first panel panel = next(iter(self.detectors.values())) - pow_angs, pow_xys, tth_ranges, eta_idx, eta_edges = panel.make_powder_rings( + _, _, tth_ranges, _, eta_edges = panel.make_powder_rings( plane_data, merge_hkls=False, delta_eta=eta_tol, @@ -1065,12 +1105,8 @@ def extract_polar_maps( ) if active_hkls is not None: - assert hasattr( - active_hkls, '__len__' - ), "active_hkls must be an iterable with __len__" - - # need to re-cast for element-wise operations - active_hkls = np.array(active_hkls) + if not hasattr(active_hkls, '__len__'): + raise TypeError("active_hkls must be an iterable with __len__") # these are all active reflection unique hklIDs active_hklIDs = plane_data.getHKLID(plane_data.hkls, master=True) @@ -1087,13 +1123,14 @@ def extract_polar_maps( delta_eta = eta_edges[1] - eta_edges[0] ncols_eta = len(eta_edges) - 1 - ring_maps_panel = dict.fromkeys(self.detectors) + ring_maps_panel: dict[str, np.ndarray] = { + k: np.empty((0, 0)) for k in self.detectors + } for det_key in self.detectors: logger.info(f"working on detector '{det_key}'...") # grab panel panel = self.detectors[det_key] - # native_area = panel.pixel_area # pixel ref area # pixel angular coords for the detector panel ptth, peta = panel.pixel_angles() @@ -1107,7 +1144,7 @@ def extract_polar_maps( except KeyError: raise RuntimeError(f"imageseries for '{det_key}' has no omega info") - # initialize maps and assing by row (omega/frame) + # initialize maps and assign by row (omega/frame) nrows_ome = len(omegas) # init map with NaNs @@ -1593,26 +1630,107 @@ def simulate_rotation_series( ) return results + def _pull_spots_check_only( + self, + plane_data: PlaneData, + grain_params: tuple | np.ndarray, + imgser_dict: dict, + tth_tol: float = 0.25, + eta_tol: float = 1.0, + ome_tol: float = 1.0, + threshold: int = 10, + eta_ranges: list[tuple[float, float]] = [(-np.pi, np.pi)], + ome_period: Optional[tuple] = None, + ): + rMat_c = make_rmat_of_expmap(grain_params[:3]) + tVec_c = np.asarray(grain_params[3:6]) + + oims0 = next(iter(imgser_dict.values())) + omega_ranges = [ + np.radians([i['ostart'], i['ostop']]) for i in oims0.omegawedges.wedges + ] + if ome_period is None: + ims = next(iter(imgser_dict.values())) + ome_period = np.radians(ims.omega[0, 0] + np.array([0.0, 360.0])) + + delta_omega = oims0.omega[0, 1] - oims0.omega[0, 0] + _, ome_grid = make_tolerance_grid(delta_omega, ome_tol, 1, adjust_window=True) + + sim_results = self.simulate_rotation_series( + plane_data, + [grain_params], + eta_ranges=eta_ranges, + ome_ranges=omega_ranges, + ome_period=ome_period, + ) + + patch_has_signal = [] + output = defaultdict(list) + for detector_id, panel in self.detectors.items(): + # pull out the OmegaImageSeries for this panel from input dict + omega_image_series = _parse_imgser_dict( + imgser_dict, detector_id, roi=panel.roi + ) + + hkl_ids, hkls_p, ang_centers, xy_centers, ang_pixel_size = [ + item[0] for item in sim_results[detector_id] + ] + + det_xy, mask = self._get_panel_mask( + tth_tol, eta_tol, ang_centers, panel, rMat_c, tVec_c + ) + + patch_xys = det_xy.reshape(-1, 4, 2)[mask] + hkls_p = hkls_p[mask] + ang_centers = ang_centers[mask] + hkl_ids = hkl_ids[mask] + xy_centers = xy_centers[mask] + ang_pixel_size = ang_pixel_size[mask] + + for ang_index, angs in enumerate(ang_centers): + omega_eval = np.degrees(angs[2]) + ome_grid + + frame_indices = [ + omega_image_series.omega_to_frame(omega)[0] for omega in omega_eval + ] + if -1 in frame_indices: + logger.warning( + f"window for {hkls_p[ang_index, :]} falls outside omega range" + ) + continue + + ijs = panel.cartToPixel(patch_xys[ang_index]) + ii, jj = polygon(ijs[:, 0], ijs[:, 1]) + + patch_data_raw = np.stack( + [omega_image_series[i_frame, ii, jj] for i_frame in frame_indices], + axis=0, + ) + patch_has_signal.append(np.any(patch_data_raw > threshold)) + output[detector_id].append((ii, jj, frame_indices)) + + return patch_has_signal, output + def pull_spots( self, plane_data: PlaneData, grain_params: tuple | np.ndarray, imgser_dict: dict, - tth_tol: Optional[float] = 0.25, - eta_tol: Optional[float] = 1.0, - ome_tol: Optional[float] = 1.0, - npdiv: Optional[int] = 2, - threshold: Optional[int] = 10, - eta_ranges: Optional[tuple[tuple]] = [(-np.pi, np.pi)], + tth_tol: float = 0.25, + eta_tol: float = 1.0, + ome_tol: float = 1.0, + npdiv: int = 2, + threshold: int = 10, + eta_ranges: list[tuple] = [(-np.pi, np.pi)], ome_period: Optional[tuple] = None, - dirname: Optional[str] = 'results', - filename: Optional[os.PathLike] = None, + dirname: str = 'results', + filename: Optional[str] = None, output_format: Literal['text', 'hdf5'] = 'text', - return_spot_list: Optional[bool] = False, - check_only: Optional[bool] = False, + return_spot_list: bool = False, + check_only: bool = False, interp: Literal['nearest', 'bilinear'] = 'nearest', quiet: Optional[Any] = None, - ) -> tuple[tuple, dict]: + ) -> tuple[list[np.bool_], defaultdict[str, list[list]]]: """Extract reflection info from a rotation series. Input must be encoded as an OmegaImageseries object. @@ -1680,7 +1798,6 @@ def pull_spots( threshold=threshold, eta_ranges=eta_ranges, ome_period=ome_period, - filename=filename, ) if filename is not None and not isinstance(filename, str): @@ -1688,14 +1805,12 @@ def pull_spots( if not isinstance(output_format, str): raise TypeError("output_format must be a string type") - output_format = output_format.lower() - interp = interp.lower() write_hdf5 = filename is not None and output_format == 'hdf5' write_text = filename is not None and output_format == 'text' rMat_c = make_rmat_of_expmap(grain_params[:3]) - tVec_c = grain_params[3:6] + tVec_c = np.asarray(grain_params[3:6]) oims0 = next(iter(imgser_dict.values())) omega_ranges = [ @@ -1728,8 +1843,8 @@ def pull_spots( ) if write_hdf5: - writer = GrainDataWriter_h5( - os.path.join(dirname, filename), + writer_hdf5 = GrainDataWriter_h5( + Path(dirname) / str(filename), self.write_config(), grain_params, ) @@ -1769,9 +1884,9 @@ def pull_spots( ) if write_text: - output_dir = os.path.join(dirname, detector_id) - os.makedirs(output_dir, exist_ok=True) - writer = PatchDataWriter(os.path.join(output_dir, filename)) + output_dir = Path(dirname) / detector_id + output_dir.mkdir(parents=True, exist_ok=True) + writer_text = PatchDataWriter(output_dir / str(filename)) for patch_id, (vtx_angs, _, _, areas, xy_eval, ijs) in enumerate(patches): prows, pcols = areas.shape @@ -1881,7 +1996,7 @@ def pull_spots( next_invalid_peak_id -= 1 if write_text: - writer.dump_patch( + writer_text.dump_patch( peak_id, hkl_id, hkl, @@ -1893,7 +2008,7 @@ def pull_spots( meas_xy, ) elif write_hdf5: - writer.dump_patch( + writer_hdf5.dump_patch( detector_id, patch_id, peak_id, @@ -1949,101 +2064,20 @@ def pull_spots( ] ) if write_text: - writer.close() + writer_text.close() if write_hdf5: - writer.close() - return patch_has_signal, output - - def _pull_spots_check_only( - self, - plane_data: PlaneData, - grain_params: tuple | np.ndarray, - imgser_dict: dict, - tth_tol: Optional[float] = 0.25, - eta_tol: Optional[float] = 1.0, - ome_tol: Optional[float] = 1.0, - threshold: Optional[int] = 10, - eta_ranges: Optional[tuple[tuple]] = [(-np.pi, np.pi)], - ome_period: Optional[tuple] = None, - ): - rMat_c = make_rmat_of_expmap(grain_params[:3]) - tVec_c = grain_params[3:6] - - oims0 = next(iter(imgser_dict.values())) - omega_ranges = [ - np.radians([i['ostart'], i['ostop']]) for i in oims0.omegawedges.wedges - ] - if ome_period is None: - ims = next(iter(imgser_dict.values())) - ome_period = np.radians(ims.omega[0, 0] + np.array([0.0, 360.0])) - - delta_omega = oims0.omega[0, 1] - oims0.omega[0, 0] - _, ome_grid = make_tolerance_grid(delta_omega, ome_tol, 1, adjust_window=True) - - sim_results = self.simulate_rotation_series( - plane_data, - [grain_params], - eta_ranges=eta_ranges, - ome_ranges=omega_ranges, - ome_period=ome_period, - ) - - patch_has_signal = [] - output = defaultdict(list) - for detector_id, panel in self.detectors.items(): - # pull out the OmegaImageSeries for this panel from input dict - omega_image_series = _parse_imgser_dict( - imgser_dict, detector_id, roi=panel.roi - ) - - hkl_ids, hkls_p, ang_centers, xy_centers, ang_pixel_size = [ - item[0] for item in sim_results[detector_id] - ] - - det_xy, mask = self._get_panel_mask( - tth_tol, eta_tol, ang_centers, panel, rMat_c, tVec_c - ) - - patch_xys = det_xy.reshape(-1, 4, 2)[mask] - hkls_p = hkls_p[mask] - ang_centers = ang_centers[mask] - hkl_ids = hkl_ids[mask] - xy_centers = xy_centers[mask] - ang_pixel_size = ang_pixel_size[mask] - - for ang_index, angs in enumerate(ang_centers): - omega_eval = np.degrees(angs[2]) + ome_grid - - frame_indices = [ - omega_image_series.omega_to_frame(omega)[0] for omega in omega_eval - ] - if -1 in frame_indices: - logger.warning( - f"window for {hkls_p[ang_index, :]} falls outside omega range" - ) - continue - - ijs = panel.cartToPixel(patch_xys[ang_index]) - ii, jj = polygon(ijs[:, 0], ijs[:, 1]) - - patch_data_raw = np.stack( - [omega_image_series[i_frame, ii, jj] for i_frame in frame_indices], - axis=0, - ) - patch_has_signal.append(np.any(patch_data_raw > threshold)) - output[detector_id].append((ii, jj, frame_indices)) - + writer_hdf5.close() return patch_has_signal, output def _get_panel_mask( self, tth_tol: float, eta_tol: float, - ang_centers: np.array, + ang_centers: NDArray[np.float64], panel: Any, - rMat_c: np.ndarray, - tVec_c: np.ndarray, - ) -> np.ndarray: + rMat_c: NDArray[np.float64], + tVec_c: NDArray[np.float64], + ) -> tuple[NDArray[np.float64], NDArray[np.bool_]]: offsets = 0.5 * np.radians( [ [-tth_tol, -eta_tol, 0], @@ -2070,13 +2104,13 @@ def _get_panel_mask( def _get_panel_patches( self, - panel: Any, - ang_centers: np.ndarray, - ang_pixel_size: np.array, + panel: Detector, + ang_centers: NDArray[np.float64], + ang_pixel_size: NDArray[np.float64], tth_tol: float, eta_tol: float, - rMat_c: np.array, - tvec_c: np.array, + rMat_c: NDArray[np.float64], + tvec_c: NDArray[np.float64], npdiv: int, ): instrument_config = panel.config_dict( @@ -2101,10 +2135,10 @@ def _get_panel_patches( def _get_meas_xy( self, - panel: Any, - meas_angles: np.array, - rMat_c: np.array, - tVec_c: np.array, + panel: Detector, + meas_angles: NDArray[np.float64], + rMat_c: NDArray[np.float64], + tVec_c: NDArray[np.float64], ): gvec_c = angles_to_gvec( meas_angles, @@ -2135,12 +2169,12 @@ def update_memoization_sizes(self): PlanarDetector.update_memoization_sizes(all_panels) CylindricalDetector.update_memoization_sizes(all_panels) - def calc_transmission(self, rMat_s: np.ndarray = None) -> dict[str, np.ndarray]: - """calculate the transmission from the - filter and polymer coating. the inverse of this - number is the intensity correction that needs - to be applied. actual computation is done inside - the detector class + def calc_transmission(self, rMat_s: Optional[NDArray[np.float64]] = None + ) -> dict[str, NDArray[np.float64]]: + """ Calculate the transmission from the filter and polymer coating. + + The inverse of this number is the intensity correction that needs to be + applied. Actual computation is done inside the detector class. """ if rMat_s is None: rMat_s = ct.identity_3x3 diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index f249b417b..9e2465dec 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -711,7 +711,7 @@ def __init__(self, hkls: Optional[np.ndarray], *args, **kwargs) -> None: self._hkls = copy.deepcopy(hkls) self._strainMag = strainMag self._structFact = np.ones(self._hkls.shape[1]) - self.tThWidth = tThWidth + self.tThWidth: float = tThWidth # ... need to implement tThMin too if 'doTThSort' in kwargs: @@ -1409,7 +1409,7 @@ def getHKLID( self, hkl: Union[int, Tuple[int, int, int], np.ndarray], master: Optional[bool] = False, - ) -> Union[List[int], int]: + ) -> List[int] | int: """ Return the unique ID of a list of hkls. diff --git a/hexrd/core/transforms/new_capi/xf_new_capi.py b/hexrd/core/transforms/new_capi/xf_new_capi.py index ab4048fbc..16646fc90 100644 --- a/hexrd/core/transforms/new_capi/xf_new_capi.py +++ b/hexrd/core/transforms/new_capi/xf_new_capi.py @@ -542,7 +542,7 @@ def make_sample_rmat(chi: float, ome: Union[float, np.ndarray]) -> np.ndarray: return result -def make_rmat_of_expmap(exp_map: np.ndarray) -> np.ndarray: +def make_rmat_of_expmap(exp_map: tuple[float] | np.ndarray) -> np.ndarray: """ Calculate the rotation matrix of an exponential map. diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 000000000..3b581fb2b --- /dev/null +++ b/mypy.ini @@ -0,0 +1,17 @@ +; [mypy] +; strict = True + +[mypy-tqdm.*] +ignore_missing_imports = True + +[mypy-yaml.*] +ignore_missing_imports = True + +[mypy-h5py.*] +ignore_missing_imports = True + +[mypy-scipy.*] +ignore_missing_imports = True + +[mypy-numba.*] +ignore_missing_imports = True From 74bfa9f9666a114a2c5c0dbee4ee6fe3ae993dad Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Wed, 28 Jan 2026 16:41:56 -0500 Subject: [PATCH 02/28] Continue typing --- hexrd/core/constants.py | 2 +- .../core/imageseries/load/eiger_stream_v2.py | 2 +- hexrd/core/imageseries/load/framecache.py | 5 +- hexrd/core/instrument/detector.py | 2 +- hexrd/core/instrument/hedm_instrument.py | 2 +- hexrd/core/instrument/physics_package.py | 4 +- hexrd/core/material/crystallography.py | 171 ++++++++++-------- hexrd/core/material/material.py | 2 +- hexrd/core/material/utils.py | 10 +- hexrd/core/utils/panel_buffer.py | 4 +- hexrd/hed/xrdutil/utils.py | 6 +- hexrd/hedm/xrdutil/utils.py | 44 ++--- mypy.ini | 18 ++ 13 files changed, 152 insertions(+), 120 deletions(-) diff --git a/hexrd/core/constants.py b/hexrd/core/constants.py index a053aa698..1c58d4c0d 100644 --- a/hexrd/core/constants.py +++ b/hexrd/core/constants.py @@ -3911,7 +3911,7 @@ def is_writable_file(path): 'Mt': 109, } -ptableinverse = dict.fromkeys(ptable.values()) +ptableinverse: dict[int, str] = dict.fromkeys(ptable.values()) for k, v in ptable.items(): ptableinverse[v] = k diff --git a/hexrd/core/imageseries/load/eiger_stream_v2.py b/hexrd/core/imageseries/load/eiger_stream_v2.py index 677083b77..9d0b554dd 100644 --- a/hexrd/core/imageseries/load/eiger_stream_v2.py +++ b/hexrd/core/imageseries/load/eiger_stream_v2.py @@ -8,7 +8,7 @@ import h5py import numpy as np -from hexrd.utils.hdf5 import unwrap_h5_to_dict +from hexrd.core.utils.hdf5 import unwrap_h5_to_dict from . import ImageSeriesAdapter from ..imageseriesiter import ImageSeriesIterator diff --git a/hexrd/core/imageseries/load/framecache.py b/hexrd/core/imageseries/load/framecache.py index 8aa834174..d6a505e45 100644 --- a/hexrd/core/imageseries/load/framecache.py +++ b/hexrd/core/imageseries/load/framecache.py @@ -319,10 +319,7 @@ def read_list_arrays_method_thread(i): framelist[i] = frame - kwargs = { - "max_workers": max_workers, - } - with ThreadPoolExecutor(**kwargs) as executor: + with ThreadPoolExecutor(max_workers) as executor: # Evaluate the results via `list()`, so that if an exception is # raised in a thread, it will be re-raised and visible to the # user. diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index 650e38170..b75822520 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -53,7 +53,7 @@ logger = logging.getLogger(__name__) distortion_registry = Registry() -max_workers_DFLT = max(1, os.cpu_count() - 1) +max_workers_DFLT = max(1, (os.cpu_count() or 1) - 1) beam_energy_DFLT = 65.351 diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index 332162edb..81bff3f27 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -1045,7 +1045,7 @@ def extract_polar_maps( active_hkls: Optional[list[int]]=None, threshold: Optional[float]=None, tth_tol: Optional[float]=None, - eta_tol: Optional[float]=0.25, + eta_tol: float=0.25, ) -> tuple[dict[str, np.ndarray], np.ndarray]: """ Extract eta-omega maps from an imageseries. diff --git a/hexrd/core/instrument/physics_package.py b/hexrd/core/instrument/physics_package.py index bcbbcd93c..def34e2cc 100644 --- a/hexrd/core/instrument/physics_package.py +++ b/hexrd/core/instrument/physics_package.py @@ -58,8 +58,8 @@ def type(self): # 1. Add the layer name to this list # 2. If the layer requires a special class (like `PinholeLayer`), # specify that in `SPECIAL_LAYERS` - LAYER_TYPES = [] - SPECIAL_LAYERS = {} + LAYER_TYPES: list[str] = [] + SPECIAL_LAYERS: dict[str, PhysicsPackageLayer] = {} def __init__(self, **kwargs): # The physics packages are set up so that you can access layer diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index 9e2465dec..05a8f574b 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -25,15 +25,18 @@ # the Free Software Foundation, Inc., 59 Temple Place, Suite 330, # Boston, MA 02111-1307 USA or visit . # ============================================================================= +from __future__ import annotations + import re import logging import copy import csv import os from math import pi -from typing import Optional, Union, Dict, List, Tuple +from typing import Any, Mapping, Optional, Union, Dict, List, Tuple, TypedDict import numpy as np +from numpy.typing import NDArray from hexrd.core.material.unitcell import unitcell from hexrd.core.deprecation import deprecated @@ -58,6 +61,19 @@ # units dUnit = 'angstrom' +LatPlaneData = Mapping[str, NDArray[Any]] +LatVecOps = Mapping[str, NDArray[Any]] + +class HKLData(TypedDict): + hklID: int + hkl: NDArray[np.integer] + tTheta: np.floating + dSpacings: np.floating + tThetaLo: np.floating + tThetaHi: np.floating + latPlnNrmls: NDArray[np.floating] + symHKLs: NDArray[np.signedinteger] + centrosym: bool def hklToStr(hkl: np.ndarray) -> str: """ @@ -79,7 +95,7 @@ def hklToStr(hkl: np.ndarray) -> str: def cosineXform( a: np.ndarray, b: np.ndarray, c: np.ndarray -) -> tuple[np.ndarray, np.ndarray]: +) -> tuple[np.float64, np.float64]: """ Spherical trig transform to take alpha, beta, gamma to expressions for cos(alpha*). See ref below. @@ -99,9 +115,9 @@ def cosineXform( Returns ------- - np.ndarray + np.float64 List of cos(alpha*) values. - np.ndarray + np.float64 List of sin(alpha*) values. """ @@ -161,8 +177,8 @@ def latticeParameters(lvec): def latticePlanes( hkls: np.ndarray, lparms: np.ndarray, - ltype: Optional[str] = 'cubic', - wavelength: Optional[float] = 1.54059292, + ltype: str = 'cubic', + wavelength: float = 1.54059292, strainMag: Optional[float] = None, ) -> Dict[str, np.ndarray]: """ @@ -241,60 +257,54 @@ def latticePlanes( 0-201-01174-3 """ - location = 'latticePlanes' - - assert hkls.shape[0] == 3, f"hkls aren't column vectors in call to '{location}'!" - - tag = ltype - wlen = wavelength + if hkls.shape[0] != 3: + raise ValueError(f"hkls must be column vectors of shape (3, n)") # get B - L = latticeVectors(lparms, tag) + L = latticeVectors(lparms, ltype) # get G-vectors -- reciprocal vectors in crystal frame - G = np.dot(L['B'], hkls) + G: NDArray[np.float64] = np.dot(L['B'], hkls) # magnitudes - d = 1 / np.sqrt(np.sum(G**2, 0)) - - aconv = 1.0 + d: NDArray[np.float64] = 1 / np.sqrt(np.sum(G**2, 0)) # two thetas - sth = wlen / 2.0 / d - mask = np.abs(sth) < 1.0 - tth = np.zeros(sth.shape) + sth: NDArray[np.float64] = wavelength / 2.0 / d + mask: NDArray[np.bool_] = np.abs(sth) < 1.0 + tth: NDArray[np.float64] = np.zeros(sth.shape) tth[~mask] = np.nan - tth[mask] = aconv * 2.0 * np.arcsin(sth[mask]) + tth[mask] = 2.0 * np.arcsin(sth[mask]) - p = dict(normals=unitVector(G), dspacings=d, tThetas=tth) + p: dict[str, NDArray[np.float64]] = dict(normals=unitVector(G), dspacings=d, tThetas=tth) if strainMag is not None: p['tThetasLo'] = np.zeros(sth.shape) p['tThetasHi'] = np.zeros(sth.shape) - mask = (np.abs(wlen / 2.0 / (d * (1.0 + strainMag))) < 1.0) & ( - np.abs(wlen / 2.0 / (d * (1.0 - strainMag))) < 1.0 + mask = (np.abs(wavelength / 2.0 / (d * (1.0 + strainMag))) < 1.0) & ( + np.abs(wavelength / 2.0 / (d * (1.0 - strainMag))) < 1.0 ) p['tThetasLo'][~mask] = np.nan p['tThetasHi'][~mask] = np.nan p['tThetasLo'][mask] = ( - aconv * 2 * np.arcsin(wlen / 2.0 / (d[mask] * (1.0 + strainMag))) + 2 * np.arcsin(wavelength / 2.0 / (d[mask] * (1.0 + strainMag))) ) p['tThetasHi'][mask] = ( - aconv * 2 * np.arcsin(wlen / 2.0 / (d[mask] * (1.0 - strainMag))) + 2 * np.arcsin(wavelength / 2.0 / (d[mask] * (1.0 - strainMag))) ) return p - +# TODO: Return a tuple of values instead of a dict - this adds needless complexity def latticeVectors( - lparms: np.ndarray, - tag: Optional[str] = 'cubic', - radians: Optional[bool] = False, -) -> Dict[str, Union[np.ndarray, float]]: + lparms: NDArray[np.float64], + tag: str = 'cubic', + radians: bool = False, +) -> Dict[str, NDArray[np.float64] | float]: """ Generates direct and reciprocal lattice vector components in a crystal-relative RHON basis, X. The convention for fixing X to the @@ -421,10 +431,10 @@ def latticeVectors( aconv = pi / 180.0 # degToRad deg90 = pi / 2.0 deg120 = 2.0 * pi / 3.0 - # + if tag == lattStrings[0]: # cubic - cellparms = np.r_[np.tile(lparms[0], (3,)), deg90 * np.ones((3,))] + cellparms: NDArray[np.float64] = np.r_[np.tile(lparms[0], (3,)), deg90 * np.ones((3,))] elif tag == lattStrings[1] or tag == lattStrings[2]: # hexagonal | trigonal (hex indices) cellparms = np.r_[lparms[0], lparms[0], lparms[1], deg90, deg90, deg120] @@ -453,62 +463,67 @@ def latticeVectors( aconv * lparms[5], ] else: - raise RuntimeError(f'lattice tag "{tag}" is not recognized') + raise ValueError(f'lattice tag "{tag}" is not recognized') + alpha: float + beta: float + gamma: float alpha, beta, gamma = cellparms[3:6] cosalfar, sinalfar = cosineXform(alpha, beta, gamma) - a = cellparms[0] * np.r_[1, 0, 0] - b = cellparms[1] * np.r_[np.cos(gamma), np.sin(gamma), 0] - c = ( + a: NDArray[np.float64] = cellparms[0] * np.array([1, 0, 0]) + b: NDArray[np.float64] = cellparms[1] * np.array([np.cos(gamma), np.sin(gamma), 0]) + c: NDArray[np.float64] = ( cellparms[2] * np.r_[np.cos(beta), -cosalfar * np.sin(beta), sinalfar * np.sin(beta)] ) - ad = np.sqrt(np.sum(a**2)) - bd = np.sqrt(np.sum(b**2)) - cd = np.sqrt(np.sum(c**2)) + ad: np.float64 = np.sqrt(np.sum(a**2)) + bd: np.float64 = np.sqrt(np.sum(b**2)) + cd: np.float64 = np.sqrt(np.sum(c**2)) # Cell volume - V = np.dot(a, np.cross(b, c)) + V: np.float64 = np.dot(a, np.cross(b, c)) # F takes components in the direct lattice to X - F = np.c_[a, b, c] + F = np.stack([a, b, c], axis=1) # Reciprocal lattice vectors - astar = np.cross(b, c) / V - bstar = np.cross(c, a) / V - cstar = np.cross(a, b) / V + astar: NDArray[np.float64] = np.cross(b, c) / V + bstar: NDArray[np.float64] = np.cross(c, a) / V + cstar: NDArray[np.float64] = np.cross(a, b) / V # and parameters - ar = np.sqrt(np.sum(astar**2)) - br = np.sqrt(np.sum(bstar**2)) - cr = np.sqrt(np.sum(cstar**2)) + ar: np.float64 = np.sqrt(np.sum(astar**2)) + br: np.float64 = np.sqrt(np.sum(bstar**2)) + cr: np.float64 = np.sqrt(np.sum(cstar**2)) - alfar = np.arccos(np.dot(bstar, cstar) / br / cr) - betar = np.arccos(np.dot(cstar, astar) / cr / ar) - gamar = np.arccos(np.dot(astar, bstar) / ar / br) + alfar: np.float64 = np.arccos(np.dot(bstar, cstar) / br / cr) + betar: np.float64 = np.arccos(np.dot(cstar, astar) / cr / ar) + gamar: np.float64 = np.arccos(np.dot(astar, bstar) / ar / br) # B takes components in the reciprocal lattice to X - B = np.c_[astar, bstar, cstar] + B: NDArray[np.float64] = np.stack([astar, bstar, cstar], axis=1) + cosalfar2: np.float64 + sinalfar2: np.float64 cosalfar2, sinalfar2 = cosineXform(alfar, betar, gamar) - afable = ar * np.r_[1, 0, 0] - bfable = br * np.r_[np.cos(gamar), np.sin(gamar), 0] - cfable = ( + afable: NDArray[np.float64] = ar * np.array([1, 0, 0]) + bfable: NDArray[np.float64] = br * np.array([np.cos(gamar), np.sin(gamar), 0]) + cfable: NDArray[np.float64] = ( cr - * np.r_[ + * np.array([ np.cos(betar), -cosalfar2 * np.sin(betar), sinalfar2 * np.sin(betar), - ] + ]) ) - BR = np.c_[afable, bfable, cfable] - U0 = np.dot(B, np.linalg.inv(BR)) - dparms = np.r_[ad, bd, cd, np.r_[alpha, beta, gamma]] - rparms = np.r_[ar, br, cr, np.r_[alfar, betar, gamar]] + BR: NDArray[np.float64] = np.stack([afable, bfable, cfable], axis=1) + U0: NDArray[np.float64] = np.dot(B, np.linalg.inv(BR)) + dparms: NDArray[np.float64] = np.r_[ad, bd, cd, np.r_[alpha, beta, gamma]] + rparms: NDArray[np.float64] = np.r_[ar, br, cr, np.r_[alfar, betar, gamar]] return { 'F': F, @@ -664,7 +679,7 @@ class PlaneData(object): tThWidth """ - def __init__(self, hkls: Optional[np.ndarray], *args, **kwargs) -> None: + def __init__(self, hkls: np.ndarray, *args, **kwargs) -> None: """ Constructor for PlaneData @@ -711,7 +726,7 @@ def __init__(self, hkls: Optional[np.ndarray], *args, **kwargs) -> None: self._hkls = copy.deepcopy(hkls) self._strainMag = strainMag self._structFact = np.ones(self._hkls.shape[1]) - self.tThWidth: float = tThWidth + self.tThWidth: float | None = tThWidth # ... need to implement tThMin too if 'doTThSort' in kwargs: @@ -728,7 +743,7 @@ def __init__(self, hkls: Optional[np.ndarray], *args, **kwargs) -> None: ) # This is only used to calculate the structure factor if invalidated - self._unitcell: unitcell = None + self._unitcell: unitcell | None = None self._calc() @@ -1075,6 +1090,7 @@ def powder_intensity(self) -> np.ndarray: Powder intensity for each hkl. """ self._compute_sf_if_needed() + assert self._powder_intensity is not None return self._powder_intensity[~self.exclusions] @property @@ -1083,6 +1099,7 @@ def hedm_intensity(self) -> np.ndarray: HEDM (high energy x-ray diffraction microscopy) intensity for each hkl. """ self._compute_sf_if_needed() + assert self._hedm_intensity is not None return self._hedm_intensity[~self.exclusions] @staticmethod @@ -1093,7 +1110,7 @@ def makePlaneData( symmGroup, strainMag, wavelength, - ) -> Tuple[Dict[str, np.ndarray], Dict[str, Union[np.ndarray, float]], List[Dict]]: + ) -> Tuple[LatPlaneData, LatVecOps, List[HKLData]]: """ Generate lattice plane data from inputs. @@ -1168,19 +1185,17 @@ def makePlaneData( np.round(np.dot(latVecOps['F'].T, latPlnNrmls)), dtype='int' ) - hklDataList.append( - dict( - hklID=iHKL, - hkl=hkls[:, iHKL], - tTheta=latPlaneData['tThetas'][iHKL], - dSpacings=latPlaneData['dspacings'][iHKL], - tThetaLo=latPlaneData['tThetasLo'][iHKL], - tThetaHi=latPlaneData['tThetasHi'][iHKL], - latPlnNrmls=unitVector(latPlnNrmls), - symHKLs=symHKLs, - centrosym=csRefl, - ) - ) + hklDataList.append({ + 'hklID': iHKL, + 'hkl': hkls[:, iHKL], + 'tTheta': latPlaneData['tThetas'][iHKL], + 'dSpacings': latPlaneData['dspacings'][iHKL], + 'tThetaLo': latPlaneData['tThetasLo'][iHKL], + 'tThetaHi': latPlaneData['tThetasHi'][iHKL], + 'latPlnNrmls': unitVector(latPlnNrmls), + 'symHKLs': symHKLs, + 'centrosym': csRefl + }) return latPlaneData, latVecOps, hklDataList diff --git a/hexrd/core/material/material.py b/hexrd/core/material/material.py index b23822b4e..bf783c6e9 100644 --- a/hexrd/core/material/material.py +++ b/hexrd/core/material/material.py @@ -1070,7 +1070,7 @@ def to_cif_block(self) -> CifBlock: # Type symbols type_symbols = [] for a_type, charge in zip(self._atomtype.tolist(), list(self._charge)): - symbol = ptableinverse[int(a_type)] + symbol: str = ptableinverse[int(a_type)] if charge != '0': type_symbols.append(f"{symbol}{charge}") else: diff --git a/hexrd/core/material/utils.py b/hexrd/core/material/utils.py index 69f85030a..7c1d1a45d 100644 --- a/hexrd/core/material/utils.py +++ b/hexrd/core/material/utils.py @@ -7,6 +7,8 @@ ) import chemparse import numpy as np +from numpy.typing import NDArray + import h5py from copy import deepcopy from scipy.interpolate import interp1d @@ -270,7 +272,7 @@ def calculate_f_squared_mean( formula = interpret_formula(composition) norm_elemental_abundances = normalize_composition(formula) - res = 0 + res: NDArray[np.float64] = np.zeros_like(Q, dtype=np.float64) for key, value in norm_elemental_abundances.items(): res += value * calculate_coherent_scattering_factor(key, Q) ** 2 return res @@ -286,7 +288,7 @@ def calculate_f_mean_squared( formula = interpret_formula(composition) norm_elemental_abundances = normalize_composition(formula) - res = 0 + res: NDArray[np.float64] = np.zeros_like(Q, dtype=np.float64) for key, value in norm_elemental_abundances.items(): res += value * calculate_coherent_scattering_factor(key, Q) return res**2 @@ -295,14 +297,14 @@ def calculate_f_mean_squared( def calculate_incoherent_scattering( composition: str, Q: np.ndarray, -) -> np.ndarray: +) -> NDArray[np.float64]: if composition is None: return np.zeros_like(Q) formula = interpret_formula(composition) norm_elemental_abundances = normalize_composition(formula) - res = 0 + res: NDArray[np.float64] = np.zeros_like(Q, dtype=np.float64) for key, value in norm_elemental_abundances.items(): res += (value * calculate_incoherent_scattering_factor(key, Q)) ** 2 return res diff --git a/hexrd/core/utils/panel_buffer.py b/hexrd/core/utils/panel_buffer.py index c833b96ad..d87c9e43c 100644 --- a/hexrd/core/utils/panel_buffer.py +++ b/hexrd/core/utils/panel_buffer.py @@ -76,12 +76,14 @@ def panel_buffer_from_str(name: str, panel: Detector) -> np.ndarray: return roi_buffer +# TODO: Broken function - dir_path.glob is not defined def valid_panel_buffer_names() -> list[str]: dir_path = importlib.resources.files(hexrd.core.resources.panel_buffers) return [path.stem for path in dir_path.glob('*.npz')] # Cache this so we only read from disk once +# TODO: Broken function - path.exists is not defined @cache def _load_panel_buffer_from_file(name: str) -> np.ndarray: path = importlib.resources.files(hexrd.core.resources.panel_buffers).joinpath( @@ -90,7 +92,7 @@ def _load_panel_buffer_from_file(name: str) -> np.ndarray: if not path.exists(): raise NotImplementedError(f'Unknown panel buffer name: {name}') - npz = np.load(path) + npz = np.load(str(path)) buffer = npz['panel_buffer'] # Since the output here is memoized, make sure this is never modified diff --git a/hexrd/hed/xrdutil/utils.py b/hexrd/hed/xrdutil/utils.py index 7d5e69799..d4cf45fea 100644 --- a/hexrd/hed/xrdutil/utils.py +++ b/hexrd/hed/xrdutil/utils.py @@ -30,6 +30,8 @@ # TODO: Resolve extra-workflow dependency from hexrd.core.distortion.distortionabc import DistortionABC +from typing import Optional + import numpy as np from hexrd.core import constants @@ -72,7 +74,7 @@ def _project_on_detector_plane( tVec_d: np.ndarray, tVec_c: np.ndarray, tVec_s: np.ndarray, - distortion: DistortionABC, + distortion: Optional[DistortionABC] = None, beamVec: np.ndarray = constants.beam_vec, ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ @@ -114,7 +116,7 @@ def _project_on_detector_cylinder( radius: float, physical_size: np.ndarray, angle_extent: float, - distortion: DistortionABC = None, + distortion: Optional[DistortionABC] = None, beamVec: np.ndarray = constants.beam_vec, etaVec: np.ndarray = constants.eta_vec, tVec_s: np.ndarray = constants.zeros_3x1, diff --git a/hexrd/hedm/xrdutil/utils.py b/hexrd/hedm/xrdutil/utils.py index 99ed77353..b66f72c5b 100644 --- a/hexrd/hedm/xrdutil/utils.py +++ b/hexrd/hedm/xrdutil/utils.py @@ -27,13 +27,13 @@ # ============================================================ -from typing import Optional, Union, Any, Generator +from typing import Callable, Optional, Union, Any, Generator from hexrd.core.material.crystallography import PlaneData from hexrd.core.distortion.distortionabc import DistortionABC import numba import numpy as np -import numba +from numpy.typing import NDArray from hexrd.core import constants from hexrd.core import matrixutil as mutil @@ -488,8 +488,8 @@ def _fetch_hkls_from_planedata(pd: PlaneData): def _filter_hkls_eta_ome( hkls: np.ndarray, angles: np.ndarray, - eta_range: list[tuple[float]], - ome_range: list[tuple[float]], + eta_range: list[tuple[float, float]], + ome_range: list[tuple[float, float]], return_mask: bool = False, ) -> Union[tuple[np.ndarray, np.ndarray], tuple[np.ndarray, np.ndarray, np.ndarray]]: """ @@ -555,16 +555,12 @@ def simulateGVecs( pd: PlaneData, detector_params: np.ndarray, grain_params: np.ndarray, - ome_range: list[tuple[float]] = [ - (-np.pi, np.pi), - ], - ome_period: tuple[float] = (-np.pi, np.pi), - eta_range: list[tuple[float]] = [ - (-np.pi, np.pi), - ], - panel_dims: list[tuple[float]] = [(-204.8, -204.8), (204.8, 204.8)], - pixel_pitch: tuple[float] = (0.2, 0.2), - distortion: DistortionABC = None, + ome_range: list[tuple[float, float]] = [(-np.pi, np.pi)], + ome_period: tuple[float, float] = (-np.pi, np.pi), + eta_range: list[tuple[float, float]] = [(-np.pi, np.pi)], + panel_dims: list[tuple[float, float]] = [(-204.8, -204.8), (204.8, 204.8)], + pixel_pitch: tuple[float, float] = (0.2, 0.2), + distortion: Optional[DistortionABC] = None, beam_vector: np.ndarray = constants.beam_vec, energy_correction: Union[dict, None] = None, ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: @@ -637,11 +633,11 @@ def simulateGVecs( allAngs, allHKLs = _filter_hkls_eta_ome(full_hkls, angList, eta_range, ome_range) if len(allAngs) == 0: - valid_ids = [] - valid_hkl = [] - valid_ang = [] - valid_xy = [] - ang_ps = [] + valid_ids = np.array([]) + valid_hkl = np.array([]) + valid_ang = np.array([]) + valid_xy = np.array([]) + ang_ps = np.array([]) else: # ??? preallocate for speed? det_xy, rMat_ss, _ = _project_on_detector_plane( @@ -733,15 +729,15 @@ def _compute_max(tth: np.ndarray, eta: np.ndarray, result: np.ndarray) -> np.nda def angularPixelSize( xy_det: np.ndarray, - xy_pixelPitch: tuple[float], + xy_pixelPitch: tuple[float, float], rMat_d: np.ndarray, rMat_s: np.ndarray, tVec_d: np.ndarray, tVec_s: np.ndarray, tVec_c: np.ndarray, - distortion: DistortionABC = None, - beamVec: np.ndarray = None, - etaVec: np.ndarray = None, + distortion: Optional[DistortionABC] = None, + beamVec: Optional[np.ndarray] = None, + etaVec: Optional[np.ndarray] = None, ) -> np.ndarray: """ Calculate angular pixel sizes on a detector. @@ -798,7 +794,7 @@ def make_reflection_patches( tvec_c: np.ndarray = np.zeros((3, 1)), npdiv: int = 1, quiet: bool = False, # TODO: Remove this parameter - it isn't used - compute_areas_func: np.ndarray = gutil.compute_areas, + compute_areas_func: Callable = gutil.compute_areas, ) -> Generator[ tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray], None, diff --git a/mypy.ini b/mypy.ini index 3b581fb2b..2f873516d 100644 --- a/mypy.ini +++ b/mypy.ini @@ -15,3 +15,21 @@ ignore_missing_imports = True [mypy-numba.*] ignore_missing_imports = True + +[mypy-lmfit.*] +ignore_missing_imports = True + +[mypy-CifFile.*] +ignore_missing_imports = True + +[mypy-chemparse.*] +ignore_missing_imports = True + +[mypy-dectris.compression.*] +ignore_missing_imports = True + +[mypy-hexrd.core.constants.*] +ignore_errors = True + +[mypy-hexrd.powder.wppf.*] +ignore_errors = True From 4baf9f1a3dd1b306e93e0fc3eaca1185ac8eba10 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Fri, 30 Jan 2026 14:36:46 -0500 Subject: [PATCH 03/28] Continue typing effort --- hexrd/core/config/config.py | 4 +- hexrd/core/config/material.py | 10 +- hexrd/core/config/root.py | 12 +- hexrd/core/constants.py | 430 +++++++++--------- hexrd/core/deprecation.py | 4 +- hexrd/core/distortion/distortionabc.py | 2 +- hexrd/core/imageseries/baseclass.py | 9 +- hexrd/core/imageseries/load/__init__.py | 30 +- .../core/imageseries/load/eiger_stream_v2.py | 2 +- hexrd/core/imageseries/load/framecache.py | 4 +- hexrd/core/imageseries/load/metadata.py | 3 +- hexrd/core/imageseries/omega.py | 13 +- hexrd/core/imageseries/save.py | 1 - hexrd/core/instrument/hedm_instrument.py | 43 +- hexrd/core/material/crystallography.py | 22 +- hexrd/core/material/material.py | 8 +- hexrd/core/material/spacegroup.py | 20 +- hexrd/core/material/symmetry.py | 56 +-- hexrd/core/matrixutil.py | 5 +- hexrd/core/rotations.py | 10 +- hexrd/core/transforms/new_capi/xf_new_capi.py | 71 ++- hexrd/core/valunits.py | 56 +-- hexrd/hedm/config/__init__.py | 2 +- hexrd/hedm/config/findorientations.py | 29 +- hexrd/hedm/findorientations.py | 47 +- mypy.ini | 6 + 26 files changed, 444 insertions(+), 455 deletions(-) diff --git a/hexrd/core/config/config.py b/hexrd/core/config/config.py index fca196f45..fc0e53b57 100644 --- a/hexrd/core/config/config.py +++ b/hexrd/core/config/config.py @@ -19,8 +19,8 @@ class Config(object): _dirty = False - def __init__(self, cfg): - self._cfg = cfg + def __init__(self, cfg: 'Config'): + self._cfg = cfg # TODO: This should really be called "_parent" @property def parent(self): diff --git a/hexrd/core/config/material.py b/hexrd/core/config/material.py index bc84bd724..789213a0a 100644 --- a/hexrd/core/config/material.py +++ b/hexrd/core/config/material.py @@ -4,6 +4,7 @@ from hexrd.core import material from hexrd.core.constants import keVToAngstrom +from hexrd.core.material.crystallography import PlaneData from hexrd.core.valunits import valWUnit from .config import Config @@ -82,16 +83,13 @@ def exclusion_parameters(self): return self._exclusion_parameters @property - def plane_data(self): + def plane_data(self) -> PlaneData: """crystallographic information""" - # - # Only generate this once, not on each call. - # if not hasattr(self, "_plane_data"): self._plane_data = self._make_plane_data() return self._plane_data - def _make_plane_data(self): + def _make_plane_data(self) -> PlaneData: """Return the active material PlaneData class.""" pd = self.materials[self.active].planeData pd.tThWidth = np.radians(self.tthw) @@ -100,7 +98,7 @@ def _make_plane_data(self): return pd @property - def beam_energy(self): + def beam_energy(self) -> float: return keVToAngstrom(self.plane_data.wavelength) @beam_energy.setter diff --git a/hexrd/core/config/root.py b/hexrd/core/config/root.py index 92803b3e4..61fea330d 100644 --- a/hexrd/core/config/root.py +++ b/hexrd/core/config/root.py @@ -95,7 +95,7 @@ def fit_grains(self): return self._fitgrain_config @property - def instrument(self): + def instrument(self) -> Instrument: if not hasattr(self, '_instr_config'): instr_file = self.get('instrument', None) if instr_file is not None: @@ -104,11 +104,11 @@ def instrument(self): return self._instr_config @instrument.setter - def instrument(self, instr_config): + def instrument(self, instr_config: Instrument): self._instr_config = instr_config @property - def material(self): + def material(self) -> MaterialConfig: if not hasattr(self, '_material_config'): self._material_config = MaterialConfig(self) @@ -120,7 +120,7 @@ def material(self): return self._material_config @material.setter - def material(self, material_config): + def material(self, material_config: MaterialConfig): self._material_config = material_config @property @@ -174,10 +174,10 @@ def multiprocessing(self, val): ) @property - def image_series(self): + def image_series(self) -> dict[str, imageseries.omega.OmegaImageSeries]: """Return the imageseries dictionary.""" if not hasattr(self, '_image_dict'): - self._image_dict = dict() + self._image_dict: dict[str, imageseries.omega.OmegaImageSeries] = {} fmt = self.get('image_series:format') imsdata = self.get('image_series:data') for ispec in imsdata: diff --git a/hexrd/core/constants.py b/hexrd/core/constants.py index 1c58d4c0d..9fa446815 100644 --- a/hexrd/core/constants.py +++ b/hexrd/core/constants.py @@ -33,6 +33,7 @@ import platform import numpy as np +from numpy.typing import NDArray from scipy import constants as scipyc @@ -57,8 +58,8 @@ sqrt3by2 = 0.5 * sqrt3 # fwhm -sigma_to_fwhm = 2.0 * np.sqrt(2.0 * np.log(2.0)) -fwhm_to_sigma = 1.0 / sigma_to_fwhm +sigma_to_fwhm: float = 2.0 * np.sqrt(2.0 * np.log(2.0)) +fwhm_to_sigma: float = 1.0 / sigma_to_fwhm # tolerancing epsf = np.finfo(float).eps # ~2.2e-16 @@ -72,17 +73,17 @@ r2d = 180.0 / pi # identity arrays -identity_3x3 = np.eye(3) # (3, 3) identity -identity_6x1 = np.r_[1.0, 1.0, 1.0, 0.0, 0.0, 0.0] +identity_3x3 = np.eye(3, dtype=np.float64) # (3, 3) identity +identity_6x1 = np.array([1.0, 1.0, 1.0, 0.0, 0.0, 0.0], dtype=np.float64) # basis vectors -lab_x = np.r_[1.0, 0.0, 0.0] # X in the lab frame -lab_y = np.r_[0.0, 1.0, 0.0] # Y in the lab frame -lab_z = np.r_[0.0, 0.0, 1.0] # Z in the lab frame +lab_x = np.array([1.0, 0.0, 0.0], dtype=np.float64) # X in the lab frame +lab_y = np.array([0.0, 1.0, 0.0], dtype=np.float64) # Y in the lab frame +lab_z = np.array([0.0, 0.0, 1.0], dtype=np.float64) # Z in the lab frame -zeros_3 = np.zeros(3) -zeros_3x1 = np.zeros((3, 1)) -zeros_6x1 = np.zeros((6, 1)) +zeros_3 = np.zeros(3, dtype=np.float64) +zeros_3x1 = np.zeros((3, 1), dtype=np.float64) +zeros_6x1 = np.zeros((6, 1), dtype=np.float64) '''reference beam direction and eta=0 ref in LAB FRAME for standard geometry''' @@ -345,7 +346,7 @@ # for energy/wavelength conversions -def keVToAngstrom(x): +def keVToAngstrom(x) -> NDArray[np.float64]: return (1e7 * scipyc.c * scipyc.h / scipyc.e) / np.array(x, dtype=float) @@ -402,18 +403,18 @@ def is_writable_file(path): # some physical constants -cAvogadro = 6.02214076e23 # Avogadro's constant Na -cBoltzmann = 1.380649e-23 # Boltzmann's constant, K -cCharge = 1.602176634e-19 # charge of electron -cJ2eV = 1.602176565e-19 # joule to ev, JperkeV*1e-3 -cLight = 299792458.0 # speed of light, same as c above but name is more descriptive -cMoment = 9.2740100707e-24 # magnetic moment of electron -cPermea = 1.2566370616e-6 # permeability of free space -cPermit = 8.8541878163e-12 # permittivity of free space -cPlanck = 6.62607015e-34 # same as h above but name is more descriptive -cRestmass = 9.1093837090e-31 # rest mass of electron -cClassicalelectronRad = 2.8179403e-6 # classical electron radius in nm -cRestmasskeV = 510.99895069 # rest mass of electron in keV +cAvogadro: float = 6.02214076e23 # Avogadro's constant Na +cBoltzmann: float = 1.380649e-23 # Boltzmann's constant, K +cCharge: float = 1.602176634e-19 # charge of electron +cJ2eV: float = 1.602176565e-19 # joule to ev, JperkeV*1e-3 +cLight: float = 299792458.0 # speed of light, same as c above but name is more descriptive +cMoment: float = 9.2740100707e-24 # magnetic moment of electron +cPermea: float = 1.2566370616e-6 # permeability of free space +cPermit: float = 8.8541878163e-12 # permittivity of free space +cPlanck: float = 6.62607015e-34 # same as h above but name is more descriptive +cRestmass: float = 9.1093837090e-31 # rest mass of electron +cClassicalelectronRad: float = 2.8179403e-6 # classical electron radius in nm +cRestmasskeV: float = 510.99895069 # rest mass of electron in keV ''' adding another parametrization of the @@ -425,7 +426,7 @@ def is_writable_file(path): BY D. WAASMAIER AND A. KIRFEL Acta Cryst. (1995). A51,416-431 ''' -scatfac = { +scatfac: dict[str, list[float]] = { 'H': [ 0.413048, 0.294953, @@ -3171,7 +3172,7 @@ def is_writable_file(path): ], } -chargestate = { +chargestate: dict[str, list[str]] = { 'H': ['0', '1-'], 'He': ['0'], 'Li': ['0', '1+'], @@ -3276,7 +3277,7 @@ def is_writable_file(path): nuclear Thomson term fNT for all elements up to Z=92 ''' -fNT = { +fNT: dict[str, float] = { 'H': -0.00054423, 'He': -0.00054817, 'Li': -0.00071131, @@ -3374,7 +3375,7 @@ def is_writable_file(path): ''' relativistic correction factor for in anomalous scattering for all elements upto Z=92 ''' -frel = { +frel: dict[str, float] = { 'H': 0.0, 'He': 0.0, 'Li': -0.0006, @@ -3474,113 +3475,112 @@ def is_writable_file(path): atomic weights for things like density computations (from NIST elemental data base) ''' -atom_weights = np.array( - [ - 1.00794, - 4.002602, - 6.941, - 9.012182, - 10.811, - 12.0107, - 14.0067, - 15.9994, - 18.9984032, - 20.1797, - 22.98976928, - 24.3050, - 26.9815386, - 28.0855, - 30.973762, - 32.065, - 35.453, - 39.948, - 39.0983, - 40.078, - 44.955912, - 47.867, - 50.9415, - 51.9961, - 54.938045, - 55.845, - 58.933195, - 58.6934, - 63.546, - 65.38, - 69.723, - 72.64, - 74.92160, - 78.96, - 79.904, - 83.798, - 85.4678, - 87.62, - 88.90585, - 91.224, - 92.90638, - 95.96, - 98.9062, - 101.07, - 102.90550, - 106.42, - 107.8682, - 112.411, - 114.818, - 118.710, - 121.760, - 127.60, - 126.90447, - 131.293, - 132.9054519, - 137.327, - 138.90547, - 140.116, - 140.90765, - 144.242, - 145.0, - 150.36, - 151.964, - 157.25, - 158.92535, - 162.500, - 164.93032, - 167.259, - 168.93421, - 173.054, - 174.9668, - 178.49, - 180.94788, - 183.84, - 186.207, - 190.23, - 192.217, - 195.084, - 196.966569, - 200.59, - 204.3833, - 207.2, - 208.98040, - 209.0, - 210.0, - 222.0, - 223.0, - 226.0, - 227.0, - 232.03806, - 231.03588, - 238.02891, - 237.0, - 244.0, - 243.0, - 247.0, - 247.0, - 251.0, - ] -) +# TODO: Remove this, it's redundant with ATOM_WEIGHTS_DICT +atom_weights = np.array([ + 1.00794, + 4.002602, + 6.941, + 9.012182, + 10.811, + 12.0107, + 14.0067, + 15.9994, + 18.9984032, + 20.1797, + 22.98976928, + 24.3050, + 26.9815386, + 28.0855, + 30.973762, + 32.065, + 35.453, + 39.948, + 39.0983, + 40.078, + 44.955912, + 47.867, + 50.9415, + 51.9961, + 54.938045, + 55.845, + 58.933195, + 58.6934, + 63.546, + 65.38, + 69.723, + 72.64, + 74.92160, + 78.96, + 79.904, + 83.798, + 85.4678, + 87.62, + 88.90585, + 91.224, + 92.90638, + 95.96, + 98.9062, + 101.07, + 102.90550, + 106.42, + 107.8682, + 112.411, + 114.818, + 118.710, + 121.760, + 127.60, + 126.90447, + 131.293, + 132.9054519, + 137.327, + 138.90547, + 140.116, + 140.90765, + 144.242, + 145.0, + 150.36, + 151.964, + 157.25, + 158.92535, + 162.500, + 164.93032, + 167.259, + 168.93421, + 173.054, + 174.9668, + 178.49, + 180.94788, + 183.84, + 186.207, + 190.23, + 192.217, + 195.084, + 196.966569, + 200.59, + 204.3833, + 207.2, + 208.98040, + 209.0, + 210.0, + 222.0, + 223.0, + 226.0, + 227.0, + 232.03806, + 231.03588, + 238.02891, + 237.0, + 244.0, + 243.0, + 247.0, + 247.0, + 251.0, +]) """ dictionary of atomic weights """ -ATOM_WEIGHTS_DICT = { +ATOM_WEIGHTS_DICT: dict[str, float] = { 'H': 1.00794, 'He': 4.002602, 'Li': 6.941, @@ -3684,7 +3684,7 @@ def is_writable_file(path): """ densities of elements in g/cc """ -DENSITY = { +DENSITY: dict[str, float] = { 'H': 8.99e-05, 'He': 0.0001785, 'Li': 0.535, @@ -3786,7 +3786,7 @@ def is_writable_file(path): } # some polymer densities commonly used in hexrd -DENSITY_COMPOUNDS = { +DENSITY_COMPOUNDS: dict[str, float] = { 'C10H8O4': 1.4, 'Ba2263F2263Br1923I339C741H1730N247O494': 3.3, 'LiF': 2.64, @@ -3799,7 +3799,7 @@ def is_writable_file(path): dictionary of atomic numbers with element symbol as keys used in I/O from cif file ''' -ptable = { +ptable: dict[str, int] = { 'H': 1, 'He': 2, 'Li': 3, @@ -3918,90 +3918,88 @@ def is_writable_file(path): ''' listing the symmorphic space groups ''' -sgnum_symmorphic = np.array( - [ - 1, - 2, - 3, - 5, - 6, - 8, - 10, - 12, - 16, - 21, - 22, - 23, - 25, - 35, - 38, - 42, - 44, - 47, - 65, - 69, - 71, - 75, - 79, - 81, - 82, - 83, - 87, - 89, - 97, - 99, - 107, - 111, - 115, - 119, - 121, - 123, - 139, - 143, - 146, - 147, - 148, - 149, - 150, - 155, - 156, - 157, - 160, - 162, - 164, - 166, - 168, - 174, - 175, - 177, - 183, - 187, - 189, - 191, - 195, - 196, - 197, - 200, - 202, - 204, - 207, - 209, - 211, - 215, - 216, - 217, - 221, - 225, - 229, - ] -) +sgnum_symmorphic: NDArray[np.int32] = np.array([ + 1, + 2, + 3, + 5, + 6, + 8, + 10, + 12, + 16, + 21, + 22, + 23, + 25, + 35, + 38, + 42, + 44, + 47, + 65, + 69, + 71, + 75, + 79, + 81, + 82, + 83, + 87, + 89, + 97, + 99, + 107, + 111, + 115, + 119, + 121, + 123, + 139, + 143, + 146, + 147, + 148, + 149, + 150, + 155, + 156, + 157, + 160, + 162, + 164, + 166, + 168, + 174, + 175, + 177, + 183, + 187, + 189, + 191, + 195, + 196, + 197, + 200, + 202, + 204, + 207, + 209, + 211, + 215, + 216, + 217, + 221, + 225, + 229, +]) ''' this variable encodes all the generators (including translations) for all 230 space groups will be used to compute the full space group symmetry operators ''' -SYM_GL = [ +SYM_GL: list[str] = [ "000 ", "100 ", "01cOOO0 ", @@ -4255,7 +4253,7 @@ def is_writable_file(path): obv. this table only has the non-symmorphic groups taken from international table of crystallography vol A ''' -SYS_AB = { +SYS_AB: dict[int, list[list[str]]] = { 4: [['', '', ''], ['', '2_1', '']], 7: [['', 'c', ''], ['', '', '']], 9: [['', 'c', ''], ['', '', '']], @@ -4425,7 +4423,7 @@ def is_writable_file(path): ''' rotational, inversions, mirrors etc. components ''' -SYM_GENERATORS = {} +SYM_GENERATORS: dict[str, NDArray[np.float64]] = {} # now start to fill them in # identity @@ -4538,7 +4536,7 @@ def is_writable_file(path): used will be the same as the one used for the space group without any translations. ''' -SYM_GL_PG = { +SYM_GL_PG: dict[str, str] = { 'c1': '1a', # only identity rotation 'ci': '1h', # only inversion operation 'c2': '1c', # 2-fold rotation about z @@ -4573,8 +4571,8 @@ def is_writable_file(path): 'oh': '3dgh', } # The above dict must be in the correct order for this to work -SYM_PG_to_PGNUM = {pg: i + 1 for i, pg in enumerate(SYM_GL_PG)} -SYM_PGNUM_to_PG = {v: k for k, v in SYM_PG_to_PGNUM.items()} +SYM_PG_to_PGNUM: dict[str, int] = {pg: i + 1 for i, pg in enumerate(SYM_GL_PG)} +SYM_PGNUM_to_PG: dict[int, str] = {v: k for k, v in SYM_PG_to_PGNUM.items()} # Set the __version__ variable try: diff --git a/hexrd/core/deprecation.py b/hexrd/core/deprecation.py index a7201e632..eaa30e730 100644 --- a/hexrd/core/deprecation.py +++ b/hexrd/core/deprecation.py @@ -2,6 +2,8 @@ import functools import logging +from typing import Optional + logger = logging.getLogger(__name__) @@ -11,7 +13,7 @@ class DeprecatedFunctionError(Exception): pass -def deprecated(new_func: str = None, removal_date: str = None): +def deprecated(new_func: Optional[str] = None, removal_date: Optional[str] = None): """ Decorator to mark functions as deprecated. Raises an error if the 'ACK_DEPRECATED' environment variable is not set. Alerts the diff --git a/hexrd/core/distortion/distortionabc.py b/hexrd/core/distortion/distortionabc.py index f2eb2882a..6f6b9cb25 100644 --- a/hexrd/core/distortion/distortionabc.py +++ b/hexrd/core/distortion/distortionabc.py @@ -3,7 +3,7 @@ class DistortionABC(metaclass=abc.ABCMeta): - maptype = None + maptype: str | None = None @abc.abstractmethod def apply(self, xy_in): diff --git a/hexrd/core/imageseries/baseclass.py b/hexrd/core/imageseries/baseclass.py index 680422f61..6b9844f87 100644 --- a/hexrd/core/imageseries/baseclass.py +++ b/hexrd/core/imageseries/baseclass.py @@ -4,6 +4,7 @@ import numpy as np +from .load.framecache import ImageSeriesAdapter from .imageseriesabc import ImageSeriesABC, RegionType @@ -14,7 +15,7 @@ class ImageSeries(ImageSeriesABC): metadata (possibly None). """ - def __init__(self, adapter): + def __init__(self, adapter: ImageSeriesAdapter): """Build FrameSeries from adapter instance *adapter* - object instance based on abstract Sequence class with @@ -22,8 +23,6 @@ def __init__(self, adapter): """ self._adapter = adapter - return - def __getitem__(self, key): return self._adapter[key] @@ -49,10 +48,6 @@ def get_region(self, frame_idx: int, region: RegionType) -> np.ndarray: return self._adapter.get_region(frame_idx, region) def set_option(self, key: str, value: Any): - if not hasattr(self._adapter, 'set_option'): - msg = f'"{type(self._adapter)}" has not implemented "set_option"' - raise NotImplementedError(msg) - self._adapter.set_option(key, value) def option_values(self) -> dict: diff --git a/hexrd/core/imageseries/load/__init__.py b/hexrd/core/imageseries/load/__init__.py index ee6ba6815..45d63915f 100644 --- a/hexrd/core/imageseries/load/__init__.py +++ b/hexrd/core/imageseries/load/__init__.py @@ -1,6 +1,8 @@ import abc import pkgutil +from typing import Any import numpy as np +from numpy.typing import NDArray from ..imageseriesabc import ImageSeriesABC, RegionType from .registry import Registry @@ -15,12 +17,38 @@ def __init__(cls, name, bases, attrs): class ImageSeriesAdapter(ImageSeriesABC, metaclass=_RegisterAdapterClass): - format = None + format: str | None = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._dtype: np.dtype = None + + @property + def dtype(self): + return self._dtype + + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value def get_region(self, frame_idx: int, region: RegionType) -> np.ndarray: r = region return self[frame_idx][r[0][0] : r[0][1], r[1][0] : r[1][1]] + @property + def metadata(self) -> dict[str, NDArray[np.float64]]: + return {} + + @property + def shape(self) -> tuple[int, ...]: + raise NotImplementedError() + + def option_values(self) -> dict: + raise NotImplementedError() + + def set_option(self, key: str, value: Any): + raise NotImplementedError() + def __getitem__(self, _): pass diff --git a/hexrd/core/imageseries/load/eiger_stream_v2.py b/hexrd/core/imageseries/load/eiger_stream_v2.py index 9d0b554dd..ac0400838 100644 --- a/hexrd/core/imageseries/load/eiger_stream_v2.py +++ b/hexrd/core/imageseries/load/eiger_stream_v2.py @@ -190,7 +190,7 @@ def _first_threshold_1_entry(self): @property def dtype(self) -> np.dtype: if self.threshold_setting == 'man_diff': - return np.float64 + return np.dtype('float64') return np.dtype(self._first_threshold_1_entry['dtype'][()]) diff --git a/hexrd/core/imageseries/load/framecache.py b/hexrd/core/imageseries/load/framecache.py index d6a505e45..ca111a91d 100644 --- a/hexrd/core/imageseries/load/framecache.py +++ b/hexrd/core/imageseries/load/framecache.py @@ -57,14 +57,14 @@ def __init__(self, fname, style='npz', **kwargs): "'yaml', 'test'", ) - def _load_yml(self): + def _load_yml(self) -> None: with open(self._fname, "r") as f: d = yaml.load(f) datad = d['data'] self._cache = datad['file'] self._nframes = datad['nframes'] self._shape = tuple(datad['shape']) - self._dtype = np.dtype(datad['dtype']) + self._dtype: str = np.dtype(datad['dtype']) self._meta = yamlmeta(d['meta'], path=self._cache) def _load_cache(self): diff --git a/hexrd/core/imageseries/load/metadata.py b/hexrd/core/imageseries/load/metadata.py index edb0ca211..3decfaba9 100644 --- a/hexrd/core/imageseries/load/metadata.py +++ b/hexrd/core/imageseries/load/metadata.py @@ -4,9 +4,10 @@ import yaml import numpy as np +from numpy.typing import NDArray -def yamlmeta(meta, path=None): +def yamlmeta(meta, path=None) -> dict[str, NDArray[np.float64]]: """Image sequence metadata *path* is a full path or directory used to find the relative location diff --git a/hexrd/core/imageseries/omega.py b/hexrd/core/imageseries/omega.py index 21ac06cdc..41c2ddc83 100644 --- a/hexrd/core/imageseries/omega.py +++ b/hexrd/core/imageseries/omega.py @@ -4,11 +4,10 @@ """ import numpy as np +from numpy.typing import NDArray from .baseclass import ImageSeries -OMEGA_KEY = 'omega' - class OmegaImageSeries(ImageSeries): """ImageSeries with omega metadata""" @@ -16,11 +15,11 @@ class OmegaImageSeries(ImageSeries): DFLT_TOL = 1.0e-6 TAU = 360 - def __init__(self, ims): + def __init__(self, ims: ImageSeries): """This class is initialized with an existing imageseries""" # check for omega metadata - if OMEGA_KEY in ims.metadata: - self._omega = ims.metadata[OMEGA_KEY] + if 'omega' in ims.metadata: + self._omega: NDArray[np.float64] = ims.metadata['omega'] if len(ims) != self._omega.shape[0]: msg = 'omega array mismatch: array has %s frames, expecting %s' msg = msg % (self._omega.shape[0], len(ims)) @@ -69,7 +68,7 @@ def _make_wedges(self, tol=DFLT_TOL): assert nf0 == nf @property - def omega(self): + def omega(self) -> NDArray[np.float64]: """return omega range array (nframes, 2)""" return self._omega @@ -143,7 +142,7 @@ class OmegaWedges(object): def __init__(self, nframes): self.nframes = nframes - self._wedges = [] + self._wedges: list[dict[str, int]] = [] # # ============================== API diff --git a/hexrd/core/imageseries/save.py b/hexrd/core/imageseries/save.py index 5b672bd78..ba55a2e7b 100644 --- a/hexrd/core/imageseries/save.py +++ b/hexrd/core/imageseries/save.py @@ -7,7 +7,6 @@ import os import threading import warnings -import sys import numpy as np import h5py diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index 81bff3f27..c3e8d0ef5 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -1046,7 +1046,7 @@ def extract_polar_maps( threshold: Optional[float]=None, tth_tol: Optional[float]=None, eta_tol: float=0.25, - ) -> tuple[dict[str, np.ndarray], np.ndarray]: + ) -> tuple[dict[str, NDArray[np.float64]], NDArray[np.float64]]: """ Extract eta-omega maps from an imageseries. @@ -1634,7 +1634,7 @@ def _pull_spots_check_only( self, plane_data: PlaneData, grain_params: tuple | np.ndarray, - imgser_dict: dict, + imgser_dict: dict[str, OmegaImageSeries], tth_tol: float = 0.25, eta_tol: float = 1.0, ome_tol: float = 1.0, @@ -2543,22 +2543,14 @@ class GenerateEtaOmeMaps(object): def __init__( self, - image_series_dict, - instrument, - plane_data, - active_hkls=None, - eta_step=0.25, - threshold=None, - ome_period=(0, 360), + image_series_dict: dict[str, OmegaImageSeries], + instrument: HEDMInstrument, + plane_data: PlaneData, + active_hkls: Optional[NDArray[np.float64] | list[int]] = None, + eta_step: float=0.25, + threshold: Optional[float]=None, + ome_period: tuple[float, float]=(0, 360), # TODO: Remove this - it does nothing. ): - """ - image_series must be OmegaImageSeries class - instrument_params must be a dict (loaded from yaml spec) - active_hkls must be a list (required for now) - - FIXME: get rid of omega period; should get it from imageseries - """ - self._planeData = plane_data # ???: change name of iHKLList? @@ -2576,14 +2568,13 @@ def __init__( # grab a det key and corresponding imageseries (first will do) # !!! assuming that the imageseries for all panels # have the same length and omegas - det_key, this_det_ims = next(iter(image_series_dict.items())) + this_det_ims = next(iter(image_series_dict.values())) # handle omegas # !!! for multi wedge, enforncing monotonicity # !!! wedges also cannot overlap or span more than 360 omegas_array = this_det_ims.metadata['omega'] # !!! DEGREES - delta_ome = omegas_array[0][-1] - omegas_array[0][0] - frame_mask = None + frame_mask: Optional[NDArray[np.bool_]] = None ome_period = omegas_array[0, 0] + np.r_[0.0, 360.0] # !!! be careful if this_det_ims.omegawedges.nwedges > 1: delta_omes = [ @@ -2636,25 +2627,25 @@ def __init__( # pack all detectors with masking # FIXME: add omega masking - data_store = [] + data_store: list[NDArray[np.float64]] = [] for i_ring in range(n_rings): # first handle etas - full_map = np.zeros(map_shape, dtype=float) - nan_mask_full = np.zeros((len(eta_mapping), map_shape[0], map_shape[1])) + full_map: NDArray[np.float64] = np.zeros(map_shape, dtype=float) + nan_mask_full: NDArray[np.bool_] = np.zeros((len(eta_mapping), map_shape[0], map_shape[1])) i_p = 0 - for det_key, eta_map in eta_mapping.items(): + for eta_map in eta_mapping.values(): nan_mask = ~np.isnan(eta_map[i_ring]) nan_mask_full[i_p] = nan_mask full_map[nan_mask] += eta_map[i_ring][nan_mask] i_p += 1 - re_nan_these = np.sum(nan_mask_full, axis=0) == 0 + re_nan_these: NDArray[np.bool_] = np.sum(nan_mask_full, axis=0) == 0 full_map[re_nan_these] = np.nan # now omegas if frame_mask is not None: # !!! must expand row dimension to include # skipped omegas - tmp = np.ones((len(frame_mask), map_shape[1])) * np.nan + tmp: NDArray[np.float64] = np.ones((len(frame_mask), map_shape[1])) * np.nan tmp[frame_mask, :] = full_map full_map = tmp data_store.append(full_map) diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index 05a8f574b..05927ebef 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -94,8 +94,8 @@ def hklToStr(hkl: np.ndarray) -> str: def cosineXform( - a: np.ndarray, b: np.ndarray, c: np.ndarray -) -> tuple[np.float64, np.float64]: + a: NDArray[np.float64] | np.float64, b: NDArray[np.float64] | np.float64, c: NDArray[np.float64] | np.float64 +) -> tuple[NDArray[np.float64], NDArray[np.float64]] | tuple[float, float]: """ Spherical trig transform to take alpha, beta, gamma to expressions for cos(alpha*). See ref below. @@ -465,9 +465,9 @@ def latticeVectors( else: raise ValueError(f'lattice tag "{tag}" is not recognized') - alpha: float - beta: float - gamma: float + alpha: np.float64 + beta: np.float64 + gamma: np.float64 alpha, beta, gamma = cellparms[3:6] cosalfar, sinalfar = cosineXform(alpha, beta, gamma) @@ -1490,7 +1490,7 @@ def _getHKLID( f"hkl '{tuple(hkl)}' is not present in this material!" ) - def getHKLs(self, *hkl_ids: int, **kwargs) -> Union[List[str], np.ndarray]: + def getHKLs(self, *hkl_ids: int, **kwargs) -> List[str] | NDArray[np.float64]: """ Returns the powder HKLs subject to specified options. @@ -1603,7 +1603,7 @@ def getSymHKLs( List of symmetry HKLs for each HKL, either as strings or as a vstacked array. """ - sym_hkls = [] + sym_hkls: list[list[str] | NDArray[np.float64]] = [] hkl_index = 0 if indices is not None: indB = np.zeros(self.nHKLs, dtype=bool) @@ -2048,14 +2048,14 @@ def getFriedelPair(tth0, eta0, *ome0, **kwargs): return ome1, eta1 -def getDparms(lp: np.ndarray, lpTag: str, radians: Optional[bool] = True) -> np.ndarray: +def getDparms(lp: np.ndarray, lpTag: str, radians: bool = True) -> NDArray[np.float64]: """ Utility routine for getting dparms, that is the lattice parameters without symmetry -- 'triclinic' Parameters ---------- - lp : np.ndarray + lp : NDArray[np.float64] Parsed lattice parameters lpTag : str Tag for the symmetry group of the lattice (from Laue group) @@ -2064,7 +2064,7 @@ def getDparms(lp: np.ndarray, lpTag: str, radians: Optional[bool] = True) -> np. Returns ------- - np.ndarray + NDArray[np.float64] The lattice parameters without symmetry. """ latVecOps = latticeVectors(lp, tag=lpTag, radians=radians) @@ -2160,8 +2160,8 @@ def lorentz_factor(tth: np.ndarray) -> np.ndarray: def polarization_factor( tth: np.ndarray, - unpolarized: Optional[bool] = True, eta: Optional[np.ndarray] = None, + unpolarized: Optional[bool] = True, f_hor: Optional[float] = None, f_vert: Optional[float] = None, ) -> np.ndarray: diff --git a/hexrd/core/material/material.py b/hexrd/core/material/material.py index bf783c6e9..30cebec3c 100644 --- a/hexrd/core/material/material.py +++ b/hexrd/core/material/material.py @@ -36,7 +36,7 @@ from configparser import ConfigParser as Parser import numpy as np -from hexrd.core.material.crystallography import PlaneData as PData +from hexrd.core.material.crystallography import PlaneData from hexrd.core.material import symmetry, unitcell from hexrd.core.material.symbols import two_origin_choice from hexrd.core.valunits import _angstroms, _degrees, _kev, valWUnit @@ -312,7 +312,7 @@ def _newPdata(self): return # Copy over attributes from the previous PlaneData object - self._pData = PData(hkls, old_pdata, exclusions=None) + self._pData = PlaneData(hkls, old_pdata, exclusions=None) # Get a mapping to new hkl indices old_indices, new_indices = map_hkls_to_new(old_pdata, self._pData) @@ -330,7 +330,7 @@ def _newPdata(self): # Make the PlaneData object from scratch... lprm = self.reduced_lattice_parameters laue = self.unitcell._laueGroup - self._pData = PData( + self._pData = PlaneData( hkls, lprm, laue, @@ -1278,7 +1278,7 @@ def unitcell(self): return self._unitcell @property - def planeData(self): + def planeData(self) -> PlaneData: """(read only) Return the planeData attribute (lattice parameters)""" return self._pData diff --git a/hexrd/core/material/spacegroup.py b/hexrd/core/material/spacegroup.py index 896ada8e9..c9b98281b 100644 --- a/hexrd/core/material/spacegroup.py +++ b/hexrd/core/material/spacegroup.py @@ -77,6 +77,7 @@ from hexrd.core.material import symbols, symmetry import numpy as np +from numpy.typing import NDArray if TYPE_CHECKING: from hexrd.core.material import Material @@ -376,7 +377,7 @@ def _sgrange(min, max): } -def get_symmetry_directions(mat: 'Material') -> np.ndarray: +def get_symmetry_directions(mat: 'Material') -> NDArray[np.int32]: """ helper function to get a list of primary, secondary and tertiary directions of the @@ -427,13 +428,11 @@ def get_symmetry_directions(mat: 'Material') -> np.ndarray: secondary = [1, 1, 1] tertiary = [1, 1, 0] - return np.array([primary, secondary, tertiary]) + return np.array([primary, secondary, tertiary], dtype=np.int32) -def Allowed_HKLs(sgnum, hkllist): - """ - this function checks if a particular g vector is allowed - by lattice centering, screw axis or glide plane +def Allowed_HKLs(sgnum: int, hkllist: NDArray[np.int32]) -> NDArray[np.int32]: + """ Checks if a g vector is allowed by lattice centering, screw axis or glide plane """ sg_hmsymbol = symbols.pstr_spacegroup[sgnum - 1].strip() symmorphic = False @@ -445,12 +444,7 @@ def Allowed_HKLs(sgnum, hkllist): centering = sg_hmsymbol[0] if centering == 'P': # all reflections are allowed - mask = np.ones( - [ - hkllist.shape[0], - ], - dtype=bool, - ) + mask = np.ones([hkllist.shape[0]], dtype=bool) elif centering == 'F': # same parity seo = np.sum(np.mod(hkllist + 100, 2), axis=1) @@ -476,7 +470,7 @@ def Allowed_HKLs(sgnum, hkllist): seo = np.mod(-hkllist[:, 0] + hkllist[:, 1] + hkllist[:, 2] + 90, 3) mask = seo == 0 else: - raise RuntimeError('IsGAllowed: unknown lattice centering encountered.') + raise ValueError(f'Unknown lattice centering: "{centering}"') hkls = hkllist[mask, :] if not symmorphic: diff --git a/hexrd/core/material/symmetry.py b/hexrd/core/material/symmetry.py index 0aaabb041..26539bdd1 100644 --- a/hexrd/core/material/symmetry.py +++ b/hexrd/core/material/symmetry.py @@ -28,9 +28,11 @@ # -*-python-*- # # Module containing functions relevant to symmetries +from typing import Literal import numpy as np from numba import njit +from numpy.typing import NDArray from numpy import array, sqrt, pi, vstack, c_, dot, argmax # from hexrd.core.rotations import quatOfAngleAxis, quatProductMatrix, fixQuat @@ -63,7 +65,7 @@ # ============================================================================= -def GeneratorString(sgnum): +def GeneratorString(sgnum: int) -> str: ''' these rhombohedral space groups have a hexagonal setting with different symmetry matrices and generator strings @@ -80,10 +82,8 @@ def GeneratorString(sgnum): return constants.SYM_GL[sg] -def MakeGenerators(genstr, setting): - - t = 'aOOO' - mat = SYM_fillgen(t) +def MakeGenerators(genstr: str, setting: int) -> tuple[NDArray[np.float64], bool]: + mat = SYM_fillgen('aOOO') genmat = mat # genmat[0,:,:] = constants.SYM_GENERATORS['a'] @@ -91,33 +91,26 @@ def MakeGenerators(genstr, setting): # check if space group has inversion symmetry if genstr[0] == '1': - t = 'hOOO' - mat = SYM_fillgen(t) + mat = SYM_fillgen('hOOO') genmat = np.concatenate((genmat, mat)) centrosymmetric = True + istop = 2 n = int(genstr[1]) if n > 0: for i in range(n): istart = 2 + i * 4 istop = 2 + (i + 1) * 4 - t = genstr[istart:istop] - - mat = SYM_fillgen(t) + mat = SYM_fillgen(genstr[istart:istop]) genmat = np.concatenate((genmat, mat)) - else: - istop = 2 - ''' - if there is an alternate setting for this space group - check if the alternate setting needs to be used - ''' + + # if there is an alternate setting for this space group check if the alternate + # setting needs to be used if genstr[istop] != '0': if setting != 0: - t = genstr[istop + 1 : istop + 4] - t = 'a' + t # get the translation without any rotation - sym = np.squeeze(SYM_fillgen(t, sgn=-1)) - sym2 = np.squeeze(SYM_fillgen(t)) + sym = np.squeeze(SYM_fillgen('a' + genstr[istop + 1 : istop + 4], sgn=-1)) + sym2 = np.squeeze(SYM_fillgen('a' + genstr[istop + 1 : istop + 4])) for i in range(1, genmat.shape[0]): generator = np.dot(sym2, np.dot(np.squeeze(genmat[i, :, :]), sym)) frac = np.modf(generator[0:3, 3])[0] @@ -130,28 +123,24 @@ def MakeGenerators(genstr, setting): return genmat, centrosymmetric -def SYM_fillgen(t, sgn=1): +def SYM_fillgen(t: str, sgn: Literal[1, -1] = 1) -> NDArray[np.float64]: mat = np.zeros([4, 4]) mat[3, 3] = 1.0 mat[0:3, 0:3] = constants.SYM_GENERATORS[t[0]] - mat[0:3, 3] = sgn * np.array( - [ - constants.SYM_GENERATORS[t[1]], - constants.SYM_GENERATORS[t[2]], - constants.SYM_GENERATORS[t[3]], - ] - ) + mat[0:3, 3] = sgn * np.array([ + constants.SYM_GENERATORS[t[1]], + constants.SYM_GENERATORS[t[2]], + constants.SYM_GENERATORS[t[3]], + ]) mat = np.broadcast_to(mat, [1, 4, 4]) return mat @memoize(maxsize=20) -def GenerateSGSym(sgnum, setting=0): - ''' - get the generators for a space group using the - generator string +def GenerateSGSym(sgnum: int, setting: int = 0) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64], bool, bool]: + ''' Get the generators for a space group using the generator string ''' genstr = GeneratorString(sgnum) genmat, centrosymmetric = MakeGenerators(genstr, setting) @@ -159,8 +148,7 @@ def GenerateSGSym(sgnum, setting=0): if sgnum in constants.sgnum_symmorphic: symmorphic = True ''' - use the generator string to get the rest of the - factor group + use the generator string to get the rest of the factor group genmat has shape ngenerators x 4 x 4 ''' diff --git a/hexrd/core/matrixutil.py b/hexrd/core/matrixutil.py index 484d1548e..68a33885d 100644 --- a/hexrd/core/matrixutil.py +++ b/hexrd/core/matrixutil.py @@ -29,6 +29,7 @@ import numpy as np from numpy.linalg import svd +from numpy.typing import NDArray from scipy import sparse import numba @@ -419,7 +420,7 @@ def multMatArray(ma1, ma2): return prod -def uniqueVectors(v, tol=1.0e-12): +def uniqueVectors(v: NDArray[np.float64], tol: float=1.0e-12): """ Sort vectors and discard duplicates. @@ -437,7 +438,7 @@ def uniqueVectors(v, tol=1.0e-12): iv = np.zeros(vdims) for row in range(vdims[0]): - tmpord = np.argsort(v[row, :]).tolist() + tmpord: list[int] = np.argsort(v[row, :]).tolist() tmpsrt = v[np.ix_([row], tmpord)].squeeze() tmpcmp = abs(tmpsrt[1:] - tmpsrt[0:-1]) indep = np.hstack([True, tmpcmp > tol]) # independent values diff --git a/hexrd/core/rotations.py b/hexrd/core/rotations.py index 519ef090a..1ca8235ff 100644 --- a/hexrd/core/rotations.py +++ b/hexrd/core/rotations.py @@ -32,6 +32,8 @@ import numpy as np from numba import njit +from numpy.typing import NDArray +from typing import Literal, Optional from scipy.optimize import leastsq from scipy.spatial.transform import Rotation as R @@ -1103,13 +1105,14 @@ def discreteFiber(c, s, B=I3, ndiv=120, invert=False, csym=None, ssym=None): # -def mapAngle(ang, ang_range=None, units=angularUnits): +def mapAngle(ang, ang_range: Optional[NDArray[np.float64]]=None, + units: Literal['degrees', 'radians']=angularUnits) -> NDArray[np.float64]: """ Utility routine to map an angle into a specified period """ - if units.lower() == 'degrees': + if units == 'degrees': period = 360.0 - elif units.lower() == 'radians': + elif units == 'radians': period = 2.0 * np.pi else: raise RuntimeError("unknown angular units: " + units) @@ -1136,6 +1139,7 @@ def mapAngle(ang, ang_range=None, units=angularUnits): return val +# TODO: Delete this function. def angularDifference_orig(angList0, angList1, units=angularUnits): # pragma: no cover """ Do the proper (acute) angular difference in the context of a branch cut. diff --git a/hexrd/core/transforms/new_capi/xf_new_capi.py b/hexrd/core/transforms/new_capi/xf_new_capi.py index 16646fc90..75f600af1 100644 --- a/hexrd/core/transforms/new_capi/xf_new_capi.py +++ b/hexrd/core/transforms/new_capi/xf_new_capi.py @@ -25,6 +25,7 @@ """ from typing import Optional, Tuple, Union import numpy as np +from numpy.typing import NDArray from hexrd.core.extensions import _new_transforms_capi as _impl from hexrd.core.extensions import transforms as cpp_transforms @@ -33,12 +34,12 @@ def angles_to_gvec( - angs: np.ndarray, - beam_vec: Optional[np.ndarray] = None, - eta_vec: Optional[np.ndarray] = None, - chi: Optional[float] = None, - rmat_c: Optional[np.ndarray] = None, -) -> np.ndarray: + angs: NDArray[np.float64], + beam_vec: NDArray[np.float64] = cnst.beam_vec, + eta_vec: NDArray[np.float64] = cnst.eta_vec, + chi: float = 0.0, + rmat_c: NDArray[np.float64] = cnst.identity_3x3, +) -> NDArray[np.float64]: """ Takes triplets of angles in the beam frame (2*theta, eta[, omega]) @@ -80,12 +81,9 @@ def angles_to_gvec( angs = np.hstack((angs, np.zeros(angs.shape[:-1] + (1,)))) angs = np.ascontiguousarray(np.atleast_2d(angs)) - beam_vec = beam_vec if beam_vec is not None else cnst.beam_vec beam_vec = np.ascontiguousarray(beam_vec.flatten()) - eta_vec = eta_vec if eta_vec is not None else cnst.eta_vec eta_vec = np.ascontiguousarray(eta_vec.flatten()) - chi = 0.0 if chi is None else float(chi) - rmat_c = cnst.identity_3x3 if rmat_c is None else np.ascontiguousarray(rmat_c) + rmat_c = np.ascontiguousarray(rmat_c) result = cpp_transforms.anglesToGVec(angs, beam_vec, eta_vec, chi, rmat_c) @@ -93,13 +91,13 @@ def angles_to_gvec( def angles_to_dvec( - angs: np.ndarray, - beam_vec: Optional[np.ndarray] = None, - eta_vec: Optional[np.ndarray] = None, - chi: Optional[float] = None, - rmat_c: Optional[np.ndarray] = None, -) -> np.ndarray: - """ + angs: NDArray[np.float64], + beam_vec: NDArray[np.float64] = cnst.beam_vec, + eta_vec: NDArray[np.float64] = cnst.eta_vec, + chi: float = 0.0, + rmat_c: NDArray[np.float64] = cnst.identity_3x3, +) -> NDArray[np.float64]: + """ Calculate diffraction vectors from beam frame angles. Takes triplets of angles in the beam frame (2*theta, eta[, omega]) to components of unit diffraction vectors in the LAB frame. If the omega @@ -131,8 +129,6 @@ def angles_to_dvec( parameters """ # TODO: Improve capi to avoid multiplications when rmat_c is None - beam_vec = beam_vec if beam_vec is not None else cnst.beam_vec - eta_vec = eta_vec if eta_vec is not None else cnst.eta_vec # if only a pair is provided... convert to a triplet with omegas == 0 # so that behavior is preserved. @@ -140,16 +136,14 @@ def angles_to_dvec( angs = np.hstack((angs, np.zeros(angs.shape[:-1] + (1,)))) angs = np.ascontiguousarray(np.atleast_2d(angs)) - beam_vec = np.ascontiguousarray(beam_vec.flatten()) eta_vec = np.ascontiguousarray(eta_vec.flatten()) - rmat_c = np.ascontiguousarray(rmat_c) if rmat_c is not None else cnst.identity_3x3 - chi = 0.0 if chi is None else float(chi) + rmat_c = np.ascontiguousarray(rmat_c) return cpp_transforms.anglesToDVec(angs, beam_vec, eta_vec, chi, rmat_c) -def makeGVector(hkl: np.ndarray, bMat: np.ndarray) -> np.ndarray: +def makeGVector(hkl: NDArray[np.float64], bMat: NDArray[np.float64]) -> NDArray[np.float64]: """ Take a crystal relative b matrix onto a list of hkls to output unit reciprocal latice vectors (a.k.a. lattice plane normals) @@ -171,7 +165,8 @@ def makeGVector(hkl: np.ndarray, bMat: np.ndarray) -> np.ndarray: plane normals) """ - assert hkl.shape[0] == 3, 'hkl input must be (3, n)' + if hkl.shape[0] != 3: + raise ValueError('hkl input must be (3, n)') return unit_vector(np.dot(bMat, hkl)) @@ -451,7 +446,7 @@ def oscill_angles_of_hkls( ) -def unit_vector(vec_in: np.ndarray) -> np.ndarray: +def unit_vector(vec_in: NDArray[np.float64]) -> NDArray[np.float64]: """ Normalize the input vector(s) to unit length. @@ -542,7 +537,7 @@ def make_sample_rmat(chi: float, ome: Union[float, np.ndarray]) -> np.ndarray: return result -def make_rmat_of_expmap(exp_map: tuple[float] | np.ndarray) -> np.ndarray: +def make_rmat_of_expmap(exp_map: tuple[float] | NDArray[np.float64]) -> NDArray[np.float64]: """ Calculate the rotation matrix of an exponential map. @@ -553,14 +548,14 @@ def make_rmat_of_expmap(exp_map: tuple[float] | np.ndarray) -> np.ndarray: Returns ------- - ndarray + NDArray[np.float64] A 3x3 rotation matrix representing the input exponential map """ arg = np.ascontiguousarray(exp_map.flatten()) return cpp_transforms.make_rot_mat_of_exp_map(arg) -def make_binary_rmat(axis: np.ndarray) -> np.ndarray: +def make_binary_rmat(axis: NDArray[np.float64]) -> NDArray[np.float64]: """ Create a 180 degree rotation matrix about the input axis. @@ -580,7 +575,7 @@ def make_binary_rmat(axis: np.ndarray) -> np.ndarray: return cpp_transforms.make_binary_rot_mat(arg) -def make_beam_rmat(bvec_l: np.ndarray, evec_l: np.ndarray) -> np.ndarray: +def make_beam_rmat(bvec_l: NDArray[np.float64], evec_l: NDArray[np.float64]) -> NDArray[np.float64]: """ Creates a COB matrix from the beam frame to the lab frame Note: beam and eta vectors must not be colinear @@ -600,11 +595,11 @@ def make_beam_rmat(bvec_l: np.ndarray, evec_l: np.ndarray) -> np.ndarray: def validate_angle_ranges( - ang_list: Union[float, np.ndarray], - start_angs: Union[float, np.ndarray], - stop_angs: Union[float, np.ndarray], + ang_list: float | NDArray[np.float64], + start_angs: float | NDArray[np.float64], + stop_angs: float | NDArray[np.float64], ccw: bool = True, -) -> np.ndarray[bool]: +) -> NDArray[np.bool_]: """ Find out if angles are in the CCW or CW range from start to stop @@ -634,8 +629,8 @@ def validate_angle_ranges( def rotate_vecs_about_axis( - angle: np.ndarray, axis: np.ndarray, vecs: np.ndarray -) -> np.ndarray: + angle: float | NDArray[np.float64], axis: NDArray[np.float64], vecs: NDArray[np.float64] +) -> NDArray[np.float64]: """ Rotate vectors about an axis @@ -660,10 +655,8 @@ def rotate_vecs_about_axis( return result.T -def quat_distance(q1: np.ndarray, q2: np.ndarray, qsym: np.ndarray) -> np.ndarray: - """ - Distance between two quaternions, taking quaternions of symmetry into - account. +def quat_distance(q1: NDArray[np.float64], q2: NDArray[np.float64], qsym: NDArray[np.float64]) -> NDArray[np.float64]: + """ Distance between two quaternions, taking quaternions of symmetry into account. Parameters ---------- diff --git a/hexrd/core/valunits.py b/hexrd/core/valunits.py index dbbe5037e..3538026d7 100644 --- a/hexrd/core/valunits.py +++ b/hexrd/core/valunits.py @@ -57,7 +57,7 @@ energy=energyUN, ) - +# TODO: Remove this class, it only adds complexity. class UNames(object): """Units used in this module""" @@ -98,34 +98,31 @@ class UNames(object): } +# Value with units class valWUnit: - "Value with units" "" - - def __init__(self, name, unitType, value, unit): - """Initialization - - INPUTS - name - (str) name of the item - unitType - (str) class of units, e.g. 'length', 'angle' - value - (float) numerical value - unit - (str) name of unit + def __init__(self, name: str, unitType: str, value: float, unit: str): + """ + Parameters + ---------- + name : str + Name of the item. + unitType : str + Class of units, e.g., 'length', 'angle', 'energy'. + value : float + Numerical value. + unit : str + Name of the unit. """ self.name = name if unitType in uTDict: self.uT = uTDict[unitType] else: + # TODO: What does the below comment mean? Is this safe? # trust that unitType is correct -- may be a combined type self.uT = unitType self.value = value self.unit = unit - # - # Original checked if unit is of unitType - # def __str__(self): tmpl = """item named "%s" representing %g %s""" @@ -135,7 +132,7 @@ def __repr__(self): tmpl = 'valWUnit("%s","%s",%s,"%s")' return tmpl % (self.name, self.uT, self.value, self.unit) - def __mul__(self, other): + def __mul__(self, other: 'valWUnit | float') -> 'valWUnit': if isinstance(other, float): new = valWUnit(self.name, self.uT, self.value * other, self.unit) return new @@ -151,7 +148,7 @@ def __mul__(self, other): else: raise RuntimeError("mul with unsupported operand") - def __add__(self, other): + def __add__(self, other: 'valWUnit | float') -> 'valWUnit': if isinstance(other, float): new = valWUnit(self.name, self.uT, self.value + other, self.unit) return new @@ -166,7 +163,7 @@ def __add__(self, other): else: raise RuntimeError("add with unsupported operand") - def __sub__(self, other): + def __sub__(self, other: 'valWUnit | float') -> 'valWUnit': if isinstance(other, float): new = valWUnit(self.name, self.uT, self.value - other, self.unit) return new @@ -181,7 +178,7 @@ def __sub__(self, other): else: raise RuntimeError("add with unsupported operand") - def _convert(self, toUnit): + def _convert(self, toUnit: str) -> float: """ Return the value of self in requested units. @@ -227,20 +224,17 @@ def _convert(self, toUnit): f"Unit conversion '{from_to[0]} --> " + f"{from_to[1]}' not recognized" ) - def isLength(self): + def isLength(self) -> bool: """Return true if quantity is a length""" - retval = self.uT == uTDict['length'] - return retval + return self.uT == uTDict['length'] - def isAngle(self): + def isAngle(self) -> bool: """Return true if quantity is an angle""" - retval = self.uT == uTDict['angle'] - return retval + return self.uT == uTDict['angle'] - def isEnergy(self): + def isEnergy(self) -> bool: """Return true if quantity is an energy""" - retval = self.uT == uTDict['energy'] - return retval + return self.uT == uTDict['energy'] def getVal(self, toUnit): """ diff --git a/hexrd/hedm/config/__init__.py b/hexrd/hedm/config/__init__.py index 9df629a75..7d439f05d 100644 --- a/hexrd/hedm/config/__init__.py +++ b/hexrd/hedm/config/__init__.py @@ -15,7 +15,7 @@ open_file = open -def open(file_name=None): +def open(file_name=None) -> list[root.RootConfig]: """ Reads configuration settings from a yaml file. diff --git a/hexrd/hedm/config/findorientations.py b/hexrd/hedm/config/findorientations.py index 7ef00bcfa..b4bf32253 100644 --- a/hexrd/hedm/config/findorientations.py +++ b/hexrd/hedm/config/findorientations.py @@ -87,7 +87,7 @@ def omega(self): # Simple Values @property - def threshold(self): + def threshold(self) -> int: return self._cfg.get('find_orientations:threshold', 1) @property @@ -110,7 +110,7 @@ def extract_measured_g_vectors(self): class ClusteringConfig(Config): @property - def algorithm(self): + def algorithm(self) -> str: key = 'find_orientations:clustering:algorithm' choices = ['dbscan', 'ort-dbscan', 'sph-dbscan', 'fclusterdata'] temp = self._cfg.get(key, 'dbscan').lower() @@ -121,7 +121,7 @@ def algorithm(self): ) @property - def completeness(self): + def completeness(self) -> float: key = 'find_orientations:clustering:completeness' temp = self._cfg.get(key, None) if temp is not None: @@ -129,7 +129,7 @@ def completeness(self): raise RuntimeError('"%s" must be specified' % key) @property - def radius(self): + def radius(self) -> float: key = 'find_orientations:clustering:radius' temp = self._cfg.get(key, None) if temp is not None: @@ -224,20 +224,19 @@ def fiber_ndiv(self): class OrientationMapsConfig(Config): - @property - def active_hkls(self): - temp = self._cfg.get( + def active_hkls(self) -> int | list[int] | None: + hkls: int | str | None = self._cfg.get( 'find_orientations:orientation_maps:active_hkls', default='all' ) - if isinstance(temp, int): - temp = [temp] - if temp == 'all': - temp = None - return temp + if isinstance(hkls, int): + hkls = [hkls] + if hkls == 'all': + hkls = None + return hkls @property - def bin_frames(self): + def bin_frames(self) -> int: return self._cfg.get('find_orientations:orientation_maps:bin_frames', default=1) @property @@ -291,11 +290,11 @@ def scored_orientations_file(self): return sof @property - def threshold(self): + def threshold(self) -> int: return self._cfg.get('find_orientations:orientation_maps:threshold') @property - def filter_maps(self): + def filter_maps(self) -> bool: return self._cfg.get( 'find_orientations:orientation_maps:filter_maps', default=False ) diff --git a/hexrd/hedm/findorientations.py b/hexrd/hedm/findorientations.py index fa9708ce8..2d60cc10a 100644 --- a/hexrd/hedm/findorientations.py +++ b/hexrd/hedm/findorientations.py @@ -4,7 +4,10 @@ import os import timeit +from typing import Optional + import numpy as np +from numpy.typing import NDArray # np.seterr(over='ignore', invalid='ignore') @@ -15,12 +18,15 @@ from hexrd.core import constants as const from hexrd.core import matrixutil as mutil +from hexrd.core.imageseries.omega import OmegaImageSeries +from hexrd.core.material.crystallography import PlaneData from hexrd.hedm import indexer from hexrd.core import instrument from hexrd.core.imageutil import find_peaks_2d from hexrd.core import rotations as rot from hexrd.core.transforms import xfcapi from hexrd.hedm.xrdutil import EtaOmeMaps +from hexrd.hedm.config import root # just require scikit-learn? have_sklearn = False @@ -60,7 +66,7 @@ def write_scored_orientations(results, cfg): ) -def _process_omegas(omegaimageseries_dict): +def _process_omegas(omegaimageseries_dict: dict[str, OmegaImageSeries]) -> tuple[NDArray[np.float64], list[list[int]]]: """Extract omega period and ranges from an OmegaImageseries dictionary.""" oims = next(iter(omegaimageseries_dict.values())) ome_period = oims.omega[0, 0] + np.r_[0.0, 360.0] @@ -367,7 +373,9 @@ def quat_distance(x, y): return np.atleast_2d(qbar), cl -def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): +# TODO: Remove image_series from this function signature. +# TODO: Remove pd from this function signature. +def load_eta_ome_maps(cfg: root.RootConfig, pd, image_series, hkls: Optional[NDArray[np.float64] | list[int]] = None, clean: bool=False): """ Load the eta-ome maps specified by the config and CLI flags. If the maps file exists, it will return those values. If the file does not exist, @@ -378,10 +386,8 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): ---------- cfg: Config instance root config file for this problem - pd: PlaneData instance - crystallographic properties - image_series: ImageSeries instance - stack of images + pd: Not used + image_series: Not used hkls: list, default = None list of HKLs used to generate the eta-omega maps clean: bool, default = False @@ -393,15 +399,15 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): list of eta-omega map arrays """ - fn = cfg.find_orientations.orientation_maps.file + filename = cfg.find_orientations.orientation_maps.file if clean: logger.info('clean option specified; recomputing eta/ome orientation maps') res = generate_eta_ome_maps(cfg, hkls=hkls) else: try: - res = EtaOmeMaps(str(fn)) + res = EtaOmeMaps(str(filename)) pd = res.planeData - logger.info(f'loaded eta/ome orientation maps from {fn}') + logger.info(f'loaded eta/ome orientation maps from {filename}') shkls = pd.getHKLs(*res.iHKLList, asStr=True) logger.info( 'hkls used to generate orientation maps: %s', @@ -409,7 +415,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): ) except (AttributeError, IOError): logger.warning( - f"specified maps file '{str(fn)}' not found " + f"specified maps file '{filename}' not found " f"and clean option not specified; " f"recomputing eta/ome orientation maps" ) @@ -419,24 +425,17 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): return res -def filter_maps_if_requested(eta_ome, cfg): - # filter if requested - filter_maps = cfg.find_orientations.orientation_maps.filter_maps +def filter_maps_if_requested(eta_ome, cfg: root.RootConfig): # !!! current logic: # if False/None don't do anything # if True, only do median subtraction # if scalar, do median + LoG filter with that many pixels std dev - if filter_maps: - if not isinstance(filter_maps, bool): - sigm = const.fwhm_to_sigma * filter_maps - logger.info("filtering eta/ome maps incl LoG with %.2f std dev", sigm) - _filter_eta_ome_maps(eta_ome, filter_stdev=sigm) - else: - logger.info("filtering eta/ome maps") - _filter_eta_ome_maps(eta_ome) + if cfg.find_orientations.orientation_maps.filter_maps: + logger.info("filtering eta/ome maps") + _filter_eta_ome_maps(eta_ome) -def generate_eta_ome_maps(cfg, hkls=None, save=True): +def generate_eta_ome_maps(cfg: root.RootConfig, hkls: Optional[NDArray[np.float64] | list[int]]=None, save: bool=True): """ Generates the eta-omega maps specified in the input config. @@ -665,7 +664,7 @@ def create_clustering_parameters(cfg, eta_ome): def find_orientations( - cfg, hkls=None, clean=False, profile=False, use_direct_testing=False + cfg: root.RootConfig, hkls: Optional[NDArray | list[int]] = None, clean: bool=False, profile: bool=False, use_direct_testing: bool=False ): """ @@ -701,7 +700,7 @@ def find_orientations( ome_tol = np.radians(cfg.find_orientations.omega.tolerance) # handle omega period - ome_period, ome_ranges = _process_omegas(imsd) + ome_period, _ = _process_omegas(imsd) # for multiprocessing ncpus = cfg.multiprocessing diff --git a/mypy.ini b/mypy.ini index 2f873516d..4f8008354 100644 --- a/mypy.ini +++ b/mypy.ini @@ -31,5 +31,11 @@ ignore_missing_imports = True [mypy-hexrd.core.constants.*] ignore_errors = True +[mypy-hdf5plugin.*] +ignore_missing_imports = True + +[mypy-psutil.*] +ignore_missing_imports = True + [mypy-hexrd.powder.wppf.*] ignore_errors = True From 50d9382dc64e8cdb22b05ff722cb59446cfc6a23 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 10:24:03 -0500 Subject: [PATCH 04/28] Continued typing updates --- .../core/imageseries/load/eiger_stream_v1.py | 34 +++++++------------ hexrd/core/imageseries/omega.py | 3 +- hexrd/core/instrument/detector.py | 2 +- hexrd/core/instrument/hedm_instrument.py | 34 +++++++++++++------ hexrd/core/material/crystallography.py | 2 +- hexrd/core/rotations.py | 5 ++- hexrd/core/utils/compatibility.py | 7 ++-- mypy.ini | 4 +-- 8 files changed, 48 insertions(+), 43 deletions(-) diff --git a/hexrd/core/imageseries/load/eiger_stream_v1.py b/hexrd/core/imageseries/load/eiger_stream_v1.py index 37b05c31b..86ab82c92 100644 --- a/hexrd/core/imageseries/load/eiger_stream_v1.py +++ b/hexrd/core/imageseries/load/eiger_stream_v1.py @@ -18,19 +18,18 @@ class EigerStreamV1ImageSeriesAdapter(ImageSeriesAdapter): def __init__(self, fname, **kwargs): if isinstance(fname, h5py.File): - self.__h5name = fname.filename - self.__h5file = fname + self._h5name: str = fname.filename + self._h5file: h5py.File = fname else: - self.__h5name = fname - self.__h5file = h5py.File(self.__h5name, 'r') + self._h5name = fname + self._h5file = h5py.File(self._h5name, 'r') - self.__data_group_path = '/data' self._load_metadata() def close(self): - if self.__h5file is not None: - self.__h5file.close() - self.__h5file = None + if self._h5file is not None: + self._h5file.close() + self._h5file = None def __del__(self): # !!! Note this is not ideal, as the use of __del__ is problematic. @@ -67,19 +66,10 @@ def __len__(self): return len(self._data_group) def __getstate__(self): - # Remove any non-pickleable attributes - to_remove = [ - '__h5file', - ] + # Remove any non-pickleable attributes. Prefix them with the private prefix + to_remove = [f'_{self.__class__.__name__}_h5file'] - # Prefix them with the private prefix - prefix = f'_{self.__class__.__name__}' - to_remove = [f'{prefix}{x}' for x in to_remove] - - # Make a copy of the dict to modify state = self.__dict__.copy() - - # Remove them for attr in to_remove: state.pop(attr) @@ -87,7 +77,7 @@ def __getstate__(self): def __setstate__(self, state): self.__dict__.update(state) - self.__h5file = h5py.File(self.__h5name, 'r') + self._h5file = h5py.File(self._h5name, 'r') self._load_metadata() def _load_metadata(self): @@ -95,7 +85,7 @@ def _load_metadata(self): def _get_metadata(self): d = {} - unwrap_h5_to_dict(self.__h5file['/metadata'], d) + unwrap_h5_to_dict(self._h5file['/metadata'], d) return d @property @@ -108,7 +98,7 @@ def metadata(self): @property def _data_group(self): - return self.__h5file[self.__data_group_path] + return self._h5file['data'] @property def _first_data_entry(self): diff --git a/hexrd/core/imageseries/omega.py b/hexrd/core/imageseries/omega.py index 41c2ddc83..c60a4e61c 100644 --- a/hexrd/core/imageseries/omega.py +++ b/hexrd/core/imageseries/omega.py @@ -8,14 +8,13 @@ from .baseclass import ImageSeries - class OmegaImageSeries(ImageSeries): """ImageSeries with omega metadata""" DFLT_TOL = 1.0e-6 TAU = 360 - def __init__(self, ims: ImageSeries): + def __init__(self, ims: ImageSeries | 'OmegaImageSeries'): """This class is initialized with an existing imageseries""" # check for omega metadata if 'omega' in ims.metadata: diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index b75822520..908d43a6c 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -1831,7 +1831,7 @@ def calc_physics_package_transmission( energy: float, rMat_s: NDArray[np.float64], physics_package: AbstractPhysicsPackage, - ) -> np.float64: + ) -> NDArray[np.float64]: """get the transmission from the physics package need to consider HED and HEDM samples separately """ diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index c3e8d0ef5..ea5ca0064 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -249,13 +249,14 @@ def chunk_instrument(instr, rects, labels, use_roi=False): return new_icfg_dict -def _parse_imgser_dict(imgser_dict, det_key, roi=None): +def _parse_imgser_dict(imgser_dict: Mapping[str, OmegaImageSeries | NDArray[np.float64]], + det_key: str, roi: Optional[tuple[tuple[int, int], ...]]=None) -> OmegaImageSeries | ProcessedImageSeries | NDArray[np.float64]: """ Associates a dict of imageseries to the target panel(s). Parameters ---------- - imgser_dict : dict + imgser_dict : dict[str, OmegaImageSeries | NDArray[np.float64]] The input dict of imageseries. Either `det_key` is in imgser_dict, or the shared key is. Entries can be an ImageSeries object or a 2- or 3-d ndarray of images. @@ -290,8 +291,8 @@ def _parse_imgser_dict(imgser_dict, det_key, roi=None): raise RuntimeError(f"multiple entries found for '{det_key}'") # use boolean array to index the proper key # !!! these should be in the same order - img_keys = img_keys = np.asarray(list(imgser_dict.keys())) - matched_det_key = img_keys[matched_det_keys][0] # !!! only one + img_keys: NDArray[np.str_] = np.asarray(list(imgser_dict.keys())) + matched_det_key = img_keys[matched_det_keys][0] images_in = imgser_dict[matched_det_key] else: raise RuntimeError( @@ -317,6 +318,7 @@ def _parse_imgser_dict(imgser_dict, det_key, roi=None): elif isinstance(images_in, np.ndarray): # 2- or 3-d array of images ndim = images_in.ndim + images_in = np.asarray(images_in) if ndim == 2: ims = images_in[roi[0][0] : roi[0][1], roi[1][0] : roi[1][1]] elif ndim == 3: @@ -1042,7 +1044,7 @@ def extract_polar_maps( self, plane_data: PlaneData, imgser_dict: dict[str, OmegaImageSeries], - active_hkls: Optional[list[int]]=None, + active_hkls: Optional[NDArray[np.int32] | list[int]]=None, threshold: Optional[float]=None, tth_tol: Optional[float]=None, eta_tol: float=0.25, @@ -1085,9 +1087,14 @@ def extract_polar_maps( !!!: images must be non-negative! !!!: plane_data is NOT a copy! """ + if plane_data.tThWidth is None and tth_tol is None: + raise RuntimeError( + "tth_tol was not specified and plane_data.tThWidth is not set" + ) if tth_tol is not None: plane_data.tThWidth = np.radians(tth_tol) else: + assert plane_data.tThWidth is not None tth_tol = np.degrees(plane_data.tThWidth) # make rings clipped to panel @@ -1137,6 +1144,7 @@ def extract_polar_maps( # grab imageseries for this detector ims = _parse_imgser_dict(imgser_dict, det_key, roi=panel.roi) + assert isinstance(ims, OmegaImageSeries) # grab omegas from imageseries and squawk if missing try: @@ -1634,7 +1642,7 @@ def _pull_spots_check_only( self, plane_data: PlaneData, grain_params: tuple | np.ndarray, - imgser_dict: dict[str, OmegaImageSeries], + imgser_dict: Mapping[str, OmegaImageSeries | NDArray[np.float64]], tth_tol: float = 0.25, eta_tol: float = 1.0, ome_tol: float = 1.0, @@ -1644,13 +1652,16 @@ def _pull_spots_check_only( ): rMat_c = make_rmat_of_expmap(grain_params[:3]) tVec_c = np.asarray(grain_params[3:6]) - + oims0 = next(iter(imgser_dict.values())) + # Assert because subsequent nested function calls are too broadly typed + assert isinstance(oims0, OmegaImageSeries) omega_ranges = [ np.radians([i['ostart'], i['ostop']]) for i in oims0.omegawedges.wedges ] if ome_period is None: ims = next(iter(imgser_dict.values())) + assert isinstance(ims, OmegaImageSeries) ome_period = np.radians(ims.omega[0, 0] + np.array([0.0, 360.0])) delta_omega = oims0.omega[0, 1] - oims0.omega[0, 0] @@ -1671,6 +1682,7 @@ def _pull_spots_check_only( omega_image_series = _parse_imgser_dict( imgser_dict, detector_id, roi=panel.roi ) + assert isinstance(omega_image_series, OmegaImageSeries) hkl_ids, hkls_p, ang_centers, xy_centers, ang_pixel_size = [ item[0] for item in sim_results[detector_id] @@ -1882,6 +1894,7 @@ def pull_spots( omega_image_series = _parse_imgser_dict( imgser_dict, detector_id, roi=panel.roi ) + assert isinstance(omega_image_series, OmegaImageSeries) if write_text: output_dir = Path(dirname) / detector_id @@ -2546,7 +2559,7 @@ def __init__( image_series_dict: dict[str, OmegaImageSeries], instrument: HEDMInstrument, plane_data: PlaneData, - active_hkls: Optional[NDArray[np.float64] | list[int]] = None, + active_hkls: Optional[NDArray[np.int32] | list[int]] = None, eta_step: float=0.25, threshold: Optional[float]=None, ome_period: tuple[float, float]=(0, 360), # TODO: Remove this - it does nothing. @@ -2557,12 +2570,13 @@ def __init__( # ???: can we change the behavior of iHKLList? if active_hkls is None: self._iHKLList = plane_data.getHKLID(plane_data.hkls, master=True) + assert isinstance(self._iHKLList, list) n_rings = len(self._iHKLList) else: assert hasattr( active_hkls, '__len__' ), "active_hkls must be an iterable with __len__" - self._iHKLList = active_hkls + self._iHKLList = np.asarray(active_hkls, dtype=np.int32).tolist() n_rings = len(active_hkls) # grab a det key and corresponding imageseries (first will do) @@ -2631,7 +2645,7 @@ def __init__( for i_ring in range(n_rings): # first handle etas full_map: NDArray[np.float64] = np.zeros(map_shape, dtype=float) - nan_mask_full: NDArray[np.bool_] = np.zeros((len(eta_mapping), map_shape[0], map_shape[1])) + nan_mask_full: NDArray[np.bool_] = np.zeros((len(eta_mapping), map_shape[0], map_shape[1]), dtype=bool) i_p = 0 for eta_map in eta_mapping.values(): nan_mask = ~np.isnan(eta_map[i_ring]) diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index 05927ebef..b8f5537be 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -1422,7 +1422,7 @@ def getMultiplicity(self, allHKLs: Optional[bool] = False) -> np.ndarray: def getHKLID( self, - hkl: Union[int, Tuple[int, int, int], np.ndarray], + hkl: int | Tuple[int, int, int] | NDArray[np.int_], master: Optional[bool] = False, ) -> List[int] | int: """ diff --git a/hexrd/core/rotations.py b/hexrd/core/rotations.py index 1ca8235ff..b44556368 100644 --- a/hexrd/core/rotations.py +++ b/hexrd/core/rotations.py @@ -1105,10 +1105,9 @@ def discreteFiber(c, s, B=I3, ndiv=120, invert=False, csym=None, ssym=None): # -def mapAngle(ang, ang_range: Optional[NDArray[np.float64]]=None, +def mapAngle(ang, ang_range: Optional[tuple[float, float] | NDArray[np.float64]]=None, units: Literal['degrees', 'radians']=angularUnits) -> NDArray[np.float64]: - """ - Utility routine to map an angle into a specified period + """ Map an angle into a specified period """ if units == 'degrees': period = 360.0 diff --git a/hexrd/core/utils/compatibility.py b/hexrd/core/utils/compatibility.py index 3b57903ec..091b47c3b 100644 --- a/hexrd/core/utils/compatibility.py +++ b/hexrd/core/utils/compatibility.py @@ -1,13 +1,16 @@ from importlib.metadata import version +import numpy as np +from numpy.typing import NDArray + import h5py -def h5py_read_string(dataset): +def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.bytes_] | NDArray[np.str_]: if version('h5py') >= '3': # In h5py >= 3.0.0, h5py no longer converts the data type to a # string automatically, and we have to do it manually... - string_dtype = h5py.check_string_dtype(dataset.dtype) + string_dtype: h5py.string_dtype = h5py.h5t.check_string_dtype(dataset.dtype) if string_dtype is not None and string_dtype.encoding == 'utf-8': dataset = dataset.asstr() diff --git a/mypy.ini b/mypy.ini index 4f8008354..fc741ba89 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,8 +7,8 @@ ignore_missing_imports = True [mypy-yaml.*] ignore_missing_imports = True -[mypy-h5py.*] -ignore_missing_imports = True +; [mypy-h5py.*] +; ignore_missing_imports = True [mypy-scipy.*] ignore_missing_imports = True From 64ff0a4bb16a37ca47dea2bb9fb67414e5022ed3 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 12:51:24 -0500 Subject: [PATCH 05/28] Continue typing --- hexrd/core/constants.py | 4 +- hexrd/core/distortion/ge_41rt.py | 3 +- hexrd/core/distortion/registry.py | 7 +- hexrd/core/extensions/transforms.pyi | 229 ++++++++++++++++++ hexrd/core/imageseries/omega.py | 2 +- hexrd/core/instrument/detector.py | 37 ++- hexrd/core/material/crystallography.py | 130 ++++++---- hexrd/core/material/material.py | 14 +- hexrd/core/material/spacegroup.py | 62 ----- hexrd/core/material/unitcell.py | 98 +++----- hexrd/core/rotations.py | 4 +- hexrd/core/transforms/new_capi/xf_new_capi.py | 2 +- hexrd/core/utils/warnings.py | 7 +- hexrd/core/valunits.py | 13 +- hexrd/hedm/xrdutil/utils.py | 51 ++-- mypy.ini | 3 + tests/test_laue.py | 4 +- 17 files changed, 435 insertions(+), 235 deletions(-) create mode 100644 hexrd/core/extensions/transforms.pyi diff --git a/hexrd/core/constants.py b/hexrd/core/constants.py index 9fa446815..4fef2732b 100644 --- a/hexrd/core/constants.py +++ b/hexrd/core/constants.py @@ -346,8 +346,8 @@ # for energy/wavelength conversions -def keVToAngstrom(x) -> NDArray[np.float64]: - return (1e7 * scipyc.c * scipyc.h / scipyc.e) / np.array(x, dtype=float) +def keVToAngstrom(x: float) -> float: + return (1e7 * scipyc.c * scipyc.h / scipyc.e) / x def set_numba_cache(): diff --git a/hexrd/core/distortion/ge_41rt.py b/hexrd/core/distortion/ge_41rt.py index e50e5d022..4e3d0ee66 100644 --- a/hexrd/core/distortion/ge_41rt.py +++ b/hexrd/core/distortion/ge_41rt.py @@ -3,6 +3,7 @@ from typing import List import numpy as np +from numpy.typing import NDArray import numba from .distortionabc import DistortionABC @@ -18,7 +19,7 @@ # NOTE: Deprecated in favor of inverse_distortion.ge_41rt_inverse_distortion @numba.njit(nogil=True, cache=True, fastmath=True) def _ge_41rt_inverse_distortion( - inputs: np.ndarray[np.float64, np.float64], + inputs: NDArray[np.float64], rhoMax: float, params: List[float], ): diff --git a/hexrd/core/distortion/registry.py b/hexrd/core/distortion/registry.py index 28dc3a49e..2c5ad853c 100644 --- a/hexrd/core/distortion/registry.py +++ b/hexrd/core/distortion/registry.py @@ -2,6 +2,8 @@ import abc +from hexrd.core.distortion.distortionabc import DistortionABC + __all__ = ['maptypes', 'get_mapping'] @@ -15,10 +17,11 @@ def __init__(cls, name, bases, attrs): class Registry(object): """Registry for imageseries adapters""" - distortion_registry = dict() + distortion_registry: dict[str, DistortionABC] = dict() @classmethod - def register(cls, acls): + def register(cls, acls: DistortionABC): """Register adapter class""" if acls.__name__ != 'DistortionBase': + assert acls.maptype is not None cls.distortion_registry[acls.maptype] = acls diff --git a/hexrd/core/extensions/transforms.pyi b/hexrd/core/extensions/transforms.pyi new file mode 100644 index 000000000..39a9e0a9f --- /dev/null +++ b/hexrd/core/extensions/transforms.pyi @@ -0,0 +1,229 @@ +from __future__ import annotations + +from typing import Final, TypeAlias +import numpy as np +from numpy.typing import NDArray + + +FloatArray: TypeAlias = NDArray[np.float64] + +Vec3: TypeAlias = FloatArray +Mat3: TypeAlias = FloatArray + +RotMatStack: TypeAlias = FloatArray +Angles: TypeAlias = FloatArray +DetectorXY: TypeAlias = FloatArray + + +def make_binary_rot_mat(a: Vec3, /) -> Mat3: + """ + Compute a 3x3 rotation matrix from a binary vector `a`. + + Parameters + ---------- + a + 3-vector. + + Returns + ------- + 3x3 rotation matrix. + """ + + +def make_rot_mat_of_exp_map(e: Vec3, /) -> Mat3: + """ + Compute a 3x3 rotation matrix from an exponential map vector `e`. + + Parameters + ---------- + e + 3-vector (axis * angle). + + Returns + ------- + 3x3 rotation matrix. + """ + + +def makeOscillRotMat(chi: float, ome: FloatArray, /) -> RotMatStack: + """ + Generate a stack of oscillation rotation matrices. + + The underlying C++ returns an array with shape (3*N, 3), where each + consecutive 3-row block is a 3x3 matrix for the corresponding omega. + + Parameters + ---------- + chi + Oscillation axis tilt angle (radians). + ome + Omega angles, shape (N,). + + Returns + ------- + Array of shape (3*N, 3) containing N rotation matrices (3x3 blocks). + """ + + +def anglesToGVec( + angs: Angles, + bHat_l: Vec3, + eHat_l: Vec3, + chi: float, + rMat_c: Mat3, + /, +) -> FloatArray: + """ + Convert angles to g-vectors in the crystal frame. + + Parameters + ---------- + angs + Angles array, shape (N, 3). + bHat_l + Beam direction (lab), shape (3,). + eHat_l + Reference direction (lab), shape (3,). + chi + Oscillation axis tilt angle (radians). + rMat_c + Crystal rotation matrix, shape (3, 3). + + Returns + ------- + g-vectors, shape (N, 3). + """ + + +def anglesToDVec( + angs: Angles, + bHat_l: Vec3, + eHat_l: Vec3, + chi: float, + rMat_c: Mat3, + /, +) -> FloatArray: + """ + Convert angles to d-vectors in the crystal frame. + + Returns an array of shape (N, 3). + """ + + +def gvecToDetectorXY( + gVec_c: FloatArray, + rMat_d: Mat3, + rMat_s: FloatArray, + rMat_c: Mat3, + tVec_d: Vec3, + tVec_s: Vec3, + tVec_c: Vec3, + beamVec: Vec3, + /, +) -> DetectorXY: + """ + Convert g-vectors (crystal frame) to detector XY. + + Parameters + ---------- + gVec_c + g-vectors, shape (N, 3). + rMat_d + Detector rotation matrix, shape (3, 3). + rMat_s + Stack of sample rotation matrices in the "3-row blocks" layout: + shape (3*N, 3). + rMat_c + Crystal rotation matrix, shape (3, 3). + tVec_d, tVec_s, tVec_c + Translation vectors, each shape (3,). + beamVec + Beam direction vector, shape (3,). + + Returns + ------- + Detector XY, shape (N, 2). May contain NaNs for invalid intersections. + """ + + +def gvec_to_detector_xy_one( + gVec_c: Vec3, + rMat_d: Mat3, + rMat_sc: Mat3, + tVec_d: Vec3, + bHat_l: Vec3, + nVec_l: Vec3, + num: float, + P0_l: Vec3, + /, +) -> FloatArray: + """ + Convert a single g-vector to detector XY. + + Returns + ------- + XY vector, shape (2,). Returns NaNs if invalid. + """ + + +def gvecToDetectorXYFromAngles( + chi: float, + omes: FloatArray, + gVec_c: FloatArray, + rMat_d: Mat3, + rMat_c: Mat3, + tVec_d: Vec3, + tVec_s: Vec3, + tVec_c: Vec3, + beamVec: Vec3, + /, +) -> DetectorXY: + """ + Convert g-vectors to detector XY using chi and omega angles. + + Parameters + ---------- + chi + Oscillation axis tilt angle. + omes + Omega array, shape (N,). (Matches the C++ signature's VectorXd usage.) + gVec_c + g-vectors, shape (N, 3). + + Returns + ------- + Detector XY, shape (N, 2). + """ + + +def anglesToDetectorXY( + chi: float, + omes: Angles, + rMat_d: Mat3, + rMat_c: Mat3, + tVec_d: Vec3, + tVec_s: Vec3, + tVec_c: Vec3, + beamVec: Vec3, + /, +) -> DetectorXY: + """ + Convert angles directly to detector XY. + + Parameters + ---------- + chi + Oscillation axis tilt angle. + omes + Angles array, shape (N, 3). (Despite the name, this is the full angles array.) + rMat_d, rMat_c + Rotation matrices, shape (3, 3). + tVec_d, tVec_s, tVec_c + Translation vectors, shape (3,). + beamVec + Beam direction vector, shape (3,). + + Returns + ------- + Detector XY, shape (N, 2). May contain NaNs for invalid intersections. + """ diff --git a/hexrd/core/imageseries/omega.py b/hexrd/core/imageseries/omega.py index c60a4e61c..3ba1551e1 100644 --- a/hexrd/core/imageseries/omega.py +++ b/hexrd/core/imageseries/omega.py @@ -14,7 +14,7 @@ class OmegaImageSeries(ImageSeries): DFLT_TOL = 1.0e-6 TAU = 360 - def __init__(self, ims: ImageSeries | 'OmegaImageSeries'): + def __init__(self, ims: 'ImageSeries | OmegaImageSeries'): """This class is initialized with an existing imageseries""" # check for omega metadata if 'omega' in ims.metadata: diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index 908d43a6c..2868c343d 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -577,9 +577,7 @@ def pixel_solid_angles(self) -> np.ndarray: # METHODS # ========================================================================= - def pixel_Q( - self, energy: np.floating, origin: np.ndarray = ct.zeros_3 - ) -> np.ndarray: + def pixel_Q(self, energy: float, origin: np.ndarray = ct.zeros_3) -> NDArray[np.float64]: '''get the equivalent momentum transfer for the angles. @@ -587,12 +585,12 @@ def pixel_Q( ---------- energy: float incident photon energy in keV - origin: np.ndarray + origin: NDArray[np.float64] = ct.zeros_3 origin of diffraction volume Returns ------- - np.ndarray + NDArray[np.float64] pixel wise Q in A^-1 ''' @@ -1512,33 +1510,29 @@ def map_to_plane(self, pts, rmat, tvec): def simulate_rotation_series( self, - plane_data, - grain_param_list, - eta_ranges=[ - (-np.pi, np.pi), - ], - ome_ranges=[ - (-np.pi, np.pi), - ], - ome_period=(-np.pi, np.pi), - chi=0.0, - tVec_s=ct.zeros_3, - wavelength=None, - energy_correction=None, + plane_data: PlaneData, + grain_param_list: list[float], + eta_ranges: list[tuple[float, float]] = [(-np.pi, np.pi)], + ome_ranges: list[tuple[float, float]] = [(-np.pi, np.pi)], + ome_period: tuple[float, float] = (-np.pi, np.pi), + chi: float = 0.0, + tVec_s: NDArray[np.float64] = ct.zeros_3, + wavelength: Optional[float] = None, + energy_correction: Optional[dict[str, float]] = None, ): """ Simulate a monochromatic rotation series for a list of grains. Parameters ---------- - plane_data : TYPE + plane_data : PlaneData DESCRIPTION. grain_param_list : TYPE DESCRIPTION. eta_ranges : TYPE, optional - DESCRIPTION. The default is [(-np.pi, np.pi), ]. + DESCRIPTION. The default is [(-np.pi, np.pi)]. ome_ranges : TYPE, optional - DESCRIPTION. The default is [(-np.pi, np.pi), ]. + DESCRIPTION. The default is [(-np.pi, np.pi)]. ome_period : TYPE, optional DESCRIPTION. The default is (-np.pi, np.pi). chi : TYPE, optional @@ -1571,6 +1565,7 @@ def simulate_rotation_series( if wavelength is None: wavelength = plane_data.wavelength else: + # TODO: This function should not be modifying input arguments if plane_data.wavelength != wavelength: plane_data.wavelength = ct.keVToAngstrom(wavelength) assert not np.any( diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index b8f5537be..665858768 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -33,7 +33,7 @@ import csv import os from math import pi -from typing import Any, Mapping, Optional, Union, Dict, List, Tuple, TypedDict +from typing import Any, Literal, Mapping, Optional, Union, Dict, List, Tuple, TypedDict, overload import numpy as np from numpy.typing import NDArray @@ -62,7 +62,15 @@ dUnit = 'angstrom' LatPlaneData = Mapping[str, NDArray[Any]] -LatVecOps = Mapping[str, NDArray[Any]] + +class LatVecOps(TypedDict): + F: NDArray[np.float64] + B: NDArray[np.float64] + BR: NDArray[np.float64] + U0: NDArray[np.float64] + vol: np.float64 + dparms: NDArray[np.float64] + rparms: NDArray[np.float64] class HKLData(TypedDict): hklID: int @@ -92,10 +100,14 @@ def hklToStr(hkl: np.ndarray) -> str: """ return re.sub(r'[\[\]\(\)\{\},]', '', str(hkl)) +@overload +def cosineXform(a: NDArray[np.float64], b: NDArray[np.float64], c: NDArray[np.float64]) -> tuple[NDArray[np.float64], NDArray[np.float64]]: ... +@overload +def cosineXform(a: np.float64, b: np.float64, c: np.float64) -> tuple[np.float64, np.float64]: ... def cosineXform( a: NDArray[np.float64] | np.float64, b: NDArray[np.float64] | np.float64, c: NDArray[np.float64] | np.float64 -) -> tuple[NDArray[np.float64], NDArray[np.float64]] | tuple[float, float]: +) -> tuple[NDArray[np.float64], NDArray[np.float64]] | tuple[np.float64, np.float64]: """ Spherical trig transform to take alpha, beta, gamma to expressions for cos(alpha*). See ref below. @@ -304,7 +316,7 @@ def latticeVectors( lparms: NDArray[np.float64], tag: str = 'cubic', radians: bool = False, -) -> Dict[str, NDArray[np.float64] | float]: +) -> LatVecOps: """ Generates direct and reciprocal lattice vector components in a crystal-relative RHON basis, X. The convention for fixing X to the @@ -525,7 +537,7 @@ def latticeVectors( dparms: NDArray[np.float64] = np.r_[ad, bd, cd, np.r_[alpha, beta, gamma]] rparms: NDArray[np.float64] = np.r_[ar, br, cr, np.r_[alfar, betar, gamar]] - return { + return LatVecOps({ 'F': F, 'B': B, 'BR': BR, @@ -533,7 +545,7 @@ def latticeVectors( 'vol': V, 'dparms': dparms, 'rparms': rparms, - } + }) def hexagonalIndicesFromRhombohedral(hkl): @@ -725,7 +737,7 @@ def __init__(self, hkls: np.ndarray, *args, **kwargs) -> None: self._laueGroup = laueGroup self._hkls = copy.deepcopy(hkls) self._strainMag = strainMag - self._structFact = np.ones(self._hkls.shape[1]) + self._structFact: Optional[NDArray[np.float64]] = np.ones(self._hkls.shape[1]) self.tThWidth: float | None = tThWidth # ... need to implement tThMin too @@ -822,7 +834,9 @@ def hkls(self) -> np.ndarray: """ hStacked Hkls of the plane data (Miller indices). """ - return self.getHKLs().T + hkls = self.getHKLs() + assert type(hkls) == np.ndarray + return hkls.T @hkls.setter def hkls(self, hkls): @@ -1065,6 +1079,7 @@ def structFact(self) -> np.ndarray: np.ndarray """ self._compute_sf_if_needed() + assert self._structFact is not None return self._structFact[~self.exclusions] @structFact.setter @@ -1150,7 +1165,7 @@ def makePlaneData( latVecOps = latticeVectors(lparms, symmGroup) - hklDataList = [] + hklDataList: list[HKLData] = [] for iHKL in range(len(hkls.T)): # need transpose because of convention for hkls ordering @@ -1263,7 +1278,7 @@ def getPlaneSpacings(self) -> List[float]: return dspacings @property - def latVecOps(self) -> Dict[str, Union[np.ndarray, float]]: + def latVecOps(self) -> LatVecOps: """ gets lattice vector operators as a new (deepcopy) @@ -1422,8 +1437,8 @@ def getMultiplicity(self, allHKLs: Optional[bool] = False) -> np.ndarray: def getHKLID( self, - hkl: int | Tuple[int, int, int] | NDArray[np.int_], - master: Optional[bool] = False, + hkl: int | tuple[int, int, int] | NDArray[np.int_], + master: bool = False, ) -> List[int] | int: """ Return the unique ID of a list of hkls. @@ -1455,19 +1470,18 @@ def getHKLID( ------- 2020-05-21 (JVB) -- modified to handle all symmetric equavlent reprs. """ - if hasattr(hkl, '__setitem__'): # tuple does not have __setitem__ - if isinstance(hkl, np.ndarray): - # if is ndarray, assume is 3xN - return [self._getHKLID(x, master=master) for x in hkl.T] - else: - return [self._getHKLID(x, master=master) for x in hkl] + if isinstance(hkl, np.ndarray): + # if is ndarray, assume is 3xN + return [self._getHKLID(x, master=master) for x in hkl.T] + elif isinstance(hkl, tuple) or isinstance(hkl, list): + return [self._getHKLID(x, master=master) for x in hkl] else: return self._getHKLID(hkl, master=master) def _getHKLID( self, - hkl: Union[int, Tuple[int, int, int], np.ndarray], - master: Optional[bool] = False, + hkl: int | tuple[int, int, int] | NDArray[np.float64], + master: bool = False, ) -> int: """ for hkl that is a tuple, return externally visible hkl index @@ -1578,12 +1592,36 @@ def getHKLs(self, *hkl_ids: int, **kwargs) -> List[str] | NDArray[np.float64]: else: return np.array(hkls) + @overload + def getSymHKLs( + self, + asStr: Literal[True], + withID: bool = ..., + indices: Optional[list[int]] = ..., + ) -> list[list[str]]: ... + + @overload + def getSymHKLs( + self, + asStr: Literal[False] = ..., + withID: Literal[False] = ..., + indices: Optional[list[int]] = ..., + ) -> list[NDArray[np.float64]]: ... + + @overload + def getSymHKLs( + self, + asStr: Literal[False] = ..., + withID: Literal[True] = ..., + indices: Optional[list[int]] = ..., + ) -> list[NDArray[np.float64]]: ... + def getSymHKLs( self, - asStr: Optional[bool] = False, - withID: Optional[bool] = False, - indices: Optional[List[int]] = None, - ) -> Union[List[List[str]], List[np.ndarray]]: + asStr: bool = False, + withID: bool = False, + indices: Optional[list[int]] = None, + ) -> list[list[str]] | list[NDArray[np.float64]]: """ Return all symmetry HKLs. @@ -1603,33 +1641,37 @@ def getSymHKLs( List of symmetry HKLs for each HKL, either as strings or as a vstacked array. """ - sym_hkls: list[list[str] | NDArray[np.float64]] = [] hkl_index = 0 if indices is not None: indB = np.zeros(self.nHKLs, dtype=bool) indB[np.array(indices)] = True else: indB = np.ones(self.nHKLs, dtype=bool) + + if asStr: + out_s: list[list[str]] = [] + for iHKLr, hklData in enumerate(self.hklDataList): + if not self._thisHKL(iHKLr): + continue + if indB[hkl_index]: + hkls = hklData["symHKLs"] + out_s.append(list(map(hklToStr, np.asarray(hkls).T))) + hkl_index += 1 + return out_s + + out_a: list[NDArray[np.float64]] = [] for iHKLr, hklData in enumerate(self.hklDataList): if not self._thisHKL(iHKLr): continue if indB[hkl_index]: - hkls = hklData['symHKLs'] - if asStr: - sym_hkls.append(list(map(hklToStr, np.array(hkls).T))) - elif withID: - sym_hkls.append( - np.vstack( - [ - np.tile(hklData['hklID'], (1, hkls.shape[1])), - hkls, - ] - ) - ) + hkls = np.asarray(hklData["symHKLs"], dtype=np.float64) + if withID: + hkl_id = np.asarray(hklData["hklID"], dtype=np.float64) + out_a.append(np.vstack([np.tile(hkl_id, (1, hkls.shape[1])), hkls])) else: - sym_hkls.append(np.array(hkls)) + out_a.append(hkls) hkl_index += 1 - return sym_hkls + return out_a @staticmethod def makeScatteringVectors( @@ -2159,12 +2201,12 @@ def lorentz_factor(tth: np.ndarray) -> np.ndarray: def polarization_factor( - tth: np.ndarray, - eta: Optional[np.ndarray] = None, + tth: NDArray[np.float64], + eta: Optional[NDArray[np.float64]] = None, unpolarized: Optional[bool] = True, f_hor: Optional[float] = None, f_vert: Optional[float] = None, -) -> np.ndarray: +) -> NDArray[np.float64]: """ 06/14/2021 SS adding lorentz polarization factor computation to the detector so that it can be compenstated for in the @@ -2184,11 +2226,13 @@ def polarization_factor( FIXME, called without parameters like eta, f_hor, f_vert, but they default to none in the current implementation, which will throw an error. """ - ctth2 = np.cos(tth) ** 2 if unpolarized: return (1 + ctth2) / 2 + assert eta is not None + assert f_hor is not None + assert f_vert is not None seta2 = np.sin(eta) ** 2 ceta2 = np.cos(eta) ** 2 diff --git a/hexrd/core/material/material.py b/hexrd/core/material/material.py index 30cebec3c..587178317 100644 --- a/hexrd/core/material/material.py +++ b/hexrd/core/material/material.py @@ -1142,7 +1142,7 @@ def name(self): return self._name @name.setter - def name(self, mat_name): + def name(self, mat_name: str): assert isinstance(mat_name, str), "must set name to a str" self._name = mat_name @@ -1304,18 +1304,6 @@ def reduced_lattice_parameters(self): ltype = self.unitcell.latticeType return [self._lparms[i] for i in unitcell._rqpDict[ltype][0]] - def _get_name(self): - """Set method for name""" - return self._name - - def _set_name(self, v): - """Set method for name""" - self._name = v - - return - - name = property(_get_name, _set_name, None, "Name of material") - @property def dmin(self): return self._dmin diff --git a/hexrd/core/material/spacegroup.py b/hexrd/core/material/spacegroup.py index c9b98281b..87283f7c5 100644 --- a/hexrd/core/material/spacegroup.py +++ b/hexrd/core/material/spacegroup.py @@ -923,65 +923,3 @@ def iroot(n): return hkls - -# -# ================================================== Test Functions -# - - -def testHKLs(): - # - # Check reduced HKLs - # - # 1. Titanium (sg 194) - # - sg = SpaceGroup(194) - print('==================== Titanium (194)') - ssmax = 20 - myHKLs = sg.getHKLs(ssmax) - print('Number of HKLs with sum of square %d or less: %d' % (ssmax, len(myHKLs))) - for hkl in myHKLs: - ss = hkl[0] ** 2 + hkl[1] ** 2 + hkl[2] ** 2 - print((hkl, ss)) - - # - # 2. Ruby (sg 167) - # - sg = SpaceGroup(167) - print('==================== Ruby (167)') - ssmax = 10 - myHKLs = sg.getHKLs(ssmax) - print('Number of HKLs with sum of square %d or less: %d' % (ssmax, len(myHKLs))) - for hkl in myHKLs: - ss = hkl[0] ** 2 + hkl[1] ** 2 + hkl[2] ** 2 - print((hkl, ss)) - # - # Test Generic HKLs - # - for ss in range(1, 10): - print('==================== ss = %d' % ss) - hkls = _getHKLsBySS(ss) - print(' number of hkls: ', len(hkls)) - print(hkls) - - -if __name__ == '__main__': - # - import sys - - # - if 'testHKLs' in sys.argv: - testHKLs() - sys.exit() - # - # Test Space groups: - # - for n in range(1, 231): - try: - sg = SpaceGroup(n) - sg.getHKLs(10) - print(sg) - print('\n') - except: - print(('failed for space group number: ', n)) - print(('Hall symbol: ', lookupHall[n])) diff --git a/hexrd/core/material/unitcell.py b/hexrd/core/material/unitcell.py index b7651b6de..60b628ea1 100644 --- a/hexrd/core/material/unitcell.py +++ b/hexrd/core/material/unitcell.py @@ -1884,89 +1884,71 @@ def _sgrange(min, max): Edited by C. J. Gilmore, J. A. Kaduk and H. Schenk ''' # independent components for the triclinic laue group -type1 = [] -for i in range(6): - for j in range(i, 6): - type1.append((i, j)) -type1 = tuple(type1) +type1: tuple[tuple[int, int], ...] = tuple((i, j) for i in range(6) for j in range(i, 6)) # independent components for the monoclinic laue group # C14 = C15 = C24 = C25 = C34 = C35 = C46 = C56 = 0 -type2 = list(type1) -type2.remove((0, 3)) -type2.remove((0, 4)) -type2.remove((1, 3)) -type2.remove((1, 4)) -type2.remove((2, 3)) -type2.remove((2, 4)) -type2.remove((3, 5)) -type2.remove((4, 5)) -type2 = tuple(type2) +type2: tuple[tuple[int, int], ...] = tuple( + ij for ij in type1 + if ij not in { + (0, 3), (0, 4), (1, 3), (1, 4), + (2, 3), (2, 4), (3, 5), (4, 5) + } +) # independent components for the orthorhombic laue group # Above, plus C16 = C26 = C36 = C45 = 0 -type3 = list(type2) -type3.remove((0, 5)) -type3.remove((1, 5)) -type3.remove((2, 5)) -type3.remove((3, 4)) -type3 = tuple(type3) +# independent components for the orthorhombic laue group +# Remove C16, C26, C36, C45 = 0 from monoclinic +type3: tuple[tuple[int, int], ...] = tuple( + ij for ij in type2 + if ij not in {(0, 5), (1, 5), (2, 5), (3, 4)} +) # independent components for the cyclic tetragonal laue group # monoclinic, plus C36 = C45 = 0, C22 = C11, C23 = C13, C26 = −C16, C55 = C44 -type4 = list(type2) -type4.remove((2, 5)) -type4.remove((3, 4)) -type4.remove((1, 1)) -type4.remove((1, 2)) -type4.remove((1, 5)) -type4.remove((4, 4)) -type4 = tuple(type4) +# independent components for the cyclic tetragonal laue group +type4: tuple[tuple[int, int], ...] = tuple( + ij for ij in type2 + if ij not in { + (2, 5), (3, 4), (1, 1), (1, 2), (1, 5), (4, 4) + } +) # independent components for the dihedral tetragonal laue group # Above, plus C16 = 0 -type5 = list(type4) -type5.remove((0, 5)) -type5 = tuple(type5) +# independent components for the dihedral tetragonal laue group +# Above, plus C16 = 0 +type5: tuple[tuple[int, int], ...] = tuple( + ij for ij in type4 if ij != (0, 5) +) # independent components for the trigonal laue group # C16 = C26 = C34 = C35 = C36 = C45 = 0, C22 = C11, C23 = C13, C24 = −C14, # C25 = −C15, C46 = −C15, C55 = C44, C56 = C14, C66 = (C11 − C12)/2 -type6 = list(type1) -type6.remove((0, 5)) -type6.remove((1, 5)) -type6.remove((2, 3)) -type6.remove((2, 4)) -type6.remove((2, 5)) -type6.remove((3, 4)) -type6.remove((1, 1)) -type6.remove((1, 2)) -type6.remove((1, 3)) -type6.remove((1, 4)) -type6.remove((3, 5)) -type6.remove((4, 4)) -type6.remove((4, 5)) -type6.remove((5, 5)) -type6 = tuple(type6) +# independent components for the trigonal laue group +type6: tuple[tuple[int, int], ...] = tuple( + ij for ij in type1 + if ij not in { + (0, 5), (1, 5), (2, 3), (2, 4), (2, 5), + (3, 4), (1, 1), (1, 2), (1, 3), (1, 4), + (3, 5), (4, 4), (4, 5), (5, 5) + } +) # independent components for the rhombohedral laue group # Above, plus C15 = 0 -type7 = list(type6) -type7.remove((0, 4)) -type7 = tuple(type7) +type7 = tuple(ij for ij in type6 if ij != (0, 4)) # independent components for the hexagonal laue group # Above, plus C14 = 0 -type8 = list(type7) -type8.remove((0, 3)) -type8 = tuple(type8) +type8: tuple[tuple[int, int], ...] = tuple(ij for ij in type7 if ij != (0, 3)) # independent components for the cubic laue group # As for dihedral tetragonal, plus C13 = C12, C33 = C11, C66 = C44 -type9 = list(type5) -type9.remove((0, 2)) -type9.remove((2, 2)) -type9.remove((5, 5)) +type9: tuple[tuple[int, int], ...] = tuple( + ij for ij in type5 if ij not in {(0, 2), (2, 2), (5, 5)} +) ''' these lambda functions take care of the equality constrains in the diff --git a/hexrd/core/rotations.py b/hexrd/core/rotations.py index b44556368..c5413d527 100644 --- a/hexrd/core/rotations.py +++ b/hexrd/core/rotations.py @@ -53,7 +53,7 @@ # Module Data # ============================================================================= -angularUnits = 'radians' # module-level angle units +angularUnits: Literal['radians', 'degrees'] = 'radians' # module-level angle units periodDict = {'degrees': 360.0, 'radians': 2 * np.pi} conversion_to_dict = {'degrees': cnst.r2d, 'radians': cnst.d2r} @@ -1123,7 +1123,7 @@ def mapAngle(ang, ang_range: Optional[tuple[float, float] | NDArray[np.float64]] # if we have a specified angular range, use that if ang_range is not None: - ang_range = np.atleast_1d(np.float64(ang_range)) + ang_range = np.atleast_1d(np.asarray(ang_range, dtype=np.float64)) min_val = ang_range.min() max_val = ang_range.max() diff --git a/hexrd/core/transforms/new_capi/xf_new_capi.py b/hexrd/core/transforms/new_capi/xf_new_capi.py index 75f600af1..c9ad1ffe2 100644 --- a/hexrd/core/transforms/new_capi/xf_new_capi.py +++ b/hexrd/core/transforms/new_capi/xf_new_capi.py @@ -551,7 +551,7 @@ def make_rmat_of_expmap(exp_map: tuple[float] | NDArray[np.float64]) -> NDArray[ NDArray[np.float64] A 3x3 rotation matrix representing the input exponential map """ - arg = np.ascontiguousarray(exp_map.flatten()) + arg = np.ascontiguousarray(exp_map).flatten() return cpp_transforms.make_rot_mat_of_exp_map(arg) diff --git a/hexrd/core/utils/warnings.py b/hexrd/core/utils/warnings.py index d21d44074..b20a8b54c 100644 --- a/hexrd/core/utils/warnings.py +++ b/hexrd/core/utils/warnings.py @@ -4,7 +4,10 @@ @contextmanager -def ignore_warnings(category: Optional[Warning] = None): +def ignore_warnings(category: Optional[type[Warning]] = None): with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=category) + if category is not None: + warnings.simplefilter('ignore', category=category) + else: + warnings.simplefilter('ignore') yield diff --git a/hexrd/core/valunits.py b/hexrd/core/valunits.py index 3538026d7..9f639dad2 100644 --- a/hexrd/core/valunits.py +++ b/hexrd/core/valunits.py @@ -108,7 +108,7 @@ def __init__(self, name: str, unitType: str, value: float, unit: str): Name of the item. unitType : str Class of units, e.g., 'length', 'angle', 'energy'. - value : float + value : float | np.floating Numerical value. unit : str Name of the unit. @@ -308,22 +308,19 @@ def valWithDflt(val, dflt, toUnit=None): return retval -FloatLike = float | np.floating - - -def _nm(x: FloatLike) -> valWUnit: +def _nm(x: float) -> valWUnit: return valWUnit("lp", "length", x, "nm") -def _kev(x: FloatLike) -> valWUnit: +def _kev(x: float) -> valWUnit: return valWUnit("kev", "energy", x, "keV") -def _angstrom(x: FloatLike) -> valWUnit: +def _angstrom(x: float) -> valWUnit: return valWUnit("lp", "length", x, "angstrom") -def _degrees(x: FloatLike) -> valWUnit: +def _degrees(x: float) -> valWUnit: return valWUnit('lp', 'angle', x, 'degrees') diff --git a/hexrd/hedm/xrdutil/utils.py b/hexrd/hedm/xrdutil/utils.py index b66f72c5b..e9873bd82 100644 --- a/hexrd/hedm/xrdutil/utils.py +++ b/hexrd/hedm/xrdutil/utils.py @@ -27,7 +27,7 @@ # ============================================================ -from typing import Callable, Optional, Union, Any, Generator +from typing import Callable, Literal, Optional, Union, Any, Generator, overload from hexrd.core.material.crystallography import PlaneData from hexrd.core.distortion.distortionabc import DistortionABC @@ -485,13 +485,32 @@ def _fetch_hkls_from_planedata(pd: PlaneData): return np.hstack(pd.getSymHKLs(withID=True)).T +@overload def _filter_hkls_eta_ome( - hkls: np.ndarray, - angles: np.ndarray, + hkls: NDArray[np.float64], + angles: NDArray[np.float64], + eta_range: list[tuple[float, float]], + ome_range: list[tuple[float, float]], + return_mask: Literal[False] = False, +) -> tuple[NDArray[np.float64], NDArray[np.float64]]: ... + +@overload +def _filter_hkls_eta_ome( + hkls: NDArray[np.float64], + angles: NDArray[np.float64], + eta_range: list[tuple[float, float]], + ome_range: list[tuple[float, float]], + return_mask: Literal[True] = True, +) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]: ... + + +def _filter_hkls_eta_ome( + hkls: NDArray[np.float64], + angles: NDArray[np.float64], eta_range: list[tuple[float, float]], ome_range: list[tuple[float, float]], return_mask: bool = False, -) -> Union[tuple[np.ndarray, np.ndarray], tuple[np.ndarray, np.ndarray, np.ndarray]]: +) -> tuple[NDArray[np.float64], NDArray[np.float64]] | tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]: """ given a set of hkls and angles, filter them by the eta and omega ranges @@ -785,21 +804,17 @@ def angularPixelSize( def make_reflection_patches( instr_cfg: dict[str, Any], - tth_eta: np.ndarray, - ang_pixel_size: np.ndarray, - omega: Optional[np.ndarray] = None, + tth_eta: NDArray[np.float64], + ang_pixel_size: NDArray[np.float64], + omega: Optional[NDArray[np.float64]] = None, tth_tol: float = 0.2, eta_tol: float = 1.0, - rmat_c: np.ndarray = np.eye(3), - tvec_c: np.ndarray = np.zeros((3, 1)), + rmat_c: NDArray[np.float64] = np.eye(3), + tvec_c: NDArray[np.float64] = np.zeros((3, 1)), npdiv: int = 1, quiet: bool = False, # TODO: Remove this parameter - it isn't used compute_areas_func: Callable = gutil.compute_areas, -) -> Generator[ - tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray], - None, - None, -]: +): """Make angular patches on a detector. panel_dims are [(xmin, ymin), (xmax, ymax)] in mm @@ -1007,9 +1022,9 @@ def extract_detector_transformation( def apply_correction_to_wavelength( wavelength: float, - energy_correction: Union[dict, None], - tvec_s: np.ndarray, - tvec_c: np.ndarray, + energy_correction: Optional[dict[str, float]] = None, + tvec_s: Optional[NDArray[np.float64]] = None, + tvec_c: Optional[NDArray[np.float64]] = None, ) -> float: """Apply an energy correction to the wavelength according to grain position @@ -1036,6 +1051,8 @@ def apply_correction_to_wavelength( # No correction return wavelength + assert tvec_s is not None + assert tvec_c is not None # 'c' here is the conversion factor between keV and angstrom c = constants.keVToAngstrom(1) diff --git a/mypy.ini b/mypy.ini index fc741ba89..73c184cdf 100644 --- a/mypy.ini +++ b/mypy.ini @@ -34,6 +34,9 @@ ignore_errors = True [mypy-hdf5plugin.*] ignore_missing_imports = True +[mypy-h5py.*] +ignore_missing_imports = True + [mypy-psutil.*] ignore_missing_imports = True diff --git a/tests/test_laue.py b/tests/test_laue.py index 63b42caf0..26f26d6f2 100644 --- a/tests/test_laue.py +++ b/tests/test_laue.py @@ -5,8 +5,8 @@ import pytest -from hexrd.material.material import load_materials_hdf5, Material -from hexrd.instrument.hedm_instrument import HEDMInstrument +from hexrd.core.material.material import load_materials_hdf5, Material +from hexrd.core.instrument.hedm_instrument import HEDMInstrument @pytest.fixture From f70845d92bd7bfd08330546d027ba9e7a3fbbca1 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 13:35:52 -0500 Subject: [PATCH 06/28] Type Registry --- hexrd/core/distortion/registry.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hexrd/core/distortion/registry.py b/hexrd/core/distortion/registry.py index 2c5ad853c..c452643d7 100644 --- a/hexrd/core/distortion/registry.py +++ b/hexrd/core/distortion/registry.py @@ -2,12 +2,11 @@ import abc -from hexrd.core.distortion.distortionabc import DistortionABC - __all__ = ['maptypes', 'get_mapping'] class _RegisterDistortionClass(abc.ABCMeta): + maptype: str | None def __init__(cls, name, bases, attrs): abc.ABCMeta.__init__(cls, name, bases, attrs) @@ -17,10 +16,10 @@ def __init__(cls, name, bases, attrs): class Registry(object): """Registry for imageseries adapters""" - distortion_registry: dict[str, DistortionABC] = dict() + distortion_registry: dict[str, _RegisterDistortionClass] = dict() @classmethod - def register(cls, acls: DistortionABC): + def register(cls, acls: _RegisterDistortionClass): """Register adapter class""" if acls.__name__ != 'DistortionBase': assert acls.maptype is not None From 239e4176e89ed3d407042cfdeafc13737de4624c Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 14:48:59 -0500 Subject: [PATCH 07/28] _new_transforms_capi.pyi creation --- .../core/extensions/_new_transforms_capi.pyi | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 hexrd/core/extensions/_new_transforms_capi.pyi diff --git a/hexrd/core/extensions/_new_transforms_capi.pyi b/hexrd/core/extensions/_new_transforms_capi.pyi new file mode 100644 index 000000000..ee2cd7c9f --- /dev/null +++ b/hexrd/core/extensions/_new_transforms_capi.pyi @@ -0,0 +1,130 @@ +# _new_transforms_CAPI.pyi +from __future__ import annotations + +from typing import Any +import numpy as np +from numpy.typing import NDArray + + +def anglesToGVec( + angs: NDArray[np.float64], + beam_vec: NDArray[np.float64], + eta_vec: NDArray[np.float64], + chi: float, + rmat_c: NDArray[np.float64], +) -> NDArray[np.float64]: ... + +def anglesToDVec( + angs: NDArray[np.float64], + beam_vec: NDArray[np.float64], + eta_vec: NDArray[np.float64], + chi: float, + rmat_c: NDArray[np.float64], +) -> NDArray[np.float64]: ... + +def gvecToDetectorXY( + gvec_c: NDArray[np.float64], + rmat_d: NDArray[np.float64], + rmat_s: NDArray[np.float64], + rmat_c: NDArray[np.float64], + tvec_d: NDArray[np.float64], + tvec_s: NDArray[np.float64], + tvec_c: NDArray[np.float64], + beam_vec: NDArray[np.float64] | None = ..., + /, +) -> NDArray[np.float64]: ... + +def gvecToDetectorXYArray( + gVec_c: NDArray[np.float64], + rMat_d: NDArray[np.float64], + rMat_s: NDArray[np.float64], + rMat_c: NDArray[np.float64], + tVec_d: NDArray[np.float64], + tVec_s: NDArray[np.float64], + tVec_c: NDArray[np.float64], + beamVec: NDArray[np.float64] | None = ..., + /, +) -> NDArray[np.float64]: ... + +def detectorXYToGvec( + xy_det: NDArray[np.float64], + rmat_d: NDArray[np.float64], + rmat_s: NDArray[np.float64], + tvec_d: NDArray[np.float64], + tvec_s: NDArray[np.float64], + tvec_c: NDArray[np.float64], + beam_vec: NDArray[np.float64], + eta_vec: NDArray[np.float64], + /, +) -> tuple[tuple[NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]]: ... + +def oscillAnglesOfHKLs( + hkls: NDArray[np.float64], + chi: float, + rMat_c: NDArray[np.float64], + bMat: NDArray[np.float64], + wavelen: float, + vInv: NDArray[np.float64], + beamVec: NDArray[np.float64], + etaVec: NDArray[np.float64], + /, +) -> tuple[NDArray[np.float64], NDArray[np.float64]]: ... + +def unitRowVector( + vecIn: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def unitRowVectors( + vecIn: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def makeOscillRotMat( + chi: float, + ome: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def makeRotMatOfExpMap( + exp_map: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def makeDetectorRotMat( + tiltAngles: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def makeBinaryRotMat( + axis: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def makeEtaFrameRotMat( + bvec_l: NDArray[np.float64], + evec_l: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def validateAngleRanges( + ang_list: NDArray[np.float64], + start_ang: NDArray[np.float64], + stop_ang: NDArray[np.float64], + ccw: bool = ..., + /, +) -> NDArray[np.bool_]: ... + +def rotate_vecs_about_axis( + angles: NDArray[np.float64], + axis: NDArray[np.float64], + vecs: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... + +def quat_distance( + q1: NDArray[np.float64], + q2: NDArray[np.float64], + qsym: NDArray[np.float64], + /, +) -> float: ... From e188aff4f22b7fa6bebc1640897eae9f1df42600 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 15:02:10 -0500 Subject: [PATCH 08/28] Use Sequence where appropriate. https://github.com/HEXRD/hexrd/pull/884#discussion_r2755862070 --- .../core/extensions/_new_transforms_capi.pyi | 2 -- hexrd/core/instrument/detector.py | 10 +++++----- hexrd/core/instrument/hedm_instrument.py | 4 ++-- hexrd/core/utils/compatibility.py | 20 +++++++++++-------- hexrd/hedm/xrdutil/utils.py | 8 ++++---- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/hexrd/core/extensions/_new_transforms_capi.pyi b/hexrd/core/extensions/_new_transforms_capi.pyi index ee2cd7c9f..91a66f511 100644 --- a/hexrd/core/extensions/_new_transforms_capi.pyi +++ b/hexrd/core/extensions/_new_transforms_capi.pyi @@ -1,7 +1,5 @@ -# _new_transforms_CAPI.pyi from __future__ import annotations -from typing import Any import numpy as np from numpy.typing import NDArray diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index 2868c343d..979098d51 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -4,7 +4,7 @@ import copy import logging import os -from typing import Optional, TYPE_CHECKING +from typing import Optional, TYPE_CHECKING, Sequence from hexrd.core.instrument.constants import ( COATING_DEFAULT, @@ -1209,8 +1209,8 @@ def make_powder_rings( merge_hkls: bool = False, delta_tth: Optional[float] = None, delta_eta: float = 10.0, - eta_period: list[float] | np.ndarray = [-np.pi, np.pi], - eta_list: Optional[list[float] | np.ndarray] = None, + eta_period: Sequence[float] | np.ndarray = (-np.pi, np.pi), + eta_list: Optional[Sequence[float] | np.ndarray] = None, rmat_s: NDArray[np.float64] = ct.identity_3x3, tvec_s: NDArray[np.float64] = ct.zeros_3, tvec_c: NDArray[np.float64] = ct.zeros_3, @@ -1512,8 +1512,8 @@ def simulate_rotation_series( self, plane_data: PlaneData, grain_param_list: list[float], - eta_ranges: list[tuple[float, float]] = [(-np.pi, np.pi)], - ome_ranges: list[tuple[float, float]] = [(-np.pi, np.pi)], + eta_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), + ome_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), ome_period: tuple[float, float] = (-np.pi, np.pi), chi: float = 0.0, tVec_s: NDArray[np.float64] = ct.zeros_3, diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index ea5ca0064..e030f9d7b 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -1647,7 +1647,7 @@ def _pull_spots_check_only( eta_tol: float = 1.0, ome_tol: float = 1.0, threshold: int = 10, - eta_ranges: list[tuple[float, float]] = [(-np.pi, np.pi)], + eta_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), ome_period: Optional[tuple] = None, ): rMat_c = make_rmat_of_expmap(grain_params[:3]) @@ -1733,7 +1733,7 @@ def pull_spots( ome_tol: float = 1.0, npdiv: int = 2, threshold: int = 10, - eta_ranges: list[tuple] = [(-np.pi, np.pi)], + eta_ranges: Sequence[tuple] = ((-np.pi, np.pi)), ome_period: Optional[tuple] = None, dirname: str = 'results', filename: Optional[str] = None, diff --git a/hexrd/core/utils/compatibility.py b/hexrd/core/utils/compatibility.py index 091b47c3b..aa355d4d4 100644 --- a/hexrd/core/utils/compatibility.py +++ b/hexrd/core/utils/compatibility.py @@ -6,12 +6,16 @@ import h5py -def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.bytes_] | NDArray[np.str_]: - if version('h5py') >= '3': - # In h5py >= 3.0.0, h5py no longer converts the data type to a - # string automatically, and we have to do it manually... - string_dtype: h5py.string_dtype = h5py.h5t.check_string_dtype(dataset.dtype) - if string_dtype is not None and string_dtype.encoding == 'utf-8': - dataset = dataset.asstr() +def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.str_]: + """ + Always return a numpy array of unicode strings (np.str_), + independent of h5py version or dataset dtype. + """ + arr = dataset[()] + arr = np.asarray(arr) - return dataset[()] + if arr.dtype.kind == "S": + # Bytes type + arr = arr.astype(np.str_) + + return arr diff --git a/hexrd/hedm/xrdutil/utils.py b/hexrd/hedm/xrdutil/utils.py index e9873bd82..bcbe95d95 100644 --- a/hexrd/hedm/xrdutil/utils.py +++ b/hexrd/hedm/xrdutil/utils.py @@ -27,7 +27,7 @@ # ============================================================ -from typing import Callable, Literal, Optional, Union, Any, Generator, overload +from typing import Callable, Literal, Optional, Union, Any, Sequence, overload from hexrd.core.material.crystallography import PlaneData from hexrd.core.distortion.distortionabc import DistortionABC @@ -574,10 +574,10 @@ def simulateGVecs( pd: PlaneData, detector_params: np.ndarray, grain_params: np.ndarray, - ome_range: list[tuple[float, float]] = [(-np.pi, np.pi)], + ome_range: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), ome_period: tuple[float, float] = (-np.pi, np.pi), - eta_range: list[tuple[float, float]] = [(-np.pi, np.pi)], - panel_dims: list[tuple[float, float]] = [(-204.8, -204.8), (204.8, 204.8)], + eta_range: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), + panel_dims: Sequence[tuple[float, float]] = ((-204.8, -204.8), (204.8, 204.8)), pixel_pitch: tuple[float, float] = (0.2, 0.2), distortion: Optional[DistortionABC] = None, beam_vector: np.ndarray = constants.beam_vec, From 9f8548595017e0542d01df383772a87d6f261ce3 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 15:06:01 -0500 Subject: [PATCH 09/28] Revert accidental change to compatibility.py --- hexrd/core/utils/compatibility.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/hexrd/core/utils/compatibility.py b/hexrd/core/utils/compatibility.py index aa355d4d4..091b47c3b 100644 --- a/hexrd/core/utils/compatibility.py +++ b/hexrd/core/utils/compatibility.py @@ -6,16 +6,12 @@ import h5py -def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.str_]: - """ - Always return a numpy array of unicode strings (np.str_), - independent of h5py version or dataset dtype. - """ - arr = dataset[()] - arr = np.asarray(arr) +def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.bytes_] | NDArray[np.str_]: + if version('h5py') >= '3': + # In h5py >= 3.0.0, h5py no longer converts the data type to a + # string automatically, and we have to do it manually... + string_dtype: h5py.string_dtype = h5py.h5t.check_string_dtype(dataset.dtype) + if string_dtype is not None and string_dtype.encoding == 'utf-8': + dataset = dataset.asstr() - if arr.dtype.kind == "S": - # Bytes type - arr = arr.astype(np.str_) - - return arr + return dataset[()] From 7b757b529d58428a456c5019c5f003e383fec581 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 15:11:47 -0500 Subject: [PATCH 10/28] Revert filter_maps_if_requested --- hexrd/hedm/findorientations.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hexrd/hedm/findorientations.py b/hexrd/hedm/findorientations.py index 2d60cc10a..523d7d107 100644 --- a/hexrd/hedm/findorientations.py +++ b/hexrd/hedm/findorientations.py @@ -430,9 +430,15 @@ def filter_maps_if_requested(eta_ome, cfg: root.RootConfig): # if False/None don't do anything # if True, only do median subtraction # if scalar, do median + LoG filter with that many pixels std dev - if cfg.find_orientations.orientation_maps.filter_maps: - logger.info("filtering eta/ome maps") - _filter_eta_ome_maps(eta_ome) + filter_maps = cfg.find_orientations.orientation_maps.filter_maps + if filter_maps: + if not isinstance(filter_maps, bool): + sigm = const.fwhm_to_sigma * filter_maps + logger.info("filtering eta/ome maps incl LoG with %.2f std dev", sigm) + _filter_eta_ome_maps(eta_ome, filter_stdev=sigm) + else: + logger.info("filtering eta/ome maps") + _filter_eta_ome_maps(eta_ome) def generate_eta_ome_maps(cfg: root.RootConfig, hkls: Optional[NDArray[np.float64] | list[int]]=None, save: bool=True): From 2b20a726ca738d34cafd847a178ed597b9c8a937 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 15:56:32 -0500 Subject: [PATCH 11/28] Continue typing - finished extensions, NDArray additions --- .../core/extensions/_new_transforms_capi.pyi | 4 +- hexrd/core/extensions/_transforms_CAPI.pyi | 196 ++++++++++++++++++ hexrd/core/extensions/inverse_distortion.pyi | 10 + hexrd/core/material/crystallography.py | 2 +- hexrd/core/transforms/new_capi/xf_new_capi.py | 56 ++--- hexrd/hedm/xrdutil/utils.py | 18 +- 6 files changed, 246 insertions(+), 40 deletions(-) create mode 100644 hexrd/core/extensions/_transforms_CAPI.pyi create mode 100644 hexrd/core/extensions/inverse_distortion.pyi diff --git a/hexrd/core/extensions/_new_transforms_capi.pyi b/hexrd/core/extensions/_new_transforms_capi.pyi index 91a66f511..e2ee8d30c 100644 --- a/hexrd/core/extensions/_new_transforms_capi.pyi +++ b/hexrd/core/extensions/_new_transforms_capi.pyi @@ -54,7 +54,7 @@ def detectorXYToGvec( beam_vec: NDArray[np.float64], eta_vec: NDArray[np.float64], /, -) -> tuple[tuple[NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]]: ... +) -> tuple[NDArray[np.float64], ...]: ... def oscillAnglesOfHKLs( hkls: NDArray[np.float64], @@ -125,4 +125,4 @@ def quat_distance( q2: NDArray[np.float64], qsym: NDArray[np.float64], /, -) -> float: ... +) -> NDArray[np.float64]: ... diff --git a/hexrd/core/extensions/_transforms_CAPI.pyi b/hexrd/core/extensions/_transforms_CAPI.pyi new file mode 100644 index 000000000..4b1bf07a4 --- /dev/null +++ b/hexrd/core/extensions/_transforms_CAPI.pyi @@ -0,0 +1,196 @@ +from __future__ import annotations + +import numpy as np +from numpy.typing import NDArray + + +def anglesToGVec( + angs: NDArray[np.float64], # (n, 3) + bHat_l: NDArray[np.float64], # (3,) + eHat_l: NDArray[np.float64], # (3,) + chi: float, + rMat_c: NDArray[np.float64], # (3, 3) +) -> NDArray[np.float64]: # (n, 3) + ... + + +def anglesToDVec( + angs: NDArray[np.float64], # (n, 3) + bHat_l: NDArray[np.float64], # (3,) + eHat_l: NDArray[np.float64], # (3,) + chi: float, + rMat_c: NDArray[np.float64], # (3, 3) +) -> NDArray[np.float64]: # (n, 3) + ... + + +def gvecToDetectorXY( + gVec_c: NDArray[np.float64], # (n, 3) + rMat_d: NDArray[np.float64], # (3, 3) + rMat_s: NDArray[np.float64], # (3, 3) + rMat_c: NDArray[np.float64], # (3, 3) + tVec_d: NDArray[np.float64], # (3,) + tVec_s: NDArray[np.float64], # (3,) + tVec_c: NDArray[np.float64], # (3,) + beamVec: NDArray[np.float64], # (3,) +) -> NDArray[np.float64]: # (n, 2) + ... + + +def gvecToDetectorXYArray( + gVec_c: NDArray[np.float64], # (n, 3) + rMat_d: NDArray[np.float64], # (3, 3) + rMat_s: NDArray[np.float64], # (n, 3, 3) + rMat_c: NDArray[np.float64], # (3, 3) + tVec_d: NDArray[np.float64], # (3,) + tVec_s: NDArray[np.float64], # (3,) + tVec_c: NDArray[np.float64], # (3,) + beamVec: NDArray[np.float64], # (3,) +) -> NDArray[np.float64]: # (n, 2) + ... + + +def detectorXYToGvec( + xy_det: NDArray[np.float64], # (n, 2) + rMat_d: NDArray[np.float64], # (3, 3) + rMat_s: NDArray[np.float64], # (3, 3) + tVec_d: NDArray[np.float64], # (3,) + tVec_s: NDArray[np.float64], # (3,) + tVec_c: NDArray[np.float64], # (3,) + beamVec: NDArray[np.float64], # (3,) + etaVec: NDArray[np.float64], # (3,) +) -> tuple[tuple[NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]]: + """ + Returns: + ((tTh, eta), gVec_l) + where: + tTh: (n,) float64 + eta: (n,) float64 + gVec_l: (n, 3) float64 + """ + ... + + +def detectorXYToGvecArray( + xy_det: NDArray[np.float64], # (n, 2) + rMat_d: NDArray[np.float64], # (3, 3) + rMat_s: NDArray[np.float64], # (n, 3, 3) + tVec_d: NDArray[np.float64], # (3,) + tVec_s: NDArray[np.float64], # (3,) + tVec_c: NDArray[np.float64], # (3,) + beamVec: NDArray[np.float64], # (3,) + etaVec: NDArray[np.float64], # (3,) +) -> tuple[tuple[NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]]: + """ + Returns: + ((tTh, eta), gVec_l) + where: + tTh: (n,) float64 + eta: (n,) float64 + gVec_l: (n, 3) float64 + """ + ... + + +def oscillAnglesOfHKLs( + hkls: NDArray[np.float64], # (n, 3) + chi: float, + rMat_c: NDArray[np.float64], # (3, 3) + bMat: NDArray[np.float64], # (3, 3) + wavelength: float, + vInv_s: NDArray[np.float64], # (6,) + beamVec: NDArray[np.float64], # (3,) + etaVec: NDArray[np.float64], # (3,) +) -> tuple[NDArray[np.float64], NDArray[np.float64]]: + """ + Returns: + (oangs0, oangs1) each (n, 3) float64 + """ + ... + + +def unitRowVector( + vecIn: NDArray[np.float64], # (n,) +) -> NDArray[np.float64]: # (n,) + ... + + +def unitRowVectors( + vecIn: NDArray[np.float64], # (m, n) +) -> NDArray[np.float64]: # (m, n) + ... + + +def makeDetectorRotMat( + tiltAngles: NDArray[np.float64], # (3,) +) -> NDArray[np.float64]: # (3, 3) + ... + + +def makeOscillRotMat( + oscillAngles: NDArray[np.float64], # (2,) +) -> NDArray[np.float64]: # (3, 3) + ... + + +def makeOscillRotMatArray( + chi: float, + omeArray: NDArray[np.float64], # (n,) +) -> NDArray[np.float64]: # (n, 3, 3) + ... + + +def makeRotMatOfExpMap( + expMap: NDArray[np.float64], # (3,) +) -> NDArray[np.float64]: # (3, 3) + ... + + +def makeRotMatOfQuat( + quat: NDArray[np.float64], # (n, 4) +) -> NDArray[np.float64]: # (n, 3, 3) + ... + + +def makeBinaryRotMat( + axis: NDArray[np.float64], # (3,) +) -> NDArray[np.float64]: # (3, 3) + ... + + +def makeEtaFrameRotMat( + bHat: NDArray[np.float64], # (3,) + eHat: NDArray[np.float64], # (3,) +) -> NDArray[np.float64]: # (3, 3) + ... + + +def validateAngleRanges( + angList: NDArray[np.float64], # (na,) + angMin: NDArray[np.float64], # (nmin,) + angMax: NDArray[np.float64], # (nmin,) + ccw: object, # expects bool-ish +) -> NDArray[np.bool_]: # (na,) + ... + + +def rotate_vecs_about_axis( + angles: NDArray[np.float64], # (1,) or (n,) + axes: NDArray[np.float64], # (1,3) or (n,3) + vecs: NDArray[np.float64], # (n,3) +) -> NDArray[np.float64]: # (n,3) + ... + + +def quat_distance( + q1: NDArray[np.float64], # (4,) + q2: NDArray[np.float64], # (4,) + qsym: NDArray[np.float64], # (4, nsym) +) -> float: + ... + + +def homochoricOfQuat( + quat: NDArray[np.float64], # (n, 4) +) -> NDArray[np.float64]: # (n, 3) + ... diff --git a/hexrd/core/extensions/inverse_distortion.pyi b/hexrd/core/extensions/inverse_distortion.pyi new file mode 100644 index 000000000..21fad1a6b --- /dev/null +++ b/hexrd/core/extensions/inverse_distortion.pyi @@ -0,0 +1,10 @@ +import numpy as np +from numpy.typing import NDArray + + +def ge_41rt_inverse_distortion( + inputs: NDArray[np.float64], + rhoMax: float, + params: NDArray[np.float64], + /, +) -> NDArray[np.float64]: ... diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index 35bbc9b31..f78d370ea 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -1504,7 +1504,7 @@ def _getHKLID( f"hkl '{tuple(hkl)}' is not present in this material!" ) - def getHKLs(self, *hkl_ids: int, asStr: bool=False, thisTTh: float=None, + def getHKLs(self, *hkl_ids: int, asStr: bool=False, thisTTh: Optional[float]=None, allHKLs: bool=False) -> Union[List[str], np.ndarray]: """ Returns the powder HKLs subject to specified options. diff --git a/hexrd/core/transforms/new_capi/xf_new_capi.py b/hexrd/core/transforms/new_capi/xf_new_capi.py index c9ad1ffe2..a25f4540a 100644 --- a/hexrd/core/transforms/new_capi/xf_new_capi.py +++ b/hexrd/core/transforms/new_capi/xf_new_capi.py @@ -171,17 +171,17 @@ def makeGVector(hkl: NDArray[np.float64], bMat: NDArray[np.float64]) -> NDArray[ def gvec_to_xy( - gvec_c: np.ndarray, - rmat_d: np.ndarray, - rmat_s: np.ndarray, - rmat_c: np.ndarray, - tvec_d: np.ndarray, - tvec_s: np.ndarray, - tvec_c: np.ndarray, - beam_vec: Optional[np.ndarray] = None, - vmat_inv: Optional[np.ndarray] = None, - bmat: Optional[np.ndarray] = None, -) -> np.ndarray: + gvec_c: NDArray[np.float64], + rmat_d: NDArray[np.float64], + rmat_s: NDArray[np.float64], + rmat_c: NDArray[np.float64], + tvec_d: NDArray[np.float64], + tvec_s: NDArray[np.float64], + tvec_c: NDArray[np.float64], + beam_vec: Optional[NDArray[np.float64]] = None, + vmat_inv: Optional[NDArray[np.float64]] = None, + bmat: Optional[NDArray[np.float64]] = None, +) -> NDArray[np.float64]: """ Takes a concatenated list of reciprocal lattice vectors components in the CRYSTAL FRAME to the specified detector-relative frame, subject to the @@ -269,16 +269,16 @@ def gvec_to_xy( def xy_to_gvec( - xy_d: np.ndarray, - rmat_d: np.ndarray, - rmat_s: np.ndarray, - tvec_d: np.ndarray, - tvec_s: np.ndarray, - tvec_c: np.ndarray, - rmat_b: Optional[np.ndarray] = None, + xy_d: NDArray[np.float64], + rmat_d: NDArray[np.float64], + rmat_s: NDArray[np.float64], + tvec_d: NDArray[np.float64], + tvec_s: NDArray[np.float64], + tvec_c: NDArray[np.float64], + rmat_b: Optional[NDArray[np.float64]] = None, distortion: Optional[DistortionABC] = None, output_ref: Optional[bool] = False, -) -> Tuple[np.ndarray, np.ndarray]: +) -> tuple[NDArray[np.float64], ...]: """ Takes a list cartesian (x, y) pairs in the DETECTOR FRAME and calculates the associated reciprocal lattice (G) vectors and (bragg angle, azimuth) @@ -347,15 +347,15 @@ def xy_to_gvec( def oscill_angles_of_hkls( - hkls: np.ndarray, + hkls: NDArray[np.float64], chi: float, - rmat_c: np.ndarray, - bmat: np.ndarray, + rmat_c: NDArray[np.float64], + bmat: NDArray[np.float64], wavelength: float, - v_inv: Optional[np.ndarray] = None, - beam_vec: np.ndarray = cnst.beam_vec, - eta_vec: np.ndarray = cnst.eta_vec, -) -> Tuple[np.ndarray, np.ndarray]: + v_inv: Optional[NDArray[np.float64]] = None, + beam_vec: NDArray[np.float64] = cnst.beam_vec, + eta_vec: NDArray[np.float64] = cnst.eta_vec, +) -> Tuple[NDArray[np.float64], NDArray[np.float64]]: """ Takes a list of unit reciprocal lattice vectors in crystal frame to the specified detector-relative frame, subject to the conditions: @@ -476,7 +476,7 @@ def unit_vector(vec_in: NDArray[np.float64]) -> NDArray[np.float64]: ) -def make_detector_rmat(tilt_angles: np.ndarray) -> np.ndarray: +def make_detector_rmat(tilt_angles: NDArray[np.float64]) -> NDArray[np.float64]: """ Form the (3, 3) tilt rotations from the tilt angle list: @@ -503,7 +503,7 @@ def make_oscill_rmat_array(chi, omeArray): return _impl.makeOscillRotMat(chi, arg) -def make_sample_rmat(chi: float, ome: Union[float, np.ndarray]) -> np.ndarray: +def make_sample_rmat(chi: float, ome: float | NDArray[np.float64]) -> NDArray[np.float64]: # TODO: Check this docstring """ Make a rotation matrix representing the COB from sample frame to the lab diff --git a/hexrd/hedm/xrdutil/utils.py b/hexrd/hedm/xrdutil/utils.py index bcbe95d95..c345db9a8 100644 --- a/hexrd/hedm/xrdutil/utils.py +++ b/hexrd/hedm/xrdutil/utils.py @@ -489,8 +489,8 @@ def _fetch_hkls_from_planedata(pd: PlaneData): def _filter_hkls_eta_ome( hkls: NDArray[np.float64], angles: NDArray[np.float64], - eta_range: list[tuple[float, float]], - ome_range: list[tuple[float, float]], + eta_range: Sequence[tuple[float, float]], + ome_range: Sequence[tuple[float, float]], return_mask: Literal[False] = False, ) -> tuple[NDArray[np.float64], NDArray[np.float64]]: ... @@ -498,8 +498,8 @@ def _filter_hkls_eta_ome( def _filter_hkls_eta_ome( hkls: NDArray[np.float64], angles: NDArray[np.float64], - eta_range: list[tuple[float, float]], - ome_range: list[tuple[float, float]], + eta_range: Sequence[tuple[float, float]], + ome_range: Sequence[tuple[float, float]], return_mask: Literal[True] = True, ) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]: ... @@ -507,8 +507,8 @@ def _filter_hkls_eta_ome( def _filter_hkls_eta_ome( hkls: NDArray[np.float64], angles: NDArray[np.float64], - eta_range: list[tuple[float, float]], - ome_range: list[tuple[float, float]], + eta_range: Sequence[tuple[float, float]], + ome_range: Sequence[tuple[float, float]], return_mask: bool = False, ) -> tuple[NDArray[np.float64], NDArray[np.float64]] | tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]: """ @@ -574,14 +574,14 @@ def simulateGVecs( pd: PlaneData, detector_params: np.ndarray, grain_params: np.ndarray, - ome_range: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), + ome_range: Sequence[tuple[float, float]] = ((-np.pi, np.pi),), ome_period: tuple[float, float] = (-np.pi, np.pi), - eta_range: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), + eta_range: Sequence[tuple[float, float]] = ((-np.pi, np.pi),), panel_dims: Sequence[tuple[float, float]] = ((-204.8, -204.8), (204.8, 204.8)), pixel_pitch: tuple[float, float] = (0.2, 0.2), distortion: Optional[DistortionABC] = None, beam_vector: np.ndarray = constants.beam_vec, - energy_correction: Union[dict, None] = None, + energy_correction: Optional[dict[str, float]] = None, ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ returns valid_ids, valid_hkl, valid_ang, valid_xy, ang_ps From ea46213a4fcd2a15857253f806c37cc6f2a9821c Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 16:18:31 -0500 Subject: [PATCH 12/28] Continue typing... --- .../calibration/lmfit_param_handling.py | 9 +- hexrd/core/instrument/detector.py | 132 +++++++++--------- hexrd/core/instrument/hedm_instrument.py | 26 ++-- hexrd/core/material/crystallography.py | 10 +- hexrd/core/rotations.py | 8 +- 5 files changed, 93 insertions(+), 92 deletions(-) diff --git a/hexrd/core/fitting/calibration/lmfit_param_handling.py b/hexrd/core/fitting/calibration/lmfit_param_handling.py index 4e4f0806d..88815f8ab 100644 --- a/hexrd/core/fitting/calibration/lmfit_param_handling.py +++ b/hexrd/core/fitting/calibration/lmfit_param_handling.py @@ -2,6 +2,7 @@ import lmfit import numpy as np +from numpy.typing import NDArray from hexrd.core.instrument import ( calc_angles_from_beam_vec, @@ -385,7 +386,7 @@ def update_unconstrained_detector_parameters(instr, params, euler_convention): def _update_constrained_detector_parameters( detectors: list[Detector], params: dict, - rotation_center: np.ndarray, + rotation_center: NDArray[np.float64], euler_convention: EULER_CONVENTION_TYPES, prefix: str, constraint_params: dict, @@ -484,7 +485,7 @@ def update_group_constrained_detector_parameters( ) -def _tilt_to_rmat(tilt: np.ndarray, euler_convention: dict | tuple) -> np.ndarray: +def _tilt_to_rmat(tilt: NDArray[np.float64], euler_convention: dict | tuple) -> NDArray[np.float64]: # Convert the tilt to exponential map parameters, and then # to the rotation matrix, and return. if euler_convention is None: @@ -498,14 +499,14 @@ def _tilt_to_rmat(tilt: np.ndarray, euler_convention: dict | tuple) -> np.ndarra ) -def _rmat_to_tilt(rmat: np.ndarray) -> np.ndarray: +def _rmat_to_tilt(rmat: NDArray[np.float64]) -> NDArray[np.float64]: phi, n = angleAxisOfRotMat(rmat) return phi * n.flatten() def create_tth_parameters( instr: HEDMInstrument, - meas_angles: dict[str, np.ndarray], + meas_angles: dict[str, NDArray[np.float64]], ) -> list[lmfit.Parameter]: prefixes = tth_parameter_prefixes(instr) diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index 979098d51..16d66dd4b 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -558,7 +558,7 @@ def pixel_coords(self): return pix_i, pix_j @property - def pixel_solid_angles(self) -> np.ndarray: + def pixel_solid_angles(self) -> NDArray[np.float64]: y, x = self.pixel_coords xy = np.vstack((x.flatten(), y.flatten())).T dvecs = self.cart_to_dvecs(xy) @@ -577,7 +577,7 @@ def pixel_solid_angles(self) -> np.ndarray: # METHODS # ========================================================================= - def pixel_Q(self, energy: float, origin: np.ndarray = ct.zeros_3) -> NDArray[np.float64]: + def pixel_Q(self, energy: float, origin: NDArray[np.float64] = ct.zeros_3) -> NDArray[np.float64]: '''get the equivalent momentum transfer for the angles. @@ -601,8 +601,8 @@ def pixel_Q(self, energy: float, origin: np.ndarray = ct.zeros_3) -> NDArray[np. def pixel_compton_energy_loss( self, energy: np.floating, - origin: np.ndarray = ct.zeros_3, - ) -> np.ndarray: + origin: NDArray[np.float64] = ct.zeros_3, + ) -> NDArray[np.float64]: '''inelastic compton scattering leads to energy loss of the incident photons. compute the final energy of the photons @@ -612,12 +612,12 @@ def pixel_compton_energy_loss( ---------- energy: float incident photon energy in keV - origin: np.ndarray + origin: NDArray[np.float64] origin of diffraction volume Returns ------- - np.ndarray + NDArray[np.float64] pixel wise energy of inelastically scatterd photons in keV ''' @@ -632,8 +632,8 @@ def pixel_compton_attenuation_length( energy: np.floating, density: np.floating, formula: str, - origin: np.ndarray = ct.zeros_3, - ) -> np.ndarray: + origin: NDArray[np.float64] = ct.zeros_3, + ) -> NDArray[np.float64]: '''each pixel intercepts inelastically scattered photons of different energy. the attenuation length and the transmission @@ -649,12 +649,12 @@ def pixel_compton_attenuation_length( density of material in g/cc formula: str formula of the material scattering - origin: np.ndarray + origin: NDArray[np.float64] origin of diffraction volume Returns ------- - np.ndarray + NDArray[np.float64] pixel wise attentuation length of compton scattered photons ''' @@ -684,13 +684,13 @@ def compute_compton_scattering_intensity( ----------- energy: float energy of incident photon - rMat_s: np.ndarray + rMat_s: NDArray[np.float64] rotation matrix of sample orientation physics_package: AbstractPhysicsPackage physics package information Returns ------- - compton_intensity: np.ndarray + compton_intensity: NDArray[np.float64] transmission corrected compton scattering intensity ''' @@ -1113,8 +1113,8 @@ def interpolate_nearest(self, xy, img, pad_with_nans=True): def interpolate_bilinear( self, - xy: np.ndarray, - img: np.ndarray, + xy: NDArray[np.float64], + img: NDArray[np.float64], pad_with_nans: bool = True, ): """ @@ -1156,8 +1156,8 @@ def interpolate_bilinear( def _generate_bilinear_interp_dict( self, - xy_clip: np.ndarray, - ) -> dict[str, np.ndarray]: + xy_clip: NDArray[np.float64], + ) -> dict[str, NDArray[np.float64]]: """Compute bilinear interpolation multipliers and indices for the panel If you are going to be using the same panel settings and performing @@ -1205,12 +1205,12 @@ def _generate_bilinear_interp_dict( def make_powder_rings( self, - pd: PlaneData | np.ndarray, # TODO: Make a different function for array input + pd: PlaneData | NDArray[np.float64], # TODO: Make a different function for array input merge_hkls: bool = False, delta_tth: Optional[float] = None, delta_eta: float = 10.0, - eta_period: Sequence[float] | np.ndarray = (-np.pi, np.pi), - eta_list: Optional[Sequence[float] | np.ndarray] = None, + eta_period: Sequence[float] | NDArray[np.float64] = (-np.pi, np.pi), + eta_list: Optional[Sequence[float] | NDArray[np.float64]] = None, rmat_s: NDArray[np.float64] = ct.identity_3x3, tvec_s: NDArray[np.float64] = ct.zeros_3, tvec_c: NDArray[np.float64] = ct.zeros_3, @@ -1218,10 +1218,10 @@ def make_powder_rings( tth_distortion: Optional[RyggPinholeDistortion | JHEPinholeDistortion | LayerDistortion] = None, ) -> tuple[ - list[np.ndarray], - list[np.ndarray], - np.ndarray, - list[np.ndarray], + list[NDArray[np.float64]], + list[NDArray[np.float64]], + NDArray[np.float64], + list[NDArray[np.float64]], NDArray[np.float64] ]: """ Generate points on Debye Scherrer rings over the detector. @@ -1230,7 +1230,7 @@ def make_powder_rings( Parameters ---------- - `pd` : PlaneData | np.ndarray + `pd` : PlaneData | NDArray[np.float64] Either a `PlaneData` object or an array of two-theta values in degrees. `merge_hkls` : bool, optional Merge hkls when `pd` is a `PlaneData` type. Defaults to `False`. @@ -1242,19 +1242,19 @@ def make_powder_rings( is a `PlaneData` type. `delta_eta` : float, optional The azimuthal width of each ring sector in degrees. Defaults to `10.0`. - `eta_period` : list[float] | np.ndarray, optional + `eta_period` : list[float] | NDArray[np.float64], optional The azimuthal period over which to generate rings in radians. Defaults to `[-pi, pi]`. - `eta_list` : list[float] | np.ndarray, optional + `eta_list` : list[float] | NDArray[np.float64], optional Azimuthal angles in degrees at which to generate rings. If supplied, this overrides `delta_eta`. Defaults to `None`. - `rmat_s` : np.ndarray, optional + `rmat_s` : NDArray[np.float64], optional The (3, 3) rotation matrix of the sample orientation. Defaults to the identity matrix. - `tvec_s` : np.ndarray, optional + `tvec_s` : NDArray[np.float64], optional The (3,) translation vector from the lab origin to the center of the sample. Defaults to `<0, 0, 0>`. - `tvec_c` : np.ndarray, optional + `tvec_c` : NDArray[np.float64], optional The (3,) translation vector from the center of the sample to the center of the crystal or grain. Defaults to `<0, 0, 0>`. `full_output` : bool, optional @@ -1273,19 +1273,19 @@ def make_powder_rings( Returns ------- - `valid_ang`: list[np.ndarray] + `valid_ang`: list[NDArray[np.float64]] A list of arrays of shape `(neta, 2)` containing the valid ring sector angles (two-theta, eta) that fall on the detector for each ring. - `valid_xy`: list[np.ndarray] + `valid_xy`: list[NDArray[np.float64]] A list of arrays of shape `(neta, 2)` containing the valid ring sector cartesian coordinates (x, y) on the detector for each ring. - `tth_ranges`: np.ndarray + `tth_ranges`: NDArray[np.float64] An array of shape `(n_rings, 2)` containing the two-theta ranges (in radians) for each ring. - `map_indices`: list[np.ndarray] + `map_indices`: list[NDArray[np.float64]] A list of arrays of shape `(neta,)` indicating whether each ring point maps to a valid pixel on the panel. Returns if `full_output` is `True`. - `eta_edges`: np.ndarray + `eta_edges`: NDArray[np.float64] An array of shape `(neta + 1,)` containing the azimuthal edges (in radians) of the ring sectors. Returns if `full_output` is `True`. @@ -1868,7 +1868,7 @@ def calc_compton_physics_package_transmission( energy: np.floating, rMat_s: NDArray[np.float64], physics_package: AbstractPhysicsPackage, - ) -> np.ndarray: + ) -> NDArray[np.float64]: '''calculate the attenuation of inelastically scattered photons. since these photons lose energy, the attenuation length is angle dependent ergo a separate @@ -1910,10 +1910,10 @@ def calc_compton_physics_package_transmission( def calc_compton_window_transmission( self, - energy: np.floating, - rMat_s: np.ndarray, + energy: np.float64, + rMat_s: NDArray[np.float64], physics_package: AbstractPhysicsPackage, - ) -> np.ndarray: + ) -> NDArray[np.float64]: '''calculate the attenuation of inelastically scattered photons just fropm the window. since these photons lose energy, the attenuation length @@ -1989,12 +1989,12 @@ def calc_transmission_window( def calc_compton_transmission( self, - seca: np.ndarray, - secb: np.ndarray, + seca: NDArray[np.float64], + secb: NDArray[np.float64], energy: np.floating, physics_package: AbstractPhysicsPackage, pp_layer: str, - ) -> np.ndarray: + ) -> NDArray[np.float64]: if pp_layer == 'sample': formula = physics_package.sample_material density = physics_package.sample_density @@ -2027,10 +2027,10 @@ def calc_compton_transmission( def calc_compton_transmission_sample( self, - seca: np.ndarray, + seca: NDArray[np.float64], energy: np.floating, physics_package: AbstractPhysicsPackage, - ) -> np.ndarray: + ) -> NDArray[np.float64]: thickness_s = physics_package.sample_thickness # in microns mu_s = 1.0 / physics_package.sample_absorption_length(energy) @@ -2038,10 +2038,10 @@ def calc_compton_transmission_sample( def calc_compton_transmission_window( self, - secb: np.ndarray, + secb: NDArray[np.float64], energy: np.floating, physics_package: AbstractPhysicsPackage, - ) -> np.ndarray: + ) -> NDArray[np.float64]: formula = physics_package.window_material if formula is None: return np.ones(self.shape) @@ -2148,16 +2148,16 @@ def _col_edge_vec(cols, pixel_size_col): @numba.njit(nogil=True, cache=True) def _interpolate_bilinear( - img: np.ndarray, - cc: np.ndarray, - fc: np.ndarray, - cf: np.ndarray, - ff: np.ndarray, - i_floor_img: np.ndarray, - j_floor_img: np.ndarray, - i_ceil_img: np.ndarray, - j_ceil_img: np.ndarray, -) -> np.ndarray: + img: NDArray[np.float64], + cc: NDArray[np.float64], + fc: NDArray[np.float64], + cf: NDArray[np.float64], + ff: NDArray[np.float64], + i_floor_img: NDArray[np.float64], + j_floor_img: NDArray[np.float64], + i_ceil_img: NDArray[np.float64], + j_ceil_img: NDArray[np.float64], +) -> NDArray[np.float64]: # The math is faster and uses the GIL less (which is more # multi-threading friendly) when we run this code in numba. result = np.zeros(i_floor_img.shape[0], dtype=img.dtype) @@ -2180,17 +2180,17 @@ def _interpolate_bilinear( @numba.njit(nogil=True, cache=True) def _interpolate_bilinear_in_place( - img: np.ndarray, - cc: np.ndarray, - fc: np.ndarray, - cf: np.ndarray, - ff: np.ndarray, - i_floor_img: np.ndarray, - j_floor_img: np.ndarray, - i_ceil_img: np.ndarray, - j_ceil_img: np.ndarray, - on_panel_idx: np.ndarray, - output_img: np.ndarray, + img: NDArray[np.float64], + cc: NDArray[np.float64], + fc: NDArray[np.float64], + cf: NDArray[np.float64], + ff: NDArray[np.float64], + i_floor_img: NDArray[np.float64], + j_floor_img: NDArray[np.float64], + i_ceil_img: NDArray[np.float64], + j_ceil_img: NDArray[np.float64], + on_panel_idx: NDArray[np.float64], + output_img: NDArray[np.float64], ): # The math is faster and uses the GIL less (which is more # multi-threading friendly) when we run this code in numba. diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index e030f9d7b..05738e399 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -326,7 +326,7 @@ def _parse_imgser_dict(imgser_dict: Mapping[str, OmegaImageSeries | NDArray[np.f ncols = roi[1][1] - roi[1][0] n_images = len(images_in) ims = np.empty((n_images, nrows, ncols), dtype=images_in.dtype) - for i, image in images_in: + for i, image in images_in: # TODO: Is this meant to be an enumerate? ims[i, :, :] = images_in[ roi[0][0] : roi[0][1], roi[1][0] : roi[1][1] ] @@ -729,12 +729,12 @@ def __init__( self.update_memoization_sizes() @property - def mean_detector_center(self) -> np.ndarray: + def mean_detector_center(self) -> NDArray[np.float64]: """Return the mean center for all detectors""" centers = np.array([panel.tvec for panel in self.detectors.values()]) return centers.sum(axis=0) / len(centers) - def mean_group_center(self, group: str) -> np.ndarray: + def mean_group_center(self, group: str) -> NDArray[np.float64]: """Return the mean center for detectors belonging to a group""" centers = np.array([x.tvec for x in self.detectors_in_group(group).values()]) return centers.sum(axis=0) / len(centers) @@ -870,11 +870,11 @@ def _update_panel_beams(self): panel.xrs_dist = self.source_distance @property - def beam_vector(self) -> np.ndarray: + def beam_vector(self) -> NDArray[np.float64]: return self.active_beam['vector'] @beam_vector.setter - def beam_vector(self, x: np.ndarray): + def beam_vector(self, x: NDArray[np.float64]): """Accepts either a 3-element unit vector, or a 2-element (azimuth, polar angle) pair in degrees to set the beam vector.""" x = np.array(x).flatten() @@ -1076,10 +1076,10 @@ def extract_polar_maps( Returns ------- - ring_maps_panel : dict[str, np.ndarray] + ring_maps_panel : dict[str, NDArray[np.float64]] Dictionary of eta-omega maps for each detector panel. Each map has shape (n_rings, n_omegas, n_eta_bins). - eta_edges : np.ndarray + eta_edges : NDArray[np.float64] The edges of the eta bins used in the histograms. TODO: streamline projection code @@ -1130,7 +1130,7 @@ def extract_polar_maps( delta_eta = eta_edges[1] - eta_edges[0] ncols_eta = len(eta_edges) - 1 - ring_maps_panel: dict[str, np.ndarray] = { + ring_maps_panel: dict[str, NDArray[np.float64]] = { k: np.empty((0, 0)) for k in self.detectors } for det_key in self.detectors: @@ -1641,14 +1641,14 @@ def simulate_rotation_series( def _pull_spots_check_only( self, plane_data: PlaneData, - grain_params: tuple | np.ndarray, + grain_params: tuple[float, ...] | NDArray[np.float64], imgser_dict: Mapping[str, OmegaImageSeries | NDArray[np.float64]], tth_tol: float = 0.25, eta_tol: float = 1.0, ome_tol: float = 1.0, threshold: int = 10, - eta_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), - ome_period: Optional[tuple] = None, + eta_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi),), + ome_period: Optional[tuple[float, float]] = None, ): rMat_c = make_rmat_of_expmap(grain_params[:3]) tVec_c = np.asarray(grain_params[3:6]) @@ -1726,7 +1726,7 @@ def _pull_spots_check_only( def pull_spots( self, plane_data: PlaneData, - grain_params: tuple | np.ndarray, + grain_params: tuple | NDArray[np.float64], imgser_dict: dict, tth_tol: float = 0.25, eta_tol: float = 1.0, @@ -1749,7 +1749,7 @@ def pull_spots( plane_data : PlaneData Object containing crystallographic plane data - grain_params : list or np.ndarray + grain_params : list or NDArray[np.float64] A 6-element array defining the grain's orientation and position. The first 3 elements are orientation parameters (exponential map), and the last 3 are the translation vector in the sample frame. diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index f78d370ea..94a6c724d 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -187,12 +187,12 @@ def latticeParameters(lvec): def latticePlanes( - hkls: np.ndarray, - lparms: np.ndarray, + hkls: NDArray[np.float64], + lparms: NDArray[np.float64], ltype: str = 'cubic', wavelength: float = 1.54059292, strainMag: Optional[float] = None, -) -> Dict[str, np.ndarray]: +) -> LatPlaneData: """ Generates lattice plane data in the direct lattice for a given set of Miller indices. Vector components are written in the @@ -289,7 +289,7 @@ def latticePlanes( tth[~mask] = np.nan tth[mask] = 2.0 * np.arcsin(sth[mask]) - p: dict[str, NDArray[np.float64]] = dict(normals=unitVector(G), dspacings=d, tThetas=tth) + p: LatPlaneData = dict(normals=unitVector(G), dspacings=d, tThetas=tth) if strainMag is not None: p['tThetasLo'] = np.zeros(sth.shape) @@ -856,7 +856,7 @@ def tThMax(self, t_th_max: Union[float, valunits.valWUnit]) -> None: self._tThMax = toFloat(t_th_max, 'radians') @property - def exclusions(self) -> np.ndarray: + def exclusions(self) -> NDArray[np.bool_]: """ Excluded HKL's the plane data. diff --git a/hexrd/core/rotations.py b/hexrd/core/rotations.py index c5413d527..f69a36a1f 100644 --- a/hexrd/core/rotations.py +++ b/hexrd/core/rotations.py @@ -102,7 +102,7 @@ def arccosSafe(cosines): # -def _quat_to_scipy_rotation(q: np.ndarray) -> R: +def _quat_to_scipy_rotation(q: NDArray[np.float64]) -> R: """ Scipy has quaternions in a differnt order, this method converts them q must be a 2d array of shape (4, n). @@ -110,7 +110,7 @@ def _quat_to_scipy_rotation(q: np.ndarray) -> R: return R.from_quat(np.roll(q.T, -1, axis=1)) -def _scipy_rotation_to_quat(r: R) -> np.ndarray: +def _scipy_rotation_to_quat(r: R) -> NDArray[np.float64]: quat = np.roll(np.atleast_2d(r.as_quat()), 1, axis=1).T # Fix quat would work, but it does too much. Only need to check positive quat *= np.sign(quat[0, :]) @@ -921,7 +921,7 @@ def exponential_map(self): Returns ------- - np.ndarray + NDArray[np.float64] The (3, ) array representing the exponential map parameters of the encoded rotation (self.rmat). @@ -975,7 +975,7 @@ def distanceToFiber(c, s, q, qsym, centrosymmetry=False, bmatrix=I3): DESCRIPTION. centrosymmetry : bool, optional If True, apply centrosymmetry to c. The default is False. - bmatrix : np.ndarray, optional + bmatrix : NDArray[np.float64], optional (3,3) b matrix. Default is the identity Raises From 72b26635289c085e9435417cfdfe711598be85cf Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Mon, 2 Feb 2026 17:19:34 -0500 Subject: [PATCH 13/28] Continue typing in hexrd/core --- .../calibration/lmfit_param_handling.py | 12 ++-- hexrd/core/imageseries/load/framecache.py | 2 +- hexrd/core/imageseries/load/registry.py | 2 +- hexrd/core/imageseries/save.py | 2 +- hexrd/core/instrument/detector.py | 67 +++++++++++++++++-- hexrd/core/instrument/detector_coatings.py | 2 +- hexrd/core/instrument/physics_package.py | 2 +- hexrd/core/material/crystallography.py | 4 +- hexrd/core/material/utils.py | 8 +-- mypy.ini | 3 + 10 files changed, 81 insertions(+), 23 deletions(-) diff --git a/hexrd/core/fitting/calibration/lmfit_param_handling.py b/hexrd/core/fitting/calibration/lmfit_param_handling.py index 88815f8ab..52855e8be 100644 --- a/hexrd/core/fitting/calibration/lmfit_param_handling.py +++ b/hexrd/core/fitting/calibration/lmfit_param_handling.py @@ -19,7 +19,7 @@ rotMatOfExpMap, ) from hexrd.core.material.unitcell import _lpname -from .relative_constraints import RelativeConstraints, RelativeConstraintsType +from .relative_constraints import RelativeConstraints, RelativeConstraintsGroup, RelativeConstraintsSystem, RelativeConstraintsType from hexrd.core.fitting.calibration.relative_constraints import ( RelativeConstraints, RelativeConstraintsType, @@ -237,9 +237,9 @@ def add_group_constrained_detector_parameters( ) -def create_beam_param_names(instr: HEDMInstrument) -> dict[str, str]: +def create_beam_param_names(instr: HEDMInstrument) -> dict[str, dict[str, str]]: param_names = {} - for k, v in instr.beam_dict.items(): + for k in instr.beam_dict.keys(): prefix = f'{k}_' if instr.has_multi_beam else '' param_names[k] = { 'beam_polar': f'{prefix}beam_polar', @@ -276,7 +276,7 @@ def fix_detector_y( def update_instrument_from_params( instr, - params, + params: lmfit.Parameters, euler_convention=DEFAULT_EULER_CONVENTION, relative_constraints: Optional[RelativeConstraints] = None, ): @@ -442,7 +442,7 @@ def update_system_constrained_detector_parameters( instr: HEDMInstrument, params: dict, euler_convention: EULER_CONVENTION_TYPES, - relative_constraints: RelativeConstraints, + relative_constraints: RelativeConstraintsSystem, ): detectors = list(instr.detectors.values()) @@ -465,7 +465,7 @@ def update_group_constrained_detector_parameters( instr: HEDMInstrument, params: dict, euler_convention: EULER_CONVENTION_TYPES, - relative_constraints: RelativeConstraints, + relative_constraints: RelativeConstraintsGroup, ): for group in instr.detector_groups: detectors = list(instr.detectors_in_group(group).values()) diff --git a/hexrd/core/imageseries/load/framecache.py b/hexrd/core/imageseries/load/framecache.py index ca111a91d..d31dbf232 100644 --- a/hexrd/core/imageseries/load/framecache.py +++ b/hexrd/core/imageseries/load/framecache.py @@ -64,7 +64,7 @@ def _load_yml(self) -> None: self._cache = datad['file'] self._nframes = datad['nframes'] self._shape = tuple(datad['shape']) - self._dtype: str = np.dtype(datad['dtype']) + self._dtype: np.dtype = np.dtype(datad['dtype']) self._meta = yamlmeta(d['meta'], path=self._cache) def _load_cache(self): diff --git a/hexrd/core/imageseries/load/registry.py b/hexrd/core/imageseries/load/registry.py index 070c426cd..0142e98cd 100644 --- a/hexrd/core/imageseries/load/registry.py +++ b/hexrd/core/imageseries/load/registry.py @@ -4,7 +4,7 @@ class Registry(object): """Registry for imageseries adapters""" - adapter_registry = dict() + adapter_registry: dict = dict() @classmethod def register(cls, acls): diff --git a/hexrd/core/imageseries/save.py b/hexrd/core/imageseries/save.py index ba55a2e7b..fd628d805 100644 --- a/hexrd/core/imageseries/save.py +++ b/hexrd/core/imageseries/save.py @@ -82,7 +82,7 @@ class Writer(object, metaclass=_RegisterWriter): options specific to format """ - fmt = None + fmt: str | None = None def __init__(self, ims, fname, **kwargs): self._ims = ims diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index 16d66dd4b..f79b77d13 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -4,7 +4,7 @@ import copy import logging import os -from typing import Optional, TYPE_CHECKING, Sequence +from typing import Literal, Optional, TYPE_CHECKING, Sequence, overload from hexrd.core.instrument.constants import ( COATING_DEFAULT, @@ -549,6 +549,12 @@ def rmat(self): def normal(self): return self.rmat[:, 2] + @property + @abstractmethod + def pixel_normal(self) -> NDArray[np.float64]: + """Unit normal vector per pixel""" + raise NotImplementedError + # ...memoize??? @property def pixel_coords(self): @@ -600,7 +606,7 @@ def pixel_Q(self, energy: float, origin: NDArray[np.float64] = ct.zeros_3) -> ND def pixel_compton_energy_loss( self, - energy: np.floating, + energy: np.float64, origin: NDArray[np.float64] = ct.zeros_3, ) -> NDArray[np.float64]: '''inelastic compton scattering leads @@ -669,7 +675,7 @@ def pixel_compton_attenuation_length( def compute_compton_scattering_intensity( self, - energy: np.floating, + energy: np.float64, rMat_s: NDArray[np.float64], physics_package: AbstractPhysicsPackage, origin: NDArray[np.float64] = ct.zeros_3, @@ -1203,6 +1209,50 @@ def _generate_bilinear_interp_dict( 'j_ceil_img': j_ceil_img, } + @overload + def make_powder_rings( + self, + pd: PlaneData | NDArray[np.float64], + merge_hkls: bool = False, + delta_tth: Optional[float] = None, + delta_eta: float = 10.0, + eta_period: Sequence[float] | NDArray[np.float64] = (-np.pi, np.pi), + eta_list: Optional[Sequence[float] | NDArray[np.float64]] = None, + rmat_s: NDArray[np.float64] = ct.identity_3x3, + tvec_s: NDArray[np.float64] = ct.zeros_3, + tvec_c: NDArray[np.float64] = ct.zeros_3, + full_output: Literal[False] = False, # TODO: Remove this option completely + tth_distortion: Optional[RyggPinholeDistortion | JHEPinholeDistortion | + LayerDistortion] = None, + ) -> tuple[ + list[NDArray[np.float64]], + list[NDArray[np.float64]], + NDArray[np.float64] + ]: ... + + @overload + def make_powder_rings( + self, + pd: PlaneData | NDArray[np.float64], + merge_hkls: bool = False, + delta_tth: Optional[float] = None, + delta_eta: float = 10.0, + eta_period: Sequence[float] | NDArray[np.float64] = (-np.pi, np.pi), + eta_list: Optional[Sequence[float] | NDArray[np.float64]] = None, + rmat_s: NDArray[np.float64] = ct.identity_3x3, + tvec_s: NDArray[np.float64] = ct.zeros_3, + tvec_c: NDArray[np.float64] = ct.zeros_3, + full_output: Literal[True] = True, # TODO: Remove this option completely + tth_distortion: Optional[RyggPinholeDistortion | JHEPinholeDistortion | + LayerDistortion] = None, + ) -> tuple[ + list[NDArray[np.float64]], + list[NDArray[np.float64]], + NDArray[np.float64], + list[NDArray[np.float64]], + NDArray[np.float64] + ]: ... + def make_powder_rings( self, pd: PlaneData | NDArray[np.float64], # TODO: Make a different function for array input @@ -1223,6 +1273,10 @@ def make_powder_rings( NDArray[np.float64], list[NDArray[np.float64]], NDArray[np.float64] + ] | tuple[ + list[NDArray[np.float64]], + list[NDArray[np.float64]], + NDArray[np.float64] ]: """ Generate points on Debye Scherrer rings over the detector. @@ -1307,7 +1361,8 @@ def make_powder_rings( # do merging if asked if merge_hkls: - _, tth_ranges = pd.getMergedRanges(cullDupl=True) + _, tth_ranges_raw = pd.getMergedRanges(cullDupl=True) + tth_ranges = np.array(tth_ranges_raw) tth = np.average(tth_ranges, axis=1) else: tth_ranges = pd.getTThRanges() @@ -1511,7 +1566,7 @@ def map_to_plane(self, pts, rmat, tvec): def simulate_rotation_series( self, plane_data: PlaneData, - grain_param_list: list[float], + grain_param_list: list[Sequence[float]], eta_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), ome_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), ome_period: tuple[float, float] = (-np.pi, np.pi), @@ -1527,7 +1582,7 @@ def simulate_rotation_series( ---------- plane_data : PlaneData DESCRIPTION. - grain_param_list : TYPE + grain_param_list : list[Sequence[float]] DESCRIPTION. eta_ranges : TYPE, optional DESCRIPTION. The default is [(-np.pi, np.pi)]. diff --git a/hexrd/core/instrument/detector_coatings.py b/hexrd/core/instrument/detector_coatings.py index f83674d7e..d3883c128 100644 --- a/hexrd/core/instrument/detector_coatings.py +++ b/hexrd/core/instrument/detector_coatings.py @@ -82,7 +82,7 @@ def thickness(self, value): self._thickness = value @property - def formula(self) -> str: + def formula(self) -> str | None: return self._formula @formula.setter diff --git a/hexrd/core/instrument/physics_package.py b/hexrd/core/instrument/physics_package.py index def34e2cc..f017498da 100644 --- a/hexrd/core/instrument/physics_package.py +++ b/hexrd/core/instrument/physics_package.py @@ -59,7 +59,7 @@ def type(self): # 2. If the layer requires a special class (like `PinholeLayer`), # specify that in `SPECIAL_LAYERS` LAYER_TYPES: list[str] = [] - SPECIAL_LAYERS: dict[str, PhysicsPackageLayer] = {} + SPECIAL_LAYERS: dict[str, PhysicsPackageLayer | PinholeLayer] = {} def __init__(self, **kwargs): # The physics packages are set up so that you can access layer diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index 94a6c724d..cc2787f8a 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -1310,7 +1310,7 @@ def _getTThRange(self, iHKLr: int) -> Tuple[float, float]: tThLo = hklData['tThetaLo'] return (tThLo, tThHi) - def getTThRanges(self, strainMag: Optional[float] = None) -> np.ndarray: + def getTThRanges(self, strainMag: Optional[float] = None) -> NDArray[np.float64]: """ Get the 2-theta ranges for included hkls @@ -1340,7 +1340,7 @@ def getTThRanges(self, strainMag: Optional[float] = None) -> np.ndarray: self._wavelength / 2.0 / (d * (1.0 - strainMag)) ) tThRanges.append((tThLo, tThHi)) - return np.array(tThRanges) + return np.array(tThRanges, dtype=np.float64) def getMergedRanges( self, cullDupl: Optional[bool] = False diff --git a/hexrd/core/material/utils.py b/hexrd/core/material/utils.py index 7c1d1a45d..8ef9a4ffd 100644 --- a/hexrd/core/material/utils.py +++ b/hexrd/core/material/utils.py @@ -232,8 +232,8 @@ def convert_density_to_atoms_per_cubic_angstrom( def calculate_coherent_scattering_factor( element: str, - Q: np.ndarray, -) -> np.ndarray: + Q: NDArray[np.float64], +) -> NDArray[np.float64]: s = Q / (4.0 * np.pi) sfact = constants.scatfac[element] fe = sfact[5] @@ -244,8 +244,8 @@ def calculate_coherent_scattering_factor( def calculate_incoherent_scattering_factor( element: str, - Q: np.ndarray, -) -> np.ndarray: + Q: NDArray[np.float64], +) -> NDArray[np.float64]: with importlib.resources.open_binary( hexrd.resources, diff --git a/mypy.ini b/mypy.ini index 73c184cdf..38a10ecd6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -40,5 +40,8 @@ ignore_missing_imports = True [mypy-psutil.*] ignore_missing_imports = True +[mypy-fabio.*] +ignore_missing_imports = True + [mypy-hexrd.powder.wppf.*] ignore_errors = True From 809b8a7c45747957d6261f787947d28f541b27f3 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 09:23:20 -0500 Subject: [PATCH 14/28] Down to 33 errors --- hexrd/core/imageseries/load/__init__.py | 8 ++++---- hexrd/core/imageseries/load/array.py | 4 ++++ hexrd/core/imageseries/load/eiger_stream_v1.py | 6 +++++- hexrd/core/imageseries/load/eiger_stream_v2.py | 4 ++++ hexrd/core/imageseries/load/framecache.py | 4 ++++ hexrd/core/imageseries/load/function.py | 6 ++++++ hexrd/core/imageseries/load/hdf5.py | 4 ++++ hexrd/core/imageseries/load/imagefiles.py | 4 ++++ hexrd/core/imageseries/omega.py | 4 +++- hexrd/core/instrument/detector.py | 4 ++-- hexrd/core/instrument/hedm_instrument.py | 2 +- hexrd/core/transforms/new_capi/xf_new_capi.py | 4 ++-- hexrd/hedm/config/findorientations.py | 6 +++--- mypy.ini | 6 ++++++ 14 files changed, 52 insertions(+), 14 deletions(-) diff --git a/hexrd/core/imageseries/load/__init__.py b/hexrd/core/imageseries/load/__init__.py index 45d63915f..bd69efef2 100644 --- a/hexrd/core/imageseries/load/__init__.py +++ b/hexrd/core/imageseries/load/__init__.py @@ -1,6 +1,6 @@ import abc import pkgutil -from typing import Any +from typing import Any, Optional import numpy as np from numpy.typing import NDArray @@ -19,16 +19,16 @@ def __init__(cls, name, bases, attrs): class ImageSeriesAdapter(ImageSeriesABC, metaclass=_RegisterAdapterClass): format: str | None = None - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - self._dtype: np.dtype = None + self._dtype: Optional[np.dtype] = None @property def dtype(self): return self._dtype @dtype.setter - def dtype(self, value: np.dtype): + def dtype(self, value: Optional[np.dtype]): self._dtype = value def get_region(self, frame_idx: int, region: RegionType) -> np.ndarray: diff --git a/hexrd/core/imageseries/load/array.py b/hexrd/core/imageseries/load/array.py index a7deb624f..e9f89736b 100644 --- a/hexrd/core/imageseries/load/array.py +++ b/hexrd/core/imageseries/load/array.py @@ -49,6 +49,10 @@ def shape(self): @property def dtype(self): return self._data.dtype + + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value def __getitem__(self, key): return self._data[key].copy() diff --git a/hexrd/core/imageseries/load/eiger_stream_v1.py b/hexrd/core/imageseries/load/eiger_stream_v1.py index 86ab82c92..4daf367e6 100644 --- a/hexrd/core/imageseries/load/eiger_stream_v1.py +++ b/hexrd/core/imageseries/load/eiger_stream_v1.py @@ -16,7 +16,7 @@ class EigerStreamV1ImageSeriesAdapter(ImageSeriesAdapter): format = 'eiger-stream-v1' - def __init__(self, fname, **kwargs): + def __init__(self, fname, **kwargs) -> None: if isinstance(fname, h5py.File): self._h5name: str = fname.filename self._h5file: h5py.File = fname @@ -107,6 +107,10 @@ def _first_data_entry(self): @property def dtype(self): return np.dtype(self._first_data_entry['dtype'][()]) + + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value @property def shape(self): diff --git a/hexrd/core/imageseries/load/eiger_stream_v2.py b/hexrd/core/imageseries/load/eiger_stream_v2.py index ac0400838..3c79bf0e4 100644 --- a/hexrd/core/imageseries/load/eiger_stream_v2.py +++ b/hexrd/core/imageseries/load/eiger_stream_v2.py @@ -193,6 +193,10 @@ def dtype(self) -> np.dtype: return np.dtype('float64') return np.dtype(self._first_threshold_1_entry['dtype'][()]) + + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value @property def shape(self): diff --git a/hexrd/core/imageseries/load/framecache.py b/hexrd/core/imageseries/load/framecache.py index d31dbf232..8868c2a25 100644 --- a/hexrd/core/imageseries/load/framecache.py +++ b/hexrd/core/imageseries/load/framecache.py @@ -197,6 +197,10 @@ def load_metadata(self, indict): def dtype(self): return self._dtype + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value + @property def shape(self): return self._shape diff --git a/hexrd/core/imageseries/load/function.py b/hexrd/core/imageseries/load/function.py index 499c11190..76f8cd91f 100644 --- a/hexrd/core/imageseries/load/function.py +++ b/hexrd/core/imageseries/load/function.py @@ -2,6 +2,8 @@ and returns a 2D numpy array. """ +import numpy as np + from . import ImageSeriesAdapter from ..imageseriesiter import ImageSeriesIterator @@ -56,6 +58,10 @@ def shape(self): @property def dtype(self): return self._dtype + + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value def __getitem__(self, key): if not isinstance(key, int): diff --git a/hexrd/core/imageseries/load/hdf5.py b/hexrd/core/imageseries/load/hdf5.py index 84112b036..1fb637773 100644 --- a/hexrd/core/imageseries/load/hdf5.py +++ b/hexrd/core/imageseries/load/hdf5.py @@ -146,6 +146,10 @@ def metadata(self): @property def dtype(self): return self.__image_dataset.dtype + + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value @property def shape(self): diff --git a/hexrd/core/imageseries/load/imagefiles.py b/hexrd/core/imageseries/load/imagefiles.py index cc3593dab..27133e6c6 100644 --- a/hexrd/core/imageseries/load/imagefiles.py +++ b/hexrd/core/imageseries/load/imagefiles.py @@ -202,6 +202,10 @@ def shape(self): @property def dtype(self): return self._dtype + + @dtype.setter + def dtype(self, value: np.dtype): + self._dtype = value @property def infolist(self): diff --git a/hexrd/core/imageseries/omega.py b/hexrd/core/imageseries/omega.py index 3ba1551e1..efb194648 100644 --- a/hexrd/core/imageseries/omega.py +++ b/hexrd/core/imageseries/omega.py @@ -6,6 +6,8 @@ import numpy as np from numpy.typing import NDArray +from hexrd.core.imageseries.process import ProcessedImageSeries + from .baseclass import ImageSeries class OmegaImageSeries(ImageSeries): @@ -14,7 +16,7 @@ class OmegaImageSeries(ImageSeries): DFLT_TOL = 1.0e-6 TAU = 360 - def __init__(self, ims: 'ImageSeries | OmegaImageSeries'): + def __init__(self, ims: 'ImageSeries | OmegaImageSeries | ProcessedImageSeries'): """This class is initialized with an existing imageseries""" # check for omega metadata if 'omega' in ims.metadata: diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index f79b77d13..06e9f6bc9 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -1567,8 +1567,8 @@ def simulate_rotation_series( self, plane_data: PlaneData, grain_param_list: list[Sequence[float]], - eta_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), - ome_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi)), + eta_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi),), + ome_ranges: Sequence[tuple[float, float]] = ((-np.pi, np.pi),), ome_period: tuple[float, float] = (-np.pi, np.pi), chi: float = 0.0, tVec_s: NDArray[np.float64] = ct.zeros_3, diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index 05738e399..eee3dc321 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -1733,7 +1733,7 @@ def pull_spots( ome_tol: float = 1.0, npdiv: int = 2, threshold: int = 10, - eta_ranges: Sequence[tuple] = ((-np.pi, np.pi)), + eta_ranges: Sequence[tuple] = ((-np.pi, np.pi),), ome_period: Optional[tuple] = None, dirname: str = 'results', filename: Optional[str] = None, diff --git a/hexrd/core/transforms/new_capi/xf_new_capi.py b/hexrd/core/transforms/new_capi/xf_new_capi.py index a25f4540a..7242f8be4 100644 --- a/hexrd/core/transforms/new_capi/xf_new_capi.py +++ b/hexrd/core/transforms/new_capi/xf_new_capi.py @@ -23,7 +23,7 @@ - makeRotMatOfQuat - homochoricOfQuat """ -from typing import Optional, Tuple, Union +from typing import Optional, Tuple, Sequence import numpy as np from numpy.typing import NDArray @@ -537,7 +537,7 @@ def make_sample_rmat(chi: float, ome: float | NDArray[np.float64]) -> NDArray[np return result -def make_rmat_of_expmap(exp_map: tuple[float] | NDArray[np.float64]) -> NDArray[np.float64]: +def make_rmat_of_expmap(exp_map: Sequence[float] | NDArray[np.float64]) -> NDArray[np.float64]: """ Calculate the rotation matrix of an exponential map. diff --git a/hexrd/hedm/config/findorientations.py b/hexrd/hedm/config/findorientations.py index b4bf32253..c09742ed4 100644 --- a/hexrd/hedm/config/findorientations.py +++ b/hexrd/hedm/config/findorientations.py @@ -225,14 +225,14 @@ def fiber_ndiv(self): class OrientationMapsConfig(Config): @property - def active_hkls(self) -> int | list[int] | None: + def active_hkls(self) -> list[int] | str | None: hkls: int | str | None = self._cfg.get( 'find_orientations:orientation_maps:active_hkls', default='all' ) if isinstance(hkls, int): - hkls = [hkls] + return [hkls] if hkls == 'all': - hkls = None + return None return hkls @property diff --git a/mypy.ini b/mypy.ini index 38a10ecd6..94df94cf5 100644 --- a/mypy.ini +++ b/mypy.ini @@ -43,5 +43,11 @@ ignore_missing_imports = True [mypy-fabio.*] ignore_missing_imports = True +[mypy-progressbar.*] +ignore_missing_imports = True + +[mypy-nvtxpy.*] +ignore_missing_imports = True + [mypy-hexrd.powder.wppf.*] ignore_errors = True From ec69225b3822e725687802149570aaae7829c736 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 10:51:30 -0500 Subject: [PATCH 15/28] 33 -> 19 mypy errors --- .../calibration/lmfit_param_handling.py | 21 ++++---- hexrd/core/imageseries/load/framecache.py | 52 ++++++++----------- hexrd/core/imageseries/omega.py | 2 +- hexrd/core/instrument/detector.py | 10 ++-- hexrd/core/instrument/hedm_instrument.py | 1 + hexrd/core/material/crystallography.py | 19 +++++-- hexrd/core/rotations.py | 4 +- hexrd/hedm/config/__init__.py | 2 +- 8 files changed, 58 insertions(+), 53 deletions(-) diff --git a/hexrd/core/fitting/calibration/lmfit_param_handling.py b/hexrd/core/fitting/calibration/lmfit_param_handling.py index 52855e8be..5d887c67c 100644 --- a/hexrd/core/fitting/calibration/lmfit_param_handling.py +++ b/hexrd/core/fitting/calibration/lmfit_param_handling.py @@ -19,9 +19,8 @@ rotMatOfExpMap, ) from hexrd.core.material.unitcell import _lpname -from .relative_constraints import RelativeConstraints, RelativeConstraintsGroup, RelativeConstraintsSystem, RelativeConstraintsType +from .relative_constraints import RelativeConstraints, RelativeConstraintsGroup, RelativeConstraintsSystem, RelativeConstraintsType, RelativeConstraintsNone from hexrd.core.fitting.calibration.relative_constraints import ( - RelativeConstraints, RelativeConstraintsType, ) @@ -29,7 +28,7 @@ # First is the axes_order, second is extrinsic DEFAULT_EULER_CONVENTION = ('zxz', False) -EULER_CONVENTION_TYPES = dict | tuple | None +EULER_CONVENTION_TYPES = dict | tuple def create_instr_params( @@ -324,23 +323,23 @@ def update_instrument_from_params( ] instr.tvec = np.r_[instr_tvec] - if ( - relative_constraints is None - or relative_constraints.type == RelativeConstraintsType.none - ): + if relative_constraints is None or isinstance(relative_constraints, RelativeConstraintsNone) or \ + relative_constraints.type == RelativeConstraintsType.none: update_unconstrained_detector_parameters( instr, params, euler_convention, ) - elif relative_constraints.type == RelativeConstraintsType.group: + elif isinstance(relative_constraints, RelativeConstraintsGroup) or \ + relative_constraints.type == RelativeConstraintsType.group: update_group_constrained_detector_parameters( instr, params, euler_convention, relative_constraints, ) - elif relative_constraints.type == RelativeConstraintsType.system: + elif isinstance(relative_constraints, RelativeConstraintsSystem) or \ + relative_constraints.type == RelativeConstraintsType.system: update_system_constrained_detector_parameters( instr, params, @@ -485,7 +484,7 @@ def update_group_constrained_detector_parameters( ) -def _tilt_to_rmat(tilt: NDArray[np.float64], euler_convention: dict | tuple) -> NDArray[np.float64]: +def _tilt_to_rmat(tilt: NDArray[np.float64], euler_convention: Optional[dict | tuple]) -> NDArray[np.float64]: # Convert the tilt to exponential map parameters, and then # to the rotation matrix, and return. if euler_convention is None: @@ -715,7 +714,7 @@ def validate_params_list(params_list): } -def normalize_euler_convention(euler_convention): +def normalize_euler_convention(euler_convention: EULER_CONVENTION_TYPES) -> tuple: if isinstance(euler_convention, dict): return ( euler_convention['axes_order'], diff --git a/hexrd/core/imageseries/load/framecache.py b/hexrd/core/imageseries/load/framecache.py index 8868c2a25..151fd73fe 100644 --- a/hexrd/core/imageseries/load/framecache.py +++ b/hexrd/core/imageseries/load/framecache.py @@ -7,6 +7,8 @@ import h5py import numpy as np +from numpy.typing import NDArray +from typing import Any import yaml from scipy.sparse import csr_array @@ -62,10 +64,10 @@ def _load_yml(self) -> None: d = yaml.load(f) datad = d['data'] self._cache = datad['file'] - self._nframes = datad['nframes'] - self._shape = tuple(datad['shape']) + self._nframes: int = datad['nframes'] + self._shape: tuple[int, ...] = tuple(datad['shape']) self._dtype: np.dtype = np.dtype(datad['dtype']) - self._meta = yamlmeta(d['meta'], path=self._cache) + self._meta: dict[str, NDArray[Any]] = yamlmeta(d['meta'], path=self._cache) def _load_cache(self): if self._style == 'fch5': @@ -95,37 +97,25 @@ def _load_cache_npz(self): with np.load(self._fname) as arrs: self._load_cache_npz_arrays(arrs) - def _load_cache_npz_arrays(self, arrs: np.lib.npyio.NpzFile): - # HACK: while the loaded npz file has a getitem method - # that mimicks a dict, it doesn't have a "pop" method. - # must make an empty dict to pop after assignment of - # class attributes so we can get to the metadata - keysd = dict.fromkeys(list(arrs.keys())) - self._nframes = int(arrs['nframes']) - self._shape = tuple(arrs['shape']) - # Check the type so we can read files written - # using Python 2.7 - array_dtype = arrs['dtype'].dtype - # Python 3 - if array_dtype.type == np.str_: - dtype_str = str(arrs['dtype']) - # Python 2.7 + def _load_cache_npz_arrays(self, arrs: np.lib.npyio.NpzFile) -> None: + self._nframes = int(arrs["nframes"]) + self._shape = tuple(int(x) for x in arrs["shape"]) + + array_dtype: np.dtype[Any] = arrs["dtype"].dtype + + if array_dtype.type is np.str_: + dtype_str: str = str(arrs["dtype"]) else: - dtype_str = arrs['dtype'].tobytes().decode() + dtype_str = arrs["dtype"].tobytes().decode() + self._dtype = np.dtype(dtype_str) - keysd.pop('nframes') - keysd.pop('shape') - keysd.pop('dtype') - for i in range(self._nframes): - keysd.pop(f"{i}_row") - keysd.pop(f"{i}_col") - keysd.pop(f"{i}_data") - - # all rmaining keys should be metadata - for key in keysd: - keysd[key] = arrs[key] - self._meta = keysd + reserved = { + "nframes", "shape", "dtype", + *{f"{i}_{s}" for i in range(self._nframes) for s in ("row", "col", "data")} + } + + self._meta = { k: arrs[k] for k in arrs.keys() if k not in reserved } def _load_framelist(self): """load into list of csr sparse matrices""" diff --git a/hexrd/core/imageseries/omega.py b/hexrd/core/imageseries/omega.py index efb194648..330443af4 100644 --- a/hexrd/core/imageseries/omega.py +++ b/hexrd/core/imageseries/omega.py @@ -141,7 +141,7 @@ class OmegaWedges(object): number of frames in imageseries """ - def __init__(self, nframes): + def __init__(self, nframes: int) -> None: self.nframes = nframes self._wedges: list[dict[str, int]] = [] diff --git a/hexrd/core/instrument/detector.py b/hexrd/core/instrument/detector.py index 06e9f6bc9..5d9ead8e7 100644 --- a/hexrd/core/instrument/detector.py +++ b/hexrd/core/instrument/detector.py @@ -1354,8 +1354,12 @@ def make_powder_rings( pd = PlaneData(None, pd) if delta_tth is not None: pd.tThWidth = np.radians(delta_tth) - else: + elif pd.tThWidth is not None: delta_tth = np.degrees(pd.tThWidth) + else: + raise ValueError( + "Must supply delta_tth if PlaneData has no tThWidth" + ) del_eta = np.radians(delta_eta) @@ -1639,8 +1643,8 @@ def simulate_rotation_series( for gparm in grain_param_list: # make useful parameters rMat_c = make_rmat_of_expmap(gparm[:3]) - tVec_c = gparm[3:6] - vInv_s = gparm[6:] + tVec_c = np.array(gparm[3:6]) + vInv_s = np.array(gparm[6:]) # Apply an energy correction according to grain position corrected_wavelength = xrdutil.apply_correction_to_wavelength( diff --git a/hexrd/core/instrument/hedm_instrument.py b/hexrd/core/instrument/hedm_instrument.py index eee3dc321..d104f4cb3 100644 --- a/hexrd/core/instrument/hedm_instrument.py +++ b/hexrd/core/instrument/hedm_instrument.py @@ -314,6 +314,7 @@ def _parse_imgser_dict(imgser_dict: Mapping[str, OmegaImageSeries | NDArray[np.f ) if isinstance(images_in, OmegaImageSeries): # if it was an OmegaImageSeries, must re-cast + assert isinstance(ims, ProcessedImageSeries) ims = OmegaImageSeries(ims) elif isinstance(images_in, np.ndarray): # 2- or 3-d array of images diff --git a/hexrd/core/material/crystallography.py b/hexrd/core/material/crystallography.py index cc2787f8a..645aa6e3b 100644 --- a/hexrd/core/material/crystallography.py +++ b/hexrd/core/material/crystallography.py @@ -61,7 +61,14 @@ # units dUnit = 'angstrom' -LatPlaneData = Mapping[str, NDArray[Any]] +# LatPlaneData = Mapping[str, NDArray[Any]] + +class LatPlaneData(TypedDict): + normals: NDArray[np.float64] + dspacings: NDArray[np.float64] + tThetas: NDArray[np.float64] + tThetasLo: NDArray[np.float64] + tThetasHi: NDArray[np.float64] class LatVecOps(TypedDict): F: NDArray[np.float64] @@ -289,7 +296,8 @@ def latticePlanes( tth[~mask] = np.nan tth[mask] = 2.0 * np.arcsin(sth[mask]) - p: LatPlaneData = dict(normals=unitVector(G), dspacings=d, tThetas=tth) + p: LatPlaneData = dict(normals=unitVector(G), dspacings=d, tThetas=tth, + tThetasLo=np.array([]), tThetasHi=np.array([])) if strainMag is not None: p['tThetasLo'] = np.zeros(sth.shape) @@ -691,7 +699,7 @@ class PlaneData(object): tThWidth """ - def __init__(self, hkls: np.ndarray, *args, **kwargs) -> None: + def __init__(self, hkls: Optional[np.ndarray], *args, **kwargs) -> None: """ Constructor for PlaneData @@ -734,8 +742,11 @@ def __init__(self, hkls: np.ndarray, *args, **kwargs) -> None: else: raise NotImplementedError(f'args : {args}') + if hkls is None: + raise ValueError('hkls cannot be None if not passing PlaneData object') + self._laueGroup = laueGroup - self._hkls = copy.deepcopy(hkls) + self._hkls: NDArray[np.int_] = copy.deepcopy(hkls) self._strainMag = strainMag self._structFact: Optional[NDArray[np.float64]] = np.ones(self._hkls.shape[1]) self.tThWidth: float | None = tThWidth diff --git a/hexrd/core/rotations.py b/hexrd/core/rotations.py index f69a36a1f..6796c2cdb 100644 --- a/hexrd/core/rotations.py +++ b/hexrd/core/rotations.py @@ -33,7 +33,7 @@ import numpy as np from numba import njit from numpy.typing import NDArray -from typing import Literal, Optional +from typing import Literal, Optional, Sequence from scipy.optimize import leastsq from scipy.spatial.transform import Rotation as R @@ -1105,7 +1105,7 @@ def discreteFiber(c, s, B=I3, ndiv=120, invert=False, csym=None, ssym=None): # -def mapAngle(ang, ang_range: Optional[tuple[float, float] | NDArray[np.float64]]=None, +def mapAngle(ang, ang_range: Optional[Sequence[float] | NDArray[np.float64]]=None, units: Literal['degrees', 'radians']=angularUnits) -> NDArray[np.float64]: """ Map an angle into a specified period """ diff --git a/hexrd/hedm/config/__init__.py b/hexrd/hedm/config/__init__.py index 7d439f05d..198708a44 100644 --- a/hexrd/hedm/config/__init__.py +++ b/hexrd/hedm/config/__init__.py @@ -29,7 +29,7 @@ def open(file_name=None) -> list[root.RootConfig]: raise ValueError(f'Config file not found: "{file_name}"') with open_file(file_name) as f: - res = [] + res: list[config.Config] = [] for cfg in yaml.load_all(f, Loader=yaml.SafeLoader): try: # take the previous config section and update with values From 4270ab17f4a12d525430f8e340d335d2b45baa7e Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 11:05:03 -0500 Subject: [PATCH 16/28] Resolve dangling mangling --- hexrd/core/imageseries/load/eiger_stream_v1.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hexrd/core/imageseries/load/eiger_stream_v1.py b/hexrd/core/imageseries/load/eiger_stream_v1.py index 4daf367e6..c2db13e7e 100644 --- a/hexrd/core/imageseries/load/eiger_stream_v1.py +++ b/hexrd/core/imageseries/load/eiger_stream_v1.py @@ -66,12 +66,8 @@ def __len__(self): return len(self._data_group) def __getstate__(self): - # Remove any non-pickleable attributes. Prefix them with the private prefix - to_remove = [f'_{self.__class__.__name__}_h5file'] - state = self.__dict__.copy() - for attr in to_remove: - state.pop(attr) + state.pop('_h5file') return state From 338532d8ba74945aa7c41757e83aeee4f0de14ae Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 11:22:30 -0500 Subject: [PATCH 17/28] Coerce data to np.str_ --- hexrd/core/utils/compatibility.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hexrd/core/utils/compatibility.py b/hexrd/core/utils/compatibility.py index 091b47c3b..991645e66 100644 --- a/hexrd/core/utils/compatibility.py +++ b/hexrd/core/utils/compatibility.py @@ -6,7 +6,7 @@ import h5py -def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.bytes_] | NDArray[np.str_]: +def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.str_]: if version('h5py') >= '3': # In h5py >= 3.0.0, h5py no longer converts the data type to a # string automatically, and we have to do it manually... @@ -14,4 +14,8 @@ def h5py_read_string(dataset: h5py.Dataset) -> NDArray[np.bytes_] | NDArray[np.s if string_dtype is not None and string_dtype.encoding == 'utf-8': dataset = dataset.asstr() - return dataset[()] + h5_data = dataset[()] + if isinstance(h5_data, (bytes, np.bytes_)): + h5_data = h5_data.decode('utf-8') + + return h5_data From 0f9adad568162e00cae2d332c9e5ba4f2451bd64 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 13:06:23 -0500 Subject: [PATCH 18/28] Begin transforms cleanup --- hexrd/core/transforms/new_capi/module.c | 98 ++---- hexrd/core/transforms/transforms_CAPI.c | 30 -- hexrd/core/transforms/transforms_CAPI.h | 10 - hexrd/core/transforms/transforms_CFUNC.c | 427 +---------------------- 4 files changed, 23 insertions(+), 542 deletions(-) diff --git a/hexrd/core/transforms/new_capi/module.c b/hexrd/core/transforms/new_capi/module.c index bbff03a30..e28616c5f 100644 --- a/hexrd/core/transforms/new_capi/module.c +++ b/hexrd/core/transforms/new_capi/module.c @@ -83,9 +83,10 @@ * ============================================================================= */ -/*#define EXPORT_METHOD(name) \ - { STR(name), CONCAT(python_, name), METH_VARARGS, CONCAT(docstring_, name) } -*/ +/* + * Module initialization (Python 3 only) + */ + #define EXPORT_METHOD(name) \ { STR(name), CONCAT(python_, name), METH_VARARGS, "" } @@ -106,85 +107,30 @@ static PyMethodDef _module_methods[] = { EXPORT_METHOD(validateAngleRanges), /* validate_angle_ranges */ EXPORT_METHOD(rotate_vecs_about_axis), /* rotate_vecs_about_axis */ EXPORT_METHOD(quat_distance), /* quat_distance */ - /* adapted */ - /* {"anglesToGVec",anglesToGVec,METH_VARARGS,"take angle tuples to G-vectors"},*/ - /* {"anglesToDVec",anglesToDVec,METH_VARARGS,"take angle tuples to unit diffraction vectors"},*/ - /* {"gvecToDetectorXY",gvecToDetectorXY,METH_VARARGS,""},*/ - /* {"gvecToDetectorXYArray",gvecToDetectorXYArray,METH_VARARGS,""},*/ - /* {"detectorXYToGvec",detectorXYToGvec,METH_VARARGS,"take cartesian coordinates to G-vectors"},*/ - /* {"oscillAnglesOfHKLs",oscillAnglesOfHKLs,METH_VARARGS,"solve angle specs for G-vectors"},*/ - /* {"unitRowVector",unitRowVector,METH_VARARGS,"Normalize a single row vector"},*/ - /* {"unitRowVectors",unitRowVectors,METH_VARARGS,"Normalize a collection of row vectors"},*/ - /* {"makeOscillRotMat",makeOscillRotMat,METH_VARARGS,""},*/ - /* {"makeOscillRotMatArray",makeOscillRotMatArray,METH_VARARGS,""},*/ - /* {"makeRotMatOfExpMap",makeRotMatOfExpMap,METH_VARARGS,""},*/ - /* {"makeBinaryRotMat",makeBinaryRotMat,METH_VARARGS,""},*/ - /* {"makeEtaFrameRotMat",makeEtaFrameRotMat,METH_VARARGS,"Make eta basis COB matrix"},*/ - /* {"validateAngleRanges",validateAngleRanges,METH_VARARGS,""}, */ - /* {"rotate_vecs_about_axis",rotate_vecs_about_axis,METH_VARARGS,"Rotate vectors about an axis"},*/ - /* {"quat_distance",quat_distance,METH_VARARGS,"Compute distance between two unit quaternions"},*/ - - /* adapted but not part of the API, so not exported */ - /* {"makeDetectorRotMat",makeDetectorRotMat,METH_VARARGS,""},*/ - - /* removed... */ - /* {"makeGVector",makeGVector,METH_VARARGS,"Make G-vectors from hkls and B-matrix"},*/ - /* {"arccosSafe",arccosSafe,METH_VARARGS,""},*/ - /* {"angularDifference",angularDifference,METH_VARARGS,"difference for cyclical angles"},*/ - /* {"mapAngle",mapAngle,METH_VARARGS,"map cyclical angles to specified range"},*/ - /* {"columnNorm",columnNorm,METH_VARARGS,""},*/ - /* {"rowNorm",rowNorm,METH_VARARGS,""},*/ - /* {"makeRotMatOfQuat",makeRotMatOfQuat,METH_VARARGS,""},*/ - /* {"homochoricOfQuat",homochoricOfQuat,METH_VARARGS,"Compute homochoric parameterization of list of unit quaternions"},*/ - {NULL,NULL,0,NULL} /* sentinel */ + {NULL, NULL, 0, NULL} /* sentinel */ }; -/* - * In Python 3 the entry point for a C module changes slightly, but both can - * be supported with little effort with some conditionals and macro magic - */ - -#if PY_VERSION_HEX >= 0x03000000 -# define IS_PY3K -#endif - -#if defined(IS_PY3K) -/* a module definition structure is required in Python 3 */ static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - STR(THIS_MODULE_NAME), - NULL, - -1, - _module_methods, - NULL, - NULL, - NULL, - NULL + PyModuleDef_HEAD_INIT, + STR(THIS_MODULE_NAME), + NULL, /* m_doc */ + -1, /* m_size */ + _module_methods, + NULL, /* m_slots */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL /* m_free */ }; -#endif - -#if defined(IS_PY3K) -# define MODULE_INIT_FUNC_NAME CONCAT(PyInit_, THIS_MODULE_NAME) -# define MODULE_RETURN(module) return module -#else -# define MODULE_INIT_FUNC_NAME CONCAT(init, THIS_MODULE_NAME) -# define MODULE_RETURN(module) return -#endif -PyMODINIT_FUNC -MODULE_INIT_FUNC_NAME(void) -{ - PyObject *module; -#if defined(IS_PY3K) - module = PyModule_Create(&moduledef); -#else - module = Py_InitModule(STR(THIS_MODULE_NAME),_module_methods); -#endif - if (NULL == module) - MODULE_RETURN(module); +PyMODINIT_FUNC CONCAT(PyInit_, THIS_MODULE_NAME)(void) +{ + PyObject *module = PyModule_Create(&moduledef); + if (module == NULL) { + return NULL; + } import_array(); - - MODULE_RETURN(module); + + return module; } diff --git a/hexrd/core/transforms/transforms_CAPI.c b/hexrd/core/transforms/transforms_CAPI.c index 68beb05b1..b2d518510 100644 --- a/hexrd/core/transforms/transforms_CAPI.c +++ b/hexrd/core/transforms/transforms_CAPI.c @@ -16,11 +16,6 @@ static PyMethodDef _transform_methods[] = { {"detectorXYToGvec",detectorXYToGvec,METH_VARARGS,"take cartesian coordinates to G-vectors"}, {"detectorXYToGvecArray",detectorXYToGvecArray,METH_VARARGS,"take cartesian coordinates to G-vectors"}, {"oscillAnglesOfHKLs",oscillAnglesOfHKLs,METH_VARARGS,"solve angle specs for G-vectors"}, - {"arccosSafe",arccosSafe,METH_VARARGS,""}, - {"angularDifference",angularDifference,METH_VARARGS,"difference for cyclical angles"}, - {"mapAngle",mapAngle,METH_VARARGS,"map cyclical angles to specified range"}, - {"columnNorm",columnNorm,METH_VARARGS,""}, - {"rowNorm",rowNorm,METH_VARARGS,""}, {"unitRowVector",unitRowVector,METH_VARARGS,"Normalize a single row vector"}, {"unitRowVectors",unitRowVectors,METH_VARARGS,"Normalize a collection of row vectors"}, {"makeDetectorRotMat",makeDetectorRotMat,METH_VARARGS,""}, @@ -716,31 +711,6 @@ static PyObject * oscillAnglesOfHKLs(PyObject * self, PyObject * args) /******************************************************************************/ /* Utility Funtions */ -static PyObject * arccosSafe(PyObject * self, PyObject * args) -{ - return(NULL); -} - -static PyObject * angularDifference(PyObject * self, PyObject * args) -{ - return(NULL); -} - -static PyObject * mapAngle(PyObject * self, PyObject * args) -{ - return(NULL); -} - -static PyObject * columnNorm(PyObject * self, PyObject * args) -{ - return(NULL); -} - -static PyObject * rowNorm(PyObject * self, PyObject * args) -{ - return(NULL); -} - static PyObject * unitRowVector(PyObject * self, PyObject * args) { PyArrayObject *vecIn, *vecOut; diff --git a/hexrd/core/transforms/transforms_CAPI.h b/hexrd/core/transforms/transforms_CAPI.h index ae0aacf75..5938ca742 100644 --- a/hexrd/core/transforms/transforms_CAPI.h +++ b/hexrd/core/transforms/transforms_CAPI.h @@ -42,16 +42,6 @@ static PyObject * oscillAnglesOfHKLs(PyObject * self, PyObject * args); /******************************************************************************/ /* Utility Funtions */ -static PyObject * arccosSafe(PyObject * self, PyObject * args); - -static PyObject * angularDifference(PyObject * self, PyObject * args); - -static PyObject * mapAngle(PyObject * self, PyObject * args); - -static PyObject * columnNorm(PyObject * self, PyObject * args); - -static PyObject * rowNorm(PyObject * self, PyObject * args); - static PyObject * unitRowVector(PyObject * self, PyObject * args); static PyObject * unitRowVectors(PyObject * self, PyObject * args); diff --git a/hexrd/core/transforms/transforms_CFUNC.c b/hexrd/core/transforms/transforms_CFUNC.c index f9b233c33..fd9b366d0 100644 --- a/hexrd/core/transforms/transforms_CFUNC.c +++ b/hexrd/core/transforms/transforms_CFUNC.c @@ -16,26 +16,8 @@ # define inline __inline #endif -/* - * For now, disable C99 codepaths - */ -#define USE_C99_CODE 0 -#if ! defined(USE_C99_CODE) -# if defined(__STDC__) -# if (__STD_VERSION__ >= 199901L) -# define USE_C99_CODE 1 -# else -# define USE_C99_CODE 0 -# endif -# endif -#endif - -#if ! USE_C99_CODE -/* - * Just remove any "restrict" keyword that may be present. - */ +// Just remove any "restrict" keyword that may be present. #define restrict -#endif static double epsf = 2.2e-16; static double sqrt_epsf = 1.5e-8; @@ -44,283 +26,7 @@ static double Zl[3] = {0.0,0.0,1.0}; /******************************************************************************/ /* Functions */ -#if USE_C99_CODE -static inline -double * -v3_v3s_inplace_add(double *dst_src1, - const double *src2, int stride) -{ - dst_src1[0] += src2[0]; - dst_src1[1] += src2[1*stride]; - dst_src1[2] += src2[2*stride]; - return dst_src1; -} - -static inline -double * -v3_v3s_add(const double *src1, - const double *src2, int stride, - double * restrict dst) -{ - dst[0] = src1[0] + src2[0]; - dst[1] = src1[1] + src2[1*stride]; - dst[2] = src1[2] + src2[2*stride]; - - return dst; -} - -static inline -double * -v3_v3s_inplace_sub(double *dst_src1, - const double *src2, int stride) -{ - dst_src1[0] -= src2[0]; - dst_src1[1] -= src2[1*stride]; - dst_src1[2] -= src2[2*stride]; - return dst_src1; -} - -static inline -double * -v3_v3s_sub(const double *src1, - const double *src2, int stride, - double * restrict dst) -{ - dst[0] = src1[0] - src2[0]; - dst[1] = src1[1] - src2[1*stride]; - dst[2] = src1[2] - src2[2*stride]; - - return dst; -} - -static inline -double * -v3_inplace_normalize(double * restrict v) -{ - double sqr_norm = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; - - if (sqr_norm > epsf) { - double normalize_factor = 1./sqrt(sqr_norm); - v[0] *= normalize_factor; - v[1] *= normalize_factor; - v[2] *= normalize_factor; - } - - return v; -} - -static inline -double * -v3_normalize(const double *in, - double * restrict out) -{ - double in0 = in[0], in1 = in[1], in2 = in[2]; - double sqr_norm = in0*in0 + in1*in1 + in2*in2; - - if (sqr_norm > epsf) { - double normalize_factor = 1./sqrt(sqr_norm); - out[0] = in0 * normalize_factor; - out[1] = in1 * normalize_factor; - out[2] = in2 * normalize_factor; - } else { - out[0] = in0; - out[1] = in1; - out[2] = in2; - } - - return out; -} - -static inline -double * -m33_inplace_transpose(double * restrict m) -{ - double e1 = m[1]; - double e2 = m[2]; - double e5 = m[5]; - m[1] = m[3]; - m[2] = m[6]; - m[5] = m[7]; - m[3] = e1; - m[6] = e2; - m[7] = e5; - - return m; -} - -static inline -double * -m33_transpose(const double *m, - double * restrict dst) -{ - dst[0] = m[0]; dst[1] = m[3]; dst[2] = m[6]; - dst[3] = m[1]; dst[4] = m[4]; dst[5] = m[7]; - dst[7] = m[2]; dst[8] = m[5]; dst[9] = m[9]; - - return dst; -} - -static inline -double -v3_v3s_dot(const double *v1, - const double *v2, int stride) -{ - return v1[0]*v2[0] + v1[1]*v2[stride] + v1[2]*v2[2*stride]; -} - - -/* 3x3 matrix by strided 3 vector product ------------------------------------- - hopefully a constant stride will be optimized - */ -static inline -double * -m33_v3s_multiply(const double *m, - const double *v, int stride, - double * restrict dst) -{ - dst[0] = m[0]*v[0] + m[1]*v[stride] + m[2]*v[2*stride]; - dst[1] = m[3]*v[0] + m[4]*v[stride] + m[5]*v[2*stride]; - dst[2] = m[6]*v[0] + m[7]*v[stride] + m[8]*v[2*stride]; - - return dst; -} - -/* transposed 3x3 matrix by strided 3 vector product -------------------------- - */ -static inline -double * -v3s_m33t_multiply(const double *v, int stride, - const double *m, - double * restrict dst) -{ - double v0 = v[0]; double v1 = v[stride]; double v2 = v[2*stride]; - dst[0] = v0*m[0] + v1*m[1] + v2*m[2]; - dst[1] = v0*m[3] + v1*m[4] + v2*m[5]; - dst[2] = v0*m[6] + v1*m[7] + v2*m[8]; - - return dst; -} - -static inline -double * -v3s_m33_multiply(const double *v, int stride, - const double *m, - double * restrict dst) -{ - double v0 = v[0]; double v1 = v[stride]; double v2 = v[2*stride]; - dst[0] = v0*m[0] + v1*m[3] + v2*m[6]; - dst[1] = v0*m[1] + v1*m[4] + v2*m[7]; - dst[2] = v0*m[2] + v1*m[5] + v2*m[8]; - - return dst; -} - -static inline -double * -m33t_v3s_multiply(const double *m, - const double *v, int stride, - double * restrict dst) -{ - dst[0] = m[0]*v[0] + m[3]*v[stride] + m[6]*v[2*stride]; - dst[1] = m[1]*v[0] + m[4]*v[stride] + m[7]*v[2*stride]; - dst[2] = m[2]*v[0] + m[5]*v[stride] + m[8]*v[2*stride]; - - return dst; -} - -static inline -double * -m33_m33_multiply(const double *src1, - const double *src2, - double * restrict dst) -{ - v3s_m33_multiply(src1 + 0, 1, src2, dst+0); - v3s_m33_multiply(src1 + 3, 1, src2, dst+3); - v3s_m33_multiply(src1 + 6, 1, src2, dst+6); - - return dst; -} - -static inline -double * -m33t_m33_multiply(const double *src1, - const double *src2, - double * restrict dst) -{ - v3s_m33_multiply(src1 + 0, 3, src2, dst+0); - v3s_m33_multiply(src1 + 1, 3, src2, dst+3); - v3s_m33_multiply(src1 + 2, 3, src2, dst+6); - - return dst; -} - -static inline -double * -m33_m33t_multiply(const double *src1, - const double *src2, - double * restrict dst) -{ - return m33_inplace_transpose(m33t_m33_multiply(src2, src1, dst)); -} - -static inline -double * -m33t_m33t_multiply(const double *src1, - const double *src2, - double * restrict dst) -{ - return m33_inplace_transpose(m33_m33_multiply(src2, src1, dst)); -} - -#endif - -#if USE_C99_CODE -static inline -void anglesToGvec_single(double *v3_ang, double *m33_e, - double chi, double *m33_c, - double * restrict v3_c) -{ - double v3_g[3], v3_tmp1[3], v3_tmp2[3], m33_s[9], m33_ctst[9]; - - /* build g */ - double cx = cos(0.5*v3_ang[0]); - double sx = sin(0.5*v3_ang[0]); - double cy = cos(v3_ang[1]); - double sy = sin(v3_ang[1]); - v3_g[0] = cx*cy; - v3_g[1] = cx*sy; - v3_g[2] = sx; - - /* build S */ - makeOscillRotMat_cfunc(chi, v3_ang[2], m33_s); - - /* beam frame to lab frame */ - /* eval the chain: - C.T _dot_ S.T _dot_ E _dot_ g - */ - m33_v3s_multiply (m33_e, v3_g, 1, v3_tmp1); /* E _dot_ g */ - m33t_v3s_multiply(m33_s, v3_tmp1, 1, v3_tmp2); /* S.T _dot_ E _dot_ g */ - m33t_v3s_multiply(m33_c, v3_tmp2, 1, v3_c); /* the whole lot */ -} - -void anglesToGvec_cfunc(long int nvecs, double * angs, - double * bHat_l, double * eHat_l, - double chi, double * rMat_c, - double * gVec_c) -{ - double m33_e[9]; - - makeEtaFrameRotMat_cfunc(bHat_l, eHat_l, m33_e); - for (int i = 0; i= ztol && bDot <= 1.0-ztol ) { - /* - * If we are here diffraction is possible so increment the number of - * admissable vectors - */ - double brMat[9]; - makeBinaryRotMat_cfunc(gVec_l, brMat); - - double dVec_l[3]; - m33_v3s_multiply(brMat, bHat_l, 1, dVec_l); - double denom = v3_v3s_dot(nVec_l, dVec_l, 1); - - if (denom > ztol) { - double u = num/denom; - double v3_tmp[3]; - - /* v3_tmp = P0_l + u*dVec_l - tVec_d */ - for (int j=0; j<3; j++) - v3_tmp[j] = P0_l[j] + u*dVec_l[j] - tVec_d[j]; - - result[0] = v3_v3s_dot(v3_tmp, rMat_d + 0, 3); - result[1] = v3_v3s_dot(v3_tmp, rMat_d + 1, 3); - - /* result when computation can be finished */ - return; - } - } - - /* default result when computation can't be finished */ - result[0] = NAN; - result[1] = NAN; -} - -/* - * The only difference between this and the non-Array version - * is that rMat_s is an array of matrices of length npts instead - * of a single matrix. - */ -void gvecToDetectorXYArray_cfunc(long int npts, double * gVec_c_array, - double * rMat_d, double * rMat_s_array, double * rMat_c, - double * tVec_d, double * tVec_s, double * tVec_c, - double * beamVec, double * result_array) -{ - /* Normalize the beam vector */ - double bHat_l[3]; - v3_normalize(beamVec, bHat_l); - double nVec_l[3]; - m33_v3s_multiply(rMat_d, Zl, 1, nVec_l); - - for (size_t i = 0; i < npts; i++) { - double *rMat_s = rMat_s_array + 9*i; - double *gVec_c = gVec_c_array + 3*i; - double * restrict result = result_array + 2*i; - /* Initialize the detector normal and frame origins */ - - double P0_l[3]; - m33_v3s_multiply(rMat_s, tVec_c, 1, P0_l); - v3_v3s_inplace_add(P0_l, tVec_s, 1); - - double P3_l_minus_P0_l[3]; - v3_v3s_sub(tVec_d, P0_l, 1, P3_l_minus_P0_l); - double num = v3_v3s_dot(nVec_l, P3_l_minus_P0_l, 1); - - double gHat_c[3]; - v3_normalize(gVec_c, gHat_c); - /* - double rMat_sc[9]; - m33_m33_multiply(rMat_s, rMat_c, rMat_sc); - double gVec_l[3]; - m33_v3s_multiply(rMat_sc, gHat_c, 1, gVec_l); - */ - double tmp_vec[3], gVec_l[3]; - m33_v3s_multiply(rMat_c, gHat_c, 1, tmp_vec); - m33_v3s_multiply(rMat_s, tmp_vec, 1, gVec_l); - - double bDot = -v3_v3s_dot(bHat_l, gVec_l, 1); - double ztol = epsf; - - if (bDot < ztol || bDot > 1.0-ztol) { - result[0] = NAN; result[1] = NAN; - continue; - } - - double brMat[9]; - makeBinaryRotMat_cfunc(gVec_l, brMat); - - double dVec_l[3]; - m33_v3s_multiply(brMat, bHat_l, 1, dVec_l); - double denom = v3_v3s_dot(nVec_l, dVec_l, 1); - if (denom < ztol) { - result[0] = NAN; result[1] = NAN; - continue; - } - - double u = num/denom; - double v3_tmp[3]; - for (int j=0; j < 3; j++) - v3_tmp[j] = u*dVec_l[j] - P3_l_minus_P0_l[j]; - - result[0] = v3_v3s_dot(v3_tmp, rMat_d + 0, 3); - result[1] = v3_v3s_dot(v3_tmp, rMat_d + 1, 3); - } -} - -#else void gvecToDetectorXYOne_cfunc(double * gVec_c, double * rMat_d, double * rMat_sc, double * tVec_d, double * bHat_l, @@ -682,7 +258,6 @@ void gvecToDetectorXYArray_cfunc(long int npts, double * gVec_c, } } -#endif void gvecToDetectorXY_cfunc(long int npts, double * gVec_c, double * rMat_d, double * rMat_s, double * rMat_c, double * tVec_d, double * tVec_s, double * tVec_c, From 472bb6ba55dd00f8910319ac1e8628ae137dde4e Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 13:34:08 -0500 Subject: [PATCH 19/28] Remove all old transforms C code --- hexrd/core/extensions/__init__.py | 1 - hexrd/core/transforms/Makefile | 47 - hexrd/core/transforms/old_xfcapi.py | 548 ---------- hexrd/core/transforms/transforms_CAPI.c | 1191 ---------------------- hexrd/core/transforms/transforms_CAPI.h | 69 -- hexrd/core/transforms/transforms_CFUNC.c | 1096 -------------------- hexrd/core/transforms/transforms_CFUNC.h | 95 -- hexrd/core/transforms/xfcapi.py | 23 - hexrd/powder/wppf/texture.py | 8 +- setup.py | 16 - 10 files changed, 4 insertions(+), 3090 deletions(-) delete mode 100644 hexrd/core/transforms/Makefile delete mode 100644 hexrd/core/transforms/old_xfcapi.py delete mode 100644 hexrd/core/transforms/transforms_CAPI.c delete mode 100644 hexrd/core/transforms/transforms_CAPI.h delete mode 100644 hexrd/core/transforms/transforms_CFUNC.c delete mode 100644 hexrd/core/transforms/transforms_CFUNC.h diff --git a/hexrd/core/extensions/__init__.py b/hexrd/core/extensions/__init__.py index 424934712..4eb891db8 100644 --- a/hexrd/core/extensions/__init__.py +++ b/hexrd/core/extensions/__init__.py @@ -1,3 +1,2 @@ from . import _new_transforms_capi -from . import _transforms_CAPI from . import inverse_distortion diff --git a/hexrd/core/transforms/Makefile b/hexrd/core/transforms/Makefile deleted file mode 100644 index 54d99d813..000000000 --- a/hexrd/core/transforms/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -#============================================================================ -# sources - -SRCS = transforms_CAPI.c transforms_CFUNC.c -OBJS = $(SRCS:.c=.o) - -#============================================================================ -# library name - -LIBBASENAME = _transforms_CAPI - -LIBNAME = $(LIBBASENAME).so - -#============================================================================ -# compiler options - -CC = gcc -CFLAGS = -O3 -fPIC -CDEFINES = - -#============================================================================ -# header location - -PYTHON_INCLUDE_DIR = ${HOME}/opt/include/python2.7 -NUMPY_INCLUDE_DIR = ${HOME}/opt/lib/python2.7/site-packages/numpy/core/include/numpy - -INCPATH = -I$(PYTHON_INCLUDE_DIR) -I$(NUMPY_INCLUDE_DIR) - -#============================================================================ -# targets - -default: lib - -lib: $(LIBNAME) - -$(LIBBASENAME).so: $(OBJS) - $(CC) -shared $(CFLAGS) -flat_namespace -o $(LIBBASENAME).so $(OBJS) -lm - -clean: - $(RM) -rf *.o $(LIBBASENAME).so - -#============================================================================ -# suffix rules - -.SUFFIXES: .c -.c.o: - $(CC) $(CFLAGS) $(CDEFINES) $(INCPATH) -c $< diff --git a/hexrd/core/transforms/old_xfcapi.py b/hexrd/core/transforms/old_xfcapi.py deleted file mode 100644 index 0bbaa6ebf..000000000 --- a/hexrd/core/transforms/old_xfcapi.py +++ /dev/null @@ -1,548 +0,0 @@ -#! /usr/bin/env python -# ============================================================ -# Copyright (c) 2012, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# Written by Joel Bernier and others. -# LLNL-CODE-529294. -# All rights reserved. -# -# This file is part of HEXRD. For details on dowloading the source, -# see the file COPYING. -# -# Please also see the file LICENSE. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License (as published by the Free -# Software Foundation) version 2.1 dated February 1999. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this program (see file LICENSE); if not, write to -# the Free Software Foundation, Inc., 59 Temple Place, Suite 330, -# Boston, MA 02111-1307 USA or visit . -# ============================================================ - -import numpy as np -import sys - -from hexrd.core.extensions import _transforms_CAPI - -# Imports so that others can import from this module -from hexrd.core.rotations import mapAngle -from hexrd.core.matrixutil import columnNorm, rowNorm - -# ###################################################################### -# Module Data -epsf = np.finfo(float).eps # ~2.2e-16 -ten_epsf = 10 * epsf # ~2.2e-15 -sqrt_epsf = np.sqrt(epsf) # ~1.5e-8 - -periodDict = {'degrees': 360.0, 'radians': 2 * np.pi} -angularUnits = 'radians' # module-level angle units - -# basis vectors -I3 = np.eye(3) # (3, 3) identity -Xl = np.ascontiguousarray(I3[:, 0].reshape(3, 1)) # X in the lab frame -Yl = np.ascontiguousarray(I3[:, 1].reshape(3, 1)) # Y in the lab frame -Zl = np.ascontiguousarray(I3[:, 2].reshape(3, 1)) # Z in the lab frame - -# reference stretch -vInv_ref = np.array([[1.0, 1.0, 1.0, 0.0, 0.0, 0.0]], order='C').T - -# reference beam direction and eta=0 ref in LAB FRAME for standard geometry -bVec_ref = -Zl -eta_ref = Xl - -# ###################################################################### -# Funtions - - -def anglesToGVec(angs, bHat_l=bVec_ref, eHat_l=eta_ref, chi=0.0, rMat_c=I3): - """ - from 'eta' frame out to lab (with handy kwargs to go to crystal or sample) - - * setting omega to zero in ang imput and omitting rMat_c leaves - in the lab frame in accordance with beam frame specs. - """ - angs = np.ascontiguousarray(np.atleast_2d(angs)) - bHat_l = np.ascontiguousarray(bHat_l.flatten()) - eHat_l = np.ascontiguousarray(eHat_l.flatten()) - rMat_c = np.ascontiguousarray(rMat_c) - chi = float(chi) - return _transforms_CAPI.anglesToGVec(angs, bHat_l, eHat_l, chi, rMat_c) - - -def anglesToDVec(angs, bHat_l=bVec_ref, eHat_l=eta_ref, chi=0.0, rMat_c=I3): - """ - from 'eta' frame out to lab (with handy kwargs to go to crystal or sample) - - * setting omega to zero in ang imput and omitting rMat_c leaves - in the lab frame in accordance with beam frame specs. - """ - angs = np.ascontiguousarray(np.atleast_2d(angs)) - bHat_l = np.ascontiguousarray(bHat_l.flatten()) - eHat_l = np.ascontiguousarray(eHat_l.flatten()) - rMat_c = np.ascontiguousarray(rMat_c) - chi = float(chi) - return _transforms_CAPI.anglesToDVec(angs, bHat_l, eHat_l, chi, rMat_c) - - -def makeGVector(hkl, bMat): - """ - take a CRYSTAL RELATIVE B matrix onto a list of hkls to output unit - reciprocal lattice vectors (a.k.a. lattice plane normals) - - Required Arguments: - hkls -- (3, n) ndarray of n hstacked reciprocal lattice vector component - triplets - bMat -- (3, 3) ndarray representing the matirix taking reciprocal lattice - vectors to the crystal reference frame - - Output: - gVecs -- (3, n) ndarray of n unit reciprocal lattice vectors - (a.k.a. lattice plane normals) - - To Do: - * might benefit from some assert statements to catch improperly shaped - input. - """ - assert hkl.shape[0] == 3, 'hkl input must be (3, n)' - return unitRowVector(np.dot(bMat, hkl)) - - -def gvecToDetectorXY( - gVec_c, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec_ref -): - """ - Takes a list of unit reciprocal lattice vectors in crystal frame to the - specified detector-relative frame, subject to the conditions: - - 1) the reciprocal lattice vector must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Required Arguments: - gVec_c -- (n, 3) ndarray of n reciprocal lattice vector components - in the CRYSTAL FRAME - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components - to LAB FRAME - rMat_s -- (3, 3) ndarray, the COB taking SAMPLE FRAME components - to LAB FRAME - rMat_c -- (3, 3) ndarray, the COB taking CRYSTAL FRAME components - to SAMPLE FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting - LAB to DETECTOR - tVec_s -- (3, 1) ndarray, the translation vector connecting - LAB to SAMPLE - tVec_c -- (3, 1) ndarray, the translation vector connecting - SAMPLE to CRYSTAL - - Outputs: - (m, 2) ndarray containing the intersections of m <= n diffracted beams - associated with gVecs - """ - rMat_d = np.ascontiguousarray(rMat_d) - rMat_s = np.ascontiguousarray(rMat_s) - rMat_c = np.ascontiguousarray(rMat_c) - gVec_c = np.ascontiguousarray(np.atleast_2d(gVec_c)) - tVec_d = np.ascontiguousarray(tVec_d.flatten()) - tVec_s = np.ascontiguousarray(tVec_s.flatten()) - tVec_c = np.ascontiguousarray(tVec_c.flatten()) - beamVec = np.ascontiguousarray(beamVec.flatten()) - return _transforms_CAPI.gvecToDetectorXY( - gVec_c, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec - ) - - -def gvecToDetectorXYArray( - gVec_c, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec_ref -): - """ - Takes a list of unit reciprocal lattice vectors in crystal frame to the - specified detector-relative frame, subject to the conditions: - - 1) the reciprocal lattice vector must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Required Arguments: - gVec_c -- (n, 3) ndarray of n reciprocal lattice vector components - in the CRYSTAL FRAME - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components - to LAB FRAME - rMat_s -- (n, 3, 3) ndarray of n COB taking SAMPLE FRAME components - to LAB FRAME - rMat_c -- (3, 3) ndarray, the COB taking CRYSTAL FRAME components - to SAMPLE FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting - LAB to DETECTOR in LAB - tVec_s -- (3, 1) ndarray, the translation vector connecting - LAB to SAMPLE in LAB - tVec_c -- (3, 1) ndarray, the translation vector connecting - SAMPLE to CRYSTAL in SAMPLE - - Outputs: - (m, 2) ndarray containing the intersections of m <= n diffracted beams - associated with gVecs - """ - gVec_c = np.ascontiguousarray(gVec_c) - rMat_d = np.ascontiguousarray(rMat_d) - rMat_s = np.ascontiguousarray(rMat_s) - rMat_c = np.ascontiguousarray(rMat_c) - tVec_d = np.ascontiguousarray(tVec_d.flatten()) - tVec_s = np.ascontiguousarray(tVec_s.flatten()) - tVec_c = np.ascontiguousarray(tVec_c.flatten()) - beamVec = np.ascontiguousarray(beamVec.flatten()) - return _transforms_CAPI.gvecToDetectorXYArray( - gVec_c, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec - ) - - -def detectorXYToGvec( - xy_det, - rMat_d, - rMat_s, - tVec_d, - tVec_s, - tVec_c, - beamVec=bVec_ref, - etaVec=eta_ref, -): - """ - Takes a list cartesian (x, y) pairs in the detector coordinates and - calculates the associated reciprocal lattice (G) vectors and - (bragg angle, azimuth) pairs with respect to the specified beam and - azimth (eta) reference directions - - Required Arguments: - xy_det -- (n, 2) ndarray or list-like input of n detector (x, y) points - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components - to LAB FRAME - rMat_s -- (3, 3) ndarray, the COB taking SAMPLE FRAME components - to LAB FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting - LAB to DETECTOR in LAB - tVec_s -- (3, 1) ndarray, the translation vector connecting - LAB to SAMPLE in LAB - tVec_c -- (3, 1) ndarray, the translation vector connecting - SAMPLE to CRYSTAL in SAMPLE - - Optional Keyword Arguments: - beamVec -- (3, 1) mdarray containing the incident beam direction - components in the LAB FRAME - etaVec -- (3, 1) mdarray containing the reference azimuth direction - components in the LAB FRAME - - Outputs: - (n, 2) ndarray containing the (tTh, eta) pairs associated with each (x, y) - (n, 3) ndarray containing the associated G vector directions - in the LAB FRAME associated with gVecs - """ - xy_det = np.ascontiguousarray(np.atleast_2d(xy_det)) - rMat_d = np.ascontiguousarray(rMat_d) - rMat_s = np.ascontiguousarray(rMat_s) - tVec_d = np.ascontiguousarray(tVec_d.flatten()) - tVec_s = np.ascontiguousarray(tVec_s.flatten()) - tVec_c = np.ascontiguousarray(tVec_c.flatten()) - beamVec = np.ascontiguousarray(beamVec.flatten()) - etaVec = np.ascontiguousarray(etaVec.flatten()) - return _transforms_CAPI.detectorXYToGvec( - xy_det, rMat_d, rMat_s, tVec_d, tVec_s, tVec_c, beamVec, etaVec - ) - - -def detectorXYToGvecArray( - xy_det, - rMat_d, - rMat_s, - tVec_d, - tVec_s, - tVec_c, - beamVec=bVec_ref, - etaVec=eta_ref, -): - """ - Takes a list cartesian (x, y) pairs in the detector coordinates and - calculates the associated reciprocal lattice (G) vectors and - (bragg angle, azimuth) pairs with respect to the specified beam and azimth - (eta) reference directions - - Required Arguments: - xy_det -- (n, 2) ndarray or list-like input of n detector (x, y) points - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components - to LAB FRAME - rMat_s -- (n, 3, 3) ndarray, the COB taking SAMPLE FRAME components - to LAB FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting - LAB to DETECTOR in LAB - tVec_s -- (3, 1) ndarray, the translation vector connecting - LAB to SAMPLE in LAB - tVec_c -- (3, 1) ndarray, the translation vector connecting - SAMPLE to CRYSTAL in SAMPLE - - Optional Keyword Arguments: - beamVec -- (3, 1) mdarray containing the incident beam direction - components in the LAB FRAME - etaVec -- (3, 1) mdarray containing the reference azimuth direction - components in the LAB FRAME - - Outputs: - (n, 2) ndarray containing the (tTh, eta) pairs associated with each (x, y) - (n, 3) ndarray containing the associated G vector directions - in the LAB FRAME associated with gVecs - """ - xy_det = np.ascontiguousarray(np.atleast_2d(xy_det)) - rMat_d = np.ascontiguousarray(rMat_d) - rMat_s = np.ascontiguousarray(rMat_s) - tVec_d = np.ascontiguousarray(tVec_d.flatten()) - tVec_s = np.ascontiguousarray(tVec_s.flatten()) - tVec_c = np.ascontiguousarray(tVec_c.flatten()) - beamVec = np.ascontiguousarray(beamVec.flatten()) - etaVec = np.ascontiguousarray(etaVec.flatten()) - return _transforms_CAPI.detectorXYToGvec( - xy_det, rMat_d, rMat_s, tVec_d, tVec_s, tVec_c, beamVec, etaVec - ) - - -def oscillAnglesOfHKLs( - hkls, - chi, - rMat_c, - bMat, - wavelength, - vInv=None, - beamVec=bVec_ref, - etaVec=eta_ref, -): - """ - Takes a list of unit reciprocal lattice vectors in crystal frame to the - specified detector-relative frame, subject to the conditions: - - 1) the reciprocal lattice vector must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Required Arguments: - hkls -- (n, 3) ndarray of n reciprocal lattice vectors - in the CRYSTAL FRAME - chi -- float representing the inclination angle of the - oscillation axis (std coords) - rMat_c -- (3, 3) ndarray, the COB taking CRYSTAL FRAME components - to SAMPLE FRAME - bMat -- (3, 3) ndarray, the COB taking RECIPROCAL LATTICE components - to CRYSTAL FRAME - wavelength -- float representing the x-ray wavelength in Angstroms - - Optional Keyword Arguments: - beamVec -- (3, 1) mdarray containing the incident beam direction - components in the LAB FRAME - etaVec -- (3, 1) mdarray containing the reference azimuth direction - components in the LAB FRAME - - Outputs: - ome0 -- (n, 3) ndarray containing the feasible (tTh, eta, ome) triplets - for each input hkl (first solution) - ome1 -- (n, 3) ndarray containing the feasible (tTh, eta, ome) triplets - for each input hkl (second solution) - - Notes: - ------------------------------------------------------------------------ - The reciprocal lattice vector, G, will satisfy the the Bragg condition - when: - - b.T * G / ||G|| = -sin(theta) - - where b is the incident beam direction (k_i) and theta is the Bragg - angle consistent with G and the specified wavelength. The components of - G in the lab frame in this case are obtained using the crystal - orientation, Rc, and the single-parameter oscillation matrix, Rs(ome): - - Rs(ome) * Rc * G / ||G|| - - The equation above can be rearranged to yield an expression of the form: - - a*sin(ome) + b*cos(ome) = c - - which is solved using the relation: - - a*sin(x) + b*cos(x) = sqrt(a**2 + b**2) * sin(x + alpha) - - --> sin(x + alpha) = c / sqrt(a**2 + b**2) - - where: - - alpha = atan2(b, a) - - The solutions are: - - / - | arcsin(c / sqrt(a**2 + b**2)) - alpha - x = < - | pi - arcsin(c / sqrt(a**2 + b**2)) - alpha - \ - - There is a double root in the case the reflection is tangent to the - Debye-Scherrer cone (c**2 = a**2 + b**2), and no solution if the - Laue condition cannot be satisfied (filled with NaNs in the results - array here) - """ - hkls = np.array(hkls, dtype=float, order='C') - if vInv is None: - vInv = np.ascontiguousarray(vInv_ref.flatten()) - else: - vInv = np.ascontiguousarray(vInv.flatten()) - beamVec = np.ascontiguousarray(beamVec.flatten()) - etaVec = np.ascontiguousarray(etaVec.flatten()) - bMat = np.ascontiguousarray(bMat) - return _transforms_CAPI.oscillAnglesOfHKLs( - hkls, chi, rMat_c, bMat, wavelength, vInv, beamVec, etaVec - ) - - -""" -####################################################################### -###### Utility Functions ###### -####################################################################### - -""" - - -def arccosSafe(temp): - """ - Protect against numbers slightly larger than 1 due to round-off - """ - - # Oh, the tricks we must play to make this overloaded and robust... - if type(temp) is list: - temp = np.asarray(temp) - elif type(temp) is np.ndarray: - if len(temp.shape) == 0: - temp = temp.reshape(1) - - if (temp > 1.00001).any() or (temp < -1.00001).any(): - raise RuntimeError(f"Failed to take arccos of {temp}") - - gte1 = temp >= 1.0 - lte1 = temp <= -1.0 - - temp[gte1] = 1 - temp[lte1] = -1 - - return np.arccos(temp) - - -def angularDifference(angList0, angList1, units=angularUnits): - """ - Do the proper (acute) angular difference in the context of a branch cut. - - *) Default angular range is [-pi, pi] - """ - period = periodDict[units] - # take difference as arrays - diffAngles = np.atleast_1d(angList0) - np.atleast_1d(angList1) - - return abs(np.remainder(diffAngles + 0.5 * period, period) - 0.5 * period) - - -def unitRowVector(vecIn): - vecIn = np.ascontiguousarray(vecIn) - if vecIn.ndim == 1: - return _transforms_CAPI.unitRowVector(vecIn) - elif vecIn.ndim == 2: - return _transforms_CAPI.unitRowVectors(vecIn) - else: - assert vecIn.ndim in [ - 1, - 2, - ], "arg shape must be 1-d or 2-d, yours is %d-d" % (vecIn.ndim) - - -def makeDetectorRotMat(tiltAngles): - """ - Form the (3, 3) tilt rotations from the tilt angle list: - - tiltAngles = [gamma_Xl, gamma_Yl, gamma_Zl] in radians - """ - arg = np.ascontiguousarray(np.r_[tiltAngles].flatten()) - return _transforms_CAPI.makeDetectorRotMat(arg) - - -def makeOscillRotMat(oscillAngles): - """ - oscillAngles = [chi, ome] - """ - arg = np.ascontiguousarray(np.r_[oscillAngles].flatten()) - return _transforms_CAPI.makeOscillRotMat(arg) - - -def makeOscillRotMatArray(chi, omeArray): - """ - Applies makeOscillAngles multiple times, for one - chi value and an array of omega values. - """ - arg = np.ascontiguousarray(omeArray) - return _transforms_CAPI.makeOscillRotMatArray(chi, arg) - - -def makeRotMatOfExpMap(expMap): - """ - make a rotation matrix from an exponential map - """ - arg = np.ascontiguousarray(expMap.flatten()) - return _transforms_CAPI.makeRotMatOfExpMap(arg) - - -def makeRotMatOfQuat(quats): - """ - make rotation matrix from vstacked unit quaternions - - """ - arg = np.ascontiguousarray(quats) - return _transforms_CAPI.makeRotMatOfQuat(arg) - - -def makeBinaryRotMat(axis): - arg = np.ascontiguousarray(axis.flatten()) - return _transforms_CAPI.makeBinaryRotMat(arg) - - -def makeEtaFrameRotMat(bHat_l, eHat_l): - arg1 = np.ascontiguousarray(bHat_l.flatten()) - arg2 = np.ascontiguousarray(eHat_l.flatten()) - return _transforms_CAPI.makeEtaFrameRotMat(arg1, arg2) - - -def validateAngleRanges(angList, angMin, angMax, ccw=True): - # FIXME: broken - angList = np.asarray(angList, dtype=np.double, order="C") - angMin = np.asarray(angMin, dtype=np.double, order="C") - angMax = np.asarray(angMax, dtype=np.double, order="C") - return _transforms_CAPI.validateAngleRanges(angList, angMin, angMax, ccw) - - -def rotate_vecs_about_axis(angle, axis, vecs): - return _transforms_CAPI.rotate_vecs_about_axis(angle, axis, vecs) - - -def quat_distance(q1, q2, qsym): - """ - qsym coming from hexrd.crystallogray.PlaneData.getQSym() is C-contiguous - """ - q1 = np.ascontiguousarray(q1.flatten()) - q2 = np.ascontiguousarray(q2.flatten()) - return _transforms_CAPI.quat_distance(q1, q2, qsym) - - -def homochoricOfQuat(quats): - """ - Compute homochoric parameters of unit quaternions - - quats is (4, n) - """ - q = np.ascontiguousarray(quats.T) - return _transforms_CAPI.homochoricOfQuat(q) - - -# def rotateVecsAboutAxis(angle, axis, vecs): -# return _transforms_CAPI.rotateVecsAboutAxis(angle, axis, vecs) diff --git a/hexrd/core/transforms/transforms_CAPI.c b/hexrd/core/transforms/transforms_CAPI.c deleted file mode 100644 index b2d518510..000000000 --- a/hexrd/core/transforms/transforms_CAPI.c +++ /dev/null @@ -1,1191 +0,0 @@ -/* - * gcc -c -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/numpy/core/include/numpy transforms_CAPI.c - * - * gcc -bundle -flat_namespace -undefined suppress -o _transforms_CAPI.so transforms_CAPI.o - */ - -#include "transforms_CAPI.h" -#include "transforms_CFUNC.h" - -static PyMethodDef _transform_methods[] = { - {"anglesToGVec",anglesToGVec,METH_VARARGS,"take angle tuples to G-vectors"}, - {"anglesToDVec",anglesToDVec,METH_VARARGS,"take angle tuples to unit diffraction vectors"}, - {"makeGVector",makeGVector,METH_VARARGS,"Make G-vectors from hkls and B-matrix"}, - {"gvecToDetectorXY",gvecToDetectorXY,METH_VARARGS,""}, - {"gvecToDetectorXYArray",gvecToDetectorXYArray,METH_VARARGS,""}, - {"detectorXYToGvec",detectorXYToGvec,METH_VARARGS,"take cartesian coordinates to G-vectors"}, - {"detectorXYToGvecArray",detectorXYToGvecArray,METH_VARARGS,"take cartesian coordinates to G-vectors"}, - {"oscillAnglesOfHKLs",oscillAnglesOfHKLs,METH_VARARGS,"solve angle specs for G-vectors"}, - {"unitRowVector",unitRowVector,METH_VARARGS,"Normalize a single row vector"}, - {"unitRowVectors",unitRowVectors,METH_VARARGS,"Normalize a collection of row vectors"}, - {"makeDetectorRotMat",makeDetectorRotMat,METH_VARARGS,""}, - {"makeOscillRotMat",makeOscillRotMat,METH_VARARGS,""}, - {"makeOscillRotMatArray",makeOscillRotMatArray,METH_VARARGS,""}, - {"makeRotMatOfExpMap",makeRotMatOfExpMap,METH_VARARGS,""}, - {"makeRotMatOfQuat",makeRotMatOfQuat,METH_VARARGS,""}, - {"makeBinaryRotMat",makeBinaryRotMat,METH_VARARGS,""}, - {"makeEtaFrameRotMat",makeEtaFrameRotMat,METH_VARARGS,"Make eta basis COB matrix"}, - {"validateAngleRanges",validateAngleRanges,METH_VARARGS,""}, - {"rotate_vecs_about_axis",rotate_vecs_about_axis,METH_VARARGS,"Rotate vectors about an axis"}, - {"quat_distance",quat_distance,METH_VARARGS,"Compute distance between two unit quaternions"}, - {"homochoricOfQuat",homochoricOfQuat,METH_VARARGS,"Compute homochoric parameterization of list of unit quaternions"}, - {NULL,NULL} -}; - -static struct PyModuleDef transforms_capi_pymodule = -{ - PyModuleDef_HEAD_INIT, - "_transforms_CAPI", /* name of module */ - "", /* module documentation, may be NULL */ - -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ - _transform_methods -}; - -PyMODINIT_FUNC PyInit__transforms_CAPI(void) -{ - import_array(); - return PyModule_Create(&transforms_capi_pymodule); -} - -/******************************************************************************/ -/* Funtions */ - -static PyObject * anglesToGVec(PyObject * self, PyObject * args) -{ - PyArrayObject *angs, *bHat_l, *eHat_l, *rMat_c; - PyArrayObject *gVec_c; - double chi; - npy_intp nvecs, rdims[2]; - - int nangs, nbhat, nehat, nrmat; - int da1, db1, de1, dr1, dr2; - - double *angs_ptr, *bHat_l_ptr, *eHat_l_ptr, *rMat_c_ptr; - double *gVec_c_ptr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOdO", - &angs, - &bHat_l, &eHat_l, - &chi, &rMat_c)) return(NULL); - if ( angs == NULL ) return(NULL); - - /* Verify shape of input arrays */ - nangs = PyArray_NDIM(angs); - nbhat = PyArray_NDIM(bHat_l); - nehat = PyArray_NDIM(eHat_l); - nrmat = PyArray_NDIM(rMat_c); - - assert( nangs==2 && nbhat==1 && nehat==1 && nrmat==2 ); - - /* Verify dimensions of input arrays */ - nvecs = PyArray_DIMS(angs)[0]; //rows - da1 = PyArray_DIMS(angs)[1]; //cols - - db1 = PyArray_DIMS(bHat_l)[0]; - de1 = PyArray_DIMS(eHat_l)[0]; - dr1 = PyArray_DIMS(rMat_c)[0]; - dr2 = PyArray_DIMS(rMat_c)[1]; - - assert( da1 == 3 ); - assert( db1 == 3 && de1 == 3); - assert( dr1 == 3 && dr2 == 3); - - /* Allocate C-style array for return data */ - rdims[0] = nvecs; rdims[1] = 3; - gVec_c = (PyArrayObject*)PyArray_EMPTY(2,rdims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - angs_ptr = (double*)PyArray_DATA(angs); - bHat_l_ptr = (double*)PyArray_DATA(bHat_l); - eHat_l_ptr = (double*)PyArray_DATA(eHat_l); - rMat_c_ptr = (double*)PyArray_DATA(rMat_c); - gVec_c_ptr = (double*)PyArray_DATA(gVec_c); - - /* Call the actual function */ - anglesToGvec_cfunc(nvecs, angs_ptr, - bHat_l_ptr, eHat_l_ptr, - chi, rMat_c_ptr, - gVec_c_ptr); - - /* Build and return the nested data structure */ - return((PyObject*)gVec_c); -} - -static PyObject * anglesToDVec(PyObject * self, PyObject * args) -{ - PyArrayObject *angs, *bHat_l, *eHat_l, *rMat_c; - PyArrayObject *dVec_c; - double chi; - npy_intp nvecs, rdims[2]; - - int nangs, nbhat, nehat, nrmat; - int da1, db1, de1, dr1, dr2; - - double *angs_ptr, *bHat_l_ptr, *eHat_l_ptr, *rMat_c_ptr; - double *dVec_c_ptr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOdO", - &angs, - &bHat_l, &eHat_l, - &chi, &rMat_c)) return(NULL); - if ( angs == NULL ) return(NULL); - - /* Verify shape of input arrays */ - nangs = PyArray_NDIM(angs); - nbhat = PyArray_NDIM(bHat_l); - nehat = PyArray_NDIM(eHat_l); - nrmat = PyArray_NDIM(rMat_c); - - assert( nangs==2 && nbhat==1 && nehat==1 && nrmat==2 ); - - /* Verify dimensions of input arrays */ - nvecs = PyArray_DIMS(angs)[0]; //rows - da1 = PyArray_DIMS(angs)[1]; //cols - - db1 = PyArray_DIMS(bHat_l)[0]; - de1 = PyArray_DIMS(eHat_l)[0]; - dr1 = PyArray_DIMS(rMat_c)[0]; - dr2 = PyArray_DIMS(rMat_c)[1]; - - assert( da1 == 3 ); - assert( db1 == 3 && de1 == 3); - assert( dr1 == 3 && dr2 == 3); - - /* Allocate C-style array for return data */ - rdims[0] = nvecs; rdims[1] = 3; - dVec_c = (PyArrayObject*)PyArray_EMPTY(2,rdims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - angs_ptr = (double*)PyArray_DATA(angs); - bHat_l_ptr = (double*)PyArray_DATA(bHat_l); - eHat_l_ptr = (double*)PyArray_DATA(eHat_l); - rMat_c_ptr = (double*)PyArray_DATA(rMat_c); - dVec_c_ptr = (double*)PyArray_DATA(dVec_c); - - /* Call the actual function */ - anglesToDvec_cfunc(nvecs, angs_ptr, - bHat_l_ptr, eHat_l_ptr, - chi, rMat_c_ptr, - dVec_c_ptr); - - /* Build and return the nested data structure */ - return((PyObject*)dVec_c); -} - -static PyObject * makeGVector(PyObject * self, PyObject * args) -{ - return(NULL); -} - -/* - Takes a list of unit reciprocal lattice vectors in crystal frame to the - specified detector-relative frame, subject to the conditions: - - 1) the reciprocal lattice vector must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Required Arguments: - gVec_c -- (n, 3) ndarray of n reciprocal lattice vectors in the CRYSTAL FRAME - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components to LAB FRAME - rMat_s -- (3, 3) ndarray, the COB taking SAMPLE FRAME components to LAB FRAME - rMat_c -- (3, 3) ndarray, the COB taking CRYSTAL FRAME components to SAMPLE FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting LAB to DETECTOR - tVec_s -- (3, 1) ndarray, the translation vector connecting LAB to SAMPLE - tVec_c -- (3, 1) ndarray, the translation vector connecting SAMPLE to CRYSTAL - - Outputs: - (m, 2) ndarray containing the intersections of m <= n diffracted beams - associated with gVecs -*/ -static PyObject * gvecToDetectorXY(PyObject * self, PyObject * args) -{ - PyArrayObject *gVec_c, - *rMat_d, *rMat_s, *rMat_c, - *tVec_d, *tVec_s, *tVec_c, - *beamVec; - PyArrayObject *result; - - int dgc, drd, drs, drc, dtd, dts, dtc, dbv; - npy_intp npts, dims[2]; - - double *gVec_c_Ptr, - *rMat_d_Ptr, *rMat_s_Ptr, *rMat_c_Ptr, - *tVec_d_Ptr, *tVec_s_Ptr, *tVec_c_Ptr, - *beamVec_Ptr; - double *result_Ptr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOOOOOO", - &gVec_c, - &rMat_d, &rMat_s, &rMat_c, - &tVec_d, &tVec_s, &tVec_c, - &beamVec)) return(NULL); - if ( gVec_c == NULL || - rMat_d == NULL || rMat_s == NULL || rMat_c == NULL || - tVec_d == NULL || tVec_s == NULL || tVec_c == NULL || - beamVec == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dgc = PyArray_NDIM(gVec_c); - drd = PyArray_NDIM(rMat_d); - drs = PyArray_NDIM(rMat_s); - drc = PyArray_NDIM(rMat_c); - dtd = PyArray_NDIM(tVec_d); - dts = PyArray_NDIM(tVec_s); - dtc = PyArray_NDIM(tVec_c); - dbv = PyArray_NDIM(beamVec); - assert( dgc == 2 ); - assert( drd == 2 && drs == 2 && drc == 2 ); - assert( dtd == 1 && dts == 1 && dtc == 1 ); - assert( dbv == 1 ); - - /* Verify dimensions of input arrays */ - npts = PyArray_DIMS(gVec_c)[0]; - - assert( PyArray_DIMS(gVec_c)[1] == 3 ); - assert( PyArray_DIMS(rMat_d)[0] == 3 && PyArray_DIMS(rMat_d)[1] == 3 ); - assert( PyArray_DIMS(rMat_s)[0] == 3 && PyArray_DIMS(rMat_s)[1] == 3 ); - assert( PyArray_DIMS(rMat_c)[0] == 3 && PyArray_DIMS(rMat_c)[1] == 3 ); - assert( PyArray_DIMS(tVec_d)[0] == 3 ); - assert( PyArray_DIMS(tVec_s)[0] == 3 ); - assert( PyArray_DIMS(tVec_c)[0] == 3 ); - assert( PyArray_DIMS(beamVec)[0] == 3 ); - - /* Allocate C-style array for return data */ - dims[0] = npts; dims[1] = 2; - result = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab data pointers into various arrays */ - gVec_c_Ptr = (double*)PyArray_DATA(gVec_c); - - rMat_d_Ptr = (double*)PyArray_DATA(rMat_d); - rMat_s_Ptr = (double*)PyArray_DATA(rMat_s); - rMat_c_Ptr = (double*)PyArray_DATA(rMat_c); - - tVec_d_Ptr = (double*)PyArray_DATA(tVec_d); - tVec_s_Ptr = (double*)PyArray_DATA(tVec_s); - tVec_c_Ptr = (double*)PyArray_DATA(tVec_c); - - beamVec_Ptr = (double*)PyArray_DATA(beamVec); - - result_Ptr = (double*)PyArray_DATA(result); - - /* Call the computational routine */ - gvecToDetectorXY_cfunc(npts, gVec_c_Ptr, - rMat_d_Ptr, rMat_s_Ptr, rMat_c_Ptr, - tVec_d_Ptr, tVec_s_Ptr, tVec_c_Ptr, - beamVec_Ptr, - result_Ptr); - - /* Build and return the nested data structure */ - return((PyObject*)result); -} - -/* - Takes a list of unit reciprocal lattice vectors in crystal frame to the - specified detector-relative frame, subject to the conditions: - - 1) the reciprocal lattice vector must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Required Arguments: - gVec_c -- (n, 3) ndarray of n reciprocal lattice vectors in the CRYSTAL FRAME - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components to LAB FRAME - rMat_s -- (n, 3, 3) ndarray, the COB taking SAMPLE FRAME components to LAB FRAME - rMat_c -- (3, 3) ndarray, the COB taking CRYSTAL FRAME components to SAMPLE FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting LAB to DETECTOR - tVec_s -- (3, 1) ndarray, the translation vector connecting LAB to SAMPLE - tVec_c -- (3, 1) ndarray, the translation vector connecting SAMPLE to CRYSTAL - - Outputs: - (m, 2) ndarray containing the intersections of m <= n diffracted beams - associated with gVecs -*/ -static PyObject * gvecToDetectorXYArray(PyObject * self, PyObject * args) -{ - PyArrayObject *gVec_c, - *rMat_d, *rMat_s, *rMat_c, - *tVec_d, *tVec_s, *tVec_c, - *beamVec; - PyArrayObject *result; - - int dgc, drd, drs, drc, dtd, dts, dtc, dbv; - npy_intp npts, dims[2]; - - double *gVec_c_Ptr, - *rMat_d_Ptr, *rMat_s_Ptr, *rMat_c_Ptr, - *tVec_d_Ptr, *tVec_s_Ptr, *tVec_c_Ptr, - *beamVec_Ptr; - double *result_Ptr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOOOOOO", - &gVec_c, - &rMat_d, &rMat_s, &rMat_c, - &tVec_d, &tVec_s, &tVec_c, - &beamVec)) return(NULL); - if ( gVec_c == NULL || - rMat_d == NULL || rMat_s == NULL || rMat_c == NULL || - tVec_d == NULL || tVec_s == NULL || tVec_c == NULL || - beamVec == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dgc = PyArray_NDIM(gVec_c); - drd = PyArray_NDIM(rMat_d); - drs = PyArray_NDIM(rMat_s); - drc = PyArray_NDIM(rMat_c); - dtd = PyArray_NDIM(tVec_d); - dts = PyArray_NDIM(tVec_s); - dtc = PyArray_NDIM(tVec_c); - dbv = PyArray_NDIM(beamVec); - assert( dgc == 2 ); - assert( drd == 2 && drs == 3 && drc == 2 ); - assert( dtd == 1 && dts == 1 && dtc == 1 ); - assert( dbv == 1 ); - - /* Verify dimensions of input arrays */ - npts = PyArray_DIMS(gVec_c)[0]; - - if (npts != PyArray_DIM(rMat_s, 0)) { - PyErr_Format(PyExc_ValueError, "gVec_c and rMat_s length mismatch %d vs %d", - (int)PyArray_DIM(gVec_c, 0), (int)PyArray_DIM(rMat_s, 0)); - return NULL; - } - assert( PyArray_DIMS(gVec_c)[1] == 3 ); - assert( PyArray_DIMS(rMat_d)[0] == 3 && PyArray_DIMS(rMat_d)[1] == 3 ); - assert( PyArray_DIMS(rMat_s)[1] == 3 && PyArray_DIMS(rMat_s)[2] == 3 ); - assert( PyArray_DIMS(rMat_c)[0] == 3 && PyArray_DIMS(rMat_c)[1] == 3 ); - assert( PyArray_DIMS(tVec_d)[0] == 3 ); - assert( PyArray_DIMS(tVec_s)[0] == 3 ); - assert( PyArray_DIMS(tVec_c)[0] == 3 ); - assert( PyArray_DIMS(beamVec)[0] == 3 ); - - /* Allocate C-style array for return data */ - dims[0] = npts; dims[1] = 2; - result = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab data pointers into various arrays */ - gVec_c_Ptr = (double*)PyArray_DATA(gVec_c); - - rMat_d_Ptr = (double*)PyArray_DATA(rMat_d); - rMat_s_Ptr = (double*)PyArray_DATA(rMat_s); - rMat_c_Ptr = (double*)PyArray_DATA(rMat_c); - - tVec_d_Ptr = (double*)PyArray_DATA(tVec_d); - tVec_s_Ptr = (double*)PyArray_DATA(tVec_s); - tVec_c_Ptr = (double*)PyArray_DATA(tVec_c); - - beamVec_Ptr = (double*)PyArray_DATA(beamVec); - - result_Ptr = (double*)PyArray_DATA(result); - - /* Call the computational routine */ - gvecToDetectorXYArray_cfunc(npts, gVec_c_Ptr, - rMat_d_Ptr, rMat_s_Ptr, rMat_c_Ptr, - tVec_d_Ptr, tVec_s_Ptr, tVec_c_Ptr, - beamVec_Ptr, - result_Ptr); - - /* Build and return the nested data structure */ - return((PyObject*)result); -} - -/* - Takes a list cartesian (x, y) pairs in the detector coordinates and calculates - the associated reciprocal lattice (G) vectors and (bragg angle, azimuth) pairs - with respect to the specified beam and azimth (eta) reference directions - - Required Arguments: - xy_det -- (n, 2) ndarray or list-like input of n detector (x, y) points - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components to LAB FRAME - rMat_s -- (3, 3) ndarray, the COB taking SAMPLE FRAME components to LAB FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting LAB to DETECTOR - tVec_s -- (3, 1) ndarray, the translation vector connecting LAB to SAMPLE - tVec_c -- (3, 1) ndarray, the translation vector connecting SAMPLE to CRYSTAL - - Optional Keyword Arguments: - beamVec -- (1, 3) mdarray containing the incident beam direction components in the LAB FRAME - etaVec -- (1, 3) mdarray containing the reference azimuth direction components in the LAB FRAME - - Outputs: - (n, 2) ndarray containing the (tTh, eta) pairs associated with each (x, y) - (n, 3) ndarray containing the associated G vector directions in the LAB FRAME - associated with gVecs -*/ -static PyObject * detectorXYToGvec(PyObject * self, PyObject * args) -{ - PyArrayObject *xy_det, *rMat_d, *rMat_s, - *tVec_d, *tVec_s, *tVec_c, - *beamVec, *etaVec; - PyArrayObject *tTh, *eta, *gVec_l; - PyObject *inner_tuple, *outer_tuple; - - int dxy, drd, drs, dtd, dts, dtc, dbv, dev; - npy_intp npts, dims[2]; - - double *xy_Ptr, *rMat_d_Ptr, *rMat_s_Ptr, - *tVec_d_Ptr, *tVec_s_Ptr, *tVec_c_Ptr, - *beamVec_Ptr, *etaVec_Ptr; - double *tTh_Ptr, *eta_Ptr, *gVec_l_Ptr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOOOOOO", - &xy_det, - &rMat_d, &rMat_s, - &tVec_d, &tVec_s, &tVec_c, - &beamVec, &etaVec)) return(NULL); - if ( xy_det == NULL || rMat_d == NULL || rMat_s == NULL || - tVec_d == NULL || tVec_s == NULL || tVec_c == NULL || - beamVec == NULL || etaVec == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dxy = PyArray_NDIM(xy_det); - drd = PyArray_NDIM(rMat_d); - drs = PyArray_NDIM(rMat_s); - dtd = PyArray_NDIM(tVec_d); - dts = PyArray_NDIM(tVec_s); - dtc = PyArray_NDIM(tVec_c); - dbv = PyArray_NDIM(beamVec); - dev = PyArray_NDIM(etaVec); - assert( dxy == 2 && drd == 2 && drs == 2 && - dtd == 1 && dts == 1 && dtc == 1 && - dbv == 1 && dev == 1); - - /* Verify dimensions of input arrays */ - npts = PyArray_DIMS(xy_det)[0]; - - assert( PyArray_DIMS(xy_det)[1] == 2 ); - assert( PyArray_DIMS(rMat_d)[0] == 3 && PyArray_DIMS(rMat_d)[1] == 3 ); - assert( PyArray_DIMS(rMat_s)[0] == 3 && PyArray_DIMS(rMat_s)[1] == 3 ); - assert( PyArray_DIMS(tVec_d)[0] == 3 ); - assert( PyArray_DIMS(tVec_s)[0] == 3 ); - assert( PyArray_DIMS(tVec_c)[0] == 3 ); - assert( PyArray_DIMS(beamVec)[0] == 3 ); - assert( PyArray_DIMS(etaVec)[0] == 3 ); - - /* Allocate arrays for return values */ - dims[0] = npts; dims[1] = 3; - gVec_l = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - tTh = (PyArrayObject*)PyArray_EMPTY(1,&npts,NPY_DOUBLE,0); - eta = (PyArrayObject*)PyArray_EMPTY(1,&npts,NPY_DOUBLE,0); - - /* Grab data pointers into various arrays */ - xy_Ptr = (double*)PyArray_DATA(xy_det); - gVec_l_Ptr = (double*)PyArray_DATA(gVec_l); - - tTh_Ptr = (double*)PyArray_DATA(tTh); - eta_Ptr = (double*)PyArray_DATA(eta); - - rMat_d_Ptr = (double*)PyArray_DATA(rMat_d); - rMat_s_Ptr = (double*)PyArray_DATA(rMat_s); - - tVec_d_Ptr = (double*)PyArray_DATA(tVec_d); - tVec_s_Ptr = (double*)PyArray_DATA(tVec_s); - tVec_c_Ptr = (double*)PyArray_DATA(tVec_c); - - beamVec_Ptr = (double*)PyArray_DATA(beamVec); - etaVec_Ptr = (double*)PyArray_DATA(etaVec); - - /* Call the computational routine */ - detectorXYToGvec_cfunc(npts, xy_Ptr, - rMat_d_Ptr, rMat_s_Ptr, - tVec_d_Ptr, tVec_s_Ptr, tVec_c_Ptr, - beamVec_Ptr, etaVec_Ptr, - tTh_Ptr, eta_Ptr, gVec_l_Ptr); - - /* Build and return the nested data structure */ - /* Note that Py_BuildValue with 'O' increases reference count */ - inner_tuple = Py_BuildValue("OO",tTh,eta); - outer_tuple = Py_BuildValue("OO", inner_tuple, gVec_l); - Py_DECREF(inner_tuple); - Py_DECREF(tTh); - Py_DECREF(eta); - Py_DECREF(gVec_l); - return outer_tuple; -} - -/* - Takes a list cartesian (x, y) pairs in the detector coordinates and calculates - the associated reciprocal lattice (G) vectors and (bragg angle, azimuth) pairs - with respect to the specified beam and azimth (eta) reference directions - - Required Arguments: - xy_det -- (n, 2) ndarray or list-like input of n detector (x, y) points - rMat_d -- (3, 3) ndarray, the COB taking DETECTOR FRAME components to LAB FRAME - rMat_s -- (n, 3, 3) ndarray, the COB taking SAMPLE FRAME components to LAB FRAME - tVec_d -- (3, 1) ndarray, the translation vector connecting LAB to DETECTOR - tVec_s -- (3, 1) ndarray, the translation vector connecting LAB to SAMPLE - tVec_c -- (3, 1) ndarray, the translation vector connecting SAMPLE to CRYSTAL - - Optional Keyword Arguments: - beamVec -- (1, 3) mdarray containing the incident beam direction components in the LAB FRAME - etaVec -- (1, 3) mdarray containing the reference azimuth direction components in the LAB FRAME - - Outputs: - (n, 2) ndarray containing the (tTh, eta) pairs associated with each (x, y) - (n, 3) ndarray containing the associated G vector directions in the LAB FRAME - associated with gVecs -*/ -static PyObject * detectorXYToGvecArray(PyObject * self, PyObject * args) -{ - PyArrayObject *xy_det, *rMat_d, *rMat_s, - *tVec_d, *tVec_s, *tVec_c, - *beamVec, *etaVec; - PyArrayObject *tTh, *eta, *gVec_l; - PyObject *inner_tuple, *outer_tuple; - - int dxy, drd, drs, dtd, dts, dtc, dbv, dev; - npy_intp npts, dims[2]; - - double *xy_Ptr, *rMat_d_Ptr, *rMat_s_Ptr, - *tVec_d_Ptr, *tVec_s_Ptr, *tVec_c_Ptr, - *beamVec_Ptr, *etaVec_Ptr; - double *tTh_Ptr, *eta_Ptr, *gVec_l_Ptr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOOOOOO", - &xy_det, - &rMat_d, &rMat_s, - &tVec_d, &tVec_s, &tVec_c, - &beamVec, &etaVec)) return(NULL); - if ( xy_det == NULL || rMat_d == NULL || rMat_s == NULL || - tVec_d == NULL || tVec_s == NULL || tVec_c == NULL || - beamVec == NULL || etaVec == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dxy = PyArray_NDIM(xy_det); - drd = PyArray_NDIM(rMat_d); - drs = PyArray_NDIM(rMat_s); - dtd = PyArray_NDIM(tVec_d); - dts = PyArray_NDIM(tVec_s); - dtc = PyArray_NDIM(tVec_c); - dbv = PyArray_NDIM(beamVec); - dev = PyArray_NDIM(etaVec); - assert( dxy == 2 && drd == 2 && drs == 2 && - dtd == 1 && dts == 1 && dtc == 1 && - dbv == 1 && dev == 1); - - /* Verify dimensions of input arrays */ - npts = PyArray_DIMS(xy_det)[0]; - if (npts != PyArray_DIM(rMat_s, 0)) { - PyErr_Format(PyExc_ValueError, "xy_det and rMat_s length mismatch %d vs %d", - (int)PyArray_DIM(xy_det, 0), (int)PyArray_DIM(rMat_s, 0)); - return NULL; - } - - assert( PyArray_DIMS(xy_det)[1] == 2 ); - assert( PyArray_DIMS(rMat_d)[0] == 3 && PyArray_DIMS(rMat_d)[1] == 3 ); - assert( PyArray_DIMS(rMat_s)[0] == 3 && PyArray_DIMS(rMat_s)[1] == 3 ); - assert( PyArray_DIMS(tVec_d)[0] == 3 ); - assert( PyArray_DIMS(tVec_s)[0] == 3 ); - assert( PyArray_DIMS(tVec_c)[0] == 3 ); - assert( PyArray_DIMS(beamVec)[0] == 3 ); - assert( PyArray_DIMS(etaVec)[0] == 3 ); - - /* Allocate arrays for return values */ - dims[0] = npts; dims[1] = 3; - gVec_l = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - tTh = (PyArrayObject*)PyArray_EMPTY(1,&npts,NPY_DOUBLE,0); - eta = (PyArrayObject*)PyArray_EMPTY(1,&npts,NPY_DOUBLE,0); - - /* Grab data pointers into various arrays */ - xy_Ptr = (double*)PyArray_DATA(xy_det); - gVec_l_Ptr = (double*)PyArray_DATA(gVec_l); - - tTh_Ptr = (double*)PyArray_DATA(tTh); - eta_Ptr = (double*)PyArray_DATA(eta); - - rMat_d_Ptr = (double*)PyArray_DATA(rMat_d); - rMat_s_Ptr = (double*)PyArray_DATA(rMat_s); - - tVec_d_Ptr = (double*)PyArray_DATA(tVec_d); - tVec_s_Ptr = (double*)PyArray_DATA(tVec_s); - tVec_c_Ptr = (double*)PyArray_DATA(tVec_c); - - beamVec_Ptr = (double*)PyArray_DATA(beamVec); - etaVec_Ptr = (double*)PyArray_DATA(etaVec); - - /* Call the computational routine */ - detectorXYToGvecArray_cfunc(npts, xy_Ptr, - rMat_d_Ptr, rMat_s_Ptr, - tVec_d_Ptr, tVec_s_Ptr, tVec_c_Ptr, - beamVec_Ptr, etaVec_Ptr, - tTh_Ptr, eta_Ptr, gVec_l_Ptr); - - /* Build and return the nested data structure */ - /* Note that Py_BuildValue with 'O' increases reference count */ - inner_tuple = Py_BuildValue("OO",tTh,eta); - outer_tuple = Py_BuildValue("OO", inner_tuple, gVec_l); - Py_DECREF(inner_tuple); - Py_DECREF(tTh); - Py_DECREF(eta); - Py_DECREF(gVec_l); - return outer_tuple; -} - -static PyObject * oscillAnglesOfHKLs(PyObject * self, PyObject * args) -{ - PyArrayObject *hkls, *rMat_c, *bMat, - *vInv_s, *beamVec, *etaVec; - PyFloatObject *chi, *wavelength; - PyArrayObject *oangs0, *oangs1; - PyObject *return_tuple; - - int dhkls, drc, dbm, dvi, dbv, dev; - npy_intp npts, dims[2]; - - double *hkls_Ptr, chi_d, - *rMat_c_Ptr, *bMat_Ptr, wavelen_d, - *vInv_s_Ptr, *beamVec_Ptr, *etaVec_Ptr; - double *oangs0_Ptr, *oangs1_Ptr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOOOOOO", - &hkls, &chi, - &rMat_c, &bMat, &wavelength, - &vInv_s, &beamVec, &etaVec)) return(NULL); - if ( hkls == NULL || chi == NULL || - rMat_c == NULL || bMat == NULL || wavelength == NULL || - vInv_s == NULL || beamVec == NULL || etaVec == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dhkls = PyArray_NDIM(hkls); - drc = PyArray_NDIM(rMat_c); - dbm = PyArray_NDIM(bMat); - dvi = PyArray_NDIM(vInv_s); - dbv = PyArray_NDIM(beamVec); - dev = PyArray_NDIM(etaVec); - assert( dhkls == 2 && drc == 2 && dbm == 2 && - dvi == 1 && dbv == 1 && dev == 1); - - /* Verify dimensions of input arrays */ - npts = PyArray_DIMS(hkls)[0]; - - assert( PyArray_DIMS(hkls)[1] == 3 ); - assert( PyArray_DIMS(rMat_c)[0] == 3 && PyArray_DIMS(rMat_c)[1] == 3 ); - assert( PyArray_DIMS(bMat)[0] == 3 && PyArray_DIMS(bMat)[1] == 3 ); - assert( PyArray_DIMS(vInv_s)[0] == 6 ); - assert( PyArray_DIMS(beamVec)[0] == 3 ); - assert( PyArray_DIMS(etaVec)[0] == 3 ); - - /* Allocate arrays for return values */ - dims[0] = npts; dims[1] = 3; - oangs0 = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - oangs1 = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab data pointers into various arrays */ - hkls_Ptr = (double*)PyArray_DATA(hkls); - - chi_d = PyFloat_AsDouble((PyObject*)chi); - wavelen_d = PyFloat_AsDouble((PyObject*)wavelength); - - rMat_c_Ptr = (double*)PyArray_DATA(rMat_c); - bMat_Ptr = (double*)PyArray_DATA(bMat); - - vInv_s_Ptr = (double*)PyArray_DATA(vInv_s); - - beamVec_Ptr = (double*)PyArray_DATA(beamVec); - etaVec_Ptr = (double*)PyArray_DATA(etaVec); - - oangs0_Ptr = (double*)PyArray_DATA(oangs0); - oangs1_Ptr = (double*)PyArray_DATA(oangs1); - - /* Call the computational routine */ - oscillAnglesOfHKLs_cfunc(npts, hkls_Ptr, chi_d, - rMat_c_Ptr, bMat_Ptr, wavelen_d, - vInv_s_Ptr, beamVec_Ptr, etaVec_Ptr, - oangs0_Ptr, oangs1_Ptr); - - /* Build and return the list data structure */ - return_tuple = Py_BuildValue("OO",oangs0,oangs1); - Py_DECREF(oangs1); - Py_DECREF(oangs0); - - return return_tuple; -} - -/******************************************************************************/ -/* Utility Funtions */ - -static PyObject * unitRowVector(PyObject * self, PyObject * args) -{ - PyArrayObject *vecIn, *vecOut; - double *cIn, *cOut; - int d; - npy_intp n; - - if ( !PyArg_ParseTuple(args,"O", &vecIn)) return(NULL); - if ( vecIn == NULL ) return(NULL); - - assert( PyArray_ISCONTIGUOUS(vecIn) ); - assert( PyArray_ISALIGNED(vecIn) ); - - d = PyArray_NDIM(vecIn); - - assert(d == 1); - - n = PyArray_DIMS(vecIn)[0]; - - vecOut = (PyArrayObject*)PyArray_EMPTY(d,PyArray_DIMS(vecIn),NPY_DOUBLE,0); - - cIn = (double*)PyArray_DATA(vecIn); - cOut = (double*)PyArray_DATA(vecOut); - - unitRowVector_cfunc(n,cIn,cOut); - - return((PyObject*)vecOut); -} - -static PyObject * unitRowVectors(PyObject *self, PyObject *args) -{ - PyArrayObject *vecIn, *vecOut; - double *cIn, *cOut; - int d; - npy_intp m,n; - - if ( !PyArg_ParseTuple(args,"O", &vecIn)) return(NULL); - if ( vecIn == NULL ) return(NULL); - - assert( PyArray_ISCONTIGUOUS(vecIn) ); - assert( PyArray_ISALIGNED(vecIn) ); - - d = PyArray_NDIM(vecIn); - - assert(d == 2); - - m = PyArray_DIMS(vecIn)[0]; - n = PyArray_DIMS(vecIn)[1]; - - vecOut = (PyArrayObject*)PyArray_EMPTY(d,PyArray_DIMS(vecIn),NPY_DOUBLE,0); - - cIn = (double*)PyArray_DATA(vecIn); - cOut = (double*)PyArray_DATA(vecOut); - - unitRowVectors_cfunc(m,n,cIn,cOut); - - return((PyObject*)vecOut); -} - -static PyObject * makeDetectorRotMat(PyObject * self, PyObject * args) -{ - PyArrayObject *tiltAngles, *rMat; - int dt; - npy_intp nt, dims[2]; - double *tPtr, *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"O", &tiltAngles)) return(NULL); - if ( tiltAngles == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dt = PyArray_NDIM(tiltAngles); - assert( dt == 1 ); - - /* Verify dimensions of input arrays */ - nt = PyArray_DIMS(tiltAngles)[0]; - assert( nt == 3 ); - - /* Allocate the result matrix with appropriate dimensions and type */ - dims[0] = 3; dims[1] = 3; - rMat = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - tPtr = (double*)PyArray_DATA(tiltAngles); - rPtr = (double*)PyArray_DATA(rMat); - - /* Call the actual function */ - makeDetectorRotMat_cfunc(tPtr,rPtr); - - return((PyObject*)rMat); -} - -static PyObject * makeOscillRotMat(PyObject * self, PyObject * args) -{ - PyArrayObject *oscillAngles, *rMat; - int doa; - npy_intp no, dims[2]; - double *oPtr, *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"O", &oscillAngles)) return(NULL); - if ( oscillAngles == NULL ) return(NULL); - - /* Verify shape of input arrays */ - doa = PyArray_NDIM(oscillAngles); - assert( doa == 1 ); - - /* Verify dimensions of input arrays */ - no = PyArray_DIMS(oscillAngles)[0]; - assert( no == 2 ); - - /* Allocate the result matrix with appropriate dimensions and type */ - dims[0] = 3; dims[1] = 3; - rMat = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - oPtr = (double*)PyArray_DATA(oscillAngles); - rPtr = (double*)PyArray_DATA(rMat); - - /* Call the actual function */ - makeOscillRotMat_cfunc(oPtr[0], oPtr[1], rPtr); - - return((PyObject*)rMat); -} - -static PyObject * makeOscillRotMatArray(PyObject * self, PyObject * args) -{ - PyObject *chiObj; - double chi; - PyArrayObject *omeArray, *rMat; - int doa; - npy_intp i, no, dims[3]; - double *oPtr, *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OO", &chiObj, &omeArray)) return(NULL); - if ( chiObj == NULL ) return(NULL); - if ( omeArray == NULL ) return(NULL); - - /* Get chi */ - chi = PyFloat_AsDouble(chiObj); - if (chi == -1 && PyErr_Occurred()) return(NULL); - - /* Verify shape of input arrays */ - doa = PyArray_NDIM(omeArray); - assert( doa == 1 ); - - /* Verify dimensions of input arrays */ - no = PyArray_DIMS(omeArray)[0]; - - /* Allocate the result matrix with appropriate dimensions and type */ - dims[0] = no; dims[1] = 3; dims[2] = 3; - rMat = (PyArrayObject*)PyArray_EMPTY(3,dims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - oPtr = (double*)PyArray_DATA(omeArray); - rPtr = (double*)PyArray_DATA(rMat); - - /* Call the actual function repeatedly */ - for (i = 0; i < no; ++i) { - makeOscillRotMat_cfunc(chi, oPtr[i], rPtr + i*9); - } - - return((PyObject*)rMat); -} - -static PyObject * makeRotMatOfExpMap(PyObject * self, PyObject * args) -{ - PyArrayObject *expMap, *rMat; - int de; - npy_intp ne, dims[2]; - double *ePtr, *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"O", &expMap)) return(NULL); - if ( expMap == NULL ) return(NULL); - - /* Verify shape of input arrays */ - de = PyArray_NDIM(expMap); - assert( de == 1 ); - - /* Verify dimensions of input arrays */ - ne = PyArray_DIMS(expMap)[0]; - assert( ne == 3 ); - - /* Allocate the result matrix with appropriate dimensions and type */ - dims[0] = 3; dims[1] = 3; - rMat = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - ePtr = (double*)PyArray_DATA(expMap); - rPtr = (double*)PyArray_DATA(rMat); - - /* Call the actual function */ - makeRotMatOfExpMap_cfunc(ePtr,rPtr); - - return((PyObject*)rMat); -} - -static PyObject * makeRotMatOfQuat(PyObject * self, PyObject * args) -{ - PyArrayObject *quat, *rMat; - int nq, ne, de; - npy_intp dims2[2]={3, 3}, dims3[3]; - double *qPtr, *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"O", &quat)) return(NULL); - if ( quat == NULL ) return(NULL); - - /* Verify shape of input arrays */ - de = PyArray_NDIM(quat); - if (de == 1) { - ne = PyArray_DIMS(quat)[0]; - assert( ne == 4 ); - nq = 1; - /* Allocate the result matrix with appropriate dimensions and type */ - rMat = (PyArrayObject*)PyArray_EMPTY(2,dims2,NPY_DOUBLE,0); - } else { - assert( de == 2 ); - nq = PyArray_DIMS(quat)[0]; - ne = PyArray_DIMS(quat)[1]; - assert( ne == 4 ); - dims3[0] = nq; dims3[1] = 3; dims3[2] = 3; - /* Allocate the result matrix with appropriate dimensions and type */ - rMat = (PyArrayObject*)PyArray_EMPTY(3,dims3,NPY_DOUBLE,0); - } - - /* Grab pointers to the various data arrays */ - qPtr = (double*)PyArray_DATA(quat); - rPtr = (double*)PyArray_DATA(rMat); - - /* Call the actual function */ - makeRotMatOfQuat_cfunc(nq, qPtr, rPtr); - - return((PyObject*)rMat); -} - -static PyObject * makeBinaryRotMat(PyObject * self, PyObject * args) -{ - PyArrayObject *axis, *rMat; - int da; - npy_intp na, dims[2]; - double *aPtr, *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"O", &axis)) return(NULL); - if ( axis == NULL ) return(NULL); - - /* Verify shape of input arrays */ - da = PyArray_NDIM(axis); - assert( da == 1 ); - - /* Verify dimensions of input arrays */ - na = PyArray_DIMS(axis)[0]; - assert( na == 3 ); - - /* Allocate the result matrix with appropriate dimensions and type */ - dims[0] = 3; dims[1] = 3; - rMat = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - aPtr = (double*)PyArray_DATA(axis); - rPtr = (double*)PyArray_DATA(rMat); - - /* Call the actual function */ - makeBinaryRotMat_cfunc(aPtr,rPtr); - - return((PyObject*)rMat); -} - -static PyObject * makeEtaFrameRotMat(PyObject * self, PyObject * args) -{ - PyArrayObject *bHat, *eHat, *rMat; - int db, de; - npy_intp nb, ne, dims[2]; - double *bPtr, *ePtr, *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OO", &bHat,&eHat)) return(NULL); - if ( bHat == NULL || eHat == NULL ) return(NULL); - - /* Verify shape of input arrays */ - db = PyArray_NDIM(bHat); - de = PyArray_NDIM(eHat); - assert( db == 1 && de == 1); - - /* Verify dimensions of input arrays */ - nb = PyArray_DIMS(bHat)[0]; - ne = PyArray_DIMS(eHat)[0]; - assert( nb == 3 && ne == 3 ); - - /* Allocate the result matrix with appropriate dimensions and type */ - dims[0] = 3; dims[1] = 3; - rMat = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - - /* Grab pointers to the various data arrays */ - bPtr = (double*)PyArray_DATA(bHat); - ePtr = (double*)PyArray_DATA(eHat); - rPtr = (double*)PyArray_DATA(rMat); - - /* Call the actual function */ - makeEtaFrameRotMat_cfunc(bPtr,ePtr,rPtr); - - return((PyObject*)rMat); -} - -static PyObject * validateAngleRanges(PyObject * self, PyObject * args) -{ - PyArrayObject *angList, *angMin, *angMax, *reflInRange; - PyObject *ccw; - int ccwVal = 1; /* ccwVal set to True by default */ - int da, dmin, dmax; - npy_intp na, nmin, nmax; - double *aPtr, *minPtr, *maxPtr; - bool *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOOO", &angList,&angMin,&angMax,&ccw)) return(NULL); - if ( angList == NULL || angMin == NULL || angMax == NULL ) return(NULL); - - /* Verify shape of input arrays */ - da = PyArray_NDIM(angList); - dmin = PyArray_NDIM(angMin); - dmax = PyArray_NDIM(angMax); - assert( da == 1 && dmin == 1 && dmax ==1 ); - - /* Verify dimensions of input arrays */ - na = PyArray_DIMS(angList)[0]; - nmin = PyArray_DIMS(angMin)[0]; - nmax = PyArray_DIMS(angMax)[0]; - assert( nmin == nmax ); - - /* Check the value of ccw */ - if ( ccw == Py_True ) - ccwVal = 1; - else - ccwVal = 0; - - /* Allocate the result matrix with appropriate dimensions and type */ - reflInRange = (PyArrayObject*)PyArray_EMPTY(1,PyArray_DIMS(angList),NPY_BOOL,false); - assert( reflInRange != NULL ); - - /* Grab pointers to the various data arrays */ - aPtr = (double*)PyArray_DATA(angList); - minPtr = (double*)PyArray_DATA(angMin); - maxPtr = (double*)PyArray_DATA(angMax); - rPtr = (bool*)PyArray_DATA(reflInRange); - - /* Call the actual function */ - validateAngleRanges_cfunc(na,aPtr,nmin,minPtr,maxPtr,rPtr,ccwVal); - - return((PyObject*)reflInRange); -} - -static PyObject * rotate_vecs_about_axis(PyObject * self, PyObject * args) -{ - PyArrayObject *angles, *axes, *vecs; - PyArrayObject *rVecs; - int da, dax, dv; - npy_intp na, nax0, nax1, nv0, nv1; - double *aPtr, *axesPtr, *vecsPtr; - double *rPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOO", &angles,&axes,&vecs)) return(NULL); - if ( angles == NULL || axes == NULL || vecs == NULL ) return(NULL); - - /* Verify shape of input arrays */ - da = PyArray_NDIM(angles); - dax = PyArray_NDIM(axes); - dv = PyArray_NDIM(vecs); - assert( da == 1 && dax == 2 && dv == 2 ); - - /* Verify dimensions of input arrays */ - na = PyArray_DIMS(angles)[0]; - nax0 = PyArray_DIMS(axes)[0]; - nax1 = PyArray_DIMS(axes)[1]; - nv0 = PyArray_DIMS(vecs)[0]; - nv1 = PyArray_DIMS(vecs)[1]; - assert( na == 1 || na == nv0 ); - assert( nax0 == 1 || nax0 == nv0 ); - assert( nax1 == 3 && nv1 == 3 ); - - /* Allocate the result vectors with appropriate dimensions and type */ - rVecs = (PyArrayObject*)PyArray_EMPTY(2,PyArray_DIMS(vecs),NPY_DOUBLE,0.0); - assert( rVecs != NULL ); - - /* Grab pointers to the various data arrays */ - aPtr = (double*)PyArray_DATA(angles); - axesPtr = (double*)PyArray_DATA(axes); - vecsPtr = (double*)PyArray_DATA(vecs); - rPtr = (double*)PyArray_DATA(rVecs); - - /* Call the actual function */ - rotate_vecs_about_axis_cfunc(na,aPtr,nax0,axesPtr,nv0,vecsPtr,rPtr); - - return((PyObject*)rVecs); -} - -static PyObject * quat_distance(PyObject * self, PyObject * args) -{ - PyArrayObject *q1, *q2, *qsym; - double *q1Ptr, *q2Ptr, *qsymPtr; - int dq1, dq2, dqsym; - int nq1, nq2, nqsym, nsym; - double dist = 0.0; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"OOO", &q1,&q2,&qsym)) return(NULL); - if ( q1 == NULL || q2 == NULL || qsym == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dq1 = PyArray_NDIM(q1); - dq2 = PyArray_NDIM(q2); - dqsym = PyArray_NDIM(qsym); - assert( dq1 == 1 && dq2 == 1 && dqsym == 2 ); - - /* Verify dimensions of input arrays */ - nq1 = PyArray_DIMS(q1)[0]; - nq2 = PyArray_DIMS(q2)[0]; - nqsym = PyArray_DIMS(qsym)[0]; - nsym = PyArray_DIMS(qsym)[1]; - assert( nq1 == 4 && nq2 == 4 && nqsym == 4 ); - - /* Grab pointers to the various data arrays */ - q1Ptr = (double*)PyArray_DATA(q1); - q2Ptr = (double*)PyArray_DATA(q2); - qsymPtr = (double*)PyArray_DATA(qsym); - - /* Call the actual function */ - dist = quat_distance_cfunc(nsym,q1Ptr,q2Ptr,qsymPtr); - if (dist < 0) { - PyErr_SetString(PyExc_RuntimeError, "Could not allocate memory"); - return NULL; - } - return(PyFloat_FromDouble(dist)); -} - -static PyObject * homochoricOfQuat(PyObject * self, PyObject * args) -{ - PyArrayObject *quat, *hVec; - int nq, dq, ne; - npy_intp dims[2]; - double *qPtr, *hPtr; - - /* Parse arguments */ - if ( !PyArg_ParseTuple(args,"O", &quat)) return(NULL); - if ( quat == NULL ) return(NULL); - - /* Verify shape of input arrays */ - dq = PyArray_NDIM(quat); - if (dq == 1) { - ne = PyArray_DIMS(quat)[0]; - assert( ne == 4 ); - nq = 1; - dims[0] = nq; dims[1] = 3; - /* Allocate the result matrix with appropriate dimensions and type */ - hVec = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - } else { - assert( dq == 2 ); - nq = PyArray_DIMS(quat)[0]; - ne = PyArray_DIMS(quat)[1]; - assert( ne == 4 ); - dims[0] = nq; dims[1] = 3; - /* Allocate the result matrix with appropriate dimensions and type */ - hVec = (PyArrayObject*)PyArray_EMPTY(2,dims,NPY_DOUBLE,0); - } - - /* Grab pointers to the various data arrays */ - qPtr = (double*)PyArray_DATA(quat); - hPtr = (double*)PyArray_DATA(hVec); - - /* Call the actual function */ - homochoricOfQuat_cfunc(nq, qPtr, hPtr); - - return((PyObject*)hVec); -} diff --git a/hexrd/core/transforms/transforms_CAPI.h b/hexrd/core/transforms/transforms_CAPI.h deleted file mode 100644 index 5938ca742..000000000 --- a/hexrd/core/transforms/transforms_CAPI.h +++ /dev/null @@ -1,69 +0,0 @@ -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -#include -#include - -/******************************************************************************/ -/** The functions declared in this header make use of Python's C API to **/ -/** parse input arguments and verify their types (and sizes when **/ -/** appropriate). They also allocte new Python objects for use as return **/ -/** values. **/ -/** **/ -/** In short, these functions perform all of the necessary C API calls. **/ -/** **/ -/** The actual computations are handled by functions declared in the file **/ -/** "transforms_CFUNC.h". **/ -/** **/ -/** This separation of Python C API calls and C implementations allows the C **/ -/** functions to call eachother without the unnecessary overhead of passing **/ -/** arguments and return values as Python objects. **/ -/** **/ -/******************************************************************************/ - -/******************************************************************************/ -/* Funtions */ - -static PyObject * anglesToGVec(PyObject * self, PyObject * args); - -static PyObject * anglesToDVec(PyObject * self, PyObject * args); - -static PyObject * makeGVector(PyObject * self, PyObject * args); - -static PyObject * gvecToDetectorXY(PyObject * self, PyObject * args); - -static PyObject * gvecToDetectorXYArray(PyObject * self, PyObject * args); - -static PyObject * detectorXYToGvec(PyObject * self, PyObject * args); - -static PyObject * detectorXYToGvecArray(PyObject * self, PyObject * args); - -static PyObject * oscillAnglesOfHKLs(PyObject * self, PyObject * args); - -/******************************************************************************/ -/* Utility Funtions */ - -static PyObject * unitRowVector(PyObject * self, PyObject * args); - -static PyObject * unitRowVectors(PyObject * self, PyObject * args); - -static PyObject * makeDetectorRotMat(PyObject * self, PyObject * args); - -static PyObject * makeOscillRotMat(PyObject * self, PyObject * args); - -static PyObject * makeOscillRotMatArray(PyObject * self, PyObject * args); - -static PyObject * makeRotMatOfExpMap(PyObject * self, PyObject * args); - -static PyObject * makeRotMatOfQuat(PyObject * self, PyObject * args); - -static PyObject * makeBinaryRotMat(PyObject * self, PyObject * args); - -static PyObject * makeEtaFrameRotMat(PyObject * self, PyObject * args); - -static PyObject * validateAngleRanges(PyObject * self, PyObject * args); - -static PyObject * rotate_vecs_about_axis(PyObject * self, PyObject * args); - -static PyObject * quat_distance(PyObject * self, PyObject * args); - -static PyObject * homochoricOfQuat(PyObject * self, PyObject * args); diff --git a/hexrd/core/transforms/transforms_CFUNC.c b/hexrd/core/transforms/transforms_CFUNC.c deleted file mode 100644 index fd9b366d0..000000000 --- a/hexrd/core/transforms/transforms_CFUNC.c +++ /dev/null @@ -1,1096 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#include -#include -#include - -#include "transforms_CFUNC.h" - -/* - * Microsoft's C compiler, when running in C mode, does not support the inline - * keyword. However it does support an __inline one. - * - * So if compiling with MSC, just use __inline as inline - */ -#if defined(_MSC_VER) -# define inline __inline -#endif - -// Just remove any "restrict" keyword that may be present. -#define restrict - -static double epsf = 2.2e-16; -static double sqrt_epsf = 1.5e-8; -static double Zl[3] = {0.0,0.0,1.0}; - - -/******************************************************************************/ -/* Functions */ - -void anglesToGvec_cfunc(long int nvecs, double * angs, - double * bHat_l, double * eHat_l, - double chi, double * rMat_c, - double * gVec_c) -{ - /* - * takes an angle spec (2*theta, eta, omega) for nvecs g-vectors and - * returns the unit g-vector components in the crystal frame - * - * For unit g-vector in the lab frame, spec rMat_c = Identity and - * overwrite the omega values with zeros - */ - int i, j, k, l; - double rMat_e[9], rMat_s[9], rMat_ctst[9]; - double gVec_e[3], gVec_l[3], gVec_c_tmp[3]; - - /* Need eta frame cob matrix (could omit for standard setting) */ - makeEtaFrameRotMat_cfunc(bHat_l, eHat_l, rMat_e); - - /* make vector array */ - for (i = 0; i < nvecs; i++) { - /* components in BEAM frame */ - gVec_e[0] = cos(0.5*angs[3*i]) * cos(angs[3*i+1]); - gVec_e[1] = cos(0.5*angs[3*i]) * sin(angs[3*i+1]); - gVec_e[2] = sin(0.5*angs[3*i]); - - /* take from beam frame to lab frame */ - for (j = 0; j < 3; j++) { - gVec_l[j] = 0.0; - for (k = 0; k < 3; k++) { - gVec_l[j] += rMat_e[3*j+k]*gVec_e[k]; - } - } - - /* need pointwise rMat_s according to omega */ - makeOscillRotMat_cfunc(chi, angs[3*i+2], rMat_s); - - /* Compute dot(rMat_c.T, rMat_s.T) and hit against gVec_l */ - for (j=0; j<3; j++) { - for (k=0; k<3; k++) { - rMat_ctst[3*j+k] = 0.0; - for (l=0; l<3; l++) { - rMat_ctst[3*j+k] += rMat_c[3*l+j]*rMat_s[3*k+l]; - } - } - gVec_c_tmp[j] = 0.0; - for (k=0; k<3; k++) { - gVec_c_tmp[j] += rMat_ctst[3*j+k]*gVec_l[k]; - } - gVec_c[3*i+j] = gVec_c_tmp[j]; - } - } -} - -void anglesToDvec_cfunc(long int nvecs, double * angs, - double * bHat_l, double * eHat_l, - double chi, double * rMat_c, - double * gVec_c) -{ - /* - * takes an angle spec (2*theta, eta, omega) for nvecs g-vectors and - * returns the unit d-vector components in the crystal frame - * - * For unit d-vector in the lab frame, spec rMat_c = Identity and - * overwrite the omega values with zeros - */ - int i, j, k, l; - - double rMat_e[9], rMat_s[9], rMat_ctst[9]; - double gVec_e[3], gVec_l[3], gVec_c_tmp[3]; - - /* Need eta frame cob matrix (could omit for standard setting) */ - makeEtaFrameRotMat_cfunc(bHat_l, eHat_l, rMat_e); - - /* make vector array */ - for (i=0; i= ztol && bDot <= 1.0-ztol ) { - /* If we are here diffraction is possible so increment the number of - admissable vectors */ - makeBinaryRotMat_cfunc(gVec_l, brMat); - - denom = 0.0; - for (j=0; j<3; j++) { - dVec_l[j] = 0.0; - for (k=0; k<3; k++) - dVec_l[j] -= brMat[3*j+k]*bHat_l[k]; - - denom += nVec_l[j]*dVec_l[j]; - } - - if ( denom < -ztol ) { - - u = num/denom; - - for (j=0; j<3; j++) - P2_l[j] = P0_l[j]+u*dVec_l[j]; - - for (j=0; j<2; j++) { - P2_d[j] = 0.0; - for (k=0; k<3; k++) - P2_d[j] += rMat_d[3*k+j]*(P2_l[k]-tVec_d[k]); - result[j] = P2_d[j]; - } - /* result when computation can be finished */ - return; - } - } - /* default result when computation can't be finished */ - result[0] = NAN; - result[1] = NAN; -} - -/* - * The only difference between this and the non-Array version - * is that rMat_s is an array of matrices of length npts instead - * of a single matrix. - */ -void gvecToDetectorXYArray_cfunc(long int npts, double * gVec_c, - double * rMat_d, double * rMat_s, - double * rMat_c, double * tVec_d, - double * tVec_s, double * tVec_c, - double * beamVec, double * result) -{ - long int i; - int j, k, l; - double num; - double nVec_l[3], bHat_l[3], P0_l[3], P3_l[3]; - double rMat_sc[9]; - - /* Normalize the beam vector */ - unitRowVector_cfunc(3,beamVec,bHat_l); - - for (i=0L; i < npts; i++) { - /* Initialize the detector normal and frame origins */ - num = 0.0; - for (j=0; j<3; j++) { - nVec_l[j] = 0.0; - P0_l[j] = tVec_s[j]; - - for (k=0; k<3; k++) { - nVec_l[j] += rMat_d[3*j+k]*Zl[k]; - P0_l[j] += rMat_s[9*i + 3*j+k]*tVec_c[k]; - } - - P3_l[j] = tVec_d[j]; - - num += nVec_l[j]*(P3_l[j]-P0_l[j]); - } - - /* Compute the matrix product of rMat_s and rMat_c */ - for (j=0; j<3; j++) { - for (k=0; k<3; k++) { - rMat_sc[3*j+k] = 0.0; - for (l=0; l<3; l++) { - rMat_sc[3*j+k] += rMat_s[9*i + 3*j+l]*rMat_c[3*l+k]; - } - } - } - - gvecToDetectorXYOne_cfunc(gVec_c + 3*i, rMat_d, rMat_sc, - tVec_d, bHat_l, nVec_l, num, - P0_l, result + 2*i); - } -} - -void gvecToDetectorXY_cfunc(long int npts, double * gVec_c, - double * rMat_d, double * rMat_s, double * rMat_c, - double * tVec_d, double * tVec_s, double * tVec_c, - double * beamVec, double * result) -{ - long int i; - int j, k, l; - - double num; - double nVec_l[3], bHat_l[3], P0_l[3], P3_l[3]; - double rMat_sc[9]; - - /* Normalize the beam vector */ - unitRowVector_cfunc(3,beamVec,bHat_l); - - /* Initialize the detector normal and frame origins */ - num = 0.0; - for (j=0; j<3; j++) { - nVec_l[j] = 0.0; - P0_l[j] = tVec_s[j]; - - for (k=0; k<3; k++) { - nVec_l[j] += rMat_d[3*j+k]*Zl[k]; - P0_l[j] += rMat_s[3*j+k]*tVec_c[k]; - } - - P3_l[j] = tVec_d[j]; - - num += nVec_l[j]*(P3_l[j]-P0_l[j]); - } - - /* Compute the matrix product of rMat_s and rMat_c */ - for (j=0; j<3; j++) { - for (k=0; k<3; k++) { - rMat_sc[3*j+k] = 0.0; - for (l=0; l<3; l++) { - rMat_sc[3*j+k] += rMat_s[3*j+l]*rMat_c[3*l+k]; - } - } - } - - for (i=0L; i epsf ) { - double nrm_factor = 1.0/sqrt(nrm); - for (j=0; j<3; j++) { - dHat_l[j] *= nrm_factor; - } - } - - /* Compute tTh */ - b_dot_dHat_l = 0.0; - for (j=0; j<3; j++) { - b_dot_dHat_l += bVec[j]*dHat_l[j]; - } - tTh = acos(b_dot_dHat_l); - - /* Compute eta */ - for (j=0; j<2; j++) { - tVec2[j] = 0.0; - for (k=0; k<3; k++) { - tVec2[j] += rMat_e[3*k+j]*dHat_l[k]; - } - } - eta = atan2(tVec2[1], tVec2[0]); - - /* Compute n_g vector */ - nrm = 0.0; - for (j=0; j<3; j++) { - double val; - int j1 = j < 2 ? j+1 : 0; - int j2 = j > 0 ? j-1 : 2; - val = bVec[j1] * dHat_l[j2] - bVec[j2] * dHat_l[j1]; - nrm += val*val; - n_g[j] = val; - } - if ( nrm > epsf ) { - double nrm_factor = 1.0/sqrt(nrm); - for (j=0; j<3; j++) { - n_g[j] *= nrm_factor; - } - } - - /* Rotate dHat_l vector */ - phi = 0.5*(M_PI-tTh); - *tTh_out = tTh; - *eta_out = eta; - rotate_vecs_about_axis_cfunc(1, &phi, 1, n_g, 1, dHat_l, gVec_l_out); -} - -void detectorXYToGvec_cfunc(long int npts, double * xy, - double * rMat_d, double * rMat_s, - double * tVec_d, double * tVec_s, double * tVec_c, - double * beamVec, double * etaVec, - double * tTh, double * eta, double * gVec_l) -{ - long int i; - int j, k; - double nrm, bVec[3], tVec1[3]; - double rMat_e[9]; - - /* Fill rMat_e */ - makeEtaFrameRotMat_cfunc(beamVec,etaVec,rMat_e); - - /* Normalize the beam vector */ - nrm = 0.0; - for (j=0; j<3; j++) { - nrm += beamVec[j]*beamVec[j]; - } - - if ( nrm > epsf ) { - double nrm_factor = 1.0/sqrt(nrm); - for (j=0; j<3; j++) - bVec[j] = beamVec[j]*nrm_factor; - } else { - for (j=0; j<3; j++) - bVec[j] = beamVec[j]; - } - - /* Compute shift vector */ - for (j=0; j<3; j++) { - tVec1[j] = tVec_d[j]-tVec_s[j]; - for (k=0; k<3; k++) { - tVec1[j] -= rMat_s[3*j+k]*tVec_c[k]; - } - } - - for (i=0; i epsf ) { - double nrm_factor = 1.0/sqrt(nrm); - for (j=0; j<3; j++) - bVec[j] = beamVec[j]*nrm_factor; - } else { - for (j=0; j<3; j++) - bVec[j] = beamVec[j]; - } - - for (j=0; j<3; j++) { - tVec1[j] = tVec_d[j]-tVec_s[j]; - for (k=0; k<3; k++) { - tVec1[j] -= rMat_s[3*j+k]*tVec_c[k]; - } - } - - for (i=0; i epsf ) { - for (j=0; j<3; j++) - bHat_l[j] = beamVec[j]/nrm0; - } else { - for (j=0; j<3; j++) - bHat_l[j] = beamVec[j]; - } - - /* Normalize the eta vector */ - nrm0 = 0.0; - for (j=0; j<3; j++) { - nrm0 += etaVec[j]*etaVec[j]; - } - nrm0 = sqrt(nrm0); - if ( nrm0 > epsf ) { - for (j=0; j<3; j++) - eHat_l[j] = etaVec[j]/nrm0; - } else { - for (j=0; j<3; j++) - eHat_l[j] = etaVec[j]; - } - - /* Check for consistent reference coordiantes */ - nrm0 = 0.0; - for (j=0; j<3; j++) { - nrm0 += bHat_l[j]*eHat_l[j]; - } - if ( fabs(nrm0) < 1.0-sqrt_epsf ) crc = true; - - /* Compute the sine and cosine of the oscillation axis tilt */ - cchi = cos(chi); - schi = sin(chi); - - for (i=0; i epsf ) { - for (j=0; j<3; j++) { - gHat_c[j] /= nrm0; - gHat_s[j] = tmpVec[j]/nrm0; - } - } - - /* Compute the sine of the Bragg angle */ - sintht = 0.5*wavelength*nrm0; - - /* Compute the coefficients of the harmonic equation */ - a = gHat_s[2]*bHat_l[0] + schi*gHat_s[0]*bHat_l[1] - cchi*gHat_s[0]*bHat_l[2]; - b = gHat_s[0]*bHat_l[0] - schi*gHat_s[2]*bHat_l[1] + cchi*gHat_s[2]*bHat_l[2]; - c = - sintht - cchi*gHat_s[1]*bHat_l[1] - schi*gHat_s[1]*bHat_l[2]; - - /* Form solution */ - abMag = sqrt(a*a + b*b); assert( abMag > 0.0 ); - phaseAng = atan2(b,a); - rhs = c/abMag; - - if ( fabs(rhs) > 1.0 ) { - for (j=0; j<3; j++) - oangs0[3L*i+j] = NAN; - for (j=0; j<3; j++) - oangs1[3L*i+j] = NAN; - continue; - } - - rhsAng = asin(rhs); - - /* Write ome angles */ - oangs0[3L*i+2] = rhsAng - phaseAng; - oangs1[3L*i+2] = M_PI - rhsAng - phaseAng; - - if ( crc ) { - makeEtaFrameRotMat_cfunc(bHat_l,eHat_l,rMat_e); - - oVec[0] = chi; - - oVec[1] = oangs0[3L*i+2]; - makeOscillRotMat_cfunc(oVec[0], oVec[1], rMat_s); - - for (j=0; j<3; j++) { - tVec0[j] = 0.0; - for (k=0; k<3; k++) { - tVec0[j] += rMat_s[3*j+k]*gHat_s[k]; - } - } - for (j=0; j<2; j++) { - gVec_e[j] = 0.0; - for (k=0; k<3; k++) { - gVec_e[j] += rMat_e[3*k+j]*tVec0[k]; - } - } - oangs0[3L*i+1] = atan2(gVec_e[1],gVec_e[0]); - - oVec[1] = oangs1[3L*i+2]; - makeOscillRotMat_cfunc(oVec[0], oVec[1], rMat_s); - - for (j=0; j<3; j++) { - tVec0[j] = 0.0; - for (k=0; k<3; k++) { - tVec0[j] += rMat_s[3*j+k]*gHat_s[k]; - } - } - for (j=0; j<2; j++) { - gVec_e[j] = 0.0; - for (k=0; k<3; k++) { - gVec_e[j] += rMat_e[3*k+j]*tVec0[k]; - } - } - oangs1[3L*i+1] = atan2(gVec_e[1],gVec_e[0]); - - oangs0[3L*i+0] = 2.0*asin(sintht); - oangs1[3L*i+0] = oangs0[3L*i+0]; - } - } -} - -/******************************************************************************/ -/* Utility Funtions */ - -void unitRowVector_cfunc(int n, double * cIn, double * cOut) -{ - int j; - double nrm; - - nrm = 0.0; - for (j=0; j epsf ) { - for (j=0; j epsf ) { - for (j=0; j epsf ) { - s = sin(phi)/phi; - c = (1.0-cos(phi))/(phi*phi); - - rPtr[1] -= s*ePtr[2]; - rPtr[2] += s*ePtr[1]; - rPtr[3] += s*ePtr[2]; - rPtr[5] -= s*ePtr[0]; - rPtr[6] -= s*ePtr[1]; - rPtr[7] += s*ePtr[0]; - - rPtr[1] += c*ePtr[0]*ePtr[1]; - rPtr[2] += c*ePtr[0]*ePtr[2]; - rPtr[3] += c*ePtr[1]*ePtr[0]; - rPtr[5] += c*ePtr[1]*ePtr[2]; - rPtr[6] += c*ePtr[2]*ePtr[0]; - rPtr[7] += c*ePtr[2]*ePtr[1]; - - rPtr[0] -= c*(ePtr[1]*ePtr[1]+ePtr[2]*ePtr[2]); - rPtr[4] -= c*(ePtr[2]*ePtr[2]+ePtr[0]*ePtr[0]); - rPtr[8] -= c*(ePtr[0]*ePtr[0]+ePtr[1]*ePtr[1]); - } -} - -void makeRotMatOfQuat_cfunc(int nq, double * qPtr, double * rPtr) -{ - int i, j; - double c, s, phi, n[3]={0.0,0.0,0.0}; - - for (i=0; i epsf) { - n[0] = (1. / sin(0.5*phi)) * qPtr[4*i+1]; - n[1] = (1. / sin(0.5*phi)) * qPtr[4*i+2]; - n[2] = (1. / sin(0.5*phi)) * qPtr[4*i+3]; - - s = sin(phi); - c = cos(phi); - - rPtr[9*i+0] = c + n[0]*n[0]*(1. - c); - rPtr[9*i+1] = n[0]*n[1]*(1. - c) - n[2]*s; - rPtr[9*i+2] = n[0]*n[2]*(1. - c) + n[1]*s; - rPtr[9*i+3] = n[1]*n[0]*(1. - c) + n[2]*s; - rPtr[9*i+4] = c + n[1]*n[1]*(1. - c); - rPtr[9*i+5] = n[1]*n[2]*(1. - c) - n[0]*s; - rPtr[9*i+6] = n[2]*n[0]*(1. - c) - n[1]*s; - rPtr[9*i+7] = n[2]*n[1]*(1. - c) + n[0]*s; - rPtr[9*i+8] = c + n[2]*n[2]*(1. - c); - } - else { - for (j=0; j<9; j++) { - if ( j%4 == 0 ) - rPtr[9*i+j] = 1.0; - else - rPtr[9*i+j] = 0.0; - } - } - } -} - -void makeBinaryRotMat_cfunc(double * aPtr, double * rPtr) -{ - int i, j; - - for (i=0; i<3; i++) { - for (j=0; j<3; j++) { - rPtr[3*i+j] = 2.0*aPtr[i]*aPtr[j]; - } - rPtr[3*i+i] -= 1.0; - } -} - -void makeEtaFrameRotMat_cfunc(double * bPtr, double * ePtr, double * rPtr) -{ - /* - * This function generates a COB matrix that takes components in BEAM frame to - * LAB frame - * - * NOTE: the beam and eta vectors MUST NOT BE COLINEAR!!!! - */ - int i; - - double yPtr[3], bHat[3], yHat[3], xHat[3]; - - /* find Y as e ^ b */ - yPtr[0] = ePtr[1]*bPtr[2] - bPtr[1]*ePtr[2]; - yPtr[1] = ePtr[2]*bPtr[0] - bPtr[2]*ePtr[0]; - yPtr[2] = ePtr[0]*bPtr[1] - bPtr[0]*ePtr[1]; - - /* Normalize beam (-Z) and Y vectors */ - unitRowVector_cfunc(3, bPtr, bHat); - unitRowVector_cfunc(3, yPtr, yHat); - - /* Find X as b ^ Y */ - xHat[0] = bHat[1]*yHat[2] - yHat[1]*bHat[2]; - xHat[1] = bHat[2]*yHat[0] - yHat[2]*bHat[0]; - xHat[2] = bHat[0]*yHat[1] - yHat[0]*bHat[1]; - - /* Assign columns */ - /* Assign Y column */ - for (i=0; i<3; i++) - { - rPtr[3*i+0] = xHat[i]; - rPtr[3*i+1] = yHat[i]; - rPtr[3*i+2] = -bHat[i]; - } -} - -void validateAngleRanges_old_cfunc(int na, double * aPtr, int nr, double * minPtr, double * maxPtr, bool * rPtr) -{ - int i, j; - double thetaMax, theta; - - /* Each angle should only be examined once. Any more is a waste of time. */ - for (i=0; i 2.0*M_PI ) - thetaMax -= 2.0*M_PI; - - while ( theta < 0.0 ) - theta += 2.0*M_PI; - while ( theta > 2.0*M_PI ) - theta -= 2.0*M_PI; - - if ( theta > -sqrt_epsf && theta < thetaMax + sqrt_epsf ) { - rPtr[i] = true; - - /* No need to check other ranges */ - break; - } - } - } -} - -void validateAngleRanges_cfunc(int na, double * aPtr, int nr, - double * minPtr, double * maxPtr, - bool * rPtr, int ccw) -{ - int i, j; - double thetaMax, theta; - double *startPtr, *stopPtr; - - if ( ccw ) { - startPtr = minPtr; - stopPtr = maxPtr; - } else { - startPtr = maxPtr; - stopPtr = minPtr; - } - - /* Each angle should only be examined once. Any more is a waste of time. */ - for (i=0; i 2.0*M_PI ) - thetaMax -= 2.0*M_PI; - - /* Check for an empty range */ - if ( fabs(thetaMax) < sqrt_epsf ) { - rPtr[i] = true; - - /* No need to check other ranges */ - break; - } - - /* Check for a range which spans a full circle */ - if ( fabs(thetaMax-2.0*M_PI) < sqrt_epsf ) { - - /* Double check the initial range */ - if ( (ccw && maxPtr[j] > minPtr[j]) || ((!ccw) && maxPtr[j] < minPtr[j]) ) { - rPtr[i] = true; - - /* No need to check other ranges */ - break; - } - } - - while ( theta < 0.0 ) - theta += 2.0*M_PI; - while ( theta > 2.0*M_PI ) - theta -= 2.0*M_PI; - - if ( theta >= -sqrt_epsf && theta <= thetaMax+sqrt_epsf ) { - rPtr[i] = true; - - /* No need to check other ranges */ - break; - } - } - } -} - - -void rotate_vecs_about_axis_cfunc(long int na, double * angles, - long int nax, double * axes, - long int nv, double * vecs, - double * rVecs) -{ - int i, j, sa, sax; - double c, s, nrm, proj, aCrossV[3]; - - if ( na == 1 ) sa = 0; - else sa = 1; - if ( nax == 1 ) sax = 0; - else sax = 3; - - for (i=0; i 1 || i == 0 ) { - nrm = 0.0; - for (j=0; j<3; j++) - nrm += axes[sax*i+j]*axes[sax*i+j]; - nrm = sqrt(nrm); - } - - /* Compute projection of vec along axis */ - proj = 0.0; - for (j=0; j<3; j++) - proj += axes[sax*i+j]*vecs[3*i+j]; - - /* Compute the cross product of the axis with vec */ - for (j=0; j<3; j++) - aCrossV[j] = axes[sax*i+(j+1)%3]*vecs[3*i+(j+2)%3]-axes[sax*i+(j+2)%3]*vecs[3*i+(j+1)%3]; - - /* Combine the three terms to compute the rotated vector */ - for (j=0; j<3; j++) { - rVecs[3*i+j] = c*vecs[3*i+j]+(s/nrm)*aCrossV[j]+(1.0-c)*proj*axes[sax*i+j]/(nrm*nrm); - } - } -} - -double quat_distance_cfunc(int nsym, double * q1, double * q2, double * qsym) -{ - int i; - double q0, q0_max = 0.0, dist = 0.0; - double *q2s; - - if ( NULL == (q2s = (double *)malloc(4*nsym*sizeof(double))) ) { - printf("malloc failed\n"); - return(-1); - } - - /* For each symmetry in qsym compute its inner product with q2 */ - for (i=0; i q0_max ) { - q0_max = fabs(q0); - } - } - - if ( q0_max <= 1.0 ) - dist = 2.0*acos(q0_max); - else if ( q0_max - 1. < 1e-12 ) - /* in case of quats loaded from single precision file */ - dist = 0.; - else - dist = NAN; - - free(q2s); - - return(dist); -} - -void homochoricOfQuat_cfunc(int nq, double * qPtr, double * hPtr) -{ - int i; - double arg, f, s, phi; - - for (i=0; i epsf) { - arg = 0.75*(phi - sin(phi)); - if (arg < 0.) { - f = -pow(-arg, 1./3.); - } else { - f = pow(arg, 1./3.); - } - s = 1. / sin(0.5*phi); - - hPtr[3*i+0] = f * s * qPtr[4*i+1]; - hPtr[3*i+1] = f * s * qPtr[4*i+2]; - hPtr[3*i+2] = f * s * qPtr[4*i+3]; - } - else { - hPtr[3*i+0] = 0.; - hPtr[3*i+1] = 0.; - hPtr[3*i+2] = 0.; - } - } -} diff --git a/hexrd/core/transforms/transforms_CFUNC.h b/hexrd/core/transforms/transforms_CFUNC.h deleted file mode 100644 index 628883f7d..000000000 --- a/hexrd/core/transforms/transforms_CFUNC.h +++ /dev/null @@ -1,95 +0,0 @@ -/******************************************************************************/ -/** The functions declared in this header use standard C to implement the **/ -/** various computations needed by the functions declared in the file **/ -/** "transforms_CAPI.h". **/ -/** **/ -/** This separation of Python C API calls and C implementations allows the C **/ -/** functions to call eachother without the unnecessary overhead of passing **/ -/** arguments and return values as Python objects. **/ -/** **/ -/******************************************************************************/ - - -#ifdef _WIN32 - #define false 0 - #define true 1 - #define bool int - #ifndef NAN - static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; - #define NAN (*(const float *) __nan) - #endif -#else - #include -#endif - -/******************************************************************************/ -/* Funtions */ - -void anglesToGvec_cfunc(long int nvecs, double * angs, - double * bHat_l, double * eHat_l, - double chi, double * rMat_c, - double * gVec_c); - -void anglesToDvec_cfunc(long int nvecs, double * angs, - double * bHat_l, double * eHat_l, - double chi, double * rMat_c, - double * gVec_c); - -void gvecToDetectorXY_cfunc(long int npts, double * gVec_c_Ptr, - double * rMat_d_Ptr, double * rMat_s_Ptr, double * rMat_c_Ptr, - double * tVec_d_Ptr, double * tVec_s_Ptr, double * tVec_c_Ptr, - double * beamVec_Ptr, - double * result_Ptr); - -void gvecToDetectorXYArray_cfunc(long int npts, double * gVec_c_Ptr, - double * rMat_d_Ptr, double * rMat_s_Ptr, double * rMat_c_Ptr, - double * tVec_d_Ptr, double * tVec_s_Ptr, double * tVec_c_Ptr, - double * beamVec_Ptr, - double * result_Ptr); - -void detectorXYToGvec_cfunc(long int npts, double * xy, - double * rMat_d, double * rMat_s, - double * tVec_d, double * tVec_s, double * tVec_c, - double * beamVec, double * etaVec, - double * tTh, double * eta, double * gVec_l); - -void detectorXYToGvecArray_cfunc(long int npts, double * xy, - double * rMat_d, double * rMat_s, - double * tVec_d, double * tVec_s, double * tVec_c, - double * beamVec, double * etaVec, - double * tTh, double * eta, double * gVec_l); - -void oscillAnglesOfHKLs_cfunc(long int npts, double * hkls, double chi, - double * rMat_c, double * bMat, double wavelength, - double * vInv_s, double * beamVec, double * etaVec, - double * oangs0, double * oangs1); - -/******************************************************************************/ -/* Utility Funtions */ - -void unitRowVector_cfunc(int n, double * cIn, double * cOut); - -void unitRowVectors_cfunc(int m, int n, double * cIn, double * cOut); - -void makeDetectorRotMat_cfunc(double * tPtr, double * rPtr); - -void makeOscillRotMat_cfunc(double chi, double ome, double * rPtr); - -void makeRotMatOfExpMap_cfunc(double * ePtr, double * rPtr); - -void makeRotMatOfQuat_cfunc(int nq, double * qPtr, double * rPtr); - -void makeBinaryRotMat_cfunc(double * aPtr, double * rPtr); - -void makeEtaFrameRotMat_cfunc(double * bPtr, double * ePtr, double * rPtr); - -void validateAngleRanges_cfunc(int na, double * aPtr, int nr, double * minPtr, double * maxPtr, bool * rPtr, int ccw); - -void rotate_vecs_about_axis_cfunc(long int na, double * angles, - long int nax, double * axes, - long int nv, double * vecs, - double * rVecs); - -double quat_distance_cfunc(int nsym, double * q1, double * q2, double * qsym); - -void homochoricOfQuat_cfunc(int nq, double * qPtr, double * hPtr); diff --git a/hexrd/core/transforms/xfcapi.py b/hexrd/core/transforms/xfcapi.py index 1e96661b4..0ee0b485d 100644 --- a/hexrd/core/transforms/xfcapi.py +++ b/hexrd/core/transforms/xfcapi.py @@ -2,29 +2,6 @@ # add and test them. # NOTE: we are only importing what is currently being used in hexrd # and hexrdgui. This is so that we can see clearly what is in use. -from .old_xfcapi import ( - anglesToDVec, - anglesToGVec, - detectorXYToGvec, - gvecToDetectorXY, - gvecToDetectorXYArray, - oscillAnglesOfHKLs, - angularDifference, - makeDetectorRotMat, - makeEtaFrameRotMat, - makeOscillRotMat, - makeOscillRotMatArray, - makeRotMatOfExpMap, - makeRotMatOfQuat, - mapAngle, - rowNorm, - unitRowVector, - bVec_ref, - eta_ref, - Xl, - Yl, -) - from .new_capi.xf_new_capi import ( angles_to_dvec, diff --git a/hexrd/powder/wppf/texture.py b/hexrd/powder/wppf/texture.py index 9e3d2d0bc..ad39b22ee 100644 --- a/hexrd/powder/wppf/texture.py +++ b/hexrd/powder/wppf/texture.py @@ -5,7 +5,7 @@ # hexrd imports # ------------- -from hexrd.core.transforms.xfcapi import anglesToGVec +from hexrd.core.transforms.new_capi.xf_new_capi import angles_to_gvec from hexrd.core import constants from hexrd.powder.wppf.phase import Material_Rietveld @@ -729,8 +729,8 @@ def bvec(self, bHat_l): self._bvec = bHat_l @etavec.setter - def etavec(self, eHat_l): - self._etavec = eHat_l + def etavec(self, eta_vec): + self._etavec = eta_vec @property def sample_normal(self): @@ -907,7 +907,7 @@ def precompute_spherical_harmonics( ) ).T - pfgrid = anglesToGVec(angs, bHat_l=self.bvec, eHat_l=self.etavec) + pfgrid = angles_to_gvec(angs, beam_vec=self.bvec, eta_vec=self.etavec) # Next part runs in a numba function to accelerate it. if calc_type == 'texture_factor': diff --git a/setup.py b/setup.py index 71d5d9200..8db4269eb 100644 --- a/setup.py +++ b/setup.py @@ -153,21 +153,6 @@ def get_cpp_extensions(): return [transforms_ext, inverse_distortion_ext] - -def get_old_xfcapi_extension_modules(): - # for transforms - srclist = ['transforms_CAPI.c', 'transforms_CFUNC.c'] - srclist = [os.path.join('hexrd/core/transforms', f) for f in srclist] - transforms_mod = Extension( - 'hexrd.core.extensions._transforms_CAPI', - sources=srclist, - include_dirs=[np_include_dir], - extra_compile_args=compiler_flags, - ) - - return [transforms_mod] - - def get_new_xfcapi_extension_modules(): transforms_mod = Extension( 'hexrd.core.extensions._new_transforms_capi', @@ -184,7 +169,6 @@ def get_extension_modules(): return [ item for sublist in ( - get_old_xfcapi_extension_modules(), get_new_xfcapi_extension_modules(), get_convolution_extensions(), get_cpp_extensions(), From 640ad5028a09ddfe8ddd197febbecd36966f24e2 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 13:54:05 -0500 Subject: [PATCH 20/28] Wipe out the old transforms CAPI info --- hexrd/core/extensions/__init__.py | 2 - hexrd/core/extensions/_transforms_CAPI.pyi | 196 ------------------ ...ansforms_capi.pyi => transforms_c_api.pyi} | 0 hexrd/core/transforms/debug_helpers.h | 61 ------ hexrd/core/transforms/new_capi/module.c | 2 +- hexrd/core/transforms/new_capi/xf_new_capi.py | 28 +-- hexrd/file_table.tsv | 4 - setup.py | 2 +- 8 files changed, 16 insertions(+), 279 deletions(-) delete mode 100644 hexrd/core/extensions/__init__.py delete mode 100644 hexrd/core/extensions/_transforms_CAPI.pyi rename hexrd/core/extensions/{_new_transforms_capi.pyi => transforms_c_api.pyi} (100%) delete mode 100644 hexrd/core/transforms/debug_helpers.h diff --git a/hexrd/core/extensions/__init__.py b/hexrd/core/extensions/__init__.py deleted file mode 100644 index 4eb891db8..000000000 --- a/hexrd/core/extensions/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import _new_transforms_capi -from . import inverse_distortion diff --git a/hexrd/core/extensions/_transforms_CAPI.pyi b/hexrd/core/extensions/_transforms_CAPI.pyi deleted file mode 100644 index 4b1bf07a4..000000000 --- a/hexrd/core/extensions/_transforms_CAPI.pyi +++ /dev/null @@ -1,196 +0,0 @@ -from __future__ import annotations - -import numpy as np -from numpy.typing import NDArray - - -def anglesToGVec( - angs: NDArray[np.float64], # (n, 3) - bHat_l: NDArray[np.float64], # (3,) - eHat_l: NDArray[np.float64], # (3,) - chi: float, - rMat_c: NDArray[np.float64], # (3, 3) -) -> NDArray[np.float64]: # (n, 3) - ... - - -def anglesToDVec( - angs: NDArray[np.float64], # (n, 3) - bHat_l: NDArray[np.float64], # (3,) - eHat_l: NDArray[np.float64], # (3,) - chi: float, - rMat_c: NDArray[np.float64], # (3, 3) -) -> NDArray[np.float64]: # (n, 3) - ... - - -def gvecToDetectorXY( - gVec_c: NDArray[np.float64], # (n, 3) - rMat_d: NDArray[np.float64], # (3, 3) - rMat_s: NDArray[np.float64], # (3, 3) - rMat_c: NDArray[np.float64], # (3, 3) - tVec_d: NDArray[np.float64], # (3,) - tVec_s: NDArray[np.float64], # (3,) - tVec_c: NDArray[np.float64], # (3,) - beamVec: NDArray[np.float64], # (3,) -) -> NDArray[np.float64]: # (n, 2) - ... - - -def gvecToDetectorXYArray( - gVec_c: NDArray[np.float64], # (n, 3) - rMat_d: NDArray[np.float64], # (3, 3) - rMat_s: NDArray[np.float64], # (n, 3, 3) - rMat_c: NDArray[np.float64], # (3, 3) - tVec_d: NDArray[np.float64], # (3,) - tVec_s: NDArray[np.float64], # (3,) - tVec_c: NDArray[np.float64], # (3,) - beamVec: NDArray[np.float64], # (3,) -) -> NDArray[np.float64]: # (n, 2) - ... - - -def detectorXYToGvec( - xy_det: NDArray[np.float64], # (n, 2) - rMat_d: NDArray[np.float64], # (3, 3) - rMat_s: NDArray[np.float64], # (3, 3) - tVec_d: NDArray[np.float64], # (3,) - tVec_s: NDArray[np.float64], # (3,) - tVec_c: NDArray[np.float64], # (3,) - beamVec: NDArray[np.float64], # (3,) - etaVec: NDArray[np.float64], # (3,) -) -> tuple[tuple[NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]]: - """ - Returns: - ((tTh, eta), gVec_l) - where: - tTh: (n,) float64 - eta: (n,) float64 - gVec_l: (n, 3) float64 - """ - ... - - -def detectorXYToGvecArray( - xy_det: NDArray[np.float64], # (n, 2) - rMat_d: NDArray[np.float64], # (3, 3) - rMat_s: NDArray[np.float64], # (n, 3, 3) - tVec_d: NDArray[np.float64], # (3,) - tVec_s: NDArray[np.float64], # (3,) - tVec_c: NDArray[np.float64], # (3,) - beamVec: NDArray[np.float64], # (3,) - etaVec: NDArray[np.float64], # (3,) -) -> tuple[tuple[NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]]: - """ - Returns: - ((tTh, eta), gVec_l) - where: - tTh: (n,) float64 - eta: (n,) float64 - gVec_l: (n, 3) float64 - """ - ... - - -def oscillAnglesOfHKLs( - hkls: NDArray[np.float64], # (n, 3) - chi: float, - rMat_c: NDArray[np.float64], # (3, 3) - bMat: NDArray[np.float64], # (3, 3) - wavelength: float, - vInv_s: NDArray[np.float64], # (6,) - beamVec: NDArray[np.float64], # (3,) - etaVec: NDArray[np.float64], # (3,) -) -> tuple[NDArray[np.float64], NDArray[np.float64]]: - """ - Returns: - (oangs0, oangs1) each (n, 3) float64 - """ - ... - - -def unitRowVector( - vecIn: NDArray[np.float64], # (n,) -) -> NDArray[np.float64]: # (n,) - ... - - -def unitRowVectors( - vecIn: NDArray[np.float64], # (m, n) -) -> NDArray[np.float64]: # (m, n) - ... - - -def makeDetectorRotMat( - tiltAngles: NDArray[np.float64], # (3,) -) -> NDArray[np.float64]: # (3, 3) - ... - - -def makeOscillRotMat( - oscillAngles: NDArray[np.float64], # (2,) -) -> NDArray[np.float64]: # (3, 3) - ... - - -def makeOscillRotMatArray( - chi: float, - omeArray: NDArray[np.float64], # (n,) -) -> NDArray[np.float64]: # (n, 3, 3) - ... - - -def makeRotMatOfExpMap( - expMap: NDArray[np.float64], # (3,) -) -> NDArray[np.float64]: # (3, 3) - ... - - -def makeRotMatOfQuat( - quat: NDArray[np.float64], # (n, 4) -) -> NDArray[np.float64]: # (n, 3, 3) - ... - - -def makeBinaryRotMat( - axis: NDArray[np.float64], # (3,) -) -> NDArray[np.float64]: # (3, 3) - ... - - -def makeEtaFrameRotMat( - bHat: NDArray[np.float64], # (3,) - eHat: NDArray[np.float64], # (3,) -) -> NDArray[np.float64]: # (3, 3) - ... - - -def validateAngleRanges( - angList: NDArray[np.float64], # (na,) - angMin: NDArray[np.float64], # (nmin,) - angMax: NDArray[np.float64], # (nmin,) - ccw: object, # expects bool-ish -) -> NDArray[np.bool_]: # (na,) - ... - - -def rotate_vecs_about_axis( - angles: NDArray[np.float64], # (1,) or (n,) - axes: NDArray[np.float64], # (1,3) or (n,3) - vecs: NDArray[np.float64], # (n,3) -) -> NDArray[np.float64]: # (n,3) - ... - - -def quat_distance( - q1: NDArray[np.float64], # (4,) - q2: NDArray[np.float64], # (4,) - qsym: NDArray[np.float64], # (4, nsym) -) -> float: - ... - - -def homochoricOfQuat( - quat: NDArray[np.float64], # (n, 4) -) -> NDArray[np.float64]: # (n, 3) - ... diff --git a/hexrd/core/extensions/_new_transforms_capi.pyi b/hexrd/core/extensions/transforms_c_api.pyi similarity index 100% rename from hexrd/core/extensions/_new_transforms_capi.pyi rename to hexrd/core/extensions/transforms_c_api.pyi diff --git a/hexrd/core/transforms/debug_helpers.h b/hexrd/core/transforms/debug_helpers.h deleted file mode 100644 index aacce7d5a..000000000 --- a/hexrd/core/transforms/debug_helpers.h +++ /dev/null @@ -1,61 +0,0 @@ -#include - - - -static void debug_dump_val(const char *name, double val) -{ - printf("%s: %10.6f\n", name, val); -} - -static void debug_dump_m33(const char *name, double *array) -{ - printf("%s:\n", name); - printf("\t%10.6f %10.6f %10.6f\n", array[0], array[1], array[2]); - printf("\t%10.6f %10.6f %10.6f\n", array[3], array[4], array[5]); - printf("\t%10.6f %10.6f %10.6f\n", array[6], array[7], array[8]); -} - -static void debug_dump_v3(const char *name, double *vec) -{ - printf("%s: %10.6f %10.6f %10.6f\n", name, vec[0], vec[1], vec[2]); -} - -/* ============================================================================ - * These can be used to initialize and check buffers if we suspect the - * code may leave it uninitialized - * ============================================================================ - */ - -#define SNAN_HI 0x7ff700a0 -#define SNAN_LO 0xbad0feed -void fill_signaling_nans(double *arr, int count) -{ - int i; - npy_uint32 *arr_32 = (npy_uint32 *)arr; - /* Fills an array with signaling nans to detect errors - * Use the 0x7ff700a0bad0feed as the pattern - */ - for (i = 0; i < count; ++i) - { - arr_32[2*i+0] = SNAN_LO; - arr_32[2*i+1] = SNAN_HI; - } -} - -int detect_signaling_nans(double *arr, int count) -{ - int i; - npy_uint32 *arr_32 = (npy_uint32 *) arr; - for (i = 0; i < count; ++i) - { - if (arr_32[2*i+0] == SNAN_LO && - arr_32[2*i+1] == SNAN_HI) - { - return 1; - } - } - - return 0; -} -#undef SNAN_HI -#undef SNAN_LO diff --git a/hexrd/core/transforms/new_capi/module.c b/hexrd/core/transforms/new_capi/module.c index e28616c5f..692bf765f 100644 --- a/hexrd/core/transforms/new_capi/module.c +++ b/hexrd/core/transforms/new_capi/module.c @@ -15,7 +15,7 @@ * Note the supporting CONCAT and STR macros... * ============================================================================= */ -#define THIS_MODULE_NAME _new_transforms_capi +#define THIS_MODULE_NAME transforms_c_api #define _CONCAT(a,b) a ## b #define CONCAT(a,b) _CONCAT(a,b) diff --git a/hexrd/core/transforms/new_capi/xf_new_capi.py b/hexrd/core/transforms/new_capi/xf_new_capi.py index 7242f8be4..b4b225af8 100644 --- a/hexrd/core/transforms/new_capi/xf_new_capi.py +++ b/hexrd/core/transforms/new_capi/xf_new_capi.py @@ -27,7 +27,7 @@ import numpy as np from numpy.typing import NDArray -from hexrd.core.extensions import _new_transforms_capi as _impl +from hexrd.core.extensions import transforms_c_api from hexrd.core.extensions import transforms as cpp_transforms from hexrd.core.distortion.distortionabc import DistortionABC from hexrd.core import constants as cnst @@ -258,11 +258,11 @@ def gvec_to_xy( # version or the "scalar" (over rmat_s) version. Note that rmat_s is either # a 3x3 matrix (ndim 2) or an nx3x4 array of matrices (ndim 3) if rmat_s.ndim > 2: - result = _impl.gvecToDetectorXYArray( + result = transforms_c_api.gvecToDetectorXYArray( gvec_c, rmat_d, rmat_s, rmat_c, tvec_d, tvec_s, tvec_c, beam_vec ) else: - result = _impl.gvecToDetectorXY( + result = transforms_c_api.gvecToDetectorXY( gvec_c, rmat_d, rmat_s, rmat_c, tvec_d, tvec_s, tvec_c, beam_vec ) return result[0] if orig_ndim == 1 else result @@ -341,7 +341,7 @@ def xy_to_gvec( tvec_c = np.ascontiguousarray(tvec_c.flatten()) beam_vec = np.ascontiguousarray((-rmat_b[:, 2]).flatten()) eta_vec = np.ascontiguousarray(rmat_b[:, 0].flatten()) - return _impl.detectorXYToGvec( + return transforms_c_api.detectorXYToGvec( xy_d, rmat_d, rmat_s, tvec_d, tvec_s, tvec_c, beam_vec, eta_vec ) @@ -441,7 +441,7 @@ def oscill_angles_of_hkls( beam_vec = np.ascontiguousarray(beam_vec.flatten()) eta_vec = np.ascontiguousarray(eta_vec.flatten()) bmat = np.ascontiguousarray(bmat) - return _impl.oscillAnglesOfHKLs( + return transforms_c_api.oscillAnglesOfHKLs( hkls, chi, rmat_c, bmat, wavelength, v_inv, beam_vec, eta_vec ) @@ -467,9 +467,9 @@ def unit_vector(vec_in: NDArray[np.float64]) -> NDArray[np.float64]: """ vec_in = np.ascontiguousarray(vec_in) if vec_in.ndim == 1: - return _impl.unitRowVector(vec_in) + return transforms_c_api.unitRowVector(vec_in) elif vec_in.ndim == 2: - return _impl.unitRowVectors(vec_in) + return transforms_c_api.unitRowVectors(vec_in) else: raise ValueError( "incorrect arg shape; must be 1-d or 2-d, yours is %d-d" % (vec_in.ndim) @@ -484,7 +484,7 @@ def make_detector_rmat(tilt_angles: NDArray[np.float64]) -> NDArray[np.float64]: Returns the (3, 3) rotation matrix from the detector frame to the lab frame """ t_angles = np.ascontiguousarray(np.r_[tilt_angles].flatten()) - return _impl.makeDetectorRotMat(t_angles) + return transforms_c_api.makeDetectorRotMat(t_angles) # make_sample_rmat in CAPI is split between makeOscillRotMat @@ -494,13 +494,13 @@ def make_detector_rmat(tilt_angles: NDArray[np.float64]) -> NDArray[np.float64]: def make_oscill_rmat(oscillAngles): chi, ome = oscillAngles ome = np.atleast_1d(ome) - result = _impl.makeOscillRotMat(chi, ome) + result = transforms_c_api.makeOscillRotMat(chi, ome) return result.reshape((3, 3)) def make_oscill_rmat_array(chi, omeArray): arg = np.ascontiguousarray(omeArray) - return _impl.makeOscillRotMat(chi, arg) + return transforms_c_api.makeOscillRotMat(chi, arg) def make_sample_rmat(chi: float, ome: float | NDArray[np.float64]) -> NDArray[np.float64]: @@ -591,7 +591,7 @@ def make_beam_rmat(bvec_l: NDArray[np.float64], evec_l: NDArray[np.float64]) -> """ arg1 = np.ascontiguousarray(bvec_l.flatten()) arg2 = np.ascontiguousarray(evec_l.flatten()) - return _impl.makeEtaFrameRotMat(arg1, arg2) + return transforms_c_api.makeEtaFrameRotMat(arg1, arg2) def validate_angle_ranges( @@ -625,7 +625,7 @@ def validate_angle_ranges( start_angs = np.atleast_1d(start_angs).astype(np.double, order='C') stop_angs = np.atleast_1d(stop_angs).astype(np.double, order='C') - return _impl.validateAngleRanges(ang_list, start_angs, stop_angs, ccw) + return transforms_c_api.validateAngleRanges(ang_list, start_angs, stop_angs, ccw) def rotate_vecs_about_axis( @@ -651,7 +651,7 @@ def rotate_vecs_about_axis( angle = np.asarray(angle) axis = np.ascontiguousarray(axis.T) vecs = np.ascontiguousarray(vecs.T) - result = _impl.rotate_vecs_about_axis(angle, axis, vecs) + result = transforms_c_api.rotate_vecs_about_axis(angle, axis, vecs) return result.T @@ -676,4 +676,4 @@ def quat_distance(q1: NDArray[np.float64], q2: NDArray[np.float64], qsym: NDArra q2 = np.ascontiguousarray(q2.flatten()) # C module expects quaternions in row major, numpy code in column major. qsym = np.ascontiguousarray(qsym.T) - return _impl.quat_distance(q1, q2, qsym) + return transforms_c_api.quat_distance(q1, q2, qsym) diff --git a/hexrd/file_table.tsv b/hexrd/file_table.tsv index ead815f99..f39c2f787 100644 --- a/hexrd/file_table.tsv +++ b/hexrd/file_table.tsv @@ -44,7 +44,6 @@ hexrd/sampleOrientations/sampleRFZ.py hexrd/hedm/sampleOrientations/sampleRFZ.py hexrd/transforms/xf.py hexrd/core/transforms/xf.py hexrd/transforms/xfcapi.py hexrd/core/transforms/xfcapi.py hexrd/transforms/__init__.py hexrd/core/transforms/__init__.py -hexrd/transforms/old_xfcapi.py hexrd/core/transforms/old_xfcapi.py hexrd/transforms/new_capi/xf_new_capi.py hexrd/core/transforms/new_capi/xf_new_capi.py hexrd/transforms/new_capi/reference.py hexrd/core/transforms/new_capi/reference.py hexrd/extensions/__init__.py hexrd/core/extensions/__init__.py @@ -278,7 +277,6 @@ hexrd/resources/tardis_reference_config.yml hexrd/core/resources/tardis_referenc hexrd/resources/window_materials.h5 hexrd/core/resources/window_materials.h5 hexrd/transforms/cpp_sublibrary/Makefile hexrd/core/transforms/cpp_sublibrary/Makefile hexrd/transforms/cpp_sublibrary/src/inverse_distortion.cpp hexrd/core/transforms/cpp_sublibrary/src/inverse_distortion.cpp -hexrd/transforms/debug_helpers.h hexrd/core/transforms/debug_helpers.h hexrd/transforms/Makefile hexrd/core/transforms/Makefile hexrd/transforms/new_capi/angles_to_dvec.c hexrd/core/transforms/new_capi/angles_to_dvec.c hexrd/transforms/new_capi/angles_to_gvec.c hexrd/core/transforms/new_capi/angles_to_gvec.c @@ -467,7 +465,6 @@ hexrd\core\rotations.py hexrd\core\rotations.py hexrd\core\transforms\__init__.py hexrd\core\transforms\__init__.py hexrd\core\transforms\cpp_sublibrary\Makefile hexrd\core\transforms\cpp_sublibrary\Makefile hexrd\core\transforms\cpp_sublibrary\src\inverse_distortion.cpp hexrd\core\transforms\cpp_sublibrary\src\inverse_distortion.cpp -hexrd\core\transforms\debug_helpers.h hexrd\core\transforms\debug_helpers.h hexrd\core\transforms\Makefile hexrd\core\transforms\Makefile hexrd\core\transforms\new_capi\angles_to_dvec.c hexrd\core\transforms\new_capi\angles_to_dvec.c hexrd\core\transforms\new_capi\angles_to_gvec.c hexrd\core\transforms\new_capi\angles_to_gvec.c @@ -493,7 +490,6 @@ hexrd\core\transforms\new_capi\unit_row_vector.c hexrd\core\transforms\new_capi\ hexrd\core\transforms\new_capi\validate_angle_ranges.c hexrd\core\transforms\new_capi\validate_angle_ranges.c hexrd\core\transforms\new_capi\xf_new_capi.py hexrd\core\transforms\new_capi\xf_new_capi.py hexrd\core\transforms\new_capi\xy_to_gvec.c hexrd\core\transforms\new_capi\xy_to_gvec.c -hexrd\core\transforms\old_xfcapi.py hexrd\core\transforms\old_xfcapi.py hexrd\core\transforms\stdbool.h hexrd\core\transforms\stdbool.h hexrd\core\transforms\transforms_CAPI.c hexrd\core\transforms\transforms_CAPI.c hexrd\core\transforms\transforms_CAPI.h hexrd\core\transforms\transforms_CAPI.h diff --git a/setup.py b/setup.py index 8db4269eb..36800aa75 100644 --- a/setup.py +++ b/setup.py @@ -155,7 +155,7 @@ def get_cpp_extensions(): def get_new_xfcapi_extension_modules(): transforms_mod = Extension( - 'hexrd.core.extensions._new_transforms_capi', + 'hexrd.core.extensions.transforms_c_api', sources=['hexrd/core/transforms/new_capi/module.c'], include_dirs=[np_include_dir], extra_compile_args=compiler_flags, From 26ddb98ac7a559d1d72631d082a2ce195318ec74 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 14:37:51 -0500 Subject: [PATCH 21/28] Remove functions already in cpp_transforms --- .../core/transforms/new_capi/angles_to_dvec.c | 124 ------------------ .../core/transforms/new_capi/angles_to_gvec.c | 124 ------------------ .../transforms/new_capi/make_binary_rmat.c | 65 --------- .../transforms/new_capi/make_rmat_of_expmap.c | 79 ----------- hexrd/core/transforms/new_capi/module.c | 8 -- hexrd/file_table.tsv | 8 -- 6 files changed, 408 deletions(-) delete mode 100644 hexrd/core/transforms/new_capi/angles_to_dvec.c delete mode 100644 hexrd/core/transforms/new_capi/angles_to_gvec.c delete mode 100644 hexrd/core/transforms/new_capi/make_binary_rmat.c delete mode 100644 hexrd/core/transforms/new_capi/make_rmat_of_expmap.c diff --git a/hexrd/core/transforms/new_capi/angles_to_dvec.c b/hexrd/core/transforms/new_capi/angles_to_dvec.c deleted file mode 100644 index f1618c389..000000000 --- a/hexrd/core/transforms/new_capi/angles_to_dvec.c +++ /dev/null @@ -1,124 +0,0 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - -XRD_CFUNCTION void -angles_to_dvec(size_t nvecs, double * angs, - double * bHat_l, double * eHat_l, - double chi, double * rMat_c, - double * gVec_c) -{ - /* - * takes an angle spec (2*theta, eta, omega) for nvecs g-vectors and - * returns the unit d-vector components in the crystal frame - * - * For unit d-vector in the lab frame, spec rMat_c = Identity and - * overwrite the omega values with zeros - */ - size_t i, j, k, l; - double rMat_e[9], rMat_s[9], rMat_ctst[9]; - double gVec_e[3], gVec_l[3], gVec_c_tmp[3]; - - /* Need eta frame cob matrix (could omit for standard setting) */ - make_beam_rmat(bHat_l, eHat_l, rMat_e); - - /* make vector array */ - for (i=0; i -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - -XRD_PYTHON_WRAPPER const char *docstring_anglesToDVec = - "c module implementation of angles_to_dvec.\n" - "Please use the Python wrapper.\n"; - -XRD_PYTHON_WRAPPER PyObject * -python_anglesToDVec(PyObject * self, PyObject * args) -{ - /* - API interface in Python is: - angs, beam_vec=None, eta_vec=None, chi=None, rmat_c=None - - right now, defaults are handled by the wrappers. This C module function - expects angs to be already 2d, and all optional arguments to have the - defaults filled by the calling wrapper. - - TODO: handle defaults here? - */ - nah_array angs = { NULL, "angs", NAH_TYPE_DP_FP, { 3, NAH_DIM_ANY }}; - nah_array beam_vec = { NULL, "beam_vec", NAH_TYPE_DP_FP, { 3 }}; - nah_array eta_vec = { NULL, "eta_vec", NAH_TYPE_DP_FP, { 3 }}; - nah_array rmat_c = { NULL, "rmat_c", NAH_TYPE_DP_FP, { 3, 3 }}; - PyArrayObject *result = NULL; - double chi; - - if ( !PyArg_ParseTuple(args,"O&O&O&dO&", - nah_array_converter, &angs, - nah_array_converter, &beam_vec, - nah_array_converter, &eta_vec, - &chi, - nah_array_converter, &rmat_c)) - return NULL; - - /* Allocate C-style array for return data */ - result = (PyArrayObject*)PyArray_EMPTY(PyArray_NDIM(angs.pyarray), - PyArray_SHAPE(angs.pyarray), - NPY_DOUBLE, - 0); - - /* Call the actual function */ - angles_to_dvec(PyArray_DIM(angs.pyarray, 0), - (double *)PyArray_DATA(angs.pyarray), - (double *)PyArray_DATA(beam_vec.pyarray), - (double *)PyArray_DATA(eta_vec.pyarray), - chi, - (double *)PyArray_DATA(rmat_c.pyarray), - (double *)PyArray_DATA(result)); - - /* Build and return the nested data structure */ - return((PyObject*)result); -} - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/angles_to_gvec.c b/hexrd/core/transforms/new_capi/angles_to_gvec.c deleted file mode 100644 index cdd2edec8..000000000 --- a/hexrd/core/transforms/new_capi/angles_to_gvec.c +++ /dev/null @@ -1,124 +0,0 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - - -XRD_CFUNCTION void -angles_to_gvec(size_t nvecs, double * angs, - double * bHat_l, double * eHat_l, - double chi, double * rMat_c, - double * gVec_c) -{ - /* - * takes an angle spec (2*theta, eta, omega) for nvecs g-vectors and - * returns the unit g-vector components in the crystal frame - * - * For unit g-vector in the lab frame, spec rMat_c = Identity and - * overwrite the omega values with zeros - */ - size_t i, j, k, l; - double rMat_e[9], rMat_s[9], rMat_ctst[9]; - double gVec_e[3], gVec_l[3], gVec_c_tmp[3]; - - /* Need eta frame cob matrix (could omit for standard setting) */ - make_beam_rmat(bHat_l, eHat_l, rMat_e); - - /* make vector array */ - for (i=0; i -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - -XRD_PYTHON_WRAPPER const char *docstring_anglesToGVec = - "c module implementation of angles_to_gvec.\n" - "Please use the Python wrapper.\n"; - -XRD_PYTHON_WRAPPER PyObject * -python_anglesToGVec(PyObject * self, PyObject * args) -{ - /* - API interface in Python is: - angs, beam_vec=None, eta_vec=None, chi=None, rmat_c=None - - currently, defaults are handled by the wrapper, so the C-module - is always called with all arguments. Wrapper always guarantees - that the input array is 2d. - TODO: handle defaults here? - */ - nah_array angs = { NULL, "angs", NAH_TYPE_DP_FP, { 3, NAH_DIM_ANY }}; - nah_array beam_vec = { NULL, "beam_vec", NAH_TYPE_DP_FP, { 3 }}; - nah_array eta_vec = { NULL, "eta_vec", NAH_TYPE_DP_FP, { 3 }}; - nah_array rmat_c = { NULL, "rmat_c", NAH_TYPE_DP_FP, { 3, 3 }}; - PyArrayObject *result = NULL; - double chi; - - if ( !PyArg_ParseTuple(args,"O&O&O&dO&", - nah_array_converter, &angs, - nah_array_converter, &beam_vec, - nah_array_converter, &eta_vec, - &chi, - nah_array_converter, &rmat_c)) - return NULL; - - /* Allocate C-style array for return data */ - result = (PyArrayObject*)PyArray_EMPTY(PyArray_NDIM(angs.pyarray), - PyArray_SHAPE(angs.pyarray), - NPY_DOUBLE, - 0); - - /* Call the actual function */ - angles_to_gvec(PyArray_DIM(angs.pyarray, 0), - (double *)PyArray_DATA(angs.pyarray), - (double *)PyArray_DATA(beam_vec.pyarray), - (double *)PyArray_DATA(eta_vec.pyarray), - chi, - (double *)PyArray_DATA(rmat_c.pyarray), - (double *)PyArray_DATA(result)); - - /* Build and return the nested data structure */ - return((PyObject*)result); -} - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/make_binary_rmat.c b/hexrd/core/transforms/new_capi/make_binary_rmat.c deleted file mode 100644 index cc50430fb..000000000 --- a/hexrd/core/transforms/new_capi/make_binary_rmat.c +++ /dev/null @@ -1,65 +0,0 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -#endif - - -XRD_CFUNCTION void -make_binary_rmat(const double *aPtr, double * restrict rPtr) -{ - int i, j; - - for (i=0; i<3; i++) { - for (j=0; j<3; j++) { - rPtr[3*i+j] = 2.0*aPtr[i]*aPtr[j]; - } - rPtr[3*i+i] -= 1.0; - } -} - - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# include "ndargs_helper.h" -# endif /* XRD_SINGLE_COMPILE_UNIT */ - -XRD_PYTHON_WRAPPER const char *docstring_makeBinaryRotMat = - "c module implementation of make_binary_rmat.\n" - "Please use the Python wrapper.\n"; - -XRD_PYTHON_WRAPPER PyObject * -python_makeBinaryRotMat(PyObject * self, PyObject * args) -{ - nah_array axis = { NULL, "axis", NAH_TYPE_DP_FP, { 3 }}; - PyArrayObject *result = NULL; - npy_intp dims[2] = { 3, 3 }; - - /* Parse arguments */ - if (!PyArg_ParseTuple(args, "O&", - nah_array_converter, &axis)) - return NULL; - - /* Allocate the result matrix with appropriate dimensions and type */ - result = (PyArrayObject*)PyArray_EMPTY(2, dims, NPY_DOUBLE, 0); - if (NULL == result) - goto fail_alloc; - - /* Call the actual function */ - make_binary_rmat((double *)PyArray_DATA(axis.pyarray), - (double *)PyArray_DATA(result)); - - return (PyObject*)result; - - fail_alloc: - Py_XDECREF(result); - - return PyErr_NoMemory(); -} - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/make_rmat_of_expmap.c b/hexrd/core/transforms/new_capi/make_rmat_of_expmap.c deleted file mode 100644 index e21209d27..000000000 --- a/hexrd/core/transforms/new_capi/make_rmat_of_expmap.c +++ /dev/null @@ -1,79 +0,0 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - - -XRD_CFUNCTION void -make_rmat_of_expmap(double *ePtr, double *rPtr) -{ - double e0 = ePtr[0]; - double e1 = ePtr[1]; - double e2 = ePtr[2]; - double sqr_phi = e0*e0 + e1*e1 + e2*e2; - - if ( sqr_phi > epsf ) { - double phi = sqrt(sqr_phi); - double s = sin(phi)/phi; - double c = (1.0-cos(phi))/sqr_phi; - - rPtr[0] = 1.0 - c*(e1*e1 + e2*e2); - rPtr[1] = -s*e2 + c*e0*e1; - rPtr[2] = s*e1 + c*e0*e2; - rPtr[3] = s*e2 + c*e1*e0; - rPtr[4] = 1.0 - c*(e2*e2 + e0*e0); - rPtr[5] = -s*e0 + c*e1*e2; - rPtr[6] = -s*e1 + c*e2*e0; - rPtr[7] = s*e0 + c*e2*e1; - rPtr[8] = 1.0 - c*(e0*e0 + e1*e1); - } else { - m33_set_identity(rPtr); - } -} - - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - -XRD_PYTHON_WRAPPER const char *docstring_makeRotMatOfExpMap = - "c module implementation of make_rmat_of_expmap.\n" - "Please use the Python wrapper.\n"; - -XRD_PYTHON_WRAPPER PyObject * -python_makeRotMatOfExpMap(PyObject * self, PyObject * args) -{ - nah_array exp_map = { NULL, "exp_map", NAH_TYPE_DP_FP, { 3 }}; - PyArrayObject *result = NULL; - npy_intp dims[2] = { 3, 3 }; - - /* Parse arguments */ - if (!PyArg_ParseTuple(args, "O&", - nah_array_converter, &exp_map)) - return NULL; - - /* Allocate the result matrix with appropriate dimensions and type */ - result = (PyArrayObject*)PyArray_EMPTY(2, dims, NPY_DOUBLE, 0); - if (NULL == result) - goto fail_alloc; - - /* Call the actual function */ - make_rmat_of_expmap(PyArray_DATA(exp_map.pyarray), - PyArray_DATA(result)); - - return (PyObject *)result; - - fail_alloc: - Py_XDECREF(result); - - return PyErr_NoMemory(); -} - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/module.c b/hexrd/core/transforms/new_capi/module.c index 692bf765f..dd47f3fc4 100644 --- a/hexrd/core/transforms/new_capi/module.c +++ b/hexrd/core/transforms/new_capi/module.c @@ -61,16 +61,12 @@ #include "ndargs_helper.h" #include "ndargs_helper.c" -#include "angles_to_gvec.c" -#include "angles_to_dvec.c" #include "gvec_to_xy.c" #include "xy_to_gvec.c" #include "oscill_angles_of_HKLs.c" #include "unit_row_vector.c" #include "make_detector_rmat.c" #include "make_sample_rmat.c" -#include "make_rmat_of_expmap.c" -#include "make_binary_rmat.c" #include "make_beam_rmat.c" #include "validate_angle_ranges.c" #include "rotate_vecs_about_axis.c" @@ -91,8 +87,6 @@ { STR(name), CONCAT(python_, name), METH_VARARGS, "" } static PyMethodDef _module_methods[] = { - EXPORT_METHOD(anglesToGVec), /* angles_to_gvec */ - EXPORT_METHOD(anglesToDVec), /* angles_to_dvec */ EXPORT_METHOD(gvecToDetectorXY), /* gvec_to_xy */ EXPORT_METHOD(gvecToDetectorXYArray), /* gvec_to_xy */ EXPORT_METHOD(detectorXYToGvec), /* xy_to_gvec */ @@ -100,9 +94,7 @@ static PyMethodDef _module_methods[] = { EXPORT_METHOD(unitRowVector), /* unit_vector */ EXPORT_METHOD(unitRowVectors), /* unit_vector */ EXPORT_METHOD(makeOscillRotMat), /* make_sample_rmat */ - EXPORT_METHOD(makeRotMatOfExpMap), /* make_rmat_of_expmap */ EXPORT_METHOD(makeDetectorRotMat), /* make_detector_rmat */ - EXPORT_METHOD(makeBinaryRotMat), /* make_binary_rmat */ EXPORT_METHOD(makeEtaFrameRotMat), /* make_beam_rmat */ EXPORT_METHOD(validateAngleRanges), /* validate_angle_ranges */ EXPORT_METHOD(rotate_vecs_about_axis), /* rotate_vecs_about_axis */ diff --git a/hexrd/file_table.tsv b/hexrd/file_table.tsv index f39c2f787..0ce858b5c 100644 --- a/hexrd/file_table.tsv +++ b/hexrd/file_table.tsv @@ -278,13 +278,9 @@ hexrd/resources/window_materials.h5 hexrd/core/resources/window_materials.h5 hexrd/transforms/cpp_sublibrary/Makefile hexrd/core/transforms/cpp_sublibrary/Makefile hexrd/transforms/cpp_sublibrary/src/inverse_distortion.cpp hexrd/core/transforms/cpp_sublibrary/src/inverse_distortion.cpp hexrd/transforms/Makefile hexrd/core/transforms/Makefile -hexrd/transforms/new_capi/angles_to_dvec.c hexrd/core/transforms/new_capi/angles_to_dvec.c -hexrd/transforms/new_capi/angles_to_gvec.c hexrd/core/transforms/new_capi/angles_to_gvec.c hexrd/transforms/new_capi/gvec_to_xy.c hexrd/core/transforms/new_capi/gvec_to_xy.c hexrd/transforms/new_capi/make_beam_rmat.c hexrd/core/transforms/new_capi/make_beam_rmat.c -hexrd/transforms/new_capi/make_binary_rmat.c hexrd/core/transforms/new_capi/make_binary_rmat.c hexrd/transforms/new_capi/make_detector_rmat.c hexrd/core/transforms/new_capi/make_detector_rmat.c -hexrd/transforms/new_capi/make_rmat_of_expmap.c hexrd/core/transforms/new_capi/make_rmat_of_expmap.c hexrd/transforms/new_capi/make_sample_rmat.c hexrd/core/transforms/new_capi/make_sample_rmat.c hexrd/transforms/new_capi/module.c hexrd/core/transforms/new_capi/module.c hexrd/transforms/new_capi/ndargs_helper.c hexrd/core/transforms/new_capi/ndargs_helper.c @@ -466,13 +462,9 @@ hexrd\core\transforms\__init__.py hexrd\core\transforms\__init__.py hexrd\core\transforms\cpp_sublibrary\Makefile hexrd\core\transforms\cpp_sublibrary\Makefile hexrd\core\transforms\cpp_sublibrary\src\inverse_distortion.cpp hexrd\core\transforms\cpp_sublibrary\src\inverse_distortion.cpp hexrd\core\transforms\Makefile hexrd\core\transforms\Makefile -hexrd\core\transforms\new_capi\angles_to_dvec.c hexrd\core\transforms\new_capi\angles_to_dvec.c -hexrd\core\transforms\new_capi\angles_to_gvec.c hexrd\core\transforms\new_capi\angles_to_gvec.c hexrd\core\transforms\new_capi\gvec_to_xy.c hexrd\core\transforms\new_capi\gvec_to_xy.c hexrd\core\transforms\new_capi\make_beam_rmat.c hexrd\core\transforms\new_capi\make_beam_rmat.c -hexrd\core\transforms\new_capi\make_binary_rmat.c hexrd\core\transforms\new_capi\make_binary_rmat.c hexrd\core\transforms\new_capi\make_detector_rmat.c hexrd\core\transforms\new_capi\make_detector_rmat.c -hexrd\core\transforms\new_capi\make_rmat_of_expmap.c hexrd\core\transforms\new_capi\make_rmat_of_expmap.c hexrd\core\transforms\new_capi\make_sample_rmat.c hexrd\core\transforms\new_capi\make_sample_rmat.c hexrd\core\transforms\new_capi\module.c hexrd\core\transforms\new_capi\module.c hexrd\core\transforms\new_capi\ndargs_helper.c hexrd\core\transforms\new_capi\ndargs_helper.c From cd922ae60b3536594dd555e9405e84d1a2534858 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 14:54:25 -0500 Subject: [PATCH 22/28] Cut very old xf.py --- hexrd/core/transforms/xf.py | 1191 ----------------------------------- 1 file changed, 1191 deletions(-) delete mode 100644 hexrd/core/transforms/xf.py diff --git a/hexrd/core/transforms/xf.py b/hexrd/core/transforms/xf.py deleted file mode 100644 index 10ae71fc4..000000000 --- a/hexrd/core/transforms/xf.py +++ /dev/null @@ -1,1191 +0,0 @@ -#! /usr/bin/env python -# ============================================================ -# Copyright (c) 2012, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# Written by Joel Bernier and others. -# LLNL-CODE-529294. -# All rights reserved. -# -# This file is part of HEXRD. For details on dowloading the source, -# see the file COPYING. -# -# Please also see the file LICENSE. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License (as published by the Free -# Software Foundation) version 2.1 dated February 1999. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this program (see file LICENSE); if not, write to -# the Free Software Foundation, Inc., 59 Temple Place, Suite 330, -# Boston, MA 02111-1307 USA or visit . -# ============================================================ - -import logging - -import numpy as np -import numba - -# np.seterr(invalid='ignore') - -import scipy.sparse as sparse - -from hexrd.core import matrixutil as mutil - - -# Added to not break people importing these methods -from hexrd.core.rotations import ( - mapAngle, - quatProductMatrix as quat_product_matrix, - arccosSafe, - angularDifference, -) -from hexrd.core.matrixutil import columnNorm, rowNorm - -logger = logging.getLogger(__name__) - -# ============================================================================= -# Module Data -# ============================================================================= - -epsf = np.finfo(float).eps # ~2.2e-16 -ten_epsf = 10 * epsf # ~2.2e-15 -sqrt_epsf = np.sqrt(epsf) # ~1.5e-8 - -periodDict = {'degrees': 360.0, 'radians': 2 * np.pi} -angularUnits = 'radians' # module-level angle units -d2r = np.pi / 180.0 - -# basis vectors -I3 = np.eye(3) # (3, 3) identity -Xl = np.array([[1.0, 0.0, 0.0]], order='C').T # X in the lab frame -Yl = np.array([[0.0, 1.0, 0.0]], order='C').T # Y in the lab frame -Zl = np.array([[0.0, 0.0, 1.0]], order='C').T # Z in the lab frame - -zeroVec = np.zeros(3, order='C') - -# reference beam direction and eta=0 ref in LAB FRAME for standard geometry -bVec_ref = -Zl -eta_ref = Xl - -# reference stretch -vInv_ref = np.array([[1.0, 1.0, 1.0, 0.0, 0.0, 0.0]], order='C').T - - -# ============================================================================= -# Functions -# ============================================================================= - - -def makeGVector(hkl, bMat): - """ - take a CRYSTAL RELATIVE B matrix onto a list of hkls to output unit - reciprocal lattice vectors (a.k.a. lattice plane normals) - - Required Arguments: - hkls -- (3, n) ndarray of n hstacked reciprocal lattice vector component - triplets - bMat -- (3, 3) ndarray representing the matirix taking reciprocal lattice - vectors to the crystal reference frame - - Output: - gVecs -- (3, n) ndarray of n unit reciprocal lattice vectors - (a.k.a. lattice plane normals) - - To Do: - * might benefit from some assert statements to catch improperly shaped - input. - """ - assert hkl.shape[0] == 3, 'hkl input must be (3, n)' - return unitVector(np.dot(bMat, hkl)) - - -@numba.njit(nogil=True, cache=True) -def _anglesToGVecHelper(angs, out): - # gVec_e = np.vstack([[np.cos(0.5*angs[:, 0]) * np.cos(angs[:, 1])], - # [np.cos(0.5*angs[:, 0]) * np.sin(angs[:, 1])], - # [np.sin(0.5*angs[:, 0])]]) - n = angs.shape[0] - for i in range(n): - ca0 = np.cos(0.5 * angs[i, 0]) - sa0 = np.sin(0.5 * angs[i, 0]) - ca1 = np.cos(angs[i, 1]) - sa1 = np.sin(angs[i, 1]) - out[i, 0] = ca0 * ca1 - out[i, 1] = ca0 * sa1 - out[i, 2] = sa0 - - -def anglesToGVec(angs, bHat_l, eHat_l, rMat_s=I3, rMat_c=I3): - """ - from 'eta' frame out to lab - (with handy kwargs to go to crystal or sample) - """ - rMat_e = makeEtaFrameRotMat(bHat_l, eHat_l) - gVec_e = np.empty((angs.shape[0], 3)) - _anglesToGVecHelper(angs, gVec_e) - mat = np.dot(rMat_c.T, np.dot(rMat_s.T, rMat_e)) - return np.dot(mat, gVec_e.T) - - -def gvecToDetectorXY( - gVec_c, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec_ref -): - """ - Takes a list of unit reciprocal lattice vectors in crystal frame to the - specified detector-relative frame. - - The following conditions must be satisfied: - - 1) the reciprocal lattice vector must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Parameters - ---------- - gVec_c : numpy.ndarray - (3, n) array of n reciprocal lattice vectors in the CRYSTAL FRAME. - rMat_d : numpy.ndarray - (3, 3) array, the COB taking DETECTOR FRAME components to LAB FRAME. - rMat_s : numpy.ndarray - (3, 3) array, the COB taking SAMPLE FRAME components to LAB FRAME. - rMat_c : numpy.ndarray - (3, 3) array, the COB taking CRYSTAL FRAME components to SAMPLE FRAME. - tVec_d : numpy.ndarray - (3, 1) array, the translation vector connecting LAB to DETECTOR. - tVec_s : numpy.ndarray - (3, 1) array, the translation vector connecting LAB to SAMPLE. - tVec_c : numpy.ndarray - (3, 1) array, the translation vector connecting SAMPLE to CRYSTAL. - beamVec : numpy.ndarray, optional - (3, 1) array, the vector pointing to the X-ray source in the LAB FRAME. - The default is bVec_ref. - - Returns - ------- - numpy.ndarray - (3, m)darray containing the intersections of m <= n diffracted beams - associated with gVecs. - - """ - ztol = epsf - - nVec_l = np.dot(rMat_d, Zl) # detector plane normal - bHat_l = unitVector(beamVec.reshape(3, 1)) # make sure beam vector is unit - P0_l = tVec_s + np.dot(rMat_s, tVec_c) # origin of CRYSTAL FRAME - P3_l = tVec_d # origin of DETECTOR FRAME - - # form unit reciprocal lattice vectors in lab frame (w/o translation) - gVec_l = np.dot(rMat_s, np.dot(rMat_c, unitVector(gVec_c))) - - # dot with beam vector (upstream direction) - bDot = np.dot(-bHat_l.T, gVec_l).squeeze() - - # see who can diffract; initialize output array with NaNs - canDiffract = np.atleast_1d(np.logical_and(bDot >= ztol, bDot <= 1.0 - ztol)) - npts = sum(canDiffract) - retval = np.nan * np.ones_like(gVec_l) - if np.any(canDiffract): - # subset of admissable reciprocal lattice vectors - adm_gVec_l = gVec_l[:, canDiffract].reshape(3, npts) - - # initialize diffracted beam vector array - dVec_l = np.empty((3, npts)) - for ipt in range(npts): - dVec_l[:, ipt] = np.dot( - makeBinaryRotMat(adm_gVec_l[:, ipt]), -bHat_l - ).squeeze() - - # ############################################################### - # displacement vector calculation - - # first check for non-instersections - denom = np.dot(nVec_l.T, dVec_l).flatten() - dzero = abs(denom) < ztol - denom[dzero] = 1.0 # mitigate divide-by-zero - cantIntersect = denom > 0.0 # index to dVec_l that can't hit det - - # displacement scaling (along dVec_l) - u = np.dot(nVec_l.T, P3_l - P0_l).flatten() / denom - - # filter out non-intersections, fill with NaNs - u[np.logical_or(dzero, cantIntersect)] = np.nan - - # diffracted beam points IN DETECTOR FRAME - P2_l = P0_l + np.tile(u, (3, 1)) * dVec_l - P2_d = np.dot(rMat_d.T, P2_l - tVec_d) - - # put feasible transformed gVecs into return array - retval[:, canDiffract] = P2_d - return retval[:2, :].T - - -def detectorXYToGvec( - xy_det, - rMat_d, - rMat_s, - tVec_d, - tVec_s, - tVec_c, - distortion=None, - beamVec=bVec_ref, - etaVec=eta_ref, - output_ref=False, -): - """ - Takes a list cartesian (x, y) pairs in the detector coordinates and - calculates the associated reciprocal lattice (G) vectors and - (bragg angle, azimuth) pairs with respect to the specified beam and - azimth (eta) reference directions. - - Parameters - ---------- - xy_det : numpy.ndarray - DESCRIPTION. - rMat_d : numpy.ndarray - (3, 3) array, the COB taking DETECTOR FRAME components to LAB FRAME. - rMat_s : numpy.ndarray - (3, 3) array, the COB taking SAMPLE FRAME components to LAB FRAME. - tVec_d : numpy.ndarray - (3, 1) array, the translation vector connecting LAB to DETECTOR. - tVec_s : numpy.ndarray - (3, 1) array, the translation vector connecting LAB to SAMPLE. - tVec_c : numpy.ndarray - (3, 1) array, the translation vector connecting SAMPLE to CRYSTAL. - distortion : class, optional - the distortion operator, if applicable. The default is None. - beamVec : numpy.ndarray, optional - (3, 1) array, the vector pointing to the X-ray source in the LAB FRAME. - The default is bVec_ref. - etaVec : numpy.ndarray, optional - (3, 1) array, the vector defining eta=0 in the LAB FRAME. - The default is eta_ref. - output_ref : bool, optional - if true, will output calculated angles in the LAB FRAME. - The default is False. - - Returns - ------- - tth_eta_ref : tuple, optional - if output_ref is True, (n, 2) ndarray containing the (tTh, eta) pairs - in the LAB REFERENCE FRAME associated with each (x, y) - tth_eta : tuple - (n, 2) ndarray containing the (tTh, eta) pairs associated with - each (x, y). - gVec_l : numpy.ndarray - (3, n) ndarray containing the associated G vector directions in the - LAB FRAME associated with gVecs - """ - - npts = len(xy_det) # number of input (x, y) pairs - - # force unit vectors for beam vector and ref eta - bHat_l = unitVector(beamVec.reshape(3, 1)) - eHat_l = unitVector(etaVec.reshape(3, 1)) - - if distortion is not None: - xy_det = distortion.apply(xy_det) - - # form in-plane vectors for detector points list in DETECTOR FRAME - P2_d = np.hstack([np.atleast_2d(xy_det), np.zeros((npts, 1))]).T - - # in LAB FRAME - P2_l = np.dot(rMat_d, P2_d) + tVec_d - P0_l = tVec_s + np.dot(rMat_s, tVec_c) # origin of CRYSTAL FRAME - - # diffraction unit vector components in LAB FRAME - dHat_l = unitVector(P2_l - P0_l) - - # ############################################################### - # generate output - - # DEBUGGING - assert ( - abs(np.dot(bHat_l.T, eHat_l)) < 1.0 - sqrt_epsf - ), "eta ref and beam cannot be parallel!" - - rMat_e = makeEtaFrameRotMat(bHat_l, eHat_l) - dHat_e = np.dot(rMat_e.T, dHat_l) - - tTh = np.arccos(np.dot(bHat_l.T, dHat_l)).flatten() - eta = np.arctan2(dHat_e[1, :], dHat_e[0, :]).flatten() - - # angles for reference frame - dHat_ref_l = unitVector(P2_l) - dHat_ref_e = np.dot(rMat_e.T, dHat_ref_l) - tTh_ref = np.arccos(np.dot(bHat_l.T, unitVector(P2_l))).flatten() - eta_ref = np.arctan2(dHat_ref_e[1, :], dHat_ref_e[0, :]).flatten() - - # get G-vectors by rotating d by 90-theta about b x d - # (numpy 'cross' works on row vectors) - n_g = unitVector(np.cross(bHat_l.T, dHat_l.T).T) - - gVec_l = rotate_vecs_about_axis(0.5 * (np.pi - tTh), n_g, dHat_l) - - if output_ref: - return (tTh_ref, eta_ref), (tTh, eta), gVec_l - return (tTh, eta), gVec_l - - -def oscillAnglesOfHKLs( - hkls, - chi, - rMat_c, - bMat, - wavelength, - vInv=vInv_ref, - beamVec=bVec_ref, - etaVec=eta_ref, -): - """ - - - Parameters - ---------- - hkls : numpy.ndarray - (3, n_) array of reciprocal lattice vector components. - chi : scalar - the inclination angle of the SAMPLE FRAME about the LAB X. - rMat_c : numpy.ndarray - (3, 3) array, the COB taking CRYSTAL FRAME components to SAMPLE FRAME. - bMat : numpy.ndarray - (3, 3) array, the COB matrix taking RECIPROCAL FRAME components to the - CRYSTAL FRAME. - wavelength : scalar - the X-ray wavelength in Ångstroms. - vInv : numpy.ndarray, optional - (6, 1) array representing the components of the inverse stretch tensor - in the SAMPLE FRAME. The default is vInv_ref. - beamVec : numpy.ndarray, optional - (3, 1) array, the vector pointing to the X-ray source in the LAB FRAME. - The default is bVec_ref. - etaVec : numpy.ndarray, optional - (3, 1) array, the vector defining eta=0 in the LAB FRAME. - The default is eta_ref. - - Returns - ------- - retval : tuple - Two (3, n) numpy.ndarrays representing the pair of feasible - (tTh, eta, ome) solutions for the n input hkls. - - Notes - ----- - The reciprocal lattice vector, G, will satisfy the the Bragg condition - when: - - b.T * G / ||G|| = -sin(theta) - - where b is the incident beam direction (k_i) and theta is the Bragg - angle consistent with G and the specified wavelength. The components of - G in the lab frame in this case are obtained using the crystal - orientation, Rc, and the single-parameter oscillation matrix, Rs(ome): - - Rs(ome) * Rc * G / ||G|| - - The equation above can be rearranged to yeild an expression of the form: - - a*sin(ome) + b*cos(ome) = c - - which is solved using the relation: - - a*sin(x) + b*cos(x) = sqrt(a**2 + b**2) * sin(x + alpha) - - --> sin(x + alpha) = c / sqrt(a**2 + b**2) - - where: - - alpha = arctan2(b, a) - - The solutions are: - - / - | arcsin(c / sqrt(a**2 + b**2)) - alpha - x = < - | pi - arcsin(c / sqrt(a**2 + b**2)) - alpha - \\ - - There is a double root in the case the reflection is tangent to the - Debye-Scherrer cone (c**2 = a**2 + b**2), and no solution if the - Laue condition cannot be satisfied (filled with NaNs in the results - array here) - """ - # reciprocal lattice vectors in CRYSTAL frame - gVec_c = np.dot(bMat, hkls) - # stretch tensor in SAMPLE frame - vMat_s = mutil.vecMVToSymm(vInv) - # reciprocal lattice vectors in SAMPLE frame - gVec_s = np.dot(vMat_s, np.dot(rMat_c, gVec_c)) - # unit reciprocal lattice vectors in SAMPLE frame - gHat_s = unitVector(gVec_s) - # unit reciprocal lattice vectors in CRYSTAL frame - gHat_c = np.dot(rMat_c.T, gHat_s) - # make sure beam direction is a unit vector - bHat_l = unitVector(beamVec.reshape(3, 1)) - # make sure eta = 0 direction is a unit vector - eHat_l = unitVector(etaVec.reshape(3, 1)) - - # sin of the Bragg angle assoc. with wavelength - sintht = 0.5 * wavelength * columnNorm(gVec_s) - - # sin and cos of the oscillation axis tilt - cchi = np.cos(chi) - schi = np.sin(chi) - - # coefficients for harmonic equation - a = ( - gHat_s[2, :] * bHat_l[0] - + schi * gHat_s[0, :] * bHat_l[1] - - cchi * gHat_s[0, :] * bHat_l[2] - ) - b = ( - gHat_s[0, :] * bHat_l[0] - - schi * gHat_s[2, :] * bHat_l[1] - + cchi * gHat_s[2, :] * bHat_l[2] - ) - c = -sintht - cchi * gHat_s[1, :] * bHat_l[1] - schi * gHat_s[1, :] * bHat_l[2] - - # should all be 1-d: a = a.flatten(); b = b.flatten(); c = c.flatten() - - # form solution - abMag = np.sqrt(a * a + b * b) - assert np.all(abMag > 0), "Beam vector specification is infealible!" - phaseAng = np.arctan2(b, a) - rhs = c / abMag - rhs[abs(rhs) > 1.0] = np.nan - rhsAng = np.arcsin(rhs) # will give NaN for abs(rhs) > 1. + 0.5*epsf - - # write ome angle output arrays (NaNs persist here) - ome0 = rhsAng - phaseAng - ome1 = np.pi - rhsAng - phaseAng - - goodOnes_s = -np.isnan(ome0) - - # DEBUGGING - assert np.all( - np.isnan(ome0) == np.isnan(ome1) - ), "infeasible hkls do not match for ome0, ome1!" - - # do etas -- ONLY COMPUTE IN CASE CONSISTENT REFERENCE COORDINATES - if abs(np.dot(bHat_l.T, eHat_l)) < 1.0 - sqrt_epsf and np.any(goodOnes_s): - eta0 = np.nan * np.ones_like(ome0) - eta1 = np.nan * np.ones_like(ome1) - - # make eta basis COB with beam antiparallel with Z - rMat_e = makeEtaFrameRotMat(bHat_l, eHat_l) - - goodOnes = np.tile(goodOnes_s, (1, 2)).flatten() - - numGood_s = sum(goodOnes_s) - numGood = 2 * numGood_s - tmp_eta = np.empty(numGood) - tmp_gvec = np.tile(gHat_c, (1, 2))[:, goodOnes] - allome = np.hstack([ome0, ome1]) - - for i in range(numGood): - rMat_s = makeOscillRotMat([chi, allome[goodOnes][i]]) - gVec_e = np.dot( - rMat_e.T, - np.dot(rMat_s, np.dot(rMat_c, tmp_gvec[:, i].reshape(3, 1))), - ) - tmp_eta[i] = np.arctan2(gVec_e[1], gVec_e[0]) - eta0[goodOnes_s] = tmp_eta[:numGood_s] - eta1[goodOnes_s] = tmp_eta[numGood_s:] - - # make assoc tTh array - tTh = 2.0 * np.arcsin(sintht).flatten() - tTh0 = tTh - tTh0[-goodOnes_s] = np.nan - retval = ( - np.vstack([tTh0.flatten(), eta0.flatten(), ome0.flatten()]), - np.vstack([tTh0.flatten(), eta1.flatten(), ome1.flatten()]), - ) - else: - retval = (ome0.flatten(), ome1.flatten()) - return retval - - -def polarRebin( - thisFrame, - npdiv=2, - mmPerPixel=(0.2, 0.2), - convertToTTh=False, - rMat_d=I3, - tVec_d=np.r_[0.0, 0.0, -1000.0], - beamVec=bVec_ref, - etaVec=eta_ref, - rhoRange=np.r_[20, 200], - numRho=1000, - etaRange=(d2r * np.r_[-5, 355]), - numEta=36, - verbose=True, - log=None, -): - """ - Performs polar rebinning of an input image. - - Parameters - ---------- - thisFrame : array_like - An (n, m) array representing an image. - npdiv : int, optional - The number of pixel subdivision. The default is 2. - mmPerPixel : array_like, optional - A 2-element sequence with the pixel pitch in mm for the row and column - dimensions, respectively. The default is (0.2, 0.2). - convertToTTh : bool, optional - If true, output abcissa is in angular (two-theta) coordinates. - The default is False. - rMat_d : numpy.ndarray, optional - (3, 3) array, the COB taking DETECTOR FRAME components to LAB FRAME. - The default is I3. - tVec_d : numpy.ndarray, optional - (3, 1) array, the translation vector connecting LAB to DETECTOR. - The default is np.r_[0., 0., -1000.]. - beamVec : TYPE, optional - DESCRIPTION. The default is bVec_ref. - etaVec : TYPE, optional - DESCRIPTION. The default is eta_ref. - rhoRange : array_like, optional - the min/max limits of the rebinning region in pixels. - The default is np.r_[20, 200]. - numRho : int, optional - the number of bins to use in the radial dimension. The default is 1000. - etaRange : array_like, optional - A 2-element sequence describing the min/max azimuthal angles in - radians. The default is (-0.08726646259971647, 6.19591884457987). - numEta : int, optional - The number of azimuthal bins. The default is 36. - verbose : bool, optional - DESCRIPTION. The default is True. - log : TYPE, optional - DESCRIPTION. The default is None. - - Raises - ------ - RuntimeError - DESCRIPTION. - - Returns - ------- - polImg : TYPE - DESCRIPTION. - - """ - """ - Caking algorithm - - INPUTS - - thisFrame - npdiv=2, pixel subdivision (n x n) to determine bin membership - rhoRange=[100, 1000] - radial range in pixels - numRho=1200 - number of radial bins - etaRange=np.pi*np.r_[-5, 355]/180. -- range of eta - numEta=36 - number of eta subdivisions - ROI=None - region of interest (four vector) - corrected=False - uses 2-theta instead of rho - verbose=True, - - """ - - startEta = etaRange[0] - stopEta = etaRange[1] - - startRho = rhoRange[0] - stopRho = rhoRange[1] - - subPixArea = 1 / float(npdiv) ** 2 # areal rescaling for subpixel intensities - - # MASTER COORDINATES - # - in pixel indices, UPPER LEFT PIXEL is [0, 0] --> (row, col) - # - in fractional pixels, UPPER LEFT CORNER is [-0.5, -0.5] --> (row, col) - # - in cartesian frame, the LOWER LEFT CORNER is [0, 0] --> (col, row) - x = thisFrame[0, :, :].flatten() - y = thisFrame[1, :, :].flatten() - roiData = thisFrame[2, :, :].flatten() - - # need rhos (or tThs) and etas) - if convertToTTh: - dAngs = detectorXYToGvec( - np.vstack([x, y]).T, - rMat_d, - I3, - tVec_d, - zeroVec, - zeroVec, - beamVec=beamVec, - etaVec=etaVec, - ) - rho = dAngs[0][0] # this is tTh now - eta = dAngs[0][1] - else: - # in here, we are vanilla cartesian - rho = np.sqrt(x * x + y * y) - eta = np.arctan2(y, x) - eta = mapAngle(eta, [startEta, 2 * np.pi + startEta], units='radians') - - # MAKE POLAR BIN CENTER ARRAY - deltaEta = (stopEta - startEta) / float(numEta) - deltaRho = (stopRho - startRho) / float(numRho) - - rowEta = startEta + deltaEta * (np.arange(numEta) + 0.5) - colRho = startRho + deltaRho * (np.arange(numRho) + 0.5) - - # initialize output dictionary - polImg = {} - polImg['radius'] = colRho - polImg['azimuth'] = rowEta - polImg['intensity'] = np.zeros((numEta, numRho)) - polImg['deltaRho'] = deltaRho - - if verbose: - msg = "INFO: Masking pixels\n" - if log: - log.write(msg) - else: - logger.info(msg) - - rhoI = startRho - 10 * deltaRho - rhoF = stopRho + 10 * deltaRho - inAnnulus = np.where((rho >= rhoI) & (rho <= rhoF))[0] - for i in range(numEta): - if verbose: - msg = f"INFO: Processing sector {i + 1} of {numEta}\n" - if log: - log.write(msg) - else: - logger.info(msg) - - # import pdb;pdb.set_trace() - etaI1 = rowEta[i] - 10.5 * deltaEta - etaF1 = rowEta[i] + 10.5 * deltaEta - - tmpEta = eta[inAnnulus] - inSector = np.where((tmpEta >= etaI1) & (tmpEta <= etaF1))[0] - - nptsIn = len(inSector) - - tmpX = x[inAnnulus[inSector]] - tmpY = y[inAnnulus[inSector]] - tmpI = roiData[inAnnulus[inSector]] - - # subdivide pixels - # - note that these are in fractional pixel coordinates (centered) - # - must convert to working units (see 'self.pixelPitchUnits') - subCrds = (np.arange(npdiv) + 0.5) / npdiv - - intX, intY = np.meshgrid(subCrds, subCrds) - - intX = np.tile(intX.flatten(), (nptsIn, 1)).T.flatten() - intY = np.tile(intY.flatten(), (nptsIn, 1)).T.flatten() - - # expand coords using pixel subdivision - tmpX = np.tile(tmpX, (npdiv**2, 1)).flatten() + (intX - 0.5) * mmPerPixel[0] - tmpY = np.tile(tmpY, (npdiv**2, 1)).flatten() + (intY - 0.5) * mmPerPixel[1] - tmpI = np.tile(tmpI, (npdiv**2, 1)).flatten() / subPixArea - - if convertToTTh: - dAngs = detectorXYToGvec( - np.vstack([tmpX, tmpY]).T, - rMat_d, - I3, - tVec_d, - zeroVec, - zeroVec, - beamVec=beamVec, - etaVec=etaVec, - ) - tmpRho = dAngs[0][0] # this is tTh now - tmpEta = dAngs[0][1] - else: - tmpRho = np.sqrt(tmpX * tmpX + tmpY * tmpY) - tmpEta = np.arctan2(tmpY, tmpX) - tmpEta = mapAngle(tmpEta, [startEta, 2 * np.pi + startEta], units='radians') - - etaI2 = rowEta[i] - 0.5 * deltaEta - etaF2 = rowEta[i] + 0.5 * deltaEta - - inSector2 = ((tmpRho >= startRho) & (tmpRho <= stopRho)) & ( - (tmpEta >= etaI2) & (tmpEta <= etaF2) - ) - - tmpRho = tmpRho[inSector2] - tmpI = tmpI[inSector2] - - binId = np.floor((tmpRho - startRho) / deltaRho) - nSubpixelsIn = len(binId) - - if nSubpixelsIn > 0: - tmpI = sparse.csc_matrix( - (tmpI, (binId, np.arange(nSubpixelsIn))), - shape=(numRho, nSubpixelsIn), - ) - binId = sparse.csc_matrix( - (np.ones(nSubpixelsIn), (binId, np.arange(nSubpixelsIn))), - shape=(numRho, nSubpixelsIn), - ) - - # Normalized contribution to the ith sector's radial bins - binIdSum = np.asarray(binId.sum(1)).flatten() - whereNZ = np.asarray(np.not_equal(polImg['intensity'][i, :], binIdSum)) - polImg['intensity'][i, whereNZ] = ( - np.asarray(tmpI.sum(1))[whereNZ].flatten() / binIdSum[whereNZ] - ) - return polImg - - -# ============================================================================= -# Utility Functions -# ============================================================================= - - -def _z_project(x, y): - return np.cos(x) * np.sin(y) - np.sin(x) * np.cos(y) - - -def reg_grid_indices(edges, points_1d): - """ - get indices in a 1-d regular grid. - - edges are just that: - - point: x (2.5) - | - edges: |1 |2 |3 |4 |5 - ------------------------- - indices: | 0 | 1 | 2 | 3 | - ------------------------- - - above the deltas are + and the index for the point is 1 - - point: x (2.5) - | - edges: |5 |4 |3 |2 |1 - ------------------------- - indices: | 0 | 1 | 2 | 3 | - ------------------------- - - here the deltas are - and the index for the point is 2 - - * can handle grids with +/- deltas - * be careful when using with a cyclical angular array! edges and points - must be mapped to the same branch cut, and - abs(edges[0] - edges[-1]) = 2*pi - """ - ztol = 1e-12 - - assert len(edges) >= 2, "must have at least 2 edges" - - points_1d = np.r_[points_1d].flatten() - delta = float(edges[1] - edges[0]) - - on_last_rhs = points_1d >= edges[-1] - ztol - points_1d[on_last_rhs] = points_1d[on_last_rhs] - ztol - - if delta > 0: - on_last_rhs = points_1d >= edges[-1] - ztol - points_1d[on_last_rhs] = points_1d[on_last_rhs] - ztol - idx = np.floor((points_1d - edges[0]) / delta) - elif delta < 0: - on_last_rhs = points_1d <= edges[-1] + ztol - points_1d[on_last_rhs] = points_1d[on_last_rhs] + ztol - idx = np.ceil((points_1d - edges[0]) / delta) - 1 - else: - raise RuntimeError("edges array gives delta of 0") - return np.array(idx, dtype=int) - - -@numba.njit(nogil=True, cache=True) -def _unitVectorSingle(a, b): - n = a.shape[0] - nrm = 0.0 - for i in range(n): - nrm += a[i] * a[i] - nrm = np.sqrt(nrm) - # prevent divide by zero - if nrm > epsf: - for i in range(n): - b[i] = a[i] / nrm - else: - for i in range(n): - b[i] = a[i] - - -@numba.njit(nogil=True, cache=True) -def _unitVectorMulti(a, b): - n = a.shape[0] - m = a.shape[1] - for j in range(m): - nrm = 0.0 - for i in range(n): - nrm += a[i, j] * a[i, j] - nrm = np.sqrt(nrm) - # prevent divide by zero - if nrm > epsf: - for i in range(n): - b[i, j] = a[i, j] / nrm - else: - for i in range(n): - b[i, j] = a[i, j] - - -def unitVector(a): - """ - normalize array of column vectors (hstacked, axis = 0) - """ - result = np.empty_like(a) - if a.ndim == 1: - _unitVectorSingle(a, result) - elif a.ndim == 2: - _unitVectorMulti(a, result) - else: - raise ValueError( - "incorrect arg shape; must be 1-d or 2-d, " + "yours is %d-d" % (a.ndim) - ) - return result - - -def makeDetectorRotMat(tiltAngles): - """ - Form the (3, 3) tilt rotations from the tilt angle list: - - tiltAngles = [gamma_Xl, gamma_Yl, gamma_Zl] in radians - """ - # form rMat_d from parameter list - cos_gX = np.cos(tiltAngles[0]) - sin_gX = np.sin(tiltAngles[0]) - - cos_gY = np.cos(tiltAngles[1]) - sin_gY = np.sin(tiltAngles[1]) - - cos_gZ = np.cos(tiltAngles[2]) - sin_gZ = np.sin(tiltAngles[2]) - - rotXl = np.array([[1.0, 0.0, 0.0], [0.0, cos_gX, -sin_gX], [0.0, sin_gX, cos_gX]]) - rotYl = np.array([[cos_gY, 0.0, sin_gY], [0.0, 1.0, 0.0], [-sin_gY, 0.0, cos_gY]]) - rotZl = np.array([[cos_gZ, -sin_gZ, 0.0], [sin_gZ, cos_gZ, 0.0], [0.0, 0.0, 1.0]]) - return np.dot(rotZl, np.dot(rotYl, rotXl)) - - -def makeOscillRotMat(oscillAngles): - """ - oscillAngles = [chi, ome] - """ - cchi = np.cos(oscillAngles[0]) - schi = np.sin(oscillAngles[0]) - - come = np.cos(oscillAngles[1]) - some = np.sin(oscillAngles[1]) - - rchi = np.array([[1.0, 0.0, 0.0], [0.0, cchi, -schi], [0.0, schi, cchi]]) - - rome = np.array([[come, 0.0, some], [0.0, 1.0, 0.0], [-some, 0.0, come]]) - - return np.dot(rchi, rome) - - -def makeRotMatOfExpMap(expMap): - """ - Calculate the rotation matrix of an exponential map. - - Parameters - ---------- - expMap : array_like - A 3-element sequence representing the exponential map n*phi. - - Returns - ------- - rMat : np.ndarray - The (3, 3) array representing the rotation matrix of the input - exponential map parameters. - """ - expMap = np.asarray(expMap).flatten() - phi = np.norm(expMap) - if phi > epsf: - wMat = np.array( - [ - [0.0, -expMap[2], expMap[1]], - [expMap[2], 0.0, -expMap[0]], - [-expMap[1], expMap[0], 0.0], - ] - ) - - rMat = ( - I3 - + (np.sin(phi) / phi) * wMat - + ((1.0 - np.cos(phi)) / (phi * phi)) * np.dot(wMat, wMat) - ) - else: - rMat = I3 - - return rMat - - -def makeBinaryRotMat(axis): - """ """ - n = np.asarray(axis).flatten() - assert len(n) == 3, 'Axis input does not have 3 components' - return 2 * np.dot(n.reshape(3, 1), n.reshape(1, 3)) - I3 - - -@numba.njit(nogil=True, cache=True) -def _makeEtaFrameRotMat(bHat_l, eHat_l, out): - # bHat_l and eHat_l CANNOT have 0 magnitude! - # must catch this case as well as colinear bHat_l/eHat_l elsewhere... - bHat_mag = np.sqrt(bHat_l[0] ** 2 + bHat_l[1] ** 2 + bHat_l[2] ** 2) - - # assign Ze as -bHat_l - for i in range(3): - out[i, 2] = -bHat_l[i] / bHat_mag - - # find Ye as Ze ^ eHat_l - Ye0 = out[1, 2] * eHat_l[2] - eHat_l[1] * out[2, 2] - Ye1 = out[2, 2] * eHat_l[0] - eHat_l[2] * out[0, 2] - Ye2 = out[0, 2] * eHat_l[1] - eHat_l[0] * out[1, 2] - - Ye_mag = np.sqrt(Ye0**2 + Ye1**2 + Ye2**2) - - out[0, 1] = Ye0 / Ye_mag - out[1, 1] = Ye1 / Ye_mag - out[2, 1] = Ye2 / Ye_mag - - # find Xe as Ye ^ Ze - out[0, 0] = out[1, 1] * out[2, 2] - out[1, 2] * out[2, 1] - out[1, 0] = out[2, 1] * out[0, 2] - out[2, 2] * out[0, 1] - out[2, 0] = out[0, 1] * out[1, 2] - out[0, 2] * out[1, 1] - - -def makeEtaFrameRotMat(bHat_l, eHat_l): - """ - make eta basis COB matrix with beam antiparallel with Z - - takes components from ETA frame to LAB - - **NO EXCEPTION HANDLING FOR COLINEAR ARGS IN NUMBA VERSION! - - ...put checks for non-zero magnitudes and non-colinearity in wrapper? - """ - result = np.empty((3, 3)) - _makeEtaFrameRotMat(bHat_l.reshape(3), eHat_l.reshape(3), result) - return result - - -def angles_in_range(angles, starts, stops, degrees=True): - """Determine whether angles lie in or out of specified ranges - - *angles* - a list/array of angles - *starts* - a list of range starts - *stops* - a list of range stops - - OPTIONAL ARGS: - *degrees* - [True] angles & ranges in degrees (or radians) - """ - TAU = 360.0 if degrees else 2 * np.pi - nw = len(starts) - na = len(angles) - in_range = np.zeros((na), dtype=bool) - for i in range(nw): - amin = starts[i] - amax = stops[i] - for j in range(na): - a = angles[j] - acheck = amin + np.mod(a - amin, TAU) - if acheck <= amax: - in_range[j] = True - - return in_range - - -def validateAngleRanges(angList, startAngs, stopAngs, ccw=True): - """ - A better way to go. find out if an angle is in the range - CCW or CW from start to stop - - There is, of course, an ambigutiy if the start and stop angle are - the same; we treat them as implying 2*pi having been mapped - """ - # Prefer ravel over flatten because flatten never skips the copy - angList = np.asarray(angList).ravel() # needs to have len - startAngs = np.asarray(startAngs).ravel() # needs to have len - stopAngs = np.asarray(stopAngs).ravel() # needs to have len - - n_ranges = len(startAngs) - assert len(stopAngs) == n_ranges, "length of min and max angular limits must match!" - - # to avoid warnings in >=, <= later down, mark nans; - # need these to trick output to False in the case of nan input - nan_mask = np.isnan(angList) - - reflInRange = np.zeros(angList.shape, dtype=bool) - - # bin length for chunking - binLen = np.pi / 2.0 - - # in plane vectors defining wedges - x0 = np.vstack([np.cos(startAngs), np.sin(startAngs)]) - x1 = np.vstack([np.cos(stopAngs), np.sin(stopAngs)]) - - # dot products - dp = np.sum(x0 * x1, axis=0) - if np.any(dp >= 1.0 - sqrt_epsf) and n_ranges > 1: - # ambiguous case - raise RuntimeError("At least one of your ranges is alread 360 degrees!") - elif dp[0] >= 1.0 - sqrt_epsf and n_ranges == 1: - # trivial case! - reflInRange = np.ones(angList.shape, dtype=bool) - reflInRange[nan_mask] = False - else: - # solve for arc lengths - # ...note: no zeros should have made it here - a = x0[0, :] * x1[1, :] - x0[1, :] * x1[0, :] - b = x0[0, :] * x1[0, :] + x0[1, :] * x1[1, :] - phi = np.arctan2(b, a) - - arclen = 0.5 * np.pi - phi # these are clockwise - cw_phis = arclen < 0 - arclen[cw_phis] = 2 * np.pi + arclen[cw_phis] # all positive (CW) now - if not ccw: - arclen = 2 * np.pi - arclen - - if sum(arclen) > 2 * np.pi: - raise RuntimeWarning("Specified angle ranges sum to > 360 degrees") - - # check that there are no more thandp = np.zeros(n_ranges) - for i in range(n_ranges): - # number or subranges using 'binLen' - numSubranges = int(np.ceil(arclen[i] / binLen)) - - # check remaider - binrem = np.remainder(arclen[i], binLen) - if binrem == 0: - finalBinLen = binLen - else: - finalBinLen = binrem - - # if clockwise, negate bin length - if not ccw: - binLen = -binLen - finalBinLen = -finalBinLen - - # Create sub ranges on the fly to avoid ambiguity in dot product - # for wedges >= 180 degrees - subRanges = np.array( - [startAngs[i] + binLen * j for j in range(numSubranges)] - + [startAngs[i] + binLen * (numSubranges - 1) + finalBinLen] - ) - - for k in range(numSubranges): - zStart = _z_project(angList, subRanges[k]) - zStop = _z_project(angList, subRanges[k + 1]) - if ccw: - zStart[nan_mask] = 999.0 - zStop[nan_mask] = -999.0 - reflInRange = reflInRange | np.logical_and(zStart <= 0, zStop >= 0) - else: - zStart[nan_mask] = -999.0 - zStop[nan_mask] = 999.0 - reflInRange = reflInRange | np.logical_and(zStart >= 0, zStop <= 0) - return reflInRange - - -def rotate_vecs_about_axis(angle, axis, vecs): - """ - Rotate vectors about an axis - - INPUTS - *angle* - array of angles (len == 1 or n) - *axis* - array of unit vectors (shape == (3, 1) or (3, n)) - *vec* - array of vectors to be rotated (shape = (3, n)) - - Quaternion formula: - if we split v into parallel and perpedicular components w.r.t. the - axis of quaternion q, - - v = a + n - - then the action of rotating the vector dot(R(q), v) becomes - - v_rot = (q0**2 - |q|**2)(a + n) + 2*dot(q, a)*q + 2*q0*cross(q, n) - - """ - angle = np.atleast_1d(angle) - - # quaternion components - q0 = np.cos(0.5 * angle) - q1 = np.sin(0.5 * angle) - qv = np.tile(q1, (3, 1)) * axis - - # component perpendicular to axes (inherits shape of vecs) - vp0 = ( - vecs[0, :] - - axis[0, :] * axis[0, :] * vecs[0, :] - - axis[0, :] * axis[1, :] * vecs[1, :] - - axis[0, :] * axis[2, :] * vecs[2, :] - ) - vp1 = ( - vecs[1, :] - - axis[1, :] * axis[1, :] * vecs[1, :] - - axis[1, :] * axis[0, :] * vecs[0, :] - - axis[1, :] * axis[2, :] * vecs[2, :] - ) - vp2 = ( - vecs[2, :] - - axis[2, :] * axis[2, :] * vecs[2, :] - - axis[2, :] * axis[0, :] * vecs[0, :] - - axis[2, :] * axis[1, :] * vecs[1, :] - ) - - # dot product with components along; cross product with components normal - qdota = ( - axis[0, :] * vecs[0, :] + axis[1, :] * vecs[1, :] + axis[2, :] * vecs[2, :] - ) * (axis[0, :] * qv[0, :] + axis[1, :] * qv[1, :] + axis[2, :] * qv[2, :]) - qcrossn = np.vstack( - [ - qv[1, :] * vp2 - qv[2, :] * vp1, - qv[2, :] * vp0 - qv[0, :] * vp2, - qv[0, :] * vp1 - qv[1, :] * vp0, - ] - ) - - # quaternion formula - v_rot = ( - np.tile(q0 * q0 - q1 * q1, (3, 1)) * vecs - + 2.0 * np.tile(qdota, (3, 1)) * qv - + 2.0 * np.tile(q0, (3, 1)) * qcrossn - ) - return v_rot - - -def quat_distance(q1, q2, qsym): - """ """ - # qsym from PlaneData objects are (4, nsym) - # convert symmetries to (4, 4) qprod matrices - nsym = qsym.shape[1] - rsym = np.zeros((nsym, 4, 4)) - for i in range(nsym): - rsym[i, :, :] = quat_product_matrix(qsym[:, i], mult='right') - - # inverse of q1 in matrix form - q1i = quat_product_matrix( - np.r_[1, -1, -1, -1] * np.atleast_1d(q1).flatten(), mult='right' - ) - - # Do R * Gc, store as vstacked equivalent quaternions (nsym, 4) - q2s = np.dot(rsym, q2) - - # Calculate the class of misorientations for full symmetrically equivalent - # q1 and q2: (4, ) * (4, nsym) - eqv_mis = np.dot(q1i, q2s.T) - - # find the largest scalar component - q0_max = np.argmax(abs(eqv_mis[0, :])) - - # compute the distance - qmin = eqv_mis[:, q0_max] - - return 2 * arccosSafe(qmin[0] * np.sign(qmin[0])) From ad17714e4c761c4cd8c93d52f54de03f16fcc6b0 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 14:57:30 -0500 Subject: [PATCH 23/28] use simple "static" keyword, remove stray calls to deleted funcs --- hexrd/core/transforms/new_capi/gvec_to_xy.c | 6 +-- .../core/transforms/new_capi/make_beam_rmat.c | 2 +- .../transforms/new_capi/make_detector_rmat.c | 2 +- .../transforms/new_capi/make_sample_rmat.c | 4 +- .../core/transforms/new_capi/ndargs_helper.c | 2 +- .../core/transforms/new_capi/ndargs_helper.h | 2 +- .../new_capi/oscill_angles_of_HKLs.c | 2 +- .../core/transforms/new_capi/quat_distance.c | 2 +- .../new_capi/rotate_vecs_about_axis.c | 2 +- .../new_capi/transforms_prototypes.h | 39 ++++++------------- .../transforms/new_capi/unit_row_vector.c | 4 +- .../new_capi/validate_angle_ranges.c | 2 +- hexrd/core/transforms/new_capi/xy_to_gvec.c | 2 +- 13 files changed, 28 insertions(+), 43 deletions(-) diff --git a/hexrd/core/transforms/new_capi/gvec_to_xy.c b/hexrd/core/transforms/new_capi/gvec_to_xy.c index 3c1e9d7af..f873902e5 100644 --- a/hexrd/core/transforms/new_capi/gvec_to_xy.c +++ b/hexrd/core/transforms/new_capi/gvec_to_xy.c @@ -326,7 +326,7 @@ ray_plane_intersect(const double *origin, const double *vect, } -XRD_CFUNCTION void +static void gvec_to_xy(size_t npts, const double *gVec_cs, const double *rMat_d, const double *rMat_ss, const double *rMat_c, const double *tVec_d, const double *tVec_s, const double *tVec_c, @@ -459,7 +459,7 @@ typedef void (*rays_to_detector_func) (const void *detector_data, double * restrict projected_xy, size_t projected_xy_count); /* experimental: */ -XRD_CFUNCTION void +static void gvec_to_xy_detector(size_t npts, const double *gVec_cs, const double *rMat_ss, const double *rMat_c, const double *tVec_s, const double *tVec_c, @@ -571,7 +571,7 @@ gvec_to_xy_detector(size_t npts, const double *gVec_cs, } } -XRD_CFUNCTION void +static void gvec_to_xy_vect(size_t npts, const double *gVec_cs, const double *rMat_d, const double *rMat_ss, const double *rMat_c, const double *tVec_d, const double *tVec_s, const double *tVec_c, diff --git a/hexrd/core/transforms/new_capi/make_beam_rmat.c b/hexrd/core/transforms/new_capi/make_beam_rmat.c index 1ea02870c..5d5ca0827 100644 --- a/hexrd/core/transforms/new_capi/make_beam_rmat.c +++ b/hexrd/core/transforms/new_capi/make_beam_rmat.c @@ -6,7 +6,7 @@ #endif -XRD_CFUNCTION int +static int make_beam_rmat(double * bPtr, double * ePtr, double * rPtr) { /* diff --git a/hexrd/core/transforms/new_capi/make_detector_rmat.c b/hexrd/core/transforms/new_capi/make_detector_rmat.c index 9be3bf096..4cb0f546a 100644 --- a/hexrd/core/transforms/new_capi/make_detector_rmat.c +++ b/hexrd/core/transforms/new_capi/make_detector_rmat.c @@ -5,7 +5,7 @@ #endif -XRD_CFUNCTION void +static void make_detector_rmat(double * tPtr, double * rPtr) { int i; diff --git a/hexrd/core/transforms/new_capi/make_sample_rmat.c b/hexrd/core/transforms/new_capi/make_sample_rmat.c index 3c7c2f501..a2f735c7a 100644 --- a/hexrd/core/transforms/new_capi/make_sample_rmat.c +++ b/hexrd/core/transforms/new_capi/make_sample_rmat.c @@ -5,7 +5,7 @@ # include "ndargs_helper.h" #endif -XRD_CFUNCTION void +static void make_sample_rmat(double chi, double ome, double *result_rmat) { double cchi, schi, come, some; @@ -28,7 +28,7 @@ make_sample_rmat(double chi, double ome, double *result_rmat) } -XRD_CFUNCTION void +static void make_sample_rmat_array(double chi, const double *ome_ptr, size_t ome_count, double *result_ptr) { double cchi,schi; diff --git a/hexrd/core/transforms/new_capi/ndargs_helper.c b/hexrd/core/transforms/new_capi/ndargs_helper.c index bad63929b..5792059ec 100644 --- a/hexrd/core/transforms/new_capi/ndargs_helper.c +++ b/hexrd/core/transforms/new_capi/ndargs_helper.c @@ -1,6 +1,6 @@ #include "ndargs_helper.h" -XRD_CFUNCTION int +static int nah_array_converter(PyObject *op, void *result) { nah_array *na = (nah_array*)result; diff --git a/hexrd/core/transforms/new_capi/ndargs_helper.h b/hexrd/core/transforms/new_capi/ndargs_helper.h index 6c7d867f3..4e43bcdb7 100644 --- a/hexrd/core/transforms/new_capi/ndargs_helper.h +++ b/hexrd/core/transforms/new_capi/ndargs_helper.h @@ -33,6 +33,6 @@ typedef struct nah_array_struct * returns 1 on success, 0 otherwise. When returning 0, a python * exception has been setup. */ -XRD_CFUNCTION int nah_array_converter(PyObject *op, void *result); +static int nah_array_converter(PyObject *op, void *result); #endif /* NDARRAY_ARGS_HELPER_H */ diff --git a/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c b/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c index 22fb34539..66004dfc0 100644 --- a/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c +++ b/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c @@ -6,7 +6,7 @@ #endif -XRD_CFUNCTION void +static void oscill_angles_of_HKLs(size_t npts, double * hkls, double chi, double * rMat_c, double * bMat, double wavelength, double * vInv_s, double * beamVec, double * etaVec, diff --git a/hexrd/core/transforms/new_capi/quat_distance.c b/hexrd/core/transforms/new_capi/quat_distance.c index 2d1b17753..c962c7ec6 100644 --- a/hexrd/core/transforms/new_capi/quat_distance.c +++ b/hexrd/core/transforms/new_capi/quat_distance.c @@ -10,7 +10,7 @@ #endif -XRD_CFUNCTION double +static double quat_distance(size_t nsym, double * q1, double * q2, double * qsym) { size_t i; diff --git a/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c b/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c index 8b8af3955..d6667241b 100644 --- a/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c +++ b/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c @@ -5,7 +5,7 @@ #endif -XRD_CFUNCTION void +static void rotate_vecs_about_axis(size_t na, double *angles, size_t nax, double *axes, size_t nv, double *vecs, diff --git a/hexrd/core/transforms/new_capi/transforms_prototypes.h b/hexrd/core/transforms/new_capi/transforms_prototypes.h index 82b6877e9..67b73040d 100644 --- a/hexrd/core/transforms/new_capi/transforms_prototypes.h +++ b/hexrd/core/transforms/new_capi/transforms_prototypes.h @@ -5,73 +5,58 @@ # include "transforms_types.h" #endif -XRD_CFUNCTION void -angles_to_gvec(size_t nvecs, double *angs, double *bHat_l, double *eHat_l, - double chi, double *rMat_c, double *gVec_c); - -XRD_CFUNCTION void -angles_to_dvec(size_t nvecs, double *angs, double *bHat_l, double *eHat_l, - double chi, double *rMat_c, double * gVec_c); - - #define GV2XY_SINGLE_RMAT_S 1 -XRD_CFUNCTION void +static void gvec_to_xy(size_t npts, const double *gVec_c_Ptr, const double *rMat_d_Ptr, const double *rMat_s_Ptr, const double *rMat_c_Ptr, const double *tVec_d_Ptr, const double *tVec_s_Ptr, const double *tVec_c_Ptr, const double *beamVec_Ptr, double * restrict result_Ptr, unsigned int flags); -XRD_CFUNCTION void +static void xy_to_gvec(size_t npts, double *xy, double *rMat_d, double *rMat_s, double *tVec_d, double *tVec_s, double *tVec_c, double *beamVec, double *etaVec, double *tTh, double *eta, double *gVec_l); -XRD_CFUNCTION void +static void oscill_angles_of_HKLs(size_t npts, double *hkls, double chi, double *rMat_c, double *bMat, double wavelength, double *vInv_s, double *beamVec, double *etaVec, double *oangs0, double *oangs1); /* this should probably be just a utility function in util... */ -XRD_CFUNCTION int +static int unit_row_vector(size_t n, double *cIn, double *cOut); -XRD_CFUNCTION void +static void unit_row_vectors(size_t m, size_t n, double *cIn, double *cOut); /* -XRD_CFUNCTION void +static void make_detector_rmat(double *tPtr, double *rPtr); */ -XRD_CFUNCTION void +static void make_sample_rmat(double chi, const double ome, double *result_rmat); -XRD_CFUNCTION void +static void make_sample_rmat_array(double chi, const double *ome_ptr, size_t ome_count, double *result_ptr); -XRD_CFUNCTION void -make_rmat_of_expmap(double *ePtr, double *rPtr); - -XRD_CFUNCTION void -make_binary_rmat(const double *aPtr, double * restrict rPtr); - #define TF_MAKE_BEAM_RMAT_ERR_BEAM_ZERO 1 #define TF_MAKE_BEAM_RMAT_ERR_COLLINEAR 2 -XRD_CFUNCTION int +static int make_beam_rmat(double *bPtr, double *ePtr, double *rPtr); /* aka make_eta_frame_rotmat */ -XRD_CFUNCTION void +static void validate_angle_ranges(size_t na, double *aPtr, size_t nr, double *minPtr, double *maxPtr, bool *rPtr, int ccw); -XRD_CFUNCTION void +static void rotate_vecs_about_axis(size_t na, double *angles, size_t nax, double *axes, size_t nv, double * vecs, double * rVecs); -XRD_CFUNCTION double +static double quat_distance(size_t nsym, double *q1, double *q2, double *qsym); diff --git a/hexrd/core/transforms/new_capi/unit_row_vector.c b/hexrd/core/transforms/new_capi/unit_row_vector.c index e402114be..420acb5a7 100644 --- a/hexrd/core/transforms/new_capi/unit_row_vector.c +++ b/hexrd/core/transforms/new_capi/unit_row_vector.c @@ -6,7 +6,7 @@ #endif -XRD_CFUNCTION int +static int unit_row_vector(size_t n, double * cIn, double * cOut) { size_t j; @@ -31,7 +31,7 @@ unit_row_vector(size_t n, double * cIn, double * cOut) } } -XRD_CFUNCTION void +static void unit_row_vectors(size_t m, size_t n, double *cIn, double *cOut) { size_t i, j; diff --git a/hexrd/core/transforms/new_capi/validate_angle_ranges.c b/hexrd/core/transforms/new_capi/validate_angle_ranges.c index c87ffd9ba..a6325dcf5 100644 --- a/hexrd/core/transforms/new_capi/validate_angle_ranges.c +++ b/hexrd/core/transforms/new_capi/validate_angle_ranges.c @@ -4,7 +4,7 @@ # include "transforms_prototypes.h" #endif -XRD_CFUNCTION void +static void validate_angle_ranges(size_t na, double *aPtr, size_t nr, double *minPtr, double *maxPtr, bool *rPtr, int ccw) { diff --git a/hexrd/core/transforms/new_capi/xy_to_gvec.c b/hexrd/core/transforms/new_capi/xy_to_gvec.c index 9b7d546f2..1b6ee1a37 100644 --- a/hexrd/core/transforms/new_capi/xy_to_gvec.c +++ b/hexrd/core/transforms/new_capi/xy_to_gvec.c @@ -6,7 +6,7 @@ #endif -XRD_CFUNCTION void +static void xy_to_gvec(size_t npts, double *xy, double *rMat_d, double *rMat_s, double *tVec_d, double *tVec_s, double *tVec_c, double *beamVec, double *etaVec, From 28fb5acff80f02076e6fbb6219c75b62279b7951 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Tue, 3 Feb 2026 14:58:58 -0500 Subject: [PATCH 24/28] Undo XRD_PYTHON_WRAPPER alias --- hexrd/core/transforms/new_capi/gvec_to_xy.c | 8 ++++---- hexrd/core/transforms/new_capi/make_beam_rmat.c | 4 ++-- hexrd/core/transforms/new_capi/make_detector_rmat.c | 4 ++-- hexrd/core/transforms/new_capi/make_sample_rmat.c | 4 ++-- hexrd/core/transforms/new_capi/module.c | 2 -- hexrd/core/transforms/new_capi/new_func.c | 2 +- hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c | 4 ++-- hexrd/core/transforms/new_capi/quat_distance.c | 4 ++-- hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c | 4 ++-- hexrd/core/transforms/new_capi/unit_row_vector.c | 8 ++++---- hexrd/core/transforms/new_capi/validate_angle_ranges.c | 4 ++-- hexrd/core/transforms/new_capi/xy_to_gvec.c | 4 ++-- 12 files changed, 25 insertions(+), 27 deletions(-) diff --git a/hexrd/core/transforms/new_capi/gvec_to_xy.c b/hexrd/core/transforms/new_capi/gvec_to_xy.c index f873902e5..08d95026b 100644 --- a/hexrd/core/transforms/new_capi/gvec_to_xy.c +++ b/hexrd/core/transforms/new_capi/gvec_to_xy.c @@ -601,11 +601,11 @@ gvec_to_xy_vect(size_t npts, const double *gVec_cs, # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_gvecToDetectorXY = +static const char *docstring_gvecToDetectorXY = "c module implementation of gvec_to_xy (single sample).\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER const char *docstring_gvecToDetectorXYArray = +static const char *docstring_gvecToDetectorXYArray = "c module implementation of gvec_to_xy (multi sample).\n" "Please use the Python wrapper.\n"; @@ -671,7 +671,7 @@ normalize_beam(double *in, double *work) (m, 2) ndarray containing the intersections of m <= n diffracted beams associated with gVecs */ -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_gvecToDetectorXY(PyObject * self, PyObject * args) { nah_array gVec_c = { NULL, "gvec_c", NAH_TYPE_DP_FP, { 3, NAH_DIM_ANY }}; @@ -756,7 +756,7 @@ python_gvecToDetectorXY(PyObject * self, PyObject * args) (m, 2) ndarray containing the intersections of m <= n diffracted beams associated with gVecs */ -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_gvecToDetectorXYArray(PyObject * self, PyObject * args) { nah_array gVec_c = { NULL, "gVec_c", NAH_TYPE_DP_FP, { 3, NAH_DIM_ANY }}; diff --git a/hexrd/core/transforms/new_capi/make_beam_rmat.c b/hexrd/core/transforms/new_capi/make_beam_rmat.c index 5d5ca0827..c5c696ef5 100644 --- a/hexrd/core/transforms/new_capi/make_beam_rmat.c +++ b/hexrd/core/transforms/new_capi/make_beam_rmat.c @@ -66,11 +66,11 @@ make_beam_rmat(double * bPtr, double * ePtr, double * rPtr) # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_makeEtaFrameRotMat = +static const char *docstring_makeEtaFrameRotMat = "c module implementation of make_beam_mat.\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_makeEtaFrameRotMat(PyObject * self, PyObject * args) { PyArrayObject *rMat=NULL; diff --git a/hexrd/core/transforms/new_capi/make_detector_rmat.c b/hexrd/core/transforms/new_capi/make_detector_rmat.c index 4cb0f546a..ed4ccfe32 100644 --- a/hexrd/core/transforms/new_capi/make_detector_rmat.c +++ b/hexrd/core/transforms/new_capi/make_detector_rmat.c @@ -37,11 +37,11 @@ make_detector_rmat(double * tPtr, double * rPtr) # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_makeDetectorRotMat = +static const char *docstring_makeDetectorRotMat = "c module implementation of makeDetectorRotMat.\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_makeDetectorRotMat(PyObject * self, PyObject * args) { PyArrayObject *tiltAngles, *rMat; diff --git a/hexrd/core/transforms/new_capi/make_sample_rmat.c b/hexrd/core/transforms/new_capi/make_sample_rmat.c index a2f735c7a..f3707b362 100644 --- a/hexrd/core/transforms/new_capi/make_sample_rmat.c +++ b/hexrd/core/transforms/new_capi/make_sample_rmat.c @@ -68,12 +68,12 @@ make_sample_rmat_array(double chi, const double *ome_ptr, size_t ome_count, doub # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_makeOscillRotMat = +static const char *docstring_makeOscillRotMat = "c module implementation of make_sample_rotmat.\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_makeOscillRotMat(PyObject * self, PyObject * args) { nah_array ome = { NULL, "ome", NAH_TYPE_DP_FP, { NAH_DIM_ANY }}; diff --git a/hexrd/core/transforms/new_capi/module.c b/hexrd/core/transforms/new_capi/module.c index dd47f3fc4..7e2b031ea 100644 --- a/hexrd/core/transforms/new_capi/module.c +++ b/hexrd/core/transforms/new_capi/module.c @@ -52,8 +52,6 @@ */ #define XRD_SINGLE_COMPILE_UNIT 1 #define XRD_INCLUDE_PYTHON_WRAPPERS 1 -#define XRD_CFUNCTION static -#define XRD_PYTHON_WRAPPER static #include "transforms_types.h" #include "transforms_utils.h" diff --git a/hexrd/core/transforms/new_capi/new_func.c b/hexrd/core/transforms/new_capi/new_func.c index 3f425b5f0..f7a92768a 100644 --- a/hexrd/core/transforms/new_capi/new_func.c +++ b/hexrd/core/transforms/new_capi/new_func.c @@ -14,7 +14,7 @@ # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *<>_docstring = +static const char *<>_docstring = "c module implementation of <>. Please use the Python wrapper.\n" diff --git a/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c b/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c index 66004dfc0..d89418d06 100644 --- a/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c +++ b/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c @@ -185,11 +185,11 @@ oscill_angles_of_HKLs(size_t npts, double * hkls, double chi, # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_oscillAnglesOfHKLs = +static const char *docstring_oscillAnglesOfHKLs = "c module implementation of solve_omega.\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_oscillAnglesOfHKLs(PyObject * self, PyObject * args) { nah_array hkls = { NULL, "hkls", NAH_TYPE_DP_FP, { 3, NAH_DIM_ANY }}; diff --git a/hexrd/core/transforms/new_capi/quat_distance.c b/hexrd/core/transforms/new_capi/quat_distance.c index c962c7ec6..f0501f7a5 100644 --- a/hexrd/core/transforms/new_capi/quat_distance.c +++ b/hexrd/core/transforms/new_capi/quat_distance.c @@ -57,11 +57,11 @@ quat_distance(size_t nsym, double * q1, double * q2, double * qsym) # include "ndargs_helper.h" # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_quat_distance = +static const char *docstring_quat_distance = "c module implementation of quat_distance.\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_quat_distance(PyObject * self, PyObject * args) { nah_array q1 = { NULL, "q1", NAH_TYPE_DP_FP, { 4 }}; diff --git a/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c b/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c index d6667241b..aad55b895 100644 --- a/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c +++ b/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c @@ -65,11 +65,11 @@ rotate_vecs_about_axis(size_t na, double *angles, # include "ndargs_helper.h" # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_rotate_vecs_about_axis = +static const char *docstring_rotate_vecs_about_axis = "c module implementation of rotate_vecs_about_axis.\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_rotate_vecs_about_axis(PyObject *self, PyObject *args) { /* API interface in Python is: diff --git a/hexrd/core/transforms/new_capi/unit_row_vector.c b/hexrd/core/transforms/new_capi/unit_row_vector.c index 420acb5a7..aeb543e04 100644 --- a/hexrd/core/transforms/new_capi/unit_row_vector.c +++ b/hexrd/core/transforms/new_capi/unit_row_vector.c @@ -65,15 +65,15 @@ unit_row_vectors(size_t m, size_t n, double *cIn, double *cOut) # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_unitRowVector = +static const char *docstring_unitRowVector = "c module implementation of unit_row_vector (one row).\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER const char *docstring_unitRowVectors = +static const char *docstring_unitRowVectors = "c module implementation of unit_row_vector (multiple rows).\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_unitRowVector(PyObject * self, PyObject * args) { PyArrayObject *aop_out = NULL; @@ -110,7 +110,7 @@ python_unitRowVector(PyObject * self, PyObject * args) } -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_unitRowVectors(PyObject *self, PyObject *args) { PyArrayObject *aop_out = NULL; diff --git a/hexrd/core/transforms/new_capi/validate_angle_ranges.c b/hexrd/core/transforms/new_capi/validate_angle_ranges.c index a6325dcf5..4a79cebe7 100644 --- a/hexrd/core/transforms/new_capi/validate_angle_ranges.c +++ b/hexrd/core/transforms/new_capi/validate_angle_ranges.c @@ -88,11 +88,11 @@ validate_angle_ranges(size_t na, double *aPtr, size_t nr, double *minPtr, # include "ndargs_helper.h" # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_validateAngleRanges = +static const char *docstring_validateAngleRanges = "c module implementation of validate_angle_ranges.\n" "Please use the Python wrapper.\n"; -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_validateAngleRanges(PyObject * self, PyObject * args) { nah_array ang_list = { NULL, "ang_list", NAH_TYPE_DP_FP, { NAH_DIM_ANY }}; diff --git a/hexrd/core/transforms/new_capi/xy_to_gvec.c b/hexrd/core/transforms/new_capi/xy_to_gvec.c index 1b6ee1a37..9d4e4216a 100644 --- a/hexrd/core/transforms/new_capi/xy_to_gvec.c +++ b/hexrd/core/transforms/new_capi/xy_to_gvec.c @@ -100,7 +100,7 @@ xy_to_gvec(size_t npts, double *xy, double *rMat_d, double *rMat_s, # include # endif /* XRD_SINGLE_COMPILE_UNIT */ -XRD_PYTHON_WRAPPER const char *docstring_detectorXYToGvec = +static const char *docstring_detectorXYToGvec = "c module implementation of xy_to_gvec.\n" "Please use the Python wrapper.\n"; @@ -127,7 +127,7 @@ XRD_PYTHON_WRAPPER const char *docstring_detectorXYToGvec = gvec_l -- (n, 3) ndarray containing the associated G vector directions in the LAB FRAME associated with gVecs */ -XRD_PYTHON_WRAPPER PyObject * +static PyObject * python_detectorXYToGvec(PyObject * self, PyObject * args) { /* Right now, the Python wrapper guarantees that: From ef68e8e8d84ae31255e2d9cd685e16588c4bb4ca Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Wed, 4 Feb 2026 10:22:38 -0500 Subject: [PATCH 25/28] Resolve importlib typing issues --- hexrd/core/utils/panel_buffer.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hexrd/core/utils/panel_buffer.py b/hexrd/core/utils/panel_buffer.py index d87c9e43c..66a02db37 100644 --- a/hexrd/core/utils/panel_buffer.py +++ b/hexrd/core/utils/panel_buffer.py @@ -76,20 +76,18 @@ def panel_buffer_from_str(name: str, panel: Detector) -> np.ndarray: return roi_buffer -# TODO: Broken function - dir_path.glob is not defined def valid_panel_buffer_names() -> list[str]: dir_path = importlib.resources.files(hexrd.core.resources.panel_buffers) - return [path.stem for path in dir_path.glob('*.npz')] + return [importlib.resources.as_file(file_) for file_ in dir_path.iterdir() if file_.name.endswith('.npz')] # Cache this so we only read from disk once -# TODO: Broken function - path.exists is not defined @cache def _load_panel_buffer_from_file(name: str) -> np.ndarray: path = importlib.resources.files(hexrd.core.resources.panel_buffers).joinpath( f'{name}.npz' ) - if not path.exists(): + if not path.is_file(): raise NotImplementedError(f'Unknown panel buffer name: {name}') npz = np.load(str(path)) From 796d88215534478f7273b5f8df5c99660b735a8d Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Wed, 4 Feb 2026 10:26:17 -0500 Subject: [PATCH 26/28] Revert "Resolve importlib typing issues" This reverts commit ef68e8e8d84ae31255e2d9cd685e16588c4bb4ca. --- hexrd/core/utils/panel_buffer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hexrd/core/utils/panel_buffer.py b/hexrd/core/utils/panel_buffer.py index 66a02db37..d87c9e43c 100644 --- a/hexrd/core/utils/panel_buffer.py +++ b/hexrd/core/utils/panel_buffer.py @@ -76,18 +76,20 @@ def panel_buffer_from_str(name: str, panel: Detector) -> np.ndarray: return roi_buffer +# TODO: Broken function - dir_path.glob is not defined def valid_panel_buffer_names() -> list[str]: dir_path = importlib.resources.files(hexrd.core.resources.panel_buffers) - return [importlib.resources.as_file(file_) for file_ in dir_path.iterdir() if file_.name.endswith('.npz')] + return [path.stem for path in dir_path.glob('*.npz')] # Cache this so we only read from disk once +# TODO: Broken function - path.exists is not defined @cache def _load_panel_buffer_from_file(name: str) -> np.ndarray: path = importlib.resources.files(hexrd.core.resources.panel_buffers).joinpath( f'{name}.npz' ) - if not path.is_file(): + if not path.exists(): raise NotImplementedError(f'Unknown panel buffer name: {name}') npz = np.load(str(path)) From 46fd547a80715d84ef12911f470446e66d0c2f0d Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Wed, 4 Feb 2026 15:22:36 -0500 Subject: [PATCH 27/28] Delete a lot of useless transforms stuff --- hexrd/core/transforms/new_capi/README.md | 9 --- hexrd/core/transforms/new_capi/gvec_to_xy.c | 19 ------- .../core/transforms/new_capi/make_beam_rmat.c | 21 ------- .../transforms/new_capi/make_detector_rmat.c | 19 ------- .../transforms/new_capi/make_sample_rmat.c | 19 ------- hexrd/core/transforms/new_capi/module.c | 3 - hexrd/core/transforms/new_capi/new_func.c | 22 ------- .../new_capi/oscill_angles_of_HKLs.c | 19 ------- .../core/transforms/new_capi/quat_distance.c | 26 --------- hexrd/core/transforms/new_capi/reference.py | 57 ------------------- .../new_capi/rotate_vecs_about_axis.c | 20 ------- .../new_capi/transforms_prototypes.h | 4 -- .../transforms/new_capi/transforms_utils.h | 4 -- .../transforms/new_capi/unit_row_vector.c | 21 ------- .../new_capi/validate_angle_ranges.c | 20 ------- hexrd/core/transforms/new_capi/xy_to_gvec.c | 19 ------- hexrd/core/transforms/stdbool.h | 6 -- 17 files changed, 308 deletions(-) delete mode 100644 hexrd/core/transforms/new_capi/README.md delete mode 100644 hexrd/core/transforms/new_capi/new_func.c delete mode 100644 hexrd/core/transforms/new_capi/reference.py delete mode 100644 hexrd/core/transforms/stdbool.h diff --git a/hexrd/core/transforms/new_capi/README.md b/hexrd/core/transforms/new_capi/README.md deleted file mode 100644 index c1641d18f..000000000 --- a/hexrd/core/transforms/new_capi/README.md +++ /dev/null @@ -1,9 +0,0 @@ -These files were copied from Oscar Villellas' xrd-transforms library -located here: https://github.com/ovillellas/xrd-transforms - -At commit: b94f8b2d7839d883829d00a2adc5bec9c80e0116 - -They have been modified a little since then (see commit history). - -Our goal is to eventually migrate all of our xfcapi function calls to these, -but we need to test them thoroughly, and do them one at a time. diff --git a/hexrd/core/transforms/new_capi/gvec_to_xy.c b/hexrd/core/transforms/new_capi/gvec_to_xy.c index 08d95026b..1c516452c 100644 --- a/hexrd/core/transforms/new_capi/gvec_to_xy.c +++ b/hexrd/core/transforms/new_capi/gvec_to_xy.c @@ -1,10 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - #define GV2XY_GROUP_SIZE 128 #define GVEC_TO_XY_FUNC gvec_to_xy_vect @@ -592,15 +585,6 @@ gvec_to_xy_vect(size_t npts, const double *gVec_cs, result, flags, rays_to_planar_detector, &planar_detector_data); } -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_gvecToDetectorXY = "c module implementation of gvec_to_xy (single sample).\n" "Please use the Python wrapper.\n"; @@ -828,6 +812,3 @@ python_gvecToDetectorXYArray(PyObject * self, PyObject * args) return PyErr_NoMemory(); } - - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/make_beam_rmat.c b/hexrd/core/transforms/new_capi/make_beam_rmat.c index c5c696ef5..b8a3a000c 100644 --- a/hexrd/core/transforms/new_capi/make_beam_rmat.c +++ b/hexrd/core/transforms/new_capi/make_beam_rmat.c @@ -1,11 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - - static int make_beam_rmat(double * bPtr, double * ePtr, double * rPtr) { @@ -56,16 +48,6 @@ make_beam_rmat(double * bPtr, double * ePtr, double * rPtr) return 0; /* no error */ } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_makeEtaFrameRotMat = "c module implementation of make_beam_mat.\n" "Please use the Python wrapper.\n"; @@ -127,6 +109,3 @@ python_makeEtaFrameRotMat(PyObject * self, PyObject * args) done: return (PyObject *)rMat; } - - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/make_detector_rmat.c b/hexrd/core/transforms/new_capi/make_detector_rmat.c index ed4ccfe32..9ccdf2982 100644 --- a/hexrd/core/transforms/new_capi/make_detector_rmat.c +++ b/hexrd/core/transforms/new_capi/make_detector_rmat.c @@ -1,10 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -#endif - - static void make_detector_rmat(double * tPtr, double * rPtr) { @@ -27,16 +20,6 @@ make_detector_rmat(double * tPtr, double * rPtr) rPtr[8] = c[0]*c[1]; } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_makeDetectorRotMat = "c module implementation of makeDetectorRotMat.\n" "Please use the Python wrapper.\n"; @@ -74,5 +57,3 @@ python_makeDetectorRotMat(PyObject * self, PyObject * args) return((PyObject*)rMat); } - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/make_sample_rmat.c b/hexrd/core/transforms/new_capi/make_sample_rmat.c index f3707b362..dacff49bc 100644 --- a/hexrd/core/transforms/new_capi/make_sample_rmat.c +++ b/hexrd/core/transforms/new_capi/make_sample_rmat.c @@ -1,10 +1,4 @@ -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - static void make_sample_rmat(double chi, double ome, double *result_rmat) { @@ -57,17 +51,6 @@ make_sample_rmat_array(double chi, const double *ome_ptr, size_t ome_count, doub } } - - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_makeOscillRotMat = "c module implementation of make_sample_rotmat.\n" "Please use the Python wrapper.\n"; @@ -105,5 +88,3 @@ python_makeOscillRotMat(PyObject * self, PyObject * args) return PyErr_NoMemory(); } - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/module.c b/hexrd/core/transforms/new_capi/module.c index 7e2b031ea..6bb5e32d8 100644 --- a/hexrd/core/transforms/new_capi/module.c +++ b/hexrd/core/transforms/new_capi/module.c @@ -50,9 +50,6 @@ * statics. * ============================================================================= */ -#define XRD_SINGLE_COMPILE_UNIT 1 -#define XRD_INCLUDE_PYTHON_WRAPPERS 1 - #include "transforms_types.h" #include "transforms_utils.h" #include "transforms_prototypes.h" diff --git a/hexrd/core/transforms/new_capi/new_func.c b/hexrd/core/transforms/new_capi/new_func.c deleted file mode 100644 index f7a92768a..000000000 --- a/hexrd/core/transforms/new_capi/new_func.c +++ /dev/null @@ -1,22 +0,0 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -#endif - - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - -static const char *<>_docstring = -"c module implementation of <>. Please use the Python wrapper.\n" - - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ - diff --git a/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c b/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c index d89418d06..5f1eaba20 100644 --- a/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c +++ b/hexrd/core/transforms/new_capi/oscill_angles_of_HKLs.c @@ -1,11 +1,4 @@ -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - - static void oscill_angles_of_HKLs(size_t npts, double * hkls, double chi, double * rMat_c, double * bMat, double wavelength, @@ -175,16 +168,6 @@ oscill_angles_of_HKLs(size_t npts, double * hkls, double chi, } } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_oscillAnglesOfHKLs = "c module implementation of solve_omega.\n" "Please use the Python wrapper.\n"; @@ -259,5 +242,3 @@ python_oscillAnglesOfHKLs(PyObject * self, PyObject * args) return PyErr_NoMemory(); } - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/quat_distance.c b/hexrd/core/transforms/new_capi/quat_distance.c index f0501f7a5..a5f21fdf3 100644 --- a/hexrd/core/transforms/new_capi/quat_distance.c +++ b/hexrd/core/transforms/new_capi/quat_distance.c @@ -1,15 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -/* TODO: No printf should go here. Also, it should be possible to avoid malloc - */ -# include -# include - -# include "transforms_utils.h" -# include "transforms_prototypes.h" -#endif - - static double quat_distance(size_t nsym, double * q1, double * q2, double * qsym) { @@ -46,17 +34,6 @@ quat_distance(size_t nsym, double * q1, double * q2, double * qsym) return dist; } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# include "ndargs_helper.h" -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_quat_distance = "c module implementation of quat_distance.\n" "Please use the Python wrapper.\n"; @@ -84,6 +61,3 @@ python_quat_distance(PyObject * self, PyObject * args) return PyFloat_FromDouble(dist); } - - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/reference.py b/hexrd/core/transforms/new_capi/reference.py deleted file mode 100644 index 36dfe681e..000000000 --- a/hexrd/core/transforms/new_capi/reference.py +++ /dev/null @@ -1,57 +0,0 @@ -import numpy as np - -# Functions to use for reference in testing and the like. These are made to be -# easily testable and clear. They will be use to test against in unit tests. -# They may be slow and not vectorized. - - -def intersect_ray_plane(ro, rv, p): - ''' - ray-plane intersection - - Parameters - ---------- - ro: array[3] - point of origin of the ray (ray origin). - - rv: array[3] - direction vector of the ray (ray vector). - - p: array[4] - (A, B, C, D) coeficients for the plane formula (Ax+By+Cz+D=0). - - Returns - ------- - t : scalar - t where ray intersects with plane, such as (ro + t*rv) lies within p. - - Notes - ----- - If t is negative, the intersection happens 'behind' the origin point. - - In the case where (A,B,C) -the plane normal- is orthogonal to rv, no - intersection will happen (or the ray is fully on the plane). In that case, - t will be non-finite. - - Vector rv needs not to be an unit vector, result will be scaled accordingly - based on the modulo of rv. However, a 0 vector will result in non-finite - result. - - The code is based on the segment-plane intersection in [1]_ - - .. [1] Ericson. (2005). Real-Time Collision Detection, pp 176. - Morgan Kaufmann. - ''' - assert ro.shape == (3,) - assert rv.shape == (3,) - assert p.shape == (4,) - - normal = p[:3] - D = p[3] - # disable divide by 0 and invalid as the division in t can cause both. - # the behavior of the function actually relies in IEEE754 with a division - # by 0 generating the appropriate infinity, or a NAN if it is a 0/0. - with np.errstate(divide='ignore', invalid='ignore'): - t = (D - normal @ ro) / (normal @ rv) - - return t diff --git a/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c b/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c index aad55b895..20faba1c7 100644 --- a/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c +++ b/hexrd/core/transforms/new_capi/rotate_vecs_about_axis.c @@ -1,10 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -#endif - - static void rotate_vecs_about_axis(size_t na, double *angles, size_t nax, double *axes, @@ -54,17 +47,6 @@ rotate_vecs_about_axis(size_t na, double *angles, } } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# include "ndargs_helper.h" -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_rotate_vecs_about_axis = "c module implementation of rotate_vecs_about_axis.\n" "Please use the Python wrapper.\n"; @@ -139,5 +121,3 @@ python_rotate_vecs_about_axis(PyObject *self, PyObject *args) return 0; } - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/transforms_prototypes.h b/hexrd/core/transforms/new_capi/transforms_prototypes.h index 67b73040d..958b265f9 100644 --- a/hexrd/core/transforms/new_capi/transforms_prototypes.h +++ b/hexrd/core/transforms/new_capi/transforms_prototypes.h @@ -1,10 +1,6 @@ #ifndef XRD_TRANSFORMS_PROTOTYPES_H #define XRD_TRANSFORMS_PROTOTYPES_H -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_types.h" -#endif - #define GV2XY_SINGLE_RMAT_S 1 static void gvec_to_xy(size_t npts, const double *gVec_c_Ptr, const double *rMat_d_Ptr, diff --git a/hexrd/core/transforms/new_capi/transforms_utils.h b/hexrd/core/transforms/new_capi/transforms_utils.h index a850b2307..895f3a2ad 100644 --- a/hexrd/core/transforms/new_capi/transforms_utils.h +++ b/hexrd/core/transforms/new_capi/transforms_utils.h @@ -1,10 +1,6 @@ #ifndef TRANSFORMS_UTILS_H #define TRANSFORMS_UTILS_H -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_types.h" -#endif - static const double m33_identity[3][3] = { { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, diff --git a/hexrd/core/transforms/new_capi/unit_row_vector.c b/hexrd/core/transforms/new_capi/unit_row_vector.c index aeb543e04..666b90fa5 100644 --- a/hexrd/core/transforms/new_capi/unit_row_vector.c +++ b/hexrd/core/transforms/new_capi/unit_row_vector.c @@ -1,11 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - - static int unit_row_vector(size_t n, double * cIn, double * cOut) { @@ -55,16 +47,6 @@ unit_row_vectors(size_t m, size_t n, double *cIn, double *cOut) } } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_unitRowVector = "c module implementation of unit_row_vector (one row).\n" "Please use the Python wrapper.\n"; @@ -140,6 +122,3 @@ python_unitRowVectors(PyObject *self, PyObject *args) return (PyObject*)aop_out; } - - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/new_capi/validate_angle_ranges.c b/hexrd/core/transforms/new_capi/validate_angle_ranges.c index 4a79cebe7..d4896723a 100644 --- a/hexrd/core/transforms/new_capi/validate_angle_ranges.c +++ b/hexrd/core/transforms/new_capi/validate_angle_ranges.c @@ -1,9 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -#endif - static void validate_angle_ranges(size_t na, double *aPtr, size_t nr, double *minPtr, double *maxPtr, bool *rPtr, int ccw) @@ -77,17 +71,6 @@ validate_angle_ranges(size_t na, double *aPtr, size_t nr, double *minPtr, } } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# include "ndargs_helper.h" -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_validateAngleRanges = "c module implementation of validate_angle_ranges.\n" "Please use the Python wrapper.\n"; @@ -145,6 +128,3 @@ python_validateAngleRanges(PyObject * self, PyObject * args) Py_XDECREF(result); return NULL; } - -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ - diff --git a/hexrd/core/transforms/new_capi/xy_to_gvec.c b/hexrd/core/transforms/new_capi/xy_to_gvec.c index 9d4e4216a..b7becdd6f 100644 --- a/hexrd/core/transforms/new_capi/xy_to_gvec.c +++ b/hexrd/core/transforms/new_capi/xy_to_gvec.c @@ -1,11 +1,3 @@ - -#if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# include "transforms_utils.h" -# include "transforms_prototypes.h" -# include "ndargs_helper.h" -#endif - - static void xy_to_gvec(size_t npts, double *xy, double *rMat_d, double *rMat_s, double *tVec_d, double *tVec_s, double *tVec_c, @@ -90,16 +82,6 @@ xy_to_gvec(size_t npts, double *xy, double *rMat_d, double *rMat_s, } } - -#if defined(XRD_INCLUDE_PYTHON_WRAPPERS) && XRD_INCLUDE_PYTHON_WRAPPERS - -# if !defined(XRD_SINGLE_COMPILE_UNIT) || !XRD_SINGLE_COMPILE_UNIT -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -# include -# include -# endif /* XRD_SINGLE_COMPILE_UNIT */ - static const char *docstring_detectorXYToGvec = "c module implementation of xy_to_gvec.\n" "Please use the Python wrapper.\n"; @@ -222,4 +204,3 @@ python_detectorXYToGvec(PyObject * self, PyObject * args) return PyErr_NoMemory(); } -#endif /* XRD_INCLUDE_PYTHON_WRAPPERS */ diff --git a/hexrd/core/transforms/stdbool.h b/hexrd/core/transforms/stdbool.h deleted file mode 100644 index 3def25f93..000000000 --- a/hexrd/core/transforms/stdbool.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#define false 0 -#define true 1 - -#define bool int From 185538d28b9efd78350599d57087e0f454b1aee1 Mon Sep 17 00:00:00 2001 From: Zack Singer Date: Fri, 6 Feb 2026 11:12:08 -0500 Subject: [PATCH 28/28] Simplify transforms architecture --- hexrd/core/transforms/__init__.py | 28 - hexrd/core/transforms/new_capi/xf_new_capi.py | 691 ----------------- hexrd/core/transforms/xfcapi.py | 711 +++++++++++++++++- hexrd/powder/wppf/texture.py | 2 +- tests/transforms/common.py | 2 +- .../test_angles_to_dvec_from_file.py | 2 +- .../test_angles_to_gvec_from_file.py | 2 +- tests/transforms/test_gvec_to_xy.py | 2 +- tests/transforms/test_gvec_to_xy_from_file.py | 2 +- .../test_make_beam_rmat_from_file.py | 2 +- tests/transforms/test_make_binary_rmat.py | 4 +- .../test_make_detector_rmat_from_file.py | 2 +- .../test_make_rmat_of_expmap_from_file.py | 2 +- .../test_make_sample_rmat_from_file.py | 2 +- .../test_quat_distance_from_file.py | 2 +- .../transforms/test_rotate_vecs_about_axis.py | 2 +- .../test_validate_angle_ranges_from_file.py | 2 +- tests/transforms/test_xy_to_gvec.py | 2 +- 18 files changed, 707 insertions(+), 755 deletions(-) delete mode 100644 hexrd/core/transforms/__init__.py delete mode 100644 hexrd/core/transforms/new_capi/xf_new_capi.py diff --git a/hexrd/core/transforms/__init__.py b/hexrd/core/transforms/__init__.py deleted file mode 100644 index 12ff60c85..000000000 --- a/hexrd/core/transforms/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# ============================================================ -# Copyright (c) 2012, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# Written by Joel Bernier and others. -# LLNL-CODE-529294. -# All rights reserved. -# -# This file is part of HEXRD. For details on dowloading the source, -# see the file COPYING. -# -# Please also see the file LICENSE. -# -# This program is free software; you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License (as published by the Free Software -# Foundation) version 2.1 dated February 1999. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this program (see file LICENSE); if not, write to -# the Free Software Foundation, Inc., 59 Temple Place, Suite 330, -# Boston, MA 02111-1307 USA or visit . -# ============================================================ -"""Tools or X-ray diffraction analysis""" -from . import xfcapi diff --git a/hexrd/core/transforms/new_capi/xf_new_capi.py b/hexrd/core/transforms/new_capi/xf_new_capi.py deleted file mode 100644 index 77d6abfc0..000000000 --- a/hexrd/core/transforms/new_capi/xf_new_capi.py +++ /dev/null @@ -1,691 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Transforms module implementation using a support C extension module. - -Currently, this implementation contains code for the following functions: - - - angles_to_gvec - - angles_to_dvec - - gvec_to_xy - - xy_to_gvec (partial) - - - unit_vector - - make_rmat_of_exp_map - - make_binary_rmat - - make_beam_rmat - - validate_angle_ranges - - rotate_vecs_about_axis - - quat_distance - -There are also some functions that maybe would be needed in the transforms -module: - - makeGVector - - makeRotMatOfQuat - - homochoricOfQuat -""" -from typing import Optional, Tuple, Sequence -import numpy as np -from numpy.typing import NDArray - -from hexrd.core.extensions import transforms_c_api -from hexrd.core.extensions import transforms as cpp_transforms -from hexrd.core.distortion.distortionabc import DistortionABC -from hexrd.core import constants as cnst - - -def angles_to_gvec( - angs: NDArray[np.float64], - beam_vec: NDArray[np.float64] = cnst.beam_vec, - eta_vec: NDArray[np.float64] = cnst.eta_vec, - chi: float = 0.0, - rmat_c: NDArray[np.float64] = cnst.identity_3x3, -) -> NDArray[np.float64]: - """ - - Takes triplets of angles in the beam frame (2*theta, eta[, omega]) - to components of unit G-vectors in the LAB frame. If the omega - values are not trivial (i.e. angs[:, 2] = 0.), then the components - are in the SAMPLE frame. If the crystal rmat is specified and - is not the identity, then the components are in the CRYSTAL frame. - - G vectors here referes to the reciprocal lattice vectors. - - Parameters - ---------- - angs : ndarray - The euler angles of diffraction. The last dimension must be 2 or 3. In - (2*theta, eta[, omega]) format. - beam_vec : ndarray, optional - Unit vector pointing towards the X-ray source in the lab frame. - Defaults to [0,0,-1] - eta_vec : ndarray, optional - Vector defining eta=0 in the lab frame. Defaults to [1,0,0] - chi : float, optional - The inclination angle of the sample frame about the lab frame X. - rmat_c : ndarray, optional - The change of basis matrix from the reciprocal frame to the crystal - frame. Defaults to the identity. - - Returns - ------- - ndarray - (3,n) array of unit reciprocal lattice vectors, frame depends on the - input parameters - """ - - orig_ndim = angs.ndim - - # if only a pair is provided... convert to a triplet with omegas == 0 - # so that behavior is preserved. - if angs.shape[-1] == 2: - angs = np.hstack((angs, np.zeros(angs.shape[:-1] + (1,)))) - - angs = np.ascontiguousarray(np.atleast_2d(angs)) - beam_vec = np.ascontiguousarray(beam_vec.flatten()) - eta_vec = np.ascontiguousarray(eta_vec.flatten()) - rmat_c = np.ascontiguousarray(rmat_c) - - result = cpp_transforms.anglesToGVec(angs, beam_vec, eta_vec, chi, rmat_c) - - return result[0] if orig_ndim == 1 else result - - -def angles_to_dvec( - angs: NDArray[np.float64], - beam_vec: NDArray[np.float64] = cnst.beam_vec, - eta_vec: NDArray[np.float64] = cnst.eta_vec, - chi: float = 0.0, - rmat_c: NDArray[np.float64] = cnst.identity_3x3, -) -> NDArray[np.float64]: - """Calculate diffraction vectors from beam frame angles. - - Takes triplets of angles in the beam frame (2*theta, eta[, omega]) - to components of unit diffraction vectors in the LAB frame. If the omega - values are not trivial (i.e. angs[:, 2] = 0.), then the components - are in the SAMPLE frame. If the crystal rmat is specified and - is not the identity, then the components are in the CRYSTAL frame. - - - Parameters - ---------- - angs : ndarray - The euler angles of diffraction. The last dimension must be 2 or 3. In - (2*theta, eta[, omega]) format. - beam_vec : ndarray, optional - Unit vector pointing towards the X-ray source in the lab frame. - Defaults to [0,0,-1] - eta_vec : ndarray, optional - Vector defining eta=0 in the lab frame. Defaults to [1,0,0] - chi : float, optional - The inclination angle of the sample frame about the lab frame X. - rmat_c : ndarray, optional - The change of basis matrix from the reciprocal frame to the crystal - frame. Defaults to the identity. - - Returns - ------- - ndarray - (3,n) array of unit diffraction vectors, frame depends on the input - parameters - """ - # TODO: Improve capi to avoid multiplications when rmat_c is None - - # if only a pair is provided... convert to a triplet with omegas == 0 - # so that behavior is preserved. - if angs.shape[-1] == 2: - angs = np.hstack((angs, np.zeros(angs.shape[:-1] + (1,)))) - - angs = np.ascontiguousarray(np.atleast_2d(angs)) - beam_vec = np.ascontiguousarray(beam_vec.flatten()) - eta_vec = np.ascontiguousarray(eta_vec.flatten()) - rmat_c = np.ascontiguousarray(rmat_c) - - return cpp_transforms.anglesToDVec(angs, beam_vec, eta_vec, chi, rmat_c) - - -def makeGVector( - hkl: NDArray[np.float64], bMat: NDArray[np.float64] -) -> NDArray[np.float64]: - """ - Take a crystal relative b matrix onto a list of hkls to output unit - reciprocal latice vectors (a.k.a. lattice plane normals) - - - Parameters - ---------- - hkl : ndarray - (3,n) ndarray of n hstacked reciprocal lattice vector component - triplets - bMat : ndarray - (3,3) ndarray of the change of basis matrix from the reciprocal - lattice to the crystal reference frame - - Returns - ------- - ndarray - (3,n) ndarray of n unit reciprocal lattice vectors (a.k.a. lattice - plane normals) - - """ - if hkl.shape[0] != 3: - raise ValueError('hkl input must be (3, n)') - return unit_vector(np.dot(bMat, hkl)) - - -def gvec_to_xy( - gvec_c: NDArray[np.float64], - rmat_d: NDArray[np.float64], - rmat_s: NDArray[np.float64], - rmat_c: NDArray[np.float64], - tvec_d: NDArray[np.float64], - tvec_s: NDArray[np.float64], - tvec_c: NDArray[np.float64], - beam_vec: Optional[NDArray[np.float64]] = None, - vmat_inv: Optional[NDArray[np.float64]] = None, - bmat: Optional[NDArray[np.float64]] = None, -) -> NDArray[np.float64]: - """ - Takes a concatenated list of reciprocal lattice vectors components in the - CRYSTAL FRAME to the specified detector-relative frame, subject to the - following: - - 1) it must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Parameters - ---------- - gvec_c : array_like - ([N,] 3) G-vector components in the CRYSTAL FRAME. - rmat_d : array_like - The (3, 3) COB matrix taking components in the - DETECTOR FRAME to the LAB FRAME - rmat_s : array_like - The ([N,] 3, 3) COB matrix taking components in the SAMPLE FRAME to the - LAB FRAME. It may be a single (3, 3) rotation matrix to use for all - gvec_c, or just one rotation matrix per gvec. - rmat_c : array_like - The (3, 3) COB matrix taking components in the - CRYSTAL FRAME to the SAMPLE FRAME - tvec_d : array_like - The (3, ) translation vector connecting LAB FRAME to DETECTOR FRAME - tvec_s : array_like - The (3, ) translation vector connecting LAB FRAME to SAMPLE FRAME - tvec_c : array_like - The ([M,] 3, ) translation vector(s) connecting SAMPLE FRAME to - CRYSTAL FRAME - beam_vec : array_like, optional - The (3, ) incident beam propagation vector components in the LAB FRAME; - the default is [0, 0, -1], which is the standard setting. - vmat_inv : array_like, optional - The (3, 3) matrix of inverse stretch tensor components in the - SAMPLE FRAME. The default is None, which implies a strain-free state - (i.e. V = I). - bmat : array_like, optional - The (3, 3) COB matrix taking components in the - RECIPROCAL LATTICE FRAME to the CRYSTAL FRAME; if supplied, it is - assumed that the input `gvecs` are G-vector components in the - RECIPROCL LATTICE FRAME (the default is None, which implies components - in the CRYSTAL FRAME) - - Returns - ------- - array_like - The ([M, ][N, ] 2) array of [x, y] diffracted beam intersections for - each of the N input G-vectors in the DETECTOR FRAME (all Z_d - coordinates are 0 and excluded) and for each of the M candidate - positions. For each input G-vector that cannot satisfy a Bragg - condition or intersect the detector plane, [NaN, Nan] is returned. - - Notes - ----- - Previously only a single candidate position was allowed. This is in - fact a vectored version of the previous API function. It is backwards - compatible, as passing single tvec_c is supported and has the same - result. - - """ - beam_vec = beam_vec if beam_vec is not None else cnst.beam_vec - - orig_ndim = gvec_c.ndim - gvec_c = np.ascontiguousarray(np.atleast_2d(gvec_c)) - rmat_d = np.ascontiguousarray(rmat_d) - rmat_s = np.ascontiguousarray(rmat_s) - rmat_c = np.ascontiguousarray(rmat_c) - tvec_d = np.ascontiguousarray(tvec_d.flatten()) - tvec_s = np.ascontiguousarray(tvec_s.flatten()) - tvec_c = np.ascontiguousarray(tvec_c.flatten()) - beam_vec = np.ascontiguousarray(beam_vec.flatten()) - - # depending on the number of dimensions of rmat_s use either the array - # version or the "scalar" (over rmat_s) version. Note that rmat_s is either - # a 3x3 matrix (ndim 2) or an nx3x4 array of matrices (ndim 3) - if rmat_s.ndim > 2: - result = transforms_c_api.gvecToDetectorXYArray( - gvec_c, rmat_d, rmat_s, rmat_c, tvec_d, tvec_s, tvec_c, beam_vec - ) - else: - result = transforms_c_api.gvecToDetectorXY( - gvec_c, rmat_d, rmat_s, rmat_c, tvec_d, tvec_s, tvec_c, beam_vec - ) - return result[0] if orig_ndim == 1 else result - - -def xy_to_gvec( - xy_d: NDArray[np.float64], - rmat_d: NDArray[np.float64], - rmat_s: NDArray[np.float64], - tvec_d: NDArray[np.float64], - tvec_s: NDArray[np.float64], - tvec_c: NDArray[np.float64], - rmat_b: Optional[NDArray[np.float64]] = None, - distortion: Optional[DistortionABC] = None, - output_ref: Optional[bool] = False, -) -> tuple[NDArray[np.float64], ...]: - """ - Takes a list cartesian (x, y) pairs in the DETECTOR FRAME and calculates - the associated reciprocal lattice (G) vectors and (bragg angle, azimuth) - pairs with respect to the specified beam and azimth (eta) reference - directions. - - Parameters - ---------- - xy_d : array_like - (n, 2) array of n (x, y) coordinates in DETECTOR FRAME - rmat_d : array_like - (3, 3) COB matrix taking components in the - DETECTOR FRAME to the LAB FRAME - rmat_s : array_like - (3, 3) COB matrix taking components in the - SAMPLE FRAME to the LAB FRAME - tvec_d : array_like - (3, ) translation vector connecting LAB FRAME to DETECTOR FRAME - tvec_s : array_like - (3, ) translation vector connecting LAB FRAME to SAMPLE FRAME - tvec_c : array_like - (3, ) translation vector connecting SAMPLE FRAME to CRYSTAL FRAME - rmat_b : array_like, optional - (3, 3) COB matrix taking components in the BEAM FRAME to the LAB FRAME; - defaults to None, which implies the standard setting of identity. - distortion : distortion class, optional - Default is None - output_ref : bool, optional - If True, prepends the apparent bragg angle and azimuth with respect to - the SAMPLE FRAME (ignoring effect of non-zero tvec_c) - - Returns - ------- - array_like - (n, 2) ndarray containing the (tth, eta) pairs associated with each - (x, y) associated with gVecs - array_like - (n, 3) ndarray containing the associated G vector directions in the - LAB FRAME - array_like, optional - if output_ref is True - """ - # It seems that the output_ref version is not present as the argument - # gets ignored - - rmat_b = rmat_b if rmat_b is not None else cnst.identity_3x3 - - # the code seems to ignore this argument, assume output_ref == True not - # implemented - assert not output_ref, 'output_ref not implemented' - - if distortion is not None: - xy_d = distortion.apply_inverse(xy_d) - - xy_d = np.ascontiguousarray(np.atleast_2d(xy_d)) - rmat_d = np.ascontiguousarray(rmat_d) - rmat_s = np.ascontiguousarray(rmat_s) - tvec_d = np.ascontiguousarray(tvec_d.flatten()) - tvec_s = np.ascontiguousarray(tvec_s.flatten()) - tvec_c = np.ascontiguousarray(tvec_c.flatten()) - beam_vec = np.ascontiguousarray((-rmat_b[:, 2]).flatten()) - eta_vec = np.ascontiguousarray(rmat_b[:, 0].flatten()) - return transforms_c_api.detectorXYToGvec( - xy_d, rmat_d, rmat_s, tvec_d, tvec_s, tvec_c, beam_vec, eta_vec - ) - - -def oscill_angles_of_hkls( - hkls: NDArray[np.float64], - chi: float, - rmat_c: NDArray[np.float64], - bmat: NDArray[np.float64], - wavelength: float, - v_inv: Optional[NDArray[np.float64]] = None, - beam_vec: NDArray[np.float64] = cnst.beam_vec, - eta_vec: NDArray[np.float64] = cnst.eta_vec, -) -> Tuple[NDArray[np.float64], NDArray[np.float64]]: - """ - Takes a list of unit reciprocal lattice vectors in crystal frame to the - specified detector-relative frame, subject to the conditions: - - 1) the reciprocal lattice vector must be able to satisfy a bragg condition - 2) the associated diffracted beam must intersect the detector plane - - Parameters - ---------- - hkls -- (n, 3) ndarray of n reciprocal lattice vectors - in the CRYSTAL FRAME - chi -- float representing the inclination angle of the - oscillation axis (std coords) - rmat_c -- (3, 3) ndarray, the COB taking CRYSTAL FRAME components - to SAMPLE FRAME - bmat -- (3, 3) ndarray, the COB taking RECIPROCAL LATTICE components - to CRYSTAL FRAME - wavelength -- float representing the x-ray wavelength in Angstroms - - Optional Keyword Arguments: - v_inv -- (6, ) ndarray, vec representation inverse stretch tensor - components in the SAMPLE FRAME. The default is None, which - implies a strain-free state (i.e. V = I). - beam_vec -- (3, 1) mdarray containing the incident beam direction - components in the LAB FRAME - eta_vec -- (3, 1) mdarray containing the reference azimuth direction - components in the LAB FRAME - - Outputs: - ome0 -- (n, 3) ndarray containing the feasible (tTh, eta, ome) triplets - for each input hkl (first solution) - ome1 -- (n, 3) ndarray containing the feasible (tTh, eta, ome) triplets - for each input hkl (second solution) - - Notes: - ------------------------------------------------------------------------ - The reciprocal lattice vector, G, will satisfy the the Bragg condition - when: - - b.T * G / ||G|| = -sin(theta) - - where b is the incident beam direction (k_i) and theta is the Bragg - angle consistent with G and the specified wavelength. The components of - G in the lab frame in this case are obtained using the crystal - orientation, Rc, and the single-parameter oscillation matrix, Rs(ome): - - Rs(ome) * Rc * G / ||G|| - - The equation above can be rearranged to yield an expression of the form: - - a*sin(ome) + b*cos(ome) = c - - which is solved using the relation: - - a*sin(x) + b*cos(x) = sqrt(a**2 + b**2) * sin(x + alpha) - - --> sin(x + alpha) = c / sqrt(a**2 + b**2) - - where: - - alpha = atan2(b, a) - - The solutions are: - - / - | arcsin(c / sqrt(a**2 + b**2)) - alpha - x = < - | pi - arcsin(c / sqrt(a**2 + b**2)) - alpha - \ - - There is a double root in the case the reflection is tangent to the - Debye-Scherrer cone (c**2 = a**2 + b**2), and no solution if the - Laue condition cannot be satisfied (filled with NaNs in the results - array here) - """ - # this was oscillAnglesOfHKLs - hkls = np.array(hkls, dtype=float, order='C') - if v_inv is None: - v_inv = np.array([1.0, 1.0, 1.0, 0.0, 0.0, 0.0]) - else: - v_inv = np.ascontiguousarray(v_inv.flatten()) - rmat_c = np.ascontiguousarray(rmat_c) - beam_vec = np.ascontiguousarray(beam_vec.flatten()) - eta_vec = np.ascontiguousarray(eta_vec.flatten()) - bmat = np.ascontiguousarray(bmat) - return transforms_c_api.oscillAnglesOfHKLs( - hkls, chi, rmat_c, bmat, wavelength, v_inv, beam_vec, eta_vec - ) - - -def unit_vector(vec_in: NDArray[np.float64]) -> NDArray[np.float64]: - """ - Normalize the input vector(s) to unit length. - - Parameters - ---------- - vec_in : ndarray - The input vector(s) (n,3) to normalize. - - Returns - ------- - ndarray - The normalized vector(s) of the same shape as the input. - - Raises - ------ - ValueError - If the input vector(s) do not have the correct shape. - """ - vec_in = np.ascontiguousarray(vec_in) - if vec_in.ndim == 1: - return transforms_c_api.unitRowVector(vec_in) - elif vec_in.ndim == 2: - return transforms_c_api.unitRowVectors(vec_in) - else: - raise ValueError( - "incorrect arg shape; must be 1-d or 2-d, yours is %d-d" % (vec_in.ndim) - ) - - -def make_detector_rmat(tilt_angles: NDArray[np.float64]) -> NDArray[np.float64]: - """ - Form the (3, 3) tilt rotations from the tilt angle list: - - tilt_angles = [gamma_Xl, gamma_Yl, gamma_Zl] in radians - Returns the (3, 3) rotation matrix from the detector frame to the lab frame - """ - t_angles = np.ascontiguousarray(np.r_[tilt_angles].flatten()) - return transforms_c_api.makeDetectorRotMat(t_angles) - - -# make_sample_rmat in CAPI is split between makeOscillRotMat -# and makeOscillRotMatArray... - - -def make_oscill_rmat(oscillAngles): - chi, ome = oscillAngles - ome = np.atleast_1d(ome) - result = transforms_c_api.makeOscillRotMat(chi, ome) - return result.reshape((3, 3)) - - -def make_oscill_rmat_array(chi, omeArray): - arg = np.ascontiguousarray(omeArray) - return transforms_c_api.makeOscillRotMat(chi, arg) - - -def make_sample_rmat( - chi: float, ome: float | NDArray[np.float64] -) -> NDArray[np.float64]: - # TODO: Check this docstring - """ - Make a rotation matrix representing the COB from sample frame to the lab - frame. - - Parameters - ---------- - chi : float - The inclination angle of the sample frame about the lab frame Y. - ome : float or ndarray - The oscillation angle(s) about the sample frame Y. - - Returns - ------- - ndarray - A 3x3 rotation matrix representing the input oscillation angles. - - """ - ome_array = np.atleast_1d(ome) - if ome is ome_array: - ome_array = np.ascontiguousarray(ome_array) - result = cpp_transforms.makeOscillRotMat(chi, ome_array).reshape( - len(ome_array), 3, 3 - ) - else: - # converted to 1d array of 1 element, no need - # to call ascontiguousarray, but need to remove - # the outer dimension from the result - result = cpp_transforms.makeOscillRotMat(chi, ome_array) - - return result - - -def make_rmat_of_expmap( - exp_map: Sequence[float] | NDArray[np.float64], -) -> NDArray[np.float64]: - """ - Calculate the rotation matrix of an exponential map. - - Parameters - ---------- - exp_map : array_like - A 3-element sequence representing the exponential map n*phi. - - Returns - ------- - NDArray[np.float64] - A 3x3 rotation matrix representing the input exponential map - """ - arg = np.ascontiguousarray(exp_map).flatten() - return cpp_transforms.make_rot_mat_of_exp_map(arg) - - -def make_binary_rmat(axis: NDArray[np.float64]) -> NDArray[np.float64]: - """ - Create a 180 degree rotation matrix about the input axis. - - Parameters - ---------- - axis : ndarray - 3-element sequence representing the rotation axis, in radians. - - Returns - ------- - ndarray - A 3x3 rotation matrix representing a 180 degree rotation about the - input axis. - """ - - arg = np.ascontiguousarray(axis.flatten()) - return cpp_transforms.make_binary_rot_mat(arg) - - -def make_beam_rmat( - bvec_l: NDArray[np.float64], evec_l: NDArray[np.float64] -) -> NDArray[np.float64]: - """ - Creates a COB matrix from the beam frame to the lab frame - Note: beam and eta vectors must not be colinear - - Parameters - ---------- - bvec_l : ndarray - The beam vector in the lab frame, The (3, ) incident beam propagation - vector components in the lab frame. - the default is [0, 0, -1], which is the standard setting. - evec_l : ndarray - Vector defining eta=0 in the lab frame. Defaults to [1,0,0] - """ - arg1 = np.ascontiguousarray(bvec_l.flatten()) - arg2 = np.ascontiguousarray(evec_l.flatten()) - return transforms_c_api.makeEtaFrameRotMat(arg1, arg2) - - -def validate_angle_ranges( - ang_list: float | NDArray[np.float64], - start_angs: float | NDArray[np.float64], - stop_angs: float | NDArray[np.float64], - ccw: bool = True, -) -> NDArray[np.bool_]: - """ - Find out if angles are in the CCW or CW range from start to stop - - Parameters - ---------- - ang_list : ndarray - The list of angles to validate - start_angs : ndarray - The starting angles - stop_angs : ndarray - The stopping angles - ccw : bool, optional - If True, the angles are in the CCW range from start to stop. - Defaults to True. - - Returns - ------- - ndarray - List of bools indicating if the angles are in the correct range - - """ - ang_list = np.atleast_1d(ang_list).astype(np.double, order='C') - start_angs = np.atleast_1d(start_angs).astype(np.double, order='C') - stop_angs = np.atleast_1d(stop_angs).astype(np.double, order='C') - - return transforms_c_api.validateAngleRanges(ang_list, start_angs, stop_angs, ccw) - - -def rotate_vecs_about_axis( - angle: float | NDArray[np.float64], - axis: NDArray[np.float64], - vecs: NDArray[np.float64], -) -> NDArray[np.float64]: - """ - Rotate vectors about an axis - - Parameters - ---------- - angle : array_like - Array of angles (len==1 or n) - axis : ndarray - Array of unit vectors (shape == (3,1) or (3,n)) - vecs : ndarray - Array of vectors to rotate (shape == (3,n)) - - Returns - ------- - ndarray - Array of rotated vectors (shape == (3,n)) - """ - angle = np.asarray(angle) - axis = np.ascontiguousarray(axis.T) - vecs = np.ascontiguousarray(vecs.T) - result = transforms_c_api.rotate_vecs_about_axis(angle, axis, vecs) - return result.T - - -def quat_distance( - q1: NDArray[np.float64], q2: NDArray[np.float64], qsym: NDArray[np.float64] -) -> NDArray[np.float64]: - """Distance between two quaternions, taking quaternions of symmetry into account. - - Parameters - ---------- - q1 : arary_like - First quaternion. - q2 : arary_like - Second quaternion. - qsym : ndarray - List of symmetry quaternions. - - Returns - ------- - float - The distance between the two quaternions. - """ - q1 = np.ascontiguousarray(q1.flatten()) - q2 = np.ascontiguousarray(q2.flatten()) - # C module expects quaternions in row major, numpy code in column major. - qsym = np.ascontiguousarray(qsym.T) - return transforms_c_api.quat_distance(q1, q2, qsym) diff --git a/hexrd/core/transforms/xfcapi.py b/hexrd/core/transforms/xfcapi.py index 0ee0b485d..77d6abfc0 100644 --- a/hexrd/core/transforms/xfcapi.py +++ b/hexrd/core/transforms/xfcapi.py @@ -1,20 +1,691 @@ -# We will replace these functions with the new versions as we -# add and test them. -# NOTE: we are only importing what is currently being used in hexrd -# and hexrdgui. This is so that we can see clearly what is in use. - -from .new_capi.xf_new_capi import ( - angles_to_dvec, - angles_to_gvec, - gvec_to_xy, - make_beam_rmat, - make_detector_rmat, - make_rmat_of_expmap, - make_sample_rmat, - oscill_angles_of_hkls, - quat_distance, - rotate_vecs_about_axis, - unit_vector, - validate_angle_ranges, - xy_to_gvec, -) +# -*- coding: utf-8 -*- +""" +Transforms module implementation using a support C extension module. + +Currently, this implementation contains code for the following functions: + + - angles_to_gvec + - angles_to_dvec + - gvec_to_xy + - xy_to_gvec (partial) + + - unit_vector + - make_rmat_of_exp_map + - make_binary_rmat + - make_beam_rmat + - validate_angle_ranges + - rotate_vecs_about_axis + - quat_distance + +There are also some functions that maybe would be needed in the transforms +module: + - makeGVector + - makeRotMatOfQuat + - homochoricOfQuat +""" +from typing import Optional, Tuple, Sequence +import numpy as np +from numpy.typing import NDArray + +from hexrd.core.extensions import transforms_c_api +from hexrd.core.extensions import transforms as cpp_transforms +from hexrd.core.distortion.distortionabc import DistortionABC +from hexrd.core import constants as cnst + + +def angles_to_gvec( + angs: NDArray[np.float64], + beam_vec: NDArray[np.float64] = cnst.beam_vec, + eta_vec: NDArray[np.float64] = cnst.eta_vec, + chi: float = 0.0, + rmat_c: NDArray[np.float64] = cnst.identity_3x3, +) -> NDArray[np.float64]: + """ + + Takes triplets of angles in the beam frame (2*theta, eta[, omega]) + to components of unit G-vectors in the LAB frame. If the omega + values are not trivial (i.e. angs[:, 2] = 0.), then the components + are in the SAMPLE frame. If the crystal rmat is specified and + is not the identity, then the components are in the CRYSTAL frame. + + G vectors here referes to the reciprocal lattice vectors. + + Parameters + ---------- + angs : ndarray + The euler angles of diffraction. The last dimension must be 2 or 3. In + (2*theta, eta[, omega]) format. + beam_vec : ndarray, optional + Unit vector pointing towards the X-ray source in the lab frame. + Defaults to [0,0,-1] + eta_vec : ndarray, optional + Vector defining eta=0 in the lab frame. Defaults to [1,0,0] + chi : float, optional + The inclination angle of the sample frame about the lab frame X. + rmat_c : ndarray, optional + The change of basis matrix from the reciprocal frame to the crystal + frame. Defaults to the identity. + + Returns + ------- + ndarray + (3,n) array of unit reciprocal lattice vectors, frame depends on the + input parameters + """ + + orig_ndim = angs.ndim + + # if only a pair is provided... convert to a triplet with omegas == 0 + # so that behavior is preserved. + if angs.shape[-1] == 2: + angs = np.hstack((angs, np.zeros(angs.shape[:-1] + (1,)))) + + angs = np.ascontiguousarray(np.atleast_2d(angs)) + beam_vec = np.ascontiguousarray(beam_vec.flatten()) + eta_vec = np.ascontiguousarray(eta_vec.flatten()) + rmat_c = np.ascontiguousarray(rmat_c) + + result = cpp_transforms.anglesToGVec(angs, beam_vec, eta_vec, chi, rmat_c) + + return result[0] if orig_ndim == 1 else result + + +def angles_to_dvec( + angs: NDArray[np.float64], + beam_vec: NDArray[np.float64] = cnst.beam_vec, + eta_vec: NDArray[np.float64] = cnst.eta_vec, + chi: float = 0.0, + rmat_c: NDArray[np.float64] = cnst.identity_3x3, +) -> NDArray[np.float64]: + """Calculate diffraction vectors from beam frame angles. + + Takes triplets of angles in the beam frame (2*theta, eta[, omega]) + to components of unit diffraction vectors in the LAB frame. If the omega + values are not trivial (i.e. angs[:, 2] = 0.), then the components + are in the SAMPLE frame. If the crystal rmat is specified and + is not the identity, then the components are in the CRYSTAL frame. + + + Parameters + ---------- + angs : ndarray + The euler angles of diffraction. The last dimension must be 2 or 3. In + (2*theta, eta[, omega]) format. + beam_vec : ndarray, optional + Unit vector pointing towards the X-ray source in the lab frame. + Defaults to [0,0,-1] + eta_vec : ndarray, optional + Vector defining eta=0 in the lab frame. Defaults to [1,0,0] + chi : float, optional + The inclination angle of the sample frame about the lab frame X. + rmat_c : ndarray, optional + The change of basis matrix from the reciprocal frame to the crystal + frame. Defaults to the identity. + + Returns + ------- + ndarray + (3,n) array of unit diffraction vectors, frame depends on the input + parameters + """ + # TODO: Improve capi to avoid multiplications when rmat_c is None + + # if only a pair is provided... convert to a triplet with omegas == 0 + # so that behavior is preserved. + if angs.shape[-1] == 2: + angs = np.hstack((angs, np.zeros(angs.shape[:-1] + (1,)))) + + angs = np.ascontiguousarray(np.atleast_2d(angs)) + beam_vec = np.ascontiguousarray(beam_vec.flatten()) + eta_vec = np.ascontiguousarray(eta_vec.flatten()) + rmat_c = np.ascontiguousarray(rmat_c) + + return cpp_transforms.anglesToDVec(angs, beam_vec, eta_vec, chi, rmat_c) + + +def makeGVector( + hkl: NDArray[np.float64], bMat: NDArray[np.float64] +) -> NDArray[np.float64]: + """ + Take a crystal relative b matrix onto a list of hkls to output unit + reciprocal latice vectors (a.k.a. lattice plane normals) + + + Parameters + ---------- + hkl : ndarray + (3,n) ndarray of n hstacked reciprocal lattice vector component + triplets + bMat : ndarray + (3,3) ndarray of the change of basis matrix from the reciprocal + lattice to the crystal reference frame + + Returns + ------- + ndarray + (3,n) ndarray of n unit reciprocal lattice vectors (a.k.a. lattice + plane normals) + + """ + if hkl.shape[0] != 3: + raise ValueError('hkl input must be (3, n)') + return unit_vector(np.dot(bMat, hkl)) + + +def gvec_to_xy( + gvec_c: NDArray[np.float64], + rmat_d: NDArray[np.float64], + rmat_s: NDArray[np.float64], + rmat_c: NDArray[np.float64], + tvec_d: NDArray[np.float64], + tvec_s: NDArray[np.float64], + tvec_c: NDArray[np.float64], + beam_vec: Optional[NDArray[np.float64]] = None, + vmat_inv: Optional[NDArray[np.float64]] = None, + bmat: Optional[NDArray[np.float64]] = None, +) -> NDArray[np.float64]: + """ + Takes a concatenated list of reciprocal lattice vectors components in the + CRYSTAL FRAME to the specified detector-relative frame, subject to the + following: + + 1) it must be able to satisfy a bragg condition + 2) the associated diffracted beam must intersect the detector plane + + Parameters + ---------- + gvec_c : array_like + ([N,] 3) G-vector components in the CRYSTAL FRAME. + rmat_d : array_like + The (3, 3) COB matrix taking components in the + DETECTOR FRAME to the LAB FRAME + rmat_s : array_like + The ([N,] 3, 3) COB matrix taking components in the SAMPLE FRAME to the + LAB FRAME. It may be a single (3, 3) rotation matrix to use for all + gvec_c, or just one rotation matrix per gvec. + rmat_c : array_like + The (3, 3) COB matrix taking components in the + CRYSTAL FRAME to the SAMPLE FRAME + tvec_d : array_like + The (3, ) translation vector connecting LAB FRAME to DETECTOR FRAME + tvec_s : array_like + The (3, ) translation vector connecting LAB FRAME to SAMPLE FRAME + tvec_c : array_like + The ([M,] 3, ) translation vector(s) connecting SAMPLE FRAME to + CRYSTAL FRAME + beam_vec : array_like, optional + The (3, ) incident beam propagation vector components in the LAB FRAME; + the default is [0, 0, -1], which is the standard setting. + vmat_inv : array_like, optional + The (3, 3) matrix of inverse stretch tensor components in the + SAMPLE FRAME. The default is None, which implies a strain-free state + (i.e. V = I). + bmat : array_like, optional + The (3, 3) COB matrix taking components in the + RECIPROCAL LATTICE FRAME to the CRYSTAL FRAME; if supplied, it is + assumed that the input `gvecs` are G-vector components in the + RECIPROCL LATTICE FRAME (the default is None, which implies components + in the CRYSTAL FRAME) + + Returns + ------- + array_like + The ([M, ][N, ] 2) array of [x, y] diffracted beam intersections for + each of the N input G-vectors in the DETECTOR FRAME (all Z_d + coordinates are 0 and excluded) and for each of the M candidate + positions. For each input G-vector that cannot satisfy a Bragg + condition or intersect the detector plane, [NaN, Nan] is returned. + + Notes + ----- + Previously only a single candidate position was allowed. This is in + fact a vectored version of the previous API function. It is backwards + compatible, as passing single tvec_c is supported and has the same + result. + + """ + beam_vec = beam_vec if beam_vec is not None else cnst.beam_vec + + orig_ndim = gvec_c.ndim + gvec_c = np.ascontiguousarray(np.atleast_2d(gvec_c)) + rmat_d = np.ascontiguousarray(rmat_d) + rmat_s = np.ascontiguousarray(rmat_s) + rmat_c = np.ascontiguousarray(rmat_c) + tvec_d = np.ascontiguousarray(tvec_d.flatten()) + tvec_s = np.ascontiguousarray(tvec_s.flatten()) + tvec_c = np.ascontiguousarray(tvec_c.flatten()) + beam_vec = np.ascontiguousarray(beam_vec.flatten()) + + # depending on the number of dimensions of rmat_s use either the array + # version or the "scalar" (over rmat_s) version. Note that rmat_s is either + # a 3x3 matrix (ndim 2) or an nx3x4 array of matrices (ndim 3) + if rmat_s.ndim > 2: + result = transforms_c_api.gvecToDetectorXYArray( + gvec_c, rmat_d, rmat_s, rmat_c, tvec_d, tvec_s, tvec_c, beam_vec + ) + else: + result = transforms_c_api.gvecToDetectorXY( + gvec_c, rmat_d, rmat_s, rmat_c, tvec_d, tvec_s, tvec_c, beam_vec + ) + return result[0] if orig_ndim == 1 else result + + +def xy_to_gvec( + xy_d: NDArray[np.float64], + rmat_d: NDArray[np.float64], + rmat_s: NDArray[np.float64], + tvec_d: NDArray[np.float64], + tvec_s: NDArray[np.float64], + tvec_c: NDArray[np.float64], + rmat_b: Optional[NDArray[np.float64]] = None, + distortion: Optional[DistortionABC] = None, + output_ref: Optional[bool] = False, +) -> tuple[NDArray[np.float64], ...]: + """ + Takes a list cartesian (x, y) pairs in the DETECTOR FRAME and calculates + the associated reciprocal lattice (G) vectors and (bragg angle, azimuth) + pairs with respect to the specified beam and azimth (eta) reference + directions. + + Parameters + ---------- + xy_d : array_like + (n, 2) array of n (x, y) coordinates in DETECTOR FRAME + rmat_d : array_like + (3, 3) COB matrix taking components in the + DETECTOR FRAME to the LAB FRAME + rmat_s : array_like + (3, 3) COB matrix taking components in the + SAMPLE FRAME to the LAB FRAME + tvec_d : array_like + (3, ) translation vector connecting LAB FRAME to DETECTOR FRAME + tvec_s : array_like + (3, ) translation vector connecting LAB FRAME to SAMPLE FRAME + tvec_c : array_like + (3, ) translation vector connecting SAMPLE FRAME to CRYSTAL FRAME + rmat_b : array_like, optional + (3, 3) COB matrix taking components in the BEAM FRAME to the LAB FRAME; + defaults to None, which implies the standard setting of identity. + distortion : distortion class, optional + Default is None + output_ref : bool, optional + If True, prepends the apparent bragg angle and azimuth with respect to + the SAMPLE FRAME (ignoring effect of non-zero tvec_c) + + Returns + ------- + array_like + (n, 2) ndarray containing the (tth, eta) pairs associated with each + (x, y) associated with gVecs + array_like + (n, 3) ndarray containing the associated G vector directions in the + LAB FRAME + array_like, optional + if output_ref is True + """ + # It seems that the output_ref version is not present as the argument + # gets ignored + + rmat_b = rmat_b if rmat_b is not None else cnst.identity_3x3 + + # the code seems to ignore this argument, assume output_ref == True not + # implemented + assert not output_ref, 'output_ref not implemented' + + if distortion is not None: + xy_d = distortion.apply_inverse(xy_d) + + xy_d = np.ascontiguousarray(np.atleast_2d(xy_d)) + rmat_d = np.ascontiguousarray(rmat_d) + rmat_s = np.ascontiguousarray(rmat_s) + tvec_d = np.ascontiguousarray(tvec_d.flatten()) + tvec_s = np.ascontiguousarray(tvec_s.flatten()) + tvec_c = np.ascontiguousarray(tvec_c.flatten()) + beam_vec = np.ascontiguousarray((-rmat_b[:, 2]).flatten()) + eta_vec = np.ascontiguousarray(rmat_b[:, 0].flatten()) + return transforms_c_api.detectorXYToGvec( + xy_d, rmat_d, rmat_s, tvec_d, tvec_s, tvec_c, beam_vec, eta_vec + ) + + +def oscill_angles_of_hkls( + hkls: NDArray[np.float64], + chi: float, + rmat_c: NDArray[np.float64], + bmat: NDArray[np.float64], + wavelength: float, + v_inv: Optional[NDArray[np.float64]] = None, + beam_vec: NDArray[np.float64] = cnst.beam_vec, + eta_vec: NDArray[np.float64] = cnst.eta_vec, +) -> Tuple[NDArray[np.float64], NDArray[np.float64]]: + """ + Takes a list of unit reciprocal lattice vectors in crystal frame to the + specified detector-relative frame, subject to the conditions: + + 1) the reciprocal lattice vector must be able to satisfy a bragg condition + 2) the associated diffracted beam must intersect the detector plane + + Parameters + ---------- + hkls -- (n, 3) ndarray of n reciprocal lattice vectors + in the CRYSTAL FRAME + chi -- float representing the inclination angle of the + oscillation axis (std coords) + rmat_c -- (3, 3) ndarray, the COB taking CRYSTAL FRAME components + to SAMPLE FRAME + bmat -- (3, 3) ndarray, the COB taking RECIPROCAL LATTICE components + to CRYSTAL FRAME + wavelength -- float representing the x-ray wavelength in Angstroms + + Optional Keyword Arguments: + v_inv -- (6, ) ndarray, vec representation inverse stretch tensor + components in the SAMPLE FRAME. The default is None, which + implies a strain-free state (i.e. V = I). + beam_vec -- (3, 1) mdarray containing the incident beam direction + components in the LAB FRAME + eta_vec -- (3, 1) mdarray containing the reference azimuth direction + components in the LAB FRAME + + Outputs: + ome0 -- (n, 3) ndarray containing the feasible (tTh, eta, ome) triplets + for each input hkl (first solution) + ome1 -- (n, 3) ndarray containing the feasible (tTh, eta, ome) triplets + for each input hkl (second solution) + + Notes: + ------------------------------------------------------------------------ + The reciprocal lattice vector, G, will satisfy the the Bragg condition + when: + + b.T * G / ||G|| = -sin(theta) + + where b is the incident beam direction (k_i) and theta is the Bragg + angle consistent with G and the specified wavelength. The components of + G in the lab frame in this case are obtained using the crystal + orientation, Rc, and the single-parameter oscillation matrix, Rs(ome): + + Rs(ome) * Rc * G / ||G|| + + The equation above can be rearranged to yield an expression of the form: + + a*sin(ome) + b*cos(ome) = c + + which is solved using the relation: + + a*sin(x) + b*cos(x) = sqrt(a**2 + b**2) * sin(x + alpha) + + --> sin(x + alpha) = c / sqrt(a**2 + b**2) + + where: + + alpha = atan2(b, a) + + The solutions are: + + / + | arcsin(c / sqrt(a**2 + b**2)) - alpha + x = < + | pi - arcsin(c / sqrt(a**2 + b**2)) - alpha + \ + + There is a double root in the case the reflection is tangent to the + Debye-Scherrer cone (c**2 = a**2 + b**2), and no solution if the + Laue condition cannot be satisfied (filled with NaNs in the results + array here) + """ + # this was oscillAnglesOfHKLs + hkls = np.array(hkls, dtype=float, order='C') + if v_inv is None: + v_inv = np.array([1.0, 1.0, 1.0, 0.0, 0.0, 0.0]) + else: + v_inv = np.ascontiguousarray(v_inv.flatten()) + rmat_c = np.ascontiguousarray(rmat_c) + beam_vec = np.ascontiguousarray(beam_vec.flatten()) + eta_vec = np.ascontiguousarray(eta_vec.flatten()) + bmat = np.ascontiguousarray(bmat) + return transforms_c_api.oscillAnglesOfHKLs( + hkls, chi, rmat_c, bmat, wavelength, v_inv, beam_vec, eta_vec + ) + + +def unit_vector(vec_in: NDArray[np.float64]) -> NDArray[np.float64]: + """ + Normalize the input vector(s) to unit length. + + Parameters + ---------- + vec_in : ndarray + The input vector(s) (n,3) to normalize. + + Returns + ------- + ndarray + The normalized vector(s) of the same shape as the input. + + Raises + ------ + ValueError + If the input vector(s) do not have the correct shape. + """ + vec_in = np.ascontiguousarray(vec_in) + if vec_in.ndim == 1: + return transforms_c_api.unitRowVector(vec_in) + elif vec_in.ndim == 2: + return transforms_c_api.unitRowVectors(vec_in) + else: + raise ValueError( + "incorrect arg shape; must be 1-d or 2-d, yours is %d-d" % (vec_in.ndim) + ) + + +def make_detector_rmat(tilt_angles: NDArray[np.float64]) -> NDArray[np.float64]: + """ + Form the (3, 3) tilt rotations from the tilt angle list: + + tilt_angles = [gamma_Xl, gamma_Yl, gamma_Zl] in radians + Returns the (3, 3) rotation matrix from the detector frame to the lab frame + """ + t_angles = np.ascontiguousarray(np.r_[tilt_angles].flatten()) + return transforms_c_api.makeDetectorRotMat(t_angles) + + +# make_sample_rmat in CAPI is split between makeOscillRotMat +# and makeOscillRotMatArray... + + +def make_oscill_rmat(oscillAngles): + chi, ome = oscillAngles + ome = np.atleast_1d(ome) + result = transforms_c_api.makeOscillRotMat(chi, ome) + return result.reshape((3, 3)) + + +def make_oscill_rmat_array(chi, omeArray): + arg = np.ascontiguousarray(omeArray) + return transforms_c_api.makeOscillRotMat(chi, arg) + + +def make_sample_rmat( + chi: float, ome: float | NDArray[np.float64] +) -> NDArray[np.float64]: + # TODO: Check this docstring + """ + Make a rotation matrix representing the COB from sample frame to the lab + frame. + + Parameters + ---------- + chi : float + The inclination angle of the sample frame about the lab frame Y. + ome : float or ndarray + The oscillation angle(s) about the sample frame Y. + + Returns + ------- + ndarray + A 3x3 rotation matrix representing the input oscillation angles. + + """ + ome_array = np.atleast_1d(ome) + if ome is ome_array: + ome_array = np.ascontiguousarray(ome_array) + result = cpp_transforms.makeOscillRotMat(chi, ome_array).reshape( + len(ome_array), 3, 3 + ) + else: + # converted to 1d array of 1 element, no need + # to call ascontiguousarray, but need to remove + # the outer dimension from the result + result = cpp_transforms.makeOscillRotMat(chi, ome_array) + + return result + + +def make_rmat_of_expmap( + exp_map: Sequence[float] | NDArray[np.float64], +) -> NDArray[np.float64]: + """ + Calculate the rotation matrix of an exponential map. + + Parameters + ---------- + exp_map : array_like + A 3-element sequence representing the exponential map n*phi. + + Returns + ------- + NDArray[np.float64] + A 3x3 rotation matrix representing the input exponential map + """ + arg = np.ascontiguousarray(exp_map).flatten() + return cpp_transforms.make_rot_mat_of_exp_map(arg) + + +def make_binary_rmat(axis: NDArray[np.float64]) -> NDArray[np.float64]: + """ + Create a 180 degree rotation matrix about the input axis. + + Parameters + ---------- + axis : ndarray + 3-element sequence representing the rotation axis, in radians. + + Returns + ------- + ndarray + A 3x3 rotation matrix representing a 180 degree rotation about the + input axis. + """ + + arg = np.ascontiguousarray(axis.flatten()) + return cpp_transforms.make_binary_rot_mat(arg) + + +def make_beam_rmat( + bvec_l: NDArray[np.float64], evec_l: NDArray[np.float64] +) -> NDArray[np.float64]: + """ + Creates a COB matrix from the beam frame to the lab frame + Note: beam and eta vectors must not be colinear + + Parameters + ---------- + bvec_l : ndarray + The beam vector in the lab frame, The (3, ) incident beam propagation + vector components in the lab frame. + the default is [0, 0, -1], which is the standard setting. + evec_l : ndarray + Vector defining eta=0 in the lab frame. Defaults to [1,0,0] + """ + arg1 = np.ascontiguousarray(bvec_l.flatten()) + arg2 = np.ascontiguousarray(evec_l.flatten()) + return transforms_c_api.makeEtaFrameRotMat(arg1, arg2) + + +def validate_angle_ranges( + ang_list: float | NDArray[np.float64], + start_angs: float | NDArray[np.float64], + stop_angs: float | NDArray[np.float64], + ccw: bool = True, +) -> NDArray[np.bool_]: + """ + Find out if angles are in the CCW or CW range from start to stop + + Parameters + ---------- + ang_list : ndarray + The list of angles to validate + start_angs : ndarray + The starting angles + stop_angs : ndarray + The stopping angles + ccw : bool, optional + If True, the angles are in the CCW range from start to stop. + Defaults to True. + + Returns + ------- + ndarray + List of bools indicating if the angles are in the correct range + + """ + ang_list = np.atleast_1d(ang_list).astype(np.double, order='C') + start_angs = np.atleast_1d(start_angs).astype(np.double, order='C') + stop_angs = np.atleast_1d(stop_angs).astype(np.double, order='C') + + return transforms_c_api.validateAngleRanges(ang_list, start_angs, stop_angs, ccw) + + +def rotate_vecs_about_axis( + angle: float | NDArray[np.float64], + axis: NDArray[np.float64], + vecs: NDArray[np.float64], +) -> NDArray[np.float64]: + """ + Rotate vectors about an axis + + Parameters + ---------- + angle : array_like + Array of angles (len==1 or n) + axis : ndarray + Array of unit vectors (shape == (3,1) or (3,n)) + vecs : ndarray + Array of vectors to rotate (shape == (3,n)) + + Returns + ------- + ndarray + Array of rotated vectors (shape == (3,n)) + """ + angle = np.asarray(angle) + axis = np.ascontiguousarray(axis.T) + vecs = np.ascontiguousarray(vecs.T) + result = transforms_c_api.rotate_vecs_about_axis(angle, axis, vecs) + return result.T + + +def quat_distance( + q1: NDArray[np.float64], q2: NDArray[np.float64], qsym: NDArray[np.float64] +) -> NDArray[np.float64]: + """Distance between two quaternions, taking quaternions of symmetry into account. + + Parameters + ---------- + q1 : arary_like + First quaternion. + q2 : arary_like + Second quaternion. + qsym : ndarray + List of symmetry quaternions. + + Returns + ------- + float + The distance between the two quaternions. + """ + q1 = np.ascontiguousarray(q1.flatten()) + q2 = np.ascontiguousarray(q2.flatten()) + # C module expects quaternions in row major, numpy code in column major. + qsym = np.ascontiguousarray(qsym.T) + return transforms_c_api.quat_distance(q1, q2, qsym) diff --git a/hexrd/powder/wppf/texture.py b/hexrd/powder/wppf/texture.py index ad39b22ee..716a8fa77 100644 --- a/hexrd/powder/wppf/texture.py +++ b/hexrd/powder/wppf/texture.py @@ -5,7 +5,7 @@ # hexrd imports # ------------- -from hexrd.core.transforms.new_capi.xf_new_capi import angles_to_gvec +from hexrd.core.transforms.xfcapi import angles_to_gvec from hexrd.core import constants from hexrd.powder.wppf.phase import Material_Rietveld diff --git a/tests/transforms/common.py b/tests/transforms/common.py index 3afa7b7a0..997815355 100644 --- a/tests/transforms/common.py +++ b/tests/transforms/common.py @@ -3,7 +3,7 @@ import numpy as np import hexrd.core.constants as ct -from hexrd.core.transforms.new_capi.xf_new_capi import unit_vector +from hexrd.core.transforms.xfcapi import unit_vector def convert_axis_angle_to_rmat(axis, angle): diff --git a/tests/transforms/test_angles_to_dvec_from_file.py b/tests/transforms/test_angles_to_dvec_from_file.py index c4f8a7c0e..5ab67a585 100644 --- a/tests/transforms/test_angles_to_dvec_from_file.py +++ b/tests/transforms/test_angles_to_dvec_from_file.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import angles_to_dvec +from hexrd.core.transforms.xfcapi import angles_to_dvec # from common import random_rotation_matrix, random_unit_vectors diff --git a/tests/transforms/test_angles_to_gvec_from_file.py b/tests/transforms/test_angles_to_gvec_from_file.py index 0273d7a40..7ed9c66dc 100644 --- a/tests/transforms/test_angles_to_gvec_from_file.py +++ b/tests/transforms/test_angles_to_gvec_from_file.py @@ -5,7 +5,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import angles_to_gvec +from hexrd.core.transforms.xfcapi import angles_to_gvec # from common import random_rotation_matrix, random_unit_vectors diff --git a/tests/transforms/test_gvec_to_xy.py b/tests/transforms/test_gvec_to_xy.py index a681ca01d..a12db71e1 100644 --- a/tests/transforms/test_gvec_to_xy.py +++ b/tests/transforms/test_gvec_to_xy.py @@ -12,7 +12,7 @@ from common import convert_axis_angle_to_rmat -from hexrd.core.transforms.new_capi.xf_new_capi import gvec_to_xy +from hexrd.core.transforms.xfcapi import gvec_to_xy # gvec_to_xy intersects vectors from crystal position with the detector plane. diff --git a/tests/transforms/test_gvec_to_xy_from_file.py b/tests/transforms/test_gvec_to_xy_from_file.py index 9f9bd5ebc..6563d0abb 100644 --- a/tests/transforms/test_gvec_to_xy_from_file.py +++ b/tests/transforms/test_gvec_to_xy_from_file.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import gvec_to_xy +from hexrd.core.transforms.xfcapi import gvec_to_xy # from common import random_rotation_matrix, random_unit_vectors diff --git a/tests/transforms/test_make_beam_rmat_from_file.py b/tests/transforms/test_make_beam_rmat_from_file.py index c3c8ea7bd..851ccb6fc 100644 --- a/tests/transforms/test_make_beam_rmat_from_file.py +++ b/tests/transforms/test_make_beam_rmat_from_file.py @@ -5,7 +5,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import make_beam_rmat +from hexrd.core.transforms.xfcapi import make_beam_rmat # from common import random_unit_vectors diff --git a/tests/transforms/test_make_binary_rmat.py b/tests/transforms/test_make_binary_rmat.py index ff809cb12..5e64b48c1 100644 --- a/tests/transforms/test_make_binary_rmat.py +++ b/tests/transforms/test_make_binary_rmat.py @@ -5,8 +5,8 @@ from __future__ import absolute_import import numpy as np -from hexrd.transforms.new_capi.xf_new_capi import make_binary_rmat -from hexrd.rotations import quatOfAngleAxis, rotMatOfQuat +from hexrd.core.transforms.xfcapi import make_binary_rmat +from hexrd.core.rotations import quatOfAngleAxis, rotMatOfQuat from transforms.common import random_unit_vectors diff --git a/tests/transforms/test_make_detector_rmat_from_file.py b/tests/transforms/test_make_detector_rmat_from_file.py index 7d8343ee1..1cea16592 100644 --- a/tests/transforms/test_make_detector_rmat_from_file.py +++ b/tests/transforms/test_make_detector_rmat_from_file.py @@ -5,7 +5,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import make_detector_rmat +from hexrd.core.transforms.xfcapi import make_detector_rmat def test_make_detector_rmat_from_file(test_data_dir): diff --git a/tests/transforms/test_make_rmat_of_expmap_from_file.py b/tests/transforms/test_make_rmat_of_expmap_from_file.py index 678194a68..dcb9d7723 100644 --- a/tests/transforms/test_make_rmat_of_expmap_from_file.py +++ b/tests/transforms/test_make_rmat_of_expmap_from_file.py @@ -5,7 +5,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import make_rmat_of_expmap +from hexrd.core.transforms.xfcapi import make_rmat_of_expmap def test_make_rmat_of_expmap_from_file(test_data_dir): diff --git a/tests/transforms/test_make_sample_rmat_from_file.py b/tests/transforms/test_make_sample_rmat_from_file.py index 49657994e..2a1fecf2b 100644 --- a/tests/transforms/test_make_sample_rmat_from_file.py +++ b/tests/transforms/test_make_sample_rmat_from_file.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import make_sample_rmat +from hexrd.core.transforms.xfcapi import make_sample_rmat def test_make_sample_rmat_from_file(test_data_dir): diff --git a/tests/transforms/test_quat_distance_from_file.py b/tests/transforms/test_quat_distance_from_file.py index b7d35e655..55be3f0b5 100644 --- a/tests/transforms/test_quat_distance_from_file.py +++ b/tests/transforms/test_quat_distance_from_file.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import quat_distance +from hexrd.core.transforms.xfcapi import quat_distance # from common import random_unit_vectors # from hexrd.core.rotations import quatOfLaueGroup diff --git a/tests/transforms/test_rotate_vecs_about_axis.py b/tests/transforms/test_rotate_vecs_about_axis.py index 94200285f..d6f0df1e4 100644 --- a/tests/transforms/test_rotate_vecs_about_axis.py +++ b/tests/transforms/test_rotate_vecs_about_axis.py @@ -1,4 +1,4 @@ -from hexrd.core.transforms.new_capi.xf_new_capi import rotate_vecs_about_axis +from hexrd.core.transforms.xfcapi import rotate_vecs_about_axis import numpy as np diff --git a/tests/transforms/test_validate_angle_ranges_from_file.py b/tests/transforms/test_validate_angle_ranges_from_file.py index 6c43012c4..f2d3fb0b9 100644 --- a/tests/transforms/test_validate_angle_ranges_from_file.py +++ b/tests/transforms/test_validate_angle_ranges_from_file.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import validate_angle_ranges +from hexrd.core.transforms.xfcapi import validate_angle_ranges def test_validate_angle_ranges_from_file(test_data_dir): diff --git a/tests/transforms/test_xy_to_gvec.py b/tests/transforms/test_xy_to_gvec.py index 54cddf1cf..899a71afd 100644 --- a/tests/transforms/test_xy_to_gvec.py +++ b/tests/transforms/test_xy_to_gvec.py @@ -9,7 +9,7 @@ from collections import namedtuple import pytest import numpy as np -from hexrd.core.transforms.new_capi.xf_new_capi import xy_to_gvec +from hexrd.core.transforms.xfcapi import xy_to_gvec Experiment = namedtuple(