Skip to content
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

Datacube core #1669

Merged
merged 97 commits into from
Oct 14, 2023
Merged

Datacube core #1669

merged 97 commits into from
Oct 14, 2023

Conversation

rcooke-ast
Copy link
Collaborator

@rcooke-ast rcooke-ast commented Sep 11, 2023

This looks like a large PR, but it's a relatively simple refactoring to make the CoAdd3D look a lot more like CoAdd2D. Here are the main changes:

  • IFU reduction is replaced throughout the code by SlicerIFU, with a view to possibly support FiberIFU as another reduction mode in the (distant) future.
  • pypeit.core.datacube.py has been refactored to core routines and code that uses PypeIt-specific objects.
  • Unused routines from pypeit.core.datacube.py have been moved to deprecated

I should emphasise that there's virtually no new code here. It's just a reorganising of the code that makes datacubes so it's easier for others to read, improve, and it mostly just required writing a lot more docstrings... joy!

I'm pretty sure that nothing is broken, but I'll run the tests just to be sure...

@codecov-commenter
Copy link

codecov-commenter commented Sep 11, 2023

Codecov Report

Merging #1669 (3ad7d3c) into develop (b23ec14) will increase coverage by 0.03%.
The diff coverage is 10.94%.

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

@@             Coverage Diff             @@
##           develop    #1669      +/-   ##
===========================================
+ Coverage    41.05%   41.09%   +0.03%     
===========================================
  Files          189      190       +1     
  Lines        43568    43621      +53     
===========================================
+ Hits         17888    17926      +38     
- Misses       25680    25695      +15     
Files Coverage Δ
pypeit/core/datacube.py 8.27% <ø> (+2.69%) ⬆️
pypeit/core/meta.py 86.95% <ø> (ø)
pypeit/display/display.py 8.00% <ø> (ø)
pypeit/find_objects.py 45.95% <100.00%> (ø)
pypeit/flatfield.py 53.48% <ø> (ø)
pypeit/pypeit.py 77.12% <100.00%> (ø)
pypeit/scripts/show_2dspec.py 11.73% <0.00%> (ø)
pypeit/core/flat.py 50.30% <0.00%> (ø)
pypeit/par/pypeitpar.py 96.03% <60.00%> (-0.05%) ⬇️
pypeit/slittrace.py 30.14% <50.00%> (-0.05%) ⬇️
... and 9 more

@jhennawi jhennawi self-requested a review September 11, 2023 21:07
Copy link
Collaborator Author

@rcooke-ast rcooke-ast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed PR comments

doc/coadd3d.rst Show resolved Hide resolved
pypeit/core/extract.py Outdated Show resolved Hide resolved
"""
Utility function to provide the world coordinate system of the datacube
"""
return wcs.WCS(self.head0)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the benefit of not computing ivar repeatedly, but I worry that we'll then be carrying around self._ivar and self.ivar, which seems a little messy to me. I also suspect it's rare that the ivar will ever be used... and it's also pretty quick to generate self.wcs. Also, is there a point in having the @property and function in this case? Couldn't we just have self.ivar = utils.inverse(self.sig**2) when a DataCube gets loaded?

Overall, I'm tempted to leave things the way they are, but it might be that I'm not appreciating the full benefit of this, so if you think this really ought to be done, then should it be done in the from_file() routine?

pypeit/coadd3d.py Outdated Show resolved Hide resolved
doc/coadd3d.rst Show resolved Hide resolved
pypeit/spectrographs/gemini_gnirs.py Outdated Show resolved Hide resolved
pypeit/spectrographs/gemini_gnirs.py Outdated Show resolved Hide resolved
pypeit/spectrographs/keck_kcwi.py Outdated Show resolved Hide resolved
pypeit/tests/test_ref_index.py Outdated Show resolved Hide resolved
pypeit/coadd3d.py Outdated Show resolved Hide resolved
msgs.info("Adopting a square pixel spatial scale of {0:f} arcsec".format(3600.0 * self._dspat))
# If the user has not specified the spectral sampling, then set it now to the largest value
if self._dwv is None:
self._dwv = np.max(self._specscale)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you return these values instead of assigning to self. For a small number of return values that makes the control flow more transparent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch - I agree 👍 Actually, I decided to move this entire section of code to core.

sensfunc = self.flux_spline(senswave)

# Generate a datacube
outfile = datacube.get_output_filename("", self.cubepar['output_filename'], True, -1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My advice is to create the this outfile name int he init method or before coadd is called. And then have self.coadd(outfile) be the calling sequence, to make it clear that this coadds and writes to disk.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea, but there is also a method here that writes out separate datacubes (when align=True and combine=False). I've just realised that this is confusing, given that the routine is called "coadd"... So I've renamed the routine to run(), which I think is more consistent with the rest of PypeIt anyway.

It has made me realise though that this routine is more about making datacubes, and not necessarily "coadding"... Coadding is just one possible outcome. So I wonder if this might be confusing, and we should name it something else (perhaps spec3dobj is more apt)?


# If the user is aligning or combining, the spatial scale of the output cubes needs to be consistent.
# Set the spatial and spectral scales of the output datacube
self.set_voxel_sampling()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comments above, set_voxel_sampling should return something. As a general rule, control flows like

self.do_x()
self.do_y()
self.do_z()

Are not only inscrutable, but often do not manage variable scope and are prone to things changing accidentally changing behind the scenes, like if x changes variables that are then required for y. I know that everything is an attribute of the object, but actually that should not really be the case. The fewer global variables the better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep - good catch - as mentioned above, I've moved this function to core.

Copy link
Collaborator

@jhennawi jhennawi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the major refactor @rcooke-ast. I have only a few relatively minor comments. Only one place self.compute_weights, where I think it makes more sense for a method to be in core. Many thanks!

Copy link
Collaborator Author

@rcooke-ast rcooke-ast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think that addresses all comments of @jhennawi and @kbwestfall. I'll run the dev suite again to make sure I haven't broken anything in the process!

pypeit/coadd3d.py Outdated Show resolved Hide resolved
pypeit/coadd3d.py Show resolved Hide resolved
msgs.info("Adopting a square pixel spatial scale of {0:f} arcsec".format(3600.0 * self._dspat))
# If the user has not specified the spectral sampling, then set it now to the largest value
if self._dwv is None:
self._dwv = np.max(self._specscale)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch - I agree 👍 Actually, I decided to move this entire section of code to core.

produce a spec3d file, which is a DataCube representation of a PypeIt spec2d frame for SlicerIFU data.
"""
# Initialise variables
wave_ref = None
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good - If I understand correctly, I've added this information to the docstring.


# If the user is aligning or combining, the spatial scale of the output cubes needs to be consistent.
# Set the spatial and spectral scales of the output datacube
self.set_voxel_sampling()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep - good catch - as mentioned above, I've moved this function to core.

pypeit/coadd3d.py Show resolved Hide resolved
sensfunc = self.flux_spline(senswave)

# Generate a datacube
outfile = datacube.get_output_filename("", self.cubepar['output_filename'], True, -1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea, but there is also a method here that writes out separate datacubes (when align=True and combine=False). I've just realised that this is confusing, given that the routine is called "coadd"... So I've renamed the routine to run(), which I think is more consistent with the rest of PypeIt anyway.

It has made me realise though that this routine is more about making datacubes, and not necessarily "coadding"... Coadding is just one possible outcome. So I wonder if this might be confusing, and we should name it something else (perhaps spec3dobj is more apt)?

Compute the relative weights to apply to pixels that are collected into the voxels of the output DataCubes
"""
# Calculate the relative spectral weights of all pixels
if self.numfiles == 1:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do agree, and I started to rework this, but then realised that this routine calls generate_image_subpixel, which is located at the bottom of this coadd3d.py file (outside of this class). It required a big change throughout a lot of the code (and avoid circular imports), and I've now moved considerably more code over to core (for better or worse).

pypeit/coadd3d.py Outdated Show resolved Hide resolved
Compute the relative weights to apply to pixels that are collected into the voxels of the output DataCubes
"""
# Calculate the relative spectral weights of all pixels
if self.numfiles == 1:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a painful change, but I think it's all there now.

@rcooke-ast
Copy link
Collaborator Author

Tests passing...
image

Copy link
Collaborator

@kbwestfall kbwestfall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a couple of lingering comments/responses, but I'm happy to approve now.

Copy link
Collaborator Author

@rcooke-ast rcooke-ast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again @kbwestfall - all comments addressed... I think!

msgs.info("Loading skysub frame:" + msgs.newline() + opts_skysub)
try:
spec2DObj_sky = spec2dobj.Spec2DObj.from_file(opts_skysub, self.detname)
skysub_exptime = fits.open(opts_skysub)[0].header['EXPTIME']
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is a good point - it's included in the header 🤦‍♂️ so I've done that now... thanks for spotting that! Fixed throughout.

msgs.info("Spatial shift of cube #{0:d}:" + msgs.newline() +
"RA, DEC (arcsec) = {1:+0.3f} E, {2:+0.3f} N".format(ff + 1,
self.opts['ra_offset'][ff],
self.opts['dec_offset'][ff]))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - I somehow missed this during the first pass!

This offset is usually something that the user sets at the telescope (i.e. it's usually a dither or sky offset - Like moving the telescope "5 arcsec East and 2 arcsec North", which already includes the cos(dec) factor). I think this is more practical than specifying two separate RA/Dec combinations (which is what appears to be needed by the SkyOffsetFrame).

Perhaps I need to clarify the docs on this?

"""
Utility function to provide the world coordinate system of the datacube
"""
return wcs.WCS(self.head0)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I think I see. No one will actually no about self._ivar, it will just be a convenience variable so that when the @property ivar is called, it doesn't (necessarily) compute the 1/sig**2. I think this is a reasonable compromise. I've implemented the change you have suggested, and I initialise self._ivar=None. Same for the WCS

Copy link
Collaborator

@jhennawi jhennawi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the hard work here @rcooke-ast

@rcooke-ast rcooke-ast merged commit 0c03daf into develop Oct 14, 2023
23 checks passed
@rcooke-ast rcooke-ast deleted the datacube_core branch October 14, 2023 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants