Skip to content

Make the RomanOptics chromatic=True option more effective and more efficient.#198

Open
rmjarvis wants to merge 19 commits intomainfrom
roman4
Open

Make the RomanOptics chromatic=True option more effective and more efficient.#198
rmjarvis wants to merge 19 commits intomainfrom
roman4

Conversation

@rmjarvis
Copy link
Copy Markdown
Owner

@rmjarvis rmjarvis commented Apr 5, 2026

This PR is focused on the implementation of the chromatic=True option for RomanOptics. It includes the following pieces:

  • Added the bandpass specification in input, rather than as a detail of the RomanOptics PSF.
  • If the bandpass is a galsim.roman version, then RomanOptics class can get the filter name from that. If not (e.g. if we want to specify a different version that is more accurate perhaps), then filter_name can be specified manually for use in the getPSF call.
  • The input SED is immediately multiplied by the bandpass, and that combination is thinned directly, rather than only thinning the SED and still using the unthinned Bandpass, which was still quite slow.
  • Since we've already multiplied by the filter throughput, the draw command needs to use a flat version of the bandpass with just the end points, and throughput = 1 in between. This is called flat_bandpass in the code.
  • I added a more aggressive kind of thinning, which specifies a maximum number of samples to use in the SED. E.g. sed_max_samples=3 keeps only 3 points, so piecewise linear with 2 segments. I think given how uncertain our SED knowledge will be in practice, this will be acceptable on real data. Either sed_tol or sed_max_samples or both are available for us to try out on the data.
  • Fixed a few bugs that turned up related to using ChromaticObjects in various places.
  • Fixed an error in both RomanOpticalModel and OpticalModel. They should both have _centered=True, not False. With _centered=False, they don't re-centroid properly with uncertain star positions. This hadn't turned up, since both of them are usually used in combination with other components (e.g. in a SumPSF), which allows for recentroiding. It turned up in one of my tests here, but the error is unrelated to the chromatic option.

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.

Comment thread piff/galsim_patch.py Outdated
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.)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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"

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

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.

Comment thread piff/galsim_patch.py Outdated
# 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(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

it can be renamed as thinned_lambda thinned_flux but if this module is going to be removed anyway please ignore this comment

Comment thread piff/psf.py Outdated
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.")
Copy link
Copy Markdown
Collaborator

@HyeongHan HyeongHan Apr 5, 2026

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

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.

Comment thread piff/convolvepsf.py

def set_context(self, wcs, pointing, bandpass):
super().set_context(wcs, pointing, bandpass)
if isinstance(self.components, list):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

if self.components is not None: can be more straight forward.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

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.

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.

2 participants