Skip to content

Implementation of corrections for projection effects [version:1.0.11]#88

Open
eduardojsbarroso wants to merge 19 commits into
mainfrom
projection_pr
Open

Implementation of corrections for projection effects [version:1.0.11]#88
eduardojsbarroso wants to merge 19 commits into
mainfrom
projection_pr

Conversation

@eduardojsbarroso

Copy link
Copy Markdown
Collaborator

This PR adds support for projection effects in CROW, including a Costanzi-style projected richness model and an optional projection-induced lensing profile correction for stacked DeltaSigma predictions.

Main Changes:

  • Added a new crow.cluster_modules.projection_effects module.
  • Implemented CostanziBaseModel, CostanziBinned, and CostanziUnBinned for projected observed-richness probabilities.
  • Added CostanziLensingBias / bsel for multiplicative selection-bias corrections to stacked DeltaSigma profiles.
  • Extended binned recipes to optionally apply lensing profile corrections after stack integration.
  • Added projection-effect grid construction and caching to GridBinnedClusterRecipe.
  • Added a projection tutorial notebook: notebooks/run_projection.ipynb.
  • Added and expanded tests for projection probabilities, projected counts, grid caching, and DeltaSigma correction behavior.

Implementation Notes:

  • The richness projection model follows the Costanzi projection-effect formalism.
  • The lensing profile correction is explicitly restricted to DeltaSigma predictions and raises an error for reduced shear.
  • Radius conversion for the lensing correction is explicit: either comoving Mpc/h input or physical Mpc converted using reference redshift and h.

yuriuno and others added 13 commits May 22, 2026 19:15
Add Costanzi19 projection model and binned_grid skeleton
…py. Add function comments. Clean the code by isort and black
Implement Costanzi binned and unbinned projection-effect probability calculations for Gaussian richness-mass relations. Add recipe support for projection-effect grid construction and cache handling.

Update tests for binned probabilities, unbinned PDFs, mass-redshift meshing, and projection recipe grid construction.
@github-actions github-actions Bot changed the title Implementation of corrections for projection effects Implementation of corrections for projection effects [version:1.0.11] Jun 19, 2026
@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.53179% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.99%. Comparing base (82846b4) to head (2a3bdc0).

Files with missing lines Patch % Lines
...cluster_modules/projection_effects/lensing_bias.py 83.60% 10 Missing ⚠️
crow/recipes/binned_grid.py 87.09% 4 Missing ⚠️
crow/recipes/binned_parent.py 88.23% 2 Missing ⚠️
...row/cluster_modules/projection_effects/costanzi.py 99.31% 1 Missing ⚠️
tests/test_recipe_binned_counts.py 98.59% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #88      +/-   ##
==========================================
- Coverage   98.41%   97.99%   -0.42%     
==========================================
  Files          28       32       +4     
  Lines        1826     2341     +515     
  Branches       60       89      +29     
==========================================
+ Hits         1797     2294     +497     
- Misses         29       47      +18     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

… models

  This update responds to review feedback by removing the combined
  projection+mass-richness classes (CostanziBinned and CostanziUnBinned) from the PR.
  The projection model is now kept as a standalone richness-bias kernel, while the full integration over rich_tru, mass, redshift, purity, and observed-richness bins is left for a future recipe-level PR.

  Main changes relative to LSSTDESC/crow main:

  - Add crow.cluster_modules.projection_effects.
  - Add richness_bias.py with CostanziRichnessBias:
    - P(rich_obs | rich_tru)
    - observed-richness-bin integral at fixed rich_tru (this is useful for calculating the number counts over a observed richness bin?)
    - default Costanzi projection parameters (this can be removed in the future)
  - Add lensing_bias.py with CostanziLensingBias / bsel:
    - multiplicative post-stacking DeltaSigma correction
    - explicit radius handling: comoving Mpc/h or physical Mpc converted using z_ref and h
  - Wire optional lensing_profile_correction into binned recipes:
    - applied after stacked lensing integration
    - restricted to DeltaSigma
    - raises for reduced shear
  - Add projection tutorial notebook.
  - Add tests for:
    - Costanzi richness-bias probability shape, positivity, finiteness
    - observed-richness-bin integration at fixed rich_tru
    - parameter validation
    - lensing-bias radius conversion and shape validation
    - exact/grid recipe DeltaSigma correction behavior
    - reduced-shear rejection
  - Remove the earlier CostanziBinned / CostanziUnBinned classes.
  - Remove the temporary grid projection-cache integration from this PR.
