Mercurial > repos > kls286 > chap_test_20230328
comparison build/lib/CHAP/models/edd.py @ 0:cbbe42422d56 draft
planemo upload for repository https://github.com/CHESSComputing/ChessAnalysisPipeline/tree/galaxy commit 1401a7e1ae007a6bda260d147f9b879e789b73e0-dirty
| author | kls286 |
|---|---|
| date | Tue, 28 Mar 2023 15:07:30 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:cbbe42422d56 |
|---|---|
| 1 from msnctools.general import create_mask | |
| 2 from msnctools.material import Material | |
| 3 from msnctools.scanparsers import SMBMCAScanParser as ScanParser | |
| 4 import numpy as np | |
| 5 from pathlib import PosixPath | |
| 6 from pydantic import (BaseModel, | |
| 7 confloat, | |
| 8 conint, | |
| 9 conlist, | |
| 10 constr, | |
| 11 FilePath, | |
| 12 validator) | |
| 13 from scipy.interpolate import interp1d | |
| 14 from typing import Optional | |
| 15 | |
| 16 | |
| 17 class MCACeriaCalibrationConfig(BaseModel): | |
| 18 '''Class representing metadata required to perform a Ceria calibration for an | |
| 19 MCA detector. | |
| 20 | |
| 21 :ivar spec_file: Path to the SPEC file containing the CeO2 scan | |
| 22 :ivar scan_number: Number of the CeO2 scan in `spec_file` | |
| 23 :ivar scan_step_index: Index of the scan step to use for calibration, | |
| 24 optional. If not specified, the calibration routine will be performed on | |
| 25 the average of all MCA spectra for the scan. | |
| 26 | |
| 27 :ivar flux_file: csv file containing station beam energy in eV (column 0) | |
| 28 and flux (column 1) | |
| 29 | |
| 30 :ivar detector_name: name of the MCA to calibrate | |
| 31 :ivar num_bins: number of channels on the MCA to calibrate | |
| 32 :ivar max_energy_kev: maximum channel energy of the MCA in keV | |
| 33 | |
| 34 :ivar hexrd_h5_material_file: path to a HEXRD materials.h5 file containing an | |
| 35 entry for the material properties. | |
| 36 :ivar hexrd_h5_material_name: Name of the material entry in | |
| 37 `hexrd_h5_material_file`, defaults to `'CeO2'`. | |
| 38 :ivar lattice_parameter_angstrom: lattice spacing in angstrom to use for | |
| 39 the cubic CeO2 crystal, defaults to `5.41153`. | |
| 40 | |
| 41 :ivar tth_max: detector rotation about hutch x axis, defaults to `90`. | |
| 42 :ivar hkl_tth_tol: minimum resolvable difference in 2&theta between two | |
| 43 unique HKL peaks, defaults to `0.15`. | |
| 44 | |
| 45 :ivar fit_include_bin_ranges: list of MCA channel index ranges whose data | |
| 46 will be included in the calibration routine | |
| 47 :ivar fit_hkls: list of unique HKL indices to fit peaks for in the | |
| 48 calibration routine | |
| 49 | |
| 50 :ivar tth_initial_guess: initial guess for 2&theta | |
| 51 :ivar slope_initial_guess: initial guess for detector channel energy | |
| 52 correction linear slope, defaults to `1.0`. | |
| 53 :ivar intercept_initial_guess: initial guess for detector channel energy | |
| 54 correction y-intercept, defaults to `0.0`. | |
| 55 | |
| 56 :ivar tth_calibrated: calibrated value for 2&theta, defaults to None | |
| 57 :ivar slope_calibrated: calibrated value for detector channel energy | |
| 58 correction linear slope, defaults to `None` | |
| 59 :ivar intercept_calibrated: calibrated value for detector channel energy | |
| 60 correction y-intercept, defaluts to None | |
| 61 | |
| 62 :ivar max_iter: maximum number of iterations of the calibration routine, | |
| 63 defaults to `10`. | |
| 64 :ivar tune_tth_tol: stop iteratively tuning 2&theta when an iteration | |
| 65 produces a change in the tuned value of 2&theta that is smaller than this | |
| 66 value, defaults to `1e-8`. | |
| 67 ''' | |
| 68 | |
| 69 spec_file: FilePath | |
| 70 scan_number: conint(gt=0) | |
| 71 scan_step_index: Optional[conint(ge=0)] | |
| 72 | |
| 73 flux_file: FilePath | |
| 74 | |
| 75 detector_name: constr(strip_whitespace=True, min_length=1) | |
| 76 num_bins: conint(gt=0) | |
| 77 max_energy_kev: confloat(gt=0) | |
| 78 | |
| 79 hexrd_h5_material_file: FilePath | |
| 80 hexrd_h5_material_name: constr(strip_whitespace=True, min_length=1) = 'CeO2' | |
| 81 lattice_parameter_angstrom: confloat(gt=0) = 5.41153 | |
| 82 | |
| 83 tth_max: confloat(gt=0, allow_inf_nan=False) = 90.0 | |
| 84 hkl_tth_tol: confloat(gt=0, allow_inf_nan=False) = 0.15 | |
| 85 | |
| 86 fit_include_bin_ranges: conlist(min_items=1, | |
| 87 item_type=conlist(item_type=conint(ge=0), | |
| 88 min_items=2, | |
| 89 max_items=2)) | |
| 90 fit_hkls: conlist(item_type=conint(ge=0), min_items=1) | |
| 91 | |
| 92 tth_initial_guess: confloat(gt=0, le=tth_max, allow_inf_nan=False) | |
| 93 slope_initial_guess: float = 1.0 | |
| 94 intercept_initial_guess: float = 0.0 | |
| 95 tth_calibrated: Optional[confloat(gt=0, allow_inf_nan=False)] | |
| 96 slope_calibrated: Optional[confloat(allow_inf_nan=False)] | |
| 97 intercept_calibrated: Optional[confloat(allow_inf_nan=False)] | |
| 98 | |
| 99 max_iter: conint(gt=0) = 10 | |
| 100 tune_tth_tol: confloat(ge=0) = 1e-8 | |
| 101 | |
| 102 @validator('fit_include_bin_ranges', each_item=True) | |
| 103 def validate_include_bin_range(cls, value, values): | |
| 104 '''Ensure no bin ranges are outside the boundary of the detector''' | |
| 105 | |
| 106 num_bins = values.get('num_bins') | |
| 107 value[1] = min(value[1], num_bins) | |
| 108 return(value) | |
| 109 | |
| 110 def mca_data(self): | |
| 111 '''Get the 1D array of MCA data to use for calibration. | |
| 112 | |
| 113 :return: MCA data | |
| 114 :rtype: np.ndarray | |
| 115 ''' | |
| 116 | |
| 117 scanparser = ScanParser(self.spec_file, self.scan_number) | |
| 118 if self.scan_step_index is None: | |
| 119 data = scanparser.get_all_detector_data(self.detector_name) | |
| 120 if scanparser.spec_scan_npts > 1: | |
| 121 data = np.average(data, axis=1) | |
| 122 else: | |
| 123 data = data[0] | |
| 124 else: | |
| 125 data = scanparser.get_detector_data(self.detector_name, self.scan_step_index) | |
| 126 | |
| 127 return(np.array(data)) | |
| 128 | |
| 129 def mca_mask(self): | |
| 130 '''Get a boolean mask array to use on MCA data before fitting. | |
| 131 | |
| 132 :return: boolean mask array | |
| 133 :rtype: numpy.ndarray | |
| 134 ''' | |
| 135 | |
| 136 mask = None | |
| 137 bin_indices = np.arange(self.num_bins) | |
| 138 for bin_range in self.fit_include_bin_ranges: | |
| 139 mask = create_mask(bin_indices, | |
| 140 bounds=bin_range, | |
| 141 exclude_bounds=False, | |
| 142 current_mask=mask) | |
| 143 | |
| 144 return(mask) | |
| 145 | |
| 146 def flux_correction_interpolation_function(self): | |
| 147 '''Get an interpolation function to correct MCA data for relative energy | |
| 148 flux of the incident beam. | |
| 149 | |
| 150 :return: energy flux correction interpolation function | |
| 151 :rtype: scipy.interpolate._polyint._Interpolator1D | |
| 152 ''' | |
| 153 | |
| 154 flux = np.loadtxt(self.flux_file) | |
| 155 energies = flux[:,0]/1.e3 | |
| 156 relative_intensities = flux[:,1]/np.max(flux[:,1]) | |
| 157 interpolation_function = interp1d(energies, relative_intensities) | |
| 158 return(interpolation_function) | |
| 159 | |
| 160 def material(self): | |
| 161 '''Get CeO2 as a `msnctools.materials.Material` object. | |
| 162 | |
| 163 :return: CeO2 material | |
| 164 :rtype: msnctools.material.Material | |
| 165 ''' | |
| 166 | |
| 167 material = Material(material_name=self.hexrd_h5_material_name, | |
| 168 material_file=self.hexrd_h5_material_file, | |
| 169 lattice_parameters_angstroms=self.lattice_parameter_angstrom) | |
| 170 # The following kwargs will be needed if we allow the material to be | |
| 171 # built using xrayutilities (for now, we only allow hexrd to make the | |
| 172 # material): | |
| 173 # sgnum=225, | |
| 174 # atoms=['Ce4p', 'O2mdot'], | |
| 175 # pos=[(0.,0.,0.), (0.25,0.75,0.75)], | |
| 176 # enrgy=50000.) # Why do we need to specify an energy to get HKLs when using xrayutilities? | |
| 177 return(material) | |
| 178 | |
| 179 def unique_ds(self): | |
| 180 '''Get a list of unique HKLs and their lattice spacings | |
| 181 | |
| 182 :return: unique HKLs and their lattice spacings in angstroms | |
| 183 :rtype: np.ndarray, np.ndarray | |
| 184 ''' | |
| 185 | |
| 186 unique_hkls, unique_ds = self.material().get_unique_ds(tth_tol=self.hkl_tth_tol, tth_max=self.tth_max) | |
| 187 | |
| 188 return(unique_hkls, unique_ds) | |
| 189 | |
| 190 def fit_ds(self): | |
| 191 '''Get a list of HKLs and their lattice spacings that will be fit in the | |
| 192 calibration routine | |
| 193 | |
| 194 :return: HKLs to fit and their lattice spacings in angstroms | |
| 195 :rtype: np.ndarray, np.ndarray | |
| 196 ''' | |
| 197 | |
| 198 unique_hkls, unique_ds = self.unique_ds() | |
| 199 | |
| 200 fit_hkls = np.array([unique_hkls[i] for i in self.fit_hkls]) | |
| 201 fit_ds = np.array([unique_ds[i] for i in self.fit_hkls]) | |
| 202 | |
| 203 return(fit_hkls, fit_ds) | |
| 204 | |
| 205 def dict(self): | |
| 206 '''Return a representation of this configuration in a dictionary that is | |
| 207 suitable for dumping to a YAML file (one that converts all instances of | |
| 208 fields with type `PosixPath` to `str`). | |
| 209 | |
| 210 :return: dictionary representation of the configuration. | |
| 211 :rtype: dict | |
| 212 ''' | |
| 213 | |
| 214 d = super().dict() | |
| 215 for k,v in d.items(): | |
| 216 if isinstance(v, PosixPath): | |
| 217 d[k] = str(v) | |
| 218 return(d) |
