GalSim-developers/GalSim — pull request #561 by jmeyers314 (2014-06-09 19:46:18+00:00)
Title: #547

I think these changes are ready for review. I realize now that I probably should have split this material into a few different issues/pull-requests, but it's easier at this point to just proceed, I think.

The three goals for this PR are:

1. Ability to normalize `SED`s using magnitudes instead of just fluxes.
2. Add a couple convenience methods to `SED` to compute chromatic PSF biases.
3. Allow subclasses of `SED` and `Bandpass` to preserve their type when methods creating new SEDs or Bandpasses are called.

For the first goal, I've introduced a 'zeropoint' argument to the `Bandpass.__init__` method. This can take a few different types of values:
- An `SED`, in which case the zeropoint will be set such that the supplied `SED` has magnitude 0.0 in the `Bandpass`.
- A string, which must be one of 'AB', 'Vega', or 'ST', which sets up AB magnitudes, Vega magnitudes, or HST ACS 'ST' magnitudes, which are similar to AB mags but where the reference spectrum is flat in flambda instead of flat in fnu.
- A number, which just becomes the zeropoint.
- None, which defaults to 'AB' magnitudes above.

The zeropoints work in the usual way: an object with magnitude equal to the zeropoint will have a flux of 1.0 in the drawn image. This does make the 'AB', 'Vega', and 'ST' zeropoint definitions above somewhat tricky, though, since to figure out how many photons will be observed in an observation of Vega, one really needs to know the telescope effective aperture and exposure time in addition to the throughput function. What I've currently implemented creates an SED with the correct normalization for a 1 second exposure through a telescope with a 1 cm^2 aperture. I'm not quite sure where to put the API for exposure time and effective aperture, though. An obvious choice might be in `Bandpass.__init__`, but then you would need a different Bandpass instance for drawing the same galaxy through the same filter but with a different exposure time, which seems a little weird. Another choice may be `ChromaticObject.drawImage()`. I'm open to suggestions.

Once the zeropoints are in place, implementing magnitudes is straightforward. Just replace the `SED` methods `withFlux` with `withMagnitude`, or `calculateFlux` with `calculateMagnitude`.

For goal number 2 above, there are new methods `calculateDCRMomentShifts` and `calculateSeeingMomentShifts`, which calculate \bar{R} and V as defined in Plazas and Bernstein (2012) (http://arxiv.org/abs/1204.1346) (for dealing with differential chromatic refraction), and r^2_PSF / r^2_500 which is the difference in PSF second-moment-squared radius compared to monochromatic 500nm PSF due to chromatic seeing or a diffraction-limited PSF. You can see how these work in test_chromatic.py, where I've replaced a few sections that previously used many lines of code with these convenience functions instead. It's possible these are mostly of interest just to me, and I'm happy to leave them in my own repo and out of GalSim provided that goal number 3 is addressed.

For goal 3, I've simply replaced a few lines in sed.py and bandpass.py so that new `SED`s and `Bandpass`es, created by the `.thin()` methods, for example, preserve the type of `self`. This way, if someone subclasses `SED` and uses `SED.thin()`, he or she will get back an instance of their subclass, instead of an instance of `SED`.