Skip to content

Commit 3b4630c

Browse files
authored
Merge pull request #236 from esheldon/psf_gauss
fix bug with metacal psf method "gauss"
2 parents 51c488b + 45c2dc3 commit 3b4630c

File tree

7 files changed

+241
-13
lines changed

7 files changed

+241
-13
lines changed

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## v2.3.1
2+
3+
## Bug Fixes
4+
5+
- The metacal psf reconvolution method 'gauss' (which uses MetacalGaussPSF)
6+
now works with a complex WCS
7+
18
## v2.3.0
29

310
### new features

mdet_tests/test_mdet_regression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ def test_mdet_regression(fname, write=False):
268268
),
269269
}
270270
else:
271-
assert col in ["shear", "shear_bands"]
271+
assert col in ["det_bands", "shear", "shear_bands"]
272272

273273

274274
if __name__ == "__main__":

ngmix/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '2.3.0' # noqa
1+
__version__ = '2.3.1' # noqa

ngmix/metacal/metacal.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ def _set_data(self):
419419
# interpolated psf deconvolved from pixel. This is what
420420
# we dilate, shear, etc and reconvolve the image by
421421
self.psf_int_nopix = galsim.Convolve([psf_int, self.pixel_inv])
422+
self.psf_int = psf_int
422423

423424
def get_wcs(self):
424425
"""
@@ -572,15 +573,15 @@ def _get_dilated_psf(self, shear, doshear=False):
572573
_do_dilate for the algorithm
573574
574575
"""
575-
import galsim
576+
# import galsim
576577

577578
assert doshear is False, 'no shearing gauss psf'
578579

579580
gauss_psf = _get_gauss_target_psf(
580-
self.psf_int_nopix, flux=self.psf_flux,
581+
self.psf_int,
582+
flux=self.psf_flux,
581583
)
582-
psf_grown_nopix = _do_dilate(gauss_psf, shear)
583-
psf_grown = galsim.Convolve(psf_grown_nopix, self.pixel)
584+
psf_grown = _do_dilate(gauss_psf, shear)
584585
return psf_grown
585586

586587
def _make_psf_obs(self, gsim):

ngmix/observation.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -463,8 +463,9 @@ def set_ormask(self, ormask):
463463
ormask = np.ascontiguousarray(ormask)
464464
assert len(ormask.shape) == 2, "ormask must be 2d"
465465

466-
assert (ormask.shape == image.shape),\
466+
assert (ormask.shape == image.shape), (
467467
"image and ormask must be same shape"
468+
)
468469

469470
self._ormask = ormask
470471

