@@ -138,4 +138,67 @@ def test_UVLF_binned():
138138 assert uvlf_nodust .shape == (3 ,)
139139
140140 # Without dust, we expect different values than with dust
141- assert not np .array_equal (uvlf , uvlf_nodust )
141+ assert not np .array_equal (uvlf , uvlf_nodust )
142+
143+
144+ def test_UVLF_binned_with_min_t_formation ():
145+ """Test that min_t_formation_Myr produces finite outputs and suppresses the bright end.
146+
147+ When sigmaUV is large, scatter can push small halos into unphysically bright bins.
148+ Setting min_t_formation_Myr places a physical upper limit on each halo's SFR based on
149+ its maximum stellar mass (all baryons converted to stars) and the minimum formation time.
150+ This should suppress the very bright end of the UVLF without affecting the faint end.
151+ """
152+ UserParams = zeus21 .User_Parameters ()
153+ CosmoParams_input = zeus21 .Cosmo_Parameters_Input (kmax_CLASS = 10. , zmax_CLASS = 20. )
154+ ClassyCosmo = zeus21 .runclass (CosmoParams_input )
155+ CosmoParams = zeus21 .Cosmo_Parameters (UserParams , CosmoParams_input , ClassyCosmo )
156+ HMFintclass = zeus21 .HMF_interpolator (UserParams , CosmoParams , ClassyCosmo )
157+
158+ # Use a large sigmaUV to create unphysical scatter into the bright end
159+ large_sigmaUV = 2.0
160+ min_t_Myr = 10.0
161+
162+ # AstroParams with the physicality cutoff applied
163+ AstroParams_cut = zeus21 .Astro_Parameters (
164+ UserParams , CosmoParams ,
165+ sigmaUV = large_sigmaUV ,
166+ min_t_formation_Myr = min_t_Myr
167+ )
168+
169+ # AstroParams without the cutoff (default None)
170+ AstroParams_nocut = zeus21 .Astro_Parameters (
171+ UserParams , CosmoParams ,
172+ sigmaUV = large_sigmaUV
173+ )
174+
175+ z_center = 6.0
176+ z_width = 0.5
177+ # Include a very bright bin (-25) where small-halo scatter is cut off,
178+ # a typical bin (-20), and a faint bin (-15) that should be unaffected
179+ MUV_centers = np .array ([- 25.0 , - 20.0 , - 15.0 ])
180+ MUV_widths = np .full_like (MUV_centers , 1.0 )
181+
182+ uvlf_cut = UVLF_binned (
183+ AstroParams_cut , CosmoParams , HMFintclass ,
184+ z_center , z_width , MUV_centers , MUV_widths ,
185+ DUST_FLAG = False , RETURNBIAS = False
186+ )
187+ uvlf_nocut = UVLF_binned (
188+ AstroParams_nocut , CosmoParams , HMFintclass ,
189+ z_center , z_width , MUV_centers , MUV_widths ,
190+ DUST_FLAG = False , RETURNBIAS = False
191+ )
192+
193+ # Output must be finite (no NaNs or Infs) with the cutoff applied
194+ assert np .all (np .isfinite (uvlf_cut )), "UVLF with min_t_formation_Myr cutoff contains NaN or Inf values"
195+
196+ # All values must be non-negative
197+ assert np .all (uvlf_cut >= 0.0 ), "UVLF with min_t_formation_Myr cutoff contains negative values"
198+
199+ # The cutoff should suppress the very bright end: small halos that could not
200+ # physically produce MUV=-25 galaxies (min_MUV~-18.7 for 1e8 Msun with t_min=10 Myr)
201+ # no longer contribute via scatter, so the bright-end UVLF should be lower
202+ assert uvlf_cut [0 ] < uvlf_nocut [0 ], (
203+ "min_t_formation_Myr cutoff should suppress the very bright end (MUV=-25) of the UVLF"
204+ )
0 commit comments