Conversation
This reverts commit 01eace8.
| bp_wave_native = _bandpass_native_waves(newx[in_band]) | ||
| tp_native[in_band] = bandpass._tp(bp_wave_native) / bandpass.wave_factor | ||
| nz = tp_native != 0. # Don't divide by 0. | ||
| assert np.all(newf[~nz] == 0.) |
There was a problem hiding this comment.
It's an temporary compatibility patch, but for clarity an error message can be added something like "thinned sed has non-zero values where throughput is zero"
There was a problem hiding this comment.
Comments on this file should probably be migrated to GalSim-developers/GalSim#1355.
(I no longer have this patch in the PR.) But also, I don't think it's possible for this assert to trigger. That's why it's an assert, not a normal error.
| # Note that this is thinning in native units, not nm and photons/nm. | ||
| interpolant = (self.interpolant if not isinstance(self._spec, LookupTable) | ||
| else self._spec.interpolant) | ||
| newx, newf = utilities.thin_tabulated_values( |
There was a problem hiding this comment.
it can be renamed as thinned_lambda thinned_flux but if this module is going to be removed anyway please ignore this comment
| self.wcs = wcs | ||
| self.pointing = pointing | ||
| if self.wcs is None: | ||
| logger.error("WARNING: wcs and pointing should now be given in process, not fit.") |
There was a problem hiding this comment.
If it's warning, shouldn't it be logger.warning?
If it's an error, the message should be ERROR.
self.pointing is None condition also be added?
There was a problem hiding this comment.
All such messages in Piff are done through the logger mechanism, not python's warnings module. Given that, logger.error vs logger.warning dictates what verbosity level it shows up. logger.error's are always written, which I think makes sense for deprecation warnings. The user should fix it. Warnings that are merely about something going a little weird in the processing, but might be ok are logger.warning.
|
|
||
| def set_context(self, wcs, pointing, bandpass): | ||
| super().set_context(wcs, pointing, bandpass) | ||
| if isinstance(self.components, list): |
There was a problem hiding this comment.
if self.components is not None: can be more straight forward.
There was a problem hiding this comment.
None wouldn't work. It's either a list or an int. The latter is when it is still in the process of being read in from a file, but _finish_read hasn't been called yet. The other check for whether the read is complete uses this, so I'm inclined to keep it this way for consistency.
nihardalal
left a comment
There was a problem hiding this comment.
Looks good from my end - I think I have a couple questions/comments here and there, feel free to answer as/if necessary
| return self._interpolate_samples(star, sample_profiles) | ||
|
|
||
| def _draw_model_image_from_samples(self, star, sample_profiles, convert_func=None): | ||
| def _draw_model_image_from_samples(self, star, sample_profiles, convert_func): |
There was a problem hiding this comment.
Why is convert_func a mandatory argument now?
There was a problem hiding this comment.
I generally prefer my backend functions (I.e. not ones that users would use) to not use default arguments. That way when I change anything about them, I immediately catch any uses where I forgot to update to a new signature, rather than silently using a default value that isn't appropriate. So this should not have used a default =None in the first place.
| self.bandpass = bandpass | ||
| self.flat_bandpass = make_flat(bandpass) | ||
|
|
||
| def _require_bandpass(self): |
There was a problem hiding this comment.
Maybe consider renaming this method to _get_bandpass? This is just a matter of preference, but require suggests a bool output to me, and we use this more like a get
There was a problem hiding this comment.
The only thing this does is validate that the PSF object has a bandpass set (which happens upstream of the Roman-specific processing). Returning that value is secondary. Maybe the better change is just to remove the return value.
There was a problem hiding this comment.
I think it is called in a place where it does actually return the value, so feel free to leave as is.
| # Only call set_num if they are actually built. | ||
| if isinstance(self.model, Model): | ||
| self.model.set_num(num) | ||
| if isinstance(self.interp, Interp): |
There was a problem hiding this comment.
Isn't the interp instance also potentially not real during the read?
There was a problem hiding this comment.
Yes, but they are real or not real in tandem. So I simplified this to a single check. If Model is real, so is interp, and they can both do their set_num now. Otherwise they are both fake and will be dealt with by the read function.
| g = np.linalg.lstsq(Aw, fw, rcond=None)[0] | ||
|
|
||
| # g is now the vector that gives the best least squares approximation f'. | ||
| return g |
| resid[selected] = -1.0 # Don't reselect something we already have. | ||
| bisect.insort(selected, np.argmax(resid)) | ||
|
|
||
| return candidate_wave[selected] |
There was a problem hiding this comment.
Also looks good to me!
| wave = np.array(sed.wave_list, dtype=float) | ||
| if len(wave) == 0: | ||
| # If the sed doesn't have a wave_list, we can't thin it. (This is very rare!) | ||
| sed = sed.withFlux(1.0, flat_bandpass) |
There was a problem hiding this comment.
I think this could potentially be a failure mode that merits some extra documentation? I can imagine us accidentally having SEDs saved in an incorrect format and then this being some weird behavior that shows up (but I might be overthinking)
There was a problem hiding this comment.
It's pretty rare, but I think it's not something that would merit an error. It requires both the SED and the bandpass to be lambda expressions, rather than lookup tables read from a file. This is technically allowed, although it would require a user doing some very specific special case work. There is a test that covers this branch (https://github.com/rmjarvis/Piff/pull/198/changes#diff-4650d01edc49b02fbf29d980f24442b85e599827c64340d1ec43820d757e511eR2246), but I'm not really worried about this happening by accident in realistic scenarios.
There was a problem hiding this comment.
Tests look good to me here!
| assert 0 < np.abs(star.fit.center[0]) < 0.05 # centroid shift should be small. | ||
| assert 0 < np.abs(star.fit.center[1]) < 0.05 | ||
|
|
||
|
|
There was a problem hiding this comment.
Tests looks good and comprehensive
This PR is focused on the implementation of the chromatic=True option for RomanOptics. It includes the following pieces:
The config file in devel/roman using chromatic=True is about 5-6 times slower than the achromatic version. This is with just a linear approximation to the SED (sed_max_samples=2) -- it's almost double that with 3 samples. This seems reasonable enough given the extra work intrinsic to drawing chromatically, but there might be further optimizations available to try. E.g. I haven't tried using photon shooting (for either one), which might be worth trying despite the fact that it results in noisy images (which makes the empirical derivatives tricky, but there might be ways around that). I also don't have a good feel for how much the different SED approximations matter in practice, since we haven't tested yet with realistic SEDs.
I'll also reiterate that even with chromatic=False, the final PSF model is chromatic. Just the fitting is done achromatically with the PSF being evaluated at just a single wavelength (the bandpass effective wavelength). I'm open to adjustments to the naming of this parameter, since it's potentially confusing as it currently stands.