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

WIP - A higher order function for wrapping synphot functionality #207

Closed
wants to merge 1 commit into from

Conversation

tcjansen
Copy link
Contributor

The initial state of this PR is beyond a work in progress if you know what I mean 😅, but I wanted to put it out there so we can chat about it in our GSoC meeting.

So I'm envisioning some kind of wrapper for synphot in which you can initiate a class with the source spectrum of an object, give a telescope diameter, and boom, you can get attributes like SNR, countrate, exptime, etc...:

>>> from synphot.spectrum import SourceSpectrum
>>> from synphot.wrapper import Observe
>>> import astropy.units as u

>>> tele_diameter = 1 * u.m
>>> source_spec = SourceSpectrum(...)
>>> some_observation = Observe(source_spec, tele_diameter)

>>> some_observation.countrate
1e6 ct/s
>>> some_observation.snr
100
...

The observation can be customized by setting a bandpass, giving a desired SNR (to get an exposure time), providing the quantum efficiency of the CCD or an atmospheric transmission model, etc.

synphot/wrapper.py Outdated Show resolved Hide resolved
def __init__(self, source_spec, diameter, distance=0 * u.pc,
bandpass=SpectralElement(Const1D, amplitude=1),
quantum_efficiency=None, atmospheric_transmission=None,
force=None, exptime=1 * u.s):
Copy link
Contributor

Choose a reason for hiding this comment

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

Should things like exptime have a default values? I think that's a thing that will really need to be supplied in every case, right? And distance sticks out as bing an odd one too, nothing you observe is actually at 0 pc.

Copy link
Contributor Author

@tcjansen tcjansen Jul 31, 2019

Choose a reason for hiding this comment

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

Sure, I agree with exptime not being a default param.

As for distance, I set the default to 0 instead of something like False for a couple of reasons... first as a way to indicate that by default Expose assumes the user has already scaled the source spectrum for distance (i.e. source_spec should be as it is just before it hits the instrument, i.e. 0 pc), and also because while scaling the spectrum for a star is straight forward ([R / d]^2), I wasn't sure how exactly to do that for something like a galaxy so I put it off for the moment 😂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess I was envisioning that the user could give the source spectrum as it is at the object, and Expose could scale the spectrum given a distance. But on second thought, I think it would be better for all parties if we get rid of distance and just specify that Expose assumes the user has already scaled the spectrum.

synphot/wrapper.py Outdated Show resolved Hide resolved
@u.quantity_input(diameter=u.m, distance=u.pc, exptime=u.s)
def __init__(self, source_spec, diameter, distance=0 * u.pc,
bandpass=SpectralElement(Const1D, amplitude=1),
quantum_efficiency=None, atmospheric_transmission=None,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this might be controversial (👀 @eteq), but I might vote for atmospheric transmission to take a boolean input, and if True, return the skycalc transmittance curve with some default parameters; if False, don't include atmospheric transmission. I think for 90% of users that will give them the answer they want within a factor of 2 with as little data wrangling as possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

I might even vote the same thing for quantum_efficiency. Perhaps it could instead be silicon_detector=True, and if True, return the generic CCD response curve that we have on hand. If False, assume a constant throughput.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I agree with this. Though I am curious what the controversy is..!

synphot/wrapper.py Outdated Show resolved Hide resolved
raise exceptions.BandpassError("quantum_efficiency should be "
"a list with 2 x n "
"dimensions.")
try:
Copy link
Contributor

Choose a reason for hiding this comment

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

Reminder RE: slack convo to let the function fail when the user supplies the wrong inputs, rather than writing the same exception block in a bunch of places. Bumpers are nice but at some point obfuscate your code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since I've moved these bits to a function (where the exception is only stated once) it's okay to keep?


self.throughput *= self.atmospheric_transmission

@u.quantity_input(waves=u.angstrom, frac=u.Unit(''))
Copy link
Contributor

@bmorris3 bmorris3 Jul 30, 2019

Choose a reason for hiding this comment

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

I don't think a private method needs a decorator to enforce that you're using it correctly. Ideally the user will never know this function exists, so they shouldn't need forceful reminders to get their units correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yeah, I put that decorator where it is because there doesn't seem to be a better place to put it. Passing these parameters in as lists is not the default option, so I couldn't put it before __init__(), but I do still want to enforce that the user's custom models are in the correct units.

synphot/wrapper.py Outdated Show resolved Hide resolved
"list of shape(2, n) containing "
"`~astropy.units.quantity.Quantity` values")

