def _merge_two_curves(curve1: Curve, curve2: Curve, qmin, qmax, qsep, use_additive_constant=False): """Merge two scattering curves :param curve1: the first curve (longer distance) :type curve1: sastool.classes.curve.GeneralCurve :param curve2: the second curve (shorter distance) :type curve2: sastool.classes.curve.GeneralCurve :param qmin: lower bound of the interval for determining the scaling factor :type qmin: float :param qmax: upper bound of the interval for determining the scaling factor :type qmax: float :param qsep: separating (tailoring) point for the merge :type qsep: float :return: merged_curve, factor, background, stat :rtype tuple of a sastool.classes2.curve.Curve and a float """ curve1=curve1.sanitize() curve2=curve2.sanitize() if len(curve1.trim(qmin, qmax)) > len(curve2.trim(qmin, qmax)): curve2_interp = curve2.trim(qmin, qmax) curve1_interp = curve1.interpolate(curve2_interp.q) else: curve1_interp = curve1.trim(qmin, qmax) curve2_interp = curve2.interpolate(curve1_interp.q) if use_additive_constant: bg_init = 0 else: bg_init = FixedParameter(0) factor, bg, stat = nonlinear_odr(curve2_interp.Intensity, curve1_interp.Intensity, curve2_interp.Error, curve1_interp.Error, lambda x, factor, bg: x * factor + bg, [1.0, bg_init]) return Curve.merge(curve1 - bg, curve2 * factor, qsep), factor, bg, stat
def _scale_two_exposures(exp1, exp2, qmin, qmax, N=10, use_additive_constant=False): qrange = np.linspace(qmin, qmax, N) rad1 = exp1.radial_average(qrange=qrange, raw_result=False) rad2 = exp2.radial_average(qrange=qrange, raw_result=False) if use_additive_constant: bg_init = 0 else: bg_init = FixedParameter(0) factor, bg, stat = nonlinear_odr(rad2.y, rad1.y, rad2.dy, rad1.dy, lambda x, factor, bg: x * factor + bg, [1, bg_init]) return factor, bg
def odrfit(self, function, parameters_init, xname='x', yname='y', dyname='dy', dxname='dx', **kwargs): """Orthogonal distance regression to the dataset. Inputs: ------- `function`: a callable, corresponding to the function to be fitted. Should have the following signature:: >>> function(x, par1, par2, par3, ...) where ``par1``, ``par2``, etc. are the values of the parameters to be fitted. `parameters_init`: a sequence (tuple or list) of the initial values for the parameters to be fitted. Their ordering should be the same as that of the arguments of `function` other keyword arguments are given to `nonlinear_odr()` without any modification. Outputs: -------- `par1_fit`, `par2_fit`, etc.: the best fitting values of the parameters. `statdict`: a dictionary with various status data, such as `R2`, `DoF`, `Chi2_reduced`, `Covariance`, `Correlation_coeffs` etc. For a full list, see the help of `sastool.misc.easylsq.odr_fit()` `func_value`: the value of the function at the best fitting parameters, represented as an instance of the same class as this curve. Notes: ------ The fitting itself is done via sastool.misc.easylsq.nonlinear_odr() """ obj = self.sanitize(fieldname=yname).sanitize(fieldname=xname) if not hasattr(obj, dyname): dy = None else: dy = getattr(obj, dyname) if not hasattr(obj, dxname): dx = None else: dx = getattr(obj, dxname) ret = nonlinear_odr(getattr(obj, xname), getattr( obj, yname), dx, dy, function, parameters_init, **kwargs) funcvalue = type(self)(getattr(obj, xname), ret[-1]['func_value']) return ret + (funcvalue,)
def do_getdistance(self): model = self.builder.get_object('pairstore') uncalval = np.array([row[2] for row in model]) uncalerr = np.array([row[3] for row in model]) calval = np.array([row[4] for row in model]) calerr = np.array([row[5] for row in model]) logger.debug('Uncalval: ' + str(uncalval)) logger.debug('Uncalerr: ' + str(uncalerr)) logger.debug('Calval: ' + str(calval)) logger.debug('Calerr: ' + str(calerr)) assert isinstance(self._exposure, Exposure) assert self._exposure.header.pixelsizex == self._exposure.header.pixelsizey if len(uncalval) > 1: def fitfunc(pix_: np.ndarray, dist: float): return qfrompix(pix_, pixelsize=self._exposure.header.pixelsizex, beampos=0, alpha=np.pi * 0.5, wavelength=self._exposure.header.wavelength, dist=dist) self._dist, stat = nonlinear_odr(uncalval, calval, uncalerr, calerr, fitfunc, [100]) x = np.linspace(uncalval.min(), uncalval.max(), len(uncalval) * 100) self.figpairsaxes.plot(x, fitfunc(x, self._dist.val), 'r-') elif len(uncalval) == 1: q = ErrorValue(float(calval[0]), float(calerr[0])) pix = ErrorValue(float(uncalval[0]), float(uncalerr[0])) wl = ErrorValue( self._exposure.header.wavelength, 0) # wavelength error is not considered here: # it has already been considered in the pixel value (peak position) pixsize = self._exposure.header.pixelsizex self._dist = (pix * pixsize) / (2.0 * (wl * q / 4.0 / np.pi).arcsin()).tan() else: self._dist = None self.builder.get_object('distance_label').set_text('--') self.builder.get_object('savedistance_button').set_sensitive(True) return self.builder.get_object('distance_label').set_text(self._dist.tostring(plusminus=' \u00b1 ') + ' mm') self.builder.get_object('savedistance_button').set_sensitive(True) self.figpairscanvas.draw()
def absolutescaling(self, im: Exposure, datared: Dict): if im.header.title == self.config['datareduction']['absintrefname']: self._logger.debug('History: {}'.format('\n'.join([h for h in datared['history']]))) dataset = np.loadtxt(self.config['datareduction']['absintrefdata']) self._logger.debug('Q-range of absint dataset: {:g} to {:g}, {:d} points.'.format( dataset[:, 0].min(), dataset[:, 0].max(), len(dataset[:, 0]))) testradavg = im.radial_average() self._logger.debug('Auto-Q-range of the measured dataset: {:g} to {:g}, {:d} points.'.format( testradavg.q.min(), testradavg.q.max(), len(testradavg))) # noinspection PyPep8Naming,PyPep8Naming q, dq, I, dI, area = im.radial_average(qrange=dataset[:, 0], raw_result=True) dataset = dataset[area > 0, :] # noinspection PyPep8Naming I = I[area > 0] # noinspection PyPep8Naming dI = dI[area > 0] q = q[area > 0] self._logger.debug('Common q-range: {:g} to {:g}, {:d} points.'.format(q.min(), q.max(), len(q))) scalingfactor, stat = nonlinear_odr(I, dataset[:, 1], dI, dataset[:, 2], lambda x, a: a * x, [1]) datared['absintscaling'] = {'q': q, 'area': area, 'Imeas': I, 'dImeas': dI, 'Iref': dataset[:, 1], 'dIref': dataset[:, 2], 'factor.val': scalingfactor.val, 'factor.err': scalingfactor.err, 'stat': stat} self._logger.debug('Scaling factor: ' + str(scalingfactor)) self._logger.debug('Chi2: {:f}'.format(stat['Chi2_reduced'])) self._lastabsintref = im self._absintscalingfactor = scalingfactor self._absintstat = stat self._absintqrange = q datared['history'].append( 'This is an absolute intensity reference measurement. ' 'Determined absolute intensity scaling factor: {}. Reduced Chi2: {:f}. DoF: {:d}. ' 'This corresponds to beam flux {} photons*eta/sec'.format( self._absintscalingfactor, self._absintstat['Chi2_reduced'], self._absintstat['DoF'], 1 / self._absintscalingfactor)) if ((im.header.distance - self._lastabsintref.header.distance).abs() < self.config['datareduction']['distancetolerance']): im *= self._absintscalingfactor datared['statistics']['08_absolutescaling'] = im.get_statistics() datared['history'].append( 'Using absolute intensity factor {} from measurement FSN #{:d} ' 'for absolute intensity calibration.'.format( self._absintscalingfactor, self._lastabsintref.header.fsn)) datared['history'].append('Absint factor was determined with Chi2 {:f} (DoF {:d})'.format( self._absintstat['Chi2_reduced'], self._absintstat['DoF'])) datared['history'].append('Estimated flux: {} photons*eta/sec'.format( self._absintscalingfactor.__reciprocal__())) datared['absintrefFSN'] = self._lastabsintref.header.fsn datared['flux'] = self._absintscalingfactor.__reciprocal__().val datared['flux.err'] = self._absintscalingfactor.__reciprocal__().err datared['absintchi2'] = self._absintstat['Chi2_reduced'] datared['absintdof'] = self._absintstat['DoF'] datared['absintfactor'] = self._absintscalingfactor.val datared['absintfactor.err'] = self._absintscalingfactor.err datared['absintqmin'] = self._absintqrange.min() datared['absintqmax'] = self._absintqrange.max() else: raise ServiceError( 'S-D distance of the last seen absolute intensity reference measurement ' 'does not match the exposure under reduction.') self._logger.debug('Done absint FSN ' + str(im.header.fsn)) return im, datared