Changes in / [ed0427a:a728658] in sasview
- Location:
- src/sas/sascalc/data_util
- Files:
-
- 2 added
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/sascalc/data_util/qsmearing.py
r8cb0692 rfc18690 13 13 import logging 14 14 import sys 15 16 from sasmodels.resolution import Slit1D, Pinhole1D 17 from sasmodels.resolution2d import Pinhole2D 18 19 def smear_selection(data, model = None): 15 import sas.models.sas_extension.smearer as smearer 16 from sas.sascalc.data_util.smearing_2d import Smearer2D 17 18 def smear_selection(data1D, model = None): 20 19 """ 21 20 Creates the right type of smearer according … … 32 31 resolution smearing data. 33 32 34 :param data : Data1D object33 :param data1D: Data1D object 35 34 :param model: sas.model instance 36 35 """ 37 36 # Sanity check. If we are not dealing with a SAS Data1D 38 37 # 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: 41 40 return None 42 elif data .dqx_data == None or data.dqy_data == None:41 elif data1D.dqx_data == None or data1D.dqy_data == None: 43 42 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"): 48 47 return None 49 48 50 49 # Look for resolution smearing data 51 50 _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): 53 52 54 53 # Check that we have non-zero data 55 if data .dx[0] > 0.0:54 if data1D.dx[0] > 0.0: 56 55 _found_resolution = True 57 56 #print "_found_resolution",_found_resolution … … 59 58 # If we found resolution smearing data, return a QSmearer 60 59 if _found_resolution == True: 61 return pinhole_smear(data, model) 60 return QSmearer(data1D, model) 61 #return pinhole_smear(data1D, model) 62 62 63 63 # Look for slit smearing data 64 64 _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): 67 67 68 68 # 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: 70 70 _found_slit = True 71 71 72 72 # 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: 75 75 _found_resolution = False 76 76 break 77 77 78 for item in data .dxw:79 if data .dxw[0] != item:78 for item in data1D.dxw: 79 if data1D.dxw[0] != item: 80 80 _found_resolution = False 81 81 break 82 82 # If we found slit smearing data, return a slit smearer 83 83 if _found_slit == True: 84 return slit_smear(data, model) 84 #return SlitSmearer(data1D, model) 85 return slit_smear(data1D, model) 85 86 return None 86 87 87 88 89 class _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 286 class _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 345 class 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 407 class _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 463 class 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 500 def 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 590 from .resolution import Slit1D, Pinhole1D 88 591 class PySmear(object): 89 592 """
Note: See TracChangeset
for help on using the changeset viewer.