self.throughput = self.bandpass
Copy link
Contributor

Choose a reason for hiding this comment

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

I might be missing something here, but is there a point of having a separate throughput and bandpass object carried around if one becomes the other eventually?

Copy link
Contributor Author

@tcjansen tcjansen Aug 1, 2019

Choose a reason for hiding this comment

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

This comes from me wanting the user to be able to call self.bandpass to inspect the filter, while self.throughput would allow them to see all of the throughput effects in play (bandpass, QE, atmosphere, etc).

synphot/wrapper.py Outdated Show resolved Hide resolved
synphot/wrapper.py Outdated Show resolved Hide resolved
synphot/wrapper.py Outdated Show resolved Hide resolved
@pllim
Copy link
Contributor

pllim commented Jul 31, 2019

Thanks! I don't have a chance to do a detailed review yet, but I would propose renaming wrapper.py to something less cryptic. How about groundbased or something to explain at a high level the intent of your new module?

@tcjansen
Copy link
Contributor Author

tcjansen commented Aug 1, 2019

Does expose.py work? It follows some other synphot style choices as well (e.g. synphot.observe.Observe)

@pllim pllim changed the title WIP - A higher order function for wrapping synphot functionality [skip ci] WIP - A higher order function for wrapping synphot functionality Aug 5, 2019
Copy link
Contributor

@pllim pllim left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution!

Re: Naming things -- How about groundbased.py and rename the class to Exposure?

Before I could review this, this PR needs to be cleaned up to not include changes from #204. There is no easy way since you built this branch off your other unmerged branch. One way I can think of is to:

  1. Make a backup of this branch, just in case.
  2. Copy the files you want to keep for this PR off somewhere outside the Git repo (e.g., HOME directory).
  3. git fetch upstream master
  4. git reset --hard upstream/master (this will bring your branch back in sync with master here but you will lose all your changes)
  5. Copy those files you moved away in Step 2 back into the branch. Add and commit them.
  6. git push -f origin higherfunc

Test failures caused by unused imports and variables, and using a new dependency that is not declared in setup nor test setup script. I would suggest you run the tests locally first, and fix the failures, before pushing commits to GitHub. You can run the following:

  1. flake8 synphot
  2. python setup.py test --remote-data --open-files

In regards to the new requests dependency, why do we need it here? Please see comment.

CHANGES.rst Outdated Show resolved Hide resolved
CHANGES.rst Outdated Show resolved Hide resolved
'skycalc_cli documentation.')

# Use the bit from skycalc_cli which queries from the SkyCalc Sky Model
server = 'http://etimecalret-001.eso.org'
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this not an astroquery call?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That bit is an artifact from me borrowing straight from skycalc_cli. Is there an astropy equivalent to requests?

Copy link
Contributor

Choose a reason for hiding this comment

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

There is a astropy.utils.data.download_file() function but I think @bmorris3 said that it is not quite what you need. So my other suggestion would be to move the skycalc query out to its own module (after you created this "experimental" folder and move your work there).

@tcjansen
Copy link
Contributor Author

tcjansen commented Aug 6, 2019

So I included changes from #204 because this PR requires those changes in order to run properly (to call ccd_snr for example). Is it not a problem to leave those changes out?

@tcjansen
Copy link
Contributor Author

tcjansen commented Aug 6, 2019

As for the name changes, maybe exposure.py and Exposure? groundbased.py would be misleading because it doesn't actually require atmospheric transmission.

@pllim
Copy link
Contributor

pllim commented Aug 6, 2019

@tcjansen

Thanks!

@tcjansen tcjansen force-pushed the higherfunc branch 3 times, most recently from 25b6b86 to 11fcdcc Compare August 6, 2019 20:59
@pllim
Copy link
Contributor

pllim commented Aug 13, 2019

As per the telecon today, @tcjansen will move these new functionalities to a new subpackage in astroplan instead (no need to put in experimental folder there), with synphot being an optional dependency over there. cc @bmorris3 and @eteq

Thank you for your continued patience, Tiffany!

@pllim
Copy link
Contributor

pllim commented Aug 21, 2019

Since the astroplan PR is live, I am closing this. Thanks again, Tiffany!

@pllim pllim closed this Aug 21, 2019
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.

None yet

3 participants