Skip to content

Cleaning the turnover mess - #737

Open
jordanflitter wants to merge 31 commits into
release-v4.3from
mturn_mess
Open

Cleaning the turnover mess#737
jordanflitter wants to merge 31 commits into
release-v4.3from
mturn_mess

Conversation

@jordanflitter

Copy link
Copy Markdown
Contributor

Fix #729 and #732.

This PR changes the following:

  • Replaced M_TURN with M_TURN_STELLAR_FEEDBACK and made M_TURN as a deprecated parameter with the appropriate deprecation warning.
  • Introduced a new enum called REIONIZATION_FEEDBACK_MODEL with the options (1) "NONE", (2) "ACG", (3) "MCG" and (4) "BOTH". This new enum helps in breaking the coupling between USE_MINI_HALOS and the application of the reionization feedback on the ACG and MCG turnover masses.
  • Renamed lyman_werner_threshold to molecular_cooling_threshold_with_feedbacks and mturn_a_nofb to mturn_acg_homogeneous.
  • Main change: Changed the logic for the ACG turnover mass, it is now taken from the maximum between (1) M_TURN_STELLAR_FEEDBACK, (2) output of atomic_cooling_threshold and (3) reionization_feedback (when applicable). This is now the logic in all source models, except CONST-ION-EFF, and regardless the value of USE_MINI_HALOS. An exception still occurs for E-INTEGRAL in SpinTemperatureBox.c, where the reionization feedback is not applied due to Reionization feedback is ignored in SpinTemperatureBox.c when SOURCE_MODEL being "E-INTEGRAL" or "CONST-ION-EFF" #470.
  • Main change: Changed the logic for the ACG turnover mass, it is now taken from the maximum between (1) M_TURN_STELLAR_FEEDBACK, (2) output of molecular_cooling_threshold_with_feedbacks and (3) reionization_feedback (when applicable). This is now the logic in all source models, except CONST-ION-EFF, and regardless the value of USE_MINI_HALOS. An exception still occurs for E-INTEGRAL in SpinTemperatureBox.c, where the reionization feedback is not applied due to Reionization feedback is ignored in SpinTemperatureBox.c when SOURCE_MODEL being "E-INTEGRAL" or "CONST-ION-EFF" #470.
  • If reionization feedback is not applied on ACGs (regardless the value of USE_MINI_HALOS), their turnover mass is homogeneous and is taken from mturn_acg_homogeneous, which is now always the maximum between (1) M_TURN_STELLAR_FEEDBACK, and (2) output of atomic_cooling_threshold. The MCG turnover mass is always inhomogeneous.
  • Main change: The MCG upper turnover mass is now always the ACG lower turnover mass.
  • Main change: if the MCG turnover mass is greater than the ACG turnover mass, the MCG contribution is zero.
  • Refactored the calculations of the inhomogeneous turnover masses in the code such that they will be obtained from a single place - compute_mturns_inhomogeneous in thermochem.c (exception still occurs for E-INTEGRAL in SpinTemperatureBox.c due to Reionization feedback is ignored in SpinTemperatureBox.c when SOURCE_MODEL being "E-INTEGRAL" or "CONST-ION-EFF" #470).
  • Also modified ComuteLF to reflect the above changes in the turnover mass logic.
  • Standardized the signatures and calls to EvaluateSomething (conditional/unconditional SFRD/Nion) to follow the above logic as well as the initialization of the interpolation tables.
  • In cfuncs.py, the following functions were added: get_atomic_cooling_mass_threshold, get_molecular_cooling_threshold_with_feedbacks and get_reionization_feedback. These are helpful functions for testing that the way the turnover masses are computed in the C code follow the exact same logic that is described above (in test_cfuncs.py::test_compute_mturns_model, this test would fail if we ever change the turnover masses model in the C code).
  • Also refactored similar code lines from various functions in cfuncs.py into a new function called get_log10mturns_helper.
  • Fixed some tests in test_c_interpolation_tables.c and added a few more, due to the introduction of REIONIZATION_FEEDBACK_MODEL in the code.
  • Add a comprehensive review that documents how the turnover masses were computed in previous and current versions of 21cmFAST. This review is found in a new markdown file called M_TURN.rst and is referred in the docs from updates_from_v3.rst. The latter markdown file was also modified to include other changes that were made between v3 and v4, and is now also mentioned in misc.rst under a new section called "I get different results compared to older versions of 21cmFAST, why?".

Note that all the integration tests keep passing without changing the referenced data files. This is because in our tests:

  1. We use the default M_TURN_STELLAR_FEEDBACK=8.7 when USE_MINI_HALOS=False. This is a very high value, much higher than the atomic cooling threshold.
  2. We were careful in setting M_TURN_STELLAR_FEEDBACK=5 when USE_MINI_HALOS=True, so the molecular cooling threshold with feedbacks is always more dominant in our tests in this configuration.
  3. We always finish the tests at z=18, which is a redshift too high for the reionization feedback to become dominant.
    So despite all the above changes in the logics, our tests keep using effectively the same turnover mass as before.

…iables when USE_MINI_HALOS=False. In addition, changed a bit the logic of the MCG lower turnover mass in IonisationBox.c for SOUCE_MODEL=E-INTEGRAL (by removing the dependency on mturn_m_nofb), to match it better with the logic used for other source model. All tests pass at this stage
…URN_STELLAR_FEEDBACK and the output of atomic_cooling_threshold, even when USE_MINI_HALOS=False. Almost all tests passed since we normally use the default value for M_TURN_STELLAR_FEEDBACK, 8.7, which is greater than the typical atomic cooling threshold. One single test didn’t pass and had to be modified, by introducing a new function called get_atomic_cooling_mass_threshold in cfuncs.py. Now all the tests pass
…replace acg_thresh. As a consequence, all calls to Nion_General_MINI were had to be updated by passing the ‘correct' ACG turnover mass, including EvaluateNionTs_MINI and EvaluateSFRD_MINI, as well as all functions that calls these functions (this includes some functions in heating_helper_progs.c, since the tau_X integrand calls EvaluateNionTs_MINI). As a consequence, also the pythonic functions evaluate_SFRD_z and evaluate_Nion_z had to be modified in order to pass the ‘correct’ ACG turnover mass. In the latter function, a new boolean flag called avoid_recursion was added in order to avoid infinite recursion (this new flag will be removed when USE_REIONIZATION_FEEDBACK is added). Finally, tests in test_c_interpolation_tables.py were updated to be able to pass at z < 10. After all these changes all non-integration tests now pass, the integration tests will be examined when acg_thresh will be removed from codespace
…alM. As a consequence, all calls to these functions had to be updated by passing the ‘correct’ ACG turnover mass, including EvaluateXray_Conditional, as well as all the functions that call EvaluateXray_Conditional. As a result, also the pythonic function evaluate_Xray_cond had to be modified in order to pass the ‘correct’ ACG turnover mass. Also changed the condition to zero the MCG contribution to include the scenario mtrun_MCG = mturn_ACG. After all these changes all non-integration tests now pass, the integration tests will be examined when acg_thresh will be removed from codespace
…t to replace acg_thresh. As a consequence, all calls to Nion_ConditionalM_MINI were had to be updated by passing the ‘correct' ACG turnover mass, including EvaluateNion_Conditional_MINI and EvaluateSFRD_Conditional_MINI, as well as all functions that call these functions. As a result, also the pythonic functions evaluate_SFRD_cond and evaluate_Nion_cond had to be modified in order to pass the ‘correct’ ACG turnover mass. After all these changes all tests now pass, including the integration tests: the integration tests keep passing (without changing any file in the database) because were careful in setting M_TURN_STELLAR_FEEDBACK=5 when USE_MINI_HALOS=True, and also since we do our tests at z=18, which is too high for the reionization feedback to become dominant
…the integration tests: the integration tests keep passing (without changing any file in the database) because we were careful in setting M_TURN_STELLAR_FEEDBACK=5 when USE_MINI_HALOS=True, and also since we do our tests at z=18, which is too high for the reionization feedback to become dominant
…te_mcrit_boxes in IonisationBox.c similar as much as possible to compute_mturns in thermochem.c. Also made some semantic changes to make it clearer that variables represent the log10 of Mturn, and not Mturn itself. In addition, the logic for determining the turnover masses in IonisationBox.c is exactly the same as in HaloBox.c (while SpinTemeratureBox.c lacks the reionization feedback). After this change, all test continue to pass, including integration tests, because we were careful in setting M_TURN_STELLAR_FEEDBACK=5 when USE_MINI_HALOS=True
…an accept either ‘NONE’, ‘ACG’, ‘MCG’ or ‘BOTH’. We now use this enum to control whether the reionization feedback is applied either on the ACG or MCG turnover masses (except in SpinTemperatureBox.c where the reionization feedback cannot be evaluated because the previous IonizedBox is not passed into that module). This enum helps to replace the previous logic that the reionization feedback is ALWAYS applied on BOTH the ACG and MCG turnover masses when USE_MINI_HALOS=True, while it is NEVER applied when USE_MINI_HALOS=False. The default of REIONIZATION_FEEDBACK_MODEL is designed to mimic the previous logic, based on the value of USE_MINI_HALOS. The logic in the C code was designed that the inhomogeneous ACG turnover mass is evaluated only when REIONIZATION_FEEDBACK_MODEL is either ‘ACG’ or ‘BOTH’, while the for other configurations the ACG turnover mass is assumed to be homogeneous and possibly depend only on redshift. Unfortunately, due to an unrelated bug, the inhomogeneous ACG turnover mass needs to be evaluated also when USE_MINI_HALOS=True and we use hmf interpolation tables (this was reported as issue 732 on github). All tests pass, including integration tests, since we run these tests with the same configuration and logic as before. One test in test_halo_sampler.py passes only when the reionization feedback is explicitly set on either ‘ACG’ or ‘BOTH’, namely the ACG turnover mass must be inhomogeneous in order for the test to work. I don’t know why, but I am not sure it’s due to this commit
…funcs.py into a new function called get_log10mturns_helper (2) removal of avoid_recursion flag in evaluate_Nion_z, (3) renaming lyman_werner_threshold to molecular_cooling_threshold_with_feedbacks and introducing a new pythonic function with a similar name, (4) renaming variables in IonisationBox.c, e.g. log10_Mturnover to log10_mturn_a and log10_Mturnover_MINI to log10_mturn_m. In addition, in EvaluateNion_Conditional we always use the input log10Mturn if we don’t use interpolation tables
…ired changing the signature of the function. Also changed the array inputs from float to double (no reason to have low-precision in this function, the arrays it contains are not huge. Finally, also renamed some variables in compute_luminosity_function to highlight that they correspond to ACGs or MCGs
…on tables for ACGs if the reionization feedback is applied on their turnover mass. This replaces the older logic that the ACG interpolation table was always (never) 2D when USE_MINI_HALOS = True (False). Also renamed several variables to make it clear that they refer to ACGs or MCGs. Finally, added new scenarios to test in test_Nion_conditional_tables by introducing reionization_feedback_model as a new parameter
…erate log10_mturn_a_grid if USE_MINI_HALOS=True and we use hmf interpolation tables, this is only required now if we apply the reionization feedback on the ACG turnover mass. (2) Prepared to pass an inhomogeneous ACG turnover mass in SpinTemperatureBox.c by changing the signature of functions and passing mturn_a_nofb (we cannot pass a fluctuating ACG turnover mass because we cannot use the reionization feedback in that module due issue #470, this is the reason why in EvaluateNionTs we can only use 1D interpolation table at the moment). All tests pass
…itialise_Nion_Conditional_spline and EvaluateSFRD_Conditional with EvaluateNion_Conditional. Most crucially, EvaluateSFRD_Conditional receives now log10Mturn_a as an input and can perform 2D interpolation if we apply the reionization feedback on the ACG turnover mass. Also added a few more tests. All tests pass
…on_Ts_spline and EvaluateSFRD with EvaluateNionTs. Most crucially, EvaluateSFRD receives now log10Mturn_ACG_ave as an input, though we still always pass there mturn_a_no_fb due to issue 470
… apply the reionization feedback on the ACG turnover mass, even when USE_MINI_HALOS=False. Also added more tests. All tests pass
We use now a new function called compute_mturns_inhomogeneous where our inhomogeneous turnover mass model is defined for both ACGs and MCGs, and it should be defined only in that function for consistency. I also changed the types of some variables from double to float, though I’m not sure it’s really necessary for all of them. In addition, I renamed mturn_a_nofb to mturn_acg_homogeneous for clarity. All tests pass
@jordanflitter jordanflitter self-assigned this Jul 2, 2026
@jordanflitter jordanflitter added type: bug Something isn't working context: python wrapper Changes predominantly concerning the python wrapper context: C backend Changes occur predominantly in the C code type: maint: documentation Improvements or additions to documentation type: maint: refactoring Refactoring type: maint: removal Removals and Deprecations type: maint: style Style labels Jul 2, 2026
@jordanflitter jordanflitter added the type: feature: ui New feature that adds functionality for the user label Jul 2, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @jordanflitter, your pull request is larger than the review limit of 150000 diff characters

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.17476% with 6 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (release-v4.3@a1b0e69). Learn more about missing BASE report.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/py21cmfast/wrapper/cfuncs.py 94.28% 2 Missing and 2 partials ⚠️
src/py21cmfast/wrapper/inputs.py 92.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##             release-v4.3     #737   +/-   ##
===============================================
  Coverage                ?   91.46%           
===============================================
  Files                   ?       33           
  Lines                   ?     5214           
  Branches                ?      911           
===============================================
  Hits                    ?     4769           
  Misses                  ?      285           
  Partials                ?      160           

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

@steven-murray steven-murray left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is really great, thank you so much @jordanflitter.

I really like the new document outlining the whole M_TURN evolution over releases, well done!

One thing I would say is that we should make it clear that f_duty should be read as the probability of a halo of mass m_h hosting a galaxy (of either ACG or MCG type). The explicit reason that the MCG's turnover for m>M_TURN_ACG is because it is more probable for them to form ACGs than MCGs. We should also make sure that f_ACG+f_MCG is never greater than unity...

Comment thread docs/faqs/misc.rst Outdated
Comment thread docs/updates_from_v3.rst Outdated
Comment thread docs/updates_from_v3.rst Outdated
Comment thread docs/updates_from_v3.rst Outdated
Comment thread docs/updates_from_v3.rst Outdated
Comment thread src/py21cmfast/src/HaloBox.c Outdated
Comment thread src/py21cmfast/src/interp_tables.c Outdated
if (astro_options_global->USE_MINI_HALOS) {
for (j = 0; j < NMTURN; j++) {
// NOTE: we use below homogeneous (feedback-free) ACG turnover mass, because if
// the reionization reionization feedback dominates, then the the turnover

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// the reionization reionization feedback dominates, then the the turnover
// the reionization feedback dominates, then the turnover

// We Force QAG due to the changing limits and messy implementation which I will fix
// later (hopefully move the whole thing to python)
if (source_model_is_mass_dependent(matter_options_global->SOURCE_MODEL)) {
// TODO: the passed ACG turnover mass that we give below has to be updated to

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Flagging this as a thign we ought to actually do

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We agreed to keep that comment there, because the photon non-conservation correction will be completely rewritten in the future (#364)

Comment thread src/py21cmfast/wrapper/cfuncs.py Outdated
Comment thread src/py21cmfast/wrapper/cfuncs.py Outdated
@steven-murray

Copy link
Copy Markdown
Member

Quick update:

It is true that the probability of hosting a galaxy (whether MCG or ACG) is never greater than unity in this model.

Furthermore, the conditional probability of hosting an MCG, given that a galaxy is hosted at all, in this model is

p(mcg|g) = exp((Macg-Mmcg)/m - m/Macg)

When Mmcg=Macg, this has the form p(mcg|g) = e^(-m/Macg) which I think is very reasonable (i.e. the probability of the galaxy being an MCG above the ACG turnover mass falls exponentially with increasing mass).

So I think this model makes sense. The only thing that probably doesn't really make sense is to force there to be no MCG's when Mmcg>Macg. Without forcing it, the number of MCG's will naturally die off exponentially as Mmcg grows above Macg anyway, but will do so smoothly.

@jordanflitter

Copy link
Copy Markdown
Contributor Author

Thanks @steven-murray for your comments, I made some changes according to them.

From what I was able to tell, I think you are satisfied with how we model the duty fraction for both ACGs and MCGs in the code, but you'd like to remove the constraint that M_ACG > M_MCG in order to make f_duty^MCG non-zero. I think I'm fine with that, but I do have to raise a concern that we already discussed. There are some functions (e.g. EvaluateSFRD_Conditional_MINI) that work with 2D interpolation tables since they evaluate quantities that depend on two local variables, delta and log10Mturn_mcg. For that reason, when we build the interpolation table, we assume that the ACG turnover mass is homogeneous and depends only on redshift (we also have now comments about this, e.g. in initialise_SFRD_Conditional_table). If we remove the M_ACG > M_MCG constraint, then it means that during reionization, the reionization feedback will be ignored for ACGs if we call EvaluateSFRD_Conditional_MINI and use interpolation tables! I think that the only way to overcome this is either to use a 3D interpolation table, or use an approximated analytic solution (which I'm not it exists), these are two solutions that are not ideal.

Plus, before I make any changes in the code's logic (and update the docs), we should see what @andreimesinger thinks, just to be sure that we are all on the same page.

Commented about the approximation we make when we model the MCG duty fraction with a sharp cutoff at M_MCG > M_ACG

@steven-murray steven-murray left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

woohoo, I like it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

context: C backend Changes occur predominantly in the C code context: python wrapper Changes predominantly concerning the python wrapper type: bug Something isn't working type: feature: ui New feature that adds functionality for the user type: maint: documentation Improvements or additions to documentation type: maint: refactoring Refactoring type: maint: removal Removals and Deprecations type: maint: style Style

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants