コード例 #1
0
    def _fit_simple_lm(self, obs, model, guesser):
        from ngmix.fitting import LMSimple

        max_pars = self['max_pars']

        prior = self['search_prior']

        #if obs.has_psf():
        #    print("psf      _data:",obs.psf.gmix._data)
        #    print("psf copy _data:",obs.psf.gmix.copy()._data)
        fitter = LMSimple(obs,
                          model,
                          prior=prior,
                          use_logpars=self['use_logpars'],
                          lm_pars=max_pars['lm_pars'])

        for i in xrange(max_pars['ntry']):
            guess = guesser(prior=prior)
            print_pars(guess, front='    lm_guess: ')

            fitter.run_max(guess)
            res = fitter.get_result()
            if res['flags'] == 0:
                break
        return fitter
コード例 #2
0
ファイル: nfititer.py プロジェクト: esheldon/gmix_meds
    def _fit_simple_lm(self, mb_obs_list, model, params, use_prior=True):
        """
        Fit one of the "simple" models, e.g. exp or dev

        use flat g prior
        """

        from ngmix.fitting import LMSimple

        if use_prior:
            prior=self['model_pars'][model]['gflat_prior']
        else:
            prior=None

        ok=False
        ntry=params['lm_ntry']
        for i in xrange(ntry):
            guess=self.guesser(prior=prior)

            fitter=LMSimple(mb_obs_list,
                            model,
                            prior=prior,
                            lm_pars=params['lm_pars'])

            fitter.run_lm(guess)
            res=fitter.get_result()
            if res['flags']==0:
                ok=True
                break

        res['ntry']=i+1
        return fitter
コード例 #3
0
ファイル: nfit.py プロジェクト: esheldon/espy
    def _fit_simple_lm(self, obs, model, guesser):
        from ngmix.fitting import LMSimple

        max_pars=self['max_pars']

        prior=self['search_prior']

        #if obs.has_psf():
        #    print("psf      _data:",obs.psf.gmix._data)
        #    print("psf copy _data:",obs.psf.gmix.copy()._data)
        fitter=LMSimple(obs,
                        model,
                        prior=prior,
                        use_logpars=self['use_logpars'],
                        lm_pars=max_pars['lm_pars'])

        for i in xrange(max_pars['ntry']):
            guess=guesser(prior=prior)
            print_pars(guess,front='    lm_guess: ')

            fitter.run_max(guess)
            res=fitter.get_result()
            if res['flags']==0:
                break
        return fitter
コード例 #4
0
ファイル: sfit.py プロジェクト: esheldon/espy
    def go(self, ntry=1):
        from ngmix.fitting import LMSimple

        for i in xrange(ntry):
            guess=self.get_guess()
            fitter=LMSimple(self.obs,self.model,lm_pars=self.lm_pars)
            fitter.go(guess)

            res=fitter.get_result()
            if res['flags']==0:
                break

        self.fitter=fitter
コード例 #5
0
    def go(self, ntry=1):
        from ngmix.fitting import LMSimple

        for i in xrange(ntry):
            guess = self.get_guess()
            fitter = LMSimple(self.obs, self.model, lm_pars=self.lm_pars)
            fitter.go(guess)

            res = fitter.get_result()
            if res['flags'] == 0:
                break

        self.fitter = fitter
コード例 #6
0
ファイル: sfit.py プロジェクト: esheldon/espy
    def _go_lm(self, ntry=1):
        from ngmix.fitting import LMSimple

        for i in xrange(ntry):
            guess=self.guesser()
            fitter=LMSimple(self.obs,
                            self.model,
                            lm_pars=self.send_pars,
                            use_logpars=self.use_logpars,
                            prior=self.prior)

            fitter.go(guess)

            res=fitter.get_result()
            if res['flags']==0:
                break

        self.fitter=fitter
コード例 #7
0
def _run_ml_fitter(mbobs, rng):
    # fit the PSF
    _fit_psf(mbobs[0][0].psf)

    # overall flags, or'ed from each moments fit
    res = {'mcal_flags': 0}
    try:
        fitter = LMSimple(mbobs[0][0], 'exp')
        fitter.go(np.array([0, 0, 0, 0, 1, 10]))
        fres = fitter.get_result()
    except Exception as err:
        print('err:', err)
        fres = {'flags': np.ones(1, dtype=[('flags', 'i4')])}

    res['mcal_flags'] |= fres['flags']
    if not res['mcal_flags']:
        res['g'] = fres['g']
    return res
コード例 #8
0
    def _go_lm(self, ntry=1):
        from ngmix.fitting import LMSimple

        for i in xrange(ntry):
            guess = self.guesser()
            fitter = LMSimple(self.obs,
                              self.model,
                              lm_pars=self.send_pars,
                              use_logpars=self.use_logpars,
                              prior=self.prior)

            fitter.go(guess)

            res = fitter.get_result()
            if res['flags'] == 0:
                break

        self.fitter = fitter
コード例 #9
0
ファイル: maxfit.py プロジェクト: esheldon/gmix_meds
    def _fit_simple_lm(self, mb_obs_list, model, max_pars, prior):
        """
        Fit one of the "simple" models, e.g. exp or dev

        use flat g prior
        """

        from ngmix.fitting import LMSimple


        ntry=max_pars['ntry']
        for i in xrange(ntry):
            guess=self.guesser(prior=prior)

            # catch case where guess is just no good...
            try:
                fitter=LMSimple(mb_obs_list,
                                model,
                                prior=prior,
                                lm_pars=max_pars['lm_pars'])

                fitter.go(guess)
                res=fitter.get_result()
                if res['flags']==0:
                    break
            except GMixRangeError:
                # probably bad guesser
                print("    caught gmix range, using psf guesser")
                self.guesser=self._get_guesser_from_me_psf()
                res={'flags': GAL_FIT_FAILURE}
                fitter=None

        if res['flags']==0 and max_pars['replace_cov']:
            self._try_replace_cov(fitter)

        res['ntry']=i+1
        return fitter
コード例 #10
0
    def _fit_psf_max(self, obs):
        from ngmix.fitting import MaxSimple, LMSimple
        assert self['psf_model'] in ["turb",
                                     "gauss"], "gauss,turb only for now"

        max_pars = self['psf_max_pars']

        if self['psf_method'] == 'Nelder-Mead':
            fitter = MaxSimple(obs,
                               self['psf_model'],
                               method=self['psf_method'])
        else:
            fitter = LMSimple(obs, self['psf_model'], lm_pars=max_pars)

        Tguess = 2 * self['psf_sigma_guess']**2
        Fguess = obs.image.sum()
        guess0 = array([0.0, 0.0, 0.0, 0.0, Tguess, Fguess])

        for i in xrange(self['psf_ntry']):
            guess = guess0.copy()
            guess[0:0 + 2] += 0.01 * srandu(2)
            guess[2:2 + 2] += 0.1 * srandu(2)
            guess[4] *= (1.0 + 0.1 * srandu())
            guess[5] *= (1.0 + 0.1 * srandu())

            print_pars(guess, front='    guess: ')

            if self['psf_method'] == 'lm':
                fitter.run_max(guess)
            else:
                fitter.run_max(guess, **max_pars)

            res = fitter.get_result()
            if res['flags'] == 0:
                print("    max_nfev:", res['nfev'])
                break

        return fitter
