Source code for neutronbraggedge.experiment_handler.experiment

import numpy as np
from numpy.typing import ArrayLike, NDArray

from ..constants import h, mn
from ..utilities import Utilities


[docs] class Experiment: """Class that allows: - convert the TOF scale into Lambda - distance source - detector - detector time offset Arguments: * tof: tof array in s * lambda_array: mandatory only if detector_offset or distance_source_detector are unknown * distance_source_detector: mandatory only if lambda is the unknown parameter (m) * detector_offset: mandatory only if lambda is the unknown parameter (micros) """ tof_array: NDArray[np.floating] distance_source_detector: float | None detector_offset_micros: float | None lambda_array: NDArray[np.floating] | None _h_over_MnLds: float
[docs] def __init__( self, tof: ArrayLike | None = None, lambda_array: ArrayLike | None = None, distance_source_detector_m: float | None = None, detector_offset_micros: float | None = None, ) -> None: if tof is None: raise ValueError("Missing TOF array") self.tof_array = np.array(tof) if not isinstance(tof, np.ndarray) else tof self.distance_source_detector = distance_source_detector_m self.detector_offset_micros = detector_offset_micros if lambda_array is not None and not isinstance(lambda_array, np.ndarray): self.lambda_array = np.array(lambda_array) else: self.lambda_array = lambda_array # if lambda_array is unknown, both distance_source_detector and detector_offset must be provided if lambda_array is None: if (distance_source_detector_m is None) or (detector_offset_micros is None): raise ValueError("Mssing arguments to calculate lambda_array") # if lambda_array is provided, either distance_source_detector_m or detector_offset_micros can be missing, # but not both if lambda_array is not None: if (distance_source_detector_m is None) and (detector_offset_micros is None): raise ValueError("Missing either distance_source detector or detector_offset") if len(lambda_array) != len(tof): raise ValueError("TOF and Lambda do not have the same size !") if distance_source_detector_m is None: self.calculate_distance_source_detector() else: self.calculate_detector_offset() else: self.calculate_lambda()
[docs] def calculate_tof_with_detector_offset(self) -> NDArray[np.floating]: """return the tof with detector_offset applied to it""" detector_offset_micros = self.detector_offset_micros detector_offset_s = Utilities.convert_time_units(detector_offset_micros, from_units="micros", to_units="s") # apply detector offset to tof array _tof = self.tof_array _tof_with_offset: NDArray[np.floating] = Utilities.array_add_coeff(data=_tof, coeff=detector_offset_s) return _tof_with_offset
[docs] def calculate_distance_source_detector(self) -> None: """return the distance source detector If lambda_array and tof_array are provided, the distance is calculated Otherwise, the distance_source_detector must be provided """ _tof_with_offset = self.calculate_tof_with_detector_offset() # calculate the constant factor _coeff = h / mn # multiply constant facor by tof array _numerator = Utilities.array_multiply_coeff(data=_tof_with_offset, coeff=_coeff) _denominator = self.lambda_array # divide numerator by denominator _ratio = Utilities.array_divide_array(numerator=_numerator, denominator=_denominator) self.distance_source_detector = float(np.mean(_ratio))
[docs] def calculate_detector_offset(self) -> None: """return the detector time offset value If lambda_array and tof_array are provided, the offset is calculated Otherwise, the detector_offset argument must be provided """ # calculate the constant factor lSD = self.distance_source_detector _coeff = h / (mn * lSD) _MnLds_over_h = 1.0 / _coeff # lambda / coeff _lambda_array = self.lambda_array _lambda_over_coeff = Utilities.array_multiply_coeff(data=_lambda_array, coeff=_MnLds_over_h) # (lambda/coeff) - tof _tof = self.tof_array _detector_offset_array = Utilities.array_minus_array(array1=_lambda_over_coeff, array2=_tof) _detector_offset_s = np.mean(_detector_offset_array) self.detector_offset_micros = Utilities.convert_time_units( data=_detector_offset_s, from_units="s", to_units="micros" )
[docs] def calculate_lambda(self) -> None: """return the lambda array when tof_array, distance_source_detector and detector_offset are provided """ _tof_with_offset = self.calculate_tof_with_detector_offset() # calculate the constant factor lSD = self.distance_source_detector _coeff = h / (mn * lSD) self._h_over_MnLds = _coeff # multiply constant factor by tof array _lambda: NDArray[np.floating] = Utilities.array_multiply_coeff(data=_tof_with_offset, coeff=_coeff) self.lambda_array = _lambda
[docs] def export_lambda(self, filename: str | None = None) -> None: """export the lambda array into a CSV data file Parameters: * filename: name of output file to create """ if filename is None: raise ValueError("Please provide a file name!") _metadata: list[str] = [] _metadata.append("Lambda (Angstroms)") _metadata.append("") _metadata.append("Distance source-detector (m): %.4f" % self.distance_source_detector) _metadata.append("Detector offset (micros): %.4f" % self.detector_offset_micros) _metadata.append("") _metadata.append("Lambda (Angstroms)") Utilities.save_csv(filename=filename, data=self.lambda_array, metadata=_metadata)