Changes in / [ed0427a:a728658] in sasview


Ignore:
Location:
src/sas/sascalc/data_util
Files:
2 added
1 edited

Legend:

Unmodified
Added
Removed
  • src/sas/sascalc/data_util/qsmearing.py

    r8cb0692 rfc18690  
    1313import logging 
    1414import sys 
    15  
    16 from sasmodels.resolution import Slit1D, Pinhole1D 
    17 from sasmodels.resolution2d import Pinhole2D 
    18  
    19 def smear_selection(data, model = None): 
     15import sas.models.sas_extension.smearer as smearer 
     16from sas.sascalc.data_util.smearing_2d import Smearer2D 
     17 
     18def smear_selection(data1D, model = None): 
    2019    """ 
    2120    Creates the right type of smearer according  
     
    3231    resolution smearing data.  
    3332     
    34     :param data: Data1D object 
     33    :param data1D: Data1D object 
    3534    :param model: sas.model instance 
    3635    """ 
    3736    # Sanity check. If we are not dealing with a SAS Data1D 
    3837    # object, just return None 
    39     if  data.__class__.__name__ not in ['Data1D', 'Theory1D']: 
    40         if data == None: 
     38    if  data1D.__class__.__name__ not in ['Data1D', 'Theory1D']: 
     39        if data1D == None: 
    4140            return None 
    42         elif data.dqx_data == None or data.dqy_data == None: 
     41        elif data1D.dqx_data == None or data1D.dqy_data == None: 
    4342            return None 
    44         return Pinhole2D(data) 
    45      
    46     if  not hasattr(data, "dx") and not hasattr(data, "dxl")\ 
    47          and not hasattr(data, "dxw"): 
     43        return Smearer2D(data1D) 
     44     
     45    if  not hasattr(data1D, "dx") and not hasattr(data1D, "dxl")\ 
     46         and not hasattr(data1D, "dxw"): 
    4847        return None 
    4948     
    5049    # Look for resolution smearing data 
    5150    _found_resolution = False 
    52     if data.dx is not None and len(data.dx) == len(data.x): 
     51    if data1D.dx is not None and len(data1D.dx) == len(data1D.x): 
    5352         
    5453        # Check that we have non-zero data 
    55         if data.dx[0] > 0.0: 
     54        if data1D.dx[0] > 0.0: 
    5655            _found_resolution = True 
    5756            #print "_found_resolution",_found_resolution 
     
    5958    # If we found resolution smearing data, return a QSmearer 
    6059    if _found_resolution == True: 
    61          return pinhole_smear(data, model) 
     60        return QSmearer(data1D, model) 
     61        #return pinhole_smear(data1D, model) 
    6262 
    6363    # Look for slit smearing data 
    6464    _found_slit = False 
    65     if data.dxl is not None and len(data.dxl) == len(data.x) \ 
    66         and data.dxw is not None and len(data.dxw) == len(data.x): 
     65    if data1D.dxl is not None and len(data1D.dxl) == len(data1D.x) \ 
     66        and data1D.dxw is not None and len(data1D.dxw) == len(data1D.x): 
    6767         
    6868        # Check that we have non-zero data 
    69         if data.dxl[0] > 0.0 or data.dxw[0] > 0.0: 
     69        if data1D.dxl[0] > 0.0 or data1D.dxw[0] > 0.0: 
    7070            _found_slit = True 
    7171         
    7272        # Sanity check: all data should be the same as a function of Q 
    73         for item in data.dxl: 
    74             if data.dxl[0] != item: 
     73        for item in data1D.dxl: 
     74            if data1D.dxl[0] != item: 
    7575                _found_resolution = False 
    7676                break 
    7777             
    78         for item in data.dxw: 
    79             if data.dxw[0] != item: 
     78        for item in data1D.dxw: 
     79            if data1D.dxw[0] != item: 
    8080                _found_resolution = False 
    8181                break 
    8282    # If we found slit smearing data, return a slit smearer 
    8383    if _found_slit == True: 
    84         return slit_smear(data, model) 
     84        #return SlitSmearer(data1D, model) 
     85        return slit_smear(data1D, model) 
    8586    return None 
    86  
    87  
     87             
     88 
     89class _BaseSmearer(object): 
     90    """ 
     91        Base class for smearers 
     92    """ 
     93    def __init__(self): 
     94        self.nbins = 0 
     95        self.nbins_low = 0 
     96        self.nbins_high = 0 
     97        self._weights = None 
     98        ## Internal flag to keep track of C++ smearer initialization 
     99        self._init_complete = False 
     100        self._smearer = None 
     101        self.model = None 
     102        self.min = None 
     103        self.max = None 
     104        self.qvalues = [] 
     105         
     106    def __deepcopy__(self, memo=None): 
     107        """ 
     108        Return a valid copy of self. 
     109        Avoid copying the _smearer C object and force a matrix recompute 
     110        when the copy is used.   
     111        """ 
     112        result = _BaseSmearer() 
     113        result.nbins = self.nbins 
     114        return result 
     115 
     116    def _compute_matrix(self): 
     117        """ 
     118            Place holder for matrix computation  
     119        """ 
     120        return NotImplemented 
     121 
     122    def get_unsmeared_range(self, q_min=None, q_max=None): 
     123        """ 
     124            Place holder for method returning unsmeared range 
     125        """ 
     126        return NotImplemented 
     127     
     128    def get_bin_range(self, q_min=None, q_max=None): 
     129        """ 
     130         
     131        :param q_min: minimum q-value to smear 
     132        :param q_max: maximum q-value to smear 
     133          
     134        """ 
     135        # If this is the first time we call for smearing, 
     136        # initialize the C++ smearer object first 
     137        if not self._init_complete: 
     138            self._initialize_smearer() 
     139        if q_min == None: 
     140            q_min = self.min 
     141        if q_max == None: 
     142            q_max = self.max 
     143 
     144        _qmin_unsmeared, _qmax_unsmeared = self.get_unsmeared_range(q_min, 
     145                                                                     q_max) 
     146        _first_bin = None 
     147        _last_bin  = None 
     148 
     149        #step = (self.max - self.min) / (self.nbins - 1.0) 
     150        # Find the first and last bin number in all extrapolated and real data 
     151        try: 
     152            for i in range(self.nbins): 
     153                q_i = smearer.get_q(self._smearer, i) 
     154                if (q_i >= _qmin_unsmeared) and (q_i <= _qmax_unsmeared): 
     155                    # Identify first and last bin 
     156                    if _first_bin is None: 
     157                        _first_bin = i 
     158                    else: 
     159                        _last_bin  = i 
     160        except: 
     161            msg = "_BaseSmearer.get_bin_range: " 
     162            msg += " error getting range\n  %s" % sys.exc_value 
     163            raise RuntimeError, msg 
     164    
     165        #  Find the first and last bin number only in the real data 
     166        _first_bin, _last_bin = self._get_unextrapolated_bin( \ 
     167                                        _first_bin, _last_bin) 
     168 
     169        return _first_bin, _last_bin 
     170 
     171    def __call__(self, iq_in, first_bin = 0, last_bin = None): 
     172        """ 
     173        Perform smearing 
     174        """ 
     175        # If this is the first time we call for smearing, 
     176        # initialize the C++ smearer object first 
     177        if not self._init_complete: 
     178            self._initialize_smearer() 
     179 
     180        if last_bin is None or last_bin >= len(iq_in): 
     181            last_bin = len(iq_in) - 1 
     182        # Check that the first bin is positive 
     183        if first_bin < 0: 
     184            first_bin = 0 
     185 
     186        # With a model given, compute I for the extrapolated points and append 
     187        # to the iq_in 
     188        iq_in_temp = iq_in 
     189        if self.model != None: 
     190            temp_first, temp_last = self._get_extrapolated_bin( \ 
     191                                                        first_bin, last_bin) 
     192            if self.nbins_low > 0: 
     193                iq_in_low = self.model.evalDistribution( \ 
     194                                    numpy.fabs(self.qvalues[0:self.nbins_low])) 
     195            iq_in_high = self.model.evalDistribution( \ 
     196                                            self.qvalues[(len(self.qvalues) - \ 
     197                                            self.nbins_high - 1):]) 
     198            # Todo: find out who is sending iq[last_poin] = 0. 
     199            if iq_in[len(iq_in) - 1] == 0: 
     200                iq_in[len(iq_in) - 1] = iq_in_high[0] 
     201            # Append the extrapolated points to the data points 
     202            if self.nbins_low > 0: 
     203                iq_in_temp = numpy.append(iq_in_low, iq_in) 
     204            if self.nbins_high > 0: 
     205                iq_in_temp = numpy.append(iq_in_temp, iq_in_high[1:]) 
     206        else: 
     207            temp_first = first_bin 
     208            temp_last = last_bin 
     209            #iq_in_temp = iq_in 
     210 
     211        # Sanity check 
     212        if len(iq_in_temp) != self.nbins: 
     213            msg = "Invalid I(q) vector: inconsistent array " 
     214            msg += " length %d != %s" % (len(iq_in_temp), str(self.nbins)) 
     215            raise RuntimeError, msg 
     216 
     217        # Storage for smeared I(q)    
     218        iq_out = numpy.zeros(self.nbins) 
     219 
     220        smear_output = smearer.smear(self._smearer, iq_in_temp, iq_out, 
     221                                      #0, self.nbins - 1) 
     222                                      temp_first, temp_last) 
     223                                      #first_bin, last_bin) 
     224        if smear_output < 0: 
     225            msg = "_BaseSmearer: could not smear, code = %g" % smear_output 
     226            raise RuntimeError, msg 
     227 
     228        temp_first = first_bin + self.nbins_low 
     229        temp_last = self.nbins - self.nbins_high 
     230        out = iq_out[temp_first: temp_last] 
     231 
     232        return out 
     233     
     234    def _initialize_smearer(self): 
     235        """ 
     236            Place holder for initializing data smearer 
     237        """ 
     238        return NotImplemented 
     239             
     240     
     241    def _get_unextrapolated_bin(self, first_bin = 0, last_bin = 0): 
     242        """ 
     243        Get unextrapolated first bin and the last bin 
     244         
     245        : param first_bin: extrapolated first_bin 
     246        : param last_bin: extrapolated last_bin 
     247         
     248        : return fist_bin, last_bin: unextrapolated first and last bin 
     249        """ 
     250        #  For first bin 
     251        if first_bin <= self.nbins_low: 
     252            first_bin = 0 
     253        else: 
     254            first_bin = first_bin - self.nbins_low 
     255        # For last bin 
     256        if last_bin >= (self.nbins - self.nbins_high): 
     257            last_bin  = self.nbins - (self.nbins_high + self.nbins_low + 1) 
     258        elif last_bin >= self.nbins_low: 
     259            last_bin = last_bin - self.nbins_low 
     260        else: 
     261            last_bin = 0 
     262        return first_bin, last_bin 
     263     
     264    def _get_extrapolated_bin(self, first_bin = 0, last_bin = 0): 
     265        """ 
     266        Get extrapolated first bin and the last bin 
     267         
     268        : param first_bin: unextrapolated first_bin 
     269        : param last_bin: unextrapolated last_bin 
     270         
     271        : return first_bin, last_bin: extrapolated first and last bin 
     272        """ 
     273        #  For the first bin 
     274        # In the case that needs low extrapolation data 
     275        first_bin = 0 
     276        # For last bin 
     277        if last_bin >= self.nbins - (self.nbins_high + self.nbins_low + 1): 
     278            # In the case that needs higher q extrapolation data  
     279            last_bin = self.nbins - 1 
     280        else: 
     281            # In the case that doesn't need higher q extrapolation data  
     282            last_bin += self.nbins_low 
     283 
     284        return first_bin, last_bin 
     285         
     286class _SlitSmearer(_BaseSmearer): 
     287    """ 
     288    Slit smearing for I(q) array 
     289    """ 
     290     
     291    def __init__(self, nbins=None, width=None, height=None, min=None, max=None): 
     292        """ 
     293        Initialization 
     294             
     295        :param iq: I(q) array [cm-1] 
     296        :param width: slit width [A-1] 
     297        :param height: slit height [A-1] 
     298        :param min: Q_min [A-1] 
     299        :param max: Q_max [A-1] 
     300         
     301        """ 
     302        _BaseSmearer.__init__(self) 
     303        ## Slit width in Q units 
     304        self.width  = width 
     305        ## Slit height in Q units 
     306        self.height = height 
     307        ## Q_min (Min Q-value for I(q)) 
     308        self.min    = min 
     309        ## Q_max (Max Q_value for I(q)) 
     310        self.max    = max 
     311        ## Number of Q bins  
     312        self.nbins  = nbins 
     313        ## Number of points used in the smearing computation 
     314        self.npts   = 3000 
     315        ## Smearing matrix 
     316        self._weights = None 
     317        self.qvalues  = None 
     318         
     319    def _initialize_smearer(self): 
     320        """ 
     321        Initialize the C++ smearer object. 
     322        This method HAS to be called before smearing 
     323        """ 
     324        #self._smearer = smearer.new_slit_smearer(self.width, 
     325        # self.height, self.min, self.max, self.nbins) 
     326        self._smearer = smearer.new_slit_smearer_with_q(self.width,  
     327                                                    self.height, self.qvalues) 
     328        self._init_complete = True 
     329 
     330    def get_unsmeared_range(self, q_min, q_max): 
     331        """ 
     332        Determine the range needed in unsmeared-Q to cover 
     333        the smeared Q range 
     334        """ 
     335        # Range used for input to smearing 
     336        _qmin_unsmeared = q_min 
     337        _qmax_unsmeared = q_max  
     338        try: 
     339            _qmin_unsmeared = self.min 
     340            _qmax_unsmeared = self.max 
     341        except: 
     342            logging.error("_SlitSmearer.get_bin_range: %s" % sys.exc_value) 
     343        return _qmin_unsmeared, _qmax_unsmeared 
     344 
     345class SlitSmearer(_SlitSmearer): 
     346    """ 
     347    Adaptor for slit smearing class and SAS data 
     348    """ 
     349    def __init__(self, data1D, model = None): 
     350        """ 
     351        Assumption: equally spaced bins of increasing q-values. 
     352         
     353        :param data1D: data used to set the smearing parameters 
     354        """ 
     355        # Initialization from parent class 
     356        super(SlitSmearer, self).__init__() 
     357         
     358        ## Slit width 
     359        self.width = 0 
     360        self.nbins_low = 0 
     361        self.nbins_high = 0 
     362        self.model = model 
     363        if data1D.dxw is not None and len(data1D.dxw) == len(data1D.x): 
     364            self.width = data1D.dxw[0] 
     365            # Sanity check 
     366            for value in data1D.dxw: 
     367                if value != self.width: 
     368                    msg = "Slit smearing parameters must " 
     369                    msg += " be the same for all data" 
     370                    raise RuntimeError, msg 
     371        ## Slit height 
     372        self.height = 0 
     373        if data1D.dxl is not None and len(data1D.dxl) == len(data1D.x): 
     374            self.height = data1D.dxl[0] 
     375            # Sanity check 
     376            for value in data1D.dxl: 
     377                if value != self.height: 
     378                    msg = "Slit smearing parameters must be" 
     379                    msg += " the same for all data" 
     380                    raise RuntimeError, msg 
     381        # If a model is given, get the q extrapolation 
     382        if self.model == None: 
     383            data1d_x = data1D.x 
     384        else: 
     385            # Take larger sigma 
     386            if self.height > self.width: 
     387                # The denominator (2.0) covers all the possible w^2 + h^2 range 
     388                sigma_in = data1D.dxl / 2.0 
     389            elif self.width > 0: 
     390                sigma_in = data1D.dxw / 2.0 
     391            else: 
     392                sigma_in = [] 
     393 
     394            self.nbins_low, self.nbins_high, _, data1d_x = \ 
     395                                get_qextrapolate(sigma_in, data1D.x) 
     396 
     397        ## Number of Q bins 
     398        self.nbins = len(data1d_x) 
     399        ## Minimum Q  
     400        self.min = min(data1d_x) 
     401        ## Maximum 
     402        self.max = max(data1d_x) 
     403        ## Q-values 
     404        self.qvalues = data1d_x 
     405         
     406 
     407class _QSmearer(_BaseSmearer): 
     408    """ 
     409    Perform Gaussian Q smearing 
     410    """ 
     411         
     412    def __init__(self, nbins=None, width=None, min=None, max=None): 
     413        """ 
     414        Initialization 
     415         
     416        :param nbins: number of Q bins 
     417        :param width: array standard deviation in Q [A-1] 
     418        :param min: Q_min [A-1] 
     419        :param max: Q_max [A-1] 
     420        """ 
     421        _BaseSmearer.__init__(self) 
     422        ## Standard deviation in Q [A-1] 
     423        self.width = width 
     424        ## Q_min (Min Q-value for I(q)) 
     425        self.min = min 
     426        ## Q_max (Max Q_value for I(q)) 
     427        self.max = max 
     428        ## Number of Q bins  
     429        self.nbins = nbins 
     430        ## Smearing matrix 
     431        self._weights = None 
     432        self.qvalues  = None 
     433         
     434    def _initialize_smearer(self): 
     435        """ 
     436        Initialize the C++ smearer object. 
     437        This method HAS to be called before smearing 
     438        """ 
     439        #self._smearer = smearer.new_q_smearer(numpy.asarray(self.width), 
     440        # self.min, self.max, self.nbins) 
     441        self._smearer = smearer.new_q_smearer_with_q(numpy.asarray(self.width), 
     442                                                      self.qvalues) 
     443        self._init_complete = True 
     444         
     445    def get_unsmeared_range(self, q_min, q_max): 
     446        """ 
     447        Determine the range needed in unsmeared-Q to cover 
     448        the smeared Q range 
     449        Take 3 sigmas as the offset between smeared and unsmeared space 
     450        """ 
     451        # Range used for input to smearing 
     452        _qmin_unsmeared = q_min 
     453        _qmax_unsmeared = q_max  
     454        try: 
     455            offset = 3.0 * max(self.width) 
     456            _qmin_unsmeared = self.min#max([self.min, q_min - offset]) 
     457            _qmax_unsmeared = self.max#min([self.max, q_max + offset]) 
     458        except: 
     459            logging.error("_QSmearer.get_bin_range: %s" % sys.exc_value) 
     460        return _qmin_unsmeared, _qmax_unsmeared 
     461         
     462     
     463class QSmearer(_QSmearer): 
     464    """ 
     465    Adaptor for Gaussian Q smearing class and SAS data 
     466    """ 
     467    def __init__(self, data1D, model = None): 
     468        """ 
     469        Assumption: equally spaced bins of increasing q-values. 
     470         
     471        :param data1D: data used to set the smearing parameters 
     472        """ 
     473        # Initialization from parent class 
     474        super(QSmearer, self).__init__() 
     475        data1d_x = [] 
     476        self.nbins_low = 0 
     477        self.nbins_high = 0 
     478        self.model = model 
     479        ## Resolution 
     480        #self.width = numpy.zeros(len(data1D.x)) 
     481        if data1D.dx is not None and len(data1D.dx) == len(data1D.x): 
     482            self.width = data1D.dx 
     483         
     484        if self.model == None: 
     485            data1d_x = data1D.x 
     486        else: 
     487            self.nbins_low, self.nbins_high, self.width, data1d_x = \ 
     488                                get_qextrapolate(self.width, data1D.x) 
     489 
     490        ## Number of Q bins 
     491        self.nbins = len(data1d_x) 
     492        ## Minimum Q  
     493        self.min = min(data1d_x) 
     494        ## Maximum 
     495        self.max = max(data1d_x) 
     496        ## Q-values 
     497        self.qvalues = data1d_x 
     498 
     499         
     500def get_qextrapolate(width, data_x): 
     501    """ 
     502    Make fake data_x points extrapolated outside of the data_x points 
     503     
     504    :param width: array of std of q resolution 
     505    :param Data1D.x: Data1D.x array 
     506     
     507    :return new_width, data_x_ext: extrapolated width array and x array 
     508     
     509    :assumption1: data_x is ordered from lower q to higher q 
     510    :assumption2: len(data) = len(width) 
     511    :assumption3: the distance between the data points is more compact than the size of width  
     512    :Todo1: Make sure that the assumptions are correct for Data1D 
     513    :Todo2: This fixes the edge problem in Qsmearer but still needs to make smearer interface  
     514    """ 
     515    # Length of the width 
     516    length = len(width) 
     517    width_low = math.fabs(width[0])    
     518    width_high = math.fabs(width[length -1]) 
     519    nbins_low = 0.0  
     520    nbins_high = 0.0 
     521    # Compare width(dQ) to the data bin size and take smaller one as the bin  
     522    # size of the extrapolation; this will correct some weird behavior  
     523    # at the edge: This method was out (commented)  
     524    # because it becomes very expansive when 
     525    # bin size is very small comparing to the width. 
     526    # Now on, we will just give the bin size of the extrapolated points  
     527    # based on the width. 
     528    # Find bin sizes 
     529    #bin_size_low = math.fabs(data_x[1] - data_x[0]) 
     530    #bin_size_high = math.fabs(data_x[length - 1] - data_x[length - 2]) 
     531    # Let's set the bin size 1/3 of the width(sigma), it is good as long as 
     532    # the scattering is monotonous. 
     533    #if width_low < (bin_size_low): 
     534    bin_size_low = width_low / 10.0 
     535    #if width_high < (bin_size_high): 
     536    bin_size_high = width_high / 10.0 
     537         
     538    # Number of q points required below the 1st data point in order to extend 
     539    # them 3 times of the width (std) 
     540    if width_low > 0.0: 
     541        nbins_low = math.ceil(3.0 * width_low / bin_size_low) 
     542    # Number of q points required above the last data point 
     543    if width_high > 0.0: 
     544        nbins_high = math.ceil(3.0 * width_high / bin_size_high) 
     545    # Make null q points         
     546    extra_low = numpy.zeros(nbins_low) 
     547    extra_high = numpy.zeros(nbins_high) 
     548    # Give extrapolated values 
     549    ind = 0 
     550    qvalue = data_x[0] - bin_size_low 
     551    #if qvalue > 0: 
     552    while(ind < nbins_low): 
     553        extra_low[nbins_low - (ind + 1)] = qvalue 
     554        qvalue -= bin_size_low 
     555        ind += 1 
     556        #if qvalue <= 0: 
     557        #    break 
     558    # Redefine nbins_low 
     559    nbins_low = ind 
     560    # Reset ind for another extrapolation 
     561    ind = 0 
     562    qvalue = data_x[length -1] + bin_size_high 
     563    while(ind < nbins_high): 
     564        extra_high[ind] = qvalue 
     565        qvalue += bin_size_high 
     566        ind += 1 
     567    # Make a new qx array 
     568    if nbins_low > 0:   
     569        data_x_ext = numpy.append(extra_low, data_x) 
     570    else: 
     571        data_x_ext = data_x 
     572    data_x_ext = numpy.append(data_x_ext, extra_high) 
     573     
     574    # Redefine extra_low and high based on corrected nbins   
     575    # And note that it is not necessary for extra_width to be a non-zero  
     576    if nbins_low > 0:      
     577        extra_low = numpy.zeros(nbins_low) 
     578    extra_high = numpy.zeros(nbins_high)  
     579    # Make new width array 
     580    new_width = numpy.append(extra_low, width) 
     581    new_width = numpy.append(new_width, extra_high) 
     582     
     583    # nbins corrections due to the negative q value 
     584    nbins_low = nbins_low - len(data_x_ext[data_x_ext <= 0]) 
     585    return nbins_low, nbins_high, \ 
     586             new_width[data_x_ext > 0], data_x_ext[data_x_ext > 0] 
     587 
     588 
     589 
     590from .resolution import Slit1D, Pinhole1D 
    88591class PySmear(object): 
    89592    """ 
Note: See TracChangeset for help on using the changeset viewer.