arrayLike = int | float | npt.ArrayLike


def _as_1d_array(name: str, value: arrayLike) -> npt.NDArray[np.floating]:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think we don't need this function. All that it is doing is calling np.atleast_1d with dtype=float.

It would be easier if all the functions just take numpy array as inputs, like the other codes.
Check: https://github.com/LSSTDESC/crow/blob/main/crow/cluster_modules/mass_proxy/murata.py

So ideally, change every function here that takes arrayLike to take npt.NDArray[np.float64]

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.

Removed.

return array


def _selection_parameter_arrays(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If you changed all the inputs to numpy array, we dont need this function anymore as well. It is just calling the as_1d_array and checking if they have the same length, which we can do later

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.

Removed.

return parameters


def bsel(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Is there an specific reason for this function to be outside the class?

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.

Have moved this inside the module.

if np.any(Rcmv <= 0.0):
raise ValueError("Rcmv must contain only positive radii.")

A_sel, alpha_sel, beta_sel, gamma_sel, R0_sel = _selection_parameter_arrays(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Here can just be now a test to see if the parameters have the same length

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.

Done.



def bsel(
Rcmv: arrayLike,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If this function is in the class, and you will check my comment about parameters, it would only take Rcmv as input to run

) * 2.0 # (n_obs, n_tru,)
term1 = (
(1.0 - fmsk) * fprj * tau + fmsk * fprj / rich_tru
) * CostanziRichnessBias._exp_times_erfc(log_exptau, erfc_arg1)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

self._exp_times_erfc

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.

I think we can still use staticmethod for this function? Because we make the probability and its integral (over rich_obs) instance methods already?

fmsk
* fprj
/ rich_tru
* CostanziRichnessBias._exp_times_erfc(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

self._exp_times_erfc

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.

Same as above.


@staticmethod
def prob_richobs_at_richtru(
rich_obs: arrayLike,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Make the input ndarray

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.

No wrappers for arrayLike now.


@staticmethod
def Sprob_at_richtru(
rich_obs_eds: arrayLike,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

NDArray and not static method anymore

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.

Done.

rich_obs_digit = np.digitize(rich_obs_bins, bins=rich_obs_eds)

# calc
prob_obs = CostanziRichnessBias.prob_richobs_at_richtru(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

self.prob_richobs_at_richtru

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.

Done.

inonchiu and others added 5 commits June 23, 2026 17:04
…tion directly to bsel. Use default Parameters to store and unpack parametrs. Corresponding tests are changed to reflect the changes. Only modify for the lensing bias module for now.
…as instance methods. Users are required to provide projection effect parameters when creating an object. Otherwise, it will use the default costanzi parameters (which can be changed later). Use PArameters to store and unpack parameters. Tests are changed to reflect the modifications. Use the variable name fractional_sig_pure instead of sig_pure.
- CostanziRichnessBias keeps its calibration parameters inside the object via Parameters.
- Array conversion wrappers and the arrayLike alias are removed. Use direct npt.ArrayLike.
- Lensing bias uses explicit radial_units with clear behavior for Mpc/h or Mpc
- Restore all recipes.
- Tests were cleaned.
  - Removed the combined binned Costanzi implementation and kept the projection model as P(lambda_obs | lambda_true).
  - Updated projection-effect inputs to use npt.NDArray[np.float64].
  - Added RICHNESS_BIAS_DEFAULT_PARAMETERS and LENSING_BIAS_DEFAULT_PARAMETERS.
  - Updated both classes to initialize self.parameters from defaults, with optional constructor overrides.
  - Moved lensing-bias evaluation into CostanziLensingBias.bsel().
  - Replaced the radius boolean with explicit radial_units.
  - Updated projection tests; tests/test_proj.py passes.
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.

3 participants