Repository navigation
Conversation
SelectionFunction.S_i / first_moment_i integrated lambda_tr with ONE 64-node GL rule over the whole bracket. The bin kernel steps at every richness edge over the kernel width (3 richness units for the example kernel) while a 1e15 bracket spans ~900, so S_i(M) of [60,200) rippled by up to 1.3% of its peak (period ~0.3 in ln M) and ClusterCounts could not converge past ~1e-3. HodMor's density also jumps from zero at lambda_cen = 1, which a single rule integrates only to O(1/n). The bracket (unchanged) is now cut into panels, n_quad nodes each: lambda_tr_e +- 5 w_e around every edge's step (new richness_edge_steps: centre (lambda_e - dmu)/b_mu, width sigma/b_mu), the MOR mean, and HodMor.support_min (new, optional MOR hook). Empty panels carry zero weight and the integrand is evaluated only on live nodes (also fixes a ValueError for HodMor + Y3 below M_min). Measured against adaptive quadrature (2 MORs x fixed/Y3 kernel x 3 binnings x z=0.2/0.4/0.6, 201 masses 1e13-10^15.5): default 20 nodes per panel is within 3e-9 of every bin's peak (old rule: 1.3e-2), ~145 live nodes per cell (~2.3x the old cost). With the GL ClusterCounts patch, the module example's convergence() goes 9.6e-4 -> 1.3e-6. Not fixed here: richness_bin_first_moment's own fixed 32-node lambda_ob rule is unconverged across bins much wider than the kernel (2.2% in [60,200) at sigma=3), which bounds first_moment_i there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
astral-sh/setup-uv@v3 has no python-version input (it only warned), so `uv sync` used whatever Python the runner had: 3.12 on ubuntu for every matrix entry, and on the current macos image Homebrew 3.15, where scipy 1.17.1 has no wheel and the source build fails for lack of a Fortran compiler. Set UV_PYTHON from the matrix at job level and drop the ignored input. Checked locally: UV_PYTHON=3.13 and 3.11 `uv sync --locked --extra dev` pick CPython 3.13.13 / 3.11.15, and the full suite with coverage passes on both (905 passed, 22 skipped, 33 xfailed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
SelectionFunction.S_iandfirst_moment_iintegrated λ_tr with one 64-node Gauss–Legendre rule over the whole bracket[max(0, μ−8σ), μ+8σ]. The bin kernel steps at every richness edge, centred where μ(λ_tr) = λ_e, over the kernel width (3 richness units forEmgParams(-1.5, 3.0, 0.3, 0.12)). Meanwhile the bracket of a 10¹⁵ h⁻¹M☉ halo spans ~900. So the rule's error oscillated as the nodes slid past the steps:LogNormalMor+ that kernel, z = 0.4:S_i(M)of[60, 200)was off by up to 1.3e-2 of its peak (7.4e-4 at 128 nodes, 6.7e-7 at 256), a ripple of period ~0.3 in ln M. No ln M rule can average it out: it keptClusterCountsat ~1e-3.HodMor's density jumps from zero at λ_cen = 1. One rule across a jump converges only as O(1/n): 5e-4 of the[20, 30)peak at 64 nodes, still 1.3e-4 at 256, with the fixed kernel.Change
The
bracketlogic is unchanged. The bracket is now cut into panels, each withn_quadGL nodes (new default 20 per panel), at:richness_edge_stepsgives centre(λ_e − Δμ)/b_μand widthσ/b_μ, exact for the Y3 fit's b_μ ≠ 1. Each step sits mid-panel and hands its neighbours only a Φ(−5) remnant. Breaking at the step centres alone was measured and rejected: it leaves a half-step at the end of a panel hundreds of units long (2e-5 at 32 nodes per panel).HodMor.support_min, a new optional MOR hook (λ_cen).LogNormalMorneeds none: its density vanishes continuously at zero.Breaks outside the bracket leave empty panels with zero weight, and the integrand is evaluated only on live nodes. That keeps the kernel off λ_tr = 0, where the Y3 σ ∝ λ^{a_σ} vanishes, and fixes a
ValueErrorforHodMor+ Y3 below M_min, where the bracket is the single point zero. New publicpanel_edgesshows the layout.Measured
Against adaptive quadrature (
quad_vecwith breakpoints): 2 relations × {fixed, Y3} kernel × 3 binnings ([20,30,45,60,200), [20,30,45,1000), [19.5,20.5)) × z ∈ {0.2, 0.4, 0.6}, 201 masses in [10¹³, 10^15.5] h⁻¹M☉.The old single 64-node rule had up to 1.3e-2 / bin peak on the same sweep, and up to 55% relative error in the small-S_i tails.
ClusterCountspatch applied,convergence()onnumber_counts.py's example goes from 9.6e-4 to 1.3e-6 at the default 96-node ln M rule. It is unchanged at 40 nodes per panel, so the residual belongs to the counts rule.S_igoes 1.8 → 5.8 ms andfirst_moment_i27 → 65–82 ms. The time is spent in the kernel itself, not in the panel bookkeeping.Tests
13 new tests in
tests/test_selection.py:HodMor.buzzard()+ Y3;first_moment_iconvergence;richness_edge_steps;n_quadvalidation.7 of the accuracy tests fail on the old rule. Full suite: 905 passed, 22 skipped, 33 xfailed (also passes with the GL-counts patch applied: 910 passed).
Not in this PR
richness_bin_first_moment's own fixed 32-node λ_ob rule is unconverged across bins much wider than the kernel: 2.2% in[60, 200)at σ = 3, worse in[45, 1000); Y3 is fine in the DES bins. That boundsfirst_moment_i/mean_richnessthere whatever the λ_tr rule does.docs/selection_function.mdstill shows the old 64-node output block (S_[60,200)(10¹⁵, z=0.3) is now 0.1533, not 0.1618) and says "one Gauss–Legendre rule".n_quad: it now counts nodes per panel. Notes scripts passingn_quad=96still work but are slower than needed; dropping the argument gives the default.🤖 Generated with Claude Code