@@ -1006,8 +1007,9 @@ def append(self, obs_list):
10061007
An ObsList. An AssertionError will be raised if `obs_list` is not
10071008
an `ngmix.ObsList`.
10081009
"""
1009-
assert isinstance(obs_list, ObsList),\
1010+
assert isinstance(obs_list, ObsList), (
10101011
'obs_list should be of type ObsList'
1012+
)
10111013
super(MultiBandObsList, self).append(obs_list)
10121014

10131015
def get_s2n(self):
@@ -1092,8 +1094,9 @@ def __setitem__(self, index, obs_list):
10921094
"""
10931095
over-riding this for type safety
10941096
"""
1095-
assert isinstance(obs_list, ObsList),\
1097+
assert isinstance(obs_list, ObsList), (
10961098
'obs_list should be of type ObsList'
1099+
)
10971100
super(MultiBandObsList, self).__setitem__(index, obs_list)
10981101

10991102

@@ -1306,8 +1309,9 @@ def __setitem__(self, index, kobs):
13061309
"""
13071310
over-riding this for type safety
13081311
"""
1309-
assert isinstance(kobs, KObservation),\
1312+
assert isinstance(kobs, KObservation), (
13101313
'kobs should be of type KObservation'
1314+
)
13111315
super(KObsList, self).__setitem__(index, kobs)
13121316

13131317

ngmix/tests/test_metacal_accuracy.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import numpy as np
2+
import ngmix
3+
import galsim
4+
import pytest
5+
6+
7+
@pytest.mark.parametrize('psf', ['gauss', 'fitgauss', 'galsim_obj'])
8+
def test_metacal_accuracy(psf):
9+
10+
ntrial = 100
11+
seed = 99
12+
13+
if psf == 'galsim_obj':
14+
psf = galsim.Gaussian(fwhm=1.05)
15+
16+
# Wacky WCS
17+
wcs_g1 = 0.1
18+
wcs_g2 = 0.0
19+
wcs = galsim.ShearWCS(0.263, galsim.Shear(g1=wcs_g1, g2=wcs_g2)).jacobian()
20+
21+
print("WCS:", wcs.getDecomposition())
22+
print()
23+
print()
24+
25+
shear_true = 0.02
26+
rng = np.random.RandomState(seed)
27+
28+
# We will measure moments with a fixed gaussian weight function
29+
weight_fwhm = 1.2
30+
fitter = ngmix.gaussmom.GaussMom(fwhm=weight_fwhm)
31+
psf_fitter = ngmix.gaussmom.GaussMom(fwhm=weight_fwhm)
32+
33+
# these "runners" run the measurement code on observations
34+
psf_runner = ngmix.runners.PSFRunner(fitter=psf_fitter)
35+
runner = ngmix.runners.Runner(fitter=fitter)
36+
37+
# this "bootstrapper" runs the metacal image shearing as well as both psf
38+
# and object measurements
39+
boot = ngmix.metacal.MetacalBootstrapper(
40+
runner=runner, psf_runner=psf_runner,
41+
rng=rng,
42+
psf=psf,
43+
types=['noshear', '1p', '1m'],
44+
)
45+
dlist = []
46+
for i in range(ntrial):
47+
im, psf_im, obs = _make_data(rng=rng, shear=shear_true, wcs=wcs)
48+
resdict, obsdict = boot.go(obs)
49+
for stype, sres in resdict.items():
50+
st = _make_struct(res=sres, obs=obsdict[stype], shear_type=stype)
51+
dlist.append(st)
52+
53+
print()
54+
data = np.hstack(dlist)
55+
56+
w = _select(data=data, shear_type='noshear')
57+
w_1p = _select(data=data, shear_type='1p')
58+
w_1m = _select(data=data, shear_type='1m')
59+
60+
g1 = data['g'][w, 0].mean()
61+
g1err = data['g'][w, 0].std() / np.sqrt(w.size)
62+
g1_1p = data['g'][w_1p, 0].mean()
63+
g1_1m = data['g'][w_1m, 0].mean()
64+
65+
R11 = (g1_1p - g1_1m)/0.02
66+
67+
shear = g1 / R11
68+
shear_err = g1err / R11
69+
m = shear / shear_true - 1
70+
merr = shear_err / shear_true
71+
72+
s2n = data['s2n'][w].mean()
73+
print('S/N: %g' % s2n)
74+
print('R11: %g' % R11)
75+
print('m: %g +/- %g (99.7%% conf)' % (m, merr*3))
76+
77+
assert np.abs(m - 0.00034) < 1.0e-4
78+
79+
80+
def _make_struct(res, obs, shear_type):
81+
"""
82+
make the data structure
83+
Parameters
84+
----------
85+
res: dict
86+
With keys 's2n', 'e', and 'T'
87+
obs: ngmix.Observation
88+
The observation for this shear type
89+
shear_type: str
90+
The shear type
91+
Returns
92+
-------
93+
1-element array with fields
94+
"""
95+
dt = [
96+
('flags', 'i4'),
97+
('shear_type', 'U7'),
98+
('s2n', 'f8'),
99+
('g', 'f8', 2),
100+
('T', 'f8'),
101+
('Tpsf', 'f8'),
102+
]
103+
data = np.zeros(1, dtype=dt)
104+
data['shear_type'] = shear_type
105+
data['flags'] = res['flags']
106+
if res['flags'] == 0:
107+
data['s2n'] = res['s2n']
108+
# for moments we are actually measureing e, the elliptity
109+
data['g'] = res['e']
110+
data['T'] = res['T']
111+
else:
112+
data['s2n'] = np.nan
113+
data['g'] = np.nan
114+
data['T'] = np.nan
115+
data['Tpsf'] = np.nan
116+
# we only have one epoch and band, so we can get the psf T from the
117+
# observation rather than averaging over epochs/bands
118+
data['Tpsf'] = obs.psf.meta['result']['T']
119+
return data
120+
121+
122+
def _select(data, shear_type):
123+
"""
124+
select the data by shear type and size
125+
Parameters
126+
----------
127+
data: array
128+
The array with fields shear_type and T
129+
shear_type: str
130+
e.g. 'noshear', '1p', etc.
131+
Returns
132+
-------
133+
array of indices
134+
"""
135+
# raw moments, so the T is the post-psf T. This the
136+
# selection is > 1.2 rather than something smaller like 0.5
137+
# for pre-psf T from one of the maximum likelihood fitters
138+
wtype, = np.where(
139+
(data['shear_type'] == shear_type) &
140+
(data['flags'] == 0)
141+
)
142+
w, = np.where(data['T'][wtype]/data['Tpsf'][wtype] > 1.2)
143+
print('%s kept: %d/%d' % (shear_type, w.size, wtype.size))
144+
w = wtype[w]
145+
return w
146+
147+
148+
def _make_data(rng, shear, wcs):
149+
"""
150+
simulate an exponential object with moffat psf
151+
the hlr of the exponential is drawn from a gaussian
152+
with mean 0.4 arcseconds and sigma 0.2
153+
Parameters
154+
----------
155+
rng: np.random.RandomState
156+
The random number generator
157+
noise: float
158+
Noise for the image
159+
shear: (g1, g2)
160+
The shear in each component
161+
Returns
162+
-------
163+
ngmix.Observation
164+
"""
165+
noise = 1.0e-8
166+
psf_noise = 1.0e-8
167+
stamp_size = 91
168+
169+
psf_fwhm = 0.9
170+
gal_hlr = 0.5
171+
psf = galsim.Moffat(
172+
beta=2.5,
173+
fwhm=psf_fwhm,
174+
).shear(
175+
g1=0.02,
176+
g2=-0.01,
177+
)
178+
obj0 = galsim.Exponential(
179+
half_light_radius=gal_hlr,
180+
).shear(
181+
g1=shear,
182+
)
183+
obj = galsim.Convolve(psf, obj0)
184+
185+
psf_im = psf.drawImage(nx=stamp_size, ny=stamp_size, wcs=wcs).array
186+
im = obj.drawImage(nx=stamp_size, ny=stamp_size, wcs=wcs).array
187+
188+
psf_im += rng.normal(scale=psf_noise, size=psf_im.shape)
189+
im += rng.normal(scale=noise, size=im.shape)
190+
191+
cen = (np.array(im.shape)-1.0)/2.0
192+
psf_cen = (np.array(psf_im.shape)-1.0)/2.0
193+
194+
jacobian = ngmix.Jacobian(
195+
x=cen[1], y=cen[0], wcs=wcs.jacobian(
196+
image_pos=galsim.PositionD(cen[1], cen[0])
197+
),
198+
)
199+
psf_jacobian = ngmix.Jacobian(
200+
x=psf_cen[1], y=psf_cen[0], wcs=wcs.jacobian(
201+
image_pos=galsim.PositionD(psf_cen[1], psf_cen[0])
202+
),
203+
)
204+
205+
wt = im*0 + 1.0/noise**2
206+
psf_wt = psf_im*0 + 1.0/psf_noise**2
207+
psf_obs = ngmix.Observation(
208+
psf_im,
209+
weight=psf_wt,
210+
jacobian=psf_jacobian,
211+
)
212+
obs = ngmix.Observation(
213+
im,
214+
weight=wt,
215+
jacobian=jacobian,
216+
psf=psf_obs,
217+
)
218+
return im, psf_im, obs

ngmix/tests/test_metacal_bootstrap.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,4 @@ def test_metacal_bootstrap_gaussmom_response(metacal_caching):
178178
Rvals[i] = (res1p['e'][0] - res1m['e'][0])/0.02
179179

180180
Rmean = Rvals.mean()
181-
# this response value comes from 2.0.0, and had an error less than of
182-
# 1.0e-5 on it. Allow for differences in rng/arch etc. by taking 1.0e-4
183-
assert abs(Rmean - 0.28535) < 1.0e-4
181+
assert abs(Rmean - 0.28159) < 1.0e-4

0 commit comments

Comments
 (0)