Skip to content

Add band-dependent Double-sersic/Catalog-Source color gradients - #438

Open
FerroYx wants to merge 11 commits into
LSST-strong-lensing:mainfrom
FerroYx:color-gradient
Open

Add band-dependent Double-sersic/Catalog-Source color gradients#438
FerroYx wants to merge 11 commits into
LSST-strong-lensing:mainfrom
FerroYx:color-gradient

Conversation

@FerroYx

@FerroYx FerroYx commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Intro

Based on MegaMorph we know that galaxy structural parameters and flux distribution can vary smoothly with wavelength.

And On the shear estimation bias induced by the spatial variation of
colour across galaxy profiles
displays that Spatial color gradients cause differences in the effective morphology across different bands and affect chromatic PSF and shear measurements.

So far, Slsim's color gradient is a simple model fixed for any band. And it's similar for Catalog Source (HST), which only offer single band images.

To achieve color gradients that more closely reflect reality, I will update the Double Sersic and Catalog Source modules to provide band-dependent color gradients.

Model

For a reference-band component weight w0, the first-component weight in band
b is defined as

[
w_0(b) =
\mathrm{sigmoid}\left[
\mathrm{logit}(w_0) +
s \left(p(b) - p(b_{\rm ref})\right)
\right],
]

where p(b) is the normalized position of the band in wavelength space and
s is color_gradient["strength"]. The second-component weight is
w1(b) = 1 - w0(b).

This form was chosen as a phenomenological parameterization because it:

  • recovers the original w0 and w1 in the reference band;
  • keeps both component weights positive and normalized;
  • preserves the total flux in every simulated band;
  • provides a single signed parameter controlling whether component 0 is
    relatively redder (strength > 0) or bluer (strength < 0);
  • orders LSST, Euclid, and Roman bands consistently through approximate
    central wavelengths.

This is not intended to be a physical component-SED model or a direct fit to
a particular observed colour-gradient relation. It provides a controlled
first-order model for multi-band simulation and sensitivity tests.

@FerroYx

FerroYx commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Still in progress; currently revising the HST section and adding a fallback option to Double Service in case there is no match in the HST source.

@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.64%. Comparing base (043276a) to head (4b9fbbf).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #438      +/-   ##
==========================================
+ Coverage   98.61%   98.64%   +0.03%     
==========================================
  Files         114      115       +1     
  Lines        8819     9018     +199     
==========================================
+ Hits         8697     8896     +199     
  Misses        122      122              
Files with missing lines Coverage Δ
...eflectors/DeflectorPopulation/galaxy_deflectors.py 100.00% <100.00%> (ø)
slsim/Deflectors/deflector_util.py 100.00% <100.00%> (ø)
slsim/ImageSimulation/image_quality_lenstronomy.py 100.00% <100.00%> (ø)
slsim/Sources/SourcePopulation/galaxies.py 99.60% <100.00%> (+0.06%) ⬆️
slsim/Sources/SourceTypes/catalog_source.py 100.00% <100.00%> (ø)
slsim/Sources/SourceTypes/double_sersic.py 100.00% <100.00%> (ø)
slsim/Util/color_gradient.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@FerroYx
FerroYx force-pushed the color-gradient branch 2 times, most recently from 6e065ba to 4f4eca3 Compare July 2, 2026 15:46
@sibirrer

sibirrer commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@FerroYx I am in the process of refactoring the Deflector() class to have access to all the Source() class features. So might not all be needed here for the deflectors.
It's just a bit of work and I hope I have it done by tomorrow

@FerroYx

FerroYx commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@sibirrer No problem, I'll wait till you update that part and make necessary modification.

@FerroYx I am in the process of refactoring the Deflector() class to have access to all the Source() class features. So might not all be needed here for the deflectors. It's just a bit of work and I hope I have it done by tomorrow

@sibirrer

Copy link
Copy Markdown
Contributor

@FerroYx we have merged a major PR for deflector re-factoring. So now, you only need the Source() classes to be updated to have this enabled. Let me know in case there are any issues with the merge conflict

@sibirrer sibirrer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you very much! I give here a first batch of comments

if "e1" in kwargs_source and "e2" in kwargs_source:
e1_light, e2_light = kwargs_source["e1"], kwargs_source["e2"]
else:
e1_light, e2_light = (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is a bit tricky. I would have liked the total effective ellipticity to be used to connect mass and light, and not just one component (which might not be the dominating one)

bands = _OBSERVATORY_REGISTRY[obs_name]["bands"]
if len(bands) == 1:
return 0.0
return float(bands.index(band)) / float(len(bands) - 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't know what is happening here. I would have assumed that you use the weighted wavelength dependent transmission

LSST_BAND_LIST = ["u", "g", "r", "i", "z", "y"]
EUCLID_BAND_LIST = ["VIS", "Y", "J", "H"]

_DEFAULT_BAND_CENTRAL_WAVELENGTH_MICRON = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is a bit dangerous as the naming convention for the bands are not handled here.
There should be a more central part where the band configurations are handled by. Tagging here @timedilatesme that might also give you a direction



def get_band_normalized_position(band, reference_band=None):
"""Return a normalized wavelength position for a registered band.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't know what "position" here means.
I don't know what this definition should achieve

raise ValueError(
"semi-major and semi-minor axis are missing for the first light"
" profile in galaxy_list columns %s" % colnames
"semi-major and semi-minor axis or angular_size are missing "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is a less specific error message. Make it clear what the issue is, not and/or

if catalog_type != "HST_COSMOS":
raise ValueError(
"band_dependent_color_gradient is currently supported only "
"for HST_COSMOS."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

make sure this is for catalog_type "HST_COSMOS". You can also print the actual catalog type used for better readability

)
if not isinstance(self._color_gradient, dict):
raise ValueError(
"color_gradient must be a dictionary when "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

add in error message what color_gradient actually is at input

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