コード例 #11
0
def test_ml_fitting_exp_obj_gauss_psf_smoke(g1_true, g2_true, wcs_g1, wcs_g2):
    rng = np.random.RandomState(seed=10)

    image_size = 33
    cen = (image_size - 1) / 2
    gs_wcs = galsim.ShearWCS(0.25, galsim.Shear(g1=wcs_g1,
                                                g2=wcs_g2)).jacobian()
    scale = np.sqrt(gs_wcs.pixelArea())

    g_prior = ngmix.priors.GPriorBA(0.1)
    cen_prior = ngmix.priors.CenPrior(0, 0, scale, scale)
    T_prior = ngmix.priors.FlatPrior(0.01, 2)
    F_prior = ngmix.priors.FlatPrior(1e-4, 1e9)
    prior = ngmix.joint_prior.PriorSimpleSep(cen_prior, g_prior, T_prior,
                                             F_prior)

    gal = galsim.Exponential(half_light_radius=0.5).shear(
        g1=g1_true, g2=g2_true).withFlux(400)
    obj = galsim.Convolve([gal, galsim.Gaussian(fwhm=0.5)])

    psf_im = galsim.Gaussian(fwhm=0.5).drawImage(nx=33,
                                                 ny=33,
                                                 wcs=gs_wcs,
                                                 method='no_pixel').array
    psf_gmix = ngmix.gmix.make_gmix_model(
        [0, 0, 0, 0, fwhm_to_T(0.5), 1], "gauss")
    psf_obs = Observation(image=psf_im, gmix=psf_gmix)

    im = obj.drawImage(nx=image_size,
                       ny=image_size,
                       wcs=gs_wcs,
                       method='no_pixel').array
    noise = np.sqrt(np.sum(im**2)) / 1e16

    wgt = np.ones_like(im) / noise**2

    guess = np.ones(6) * 0.1
    guess[0] = 0
    guess[1] = 0
    guess[2] = g1_true
    guess[3] = g2_true
    guess[4] = fwhm_to_T(0.5)
    guess[5] = 400

    g1arr = []
    g2arr = []
    farr = []
    xarr = []
    yarr = []
    for _ in range(50):
        shift = rng.uniform(low=-scale / 2, high=scale / 2, size=2)
        xy = gs_wcs.toImage(galsim.PositionD(shift))

        im = obj.shift(dx=shift[0],
                       dy=shift[1]).drawImage(nx=image_size,
                                              ny=image_size,
                                              wcs=gs_wcs,
                                              method='no_pixel',
                                              dtype=np.float64).array

        jac = Jacobian(y=cen + xy.y,
                       x=cen + xy.x,
                       dudx=gs_wcs.dudx,
                       dudy=gs_wcs.dudy,
                       dvdx=gs_wcs.dvdx,
                       dvdy=gs_wcs.dvdy)

        _im = im + (rng.normal(size=im.shape) * noise)
        obs = Observation(image=_im, weight=wgt, jacobian=jac, psf=psf_obs)
        fitter = LMSimple(obs, 'exp', prior=prior)
        fitter.go(guess + rng.normal(size=6) * 0.01)
        res = fitter.get_result()
        if res['flags'] == 0:
            _g1, _g2, _ = res['g'][0], res['g'][1], res['pars'][4]
            g1arr.append(_g1)
            g2arr.append(_g2)
            farr.append(res['pars'][5])
            xarr.append(res['pars'][1])
            yarr.append(res['pars'][0])

    g1 = np.mean(g1arr)
    g2 = np.mean(g2arr)
    gtol = 1e-5
    assert np.abs(g1 - g1_true) < gtol
    assert np.abs(g2 - g2_true) < gtol

    xerr = np.std(xarr) / np.sqrt(len(xarr))
    assert np.abs(np.mean(xarr)) < xerr * 5
    yerr = np.std(yarr) / np.sqrt(len(yarr))
    assert np.abs(np.mean(yarr)) < yerr * 5
