-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix spectra coaddition and cosmic masking #2377
Conversation
Thanks. I confirm that the original code is a bug, i.e. we agree on the math of what should be done and that the current code does not do that. Since this implementation is a little different than the #2372 I'd like to study it a bit more and run some test tiles to check for corner cases (e.g. entirely masked spectra). Ideally I'd like to also add some unit tests that would have caught the original bug, e.g. that a "coaddition" of a single resolution matrix should be the same as its input, and verifying what we expect the impact to be of a masked pixel on 1 out of N exposures, and the same pixel masked on N out of N exposures. In issue #2372 you documented the impact on chi2; I'd also like to study the impact on final redshifts to decide what to do about Kibo (e.g. a coadds+redshifts-only supplemental run). But that doesn't have to be a blocking factor for wrapping this up. |
Thanks for looking into that. As far as I tested, this implementation should give the same result as the previous one. I just felt it was a bit clearer. but for sure it's worth double checking. FWIW these are my runs https://data.desi.lbl.gov/desi/users/koposov/coadd_test/output/ on random hpx in kibo using this patch. So the redshfits can be directly compared to kibo. (this used earlier version of the patch, but AFAICS it should be exactly equal to what was before). |
I've added the test that trips the current desispec but passes after the PR. (coaddition of one spectrum with one masked pixel) |
extracted spectrum S and true spectrum S = R * M where R is resolution matrix is preserved exactly after the coadd
I've added a final (IMO) test that verifies if one has 'true 'spectrum M, Observations with 'random' resolution matrices R_i, random covariance matrices and extracted spectra S_i (obeying S_i= R_i M), then after running coadd the relationship |
Thanks, this all looks good. In the spirit of not making a major change just before the weekend, I will wait until Monday to merge this, but I don't see any problems. In the meantime, two more tests to request to really drive this home:
I'm not anticipating problems with those. Let's discuss at the Tuesday data telecon about implications for Kibo. I'm not worried from a quick spot check, but I'd like to quantify that and study some rrdetails chi2 vs. z curves. |
and fix the bug with masked pixels uncovered by the test
I've added the tests. Actually one of the tests did uncover the issue with the code. See b2f71bb While adding those, I thought I'd add a test of cosmic ray masking, but an intuitive test that I wrote completely fails currently. I.e. here I put a large cosmic in i-th pixel in i-th spectrum. def test_coadd_cosmic(self):
"""
Test coadd with cosmic rays
"""
nspec, nwave = 1000, 30
s1 = self._random_spectra(nspec, nwave)
rng = np.random.default_rng(432)
model0 = rng.uniform(0, 1, size=nwave)
ivar0 = rng.uniform(size=(nspec, nwave))
flux0 = model0 + 1./np.sqrt(ivar0) * rng.normal(size=(nspec,nwave))
resol0 = rng.uniform(0, 1, size=s1.resolution_data['b'].shape)
s1.flux['b'] = flux0 * 1
s1.ivar['b'] = ivar0 * 1
s1.resolution_data['b'] = resol0 * 1
COSMIC_VAL = 1e9
xgrid,ygrid = np.meshgrid(np.arange(nwave), np.arange(nspec))
mask = ygrid == (xgrid%nwave)
# make i-th pixel in i-th spectrum a cosmic
s1.flux['b'][mask] = COSMIC_VAL
s1.fibermap['TARGETID'] = [10] * nspec
coadd(s1, cosmics_nsig=5)
# prepare nspec-1 spectra with those cosmic affected pixels excised.
s2 = self._random_spectra(nspec-1, nwave)
for i in range(nwave):
s2.flux['b'][:, i] = flux0[~mask[:,i],i]*1
s2.ivar['b'][:, i] = ivar0[~mask[:,i],i]*1
s2.resolution_data['b'][:,:,i] = resol0[~mask[:,i],:,i]*1
coadd(s2, cosmics_nsig=5)
print(s1.flux['b'],s2.flux['b'])
self.assertTrue(np.allclose(s1.flux['b'], s2.flux['b']))
self.assertTrue(np.allclose(s1.ivar['b'], s2.ivar['b']))
self.assertTrue(np.allclose(s1.resolution_data['b'],
s2.resolution_data['b'])) |
Just a few more comments after looking at the code.
|
coadd_cameras is used extensively by fastspecfit to write out coadeed spectra and models and also by the GQP working group. @stephjuneau |
And @adambolton I think did a full coadd across cameras for iron to load into SPARCL. This may need to be reconsidered. |
There was some divergence there already i.e. gradivar=1./gradvar vs gradivar = (gradvar>0)/(gradvar+(gradvar==0))
Hi @segasai: confirming what @moustakas and @weaverba137 mentioned above that coadd_across_cameras is very important for GQP and beyond. Any change/streamlining should at least be backward compatible to avoid breaking workflows. Thanks! |
@stephjuneau I wasn't talking about breaking any interfaces here. I was just pointing that the function seem to suffer from the same issue that is topic of this PR. But indeed I wasn't aware that this f-n is widely used outside the main pipeline. |
The test case was also incorrect
When resolution matrices were added, the non-masked ivars were used. (this is kinda irrelevant with the oncoming fix, but important first step)
My latest commits include two tests for issues that @segasai identified that we need to fix:
|
I've committed the masker that can deal with more than one cosmic per pixel. Currently it's doing essentially iterative sigma clipping. That passes the tests, but my only worry are variable objects. With the new code we continue clipping till the chi^2 becomes low enough or we have less than 3 pixels in the stack. UPDATE on variable objects -- looking at sv1/dark/5059 RRL with targetid=39628511287183022 with 30 observations: |
add test for coadd_cameras bits
one when the comismic maskign would mask completely unrelated pixels when one of the spectra in the set is fully masked
There was one more bug in the original implementation (and the new one) is that desispec/py/desispec/coaddition.py Line 523 in 4eae355
When spectra are collected for masking, the ivar=0 ones are skipped, but when doing the masking, that 'off-by-N' desispec/py/desispec/coaddition.py Line 561 in 4eae355
Encountered when running on sv1/dark/5059 The last commit fixes that and adds a test. |
Add camera option to mask_cosmics for printing Also put the check for the minimum number of spectra needed for cosmic masking inside the function to avoid having it in two places
(motivated by desire of having all the lines of coadd()/coadd_cameras() test-covered)
Move the chi2 threshold into a function also get rid of harcoded value of 3 for min_cosmic rejection
deviate clearly more than prescribed by variance vector. In that case I ignore the chi^2 stats and just try to empirically estimate the threshold. It will never work particularly well, but it is better than doing what we are doing
make a much cleaner interface for mask_cosmics as it does not need to get all the spectra + indices + mask Now it will just accept the spectra that needs masking and it will return the boolean mask of cosmics. That will also make it easier to store that information in the future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks! So that we don't make major changes going into a weekend, I'll wait until Monday to merge and deploy this.
This is an a actual PR fixing #2372