def cake_saxs(inpaints, ais, masks, radial_range=(0, 60), azimuth_range=(-90, 90), npt_rad=250, npt_azim=250): """ Unwrapp the stitched image from q-space to 2theta-Chi space (Radial-Azimuthal angle) Parameters: ----------- :param inpaints: List of 2D inpainted images :type inpaints: List of ndarray :param ais: List of AzimuthalIntegrator/Transform generated using pyGIX/pyFAI which contain the information about the experiment geometry :type ais: list of AzimuthalIntegrator / TransformIntegrator :param masks: List of 2D image (same dimension as inpaints) :type masks: List of ndarray :param radial_range: minimum and maximum of the radial range in degree :type radial_range: Tuple :param azimuth_range: minimum and maximum of the 2th range in degree :type azimuth_range: Tuple :param npt_rad: number of point in the radial range :type npt_rad: int :param npt_azim: number of point in the azimuthal range :type npt_azim: int """ mg = MultiGeometry(ais, unit='q_A^-1', radial_range=radial_range, azimuth_range=azimuth_range, wavelength=None, empty=0.0, chi_disc=180) cake, q, chi = mg.integrate2d(lst_data=inpaints, npt_rad=npt_rad, npt_azim=npt_azim, correctSolidAngle=True, lst_mask=masks) return cake, q, chi[::-1]
class TestMultiGeometry(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) self.data = fabio.open(UtilsTest.getimage("1788/moke.tif")).data self.lst_data = [self.data[:250, :300], self.data[250:, :300], self.data[:250, 300:], self.data[250:, 300:]] self.det = Detector(1e-4, 1e-4) self.det.max_shape = (500, 600) self.sub_det = Detector(1e-4, 1e-4) self.sub_det.max_shape = (250, 300) self.ai = AzimuthalIntegrator(0.1, 0.03, 0.03, detector=self.det) self.range = (0, 23) self.ais = [AzimuthalIntegrator(0.1, 0.030, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.030, 0.00, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.00, detector=self.sub_det), ] self.mg = MultiGeometry(self.ais, radial_range=self.range, unit="2th_deg") self.N = 390 def tearDown(self): unittest.TestCase.tearDown(self) self.data = None self.lst_data = None self.det = None self.sub_det = None self.ai = None self.ais = None self.mg = None def test_integrate1d(self): tth_ref, I_ref = self.ai.integrate1d(self.data, radial_range=self.range, npt=self.N, unit="2th_deg", method="splitpixel") obt = self.mg.integrate1d(self.lst_data, self.N) tth_obt, I_obt = obt self.assertEqual(abs(tth_ref - tth_obt).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = (abs(I_obt * 1e6 - I_ref).max()) self.assertLessEqual(delta, 5e-5, "Intensity is the same delta=%s" % delta) def test_integrate2d(self): ref = self.ai.integrate2d(self.data, self.N, 360, radial_range=self.range, azimuth_range=(-180, 180), unit="2th_deg", method="splitpixel", all=True) obt = self.mg.integrate2d(self.lst_data, self.N, 360, all=True) self.assertEqual(abs(ref["radial"] - obt["radial"]).max(), 0, "Bin position is the same") self.assertEqual(abs(ref["azimuthal"] - obt["azimuthal"]).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = abs(obt["I"] * 1e6 - ref["I"])[obt["count"] >= 1] # restict on valid pixel delta_cnt = abs(obt["count"] - ref["count"]) delta_sum = abs(obt["sum"] * 1e6 - ref["sum"]) if delta.max() > 1: logger.warning("TestMultiGeometry.test_integrate2d gave intensity difference of %s" % delta.max()) if logger.level <= logging.DEBUG: from matplotlib import pyplot as plt f = plt.figure() a1 = f.add_subplot(2, 2, 1) a1.imshow(ref["sum"]) a2 = f.add_subplot(2, 2, 2) a2.imshow(obt["sum"]) a3 = f.add_subplot(2, 2, 3) a3.imshow(delta_sum) a4 = f.add_subplot(2, 2, 4) a4.plot(delta_sum.sum(axis=0)) f.show() raw_input() self.assertLess(delta_cnt.max(), 0.001, "pixel count is the same delta=%s" % delta_cnt.max()) self.assertLess(delta_sum.max(), 0.03, "pixel sum is the same delta=%s" % delta_sum.max()) self.assertLess(delta.max(), 0.004, "pixel intensity is the same (for populated pixels) delta=%s" % delta.max())
def plot_100k(files, centerx, centery, sdd, name, wavelength=.992e-10, Qchidir='./plots/'): """ Return Q,I and save the Q-Chi plot to Qchidir. Takes a list of filenames (use find_scans), centerx and centery, the sdd (in meters), wavelength (in meters), sample name, and the directory to save a Q-chi image. """ import pyFAI from pyFAI.multi_geometry import MultiGeometry from copy import deepcopy import numpy as np import matplotlib.pyplot as plt # Check this is right? The mono energy should be in the spec file (no filetype) w1 = wavelength # Create a new detector. You should also be able to import the pilatus 100k from pyFAI.detectors # but be careful, this has the angles defined differently (i.e.: positive tth = negative rot2) det = pyFAI.detectors.Detector(172e-6, 172e-6) det.max_shape = (487, 195) poni1 = (487 - centerx) * 172e-6 poni2 = centery * 172e-6 # Define ai for scan 1 ai = pyFAI.AzimuthalIntegrator(dist=sdd, poni1=poni1, poni2=poni2, detector=det) ai.set_wavelength(w1) # If you want to play with tilts, set the initial ai.rot1, rot2, rot3 here. ai.rot1 = 0 * np.pi / 180 ai.rot2 = -6 * np.pi / 180 ai.rot3 = 0 * np.pi / 180 imgs = [] ais = [] step = 1 * np.pi / 180 # For each scan, move rot2 and create a new ai using deepcopy for i in range(54): fn = files[i] img = read_raw_100k(fn) my_ai = deepcopy(ai) my_ai.rot2 -= i * step imgs.append(img) ais.append(my_ai) if i == 5: plt.imshow(img, origin='lower') mg = MultiGeometry(ais, unit="q_A^-1", radial_range=(0.5, 5)) Q, I = mg.integrate1d(imgs, 5000) # This creates and plots a Q-chi image sns.set_style("white") I2D, Q2D, chi = mg.integrate2d(imgs, 1000, 360) fig2 = plt.figure(figsize=(10, 6)) plt.imshow(I2D[245:295], origin="lower", extent=[Q2D.min(), Q2D.max(), 0, chi.max()], aspect='auto', cmap='hot') plt.xlabel('Q / $\\AA ^{-1}$', fontsize=20) plt.tick_params(axis='x', which='major', labelsize=16) plt.ylabel('chi / $\\AA ^{-1}$', fontsize=20) plt.tick_params(axis='y', which='major', labelsize=16) plt.xlim(0.5, 2.5) plt.title("{} Q vs chi".format(name), fontsize=24) figname = "{}_Q_chi.png".format(name) plt.savefig(Qchidir + figname) plt.close(fig2) return Q, I
class EwaldSphere: """Class for storing multiple arch objects, and stores a MultiGeometry integrator from pyFAI. Attributes: arches: ArchSeries, list of arches indexed by their idx value bai_1d: int_1d_data object, stores result of 1d integration bai_1d_args: dict, arguments for invidivual arch integrate1d method bai_2d: int_2d_data object, stores result of 2d integration bai_2d_args: dict, arguments for invidivual arch integrate2d method data_file: str, file to save data to file_lock: lock for ensuring one writer to hdf5 file mg_args: arguments for MultiGeometry constructor mgi_1d: int_1d_data object, stores result from multigeometry integrate1d method mgi_2d: int_2d_data object, stores result from multigeometry integrate2d method multi_geo: MultiGeometry instance name: str, name of the sphere scan_data: DataFrame, stores all scan metadata sphere_lock: lock for modifying data in sphere Methods: add_arch: adds new arch and optionally updates other data by_arch_integrate_1d: Runs 1 dimensional integration of each arch individually and sums the result, stored in bai_1d by_arch_integrate_2d: Runs 2 dimensional integration of each arch individually and sums the result, stored in bai_2d load_from_h5: loads data from hdf5 file set_multi_geo: sets the MultiGeometry instance multigeometry_integrate_1d: wrapper for MultiGeometry integrate1d method, result stored in mgi_1d multigeometry_integrate_2d: wrapper for MultiGeometry integrate2d method, result stored in mgi_2d save_bai_1d: Saves only bai_1d to the data_file save_bai_2d: Saves only bai_2d to the data_file save_to_h5: saves data to hdf5 file set_multi_geo: instatiates the multigeometry object, or overrides it if it already exists. """ def __init__(self, name='scan0', arches=[], data_file=None, scan_data=pd.DataFrame(), mg_args={'wavelength': 1e-10}, bai_1d_args={}, bai_2d_args={}, static=False, gi=False, th_mtr='th', overall_raw=0, single_img=False, global_mask=None, poni_dict={}): """name: string, name of sphere object. arches: list of EwaldArch object, data to intialize with data_file: str, path to hdf5 file where data is stored scan_data: DataFrame, scan metadata mg_args: dict, arguments for Multigeometry. Must include at least 'wavelength' attribute in Angstroems bai_1d_args: dict, arguments for the integrate1d method of pyFAI AzimuthalIntegrator bai_2d_args: dict, arguments for the integrate2d method of pyFAI AzimuthalIntegrator """ super().__init__() self.file_lock = Condition() if name is None: self.name = os.path.split(data_file)[-1].split('.')[0] else: self.name = name if data_file is None: self.data_file = name + ".hdf5" else: self.data_file = data_file self.static = static self.gi = gi self.th_mtr = th_mtr self.single_img = single_img if arches: self.arches = ArchSeries(self.data_file, self.file_lock, arches, static=self.static, gi=self.gi) else: self.arches = ArchSeries(self.data_file, self.file_lock, static=self.static, gi=self.gi) self.scan_data = scan_data self.mg_args = mg_args self.multi_geo = MultiGeometry([a.integrator for a in arches], **mg_args) self.bai_1d_args = bai_1d_args self.bai_2d_args = bai_2d_args self.mgi_1d = int_1d_data() self.mgi_2d = int_2d_data() self.sphere_lock = Condition(_PyRLock()) if self.static: self.bai_1d = int_1d_data_static() self.bai_2d = int_2d_data_static() else: self.bai_1d = int_1d_data() self.bai_2d = int_2d_data() self.overall_raw = overall_raw self.global_mask = global_mask self.poni_dict = poni_dict def reset(self): """Resets all held data objects to blank state, called when all new data is going to be loaded or when a sphere needs to be purged of old data. """ with self.sphere_lock: self.scan_data = pd.DataFrame() self.mgi_1d = int_1d_data() self.mgi_2d = int_2d_data() self.arches = ArchSeries(self.data_file, self.file_lock, static=self.static, gi=self.gi) self.global_mask = None if self.static: self.bai_1d = int_1d_data_static() self.bai_2d = int_2d_data_static() else: self.bai_1d = int_1d_data() self.bai_2d = int_2d_data() self.overall_raw = 0 def add_arch(self, arch=None, calculate=True, update=True, get_sd=True, set_mg=True, **kwargs): """Adds new arch to sphere. args: arch: EwaldArch instance, arch to be added. Recommended to always pass a copy of an arch with the arch.copy method or intialize with kwargs calculate: whether to run the arch's calculate methods after adding update: bool, if True updates the bai_1d and bai_2d attributes get_sd: bool, if True tries to get scan data from arch set_mg: bool, if True sets the MultiGeometry attribute. Takes a long time, especially with longer lists. Recommended to run set_multi_geo method after all arches are loaded. kwargs: If arch is None, used to intialize the EwaldArch, see EwaldArch for arguments. returns None """ with self.sphere_lock: if arch is None: arch = EwaldArch(**kwargs) if calculate: arch.integrate_1d(global_mask=self.global_mask, **self.bai_1d_args) arch.integrate_2d(global_mask=self.global_mask, **self.bai_2d_args) arch.file_lock = self.file_lock self.arches = self.arches.append(pd.Series(arch, index=[arch.idx])) self.arches.sort_index(inplace=True) if arch.scan_info and get_sd: ser = pd.Series(arch.scan_info, dtype='float64') if list(self.scan_data.columns): try: self.scan_data.loc[arch.idx] = ser except ValueError: print('Mismatched columns') else: self.scan_data = pd.DataFrame(arch.scan_info, index=[arch.idx], dtype='float64') self.scan_data.sort_index(inplace=True) with self.file_lock: with utils.catch_h5py_file(self.data_file, 'a') as file: compression = 'lzf' if self.static: compression = None utils.dataframe_to_h5(self.scan_data, file, 'scan_data', compression) if update: self._update_bai_1d(arch) self._update_bai_2d(arch) if set_mg: self.multi_geo = MultiGeometry( [a.integrator for a in self.arches], **self.mg_args) self.overall_raw += arch.map_raw def by_arch_integrate_1d(self, **args): """Integrates all arches individually, then sums the results for the overall integration result. args: see EwaldArch.integrate_1d. If any args are passed, the bai_1d_args dictionary is also updated with the new args. If no args are passed, uses bai_1d_args attribute. """ if not args: args = self.bai_1d_args else: self.bai_1d_args = args.copy() with self.sphere_lock: if self.static: self.bai_1d = int_1d_data_static() else: self.bai_1d = int_1d_data() for arch in self.arches: arch.integrate_1d(global_mask=self.global_mask, **args) self.arches[arch.idx] = arch self._update_bai_1d(arch) def by_arch_integrate_2d(self, **args): """Integrates all arches individually, then sums the results for the overall integration result. args: see EwaldArch.integrate_2d. If any args are passed, the bai_2d_args dictionary is also updated with the new args. If no args are passed, uses bai_2d_args attribute. """ if not args: args = self.bai_2d_args else: self.bai_2d_args = args.copy() with self.sphere_lock: if self.static: self.bai_2d = int_2d_data_static() else: self.bai_2d = int_2d_data() for arch in self.arches: arch.integrate_2d(global_mask=self.global_mask, **args) self.arches[arch.idx] = arch self._update_bai_2d(arch) def _update_bai_1d(self, arch): """helper function to update overall bai variables. """ with self.sphere_lock: try: assert list(self.bai_1d.raw.shape) == list( arch.int_1d.raw.shape) except (AssertionError, AttributeError): if not self.static: self.bai_1d.raw = np.zeros(arch.int_1d.raw.shape) self.bai_1d.pcount = np.zeros(arch.int_1d.pcount.shape) self.bai_1d.norm = np.zeros(arch.int_1d.norm.shape) self.bai_1d.sigma = np.zeros(arch.int_1d.norm.shape) self.bai_1d.sigma_raw = np.zeros(arch.int_1d.norm.shape) try: self.bai_1d += arch.int_1d self.bai_1d.ttheta = arch.int_1d.ttheta self.bai_1d.q = arch.int_1d.q except AttributeError: pass self.save_bai_1d() def _update_bai_2d(self, arch): """helper function to update overall bai variables. """ with self.sphere_lock: try: # assert self.bai_2d.raw.shape == arch.int_2d.raw.shape if self.static: assert self.bai_2d.i_qChi.shape == arch.int_2d.i_qChi.shape else: assert self.bai_2d.norm.shape == arch.int_2d.norm.shape except (AssertionError, AttributeError): if self.static: self.bai_2d.i_qChi = np.zeros(arch.int_2d.i_qChi.shape) self.bai_2d.i_tthChi = np.zeros(arch.int_2d.i_tthChi.shape) self.bai_2d.i_QxyQz = np.zeros(arch.int_2d.i_QxyQz.shape) else: self.bai_2d.norm = np.zeros(arch.int_2d.norm.shape) self.bai_2d.raw = np.zeros(arch.int_2d.raw.shape) self.bai_2d.pcount = np.zeros(arch.int_2d.pcount.shape) self.bai_2d.sigma = np.zeros(arch.int_2d.norm.shape) self.bai_2d.sigma_raw = np.zeros(arch.int_2d.norm.shape) try: self.bai_2d.ttheta = arch.int_2d.ttheta self.bai_2d.q = arch.int_2d.q self.bai_2d.chi = arch.int_2d.chi if not self.static: self.bai_2d += arch.int_2d else: self.bai_2d.i_qChi += arch.int_2d.i_qChi self.bai_2d.i_tthChi += arch.int_2d.i_tthChi self.bai_2d.i_QxyQz += arch.int_2d.i_QxyQz self.bai_2d.qz = arch.int_2d.qz self.bai_2d.qxy = arch.int_2d.qxy except AttributeError: pass self.save_bai_2d() def set_multi_geo(self, **args): """Sets the MultiGeometry instance stored in the arch. args: see pyFAI.multiple_geometry.MultiGeometry. If no args are passed, uses mg_args attribute. Otherwise, updates mg_args and uses passed arguments. """ self.mg_args.update(args) with self.sphere_lock: self.multi_geo = MultiGeometry([a.integrator for a in self.arches], **self.mg_args) def multigeometry_integrate_1d(self, monitor=None, **kwargs): """Wrapper for integrate1d method of MultiGeometry. args: monitor: channel with normalization value kwargs: see MultiGeometry.integrate1d returns: result: result from MultiGeometry.integrate1d """ with self.sphere_lock: lst_mask = [a.get_mask() for a in self.arches] if monitor is None: try: result = self.multi_geo.integrate1d( [a.map_norm for a in self.arches], lst_mask=lst_mask, **kwargs) except Exception as e: print(e) print('Exception 1') result = self.multi_geo.integrate1d( [a.map_raw for a in self.arches], lst_mask=lst_mask, **kwargs) else: result = self.multi_geo.integrate1d( [a.map_raw for a in self.arches], lst_mask=lst_mask, normalization_factor=list(self.scan_data[monitor]), **kwargs) self.mgi_1d.from_result(result, self.multi_geo.wavelength) return result def multigeometry_integrate_2d(self, monitor=None, **kwargs): """Wrapper for integrate1d method of MultiGeometry. args: monitor: channel with normalization value kwargs: see MultiGeometry.integrate1d returns: result: result from MultiGeometry.integrate1d """ with self.sphere_lock: lst_mask = [a.get_mask() for a in self.arches] if monitor is None: try: result = self.multi_geo.integrate2d( [a.map_raw / a.map_norm for a in self.arches], lst_mask=lst_mask, **kwargs) except Exception as e: print(e) print('Exception 2') result = self.multi_geo.integrate2d( [a.map_raw for a in self.arches], lst_mask=lst_mask, **kwargs) else: result = self.multi_geo.integrate2d( [a.map_raw for a in self.arches], lst_mask=lst_mask, normalization_factor=list(self.scan_data[monitor]), **kwargs) self.mgi_2d.from_result(result, self.multi_geo.wavelength) return result def save_to_h5(self, replace=False, *args, **kwargs): """Saves data to hdf5 file. args: replace: bool, if True file is truncated prior to writing data. arches: list, list of arch ids to save. Deprecated. data_onle: bool, if true only saves the scan_data attribute and does not save mg_args, bai_1d_args, or bai_2d_args. compression: str, what compression algorithm to pass to h5py. See h5py documentation for acceptable compression algorithms. """ if replace: mode = 'w' else: mode = 'a' with self.file_lock: with utils.catch_h5py_file(self.data_file, mode) as file: self._save_to_h5(file, *args, **kwargs) def _save_to_h5(self, grp, arches=None, data_only=False, compression='lzf'): """Actual function for saving data, run with the file open and holding the file_lock. """ if self.static: compression = None with self.sphere_lock: grp.attrs['type'] = 'EwaldSphere' if data_only: lst_attr = [ "scan_data", "global_mask", "overall_raw", ] else: lst_attr = [ "scan_data", "global_mask", "mg_args", "bai_1d_args", "bai_2d_args", "overall_raw", "static", "gi", "th_mtr", "single_img", "poni_dict" ] utils.attributes_to_h5(self, grp, lst_attr, compression=compression) keys = ('bai_1d', 'bai_2d', 'mgi_1d', 'mgi_2d') if self.static: keys = ('bai_1d', 'bai_2d') for key in keys: if key not in grp: grp.create_group(key) self.bai_1d.to_hdf5(grp['bai_1d'], compression) self.bai_2d.to_hdf5(grp['bai_2d'], compression) if not self.static: self.mgi_1d.to_hdf5(grp['mgi_1d'], compression) self.mgi_2d.to_hdf5(grp['mgi_2d'], compression) def load_from_h5(self, replace=True, mode='r', *args, **kwargs): """Loads data stored in hdf5 file. args: data_only: bool, if True only loads the scan_data attribute and does not load mg_args, bai_1d_args, or bai_2d_args. set_mg: bool, if True instantiates the Multigeometry object. """ with self.file_lock: if replace: self.reset() with utils.catch_h5py_file(self.data_file, mode=mode) as file: self._load_from_h5(file, *args, **kwargs) def _load_from_h5(self, grp, data_only=False, set_mg=True): """Actual function for loading data, run with the file open and holding the file_lock. """ with self.sphere_lock: if 'type' in grp.attrs: if grp.attrs['type'] == 'EwaldSphere': for arch in grp['arches']: if int(arch) not in self.arches.index: self.arches.index.append(int(arch)) self.arches.sort_index(inplace=True) if data_only: lst_attr = [ "scan_data", "overall_raw", ] utils.h5_to_attributes(self, grp, lst_attr) else: lst_attr = [ "scan_data", "mg_args", "bai_1d_args", "bai_2d_args", "overall_raw", "static", "gi", "th_mtr", "single_img", "poni_dict" ] utils.h5_to_attributes(self, grp, lst_attr) self._set_args(self.bai_1d_args) self._set_args(self.bai_2d_args) if not self.static: self._set_args(self.mg_args) if "global_mask" in grp: utils.h5_to_attributes(self, grp, ["global_mask"]) else: self.global_mask = None self.bai_1d.from_hdf5(grp['bai_1d']) self.bai_2d.from_hdf5(grp['bai_2d']) if not self.static: self.mgi_1d.from_hdf5(grp['mgi_1d']) self.mgi_2d.from_hdf5(grp['mgi_2d']) if set_mg: self.set_multi_geo(**self.mg_args) def set_datafile(self, fname, name=None, keep_current_data=False, save_args={}, load_args={}): """Sets the data_file. If file exists and has data, loads in the data. Otherwise, creates new file and resets self. args: fname: str, new data file name: str or None, new name. If None, name is obtained from fname. keep_current_data: bool, if True overwrites any existing data in the file. Otherwise, current data is either overwritten by data in file or deleted if no data exists, except any args dicts which are untouched. save_args: dict, arguments to be passed to save_to_h5 load_args: dict, arguments to be passed to load_from_h5 """ with self.sphere_lock: self.data_file = fname if name is None: self.name = os.path.split(fname)[-1].split('.')[0] else: self.name = name if keep_current_data: self.save_to_h5(replace=True, **save_args) else: if os.path.exists(fname): self.load_from_h5(replace=True, **load_args) else: self.reset() self.save_to_h5(replace=True, **save_args) def save_bai_1d(self, compression='lzf'): """Function to save only the bai_1d object. args: compression: str, what compression algorithm to pass to h5py. See h5py documentation for acceptable compression algorithms. """ compression = None if self.static: compression = None with self.file_lock: with utils.catch_h5py_file(self.data_file, 'a') as file: self.bai_1d.to_hdf5(file['bai_1d'], compression=compression) def save_bai_2d(self, compression='lzf'): """Function to save only the bai_2d object. args: compression: str, what compression algorithm to pass to h5py. See h5py documentation for acceptable compression algorithms. """ compression = None if self.static: compression = None with self.file_lock: with utils.catch_h5py_file(self.data_file, 'a') as file: self.bai_2d.to_hdf5(file['bai_2d'], compression=compression) def _set_args(self, args): """Ensures any range args are lists. """ for arg in args: if 'range' in arg: if args[arg] is not None: args[arg] = list(args[arg])
class TestMultiGeometry(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) self.data = fabio.open(UtilsTest.getimage("1788/moke.tif")).data self.lst_data = [ self.data[:250, :300], self.data[250:, :300], self.data[:250, 300:], self.data[250:, 300:] ] self.det = Detector(1e-4, 1e-4) self.det.max_shape = (500, 600) self.sub_det = Detector(1e-4, 1e-4) self.sub_det.max_shape = (250, 300) self.ai = AzimuthalIntegrator(0.1, 0.03, 0.03, detector=self.det) self.range = (0, 23) self.ais = [ AzimuthalIntegrator(0.1, 0.030, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.030, 0.00, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.00, detector=self.sub_det), ] self.mg = MultiGeometry(self.ais, radial_range=self.range, unit="2th_deg") self.N = 390 def tearDown(self): unittest.TestCase.tearDown(self) self.data = None self.lst_data = None self.det = None self.sub_det = None self.ai = None self.ais = None self.mg = None def test_integrate1d(self): tth_ref, I_ref = self.ai.integrate1d(self.data, radial_range=self.range, npt=self.N, unit="2th_deg", method="splitpixel") obt = self.mg.integrate1d(self.lst_data, self.N) tth_obt, I_obt = obt self.assertEqual( abs(tth_ref - tth_obt).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = (abs(I_obt * 1e6 - I_ref).max()) self.assert_(delta < 5e-5, "Intensity is the same delta=%s" % delta) def test_integrate2d(self): ref = self.ai.integrate2d(self.data, self.N, 360, radial_range=self.range, azimuth_range=(-180, 180), unit="2th_deg", method="splitpixel", all=True) obt = self.mg.integrate2d(self.lst_data, self.N, 360, all=True) self.assertEqual( abs(ref["radial"] - obt["radial"]).max(), 0, "Bin position is the same") self.assertEqual( abs(ref["azimuthal"] - obt["azimuthal"]).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = abs(obt["I"] * 1e6 - ref["I"])[obt["count"] >= 1e-6] # restrict on valid pixel delta_cnt = abs(obt["count"] - ref["count"]) delta_sum = abs(obt["sum"] * 1e6 - ref["sum"]) if delta.max() > 0: logger.warning( "TestMultiGeometry.test_integrate2d gave intensity difference of %s" % delta.max()) if logger.level <= logging.DEBUG: from matplotlib import pyplot as plt f = plt.figure() a1 = f.add_subplot(2, 2, 1) a1.imshow(ref["sum"]) a2 = f.add_subplot(2, 2, 2) a2.imshow(obt["sum"]) a3 = f.add_subplot(2, 2, 3) a3.imshow(delta_sum) a4 = f.add_subplot(2, 2, 4) a4.plot(delta_sum.sum(axis=0)) f.show() raw_input() self.assert_(delta_cnt.max() < 0.001, "pixel count is the same delta=%s" % delta_cnt.max()) self.assert_(delta_sum.max() < 0.03, "pixel sum is the same delta=%s" % delta_sum.max()) self.assert_( delta.max() < 0.004, "pixel intensity is the same (for populated pixels) delta=%s" % delta.max())