""" This software was developed by the University of Tennessee as part of the Distributed Data Analysis of Neutron Scattering Experiments (DANSE) project funded by the US National Science Foundation. See the license text in license.txt copyright 2008, University of Tennessee """ import numpy import math import scipy.special def smear_selection(data1D): """ Creates the right type of smearer according to the data """ pass class _BaseSmearer(object): def __init__(self): self.nbins = 0 self._weights = None def _compute_matrix(self): return NotImplemented def __call__(self, iq): """ Return the smeared I(q) value at the given q. The smeared I(q) is computed using a predetermined smearing matrix for a particular binning. @param q: I(q) array @return: smeared I(q) """ # Sanity check if len(iq) != self.nbins: raise RuntimeError, "Invalid I(q) vector: inconsistent array length %s <> %s" % (len(iq), self.nbins) if self._weights == None: self._compute_matrix() iq_smeared = numpy.zeros(self.nbins) # Loop over q-values for q_i in range(self.nbins): sum = 0.0 counts = 0.0 for i in range(self.nbins): sum += iq[i] * self._weights[q_i][i] counts += self._weights[q_i][i] iq_smeared[q_i] = sum/counts return iq_smeared class _SlitSmearer(_BaseSmearer): """ Slit smearing for I(q) array """ def __init__(self, nbins=None, width=None, height=None, min=None, max=None): """ Initialization @param iq: I(q) array [cm-1] @param width: slit width [A-1] @param height: slit height [A-1] @param min: Q_min [A-1] @param max: Q_max [A-1] """ ## Slit width in Q units self.width = width ## Slit height in Q units self.height = height ## Q_min (Min Q-value for I(q)) self.min = min ## Q_max (Max Q_value for I(q)) self.max = max ## Number of Q bins self.nbins = nbins ## Number of points used in the smearing computation self.npts = 1000 ## Smearing matrix self._weights = None def _compute_matrix(self): """ Compute the smearing matrix for the current I(q) array """ weights = numpy.zeros([self.nbins, self.nbins]) # Loop over all q-values for i in range(self.nbins): q = self.min + i*(self.max-self.min)/float(self.nbins-1.0) # For each q-value, compute the weight of each other q-bin # in the I(q) array npts_h = self.npts if self.height>0 else 1 npts_w = self.npts if self.width>0 else 1 # If both height and width are great than zero, # modify the number of points in each direction so # that the total number of points is still what # the user would expect (downgrade resolution) if npts_h>1 and npts_w>1: npts_h = int(math.ceil(math.sqrt(self.npts))) npts_w = npts_h for k in range(npts_h): if npts_h==1: shift_h = 0 else: shift_h = self.height/(float(npts_h-1.0)) * k for j in range(npts_w): if npts_w==1: shift_w = 0 else: shift_w = self.width/(float(npts_w-1.0)) * j q_shifted = math.sqrt( ((q-shift_w)*(q-shift_w) + shift_h*shift_h) ) q_i = int(math.floor( (q_shifted-self.min)/((self.max-self.min)/(self.nbins -1.0)) )) # Skip the entries outside our I(q) range #TODO: be careful with edge effect if q_i