コード例 #12
0
    def measure(self, measRecord, exposure):
        psf = exposure.getPsf()
        if psf is None:
            raise MeasurementError(
                self.flagHandler.getDefinition(
                    SingleFrameLMSimpleShapePlugin.ErrEnum.flag_noPsf).doc,
                SingleFrameLMSimpleShapePlugin.ErrEnum.flag_noPsf)
        # make an observation for the psf image
        psfArray = psf.computeKernelImage().getArray()
        psfJacob = ngmix.UnitJacobian(row=psfArray.shape[0] / 2.0,
                                      col=psfArray.shape[1] / 2.0)
        psfObs = Observation(psfArray, jacobian=psfJacob)

        #   Fallback code if no psf algorithm is requested.  Then just do an LM single gaussian fit
        if self.config.psfName is None:
            pfitter = LMSimple(psfObs, 'gauss')
            #   gues parameters for a simple Gaussian
            psfPars = [0.0, 0.0, -0.03, 0.02, 8.0, 1.0]
            pfitter.go(psfPars)
            psfGMix = pfitter.get_gmix()
        else:
            shape = psfArray.shape
            image = lsst.afw.image.ImageD(shape[1], shape[0])
            evaluate = measRecord.get(self.psfMsfKey).evaluate()
            evaluate.addToImage(
                image.getArray(),
                lsst.afw.geom.Point2I(-(shape[0] - 1) // 2,
                                      -(shape[1] - 1) // 2))
            psfObs = Observation(image.getArray(), jacobian=psfJacob)
            #   Now create a gmix directly from what the PsfApprox algorithm produced
            multiShapeletFunction = measRecord.get(self.psfMsfKey)
            shapeletFunctions = multiShapeletFunction.getComponents()
            psfPars = []
            #   add pars in the order flux, y, x, yy, xy, xx
            for sf in shapeletFunctions:
                psfPars.append(sf.getCoefficients()[0])
                psfPars.append(sf.getEllipse().getCenter().getY())
                psfPars.append(sf.getEllipse().getCenter().getX())
                psfPars.append(sf.getEllipse().getCore().getIyy())
                psfPars.append(sf.getEllipse().getCore().getIxy())
                psfPars.append(sf.getEllipse().getCore().getIxx())
            psfGMix = ngmix.gmix.GMix(len(shapeletFunctions), psfPars)
        psfObs.set_gmix(psfGMix)

        #   Now create an obs for the galaxy itself, including the weight plane
        galArray = exposure.getMaskedImage().getImage().getArray()
        galShape = galArray.shape
        galJacob = ngmix.UnitJacobian(row=galShape[1] / 2.0,
                                      col=galShape[0] / 2.0)
        variance = exposure.getMaskedImage().getVariance().getArray()
        gal_weight = 1 / variance
        obs = Observation(galArray,
                          weight=gal_weight,
                          jacobian=galJacob,
                          psf=psfObs)

        fp = measRecord.getFootprint()

        bbox = fp.getBBox()
        ymin = bbox.getBeginY() - exposure.getXY0().getY()
        ymax = bbox.getEndY() - exposure.getXY0().getY()
        xmin = bbox.getBeginX() - exposure.getXY0().getX()
        xmax = bbox.getEndX() - exposure.getXY0().getX()
        flux = exposure.getMaskedImage().getImage().getArray()[
            ymin:ymax, xmin:xmax].sum()

        xx = fp.getShape().getIxx()
        yy = fp.getShape().getIyy()
        xy = fp.getShape().getIxy()

        #   Now run the shape algorithm, using the config parameters
        lmPars = {
            'maxfev': self.config.maxfev,
            'ftol': self.config.ftol,
            'xtol': self.config.xtol
        }
        maxPars = {'method': 'lm', 'lm_pars': lmPars}
        guesser = ngmix.guessers.TFluxGuesser(xx + yy, flux)
        runner = ngmix.bootstrap.MaxRunner(obs, self.config.model, maxPars,
                                           guesser)
        runner.go(ntry=4)
        fitter = runner.fitter
        res = fitter.get_result()

        #   Set the results, including the fit info returned by MaxRunner
        if res['flags'] != 0:
            if res['flags'] & LM_SINGULAR_MATRIX:
                raise MeasurementError(
                    self.flagHandler.getDefinition(
                        SingleFrameLMSimpleShapePlugin.ErrEnum.
                        flag_singularMatrix).doc,
                    SingleFrameLMSimpleShapePlugin.ErrEnum.flag_singularMatrix)
            if res['flags'] & LM_NEG_COV_EIG:
                raise MeasurementError(
                    self.flagHandler.getDefinition(
                        SingleFrameLMSimpleShapePlugin.ErrEnum.
                        flag_negativeCovEig).doc,
                    SingleFrameLMSimpleShapePlugin.ErrEnum.flag_negativeCovEig)
            if res['flags'] & LM_NEG_COV_DIAG:
                raise MeasurementError(
                    self.flagHandler.getDefinition(
                        SingleFrameLMSimpleShapePlugin.ErrEnum.
                        flag_negativeCovDiag).doc,
                    SingleFrameLMSimpleShapePlugin.ErrEnum.flag_negativeCovDiag
                )
            if res['flags'] & EIG_NOTFINITE:
                raise MeasurementError(
                    self.flagHandler.getDefinition(
                        SingleFrameLMSimpleShapePlugin.ErrEnum.
                        flag_eigNotFinite).doc,
                    SingleFrameLMSimpleShapePlugin.ErrEnum.flag_eigNotFinite)
            if res['flags'] & LM_FUNC_NOTFINITE:
                raise MeasurementError(
                    self.flagHandler.getDefinition(
                        SingleFrameLMSimpleShapePlugin.ErrEnum.
                        flag_funcNotFinite).doc,
                    SingleFrameLMSimpleShapePlugin.ErrEnum.flag_funcNotFinite)
            if res['flags'] & DIV_ZERO:
                raise MeasurementError(
                    self.flagHandler.getDefinition(
                        SingleFrameLMSimpleShapePlugin.ErrEnum.flag_divZero).
                    doc, SingleFrameLMSimpleShapePlugin.ErrEnum.flag_divZero)
            if res['nfev'] >= self.config.maxfev:
                raise MeasurementError(
                    self.flagHandler.getDefinition(
                        SingleFrameLMSimpleShapePlugin.ErrEnum.flag_maxFev).
                    doc, SingleFrameLMSimpleShapePlugin.ErrEnum.flag_maxFev)
            #   Unknown error, but there should be an errmsg set by ngmix
            raise RuntimeError(res['errmsg'])

        #   Convert the nGauss Gaussians to ShapeletFunction's. Zeroth order HERMITES are Gaussians.
        #   There are always 6 parameters for each Gaussian.
        galGMix = fitter.get_gmix()
        galPars = galGMix.get_full_pars()

        # convert the center in row,col coordinates to pixel coordinates and save
        cen = galGMix.get_cen()
        measRecord.set(self.xKey, cen[1] + galShape[1] / 2.0)
        measRecord.set(self.yKey, cen[0] + galShape[0] / 2.0)
        # save the model flux
        measRecord.set(self.fluxKey, galGMix.get_flux())
        # save the model T=xx+yy, e1, and e2
        (e1, e2, T) = galGMix.get_e1e2T()
        measRecord.set(self.e1Key, e1)
        measRecord.set(self.e2Key, e2)
        measRecord.set(self.TKey, T)
        for i in range(self.nGauss):
            flux, y, x, iyy, ixy, ixx = galPars[i *
                                                self._gaussian_pars_len:(i +
                                                                         1) *
                                                self._gaussian_pars_len]
            quad = lsst.afw.geom.ellipses.Quadrupole(ixx, iyy, ixy)
            ellipse = lsst.afw.geom.ellipses.Ellipse(
                quad, lsst.afw.geom.Point2D(x, y))
            # create a 0th order (gaussian) shapelet function.
            sf = ShapeletFunction(0, HERMITE, ellipse)
            sf.getCoefficients()[0] = flux / ShapeletFunction.FLUX_FACTOR
            measRecord.set(self.keys[i], sf)
コード例 #13
0
ファイル: des_piff.py プロジェクト: beckermr/misc
    def _fit_smooth_model(self):
        dxy = 256
        ny = 4096 // dxy + 1
        nx = 2048 // dxy + 1

        xloc = np.empty((ny, nx), dtype=np.float64)
        yloc = np.empty((ny, nx), dtype=np.float64)
        pars = np.empty((ny, nx, 3), dtype=np.float64)
        for yi, yl in enumerate(np.linspace(1, 4096, ny)):
            for xi, xl in enumerate(np.linspace(1, 2048, nx)):
                rng = np.random.RandomState(seed=yi + nx * xi)
                xloc[yi, xi] = xl
                yloc[yi, xi] = yl

                pos = galsim.PositionD(x=xl, y=yl)
                img = self._draw(pos).drawImage(
                    nx=19, ny=19, scale=0.25, method='sb').array
                nse = np.std(
                    np.concatenate([img[0, :], img[-1, :]]))
                obs = ngmix.Observation(
                    image=img,
                    weight=np.ones_like(img)/nse**2,
                    jacobian=ngmix.jacobian.DiagonalJacobian(
                        x=9, y=9, scale=0.25))

                _g1 = np.nan
                _g2 = np.nan
                _T = np.nan
                for _ in range(5):
                    try:
                        am = Admom(obs, rng=rng)
                        am.go(0.3)
                        res = am.get_result()
                        if res['flags'] != 0:
                            continue

                        lm = LMSimple(obs, 'turb')
                        lm.go(res['pars'])
                        lm_res = lm.get_result()
                        if lm_res['flags'] == 0:
                            _g1 = lm_res['pars'][2]
                            _g2 = lm_res['pars'][3]
                            _T = lm_res['pars'][4]
                            break
                    except ngmix.gexceptions.GMixRangeError:
                        pass

                pars[yi, xi, 0] = _g1
                pars[yi, xi, 1] = _g2
                pars[yi, xi, 2] = _T

        xloc = xloc.ravel()
        yloc = yloc.ravel()
        pos = np.stack([xloc, yloc], axis=1)
        assert pos.shape == (xloc.shape[0], 2)

        # make interps
        g1 = pars[:, :, 0].ravel()
        msk = np.isfinite(g1)
        if len(msk) < 10:
            raise ValueError('DES Piff fitting failed too much!')
        if np.any(~msk):
            g1[~msk] = np.mean(g1[msk])
        self._g1int = CloughTocher2DInterpolator(pos, g1)

        g2 = pars[:, :, 1].ravel()
        msk = np.isfinite(g2)
        if len(msk) < 10:
            raise ValueError('DES Piff fitting failed too much!')
        if np.any(~msk):
            g2[~msk] = np.mean(g2[msk])
        self._g2int = CloughTocher2DInterpolator(pos, g2)

        T = pars[:, :, 2].ravel()
        msk = np.isfinite(T)
        if len(msk) < 10:
            raise ValueError('DES Piff fitting failed too much!')
        if np.any(~msk):
            T[~msk] = np.mean(T[msk])
        self._Tint = CloughTocher2DInterpolator(pos, T)

        self._did_fit = True
コード例 #14
0
def test_ml_fitting_gauss_smoke(g1_true, g2_true, wcs_g1, wcs_g2):
    rng = np.random.RandomState(seed=42)

    # allow some small relative bias due to approximate exp function
    tol = 1.0e-5

    image_size = 33
    cen = (image_size - 1) / 2
    gs_wcs = galsim.ShearWCS(0.25, galsim.Shear(g1=wcs_g1,
                                                g2=wcs_g2)).jacobian()
    scale = np.sqrt(gs_wcs.pixelArea())

    g_prior = ngmix.priors.GPriorBA(0.2)
    cen_prior = ngmix.priors.CenPrior(0, 0, scale, scale)
    T_prior = ngmix.priors.FlatPrior(0.1, 2)
    F_prior = ngmix.priors.FlatPrior(1e-4, 1e9)
    prior = ngmix.joint_prior.PriorSimpleSep(cen_prior, g_prior, T_prior,
                                             F_prior)

    obj = galsim.Gaussian(fwhm=0.9).shear(g1=g1_true, g2=g2_true).withFlux(400)
    im = obj.drawImage(nx=image_size,
                       ny=image_size,
                       wcs=gs_wcs,
                       method='no_pixel').array
    noise = np.sqrt(np.sum(im**2)) / 1e16
    wgt = np.ones_like(im) / noise**2

    g1arr = []
    g2arr = []
    Tarr = []
    farr = []
    xarr = []
    yarr = []
    for _ in range(100):
        shift = rng.uniform(low=-scale / 2, high=scale / 2, size=2)
        xy = gs_wcs.toImage(galsim.PositionD(shift))

        im = obj.shift(dx=shift[0],
                       dy=shift[1]).drawImage(nx=image_size,
                                              ny=image_size,
                                              wcs=gs_wcs,
                                              method='no_pixel',
                                              dtype=np.float64).array

        jac = Jacobian(y=cen + xy.y,
                       x=cen + xy.x,
                       dudx=gs_wcs.dudx,
                       dudy=gs_wcs.dudy,
                       dvdx=gs_wcs.dvdx,
                       dvdy=gs_wcs.dvdy)

        _im = im + (rng.normal(size=im.shape) * noise)
        obs = Observation(image=_im, weight=wgt, jacobian=jac)
        fitter = LMSimple(obs, 'gauss', prior=prior)

        guess = np.ones(6) * 0.1
        guess[0] = 0
        guess[1] = 0
        guess[2] = g1_true
        guess[3] = g2_true
        guess[4] = fwhm_to_T(0.9)
        guess[5] = 400 * scale * scale

        fitter.go(guess + rng.normal(size=6) * 0.01)
        res = fitter.get_result()

        if res['flags'] == 0:
            _g1, _g2, _T = res['g'][0], res['g'][1], res['pars'][4]
            g1arr.append(_g1)
            g2arr.append(_g2)
            Tarr.append(_T)
            farr.append(res['pars'][5])
            xarr.append(res['pars'][1])
            yarr.append(res['pars'][0])

    g1 = np.mean(g1arr)
    g2 = np.mean(g2arr)
    assert np.abs(g1 - g1_true) < tol
    assert np.abs(g2 - g2_true) < tol

    if g1_true == 0 and g2_true == 0:
        T = np.mean(Tarr)
        Ttrue = fwhm_to_T(0.9)
        assert T / Ttrue - 1 < tol

    fmn = np.mean(farr) / scale / scale

    assert np.abs(fmn / 400 - 1) < tol

    xerr = np.std(xarr) / np.sqrt(len(xarr))
    assert np.abs(np.mean(xarr)) < xerr * 5
    yerr = np.std(yarr) / np.sqrt(len(yarr))
    assert np.abs(np.mean(yarr)) < yerr * 5