diff --git a/.github/workflows/test_suite_nointegration.yaml b/.github/workflows/test_suite_nointegration.yaml new file mode 100644 index 00000000..e4dda218 --- /dev/null +++ b/.github/workflows/test_suite_nointegration.yaml @@ -0,0 +1,113 @@ +name: Tests (No Integration Tests) + +# Test on all pushes, except when the push is literally just a tag (because we +# tag automatically via CI, and therefore there's no extra code in that push). +# Also, only test on pull requests into master/production. +on: + push: + tags-ignore: + - 'v*' + pull_request: + branches: + - 'v4-prep' + + +jobs: + tests: + if: "!contains(github.event.pull_request.labels.*.name, 'auto-pr')" + env: + ENV_NAME: tests + PYTHON: ${{ matrix.python-version }} + OS: ${{ matrix.os }} + CC: gcc + name: Testing + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + python-version: [3.9, "3.10", "3.11", "3.12"] + defaults: + run: + # Adding -l {0} ensures conda can be found properly in each step + shell: bash -l {0} + steps: + - uses: actions/checkout@master + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Print head git commit message + id: get_head_commit_message + run: echo "commit_message=$(git show -s --format=%s)" >> "$GITHUB_ENV" + + - name: Setup Miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + # auto-update-conda: true + mamba-version: "*" + channels: conda-forge,defaults + python-version: ${{ matrix.python-version }} + environment-file: ci/${{ matrix.os }}-env.yml + activate-environment: tests + channel-priority: true + + - name: Conda Info + run: | + conda info -a + conda list + conda config --show-sources + conda config --show + printenv | sort + + - name: Make it a Debug Run + if: "contains(env.commit_message, 'ci debug')" + run: | + echo "log_level=ULTRA_DEBUG" >> $GITHUB_ENV + echo "extra_pytest_args=-s --log-level-21=DEBUG" >> $GITHUB_ENV + + - name: Make it a Normal Run + if: "!contains(env.commit_message, 'ci debug')" + run: | + echo "log_level=INFO" >> $GITHUB_ENV + echo "extra_pytest_args=" >> $GITHUB_ENV + + - name: Get C Libraries Linux + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install libfftw3-dev + sudo apt-get install libgsl0-dev + + - name: Setup GCC + uses: Dup4/actions-setup-gcc@v1 + with: + version: latest + + - name: Install 21cmFAST Linux + if: matrix.os == 'ubuntu-latest' + run: | + LOG_LEVEL=${{ env.log_level }} pip install . + + - name: Install 21cmFAST MacOS + if: matrix.os == 'macos-latest' + run: | + LOG_LEVEL=${{ env.log_level }} CFLAGS="-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" pip install . + + - name: Run Tests + run: | + python -m pytest -n 2 -l ${{ env.extra_pytest_args }} --ignore tests/test_integration_features.py --cov=py21cmfast --cov-config=.coveragerc -vv --cov-report xml:./coverage.xml --durations=25 --plots=testplots + + - name: Archive Integration Test Plots + if: always() + uses: actions/upload-artifact@v3 + with: + name: integration-test-plots-${{ matrix.os }}-${{ matrix.python-version }} + path: | + testplots/*.pdf + + - uses: codecov/codecov-action@v3 + if: matrix.os == 'ubuntu-latest' && success() && !contains(github.event.pull_request.labels.*.name, 'auto-pr') + with: + fail_ci_if_error: true + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/build_cffi.py b/build_cffi.py index edce958e..75ee9b8e 100755 --- a/build_cffi.py +++ b/build_cffi.py @@ -21,28 +21,11 @@ CLOC = os.path.join(LOCATION, "src", "py21cmfast", "src") include_dirs = [CLOC] -# ================================================== -# Set compilation arguments dependent on environment -# ================================================== -extra_compile_args = ["-w", "--verbose"] - -if "DEBUG" in os.environ: - extra_compile_args += ["-g", "-O0"] -else: - extra_compile_args += ["-Ofast"] - -if sys.platform == "darwin": - extra_compile_args += ["-Xpreprocessor"] - -extra_compile_args += ["-fopenmp"] - -libraries = ["m", "gsl", "gslcblas", "fftw3f_omp", "fftw3f"] - -# stuff for gperftools -if "PROFILE" in os.environ: - libraries += ["profiler", "tcmalloc"] - # we need this even if DEBUG is off - extra_compile_args += ["-g"] +c_files = [ + os.path.join("src", "py21cmfast", "src", f) + for f in os.listdir(CLOC) + if f.endswith(".c") +] # Set the C-code logging level. # If DEBUG is set, we default to the highest level, but if not, @@ -71,6 +54,33 @@ "of {}".format(available_levels) ) +# ================================================== +# Set compilation arguments dependent on environment +# ================================================== + +extra_compile_args = ["-Wall", "--verbose", f"-DLOG_LEVEL={log_level:d}"] + +if "DEBUG" in os.environ: + extra_compile_args += ["-g", "-O0"] +else: + extra_compile_args += ["-Ofast"] + +if sys.platform == "darwin": + extra_compile_args += ["-Xpreprocessor"] + +extra_compile_args += ["-fopenmp"] + +libraries = ["m", "gsl", "gslcblas", "fftw3f_omp", "fftw3f"] + +# stuff for gperftools +if "PROFILE" in os.environ: + libraries += ["profiler", "tcmalloc"] + # we need this even if DEBUG is off + extra_compile_args += ["-g"] + +if compiler == "clang": + libraries += ["omp"] + library_dirs = [] for k, v in os.environ.items(): if "inc" in k.lower(): @@ -78,35 +88,45 @@ elif "lib" in k.lower(): library_dirs += [v] -libraries = ["m", "gsl", "gslcblas", "fftw3f_omp", "fftw3f"] - -if compiler == "clang": - libraries += ["omp"] - # ================================================================= +# NOTES FOR DEVELOPERS: +# The CFFI implementation works as follows: +# - All function prototypes, global variables and type definitions *directly* used +# in the python wrapper must be declared via ffi.cdef("""C CODE"""). +# There must be no compiler directives in this code (#include, #define, etc) +# - All implementations of global variables and types present in the cdef() calls +# must also be present in the second argument of set_source. +# This is passed to the compiler. +# - The `sources` kwarg then contains all the .c files in the library which are to be compiled # This is the overall C code. ffi.set_source( "py21cmfast.c_21cmfast", # Name/Location of shared library module """ - #define LOG_LEVEL {log_level} - - #include "GenerateICs.c" - """.format( - log_level=log_level - ), + #include "21cmFAST.h" + """, + sources=c_files, include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, extra_compile_args=extra_compile_args, ) -# This is the Header file -with open(os.path.join(CLOC, "21cmFAST.h")) as f: +# Header files containing types, globals and function prototypes +with open(os.path.join(CLOC, "_inputparams_wrapper.h")) as f: ffi.cdef(f.read()) - -with open(os.path.join(CLOC, "Globals.h")) as f: +with open(os.path.join(CLOC, "_outputstructs_wrapper.h")) as f: ffi.cdef(f.read()) +with open(os.path.join(CLOC, "_functionprototypes_wrapper.h")) as f: + ffi.cdef(f.read()) + +# CFFI needs to be able to access a free function to make the __del__ method for OutputStruct fields +# This will expose the standard free() function to the wrapper so it can be used +ffi.cdef( + """ + void free(void *ptr); + """ +) if __name__ == "__main__": ffi.compile() diff --git a/src/py21cmfast/_utils.py b/src/py21cmfast/_utils.py index f207e343..80403622 100644 --- a/src/py21cmfast/_utils.py +++ b/src/py21cmfast/_utils.py @@ -289,13 +289,13 @@ def _cstruct(self): def _new(self): """Return a new empty C structure corresponding to this class.""" - return self._ffi.new("struct " + self._name + "*") + return self._ffi.new(self._name + "*") @classmethod def get_fields(cls, cstruct=None) -> Dict[str, Any]: """Obtain the C-side fields of this struct.""" if cstruct is None: - cstruct = cls._ffi.new("struct " + cls._get_name() + "*") + cstruct = cls._ffi.new(cls._get_name() + "*") return dict(cls._ffi.typeof(cstruct[0]).fields) @classmethod diff --git a/src/py21cmfast/outputs.py b/src/py21cmfast/outputs.py index 534fdddb..817ee246 100644 --- a/src/py21cmfast/outputs.py +++ b/src/py21cmfast/outputs.py @@ -328,7 +328,7 @@ def __init__( self, *, desc_redshift: float | None = None, - buffer_size: int = 0.0, + buffer_size: int = 0, **kwargs, ): self.desc_redshift = desc_redshift diff --git a/src/py21cmfast/photoncons.py b/src/py21cmfast/photoncons.py index b08680f8..09054e50 100644 --- a/src/py21cmfast/photoncons.py +++ b/src/py21cmfast/photoncons.py @@ -58,11 +58,11 @@ lib.adjust_redshifts_for_photoncons() lib.determine_deltaz_for_photoncons() (first time only) --> calculates the deltaz array with some smoothing --> does more smoothing and returns the adjusted redshift - ELIF PHOTON_CONS_TYPE==1: + ELIF PHOTON_CONS_TYPE==2: photoncons_alpha() --> computes and stores ALPHA_ESC shift vs global neutral fraction lib.ComputeIonizedBox() get_fesc_fit() --> applies the fit to the current redshift - ELIF PHOTON_CONS_TYPE==2: + ELIF PHOTON_CONS_TYPE==3: photoncons_fesc() --> computes and stores F_ESC10 shift vs global neutral fraction lib.ComputeIonizedBox() get_fesc_fit() --> applies the fit to the current redshift @@ -327,11 +327,13 @@ def calibrate_photon_cons( astro_params_photoncons = deepcopy(astro_params) astro_params_photoncons._R_BUBBLE_MAX = astro_params.R_BUBBLE_MAX - flag_options_photoncons = FlagOptions( - USE_MASS_DEPENDENT_ZETA=flag_options.USE_MASS_DEPENDENT_ZETA, - M_MIN_in_Mass=flag_options.M_MIN_in_Mass, + flag_options_photoncons = flag_options.clone( + USE_TS_FLUCT=False, + INHOMO_RECO=False, + USE_MINI_HALOS=False, + HALO_STOCHASTICITY=False, + PHOTON_CONS_TYPE=0, ) - ib = None prev_perturb = None diff --git a/src/py21cmfast/src/21cmFAST.h b/src/py21cmfast/src/21cmFAST.h index 080d7342..c3c4b573 100644 --- a/src/py21cmfast/src/21cmFAST.h +++ b/src/py21cmfast/src/21cmFAST.h @@ -1,371 +1,14 @@ /* This is the header file for the wrappable version of 21cmFAST, or 21cmMC. - It contains function signatures, struct definitions and globals to which the Python wrapper code - requires access. + It contains all the header files of the backend. This is not imported by + any module in the backend, but is provided to the cffi set_source() call, + and makes sure all headers are accessible to the compiler. */ -struct CosmoParams{ +//These are the includes which are needed for the wrapper, +// which have all the typedefs and globals the wrapper explicitly uses - float SIGMA_8; - float hlittle; - float OMm; - float OMl; - float OMb; - float POWER_INDEX; - -}; - -struct UserParams{ - // Parameters taken from INIT_PARAMS.H - int HII_DIM; - int DIM; - float BOX_LEN; - float NON_CUBIC_FACTOR; - bool USE_FFTW_WISDOM; - int HMF; - int USE_RELATIVE_VELOCITIES; - int POWER_SPECTRUM; - int N_THREADS; - bool PERTURB_ON_HIGH_RES; - bool NO_RNG; - bool USE_INTERPOLATION_TABLES; - int INTEGRATION_METHOD_ATOMIC; - int INTEGRATION_METHOD_MINI; - bool USE_2LPT; - bool MINIMIZE_MEMORY; - bool KEEP_3D_VELOCITIES; - - //Halo Sampler Options - float SAMPLER_MIN_MASS; - double SAMPLER_BUFFER_FACTOR; - float MAXHALO_FACTOR; - int N_COND_INTERP; - int N_PROB_INTERP; - double MIN_LOGPROB; - int SAMPLE_METHOD; - bool AVG_BELOW_SAMPLER; - double HALOMASS_CORRECTION; - double PARKINSON_G0; - double PARKINSON_y1; - double PARKINSON_y2; -}; - -struct AstroParams{ - // Parameters taken from INIT_PARAMS.H - float HII_EFF_FACTOR; - - //SHMR - float F_STAR10; - float ALPHA_STAR; - float ALPHA_STAR_MINI; - float SIGMA_STAR; - float CORR_STAR; - double UPPER_STELLAR_TURNOVER_MASS; - double UPPER_STELLAR_TURNOVER_INDEX; - float F_STAR7_MINI; - - //SFMS - float t_STAR; - float CORR_SFR; - double SIGMA_SFR_INDEX; - double SIGMA_SFR_LIM; - - //L_X/SFR - double L_X; - double L_X_MINI; - double SIGMA_LX; - double CORR_LX; - - //Escape Fraction - float F_ESC10; - float ALPHA_ESC; - float F_ESC7_MINI; - - float M_TURN; - float R_BUBBLE_MAX; - float ION_Tvir_MIN; - double F_H2_SHIELD; - float NU_X_THRESH; - float X_RAY_SPEC_INDEX; - float X_RAY_Tvir_MIN; - - double A_LW; - double BETA_LW; - double A_VCB; - double BETA_VCB; - - int N_RSD_STEPS; -}; - -struct FlagOptions{ - - // Parameters taken from INIT_PARAMS.H - bool USE_HALO_FIELD; - bool USE_MINI_HALOS; - bool USE_CMB_HEATING; //CMB Heating Flag - bool USE_LYA_HEATING; //Lya Heating Flag - bool USE_MASS_DEPENDENT_ZETA; - bool SUBCELL_RSD; - bool APPLY_RSDS; - bool INHOMO_RECO; - bool USE_TS_FLUCT; - bool M_MIN_in_Mass; - bool FIX_VCB_AVG; - bool HALO_STOCHASTICITY; - bool USE_EXP_FILTER; - bool FIXED_HALO_GRIDS; - bool CELL_RECOMB; - int PHOTON_CONS_TYPE; - bool USE_UPPER_STELLAR_TURNOVER; -}; - - -struct InitialConditions{ - float *lowres_density, *lowres_vx, *lowres_vy, *lowres_vz, *lowres_vx_2LPT, *lowres_vy_2LPT, *lowres_vz_2LPT; - float *hires_density, *hires_vx, *hires_vy, *hires_vz, *hires_vx_2LPT, *hires_vy_2LPT, *hires_vz_2LPT; //cw addition - float *lowres_vcb; -}; - -struct PerturbedField{ - float *density, *velocity_x, *velocity_y, *velocity_z; -}; - -struct HaloField{ - long long unsigned int n_halos; - long long unsigned int buffer_size; - float *halo_masses; - int *halo_coords; - - //Halo properties for stochastic model - float *star_rng; - float *sfr_rng; - float *xray_rng; -}; - -//gridded halo properties -struct HaloBox{ - //Things that aren't used in radiation fields but useful outputs - float *halo_mass; - float *halo_stars; - float *halo_stars_mini; - int *count; - - //For IonisationBox.c and SpinTemperatureBox.c - float *n_ion; //weighted by F_ESC*PopN_ion - float *halo_sfr; //for x-rays and Ts stuff - float *halo_xray; - float *halo_sfr_mini; //for x-rays and Ts stuff - float *whalo_sfr; //SFR weighted by PopN_ion and F_ESC, used for Gamma12 - - //Average volume-weighted log10 Turnover masses are kept in order to compare with the expected MF integrals - double log10_Mcrit_ACG_ave; - double log10_Mcrit_MCG_ave; -}; - -struct PerturbHaloField{ - long long unsigned int n_halos; - long long unsigned int buffer_size; - float *halo_masses; - int *halo_coords; - - //Halo properties for stochastic model - float *star_rng; - float *sfr_rng; - float *xray_rng; -}; - -struct TsBox{ - float *Ts_box; - float *x_e_box; - float *Tk_box; - float *J_21_LW_box; -}; - -struct XraySourceBox{ - float *filtered_sfr; - float *filtered_xray; - float *filtered_sfr_mini; - - double *mean_log10_Mcrit_LW; - double *mean_sfr; - double *mean_sfr_mini; -}; - -struct IonizedBox{ - double mean_f_coll; - double mean_f_coll_MINI; - double log10_Mturnover_ave; - double log10_Mturnover_MINI_ave; - float *xH_box; - float *Gamma12_box; - float *MFP_box; - float *z_re_box; - float *dNrec_box; - float *temp_kinetic_all_gas; - float *Fcoll; - float *Fcoll_MINI; -}; - -struct BrightnessTemp{ - float *brightness_temp; -}; - -int ComputeInitialConditions(unsigned long long random_seed, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct InitialConditions *boxes); - -int ComputePerturbField(float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct InitialConditions *boxes, struct PerturbedField *perturbed_field); - -int ComputeHaloField(float redshift_desc, float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, - struct InitialConditions *boxes, int random_seed, struct HaloField * halos_desc, struct HaloField *halos); - -int ComputePerturbHaloField(float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, - struct InitialConditions *boxes, struct HaloField *halos, struct PerturbHaloField *halos_perturbed); - -int ComputeTsBox(float redshift, float prev_redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, float perturbed_field_redshift, - short cleanup, - struct PerturbedField *perturbed_field, struct XraySourceBox * source_box, struct TsBox *previous_spin_temp, struct InitialConditions *ini_boxes, - struct TsBox *this_spin_temp); - -int ComputeIonizedBox(float redshift, float prev_redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, struct PerturbedField *perturbed_field, - struct PerturbedField *previous_perturbed_field, struct IonizedBox *previous_ionize_box, - struct TsBox *spin_temp, struct HaloBox *halos, struct InitialConditions *ini_boxes, - struct IonizedBox *box); - -int ComputeBrightnessTemp(float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, - struct TsBox *spin_temp, struct IonizedBox *ionized_box, - struct PerturbedField *perturb_field, struct BrightnessTemp *box); - -int InitialisePhotonCons(struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options); - -int PhotonCons_Calibration(double *z_estimate, double *xH_estimate, int NSpline); -int ComputeZstart_PhotonCons(double *zstart); - -//(jdavies): I need this to be accessible in python to pass the right haloboxes to IonizeBox -float adjust_redshifts_for_photoncons( - struct AstroParams *astro_params, struct FlagOptions *flag_options, float *redshift, - float *stored_redshift, float *absolute_delta_z -); - -void determine_deltaz_for_photoncons(); - -int ObtainPhotonConsData(double *z_at_Q_data, double *Q_data, int *Ndata_analytic, double *z_cal_data, double *nf_cal_data, int *Ndata_calibration, - double *PhotonCons_NFdata, double *PhotonCons_deltaz, int *Ndata_PhotonCons); - -int ComputeLF(int nbins, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, - struct FlagOptions *flag_options, int component, int NUM_OF_REDSHIFT_FOR_LF, float *z_LF, float *M_TURNs, double *M_uv_z, double *M_h_z, double *log10phi); - -float ComputeTau(struct UserParams *user_params, struct CosmoParams *cosmo_params, int Npoints, float *redshifts, float *global_xHI); - -int CreateFFTWWisdoms(struct UserParams *user_params, struct CosmoParams *cosmo_params); - -void Broadcast_struct_global_PS(struct UserParams *user_params, struct CosmoParams *cosmo_params); -void Broadcast_struct_global_UF(struct UserParams *user_params, struct CosmoParams *cosmo_params); -void Broadcast_struct_global_HF(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options); -void Broadcast_struct_global_STOC(struct UserParams *user_params, struct CosmoParams *cosmo_params,struct AstroParams *astro_params, struct FlagOptions *flag_options); -void Broadcast_struct_global_TS(struct UserParams *user_params, struct CosmoParams *cosmo_params,struct AstroParams *astro_params, struct FlagOptions *flag_options); -void Broadcast_struct_global_IT(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options); - -// void free_TsCalcBoxes(struct UserParams *user_params, struct FlagOptions *flag_options); -void FreePhotonConsMemory(); -bool photon_cons_allocated = false; -int SomethingThatCatches(bool sub_func); -int FunctionThatCatches(bool sub_func, bool pass, double* result); -void FunctionThatThrows(); -int init_heat(); -void free(void *ptr); - -//need a python visible function to test lower level functions using the package -int single_test_sample(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options, - int seed, int n_condition, float *conditions, int *cond_crd, double z_out, double z_in, int *out_n_tot, int *out_n_cell, double *out_n_exp, - double *out_m_cell, double *out_m_exp, float *out_halo_masses, int *out_halo_coords); -//test function for getting halo properties from the wrapper, can use a lot of memory for large catalogs -int test_halo_props(double redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, - struct FlagOptions * flag_options, float * vcb_grid, float *J21_LW_grid, float *z_re_grid, float *Gamma12_ion_grid, - struct PerturbHaloField *halos, float *halo_props_out); - -//This function, designed to be used in the wrapper to estimate Halo catalogue size, takes the parameters and returns average number of halos within the box -double expected_nhalo(double redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions * flag_options); - -//these two functions compute the new classes HaloBox and XraySourceBox, and need to be visible -int ComputeHaloBox(double redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params - , struct FlagOptions * flag_options, struct InitialConditions * ini_boxes, struct PerturbedField * perturbed_field, struct PerturbHaloField *halos - , struct TsBox *previous_spin_temp, struct IonizedBox *previous_ionize_box, struct HaloBox *grids); - -int UpdateXraySourceBox(struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, struct HaloBox *halobox, - double R_inner, double R_outer, int R_ct, struct XraySourceBox *source_box); - -//alpha photoncons functions -void set_alphacons_params(double norm, double slope); - -//exposing lower-level functions for testing -//Initialisation of tables -double init_ps(); -double dicke(double z); -double sigma_z0(double M); -double dsigmasqdm_z0(double M); -double get_delta_crit(int HMF, double sigma, double growthf); -void initialiseSigmaMInterpTable(float M_Min, float M_Max); -double EvaluateSigma(double lnM); -double EvaluatedSigmasqdm(double lnM); - -void initialise_SFRD_spline(int Nbin, float zmin, float zmax, float Alpha_star, float Alpha_star_mini, float Fstar10, float Fstar7_MINI, - float mturn_a_const, bool minihalos); -void initialise_Nion_Ts_spline(int Nbin, float zmin, float zmax, float Alpha_star, float Alpha_star_mini, float Alpha_esc, float Fstar10, - float Fesc10, float Fstar7_MINI, float Fesc7_MINI, float mturn_a_const, bool minihalos); -void initialise_FgtrM_delta_table(double min_dens, double max_dens, double zpp, double growth_zpp, double smin_zpp, double smax_zpp); -void init_FcollTable(double zmin, double zmax, bool x_ray); -void initialise_Nion_Conditional_spline(float z, float Mcrit_atom, float min_density, float max_density, - float Mmin, float Mmax, float Mcond, float log10Mturn_min, float log10Mturn_max, - float log10Mturn_min_MINI, float log10Mturn_max_MINI, float Alpha_star, - float Alpha_star_mini, float Alpha_esc, float Fstar10, float Fesc10, - float Mlim_Fstar, float Mlim_Fesc, float Fstar7_MINI, float Fesc7_MINI, - float Mlim_Fstar_MINI, float Mlim_Fesc_MINI, int method, int method_mini, - bool minihalos, bool prev); -void initialise_SFRD_Conditional_table(double min_density, double max_density, double growthf, - float Mcrit_atom, double Mmin, double Mmax, double Mcond, float Alpha_star, float Alpha_star_mini, - float Fstar10, float Fstar7_MINI, int method, int method_mini, bool minihalos); - -void initialise_dNdM_tables(double xmin, double xmax, double ymin, double ymax, double growth1, double param, bool from_catalog); -void initialise_dNdM_inverse_table(double xmin, double xmax, double lnM_min, double growth1, double param, bool from_catalog); - -//evaluation of tables -double EvaluateNionTs(double redshift, double Mlim_Fstar, double Mlim_Fesc); -double EvaluateNionTs_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI, double Mlim_Fesc_MINI); -double EvaluateSFRD(double redshift, double Mlim_Fstar); -double EvaluateSFRD_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI); -double EvaluateSFRD_Conditional(double delta, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar); -double EvaluateSFRD_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar); -double EvaluateNion_Conditional(double delta, double log10Mturn, double growthf, double M_min, double M_max, double M_cond, double sigma_max, - double Mlim_Fstar, double Mlim_Fesc, bool prev); -double EvaluateNion_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, - double Mturn_a, double Mlim_Fstar, double Mlim_Fesc, bool prev); -double EvaluateNhalo(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta); -double EvaluateMcoll(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta); -double EvaluateNhaloInv(double condition, double prob); -double EvaluateFcoll_delta(double delta, double growthf, double sigma_min, double sigma_max); -double EvaluatedFcolldz(double delta, double redshift, double sigma_min, double sigma_max); - -//integrals -void initialise_GL(int n, float lnM_Min, float lnM_Max); -double Nhalo_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method); -double Mcoll_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method); -double Nion_ConditionalM(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, - double Alpha_star, double Alpha_esc, double Fstar10, double Fesc10, double Mlim_Fstar, - double Mlim_Fesc, int method); -double Nion_ConditionalM_MINI(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, - double MassTurnover_upper, double Alpha_star, double Alpha_esc, double Fstar7, - double Fesc7, double Mlim_Fstar, double Mlim_Fesc, int method); -double Nion_General(double z, double lnM_Min, double lnM_Max, double MassTurnover, double Alpha_star, double Alpha_esc, double Fstar10, - double Fesc10, double Mlim_Fstar, double Mlim_Fesc); -double Nion_General_MINI(double z, double lnM_Min, double lnM_Max, double MassTurnover, double MassTurnover_upper, double Alpha_star, - double Alpha_esc, double Fstar7_MINI, double Fesc7_MINI, double Mlim_Fstar, double Mlim_Fesc); -double Fcoll_General(double z, double lnM_min, double lnM_max); -double unconditional_mf(double growthf, double lnM, double z, int HMF); -double conditional_mf(double growthf, double lnM, double delta_cond, double sigma_cond, int HMF); -double atomic_cooling_threshold(float z); +#include "InputParameters.h" +#include "OutputStructs.h" +#include "photoncons.h" +#include "_functionprototypes_wrapper.h" diff --git a/src/py21cmfast/src/BrightnessTemperatureBox.c b/src/py21cmfast/src/BrightnessTemperatureBox.c index 7eefb446..8c8fb27a 100644 --- a/src/py21cmfast/src/BrightnessTemperatureBox.c +++ b/src/py21cmfast/src/BrightnessTemperatureBox.c @@ -1,29 +1,44 @@ +/* */ +#include +#include +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "indexing.h" +#include "subcell_rsds.h" +#include "dft.h" + +#include "BrightnessTemperatureBox.h" + +float clip(float x, float min, float max){ + if(xmax) return max; + return x; +} -// Re-write of find_HII_bubbles.c for being accessible within the MCMC - -void get_velocity_gradient(struct UserParams *user_params, float *v, float *vel_gradient) +void get_velocity_gradient(UserParams *user_params, float *v, float *vel_gradient) { memcpy(vel_gradient, v, sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); dft_r2c_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, (fftwf_complex *)vel_gradient); - float k_x, k_y, k_z; + float k_z; int n_x, n_y, n_z; -#pragma omp parallel shared(vel_gradient) private(n_x,n_y,n_z,k_x,k_y,k_z) num_threads(user_params->N_THREADS) +#pragma omp parallel shared(vel_gradient) private(n_x,n_y,n_z,k_z) num_threads(user_params->N_THREADS) { #pragma omp for for (n_x=0; n_xHII_DIM; n_x++){ - if (n_x>HII_MIDDLE) - k_x =(n_x-user_params->HII_DIM) * DELTA_K; // wrap around for FFT convention - else - k_x = n_x * DELTA_K; - for (n_y=0; n_yHII_DIM; n_y++){ - if (n_y>HII_MIDDLE) - k_y =(n_y-user_params->HII_DIM) * DELTA_K; - else - k_y = n_y * DELTA_K; - for (n_z=0; n_z<=HII_MIDDLE_PARA; n_z++){ k_z = n_z * DELTA_K_PARA; @@ -37,22 +52,19 @@ void get_velocity_gradient(struct UserParams *user_params, float *v, float *vel_ dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, (fftwf_complex *)vel_gradient); } -int ComputeBrightnessTemp(float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, - struct TsBox *spin_temp, struct IonizedBox *ionized_box, - struct PerturbedField *perturb_field, struct BrightnessTemp *box) { +int ComputeBrightnessTemp(float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + TsBox *spin_temp, IonizedBox *ionized_box, + PerturbedField *perturb_field, BrightnessTemp *box){ int status; Try{ // Try block around whole function. LOG_DEBUG("Starting Brightness Temperature calculation for redshift %f", redshift); // Makes the parameter structs visible to a variety of functions/macros // Do each time to avoid Python garbage collection issues - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); - char wisdom_filename[500]; - int i, ii, j, k, n_x, n_y, n_z; - float k_x, k_y, k_z; + int i, j, k; double ave; ave = 0.; @@ -82,7 +94,7 @@ int ComputeBrightnessTemp(float redshift, struct UserParams *user_params, struct } } - float gradient_component, min_gradient_component; + float gradient_component; double dvdx, max_v_deriv; float const_factor, T_rad, pixel_Ts_factor, pixel_x_HI, pixel_deltax, H; @@ -146,7 +158,6 @@ int ComputeBrightnessTemp(float redshift, struct UserParams *user_params, struct // now add the velocity correction to the delta_T maps (only used for T_S >> T_CMB case). max_v_deriv = fabs(global_params.MAX_DVDR*H); - int fft_index, real_index; if(!(flag_options->USE_TS_FLUCT && flag_options->SUBCELL_RSD )){ // Do this unless we are doing BOTH Ts fluctuations and subcell RSDs #pragma omp parallel shared(vel_gradient,T_rad,redshift,spin_temp,box,max_v_deriv) \ @@ -166,8 +177,6 @@ int ComputeBrightnessTemp(float redshift, struct UserParams *user_params, struct ave /= (float)HII_TOT_NUM_PIXELS; } else { // This is if we're doing both TS_FLUCT and SUBCELL_RSD - min_gradient_component = 1.0; - #pragma omp parallel shared(vel_gradient,T_rad,redshift,spin_temp,box,max_v_deriv) \ private(i,j,k,gradient_component,dvdx) num_threads(user_params->N_THREADS) { @@ -175,20 +184,18 @@ int ComputeBrightnessTemp(float redshift, struct UserParams *user_params, struct for (i=0; iHII_DIM; i++){ for (j=0; jHII_DIM; j++){ for (k=0; kbrightness_temp[real_index] = 1000.*(spin_temp->Ts_box[real_index] - T_rad)/(1. + redshift); + box->brightness_temp[HII_R_INDEX(i,j,k)] = 1000.*(spin_temp->Ts_box[HII_R_INDEX(i,j,k)] - T_rad)/(1. + redshift); } else { - box->brightness_temp[real_index] = (1. - exp(- box->brightness_temp[real_index]/gradient_component ))*\ - 1000.*(spin_temp->Ts_box[real_index] - T_rad)/(1. + redshift); + box->brightness_temp[HII_R_INDEX(i,j,k)] = (1. - exp(- box->brightness_temp[HII_R_INDEX(i,j,k)]/gradient_component ))*\ + 1000.*(spin_temp->Ts_box[HII_R_INDEX(i,j,k)] - T_rad)/(1. + redshift); } } } diff --git a/src/py21cmfast/src/BrightnessTemperatureBox.h b/src/py21cmfast/src/BrightnessTemperatureBox.h new file mode 100644 index 00000000..449f2c77 --- /dev/null +++ b/src/py21cmfast/src/BrightnessTemperatureBox.h @@ -0,0 +1,13 @@ +/* definitions and function prototypes regarding the brightness temperature box */ +#ifndef _BTEMP_H +#define _BTEMP_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +int ComputeBrightnessTemp(float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + TsBox *spin_temp, IonizedBox *ionized_box, + PerturbedField *perturb_field, BrightnessTemp *box); + +#endif diff --git a/src/py21cmfast/src/Constants.h b/src/py21cmfast/src/Constants.h index 2fb6b367..52f7d73d 100644 --- a/src/py21cmfast/src/Constants.h +++ b/src/py21cmfast/src/Constants.h @@ -8,6 +8,10 @@ NOTE: Not all 21cmFAST variables will be found below. Only those useful for 21CMMC */ +#ifndef _CONSTANTS_H +#define _CONSTANTS_H + +#include "InputParameters.h" // ----------------------------------------------------------------------------------------- // @@ -17,14 +21,6 @@ #define L_FACTOR (float) (0.620350491) // factor relating cube length to filter radius = (4PI/3)^(-1/3) -// ----------------------------------------------------------------------------------------- // - -// Taken from elec_interp.c - -// ----------------------------------------------------------------------------------------- // - - - /* Filenames of the appropriate output from RECFAST to be used as boundary conditions in Ts.c as well as other tables used to compute the spin temperature @@ -177,12 +173,6 @@ // ----------------------------------------------------------------------------------------- // - -#define x_int_NXHII 14 -#define x_int_NENERGY 258 - - - // ----------------------------------------------------------------------------------------- // // From ps.c (temporary way to including transfer function from CLASS @@ -221,3 +211,17 @@ #define REION_SM13_B (double) -2.1 #define REION_SM13_C (double) 2.0 #define REION_SM13_D (double) 2.5 + +#endif + +// ------------------------------------------------------------------------------------- +// Taken from COSMOLOGY.H +// ------------------------------------------------------------------------------------- +#define Ho (double) (cosmo_params_global->hlittle*3.2407e-18) // s^-1 at z=0 +#define RHOcrit (double) ( (3.0*Ho*Ho / (8.0*PI*G)) * (CMperMPC*CMperMPC*CMperMPC)/Msun) // Msun Mpc^-3 ---- at z=0 +#define RHOcrit_cgs (double) (3.0*Ho*Ho / (8.0*PI*G)) // g pcm^-3 ---- at z=0 +#define No (double) (RHOcrit_cgs*cosmo_params_global->OMb*(1-global_params.Y_He)/m_p) // current hydrogen number density estimate (#/cm^3) ~1.92e-7 +#define He_No (double) (RHOcrit_cgs*cosmo_params_global->OMb*global_params.Y_He/(4.0*m_p)) // current helium number density estimate +#define N_b0 (double) (No+He_No) // present-day baryon num density, H + He +#define f_H (double) (No/(No+He_No)) // hydrogen number fraction +#define f_He (double) (He_No/(No+He_No)) // helium number fraction diff --git a/src/py21cmfast/src/FindHaloes.c b/src/py21cmfast/src/FindHaloes.c deleted file mode 100644 index 824e4436..00000000 --- a/src/py21cmfast/src/FindHaloes.c +++ /dev/null @@ -1,558 +0,0 @@ - -// Re-write of find_halos.c from the original 21cmFAST - - -// ComputeHaloField takes in a k_space box of the linear overdensity field -// and filters it on decreasing scales in order to find virialized halos. -// Virialized halos are defined according to the linear critical overdensity. -// ComputeHaloField outputs a cube with non-zero elements containing the Mass of -// the virialized halos - -int check_halo(char * in_halo, struct UserParams *user_params, float R, int x, int y, int z, int check_type); -void init_halo_coords(struct HaloField *halos, long long unsigned int n_halos); -int pixel_in_halo(int grid_dim, int z_dim, int x, int x_index, int y, int y_index, int z, int z_index, float Rsq_curr_index ); -void free_halo_field(struct HaloField *halos); - -int ComputeHaloField(float redshift_desc, float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, - struct InitialConditions *boxes, int random_seed, struct HaloField * halos_desc, struct HaloField *halos) { - - int status; - - Try{ // This Try brackets the whole function, so we don't indent. - - //This happens if we are updating a halo field (no need to redo big halos) - if(flag_options->HALO_STOCHASTICITY && redshift_desc > 0){ - LOG_DEBUG("Halo sampling switched on, bypassing halo finder to update %d halos...",halos_desc->n_halos); - //this would hold the two boxes used in the halo sampler, but here we are taking the sample from a catalogue so we define a dummy here - float *dummy_box; - stochastic_halofield(user_params, cosmo_params, astro_params, flag_options, random_seed, redshift_desc, - redshift, dummy_box, dummy_box, halos_desc, halos); - return 0; - } - -LOG_DEBUG("input value:"); -LOG_DEBUG("redshift=%f", redshift); -#if LOG_LEVEL >= DEBUG_LEVEL - writeUserParams(user_params); - writeCosmoParams(cosmo_params); - writeAstroParams(flag_options, astro_params); - writeFlagOptions(flag_options); -#endif - - // Makes the parameter structs visible to a variety of functions/macros - // Do each time to avoid Python garbage collection issues - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); - - omp_set_num_threads(user_params->N_THREADS); - - fftwf_complex *density_field, *density_field_saved; - - float growth_factor, R, delta_m, M, Delta_R, delta_crit; - char *in_halo, *forbidden; - int i,j,k,x,y,z; - long long unsigned int total_halo_num, r_halo_num; - float R_temp, M_MIN; - - LOG_DEBUG("Begin Initialisation"); - - // ***************** BEGIN INITIALIZATION ***************** // - init_ps(); - - growth_factor = dicke(redshift); // normalized to 1 at z=0 - delta_crit = Deltac; // for now set to spherical; check if we want elipsoidal later - - //store highly used parameters - int grid_dim = user_params->DIM; - int z_dim = D_PARA; - - //set minimum source mass - M_MIN = minimum_source_mass(redshift, false, astro_params, flag_options); - //if we use the sampler we want to stop at the HII cell mass - if(flag_options->HALO_STOCHASTICITY) - M_MIN = fmax(M_MIN,RtoM(L_FACTOR*user_params->BOX_LEN/user_params->HII_DIM)); - //otherwise we stop at the cell mass - else - M_MIN = fmax(M_MIN,RtoM(L_FACTOR*user_params->BOX_LEN/grid_dim)); - - // allocate array for the k-space box - density_field = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); - density_field_saved = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); - - // allocate memory for the boolean in_halo box - in_halo = (char *) malloc(sizeof(char)*TOT_NUM_PIXELS); - - // initialize - memset(in_halo, 0, sizeof(char)*TOT_NUM_PIXELS); - - if(global_params.OPTIMIZE) { - forbidden = (char *) malloc(sizeof(char)*TOT_NUM_PIXELS); - } - - unsigned long long int nhalo_threads[user_params->N_THREADS]; - unsigned long long int istart_threads[user_params->N_THREADS]; - //expected TOTAL halos in box from minimum source mass - - unsigned long long int arraysize_total = halos->buffer_size; - unsigned long long int arraysize_local = arraysize_total / user_params->N_THREADS; - - if(LOG_LEVEL >= DEBUG_LEVEL){ - double Mmax_debug = 1e16; - initialiseSigmaMInterpTable(M_MIN*0.9,Mmax_debug*1.1); - struct parameters_gsl_MF_integrals params = { - .redshift = redshift, - .growthf = growth_factor, - .HMF = user_params->HMF, - }; - - double nhalo_debug = IntegratedNdM(log(M_MIN), log(Mmax_debug), params, 1, 0) * VOLUME; - //expected halos above minimum filter mass - LOG_DEBUG("DexM: We expect %.2f Halos between Masses [%.2e,%.2e]",nhalo_debug,M_MIN,Mmax_debug); - } - -#pragma omp parallel shared(boxes,density_field) private(i,j,k) num_threads(user_params->N_THREADS) - { -#pragma omp for - for (i=0; ihires_density + R_INDEX(i,j,k)); - } - } - } - } - - dft_r2c_cube(user_params->USE_FFTW_WISDOM, grid_dim, z_dim, user_params->N_THREADS, density_field); - - // save a copy of the k-space density field - memcpy(density_field_saved, density_field, sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); - - // ***************** END INITIALIZATION ***************** // - - LOG_DEBUG("Finalised Initialisation"); - - // lets filter it now - // set initial R value - Delta_R = L_FACTOR*2.*user_params->BOX_LEN/(grid_dim+0.0); - - total_halo_num = 0; - R = MtoR(M_MIN*1.01); // one percent higher for rounding - - LOG_DEBUG("Prepare to filter to find halos"); - - while (R < L_FACTOR*user_params->BOX_LEN) - R*=global_params.DELTA_R_FACTOR; - - struct HaloField *halos_dexm; - if(flag_options->HALO_STOCHASTICITY){ - //To save memory, we allocate the smaller (large mass) halofield here instead of using halos_desc - halos_dexm = malloc(sizeof(struct HaloField)); - } - else{ - //assign directly to the output field instead - halos_dexm = halos; - } - - float *halo_field = calloc(TOT_NUM_PIXELS, sizeof(float)); - - while ((R > 0.5*Delta_R) && (RtoM(R) >= M_MIN)){ // filter until we get to half the pixel size or M_MIN - M = RtoM(R); - LOG_SUPER_DEBUG("while loop for finding halos: R = %f 0.5*Delta_R = %f RtoM(R)=%e M_MIN=%e", R, 0.5*Delta_R, M, M_MIN); - - //Pending a serious deep-dive into this algorithm, I will force DexM to use the fitted parameters to the - // Sheth-Tormen mass function (as of right now, We do not even reproduce EPS results) - delta_crit = growth_factor*sheth_delc_dexm(Deltac/growth_factor, sigma_z0(M)); - - // if(global_params.DELTA_CRIT_MODE == 1){ - // //This algorithm does not use the sheth tormen OR Jenkins parameters, - // // rather it uses a reduced barrier to correct for some part of this algorithm, - // // which would otherwise result in ~3x halos at redshift 6 (including with EPS). - // // Once I Figure out the cause of the discrepancy I can adjust again but for now - // // we will use the parameters that roughly give the ST mass function. - // // delta_crit = growth_factor*sheth_delc(Deltac/growth_factor, sigma_z0(M)); - // if(user_params->HMF==1) { - // // use sheth tormen correction - // delta_crit = growth_factor*sheth_delc(Deltac/growth_factor, sigma_z0(M)); - // } - // else if(user_params->HMF==4) { - // // use Delos 2023 flat barrier - // delta_crit = DELTAC_DELOS; - // } - // else if(user_params->HMF!=0){ - // LOG_WARNING("Halo Finder: You have selected DELTA_CRIT_MODE==1 with HMF %d which does not have a barrier\ - // , using EPS deltacrit = 1.68",user_params->HMF); - // } - // } - - // first let's check if virialized halos of this size are rare enough - // that we don't have to worry about them (let's define 7 sigma away, as in Mesinger et al 05) - if ((sigma_z0(M)*growth_factor*7.) < delta_crit){ - LOG_SUPER_DEBUG("Haloes too rare for M = %e! Skipping...", M); - R /= global_params.DELTA_R_FACTOR; - continue; - } - - memcpy(density_field, density_field_saved, sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); - - // now filter the box on scale R - // 0 = top hat in real space, 1 = top hat in k space - filter_box(density_field, 0, global_params.HALO_FILTER, R); - - // do the FFT to get delta_m box - dft_c2r_cube(user_params->USE_FFTW_WISDOM, grid_dim, z_dim, user_params->N_THREADS, density_field); - - // ***************** BEGIN OPTIMIZATION ***************** // - // to optimize speed, if the filter size is large (switch to collapse fraction criteria later) - if(global_params.OPTIMIZE) { - if(M > global_params.OPTIMIZE_MIN_MASS) { - memset(forbidden, 0, sizeof(char)*TOT_NUM_PIXELS); - // now go through the list of existing halos and paint on the no-go region onto - -#pragma omp parallel shared(forbidden,R) private(x,y,z,R_temp) num_threads(user_params->N_THREADS) - { - float halo_buf; -#pragma omp for - for (x=0; x 0.) { - R_temp = MtoR(halo_buf); - check_halo(forbidden, user_params, R_temp+global_params.R_OVERLAP_FACTOR*R, x,y,z,2); - } - } - } - } - } - } - } - // ***************** END OPTIMIZATION ***************** // - // now lets scroll through the box, flagging all pixels with delta_m > delta_crit - r_halo_num=0; - - //THREADING: Fix the race condition propertly to thread: it doesn't matter which thread finds the halo first - // but if two threads find a halo in the same region simultaneously (before the first one updates in_halo) some halos could double-up - //checking for overlaps in new halos after this loop could work, but I would have to calculate distances between all new halos which sounds slow - for (x=0; x global_params.OPTIMIZE_MIN_MASS)) { - if ( (delta_m > delta_crit) && !forbidden[R_INDEX(x,y,z)]){ - check_halo(in_halo, user_params, R, x,y,z,2); // flag the pixels contained within this halo - check_halo(forbidden, user_params, (1.+global_params.R_OVERLAP_FACTOR)*R, x,y,z,2); // flag the pixels contained within this halo - - halo_field[R_INDEX(x,y,z)] = M; - - r_halo_num++; // keep track of the number of halos - } - } - // ***************** END OPTIMIZATION ***************** // - else { - if ((delta_m > delta_crit)){ - // LOG_ULTRA_DEBUG("delta peak found at (%d,%d,%d), delta = %.4f",x,y,z,delta_m); - if(!in_halo[R_INDEX(x,y,z)]){ - // LOG_ULTRA_DEBUG("Not in halo",in_halo[R_INDEX(x,y,z)]); - if(!check_halo(in_halo, user_params, R, x,y,z,1)){ // we found us a "new" halo! - check_halo(in_halo, user_params, R, x,y,z,2); // flag the pixels contained within this halo - // LOG_ULTRA_DEBUG("Flagged Pixels"); - - halo_field[R_INDEX(x,y,z)] = M; - - r_halo_num++; - } - } - } - } - } - } - } - total_halo_num += r_halo_num; - LOG_SUPER_DEBUG("n_halo = %llu, total = %llu , D = %.3f, delcrit = %.3f", r_halo_num, total_halo_num, growth_factor, delta_crit); - - R /= global_params.DELTA_R_FACTOR; - } - - LOG_DEBUG("Obtained %d halo masses and positions, now saving to HaloField struct.",total_halo_num); - - //Allocate the Halo Mass and Coordinate Fields (non-wrapper structure) - if(flag_options->HALO_STOCHASTICITY) - init_halo_coords(halos_dexm, total_halo_num); - else - halos_dexm->n_halos = total_halo_num; - - //Assign to the struct - //NOTE: To thread this part, we would need to keep track of how many halos are in each thread before - // OR assign a buffer of size n_halo * n_thread (in case the last thread has all the halos), - // copy the structure from stochasticity.c with the assignment and condensing - unsigned long long int count=0; - float halo_buf = 0; - for (x=0; x 0.) { - halos_dexm->halo_masses[count] = halo_buf; - halos_dexm->halo_coords[3*count + 0] = x; - halos_dexm->halo_coords[3*count + 1] = y; - halos_dexm->halo_coords[3*count + 2] = z; - count++; - } - } - } - } - - add_properties_cat(user_params, cosmo_params, astro_params, flag_options, random_seed, redshift, halos_dexm); - LOG_DEBUG("Found %d DexM halos",halos_dexm->n_halos); - - if(flag_options->HALO_STOCHASTICITY){ - LOG_DEBUG("Finding halos below grid resolution %.3e",M_MIN); - //First we construct a grid which corresponds to how much of a HII_DIM cell is covered by halos - // This is used in the sampler - //we don't need the density field anymore so we reuse it -#pragma omp parallel private(i,j,k) num_threads(user_params->N_THREADS) - { -#pragma omp for - for (i=0; iUSE_FFTW_WISDOM, grid_dim, z_dim, user_params->N_THREADS, density_field); - if (user_params->DIM != user_params->HII_DIM) { - //the tophat filter here will smoothe the grid to HII_DIM - filter_box(density_field, 0, 0, L_FACTOR*user_params->BOX_LEN/(user_params->HII_DIM+0.0)); - } - dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->DIM, D_PARA, user_params->N_THREADS, density_field); - - float *halo_overlap_box = calloc(HII_TOT_NUM_PIXELS,sizeof(float)); - float f_pixel_factor = user_params->DIM/(float)user_params->HII_DIM; - //Now downsample the highres grid to get the lowres version -#pragma omp parallel private(i,j,k) num_threads(user_params->N_THREADS) - { -#pragma omp for - for (i=0; iHII_DIM; i++){ - for (j=0; jHII_DIM; j++){ - for (k=0; klowres_density, halo_overlap_box, halos_dexm, halos); - - //Here, halos_dexm is allocated in the C, so free it - free_halo_field(halos_dexm); - free(halos_dexm); - free(halo_overlap_box); - } - - LOG_DEBUG("Finished halo processing."); - - free(in_halo); - free(halo_field); - - if(global_params.OPTIMIZE) { - free(forbidden); - } - - fftwf_free(density_field); - fftwf_free(density_field_saved); - - fftwf_cleanup_threads(); - fftwf_cleanup(); - fftwf_forget_wisdom(); - -LOG_DEBUG("Finished halo cleanup."); -LOG_DEBUG("Found %llu Halos", halos->n_halos); -if (halos->n_halos > 3) - LOG_DEBUG("Halo Masses: %e %e %e %e", halos->halo_masses[0], halos->halo_masses[1], halos->halo_masses[2], halos->halo_masses[3]); - - } // End of Try() - Catch(status){ - return(status); - } - return(0); - -} - -// Function check_halo combines the original two functions overlap_halo and update_in_halo -// from the original 21cmFAST. Lots of redundant code, hence reduced into a single function -int check_halo(char * in_halo, struct UserParams *user_params, float R, int x, int y, int z, int check_type) { - - // if check_type == 1 (perform original overlap halo) - // Funtion OVERLAP_HALO checks if the would be halo with radius R - // and centered on (x,y,z) overlaps with a pre-existing halo - // if check_type == 2 (perform original update in halo) - // Funtion UPDATE_IN_HALO takes in a box and flags all points - // which fall within radius R of (x,y,z). - - int x_curr, y_curr, z_curr, x_min, x_max, y_min, y_max, z_min, z_max, R_index; - float Rsq_curr_index, xsq, xplussq, xminsq, ysq, yplussq, yminsq, zsq, zplussq, zminsq; - int x_index, y_index, z_index; - long long unsigned int curr_index; - - if(check_type==1) { - // scale R to a effective overlap size, using R_OVERLAP_FACTOR - R *= global_params.R_OVERLAP_FACTOR; - } - - int grid_dim = user_params->DIM; - int z_dim = D_PARA; - - // convert R to index units - R_index = ceil(R/user_params->BOX_LEN*grid_dim); - Rsq_curr_index = pow(R/user_params->BOX_LEN*grid_dim, 2); // convert to index - - // set parameter range - x_min = x-R_index; - x_max = x+R_index; - y_min = y-R_index; - y_max = y+R_index; - z_min = z-R_index; - z_max = z+R_index; - // LOG_ULTRA_DEBUG("Starting check from (%d,%d,%d) to (%d,%d,%d)",x_min,y_min,z_min,x_max,y_max,z_max); - - for (x_curr=x_min; x_curr<=x_max; x_curr++){ - for (y_curr=y_min; y_curr<=y_max; y_curr++){ - for (z_curr=z_min; z_curr<=z_max; z_curr++){ - x_index = x_curr; - y_index = y_curr; - z_index = z_curr; - // adjust if we are outside of the box - if (x_index<0) {x_index += grid_dim;} - else if (x_index>=grid_dim) {x_index -= grid_dim;} - if (y_index<0) {y_index += grid_dim;} - else if (y_index>=grid_dim) {y_index -= grid_dim;} - if (z_index<0) {z_index += z_dim;} - else if (z_index>=z_dim) {z_index -= z_dim;} - - curr_index = R_INDEX(x_index,y_index,z_index); - // LOG_ULTRA_DEBUG("current point (%d,%d,%d) idx %d",x_curr,y_curr,z_curr,curr_index); - - if(check_type==1) { - if ( in_halo[curr_index] && - pixel_in_halo(grid_dim,z_dim,x,x_index,y,y_index,z,z_index,Rsq_curr_index) ) { - // this pixel already belongs to a halo, and would want to become part of this halo as well - return 1; - } - } - else if(check_type==2) { - // now check - if (!in_halo[curr_index]){ - if(pixel_in_halo(grid_dim,z_dim,x,x_index,y,y_index,z,z_index,Rsq_curr_index)) { - // we are within the sphere defined by R, so change flag in in_halo array - in_halo[curr_index] = 1; - } - } - } - else { - LOG_ERROR("check_type must be 1 or 2, got %d", check_type); - Throw(ValueError); - } - } - } - } - if(check_type==1) { - return 0; - } -} - -void init_halo_coords(struct HaloField *halos, long long unsigned int n_halos){ - // Minimise memory usage by only storing the halo mass and positions - halos->n_halos = n_halos; - halos->halo_masses = (float *)calloc(n_halos,sizeof(float)); - halos->halo_coords = (int *)calloc(3*n_halos,sizeof(int)); - - halos->star_rng = (float *) calloc(n_halos,sizeof(float)); - halos->sfr_rng = (float *) calloc(n_halos,sizeof(float)); - halos->xray_rng = (float *) calloc(n_halos,sizeof(float)); -} - -void free_halo_field(struct HaloField *halos){ - LOG_DEBUG("Freeing HaloField instance."); - free(halos->halo_masses); - free(halos->halo_coords); - free(halos->star_rng); - free(halos->sfr_rng); - free(halos->xray_rng); - halos->n_halos = 0; -} - -int pixel_in_halo(int grid_dim, int z_dim, int x, int x_index, int y, int y_index, int z, int z_index, float Rsq_curr_index ) { - - float xsq, xplussq, xminsq, ysq, yplussq, yminsq, zsq, zplussq, zminsq; - - // remember to check all reflections - xsq = pow(x-x_index, 2); - ysq = pow(y-y_index, 2); - zsq = pow(z-z_index, 2); - xplussq = pow(x-x_index+grid_dim, 2); - yplussq = pow(y-y_index+grid_dim, 2); - zplussq = pow(z-z_index+z_dim, 2); - xminsq = pow(x-x_index-grid_dim, 2); - yminsq = pow(y-y_index-grid_dim, 2); - zminsq = pow(z-z_index-z_dim, 2); - - //This checks the center, 6 faces, 12 edges and 8 corners of the cell == 27 points - //NOTE:The center check is not really necessary - if( - ( (Rsq_curr_index > (xsq + ysq + zsq)) || // AND pixel is within this halo - (Rsq_curr_index > (xsq + ysq + zplussq)) || - (Rsq_curr_index > (xsq + ysq + zminsq)) || - - (Rsq_curr_index > (xsq + yplussq + zsq)) || - (Rsq_curr_index > (xsq + yplussq + zplussq)) || - (Rsq_curr_index > (xsq + yplussq + zminsq)) || - - (Rsq_curr_index > (xsq + yminsq + zsq)) || - (Rsq_curr_index > (xsq + yminsq + zplussq)) || - (Rsq_curr_index > (xsq + yminsq + zminsq)) || - - - (Rsq_curr_index > (xplussq + ysq + zsq)) || - (Rsq_curr_index > (xplussq + ysq + zplussq)) || - (Rsq_curr_index > (xplussq + ysq + zminsq)) || - - (Rsq_curr_index > (xplussq + yplussq + zsq)) || - (Rsq_curr_index > (xplussq + yplussq + zplussq)) || - (Rsq_curr_index > (xplussq + yplussq + zminsq)) || - - (Rsq_curr_index > (xplussq + yminsq + zsq)) || - (Rsq_curr_index > (xplussq + yminsq + zplussq)) || - (Rsq_curr_index > (xplussq + yminsq + zminsq)) || - - (Rsq_curr_index > (xminsq + ysq + zsq)) || - (Rsq_curr_index > (xminsq + ysq + zplussq)) || - (Rsq_curr_index > (xminsq + ysq + zminsq)) || - - (Rsq_curr_index > (xminsq + yplussq + zsq)) || - (Rsq_curr_index > (xminsq + yplussq + zplussq)) || - (Rsq_curr_index > (xminsq + yplussq + zminsq)) || - - (Rsq_curr_index > (xminsq + yminsq + zsq)) || - (Rsq_curr_index > (xminsq + yminsq + zplussq)) || - (Rsq_curr_index > (xminsq + yminsq + zminsq)) - ) - ) { - return(1); - } - else { - return(0); - } -} diff --git a/src/py21cmfast/src/Globals.h b/src/py21cmfast/src/Globals.h deleted file mode 100644 index 3df86949..00000000 --- a/src/py21cmfast/src/Globals.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - This is a header file containing some global variables that the user might want to change - on the rare occasion. - - Do a text search to find parameters from a specific .H file from 21cmFAST - (i.e. INIT_PARAMS.H, COSMOLOGY.H, ANAL_PARAMS.H and HEAT_PARAMS) - - NOTE: Not all 21cmFAST variables will be found below. Only those useful for 21CMMC - - */ - -struct GlobalParams{ - float ALPHA_UVB; - int EVOLVE_DENSITY_LINEARLY; - int SMOOTH_EVOLVED_DENSITY_FIELD; - float R_smooth_density; - float HII_ROUND_ERR; - int FIND_BUBBLE_ALGORITHM; - int N_POISSON; - int T_USE_VELOCITIES; - float MAX_DVDR; - float DELTA_R_HII_FACTOR; - float DELTA_R_FACTOR; - int HII_FILTER; - float INITIAL_REDSHIFT; - float R_OVERLAP_FACTOR; - int DELTA_CRIT_MODE; - int HALO_FILTER; - int OPTIMIZE; - float OPTIMIZE_MIN_MASS; - - - float CRIT_DENS_TRANSITION; - float MIN_DENSITY_LOW_LIMIT; - - int RecombPhotonCons; - float PhotonConsStart; - float PhotonConsEnd; - float PhotonConsAsymptoteTo; - float PhotonConsEndCalibz; - int PhotonConsSmoothing; - - int HEAT_FILTER; - double CLUMPING_FACTOR; - float Z_HEAT_MAX; - float R_XLy_MAX; - int NUM_FILTER_STEPS_FOR_Ts; - float ZPRIME_STEP_FACTOR; - double TK_at_Z_HEAT_MAX; - double XION_at_Z_HEAT_MAX; - int Pop; - float Pop2_ion; - float Pop3_ion; - - float NU_X_BAND_MAX; - float NU_X_MAX; - - int NBINS_LF; - - int P_CUTOFF; - float M_WDM; - float g_x; - float OMn; - float OMk; - float OMr; - float OMtot; - float Y_He; - float wl; - float SHETH_b; - float SHETH_c; - double Zreion_HeII; - int FILTER; - - char *external_table_path; - char *wisdoms_path; - float R_BUBBLE_MIN; - float M_MIN_INTEGRAL; - float M_MAX_INTEGRAL; - - float T_RE; - - float VAVG; - - bool USE_ADIABATIC_FLUCTUATIONS; -}; - -extern struct GlobalParams global_params = { - - .ALPHA_UVB = 5.0, - .EVOLVE_DENSITY_LINEARLY = 0, - .SMOOTH_EVOLVED_DENSITY_FIELD = 0, - .R_smooth_density = 0.2, - .HII_ROUND_ERR = 1e-5, - .FIND_BUBBLE_ALGORITHM = 2, - .N_POISSON = 5, - .T_USE_VELOCITIES = 1, - .MAX_DVDR = 0.2, - .DELTA_R_HII_FACTOR = 1.1, - .DELTA_R_FACTOR = 1.1, - .HII_FILTER = 0, - .INITIAL_REDSHIFT = 300., - .R_OVERLAP_FACTOR = 1., - .DELTA_CRIT_MODE = 1, - .HALO_FILTER = 0, - .OPTIMIZE = 0, - .OPTIMIZE_MIN_MASS = 1e11, - - - .CRIT_DENS_TRANSITION = 1.5, - .MIN_DENSITY_LOW_LIMIT = 9e-8, - - .RecombPhotonCons = 0, - .PhotonConsStart = 0.995, - .PhotonConsEnd = 0.3, - .PhotonConsAsymptoteTo = 0.01, - .PhotonConsEndCalibz = 3.5, - .PhotonConsSmoothing = 1, - - .HEAT_FILTER = 0, - .CLUMPING_FACTOR = 2., - .Z_HEAT_MAX = 35.0, - .R_XLy_MAX = 500., - .NUM_FILTER_STEPS_FOR_Ts = 40, - .ZPRIME_STEP_FACTOR = 1.02, - .TK_at_Z_HEAT_MAX = -1, - .XION_at_Z_HEAT_MAX = -1, - .Pop = 2, - .Pop2_ion = 5000, - .Pop3_ion = 44021, - - .NU_X_BAND_MAX = 2000.0, - .NU_X_MAX = 10000.0, - - .NBINS_LF = 100, - - .P_CUTOFF = 0, - .M_WDM = 2, - .g_x = 1.5, - .OMn = 0.0, - .OMk = 0.0, - .OMr = 8.6e-5, - .OMtot = 1.0, - .Y_He = 0.245, - .wl = -1.0, - .SHETH_b = 0.15, //In the literature this is 0.485 (RM08) or 0.5 (SMT01) or 0.34 (Barkana+01) Master 21cmFAST currently has 0.15 - .SHETH_c = 0.05, //In the literature this is 0.615 (RM08) or 0.6 (SMT01) or 0.81 (Barkana+01) Master 21cmFAST currently has 0.05 - .Zreion_HeII = 3.0, - .FILTER = 0, - .R_BUBBLE_MIN = 0.620350491, - .M_MIN_INTEGRAL = 1e5, - .M_MAX_INTEGRAL = 1e16, - - .T_RE = 2e4, - - .VAVG=25.86, - - .USE_ADIABATIC_FLUCTUATIONS = 1, -}; diff --git a/src/py21cmfast/src/HaloBox.c b/src/py21cmfast/src/HaloBox.c index 535795d9..1d9cb694 100644 --- a/src/py21cmfast/src/HaloBox.c +++ b/src/py21cmfast/src/HaloBox.c @@ -1,6 +1,26 @@ /* This file contains fucntions for calculating the HaloBox output for 21cmfast, containing the gridded * source properties, either from integrating the conditional mass functions in a cell or from the halo sampler */ - +#include +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "indexing.h" +#include "interp_tables.h" +#include "thermochem.h" +#include "hmf.h" +#include "photoncons.h" + +#include "HaloBox.h" //Parameters for the halo box calculations // These are just the values which are calculated at the beginning of ComputeHaloBox and don't change // using this reduces the use of the global parameter structs and allows fewer exp/log unit changes @@ -60,7 +80,7 @@ struct HaloProperties{ double m_turn_reion; }; -void set_hbox_constants(double redshift, struct AstroParams *astro_params, struct FlagOptions *flag_options, struct HaloBoxConstants *consts){ +void set_hbox_constants(double redshift, AstroParams *astro_params, FlagOptions *flag_options, struct HaloBoxConstants *consts){ consts->redshift = redshift; //whether to fix *integrated* (not sampled) galaxy properties to the expected mean // constant for now, to be a flag later @@ -103,7 +123,7 @@ void set_hbox_constants(double redshift, struct AstroParams *astro_params, struc consts->vcb_norel = flag_options->FIX_VCB_AVG ? global_params.VAVG : 0; } - if(flag_options->FIXED_HALO_GRIDS || user_params_stoc->AVG_BELOW_SAMPLER){ + if(flag_options->FIXED_HALO_GRIDS || user_params_global->AVG_BELOW_SAMPLER){ consts->Mlim_Fstar = Mass_limit_bisection(global_params.M_MIN_INTEGRAL, global_params.M_MAX_INTEGRAL, consts->alpha_star, consts->fstar_10); consts->Mlim_Fesc = Mass_limit_bisection(global_params.M_MIN_INTEGRAL, global_params.M_MAX_INTEGRAL, consts->alpha_esc, consts->fesc_10); @@ -208,23 +228,23 @@ void get_halo_stellarmass(double halo_mass, double mturn_acg, double mturn_mcg, double f_sample, f_sample_mini; double sm_sample,sm_sample_mini; - double baryon_ratio = cosmo_params_stoc->OMb / cosmo_params_stoc->OMm; + double baryon_ratio = cosmo_params_global->OMb / cosmo_params_global->OMm; double stoc_adjustment_term = sigma_star * sigma_star / 2.; //adjustment to the mean for lognormal scatter //We don't want an upturn even with a negative ALPHA_STAR - if(flag_options_stoc->USE_UPPER_STELLAR_TURNOVER && (f_a > fu_a)){ + if(flag_options_global->USE_UPPER_STELLAR_TURNOVER && (f_a > fu_a)){ fstar_mean = consts->upper_pivot_ratio / (pow(halo_mass/fu_p,-f_a)+pow(halo_mass/fu_p,-fu_a)); } else{ fstar_mean = pow(halo_mass/1e10,f_a); //PL term } - f_sample = f_10 * fstar_mean * exp(-mturn_acg/halo_mass + star_rng*sigma_star); //1e10 normalisation of stellar mass + f_sample = f_10 * fstar_mean * exp(-mturn_acg/halo_mass + star_rng*sigma_star - stoc_adjustment_term); //1e10 normalisation of stellar mass if(f_sample > 1.) f_sample = 1.; sm_sample = f_sample * halo_mass * baryon_ratio; *star_acg = sm_sample; - if(!flag_options_stoc->USE_MINI_HALOS){ + if(!flag_options_global->USE_MINI_HALOS){ *star_mcg = 0.; return; } @@ -259,7 +279,7 @@ void get_halo_sfr(double stellar_mass, double stellar_mass_mini, double sfr_rng, sfr_sample = sfr_mean * exp(sfr_rng*sigma_sfr - stoc_adjustment_term); *sfr = sfr_sample; - if(!flag_options_stoc->USE_MINI_HALOS){ + if(!flag_options_global->USE_MINI_HALOS){ *sfr_mini = 0.; return; } @@ -286,7 +306,7 @@ void get_halo_xray(double sfr, double sfr_mini, double metallicity, double xray_ double lx_over_sfr = get_lx_on_sfr(sfr,metallicity,consts->l_x); double xray = lx_over_sfr*(sfr*SperYR)*rng_factor; - if(flag_options_stoc->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ lx_over_sfr = get_lx_on_sfr(sfr_mini,metallicity,consts->l_x_mini); //Since there *are* some SFR-dependent models, this is done separately xray += lx_over_sfr*(sfr_mini*SperYR)*rng_factor; } @@ -317,14 +337,14 @@ void set_halo_properties(double halo_mass, double M_turn_a, double M_turn_m, double metallicity=0; double xray_lum=0; - if(flag_options_stoc->USE_TS_FLUCT){ + if(flag_options_global->USE_TS_FLUCT){ get_halo_metallicity(sfr+sfr_mini,stellar_mass+stellar_mass_mini,consts->redshift,&metallicity); get_halo_xray(sfr,sfr_mini,metallicity,input_rng[2],consts,&xray_lum); } //no rng for escape fraction yet fesc = fmin(consts->fesc_10*pow(halo_mass/1e10,consts->alpha_esc),1); - if(flag_options_stoc->USE_MINI_HALOS) + if(flag_options_global->USE_MINI_HALOS) fesc_mini = fmin(consts->fesc_7*pow(halo_mass/1e7,consts->alpha_esc),1); n_ion_sample = stellar_mass*global_params.Pop2_ion*fesc + stellar_mass_mini*global_params.Pop3_ion*fesc_mini; @@ -349,9 +369,9 @@ int get_box_averages(double M_min, double M_max, double M_turn_a, double M_turn_ double lnMmax = log(M_max); double lnMmin = log(M_min); - double prefactor_mass = RHOcrit * cosmo_params_stoc->OMm; - double prefactor_stars = RHOcrit * cosmo_params_stoc->OMb * consts->fstar_10; - double prefactor_stars_mini = RHOcrit * cosmo_params_stoc->OMb * consts->fstar_7; + double prefactor_mass = RHOcrit * cosmo_params_global->OMm; + double prefactor_stars = RHOcrit * cosmo_params_global->OMb * consts->fstar_10; + double prefactor_stars_mini = RHOcrit * cosmo_params_global->OMb * consts->fstar_7; double prefactor_sfr = prefactor_stars / consts->t_star / t_h; double prefactor_sfr_mini = prefactor_stars_mini / consts->t_star / t_h; double prefactor_nion = prefactor_stars * consts->fesc_10 * global_params.Pop2_ion; @@ -361,7 +381,7 @@ int get_box_averages(double M_min, double M_max, double M_turn_a, double M_turn_ double prefactor_xray = prefactor_sfr * consts->l_x * SperYR; double prefactor_xray_mini = prefactor_sfr_mini * consts->l_x_mini * SperYR; - double mass_intgrl, h_count; + double mass_intgrl; double intgrl_fesc_weighted, intgrl_stars_only; double intgrl_fesc_weighted_mini=0., intgrl_stars_only_mini=0.; @@ -371,7 +391,7 @@ int get_box_averages(double M_min, double M_max, double M_turn_a, double M_turn_ consts->fesc_10, consts->Mlim_Fstar, consts->Mlim_Fesc); intgrl_stars_only = Nion_General(consts->redshift, lnMmin, lnMmax, M_turn_a, consts->alpha_star, 0., consts->fstar_10, 1., consts->Mlim_Fstar, 0.); - if(flag_options_stoc->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ intgrl_fesc_weighted_mini = Nion_General_MINI(consts->redshift, lnMmin, lnMmax, M_turn_m, M_turn_a, consts->alpha_star_mini, consts->alpha_esc, consts->fstar_7, consts->fesc_7, consts->Mlim_Fstar_mini, consts->Mlim_Fesc_mini); @@ -398,14 +418,14 @@ int get_box_averages(double M_min, double M_max, double M_turn_a, double M_turn_ //This takes a HaloBox struct and fixes it's mean to exactly what we expect from the UMF integrals. // Generally should only be done for the fixed portion of the grids, since // it will otherwise make the box inconsistent with the input catalogue -void mean_fix_grids(double M_min, double M_max, struct HaloBox *grids, struct HaloProperties *averages_box, struct HaloBoxConstants *consts){ +void mean_fix_grids(double M_min, double M_max, HaloBox *grids, struct HaloProperties *averages_box, struct HaloBoxConstants *consts){ struct HaloProperties averages_global; double M_turn_a_global = averages_box->m_turn_acg; double M_turn_m_global = averages_box->m_turn_mcg; get_box_averages(M_min, M_max, M_turn_a_global, M_turn_m_global, consts, &averages_global); - int idx; - #pragma omp parallel for num_threads(user_params_stoc->N_THREADS) private(idx) + unsigned long long int idx; + #pragma omp parallel for num_threads(user_params_global->N_THREADS) private(idx) for(idx=0;idxhalo_mass[idx] *= averages_global.halo_mass/averages_box->halo_mass; grids->halo_stars[idx] *= averages_global.stellar_mass/averages_box->stellar_mass; @@ -421,10 +441,10 @@ void mean_fix_grids(double M_min, double M_max, struct HaloBox *grids, struct Ha //Fixed halo grids, where each property is set as the integral of the CMF on the EULERIAN cell scale //As per default 21cmfast (strange pretending that the lagrangian density is eulerian and then *(1+delta)) //This outputs the UN-NORMALISED grids (before mean-adjustment) -int set_fixed_grids(double M_min, double M_max, struct InitialConditions *ini_boxes, - struct PerturbedField *perturbed_field, struct TsBox *previous_spin_temp, struct IonizedBox *previous_ionize_box, - struct HaloBoxConstants *consts, struct HaloBox *grids, struct HaloProperties *averages){ - double M_cell = RHOcrit * cosmo_params_stoc->OMm * VOLUME / HII_TOT_NUM_PIXELS; //mass in cell of mean dens +int set_fixed_grids(double M_min, double M_max, InitialConditions *ini_boxes, + PerturbedField *perturbed_field, TsBox *previous_spin_temp, IonizedBox *previous_ionize_box, + struct HaloBoxConstants *consts, HaloBox *grids, struct HaloProperties *averages){ + double M_cell = RHOcrit * cosmo_params_global->OMm * VOLUME / HII_TOT_NUM_PIXELS; //mass in cell of mean dens double growth_z = dicke(consts->redshift); double lnMmin = log(M_min); @@ -433,9 +453,9 @@ int set_fixed_grids(double M_min, double M_max, struct InitialConditions *ini_bo double sigma_cell = EvaluateSigma(lnMcell); - double prefactor_mass = RHOcrit * cosmo_params_stoc->OMm; - double prefactor_stars = RHOcrit * cosmo_params_stoc->OMb * consts->fstar_10; - double prefactor_stars_mini = RHOcrit * cosmo_params_stoc->OMb * consts->fstar_7; + double prefactor_mass = RHOcrit * cosmo_params_global->OMm; + double prefactor_stars = RHOcrit * cosmo_params_global->OMb * consts->fstar_10; + double prefactor_stars_mini = RHOcrit * cosmo_params_global->OMb * consts->fstar_7; double prefactor_sfr = prefactor_stars / consts->t_star / consts->t_h; double prefactor_sfr_mini = prefactor_stars_mini / consts->t_star / consts->t_h; double prefactor_nion = prefactor_stars * consts->fesc_10 * global_params.Pop2_ion; @@ -445,102 +465,125 @@ int set_fixed_grids(double M_min, double M_max, struct InitialConditions *ini_bo double prefactor_xray = prefactor_sfr * consts->l_x * SperYR; double prefactor_xray_mini = prefactor_sfr_mini * consts->l_x_mini * SperYR; - double hm_avg=0, nion_avg=0, sfr_avg=0, sfr_avg_mini=0, wsfr_avg=0, xray_avg=0; - double sm_avg=0, sm_avg_mini=0; - double l10_mlim_m_avg=0,l10_mlim_a_avg=0,l10_mlim_r_avg; + double hm_sum=0, nion_sum=0, wsfr_sum=0, xray_sum=0; + double sm_sum=0, sm_sum_mini=0, sfr_sum=0, sfr_sum_mini=0; + double l10_mlim_m_sum=0.,l10_mlim_a_sum=0.,l10_mlim_r_sum=0.; - //find density grid limits + //find grid limits for tables double min_density = 0.; double max_density = 0.; - #pragma omp parallel num_threads(user_params_stoc->N_THREADS) + double min_log10_mturn_a = 0.; + double min_log10_mturn_m = 0.; + double max_log10_mturn_a = 0.; + double max_log10_mturn_m = 0.; + float *mturn_a_grid = calloc(HII_TOT_NUM_PIXELS,sizeof(float)); + float *mturn_m_grid = calloc(HII_TOT_NUM_PIXELS,sizeof(float)); + #pragma omp parallel num_threads(user_params_global->N_THREADS) { - int i; + unsigned long long int i; double dens; - #pragma omp for reduction(min:min_density) reduction(max:max_density) + double J21_val, Gamma12_val, zre_val; + double M_turn_r = 0.; + double M_turn_m = consts->mturn_m_nofb; + double M_turn_a = consts->mturn_a_nofb; + double curr_vcb = consts->vcb_norel; + #pragma omp for reduction(min:min_density,min_log10_mturn_a,min_log10_mturn_m)\ + reduction(max:max_density,max_log10_mturn_a,max_log10_mturn_m)\ + reduction(+:l10_mlim_m_sum,l10_mlim_a_sum,l10_mlim_r_sum) for(i=0;idensity[i]; if(dens > max_density) max_density = dens; if(dens < min_density) min_density = dens; + + if(flag_options_global->USE_MINI_HALOS){ + if(!flag_options_global->FIX_VCB_AVG && user_params_global->USE_RELATIVE_VELOCITIES){ + curr_vcb = ini_boxes->lowres_vcb[i]; + } + J21_val=Gamma12_val=zre_val=0.; + if(consts->redshift < global_params.Z_HEAT_MAX){ + J21_val = previous_spin_temp->J_21_LW_box[i]; + Gamma12_val = previous_ionize_box->Gamma12_box[i]; + zre_val = previous_ionize_box->z_re_box[i]; + } + M_turn_a = consts->mturn_a_nofb; + M_turn_m = lyman_werner_threshold(consts->redshift, J21_val, curr_vcb, astro_params_global); + M_turn_r = reionization_feedback(consts->redshift, Gamma12_val, zre_val); + M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_global->M_TURN)); + M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_global->M_TURN)); + } + mturn_a_grid[i] = log10(M_turn_a); + mturn_m_grid[i] = log10(M_turn_m); + + if(min_log10_mturn_a > mturn_a_grid[i]) min_log10_mturn_a = mturn_a_grid[i]; + if(min_log10_mturn_m > mturn_m_grid[i]) min_log10_mturn_m = mturn_m_grid[i]; + if(max_log10_mturn_a < mturn_a_grid[i]) max_log10_mturn_a = mturn_a_grid[i]; + if(max_log10_mturn_m < mturn_m_grid[i]) max_log10_mturn_m = mturn_a_grid[i]; + + l10_mlim_a_sum += mturn_a_grid[i]; + l10_mlim_m_sum += mturn_m_grid[i]; + l10_mlim_r_sum += log10(M_turn_r); } } //buffers for table ranges - min_density = min_density*1.001; + min_density = min_density*1.001; //negative max_density = max_density*1.001; + min_log10_mturn_a = min_log10_mturn_a*0.999; + min_log10_mturn_m = min_log10_mturn_m*0.999; + max_log10_mturn_a = max_log10_mturn_a*1.001; + max_log10_mturn_m = max_log10_mturn_m*1.001; LOG_DEBUG("Mean halo boxes || M = [%.2e %.2e] | Mcell = %.2e (s=%.2e) | z = %.2e | D = %.2e",M_min,M_max,M_cell,sigma_cell, consts->redshift,growth_z); //These tables are coarser than needed, an initial loop for Mturn to find limits may help - if(user_params_stoc->USE_INTERPOLATION_TABLES){ - if(user_params_stoc->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options_stoc->USE_MINI_HALOS && user_params_stoc->INTEGRATION_METHOD_MINI == 1)){ - initialise_GL(NGL_INT, lnMmin, lnMmax); + if(user_params_global->USE_INTERPOLATION_TABLES){ + if(user_params_global->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options_global->USE_MINI_HALOS && user_params_global->INTEGRATION_METHOD_MINI == 1)){ + initialise_GL(lnMmin, lnMmax); } //This table assumes no reionisation feedback initialise_SFRD_Conditional_table(min_density,max_density,growth_z,consts->mturn_a_nofb,M_min,M_max,M_cell, consts->alpha_star, consts->alpha_star_mini, consts->fstar_10, - consts->fstar_7, user_params_stoc->INTEGRATION_METHOD_ATOMIC, - user_params_stoc->INTEGRATION_METHOD_MINI, - flag_options_stoc->USE_MINI_HALOS); + consts->fstar_7, user_params_global->INTEGRATION_METHOD_ATOMIC, + user_params_global->INTEGRATION_METHOD_MINI, + flag_options_global->USE_MINI_HALOS); //This table includes reionisation feedback, but takes the atomic turnover anyway for the upper turnover initialise_Nion_Conditional_spline(consts->redshift,consts->mturn_a_nofb,min_density,max_density,M_min,M_max,M_cell, - LOG10_MTURN_MIN,LOG10_MTURN_MAX,LOG10_MTURN_MIN,LOG10_MTURN_MAX, + min_log10_mturn_a,max_log10_mturn_a,min_log10_mturn_m,max_log10_mturn_m, consts->alpha_star, consts->alpha_star_mini, consts->alpha_esc, consts->fstar_10, consts->fesc_10,consts->Mlim_Fstar,consts->Mlim_Fesc,consts->fstar_7, consts->fesc_7,consts->Mlim_Fstar_mini, consts->Mlim_Fesc_mini, - user_params_stoc->INTEGRATION_METHOD_ATOMIC, - user_params_stoc->INTEGRATION_METHOD_MINI, - flag_options_stoc->USE_MINI_HALOS, false); + user_params_global->INTEGRATION_METHOD_ATOMIC, + user_params_global->INTEGRATION_METHOD_MINI, + flag_options_global->USE_MINI_HALOS, false); initialise_dNdM_tables(min_density, max_density, lnMmin, lnMmax, growth_z, lnMcell, false); } -#pragma omp parallel num_threads(user_params_stoc->N_THREADS) +#pragma omp parallel num_threads(user_params_global->N_THREADS) { - int i; + unsigned long long int i; double dens; - double J21_val, Gamma12_val, zre_val; - double curr_vcb = consts->vcb_norel; - double M_turn_m = consts->mturn_m_nofb; - double M_turn_a = consts->mturn_a_nofb; - double M_turn_r; double l10_mturn_a,l10_mturn_m; double mass_intgrl, h_count; double intgrl_fesc_weighted, intgrl_stars_only; double intgrl_fesc_weighted_mini=0., intgrl_stars_only_mini=0.; -#pragma omp for reduction(+:hm_avg,sm_avg,sm_avg_mini,sfr_avg,sfr_avg_mini,xray_avg,nion_avg,wsfr_avg,l10_mlim_m_avg,l10_mlim_a_avg,l10_mlim_r_avg) +#pragma omp for reduction(+:hm_sum,sm_sum,sm_sum_mini,sfr_sum,sfr_sum_mini,xray_sum,nion_sum,wsfr_sum) for(i=0;idensity[i]; - - if(flag_options_stoc->USE_MINI_HALOS){ - if(!flag_options_stoc->FIX_VCB_AVG && user_params_stoc->USE_RELATIVE_VELOCITIES){ - curr_vcb = ini_boxes->lowres_vcb[i]; - } - J21_val=Gamma12_val=zre_val=0.; - if(consts->redshift < global_params.Z_HEAT_MAX){ - J21_val = previous_spin_temp->J_21_LW_box[i]; - Gamma12_val = previous_ionize_box->Gamma12_box[i]; - zre_val = previous_ionize_box->z_re_box[i]; - } - M_turn_a = consts->mturn_a_nofb; - M_turn_m = lyman_werner_threshold(consts->redshift, J21_val, curr_vcb, astro_params_stoc); - M_turn_r = reionization_feedback(consts->redshift, Gamma12_val, zre_val); - M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_stoc->M_TURN)); - M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_stoc->M_TURN)); - } - l10_mturn_a = log10(M_turn_a); - l10_mturn_m = log10(M_turn_m); + l10_mturn_a = mturn_a_grid[i]; + l10_mturn_m = mturn_m_grid[i]; h_count = EvaluateNhalo(dens, growth_z, lnMmin, lnMmax, M_cell, sigma_cell, dens); mass_intgrl = EvaluateMcoll(dens, growth_z, lnMmin, lnMmax, M_cell, sigma_cell, dens); intgrl_fesc_weighted = EvaluateNion_Conditional(dens,l10_mturn_a,growth_z,M_min,M_max,M_cell,sigma_cell, consts->Mlim_Fstar,consts->Mlim_Fesc,false); intgrl_stars_only = EvaluateSFRD_Conditional(dens,growth_z,M_min,M_max,M_cell,sigma_cell, - log10(M_turn_a),consts->Mlim_Fstar); - if(flag_options_stoc->USE_MINI_HALOS){ + l10_mturn_a,consts->Mlim_Fstar); + if(flag_options_global->USE_MINI_HALOS){ intgrl_stars_only_mini = EvaluateSFRD_Conditional_MINI(dens,l10_mturn_m,growth_z,M_min,M_max,M_cell,sigma_cell, l10_mturn_a,consts->Mlim_Fstar); intgrl_fesc_weighted_mini = EvaluateNion_Conditional_MINI(dens,l10_mturn_m,growth_z,M_min,M_max,M_cell,sigma_cell, @@ -557,46 +600,32 @@ int set_fixed_grids(double M_min, double M_max, struct InitialConditions *ini_bo grids->halo_stars[i] = intgrl_stars_only*prefactor_stars * (1+dens); grids->halo_stars_mini[i] = intgrl_stars_only_mini*prefactor_stars_mini * (1+dens); - hm_avg += grids->halo_mass[i]; - nion_avg += grids->n_ion[i]; - sfr_avg += grids->halo_sfr[i]; - wsfr_avg += grids->whalo_sfr[i]; - sfr_avg_mini += grids->halo_sfr_mini[i]; - xray_avg += grids->halo_xray[i]; - sm_avg += grids->halo_stars[i]; - sm_avg_mini += grids->halo_stars_mini[i]; - - l10_mlim_m_avg += l10_mturn_m; - l10_mlim_a_avg += l10_mturn_a; - l10_mlim_r_avg += log10(M_turn_r); + hm_sum += grids->halo_mass[i]; + nion_sum += grids->n_ion[i]; + sfr_sum += grids->halo_sfr[i]; + wsfr_sum += grids->whalo_sfr[i]; + sfr_sum_mini += grids->halo_sfr_mini[i]; + xray_sum += grids->halo_xray[i]; + sm_sum += grids->halo_stars[i]; + sm_sum_mini += grids->halo_stars_mini[i]; } } + free(mturn_a_grid); + free(mturn_m_grid); free_conditional_tables(); - hm_avg /= HII_TOT_NUM_PIXELS; - sm_avg /= HII_TOT_NUM_PIXELS; - sm_avg_mini /= HII_TOT_NUM_PIXELS; - nion_avg /= HII_TOT_NUM_PIXELS; - sfr_avg /= HII_TOT_NUM_PIXELS; - sfr_avg_mini /= HII_TOT_NUM_PIXELS; - wsfr_avg /= HII_TOT_NUM_PIXELS; - xray_avg /= HII_TOT_NUM_PIXELS; - l10_mlim_m_avg /= HII_TOT_NUM_PIXELS; - l10_mlim_a_avg /= HII_TOT_NUM_PIXELS; - l10_mlim_r_avg /= HII_TOT_NUM_PIXELS; - - averages->halo_mass = hm_avg; - averages->stellar_mass = sm_avg; - averages->stellar_mass_mini = sm_avg_mini; - averages->halo_sfr = sfr_avg; - averages->sfr_mini = sfr_avg_mini; - averages->n_ion = nion_avg; - averages->halo_xray = xray_avg; - averages->fescweighted_sfr = wsfr_avg; - averages->m_turn_acg = pow(10,l10_mlim_a_avg); - averages->m_turn_mcg = pow(10,l10_mlim_m_avg); - averages->m_turn_reion = pow(10,l10_mlim_r_avg); + averages->halo_mass = hm_sum/HII_TOT_NUM_PIXELS; + averages->stellar_mass = sm_sum/HII_TOT_NUM_PIXELS; + averages->stellar_mass_mini = sm_sum_mini/HII_TOT_NUM_PIXELS; + averages->halo_sfr = sfr_sum/HII_TOT_NUM_PIXELS; + averages->sfr_mini = sfr_sum_mini/HII_TOT_NUM_PIXELS; + averages->n_ion = nion_sum/HII_TOT_NUM_PIXELS; + averages->halo_xray = xray_sum/HII_TOT_NUM_PIXELS; + averages->fescweighted_sfr = wsfr_sum/HII_TOT_NUM_PIXELS; + averages->m_turn_acg = pow(10,l10_mlim_a_sum/HII_TOT_NUM_PIXELS); + averages->m_turn_mcg = pow(10,l10_mlim_m_sum/HII_TOT_NUM_PIXELS); + averages->m_turn_reion = pow(10,l10_mlim_r_sum/HII_TOT_NUM_PIXELS); //mean-fix the grids //TODO: put this behind a flag @@ -604,8 +633,8 @@ int set_fixed_grids(double M_min, double M_max, struct InitialConditions *ini_bo mean_fix_grids(M_min,M_max,grids,averages,consts); //assign the log10 average Mturn for the Ts global tables - grids->log10_Mcrit_MCG_ave = l10_mlim_m_avg; - grids->log10_Mcrit_ACG_ave = l10_mlim_a_avg; + grids->log10_Mcrit_MCG_ave = l10_mlim_m_sum/HII_TOT_NUM_PIXELS; + grids->log10_Mcrit_ACG_ave = l10_mlim_a_sum/HII_TOT_NUM_PIXELS; return 0; } @@ -615,13 +644,13 @@ void halobox_debug_print_avg(struct HaloProperties *averages_box, struct HaloPro return; struct HaloProperties averages_sub_expected, averages_global; LOG_DEBUG("HALO BOXES REDSHIFT %.2f [%.2e %.2e]",consts->redshift,M_min,M_max); - if(flag_options_stoc->FIXED_HALO_GRIDS){ + if(flag_options_global->FIXED_HALO_GRIDS){ get_box_averages(M_min, M_max, averages_box->m_turn_acg, averages_box->m_turn_mcg, consts, &averages_global); } else{ - get_box_averages(user_params_stoc->SAMPLER_MIN_MASS, M_max, averages_box->m_turn_acg, averages_box->m_turn_mcg, consts, &averages_global); - if(user_params_stoc->AVG_BELOW_SAMPLER && M_min < user_params_stoc->SAMPLER_MIN_MASS){ - get_box_averages(M_min, user_params_stoc->SAMPLER_MIN_MASS, averages_box->m_turn_acg, averages_box->m_turn_mcg, consts, &averages_sub_expected); + get_box_averages(user_params_global->SAMPLER_MIN_MASS, M_max, averages_box->m_turn_acg, averages_box->m_turn_mcg, consts, &averages_global); + if(user_params_global->AVG_BELOW_SAMPLER && M_min < user_params_global->SAMPLER_MIN_MASS){ + get_box_averages(M_min, user_params_global->SAMPLER_MIN_MASS, averages_box->m_turn_acg, averages_box->m_turn_mcg, consts, &averages_sub_expected); } } @@ -632,8 +661,8 @@ void halobox_debug_print_avg(struct HaloProperties *averages_box, struct HaloPro averages_box->halo_mass,averages_box->stellar_mass,averages_box->stellar_mass_mini,averages_box->halo_sfr, averages_box->sfr_mini,averages_box->halo_xray,averages_box->n_ion); - if(!flag_options_stoc->FIXED_HALO_GRIDS && user_params_stoc->AVG_BELOW_SAMPLER && M_min < user_params_stoc->SAMPLER_MIN_MASS){ - LOG_DEBUG("SUB-SAMPLER",consts->redshift); + if(!flag_options_global->FIXED_HALO_GRIDS && user_params_global->AVG_BELOW_SAMPLER && M_min < user_params_global->SAMPLER_MIN_MASS){ + LOG_DEBUG("SUB-SAMPLER"); LOG_DEBUG("Exp. averages: (HM %11.3e, SM %11.3e SM_MINI %11.3e SFR %11.3e, SFR_MINI %11.3e, XRAY %11.3e, NION %11.3e)", averages_sub_expected.halo_mass,averages_sub_expected.stellar_mass, averages_sub_expected.stellar_mass_mini, averages_sub_expected.halo_sfr, averages_sub_expected.sfr_mini,averages_sub_expected.halo_xray,averages_sub_expected.n_ion); @@ -646,9 +675,9 @@ void halobox_debug_print_avg(struct HaloProperties *averages_box, struct HaloPro //We need the mean log10 turnover masses for comparison with expected global Nion and SFRD. //Sometimes we don't calculate these on the grid (if we use halos and no sub-sampler) //So this function simply returns the volume-weighted average log10 turnover mass -void get_mean_log10_turnovers(struct InitialConditions *ini_boxes, struct TsBox *previous_spin_temp, struct IonizedBox *previous_ionize_box, - struct PerturbedField *perturbed_field, struct HaloBoxConstants *consts, double turnovers[3]){ - if(!flag_options_stoc->USE_MINI_HALOS){ +void get_mean_log10_turnovers(InitialConditions *ini_boxes, TsBox *previous_spin_temp, IonizedBox *previous_ionize_box, + PerturbedField *perturbed_field, struct HaloBoxConstants *consts, double turnovers[3]){ + if(!flag_options_global->USE_MINI_HALOS){ turnovers[0] = log10(consts->mturn_a_nofb); //ACG turnovers[1] = log10(consts->mturn_m_nofb); //MCG turnovers[2] = 0.; //reion (log10 so effectively 1 solar mass) @@ -656,10 +685,9 @@ void get_mean_log10_turnovers(struct InitialConditions *ini_boxes, struct TsBox } double l10_mturn_a_avg=0., l10_mturn_m_avg=0., l10_mturn_r_avg=0.; - #pragma omp parallel num_threads(user_params_stoc->N_THREADS) + #pragma omp parallel num_threads(user_params_global->N_THREADS) { - int i; - double dens; + unsigned long long int i; double J21_val, Gamma12_val, zre_val; double curr_vcb = consts->vcb_norel; double M_turn_m = consts->mturn_m_nofb; @@ -668,8 +696,7 @@ void get_mean_log10_turnovers(struct InitialConditions *ini_boxes, struct TsBox #pragma omp for reduction(+:l10_mturn_m_avg,l10_mturn_a_avg,l10_mturn_r_avg) for(i=0;idensity[i]; - if(!flag_options_stoc->FIX_VCB_AVG && user_params_stoc->USE_RELATIVE_VELOCITIES){ + if(!flag_options_global->FIX_VCB_AVG && user_params_global->USE_RELATIVE_VELOCITIES){ curr_vcb = ini_boxes->lowres_vcb[i]; } J21_val=Gamma12_val=zre_val=0.; @@ -679,10 +706,10 @@ void get_mean_log10_turnovers(struct InitialConditions *ini_boxes, struct TsBox zre_val = previous_ionize_box->z_re_box[i]; } M_turn_a = consts->mturn_a_nofb; - M_turn_m = lyman_werner_threshold(consts->redshift, J21_val, curr_vcb, astro_params_stoc); + M_turn_m = lyman_werner_threshold(consts->redshift, J21_val, curr_vcb, astro_params_global); M_turn_r = reionization_feedback(consts->redshift, Gamma12_val, zre_val); - M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_stoc->M_TURN)); - M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_stoc->M_TURN)); + M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_global->M_TURN)); + M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_global->M_TURN)); l10_mturn_a_avg += log10(M_turn_a); l10_mturn_m_avg += log10(M_turn_m); l10_mturn_r_avg += log10(M_turn_r); @@ -697,8 +724,8 @@ void get_mean_log10_turnovers(struct InitialConditions *ini_boxes, struct TsBox } } -void sum_halos_onto_grid(struct InitialConditions *ini_boxes, struct TsBox *previous_spin_temp, struct IonizedBox * previous_ionize_box, - struct PerturbHaloField *halos, struct HaloBoxConstants *consts, struct HaloBox *grids, struct HaloProperties *averages){ +void sum_halos_onto_grid(InitialConditions *ini_boxes, TsBox *previous_spin_temp, IonizedBox * previous_ionize_box, + PerturbHaloField *halos, struct HaloBoxConstants *consts, HaloBox *grids, struct HaloProperties *averages){ double redshift = consts->redshift; //averages double hm_avg=0.,sm_avg=0.,sfr_avg=0.; @@ -709,7 +736,7 @@ void sum_halos_onto_grid(struct InitialConditions *ini_boxes, struct TsBox *prev unsigned long long int total_n_halos, n_halos_cut=0.; double cell_volume = VOLUME / HII_TOT_NUM_PIXELS; - #pragma omp parallel num_threads(user_params_stoc->N_THREADS) + #pragma omp parallel num_threads(user_params_global->N_THREADS) { int x,y,z; unsigned long long int i_halo,i_cell; @@ -741,8 +768,8 @@ void sum_halos_onto_grid(struct InitialConditions *ini_boxes, struct TsBox *prev //set values before reionisation feedback //NOTE: I could easily apply reionization feedback without minihalos but this was not done previously - if(flag_options_stoc->USE_MINI_HALOS){ - if(!flag_options_stoc->FIX_VCB_AVG && user_params_stoc->USE_RELATIVE_VELOCITIES) + if(flag_options_global->USE_MINI_HALOS){ + if(!flag_options_global->FIX_VCB_AVG && user_params_global->USE_RELATIVE_VELOCITIES) curr_vcb = ini_boxes->lowres_vcb[i_cell]; J21_val=Gamma12_val=zre_val=0.; @@ -753,10 +780,10 @@ void sum_halos_onto_grid(struct InitialConditions *ini_boxes, struct TsBox *prev } M_turn_a = consts->mturn_a_nofb; - M_turn_m = lyman_werner_threshold(redshift, J21_val, curr_vcb, astro_params_stoc); + M_turn_m = lyman_werner_threshold(redshift, J21_val, curr_vcb, astro_params_global); M_turn_r = reionization_feedback(redshift, Gamma12_val, zre_val); - M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_stoc->M_TURN)); - M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_stoc->M_TURN)); + M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_global->M_TURN)); + M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_global->M_TURN)); } //these are the halo property RNG sequences @@ -827,7 +854,7 @@ void sum_halos_onto_grid(struct InitialConditions *ini_boxes, struct TsBox *prev } } total_n_halos = halos->n_halos - n_halos_cut; - LOG_SUPER_DEBUG("Cell 0 Halo %d: HM: %.2e SM: %.2e (%.2e) SF: %.2e (%.2e) X: %.2e NI: %.2e WS: %.2e ct : %d",grids->halo_mass[HII_R_INDEX(0,0,0)], + LOG_SUPER_DEBUG("Cell 0 Halo: HM: %.2e SM: %.2e (%.2e) SF: %.2e (%.2e) X: %.2e NI: %.2e WS: %.2e ct : %d",grids->halo_mass[HII_R_INDEX(0,0,0)], grids->halo_stars[HII_R_INDEX(0,0,0)],grids->halo_stars_mini[HII_R_INDEX(0,0,0)], grids->halo_sfr[HII_R_INDEX(0,0,0)],grids->halo_sfr_mini[HII_R_INDEX(0,0,0)], grids->halo_xray[HII_R_INDEX(0,0,0)],grids->n_ion[HII_R_INDEX(0,0,0)], @@ -869,17 +896,13 @@ void sum_halos_onto_grid(struct InitialConditions *ini_boxes, struct TsBox *prev } //We grid a PERTURBED halofield into the necessary quantities for calculating radiative backgrounds -int ComputeHaloBox(double redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, - struct FlagOptions * flag_options, struct InitialConditions *ini_boxes, struct PerturbedField * perturbed_field, struct PerturbHaloField *halos, - struct TsBox *previous_spin_temp, struct IonizedBox *previous_ionize_box, struct HaloBox *grids){ +int ComputeHaloBox(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, + FlagOptions * flag_options, InitialConditions *ini_boxes, PerturbedField * perturbed_field, PerturbHaloField *halos, + TsBox *previous_spin_temp, IonizedBox *previous_ionize_box, HaloBox *grids){ int status; Try{ //get parameters - Broadcast_struct_global_UF(user_params,cosmo_params); - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_IT(user_params,cosmo_params,astro_params,flag_options); - Broadcast_struct_global_STOC(user_params,cosmo_params,astro_params,flag_options); - Broadcast_struct_global_IT(user_params,cosmo_params,astro_params,flag_options); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); unsigned long long int idx; #pragma omp parallel for num_threads(user_params->N_THREADS) private(idx) @@ -904,7 +927,6 @@ int ComputeHaloBox(double redshift, struct UserParams *user_params, struct Cosmo double M_max = global_params.M_MAX_INTEGRAL; double cell_volume = VOLUME/HII_TOT_NUM_PIXELS; - double M_turn_a_global, M_turn_m_global; double turnovers[3]; struct HaloProperties averages_box, averages_subsampler; @@ -957,9 +979,9 @@ int ComputeHaloBox(double redshift, struct UserParams *user_params, struct Cosmo //NOTE: the density-grid based calculations (!USE_HALO_FIELD) // use the cell-weighted average of the log10(Mturn) (see issue #369) LOG_SUPER_DEBUG("log10 Mutrn ACG: log10 cell-weighted %.6e Halo-weighted %.6e", - pow(10,grids->log10_Mcrit_ACG_ave),averages_box->m_turn_acg); + pow(10,grids->log10_Mcrit_ACG_ave),averages_box.m_turn_acg); LOG_SUPER_DEBUG("log10 Mutrn MCG: log10 cell-weighted %.6e Halo-weighted %.6e", - pow(10,grids->log10_Mcrit_MCG_ave),averages_box->m_turn_mcg); + pow(10,grids->log10_Mcrit_MCG_ave),averages_box.m_turn_mcg); if(user_params->USE_INTERPOLATION_TABLES){ freeSigmaMInterpTable(); @@ -973,26 +995,24 @@ int ComputeHaloBox(double redshift, struct UserParams *user_params, struct Cosmo } //test function for getting halo properties from the wrapper, can use a lot of memory for large catalogs -int test_halo_props(double redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, - struct FlagOptions * flag_options, float * vcb_grid, float *J21_LW_grid, float *z_re_grid, float *Gamma12_ion_grid, - struct PerturbHaloField *halos, float *halo_props_out){ +int test_halo_props(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, + FlagOptions * flag_options, float * vcb_grid, float *J21_LW_grid, float *z_re_grid, float *Gamma12_ion_grid, + PerturbHaloField *halos, float *halo_props_out){ int status; Try{ //get parameters - Broadcast_struct_global_UF(user_params,cosmo_params); - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_STOC(user_params,cosmo_params,astro_params,flag_options); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); struct HaloBoxConstants hbox_consts; set_hbox_constants(redshift,astro_params,flag_options,&hbox_consts); LOG_DEBUG("Getting props for %llu halos at z=%.2f",halos->n_halos,redshift); - #pragma omp parallel num_threads(user_params_stoc->N_THREADS) + #pragma omp parallel num_threads(user_params_global->N_THREADS) { int x,y,z; unsigned long long int i_halo, i_cell; - double m,nion,sfr,wsfr,sfr_mini,stars_mini,stars,xray; + double m; double J21_val, Gamma12_val, zre_val; double curr_vcb = hbox_consts.vcb_norel; @@ -1018,8 +1038,8 @@ int test_halo_props(double redshift, struct UserParams *user_params, struct Cosm //set values before reionisation feedback //NOTE: I could easily apply reionization feedback without minihalos but this was not done previously - if(flag_options_stoc->USE_MINI_HALOS){ - if(!flag_options_stoc->FIX_VCB_AVG && user_params_stoc->USE_RELATIVE_VELOCITIES) + if(flag_options_global->USE_MINI_HALOS){ + if(!flag_options_global->FIX_VCB_AVG && user_params_global->USE_RELATIVE_VELOCITIES) curr_vcb = vcb_grid[i_cell]; J21_val=Gamma12_val=zre_val=0.; @@ -1029,10 +1049,10 @@ int test_halo_props(double redshift, struct UserParams *user_params, struct Cosm zre_val = z_re_grid[i_cell]; } M_turn_a = hbox_consts.mturn_a_nofb; - M_turn_m = lyman_werner_threshold(redshift, J21_val, curr_vcb, astro_params_stoc); + M_turn_m = lyman_werner_threshold(redshift, J21_val, curr_vcb, astro_params_global); M_turn_r = reionization_feedback(redshift, Gamma12_val, zre_val); - M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_stoc->M_TURN)); - M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_stoc->M_TURN)); + M_turn_a = fmax(M_turn_a,fmax(M_turn_r,astro_params_global->M_TURN)); + M_turn_m = fmax(M_turn_m,fmax(M_turn_r,astro_params_global->M_TURN)); } //these are the halo property RNG sequences diff --git a/src/py21cmfast/src/HaloBox.h b/src/py21cmfast/src/HaloBox.h new file mode 100644 index 00000000..e368d2e4 --- /dev/null +++ b/src/py21cmfast/src/HaloBox.h @@ -0,0 +1,24 @@ +#ifndef _HALOBOX_H +#define _HALOBOX_H + +#include "InputParameters.h" +#include "IonisationBox.h" +#include "SpinTemperatureBox.h" +#include "InitialConditions.h" +#include "HaloField.h" +#include "PerturbHaloField.h" + +#include "InputParameters.h" +#include "OutputStructs.h" + +//Compute the HaloBox Object +int ComputeHaloBox(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, + FlagOptions * flag_options, InitialConditions *ini_boxes, PerturbedField * perturbed_field, PerturbHaloField *halos, + TsBox *previous_spin_temp, IonizedBox *previous_ionize_box, HaloBox *grids); + +//Test function which stops early and returns galaxy properties for each halo +int test_halo_props(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, + FlagOptions * flag_options, float * vcb_grid, float *J21_LW_grid, float *z_re_grid, float *Gamma12_ion_grid, + PerturbHaloField *halos, float *halo_props_out); + +#endif diff --git a/src/py21cmfast/src/HaloField.c b/src/py21cmfast/src/HaloField.c new file mode 100644 index 00000000..02b8854d --- /dev/null +++ b/src/py21cmfast/src/HaloField.c @@ -0,0 +1,569 @@ +// Re-write of find_halos.c from the original 21cmFAST + + +// ComputeHaloField takes in a k_space box of the linear overdensity field +// and filters it on decreasing scales in order to find virialized halos. +// Virialized halos are defined according to the linear critical overdensity. +// ComputeHaloField outputs a cube with non-zero elements containing the Mass of +// the virialized halos +#include +#include +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "indexing.h" +#include "hmf.h" +#include "interp_tables.h" +#include "dft.h" +#include "filtering.h" +#include "debugging.h" +#include "Stochasticity.h" + +#include "HaloField.h" + +int check_halo(char * in_halo, UserParams *user_params, float R, int x, int y, int z, int check_type); +void init_halo_coords(HaloField *halos, long long unsigned int n_halos); +int pixel_in_halo(int grid_dim, int z_dim, int x, int x_index, int y, int y_index, int z, int z_index, float Rsq_curr_index ); +void free_halo_field(HaloField *halos); + +int ComputeHaloField(float redshift_desc, float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + InitialConditions *boxes, unsigned long long int random_seed, + HaloField * halos_desc, HaloField *halos) { + + int status; + + Try{ // This Try brackets the whole function, so we don't indent. + + //This happens if we are updating a halo field (no need to redo big halos) + if(flag_options->HALO_STOCHASTICITY && redshift_desc > 0){ + LOG_DEBUG("Halo sampling switched on, bypassing halo finder to update %llu halos...",halos_desc->n_halos); + //this would hold the two boxes used in the halo sampler, but here we are taking the sample from a catalogue so we define a dummy here + float *dummy_box = NULL; + stochastic_halofield(user_params, cosmo_params, astro_params, flag_options, random_seed, redshift_desc, + redshift, dummy_box, dummy_box, halos_desc, halos); + return 0; + } + + LOG_DEBUG("input value:"); + LOG_DEBUG("redshift=%f", redshift); + #if LOG_LEVEL >= DEBUG_LEVEL + writeUserParams(user_params); + writeCosmoParams(cosmo_params); + writeAstroParams(flag_options, astro_params); + writeFlagOptions(flag_options); + #endif + + // Makes the parameter structs visible to a variety of functions/macros + // Do each time to avoid Python garbage collection issues + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); + + omp_set_num_threads(user_params->N_THREADS); + + fftwf_complex *density_field, *density_field_saved; + + float growth_factor, R, delta_m, M, Delta_R, delta_crit; + char *in_halo, *forbidden; + int i,j,k,x,y,z; + long long unsigned int total_halo_num, r_halo_num; + float R_temp, M_MIN; + + LOG_DEBUG("Begin Initialisation"); + + // ***************** BEGIN INITIALIZATION ***************** // + init_ps(); + + growth_factor = dicke(redshift); // normalized to 1 at z=0 + delta_crit = Deltac; // for now set to spherical; check if we want elipsoidal later + + //store highly used parameters + int grid_dim = user_params->DIM; + int z_dim = D_PARA; + + //set minimum source mass + M_MIN = minimum_source_mass(redshift, false, astro_params, flag_options); + //if we use the sampler we want to stop at the HII cell mass + if(flag_options->HALO_STOCHASTICITY) + M_MIN = fmax(M_MIN,RtoM(L_FACTOR*user_params->BOX_LEN/user_params->HII_DIM)); + //otherwise we stop at the cell mass + else + M_MIN = fmax(M_MIN,RtoM(L_FACTOR*user_params->BOX_LEN/grid_dim)); + + // allocate array for the k-space box + density_field = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); + density_field_saved = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); + + // allocate memory for the boolean in_halo box + in_halo = (char *) malloc(sizeof(char)*TOT_NUM_PIXELS); + + // initialize + memset(in_halo, 0, sizeof(char)*TOT_NUM_PIXELS); + + if(global_params.OPTIMIZE) { + forbidden = (char *) malloc(sizeof(char)*TOT_NUM_PIXELS); + } + + //Unused variables, for future threading + // unsigned long long int nhalo_threads[user_params->N_THREADS]; + // unsigned long long int istart_threads[user_params->N_THREADS]; + // //expected TOTAL halos in box from minimum source mass + + // unsigned long long int arraysize_total = halos->buffer_size; + // unsigned long long int arraysize_local = arraysize_total / user_params->N_THREADS; + + #if LOG_LEVEL >= DEBUG_LEVEL + initialiseSigmaMInterpTable(M_MIN,global_params.M_MAX_INTEGRAL); + double nhalo_debug = Nhalo_General(redshift, log(M_MIN), log(global_params.M_MAX_INTEGRAL)) * VOLUME * cosmo_params->OMm * RHOcrit; + //expected halos above minimum filter mass + LOG_DEBUG("DexM: We expect %.2f Halos between Masses [%.2e,%.2e] D %.3e",nhalo_debug,M_MIN,global_params.M_MAX_INTEGRAL, growth_factor); + #endif + + #pragma omp parallel shared(boxes,density_field) private(i,j,k) num_threads(user_params->N_THREADS) + { + #pragma omp for + for (i=0; ihires_density[R_INDEX(i,j,k)]; + } + } + } + } + + dft_r2c_cube(user_params->USE_FFTW_WISDOM, grid_dim, z_dim, user_params->N_THREADS, density_field); + + // save a copy of the k-space density field + memcpy(density_field_saved, density_field, sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); + + // ***************** END INITIALIZATION ***************** // + + LOG_DEBUG("Finalised Initialisation"); + + // lets filter it now + // set initial R value + Delta_R = L_FACTOR*2.*user_params->BOX_LEN/(grid_dim+0.0); + + total_halo_num = 0; + R = MtoR(M_MIN*1.01); // one percent higher for rounding + + LOG_DEBUG("Prepare to filter to find halos"); + + while (R < L_FACTOR*user_params->BOX_LEN) + R*=global_params.DELTA_R_FACTOR; + + HaloField *halos_dexm; + if(flag_options->HALO_STOCHASTICITY){ + //To save memory, we allocate the smaller (large mass) halofield here instead of using halos_desc + halos_dexm = malloc(sizeof(HaloField)); + } + else{ + //assign directly to the output field instead + halos_dexm = halos; + } + + float *halo_field = calloc(TOT_NUM_PIXELS, sizeof(float)); + + while ((R > 0.5*Delta_R) && (RtoM(R) >= M_MIN)){ // filter until we get to half the pixel size or M_MIN + M = RtoM(R); + LOG_SUPER_DEBUG("while loop for finding halos: R = %f 0.5*Delta_R = %f RtoM(R)=%e M_MIN=%e", R, 0.5*Delta_R, M, M_MIN); + + //Pending a serious deep-dive into this algorithm, I will force DexM to use the fitted parameters to the + // Sheth-Tormen mass function (as of right now, We do not even reproduce EPS results) + delta_crit = growth_factor*sheth_delc_dexm(Deltac/growth_factor, sigma_z0(M)); + + // if(global_params.DELTA_CRIT_MODE == 1){ + // //This algorithm does not use the sheth tormen OR Jenkins parameters, + // // rather it uses a reduced barrier to correct for some part of this algorithm, + // // which would otherwise result in ~3x halos at redshift 6 (including with EPS). + // // Once I Figure out the cause of the discrepancy I can adjust again but for now + // // we will use the parameters that roughly give the ST mass function. + // // delta_crit = growth_factor*sheth_delc(Deltac/growth_factor, sigma_z0(M)); + // if(user_params->HMF==1) { + // // use sheth tormen correction + // delta_crit = growth_factor*sheth_delc(Deltac/growth_factor, sigma_z0(M)); + // } + // else if(user_params->HMF==4) { + // // use Delos 2023 flat barrier + // delta_crit = DELTAC_DELOS; + // } + // else if(user_params->HMF!=0){ + // LOG_WARNING("Halo Finder: You have selected DELTA_CRIT_MODE==1 with HMF %d which does not have a barrier + // , using EPS deltacrit = 1.68",user_params->HMF); + // } + // } + + // first let's check if virialized halos of this size are rare enough + // that we don't have to worry about them (let's define 7 sigma away, as in Mesinger et al 05) + if ((sigma_z0(M)*growth_factor*7.) < delta_crit){ + LOG_SUPER_DEBUG("Haloes too rare for M = %e! Skipping...", M); + R /= global_params.DELTA_R_FACTOR; + continue; + } + + memcpy(density_field, density_field_saved, sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); + + // now filter the box on scale R + // 0 = top hat in real space, 1 = top hat in k space + filter_box(density_field, 0, global_params.HALO_FILTER, R); + + // do the FFT to get delta_m box + dft_c2r_cube(user_params->USE_FFTW_WISDOM, grid_dim, z_dim, user_params->N_THREADS, density_field); + + // ***************** BEGIN OPTIMIZATION ***************** // + // to optimize speed, if the filter size is large (switch to collapse fraction criteria later) + if(global_params.OPTIMIZE) { + if(M > global_params.OPTIMIZE_MIN_MASS) { + memset(forbidden, 0, sizeof(char)*TOT_NUM_PIXELS); + // now go through the list of existing halos and paint on the no-go region onto + + #pragma omp parallel shared(forbidden,R) private(x,y,z,R_temp) num_threads(user_params->N_THREADS) + { + float halo_buf; + #pragma omp for + for (x=0; x 0.) { + R_temp = MtoR(halo_buf); + check_halo(forbidden, user_params, R_temp+global_params.R_OVERLAP_FACTOR*R, x,y,z,2); + } + } + } + } + } + } + } + // ***************** END OPTIMIZATION ***************** // + // now lets scroll through the box, flagging all pixels with delta_m > delta_crit + r_halo_num=0; + + //THREADING: Fix the race condition propertly to thread: it doesn't matter which thread finds the halo first + // but if two threads find a halo in the same region simultaneously (before the first one updates in_halo) some halos could double-up + //checking for overlaps in new halos after this loop could work, but I would have to calculate distances between all new halos which sounds slow + for (x=0; x global_params.OPTIMIZE_MIN_MASS)) { + if ( (delta_m > delta_crit) && !forbidden[R_INDEX(x,y,z)]){ + check_halo(in_halo, user_params, R, x,y,z,2); // flag the pixels contained within this halo + check_halo(forbidden, user_params, (1.+global_params.R_OVERLAP_FACTOR)*R, x,y,z,2); // flag the pixels contained within this halo + + halo_field[R_INDEX(x,y,z)] = M; + + r_halo_num++; // keep track of the number of halos + } + } + // ***************** END OPTIMIZATION ***************** // + else { + if ((delta_m > delta_crit)){ + if(!in_halo[R_INDEX(x,y,z)]){ + if(!check_halo(in_halo, user_params, R, x,y,z,1)){ // we found us a "new" halo! + check_halo(in_halo, user_params, R, x,y,z,2); // flag the pixels contained within this halo + + halo_field[R_INDEX(x,y,z)] = M; + + r_halo_num++; + } + } + } + } + } + } + } + total_halo_num += r_halo_num; + LOG_SUPER_DEBUG("n_halo = %llu, total = %llu , D = %.3f, delcrit = %.3f", r_halo_num, total_halo_num, growth_factor, delta_crit); + + R /= global_params.DELTA_R_FACTOR; + } + + LOG_DEBUG("Obtained %llu halo masses and positions, now saving to HaloField struct.",total_halo_num); + + //Allocate the Halo Mass and Coordinate Fields (non-wrapper structure) + if(flag_options->HALO_STOCHASTICITY) + init_halo_coords(halos_dexm, total_halo_num); + else + halos_dexm->n_halos = total_halo_num; + + //Assign to the struct + //NOTE: To thread this part, we would need to keep track of how many halos are in each thread before + // OR assign a buffer of size n_halo * n_thread (in case the last thread has all the halos), + // copy the structure from stochasticity.c with the assignment and condensing + unsigned long long int count=0; + float halo_buf = 0; + for (x=0; x 0.) { + halos_dexm->halo_masses[count] = halo_buf; + halos_dexm->halo_coords[3*count + 0] = x; + halos_dexm->halo_coords[3*count + 1] = y; + halos_dexm->halo_coords[3*count + 2] = z; + count++; + } + } + } + } + + add_properties_cat(random_seed, redshift, halos_dexm); + LOG_DEBUG("Found %llu DexM halos",halos_dexm->n_halos); + + if(flag_options->HALO_STOCHASTICITY){ + LOG_DEBUG("Finding halos below grid resolution %.3e",M_MIN); + //First we construct a grid which corresponds to how much of a HII_DIM cell is covered by halos + // This is used in the sampler + //we don't need the density field anymore so we reuse it +#pragma omp parallel private(i,j,k) num_threads(user_params->N_THREADS) + { +#pragma omp for + for (i=0; iUSE_FFTW_WISDOM, grid_dim, z_dim, user_params->N_THREADS, density_field); + if (user_params->DIM != user_params->HII_DIM) { + //the tophat filter here will smoothe the grid to HII_DIM + filter_box(density_field, 0, 0, L_FACTOR*user_params->BOX_LEN/(user_params->HII_DIM+0.0)); + } + dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->DIM, D_PARA, user_params->N_THREADS, density_field); + + float *halo_overlap_box = calloc(HII_TOT_NUM_PIXELS,sizeof(float)); + float f_pixel_factor = user_params->DIM/(float)user_params->HII_DIM; + //Now downsample the highres grid to get the lowres version +#pragma omp parallel private(i,j,k) num_threads(user_params->N_THREADS) + { +#pragma omp for + for (i=0; iHII_DIM; i++){ + for (j=0; jHII_DIM; j++){ + for (k=0; klowres_density, halo_overlap_box, halos_dexm, halos); + + //Here, halos_dexm is allocated in the C, so free it + free_halo_field(halos_dexm); + free(halos_dexm); + free(halo_overlap_box); + } + + LOG_DEBUG("Finished halo processing."); + + free(in_halo); + free(halo_field); + + if(global_params.OPTIMIZE) { + free(forbidden); + } + + fftwf_free(density_field); + fftwf_free(density_field_saved); + + fftwf_cleanup_threads(); + fftwf_cleanup(); + fftwf_forget_wisdom(); + + LOG_DEBUG("Finished halo cleanup."); + LOG_DEBUG("Found %llu Halos", halos->n_halos); + if (halos->n_halos > 3) + LOG_DEBUG("Halo Masses: %e %e %e %e", halos->halo_masses[0], halos->halo_masses[1], halos->halo_masses[2], halos->halo_masses[3]); + + } // End of Try() + Catch(status){ + return(status); + } + return(0); + +} + +// Function check_halo combines the original two functions overlap_halo and update_in_halo +// from the original 21cmFAST. Lots of redundant code, hence reduced into a single function +int check_halo(char * in_halo, UserParams *user_params, float R, int x, int y, int z, int check_type) { + + // if check_type == 1 (perform original overlap halo) + // Funtion OVERLAP_HALO checks if the would be halo with radius R + // and centered on (x,y,z) overlaps with a pre-existing halo + // if check_type == 2 (perform original update in halo) + // Funtion UPDATE_IN_HALO takes in a box and flags all points + // which fall within radius R of (x,y,z). + + int x_curr, y_curr, z_curr, x_min, x_max, y_min, y_max, z_min, z_max, R_index; + float Rsq_curr_index; + int x_index, y_index, z_index; + long long unsigned int curr_index; + + if(check_type==1) { + // scale R to a effective overlap size, using R_OVERLAP_FACTOR + R *= global_params.R_OVERLAP_FACTOR; + } + + int grid_dim = user_params->DIM; + int z_dim = D_PARA; + + // convert R to index units + R_index = ceil(R/user_params->BOX_LEN*grid_dim); + Rsq_curr_index = pow(R/user_params->BOX_LEN*grid_dim, 2); // convert to index + + // set parameter range + x_min = x-R_index; + x_max = x+R_index; + y_min = y-R_index; + y_max = y+R_index; + z_min = z-R_index; + z_max = z+R_index; + // LOG_ULTRA_DEBUG("Starting check from (%d,%d,%d) to (%d,%d,%d)",x_min,y_min,z_min,x_max,y_max,z_max); + + for (x_curr=x_min; x_curr<=x_max; x_curr++){ + for (y_curr=y_min; y_curr<=y_max; y_curr++){ + for (z_curr=z_min; z_curr<=z_max; z_curr++){ + x_index = x_curr; + y_index = y_curr; + z_index = z_curr; + // adjust if we are outside of the box + if (x_index<0) {x_index += grid_dim;} + else if (x_index>=grid_dim) {x_index -= grid_dim;} + if (y_index<0) {y_index += grid_dim;} + else if (y_index>=grid_dim) {y_index -= grid_dim;} + if (z_index<0) {z_index += z_dim;} + else if (z_index>=z_dim) {z_index -= z_dim;} + + curr_index = R_INDEX(x_index,y_index,z_index); + // LOG_ULTRA_DEBUG("current point (%d,%d,%d) idx %d",x_curr,y_curr,z_curr,curr_index); + + if(check_type==1) { + if ( in_halo[curr_index] && + pixel_in_halo(grid_dim,z_dim,x,x_index,y,y_index,z,z_index,Rsq_curr_index) ) { + // this pixel already belongs to a halo, and would want to become part of this halo as well + return 1; + } + } + else if(check_type==2) { + // now check + if (!in_halo[curr_index]){ + if(pixel_in_halo(grid_dim,z_dim,x,x_index,y,y_index,z,z_index,Rsq_curr_index)) { + // we are within the sphere defined by R, so change flag in in_halo array + in_halo[curr_index] = 1; + } + } + } + else { + LOG_ERROR("check_type must be 1 or 2, got %d", check_type); + Throw(ValueError); + } + } + } + } + //if check_type==1, we found no halos + //if check_type==2, we don't use the return value + return 0; +} + +void init_halo_coords(HaloField *halos, long long unsigned int n_halos){ + // Minimise memory usage by only storing the halo mass and positions + halos->n_halos = n_halos; + halos->halo_masses = (float *)calloc(n_halos,sizeof(float)); + halos->halo_coords = (int *)calloc(3*n_halos,sizeof(int)); + + halos->star_rng = (float *) calloc(n_halos,sizeof(float)); + halos->sfr_rng = (float *) calloc(n_halos,sizeof(float)); + halos->xray_rng = (float *) calloc(n_halos,sizeof(float)); +} + +void free_halo_field(HaloField *halos){ + LOG_DEBUG("Freeing HaloField instance."); + free(halos->halo_masses); + free(halos->halo_coords); + free(halos->star_rng); + free(halos->sfr_rng); + free(halos->xray_rng); + halos->n_halos = 0; +} + +int pixel_in_halo(int grid_dim, int z_dim, int x, int x_index, int y, int y_index, int z, int z_index, float Rsq_curr_index ) { + + float xsq, xplussq, xminsq, ysq, yplussq, yminsq, zsq, zplussq, zminsq; + + // remember to check all reflections + xsq = pow(x-x_index, 2); + ysq = pow(y-y_index, 2); + zsq = pow(z-z_index, 2); + xplussq = pow(x-x_index+grid_dim, 2); + yplussq = pow(y-y_index+grid_dim, 2); + zplussq = pow(z-z_index+z_dim, 2); + xminsq = pow(x-x_index-grid_dim, 2); + yminsq = pow(y-y_index-grid_dim, 2); + zminsq = pow(z-z_index-z_dim, 2); + + //This checks the center, 6 faces, 12 edges and 8 corners of the cell == 27 points + //NOTE:The center check is not really necessary + if( + ( (Rsq_curr_index > (xsq + ysq + zsq)) || // AND pixel is within this halo + (Rsq_curr_index > (xsq + ysq + zplussq)) || + (Rsq_curr_index > (xsq + ysq + zminsq)) || + + (Rsq_curr_index > (xsq + yplussq + zsq)) || + (Rsq_curr_index > (xsq + yplussq + zplussq)) || + (Rsq_curr_index > (xsq + yplussq + zminsq)) || + + (Rsq_curr_index > (xsq + yminsq + zsq)) || + (Rsq_curr_index > (xsq + yminsq + zplussq)) || + (Rsq_curr_index > (xsq + yminsq + zminsq)) || + + + (Rsq_curr_index > (xplussq + ysq + zsq)) || + (Rsq_curr_index > (xplussq + ysq + zplussq)) || + (Rsq_curr_index > (xplussq + ysq + zminsq)) || + + (Rsq_curr_index > (xplussq + yplussq + zsq)) || + (Rsq_curr_index > (xplussq + yplussq + zplussq)) || + (Rsq_curr_index > (xplussq + yplussq + zminsq)) || + + (Rsq_curr_index > (xplussq + yminsq + zsq)) || + (Rsq_curr_index > (xplussq + yminsq + zplussq)) || + (Rsq_curr_index > (xplussq + yminsq + zminsq)) || + + (Rsq_curr_index > (xminsq + ysq + zsq)) || + (Rsq_curr_index > (xminsq + ysq + zplussq)) || + (Rsq_curr_index > (xminsq + ysq + zminsq)) || + + (Rsq_curr_index > (xminsq + yplussq + zsq)) || + (Rsq_curr_index > (xminsq + yplussq + zplussq)) || + (Rsq_curr_index > (xminsq + yplussq + zminsq)) || + + (Rsq_curr_index > (xminsq + yminsq + zsq)) || + (Rsq_curr_index > (xminsq + yminsq + zplussq)) || + (Rsq_curr_index > (xminsq + yminsq + zminsq)) + ) + ) { + return(1); + } + else { + return(0); + } +} diff --git a/src/py21cmfast/src/HaloField.h b/src/py21cmfast/src/HaloField.h new file mode 100644 index 00000000..09ce507d --- /dev/null +++ b/src/py21cmfast/src/HaloField.h @@ -0,0 +1,13 @@ +/* Function Prototypes and definitions for the Halo Catalogs */ +#ifndef _HALOFIELD_H +#define _HALOFIELD_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +int ComputeHaloField(float redshift_desc, float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + InitialConditions *boxes, unsigned long long int random_seed, + HaloField * halos_desc, HaloField *halos); + +#endif diff --git a/src/py21cmfast/src/GenerateICs.c b/src/py21cmfast/src/InitialConditions.c similarity index 82% rename from src/py21cmfast/src/GenerateICs.c rename to src/py21cmfast/src/InitialConditions.c index 04a2ebce..7b39a9d4 100644 --- a/src/py21cmfast/src/GenerateICs.c +++ b/src/py21cmfast/src/InitialConditions.c @@ -1,51 +1,94 @@ -#include -#include -#include -#include -#include #include -#include -#include -//#include -#include +#include +#include +#include #include -#include -#include -#include +#include #include #include -#include -#include -#include -#include +#include -#include "21cmFAST.h" +#include "cexcept.h" #include "exceptions.h" #include "logger.h" #include "Constants.h" -#include "Globals.h" -#include "indexing.c" -#include "UsefulFunctions.c" -#include "interpolation.c" -#include "ps.c" -#include "photoncons.c" -#include "interp_tables.c" -#include "dft.c" -#include "PerturbField.c" -#include "bubble_helper_progs.c" -#include "elec_interp.c" -#include "heating_helper_progs.c" -#include "recombinations.c" -#include "IonisationBox.c" -#include "Stochasticity.c" -#include "HaloBox.c" -#include "SpinTemperatureBox.c" -#include "subcell_rsds.c" -#include "BrightnessTemperatureBox.c" -#include "FindHaloes.c" -#include "PerturbHaloField.c" - -void adj_complex_conj(fftwf_complex *HIRES_box, struct UserParams *user_params, struct CosmoParams *cosmo_params){ +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "debugging.h" +#include "indexing.h" +#include "dft.h" +#include "filtering.h" + +#include "InitialConditions.h" + +void seed_rng_threads(gsl_rng * rng_arr[], unsigned long long int seed){ + // setting tbe random seeds + gsl_rng * rseed = gsl_rng_alloc(gsl_rng_mt19937); // An RNG for generating seeds for multithreading + + gsl_rng_set(rseed, seed); + + unsigned int seeds[user_params_global->N_THREADS]; + + // For multithreading, seeds for the RNGs are generated from an initial RNG (based on the input random_seed) and then shuffled (Author: Fred Davies) + // int num_int = INT_MAX/16; + int num_int = INT_MAX/256; //JD: this was taking a few seconds per snapshot so i reduced the number TODO: init the RNG once + int i, thread_num; + unsigned int *many_ints = (unsigned int *)malloc((size_t)(num_int*sizeof(unsigned int))); // Some large number of possible integers + for (i=0; iN_THREADS, many_ints, num_int, sizeof(unsigned int)); // Populate the seeds array from the large list of integers + gsl_ran_shuffle(rseed, seeds, user_params_global->N_THREADS, sizeof(unsigned int)); // Shuffle the randomly selected integers + + int checker; + + checker = 0; + // seed the random number generators + for (thread_num = 0; thread_num < user_params_global->N_THREADS; thread_num++){ + switch (checker){ + case 0: + rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_mt19937); + gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); + break; + case 1: + rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_gfsr4); + gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); + break; + case 2: + rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_cmrg); + gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); + break; + case 3: + rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_mrg); + gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); + break; + case 4: + rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_taus2); + gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); + break; + } // end switch + + checker += 1; + + if(checker==5) { + checker = 0; + } + } + + gsl_rng_free(rseed); + free(many_ints); +} + +void free_rng_threads(gsl_rng * rng_arr[]){ + int ii; + for(ii=0;iiN_THREADS;ii++){ + gsl_rng_free(rng_arr[ii]); + } +} + +void adj_complex_conj(fftwf_complex *HIRES_box, UserParams *user_params, CosmoParams *cosmo_params){ /***** Adjust the complex conjugate relations for a real array *****/ int i, j, k; @@ -99,8 +142,8 @@ void adj_complex_conj(fftwf_complex *HIRES_box, struct UserParams *user_params, // Re-write of init.c for original 21cmFAST int ComputeInitialConditions( - unsigned long long random_seed, struct UserParams *user_params, - struct CosmoParams *cosmo_params, struct InitialConditions *boxes + unsigned long long random_seed, UserParams *user_params, + CosmoParams *cosmo_params, InitialConditions *boxes ){ // Generates the initial conditions: gaussian random density field (user_params->DIM^3) as well as the equal or lower resolution velocity fields, and smoothed density field (user_params->HII_DIM^3). @@ -114,13 +157,10 @@ int ComputeInitialConditions( // Makes the parameter structs visible to a variety of functions/macros // Do each time to avoid Python garbage collection issues - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); + Broadcast_struct_global_noastro(user_params,cosmo_params); - unsigned long long ct; - int n_x, n_y, n_z, i, j, k, ii, thread_num, dimension; + int n_x, n_y, n_z, i, j, k, ii, dimension; float k_x, k_y, k_z, k_mag, p, a, b, k_sq; - double pixel_deltax; float p_vcb, vcb_i; float f_pixel_factor; @@ -144,11 +184,6 @@ int ComputeInitialConditions( fftwf_complex *HIRES_box = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); fftwf_complex *HIRES_box_saved = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); - // allocate array for the k-space and real-space boxes for vcb - fftwf_complex *HIRES_box_vcb_saved; - // HIRES_box_vcb_saved may be needed if FFTW_Wisdom doesn't exist -- currently unused - // but I am not going to allocate it until I am certain I needed it. - // find factor of HII pixel size / deltax pixel size f_pixel_factor = user_params->DIM/(float)user_params->HII_DIM; @@ -161,6 +196,7 @@ int ComputeInitialConditions( #pragma omp parallel shared(HIRES_box,r) \ private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,p,a,b,p_vcb) num_threads(user_params->N_THREADS) { + int thread_num = omp_get_thread_num(); #pragma omp for for (n_x=0; n_xDIM; n_x++){ // convert index to numerical value for this component of the k-mode: k = (2*pi/L) * n @@ -193,8 +229,8 @@ int ComputeInitialConditions( b = -1.0; } else { - a = gsl_ran_ugaussian(r[omp_get_thread_num()]); - b = gsl_ran_ugaussian(r[omp_get_thread_num()]); + a = gsl_ran_ugaussian(r[thread_num]); + b = gsl_ran_ugaussian(r[thread_num]); } HIRES_box[C_INDEX(n_x, n_y, n_z)] = sqrt(VOLUME*p/2.0) * (a + b*I); @@ -259,102 +295,89 @@ int ComputeInitialConditions( // ******* Relative Velocity part ******* // - if(user_params->USE_RELATIVE_VELOCITIES){ + if(user_params->USE_RELATIVE_VELOCITIES){ //JBM: We use the memory allocated to HIRES_box as it's free. - - for(ii=0;ii<3;ii++) { - - memcpy(HIRES_box, HIRES_box_saved, sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); - -#pragma omp parallel shared(HIRES_box,ii) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,p,p_vcb) num_threads(user_params->N_THREADS) - { -#pragma omp for - for (n_x=0; n_xDIM; n_x++){ - if (n_x>MIDDLE) - k_x =(n_x-user_params->DIM) * DELTA_K; // wrap around for FFT convention - else - k_x = n_x * DELTA_K; - - for (n_y=0; n_yDIM; n_y++){ - if (n_y>MIDDLE) - k_y =(n_y-user_params->DIM) * DELTA_K; + for(ii=0;ii<3;ii++) { + memcpy(HIRES_box, HIRES_box_saved, sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); + #pragma omp parallel shared(HIRES_box,ii) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,p,p_vcb) num_threads(user_params->N_THREADS) + { + #pragma omp for + for (n_x=0; n_xDIM; n_x++){ + if (n_x>MIDDLE) + k_x =(n_x-user_params->DIM) * DELTA_K; // wrap around for FFT convention else - k_y = n_y * DELTA_K; + k_x = n_x * DELTA_K; - for (n_z=0; n_z<=MIDDLE_PARA; n_z++){ - k_z = n_z * DELTA_K_PARA; + for (n_y=0; n_yDIM; n_y++){ + if (n_y>MIDDLE) + k_y =(n_y-user_params->DIM) * DELTA_K; + else + k_y = n_y * DELTA_K; - k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); - p = power_in_k(k_mag); - p_vcb = power_in_vcb(k_mag); + for (n_z=0; n_z<=MIDDLE_PARA; n_z++){ + k_z = n_z * DELTA_K_PARA; + k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); + p = power_in_k(k_mag); + p_vcb = power_in_vcb(k_mag); - // now set the velocities - if ((n_x==0) && (n_y==0) && (n_z==0)){ // DC mode - HIRES_box[0] = 0; - } - else{ - if(ii==0) { - HIRES_box[C_INDEX(n_x,n_y,n_z)] *= I * k_x/k_mag * sqrt(p_vcb/p) * C_KMS; - } - if(ii==1) { - HIRES_box[C_INDEX(n_x,n_y,n_z)] *= I * k_y/k_mag * sqrt(p_vcb/p) * C_KMS; + // now set the velocities + if ((n_x==0) && (n_y==0) && (n_z==0)){ // DC mode + HIRES_box[0] = 0; } - if(ii==2) { - HIRES_box[C_INDEX(n_x,n_y,n_z)] *= I * k_z/k_mag * sqrt(p_vcb/p) * C_KMS; + else{ + if(ii==0) { + HIRES_box[C_INDEX(n_x,n_y,n_z)] *= I * k_x/k_mag * sqrt(p_vcb/p) * C_KMS; + } + if(ii==1) { + HIRES_box[C_INDEX(n_x,n_y,n_z)] *= I * k_y/k_mag * sqrt(p_vcb/p) * C_KMS; + } + if(ii==2) { + HIRES_box[C_INDEX(n_x,n_y,n_z)] *= I * k_z/k_mag * sqrt(p_vcb/p) * C_KMS; + } } } } } } - } - - -//we only care about the lowres vcb box, so we filter it directly. - if (user_params->DIM != user_params->HII_DIM) { - filter_box(HIRES_box, 0, 0, L_FACTOR*user_params->BOX_LEN/(user_params->HII_DIM+0.0)); - } - -//fft each velocity component back to real space - dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->DIM, D_PARA, user_params->N_THREADS, HIRES_box); + //we only care about the lowres vcb box, so we filter it directly. + if (user_params->DIM != user_params->HII_DIM) { + filter_box(HIRES_box, 0, 0, L_FACTOR*user_params->BOX_LEN/(user_params->HII_DIM+0.0)); + } - #pragma omp parallel shared(boxes,HIRES_box,f_pixel_factor,ii) private(i,j,k,vcb_i) num_threads(user_params->N_THREADS) - { - #pragma omp for - for (i=0; iHII_DIM; i++){ - for (j=0; jHII_DIM; j++){ - for (k=0; klowres_vcb[HII_R_INDEX(i,j,k)] += vcb_i*vcb_i; - } - } - } - } - - - } - + //fft each velocity component back to real space + dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->DIM, D_PARA, user_params->N_THREADS, HIRES_box); -//now we take the sqrt of that and normalize the FFT - for (i=0; iHII_DIM; i++){ - for (j=0; jHII_DIM; j++){ - for (k=0; klowres_vcb[HII_R_INDEX(i,j,k)] = sqrt(boxes->lowres_vcb[HII_R_INDEX(i,j,k)])/VOLUME; + #pragma omp parallel shared(boxes,HIRES_box,f_pixel_factor,ii) private(i,j,k,vcb_i) num_threads(user_params->N_THREADS) + { + #pragma omp for + for (i=0; iHII_DIM; i++){ + for (j=0; jHII_DIM; j++){ + for (k=0; klowres_vcb[HII_R_INDEX(i,j,k)] += vcb_i*vcb_i; + } + } + } + } + } + //now we take the sqrt of that and normalize the FFT + for (i=0; iHII_DIM; i++){ + for (j=0; jHII_DIM; j++){ + for (k=0; klowres_vcb[HII_R_INDEX(i,j,k)] = sqrt(boxes->lowres_vcb[HII_R_INDEX(i,j,k)])/VOLUME; + } } } - } - } + } LOG_DEBUG("Completed Relative velocities."); // ******* End of Relative Velocity part ******* // - - - // Now look at the velocities for(ii=0;ii<3;ii++) { @@ -645,20 +668,24 @@ int ComputeInitialConditions( (unsigned long long)(j), (unsigned long long)(k))); } - if(phi_component==1) { + else if(phi_component==1) { component_ii = boxes->hires_vx_2LPT[R_INDEX(i,j,k)]; component_jj = boxes->hires_vz_2LPT[R_INDEX(i,j,k)]; component_ij = *((float *)phi_1 + R_FFT_INDEX((unsigned long long)(i), (unsigned long long)(j), (unsigned long long)(k))); } - if(phi_component==2) { + else if(phi_component==2) { component_ii = boxes->hires_vy_2LPT[R_INDEX(i,j,k)]; component_jj = boxes->hires_vz_2LPT[R_INDEX(i,j,k)]; component_ij = *((float *)phi_1 + R_FFT_INDEX((unsigned long long)(i), (unsigned long long)(j), (unsigned long long)(k))); } + else{ + LOG_ERROR("Invalid phi component?"); + Throw(ValueError); + } // Kept in this form to maintain similar (possible) rounding errors *((float *)HIRES_box + R_FFT_INDEX((unsigned long long)(i), diff --git a/src/py21cmfast/src/InitialConditions.h b/src/py21cmfast/src/InitialConditions.h new file mode 100644 index 00000000..181bd086 --- /dev/null +++ b/src/py21cmfast/src/InitialConditions.h @@ -0,0 +1,17 @@ + +#ifndef _INITCONDITIONS_H +#define _INITCONDITIONS_H + +#include "InputParameters.h" +#include "OutputStructs.h" +#include + +int ComputeInitialConditions( + unsigned long long random_seed, UserParams *user_params, + CosmoParams *cosmo_params, InitialConditions *boxes +); + +void seed_rng_threads(gsl_rng * rng_arr[], unsigned long long int seed); +void free_rng_threads(gsl_rng * rng_arr[]); + +#endif diff --git a/src/py21cmfast/src/InputParameters.c b/src/py21cmfast/src/InputParameters.c new file mode 100644 index 00000000..b49c4df0 --- /dev/null +++ b/src/py21cmfast/src/InputParameters.c @@ -0,0 +1,91 @@ +#include "InputParameters.h" + +void Broadcast_struct_global_all(UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options){ + user_params_global = user_params; + cosmo_params_global = cosmo_params; + astro_params_global = astro_params; + flag_options_global = flag_options; +} + +void Broadcast_struct_global_noastro(UserParams *user_params, CosmoParams *cosmo_params){ + user_params_global = user_params; + cosmo_params_global = cosmo_params; +} + +/*GLOBAL INPUT STRUCT DEFINITION*/ +UserParams *user_params_global; +CosmoParams *cosmo_params_global; +AstroParams *astro_params_global; +FlagOptions *flag_options_global; + +GlobalParams global_params = { + .ALPHA_UVB = 5.0, + .EVOLVE_DENSITY_LINEARLY = 0, + .SMOOTH_EVOLVED_DENSITY_FIELD = 0, + .R_smooth_density = 0.2, + .HII_ROUND_ERR = 1e-5, + .FIND_BUBBLE_ALGORITHM = 2, + .N_POISSON = 5, + .T_USE_VELOCITIES = 1, + .MAX_DVDR = 0.2, + .DELTA_R_HII_FACTOR = 1.1, + .DELTA_R_FACTOR = 1.1, + .HII_FILTER = 0, + .INITIAL_REDSHIFT = 300., + .R_OVERLAP_FACTOR = 1., + .DELTA_CRIT_MODE = 1, + .HALO_FILTER = 0, + .OPTIMIZE = 0, + .OPTIMIZE_MIN_MASS = 1e11, + + + .CRIT_DENS_TRANSITION = 1.5, + .MIN_DENSITY_LOW_LIMIT = 9e-8, + + .RecombPhotonCons = 0, + .PhotonConsStart = 0.995, + .PhotonConsEnd = 0.3, + .PhotonConsAsymptoteTo = 0.01, + .PhotonConsEndCalibz = 3.5, + .PhotonConsSmoothing = 1, + + .HEAT_FILTER = 0, + .CLUMPING_FACTOR = 2., + .Z_HEAT_MAX = 35.0, + .R_XLy_MAX = 500., + .NUM_FILTER_STEPS_FOR_Ts = 40, + .ZPRIME_STEP_FACTOR = 1.02, + .TK_at_Z_HEAT_MAX = -1, + .XION_at_Z_HEAT_MAX = -1, + .Pop = 2, + .Pop2_ion = 5000, + .Pop3_ion = 44021, + + .NU_X_BAND_MAX = 2000.0, + .NU_X_MAX = 10000.0, + + .NBINS_LF = 100, + + .P_CUTOFF = 0, + .M_WDM = 2, + .g_x = 1.5, + .OMn = 0.0, + .OMk = 0.0, + .OMr = 8.6e-5, + .OMtot = 1.0, + .Y_He = 0.245, + .wl = -1.0, + .SHETH_b = 0.15, //In the literature this is 0.485 (RM08) or 0.5 (SMT01) or 0.34 (Barkana+01) Master 21cmFAST currently has 0.15 + .SHETH_c = 0.05, //In the literature this is 0.615 (RM08) or 0.6 (SMT01) or 0.81 (Barkana+01) Master 21cmFAST currently has 0.05 + .Zreion_HeII = 3.0, + .FILTER = 0, + .R_BUBBLE_MIN = 0.620350491, + .M_MIN_INTEGRAL = 1e5, + .M_MAX_INTEGRAL = 1e16, + + .T_RE = 2e4, + + .VAVG=25.86, + + .USE_ADIABATIC_FLUCTUATIONS = 1, +}; diff --git a/src/py21cmfast/src/InputParameters.h b/src/py21cmfast/src/InputParameters.h new file mode 100644 index 00000000..b4c10419 --- /dev/null +++ b/src/py21cmfast/src/InputParameters.h @@ -0,0 +1,12 @@ +#ifndef _PARAMSTRUCTURES_H +#define _PARAMSTRUCTURES_H + +#include +//since ffi.cdef() cannot include directives, we store the types and globals in another file +// Since it is unguarded, make sure to ONLY include this file from here +#include "_inputparams_wrapper.h" + +void Broadcast_struct_global_all(UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options); +void Broadcast_struct_global_noastro(UserParams *user_params, CosmoParams *cosmo_params); + +#endif diff --git a/src/py21cmfast/src/IonisationBox.c b/src/py21cmfast/src/IonisationBox.c index bc4a8887..06adf42f 100644 --- a/src/py21cmfast/src/IonisationBox.c +++ b/src/py21cmfast/src/IonisationBox.c @@ -1,4 +1,36 @@ // Re-write of find_HII_bubbles.c for being accessible within the MCMC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "hmf.h" +#include "indexing.h" +#include "dft.h" +#include "recombinations.h" +#include "debugging.h" +#include "heating_helper_progs.h" +#include "photoncons.h" +#include "thermochem.h" +#include "interp_tables.h" +#include "filtering.h" +#include "bubble_helper_progs.h" + +#include "IonisationBox.h" + +#define LOG10_MTURN_MAX ((double)(10)) //maximum mturn limit enforced on grids int INIT_ERFC_INTERPOLATION = 1; int INIT_RECOMBINATIONS = 1; @@ -9,15 +41,15 @@ float absolute_delta_z; float thistk; -int ComputeIonizedBox(float redshift, float prev_redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, - struct PerturbedField *perturbed_field, - struct PerturbedField *previous_perturbed_field, - struct IonizedBox *previous_ionize_box, - struct TsBox *spin_temp, - struct HaloBox *halos, - struct InitialConditions *ini_boxes, - struct IonizedBox *box) { +int ComputeIonizedBox(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + PerturbedField *perturbed_field, + PerturbedField *previous_perturbed_field, + IonizedBox *previous_ionize_box, + TsBox *spin_temp, + HaloBox *halos, + InitialConditions *ini_boxes, + IonizedBox *box) { int status; @@ -33,38 +65,31 @@ int ComputeIonizedBox(float redshift, float prev_redshift, struct UserParams *us // Makes the parameter structs visible to a variety of functions/macros // Do each time to avoid Python garbage collection issues - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); - Broadcast_struct_global_IT(user_params,cosmo_params,astro_params,flag_options); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); omp_set_num_threads(user_params->N_THREADS); // Other parameters used in the code - int i,j,k,x,y,z, LAST_FILTER_STEP, first_step_R, short_completely_ionised,i_halo; + int i,j,k,x,y,z, LAST_FILTER_STEP, first_step_R; int counter, N_halos_in_cell; unsigned long long ct; float growth_factor, pixel_mass, cell_length_factor, M_MIN, prev_growth_factor; - float erfc_denom, erfc_denom_cell, res_xH, Splined_Fcoll, xHII_from_xrays, curr_dens, massofscaleR, ION_EFF_FACTOR, growth_factor_dz; + float erfc_denom, res_xH, Splined_Fcoll, xHII_from_xrays, curr_dens, massofscaleR, ION_EFF_FACTOR; float Splined_Fcoll_MINI, prev_dens, ION_EFF_FACTOR_MINI, prev_Splined_Fcoll, prev_Splined_Fcoll_MINI; - float ave_M_coll_cell, ave_N_min_cell, pixel_volume, density_over_mean; + float ave_M_coll_cell, ave_N_min_cell; double lnMmin,lnMmax,lnM_cond; float curr_vcb; - double global_xH, ST_over_PS, f_coll, R, stored_R, f_coll_min, f_total; + double global_xH, ST_over_PS, f_coll, R, stored_R, f_coll_min; double ST_over_PS_MINI, f_coll_MINI, f_coll_min_MINI; double t_ast, Gamma_R_prefactor, rec, dNrec, sigmaMmax; double Gamma_R_prefactor_MINI; float fabs_dtdz, ZSTEP, z_eff; - const float dz = 0.01; - float dens_val, prev_dens_val; - - int overdense_int,status_int; int something_finite_or_infinite = 0; - int log10_Mturnover_MINI_int, log10_Mturnover_int; int *overdense_int_boundexceeded_threaded = calloc(user_params->N_THREADS,sizeof(int)); double ave_log10_Mturnover, ave_log10_Mturnover_MINI; @@ -79,12 +104,11 @@ int ComputeIonizedBox(float redshift, float prev_redshift, struct UserParams *us float min_density, max_density; float prev_min_density, prev_max_density; - double density_bin_width, prev_density_bin_width; float stored_redshift, adjustment_factor; - float log10Mturn_min, log10Mturn_max, log10Mturn_bin_width, log10Mturn_bin_width_inv; - float log10Mturn_min_MINI, log10Mturn_max_MINI, log10Mturn_bin_width_MINI, log10Mturn_bin_width_inv_MINI; + float log10Mturn_min, log10Mturn_max; + float log10Mturn_min_MINI, log10Mturn_max_MINI; gsl_rng * r[user_params->N_THREADS]; @@ -100,8 +124,6 @@ LOG_SUPER_DEBUG("inited heat"); LOG_SUPER_DEBUG("defined parameters"); - pixel_volume = pow(user_params->BOX_LEN/((float)(user_params->HII_DIM)), 3); - double alpha_esc_var = astro_params->ALPHA_ESC; double norm_esc_var = astro_params->F_ESC10; @@ -176,7 +198,6 @@ LOG_SUPER_DEBUG("defined parameters"); fabs_dtdz = fabs(dtdz(redshift))/1e15; //reduce to have good precision t_ast = astro_params->t_STAR * t_hubble(redshift); - growth_factor_dz = dicke(redshift-dz); // Modify the current sampled redshift to a redshift which matches the expected filling factor given our astrophysical parameterisation. // This is the photon non-conservation correction @@ -307,6 +328,7 @@ LOG_SUPER_DEBUG("erfc interpolation done"); i=0; // Newer setup to be performed in parallel + // TODO: examine this RNG model, seed properly with seed_rng_threads() from ICs int thread_num; for(thread_num = 0; thread_num < user_params->N_THREADS; thread_num++){ // Original defaults with gsl_rng_mt19937 and SEED = 0, thus start with this and iterate for all other threads by their thread number @@ -531,7 +553,7 @@ LOG_SUPER_DEBUG("sigma table has been initialised"); global_xH = 0.0; if(user_params->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options->USE_MINI_HALOS && user_params->INTEGRATION_METHOD_MINI == 1)) - initialise_GL(NGL_INT, lnMmin, lnMmax); + initialise_GL(lnMmin, lnMmax); // Determine the normalisation for the excursion set algorithm if (flag_options->USE_MASS_DEPENDENT_ZETA) { @@ -715,11 +737,6 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f // ************************************************************************************* // // set the max radius we will use, making sure we are always sampling the same values of radius // (this avoids aliasing differences w redshift) - - short_completely_ionised = 0; - // loop through the filter radii (in Mpc) - erfc_denom_cell = 1; //dummy value - R=fmax(global_params.R_BUBBLE_MIN, (cell_length_factor*user_params->BOX_LEN/(float)user_params->HII_DIM)); while ((R - fmin(astro_params->R_BUBBLE_MAX, L_FACTOR * user_params->BOX_LEN)) <= FRACT_FLOAT_ERR) { @@ -737,8 +754,6 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f first_step_R = 1; - double R_temp = (double) (astro_params->R_BUBBLE_MAX); - counter = 0; while (!LAST_FILTER_STEP && (M_MIN < RtoM(R)) ){ @@ -833,7 +848,7 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f if (!flag_options->USE_HALO_FIELD) { if(user_params->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options->USE_MINI_HALOS && user_params->INTEGRATION_METHOD_MINI == 1)) - initialise_GL(NGL_INT, lnMmin, lnM_cond); + initialise_GL(lnMmin, lnM_cond); if (flag_options->USE_MASS_DEPENDENT_ZETA) { min_density = max_density = 0.0; @@ -923,11 +938,6 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f log10Mturn_max = log10Mturn_max *1.01; log10Mturn_min_MINI = log10Mturn_min_MINI *0.99; log10Mturn_max_MINI = log10Mturn_max_MINI *1.01; - - log10Mturn_bin_width = (log10Mturn_max - log10Mturn_min) / (float)(NMTURN-1); - log10Mturn_bin_width_inv = 1./log10Mturn_bin_width; - log10Mturn_bin_width_MINI = (log10Mturn_max_MINI - log10Mturn_min_MINI) / (float)(NMTURN-1); - log10Mturn_bin_width_inv_MINI = 1./log10Mturn_bin_width_MINI; } } @@ -940,8 +950,6 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f max_density += 0.001; prev_min_density -= 0.001; prev_max_density += 0.001; - density_bin_width = (max_density - min_density)/((double)NDELTA-1); - prev_density_bin_width = (prev_max_density - prev_min_density)/((double)NDELTA-1); initialise_Nion_Conditional_spline(redshift,Mcrit_atom,min_density,max_density,M_MIN,massofscaleR,massofscaleR, log10Mturn_min,log10Mturn_max,log10Mturn_min_MINI,log10Mturn_max_MINI, astro_params->ALPHA_STAR, astro_params->ALPHA_STAR_MINI, @@ -960,9 +968,6 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f astro_params->F_ESC7_MINI,Mlim_Fstar_MINI, Mlim_Fesc_MINI, user_params->INTEGRATION_METHOD_ATOMIC, user_params->INTEGRATION_METHOD_MINI, flag_options->USE_MINI_HALOS,true); - LOG_SUPER_DEBUG("Tb midpoints %.4e %.4e %.4e %.4e",Nion_conditional_table2D.z_arr[NDELTA/2][NMTURN/2], - Nion_conditional_table_MINI.z_arr[NDELTA/2][NMTURN/2],Nion_conditional_table_prev.z_arr[NDELTA/2][NMTURN/2], - Nion_conditional_table_MINI_prev.z_arr[NDELTA/2][NMTURN/2]); } } } @@ -986,13 +991,12 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f // renormalize the collapse fraction so that the mean matches ST, // since we are using the evolved (non-linear) density field #pragma omp parallel shared(deltax_filtered,N_rec_filtered,xe_filtered,overdense_int_boundexceeded_threaded,erfc_denom,erfc_arg_min,\ - erfc_arg_max,InvArgBinWidth,ArgBinWidth,ERFC_VALS_DIFF,ERFC_VALS,log10_Mturnover_filtered,log10Mturn_min,log10Mturn_bin_width_inv, \ - log10_Mturnover_MINI_filtered,log10Mturn_bin_width_inv_MINI,prev_deltax_filtered,previous_ionize_box,ION_EFF_FACTOR,\ - box,counter,stars_filtered,massofscaleR,pixel_volume,sigmaMmax,\ + erfc_arg_max,InvArgBinWidth,ArgBinWidth,ERFC_VALS_DIFF,ERFC_VALS,log10_Mturnover_filtered,log10Mturn_min, \ + log10_Mturnover_MINI_filtered,prev_deltax_filtered,previous_ionize_box,ION_EFF_FACTOR,\ + box,counter,stars_filtered,massofscaleR,sigmaMmax,\ M_MIN,growth_factor,Mlim_Fstar,Mlim_Fesc,Mcrit_atom,Mlim_Fstar_MINI,Mlim_Fesc_MINI,prev_growth_factor) \ - private(x,y,z,curr_dens,Splined_Fcoll,Splined_Fcoll_MINI,dens_val,overdense_int,erfc_arg_val,erfc_arg_val_index,log10_Mturnover,\ - log10_Mturnover_int,log10_Mturnover_MINI,log10_Mturnover_MINI_int,prev_dens,prev_Splined_Fcoll,prev_Splined_Fcoll_MINI,\ - prev_dens_val,density_over_mean,status_int) \ + private(x,y,z,curr_dens,Splined_Fcoll,Splined_Fcoll_MINI,erfc_arg_val,erfc_arg_val_index,log10_Mturnover,\ + log10_Mturnover_MINI,prev_dens,prev_Splined_Fcoll,prev_Splined_Fcoll_MINI) \ num_threads(user_params->N_THREADS) { #pragma omp for reduction(+:f_coll,f_coll_MINI) @@ -1168,8 +1172,6 @@ LOG_SUPER_DEBUG("excursion set normalisation, mean_f_coll_MINI: %e", box->mean_f LOG_SUPER_DEBUG("global mean fcoll %.4e box mean fcoll %.4e ratio %.4e",box->mean_f_coll,f_coll,ST_over_PS); LOG_SUPER_DEBUG("MINI: global mean fcoll %.4e box mean fcoll %.4e ratio %.4e",box->mean_f_coll_MINI,f_coll_MINI,ST_over_PS_MINI); } - //saved for debug - f_total = f_coll; Gamma_R_prefactor = (R*CMperMPC) * SIGMA_HI * global_params.ALPHA_UVB / (global_params.ALPHA_UVB+2.75) * N_b0 * ION_EFF_FACTOR / 1.0e-12; Gamma_R_prefactor_MINI = (R*CMperMPC) * SIGMA_HI * global_params.ALPHA_UVB / (global_params.ALPHA_UVB+2.75) * N_b0 * ION_EFF_FACTOR_MINI / 1.0e-12; diff --git a/src/py21cmfast/src/IonisationBox.h b/src/py21cmfast/src/IonisationBox.h new file mode 100644 index 00000000..302e69fa --- /dev/null +++ b/src/py21cmfast/src/IonisationBox.h @@ -0,0 +1,14 @@ +#ifndef _IONBOX_H +#define _IONBOX_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +int ComputeIonizedBox(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + PerturbedField *perturbed_field, PerturbedField *previous_perturbed_field, + IonizedBox *previous_ionize_box, TsBox *spin_temp, + HaloBox *halos, InitialConditions *ini_boxes, + IonizedBox *box); + +#endif diff --git a/src/py21cmfast/src/LuminosityFunction.c b/src/py21cmfast/src/LuminosityFunction.c new file mode 100644 index 00000000..944b7ad2 --- /dev/null +++ b/src/py21cmfast/src/LuminosityFunction.c @@ -0,0 +1,263 @@ +// Luv/SFR = 1 / 1.15 x 10^-28 [M_solar yr^-1/erg s^-1 Hz^-1] +// G. Sun and S. R. Furlanetto (2016) MNRAS, 417, 33 + + +#include +#include +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "interp_tables.h" +#include "thermochem.h" +#include "hmf.h" + +#include "LuminosityFunction.h" + +#define Luv_over_SFR (double)(1./1.15/1e-28) + +#define delta_lnMhalo (double)(5e-6) +#define Mhalo_min (double)(1e6) +#define Mhalo_max (double)(1e16) + +bool initialised_ComputeLF = false; + +gsl_interp_accel *LF_spline_acc; +gsl_spline *LF_spline; + +gsl_interp_accel *deriv_spline_acc; +gsl_spline *deriv_spline; + +double *lnMhalo_param, *Muv_param, *Mhalo_param; +double *log10phi, *M_uv_z, *M_h_z; +double *deriv, *lnM_temp, *deriv_temp; + +int initialise_ComputeLF(int nbins, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options) { + + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); + + lnMhalo_param = calloc(nbins,sizeof(double)); + Muv_param = calloc(nbins,sizeof(double)); + Mhalo_param = calloc(nbins,sizeof(double)); + + LF_spline_acc = gsl_interp_accel_alloc(); + LF_spline = gsl_spline_alloc(gsl_interp_cspline, nbins); + + init_ps(); + + int status; + Try initialiseSigmaMInterpTable(0.999*Mhalo_min,1.001*Mhalo_max); + Catch(status) { + LOG_ERROR("\t...called from initialise_ComputeLF"); + return(status); + } + + initialised_ComputeLF = true; + return(0); +} + +void cleanup_ComputeLF(){ + free(lnMhalo_param); + free(Muv_param); + free(Mhalo_param); + gsl_spline_free (LF_spline); + gsl_interp_accel_free(LF_spline_acc); + freeSigmaMInterpTable(); + initialised_ComputeLF = 0; +} + +int ComputeLF(int nbins, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, + FlagOptions *flag_options, int component, int NUM_OF_REDSHIFT_FOR_LF, float *z_LF, float *M_TURNs, double *M_uv_z, double *M_h_z, double *log10phi) { + /* + This is an API-level function and thus returns an int status. + */ + int status; + Try{ // This try block covers the whole function. + // This NEEDS to be done every time, because the actual object passed in as + // user_params, cosmo_params etc. can change on each call, freeing up the memory. + initialise_ComputeLF(nbins, user_params,cosmo_params,astro_params,flag_options); + + int i,i_z; + int i_unity, i_smth, mf, nbins_smth=7; + double dlnMhalo, lnMhalo_i, SFRparam, Muv_1, Muv_2, dMuvdMhalo; + double Mhalo_i, lnMhalo_min, lnMhalo_max, lnMhalo_lo, lnMhalo_hi, dlnM, growthf; + double f_duty_upper, Mcrit_atom; + float Fstar, Fstar_temp; + double dndm; + int gsl_status; + + gsl_set_error_handler_off(); + if (astro_params->ALPHA_STAR < -0.5) + LOG_WARNING( + "ALPHA_STAR is %f, which is unphysical value given the observational LFs.\n"\ + "Also, when ALPHA_STAR < -.5, LFs may show a kink. It is recommended to set ALPHA_STAR > -0.5.", + astro_params->ALPHA_STAR + ); + + mf = user_params->HMF; + + lnMhalo_min = log(Mhalo_min*0.999); + lnMhalo_max = log(Mhalo_max*1.001); + dlnMhalo = (lnMhalo_max - lnMhalo_min)/(double)(nbins - 1); + + for (i_z=0; i_zF_STAR10*pow(Mhalo_i/1e10,astro_params->ALPHA_STAR); + else + Fstar = astro_params->F_STAR7_MINI*pow(Mhalo_i/1e7,astro_params->ALPHA_STAR_MINI); + if (Fstar > 1.) Fstar = 1; + + if (i_unity < 0) { // Find the array number at which Fstar crosses unity. + if (astro_params->ALPHA_STAR > 0.) { + if ( (1.- Fstar) < FRACT_FLOAT_ERR ) i_unity = i; + } + else if (astro_params->ALPHA_STAR < 0. && i < nbins-1) { + if (component == 1) + Fstar_temp = astro_params->F_STAR10*pow( exp(lnMhalo_min + dlnMhalo*(double)(i+1))/1e10,astro_params->ALPHA_STAR); + else + Fstar_temp = astro_params->F_STAR7_MINI*pow( exp(lnMhalo_min + dlnMhalo*(double)(i+1))/1e7,astro_params->ALPHA_STAR_MINI); + if (Fstar_temp < 1. && (1.- Fstar) < FRACT_FLOAT_ERR) i_unity = i; + } + } + + // parametrization of SFR + SFRparam = Mhalo_i * cosmo_params->OMb/cosmo_params->OMm * (double)Fstar * (double)(hubble(z_LF[i_z])*SperYR/astro_params->t_STAR); // units of M_solar/year + + Muv_param[i] = 51.63 - 2.5*log10(SFRparam*Luv_over_SFR); // UV magnitude + // except if Muv value is nan or inf, but avoid error put the value as 10. + if ( isinf(Muv_param[i]) || isnan(Muv_param[i]) ) Muv_param[i] = 10.; + + M_uv_z[i + i_z*nbins] = Muv_param[i]; + } + + gsl_status = gsl_spline_init(LF_spline, lnMhalo_param, Muv_param, nbins); + CATCH_GSL_ERROR(gsl_status); + + lnMhalo_lo = log(Mhalo_min); + lnMhalo_hi = log(Mhalo_max); + dlnM = (lnMhalo_hi - lnMhalo_lo)/(double)(nbins - 1); + + // There is a kink on LFs at which Fstar crosses unity. This kink is a numerical artefact caused by the derivate of dMuvdMhalo. + // Most of the cases the kink doesn't appear in magnitude ranges we are interested (e.g. -22 < Muv < -10). However, for some extreme + // parameters, it appears. To avoid this kink, we use the interpolation of the derivate in the range where the kink appears. + // 'i_unity' is the array number at which the kink appears. 'i_unity-3' and 'i_unity+12' are related to the range of interpolation, + // which is an arbitrary choice. + // NOTE: This method does NOT work in cases with ALPHA_STAR < -0.5. But, this parameter range is unphysical given that the + // observational LFs favour positive ALPHA_STAR in this model. + // i_smth = 0: calculates LFs without interpolation. + // i_smth = 1: calculates LFs using interpolation where Fstar crosses unity. + if (i_unity-3 < 0) i_smth = 0; + else if (i_unity+12 > nbins-1) i_smth = 0; + else i_smth = 1; + if (i_smth == 0) { + for (i=0; iOMm*RHOcrit)) + * f_duty_upper / fabs(dMuvdMhalo) ); + + if (isinf(log10phi[i + i_z*nbins]) || isnan(log10phi[i + i_z*nbins]) || log10phi[i + i_z*nbins] < -30.) + log10phi[i + i_z*nbins] = -30.; + } + } + else { + lnM_temp = calloc(nbins_smth,sizeof(double)); + deriv_temp = calloc(nbins_smth,sizeof(double)); + deriv = calloc(nbins,sizeof(double)); + + for (i=0; i +#include +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "indexing.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "dft.h" +#include "debugging.h" +#include "filtering.h" + +#include "PerturbField.h" + void compute_perturbed_velocities( unsigned short axis, - struct UserParams *user_params, + UserParams *user_params, fftwf_complex *HIRES_density_perturb, fftwf_complex *HIRES_density_perturb_saved, fftwf_complex *LOWRES_density_perturb, @@ -14,8 +35,8 @@ void compute_perturbed_velocities( ){ float k_x, k_y, k_z, k_sq; - int n_x, n_y, n_z; - int i,j,k; + unsigned long long int n_x, n_y, n_z; + unsigned long long int i,j,k; float kvec[3]; @@ -137,8 +158,8 @@ void compute_perturbed_velocities( } int ComputePerturbField( - float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct InitialConditions *boxes, struct PerturbedField *perturbed_field + float redshift, UserParams *user_params, CosmoParams *cosmo_params, + InitialConditions *boxes, PerturbedField *perturbed_field ){ /* ComputePerturbField uses the first-order Langragian displacement field to move the @@ -153,8 +174,7 @@ int ComputePerturbField( // Makes the parameter structs visible to a variety of functions/macros // Do each time to avoid Python garbage collection issues - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); + Broadcast_struct_global_noastro(user_params,cosmo_params); omp_set_num_threads(user_params->N_THREADS); @@ -164,9 +184,8 @@ int ComputePerturbField( float growth_factor, displacement_factor_2LPT, init_growth_factor, init_displacement_factor_2LPT; double xf, yf, zf; float mass_factor, dDdt, f_pixel_factor, velocity_displacement_factor, velocity_displacement_factor_2LPT; - unsigned long long ct, HII_i, HII_j, HII_k; + unsigned long long HII_i, HII_j, HII_k; int i,j,k,xi, yi, zi, dimension, switch_mid; - double ave_delta, new_ave_delta; // Variables to perform cloud in cell re-distribution of mass for the perturbed field int xp1,yp1,zp1; @@ -683,8 +702,7 @@ int ComputePerturbField( // **** Convert to velocities ***** // LOG_DEBUG("Generate velocity fields"); - float k_x, k_y, k_z, k_sq, dDdt_over_D; - int n_x, n_y, n_z; + float dDdt_over_D; dDdt_over_D = dDdt/growth_factor; diff --git a/src/py21cmfast/src/PerturbField.h b/src/py21cmfast/src/PerturbField.h new file mode 100644 index 00000000..2a2da997 --- /dev/null +++ b/src/py21cmfast/src/PerturbField.h @@ -0,0 +1,12 @@ +#ifndef _PERTURBFIELD_H +#define _PERTURBFIELD_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +int ComputePerturbField( + float redshift, UserParams *user_params, CosmoParams *cosmo_params, + InitialConditions *boxes, PerturbedField *perturbed_field +); + +#endif diff --git a/src/py21cmfast/src/PerturbHaloField.c b/src/py21cmfast/src/PerturbHaloField.c index 8f2a9035..62613737 100644 --- a/src/py21cmfast/src/PerturbHaloField.c +++ b/src/py21cmfast/src/PerturbHaloField.c @@ -4,9 +4,26 @@ // ComputePerturbHaloField reads in the linear velocity field, and uses // it to update halo locations with a corresponding displacement field -int ComputePerturbHaloField(float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, - struct InitialConditions *boxes, struct HaloField *halos, struct PerturbHaloField *halos_perturbed) { +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "indexing.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "debugging.h" +#include "cosmology.h" + +#include "PerturbHaloField.h" + +int ComputePerturbHaloField(float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + InitialConditions *boxes, HaloField *halos, PerturbHaloField *halos_perturbed) { int status; @@ -23,15 +40,13 @@ LOG_DEBUG("redshift=%f", redshift); // Makes the parameter structs visible to a variety of functions/macros // Do each time to avoid Python garbage collection issues - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); omp_set_num_threads(user_params->N_THREADS); - float growth_factor, displacement_factor_2LPT, mass, xf, yf, zf, z, growth_factor_over_BOX_LEN,displacement_factor_2LPT_over_BOX_LEN; - int i, j, k, xi, yi, zi, DI, dimension; - unsigned long long ct, i_halo; - float dz = 1e-10; + float growth_factor, displacement_factor_2LPT, xf, yf, zf, growth_factor_over_BOX_LEN,displacement_factor_2LPT_over_BOX_LEN; + unsigned long long int i, j, k, DI, dimension; + unsigned long long i_halo; LOG_DEBUG("Begin Initialisation"); @@ -112,17 +127,10 @@ LOG_DEBUG("Begin Initialisation"); // ************************************************************************* // // END 2LPT PART // // ************************************************************************* // - - unsigned long long n_halos; - halos_perturbed->n_halos = halos->n_halos; // ****************** END INITIALIZATION ******************************** // - float mean_correction = 0.0, mean_correction_2LPT = 0.0, mean_ratio = 0.0; - float max_correction = 1e-10, max_correction_2LPT = 1e-10, max_ratio = 1e-10; - int den = 0; - #pragma omp parallel shared(boxes,halos,halos_perturbed) \ private(i_halo,i,j,k,xf,yf,zf) num_threads(user_params->N_THREADS) { @@ -250,7 +258,7 @@ LOG_DEBUG("Begin Initialisation"); return(0); } -void free_phf(struct PerturbHaloField* halos){ +void free_phf(PerturbHaloField* halos){ LOG_DEBUG("Freeing PerturbHaloField"); free(halos->halo_masses); free(halos->halo_coords); diff --git a/src/py21cmfast/src/PerturbHaloField.h b/src/py21cmfast/src/PerturbHaloField.h new file mode 100644 index 00000000..dfc9b6c1 --- /dev/null +++ b/src/py21cmfast/src/PerturbHaloField.h @@ -0,0 +1,12 @@ +#ifndef _PTHALOS_H +#define _PTHALOS_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +int ComputePerturbHaloField(float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + InitialConditions *boxes, HaloField *halos, + PerturbHaloField *halos_perturbed); + +#endif diff --git a/src/py21cmfast/src/SpinTemperatureBox.c b/src/py21cmfast/src/SpinTemperatureBox.c index 23f24aa7..11d66796 100644 --- a/src/py21cmfast/src/SpinTemperatureBox.c +++ b/src/py21cmfast/src/SpinTemperatureBox.c @@ -1,9 +1,34 @@ // Re-write of find_HII_bubbles.c for being accessible within the MCMC - -void ts_main(float redshift, float prev_redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, float perturbed_field_redshift, short cleanup, - struct PerturbedField *perturbed_field, struct XraySourceBox *source_box, struct TsBox *previous_spin_temp, - struct InitialConditions *ini_boxes, struct TsBox *this_spin_temp); +#include +#include +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "indexing.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "heating_helper_progs.h" +#include "elec_interp.h" +#include "interp_tables.h" +#include "debugging.h" +#include "cosmology.h" +#include "hmf.h" +#include "dft.h" +#include "filtering.h" +#include "thermochem.h" + +#include "SpinTemperatureBox.h" + +void ts_main(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, float perturbed_field_redshift, short cleanup, + PerturbedField *perturbed_field, XraySourceBox *source_box, TsBox *previous_spin_temp, + InitialConditions *ini_boxes, TsBox *this_spin_temp); //Global arrays which have yet to be moved to structs //R x box arrays @@ -63,11 +88,11 @@ bool TsInterpArraysInitialised = false; //a debug flag for printing results from a single cell without passing cell number to the functions static int debug_printed; -int ComputeTsBox(float redshift, float prev_redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, +int ComputeTsBox(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, float perturbed_field_redshift, short cleanup, - struct PerturbedField *perturbed_field, struct XraySourceBox *source_box, struct TsBox *previous_spin_temp, - struct InitialConditions *ini_boxes, struct TsBox *this_spin_temp) { + PerturbedField *perturbed_field, XraySourceBox *source_box, TsBox *previous_spin_temp, + InitialConditions *ini_boxes, TsBox *this_spin_temp) { int status; Try{ // This Try{} wraps the whole function. LOG_DEBUG("input values:"); @@ -78,11 +103,7 @@ int ComputeTsBox(float redshift, float prev_redshift, struct UserParams *user_pa // Makes the parameter structs visible to a variety of functions/macros // Do each time to avoid Python garbage collection issues - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); - Broadcast_struct_global_HF(user_params,cosmo_params,astro_params,flag_options); - Broadcast_struct_global_TS(user_params,cosmo_params,astro_params,flag_options); - Broadcast_struct_global_IT(user_params,cosmo_params,astro_params,flag_options); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); omp_set_num_threads(user_params->N_THREADS); debug_printed = 0; @@ -98,21 +119,8 @@ int ComputeTsBox(float redshift, float prev_redshift, struct UserParams *user_pa return(0); } -static struct AstroParams *astro_params_ts; -static struct CosmoParams *cosmo_params_ts; -static struct UserParams *user_params_ts; -static struct FlagOptions *flag_options_ts; - -//allocate the arrays that are always needed and defined globally, including frequency integrals, zp edges, etc -void Broadcast_struct_global_TS(struct UserParams *user_params, struct CosmoParams *cosmo_params,struct AstroParams *astro_params, struct FlagOptions *flag_options){ - cosmo_params_ts = cosmo_params; - user_params_ts = user_params; - astro_params_ts = astro_params; - flag_options_ts = flag_options; -} - void alloc_global_arrays(){ - int i,j; + int i; //z-edges zpp_for_evolve_list = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); zpp_growth = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); @@ -147,26 +155,26 @@ void alloc_global_arrays(){ dxion_source_dt_box = (double *) calloc(HII_TOT_NUM_PIXELS,sizeof(double)); dxlya_dt_box = (double *) calloc(HII_TOT_NUM_PIXELS,sizeof(double)); dstarlya_dt_box = (double *) calloc(HII_TOT_NUM_PIXELS,sizeof(double)); - if(flag_options_ts->USE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ dstarlya_cont_dt_box = (double *) calloc(HII_TOT_NUM_PIXELS,sizeof(double)); dstarlya_inj_dt_box = (double *) calloc(HII_TOT_NUM_PIXELS,sizeof(double)); } - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ dstarlyLW_dt_box = (double *) calloc(HII_TOT_NUM_PIXELS,sizeof(double)); } //spectral stuff dstarlya_dt_prefactor = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); - if(flag_options_ts->USE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ dstarlya_cont_dt_prefactor = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); dstarlya_inj_dt_prefactor = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); } - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ dstarlya_dt_prefactor_MINI = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); dstarlyLW_dt_prefactor = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); dstarlyLW_dt_prefactor_MINI = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); - if(flag_options_ts->USE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ dstarlya_cont_dt_prefactor_MINI = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); dstarlya_inj_dt_prefactor_MINI = calloc(global_params.NUM_FILTER_STEPS_FOR_Ts,sizeof(double)); } @@ -174,13 +182,13 @@ void alloc_global_arrays(){ //Nonhalo stuff - int num_R_boxes = user_params_ts->MINIMIZE_MEMORY ? 1 : global_params.NUM_FILTER_STEPS_FOR_Ts; - if(!flag_options_ts->USE_HALO_FIELD){ + int num_R_boxes = user_params_global->MINIMIZE_MEMORY ? 1 : global_params.NUM_FILTER_STEPS_FOR_Ts; + if(!flag_options_global->USE_HALO_FIELD){ delNL0 = (float **)calloc(num_R_boxes,sizeof(float *)); for(i=0;iUSE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ log10_Mcrit_LW = (float **)calloc(num_R_boxes,sizeof(float *)); for (i=0; iUSE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ del_fcoll_Rct_MINI = calloc(HII_TOT_NUM_PIXELS,sizeof(float)); } @@ -206,7 +214,7 @@ void alloc_global_arrays(){ } void free_ts_global_arrays(){ - int i,j; + int i; //frequency integrals for(i=0;iUSE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ free(dstarlya_cont_dt_prefactor); free(dstarlya_inj_dt_prefactor); } - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ free(dstarlya_dt_prefactor_MINI); free(dstarlyLW_dt_prefactor); free(dstarlyLW_dt_prefactor_MINI); - if(flag_options_ts->USE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ free(dstarlya_inj_dt_prefactor_MINI); free(dstarlya_cont_dt_prefactor_MINI); } @@ -258,10 +266,10 @@ void free_ts_global_arrays(){ free(dxion_source_dt_box); free(dxlya_dt_box); free(dstarlya_dt_box); - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ free(dstarlyLW_dt_box); } - if(flag_options_ts->USE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ free(dstarlya_cont_dt_box); free(dstarlya_inj_dt_box); } @@ -273,14 +281,14 @@ void free_ts_global_arrays(){ free(Mcrit_atom_interp_table); //interp tables - int num_R_boxes = user_params_ts->MINIMIZE_MEMORY ? 1 : global_params.NUM_FILTER_STEPS_FOR_Ts; - if(!flag_options_ts->USE_HALO_FIELD){ + int num_R_boxes = user_params_global->MINIMIZE_MEMORY ? 1 : global_params.NUM_FILTER_STEPS_FOR_Ts; + if(!flag_options_global->USE_HALO_FIELD){ for(i=0;iUSE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ for(i=0;iUSE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ free(del_fcoll_Rct_MINI); } @@ -304,11 +312,10 @@ void setup_z_edges(double zp){ double R, R_factor; double zpp, prev_zpp, prev_R; double dzpp_for_evolve; - double determine_zpp_max; //this is not global like the minimum int R_ct; LOG_DEBUG("Starting z edges"); - R = L_FACTOR*user_params_ts->BOX_LEN/(float)user_params_ts->HII_DIM; + R = L_FACTOR*user_params_global->BOX_LEN/(float)user_params_global->HII_DIM; R_factor = pow(global_params.R_XLy_MAX/R, 1/((float)global_params.NUM_FILTER_STEPS_FOR_Ts)); for (R_ct=0; R_ct2 cases nuprime = nu_n(2)*(1.+zpp)/(1.+zp); if(zpp < zmax(zp,2)){ - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ sum_ly2_val = frecycle(2) * spectral_emissivity(nuprime, 0, 2); sum_ly2_val_MINI = frecycle(2) * spectral_emissivity(nuprime, 0, 3); @@ -386,11 +393,11 @@ void calculate_spectral_factors(double zp){ nuprime = NU_LW_THRESH / NUIONIZATION; //NOTE: are we comparing nuprime at z' and z'' correctly here? // currently: emitted frequency >= received frequency of next n - if (nuprime >= nu_n(n_ct + 1)) + if (nuprime >= nu_n(2 + 1)) continue; - sum_lyLW_val += (1. - astro_params_ts->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 2); - sum_lyLW_val_MINI += (1. - astro_params_ts->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 3); + sum_lyLW_val += (1. - astro_params_global->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 2); + sum_lyLW_val_MINI += (1. - astro_params_global->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 3); } else{ sum_ly2_val = frecycle(2) * spectral_emissivity(nuprime, 0, global_params.Pop); @@ -403,7 +410,7 @@ void calculate_spectral_factors(double zp){ nuprime = nu_n(n_ct)*(1+zpp)/(1.0+zp); - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ sum_lynto2_val += frecycle(n_ct) * spectral_emissivity(nuprime, 0, 2); sum_lynto2_val_MINI += frecycle(n_ct) * spectral_emissivity(nuprime, 0, 3); @@ -411,8 +418,8 @@ void calculate_spectral_factors(double zp){ nuprime = NU_LW_THRESH / NUIONIZATION; if (nuprime >= nu_n(n_ct + 1)) continue; - sum_lyLW_val += (1. - astro_params_ts->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 2); - sum_lyLW_val_MINI += (1. - astro_params_ts->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 3); + sum_lyLW_val += (1. - astro_params_global->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 2); + sum_lyLW_val_MINI += (1. - astro_params_global->F_H2_SHIELD) * spectral_emissivity(nuprime, 2, 3); } else{ //This is only useful if global_params.Pop is ever used, which I think is rare @@ -448,7 +455,7 @@ void calculate_spectral_factors(double zp){ sum_lyn_val = weight * sum_lyn_prev; sum_ly2_val = weight * sum_ly2_prev; sum_lynto2_val = weight * sum_lynto2_prev; - if (flag_options_ts->USE_MINI_HALOS){ + if (flag_options_global->USE_MINI_HALOS){ sum_lyn_val_MINI = weight * sum_lyn_prev_MINI; sum_ly2_val_MINI = weight * sum_ly2_prev_MINI; sum_lynto2_val_MINI = weight * sum_lynto2_prev_MINI; @@ -459,16 +466,16 @@ void calculate_spectral_factors(double zp){ dstarlya_dt_prefactor[R_ct] = zpp_integrand * sum_lyn_val; LOG_ULTRA_DEBUG("z: %.2e R: %.2e int %.2e starlya: %.4e",zpp,R_values[R_ct],zpp_integrand,dstarlya_dt_prefactor[R_ct]); - if(flag_options_ts->USE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ dstarlya_cont_dt_prefactor[R_ct] = zpp_integrand * sum_ly2_val; dstarlya_inj_dt_prefactor[R_ct] = zpp_integrand * sum_lynto2_val; LOG_ULTRA_DEBUG("cont %.2e inj %.2e",dstarlya_cont_dt_prefactor[R_ct],dstarlya_inj_dt_prefactor[R_ct]); } - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ dstarlya_dt_prefactor_MINI[R_ct] = zpp_integrand * sum_lyn_val_MINI; dstarlyLW_dt_prefactor[R_ct] = zpp_integrand * sum_lyLW_val; dstarlyLW_dt_prefactor_MINI[R_ct] = zpp_integrand * sum_lyLW_val_MINI; - if(flag_options_ts->USE_LYA_HEATING){ + if(flag_options_global->USE_LYA_HEATING){ dstarlya_cont_dt_prefactor_MINI[R_ct] = zpp_integrand * sum_ly2_val_MINI; dstarlya_inj_dt_prefactor_MINI[R_ct] = zpp_integrand * sum_lynto2_val_MINI; } @@ -498,42 +505,42 @@ void prepare_filter_boxes(double redshift, float *input_dens, float *input_vcb, //NOTE: Meraxes just applies a pointer cast box = (fftwf_complex *) input. Figure out why this works, // They pad the input by a factor of 2 to cover the complex part, but from the type I thought it would be stored [(r,c),(r,c)...] // Not [(r,r,r,r....),(c,c,c....)] so the alignment should be wrong, right? - #pragma omp parallel for private(i,j,k) num_threads(user_params_ts->N_THREADS) collapse(3) - for(i=0;iHII_DIM;i++){ - for(j=0;jHII_DIM;j++){ + #pragma omp parallel for private(i,j,k) num_threads(user_params_global->N_THREADS) collapse(3) + for(i=0;iHII_DIM;i++){ + for(j=0;jHII_DIM;j++){ for(k=0;kUSE_FFTW_WISDOM, user_params_ts->HII_DIM, HII_D_PARA, user_params_ts->N_THREADS, output_dens); - #pragma omp parallel for num_threads(user_params_ts->N_THREADS) + dft_r2c_cube(user_params_global->USE_FFTW_WISDOM, user_params_global->HII_DIM, HII_D_PARA, user_params_global->N_THREADS, output_dens); + #pragma omp parallel for num_threads(user_params_global->N_THREADS) for (ct=0; ctUSE_MINI_HALOS){ - curr_vcb = flag_options_ts->FIX_VCB_AVG ? global_params.VAVG : 0; - #pragma omp parallel for firstprivate(curr_vcb) private(i,j,k,curr_j21,M_buf) num_threads(user_params_ts->N_THREADS) collapse(3) - for(i=0;iHII_DIM;i++){ - for(j=0;jHII_DIM;j++){ + if(flag_options_global->USE_MINI_HALOS){ + curr_vcb = flag_options_global->FIX_VCB_AVG ? global_params.VAVG : 0; + #pragma omp parallel for firstprivate(curr_vcb) private(i,j,k,curr_j21,M_buf) num_threads(user_params_global->N_THREADS) collapse(3) + for(i=0;iHII_DIM;i++){ + for(j=0;jHII_DIM;j++){ for(k=0;kFIX_VCB_AVG && user_params_ts->USE_RELATIVE_VELOCITIES){ + if(!flag_options_global->FIX_VCB_AVG && user_params_global->USE_RELATIVE_VELOCITIES){ curr_vcb = input_vcb[HII_R_INDEX(i,j,k)]; } curr_j21 = input_j21[HII_R_INDEX(i,j,k)]; //NOTE: we don't use reionization_feedback here, I assume it wouldn't do much but it's inconsistent M_buf = lyman_werner_threshold(redshift, - curr_j21, curr_vcb, astro_params_ts); - M_buf = fmax(M_buf,astro_params_ts->M_TURN); + curr_j21, curr_vcb, astro_params_global); + M_buf = fmax(M_buf,astro_params_global->M_TURN); *((float *)output_LW + HII_R_FFT_INDEX(i,j,k)) = log10(M_buf); } } } ////////////////// Transform unfiltered box to k-space to prepare for filtering ///////////////// - dft_r2c_cube(user_params_ts->USE_FFTW_WISDOM, user_params_ts->HII_DIM, HII_D_PARA, user_params_ts->N_THREADS, output_LW); - #pragma omp parallel for num_threads(user_params_ts->N_THREADS) + dft_r2c_cube(user_params_global->USE_FFTW_WISDOM, user_params_global->HII_DIM, HII_D_PARA, user_params_global->N_THREADS, output_LW); + #pragma omp parallel for num_threads(user_params_global->N_THREADS) for (ct=0; ct L_FACTOR*(user_params_ts->BOX_LEN / user_params_ts->HII_DIM)){ + if (R > L_FACTOR*(user_params_global->BOX_LEN / user_params_global->HII_DIM)){ filter_box(box, 1, global_params.HEAT_FILTER, R); } // now fft back to real space - dft_c2r_cube(user_params_ts->USE_FFTW_WISDOM, user_params_ts->HII_DIM, HII_D_PARA, user_params_ts->N_THREADS, box); + dft_c2r_cube(user_params_global->USE_FFTW_WISDOM, user_params_global->HII_DIM, HII_D_PARA, user_params_global->N_THREADS, box); // copy over the values -#pragma omp parallel private(i,j,k) num_threads(user_params_ts->N_THREADS) +#pragma omp parallel private(i,j,k) num_threads(user_params_global->N_THREADS) { float curr; #pragma omp for reduction(+:ave_buffer) reduction(max:max_out_R) reduction(min:min_out_R) - for (i=0;iHII_DIM; i++){ - for (j=0;jHII_DIM; j++){ + for (i=0;iHII_DIM; i++){ + for (j=0;jHII_DIM; j++){ for (k=0;kN_THREADS) reduction(+:unfiltered_avg) + #pragma omp parallel private(i,j,k) num_threads(user_params_global->N_THREADS) reduction(+:unfiltered_avg) { float curr_val; #pragma omp for - for (i=0; iHII_DIM; i++){ - for (j=0; jHII_DIM; j++){ + for (i=0; iHII_DIM; i++){ + for (j=0; jHII_DIM; j++){ for (k=0; kUSE_FFTW_WISDOM, user_params_ts->HII_DIM, HII_D_PARA, user_params_ts->N_THREADS, unfiltered_box); + dft_r2c_cube(user_params_global->USE_FFTW_WISDOM, user_params_global->HII_DIM, HII_D_PARA, user_params_global->N_THREADS, unfiltered_box); // remember to add the factor of VOLUME/TOT_NUM_PIXELS when converting from real space to k-space // Note: we will leave off factor of VOLUME, in anticipation of the inverse FFT below - #pragma omp parallel num_threads(user_params_ts->N_THREADS) + #pragma omp parallel num_threads(user_params_global->N_THREADS) { #pragma omp for for (ct=0; ctUSE_FFTW_WISDOM, user_params_ts->HII_DIM, HII_D_PARA, user_params_ts->N_THREADS, filtered_box); + dft_c2r_cube(user_params_global->USE_FFTW_WISDOM, user_params_global->HII_DIM, HII_D_PARA, user_params_global->N_THREADS, filtered_box); // copy over the values - #pragma omp parallel private(i,j,k) num_threads(user_params_ts->N_THREADS) reduction(+:filtered_avg) + #pragma omp parallel private(i,j,k) num_threads(user_params_global->N_THREADS) reduction(+:filtered_avg) { float curr_val; #pragma omp for - for (i=0;iHII_DIM; i++){ - for (j=0;jHII_DIM; j++){ + for (i=0;iHII_DIM; i++){ + for (j=0;jHII_DIM; j++){ for (k=0;kMINIMIZE_MEMORY){ + if(user_params_global->MINIMIZE_MEMORY){ R_start = R_mm; R_end = R_mm+1; } @@ -744,7 +749,7 @@ void fill_freqint_tables(double zp, double x_e_ave, double filling_factor_of_HI_ R_start = 0; R_end = global_params.NUM_FILTER_STEPS_FOR_Ts; } -#pragma omp parallel private(R_ct,x_e_ct,lower_int_limit) num_threads(user_params_ts->N_THREADS) +#pragma omp parallel private(R_ct,x_e_ct,lower_int_limit) num_threads(user_params_global->N_THREADS) { #pragma omp for //In TauX we integrate Nion from zpp to zp using the LW turnover mass at zp (predending its at zpp) @@ -752,14 +757,14 @@ void fill_freqint_tables(double zp, double x_e_ave, double filling_factor_of_HI_ //NOTE: The one difference currently between the halobox and density field options is the weighting of the average // density -> volume weighted cell average || halo -> halo weighted average for (R_ct=R_start; R_ctUSE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ lower_int_limit = fmax(nu_tau_one_MINI(zp, zpp_for_evolve_list[R_ct], x_e_ave, filling_factor_of_HI_zp, log10_Mcrit_LW_ave[R_ct], Mlim_Fstar_g, Mlim_Fesc_g, Mlim_Fstar_MINI_g, - Mlim_Fesc_MINI_g), (astro_params_ts->NU_X_THRESH)*NU_over_EV); + Mlim_Fesc_MINI_g), (astro_params_global->NU_X_THRESH)*NU_over_EV); } else{ lower_int_limit = fmax(nu_tau_one(zp, zpp_for_evolve_list[R_ct], x_e_ave, filling_factor_of_HI_zp, Mlim_Fstar_g, Mlim_Fesc_g), - (astro_params_ts->NU_X_THRESH)*NU_over_EV); + (astro_params_global->NU_X_THRESH)*NU_over_EV); } // set up frequency integral table for later interpolation for the cell's x_e value for (x_e_ct = 0; x_e_ct < x_int_NXHII; x_e_ct++){ @@ -800,7 +805,7 @@ void fill_freqint_tables(double zp, double x_e_ave, double filling_factor_of_HI_ //construct a Ts table above Z_HEAT_MAX, this can happen if we are computing the first box or if we //request a redshift above Z_HEAT_MAX -void init_first_Ts(struct TsBox * box, float *dens, float z, float zp, double *x_e_ave, double *Tk_ave){ +void init_first_Ts(TsBox * box, float *dens, float z, float zp, double *x_e_ave, double *Tk_ave){ unsigned long long int box_ct; //zp is the requested redshift, z is the perturbed field redshift float growth_factor_zp; @@ -817,10 +822,10 @@ void init_first_Ts(struct TsBox * box, float *dens, float z, float zp, double *x *x_e_ave = xe; *Tk_ave = TK; -#pragma omp parallel private(box_ct) num_threads(user_params_ts->N_THREADS) +#pragma omp parallel private(box_ct) num_threads(user_params_global->N_THREADS) { double gdens; - double curr_xalpha; + float curr_xalpha; #pragma omp for for (box_ct=0; box_ctINTEGRATION_METHOD_ATOMIC == 1 || (flag_options_ts->USE_MINI_HALOS && user_params_ts->INTEGRATION_METHOD_MINI == 1)) - initialise_GL(NGL_INT,log(M_min_R[R_ct]),log(global_params.M_MAX_INTEGRAL)); + double determine_zpp_max, determine_zpp_min; - if(user_params_ts->USE_INTERPOLATION_TABLES){ - determine_zpp_min = zp*0.999; //global + if(user_params_global->USE_INTERPOLATION_TABLES){ + determine_zpp_min = zp*0.999; //NOTE: must be called after setup_z_edges for this line determine_zpp_max = zpp_for_evolve_list[global_params.NUM_FILTER_STEPS_FOR_Ts-1]*1.001; - zpp_bin_width = (determine_zpp_max - determine_zpp_min)/((float)zpp_interp_points_SFR-1.0); //global //We need the tables for the frequency integrals & mean fixing //NOTE: These global tables confuse me, we do ~400 (x50 for mini) integrals to build the table, despite only having // ~100 redshifts. The benefit of interpolating here would only matter if we keep the same table // over subsequent snapshots, which we don't seem to do. // The Nion table is used in nu_tau_one a lot but I think there's a better way to do that - if(flag_options_ts->USE_MASS_DEPENDENT_ZETA){ + if(flag_options_global->USE_MASS_DEPENDENT_ZETA){ /* initialise interpolation of the mean collapse fraction for global reionization.*/ initialise_Nion_Ts_spline(zpp_interp_points_SFR, determine_zpp_min, determine_zpp_max, - astro_params_ts->ALPHA_STAR, astro_params_ts->ALPHA_STAR_MINI, astro_params_ts->ALPHA_ESC, - astro_params_ts->F_STAR10, astro_params_ts->F_ESC10, astro_params_ts->F_STAR7_MINI, astro_params_ts->F_ESC7_MINI, - astro_params_ts->M_TURN, flag_options_ts->USE_MINI_HALOS); + astro_params_global->ALPHA_STAR, astro_params_global->ALPHA_STAR_MINI, astro_params_global->ALPHA_ESC, + astro_params_global->F_STAR10, astro_params_global->F_ESC10, astro_params_global->F_STAR7_MINI, astro_params_global->F_ESC7_MINI, + astro_params_global->M_TURN, flag_options_global->USE_MINI_HALOS); initialise_SFRD_spline(zpp_interp_points_SFR, determine_zpp_min, determine_zpp_max, - astro_params_ts->ALPHA_STAR, astro_params_ts->ALPHA_STAR_MINI, - astro_params_ts->F_STAR10, astro_params_ts->F_STAR7_MINI,astro_params_ts->M_TURN, - flag_options_ts->USE_MINI_HALOS); + astro_params_global->ALPHA_STAR, astro_params_global->ALPHA_STAR_MINI, + astro_params_global->F_STAR10, astro_params_global->F_STAR7_MINI,astro_params_global->M_TURN, + flag_options_global->USE_MINI_HALOS); } else{ init_FcollTable(determine_zpp_min,determine_zpp_max,true); @@ -882,26 +881,26 @@ int global_reion_properties(double zp, double x_e_ave, double *log10_Mcrit_LW_av // are based on the expected global Nion. as mentioned above it would be nice to // change this to a saved reionisation/sfrd history from previous snapshots sum_nion = EvaluateNionTs(zp,Mlim_Fstar_g,Mlim_Fesc_g); - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ sum_nion_mini = EvaluateNionTs_MINI(zp,log10_Mcrit_LW_ave[0],Mlim_Fstar_MINI_g,Mlim_Fesc_MINI_g); } LOG_DEBUG("nion zp = %.3e (%.3e MINI)",sum_nion,sum_nion_mini); double ION_EFF_FACTOR,ION_EFF_FACTOR_MINI; - ION_EFF_FACTOR = astro_params_ts->F_STAR10 * astro_params_ts->F_ESC10 * global_params.Pop2_ion; - ION_EFF_FACTOR_MINI = astro_params_ts->F_STAR7_MINI * astro_params_ts->F_ESC7_MINI * global_params.Pop3_ion; + ION_EFF_FACTOR = astro_params_global->F_STAR10 * astro_params_global->F_ESC10 * global_params.Pop2_ion; + ION_EFF_FACTOR_MINI = astro_params_global->F_STAR7_MINI * astro_params_global->F_ESC7_MINI * global_params.Pop3_ion; //NOTE: only used without MASS_DEPENDENT_ZETA *Q_HI = 1 - ( ION_EFF_FACTOR * sum_nion + ION_EFF_FACTOR_MINI * sum_nion_mini )/ (1.0 - x_e_ave); //Initialise freq tables & prefactors (x_e by R tables) - if(!user_params_ts->MINIMIZE_MEMORY){ + if(!user_params_global->MINIMIZE_MEMORY){ //Now global SFRD at (R_ct) for the mean fixing for(R_ct=0;R_ctUSE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ mean_sfr_zpp_mini[R_ct] = EvaluateSFRD_MINI(zpp,log10_Mcrit_LW_ave[R_ct],Mlim_Fstar_MINI_g); } } @@ -917,17 +916,17 @@ void calculate_sfrd_from_grid(int R_ct, float *dens_R_grid, float *Mcrit_R_grid, float *sfrd_grid_mini, double *ave_sfrd, double *ave_sfrd_mini){ double ave_sfrd_buf=0; double ave_sfrd_buf_mini=0; - if(user_params_ts->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options_ts->USE_MINI_HALOS && user_params_ts->INTEGRATION_METHOD_MINI == 1)) - initialise_GL(NGL_INT,log(M_min_R[R_ct]),log(M_max_R[R_ct])); + if(user_params_global->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options_global->USE_MINI_HALOS && user_params_global->INTEGRATION_METHOD_MINI == 1)) + initialise_GL(log(M_min_R[R_ct]),log(M_max_R[R_ct])); - if(user_params_ts->USE_INTERPOLATION_TABLES){ - if(flag_options_ts->USE_MASS_DEPENDENT_ZETA){ + if(user_params_global->USE_INTERPOLATION_TABLES){ + if(flag_options_global->USE_MASS_DEPENDENT_ZETA){ initialise_SFRD_Conditional_table(min_densities[R_ct]*zpp_growth[R_ct], max_densities[R_ct]*zpp_growth[R_ct]*1.001,zpp_growth[R_ct],Mcrit_atom_interp_table[R_ct], M_min_R[R_ct],M_max_R[R_ct],M_max_R[R_ct], - astro_params_ts->ALPHA_STAR, astro_params_ts->ALPHA_STAR_MINI, astro_params_ts->F_STAR10, - astro_params_ts->F_STAR7_MINI, user_params_ts->INTEGRATION_METHOD_ATOMIC, user_params_ts->INTEGRATION_METHOD_MINI, - flag_options_ts->USE_MINI_HALOS); + astro_params_global->ALPHA_STAR, astro_params_global->ALPHA_STAR_MINI, astro_params_global->F_STAR10, + astro_params_global->F_STAR7_MINI, user_params_global->INTEGRATION_METHOD_ATOMIC, user_params_global->INTEGRATION_METHOD_MINI, + flag_options_global->USE_MINI_HALOS); } else{ initialise_FgtrM_delta_table(min_densities[R_ct]*zpp_growth[R_ct], max_densities[R_ct]*zpp_growth[R_ct], zpp_for_evolve_list[R_ct], @@ -935,25 +934,26 @@ void calculate_sfrd_from_grid(int R_ct, float *dens_R_grid, float *Mcrit_R_grid, } } - #pragma omp parallel num_threads(user_params_ts->N_THREADS) + #pragma omp parallel num_threads(user_params_global->N_THREADS) { unsigned long long int box_ct; - double curr_dens,curr_mcrit; + double curr_dens; + double curr_mcrit = 0.; double fcoll, dfcoll; double fcoll_MINI=0; #pragma omp for reduction(+:ave_sfrd_buf,ave_sfrd_buf_mini) for (box_ct=0; box_ctUSE_MINI_HALOS) + if(flag_options_global->USE_MINI_HALOS) curr_mcrit = Mcrit_R_grid[box_ct]; - if(flag_options_ts->USE_MASS_DEPENDENT_ZETA){ + if(flag_options_global->USE_MASS_DEPENDENT_ZETA){ fcoll = EvaluateSFRD_Conditional(curr_dens,zpp_growth[R_ct],M_min_R[R_ct],M_max_R[R_ct],M_max_R[R_ct],sigma_max[R_ct], Mcrit_atom_interp_table[R_ct],Mlim_Fstar_g); sfrd_grid[box_ct] = (1.+curr_dens)*fcoll; - if (flag_options_ts->USE_MINI_HALOS){ + if (flag_options_global->USE_MINI_HALOS){ fcoll_MINI = EvaluateSFRD_Conditional_MINI(curr_dens,curr_mcrit,zpp_growth[R_ct],M_min_R[R_ct],M_max_R[R_ct],M_max_R[R_ct], sigma_max[R_ct],Mcrit_atom_interp_table[R_ct],Mlim_Fstar_MINI_g); sfrd_grid_mini[box_ct] = (1.+curr_dens)*fcoll_MINI; @@ -1003,24 +1003,24 @@ void set_zp_consts(double zp, struct spintemp_from_sfr_prefactors *consts){ consts->hubble_zp = hubble(zp); consts->dgrowth_dzp = ddicke_dz(zp); consts->dt_dzp = dtdz(zp); - if(fabs(astro_params_ts->X_RAY_SPEC_INDEX - 1.0) < 1e-6) { - luminosity_converstion_factor = (astro_params_ts->NU_X_THRESH)*NU_over_EV * log( global_params.NU_X_BAND_MAX/(astro_params_ts->NU_X_THRESH) ); + if(fabs(astro_params_global->X_RAY_SPEC_INDEX - 1.0) < 1e-6) { + luminosity_converstion_factor = (astro_params_global->NU_X_THRESH)*NU_over_EV * log( global_params.NU_X_BAND_MAX/(astro_params_global->NU_X_THRESH) ); luminosity_converstion_factor = 1./luminosity_converstion_factor; } else { - luminosity_converstion_factor = pow( (global_params.NU_X_BAND_MAX)*NU_over_EV , 1. - (astro_params_ts->X_RAY_SPEC_INDEX) ) - \ - pow( (astro_params_ts->NU_X_THRESH)*NU_over_EV , 1. - (astro_params_ts->X_RAY_SPEC_INDEX) ) ; + luminosity_converstion_factor = pow( (global_params.NU_X_BAND_MAX)*NU_over_EV , 1. - (astro_params_global->X_RAY_SPEC_INDEX) ) - \ + pow( (astro_params_global->NU_X_THRESH)*NU_over_EV , 1. - (astro_params_global->X_RAY_SPEC_INDEX) ) ; luminosity_converstion_factor = 1./luminosity_converstion_factor; - luminosity_converstion_factor *= pow( (astro_params_ts->NU_X_THRESH)*NU_over_EV, - (astro_params_ts->X_RAY_SPEC_INDEX) )*\ - (1 - (astro_params_ts->X_RAY_SPEC_INDEX)); + luminosity_converstion_factor *= pow( (astro_params_global->NU_X_THRESH)*NU_over_EV, - (astro_params_global->X_RAY_SPEC_INDEX) )*\ + (1 - (astro_params_global->X_RAY_SPEC_INDEX)); } // Finally, convert to the correct units. NU_over_EV*hplank as only want to divide by eV -> erg (owing to the definition of Luminosity) luminosity_converstion_factor /= (hplank); //for halos, we just want the SFR -> X-ray part //NOTE: compared to Mesinger+11: (1+zpp)^2 (1+zp) -> (1+zp)^3 - consts->xray_prefactor = luminosity_converstion_factor / ((astro_params_ts->NU_X_THRESH)*NU_over_EV) \ - * C * pow(1+zp, astro_params_ts->X_RAY_SPEC_INDEX + 3); //(1+z)^3 is here because we don't want it in the star lya (already in zpp integrand) + consts->xray_prefactor = luminosity_converstion_factor / ((astro_params_global->NU_X_THRESH)*NU_over_EV) \ + * C * pow(1+zp, astro_params_global->X_RAY_SPEC_INDEX + 3); //(1+z)^3 is here because we don't want it in the star lya (already in zpp integrand) // Required quantities for calculating the IGM spin temperature // Note: These used to be determined in evolveInt (and other functions). But I moved them all here, into a single location. @@ -1037,18 +1037,18 @@ void set_zp_consts(double zp, struct spintemp_from_sfr_prefactors *consts){ consts->xc_inverse = pow(1.0+zp,3.0)*T21/(consts->Trad*A10_HYPERFINE); - consts->dcomp_dzp_prefactor = (-1.51e-4)/(consts->hubble_zp/Ho)/(cosmo_params_ts->hlittle)*pow(consts->Trad,4.0)/(1.0+zp); + consts->dcomp_dzp_prefactor = (-1.51e-4)/(consts->hubble_zp/Ho)/(cosmo_params_global->hlittle)*pow(consts->Trad,4.0)/(1.0+zp); consts->Nb_zp = N_b0 * (1+zp)*(1+zp)*(1+zp); //used for lya_X and sinks NOTE: the 2 density factors are from source & absorber since its downscattered x-ray consts->N_zp = No * (1+zp)*(1+zp)*(1+zp); //used for CMB consts->lya_star_prefactor = C / FOURPI * Msun / m_p * (1 - 0.75*global_params.Y_He); //converts SFR density -> stellar baryon density + prefactors //converts the grid emissivity unit to per cm-3 - if(flag_options_ts->USE_HALO_FIELD){ + if(flag_options_global->USE_HALO_FIELD){ consts->volunit_inv = pow(CMperMPC,-3); //changes to emissivity per cm-3 } else{ - consts->volunit_inv = cosmo_params_ts->OMb * RHOcrit * pow(CMperMPC,-3); + consts->volunit_inv = cosmo_params_global->OMb * RHOcrit * pow(CMperMPC,-3); } LOG_DEBUG("Set zp consts xr %.2e Tr %.2e Ts %.2e xa %.2e xc %.2e cm %.2e",consts->xray_prefactor,consts->Trad, @@ -1116,7 +1116,7 @@ struct Ts_cell get_Ts_fast(float zp, float dzp, struct spintemp_from_sfr_prefact dxheat_dzp = rad->dxheat_dt * consts->dt_dzp * 2.0 / 3.0 / k_B / (1.0+rad->prev_xe); //CMB heating rate dCMBheat_dzp = 0.; - if (flag_options_ts->USE_CMB_HEATING) { + if (flag_options_global->USE_CMB_HEATING) { //Meiksin et al. 2021 eps_CMB = (3./4.) * (consts->Trad/T21) * A10_HYPERFINE * f_H * (hplank*hplank/Lambda_21/Lambda_21/m_p) * (1. + 2.*rad->prev_Tk/T21); dCMBheat_dzp = -eps_CMB * (2./3./k_B/(1.+rad->prev_xe))/consts->hubble_zp/(1.+zp); @@ -1125,7 +1125,7 @@ struct Ts_cell get_Ts_fast(float zp, float dzp, struct spintemp_from_sfr_prefact //Ly-alpha heating rate eps_Lya_cont = 0.; eps_Lya_inj = 0.; - if (flag_options_ts->USE_LYA_HEATING) { + if (flag_options_global->USE_LYA_HEATING) { E_continuum = Energy_Lya_heating(rad->prev_Tk, rad->prev_Ts, taugp(zp,rad->delta,rad->prev_xe), 2); E_injected = Energy_Lya_heating(rad->prev_Tk, rad->prev_Ts, taugp(zp,rad->delta,rad->prev_xe), 3); if (isnan(E_continuum) || isinf(E_continuum)){ @@ -1164,7 +1164,7 @@ struct Ts_cell get_Ts_fast(float zp, float dzp, struct spintemp_from_sfr_prefact output.x_e = x_e; output.Tk = Tk; - output.J_21_LW = flag_options_ts->USE_MINI_HALOS ? rad->dstarLW_dt : 0.; + output.J_21_LW = flag_options_global->USE_MINI_HALOS ? rad->dstarLW_dt : 0.; double J_alpha_tot = rad->dstarlya_dt + rad->dxlya_dt; //not really d/dz, but the lya flux @@ -1212,11 +1212,11 @@ struct Ts_cell get_Ts_fast(float zp, float dzp, struct spintemp_from_sfr_prefact } //outer-level function for calculating Ts based on the Halo boxes -void ts_main(float redshift, float prev_redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options, float perturbed_field_redshift, short cleanup, - struct PerturbedField *perturbed_field, struct XraySourceBox *source_box, struct TsBox *previous_spin_temp, - struct InitialConditions *ini_boxes, struct TsBox *this_spin_temp){ - int R_ct, i, j, k; +void ts_main(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, float perturbed_field_redshift, short cleanup, + PerturbedField *perturbed_field, XraySourceBox *source_box, TsBox *previous_spin_temp, + InitialConditions *ini_boxes, TsBox *this_spin_temp){ + int R_ct; unsigned long long int box_ct; double x_e_ave_p, Tk_ave_p; double growth_factor_z, growth_factor_zp; @@ -1279,11 +1279,9 @@ void ts_main(float redshift, float prev_redshift, struct UserParams *user_params double min_log10_MturnLW[global_params.NUM_FILTER_STEPS_FOR_Ts]; double max_log10_MturnLW[global_params.NUM_FILTER_STEPS_FOR_Ts]; double ave_dens[global_params.NUM_FILTER_STEPS_FOR_Ts]; - fftwf_complex *log10_Mcrit_LW_unfiltered; - fftwf_complex *delta_unfiltered; - double log10_Mcrit_limit, curr_vcb; - double max_buf=-1e20, min_buf=1e20, curr_dens; - curr_vcb = flag_options->FIX_VCB_AVG ? global_params.VAVG : 0; + fftwf_complex *log10_Mcrit_LW_unfiltered = NULL; + fftwf_complex *delta_unfiltered = NULL; + double log10_Mcrit_limit; if(!flag_options->USE_HALO_FIELD){ //copy over to FFTW, do the forward FFTs and apply constants @@ -1334,8 +1332,8 @@ void ts_main(float redshift, float prev_redshift, struct UserParams *user_params } } else{ - for(i=0;imean_log10_Mcrit_LW[i]; + for(R_ct=0;R_ctmean_log10_Mcrit_LW[R_ct]; } } //set the constants calculated once per snapshot @@ -1405,10 +1403,9 @@ void ts_main(float redshift, float prev_redshift, struct UserParams *user_params double ave_fcoll,ave_fcoll_MINI; double avg_fix_term=1.; double avg_fix_term_MINI=1.; - double min_d_buf,ave_d_buf,max_d_buf; int R_index; float *delta_box_input; - float *Mcrit_box_input; + float *Mcrit_box_input = NULL; //may be unused //if we have stars, fill in the heating term boxes if(!NO_LIGHT) { @@ -1439,7 +1436,7 @@ void ts_main(float redshift, float prev_redshift, struct UserParams *user_params } //get the global things we missed before mean_sfr_zpp[R_ct] = EvaluateSFRD(zpp_for_evolve_list[R_ct],Mlim_Fstar_g); - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ mean_sfr_zpp_mini[R_ct] = EvaluateSFRD_MINI(zpp_for_evolve_list[R_ct],ave_log10_MturnLW[R_ct],Mlim_Fstar_MINI_g); } //fill one row of the interp tables @@ -1457,15 +1454,15 @@ void ts_main(float redshift, float prev_redshift, struct UserParams *user_params LOG_SUPER_DEBUG("z %6.2f ave sfrd val %.3e global %.3e (int %.3e) Mmin %.3e ratio %.4e z_edge %.4e", zpp_for_evolve_list[R_ct],ave_fcoll,mean_sfr_zpp[R_ct], Nion_General(zpp_for_evolve_list[R_ct], log(M_min_R[R_ct]), log(global_params.M_MAX_INTEGRAL), - Mcrit_atom_interp_table[R_ct], astro_params_it->ALPHA_STAR, 0., - astro_params_it->F_STAR10, 1., Mlim_Fstar_g, 0.), + Mcrit_atom_interp_table[R_ct], astro_params_global->ALPHA_STAR, 0., + astro_params_global->F_STAR10, 1., Mlim_Fstar_g, 0.), M_min_R[R_ct],avg_fix_term,z_edge_factor); - if(flag_options_ts->USE_MINI_HALOS){ + if(flag_options_global->USE_MINI_HALOS){ LOG_SUPER_DEBUG("MINI sfrd val %.3e global %.3e (int %.3e) ratio %.3e log10McritLW %.3e Mlim %.3e", ave_fcoll_MINI,mean_sfr_zpp_mini[R_ct], Nion_General_MINI(zpp_for_evolve_list[R_ct], log(M_min_R[R_ct]), log(global_params.M_MAX_INTEGRAL), pow(10.,ave_log10_MturnLW[R_ct]), Mcrit_atom_interp_table[R_ct], - astro_params_ts->ALPHA_STAR_MINI, 0., astro_params_ts->F_STAR7_MINI, + astro_params_global->ALPHA_STAR_MINI, 0., astro_params_global->F_STAR7_MINI, 1., Mlim_Fstar_MINI_g, 0.), avg_fix_term_MINI,ave_log10_MturnLW[R_ct],Mlim_Fstar_MINI_g); } @@ -1487,7 +1484,7 @@ void ts_main(float redshift, float prev_redshift, struct UserParams *user_params { //private variables int xidx; - double ival, sfr_term, xray_sfr, x_e, T; + double ival, sfr_term, xray_sfr; double sfr_term_mini=0; #pragma omp for for(box_ct=0; box_ctTs_box[box_ct])==0) { LOG_ERROR("Estimated spin temperature is either infinite of NaN!" - "idx %d delta %.3e dxheat %.3e dxion %.3e dxlya %.3e dstarlya %.3e",box_ct,perturbed_field->density[box_ct] + "idx %llu delta %.3e dxheat %.3e dxion %.3e dxlya %.3e dstarlya %.3e",box_ct,perturbed_field->density[box_ct] ,dxheat_dt_box[box_ct],dxion_source_dt_box[box_ct],dxlya_dt_box[box_ct],dstarlya_dt_box[box_ct]); // Throw(ParameterError); Throw(InfinityorNaNError); diff --git a/src/py21cmfast/src/SpinTemperatureBox.h b/src/py21cmfast/src/SpinTemperatureBox.h new file mode 100644 index 00000000..2d081ee3 --- /dev/null +++ b/src/py21cmfast/src/SpinTemperatureBox.h @@ -0,0 +1,17 @@ +#ifndef _SPINTEMP_H +#define _SPINTEMP_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +int ComputeTsBox(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + float perturbed_field_redshift, short cleanup, + PerturbedField *perturbed_field, XraySourceBox *source_box, TsBox *previous_spin_temp, + InitialConditions *ini_boxes, TsBox *this_spin_temp); + +int UpdateXraySourceBox(UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, HaloBox *halobox, + double R_inner, double R_outer, int R_ct, XraySourceBox *source_box); + +#endif diff --git a/src/py21cmfast/src/Stochasticity.c b/src/py21cmfast/src/Stochasticity.c index 1149a394..ac0ea9fd 100644 --- a/src/py21cmfast/src/Stochasticity.c +++ b/src/py21cmfast/src/Stochasticity.c @@ -1,16 +1,27 @@ /*functions which deal with stochasticity * i.e sampling the halo mass function and * other halo relations.*/ - +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "indexing.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "interp_tables.h" +#include "hmf.h" +#include "cosmology.h" +#include "InitialConditions.h" + +#include "Stochasticity.h" //buffer size (per cell of arbitrary size) in the sampling function #define MAX_HALO_CELL (int)1e5 -//NOTE: because the .c files are directly included in GenerateIC.c, the static doesn't really do anything :( -static struct AstroParams *astro_params_stoc; -static struct CosmoParams *cosmo_params_stoc; -static struct UserParams *user_params_stoc; -static struct FlagOptions *flag_options_stoc; - //parameters for the halo mass->stars calculations //Note: ideally I would split this into constants set per snapshot and // constants set per condition, however some variables (delta or Mass) @@ -55,36 +66,20 @@ void print_hs_consts(struct HaloSamplingConstants * c){ return; } - -void Broadcast_struct_global_STOC(struct UserParams *user_params, struct CosmoParams *cosmo_params,struct AstroParams *astro_params, struct FlagOptions *flag_options){ - cosmo_params_stoc = cosmo_params; - user_params_stoc = user_params; - astro_params_stoc = astro_params; - flag_options_stoc = flag_options; -} - //This function, designed to be used in the wrapper to estimate Halo catalogue size, takes the parameters and returns average number of halos within the entire box -double expected_nhalo(double redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions * flag_options){ +double expected_nhalo(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions * flag_options){ //minimum sampled mass - Broadcast_struct_global_UF(user_params,cosmo_params); - Broadcast_struct_global_PS(user_params,cosmo_params); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); double M_min = user_params->SAMPLER_MIN_MASS; //maximum sampled mass double M_max = RHOcrit * cosmo_params->OMm * VOLUME / HII_TOT_NUM_PIXELS; - double growthf = dicke(redshift); double result; init_ps(); if(user_params->USE_INTERPOLATION_TABLES) initialiseSigmaMInterpTable(M_min,M_max); - struct parameters_gsl_MF_integrals params = { - .redshift = redshift, - .growthf = growthf, - .HMF = user_params->HMF, - }; - - result = IntegratedNdM(log(M_min), log(M_max), params, 1, 0) * VOLUME; + result = Nhalo_General(redshift, log(M_min), log(M_max)) * VOLUME * cosmo_params->OMm * RHOcrit; LOG_DEBUG("Expected %.2e Halos in the box from masses %.2e to %.2e at z=%.2f",result,M_min,M_max,redshift); if(user_params->USE_INTERPOLATION_TABLES) @@ -94,7 +89,7 @@ double expected_nhalo(double redshift, struct UserParams *user_params, struct Co } double sample_dndM_inverse(double condition, struct HaloSamplingConstants * hs_constants, gsl_rng * rng){ - double p_in, min_prob, result; + double p_in, result; p_in = gsl_rng_uniform(rng); result = EvaluateNhaloInv(condition,p_in); result = fmin(1,fmax(0,result)); //clip in case of extrapolation @@ -109,19 +104,19 @@ void stoc_set_consts_z(struct HaloSamplingConstants *const_struct, double redshi const_struct->z_out = redshift; const_struct->z_in = redshift_desc; - const_struct->M_min = user_params_stoc->SAMPLER_MIN_MASS / user_params_stoc->SAMPLER_BUFFER_FACTOR; + const_struct->M_min = user_params_global->SAMPLER_MIN_MASS / user_params_global->SAMPLER_BUFFER_FACTOR; const_struct->lnM_min = log(const_struct->M_min); const_struct->M_max_tables = global_params.M_MAX_INTEGRAL; const_struct->lnM_max_tb = log(const_struct->M_max_tables); init_ps(); - if(user_params_stoc->USE_INTERPOLATION_TABLES){ - if(user_params_stoc->SAMPLE_METHOD == 3) + if(user_params_global->USE_INTERPOLATION_TABLES){ + if(user_params_global->SAMPLE_METHOD == 3) initialiseSigmaMInterpTable(const_struct->M_min/2,const_struct->M_max_tables); //the binary split needs to go below the resolution else initialiseSigmaMInterpTable(const_struct->M_min,const_struct->M_max_tables); - if(user_params_stoc->SAMPLE_METHOD == 2) + if(user_params_global->SAMPLE_METHOD == 2) InitialiseSigmaInverseTable(); } @@ -129,38 +124,39 @@ void stoc_set_consts_z(struct HaloSamplingConstants *const_struct, double redshi if(redshift_desc >= 0){ const_struct->growth_in = dicke(redshift_desc); - if(astro_params_stoc->CORR_SFR > 0) - const_struct->corr_sfr = exp(-(redshift - redshift_desc)/astro_params_stoc->CORR_SFR); + if(astro_params_global->CORR_SFR > 0) + const_struct->corr_sfr = exp(-(redshift - redshift_desc)/astro_params_global->CORR_SFR); else const_struct->corr_sfr = 0; - if(astro_params_stoc->CORR_STAR > 0) - const_struct->corr_star = exp(-(redshift - redshift_desc)/astro_params_stoc->CORR_STAR); + if(astro_params_global->CORR_STAR > 0) + const_struct->corr_star = exp(-(redshift - redshift_desc)/astro_params_global->CORR_STAR); else const_struct->corr_star = 0; - if(astro_params_stoc->CORR_LX > 0) - const_struct->corr_xray = exp(-(redshift - redshift_desc)/astro_params_stoc->CORR_LX); + if(astro_params_global->CORR_LX > 0) + const_struct->corr_xray = exp(-(redshift - redshift_desc)/astro_params_global->CORR_LX); else const_struct->corr_xray = 0; const_struct->from_catalog = 1; - initialise_dNdM_tables(log(user_params_stoc->SAMPLER_MIN_MASS), const_struct->lnM_max_tb, const_struct->lnM_min, const_struct->lnM_max_tb, + initialise_dNdM_tables(log(user_params_global->SAMPLER_MIN_MASS), const_struct->lnM_max_tb, const_struct->lnM_min, const_struct->lnM_max_tb, const_struct->growth_out, const_struct->growth_in, true); - if(user_params_stoc->SAMPLE_METHOD == 0 || user_params_stoc->SAMPLE_METHOD == 1){ - initialise_dNdM_inverse_table(log(user_params_stoc->SAMPLER_MIN_MASS), const_struct->lnM_max_tb, const_struct->lnM_min, + if(user_params_global->SAMPLE_METHOD == 0 || user_params_global->SAMPLE_METHOD == 1){ + initialise_dNdM_inverse_table(log(user_params_global->SAMPLER_MIN_MASS), const_struct->lnM_max_tb, const_struct->lnM_min, const_struct->growth_out, const_struct->growth_in, true); } - if(user_params_stoc->SAMPLE_METHOD == 3){ + if(user_params_global->SAMPLE_METHOD == 3){ initialise_J_split_table(200,1e-4,20.,0.2); } } else { - double M_cond = RHOcrit * cosmo_params_stoc->OMm * VOLUME / HII_TOT_NUM_PIXELS; + double M_cond = RHOcrit * cosmo_params_global->OMm * VOLUME / HII_TOT_NUM_PIXELS; const_struct->M_cond = M_cond; const_struct->lnM_cond = log(M_cond); const_struct->sigma_cond = EvaluateSigma(const_struct->lnM_cond); //for the table limits - double delta_crit = get_delta_crit(user_params_stoc->HMF,const_struct->sigma_cond,const_struct->growth_out); + double delta_crit = get_delta_crit(user_params_global->HMF,const_struct->sigma_cond,const_struct->growth_out); const_struct->from_catalog = 0; + //TODO: determine the minimum density in the field and pass it in (<-1 is fine for Lagrangian) initialise_dNdM_tables(DELTA_MIN, MAX_DELTAC_FRAC*delta_crit, const_struct->lnM_min, const_struct->lnM_max_tb, const_struct->growth_out, const_struct->lnM_cond, false); initialise_dNdM_inverse_table(DELTA_MIN, MAX_DELTAC_FRAC*delta_crit, const_struct->lnM_min, @@ -184,7 +180,7 @@ void stoc_set_consts_cond(struct HaloSamplingConstants *const_struct, double con //mean stellar mass of this halo mass, used for stellar z correlations const_struct->cond_val = const_struct->lnM_cond; //condition delta is the previous delta crit - const_struct->delta = get_delta_crit(user_params_stoc->HMF,const_struct->sigma_cond,const_struct->growth_in)\ + const_struct->delta = get_delta_crit(user_params_global->HMF,const_struct->sigma_cond,const_struct->growth_in)\ / const_struct->growth_in * const_struct->growth_out; } //Here the condition is a cell of a given density, the volume/mass is given by the grid parameters @@ -198,7 +194,7 @@ void stoc_set_consts_cond(struct HaloSamplingConstants *const_struct, double con //the splines don't work well for cells above Deltac, but there CAN be cells above deltac, since this calculation happens //before the overlap, and since the smallest dexm mass is M_cell*(1.01^3) there *could* be a cell above Deltac not in a halo //NOTE: all this does is prevent integration errors below since these cases are also dealt with in stoc_sample - if(const_struct->delta > MAX_DELTAC_FRAC*get_delta_crit(user_params_stoc->HMF,const_struct->sigma_cond,const_struct->growth_out)){ + if(const_struct->delta > MAX_DELTAC_FRAC*get_delta_crit(user_params_global->HMF,const_struct->sigma_cond,const_struct->growth_out)){ const_struct->expected_M = const_struct->M_cond; const_struct->expected_N = 1; } @@ -222,8 +218,8 @@ void place_on_hires_grid(int x, int y, int z, int *crd_hi, gsl_rng * rng){ //this is so halos are on DIM grids to match HaloField and Perturb options int x_hi,y_hi,z_hi; double randbuf; - int lo_dim = user_params_stoc->HII_DIM; - int hi_dim = user_params_stoc->DIM; + int lo_dim = user_params_global->HII_DIM; + int hi_dim = user_params_global->DIM; randbuf = gsl_rng_uniform(rng); x_hi = (int)((x + randbuf) / (double)(lo_dim) * (double)(hi_dim)); randbuf = gsl_rng_uniform(rng); @@ -236,13 +232,13 @@ void place_on_hires_grid(int x, int y, int z, int *crd_hi, gsl_rng * rng){ } //This function adds stochastic halo properties to an existing halo -int set_prop_rng(gsl_rng *rng, bool from_catalog, double *interp, float * input, float * output){ +void set_prop_rng(gsl_rng *rng, bool from_catalog, double *interp, double * input, double * output){ double rng_star,rng_sfr,rng_xray; //Correlate properties by interpolating between the sampled and descendant gaussians - rng_star = astro_params_stoc->SIGMA_STAR > 0. ? gsl_ran_ugaussian(rng) : 0.; - rng_sfr = astro_params_stoc->SIGMA_SFR_LIM > 0. ? gsl_ran_ugaussian(rng) : 0.; - rng_xray = astro_params_stoc->SIGMA_LX > 0. ? gsl_ran_ugaussian(rng) : 0.; + rng_star = astro_params_global->SIGMA_STAR > 0. ? gsl_ran_ugaussian(rng) : 0.; + rng_sfr = astro_params_global->SIGMA_SFR_LIM > 0. ? gsl_ran_ugaussian(rng) : 0.; + rng_xray = astro_params_global->SIGMA_LX > 0. ? gsl_ran_ugaussian(rng) : 0.; if(from_catalog){ rng_star = (1-interp[0])*rng_star + interp[0]*input[0]; @@ -257,19 +253,17 @@ int set_prop_rng(gsl_rng *rng, bool from_catalog, double *interp, float * input, } //This is the function called to assign halo properties to an entire catalogue, used for DexM halos -int add_properties_cat(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options - , int seed, float redshift, struct HaloField *halos){ - Broadcast_struct_global_STOC(user_params,cosmo_params,astro_params,flag_options); +int add_properties_cat(unsigned long long int seed, float redshift, HaloField *halos){ //set up the rng - gsl_rng * rng_stoc[user_params->N_THREADS]; + gsl_rng * rng_stoc[user_params_global->N_THREADS]; seed_rng_threads(rng_stoc,seed); LOG_DEBUG("computing rng for %llu halos",halos->n_halos); //loop through the halos and assign properties unsigned long long int i; - float buf[3]; - float dummy[3]; //we don't need interpolation here + double buf[3]; + double dummy[3]; //we don't need interpolation here #pragma omp parallel for private(i,buf) for(i=0;in_halos;i++){ // LOG_ULTRA_DEBUG("halo %d hm %.2e crd %d %d %d",i,halos->halo_masses[i],halos->halo_coords[3*i+0],halos->halo_coords[3*i+1],halos->halo_coords[3*i+2]); @@ -290,8 +284,6 @@ int add_properties_cat(struct UserParams *user_params, struct CosmoParams *cosmo * conditional property PDFs, the number of halos is poisson sampled from the integrated CMF*/ int stoc_halo_sample(struct HaloSamplingConstants *hs_constants, gsl_rng * rng, int *n_halo_out, float *M_out){ double exp_N = hs_constants->expected_N; - - double hm_sample; int ii, nh; int halo_count=0; @@ -340,10 +332,10 @@ void fix_mass_sample(gsl_rng * rng, double exp_M, int *n_halo_pt, double *M_tot_ } } else{ - while(*M_tot_pt > exp_M){ + do{ //here we remove by setting halo mass to zero, skipping it during the consolidation last_M_del = remove_random_halo(rng,*n_halo_pt,&random_idx,M_tot_pt,M_out); - } + }while(*M_tot_pt > exp_M); // if the sample with the last subtracted halo is closer to the expected mass, keep it // LOG_ULTRA_DEBUG("Deciding to keep last halo M %.3e tot %.3e exp %.3e",last_M_del,*M_tot_pt,exp_M); @@ -363,7 +355,7 @@ int stoc_mass_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * rng, //The mass-limited sampling as-is has a slight bias to producing too many halos, // which is independent of density or halo mass, // this factor reduces the total expected mass to bring it into line with the CMF - exp_M *= user_params_stoc->HALOMASS_CORRECTION; + exp_M *= user_params_global->HALOMASS_CORRECTION; int n_halo_sampled=0; double M_prog=0; @@ -387,10 +379,10 @@ int stoc_mass_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * rng, bool partition_rejection(double sigma_m, double sigma_min, double sigma_cond, double del_c, double growthf, gsl_rng * rng){ //no rejection in EPS double test1,test2,randval; - if(user_params_stoc->HMF == 0){ + if(user_params_global->HMF == 0){ return false; } - else if(user_params_stoc->HMF == 1){ + else if(user_params_global->HMF == 1){ test1 = st_taylor_factor(sigma_m,sigma_cond,growthf,NULL) - del_c; //maximum barrier term in mass range test2 = st_taylor_factor(sigma_min,sigma_cond,growthf,NULL) - del_c; randval = gsl_rng_uniform(rng); @@ -420,10 +412,8 @@ int stoc_partition_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * int n_halo_sampled; double nu_sample, sigma_sample, M_sample, M_remaining, delta_current; double lnM_remaining, sigma_r, del_term; - - double tbl_arg = hs_constants->cond_val; n_halo_sampled = 0; - double nu_fudge_factor = user_params_stoc->HALOMASS_CORRECTION; + double nu_fudge_factor = user_params_global->HALOMASS_CORRECTION; //set initial amount // M_remaining = M_cond; // full condition @@ -431,10 +421,10 @@ int stoc_partition_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * lnM_remaining = log(M_remaining); double nu_min; - while(M_remaining > user_params_stoc->SAMPLER_MIN_MASS){ + while(M_remaining > user_params_global->SAMPLER_MIN_MASS){ sigma_r = EvaluateSigma(lnM_remaining); - delta_current = (get_delta_crit(user_params_stoc->HMF,sigma_r,growthf) - d_cond)/(M_remaining/M_cond); + delta_current = (get_delta_crit(user_params_global->HMF,sigma_r,growthf) - d_cond)/(M_remaining/M_cond); del_term = delta_current*delta_current/growthf/growthf; nu_min = sqrt(del_term/(sigma_min*sigma_min - sigma_r*sigma_r)); //nu at minimum progenitor @@ -469,9 +459,9 @@ double ComputeFraction_split( //This code was modified from the tree generation function in Darkforest (Qiu et al 2020. ArXiv: 2007.14624) int stoc_split_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * rng, int *n_halo_out, float *M_out){ //define constants - double G0 = user_params_stoc->PARKINSON_G0; - double gamma1 = user_params_stoc->PARKINSON_y1; - double gamma2 = user_params_stoc->PARKINSON_y2; + double G0 = user_params_global->PARKINSON_G0; + double gamma1 = user_params_global->PARKINSON_y1; + double gamma2 = user_params_global->PARKINSON_y2; double m_res = hs_constants->M_min; double lnm_res = hs_constants->lnM_min; double eps1 = 0.1; @@ -639,12 +629,12 @@ int stoc_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * rng, int int err; //If the expected mass is below our minimum saved mass, don't bother calculating //NOTE: some of these conditions are redundant with set_consts_cond() - if(hs_constants->delta <= DELTA_MIN || hs_constants->expected_M < user_params_stoc->SAMPLER_MIN_MASS){ + if(hs_constants->delta <= DELTA_MIN || hs_constants->expected_M < user_params_global->SAMPLER_MIN_MASS){ *n_halo_out = 0; return 0; } //if delta is above critical, form one big halo - if(hs_constants->delta >= MAX_DELTAC_FRAC*get_delta_crit(user_params_stoc->HMF,hs_constants->sigma_cond,hs_constants->growth_out)){ + if(hs_constants->delta >= MAX_DELTAC_FRAC*get_delta_crit(user_params_global->HMF,hs_constants->sigma_cond,hs_constants->growth_out)){ *n_halo_out = 1; //Expected mass takes into account potential dexm overlap @@ -653,16 +643,16 @@ int stoc_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * rng, int } //We always use Number-Limited sampling for grid-based cases - if(user_params_stoc->SAMPLE_METHOD == 1 || !hs_constants->from_catalog){ + if(user_params_global->SAMPLE_METHOD == 1 || !hs_constants->from_catalog){ err = stoc_halo_sample(hs_constants, rng, n_halo_out, M_out); } - else if(user_params_stoc->SAMPLE_METHOD == 0){ + else if(user_params_global->SAMPLE_METHOD == 0){ err = stoc_mass_sample(hs_constants, rng, n_halo_out, M_out); } - else if(user_params_stoc->SAMPLE_METHOD == 2){ + else if(user_params_global->SAMPLE_METHOD == 2){ err = stoc_partition_sample(hs_constants, rng, n_halo_out, M_out); } - else if(user_params_stoc->SAMPLE_METHOD == 3){ + else if(user_params_global->SAMPLE_METHOD == 3){ err = stoc_split_sample(hs_constants, rng, n_halo_out, M_out); } else{ @@ -677,32 +667,29 @@ int stoc_sample(struct HaloSamplingConstants * hs_constants, gsl_rng * rng, int } // will have to add properties here and output grids, instead of in perturbed -int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, float *halo_overlap_box, struct HaloField *halofield_large, struct HaloField *halofield_out, struct HaloSamplingConstants *hs_constants){ - int lo_dim = user_params_stoc->HII_DIM; - double boxlen = user_params_stoc->BOX_LEN; +int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, float *halo_overlap_box, HaloField *halofield_large, HaloField *halofield_out, struct HaloSamplingConstants *hs_constants){ + int lo_dim = user_params_global->HII_DIM; double Mcell = hs_constants->M_cond; double Mmin = hs_constants->M_min; - double lnMmin = hs_constants->lnM_min; double growthf = hs_constants->growth_out; unsigned long long int nhalo_in = halofield_large->n_halos; - unsigned long long int nhalo_threads[user_params_stoc->N_THREADS]; - unsigned long long int istart_threads[user_params_stoc->N_THREADS]; + unsigned long long int nhalo_threads[user_params_global->N_THREADS]; + unsigned long long int istart_threads[user_params_global->N_THREADS]; unsigned long long int arraysize_total = halofield_out->buffer_size; - unsigned long long int arraysize_local = arraysize_total / user_params_stoc->N_THREADS; + unsigned long long int arraysize_local = arraysize_total / user_params_global->N_THREADS; LOG_DEBUG("Beginning stochastic halo sampling on %d ^3 grid",lo_dim); - LOG_DEBUG("z = %f, Mmin = %e, Mmax = %e,volume = %.3e, D = %.3e",redshift,Mmin,Mcell,Mcell/RHOcrit/cosmo_params_stoc->OMm,growthf); + LOG_DEBUG("z = %f, Mmin = %e, Mmax = %e,volume = %.3e, D = %.3e",redshift,Mmin,Mcell,Mcell/RHOcrit/cosmo_params_global->OMm,growthf); LOG_DEBUG("Total Array Size %llu, array size per thread %llu (~%.3e GB total)",arraysize_total,arraysize_local,6.*arraysize_total*sizeof(int)/1e9); double total_volume_excluded=0.; double total_volume_dexm=0.; - double vol_conversion = pow((double)user_params_stoc->HII_DIM / (double)user_params_stoc->DIM,3); - double cell_volume = VOLUME / pow((double)user_params_stoc->HII_DIM,3); + double cell_volume = VOLUME / pow((double)user_params_global->HII_DIM,3); -#pragma omp parallel num_threads(user_params_stoc->N_THREADS) +#pragma omp parallel num_threads(user_params_global->N_THREADS) { //PRIVATE VARIABLES int x,y,z,i; @@ -711,7 +698,7 @@ int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, flo int nh_buf; double delta; - float prop_buf[3], prop_dummy[3]; + double prop_buf[3], prop_dummy[3]; int crd_hi[3]; double mass_defc; @@ -738,7 +725,7 @@ int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, flo halofield_out->halo_coords[1 + 3*(istart+count)] = halofield_large->halo_coords[1 + 3*halo_idx]; halofield_out->halo_coords[2 + 3*(istart+count)] = halofield_large->halo_coords[2 + 3*halo_idx]; - total_volume_dexm += halofield_large->halo_masses[halo_idx] / (RHOcrit * cosmo_params_stoc->OMm) / cell_volume; + total_volume_dexm += halofield_large->halo_masses[halo_idx] / (RHOcrit * cosmo_params_global->OMm * cell_volume); count++; } @@ -764,11 +751,11 @@ int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, flo for(i=0;iSAMPLER_MIN_MASS) continue; + if(hm_buf[i] < user_params_global->SAMPLER_MIN_MASS) continue; if(count >= arraysize_local){ - LOG_ERROR("More than %d halos (expected %.1f) with buffer size factor %d", - arraysize_local,arraysize_local/user_params_stoc->MAXHALO_FACTOR,user_params_stoc->MAXHALO_FACTOR); + LOG_ERROR("More than %llu halos (expected %.1e) with buffer size factor %.1f", + arraysize_local,arraysize_local/user_params_global->MAXHALO_FACTOR,user_params_global->MAXHALO_FACTOR); LOG_ERROR("If you expected to have an above average halo number try raising user_params->MAXHALO_FACTOR"); Throw(ValueError); } @@ -798,7 +785,7 @@ int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, flo } } } - LOG_SUPER_DEBUG("Thread %llu found %llu halos",threadnum,count); + LOG_SUPER_DEBUG("Thread %d found %llu halos",threadnum,count); istart_threads[threadnum] = istart; nhalo_threads[threadnum] = count; @@ -809,7 +796,7 @@ int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, flo //Condense the sparse array (serial) int i=0; unsigned long long int count_total = 0; - for(i=0;iN_THREADS;i++){ + for(i=0;iN_THREADS;i++){ memmove(&halofield_out->halo_masses[count_total],&halofield_out->halo_masses[istart_threads[i]],sizeof(float)*nhalo_threads[i]); memmove(&halofield_out->star_rng[count_total],&halofield_out->star_rng[istart_threads[i]],sizeof(float)*nhalo_threads[i]); memmove(&halofield_out->sfr_rng[count_total],&halofield_out->sfr_rng[istart_threads[i]],sizeof(float)*nhalo_threads[i]); @@ -831,31 +818,23 @@ int sample_halo_grids(gsl_rng **rng_arr, double redshift, float *dens_field, flo } //NOTE: there's a lot of repeated code here and in build_halo_cats, find a way to merge -int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, struct HaloField *halofield_in, - struct HaloField *halofield_out, struct HaloSamplingConstants *hs_constants){ +int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, HaloField *halofield_in, + HaloField *halofield_out, struct HaloSamplingConstants *hs_constants){ if(z_in >= z_out){ LOG_ERROR("halo progenitors must go backwards in time!!! z_in = %.1f, z_out = %.1f",z_in,z_out); Throw(ValueError); } - - double growth_in = hs_constants->growth_in; - double growth_out = hs_constants->growth_out; - int lo_dim = user_params_stoc->HII_DIM; - int hi_dim = user_params_stoc->DIM; - double boxlen = user_params_stoc->BOX_LEN; //cell size for smoothing / CMF calculation - double lnMmax_tb = hs_constants->lnM_max_tb; double Mmax_tb = hs_constants->M_max_tables; double Mmin = hs_constants->M_min; - double lnMmin = hs_constants->lnM_min; double delta = hs_constants->delta; unsigned long long int nhalo_in = halofield_in->n_halos; - unsigned long long int nhalo_threads[user_params_stoc->N_THREADS]; - unsigned long long int istart_threads[user_params_stoc->N_THREADS]; + unsigned long long int nhalo_threads[user_params_global->N_THREADS]; + unsigned long long int istart_threads[user_params_global->N_THREADS]; unsigned long long int arraysize_total = halofield_out->buffer_size; - unsigned long long int arraysize_local = arraysize_total / user_params_stoc->N_THREADS; + unsigned long long int arraysize_local = arraysize_total / user_params_global->N_THREADS; LOG_DEBUG("Beginning stochastic halo sampling of progenitors on %llu halos",nhalo_in); LOG_DEBUG("z = %f, Mmin = %e, d = %.3e",z_out,Mmin,delta); @@ -863,14 +842,14 @@ int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, struc double corr_arr[3] = {hs_constants->corr_star,hs_constants->corr_sfr,hs_constants->corr_xray}; -#pragma omp parallel num_threads(user_params_stoc->N_THREADS) +#pragma omp parallel num_threads(user_params_global->N_THREADS) { float prog_buf[MAX_HALO_CELL]; int n_prog; double M_prog; - float propbuf_in[3]; - float propbuf_out[3]; + double propbuf_in[3]; + double propbuf_out[3]; int threadnum = omp_get_thread_num(); double M2; @@ -888,7 +867,7 @@ int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, struc for(ii=0;iihalo_masses[ii]; if(M2 < Mmin || M2 > Mmax_tb){ - LOG_ERROR("Input Mass = %.2e at %d of %d, something went wrong in the input catalogue",M2,ii,nhalo_in); + LOG_ERROR("Input Mass = %.2e at %llu of %llu, something went wrong in the input catalogue",M2,ii,nhalo_in); Throw(ValueError); } //set condition-dependent variables for sampling @@ -906,12 +885,12 @@ int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, struc for(jj=0;jjSAMPLER_MIN_MASS) continue; + if(prog_buf[jj] < user_params_global->SAMPLER_MIN_MASS) continue; if(count >= arraysize_local){ - LOG_ERROR("More than %d halos (expected %d) with buffer size factor %d", - arraysize_local,arraysize_local/user_params_stoc->MAXHALO_FACTOR,user_params_stoc->MAXHALO_FACTOR); - LOG_ERROR("If you expected to have an above average halo number try raising user_params_stoc->MAXHALO_FACTOR"); + LOG_ERROR("More than %llu halos (expected %.1e) with buffer size factor %.1f", + arraysize_local,arraysize_local/user_params_global->MAXHALO_FACTOR,user_params_global->MAXHALO_FACTOR); + LOG_ERROR("If you expected to have an above average halo number try raising user_params_global->MAXHALO_FACTOR"); Throw(ValueError); } @@ -929,15 +908,17 @@ int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, struc if(ii==0){ M_prog += prog_buf[jj]; - LOG_ULTRA_DEBUG("First Halo Prog %d: Mass %.2e Stellar %.2e SFR %.2e e_d %.3f",jj,prog_buf[jj],propbuf_out[0],propbuf_out[1],Deltac*growth_out/growth_in); + LOG_ULTRA_DEBUG("First Halo Prog %d: Mass %.2e Stellar %.2e SFR %.2e e_d %.3f", + jj,prog_buf[jj],propbuf_out[0],propbuf_out[1],Deltac*hs_constants->growth_out/hs_constants->growth_in); } } if(ii==0){ - LOG_ULTRA_DEBUG(" HMF %d delta %.3f delta_coll %.3f delta_desc %.3f adjusted %.3f",user_params_stoc->HMF, + LOG_ULTRA_DEBUG(" HMF %d delta %.3f delta_coll %.3f delta_desc %.3f adjusted %.3f",user_params_global->HMF, hs_constants_priv.delta, - get_delta_crit(user_params_stoc->HMF,hs_constants_priv.sigma_cond,growth_out), - get_delta_crit(user_params_stoc->HMF,hs_constants_priv.sigma_cond,growth_in), - get_delta_crit(user_params_stoc->HMF,hs_constants_priv.sigma_cond,growth_in)*growth_out/growth_in); + get_delta_crit(user_params_global->HMF,hs_constants_priv.sigma_cond,hs_constants->growth_out), + get_delta_crit(user_params_global->HMF,hs_constants_priv.sigma_cond,hs_constants->growth_in), + get_delta_crit(user_params_global->HMF,hs_constants_priv.sigma_cond,hs_constants->growth_in) + *hs_constants->growth_out/hs_constants->growth_in); print_hs_consts(&hs_constants_priv); LOG_SUPER_DEBUG("First Halo: Mass %.2f | N %d (exp. %.2e) | Total M %.2e (exp. %.2e)", M2,n_prog,hs_constants_priv.expected_N,M_prog,hs_constants_priv.expected_M); @@ -950,8 +931,8 @@ int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, struc //Condense the sparse array int i=0; unsigned long long int count_total = 0; - for(i=0;iN_THREADS;i++){ - LOG_SUPER_DEBUG("Thread %llu found %llu Halos",i,nhalo_threads[i]); + for(i=0;iN_THREADS;i++){ + LOG_SUPER_DEBUG("Thread %d found %llu Halos",i,nhalo_threads[i]); memmove(&halofield_out->halo_masses[count_total],&halofield_out->halo_masses[istart_threads[i]],sizeof(float)*nhalo_threads[i]); memmove(&halofield_out->star_rng[count_total],&halofield_out->star_rng[istart_threads[i]],sizeof(float)*nhalo_threads[i]); memmove(&halofield_out->sfr_rng[count_total],&halofield_out->sfr_rng[istart_threads[i]],sizeof(float)*nhalo_threads[i]); @@ -969,16 +950,11 @@ int sample_halo_progenitors(gsl_rng ** rng_arr, double z_in, double z_out, struc } //function that talks between the structures (Python objects) and the sampling functions -int stochastic_halofield(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options - , int seed, float redshift_desc, float redshift, float *dens_field, float *halo_overlap_box, struct HaloField *halos_desc, struct HaloField *halos){ - Broadcast_struct_global_UF(user_params,cosmo_params); - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_STOC(user_params,cosmo_params,astro_params,flag_options); - Broadcast_struct_global_IT(user_params,cosmo_params,astro_params,flag_options); - - int n_halo_stoc; - int i_start,i; - +int stochastic_halofield(UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + unsigned long long int seed, float redshift_desc, float redshift, + float *dens_field, float *halo_overlap_box, HaloField *halos_desc, HaloField *halos){ + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); if(redshift_desc > 0 && halos_desc->n_halos == 0){ LOG_DEBUG("No halos to sample from redshifts %.2f to %.2f, continuing...",redshift_desc,redshift); return 0; @@ -1010,7 +986,7 @@ int stochastic_halofield(struct UserParams *user_params, struct CosmoParams *cos LOG_DEBUG("First few SFR RNG: %11.3e %11.3e %11.3e",halos->sfr_rng[0],halos->sfr_rng[1],halos->sfr_rng[2]); } - if(user_params_stoc->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ freeSigmaMInterpTable(); } free_dNdM_tables(); @@ -1020,69 +996,16 @@ int stochastic_halofield(struct UserParams *user_params, struct CosmoParams *cos return 0; } -int test_mfp_filter(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options - , float *input_box, double R, double mfp, double *result){ - int i,j,k; - //setup the box - - fftwf_complex *box_unfiltered = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); - fftwf_complex *box_filtered = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); - LOG_DEBUG("Allocated"); - - for (i=0; iHII_DIM; i++) - for (j=0; jHII_DIM; j++) - for (k=0; kUSE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, box_unfiltered); - - LOG_DEBUG("FFT'd"); - - //QUESTION: why do this here instead of at the end? - for(i=0;iUSE_EXP_FILTER) - filter_box_mfp(box_filtered, 1, R, mfp); - else - filter_box(box_filtered,1,global_params.HII_FILTER,R); - - - LOG_DEBUG("Filtered"); - dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, box_filtered); - LOG_DEBUG("IFFT'd"); - - for (i=0; iHII_DIM; i++) - for (j=0; jHII_DIM; j++) - for (k=0; kN_THREADS); @@ -1105,12 +1028,6 @@ int single_test_sample(struct UserParams *user_params, struct CosmoParams *cosmo LOG_DEBUG("SINGLE SAMPLE: z = (%.2f,%.2f), Mmin = %.3e, cond(%d)=[%.2e,%.2e,%.2e...]",z_out,z_in,hs_constants->M_min, n_condition,conditions[0],conditions[1],conditions[2]); - struct parameters_gsl_MF_integrals integral_params = { - .redshift = z_out, - .growthf = hs_constants->growth_out, - .HMF = user_params->HMF, - }; - //halo catalogues + cell sums from multiple conditions, given M as cell descendant halos/cells //the result mapping is n_halo_total (1) (exp_n,exp_m,n_prog,m_prog) (n_desc) M_cat (n_prog_total) int n_halo_tot=0; @@ -1170,7 +1087,7 @@ int single_test_sample(struct UserParams *user_params, struct CosmoParams *cosmo hs_constants->lnM_max_tb, hs_constants->growth_out, hs_constants->growth_in, true); } else{ - double delta_crit = get_delta_crit(user_params_stoc->HMF,hs_constants->sigma_cond,hs_constants->growth_out); + double delta_crit = get_delta_crit(user_params_global->HMF,hs_constants->sigma_cond,hs_constants->growth_out); initialise_dNdM_tables(DELTA_MIN, MAX_DELTAC_FRAC*delta_crit, log(user_params->SAMPLER_MIN_MASS), hs_constants->lnM_max_tb, hs_constants->growth_out, hs_constants->lnM_cond, false); } @@ -1189,7 +1106,7 @@ int single_test_sample(struct UserParams *user_params, struct CosmoParams *cosmo } } - if(user_params_stoc->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ freeSigmaMInterpTable(); } free_dNdM_tables(); diff --git a/src/py21cmfast/src/Stochasticity.h b/src/py21cmfast/src/Stochasticity.h new file mode 100644 index 00000000..be76ee7a --- /dev/null +++ b/src/py21cmfast/src/Stochasticity.h @@ -0,0 +1,22 @@ +#ifndef _STOCHASTICITY_H +#define _STOCHASTICITY_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +int stochastic_halofield(UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options + , unsigned long long int seed, float redshift_desc, float redshift, float *dens_field, float *halo_overlap_box, + HaloField *halos_desc, HaloField *halos); + +int single_test_sample(UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options, + unsigned long long int seed, int n_condition, float *conditions, int *cond_crd, double z_out, double z_in, + int *out_n_tot, int *out_n_cell, double *out_n_exp, + double *out_m_cell, double *out_m_exp, float *out_halo_masses, int *out_halo_coords); + +//This function, designed to be used in the wrapper to estimate Halo catalogue size, takes the parameters and returns average number of halos within the box +double expected_nhalo(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions * flag_options); + +//used in HaloField.c to assign rng to DexM halos +int add_properties_cat(unsigned long long int seed, float redshift, HaloField *halos); + +#endif diff --git a/src/py21cmfast/src/UsefulFunctions.c b/src/py21cmfast/src/UsefulFunctions.c deleted file mode 100644 index 380e9f98..00000000 --- a/src/py21cmfast/src/UsefulFunctions.c +++ /dev/null @@ -1,1259 +0,0 @@ -// ------------------------------------------------------------------------------------- -// Taken from COSMOLOGY.H -// ------------------------------------------------------------------------------------- -#define Ho (double) (cosmo_params_ufunc->hlittle*3.2407e-18) // s^-1 at z=0 -#define RHOcrit (double) ( (3.0*Ho*Ho / (8.0*PI*G)) * (CMperMPC*CMperMPC*CMperMPC)/Msun) // Msun Mpc^-3 ---- at z=0 -#define RHOcrit_cgs (double) (3.0*Ho*Ho / (8.0*PI*G)) // g pcm^-3 ---- at z=0 -#define No (double) (RHOcrit_cgs*cosmo_params_ufunc->OMb*(1-global_params.Y_He)/m_p) // current hydrogen number density estimate (#/cm^3) ~1.92e-7 -#define He_No (double) (RHOcrit_cgs*cosmo_params_ufunc->OMb*global_params.Y_He/(4.0*m_p)) // current helium number density estimate -#define N_b0 (double) (No+He_No) // present-day baryon num density, H + He -#define f_H (double) (No/(No+He_No)) // hydrogen number fraction -#define f_He (double) (He_No/(No+He_No)) // helium number fraction - -struct CosmoParams *cosmo_params_ufunc; -struct UserParams *user_params_ufunc; - -void Broadcast_struct_global_UF(struct UserParams *user_params, struct CosmoParams *cosmo_params){ - cosmo_params_ufunc = cosmo_params; - user_params_ufunc = user_params; -} - -void seed_rng_threads(gsl_rng * rng_arr[], int seed){ - // setting tbe random seeds - gsl_rng * rseed = gsl_rng_alloc(gsl_rng_mt19937); // An RNG for generating seeds for multithreading - - gsl_rng_set(rseed, seed); - - unsigned int seeds[user_params_ufunc->N_THREADS]; - - // For multithreading, seeds for the RNGs are generated from an initial RNG (based on the input random_seed) and then shuffled (Author: Fred Davies) - // int num_int = INT_MAX/16; - int num_int = INT_MAX/256; //JD: this was taking a few seconds per snapshot so i reduced the number TODO: init the RNG once - int i, thread_num; - unsigned int *many_ints = (unsigned int *)malloc((size_t)(num_int*sizeof(unsigned int))); // Some large number of possible integers - for (i=0; iN_THREADS, many_ints, num_int, sizeof(unsigned int)); // Populate the seeds array from the large list of integers - gsl_ran_shuffle(rseed, seeds, user_params_ufunc->N_THREADS, sizeof(unsigned int)); // Shuffle the randomly selected integers - - int checker; - - checker = 0; - // seed the random number generators - for (thread_num = 0; thread_num < user_params_ufunc->N_THREADS; thread_num++){ - switch (checker){ - case 0: - rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_mt19937); - gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); - break; - case 1: - rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_gfsr4); - gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); - break; - case 2: - rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_cmrg); - gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); - break; - case 3: - rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_mrg); - gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); - break; - case 4: - rng_arr[thread_num] = gsl_rng_alloc(gsl_rng_taus2); - gsl_rng_set(rng_arr[thread_num], seeds[thread_num]); - break; - } // end switch - - checker += 1; - - if(checker==5) { - checker = 0; - } - } - - gsl_rng_free(rseed); - free(many_ints); -} - -void free_rng_threads(gsl_rng * rng_arr[]){ - int ii; - for(ii=0;iiN_THREADS;ii++){ - gsl_rng_free(rng_arr[ii]); - } -} - -float ComputeFullyIoinizedTemperature(float z_re, float z, float delta){ - // z_re: the redshift of reionization - // z: the current redshift - // delta:the density contrast - float result, delta_re; - // just be fully ionized - if (fabs(z - z_re) < 1e-4) - result = 1; - else{ - // linearly extrapolate to get density at reionization - delta_re = delta * (1. + z ) / (1. + z_re); - if (delta_re<=-1) delta_re=-1. + global_params.MIN_DENSITY_LOW_LIMIT; - // evolving ionized box eq. 6 of McQuinn 2015, ignored the dependency of density at ionization - if (delta<=-1) delta=-1. + global_params.MIN_DENSITY_LOW_LIMIT; - result = pow((1. + delta) / (1. + delta_re), 1.1333); - result *= pow((1. + z) / (1. + z_re), 3.4); - result *= expf(pow((1. + z)/7.1, 2.5) - pow((1. + z_re)/7.1, 2.5)); - } - result *= pow(global_params.T_RE, 1.7); - // 1e4 before helium reionization; double it after - result += pow(1e4 * ((1. + z)/4.), 1.7) * ( 1 + delta); - result = pow(result, 0.5882); - //LOG_DEBUG("z_re=%.4f, z=%.4f, delta=%e, Tk=%.f", z_re, z, delta, result); - return result; -} - -float ComputePartiallyIoinizedTemperature(float T_HI, float res_xH){ - if (res_xH<=0.) return global_params.T_RE; - if (res_xH>=1) return T_HI; - - return T_HI * res_xH + global_params.T_RE * (1. - res_xH); -} - -//filter_box, filter_box_annulus and filter_box_mfp should be combined in a better way, they require different inputs -//and they are run on different subsets of the boxes but they contain a lot of the same math -void filter_box_mfp(fftwf_complex *box, int RES, float R, float mfp){ - int n_x, n_z, n_y, dimension,midpoint; - float k_x, k_y, k_z, k_mag, f, kR, kl; - float const1; - const1 = exp(-R/mfp); //independent of k, move it out of the loop - // LOG_DEBUG("Filtering box with R=%.2e, L=%.2e",R,mfp); - - switch(RES) { - case 0: - dimension = user_params_ufunc->DIM; - midpoint = MIDDLE; - break; - case 1: - dimension = user_params_ufunc->HII_DIM; - midpoint = HII_MIDDLE; - break; - } - // loop through k-box - -#pragma omp parallel shared(box) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,kR,kl,f) num_threads(user_params_ufunc->N_THREADS) - { -#pragma omp for - for (n_x=0; n_xmidpoint) {k_x =(n_x-dimension) * DELTA_K;} - else {k_x = n_x * DELTA_K;} - - for (n_y=0; n_ymidpoint) {k_y =(n_y-dimension) * DELTA_K;} - else {k_y = n_y * DELTA_K;} - for (n_z=0; n_z<=(unsigned long long)(user_params_ufunc->NON_CUBIC_FACTOR*midpoint); n_z++){ - k_z = n_z * DELTA_K_PARA; - - k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); - - kR = k_mag*R; - kl = k_mag*mfp; - - //Davies & Furlanetto MFP-eps(r) window function - //The filter no longer approaches 1 at k->0, so we can't use the limit - if (kR > 1e-4){ - //build the filter - f = (kl*kl*R + 2*mfp + R)*kl*cos(kR); - f += (-kl*kl*mfp + kl*kl*R + mfp + R)*sin(kR); - f *= const1; - f -= 2*kl*mfp; - f *= -3.0*mfp/(kR*R*R*(kl*kl+1)*(kl*kl+1)); - } - else{ - // k-> 0 limit - f = 2*mfp*mfp + 2*mfp*R + R*R; - f *= -const1; - f += 2*mfp*mfp; - f *= 3*mfp/(R*R*R); - } - if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= f; } - if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= f; } - } - } - } // end looping through k box - } - return; -} - -void filter_box_annulus(fftwf_complex *box, int RES, float R_inner, float R_outer){ - int n_x, n_z, n_y, dimension,midpoint; - float k_x, k_y, k_z, k_mag, kRinner, kRouter; - float f_inner, f_outer; - - switch(RES) { - case 0: - dimension = user_params_ufunc->DIM; - midpoint = MIDDLE; - break; - case 1: - dimension = user_params_ufunc->HII_DIM; - midpoint = HII_MIDDLE; - break; - } - // loop through k-box - -#pragma omp parallel shared(box) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,kRinner,kRouter,f_inner,f_outer) num_threads(user_params_ufunc->N_THREADS) - { -#pragma omp for - for (n_x=0; n_xmidpoint) {k_x =(n_x-dimension) * DELTA_K;} - else {k_x = n_x * DELTA_K;} - - for (n_y=0; n_ymidpoint) {k_y =(n_y-dimension) * DELTA_K;} - else {k_y = n_y * DELTA_K;} - - for (n_z=0; n_z<=(unsigned long long)(user_params_ufunc->NON_CUBIC_FACTOR*midpoint); n_z++){ - k_z = n_z * DELTA_K_PARA; - - k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); - - kRinner = k_mag*R_inner; - kRouter = k_mag*R_outer; - - if (kRinner > 1e-4){ - f_inner = 3.0/(pow(kRouter, 3) - pow(kRinner, 3)) * (sin(kRinner) - cos(kRinner)*kRinner); - f_outer = 3.0/(pow(kRouter, 3) - pow(kRinner, 3)) * (sin(kRouter) - cos(kRouter)*kRouter); - if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= (f_outer - f_inner); } - if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= (f_outer - f_inner); } - } - - } - } - } // end looping through k box - } - return; -} - -void filter_box(fftwf_complex *box, int RES, int filter_type, float R){ - int n_x, n_z, n_y, dimension,midpoint; - float k_x, k_y, k_z, k_mag, kR; - - switch(RES) { - case 0: - dimension = user_params_ufunc->DIM; - midpoint = MIDDLE; - break; - case 1: - dimension = user_params_ufunc->HII_DIM; - midpoint = HII_MIDDLE; - break; - } - - // loop through k-box - -#pragma omp parallel shared(box) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,kR) num_threads(user_params_ufunc->N_THREADS) - { -#pragma omp for - for (n_x=0; n_xmidpoint) {k_x =(n_x-dimension) * DELTA_K;} - else {k_x = n_x * DELTA_K;} - - for (n_y=0; n_ymidpoint) {k_y =(n_y-dimension) * DELTA_K;} - else {k_y = n_y * DELTA_K;} - -// for (n_z=(midpoint+1); n_z--;){ - for (n_z=0; n_z<=(unsigned long long)(user_params_ufunc->NON_CUBIC_FACTOR*midpoint); n_z++){ - k_z = n_z * DELTA_K_PARA; - - if (filter_type == 0){ // real space top-hat - - k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); - - kR = k_mag*R; // real space top-hat - - if (kR > 1e-4){ - if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= 3.0*pow(kR, -3) * (sin(kR) - cos(kR)*kR); } - if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= 3.0*pow(kR, -3) * (sin(kR) - cos(kR)*kR); } - } - } - else if (filter_type == 1){ // k-space top hat - - // This is actually (kR^2) but since we zero the value and find kR > 1 this is more computationally efficient - // as we don't need to evaluate the slower sqrt function -// kR = 0.17103765852*( k_x*k_x + k_y*k_y + k_z*k_z )*R*R; - - k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); - kR = k_mag*R; // real space top-hat - - kR *= 0.413566994; // equates integrated volume to the real space top-hat (9pi/2)^(-1/3) - if (kR > 1){ - if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] = 0; } - if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] = 0; } - } - } - else if (filter_type == 2){ // gaussian - // This is actually (kR^2) but since we zero the value and find kR > 1 this is more computationally efficient - // as we don't need to evaluate the slower sqrt function - kR = 0.643*0.643*( k_x*k_x + k_y*k_y + k_z*k_z )*R*R; -// kR *= 0.643; // equates integrated volume to the real space top-hat - if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= pow(E, -kR/2.0); } - if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= pow(E, -kR/2.0); } - } - else{ - if ( (n_x==0) && (n_y==0) && (n_z==0) ) - LOG_WARNING("Filter type %i is undefined. Box is unfiltered.", filter_type); - } - } - } - } // end looping through k box - } - - return; -} - -double MtoR(double M); -double RtoM(double R); -double TtoM(double z, double T, double mu); -double dicke(double z); -double dtdz(float z); -double ddickedt(double z); -double omega_mz(float z); -double Deltac_nonlinear(float z); -double drdz(float z); /* comoving distance, (1+z)*C*dtdz(in cm) per unit z */ -double alpha_A(double T); -/* returns the case B hydrogen recombination coefficient (Spitzer 1978) in cm^3 s^-1*/ -double alpha_B(double T); - -double HeI_ion_crosssec(double nu); -double HeII_ion_crosssec(double nu); -double HI_ion_crosssec(double nu); - - - -/* R in Mpc, M in Msun */ -double MtoR(double M){ - - // set R according to M<->R conversion defined by the filter type in ../Parameter_files/COSMOLOGY.H - if (global_params.FILTER == 0) //top hat M = (4/3) PI R^3 - return pow(3*M/(4*PI*cosmo_params_ufunc->OMm*RHOcrit), 1.0/3.0); - else if (global_params.FILTER == 1) //gaussian: M = (2PI)^1.5 R^3 - return pow( M/(pow(2*PI, 1.5) * cosmo_params_ufunc->OMm * RHOcrit), 1.0/3.0 ); - else // filter not defined - LOG_ERROR("No such filter = %i. Results are bogus.", global_params.FILTER); - Throw ValueError; -} - -/* R in Mpc, M in Msun */ -double RtoM(double R){ - // set M according to M<->R conversion defined by the filter type in ../Parameter_files/COSMOLOGY.H - if (global_params.FILTER == 0) //top hat M = (4/3) PI R^3 - return (4.0/3.0)*PI*pow(R,3)*(cosmo_params_ufunc->OMm*RHOcrit); - else if (global_params.FILTER == 1) //gaussian: M = (2PI)^1.5 R^3 - return pow(2*PI, 1.5) * cosmo_params_ufunc->OMm*RHOcrit * pow(R, 3); - else // filter not defined - LOG_ERROR("No such filter = %i. Results are bogus.", global_params.FILTER); - Throw ValueError; -} - -/* - T in K, M in Msun, mu is mean molecular weight - from Barkana & Loeb 2001 - - SUPRESS = 0 for no radiation field supression; - SUPRESS = 1 for supression (step function at z=z_ss, at v=v_zz) - */ -double TtoM(double z, double T, double mu){ - return 7030.97 / (cosmo_params_ufunc->hlittle) * sqrt( omega_mz(z) / (cosmo_params_ufunc->OMm*Deltac_nonlinear(z))) * - pow( T/(mu * (1+z)), 1.5 ); - /* if (!SUPRESS || (z >= z_re) ) // pre-reionization or don't worry about supression - return 7030.97 / hlittle * sqrt( omega_mz(z) / (OMm*Deltac_nonlinear(z)) ) * - pow( T/(mu * (1+z)), 1.5 ); - - if (z >= z_ss) // self-shielding dominates, use T = 1e4 K - return 7030.97 / hlittle * sqrt( omega_mz(z) / (OMm*Deltac_nonlinear(z)) ) * - pow( 1.0e4 /(mu * (1+z)), 1.5 ); - - // optically thin - return 7030.97 / hlittle * sqrt( omega_mz(z) / (OMm*Deltac_nonlinear(z)) ) * - pow( VcirtoT(v_ss, mu) /(mu * (1+z)), 1.5 ); - */ -} - -/* Physical (non-linear) overdensity at virialization (relative to critical density) - i.e. answer is rho / rho_crit - In Einstein de sitter model = 178 - (fitting formula from Bryan & Norman 1998) */ -double Deltac_nonlinear(float z){ - double d; - d = omega_mz(z) - 1.0; - return 18*PI*PI + 82*d - 39*d*d; -} - -/* Omega matter at redshift z */ -double omega_mz(float z){ - return cosmo_params_ufunc->OMm*pow(1+z,3) / (cosmo_params_ufunc->OMm*pow(1+z,3) + cosmo_params_ufunc->OMl + global_params.OMr*pow(1+z,4) + global_params.OMk*pow(1+z, 2)); -} - - -/* - FUNCTION dicke(z) - Computes the dicke growth function at redshift z, i.e. the z dependance part of sigma - - References: Peebles, "Large-Scale...", pg.53 (eq. 11.16). Includes omega<=1 - Nonzero Lambda case from Liddle et al, astro-ph/9512102, eqs. 6-8. - and quintessence case from Wang et al, astro-ph/9804015 - - Normalized to dicke(z=0)=1 - */ -double dicke(double z){ - double omegaM_z, dick_z, dick_0, x, x_0; - double tiny = 1e-4; - - if (fabs(cosmo_params_ufunc->OMm-1.0) < tiny){ //OMm = 1 (Einstein de-Sitter) - return 1.0/(1.0+z); - } - else if ( (cosmo_params_ufunc->OMl > (-tiny)) && (fabs(cosmo_params_ufunc->OMl+cosmo_params_ufunc->OMm+global_params.OMr-1.0) < 0.01) && (fabs(global_params.wl+1.0) < tiny) ){ - //this is a flat, cosmological CONSTANT universe, with only lambda, matter and radiation - //it is taken from liddle et al. - omegaM_z = cosmo_params_ufunc->OMm*pow(1+z,3) / ( cosmo_params_ufunc->OMl + cosmo_params_ufunc->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4) ); - dick_z = 2.5*omegaM_z / ( 1.0/70.0 + omegaM_z*(209-omegaM_z)/140.0 + pow(omegaM_z, 4.0/7.0) ); - dick_0 = 2.5*cosmo_params_ufunc->OMm / ( 1.0/70.0 + cosmo_params_ufunc->OMm*(209-cosmo_params_ufunc->OMm)/140.0 + pow(cosmo_params_ufunc->OMm, 4.0/7.0) ); - return dick_z / (dick_0 * (1.0+z)); - } - else if ( (global_params.OMtot < (1+tiny)) && (fabs(cosmo_params_ufunc->OMl) < tiny) ){ //open, zero lambda case (peebles, pg. 53) - x_0 = 1.0/(cosmo_params_ufunc->OMm+0.0) - 1.0; - dick_0 = 1 + 3.0/x_0 + 3*log(sqrt(1+x_0)-sqrt(x_0))*sqrt(1+x_0)/pow(x_0,1.5); - x = fabs(1.0/(cosmo_params_ufunc->OMm+0.0) - 1.0) / (1+z); - dick_z = 1 + 3.0/x + 3*log(sqrt(1+x)-sqrt(x))*sqrt(1+x)/pow(x,1.5); - return dick_z/dick_0; - } - else if ( (cosmo_params_ufunc->OMl > (-tiny)) && (fabs(global_params.OMtot-1.0) < tiny) && (fabs(global_params.wl+1) > tiny) ){ - LOG_WARNING("IN WANG."); - Throw ValueError; - } - - LOG_ERROR("No growth function!"); - Throw ValueError; -} - -/* function DTDZ returns the value of dt/dz at the redshift parameter z. */ -double dtdz(float z){ - double x, dxdz, const1, denom, numer; - x = sqrt( cosmo_params_ufunc->OMl/cosmo_params_ufunc->OMm ) * pow(1+z, -3.0/2.0); - dxdz = sqrt( cosmo_params_ufunc->OMl/cosmo_params_ufunc->OMm ) * pow(1+z, -5.0/2.0) * (-3.0/2.0); - const1 = 2 * sqrt( 1 + cosmo_params_ufunc->OMm/cosmo_params_ufunc->OMl ) / (3.0 * Ho) ; - - numer = dxdz * (1 + x*pow( pow(x,2) + 1, -0.5)); - denom = x + sqrt(pow(x,2) + 1); - return (const1 * numer / denom); -} - -/* Time derivative of the growth function at z */ -double ddickedt(double z){ - float dz = 1e-10; - double omegaM_z, ddickdz, dick_0, x, x_0, domegaMdz; - double tiny = 1e-4; - - return (dicke(z+dz)-dicke(z))/dz/dtdz(z); // lazy non-analytic form getting - - if (fabs(cosmo_params_ufunc->OMm-1.0) < tiny){ //OMm = 1 (Einstein de-Sitter) - return -pow(1+z,-2)/dtdz(z); - } - else if ( (cosmo_params_ufunc->OMl > (-tiny)) && (fabs(cosmo_params_ufunc->OMl+cosmo_params_ufunc->OMm+global_params.OMr-1.0) < 0.01) && (fabs(global_params.wl+1.0) < tiny) ){ - //this is a flat, cosmological CONSTANT universe, with only lambda, matter and radiation - //it is taken from liddle et al. - omegaM_z = cosmo_params_ufunc->OMm*pow(1+z,3) / ( cosmo_params_ufunc->OMl + cosmo_params_ufunc->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4) ); - domegaMdz = omegaM_z*3/(1+z) - cosmo_params_ufunc->OMm*pow(1+z,3)*pow(cosmo_params_ufunc->OMl + cosmo_params_ufunc->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4), -2) * (3*cosmo_params_ufunc->OMm*(1+z)*(1+z) + 4*global_params.OMr*pow(1+z,3)); - dick_0 = cosmo_params_ufunc->OMm / ( 1.0/70.0 + cosmo_params_ufunc->OMm*(209-cosmo_params_ufunc->OMm)/140.0 + pow(cosmo_params_ufunc->OMm, 4.0/7.0) ); - - ddickdz = (domegaMdz/(1+z)) * (1.0/70.0*pow(omegaM_z,-2) + 1.0/140.0 + 3.0/7.0*pow(omegaM_z, -10.0/3.0)) * pow(1.0/70.0/omegaM_z + (209.0-omegaM_z)/140.0 + pow(omegaM_z, -3.0/7.0) , -2); - ddickdz -= pow(1+z,-2)/(1.0/70.0/omegaM_z + (209.0-omegaM_z)/140.0 + pow(omegaM_z, -3.0/7.0)); - - return ddickdz / dick_0 / dtdz(z); - } - - LOG_ERROR("No growth function!"); - Throw ValueError; -} - -/* returns the hubble "constant" (in 1/sec) at z */ -double hubble(float z){ - return Ho*sqrt(cosmo_params_ufunc->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4) + cosmo_params_ufunc->OMl); -} - - -/* returns hubble time (in sec), t_h = 1/H */ -double t_hubble(float z){ - return 1.0/hubble(z); -} - -/* comoving distance (in cm) per unit redshift */ -double drdz(float z){ - return (1.0+z)*C*dtdz(z); -} - -/* returns the case A hydrogen recombination coefficient (Abel et al. 1997) in cm^3 s^-1*/ -double alpha_A(double T){ - double logT, ans; - logT = log(T/(double)1.1604505e4); - ans = pow(E, -28.6130338 - 0.72411256*logT - 2.02604473e-2*pow(logT, 2) - - 2.38086188e-3*pow(logT, 3) - 3.21260521e-4*pow(logT, 4) - - 1.42150291e-5*pow(logT, 5) + 4.98910892e-6*pow(logT, 6) - + 5.75561414e-7*pow(logT, 7) - 1.85676704e-8*pow(logT, 8) - - 3.07113524e-9 * pow(logT, 9)); - return ans; -} - -/* returns the case B hydrogen recombination coefficient (Spitzer 1978) in cm^3 s^-1*/ -double alpha_B(double T){ - return alphaB_10k * pow (T/1.0e4, -0.75); -} - - -/* - Function NEUTRAL_FRACTION returns the hydrogen neutral fraction, chi, given: - hydrogen density (pcm^-3) - gas temperature (10^4 K) - ionization rate (1e-12 s^-1) - */ -double neutral_fraction(double density, double T4, double gamma, int usecaseB){ - double chi, b, alpha, corr_He = 1.0/(4.0/global_params.Y_He - 3); - - if (usecaseB) - alpha = alpha_B(T4*1e4); - else - alpha = alpha_A(T4*1e4); - - gamma *= 1e-12; - - // approximation chi << 1 - chi = (1+corr_He)*density * alpha / gamma; - if (chi < TINY){ return 0;} - if (chi < 1e-5) - return chi; - - // this code, while mathematically accurate, is numerically buggy for very small x_HI, so i will use valid approximation x_HI <<1 above when x_HI < 1e-5, and this otherwise... the two converge seemlessly - //get solutions of quadratic of chi (neutral fraction) - b = -2 - gamma / (density*(1+corr_He)*alpha); - chi = ( -b - sqrt(b*b - 4) ) / 2.0; //correct root - return chi; -} - -/* function HeI_ion_crosssec returns the HI ionization cross section at parameter frequency - (taken from Verner et al (1996) */ -double HeI_ion_crosssec(double nu){ - double x,y,Fy; - - if (nu < HeI_NUIONIZATION) - return 0; - - x = nu/NU_over_EV/13.61 - 0.4434; - y = sqrt(x*x + pow(2.136, 2)); - return 9.492e-16*((x-1)*(x-1) + 2.039*2.039) * - pow(y, (0.5 * 3.188 - 5.5)) - * pow(1.0 + sqrt(y/1.469), -3.188); -} - - -/* function HeII_ion_crosssec returns the HeII ionization cross section at parameter frequency - (taken from Osterbrock, pg. 14) */ -double HeII_ion_crosssec(double nu){ - double epsilon, Z = 2; - - if (nu < HeII_NUIONIZATION) - return 0; - - if (nu == HeII_NUIONIZATION) - nu+=TINY; - - epsilon = sqrt( nu/HeII_NUIONIZATION - 1); - return (6.3e-18)/Z/Z * pow(HeII_NUIONIZATION/nu, 4) - * pow(E, 4-(4*atan(epsilon)/epsilon)) / (1-pow(E, -2*PI/epsilon)); -} - - -/* function HI_ion_crosssec returns the HI ionization cross section at parameter frequency - (taken from Osterbrock, pg. 14) */ -double HI_ion_crosssec(double nu){ - double epsilon, Z = 1; - - if (nu < NUIONIZATION) - return 0; - - if (nu == NUIONIZATION) - nu+=TINY; - - epsilon = sqrt( nu/NUIONIZATION - 1); - return (6.3e-18)/Z/Z * pow(NUIONIZATION/nu, 4) - * pow(E, 4-(4*atan(epsilon)/epsilon)) / (1-pow(E, -2*PI/epsilon)); -} - - - -/* Return the thomspon scattering optical depth from zstart to zend through fully ionized IGM. - The hydrogen reionization history is given by the zarry and xHarry parameters, in increasing - redshift order of length len.*/ -typedef struct{ - float *z, *xH; - int len; -} tau_e_params; -double dtau_e_dz(double z, void *params){ - float xH, xi; - int i=1; - tau_e_params p = *(tau_e_params *)params; - - if ((p.len == 0) || !(p.z)) { - return (1+z)*(1+z)*drdz(z); - } - else{ - // find where we are in the redshift array - if (p.z[0]>z) // ionization fraction is 1 prior to start of array - return (1+z)*(1+z)*drdz(z); - while ( (i < p.len) && (p.z[i] < z) ) {i++;} - if (i == p.len) - return 0; - - // linearly interpolate in redshift - xH = p.xH[i-1] + (p.xH[i] - p.xH[i-1])/(p.z[i] - p.z[i-1]) * (z - p.z[i-1]); - xi = 1.0-xH; - if (xi<0){ - LOG_WARNING("in taue: funny business xi=%e, changing to 0.", xi); - xi=0; - } - if (xi>1){ - LOG_WARNING("in taue: funny business xi=%e, changing to 1", xi); - xi=1; - } - - return xi*(1+z)*(1+z)*drdz(z); - } -} -double tau_e(float zstart, float zend, float *zarry, float *xHarry, int len){ - double prehelium, posthelium, error; - gsl_function F; - double rel_tol = 1e-3; //<- relative tolerance - gsl_integration_workspace * w - = gsl_integration_workspace_alloc (1000); - tau_e_params p; - - if (zstart >= zend){ - LOG_ERROR("in tau_e: First parameter must be smaller than the second.\n"); - Throw ValueError; - } - - F.function = &dtau_e_dz; - p.z = zarry; - p.xH = xHarry; - p.len = len; - F.params = &p; - if ((len > 0) && zarry) - zend = zarry[len-1] - FRACT_FLOAT_ERR; - - int status; - - gsl_set_error_handler_off(); - - if (zend > global_params.Zreion_HeII){// && (zstart < Zreion_HeII)){ - if (zstart < global_params.Zreion_HeII){ - status = gsl_integration_qag (&F, global_params.Zreion_HeII, zstart, 0, rel_tol, - 1000, GSL_INTEG_GAUSS61, w, &prehelium, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",global_params.Zreion_HeII,zstart,rel_tol,prehelium,error); - LOG_ERROR("data: zstart=%e zend=%e",zstart,zend); - GSL_ERROR(status); - } - - status = gsl_integration_qag (&F, zend, global_params.Zreion_HeII, 0, rel_tol, - 1000, GSL_INTEG_GAUSS61, w, &posthelium, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",zend,global_params.Zreion_HeII,rel_tol,posthelium,error); - LOG_ERROR("data: zstart=%e zend=%e",zstart,zend); - GSL_ERROR(status); - } - } - else{ - prehelium = 0; - status = gsl_integration_qag (&F, zend, zstart, 0, rel_tol, - 1000, GSL_INTEG_GAUSS61, w, &posthelium, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",zend,zstart,rel_tol,posthelium,error); - GSL_ERROR(status); - } - } - } - else{ - posthelium = 0; - status = gsl_integration_qag (&F, zend, zstart, 0, rel_tol, - 1000, GSL_INTEG_GAUSS61, w, &prehelium, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",zend,zstart,rel_tol,prehelium,error); - GSL_ERROR(status); - } - } - gsl_integration_workspace_free (w); - - return SIGMAT * ( (N_b0+He_No)*prehelium + N_b0*posthelium ); -} - -float ComputeTau(struct UserParams *user_params, struct CosmoParams *cosmo_params, int NPoints, float *redshifts, float *global_xHI) { - - int i; - float tau; - - Broadcast_struct_global_UF(user_params,cosmo_params); - - tau = tau_e(0, redshifts[NPoints-1], redshifts, global_xHI, NPoints); - - return tau; -} - -void writeUserParams(struct UserParams *p){ - LOG_INFO("\n UserParams:\n" - " HII_DIM=%4d, DIM=%4d, BOX_LEN=%8.3f, NON_CUBIC_FACTOR=%8.3f,\n" - " HMF=%2d, POWER_SPECTRUM=%2d, USE_RELATIVE_VELOCITIES=%1d, N_THREADS=%2d,\n" - " PERTURB_ON_HIGH_RES=%1d, NO_RNG=%1d, USE_FFTW_WISDOM=%1d, USE_INTERPOLATION_TABLES=%1d,\n" - " INTEGRATION_METHOD_ATOMIC=%2d, INTEGRATION_METHOD_MINI=%2d, USE_2LPT=%1d, MINIMIZE_MEMORY=%1d,\n" - " KEEP_3D_VELOCITIES=%1d, SAMPLER_MIN_MASS=%10.3e, SAMPLER_BUFFER_FACTOR=%8.3f, MAXHALO_FACTOR=%8.3f,\n" - " N_COND_INTERP=%4d, N_PROB_INTERP=%4d, MIN_LOGPROB=%8.3f, SAMPLE_METHOD=%2d, AVG_BELOW_SAMPLER=%1d,\n" - " HALOMASS_CORRECTION=%8.3f", - p->HII_DIM, p->DIM, p->BOX_LEN, p->NON_CUBIC_FACTOR, p->HMF, p->POWER_SPECTRUM, p->USE_RELATIVE_VELOCITIES, - p->N_THREADS, p->PERTURB_ON_HIGH_RES, p->NO_RNG, p->USE_FFTW_WISDOM, p->USE_INTERPOLATION_TABLES, - p->INTEGRATION_METHOD_ATOMIC,p->INTEGRATION_METHOD_MINI,p->USE_2LPT,p->MINIMIZE_MEMORY,p->KEEP_3D_VELOCITIES, - p->SAMPLER_MIN_MASS,p->SAMPLER_BUFFER_FACTOR,p->MAXHALO_FACTOR,p->N_COND_INTERP,p->N_PROB_INTERP,p->MIN_LOGPROB, - p->SAMPLE_METHOD,p->AVG_BELOW_SAMPLER,p->HALOMASS_CORRECTION); -} - -void writeCosmoParams(struct CosmoParams *p){ - LOG_INFO("\n CosmoParams:\n" - " SIGMA_8=%8.3f, hlittle=%8.3f, OMm=%8.3f, OMl=%8.3f, OMb=%8.3f, POWER_INDEX=%8.3f", - p->SIGMA_8, p->hlittle, p->OMm, p->OMl, p->OMb, p->POWER_INDEX); -} - -void writeAstroParams(struct FlagOptions *fo, struct AstroParams *p){ - if(fo->USE_MASS_DEPENDENT_ZETA) { - LOG_INFO("\n AstroParams:\n" - " M_TURN=%10.3e, R_BUBBLE_MAX=%8.3f, N_RSD_STEPS=%5d\n" - " F_STAR10=%8.3f, ALPHA_STAR=%8.3f, F_ESC10=%8.3f, ALPHA_ESC=%8.3f,\n" - " t_STAR=%8.3f, L_X=%10.3e, NU_X_THRESH=%8.3f, X_RAY_SPEC_INDEX=%8.3f,\n" - " UPPER_STELLAR_TURNOVER_MASS=%10.3e, UPPER_STELLAR_TURNOVER_INDEX=%8.3e", - p->M_TURN, p->R_BUBBLE_MAX, p->N_RSD_STEPS, p->F_STAR10, p->ALPHA_STAR, p->F_ESC10, - p->ALPHA_ESC, p->t_STAR, p->L_X, p->NU_X_THRESH, p->X_RAY_SPEC_INDEX, - p->UPPER_STELLAR_TURNOVER_MASS,p->UPPER_STELLAR_TURNOVER_INDEX); - if(fo->USE_HALO_FIELD){ - LOG_INFO("\n HaloField AstroParams:\n" - " SIGMA_STAR=%8.3f, CORR_STAR=%8.3f, \n" - " SIGMA_SFR_LIM=%8.3f (SIGMA_SFR_INDEX=%8.3f), CORR_SFR=%8.3f\n" - " SIGMA_LX=%8.3f, CORR_LX=%8.3f", - p->SIGMA_STAR, p->CORR_STAR, p->SIGMA_SFR_LIM, p->SIGMA_SFR_INDEX, p->CORR_SFR, - p->SIGMA_LX, p->CORR_LX); - } - if(fo->USE_MINI_HALOS){ - LOG_INFO("\n MiniHalo AstroParams:\n" - " ALPHA_STAR_MINI=%8.3f, F_ESC7_MINI=%8.3f, L_X_MINI=%10.3e, F_STAR7_MINI=%8.3f,\n" - " F_H2_SHIELD=%8.3f, A_LW=%8.3f, BETA_LW=%8.3f, A_VCB=%8.3f, BETA_VCB=%8.3f", - p->ALPHA_STAR_MINI,p->F_ESC7_MINI, p->L_X_MINI, p->F_STAR7_MINI, - p->F_H2_SHIELD, p->A_LW, p->BETA_LW, p->A_VCB, p->BETA_VCB); - - } - } - else { - LOG_INFO("\n AstroParams:\n" - " HII_EFF_FACTOR=%10.3e, ION_Tvir_MIN=%10.3e, X_RAY_Tvir_MIN=%10.3e,\n" - " R_BUBBLE_MAX=%8.3f, L_X=%10.3e, NU_X_THRESH=%8.3f, X_RAY_SPEC_INDEX=%8.3f,\n" - " F_STAR10=%8.3f, t_STAR=%8.3f, N_RSD_STEPS=%5d]", - p->HII_EFF_FACTOR, p->ION_Tvir_MIN, p->X_RAY_Tvir_MIN, - p->R_BUBBLE_MAX, p->L_X, p->NU_X_THRESH, p->X_RAY_SPEC_INDEX, p->F_STAR10, p->t_STAR, p->N_RSD_STEPS); - } -} - -void writeFlagOptions(struct FlagOptions *p){ - LOG_INFO("\n FlagOptions:\n" - " USE_HALO_FIELD=%1d, USE_MINI_HALOS=%1d, USE_MASS_DEPENDENT_ZETA=%1d, SUBCELL_RSD=%1d,\n" - " INHOMO_RECO=%1d, USE_TS_FLUCT=%1d, M_MIN_in_Mass=%1d, PHOTON_CONS=%1d,\n" - " HALO_STOCHASTICITY=%1d, FIXED_HALO_GRIDS=%1d, USE_EXP_FILTER=%1d\n" - " USE_CMB_HEATING=%1d, USE_LYA_HEATING=%1d, APPLY_RSDS=%1d, FIX_VCB_AVG=%1d\n" - " CELL_RECOMB=%1d, PHOTON_CONS_TYPE=%2d, USE_UPPER_STELLAR_TURNOVER=%1d", - p->USE_HALO_FIELD, p->USE_MINI_HALOS, p->USE_MASS_DEPENDENT_ZETA, p->SUBCELL_RSD, - p->INHOMO_RECO, p->USE_TS_FLUCT, p->M_MIN_in_Mass, p->PHOTON_CONS_TYPE, p->HALO_STOCHASTICITY, - p->FIXED_HALO_GRIDS, p->USE_EXP_FILTER,p->USE_CMB_HEATING,p->USE_LYA_HEATING,p->APPLY_RSDS, - p->FIX_VCB_AVG,p->CELL_RECOMB,p->PHOTON_CONS_TYPE,p->USE_UPPER_STELLAR_TURNOVER); -} - - -char *print_output_header(int print_pid, const char *name){ - char * pid = malloc(12*sizeof(char)); - - if(print_pid){ - sprintf(pid, "<%d>\t", getpid()); - }else{ - sprintf(pid, ""); - } - - printf("%s%s:\n", pid, name); - return (pid); -} - - -void print_corners_real(float *x, int size, float ncf){ - int s = size-1; - int s_ncf = size*ncf-1; - int i,j,k; - for(i=0;i= SUPER_DEBUG_LEVEL){ - - float corners[8]; - - int i,j,k, counter; - int s = size-1; - int s_ncf = size*ncf-1; - - counter = 0; - for(i=0;i= SUPER_DEBUG_LEVEL){ - - float corners_real[8]; - float corners_imag[8]; - - int i,j,k, counter; - int s = size-1; - int s_ncf = size*ncf-1; - - counter = 0; - for(i=0;i= SUPER_DEBUG_LEVEL){ - - double corners[8]; - - int i,j,k, counter; - int s = size-1; - int s_ncf = size*ncf-1; - - counter = 0; - for(i=0;ilowres_density, HII_DIM, NCF, " "); - LOG_SUPER_DEBUG(" hires_density: "); - debugSummarizeBox(x->hires_density, DIM, NCF, " "); - LOG_SUPER_DEBUG(" lowres_vx: "); - debugSummarizeBox(x->lowres_vx, HII_DIM, NCF, " "); - LOG_SUPER_DEBUG(" lowres_vy: "); - debugSummarizeBox(x->lowres_vy, HII_DIM, NCF, " "); - LOG_SUPER_DEBUG(" lowres_vz: "); - debugSummarizeBox(x->lowres_vz, HII_DIM, NCF, " "); -} - -void debugSummarizePerturbField(struct PerturbedField *x, int HII_DIM, float NCF){ - LOG_SUPER_DEBUG("Summary of PerturbedField:"); - LOG_SUPER_DEBUG(" density: "); - debugSummarizeBox(x->density, HII_DIM, NCF, " "); - LOG_SUPER_DEBUG(" velocity_x: "); - debugSummarizeBox(x->velocity_x, HII_DIM, NCF, " "); - LOG_SUPER_DEBUG(" velocity_y: "); - debugSummarizeBox(x->velocity_y, HII_DIM, NCF, " "); - LOG_SUPER_DEBUG(" velocity_z: "); - debugSummarizeBox(x->velocity_z, HII_DIM, NCF, " "); - -} -void inspectInitialConditions(struct InitialConditions *x, int print_pid, int print_corners, int print_first, - int HII_DIM, float NCF){ - int i; - char *pid = print_output_header(print_pid, "InitialConditions"); - - if(print_first){ - printf("%s\tFirstRow: ",pid); - - printf("%s\t\tlowres_density: "); - for(i=0;i<10;i++){ - printf("%f, ", x->lowres_density[i]); - } - printf("\n"); - - printf("%s\t\tlowres_vx : "); - for(i=0;i<10;i++){ - printf("%f, ", x->lowres_vx[i]); - } - printf("\n"); - - printf("%s\t\tlowres_vx_2LPT: "); - for(i=0;i<10;i++){ - printf("%f, ", x->lowres_vx_2LPT[i]); - } - printf("\n"); - } - - if(print_corners){ - printf("%s\tCorners: ",pid); - - printf("%s\t\tlowres_density: ",pid); - print_corners_real(x->lowres_density, HII_DIM, NCF); - - printf("%s\t\tlowres_vx : ", pid); - print_corners_real(x->lowres_vx, HII_DIM, NCF); - - printf("%s\t\tlowres_vx_2LPT: ", pid); - print_corners_real(x->lowres_vx_2LPT, HII_DIM, NCF); - } -} - - -void inspectPerturbedField(struct PerturbedField *x, int print_pid, int print_corners, int print_first, - int HII_DIM, float NCF){ - int i; - char *pid = print_output_header(print_pid, "PerturbedField"); - - if(print_first){ - printf("%s\tFirstRow: \n",pid); - - printf("%s\t\tdensity: ", pid); - for(i=0;i<10;i++){ - printf("%f, ", x->density[i]); - } - printf("\n"); - - printf("%s\t\tvelocity: ", pid); - for(i=0;i<10;i++){ - printf("%f, ", x->velocity_x[i]); - } - printf("\n"); - - printf("%s\t\tvelocity: ", pid); - for(i=0;i<10;i++){ - printf("%f, ", x->velocity_y[i]); - } - printf("\n"); - - printf("%s\t\tvelocity: ", pid); - for(i=0;i<10;i++){ - printf("%f, ", x->velocity_z[i]); - } - printf("\n"); - - } - - if(print_corners){ - printf("%s\tCorners: \n",pid); - - printf("%s\t\tdensity: ",pid); - print_corners_real(x->density, HII_DIM, NCF); - - printf("%s\t\tvelocity: ", pid); - print_corners_real(x->velocity_x, HII_DIM, NCF); - - printf("%s\t\tvelocity: ", pid); - print_corners_real(x->velocity_y, HII_DIM, NCF); - - printf("%s\t\tvelocity: ", pid); - print_corners_real(x->velocity_z, HII_DIM, NCF); - } - -} - - -void inspectTsBox(struct TsBox *x, int print_pid, int print_corners, int print_first, int HII_DIM, float NCF){ - int i; - char *pid = print_output_header(print_pid, "TsBox"); - - if(print_first){ - printf("%s\tFirstRow: ",pid); - - printf("%s\t\tTs_box : "); - for(i=0;i<10;i++){ - printf("%f, ", x->Ts_box[i]); - } - printf("\n"); - - printf("%s\t\tx_e_box: "); - for(i=0;i<10;i++){ - printf("%f, ", x->x_e_box[i]); - } - printf("\n"); - - printf("%s\t\tTk_box : "); - for(i=0;i<10;i++){ - printf("%f, ", x->Tk_box[i]); - } - printf("\n"); - } - - if(print_corners){ - printf("%s\tCorners: ",pid); - - printf("%s\t\tTs_box : ",pid); - print_corners_real(x->Ts_box, HII_DIM, NCF); - - printf("%s\t\tx_e_box: ", pid); - print_corners_real(x->x_e_box, HII_DIM, NCF); - - printf("%s\t\tTk_box : ", pid); - print_corners_real(x->Tk_box, HII_DIM, NCF); - } -} - -void inspectIonizedBox(struct IonizedBox *x, int print_pid, int print_corners, int print_first, int HII_DIM, float NCF){ - int i; - char *pid = print_output_header(print_pid, "IonizedBox"); - - if(print_first){ - printf("%s\tFirstRow: ",pid); - - printf("%s\t\txH_box : "); - for(i=0;i<10;i++){ - printf("%f, ", x->xH_box[i]); - } - printf("\n"); - - printf("%s\t\tGamma12_box: "); - for(i=0;i<10;i++){ - printf("%f, ", x->Gamma12_box[i]); - } - printf("\n"); - - printf("%s\t\tz_re_box : "); - for(i=0;i<10;i++){ - printf("%f, ", x->z_re_box[i]); - } - printf("\n"); - - printf("%s\t\tdNrec_box : "); - for(i=0;i<10;i++){ - printf("%f, ", x->dNrec_box[i]); - } - printf("\n"); - } - - if(print_corners){ - printf("%s\tCorners: ",pid); - - printf("%s\t\txH_box : ",pid); - print_corners_real(x->xH_box, HII_DIM, NCF); - - printf("%s\t\tGamma12_box: ", pid); - print_corners_real(x->Gamma12_box, HII_DIM, NCF); - - printf("%s\t\tz_re_box : ", pid); - print_corners_real(x->z_re_box, HII_DIM, NCF); - - printf("%s\t\tdNrec_box : ", pid); - print_corners_real(x->dNrec_box, HII_DIM, NCF); - } -} - -void inspectBrightnessTemp(struct BrightnessTemp *x, int print_pid, int print_corners, int print_first, int HII_DIM, float NCF){ - int i; - - char *pid = print_output_header(print_pid, "BrightnessTemp"); - - if(print_first){ - printf("%s\tFirstRow: ",pid); - - printf("%s\t\tbrightness_temp: "); - for(i=0;i<10;i++){ - printf("%f, ", x->brightness_temp[i]); - } - printf("\n"); - } - - if(print_corners){ - printf("%s\tCorners: ",pid); - - printf("%s\t\tbrightness_temp: ",pid); - print_corners_real(x->brightness_temp, HII_DIM, NCF); - } -} -double atomic_cooling_threshold(float z){ - return TtoM(z, 1e4, 0.59); -} - -double molecular_cooling_threshold(float z){ - return TtoM(z, 600, 1.22); -} - -double lyman_werner_threshold(float z, float J_21_LW, float vcb, struct AstroParams *astro_params){ - // correction follows Schauer+20, fit jointly to LW feedback and relative velocities. They find weaker effect of LW feedback than before (Stacy+11, Greif+11, etc.) due to HII self shielding. - double mcrit_noLW = 3.314e7 * pow( 1.+z, -1.5);// this follows Visbal+15, which is taken as the optimal fit from Fialkov+12 which was calibrated with the simulations of Stacy+11 and Greif+11; - - double f_LW = 1.0 + astro_params->A_LW * pow(J_21_LW, astro_params->BETA_LW); - - double f_vcb = pow(1.0 + astro_params->A_VCB * vcb/SIGMAVCB, astro_params->BETA_VCB); - - // double mcrit_LW = mcrit_noLW * (1.0 + 10. * sqrt(J_21_LW)); //Eq. (12) in Schauer+20 - // return pow(10.0, log10(mcrit_LW) + 0.416 * vcb/SIGMAVCB ); //vcb and sigmacb in km/s, from Eq. (9) - - return (mcrit_noLW * f_LW * f_vcb); - -} - -double reionization_feedback(float z, float Gamma_halo_HII, float z_IN){ - if (z_IN<=1e-19) - return 1e-40; - return REION_SM13_M0 * pow(HALO_BIAS * Gamma_halo_HII, REION_SM13_A) * pow((1.+z)/10, REION_SM13_B) * - pow(1 - pow((1.+z)/(1.+z_IN), REION_SM13_C), REION_SM13_D); -} - - -/* - The following functions are simply for testing the exception framework -*/ -void FunctionThatThrows(){ - Throw(PhotonConsError); -} - -int SomethingThatCatches(bool sub_func){ - // A simple function that catches a thrown error. - int status; - Try{ - if(sub_func) FunctionThatThrows(); - else Throw(PhotonConsError); - } - Catch(status){ - return status; - } - return 0; -} - -int FunctionThatCatches(bool sub_func, bool pass, double *result){ - int status; - if(!pass){ - Try{ - if(sub_func) FunctionThatThrows(); - else Throw(PhotonConsError); - } - Catch(status){ - LOG_DEBUG("Caught the problem with status %d.", status); - return status; - } - } - *result = 5.0; - return 0; -} - -float clip(float x, float min, float max){ - if(xmax) return max; - return x; -} diff --git a/src/py21cmfast/src/_functionprototypes_wrapper.h b/src/py21cmfast/src/_functionprototypes_wrapper.h new file mode 100644 index 00000000..a7a5c1ad --- /dev/null +++ b/src/py21cmfast/src/_functionprototypes_wrapper.h @@ -0,0 +1,168 @@ +/* This file contains the repeated function prototypes which are needed by CFFI + to be included explicitly via ffi.cdef(), These are the only functions which + are visible to the python wrapper */ + +/* OutputStruct COMPUTE FUNCTIONS */ +int ComputeInitialConditions(unsigned long long random_seed, UserParams *user_params, CosmoParams *cosmo_params, InitialConditions *boxes); + +int ComputePerturbField(float redshift, UserParams *user_params, CosmoParams *cosmo_params, InitialConditions *boxes, PerturbedField *perturbed_field); + +int ComputeHaloField(float redshift_desc, float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + InitialConditions *boxes, unsigned long long int random_seed, HaloField * halos_desc, HaloField *halos); + +int ComputePerturbHaloField(float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + InitialConditions *boxes, HaloField *halos, PerturbHaloField *halos_perturbed); + +int ComputeTsBox(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, float perturbed_field_redshift, + short cleanup, + PerturbedField *perturbed_field, XraySourceBox * source_box, TsBox *previous_spin_temp, InitialConditions *ini_boxes, + TsBox *this_spin_temp); + +int ComputeIonizedBox(float redshift, float prev_redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, PerturbedField *perturbed_field, + PerturbedField *previous_perturbed_field, IonizedBox *previous_ionize_box, + TsBox *spin_temp, HaloBox *halos, InitialConditions *ini_boxes, + IonizedBox *box); + +int ComputeBrightnessTemp(float redshift, UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, + TsBox *spin_temp, IonizedBox *ionized_box, + PerturbedField *perturb_field, BrightnessTemp *box); + +int ComputeHaloBox(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params + , FlagOptions * flag_options, InitialConditions * ini_boxes, PerturbedField * perturbed_field, PerturbHaloField *halos + , TsBox *previous_spin_temp, IonizedBox *previous_ionize_box, HaloBox *grids); + +int UpdateXraySourceBox( UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options, HaloBox *halobox, + double R_inner, double R_outer, int R_ct, XraySourceBox *source_box); +/*--------------------------*/ + +/* PHOTON CONSERVATION MODEL FUNCTIONS */ +int InitialisePhotonCons( UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options); + +int PhotonCons_Calibration(double *z_estimate, double *xH_estimate, int NSpline); +int ComputeZstart_PhotonCons(double *zstart); + +void adjust_redshifts_for_photoncons( + AstroParams *astro_params, FlagOptions *flag_options, float *redshift, + float *stored_redshift, float *absolute_delta_z +); + +void determine_deltaz_for_photoncons(); + +int ObtainPhotonConsData(double *z_at_Q_data, double *Q_data, int *Ndata_analytic, double *z_cal_data, double *nf_cal_data, int *Ndata_calibration, + double *PhotonCons_NFdata, double *PhotonCons_deltaz, int *Ndata_PhotonCons); + +void FreePhotonConsMemory(); +extern bool photon_cons_allocated; + +void set_alphacons_params(double norm, double slope); +/* ------------------------------- */ + +/* Non-OutputStruct data products */ +int ComputeLF(int nbins, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, + FlagOptions *flag_options, int component, int NUM_OF_REDSHIFT_FOR_LF, float *z_LF, float *M_TURNs, double *M_uv_z, double *M_h_z, double *log10phi); + +float ComputeTau( UserParams *user_params, CosmoParams *cosmo_params, int Npoints, float *redshifts, float *global_xHI); +/*-----------------------------*/ + + +/* Initialisation functions needed in the wrapper*/ +double init_ps(); +int init_heat(); +int CreateFFTWWisdoms( UserParams *user_params, CosmoParams *cosmo_params); +void Broadcast_struct_global_noastro( UserParams *user_params, CosmoParams *cosmo_params); +void Broadcast_struct_global_all( UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options); +/*---------------------------*/ + +/* Interpolation Table Functions */ +//Initialisation +void initialiseSigmaMInterpTable(float M_Min, float M_Max); +void initialise_SFRD_spline(int Nbin, float zmin, float zmax, float Alpha_star, float Alpha_star_mini, float Fstar10, float Fstar7_MINI, + float mturn_a_const, bool minihalos); +void initialise_Nion_Ts_spline(int Nbin, float zmin, float zmax, float Alpha_star, float Alpha_star_mini, float Alpha_esc, float Fstar10, + float Fesc10, float Fstar7_MINI, float Fesc7_MINI, float mturn_a_const, bool minihalos); +void initialise_FgtrM_delta_table(double min_dens, double max_dens, double zpp, double growth_zpp, double smin_zpp, double smax_zpp); +void init_FcollTable(double zmin, double zmax, bool x_ray); +void initialise_Nion_Conditional_spline(float z, float Mcrit_atom, float min_density, float max_density, + float Mmin, float Mmax, float Mcond, float log10Mturn_min, float log10Mturn_max, + float log10Mturn_min_MINI, float log10Mturn_max_MINI, float Alpha_star, + float Alpha_star_mini, float Alpha_esc, float Fstar10, float Fesc10, + float Mlim_Fstar, float Mlim_Fesc, float Fstar7_MINI, float Fesc7_MINI, + float Mlim_Fstar_MINI, float Mlim_Fesc_MINI, int method, int method_mini, + bool minihalos, bool prev); +void initialise_SFRD_Conditional_table(double min_density, double max_density, double growthf, + float Mcrit_atom, double Mmin, double Mmax, double Mcond, float Alpha_star, float Alpha_star_mini, + float Fstar10, float Fstar7_MINI, int method, int method_mini, bool minihalos); + +void initialise_dNdM_tables(double xmin, double xmax, double ymin, double ymax, double growth1, double param, bool from_catalog); +void initialise_dNdM_inverse_table(double xmin, double xmax, double lnM_min, double growth1, double param, bool from_catalog); + +//Evaluation +double EvaluateNionTs(double redshift, double Mlim_Fstar, double Mlim_Fesc); +double EvaluateNionTs_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI, double Mlim_Fesc_MINI); +double EvaluateSFRD(double redshift, double Mlim_Fstar); +double EvaluateSFRD_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI); +double EvaluateSFRD_Conditional(double delta, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar); +double EvaluateSFRD_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar); +double EvaluateNion_Conditional(double delta, double log10Mturn, double growthf, double M_min, double M_max, double M_cond, double sigma_max, + double Mlim_Fstar, double Mlim_Fesc, bool prev); +double EvaluateNion_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, + double Mturn_a, double Mlim_Fstar, double Mlim_Fesc, bool prev); +double EvaluateNhalo(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta); +double EvaluateMcoll(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta); +double EvaluateNhaloInv(double condition, double prob); +double EvaluateFcoll_delta(double delta, double growthf, double sigma_min, double sigma_max); +double EvaluatedFcolldz(double delta, double redshift, double sigma_min, double sigma_max); +double EvaluateSigma(double lnM); +double EvaluatedSigmasqdm(double lnM); + +/*--------------------------------*/ + +/*HMF Integrals*/ +void initialise_GL(float lnM_Min, float lnM_Max); +double Nhalo_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method); +double Mcoll_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method); +double Nion_ConditionalM(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, + double Alpha_star, double Alpha_esc, double Fstar10, double Fesc10, double Mlim_Fstar, + double Mlim_Fesc, int method); +double Nion_ConditionalM_MINI(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, + double MassTurnover_upper, double Alpha_star, double Alpha_esc, double Fstar7, + double Fesc7, double Mlim_Fstar, double Mlim_Fesc, int method); +double Nion_General(double z, double lnM_Min, double lnM_Max, double MassTurnover, double Alpha_star, double Alpha_esc, double Fstar10, + double Fesc10, double Mlim_Fstar, double Mlim_Fesc); +double Nion_General_MINI(double z, double lnM_Min, double lnM_Max, double MassTurnover, double MassTurnover_upper, double Alpha_star, + double Alpha_esc, double Fstar7_MINI, double Fesc7_MINI, double Mlim_Fstar, double Mlim_Fesc); +double Fcoll_General(double z, double lnM_min, double lnM_max); +double unconditional_mf(double growthf, double lnM, double z, int HMF); +double conditional_mf(double growthf, double lnM, double delta_cond, double sigma_cond, int HMF); +/*----------------------------*/ + +/* Error framework testing */ +int SomethingThatCatches(bool sub_func); +int FunctionThatCatches(bool sub_func, bool pass, double* result); +void FunctionThatThrows(); +/*------------------------*/ + +/* Test Outputs For Specific Models */ +int single_test_sample( UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options, + int seed, int n_condition, float *conditions, int *cond_crd, double z_out, double z_in, int *out_n_tot, int *out_n_cell, double *out_n_exp, + double *out_m_cell, double *out_m_exp, float *out_halo_masses, int *out_halo_coords); +//test function for getting halo properties from the wrapper, can use a lot of memory for large catalogs +int test_halo_props(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, + FlagOptions * flag_options, float * vcb_grid, float *J21_LW_grid, float *z_re_grid, float *Gamma12_ion_grid, + PerturbHaloField *halos, float *halo_props_out); + +/* Miscellaneous exposed functions for testing */ +double dicke(double z); +double sigma_z0(double M); +double dsigmasqdm_z0(double M); +double get_delta_crit(int HMF, double sigma, double growthf); +double atomic_cooling_threshold(float z); +double expected_nhalo(double redshift, UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions * flag_options); +/*-----------------------*/ diff --git a/src/py21cmfast/src/_inputparams_wrapper.h b/src/py21cmfast/src/_inputparams_wrapper.h new file mode 100644 index 00000000..67a2285c --- /dev/null +++ b/src/py21cmfast/src/_inputparams_wrapper.h @@ -0,0 +1,212 @@ +/*We need to explicitly define the types used by the warpper using ffi.cdef() + However, that function does not take directives, so we separate the types here +*/ +//WARNING: DO NOT #include THIS FILE IN THE C CODE EXCEPT FOR IN InputParameters.h + + +typedef struct CosmoParams{ + + float SIGMA_8; + float hlittle; + float OMm; + float OMl; + float OMb; + float POWER_INDEX; + +} CosmoParams; + +typedef struct UserParams{ + // Parameters taken from INIT_PARAMS.H + int HII_DIM; + int DIM; + float BOX_LEN; + float NON_CUBIC_FACTOR; + bool USE_FFTW_WISDOM; + int HMF; + int USE_RELATIVE_VELOCITIES; + int POWER_SPECTRUM; + int N_THREADS; + bool PERTURB_ON_HIGH_RES; + bool NO_RNG; + bool USE_INTERPOLATION_TABLES; + int INTEGRATION_METHOD_ATOMIC; + int INTEGRATION_METHOD_MINI; + bool USE_2LPT; + bool MINIMIZE_MEMORY; + bool KEEP_3D_VELOCITIES; + + //Halo Sampler Options + float SAMPLER_MIN_MASS; + double SAMPLER_BUFFER_FACTOR; + float MAXHALO_FACTOR; + int N_COND_INTERP; + int N_PROB_INTERP; + double MIN_LOGPROB; + int SAMPLE_METHOD; + bool AVG_BELOW_SAMPLER; + double HALOMASS_CORRECTION; + double PARKINSON_G0; + double PARKINSON_y1; + double PARKINSON_y2; +} UserParams; + +typedef struct AstroParams{ + // Parameters taken from INIT_PARAMS.H + float HII_EFF_FACTOR; + + //SHMR + float F_STAR10; + float ALPHA_STAR; + float ALPHA_STAR_MINI; + float SIGMA_STAR; + float CORR_STAR; + double UPPER_STELLAR_TURNOVER_MASS; + double UPPER_STELLAR_TURNOVER_INDEX; + float F_STAR7_MINI; + + //SFMS + float t_STAR; + float CORR_SFR; + double SIGMA_SFR_INDEX; + double SIGMA_SFR_LIM; + + //L_X/SFR + double L_X; + double L_X_MINI; + double SIGMA_LX; + double CORR_LX; + + //Escape Fraction + float F_ESC10; + float ALPHA_ESC; + float F_ESC7_MINI; + + float M_TURN; + float R_BUBBLE_MAX; + float ION_Tvir_MIN; + double F_H2_SHIELD; + float NU_X_THRESH; + float X_RAY_SPEC_INDEX; + float X_RAY_Tvir_MIN; + + double A_LW; + double BETA_LW; + double A_VCB; + double BETA_VCB; + + int N_RSD_STEPS; +} AstroParams; + +typedef struct FlagOptions{ + // Parameters taken from INIT_PARAMS.H + bool USE_HALO_FIELD; + bool USE_MINI_HALOS; + bool USE_CMB_HEATING; //CMB Heating Flag + bool USE_LYA_HEATING; //Lya Heating Flag + bool USE_MASS_DEPENDENT_ZETA; + bool SUBCELL_RSD; + bool APPLY_RSDS; + bool INHOMO_RECO; + bool USE_TS_FLUCT; + bool M_MIN_in_Mass; + bool FIX_VCB_AVG; + bool HALO_STOCHASTICITY; + bool USE_EXP_FILTER; + bool FIXED_HALO_GRIDS; + bool CELL_RECOMB; + int PHOTON_CONS_TYPE; + bool USE_UPPER_STELLAR_TURNOVER; +} FlagOptions; + +typedef struct GlobalParams{ + float ALPHA_UVB; + int EVOLVE_DENSITY_LINEARLY; + int SMOOTH_EVOLVED_DENSITY_FIELD; + float R_smooth_density; + float HII_ROUND_ERR; + int FIND_BUBBLE_ALGORITHM; + int N_POISSON; + int T_USE_VELOCITIES; + float MAX_DVDR; + float DELTA_R_HII_FACTOR; + float DELTA_R_FACTOR; + int HII_FILTER; + float INITIAL_REDSHIFT; + float R_OVERLAP_FACTOR; + int DELTA_CRIT_MODE; + int HALO_FILTER; + int OPTIMIZE; + float OPTIMIZE_MIN_MASS; + + + float CRIT_DENS_TRANSITION; + float MIN_DENSITY_LOW_LIMIT; + + int RecombPhotonCons; + float PhotonConsStart; + float PhotonConsEnd; + float PhotonConsAsymptoteTo; + float PhotonConsEndCalibz; + int PhotonConsSmoothing; + + int HEAT_FILTER; + double CLUMPING_FACTOR; + float Z_HEAT_MAX; + float R_XLy_MAX; + int NUM_FILTER_STEPS_FOR_Ts; + float ZPRIME_STEP_FACTOR; + double TK_at_Z_HEAT_MAX; + double XION_at_Z_HEAT_MAX; + int Pop; + float Pop2_ion; + float Pop3_ion; + + float NU_X_BAND_MAX; + float NU_X_MAX; + + int NBINS_LF; + + int P_CUTOFF; + float M_WDM; + float g_x; + float OMn; + float OMk; + float OMr; + float OMtot; + float Y_He; + float wl; + float SHETH_b; + float SHETH_c; + double Zreion_HeII; + int FILTER; + + char *external_table_path; + char *wisdoms_path; + float R_BUBBLE_MIN; + float M_MIN_INTEGRAL; + float M_MAX_INTEGRAL; + + float T_RE; + + float VAVG; + + bool USE_ADIABATIC_FLUCTUATIONS; +}GlobalParams; + + +/* Previously, we had a few structures spread throughout the code e.g user_params_ufunc which + were all globally defined and separately broadcast at different times. Several of these were used + across different files and some inside #defines (e.g indexing.h), so for now I've combined + the parameter structures to avoid confusion (we shouldn't have the possibility of two files using + different parameters). + + In future we should have a parameter structure in each .c file containing ONLY parameters relevant to it + (look at HaloBox.c), and force the broadcast at each _compute() step (or even decorate any library call) + However this would require us to be very careful about initialising the globals when ANY function from that + file is called */ +extern UserParams *user_params_global; +extern CosmoParams *cosmo_params_global; +extern AstroParams *astro_params_global; +extern FlagOptions *flag_options_global; + +extern GlobalParams global_params; diff --git a/src/py21cmfast/src/_outputstructs_wrapper.h b/src/py21cmfast/src/_outputstructs_wrapper.h new file mode 100644 index 00000000..43924f52 --- /dev/null +++ b/src/py21cmfast/src/_outputstructs_wrapper.h @@ -0,0 +1,93 @@ +/*We need to explicitly define the types used by the warpper using ffi.cdef() + However, that function does not take directives, so we separate the types here +*/ +//WARNING: DO NOT #include THIS FILE IN THE C CODE EXCEPT FOR IN OutputStructs.h + +typedef struct InitialConditions{ + float *lowres_density, *lowres_vx, *lowres_vy, *lowres_vz, *lowres_vx_2LPT, *lowres_vy_2LPT, *lowres_vz_2LPT; + float *hires_density, *hires_vx, *hires_vy, *hires_vz, *hires_vx_2LPT, *hires_vy_2LPT, *hires_vz_2LPT; //cw addition + float *lowres_vcb; +} InitialConditions; + +typedef struct PerturbedField{ + float *density, *velocity_x, *velocity_y, *velocity_z; +} PerturbedField; + +typedef struct HaloField{ + long long unsigned int n_halos; + long long unsigned int buffer_size; + float *halo_masses; + int *halo_coords; + + //Halo properties for stochastic model + float *star_rng; + float *sfr_rng; + float *xray_rng; +} HaloField; + +typedef struct PerturbHaloField{ + long long unsigned int n_halos; + long long unsigned int buffer_size; + float *halo_masses; + int *halo_coords; + + //Halo properties for stochastic model + float *star_rng; + float *sfr_rng; + float *xray_rng; +} PerturbHaloField; + +typedef struct HaloBox{ + //Things that aren't used in radiation fields but useful outputs + float *halo_mass; + float *halo_stars; + float *halo_stars_mini; + int *count; + + //For IonisationBox.c and SpinTemperatureBox.c + float *n_ion; //weighted by F_ESC*PopN_ion + float *halo_sfr; //for x-rays and Ts stuff + float *halo_xray; + float *halo_sfr_mini; //for x-rays and Ts stuff + float *whalo_sfr; //SFR weighted by PopN_ion and F_ESC, used for Gamma12 + + //Average volume-weighted log10 Turnover masses are kept in order to compare with the expected MF integrals + double log10_Mcrit_ACG_ave; + double log10_Mcrit_MCG_ave; +} HaloBox; + +typedef struct XraySourceBox{ + float *filtered_sfr; + float *filtered_xray; + float *filtered_sfr_mini; + + double *mean_log10_Mcrit_LW; + double *mean_sfr; + double *mean_sfr_mini; +} XraySourceBox; + +typedef struct TsBox{ + float *Ts_box; + float *x_e_box; + float *Tk_box; + float *J_21_LW_box; +} TsBox; + +typedef struct IonizedBox{ + double mean_f_coll; + double mean_f_coll_MINI; + double log10_Mturnover_ave; + double log10_Mturnover_MINI_ave; + float *xH_box; + float *Gamma12_box; + float *MFP_box; + float *z_re_box; + float *dNrec_box; + float *temp_kinetic_all_gas; + float *Fcoll; + float *Fcoll_MINI; +} IonizedBox; + +typedef struct BrightnessTemp{ + float *brightness_temp; +} BrightnessTemp; diff --git a/src/py21cmfast/src/bubble_helper_progs.c b/src/py21cmfast/src/bubble_helper_progs.c index 22a16388..5077082c 100755 --- a/src/py21cmfast/src/bubble_helper_progs.c +++ b/src/py21cmfast/src/bubble_helper_progs.c @@ -1,9 +1,12 @@ -#ifndef _BUBBLE_HELPERS_ -#define _BUBBLE_HELPERS_ +#include + +#include "bubble_helper_progs.h" +#include "indexing.h" /* all lengths are in units of the box size (x,y,z) is the closest reflection of (x2,y2,z2) to (x1, y1, z1) + JD: this is UNUSED Currently */ float distance_coord(float x1, float y1, float z1, float x2, float y2, float z2, @@ -146,6 +149,7 @@ float distance_coord(float x1, float y1, float z1, /* all lengths are in units of the box size + JD: This is UNUSED currently */ float distance(float x1, float y1, float z1, float x2, float y2, float z2){ float minimumsq, xsq, ysq, zsq, xplussq, yplussq, zplussq, xminsq, yminsq, zminsq; @@ -382,6 +386,3 @@ void update_in_sphere(float * box, int dimensions, int dimensions_ncf, float R, check_region(box, dimensions, dimensions_ncf, Rsq_curr_index, x,y,z, xb_min, xb_max, yb_min, yb_max, zb_min, zl_min); check_region(box, dimensions, dimensions_ncf, Rsq_curr_index, x,y,z, xb_min, xb_max, yb_min, yb_max, zl_max, zb_max); } - - -#endif diff --git a/src/py21cmfast/src/bubble_helper_progs.h b/src/py21cmfast/src/bubble_helper_progs.h new file mode 100644 index 00000000..9a580724 --- /dev/null +++ b/src/py21cmfast/src/bubble_helper_progs.h @@ -0,0 +1,8 @@ +/* Function prototypes for bubble_helper_progs.c */ +#ifndef _BUBBLEHELP_H +#define _BUBBLEHELP_H + +//NOTE: This file is only used for the old bubble finding algorithm which updates the whole sphere +void update_in_sphere(float * box, int dimensions, int dimensions_ncf, float R, float xf, float yf, float zf); + +#endif diff --git a/src/py21cmfast/src/cosmology.c b/src/py21cmfast/src/cosmology.c new file mode 100644 index 00000000..c2957edc --- /dev/null +++ b/src/py21cmfast/src/cosmology.c @@ -0,0 +1,788 @@ +/* This file contatins functions regarding the matter power-sepctrum and cosmology */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Constants.h" +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "InputParameters.h" + +#include "cosmology.h" + +//These globals hold values initialised in init_ps() and used throughout the rest of the file +static double sigma_norm, theta_cmb, omhh, z_equality, y_d, sound_horizon, alpha_nu, f_nu, f_baryon, beta_c, d2fact, R_CUTOFF; + +/* + this function reads the z=0 matter (CDM+baryons) and relative velocity transfer functions from CLASS (from a file) + flag_int = 0 to initialize interpolator, flag_int = -1 to free memory, flag_int = else to interpolate. + flag_dv = 0 to output density, flag_dv = 1 to output velocity. + similar to built-in function "double T_RECFAST(float z, int flag)" +*/ + +// FUNCTION TFmdm is the power spectrum transfer function from Eisenstein & Hu ApJ, 1999, 511, 5 +double TFmdm(double k){ + double q, gamma_eff, q_eff, TF_m, q_nu; + + q = k*pow(theta_cmb,2)/omhh; + gamma_eff=sqrt(alpha_nu) + (1.0-sqrt(alpha_nu))/(1.0+pow(0.43*k*sound_horizon, 4)); + q_eff = q/gamma_eff; + TF_m= log(E+1.84*beta_c*sqrt(alpha_nu)*q_eff); + TF_m /= TF_m + pow(q_eff,2) * (14.4 + 325.0/(1.0+60.5*pow(q_eff,1.11))); + q_nu = 3.92*q/sqrt(f_nu/N_nu); + TF_m *= 1.0 + (1.2*pow(f_nu,0.64)*pow(N_nu,0.3+0.6*f_nu)) / + (pow(q_nu,-1.6)+pow(q_nu,0.8)); + + return TF_m; +} + +double TF_CLASS(double k, int flag_int, int flag_dv) +{ + static double kclass[CLASS_LENGTH], Tmclass[CLASS_LENGTH], Tvclass_vcb[CLASS_LENGTH]; + static gsl_interp_accel *acc_density, *acc_vcb; + static gsl_spline *spline_density, *spline_vcb; + float currk, currTm, currTv; + double ans; + int i; + int gsl_status; + FILE *F; + + char filename[500]; + sprintf(filename,"%s/%s",global_params.external_table_path,CLASS_FILENAME); + + + if (flag_int == 0) { // Initialize vectors and read file + if (!(F = fopen(filename, "r"))) { + LOG_ERROR("Unable to open file: %s for reading.", filename); + Throw(IOError); + } + + int nscans; + for (i = 0; i < CLASS_LENGTH; i++) { + nscans = fscanf(F, "%e %e %e ", &currk, &currTm, &currTv); + if (nscans != 3) { + LOG_ERROR("Reading CLASS Transfer Function failed."); + Throw(IOError); + } + kclass[i] = currk; + Tmclass[i] = currTm; + Tvclass_vcb[i] = currTv; + if (i > 0 && kclass[i] <= kclass[i - 1]) { + LOG_WARNING("Tk table not ordered"); + LOG_WARNING("k=%.1le kprev=%.1le", kclass[i], kclass[i - 1]); + } + } + fclose(F); + + + LOG_SUPER_DEBUG("Read CLASS Transfer file"); + + gsl_set_error_handler_off(); + // Set up spline table for densities + acc_density = gsl_interp_accel_alloc (); + spline_density = gsl_spline_alloc (gsl_interp_cspline, CLASS_LENGTH); + gsl_status = gsl_spline_init(spline_density, kclass, Tmclass, CLASS_LENGTH); + CATCH_GSL_ERROR(gsl_status); + + LOG_SUPER_DEBUG("Generated CLASS Density Spline."); + + //Set up spline table for velocities + acc_vcb = gsl_interp_accel_alloc (); + spline_vcb = gsl_spline_alloc (gsl_interp_cspline, CLASS_LENGTH); + gsl_status = gsl_spline_init(spline_vcb, kclass, Tvclass_vcb, CLASS_LENGTH); + CATCH_GSL_ERROR(gsl_status); + + LOG_SUPER_DEBUG("Generated CLASS velocity Spline."); + return 0; + } + else if (flag_int == -1) { + gsl_spline_free (spline_density); + gsl_interp_accel_free(acc_density); + gsl_spline_free (spline_vcb); + gsl_interp_accel_free(acc_vcb); + return 0; + } + + + if (k > kclass[CLASS_LENGTH-1]) { // k>kmax + LOG_WARNING("Called TF_CLASS with k=%f, larger than kmax! Returning value at kmax.", k); + if(flag_dv == 0){ // output is density + return (Tmclass[CLASS_LENGTH]/kclass[CLASS_LENGTH-1]/kclass[CLASS_LENGTH-1]); + } + else if(flag_dv == 1){ // output is rel velocity + return (Tvclass_vcb[CLASS_LENGTH]/kclass[CLASS_LENGTH-1]/kclass[CLASS_LENGTH-1]); + } //we just set it to the last value, since sometimes it wants large k for R<R conversion). +// The sigma is evaluated at z=0, with the time evolution contained in the dicke(z) factor, +// i.e. sigma(M,z) = sigma_z0(m) * dicke(z) + +// normalized so that sigma_z0(M->8/h Mpc) = SIGMA8 in ../Parameter_files/COSMOLOGY.H + +// NOTE: volume is normalized to = 1, so this is equvalent to the mass standard deviation + +// M is in solar masses + +// References: Padmanabhan, pg. 210, eq. 5.107 +double dsigma_dk(double k, void *params){ + double p, w, T, gamma, q, aa, bb, cc, kR; + + // get the power spectrum.. choice of 5: + if (user_params_global->POWER_SPECTRUM == 0){ // Eisenstein & Hu + T = TFmdm(k); + // check if we should cuttoff power spectrum according to Bode et al. 2000 transfer function + if (global_params.P_CUTOFF) T *= pow(1 + pow(BODE_e*k*R_CUTOFF, 2*BODE_v), -BODE_n/BODE_v); + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + } + else if (user_params_global->POWER_SPECTRUM == 1){ // BBKS + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb/cosmo_params_global->OMm)); + q = k / (cosmo_params_global->hlittle*gamma); + T = (log(1.0+2.34*q)/(2.34*q)) * + pow( 1.0+3.89*q + pow(16.1*q, 2) + pow( 5.46*q, 3) + pow(6.71*q, 4), -0.25); + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + } + else if (user_params_global->POWER_SPECTRUM == 2){ // Efstathiou,G., Bond,J.R., and White,S.D.M., MNRAS,258,1P (1992) + gamma = 0.25; + aa = 6.4/(cosmo_params_global->hlittle*gamma); + bb = 3.0/(cosmo_params_global->hlittle*gamma); + cc = 1.7/(cosmo_params_global->hlittle*gamma); + p = pow(k, cosmo_params_global->POWER_INDEX) / pow( 1+pow( aa*k + pow(bb*k, 1.5) + pow(cc*k,2), 1.13), 2.0/1.13 ); + } + else if (user_params_global->POWER_SPECTRUM == 3){ // Peebles, pg. 626 + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb/cosmo_params_global->OMm)); + aa = 8.0 / (cosmo_params_global->hlittle*gamma); + bb = 4.7 / pow(cosmo_params_global->hlittle*gamma, 2); + p = pow(k, cosmo_params_global->POWER_INDEX) / pow(1 + aa*k + bb*k*k, 2); + } + else if (user_params_global->POWER_SPECTRUM == 4){ // White, SDM and Frenk, CS, 1991, 379, 52 + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb/cosmo_params_global->OMm)); + aa = 1.7/(cosmo_params_global->hlittle*gamma); + bb = 9.0/pow(cosmo_params_global->hlittle*gamma, 1.5); + cc = 1.0/pow(cosmo_params_global->hlittle*gamma, 2); + p = pow(k, cosmo_params_global->POWER_INDEX) * 19400.0 / pow(1 + aa*k + bb*pow(k, 1.5) + cc*k*k, 2); + } + else if (user_params_global->POWER_SPECTRUM == 5){ // output of CLASS + T = TF_CLASS(k, 1, 0); //read from z=0 output of CLASS. Note, flag_int = 1 here always, since now we have to have initialized the interpolator for CLASS + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + if(user_params_global->USE_RELATIVE_VELOCITIES) { //jbm:Add average relvel suppression + p *= 1.0 - A_VCB_PM*exp( -pow(log(k/KP_VCB_PM),2.0)/(2.0*SIGMAK_VCB_PM*SIGMAK_VCB_PM)); //for v=vrms + } + } + else{ + LOG_ERROR("No such power spectrum defined: %i. Output is bogus.", user_params_global->POWER_SPECTRUM); + Throw(ValueError); + } + double Radius; + + Radius = *(double *)params; + + kR = k*Radius; + + if ( (global_params.FILTER == 0) || (sigma_norm < 0) ){ // top hat + if ( (kR) < 1.0e-4 ){ w = 1.0;} // w converges to 1 as (kR) -> 0 + else { w = 3.0 * (sin(kR)/pow(kR, 3) - cos(kR)/pow(kR, 2));} + } + else if (global_params.FILTER == 1){ // gaussian of width 1/R + w = pow(E, -kR*kR/2.0); + } + else { + LOG_ERROR("No such filter: %i. Output is bogus.", global_params.FILTER); + Throw(ValueError); + } + return k*k*p*w*w; +} +double sigma_z0(double M){ + + double result, error, lower_limit, upper_limit; + gsl_function F; + double rel_tol = FRACT_FLOAT_ERR*10; //<- relative tolerance + gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000); + double kstart, kend; + + double Radius; +// R = MtoR(M); + + Radius = MtoR(M); + // now lets do the integral for sigma and scale it with sigma_norm + + if(user_params_global->POWER_SPECTRUM == 5){ + kstart = fmax(1.0e-99/Radius, KBOT_CLASS); + kend = fmin(350.0/Radius, KTOP_CLASS); + }//we establish a maximum k of KTOP_CLASS~1e3 Mpc-1 and a minimum at KBOT_CLASS,~1e-5 Mpc-1 since the CLASS transfer function has a max! + else{ + kstart = 1.0e-99/Radius; + kend = 350.0/Radius; + } + + lower_limit = kstart;//log(kstart); + upper_limit = kend;//log(kend); + + F.function = &dsigma_dk; + F.params = &Radius; + + int status; + + gsl_set_error_handler_off(); + + status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol,1000, GSL_INTEG_GAUSS61, w, &result, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); + LOG_ERROR("data: M=%e",M); + CATCH_GSL_ERROR(status); + } + + gsl_integration_workspace_free (w); + + return sigma_norm * sqrt(result); +} + +void TFset_parameters(){ + double z_drag, R_drag, R_equality, p_c, p_cb, f_c, f_cb, f_nub, k_equality; + + LOG_DEBUG("Setting Transfer Function parameters."); + + z_equality = 25000*omhh*pow(theta_cmb, -4) - 1.0; + k_equality = 0.0746*omhh/(theta_cmb*theta_cmb); + + z_drag = 0.313*pow(omhh,-0.419) * (1 + 0.607*pow(omhh, 0.674)); + z_drag = 1 + z_drag*pow(cosmo_params_global->OMb*cosmo_params_global->hlittle*cosmo_params_global->hlittle, 0.238*pow(omhh, 0.223)); + z_drag *= 1291 * pow(omhh, 0.251) / (1 + 0.659*pow(omhh, 0.828)); + + y_d = (1 + z_equality) / (1.0 + z_drag); + + R_drag = 31.5 * cosmo_params_global->OMb*cosmo_params_global->hlittle*cosmo_params_global->hlittle * pow(theta_cmb, -4) * 1000 / (1.0 + z_drag); + R_equality = 31.5 * cosmo_params_global->OMb*cosmo_params_global->hlittle*cosmo_params_global->hlittle * pow(theta_cmb, -4) * 1000 / (1.0 + z_equality); + + sound_horizon = 2.0/3.0/k_equality * sqrt(6.0/R_equality) * + log( (sqrt(1+R_drag) + sqrt(R_drag+R_equality)) / (1.0 + sqrt(R_equality)) ); + + p_c = -(5 - sqrt(1 + 24*(1 - f_nu-f_baryon)))/4.0; + p_cb = -(5 - sqrt(1 + 24*(1 - f_nu)))/4.0; + f_c = 1 - f_nu - f_baryon; + f_cb = 1 - f_nu; + f_nub = f_nu+f_baryon; + + alpha_nu = (f_c/f_cb) * (2*(p_c+p_cb)+5)/(4*p_cb+5.0); + alpha_nu *= 1 - 0.553*f_nub+0.126*pow(f_nub,3); + alpha_nu /= 1-0.193*sqrt(f_nu)+0.169*f_nu; + alpha_nu *= pow(1+y_d, p_c-p_cb); + alpha_nu *= 1+ (p_cb-p_c)/2.0 * (1.0+1.0/(4.0*p_c+3.0)/(4.0*p_cb+7.0))/(1.0+y_d); + beta_c = 1.0/(1.0-0.949*f_nub); +} + + +// Returns the value of the linear power spectrum DENSITY (i.e. <|delta_k|^2>/V) +// at a given k mode linearly extrapolated to z=0 +double power_in_k(double k){ + double p, T, gamma, q, aa, bb, cc; + + // get the power spectrum.. choice of 5: + if (user_params_global->POWER_SPECTRUM == 0){ // Eisenstein & Hu + T = TFmdm(k); + // check if we should cuttoff power spectrum according to Bode et al. 2000 transfer function + if (global_params.P_CUTOFF) T *= pow(1 + pow(BODE_e*k*R_CUTOFF, 2*BODE_v), -BODE_n/BODE_v); + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + //p = pow(k, POWER_INDEX - 0.05*log(k/0.05)) * T * T; //running, alpha=0.05 + } + else if (user_params_global->POWER_SPECTRUM == 1){ // BBKS + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb/cosmo_params_global->OMm)); + q = k / (cosmo_params_global->hlittle*gamma); + T = (log(1.0+2.34*q)/(2.34*q)) * + pow( 1.0+3.89*q + pow(16.1*q, 2) + pow( 5.46*q, 3) + pow(6.71*q, 4), -0.25); + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + } + else if (user_params_global->POWER_SPECTRUM == 2){ // Efstathiou,G., Bond,J.R., and White,S.D.M., MNRAS,258,1P (1992) + gamma = 0.25; + aa = 6.4/(cosmo_params_global->hlittle*gamma); + bb = 3.0/(cosmo_params_global->hlittle*gamma); + cc = 1.7/(cosmo_params_global->hlittle*gamma); + p = pow(k, cosmo_params_global->POWER_INDEX) / pow( 1+pow( aa*k + pow(bb*k, 1.5) + pow(cc*k,2), 1.13), 2.0/1.13 ); + } + else if (user_params_global->POWER_SPECTRUM == 3){ // Peebles, pg. 626 + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb)/(cosmo_params_global->OMm)); + aa = 8.0 / (cosmo_params_global->hlittle*gamma); + bb = 4.7 / pow(cosmo_params_global->hlittle*gamma, 2); + p = pow(k, cosmo_params_global->POWER_INDEX) / pow(1 + aa*k + bb*k*k, 2); + } + else if (user_params_global->POWER_SPECTRUM == 4){ // White, SDM and Frenk, CS, 1991, 379, 52 + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb/cosmo_params_global->OMm)); + aa = 1.7/(cosmo_params_global->hlittle*gamma); + bb = 9.0/pow(cosmo_params_global->hlittle*gamma, 1.5); + cc = 1.0/pow(cosmo_params_global->hlittle*gamma, 2); + p = pow(k, cosmo_params_global->POWER_INDEX) * 19400.0 / pow(1 + aa*k + bb*pow(k, 1.5) + cc*k*k, 2); + } + else if (user_params_global->POWER_SPECTRUM == 5){ // output of CLASS + T = TF_CLASS(k, 1, 0); //read from z=0 output of CLASS. Note, flag_int = 1 here always, since now we have to have initialized the interpolator for CLASS + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + if(user_params_global->USE_RELATIVE_VELOCITIES) { //jbm:Add average relvel suppression + p *= 1.0 - A_VCB_PM*exp( -pow(log(k/KP_VCB_PM),2.0)/(2.0*SIGMAK_VCB_PM*SIGMAK_VCB_PM)); //for v=vrms + } + } + else{ + LOG_ERROR("No such power spectrum defined: %i. Output is bogus.", user_params_global->POWER_SPECTRUM); + Throw(ValueError); + } + + + return p*TWOPI*PI*sigma_norm*sigma_norm; +} + + +/* + Returns the value of the linear power spectrum of the DM-b relative velocity + at kinematic decoupling (which we set at zkin=1010) +*/ +double power_in_vcb(double k){ + + double p, T; + + //only works if using CLASS + if (user_params_global->POWER_SPECTRUM == 5){ // CLASS + T = TF_CLASS(k, 1, 1); //read from CLASS file. flag_int=1 since we have initialized before, flag_vcb=1 for velocity + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + } + else{ + LOG_ERROR("Cannot get P_cb unless using CLASS: %i\n Set USE_RELATIVE_VELOCITIES 0 or use CLASS.\n", user_params_global->POWER_SPECTRUM); + Throw(ValueError); + } + + return p*TWOPI*PI*sigma_norm*sigma_norm; +} + + +double init_ps(){ + double result, error, lower_limit, upper_limit; + gsl_function F; + double rel_tol = FRACT_FLOAT_ERR*10; //<- relative tolerance + gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000); + double kstart, kend; + + //we start the interpolator if using CLASS: + if (user_params_global->POWER_SPECTRUM == 5){ + LOG_DEBUG("Setting CLASS Transfer Function inits."); + TF_CLASS(1.0, 0, 0); + } + + // Set cuttoff scale for WDM (eq. 4 in Barkana et al. 2001) in comoving Mpc + R_CUTOFF = 0.201*pow((cosmo_params_global->OMm-cosmo_params_global->OMb)*cosmo_params_global->hlittle*cosmo_params_global->hlittle/0.15, 0.15)*pow(global_params.g_x/1.5, -0.29)*pow(global_params.M_WDM, -1.15); + + omhh = cosmo_params_global->OMm*cosmo_params_global->hlittle*cosmo_params_global->hlittle; + theta_cmb = T_cmb / 2.7; + + // Translate Parameters into forms GLOBALVARIABLES form + f_nu = global_params.OMn/cosmo_params_global->OMm; + f_baryon = cosmo_params_global->OMb/cosmo_params_global->OMm; + if (f_nu < TINY) f_nu = 1e-10; + if (f_baryon < TINY) f_baryon = 1e-10; + + TFset_parameters(); + + sigma_norm = -1; + + double Radius_8; + Radius_8 = 8.0/cosmo_params_global->hlittle; + + if(user_params_global->POWER_SPECTRUM == 5){ + kstart = fmax(1.0e-99/Radius_8, KBOT_CLASS); + kend = fmin(350.0/Radius_8, KTOP_CLASS); + }//we establish a maximum k of KTOP_CLASS~1e3 Mpc-1 and a minimum at KBOT_CLASS,~1e-5 Mpc-1 since the CLASS transfer function has a max! + else{ + kstart = 1.0e-99/Radius_8; + kend = 350.0/Radius_8; + } + + lower_limit = kstart; + upper_limit = kend; + + LOG_DEBUG("Initializing Power Spectrum with lower_limit=%e, upper_limit=%e, rel_tol=%e, radius_8=%g", lower_limit,upper_limit, rel_tol, Radius_8); + + F.function = &dsigma_dk; + F.params = &Radius_8; + + int status; + + gsl_set_error_handler_off(); + + status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol, + 1000, GSL_INTEG_GAUSS61, w, &result, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); + CATCH_GSL_ERROR(status); + } + + gsl_integration_workspace_free (w); + + LOG_DEBUG("Initialized Power Spectrum."); + + sigma_norm = cosmo_params_global->SIGMA_8/sqrt(result); //takes care of volume factor + return R_CUTOFF; +} + + + + +//function to free arrays related to the power spectrum +void free_ps(){ + + //we free the PS interpolator if using CLASS: + if (user_params_global->POWER_SPECTRUM == 5){ + TF_CLASS(1.0, -1, 0); + } + + return; +} + + + +/* + FUNCTION dsigmasqdm_z0(M) + returns d/dm (sigma^2) (see function sigma), in units of Msun^-1 + */ +double dsigmasq_dm(double k, void *params){ + double p, w, T, gamma, q, aa, bb, cc, dwdr, drdm, kR; + + // get the power spectrum.. choice of 5: + if (user_params_global->POWER_SPECTRUM == 0){ // Eisenstein & Hu ApJ, 1999, 511, 5 + T = TFmdm(k); + // check if we should cuttoff power spectrum according to Bode et al. 2000 transfer function + if (global_params.P_CUTOFF) T *= pow(1 + pow(BODE_e*k*R_CUTOFF, 2*BODE_v), -BODE_n/BODE_v); + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + //p = pow(k, POWER_INDEX - 0.05*log(k/0.05)) * T * T; //running, alpha=0.05 + } + else if (user_params_global->POWER_SPECTRUM == 1){ // BBKS + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb)/(cosmo_params_global->OMm)); + q = k / (cosmo_params_global->hlittle*gamma); + T = (log(1.0+2.34*q)/(2.34*q)) * + pow( 1.0+3.89*q + pow(16.1*q, 2) + pow( 5.46*q, 3) + pow(6.71*q, 4), -0.25); + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + } + else if (user_params_global->POWER_SPECTRUM == 2){ // Efstathiou,G., Bond,J.R., and White,S.D.M., MNRAS,258,1P (1992) + gamma = 0.25; + aa = 6.4/(cosmo_params_global->hlittle*gamma); + bb = 3.0/(cosmo_params_global->hlittle*gamma); + cc = 1.7/(cosmo_params_global->hlittle*gamma); + p = pow(k, cosmo_params_global->POWER_INDEX) / pow( 1+pow( aa*k + pow(bb*k, 1.5) + pow(cc*k,2), 1.13), 2.0/1.13 ); + } + else if (user_params_global->POWER_SPECTRUM == 3){ // Peebles, pg. 626 + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb)/(cosmo_params_global->OMm)); + aa = 8.0 / (cosmo_params_global->hlittle*gamma); + bb = 4.7 / (cosmo_params_global->hlittle*gamma); + p = pow(k, cosmo_params_global->POWER_INDEX) / pow(1 + aa*k + bb*k*k, 2); + } + else if (user_params_global->POWER_SPECTRUM == 4){ // White, SDM and Frenk, CS, 1991, 379, 52 + gamma = cosmo_params_global->OMm * cosmo_params_global->hlittle * pow(E, -(cosmo_params_global->OMb) - (cosmo_params_global->OMb)/(cosmo_params_global->OMm)); + aa = 1.7/(cosmo_params_global->hlittle*gamma); + bb = 9.0/pow(cosmo_params_global->hlittle*gamma, 1.5); + cc = 1.0/pow(cosmo_params_global->hlittle*gamma, 2); + p = pow(k, cosmo_params_global->POWER_INDEX) * 19400.0 / pow(1 + aa*k + pow(bb*k, 1.5) + cc*k*k, 2); + } + else if (user_params_global->POWER_SPECTRUM == 5){ // JBM: CLASS + T = TF_CLASS(k, 1, 0); //read from z=0 output of CLASS + p = pow(k, cosmo_params_global->POWER_INDEX) * T * T; + if(user_params_global->USE_RELATIVE_VELOCITIES) { //jbm:Add average relvel suppression + p *= 1.0 - A_VCB_PM*exp( -pow(log(k/KP_VCB_PM),2.0)/(2.0*SIGMAK_VCB_PM*SIGMAK_VCB_PM)); //for v=vrms + } + } + else{ + LOG_ERROR("No such power spectrum defined: %i. Output is bogus.", user_params_global->POWER_SPECTRUM); + Throw(ValueError); + } + + double Radius; + Radius = *(double *)params; + + // now get the value of the window function + kR = k * Radius; + if (global_params.FILTER == 0){ // top hat + if ( (kR) < 1.0e-4 ){ w = 1.0; }// w converges to 1 as (kR) -> 0 + else { w = 3.0 * (sin(kR)/pow(kR, 3) - cos(kR)/pow(kR, 2));} + + // now do d(w^2)/dm = 2 w dw/dr dr/dm + if ( (kR) < 1.0e-10 ){ dwdr = 0;} + else{ dwdr = 9*cos(kR)*k/pow(kR,3) + 3*sin(kR)*(1 - 3/(kR*kR))/(kR*Radius);} + //3*k*( 3*cos(kR)/pow(kR,3) + sin(kR)*(-3*pow(kR, -4) + 1/(kR*kR)) );} + // dwdr = -1e8 * k / (R*1e3); + drdm = 1.0 / (4.0*PI * cosmo_params_global->OMm*RHOcrit * Radius*Radius); + } + else if (global_params.FILTER == 1){ // gaussian of width 1/R + w = pow(E, -kR*kR/2.0); + dwdr = - k*kR * w; + drdm = 1.0 / (pow(2*PI, 1.5) * cosmo_params_global->OMm*RHOcrit * 3*Radius*Radius); + } + else { + LOG_ERROR("No such filter: %i. Output is bogus.", global_params.FILTER); + Throw(ValueError); + } + +// return k*k*p*2*w*dwdr*drdm * d2fact; + return k*k*p*2*w*dwdr*drdm; +} +double dsigmasqdm_z0(double M){ + double result, error, lower_limit, upper_limit; + gsl_function F; + double rel_tol = FRACT_FLOAT_ERR*10; //<- relative tolerance + gsl_integration_workspace * w + = gsl_integration_workspace_alloc (1000); + double kstart, kend; + + double Radius; +// R = MtoR(M); + Radius = MtoR(M); + + // now lets do the integral for sigma and scale it with sigma_norm + if(user_params_global->POWER_SPECTRUM == 5){ + kstart = fmax(1.0e-99/Radius, KBOT_CLASS); + kend = fmin(350.0/Radius, KTOP_CLASS); + }//we establish a maximum k of KTOP_CLASS~1e3 Mpc-1 and a minimum at KBOT_CLASS,~1e-5 Mpc-1 since the CLASS transfer function has a max! + else{ + kstart = 1.0e-99/Radius; + kend = 350.0/Radius; + } + + lower_limit = kstart;//log(kstart); + upper_limit = kend;//log(kend); + + + if (user_params_global->POWER_SPECTRUM == 5){ // for CLASS we do not need to renormalize the sigma integral. + d2fact=1.0; + } + else { + d2fact = M*10000/sigma_z0(M); + } + + + F.function = &dsigmasq_dm; + F.params = &Radius; + + int status; + + gsl_set_error_handler_off(); + + status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol,1000, GSL_INTEG_GAUSS61, w, &result, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); + LOG_ERROR("data: M=%e",M); + CATCH_GSL_ERROR(status); + } + + gsl_integration_workspace_free (w); + +// return sigma_norm * sigma_norm * result /d2fact; + return sigma_norm * sigma_norm * result; +} + + +/* returns the "effective Jeans mass" in Msun + corresponding to the gas analog of WDM ; eq. 10 in Barkana+ 2001 */ +double M_J_WDM(){ + double z_eq, fudge=60; + if (!(global_params.P_CUTOFF)) + return 0; + z_eq = 3600*(cosmo_params_global->OMm-cosmo_params_global->OMb)*cosmo_params_global->hlittle*cosmo_params_global->hlittle/0.15; + return fudge*3.06e8 * (1.5/global_params.g_x) * sqrt((cosmo_params_global->OMm-cosmo_params_global->OMb)*cosmo_params_global->hlittle*cosmo_params_global->hlittle/0.15) * pow(global_params.M_WDM, -4) * pow(z_eq/3000.0, 1.5); +} + +/* redshift derivative of the growth function at z */ +double ddicke_dz(double z){ + float dz = 1e-10; + //NOTE: this isnt called a lot, I'm not sure why we don't use a similar method to ddicke_dt + return (dicke(z+dz)-dicke(z))/dz; +} + + +/* R in Mpc, M in Msun */ +double MtoR(double M){ + + // set R according to M<->R conversion defined by the filter type in ../Parameter_files/COSMOLOGY.H + if (global_params.FILTER == 0) //top hat M = (4/3) PI R^3 + return pow(3*M/(4*PI*cosmo_params_global->OMm*RHOcrit), 1.0/3.0); + else if (global_params.FILTER == 1) //gaussian: M = (2PI)^1.5 R^3 + return pow( M/(pow(2*PI, 1.5) * cosmo_params_global->OMm * RHOcrit), 1.0/3.0 ); + else // filter not defined + LOG_ERROR("No such filter = %i. Results are bogus.", global_params.FILTER); + Throw(ValueError); +} + +/* R in Mpc, M in Msun */ +double RtoM(double R){ + // set M according to M<->R conversion defined by the filter type in ../Parameter_files/COSMOLOGY.H + if (global_params.FILTER == 0) //top hat M = (4/3) PI R^3 + return (4.0/3.0)*PI*pow(R,3)*(cosmo_params_global->OMm*RHOcrit); + else if (global_params.FILTER == 1) //gaussian: M = (2PI)^1.5 R^3 + return pow(2*PI, 1.5) * cosmo_params_global->OMm*RHOcrit * pow(R, 3); + else // filter not defined + LOG_ERROR("No such filter = %i. Results are bogus.", global_params.FILTER); + Throw(ValueError); +} + +/* Omega matter at redshift z */ +double omega_mz(float z){ + return cosmo_params_global->OMm*pow(1+z,3) / (cosmo_params_global->OMm*pow(1+z,3) + cosmo_params_global->OMl + global_params.OMr*pow(1+z,4) + global_params.OMk*pow(1+z, 2)); +} + +/* Physical (non-linear) overdensity at virialization (relative to critical density) + i.e. answer is rho / rho_crit + In Einstein de sitter model = 178 + (fitting formula from Bryan & Norman 1998) */ +double Deltac_nonlinear(float z){ + double d; + d = omega_mz(z) - 1.0; + return 18*PI*PI + 82*d - 39*d*d; +} + +/* + T in K, M in Msun, mu is mean molecular weight + from Barkana & Loeb 2001 + + SUPRESS = 0 for no radiation field supression; + SUPRESS = 1 for supression (step function at z=z_ss, at v=v_zz) + */ +double TtoM(double z, double T, double mu){ + return 7030.97 / (cosmo_params_global->hlittle) * sqrt( omega_mz(z) / (cosmo_params_global->OMm*Deltac_nonlinear(z))) * + pow( T/(mu * (1+z)), 1.5 ); + /* if (!SUPRESS || (z >= z_re) ) // pre-reionization or don't worry about supression + return 7030.97 / hlittle * sqrt( omega_mz(z) / (OMm*Deltac_nonlinear(z)) ) * + pow( T/(mu * (1+z)), 1.5 ); + + if (z >= z_ss) // self-shielding dominates, use T = 1e4 K + return 7030.97 / hlittle * sqrt( omega_mz(z) / (OMm*Deltac_nonlinear(z)) ) * + pow( 1.0e4 /(mu * (1+z)), 1.5 ); + + // optically thin + return 7030.97 / hlittle * sqrt( omega_mz(z) / (OMm*Deltac_nonlinear(z)) ) * + pow( VcirtoT(v_ss, mu) /(mu * (1+z)), 1.5 ); + */ +} + +/* + FUNCTION dicke(z) + Computes the dicke growth function at redshift z, i.e. the z dependance part of sigma + + References: Peebles, "Large-Scale...", pg.53 (eq. 11.16). Includes omega<=1 + Nonzero Lambda case from Liddle et al, astro-ph/9512102, eqs. 6-8. + and quintessence case from Wang et al, astro-ph/9804015 + + Normalized to dicke(z=0)=1 + */ +double dicke(double z){ + double omegaM_z, dick_z, dick_0, x, x_0; + double tiny = 1e-4; + + if (fabs(cosmo_params_global->OMm-1.0) < tiny){ //OMm = 1 (Einstein de-Sitter) + return 1.0/(1.0+z); + } + else if ( (cosmo_params_global->OMl > (-tiny)) && (fabs(cosmo_params_global->OMl+cosmo_params_global->OMm+global_params.OMr-1.0) < 0.01) && (fabs(global_params.wl+1.0) < tiny) ){ + //this is a flat, cosmological CONSTANT universe, with only lambda, matter and radiation + //it is taken from liddle et al. + omegaM_z = cosmo_params_global->OMm*pow(1+z,3) / ( cosmo_params_global->OMl + cosmo_params_global->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4) ); + dick_z = 2.5*omegaM_z / ( 1.0/70.0 + omegaM_z*(209-omegaM_z)/140.0 + pow(omegaM_z, 4.0/7.0) ); + dick_0 = 2.5*cosmo_params_global->OMm / ( 1.0/70.0 + cosmo_params_global->OMm*(209-cosmo_params_global->OMm)/140.0 + pow(cosmo_params_global->OMm, 4.0/7.0) ); + return dick_z / (dick_0 * (1.0+z)); + } + else if ( (global_params.OMtot < (1+tiny)) && (fabs(cosmo_params_global->OMl) < tiny) ){ //open, zero lambda case (peebles, pg. 53) + x_0 = 1.0/(cosmo_params_global->OMm+0.0) - 1.0; + dick_0 = 1 + 3.0/x_0 + 3*log(sqrt(1+x_0)-sqrt(x_0))*sqrt(1+x_0)/pow(x_0,1.5); + x = fabs(1.0/(cosmo_params_global->OMm+0.0) - 1.0) / (1+z); + dick_z = 1 + 3.0/x + 3*log(sqrt(1+x)-sqrt(x))*sqrt(1+x)/pow(x,1.5); + return dick_z/dick_0; + } + else if ( (cosmo_params_global->OMl > (-tiny)) && (fabs(global_params.OMtot-1.0) < tiny) && (fabs(global_params.wl+1) > tiny) ){ + LOG_WARNING("IN WANG."); + Throw(ValueError); + } + + LOG_ERROR("No growth function!"); + Throw(ValueError); +} + +/* function DTDZ returns the value of dt/dz at the redshift parameter z. */ +double dtdz(float z){ + double x, dxdz, const1, denom, numer; + x = sqrt( cosmo_params_global->OMl/cosmo_params_global->OMm ) * pow(1+z, -3.0/2.0); + dxdz = sqrt( cosmo_params_global->OMl/cosmo_params_global->OMm ) * pow(1+z, -5.0/2.0) * (-3.0/2.0); + const1 = 2 * sqrt( 1 + cosmo_params_global->OMm/cosmo_params_global->OMl ) / (3.0 * Ho) ; + + numer = dxdz * (1 + x*pow( pow(x,2) + 1, -0.5)); + denom = x + sqrt(pow(x,2) + 1); + return (const1 * numer / denom); +} + +/* Time derivative of the growth function at z */ +double ddickedt(double z){ + float dz = 1e-10; + double omegaM_z, ddickdz, dick_0, domegaMdz; + double tiny = 1e-4; + + return (dicke(z+dz)-dicke(z))/dz/dtdz(z); // lazy non-analytic form getting + + if (fabs(cosmo_params_global->OMm-1.0) < tiny){ //OMm = 1 (Einstein de-Sitter) + return -pow(1+z,-2)/dtdz(z); + } + else if ( (cosmo_params_global->OMl > (-tiny)) && (fabs(cosmo_params_global->OMl+cosmo_params_global->OMm+global_params.OMr-1.0) < 0.01) && (fabs(global_params.wl+1.0) < tiny) ){ + //this is a flat, cosmological CONSTANT universe, with only lambda, matter and radiation + //it is taken from liddle et al. + omegaM_z = cosmo_params_global->OMm*pow(1+z,3) / ( cosmo_params_global->OMl + cosmo_params_global->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4) ); + domegaMdz = omegaM_z*3/(1+z) - cosmo_params_global->OMm*pow(1+z,3)*pow(cosmo_params_global->OMl + cosmo_params_global->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4), -2) * (3*cosmo_params_global->OMm*(1+z)*(1+z) + 4*global_params.OMr*pow(1+z,3)); + dick_0 = cosmo_params_global->OMm / ( 1.0/70.0 + cosmo_params_global->OMm*(209-cosmo_params_global->OMm)/140.0 + pow(cosmo_params_global->OMm, 4.0/7.0) ); + + ddickdz = (domegaMdz/(1+z)) * (1.0/70.0*pow(omegaM_z,-2) + 1.0/140.0 + 3.0/7.0*pow(omegaM_z, -10.0/3.0)) * pow(1.0/70.0/omegaM_z + (209.0-omegaM_z)/140.0 + pow(omegaM_z, -3.0/7.0) , -2); + ddickdz -= pow(1+z,-2)/(1.0/70.0/omegaM_z + (209.0-omegaM_z)/140.0 + pow(omegaM_z, -3.0/7.0)); + + return ddickdz / dick_0 / dtdz(z); + } + + LOG_ERROR("No growth function!"); + Throw(ValueError); +} + +/* returns the hubble "constant" (in 1/sec) at z */ +double hubble(float z){ + return Ho*sqrt(cosmo_params_global->OMm*pow(1+z,3) + global_params.OMr*pow(1+z,4) + cosmo_params_global->OMl); +} + + +/* returns hubble time (in sec), t_h = 1/H */ +double t_hubble(float z){ + return 1.0/hubble(z); +} + +/* comoving distance (in cm) per unit redshift */ +double drdz(float z){ + return (1.0+z)*C*dtdz(z); +} diff --git a/src/py21cmfast/src/cosmology.h b/src/py21cmfast/src/cosmology.h new file mode 100644 index 00000000..244b6b92 --- /dev/null +++ b/src/py21cmfast/src/cosmology.h @@ -0,0 +1,32 @@ +#ifndef _PS_H +#define _PS_H + +double init_ps(); +double dicke(double z); +double sigma_z0(double M); +double dsigmasqdm_z0(double M); + +double MtoR(double M); +double RtoM(double R); +double TtoM(double z, double T, double mu); + +void free_ps(); /* deallocates the gsl structures from init_ps */ +double power_in_k(double k); /* Returns the value of the linear power spectrum density (i.e. <|delta_k|^2>/V) at a given k mode at z=0 */ + +double TF_CLASS(double k, int flag_int, int flag_dv); //transfer function of matter (flag_dv=0) and relative velocities (flag_dv=1) fluctuations from CLASS +double power_in_vcb(double k); /* Returns the value of the DM-b relative velocity power spectrum density (i.e. <|delta_k|^2>/V) at a given k mode at z=0 */ + +double MtoR(double M); +double RtoM(double R); +double TtoM(double z, double T, double mu); +double dicke(double z); +double ddickedt(double z); +double ddicke_dz(double z); +double dtdz(float z); +double drdz(float z); /* comoving distance, (1+z)*C*dtdz(in cm) per unit z */ + +double hubble(float z); +double t_hubble(float z); +double M_J_WDM(); + +#endif diff --git a/src/py21cmfast/src/debugging.c b/src/py21cmfast/src/debugging.c new file mode 100644 index 00000000..f7f03d75 --- /dev/null +++ b/src/py21cmfast/src/debugging.c @@ -0,0 +1,284 @@ +/* + The following functions are simply for testing the exception framework +*/ + +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "InputParameters.h" +#include "OutputStructs.h" + +#include "debugging.h" + +//definition of the global exception context +struct exception_context the_exception_context[1]; + +void FunctionThatThrows(){ + Throw(PhotonConsError); +} + +int SomethingThatCatches(bool sub_func){ + // A simple function that catches a thrown error. + int status; + Try{ + if(sub_func) FunctionThatThrows(); + else Throw(PhotonConsError); + } + Catch(status){ + return status; + } + return 0; +} + +int FunctionThatCatches(bool sub_func, bool pass, double *result){ + int status; + if(!pass){ + Try{ + if(sub_func) FunctionThatThrows(); + else Throw(PhotonConsError); + } + Catch(status){ + LOG_DEBUG("Caught the problem with status %d.", status); + return status; + } + } + *result = 5.0; + return 0; +} +void writeUserParams(UserParams *p){ + LOG_INFO("\n UserParams:\n" + " HII_DIM=%4d, DIM=%4d, BOX_LEN=%8.3f, NON_CUBIC_FACTOR=%8.3f,\n" + " HMF=%2d, POWER_SPECTRUM=%2d, USE_RELATIVE_VELOCITIES=%1d, N_THREADS=%2d,\n" + " PERTURB_ON_HIGH_RES=%1d, NO_RNG=%1d, USE_FFTW_WISDOM=%1d, USE_INTERPOLATION_TABLES=%1d,\n" + " INTEGRATION_METHOD_ATOMIC=%2d, INTEGRATION_METHOD_MINI=%2d, USE_2LPT=%1d, MINIMIZE_MEMORY=%1d,\n" + " KEEP_3D_VELOCITIES=%1d, SAMPLER_MIN_MASS=%10.3e, SAMPLER_BUFFER_FACTOR=%8.3f, MAXHALO_FACTOR=%8.3f,\n" + " N_COND_INTERP=%4d, N_PROB_INTERP=%4d, MIN_LOGPROB=%8.3f, SAMPLE_METHOD=%2d, AVG_BELOW_SAMPLER=%1d,\n" + " HALOMASS_CORRECTION=%8.3f", + p->HII_DIM, p->DIM, p->BOX_LEN, p->NON_CUBIC_FACTOR, p->HMF, p->POWER_SPECTRUM, p->USE_RELATIVE_VELOCITIES, + p->N_THREADS, p->PERTURB_ON_HIGH_RES, p->NO_RNG, p->USE_FFTW_WISDOM, p->USE_INTERPOLATION_TABLES, + p->INTEGRATION_METHOD_ATOMIC,p->INTEGRATION_METHOD_MINI,p->USE_2LPT,p->MINIMIZE_MEMORY,p->KEEP_3D_VELOCITIES, + p->SAMPLER_MIN_MASS,p->SAMPLER_BUFFER_FACTOR,p->MAXHALO_FACTOR,p->N_COND_INTERP,p->N_PROB_INTERP,p->MIN_LOGPROB, + p->SAMPLE_METHOD,p->AVG_BELOW_SAMPLER,p->HALOMASS_CORRECTION); +} + +void writeCosmoParams(CosmoParams *p){ + LOG_INFO("\n CosmoParams:\n" + " SIGMA_8=%8.3f, hlittle=%8.3f, OMm=%8.3f, OMl=%8.3f, OMb=%8.3f, POWER_INDEX=%8.3f", + p->SIGMA_8, p->hlittle, p->OMm, p->OMl, p->OMb, p->POWER_INDEX); +} + +void writeAstroParams(FlagOptions *fo, AstroParams *p){ + if(fo->USE_MASS_DEPENDENT_ZETA) { + LOG_INFO("\n AstroParams:\n" + " M_TURN=%10.3e, R_BUBBLE_MAX=%8.3f, N_RSD_STEPS=%5d\n" + " F_STAR10=%8.3f, ALPHA_STAR=%8.3f, F_ESC10=%8.3f, ALPHA_ESC=%8.3f,\n" + " t_STAR=%8.3f, L_X=%10.3e, NU_X_THRESH=%8.3f, X_RAY_SPEC_INDEX=%8.3f,\n" + " UPPER_STELLAR_TURNOVER_MASS=%10.3e, UPPER_STELLAR_TURNOVER_INDEX=%8.3e", + p->M_TURN, p->R_BUBBLE_MAX, p->N_RSD_STEPS, p->F_STAR10, p->ALPHA_STAR, p->F_ESC10, + p->ALPHA_ESC, p->t_STAR, p->L_X, p->NU_X_THRESH, p->X_RAY_SPEC_INDEX, + p->UPPER_STELLAR_TURNOVER_MASS,p->UPPER_STELLAR_TURNOVER_INDEX); + if(fo->USE_HALO_FIELD){ + LOG_INFO("\n HaloField AstroParams:\n" + " SIGMA_STAR=%8.3f, CORR_STAR=%8.3f, \n" + " SIGMA_SFR_LIM=%8.3f (SIGMA_SFR_INDEX=%8.3f), CORR_SFR=%8.3f\n" + " SIGMA_LX=%8.3f, CORR_LX=%8.3f", + p->SIGMA_STAR, p->CORR_STAR, p->SIGMA_SFR_LIM, p->SIGMA_SFR_INDEX, p->CORR_SFR, + p->SIGMA_LX, p->CORR_LX); + } + if(fo->USE_MINI_HALOS){ + LOG_INFO("\n MiniHalo AstroParams:\n" + " ALPHA_STAR_MINI=%8.3f, F_ESC7_MINI=%8.3f, L_X_MINI=%10.3e, F_STAR7_MINI=%8.3f,\n" + " F_H2_SHIELD=%8.3f, A_LW=%8.3f, BETA_LW=%8.3f, A_VCB=%8.3f, BETA_VCB=%8.3f", + p->ALPHA_STAR_MINI,p->F_ESC7_MINI, p->L_X_MINI, p->F_STAR7_MINI, + p->F_H2_SHIELD, p->A_LW, p->BETA_LW, p->A_VCB, p->BETA_VCB); + + } + } + else { + LOG_INFO("\n AstroParams:\n" + " HII_EFF_FACTOR=%10.3e, ION_Tvir_MIN=%10.3e, X_RAY_Tvir_MIN=%10.3e,\n" + " R_BUBBLE_MAX=%8.3f, L_X=%10.3e, NU_X_THRESH=%8.3f, X_RAY_SPEC_INDEX=%8.3f,\n" + " F_STAR10=%8.3f, t_STAR=%8.3f, N_RSD_STEPS=%5d]", + p->HII_EFF_FACTOR, p->ION_Tvir_MIN, p->X_RAY_Tvir_MIN, + p->R_BUBBLE_MAX, p->L_X, p->NU_X_THRESH, p->X_RAY_SPEC_INDEX, p->F_STAR10, p->t_STAR, p->N_RSD_STEPS); + } +} + +void writeFlagOptions(FlagOptions *p){ + LOG_INFO("\n FlagOptions:\n" + " USE_HALO_FIELD=%1d, USE_MINI_HALOS=%1d, USE_MASS_DEPENDENT_ZETA=%1d, SUBCELL_RSD=%1d,\n" + " INHOMO_RECO=%1d, USE_TS_FLUCT=%1d, M_MIN_in_Mass=%1d, PHOTON_CONS=%1d,\n" + " HALO_STOCHASTICITY=%1d, FIXED_HALO_GRIDS=%1d, USE_EXP_FILTER=%1d\n" + " USE_CMB_HEATING=%1d, USE_LYA_HEATING=%1d, APPLY_RSDS=%1d, FIX_VCB_AVG=%1d\n" + " CELL_RECOMB=%1d, PHOTON_CONS_TYPE=%2d, USE_UPPER_STELLAR_TURNOVER=%1d", + p->USE_HALO_FIELD, p->USE_MINI_HALOS, p->USE_MASS_DEPENDENT_ZETA, p->SUBCELL_RSD, + p->INHOMO_RECO, p->USE_TS_FLUCT, p->M_MIN_in_Mass, p->PHOTON_CONS_TYPE, p->HALO_STOCHASTICITY, + p->FIXED_HALO_GRIDS, p->USE_EXP_FILTER,p->USE_CMB_HEATING,p->USE_LYA_HEATING,p->APPLY_RSDS, + p->FIX_VCB_AVG,p->CELL_RECOMB,p->PHOTON_CONS_TYPE,p->USE_UPPER_STELLAR_TURNOVER); +} + +void debugSummarizeBox(float *box, int size, float ncf, char *indent){ +#if LOG_LEVEL >= SUPER_DEBUG_LEVEL + float corners[8]; + + int i,j,k, counter; + int s = size-1; + int s_ncf = size*ncf-1; + + counter = 0; + for(i=0;i= SUPER_DEBUG_LEVEL + double corners[8]; + + int i,j,k, counter; + int s = size-1; + int s_ncf = size*ncf-1; + + counter = 0; + for(i=0;i= SUPER_DEBUG_LEVEL + float corners_real[8]; + float corners_imag[8]; + + int i,j,k, counter; + int s = size-1; + int s_ncf = size*ncf-1; + + counter = 0; + for(i=0;ilowres_density, HII_DIM, NCF, " "); + LOG_SUPER_DEBUG(" hires_density: "); + debugSummarizeBox(x->hires_density, DIM, NCF, " "); + LOG_SUPER_DEBUG(" lowres_vx: "); + debugSummarizeBox(x->lowres_vx, HII_DIM, NCF, " "); + LOG_SUPER_DEBUG(" lowres_vy: "); + debugSummarizeBox(x->lowres_vy, HII_DIM, NCF, " "); + LOG_SUPER_DEBUG(" lowres_vz: "); + debugSummarizeBox(x->lowres_vz, HII_DIM, NCF, " "); +} + +void debugSummarizePerturbField(PerturbedField *x, int HII_DIM, float NCF){ + LOG_SUPER_DEBUG("Summary of PerturbedField:"); + LOG_SUPER_DEBUG(" density: "); + debugSummarizeBox(x->density, HII_DIM, NCF, " "); + LOG_SUPER_DEBUG(" velocity_x: "); + debugSummarizeBox(x->velocity_x, HII_DIM, NCF, " "); + LOG_SUPER_DEBUG(" velocity_y: "); + debugSummarizeBox(x->velocity_y, HII_DIM, NCF, " "); + LOG_SUPER_DEBUG(" velocity_z: "); + debugSummarizeBox(x->velocity_z, HII_DIM, NCF, " "); + +} diff --git a/src/py21cmfast/src/debugging.h b/src/py21cmfast/src/debugging.h new file mode 100644 index 00000000..a6ae388a --- /dev/null +++ b/src/py21cmfast/src/debugging.h @@ -0,0 +1,27 @@ +#ifndef _DEBUGGING_H +#define _DEBUGGING_H + +#include +#include +#include "InputParameters.h" +#include "OutputStructs.h" + +//Input debugging +void writeFlagOptions(FlagOptions *p); +void writeUserParams(UserParams *p); +void writeCosmoParams(CosmoParams *p); +void writeAstroParams(FlagOptions *fo, AstroParams *p); + +//output debugging +void debugSummarizeIC(InitialConditions *x, int HII_DIM, int DIM, float NCF); +void debugSummarizePerturbField(PerturbedField *x, int HII_DIM, float NCF); +void debugSummarizeBox(float *box, int size, float ncf, char *indent); +void debugSummarizeBoxDouble(double *box, int size, float ncf, char *indent); +void debugSummarizeBoxComplex(fftwf_complex *box, int size, float ncf, char *indent); + +//error debugging +int SomethingThatCatches(bool sub_func); +int FunctionThatCatches(bool sub_func, bool pass, double* result); +void FunctionThatThrows(); + +#endif diff --git a/src/py21cmfast/src/dft.c b/src/py21cmfast/src/dft.c index 303fb21d..3486d8ba 100644 --- a/src/py21cmfast/src/dft.c +++ b/src/py21cmfast/src/dft.c @@ -1,3 +1,20 @@ +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" +#include "indexing.h" + +#include "dft.h" + int dft_c2r_cube(bool use_wisdom, int dim, int dim_los, int n_threads, fftwf_complex *box){ char wisdom_filename[500]; unsigned flag = FFTW_ESTIMATE; @@ -10,7 +27,7 @@ int dft_c2r_cube(bool use_wisdom, int dim, int dim_los, int n_threads, fftwf_com sprintf(wisdom_filename,"%s/c2r_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, dim, dim_los, n_threads); if(fftwf_import_wisdom_from_filename(wisdom_filename)!=0) { - unsigned flag = FFTW_WISDOM_ONLY; + flag = FFTW_WISDOM_ONLY; } else { LOG_WARNING("Cannot locate FFTW Wisdom: %s file not found. Reverting to FFTW_ESTIMATE.", wisdom_filename); @@ -38,7 +55,7 @@ int dft_r2c_cube(bool use_wisdom, int dim, int dim_los, int n_threads, fftwf_com sprintf(wisdom_filename,"%s/r2c_DIM%d_DIM%d_NTHREADS%d", global_params.wisdoms_path, dim, dim_los, n_threads); if(fftwf_import_wisdom_from_filename(wisdom_filename)!=0) { - unsigned flag = FFTW_WISDOM_ONLY; + flag = FFTW_WISDOM_ONLY; } else { LOG_WARNING("Cannot locate FFTW Wisdom: %s file not found. Reverting to FFTW_ESTIMATE.", wisdom_filename); @@ -54,21 +71,18 @@ int dft_r2c_cube(bool use_wisdom, int dim, int dim_los, int n_threads, fftwf_com return(0); } -int CreateFFTWWisdoms(struct UserParams *user_params, struct CosmoParams *cosmo_params) { +int CreateFFTWWisdoms(UserParams *user_params, CosmoParams *cosmo_params) { int status; - char *wisdom_string; Try{ // This Try wraps the entire function so we don't indent. - Broadcast_struct_global_UF(user_params,cosmo_params); + Broadcast_struct_global_noastro(user_params,cosmo_params); fftwf_plan plan; char wisdom_filename[500]; - int i,j,k; - omp_set_num_threads(user_params->N_THREADS); fftwf_init_threads(); fftwf_plan_with_nthreads(user_params->N_THREADS); @@ -78,7 +92,7 @@ int CreateFFTWWisdoms(struct UserParams *user_params, struct CosmoParams *cosmo_ fftwf_complex *HIRES_box = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*KSPACE_NUM_PIXELS); fftwf_complex *LOWRES_box = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); - sprintf(wisdom_filename,"%s/r2c_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->DIM,D_PARA,user_params->N_THREADS); + sprintf(wisdom_filename,"%s/r2c_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->DIM,(int)D_PARA,user_params->N_THREADS); if(fftwf_import_wisdom_from_filename(wisdom_filename)==0) { plan = fftwf_plan_dft_r2c_3d(user_params->DIM, user_params->DIM, D_PARA, (float *)HIRES_box, (fftwf_complex *)HIRES_box, FFTW_PATIENT); @@ -86,7 +100,7 @@ int CreateFFTWWisdoms(struct UserParams *user_params, struct CosmoParams *cosmo_ fftwf_destroy_plan(plan); } - sprintf(wisdom_filename,"%s/c2r_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->DIM,D_PARA,user_params->N_THREADS); + sprintf(wisdom_filename,"%s/c2r_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->DIM,(int)D_PARA,user_params->N_THREADS); if(fftwf_import_wisdom_from_filename(wisdom_filename)==0) { plan = fftwf_plan_dft_c2r_3d(user_params->DIM, user_params->DIM, D_PARA, (fftwf_complex *)HIRES_box, (float *)HIRES_box, FFTW_PATIENT); @@ -94,7 +108,7 @@ int CreateFFTWWisdoms(struct UserParams *user_params, struct CosmoParams *cosmo_ fftwf_destroy_plan(plan); } - sprintf(wisdom_filename,"%s/r2c_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->HII_DIM,HII_D_PARA,user_params->N_THREADS); + sprintf(wisdom_filename,"%s/r2c_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->HII_DIM,(int)HII_D_PARA,user_params->N_THREADS); if(fftwf_import_wisdom_from_filename(wisdom_filename)==0) { plan = fftwf_plan_dft_r2c_3d(user_params->HII_DIM, user_params->HII_DIM, HII_D_PARA, (float *)LOWRES_box, (fftwf_complex *)LOWRES_box, FFTW_PATIENT); @@ -102,7 +116,7 @@ int CreateFFTWWisdoms(struct UserParams *user_params, struct CosmoParams *cosmo_ fftwf_destroy_plan(plan); } - sprintf(wisdom_filename,"%s/c2r_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->HII_DIM,HII_D_PARA,user_params->N_THREADS); + sprintf(wisdom_filename,"%s/c2r_DIM%d_DIM%d_NTHREADS%d",global_params.wisdoms_path, user_params->HII_DIM,(int)HII_D_PARA,user_params->N_THREADS); if(fftwf_import_wisdom_from_filename(wisdom_filename)==0) { plan = fftwf_plan_dft_c2r_3d(user_params->HII_DIM, user_params->HII_DIM, HII_D_PARA, (fftwf_complex *)LOWRES_box, (float *)LOWRES_box, FFTW_PATIENT); diff --git a/src/py21cmfast/src/dft.h b/src/py21cmfast/src/dft.h new file mode 100644 index 00000000..55815d2d --- /dev/null +++ b/src/py21cmfast/src/dft.h @@ -0,0 +1,15 @@ +/* Function prototypes and definitions used in the fourier transforms */ +#ifndef _DFT_H +#define _DFT_H + +#include +#include +#include + +#include "InputParameters.h" + +int dft_c2r_cube(bool use_wisdom, int dim, int dim_los, int n_threads, fftwf_complex *box); +int dft_r2c_cube(bool use_wisdom, int dim, int dim_los, int n_threads, fftwf_complex *box); +int CreateFFTWWisdoms(UserParams *user_params, CosmoParams *cosmo_params); + +#endif diff --git a/src/py21cmfast/src/elec_interp.c b/src/py21cmfast/src/elec_interp.c index 11e65172..e7597ce1 100755 --- a/src/py21cmfast/src/elec_interp.c +++ b/src/py21cmfast/src/elec_interp.c @@ -1,26 +1,4 @@ /* Written by Steven Furlanetto */ -//#include "stdio.h" -//#include "stdlib.h" -//#include "math.h" -// Below gives grid sizes for the interpolation arrays - -// Initialization; must be called once to -void initialize_interp_arrays(); - -// Primary functions to compute heating fractions and number of Lya photons or ionization produced, -// Note that En is the energy of the *primary* photon, so the energy in the initial ionization is -// included in all these. -// All energies are in eV. -// xHII_call is the desired ionized fraction. -float interp_fheat(float En, float xHII_call); -float interp_n_Lya(float En, float xHII_call); -float interp_nion_HI(float En, float xHII_call); -float interp_nion_HeI(float En, float xHII_call); -float interp_nion_HeII(float En, float xHII_call); - -int locate_energy_index(float En); -int locate_xHII_index(float xHII_call); - // Functions to interpolate the energy deposition fractions of high-energy secondary electrons // in the IGM. @@ -36,13 +14,25 @@ int locate_xHII_index(float xHII_call); // The interpolation arrays. Note all are defined in global scope, but x_int_ prefix should // ensure no conflicts with other code -float x_int_Energy[x_int_NENERGY]; + +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "InputParameters.h" + +#include "elec_interp.h" + float x_int_XHII[x_int_NXHII]; -float x_int_fheat[x_int_NXHII][x_int_NENERGY]; -float x_int_n_Lya[x_int_NXHII][x_int_NENERGY]; -float x_int_nion_HI[x_int_NXHII][x_int_NENERGY]; -float x_int_nion_HeI[x_int_NXHII][x_int_NENERGY]; -float x_int_nion_HeII[x_int_NXHII][x_int_NENERGY]; +static float x_int_Energy[x_int_NENERGY]; +static float x_int_fheat[x_int_NXHII][x_int_NENERGY]; +static float x_int_n_Lya[x_int_NXHII][x_int_NENERGY]; +static float x_int_nion_HI[x_int_NXHII][x_int_NENERGY]; +static float x_int_nion_HeI[x_int_NXHII][x_int_NENERGY]; +static float x_int_nion_HeII[x_int_NXHII][x_int_NENERGY]; void skipline(FILE *fl, int n){ int i; @@ -64,7 +54,6 @@ void initialize_interp_arrays() float xHI,xHeI,xHeII,z,T; float trash; - char label[] = " "; int i; int n_ion; diff --git a/src/py21cmfast/src/elec_interp.h b/src/py21cmfast/src/elec_interp.h new file mode 100644 index 00000000..77ba7932 --- /dev/null +++ b/src/py21cmfast/src/elec_interp.h @@ -0,0 +1,28 @@ +/* Function prototypes for the xray/lya tables as a function of energy and ionisation fraction*/ +#ifndef _ELEC_INT_H +#define _ELEC_INT_H + +#define x_int_NXHII 14 +#define x_int_NENERGY 258 + +void initialize_interp_arrays(); + +// Primary functions to compute heating fractions and number of Lya photons or ionization produced, +// Note that En is the energy of the *primary* photon, so the energy in the initial ionization is +// included in all these. +// All energies are in eV. +// xHII_call is the desired ionized fraction. +float interp_fheat(float En, float xHII_call); +float interp_n_Lya(float En, float xHII_call); +float interp_nion_HI(float En, float xHII_call); +float interp_nion_HeI(float En, float xHII_call); +float interp_nion_HeII(float En, float xHII_call); + +int locate_energy_index(float En); +int locate_xHII_index(float xHII_call); + +// this array is currently used in SpinTemperatureBox.c and passed into heating_helper_progs.c functions +// TODO: remove it and make it static in elec_interp.c +extern float x_int_XHII[x_int_NXHII]; + +#endif diff --git a/src/py21cmfast/src/exceptions.h b/src/py21cmfast/src/exceptions.h index 07df4ae7..195cdbbd 100644 --- a/src/py21cmfast/src/exceptions.h +++ b/src/py21cmfast/src/exceptions.h @@ -1,8 +1,12 @@ +#ifndef _EXCEPTIONS_H +#define _EXCEPTIONS_H + #include "cexcept.h" + define_exception_type(int); -extern struct exception_context the_exception_context[1]; -struct exception_context the_exception_context[1]; +//NOTE: declaration here, definition in debugging.c +extern struct exception_context the_exception_context[1]; // Our own error codes #define SUCCESS 0 @@ -16,4 +20,6 @@ struct exception_context the_exception_context[1]; #define MassDepZetaError 8 #define MemoryAllocError 9 -#define GSL_ERROR(status) if(status>0) {LOG_ERROR("GSL Error Encountered (Code = %d): %s", status, gsl_strerror(status)); Throw(GSLError);} +#define CATCH_GSL_ERROR(status) if(status>0) {LOG_ERROR("GSL Error Encountered (Code = %d): %s", status, gsl_strerror(status)); Throw(GSLError);} + +#endif diff --git a/src/py21cmfast/src/filtering.c b/src/py21cmfast/src/filtering.c new file mode 100644 index 00000000..00ba9186 --- /dev/null +++ b/src/py21cmfast/src/filtering.c @@ -0,0 +1,262 @@ + +//filter_box, filter_box_annulus and filter_box_mfp should be combined in a better way, they require different inputs +//and they are run on different subsets of the boxes but they contain a lot of the same math + +#include +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "indexing.h" +#include "dft.h" + +void filter_box_annulus(fftwf_complex *box, int RES, float R_inner, float R_outer){ + int n_x, n_z, n_y, dimension, midpoint; + float k_x, k_y, k_z, k_mag, kRinner, kRouter; + float f_inner, f_outer; + + switch(RES) { + case 0: + dimension = user_params_global->DIM; + midpoint = MIDDLE; + break; + case 1: + dimension = user_params_global->HII_DIM; + midpoint = HII_MIDDLE; + break; + default: + LOG_ERROR("Resolution for filter functions must be 0(DIM) or 1(HII_DIM)"); + Throw(ValueError); + break; + } + // loop through k-box + +#pragma omp parallel shared(box) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,kRinner,kRouter,f_inner,f_outer) num_threads(user_params_global->N_THREADS) + { +#pragma omp for + for (n_x=0; n_xmidpoint) {k_x =(n_x-dimension) * DELTA_K;} + else {k_x = n_x * DELTA_K;} + + for (n_y=0; n_ymidpoint) {k_y =(n_y-dimension) * DELTA_K;} + else {k_y = n_y * DELTA_K;} + + for (n_z=0; n_z<=(int)(user_params_global->NON_CUBIC_FACTOR*midpoint); n_z++){ + k_z = n_z * DELTA_K_PARA; + + k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); + + kRinner = k_mag*R_inner; + kRouter = k_mag*R_outer; + + if (kRinner > 1e-4){ + f_inner = 3.0/(pow(kRouter, 3) - pow(kRinner, 3)) * (sin(kRinner) - cos(kRinner)*kRinner); + f_outer = 3.0/(pow(kRouter, 3) - pow(kRinner, 3)) * (sin(kRouter) - cos(kRouter)*kRouter); + if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= (f_outer - f_inner); } + if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= (f_outer - f_inner); } + } + + } + } + } // end looping through k box + } + return; +} + +void filter_box(fftwf_complex *box, int RES, int filter_type, float R){ + int n_x, n_z, n_y, dimension, midpoint; //TODO: figure out why defining as ULL breaks this + float k_x, k_y, k_z, k_mag, kR; + + switch(RES) { + case 0: + dimension = user_params_global->DIM; + midpoint = MIDDLE; + break; + case 1: + dimension = user_params_global->HII_DIM; + midpoint = HII_MIDDLE; + break; + default: + LOG_ERROR("Resolution for filter functions must be 0(DIM) or 1(HII_DIM)"); + Throw(ValueError); + break; + } + + // loop through k-box + +#pragma omp parallel shared(box) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,kR) num_threads(user_params_global->N_THREADS) + { +#pragma omp for + for (n_x=0; n_xmidpoint) {k_x =(n_x-dimension) * DELTA_K;} + else {k_x = n_x * DELTA_K;} + + for (n_y=0; n_ymidpoint) {k_y =(n_y-dimension) * DELTA_K;} + else {k_y = n_y * DELTA_K;} + for (n_z=0; n_z<=(int)(user_params_global->NON_CUBIC_FACTOR*midpoint); n_z++){ + k_z = n_z * DELTA_K_PARA; + + if (filter_type == 0){ // real space top-hat + + k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); + + kR = k_mag*R; // real space top-hat + + if (kR > 1e-4){ + if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= 3.0*pow(kR, -3) * (sin(kR) - cos(kR)*kR); } + if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= 3.0*pow(kR, -3) * (sin(kR) - cos(kR)*kR); } + } + } + else if (filter_type == 1){ // k-space top hat + + // This is actually (kR^2) but since we zero the value and find kR > 1 this is more computationally efficient + // as we don't need to evaluate the slower sqrt function +// kR = 0.17103765852*( k_x*k_x + k_y*k_y + k_z*k_z )*R*R; + + k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); + kR = k_mag*R; // real space top-hat + + kR *= 0.413566994; // equates integrated volume to the real space top-hat (9pi/2)^(-1/3) + if (kR > 1){ + if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] = 0; } + if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] = 0; } + } + } + else if (filter_type == 2){ // gaussian + // This is actually (kR^2) but since we zero the value and find kR > 1 this is more computationally efficient + // as we don't need to evaluate the slower sqrt function + kR = 0.643*0.643*( k_x*k_x + k_y*k_y + k_z*k_z )*R*R; +// kR *= 0.643; // equates integrated volume to the real space top-hat + if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= pow(E, -kR/2.0); } + if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= pow(E, -kR/2.0); } + } + else{ + if ( (n_x==0) && (n_y==0) && (n_z==0) ) + LOG_WARNING("Filter type %i is undefined. Box is unfiltered.", filter_type); + } + } + } + } // end looping through k box + } + + return; +} + +void filter_box_mfp(fftwf_complex *box, int RES, float R, float mfp){ + int n_x, n_z, n_y, dimension, midpoint; + float k_x, k_y, k_z, k_mag, f, kR, kl; + float const1; + const1 = exp(-R/mfp); //independent of k, move it out of the loop + // LOG_DEBUG("Filtering box with R=%.2e, L=%.2e",R,mfp); + + switch(RES) { + case 0: + dimension = user_params_global->DIM; + midpoint = MIDDLE; + break; + case 1: + dimension = user_params_global->HII_DIM; + midpoint = HII_MIDDLE; + break; + default: + LOG_ERROR("Resolution for filter functions must be 0(DIM) or 1(HII_DIM)"); + Throw(ValueError); + break; + } + // loop through k-box + +#pragma omp parallel shared(box) private(n_x,n_y,n_z,k_x,k_y,k_z,k_mag,kR,kl,f) num_threads(user_params_global->N_THREADS) + { +#pragma omp for + for (n_x=0; n_xmidpoint) {k_x =(n_x-dimension) * DELTA_K;} + else {k_x = n_x * DELTA_K;} + + for (n_y=0; n_ymidpoint) {k_y =(n_y-dimension) * DELTA_K;} + else {k_y = n_y * DELTA_K;} + for (n_z=0; n_z<=(int)(user_params_global->NON_CUBIC_FACTOR*midpoint); n_z++){ + k_z = n_z * DELTA_K_PARA; + + k_mag = sqrt(k_x*k_x + k_y*k_y + k_z*k_z); + + kR = k_mag*R; + kl = k_mag*mfp; + + //Davies & Furlanetto MFP-eps(r) window function + //The filter no longer approaches 1 at k->0, so we can't use the limit + if (kR > 1e-4){ + //build the filter + f = (kl*kl*R + 2*mfp + R)*kl*cos(kR); + f += (-kl*kl*mfp + kl*kl*R + mfp + R)*sin(kR); + f *= const1; + f -= 2*kl*mfp; + f *= -3.0*mfp/(kR*R*R*(kl*kl+1)*(kl*kl+1)); + } + else{ + // k-> 0 limit + f = 2*mfp*mfp + 2*mfp*R + R*R; + f *= -const1; + f += 2*mfp*mfp; + f *= 3*mfp/(R*R*R); + } + if(RES==1) { box[HII_C_INDEX(n_x, n_y, n_z)] *= f; } + if(RES==0) { box[C_INDEX(n_x, n_y, n_z)] *= f; } + } + } + } // end looping through k box + } + return; +} + +int test_mfp_filter(UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options + , float *input_box, double R, double mfp, double *result){ + int i,j,k; + unsigned long long int ii; + //setup the box + + fftwf_complex *box_unfiltered = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); + fftwf_complex *box_filtered = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); + + for (i=0; iHII_DIM; i++) + for (j=0; jHII_DIM; j++) + for (k=0; kUSE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, box_unfiltered); + + //QUESTION: why do this here instead of at the end? + for(ii=0;iiUSE_EXP_FILTER) + filter_box_mfp(box_filtered, 1, R, mfp); + else + filter_box(box_filtered,1,global_params.HII_FILTER,R); + + + dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, box_filtered); + + for (i=0; iHII_DIM; i++) + for (j=0; jHII_DIM; j++) + for (k=0; k + +void filter_box(fftwf_complex *box, int RES, int filter_type, float R); +void filter_box_annulus(fftwf_complex *box, int RES, float R_inner, float R_outer); +void filter_box_mfp(fftwf_complex *box, int RES, float R, float mfp); + +int test_mfp_filter(UserParams *user_params, CosmoParams *cosmo_params, AstroParams *astro_params, FlagOptions *flag_options + , float *input_box, double R, double mfp, double *result); + +#endif diff --git a/src/py21cmfast/src/heating_helper_progs.c b/src/py21cmfast/src/heating_helper_progs.c index 30b0d9ac..2544d4be 100755 --- a/src/py21cmfast/src/heating_helper_progs.c +++ b/src/py21cmfast/src/heating_helper_progs.c @@ -1,72 +1,27 @@ -struct UserParams *user_params_hf; -struct CosmoParams *cosmo_params_hf; -struct AstroParams *astro_params_hf; -struct FlagOptions *flag_options_hf; - -float determine_zpp_min, zpp_bin_width; - -double BinWidth_pH,inv_BinWidth_pH,BinWidth_elec,inv_BinWidth_elec,BinWidth_10,inv_BinWidth_10,PS_ION_EFF; - -void Broadcast_struct_global_HF(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options){ - - user_params_hf = user_params; - cosmo_params_hf = cosmo_params; - astro_params_hf = astro_params; - flag_options_hf = flag_options; -} - -// * initialization routine * // -// int init_heat(); - -/* destruction/deallocation routine */ -void destruct_heat(); - -// * returns the spectral emissity * // -double spectral_emissivity(double nu_norm, int flag, int Population); - -// * Ionization fraction from RECFAST. * // -double xion_RECFAST(float z, int flag); - -// * IGM temperature from RECFAST; includes Compton heating and adiabatic expansion only. * // -double T_RECFAST(float z, int flag); - -// approximation for the adiabatic index at z=6-50 from 2302.08506 -float cT_approx(float z); - -// * returns the spin temperature * // -float get_Ts(float z, float delta, float TK, float xe, float Jalpha, float * curr_xalpha); - -//* Returns recycling fraction (=fraction of photons converted into Lyalpha for Ly-n resonance * // -double frecycle(int n); - -// * Returns frequency of Lyman-n, in units of Lyman-alpha * // -double nu_n(int n); - -double kappa_10_pH(double T, int flag); -double kappa_10_elec(double T, int flag); -double kappa_10(double TK, int flag); - -double xcoll(double z, double TK, double delta, double xe); -double xcoll_HI(double z, double TK, double delta, double xe); -double xcoll_elec(double z, double TK, double delta, double xe); -double xcoll_prot(double z, double TK, double delta, double xe); - -double xalpha_tilde(double z, double Jalpha, double TK, double TS, double delta, double xe); -double Tc_eff(double TK, double TS); -double Salpha_tilde(double TK, double TS, double tauGP); -double taugp(double z, double delta, double xe); - -double species_weighted_x_ray_cross_section(double nu, double x_e); - -// * Returns the maximum redshift at which a Lyn transition contributes to Lya flux at z * // -float zmax(float z, int n); - -//Lyman-Alpha heating functions -int find_nearest_point(double min, double max, int n, double value); -int find_xyz_pos(int xpos, int ypos, int zpos, int len_yarr, int len_zarr); -double interpolate_heating_efficiencies(double tk, double ts, double taugp, double *arrE); -double Energy_Lya_heating(double Tk, double Ts, double tau_gp, int flag); +#include +#include +#include +#include +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "indexing.h" +#include "elec_interp.h" +#include "cosmology.h" +#include "thermochem.h" +#include "interp_tables.h" + +#include "heating_helper_progs.h" + +static double BinWidth_pH,inv_BinWidth_pH,BinWidth_elec,inv_BinWidth_elec,BinWidth_10,inv_BinWidth_10,PS_ION_EFF; int init_heat() { @@ -104,31 +59,6 @@ void destruct_heat() xion_RECFAST(100.0,2); } -float get_Ts(float z, float delta, float TK, float xe, float Jalpha, float * curr_xalpha){ - double Trad,xc,xa_tilde; - double TS,TSold,TSinv; - double Tceff; - - Trad = T_cmb*(1.0+z); - xc = xcoll(z,TK,delta,xe); - if (Jalpha > 1.0e-20) { // * Must use WF effect * // - TS = Trad; - TSold = 0.0; - while (fabs(TS-TSold)/TS > 1.0e-3) { - TSold = TS; - xa_tilde = xalpha_tilde(z,Jalpha,TK,TS,delta,xe); - Tceff = Tc_eff(1./TK,1./TS); - TS = (1.0+xa_tilde+xc)/(1.0/Trad+xa_tilde/Tceff + xc/TK); - } - *curr_xalpha = xa_tilde; - } else { // * Collisions only * // - TS = (1.0 + xc)/(1.0/Trad + xc/TK); - *curr_xalpha = 0; - } - - return TS; -} - // ******************************************************************** // // ************************ RECFAST quantities ************************ // // ******************************************************************** // @@ -401,59 +331,6 @@ double spectral_emissivity(double nu_norm, int flag, int Population) } } - -double xcoll(double z, double TK, double delta, double xe){ - return xcoll_HI(z,TK,delta,xe) + xcoll_elec(z,TK,delta,xe) + xcoll_prot(z,TK,delta,xe); -} - -double xcoll_HI(double z, double TK, double delta, double xe) -{ - double krate,nH,Trad; - double xcoll; - - Trad = T_cmb*(1.0+z); - nH = (1.0-xe)*No*pow(1.0+z,3.0)*(1.0+delta); - krate = kappa_10(TK,0); - xcoll = T21/Trad*nH*krate/A10_HYPERFINE; - return xcoll; -} - -// Note that this assumes Helium ionized same as Hydrogen // -double xcoll_elec(double z, double TK, double delta, double xe) -{ - double krate,ne,Trad; - double xcoll; - - Trad = T_cmb*(1.0+z); - ne = xe*N_b0*pow(1.0+z,3.0)*(1.0+delta); - krate = kappa_10_elec(TK,0); - xcoll = T21/Trad*ne*krate/A10_HYPERFINE; - return xcoll; -} - -double xcoll_prot(double z, double TK, double delta, double xe) -{ - double krate,np,Trad; - double xcoll; - - Trad = T_cmb*(1.0+z); - np = xe*No*pow(1.0+z,3.0)*(1.0+delta); - krate = kappa_10_pH(TK,0); - xcoll = T21/Trad*np*krate/A10_HYPERFINE; - return xcoll; -} - -double Salpha_tilde(double TK, double TS, double tauGP) -{ - double xi; - double ans; - - xi = pow(1.0e-7*tauGP*TK*TK, 1.0/3.0); - ans = (1.0 - 0.0631789*TK + 0.115995*TK*TK - 0.401403*TS*TK + 0.336463*TS*TK*TK)/(1.0 + 2.98394*xi + 1.53583*xi*xi + 3.85289*xi*xi*xi); - return ans; -} - - // * Returns frequency of Lyman-n, in units of Lyman-alpha * // double nu_n(int n) { @@ -654,6 +531,21 @@ double kappa_10_elec(double T, int flag) // ********************* Wouthuysen-Field Coupling ******************** // // ******************************************************************** // +// Compute the Gunn-Peterson optical depth. +double taugp(double z, double delta, double xe){ + return 1.342881e-7 / hubble(z)*No*pow(1+z,3) * (1.0+delta)*(1.0-xe); +} + +double Salpha_tilde(double TK, double TS, double tauGP) +{ + double xi; + double ans; + + xi = pow(1.0e-7*tauGP*TK*TK, 1.0/3.0); + ans = (1.0 - 0.0631789*TK + 0.115995*TK*TK - 0.401403*TS*TK + 0.336463*TS*TK*TK)/(1.0 + 2.98394*xi + 1.53583*xi*xi + 3.85289*xi*xi*xi); + return ans; +} + // NOTE Jalpha is by number // double xalpha_tilde(double z, double Jalpha, double TK, double TS, double delta, double xe){ @@ -665,9 +557,45 @@ double xalpha_tilde(double z, double Jalpha, double TK, double TS, return x; } -// Compute the Gunn-Peterson optical depth. -double taugp(double z, double delta, double xe){ - return 1.342881e-7 / hubble(z)*No*pow(1+z,3) * (1.0+delta)*(1.0-xe); +double xcoll_HI(double z, double TK, double delta, double xe) +{ + double krate,nH,Trad; + double xcoll; + + Trad = T_cmb*(1.0+z); + nH = (1.0-xe)*No*pow(1.0+z,3.0)*(1.0+delta); + krate = kappa_10(TK,0); + xcoll = T21/Trad*nH*krate/A10_HYPERFINE; + return xcoll; +} + +// Note that this assumes Helium ionized same as Hydrogen // +double xcoll_elec(double z, double TK, double delta, double xe) +{ + double krate,ne,Trad; + double xcoll; + + Trad = T_cmb*(1.0+z); + ne = xe*N_b0*pow(1.0+z,3.0)*(1.0+delta); + krate = kappa_10_elec(TK,0); + xcoll = T21/Trad*ne*krate/A10_HYPERFINE; + return xcoll; +} + +double xcoll_prot(double z, double TK, double delta, double xe) +{ + double krate,np,Trad; + double xcoll; + + Trad = T_cmb*(1.0+z); + np = xe*No*pow(1.0+z,3.0)*(1.0+delta); + krate = kappa_10_pH(TK,0); + xcoll = T21/Trad*np*krate/A10_HYPERFINE; + return xcoll; +} + +double xcoll(double z, double TK, double delta, double xe){ + return xcoll_HI(z,TK,delta,xe) + xcoll_elec(z,TK,delta,xe) + xcoll_prot(z,TK,delta,xe); } double Tc_eff(double TK, double TS) @@ -678,6 +606,33 @@ double Tc_eff(double TK, double TS) return ans; } +float get_Ts(float z, float delta, float TK, float xe, float Jalpha, float * curr_xalpha){ + double Trad,xc,xa_tilde; + double TS,TSold; + double Tceff; + + Trad = T_cmb*(1.0+z); + xc = xcoll(z,TK,delta,xe); + if (Jalpha > 1.0e-20) { // * Must use WF effect * // + TS = Trad; + TSold = 0.0; + //TODO: changed to do-while so we never use uninitialised variables + // Make sure it didn't effect anything + do{ + TSold = TS; + xa_tilde = xalpha_tilde(z,Jalpha,TK,TS,delta,xe); + Tceff = Tc_eff(1./TK,1./TS); + TS = (1.0+xa_tilde+xc)/(1.0/Trad+xa_tilde/Tceff + xc/TK); + }while(fabs(TS-TSold)/TS > 1.0e-3); + *curr_xalpha = xa_tilde; + } else { // * Collisions only * // + TS = (1.0 + xc)/(1.0/Trad + xc/TK); + *curr_xalpha = 0; + } + + return TS; +} + // // Evaluates the frequency integral in the Tx evolution equation // photons starting from zpp arive at zp, with mean IGM electron @@ -703,7 +658,7 @@ double integrand_in_nu_heat_integral(double nu, void * params){ species_sum += interp_fheat((nu - HeII_NUIONIZATION)/NU_over_EV, x_e) * hplank*(nu - HeII_NUIONIZATION) * f_He * x_e * HeII_ion_crosssec(nu); - return species_sum * pow(nu/((astro_params_hf->NU_X_THRESH)*NU_over_EV), -(astro_params_hf->X_RAY_SPEC_INDEX)-1); + return species_sum * pow(nu/((astro_params_global->NU_X_THRESH)*NU_over_EV), -(astro_params_global->X_RAY_SPEC_INDEX)-1); } double integrand_in_nu_ion_integral(double nu, void * params){ @@ -728,7 +683,7 @@ double integrand_in_nu_ion_integral(double nu, void * params){ interp_nion_HeII((nu - HeII_NUIONIZATION)/NU_over_EV, x_e) + 1; species_sum += F_i * f_He * x_e * HeII_ion_crosssec(nu); - return species_sum * pow(nu/((astro_params_hf->NU_X_THRESH)*NU_over_EV), -(astro_params_hf->X_RAY_SPEC_INDEX)-1); + return species_sum * pow(nu/((astro_params_global->NU_X_THRESH)*NU_over_EV), -(astro_params_global->X_RAY_SPEC_INDEX)-1); } double integrand_in_nu_lya_integral(double nu, void * params){ @@ -747,7 +702,7 @@ double integrand_in_nu_lya_integral(double nu, void * params){ species_sum += interp_n_Lya((nu - HeII_NUIONIZATION)/NU_over_EV, x_e) * f_He * (double)x_e * HeII_ion_crosssec(nu); - return species_sum * pow(nu/((astro_params_hf->NU_X_THRESH)*NU_over_EV), -(astro_params_hf->X_RAY_SPEC_INDEX)-1); + return species_sum * pow(nu/((astro_params_global->NU_X_THRESH)*NU_over_EV), -(astro_params_global->X_RAY_SPEC_INDEX)-1); } double integrate_over_nu(double zp, double local_x_e, double lower_int_limit, int FLAG){ @@ -773,7 +728,7 @@ double integrate_over_nu(double zp, double local_x_e, double lower_int_limit, in if(status!=0){ LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_int_limit,global_params.NU_X_MAX*NU_over_EV,rel_tol,result,error); LOG_ERROR("data: zp=%e local_x_e=%e FLAG=%d",zp,local_x_e,FLAG); - GSL_ERROR(status); + CATCH_GSL_ERROR(status); } gsl_integration_workspace_free (w); @@ -788,6 +743,20 @@ double integrate_over_nu(double zp, double local_x_e, double lower_int_limit, in return result; } +// The total weighted HI + HeI + HeII cross-section in pcm^-2 +// technically, the x_e should be local, line of sight (not global) here, +// but that would be very slow... + +double species_weighted_x_ray_cross_section(double nu, double x_e){ + double HI_factor, HeI_factor, HeII_factor; + + HI_factor = f_H * (1-x_e) * HI_ion_crosssec(nu); + HeI_factor = f_He * (1-x_e) * HeI_ion_crosssec(nu); + HeII_factor = f_He * x_e * HeII_ion_crosssec(nu); + + return HI_factor + HeI_factor + HeII_factor; +} + // Calculates the optical depth for a photon arriving at z = zp with frequency nu, // emitted at z = zpp. // The filling factor of neutral IGM at zp is HI_filling_factor_zp. @@ -863,8 +832,8 @@ double tauX_MINI(double nu, double x_e, double x_e_ave, double zp, double zpp, d p.nu_0 = nu/(1+zp); p.x_e = x_e; p.x_e_ave = x_e_ave; - p.ion_eff = global_params.Pop2_ion*astro_params_hf->F_STAR10*astro_params_hf->F_ESC10; - p.ion_eff_MINI = global_params.Pop3_ion*astro_params_hf->F_STAR7_MINI*astro_params_hf->F_ESC7_MINI; + p.ion_eff = global_params.Pop2_ion*astro_params_global->F_STAR10*astro_params_global->F_ESC10; + p.ion_eff_MINI = global_params.Pop3_ion*astro_params_global->F_STAR7_MINI*astro_params_global->F_ESC7_MINI; p.log10_Mturn_MINI = log10_Mturn_MINI; p.Mlim_Fstar = Mlim_Fstar; p.Mlim_Fesc = Mlim_Fesc; @@ -883,7 +852,7 @@ double tauX_MINI(double nu, double x_e, double x_e_ave, double zp, double zpp, d LOG_ERROR("(function argument): zp=%e zpp=%e rel_tol=%e result=%e error=%e",zp,zpp,rel_tol,result,error); LOG_ERROR("data: nu=%e nu_0=%e x_e=%e x_e_ave=%e",nu,p.nu_0,p.x_e,p.x_e_ave); LOG_ERROR("data: ion_eff=%e ion_eff_MINI=%e log10_Mturn_MINI=%e",p.ion_eff,p.ion_eff_MINI,p.log10_Mturn_MINI); - GSL_ERROR(status); + CATCH_GSL_ERROR(status); } gsl_integration_workspace_free (w); @@ -906,8 +875,8 @@ double tauX(double nu, double x_e, double x_e_ave, double zp, double zpp, double p.Mlim_Fstar = Mlim_Fstar; p.Mlim_Fesc = Mlim_Fesc; - if(flag_options_hf->USE_MASS_DEPENDENT_ZETA) { - p.ion_eff = global_params.Pop2_ion*astro_params_hf->F_STAR10*astro_params_hf->F_ESC10; + if(flag_options_global->USE_MASS_DEPENDENT_ZETA) { + p.ion_eff = global_params.Pop2_ion*astro_params_global->F_STAR10*astro_params_global->F_ESC10; } else { //if we don't have an explicit ionising efficiency, we estimate one by using the values at zp @@ -929,7 +898,7 @@ double tauX(double nu, double x_e, double x_e_ave, double zp, double zpp, double if(status!=0){ LOG_ERROR("(function argument): zp=%e zpp=%e rel_tol=%e result=%e error=%e",zp,zpp,rel_tol,result,error); LOG_ERROR("data: nu=%e nu_0=%e x_e=%e x_e_ave=%e ion_eff=%e",nu,nu/(1+zp),x_e,x_e_ave,p.ion_eff); - GSL_ERROR(status); + CATCH_GSL_ERROR(status); } gsl_integration_workspace_free (w); @@ -974,7 +943,7 @@ double nu_tau_one_MINI(double zp, double zpp, double x_e, double HI_filling_fact // check if too ionized if (x_e > 0.9999){ // LOG_ERROR("x_e value is too close to 1 for convergence."); - return astro_params_hf->NU_X_THRESH; + return astro_params_global->NU_X_THRESH; } // select solver and allocate memory @@ -1047,7 +1016,7 @@ double nu_tau_one(double zp, double zpp, double x_e, double HI_filling_factor_zp // check if too ionized if (x_e > 0.9999){ // LOG_ERROR("x_e value is too close to 1 for convergence."); - return astro_params_hf->NU_X_THRESH; + return astro_params_global->NU_X_THRESH; } // select solver and allocate memory @@ -1102,22 +1071,6 @@ double nu_tau_one(double zp, double zpp, double x_e, double HI_filling_factor_zp return r; } - -// The total weighted HI + HeI + HeII cross-section in pcm^-2 -// technically, the x_e should be local, line of sight (not global) here, -// but that would be very slow... - -double species_weighted_x_ray_cross_section(double nu, double x_e){ - double HI_factor, HeI_factor, HeII_factor; - - HI_factor = f_H * (1-x_e) * HI_ion_crosssec(nu); - HeI_factor = f_He * (1-x_e) * HeI_ion_crosssec(nu); - HeII_factor = f_He * x_e * HeII_ion_crosssec(nu); - - return HI_factor + HeI_factor + HeII_factor; -} - - // * Returns the maximum redshift at which a Lyn transition contributes to Lya flux at z * // float zmax(float z, int n){ double num, denom; @@ -1126,50 +1079,6 @@ float zmax(float z, int n){ return (1+z)*num/denom - 1; } -//Function to read \deltaE -double Energy_Lya_heating(double Tk, double Ts, double tau_gp, int flag) -{ - double ans; - static double dEC[nT * nT * ngp]; - static double dEI[nT * nT * ngp]; - double dummy_heat; - int ii, jj, kk, index; - FILE *F; - - char filename[500]; - - if (flag == 1) { - //Read in the Lya heating table - sprintf(filename,"%s/%s",global_params.external_table_path,LYA_HEATING_FILENAME); - - if ( !(F=fopen(filename, "r")) ){ - LOG_ERROR("Energy_Lya_heating: Unable to open file: %s for reading.", filename); - Throw(IOError); - } - - for (ii=0;ii +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "cosmology.h" +#include "interp_tables.h" + +#include "hmf.h" + +#define EPS2 3.0e-11 //small number limit for GL integration +#define JENKINS_a (0.73) //Jenkins+01, SMT has 0.707 +#define JENKINS_b (0.34) //Jenkins+01 fit from Barkana+01, SMT has 0.5 +#define JENKINS_c (0.81) //Jenkins+01 from from Barkana+01, SMT has 0.6 + +//Gauss-Legendre integration constants +#define NGL_INT 100 // 100 +//These arrays hold the points and weights for the Gauss-Legendre integration routine +//(JD) Since these were always malloc'd one at a time with fixed length ~100, I've changed them to fixed-length arrays +static float xi_GL[NGL_INT+1], wi_GL[NGL_INT+1]; +static float GL_limit[2] = {0}; + +//gets the (stellar/escape) fraction at a certain mass (in units of 1/normalisation at 1e10) +double get_frac_limit(double M, double norm, double alpha, double limit, bool mini){ + double pivot = mini ? 1e7 : 1e10; + if ((alpha > 0. && M > limit) || (alpha < 0. && M < limit)) + return 1/norm; + + //if alpha is zero, this returns 1 as expected (note strict inequalities above) + return pow(M/pivot,alpha); +} + +////MASS FUNCTIONS BELOW////// + +/* sheth correction to delta crit */ +double sheth_delc_dexm(double del, double sig){ + return sqrt(SHETH_a)*del*(1. + global_params.SHETH_b*pow(sig*sig/(SHETH_a*del*del), global_params.SHETH_c)); +} + +/*DexM uses a fit to this barrier to acheive MF similar to ST, Here I use the fixed version for the sampler*/ +//NOTE: if I made this a table it would save a pow call per condition in the sampler +double sheth_delc_fixed(double del, double sig){ + return sqrt(JENKINS_a)*del*(1. + JENKINS_b*pow(sig*sig/(JENKINS_a*del*del), JENKINS_c)); +} + +//Get the relevant excursion set barrier density given the user-specified HMF +double get_delta_crit(int HMF, double sigma, double growthf){ + if(HMF==4) + return DELTAC_DELOS; + if(HMF==1) + return sheth_delc_fixed(Deltac/growthf,sigma)*growthf; + + return Deltac; +} + +/* +Unconditional Mass function from Delos 2023 (https://arxiv.org/pdf/2311.17986.pdf) +Matches well with N-bodies (M200), has a corresponding Conditional Mass Function (below) and +an excursion set method. Hence can be consistently used throughout the Halo Finder, Halo Sampler +And radiation. The mass functions are based off a constant barrier delta = 1.5 and a top-hat window function +*/ +double dNdlnM_Delos(double growthf, double lnM){ + double dfdnu,dsigmadm,sigma,sigma_inv,dfdM; + double nu; + //hardcoded for now + const double coeff_nu = 0.519; + const double index_nu = 0.582; + const double exp_factor = -0.469; + + sigma = EvaluateSigma(lnM); + sigma_inv = 1/sigma; + dsigmadm = EvaluatedSigmasqdm(lnM) * (0.5*sigma_inv); //d(s^2)/dm z0 to dsdm + + nu = DELTAC_DELOS*sigma_inv/growthf; + + dfdnu = coeff_nu*pow(nu,index_nu)*exp(exp_factor*nu*nu); + dfdM = dfdnu * fabs(dsigmadm) * sigma_inv; + + //NOTE: dfdM == constants*dNdlnM + return dfdM; +} + +double dNdlnM_conditional_Delos(double growthf, double lnM, double delta_cond, double sigma_cond){ + double dfdnu,dsigmadm,sigma,sigdiff_inv,dfdM; + double nu; + //hardcoded for now + const double coeff_nu = 0.519; + const double index_nu = 0.582; + const double exp_factor = -0.469; + + sigma = EvaluateSigma(lnM); + if(sigma < sigma_cond) return 0.; + dsigmadm = EvaluatedSigmasqdm(lnM) * 0.5; //d(s^2)/dm to s*dsdm + sigdiff_inv = sigma == sigma_cond ? 1e6 : 1/(sigma*sigma - sigma_cond*sigma_cond); + + nu = (DELTAC_DELOS - delta_cond)*sqrt(sigdiff_inv)/growthf; + + dfdnu = coeff_nu*pow(nu,index_nu)*exp(exp_factor*nu*nu); + dfdM = dfdnu * fabs(dsigmadm) * sigdiff_inv; + + //NOTE: dfdM == constants*dNdlnM + return dfdM; +} + +//Sheth Tormen 2002 fit for the CMF, while the moving barrier does not allow for a simple rescaling, it has been found +//That a taylor expansion of the barrier shape around the point of interest well approximates the simulations +double st_taylor_factor(double sig, double sig_cond, double growthf, double *zeroth_order){ + int i; + double a = JENKINS_a; + double alpha = JENKINS_c; //fixed instead of global_params.SHETH_c bc of DexM corrections + double beta = JENKINS_b; //fixed instead of global_params.SHETH_b + + double del = Deltac/growthf; + + double sigsq = sig*sig; + double sigsq_inv = 1./sigsq; + double sigcsq = sig_cond*sig_cond; + double sigdiff = sig == sig_cond ? 1e-6 : sigsq - sigcsq; + + // This array cumulatively builds the taylor series terms + // sigdiff^n / n! * df/dsigma (polynomial w alpha) + double t_array[6]; + t_array[0] = 1.; + for(i=1;i<6;i++) + t_array[i] = t_array[i-1] * (-sigdiff) / i * (alpha-i+1) * sigsq_inv; + + //Sum small to large + double result = 0.; + for(i=5;i>=0;i--){ + result += t_array[i]; + } + + double prefactor_1 = sqrt(a)*del; + double prefactor_2 = beta*pow(sigsq_inv*(a*del*del),-alpha); + + result = prefactor_1*(1 + prefactor_2*result); + *zeroth_order = prefactor_1*(1+prefactor_2); //0th order term gives the barrier for efficiency + return result; +} + +//CMF Corresponding to the Sheth Mo Tormen HMF (Sheth+ 2002) +double dNdM_conditional_ST(double growthf, double lnM, double delta_cond, double sigma_cond){ + double sigma1, dsigmasqdm, Barrier, factor, sigdiff_inv, result; + double delta_0 = delta_cond / growthf; + sigma1 = EvaluateSigma(lnM); + dsigmasqdm = EvaluatedSigmasqdm(lnM); + if(sigma1 < sigma_cond) return 0.; + + factor = st_taylor_factor(sigma1,sigma_cond,growthf,&Barrier) - delta_0; + + sigdiff_inv = sigma1 == sigma_cond ? 1e6 : 1/(sigma1*sigma1 - sigma_cond*sigma_cond); + + result = -dsigmasqdm*factor*pow(sigdiff_inv,1.5)*exp(-(Barrier - delta_0)*(Barrier - delta_0)*0.5*(sigdiff_inv))/sqrt(2.*PI); + return result; +} + +/* + FUNCTION dNdM_st(z, M) + Computes the Press_schechter mass function with Sheth-Torman correction for ellipsoidal collapse at + redshift z, and dark matter halo mass M (in solar masses). Moving barrier B(z,sigma) and sharp-k window functino + + Uses interpolated sigma and dsigmadm to be computed faster. Necessary for mass-dependent ionising efficiencies. + + The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: + comoving Mpc^-3 Msun^-1 + + Reference: Sheth, Mo, Torman 2001 + */ +double dNdlnM_st(double growthf, double lnM){ + double sigma, dsigmadm, nuhat; + sigma = EvaluateSigma(lnM); + dsigmadm = EvaluatedSigmasqdm(lnM); + + sigma = sigma * growthf; + dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); + + nuhat = sqrt(SHETH_a) * Deltac / sigma; + + return -(dsigmadm/sigma) * sqrt(2./PI)*SHETH_A * (1+ pow(nuhat, -2*SHETH_p)) * nuhat * pow(E, -nuhat*nuhat/2.0); +} + +//Conditional Extended Press-Schechter Mass function, with constant barrier delta=1.682 and sharp-k window function +double dNdM_conditional_EPS(double growthf, double lnM, double delta_cond, double sigma_cond){ + double sigma1, dsigmasqdm, sigdiff_inv, del; + + sigma1 = EvaluateSigma(lnM); + dsigmasqdm = EvaluatedSigmasqdm(lnM); + + //limit setting + if(sigma1 < sigma_cond) return 0.; + sigdiff_inv = sigma1 == sigma_cond ? 1e6 : 1/(sigma1*sigma1 - sigma_cond*sigma_cond); + del = (Deltac - delta_cond)/growthf; + + return -del*dsigmasqdm*pow(sigdiff_inv, 1.5)*exp(-del*del*0.5*sigdiff_inv)/sqrt(2.*PI); +} + +/* + FUNCTION dNdM(growthf, M) + Computes the Press_schechter mass function at + redshift z (using the growth factor), and dark matter halo mass M (in solar masses). + + Uses interpolated sigma and dsigmadm to be computed faster. Necessary for mass-dependent ionising efficiencies. + + The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: + comoving Mpc^-3 Msun^-1 + + Reference: Padmanabhan, pg. 214 + */ +double dNdlnM_PS(double growthf, double lnM){ + double sigma, dsigmadm; + + sigma = EvaluateSigma(lnM); + dsigmadm = EvaluatedSigmasqdm(lnM); + + sigma = sigma * growthf; + dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); + return -sqrt(2/PI) * (Deltac/(sigma*sigma)) * dsigmadm * exp(-(Deltac*Deltac)/(2*sigma*sigma)); +} + +//The below mass functions do not have a CMF given +/* + FUNCTION dNdM_WatsonFOF(z, M) + Computes the Press_schechter mass function with Warren et al. 2011 correction for ellipsoidal collapse at + redshift z, and dark matter halo mass M (in solar masses). + + The Universial FOF function (Eq. 12) of Watson et al. 2013 + + The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: + comoving Mpc^-3 Msun^-1 + + Reference: Watson et al. 2013 + */ +double dNdlnM_WatsonFOF(double growthf, double lnM){ + + double sigma, dsigmadm, f_sigma; + + sigma = EvaluateSigma(lnM); + dsigmadm = EvaluatedSigmasqdm(lnM); + + sigma = sigma * growthf; + dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); + + f_sigma = Watson_A * ( pow( Watson_beta/sigma, Watson_alpha) + 1. ) * exp( - Watson_gamma/(sigma*sigma) ); + + return -(dsigmadm/sigma) * f_sigma; +} + +/* + FUNCTION dNdM_WatsonFOF_z(z, M) + Computes the Press_schechter mass function with Warren et al. 2011 correction for ellipsoidal collapse at + redshift z, and dark matter halo mass M (in solar masses). + + The Universial FOF function, with redshift evolution (Eq. 12 - 15) of Watson et al. 2013. + + The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: + comoving Mpc^-3 Msun^-1 + + Reference: Watson et al. 2013 + */ +double dNdlnM_WatsonFOF_z(double z, double growthf, double lnM){ + double sigma, dsigmadm, A_z, alpha_z, beta_z, Omega_m_z, f_sigma; + + sigma = EvaluateSigma(lnM); + dsigmadm = EvaluatedSigmasqdm(lnM); + + sigma = sigma * growthf; + dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); + + Omega_m_z = (cosmo_params_global->OMm)*pow(1.+z,3.) / ( (cosmo_params_global->OMl) + (cosmo_params_global->OMm)*pow(1.+z,3.) + (global_params.OMr)*pow(1.+z,4.) ); + + A_z = Omega_m_z * ( Watson_A_z_1 * pow(1. + z, Watson_A_z_2 ) + Watson_A_z_3 ); + alpha_z = Omega_m_z * ( Watson_alpha_z_1 * pow(1.+z, Watson_alpha_z_2 ) + Watson_alpha_z_3 ); + beta_z = Omega_m_z * ( Watson_beta_z_1 * pow(1.+z, Watson_beta_z_2 ) + Watson_beta_z_3 ); + + f_sigma = A_z * ( pow(beta_z/sigma, alpha_z) + 1. ) * exp( - Watson_gamma_z/(sigma*sigma) ); + + return -(dsigmadm/sigma) * f_sigma; +} + +double nion_fraction(double M, void *param_struct){ + struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; + double M_turn_lower = params.Mturn; + double f_starn = params.f_star_norm; + double a_star = params.alpha_star; + double f_escn = params.f_esc_norm; + double a_esc = params.alpha_esc; + double Mlim_star = params.Mlim_star; + double Mlim_esc = params.Mlim_esc; + + double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,false); + double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,false); + + return Fstar * Fesc * exp(-M_turn_lower/M); +} + +double nion_fraction_mini(double M, void *param_struct){ + struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; + double M_turn_lower = params.Mturn; + double M_turn_upper = params.Mturn_upper; + double f_starn = params.f_star_norm; + double a_star = params.alpha_star; + double f_escn = params.f_esc_norm; + double a_esc = params.alpha_esc; + double Mlim_star = params.Mlim_star; + double Mlim_esc = params.Mlim_esc; + + double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,true); + double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,true); + + return Fstar * Fesc * exp(-M_turn_lower/M - M/M_turn_upper); +} + +double conditional_mf(double growthf, double lnM, double delta, double sigma, int HMF){ + //dNdlnM = dfcoll/dM * M / M * constants + if(HMF==0) { + return dNdM_conditional_EPS(growthf,lnM,delta,sigma); + } + if(HMF==1) { + return dNdM_conditional_ST(growthf,lnM,delta,sigma); + } + if(HMF==4) { + return dNdlnM_conditional_Delos(growthf,lnM,delta,sigma); + } + //NOTE: Normalisation scaling is currently applied outside the integral, per condition + //This will be the rescaled EPS CMF, + return dNdM_conditional_EPS(growthf,lnM,delta,sigma); + +} + +double c_mf_integrand(double lnM, void *param_struct){ + struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; + double growthf = params.growthf; + double delta = params.delta; //the condition delta + double sigma2 = params.sigma_cond; + int HMF = params.HMF; + + return conditional_mf(growthf,lnM,delta,sigma2,HMF); +} + +double c_fcoll_integrand(double lnM, void *param_struct){ + return exp(lnM) * c_mf_integrand(lnM,param_struct); +} + +double c_nion_integrand(double lnM, void *param_struct){ + return nion_fraction(exp(lnM),param_struct) * exp(lnM) * c_mf_integrand(lnM,param_struct); +} + +//The reason this is separated from the above is the second exponent +double c_nion_integrand_mini(double lnM, void *param_struct){ + + return nion_fraction_mini(exp(lnM),param_struct) * exp(lnM) * c_mf_integrand(lnM,param_struct); +} + +double unconditional_mf(double growthf, double lnM, double z, int HMF){ + //most of the UMFs are defined with M, but we integrate over lnM + //NOTE: HMF > 4 or < 0 gets caught earlier, so unless some strange change is made this is fine + if(HMF==0) { + return dNdlnM_PS(growthf, lnM); + } + if(HMF==1) { + return dNdlnM_st(growthf, lnM); + } + if(HMF==2) { + return dNdlnM_WatsonFOF(growthf, lnM); + } + if(HMF==3) { + return dNdlnM_WatsonFOF_z(z, growthf, lnM); + } + if(HMF==4) { + return dNdlnM_Delos(growthf, lnM); + } + else{ + LOG_ERROR("Invalid HMF %d",HMF); + Throw(ValueError); + } +} + +double u_mf_integrand(double lnM, void *param_struct){ + struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; + double growthf = params.growthf; + double z = params.redshift; + int HMF = params.HMF; + + return unconditional_mf(growthf,lnM,z,HMF); +} + +double u_fcoll_integrand(double lnM, void *param_struct){ + return exp(lnM) * u_mf_integrand(lnM,param_struct); +} + +double u_nion_integrand(double lnM, void *param_struct){ + struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; + double M_turn = params.Mturn; + double f_starn = params.f_star_norm; + double a_star = params.alpha_star; + double f_escn = params.f_esc_norm; + double a_esc = params.alpha_esc; + double Mlim_star = params.Mlim_star; + double Mlim_esc = params.Mlim_esc; + + double M = exp(lnM); + + double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,false); + double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,false); + + return M * Fstar * Fesc * exp(-M_turn/M) * u_mf_integrand(lnM,param_struct); +} + +//The reason this is separated from the above is the second exponent +double u_nion_integrand_mini(double lnM, void *param_struct){ + struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; + double M_turn_lower = params.Mturn; + double M_turn_upper = params.Mturn_upper; + double f_starn = params.f_star_norm; + double a_star = params.alpha_star; + double f_escn = params.f_esc_norm; + double a_esc = params.alpha_esc; + double Mlim_star = params.Mlim_star; + double Mlim_esc = params.Mlim_esc; + + double M = exp(lnM); + + double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,true); + double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,true); + + return M * Fstar * Fesc * exp(-M_turn_lower/M - M/M_turn_upper) * u_mf_integrand(lnM,param_struct); +} + +///// INTEGRATION ROUTINES BELOW ///// + +//TODO: make type enum for clarity (but cffi doesn't seem to like enum in 21cmFAST.h) +//NOTE: SFR is obtained from nion with alpha_esc==0 and f_esc==1 +//Currently the scheme is to use negative numbers for conditionals, and (1,2,3,4) for (number,mass,n_ion,n_ion_mini) +double (*get_integrand_function(int type))(double,void*){ + if(type==1) + return &u_mf_integrand; //Unondtional mass function integral + if(type==2) + return &u_fcoll_integrand; //Unconditional collapsed fraction integral + if(type==3) + return &u_nion_integrand; //Unconditional N_ion integral (two power-laws and one exponential) + if(type==4) + return &u_nion_integrand_mini; //Unconditional N_ion minihalo integral (two power-laws and two exponentials) + if(type==-1) + return &c_mf_integrand; //Conditional mass function integral + if(type==-2) + return &c_fcoll_integrand; //Conditional collapsed fraction integral + if(type==-3) + return &c_nion_integrand; //Conditional N_ion integral (two power-laws and one exponential) + if(type==-4) + return &c_nion_integrand_mini; //Conditional N_ion minihalo integral (two power-laws and two exponentials) + + LOG_ERROR("Invalid type %d for MF integral",type); + Throw(ValueError); +} + +//Integral of a CMF or UMF +//In future all MF integrals will go through here, simply selecting the integrand function from a switch +double IntegratedNdM_QAG(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type){ + double result, error, lower_limit, upper_limit; + gsl_function F; + // double rel_tol = FRACT_FLOAT_ERR*128; //<- relative tolerance + double rel_tol = 1e-3; //<- relative tolerance + int w_size = 1000; + gsl_integration_workspace * w + = gsl_integration_workspace_alloc (w_size); + + int status; + F.function = get_integrand_function(type); + F.params = ¶ms; + lower_limit = lnM_lo; + upper_limit = lnM_hi; + + gsl_set_error_handler_off(); + status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol, + w_size, GSL_INTEG_GAUSS61, w, &result, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%.3e (%.3e) upper_limit=%.3e (%.3e) rel_tol=%.3e result=%.3e error=%.3e",lower_limit,exp(lower_limit),upper_limit,exp(upper_limit),rel_tol,result,error); + LOG_ERROR("data: z=%.3e growthf=%.3e HMF=%d type=%d ",params.redshift,params.growthf,params.HMF,type); + LOG_ERROR("sigma=%.3e delta=%.3e",params.sigma_cond,params.delta); + LOG_ERROR("Mturn_lo=%.3e f*=%.3e a*=%.3e Mlim*=%.3e",params.Mturn,params.f_star_norm,params.alpha_star,params.Mlim_star); + LOG_ERROR("f_escn=%.3e a_esc=%.3e Mlim_esc=%.3e",params.f_esc_norm,params.alpha_esc,params.Mlim_esc); + LOG_ERROR("Mturn_hi %.3e",params.Mturn_upper); + CATCH_GSL_ERROR(status); + } + + gsl_integration_workspace_free (w); + + return result; +} + +//calculates the weightings and the positions for any Gauss-Legendre quadrature. +void gauleg(float x1, float x2, float x[], float w[], int n) +//Given the lower and upper limits of integration x1 and x2, and given n, this routine returns arrays x[1..n] and w[1..n] of length n, +//containing the abscissas and weights of the Gauss- Legendre n-point quadrature formula. +{ + + int m,j,i; + double z1,z,xm,xl,pp,p3,p2,p1; + + m=(n+1)/2; + xm=0.5*(x2+x1); + xl=0.5*(x2-x1); + for (i=1;i<=m;i++) { + //High precision is a good idea for this routine. + //The roots are symmetric in the interval, so we only have to find half of them. + //Loop over the desired roots. + + z=cos(3.141592654*(i-0.25)/(n+0.5)); + + //Starting with the above approximation to the ith root, we enter the main loop of refinement by Newton’s method. + do { + p1=1.0; + p2=0.0; + for (j=1;j<=n;j++) { + //Loop up the recurrence relation to get the Legendre polynomial evaluated at z. + p3=p2; + p2=p1; + p1=((2.0*j-1.0)*z*p2-(j-1.0)*p3)/j; + } + //p1 is now the desired Legendre polynomial. We next compute pp, its derivative, by a standard relation involving also p2, + //the polynomial of one lower order. + pp=n*(z*p1-p2)/(z*z-1.0); + z1=z; + z=z1-p1/pp; + } while (fabs(z-z1) > EPS2); + x[i]=xm-xl*z; + x[n+1-i]=xm+xl*z; + w[i]=2.0*xl/((1.0-z*z)*pp*pp); + w[n+1-i]=w[i]; + } +} + +//Specific initialistion for the global arrays +void initialise_GL(float lnM_Min, float lnM_Max){ + //don't redo if you don't have to + if(lnM_Min == GL_limit[0] && lnM_Max == GL_limit[1]) + return; + + gauleg(lnM_Min,lnM_Max,xi_GL,wi_GL,NGL_INT); + GL_limit[0] = lnM_Min; + GL_limit[1] = lnM_Max; +} + +//actually perform the GL integration +//NOTE: that the lnM limits are not used +double IntegratedNdM_GL(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type){ + int i; + double integral = 0; + if((float)lnM_lo != (float)GL_limit[0] || (float)lnM_hi != (float)GL_limit[1]){ + LOG_ERROR("Integral limits [%.8e %.8e] do not match Gauss Legendre limits [%.8e %.8e]!",exp(lnM_lo),exp(lnM_hi),GL_limit[0],GL_limit[1]); + Throw(TableGenerationError); + } + + for(i=1; i<(NGL_INT+1); i++){ + integral += wi_GL[i]*(get_integrand_function(type))(xi_GL[i],¶ms); + } + + return integral; +} + +#include +//JBM: Integral of a power-law times exponential for EPS: \int dnu nu^beta * exp(-nu/2)/sqrt(nu) from numin to infty. +double Fcollapprox(double numin, double beta){ +//nu is deltacrit^2/sigma^2, corrected by delta(R) and sigma(R) + double gg = gsl_sf_gamma_inc(0.5+beta,0.5*numin); + return gg*pow(2,0.5+beta)*pow(2.0*PI,-0.5); +} + +//This takes into account the last approximation in Munoz+22, where erfc (beta=0) is used +//NOTE: even though nu_condition is defined in the unconditional (no sigma_cond), here it +// represents where nu_tilde == nu_condition (effectively a final pivot point) +//NOTE: This assumes numin < nucondition, otherise it fails +double Fcollapprox_condition(double numin, double nucondition, double beta){ + return (Fcollapprox(numin,beta) - Fcollapprox(nucondition,beta)) + Fcollapprox(nucondition,0.)*pow(nucondition,beta); +} + +//This routine assumes sharp cutoffs for each turnover rather than exponential, assumes a triple power-law form for sigma(M) +// and takes advantage of the fact that Gamma_inc(x,min) = integral_min^inf (t^(x-1)exp(-t)) dt which is satisfied for the HMF when the +// above approximations are made +//Originally written by JBM within the GL integration before it was separated here and generalised to the other integrals +double MFIntegral_Approx(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type){ + //variables used in the calculation + double delta,sigma_c; + double index_base; + + if(params.HMF != 0){ + LOG_ERROR("Approximate Fcoll is currently only implemented for EPS"); + LOG_ERROR("Ensure parameter input specifically to this function has HMF==0"); + Throw(TableGenerationError); + } + double growthf = params.growthf; + if(type < 0){ + //we are a conditional mf + delta = params.delta; + sigma_c = params.sigma_cond; + } + else{ + //unconditional + delta = 0.; + sigma_c = 0.; + } + + double lnM_lo_limit = lnM_lo; + double lnM_hi_limit = lnM_hi; + //(Speed): by passing in log(M_turnover) i can avoid these 2 log calls + double lnMturn_l = log(params.Mturn); + double lnMturn_u = log(params.Mturn_upper); + //(Speed): LOG(MPIVOTn) can be pre-defined via macro + double lnMp1 = log(MPIVOT1); + double lnMp2 = log(MPIVOT2); + + //The below limit setting is done simply so that variables which do not conern particular integrals + // can be left undefined, rather than explicitly set to some value (0 or 1e20) + //Mass and number integrals set the lower cutoff to the integral limit + if(fabs(type) >= 3 && lnMturn_l > lnM_lo_limit) + lnM_lo_limit = lnMturn_l; + //non-minihalo integrals set the upper cutoff to the integral limit + if(fabs(type) == 4 && lnMturn_u < lnM_hi_limit) + lnM_hi_limit = lnMturn_u; + + //it is possible for the lower turnover (LW crit or reion feedback) + // to be higher than the upper limit (atomic limit) or the condition + if(lnM_lo_limit >= lnM_hi_limit || EvaluateSigma(lnM_lo_limit) <= sigma_c){ + return 0.; + } + + //n_ion or MINI + if(fabs(type) >= 3) + index_base = params.alpha_star + params.alpha_esc; + //fcoll + else if(fabs(type)==2) + index_base = 0.; + //nhalo + else + index_base = -1.; + + double delta_arg = pow((Deltac - delta)/growthf, 2); + double beta1 = index_base * AINDEX1 * 0.5; //exponent for Fcollapprox for nu>nupivot1 (large M) + double beta2 = index_base * AINDEX2 * 0.5; //exponent for Fcollapprox for nupivot2 nu_pivot2){ + fcoll += (Fcollapprox(nu_lo_limit,beta2))*pow(nu_pivot1_umf,-beta2); + } + else { + fcoll += (Fcollapprox(nu_pivot2,beta2))*pow(nu_pivot1_umf,-beta2); + fcoll += (Fcollapprox(nu_lo_limit,beta3)-Fcollapprox(nu_pivot2,beta3) )*pow(nu_pivot2_umf,-beta3); + } + } + } + else{ + if(nu_lo_limit >= nu_condition){ //fully in the flat part of sigma(nu), M^alpha is nu-independent. + // This is just an erfc, remembering that the conditional nu can be higher than the unconditional nu of the condition + return Fcollapprox(nu_lo_limit,0.); + } + + if(nu_lo_limit >= nu_pivot1){ + //We use the condition version wherever the nu range may intersect nu_condition (i.e beta1) + fcoll += Fcollapprox_condition(nu_lo_limit,nu_condition,beta1)*pow(nu_pivot1_umf,-beta1); + } + else{ + fcoll += Fcollapprox_condition(nu_pivot1,nu_condition,beta1)*pow(nu_pivot1_umf,-beta1); + if (nu_lo_limit > nu_pivot2){ + fcoll += (Fcollapprox(nu_lo_limit,beta2)-Fcollapprox(nu_pivot1,beta2))*pow(nu_pivot1_umf,-beta2); + } + else { + fcoll += (Fcollapprox(nu_pivot2,beta2)-Fcollapprox(nu_pivot1,beta2) )*pow(nu_pivot1_umf,-beta2); + fcoll += (Fcollapprox(nu_lo_limit,beta3)-Fcollapprox(nu_pivot2,beta3) )*pow(nu_pivot2_umf,-beta3); + } + } + } + + if (fcoll<=0.0){ + LOG_DEBUG("Negative fcoll? fc=%.1le\n",fcoll); + fcoll=1e-40; + } + return fcoll; +} + +double IntegratedNdM(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type, int method){ + if(method==0 || (method==1 && params.delta > global_params.CRIT_DENS_TRANSITION)) + return IntegratedNdM_QAG(lnM_lo, lnM_hi, params, type); + if(method==1) + return IntegratedNdM_GL(lnM_lo, lnM_hi, params, type); + if(method==2) + return MFIntegral_Approx(lnM_lo, lnM_hi, params, type); + + LOG_ERROR("Invalid integration method %d",method); + Throw(ValueError); +} + +//Some wrappers over the integration functions for specific integrals// + +/* + FUNCTION FgtrM(z, M) + Computes the fraction of mass contained in haloes with mass > M at redshift z + */ +double FgtrM(double z, double M){ + double del, sig; + + del = Deltac/dicke(z); //regular spherical collapse delta + sig = sigma_z0(M); + + return splined_erfc(del / (sqrt(2)*sig)); +} + +/* + FUNCTION FgtrM_wsigma(z, sigma_z0(M)) + Computes the fraction of mass contained in haloes with mass > M at redshift z. + Requires sigma_z0(M) rather than M to make certain heating integrals faster + */ +double FgtrM_wsigma(double z, double sig){ + double del; + + del = Deltac/dicke(z); //regular spherical collapse delta + + return splined_erfc(del / (sqrt(2)*sig)); +} + +double Nhalo_General(double z, double lnM_min, double lnM_max){ + struct parameters_gsl_MF_integrals integral_params = { + .redshift = z, + .growthf = dicke(z), + .HMF = user_params_global->HMF, + }; + return IntegratedNdM(lnM_min, lnM_max, integral_params, 1, 0); +} + +double Fcoll_General(double z, double lnM_min, double lnM_max){ + struct parameters_gsl_MF_integrals integral_params = { + .redshift = z, + .growthf = dicke(z), + .HMF = user_params_global->HMF, + }; + return IntegratedNdM(lnM_min, lnM_max, integral_params, 2, 0); +} + +double Nion_General(double z, double lnM_Min, double lnM_Max, double MassTurnover, double Alpha_star, double Alpha_esc, double Fstar10, + double Fesc10, double Mlim_Fstar, double Mlim_Fesc){ + struct parameters_gsl_MF_integrals params = { + .redshift = z, + .growthf = dicke(z), + .Mturn = MassTurnover, + .alpha_star = Alpha_star, + .alpha_esc = Alpha_esc, + .f_star_norm = Fstar10, + .f_esc_norm = Fesc10, + .Mlim_star = Mlim_Fstar, + .Mlim_esc = Mlim_Fesc, + .HMF = user_params_global->HMF, + }; + return IntegratedNdM(lnM_Min,lnM_Max,params,3,0); +} + +double Nion_General_MINI(double z, double lnM_Min, double lnM_Max, double MassTurnover, double MassTurnover_upper, double Alpha_star, + double Alpha_esc, double Fstar7_MINI, double Fesc7_MINI, double Mlim_Fstar, double Mlim_Fesc){ + struct parameters_gsl_MF_integrals params = { + .redshift = z, + .growthf = dicke(z), + .Mturn = MassTurnover, + .Mturn_upper = MassTurnover_upper, + .alpha_star = Alpha_star, + .alpha_esc = Alpha_esc, + .f_star_norm = Fstar7_MINI, + .f_esc_norm = Fesc7_MINI, + .Mlim_star = Mlim_Fstar, + .Mlim_esc = Mlim_Fesc, + .HMF = user_params_global->HMF, + }; + return IntegratedNdM(lnM_Min,lnM_Max,params,4,0); +} + +double Nhalo_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method){ + struct parameters_gsl_MF_integrals params = { + .growthf = growthf, + .HMF = user_params_global->HMF, + .sigma_cond = sigma, + .delta = delta, + }; + + if(delta <= -1. || lnM1 >= log(M_cond)) + return 0.; + //return 1 halo AT THE CONDITION MASS if delta is exceeded + if(delta > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma,growthf)){ + if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) //this limit is not ideal, but covers floating point errors when we set lnM2 == log(M_cond) + return 1./M_cond; + else + return 0.; + } + + return IntegratedNdM(lnM1,lnM2,params,-1, method); +} + +double Mcoll_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method){ + struct parameters_gsl_MF_integrals params = { + .growthf = growthf, + .HMF = user_params_global->HMF, + .sigma_cond = sigma, + .delta = delta, + }; + + if(delta <= -1. || lnM1 >= log(M_cond)) + return 0.; + //return 100% of mass AT THE CONDITION MASS if delta is exceeded + if(delta > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma,growthf)){ + if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) //this limit is not ideal, but covers floating point errors when we set lnM2 == log(M_cond) + return 1.; + else + return 0.; + } + return IntegratedNdM(lnM1,lnM2,params,-2, method); +} + +double Nion_ConditionalM_MINI(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, + double MassTurnover_upper, double Alpha_star, double Alpha_esc, double Fstar7, + double Fesc7, double Mlim_Fstar, double Mlim_Fesc, int method){ + struct parameters_gsl_MF_integrals params = { + .growthf = growthf, + .Mturn = MassTurnover, + .Mturn_upper = MassTurnover_upper, + .alpha_star = Alpha_star, + .alpha_esc = Alpha_esc, + .f_star_norm = Fstar7, + .f_esc_norm = Fesc7, + .Mlim_star = Mlim_Fstar, + .Mlim_esc = Mlim_Fesc, + .HMF = user_params_global->HMF, + .sigma_cond = sigma2, + .delta = delta2, + }; + + if(delta2 <= -1. || lnM1 >= log(M_cond)) + return 0.; + //return 1 halo at the condition mass if delta is exceeded + //NOTE: this will almost always be zero, due to the upper turover, + // however this replaces an integral so it won't be slow + if(delta2 > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma2,growthf)){ + if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) //this limit is not ideal, but covers floating point errors when we set lnM2 == log(M_cond) + return nion_fraction_mini(M_cond,¶ms); //NOTE: condition mass is used as if it were Lagrangian (no 1+delta) + else + return 0.; + } + + //If we don't have a corresponding CMF, use EPS and normalise + //NOTE: it's possible we may want to use another default + if(params.HMF != 0 && params.HMF != 1 && params.HMF != 4) + params.HMF = 0; + + return IntegratedNdM(lnM1,lnM2,params,-4,method); +} + +double Nion_ConditionalM(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, + double Alpha_star, double Alpha_esc, double Fstar10, double Fesc10, double Mlim_Fstar, + double Mlim_Fesc, int method){ + struct parameters_gsl_MF_integrals params = { + .growthf = growthf, + .Mturn = MassTurnover, + .alpha_star = Alpha_star, + .alpha_esc = Alpha_esc, + .f_star_norm = Fstar10, + .f_esc_norm = Fesc10, + .Mlim_star = Mlim_Fstar, + .Mlim_esc = Mlim_Fesc, + .HMF = user_params_global->HMF, + .sigma_cond = sigma2, + .delta = delta2, + }; + + if(delta2 <= -1. || lnM1 >= log(M_cond)) + return 0.; + //return 1 halo at the condition mass if delta is exceeded + if(delta2 > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma2,growthf)){ + if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) + return nion_fraction(M_cond,¶ms); //NOTE: condition mass is used as if it were Lagrangian (no 1+delta) + else + return 0.; + } + + //If we don't have a corresponding CMF, use EPS and normalise + //NOTE: it's possible we may want to use another default + if(params.HMF != 0 && params.HMF != 1 && params.HMF != 4) + params.HMF = 0; + + return IntegratedNdM(lnM1,lnM2,params,-3, method); +} + +float erfcc(float x) +{ + double t,q,ans; + + q=fabs(x); + t=1.0/(1.0+0.5*q); + ans=t*exp(-q*q-1.2655122+t*(1.0000237+t*(0.374092+t*(0.0967842+ + t*(-0.1862881+t*(0.2788681+t*(-1.13520398+t*(1.4885159+ + t*(-0.82215223+t*0.17087277))))))))); + return x >= 0.0 ? ans : 2.0-ans; +} + +double splined_erfc(double x){ + if (x < 0){ + return 1.0; + } + + //The Actual ERFC spline was removed a while ago, if we remake it it should be + // in interp_tables.c with the others as an RGTable1D + return erfcc(x); +} + + +/* + calculates the fraction of mass contained in haloes with mass > M at redshift z, + in regions with a linear overdensity of del_bias, and standard deviation sig_bias +*/ + +//I wrote a version of FgtrM which takes the growth func instead of z for a bit of speed +double FgtrM_bias_fast(float growthf, float del_bias, float sig_small, float sig_large){ + double del, sig; + if (sig_large > sig_small){ // biased region is smaller that halo! + LOG_ERROR("Trying to compute FgtrM in region where M_min > M_max"); + Throw(ValueError); + } + //sometimes they are the same to float precision, where the M_condition ~ M_Min + if(sig_large == sig_small){ + return 0.; + } + // del = Deltac/growthf - del_bias; //NOTE HERE DELTA EXTRAPOLATED TO z=0 + sig = sqrt(sig_small*sig_small - sig_large*sig_large); + del = (Deltac - del_bias)/growthf; + + //if the density is above critical on this scale, it is collapsed + //NOTE: should we allow del < 0??? We would need to change dfcolldz to prevent zero dfcoll + // if(del < FRACT_FLOAT_ERR){ + // return 1.; + // } + return splined_erfc(del / (sqrt(2)*sig)); +} + +/* Uses sigma parameters instead of Mass for scale */ +double sigmaparam_FgtrM_bias(float z, float sigsmallR, float del_bias, float sig_bias){ + return FgtrM_bias_fast(dicke(z),del_bias,sigsmallR,sig_bias); +} + +double FgtrM_bias(double z, double M, double del_bias, double sig_bias){ + return sigmaparam_FgtrM_bias(z,EvaluateSigma(log(M)),del_bias,sig_bias); +} + +// Redshift derivative of the conditional collapsed fraction +float dfcoll_dz(float z, float sigma_min, float del_bias, float sig_bias){ + double dz,z1,z2; + double fc1,fc2,ans; + + dz = 0.001; + z1 = z + dz; + z2 = z - dz; + fc1 = sigmaparam_FgtrM_bias(z1, sigma_min, del_bias, sig_bias); + fc2 = sigmaparam_FgtrM_bias(z2, sigma_min, del_bias, sig_bias); + ans = (fc1 - fc2)/(2.0*dz); + return ans; +} +/* compute a mass limit where the stellar baryon fraction and the escape fraction exceed unity */ +//NOTE (JD): Why aren't we using 1e10 * pow(FRAC,-1/PL)? what am I missing here that makes the rootfind necessary +float Mass_limit (float logM, float PL, float FRAC) { + return FRAC*pow(pow(10.,logM)/1e10,PL); +} +void bisection(float *x, float xlow, float xup, int *iter){ + *x=(xlow + xup)/2.; + ++(*iter); +} + +float Mass_limit_bisection(float Mmin, float Mmax, float PL, float FRAC){ + int iter, max_iter=200; + float rel_tol=0.001; + float logMlow, logMupper, x, x1; + iter = 0; + logMlow = log10(Mmin); + logMupper = log10(Mmax); + + if (PL < 0.) { + if (Mass_limit(logMlow,PL,FRAC) <= 1.) { + return Mmin; + } + } + else if (PL > 0.) { + if (Mass_limit(logMupper,PL,FRAC) <= 1.) { + return Mmax; + } + } + else + return 0; + bisection(&x, logMlow, logMupper, &iter); + do { + if((Mass_limit(logMlow,PL,FRAC)-1.)*(Mass_limit(x,PL,FRAC)-1.) < 0.) + logMupper = x; + else + logMlow = x; + bisection(&x1, logMlow, logMupper, &iter); + if(fabs(x1-x) < rel_tol) { + return pow(10.,x1); + } + x = x1; + } + while(iter < max_iter); + + // Got to max_iter without finding a solution. + LOG_ERROR("Failed to find a mass limit to regulate stellar fraction/escape fraction is between 0 and 1."); + LOG_ERROR(" The solution does not converge or iterations are not sufficient."); +// Throw(ParameterError); + Throw(MassDepZetaError); + + return(0.0); +} +//set the minimum source mass for the integrals, If we have an exponential cutoff we go below the chosen mass by a factor of 50 +//NOTE: previously, with USE_MINI_HALOS, the sigma table was initialised with M_MIN_INTEGRAL/50, but then all integrals perofmed +// from M_MIN_INTEGRAL +double minimum_source_mass(double redshift, bool xray, AstroParams *astro_params, FlagOptions *flag_options){ + double Mmin,min_factor,mu_factor,t_vir_min; + if(flag_options->USE_MASS_DEPENDENT_ZETA && !flag_options->USE_MINI_HALOS) + min_factor = 50.; // small lower bound to cover far below the turnover + else + min_factor = 1.; //sharp cutoff + + // automatically false if !USE_MASS_DEPENDENT_ZETA + if(flag_options->USE_MINI_HALOS){ + Mmin = global_params.M_MIN_INTEGRAL; + } + // automatically true if USE_MASS_DEPENDENT_ZETA + else if(flag_options->M_MIN_in_Mass) { + //NOTE: previously this divided Mturn by 50 in spin temperature, but not in the ionised box + // which I think is a bug with M_MIN_in_Mass, since there is a sharp cutoff + Mmin = astro_params->M_TURN; + } + else { + //if the virial temp minimum is set below ionisation we need to set mu accordingly + t_vir_min = xray ? astro_params->X_RAY_Tvir_MIN : astro_params->ION_Tvir_MIN; + mu_factor = t_vir_min < 9.99999e3 ? 1.22 : 0.6; + Mmin = TtoM(redshift, t_vir_min, mu_factor); + } + + //This is mostly unused and needs to be tested + if(global_params.P_CUTOFF){ + Mmin = fmax(Mmin,M_J_WDM()); + } + + Mmin /= min_factor; + + return Mmin; +} diff --git a/src/py21cmfast/src/hmf.h b/src/py21cmfast/src/hmf.h new file mode 100644 index 00000000..cc277620 --- /dev/null +++ b/src/py21cmfast/src/hmf.h @@ -0,0 +1,69 @@ +#ifndef _HMF_H +#define _HMF_H + +#include "InputParameters.h" +//integrals + +#define MAX_DELTAC_FRAC (float)0.99 //max delta/deltac for the mass function integrals +#define DELTA_MIN -1 //minimum delta for Lagrangian mass function integrals + +//Parameters used for gsl integral on the mass function +struct parameters_gsl_MF_integrals{ + //parameters for all MF integrals + double redshift; + double growthf; + int HMF; + + //Conditional parameters + double sigma_cond; + double delta; + + //SFR additions + double Mturn; + double f_star_norm; + double alpha_star; + double Mlim_star; + + //Nion additions + double f_esc_norm; + double alpha_esc; + double Mlim_esc; + + //Minihalo additions + double Mturn_upper; +}; + +/* HMF Integrals */ +void initialise_GL(float lnM_Min, float lnM_Max); +double Nhalo_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method); +double Mcoll_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method); +double Nion_ConditionalM(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, + double Alpha_star, double Alpha_esc, double Fstar10, double Fesc10, double Mlim_Fstar, + double Mlim_Fesc, int method); +double Nion_ConditionalM_MINI(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, + double MassTurnover_upper, double Alpha_star, double Alpha_esc, double Fstar7, + double Fesc7, double Mlim_Fstar, double Mlim_Fesc, int method); +double Nion_General(double z, double lnM_Min, double lnM_Max, double MassTurnover, double Alpha_star, double Alpha_esc, double Fstar10, + double Fesc10, double Mlim_Fstar, double Mlim_Fesc); +double Nion_General_MINI(double z, double lnM_Min, double lnM_Max, double MassTurnover, double MassTurnover_upper, double Alpha_star, + double Alpha_esc, double Fstar7_MINI, double Fesc7_MINI, double Mlim_Fstar, double Mlim_Fesc); +double Nhalo_General(double z, double lnM_min, double lnM_max); +double Fcoll_General(double z, double lnM_min, double lnM_max); +double unconditional_mf(double growthf, double lnM, double z, int HMF); +double conditional_mf(double growthf, double lnM, double delta_cond, double sigma_cond, int HMF); + +/* erfc-based HMF integrals (!USE_MASS_DEPENDENT_ZETA and EPS) */ +double FgtrM(double z, double M); +double FgtrM_bias_fast(float growthf, float del_bias, float sig_small, float sig_large); +float dfcoll_dz(float z, float sigma_min, float del_bias, float sig_bias); +double splined_erfc(double x); + +/* Other values required in other files */ +double get_delta_crit(int HMF, double sigma, double growthf); +double st_taylor_factor(double sig, double sig_cond, double growthf, double *zeroth_order); +double atomic_cooling_threshold(float z); +double minimum_source_mass(double redshift, bool xray, AstroParams *astro_params, FlagOptions *flag_options); +double sheth_delc_dexm(double del, double sig); +float Mass_limit_bisection(float Mmin, float Mmax, float PL, float FRAC); + +#endif diff --git a/src/py21cmfast/src/indexing.c b/src/py21cmfast/src/indexing.h similarity index 73% rename from src/py21cmfast/src/indexing.c rename to src/py21cmfast/src/indexing.h index 4d1463f8..3da68b7b 100644 --- a/src/py21cmfast/src/indexing.c +++ b/src/py21cmfast/src/indexing.h @@ -4,8 +4,8 @@ How this works: The box size and dimensionalities (eg. D and HII_D) expressed here rely on - ``user_params_ufunc`` being initialized. This is initialized by - ``Broadcast_struct_global_UF`` in ``UsefulFunctions.c``. Thus, that function + ``user_params_global`` being initialized. This is initialized by + ``Broadcast_struct_global_[all/noastro]`` in ``InputParameters.c``. Thus, that function must be called any time the user/cosmo params change. A note on the Fourier Transform number of pixels and indexing: @@ -20,21 +20,22 @@ to do the FFT, the extra spaces don't actually get used, and the indexing macros (eg. R_FFT_INDEX) skip these extra bits to index the truly used array. */ +#include "InputParameters.h" // ------------------------------------------------------------------------------------- // Convenience Constants // ------------------------------------------------------------------------------------- -#define VOLUME (user_params_ufunc->BOX_LEN*user_params_ufunc->BOX_LEN*user_params_ufunc->NON_CUBIC_FACTOR*user_params_ufunc->BOX_LEN) // in Mpc^3 -#define DELTA_K (TWOPI/user_params_ufunc->BOX_LEN) -#define DELTA_K_PARA (TWOPI/(user_params_ufunc->NON_CUBIC_FACTOR*user_params_ufunc->BOX_LEN)) +#define VOLUME (user_params_global->BOX_LEN*user_params_global->BOX_LEN*user_params_global->NON_CUBIC_FACTOR*user_params_global->BOX_LEN) // in Mpc^3 +#define DELTA_K (TWOPI/user_params_global->BOX_LEN) +#define DELTA_K_PARA (TWOPI/(user_params_global->NON_CUBIC_FACTOR*user_params_global->BOX_LEN)) // ------------------------------------------------------------------------------------- // Convenience Macros for hi-resolution boxes // ------------------------------------------------------------------------------------- -#define D (long long)user_params_ufunc->DIM // the long long dimension -#define D_PARA (long long)(user_params_ufunc->NON_CUBIC_FACTOR*user_params_ufunc->DIM) // the long long dimension -#define MIDDLE (user_params_ufunc->DIM/2) -#define MIDDLE_PARA (user_params_ufunc->NON_CUBIC_FACTOR*user_params_ufunc->DIM/2) +#define D (long long)user_params_global->DIM // the long long dimension +#define D_PARA (long long)(user_params_global->NON_CUBIC_FACTOR*user_params_global->DIM) // the long long dimension +#define MIDDLE (user_params_global->DIM/2) +#define MIDDLE_PARA (user_params_global->NON_CUBIC_FACTOR*user_params_global->DIM/2) #define MID ((long long)MIDDLE) #define MID_PARA ((long long)MIDDLE_PARA) #define TOT_NUM_PIXELS ((unsigned long long)(D*D*D_PARA)) // no padding @@ -55,10 +56,10 @@ // ------------------------------------------------------------------------------------- // Convenience Macros for low-resolution boxes // ------------------------------------------------------------------------------------- -#define HII_D (long long) (user_params_ufunc->HII_DIM) -#define HII_D_PARA (long long) (user_params_ufunc->NON_CUBIC_FACTOR*user_params_ufunc->HII_DIM) -#define HII_MIDDLE (user_params_ufunc->HII_DIM/2) -#define HII_MIDDLE_PARA (user_params_ufunc->NON_CUBIC_FACTOR*user_params_ufunc->HII_DIM/2) +#define HII_D (long long) (user_params_global->HII_DIM) +#define HII_D_PARA (long long) (user_params_global->NON_CUBIC_FACTOR*user_params_global->HII_DIM) +#define HII_MIDDLE (user_params_global->HII_DIM/2) +#define HII_MIDDLE_PARA (user_params_global->NON_CUBIC_FACTOR*user_params_global->HII_DIM/2) #define HII_MID ((long long)HII_MIDDLE) #define HII_MID_PARA ((long long)HII_MIDDLE_PARA) #define HII_TOT_NUM_PIXELS (unsigned long long)(HII_D*HII_D*HII_D_PARA) diff --git a/src/py21cmfast/src/interp_tables.c b/src/py21cmfast/src/interp_tables.c index 3ef44412..3b860ebd 100644 --- a/src/py21cmfast/src/interp_tables.c +++ b/src/py21cmfast/src/interp_tables.c @@ -1,63 +1,83 @@ /* This file defines specific interpolation table initialisation functions, kept separate from the general interpolation table routines In order to allow them to use calculations based on other interpolation tables. Most importantly these fucntions require those from ps.c which requires the sigma(M) interpolation tables */ - +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "cosmology.h" +#include "InputParameters.h" +#include "hmf.h" +#include "interpolation.h" + +#include "interp_tables.h" + +//fixed limits and bin numbers for tables #define NDELTA 400 #define NMTURN 50//100 #define LOG10_MTURN_MAX ((double)(10)) #define LOG10_MTURN_MIN ((double)(5.-9e-8)) #define MAX_ITER_RF 200 +#define N_MASS_INTERP 300 //we need to define a density minimum for the tables, since we are in lagrangian density / linear growth it's possible to go below -1 //so we explicitly set a minimum here which sets table limits and puts no halos in cells below that (Lagrangian) density -#define DELTA_MIN -1 - -static struct UserParams * user_params_it; -static struct CosmoParams * cosmo_params_it; -static struct AstroParams * astro_params_it; -static struct FlagOptions * flag_options_it; - -void Broadcast_struct_global_IT(struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options){ - user_params_it = user_params; - cosmo_params_it = cosmo_params; - astro_params_it = astro_params; - flag_options_it = flag_options; -} //Tables for the grids -struct RGTable1D SFRD_z_table = {.allocated = false}; -struct RGTable1D Nion_z_table = {.allocated = false}; -struct RGTable2D SFRD_z_table_MINI = {.allocated = false}; -struct RGTable2D Nion_z_table_MINI = {.allocated = false}; -struct RGTable1D_f Nion_conditional_table1D = {.allocated = false}; -struct RGTable1D_f SFRD_conditional_table = {.allocated = false}; -struct RGTable2D_f Nion_conditional_table2D = {.allocated = false}; -struct RGTable2D_f Nion_conditional_table_MINI = {.allocated = false}; -struct RGTable2D_f SFRD_conditional_table_MINI = {.allocated = false}; -struct RGTable2D_f Nion_conditional_table_prev = {.allocated = false}; -struct RGTable2D_f Nion_conditional_table_MINI_prev = {.allocated = false}; +static RGTable1D SFRD_z_table = {.allocated = false}; +static RGTable1D Nion_z_table = {.allocated = false}; +static RGTable2D SFRD_z_table_MINI = {.allocated = false}; +static RGTable2D Nion_z_table_MINI = {.allocated = false}; +static RGTable1D_f Nion_conditional_table1D = {.allocated = false}; +static RGTable1D_f SFRD_conditional_table = {.allocated = false}; +static RGTable2D_f Nion_conditional_table2D = {.allocated = false}; +static RGTable2D_f Nion_conditional_table_MINI = {.allocated = false}; +static RGTable2D_f SFRD_conditional_table_MINI = {.allocated = false}; +static RGTable2D_f Nion_conditional_table_prev = {.allocated = false}; +static RGTable2D_f Nion_conditional_table_MINI_prev = {.allocated = false}; //Tables for the catalogues -struct RGTable1D Nhalo_table = {.allocated = false}; -struct RGTable1D Mcoll_table = {.allocated = false}; -struct RGTable2D Nhalo_inv_table = {.allocated = false}; +static RGTable1D Nhalo_table = {.allocated = false}; +static RGTable1D Mcoll_table = {.allocated = false}; +static RGTable2D Nhalo_inv_table = {.allocated = false}; //Tables for the old parametrization -struct RGTable1D fcoll_z_table = {.allocated = false}; -struct RGTable1D_f fcoll_conditional_table = {.allocated = false,}; -struct RGTable1D_f dfcoll_conditional_table = {.allocated = false,}; +static RGTable1D fcoll_z_table = {.allocated = false}; +static RGTable1D_f fcoll_conditional_table = {.allocated = false,}; +static RGTable1D_f dfcoll_conditional_table = {.allocated = false,}; //J table for binary split algorithm -struct RGTable1D J_split_table = {.allocated = false}; +static RGTable1D J_split_table = {.allocated = false}; + //Sigma inverse table for partition algorithm -struct RGTable1D Sigma_inv_table = {.allocated = false}; +//Since we want to easily construct it from the sigma table, it won't be uniform so use GSL +//TODO: Consider a rootfind on the integrals for accuracy and speed if we want an RGTable +// It should only need one calculation per run +static gsl_spline *Sigma_inv_table; +static gsl_interp_accel *Sigma_inv_table_acc; +#pragma omp threadprivate(Sigma_inv_table_acc) + +//Sigma interpolation tables +static RGTable1D_f Sigma_InterpTable = {.allocated = false,}; +static RGTable1D_f dSigmasqdm_InterpTable = {.allocated = false,}; //NOTE: this table is initialised for up to N_redshift x N_Mturn, but only called N_filter times to assign ST_over_PS in Spintemp. // It may be better to just do the integrals at each R void initialise_SFRD_spline(int Nbin, float zmin, float zmax, float Alpha_star, float Alpha_star_mini, float Fstar10, float Fstar7_MINI, float mturn_a_const, bool minihalos){ int i,j; - float Mlim_Fstar, Mlim_Fstar_MINI; + float Mlim_Fstar; + float Mlim_Fstar_MINI=0.; double Mmin = global_params.M_MIN_INTEGRAL; double Mmax = global_params.M_MAX_INTEGRAL; double lnMmax = log(Mmax); @@ -83,7 +103,7 @@ void initialise_SFRD_spline(int Nbin, float zmin, float zmax, float Alpha_star, Mlim_Fstar_MINI = Mass_limit_bisection(Mmin, Mmax, Alpha_star_mini, Fstar7_MINI * pow(1e3, Alpha_star_mini)); } - #pragma omp parallel private(i,j) num_threads(user_params_it->N_THREADS) + #pragma omp parallel private(i,j) num_threads(user_params_global->N_THREADS) { double Mcrit_atom_val = mturn_a_const; double mturn_val; @@ -92,7 +112,7 @@ void initialise_SFRD_spline(int Nbin, float zmin, float zmax, float Alpha_star, #pragma omp for for (i=0; iN_THREADS) +#pragma omp parallel private(i,j) num_threads(user_params_global->N_THREADS) { double Mcrit_atom_val = mturn_a_const; double mturn_val; @@ -162,7 +183,7 @@ void initialise_Nion_Ts_spline(int Nbin, float zmin, float zmax, float Alpha_sta for (i=0; iHMF == 0) + if(user_params_global->HMF == 0) fcoll_z_table.y_arr[i] = FgtrM(z_val, M_min); else{ - if(user_params_it->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options_it->USE_MINI_HALOS && user_params_it->INTEGRATION_METHOD_MINI == 1)) - initialise_GL(NGL_INT,lnMmin,lnMmax); + if(user_params_global->INTEGRATION_METHOD_ATOMIC == 1 || (flag_options_global->USE_MINI_HALOS && user_params_global->INTEGRATION_METHOD_MINI == 1)) + initialise_GL(lnMmin,lnMmax); fcoll_z_table.y_arr[i] = Fcoll_General(z_val, lnMmin, lnMmax); } } @@ -265,7 +286,7 @@ void initialise_Nion_Conditional_spline(float z, float Mcrit_atom, float min_den int i,j; double overdense_table[NDELTA]; double mturns[NMTURN], mturns_MINI[NMTURN]; - struct RGTable2D_f *table_2d, *table_mini; + RGTable2D_f *table_2d, *table_mini; LOG_SUPER_DEBUG("Initialising Nion conditional table at mass %.2e from delta %.2e to %.2e",Mcond,min_density,max_density); @@ -321,7 +342,7 @@ void initialise_Nion_Conditional_spline(float z, float Mcrit_atom, float min_den } } -#pragma omp parallel private(i,j) num_threads(user_params_it->N_THREADS) +#pragma omp parallel private(i,j) num_threads(user_params_global->N_THREADS) { #pragma omp for for(i=0;iINTEGRATION_METHOD_ATOMIC)); + Fstar10,Fesc10,Mlim_Fstar,Mlim_Fesc,user_params_global->INTEGRATION_METHOD_ATOMIC)); if(Nion_conditional_table1D.y_arr[i] < -40.) Nion_conditional_table1D.y_arr[i] = -40.; @@ -338,14 +359,14 @@ void initialise_Nion_Conditional_spline(float z, float Mcrit_atom, float min_den for (j=0; jz_arr[i][j] = log(Nion_ConditionalM(growthf,lnMmin,lnMmax,Mcond,sigma2, overdense_table[i],mturns[j],Alpha_star,Alpha_esc, - Fstar10,Fesc10,Mlim_Fstar,Mlim_Fesc,user_params_it->INTEGRATION_METHOD_ATOMIC)); + Fstar10,Fesc10,Mlim_Fstar,Mlim_Fesc,user_params_global->INTEGRATION_METHOD_ATOMIC)); if(table_2d->z_arr[i][j] < -40.) table_2d->z_arr[i][j] = -40.; table_mini->z_arr[i][j] = log(Nion_ConditionalM_MINI(growthf,lnMmin,lnMmax,Mcond,sigma2,overdense_table[i], mturns_MINI[j],Mcrit_atom,Alpha_star_mini,Alpha_esc,Fstar7_MINI,Fesc7_MINI, - Mlim_Fstar_MINI,Mlim_Fesc_MINI,user_params_it->INTEGRATION_METHOD_MINI)); + Mlim_Fstar_MINI,Mlim_Fesc_MINI,user_params_global->INTEGRATION_METHOD_MINI)); if(table_mini->z_arr[i][j] < -40.) table_mini->z_arr[i][j] = -40.; @@ -382,8 +403,9 @@ void initialise_Nion_Conditional_spline(float z, float Mcrit_atom, float min_den void initialise_SFRD_Conditional_table(double min_density, double max_density, double growthf, float Mcrit_atom, double Mmin, double Mmax, double Mcond, float Alpha_star, float Alpha_star_mini, float Fstar10, float Fstar7_MINI, int method, int method_mini, bool minihalos){ - float Mlim_Fstar,sigma2,Mlim_Fstar_MINI; - int i,j,k,i_tot; + float Mlim_Fstar,sigma2; + float Mlim_Fstar_MINI=0.; + int i,k; LOG_SUPER_DEBUG("Initialising SFRD conditional table at mass %.2e from delta %.2e to %.2e",Mcond,min_density,max_density); @@ -417,14 +439,14 @@ void initialise_SFRD_Conditional_table(double min_density, double max_density, d double lnMmax = log(Mmax); sigma2 = EvaluateSigma(lnM_condition); //sigma is always the condition, whereas lnMmax is just the integral limit -#pragma omp parallel private(i,k) num_threads(user_params_it->N_THREADS) +#pragma omp parallel private(i,k) num_threads(user_params_global->N_THREADS) { double curr_dens; #pragma omp for for (i=0; iINTEGRATION_METHOD_ATOMIC)); + Mcrit_atom,Alpha_star,0.,Fstar10,1.,Mlim_Fstar,0., user_params_global->INTEGRATION_METHOD_ATOMIC)); if(SFRD_conditional_table.y_arr[i] < -50.) SFRD_conditional_table.y_arr[i] = -50.; @@ -434,7 +456,7 @@ void initialise_SFRD_Conditional_table(double min_density, double max_density, d for (k=0; kINTEGRATION_METHOD_MINI)); + Alpha_star_mini,0.,Fstar7_MINI,1.,Mlim_Fstar_MINI, 0., user_params_global->INTEGRATION_METHOD_MINI)); if(SFRD_conditional_table_MINI.z_arr[i][k] < -50.) SFRD_conditional_table_MINI.z_arr[i][k] = -50.; @@ -459,8 +481,8 @@ void initialise_SFRD_Conditional_table(double min_density, double max_density, d void initialise_dNdM_tables(double xmin, double xmax, double ymin, double ymax, double growth_out, double param, bool from_catalog){ int nx; - double lnM_cond; - double sigma_cond; + double lnM_cond=0.; + double sigma_cond=0.; LOG_DEBUG("Initialising dNdM Tables from [%.2e,%.2e] (Intg. Limits %.2e %.2e)",xmin,xmax,ymin,ymax); LOG_DEBUG("D_out %.2e P %.2e from_cat %d",growth_out,param,from_catalog); @@ -469,7 +491,7 @@ void initialise_dNdM_tables(double xmin, double xmax, double ymin, double ymax, sigma_cond = EvaluateSigma(lnM_cond); } - nx = user_params_it->N_COND_INTERP; + nx = user_params_global->N_COND_INTERP; double xa[nx]; int i; @@ -489,7 +511,7 @@ void initialise_dNdM_tables(double xmin, double xmax, double ymin, double ymax, Mcoll_table.x_min = xmin; Mcoll_table.x_width = (xmax - xmin)/((double)nx-1); - #pragma omp parallel num_threads(user_params_it->N_THREADS) private(i) firstprivate(sigma_cond,lnM_cond) + #pragma omp parallel num_threads(user_params_global->N_THREADS) private(i) firstprivate(sigma_cond,lnM_cond) { double x; double delta; @@ -503,7 +525,7 @@ void initialise_dNdM_tables(double xmin, double xmax, double ymin, double ymax, lnM_cond = x; sigma_cond = EvaluateSigma(lnM_cond); //barrier at descendant mass - delta = get_delta_crit(user_params_it->HMF,sigma_cond,param)/param*growth_out; + delta = get_delta_crit(user_params_global->HMF,sigma_cond,param)/param*growth_out; } else{ delta = x; @@ -533,7 +555,7 @@ double dndm_inv_f(double lnM_min, void * params){ struct rf_inv_params *p = (struct rf_inv_params *)params; double integral = Nhalo_Conditional(p->growthf,lnM_min,p->lnM_cond,p->M_cond,p->sigma,p->delta,0); //This ensures that we never find the root if the ratio is zero, since that will set to M_cond - double result = integral == 0 ? 2*user_params_it->MIN_LOGPROB : log(integral / p->rf_norm); + double result = integral == 0 ? 2*user_params_global->MIN_LOGPROB : log(integral / p->rf_norm); return result - p->rf_target; } @@ -545,21 +567,21 @@ void initialise_dNdM_inverse_table(double xmin, double xmax, double lnM_min, dou LOG_DEBUG("Initialising dNdM Tables from [%.2e,%.2e] (Intg. Min. %.2e)",xmin,xmax,lnM_min); LOG_DEBUG("D_out %.2e P %.2e up %d",growth_out,param,from_catalog); - int nx = user_params_it->N_COND_INTERP; - int np = user_params_it->N_PROB_INTERP; + int nx = user_params_global->N_COND_INTERP; + int np = user_params_global->N_PROB_INTERP; double xa[nx], pa[np]; double rf_tol_abs = 1e-4; double rf_tol_rel = 0.; - double lnM_cond; - double sigma_cond; - double min_lp = user_params_it->MIN_LOGPROB; + double lnM_cond=0.; + double sigma_cond=0.; + double min_lp = user_params_global->MIN_LOGPROB; if(!from_catalog){ lnM_cond = param; sigma_cond = EvaluateSigma(lnM_cond); } - int i,j,k; + int i,k; //set up coordinate grids for(i=0;iN_THREADS) private(i,j,k) firstprivate(sigma_cond,lnM_cond) + #pragma omp parallel num_threads(user_params_global->N_THREADS) private(i,k) firstprivate(sigma_cond,lnM_cond) { double x; double norm; @@ -606,7 +628,7 @@ void initialise_dNdM_inverse_table(double xmin, double xmax, double lnM_min, dou lnM_cond = x; sigma_cond = EvaluateSigma(lnM_cond); //Barrier at descendant mass scaled to progenitor redshift - delta = get_delta_crit(user_params_it->HMF,sigma_cond,param)/param*growth_out; + delta = get_delta_crit(user_params_global->HMF,sigma_cond,param)/param*growth_out; } else{ delta = x; @@ -658,7 +680,7 @@ void initialise_dNdM_inverse_table(double xmin, double xmax, double lnM_min, dou }while((status == GSL_CONTINUE) && (iter < MAX_ITER_RF)); if(status!=GSL_SUCCESS) { LOG_ERROR("gsl RF error occured! %d",status); - GSL_ERROR(status); + CATCH_GSL_ERROR(status); } } } @@ -693,7 +715,7 @@ double integrate_J(double u_res, double gamma1){ if(status!=0) { LOG_ERROR("gsl integration error occured!"); LOG_ERROR("J: gamma1 = %.4e u_res = %.4e",gamma1,u_res); - GSL_ERROR(status); + CATCH_GSL_ERROR(status); } gsl_integration_workspace_free (w); @@ -719,7 +741,13 @@ void free_dNdM_tables(){ free_RGTable1D(&Nhalo_table); free_RGTable1D(&Mcoll_table); free_RGTable1D(&J_split_table); - free_RGTable1D(&Sigma_inv_table); + if(user_params_global->SAMPLE_METHOD == 2){ + gsl_spline_free(Sigma_inv_table); + #pragma omp parallel num_threads(user_params_global->N_THREADS) + { + gsl_interp_accel_free(Sigma_inv_table_acc); + } + } } void free_conditional_tables(){ @@ -734,34 +762,42 @@ void free_conditional_tables(){ free_RGTable2D_f(&Nion_conditional_table_MINI_prev); } +void free_global_tables(){ + free_RGTable1D(&SFRD_z_table); + free_RGTable2D(&SFRD_z_table_MINI); + free_RGTable1D(&Nion_z_table); + free_RGTable2D(&Nion_z_table_MINI); + free_RGTable1D(&fcoll_z_table); +} + //JD: moving the interp table evaluations here since some of them are needed in nu_tau_one //NOTE: with !USE_MASS_DEPENDENT_ZETA both EvaluateNionTs and EvaluateSFRD return Fcoll double EvaluateNionTs(double redshift, double Mlim_Fstar, double Mlim_Fesc){ //differences in turnover are handled by table setup - if(user_params_it->USE_INTERPOLATION_TABLES){ - if(flag_options_it->USE_MASS_DEPENDENT_ZETA) + if(user_params_global->USE_INTERPOLATION_TABLES){ + if(flag_options_global->USE_MASS_DEPENDENT_ZETA) return EvaluateRGTable1D(redshift,&Nion_z_table); //the correct table should be passed return EvaluateRGTable1D(redshift,&fcoll_z_table); } //Currently assuming this is only called in the X-ray/spintemp calculation, this will only affect !USE_MASS_DEPENDENT_ZETA and !M_MIN_in_mass // and only if the minimum virial temperatures ION_Tvir_min and X_RAY_Tvir_min are different - double lnMmin = log(minimum_source_mass(redshift,true,astro_params_it,flag_options_it)); + double lnMmin = log(minimum_source_mass(redshift,true,astro_params_global,flag_options_global)); double lnMmax = log(global_params.M_MAX_INTEGRAL); //minihalos uses a different turnover mass - if(flag_options_it->USE_MINI_HALOS) - return Nion_General(redshift, lnMmin, lnMmax, atomic_cooling_threshold(redshift), astro_params_it->ALPHA_STAR, astro_params_it->ALPHA_ESC, - astro_params_it->F_STAR10, astro_params_it->F_ESC10, Mlim_Fstar, Mlim_Fesc); - if(flag_options_it->USE_MASS_DEPENDENT_ZETA) - return Nion_General(redshift, lnMmin, lnMmax, astro_params_it->M_TURN, astro_params_it->ALPHA_STAR, astro_params_it->ALPHA_ESC, - astro_params_it->F_STAR10, astro_params_it->F_ESC10, Mlim_Fstar, Mlim_Fesc); + if(flag_options_global->USE_MINI_HALOS) + return Nion_General(redshift, lnMmin, lnMmax, atomic_cooling_threshold(redshift), astro_params_global->ALPHA_STAR, astro_params_global->ALPHA_ESC, + astro_params_global->F_STAR10, astro_params_global->F_ESC10, Mlim_Fstar, Mlim_Fesc); + if(flag_options_global->USE_MASS_DEPENDENT_ZETA) + return Nion_General(redshift, lnMmin, lnMmax, astro_params_global->M_TURN, astro_params_global->ALPHA_STAR, astro_params_global->ALPHA_ESC, + astro_params_global->F_STAR10, astro_params_global->F_ESC10, Mlim_Fstar, Mlim_Fesc); return Fcoll_General(redshift, lnMmin, lnMmax); } double EvaluateNionTs_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI, double Mlim_Fesc_MINI){ - if(user_params_it->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ // LOG_SUPER_DEBUG("z %.3f l10M %.3e result %.3e",redshift,log10_Mturn_LW_ave,EvaluateRGTable2D(redshift,log10_Mturn_LW_ave,&Nion_z_table_MINI)); // LOG_SUPER_DEBUG("Table N (%d,%d) min (%.2e %.2e) width (%.2e %.2e) max (%.2e %.2e)",Nion_z_table.nx_bin,Nion_z_table.ny_bin, // Nion_z_table.x_min,Nion_z_table.y_min, @@ -772,30 +808,30 @@ double EvaluateNionTs_MINI(double redshift, double log10_Mturn_LW_ave, double Ml } return Nion_General_MINI(redshift, log(global_params.M_MIN_INTEGRAL), log(global_params.M_MAX_INTEGRAL), pow(10.,log10_Mturn_LW_ave), atomic_cooling_threshold(redshift), - astro_params_it->ALPHA_STAR_MINI, astro_params_it->ALPHA_ESC, astro_params_it->F_STAR7_MINI, - astro_params_it->F_ESC7_MINI, Mlim_Fstar_MINI, Mlim_Fesc_MINI); + astro_params_global->ALPHA_STAR_MINI, astro_params_global->ALPHA_ESC, astro_params_global->F_STAR7_MINI, + astro_params_global->F_ESC7_MINI, Mlim_Fstar_MINI, Mlim_Fesc_MINI); } double EvaluateSFRD(double redshift, double Mlim_Fstar){ - if(user_params_it->USE_INTERPOLATION_TABLES){ - if(flag_options_it->USE_MASS_DEPENDENT_ZETA) + if(user_params_global->USE_INTERPOLATION_TABLES){ + if(flag_options_global->USE_MASS_DEPENDENT_ZETA) return EvaluateRGTable1D(redshift,&SFRD_z_table); return EvaluateRGTable1D(redshift,&fcoll_z_table); } //Currently assuming this is only called in the X-ray/spintemp calculation, this will only affect !USE_MASS_DEPENDENT_ZETA and !M_MIN_in_mass // and only if the minimum virial temperatures ION_Tvir_min and X_RAY_Tvir_min are different - double lnMmin = log(minimum_source_mass(redshift,true,astro_params_it,flag_options_it)); + double lnMmin = log(minimum_source_mass(redshift,true,astro_params_global,flag_options_global)); double lnMmax = log(global_params.M_MAX_INTEGRAL); //minihalos uses a different turnover mass - if(flag_options_it->USE_MINI_HALOS) - return Nion_General(redshift, lnMmin, lnMmax, atomic_cooling_threshold(redshift), astro_params_it->ALPHA_STAR, 0., - astro_params_it->F_STAR10, 1., Mlim_Fstar, 0.); + if(flag_options_global->USE_MINI_HALOS) + return Nion_General(redshift, lnMmin, lnMmax, atomic_cooling_threshold(redshift), astro_params_global->ALPHA_STAR, 0., + astro_params_global->F_STAR10, 1., Mlim_Fstar, 0.); - if(flag_options_it->USE_MASS_DEPENDENT_ZETA) - return Nion_General(redshift, lnMmin, lnMmax, astro_params_it->M_TURN, astro_params_it->ALPHA_STAR, 0., - astro_params_it->F_STAR10, 1., Mlim_Fstar, 0.); + if(flag_options_global->USE_MASS_DEPENDENT_ZETA) + return Nion_General(redshift, lnMmin, lnMmax, astro_params_global->M_TURN, astro_params_global->ALPHA_STAR, 0., + astro_params_global->F_STAR10, 1., Mlim_Fstar, 0.); //NOTE: Previously, with M_MIN_IN_MASS, the FgtrM function used M_turn/50, which seems like a bug // since it goes against the assumption of sharp cutoff @@ -806,79 +842,79 @@ double EvaluateSFRD(double redshift, double Mlim_Fstar){ } double EvaluateSFRD_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI){ - if(user_params_it->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ return EvaluateRGTable2D(redshift,log10_Mturn_LW_ave,&SFRD_z_table_MINI); } return Nion_General_MINI(redshift, log(global_params.M_MIN_INTEGRAL), log(global_params.M_MAX_INTEGRAL), pow(10.,log10_Mturn_LW_ave), atomic_cooling_threshold(redshift), - astro_params_it->ALPHA_STAR_MINI, 0., astro_params_it->F_STAR7_MINI, + astro_params_global->ALPHA_STAR_MINI, 0., astro_params_global->F_STAR7_MINI, 1., Mlim_Fstar_MINI, 0.); } double EvaluateSFRD_Conditional(double delta, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar){ - if(user_params_it->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ return exp(EvaluateRGTable1D_f(delta,&SFRD_conditional_table)); } return Nion_ConditionalM(growthf,log(M_min),log(M_max),M_cond,sigma_max,delta,Mturn_a, - astro_params_it->ALPHA_STAR,0.,astro_params_it->F_STAR10,1.,Mlim_Fstar,0., user_params_it->INTEGRATION_METHOD_ATOMIC); + astro_params_global->ALPHA_STAR,0.,astro_params_global->F_STAR10,1.,Mlim_Fstar,0., user_params_global->INTEGRATION_METHOD_ATOMIC); } double EvaluateSFRD_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar){ - if(user_params_it->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ return exp(EvaluateRGTable2D_f(delta,log10Mturn_m,&SFRD_conditional_table_MINI)); } - return Nion_ConditionalM_MINI(growthf,log(M_min),log(M_max),M_cond,sigma_max,delta,pow(10,log10Mturn_m),Mturn_a,astro_params_it->ALPHA_STAR_MINI, - 0.,astro_params_it->F_STAR7_MINI,1.,Mlim_Fstar, 0., user_params_it->INTEGRATION_METHOD_MINI); + return Nion_ConditionalM_MINI(growthf,log(M_min),log(M_max),M_cond,sigma_max,delta,pow(10,log10Mturn_m),Mturn_a,astro_params_global->ALPHA_STAR_MINI, + 0.,astro_params_global->F_STAR7_MINI,1.,Mlim_Fstar, 0., user_params_global->INTEGRATION_METHOD_MINI); } double EvaluateNion_Conditional(double delta, double log10Mturn, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mlim_Fstar, double Mlim_Fesc, bool prev){ - struct RGTable2D_f *table = prev ? &Nion_conditional_table_prev : &Nion_conditional_table2D; - if(user_params_it->USE_INTERPOLATION_TABLES){ - if(flag_options_it->USE_MINI_HALOS) + RGTable2D_f *table = prev ? &Nion_conditional_table_prev : &Nion_conditional_table2D; + if(user_params_global->USE_INTERPOLATION_TABLES){ + if(flag_options_global->USE_MINI_HALOS) return exp(EvaluateRGTable2D_f(delta, log10Mturn, table)); return exp(EvaluateRGTable1D_f(delta, &Nion_conditional_table1D)); } return Nion_ConditionalM(growthf,log(M_min),log(M_max),M_cond,sigma_max,delta,pow(10,log10Mturn), - astro_params_it->ALPHA_STAR,astro_params_it->ALPHA_ESC,astro_params_it->F_STAR10, - astro_params_it->F_ESC10,Mlim_Fstar,Mlim_Fesc,user_params_it->INTEGRATION_METHOD_ATOMIC); + astro_params_global->ALPHA_STAR,astro_params_global->ALPHA_ESC,astro_params_global->F_STAR10, + astro_params_global->F_ESC10,Mlim_Fstar,Mlim_Fesc,user_params_global->INTEGRATION_METHOD_ATOMIC); } double EvaluateNion_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar, double Mlim_Fesc, bool prev){ - struct RGTable2D_f *table = prev ? &Nion_conditional_table_MINI_prev : &Nion_conditional_table_MINI; - if(user_params_it->USE_INTERPOLATION_TABLES){ + RGTable2D_f *table = prev ? &Nion_conditional_table_MINI_prev : &Nion_conditional_table_MINI; + if(user_params_global->USE_INTERPOLATION_TABLES){ return exp(EvaluateRGTable2D_f(delta,log10Mturn_m,table)); } - return Nion_ConditionalM_MINI(growthf,log(M_min),log(M_max),M_cond,sigma_max,delta,pow(10,log10Mturn_m),Mturn_a,astro_params_it->ALPHA_STAR_MINI, - astro_params_it->ALPHA_ESC,astro_params_it->F_STAR7_MINI,astro_params_it->F_ESC7_MINI,Mlim_Fstar, - Mlim_Fesc,user_params_it->INTEGRATION_METHOD_MINI); + return Nion_ConditionalM_MINI(growthf,log(M_min),log(M_max),M_cond,sigma_max,delta,pow(10,log10Mturn_m),Mturn_a,astro_params_global->ALPHA_STAR_MINI, + astro_params_global->ALPHA_ESC,astro_params_global->F_STAR7_MINI,astro_params_global->F_ESC7_MINI,Mlim_Fstar, + Mlim_Fesc,user_params_global->INTEGRATION_METHOD_MINI); } double EvaluateFcoll_delta(double delta, double growthf, double sigma_min, double sigma_max){ - if(user_params_it->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ return EvaluateRGTable1D_f(delta,&fcoll_conditional_table); } return FgtrM_bias_fast(growthf,delta,sigma_min,sigma_max); } double EvaluatedFcolldz(double delta, double redshift, double sigma_min, double sigma_max){ - if(user_params_it->USE_INTERPOLATION_TABLES){ + if(user_params_global->USE_INTERPOLATION_TABLES){ return EvaluateRGTable1D_f(delta,&dfcoll_conditional_table); } return dfcoll_dz(redshift,sigma_min,delta,sigma_max); } double EvaluateNhalo(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta){ - if(user_params_it->USE_INTERPOLATION_TABLES) + if(user_params_global->USE_INTERPOLATION_TABLES) return EvaluateRGTable1D(condition,&Nhalo_table); return Nhalo_Conditional(growthf, lnMmin, lnMmax, M_cond, sigma, delta, 0); } double EvaluateMcoll(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta){ - if(user_params_it->USE_INTERPOLATION_TABLES) + if(user_params_global->USE_INTERPOLATION_TABLES) return EvaluateRGTable1D(condition,&Mcoll_table); return Mcoll_Conditional(growthf, lnMmin, lnMmax, M_cond, sigma, delta, 0); } @@ -893,7 +929,7 @@ double extrapolate_dNdM_inverse(double condition, double lnp){ double x_table = x_min + x_idx*x_width; double interp_point_x = (condition - x_table)/x_width; - double extrap_point_y = (lnp - user_params_it->MIN_LOGPROB)/Nhalo_inv_table.y_width; + double extrap_point_y = (lnp - user_params_global->MIN_LOGPROB)/Nhalo_inv_table.y_width; //find the log-mass at the edge of the table for this condition double xlimit = Nhalo_inv_table.z_arr[x_idx][0]*(interp_point_x) @@ -911,7 +947,7 @@ double EvaluateNhaloInv(double condition, double prob){ if(prob == 0.) return 1.; //q == 1 -> condition mass double lnp = log(prob); - if(lnp < user_params_it->MIN_LOGPROB) + if(lnp < user_params_global->MIN_LOGPROB) return extrapolate_dNdM_inverse(condition,lnp); return EvaluateRGTable2D(condition,lnp,&Nhalo_inv_table); } @@ -935,26 +971,78 @@ void InitialiseSigmaInverseTable(){ Throw(TableGenerationError); } int i; - int n_bin = Sigma_InterpTable.n_bin; - double sigma_min = Sigma_InterpTable.y_arr[n_bin-1]; - double sigma_max = Sigma_InterpTable.y_arr[0]; - if(!Sigma_inv_table.allocated) - allocate_RGTable1D(n_bin,&Sigma_inv_table); + double xa[n_bin], ya[n_bin]; - Sigma_inv_table.x_min = sigma_min; - Sigma_inv_table.x_width = (sigma_max-sigma_min)/((double)n_bin-1); + Sigma_inv_table = gsl_spline_alloc(gsl_interp_linear,n_bin); + Sigma_inv_table_acc = gsl_interp_accel_alloc(); for(i=0;iUSE_INTERPOLATION_TABLES){ + if(!user_params_global->USE_INTERPOLATION_TABLES){ LOG_ERROR("Cannot currently do sigma inverse without USE_INTERPOLATION_TABLES"); Throw(ValueError); } - return EvaluateRGTable1D(sigma,&Sigma_inv_table); + return gsl_spline_eval(Sigma_inv_table,sigma,Sigma_inv_table_acc); +} + +void initialiseSigmaMInterpTable(float M_min, float M_max){ + int i; + + if(!Sigma_InterpTable.allocated) + allocate_RGTable1D_f(N_MASS_INTERP,&Sigma_InterpTable); + if(!dSigmasqdm_InterpTable.allocated) + allocate_RGTable1D_f(N_MASS_INTERP,&dSigmasqdm_InterpTable); + + Sigma_InterpTable.x_min = log(M_min); + Sigma_InterpTable.x_width = (log(M_max) - log(M_min))/(N_MASS_INTERP-1.); + dSigmasqdm_InterpTable.x_min = log(M_min); + dSigmasqdm_InterpTable.x_width = (log(M_max) - log(M_min))/(N_MASS_INTERP-1.); + +#pragma omp parallel private(i) num_threads(user_params_global->N_THREADS) + { + float Mass; +#pragma omp for + for(i=0;iUSE_INTERPOLATION_TABLES) { + return EvaluateRGTable1D_f(lnM, &Sigma_InterpTable); + } + return sigma_z0(exp(lnM)); +} + +double EvaluatedSigmasqdm(double lnM){ + //this may be slow, figure out why the dsigmadm table is in log10 + if(user_params_global->USE_INTERPOLATION_TABLES){ + return -pow(10., EvaluateRGTable1D_f(lnM, &dSigmasqdm_InterpTable)); + } + return dsigmasqdm_z0(exp(lnM)); } diff --git a/src/py21cmfast/src/interp_tables.h b/src/py21cmfast/src/interp_tables.h new file mode 100644 index 00000000..87e8fc4c --- /dev/null +++ b/src/py21cmfast/src/interp_tables.h @@ -0,0 +1,65 @@ +#ifndef _INTERP_TABLES_H +#define _INTERP_TABLES_H + +#include "InputParameters.h" + +//Functions within interp_tables.c need the parameter structures, but we don't want to pass them all down the chain, so we broadcast them +//TODO: in future it would be better to use a context struct. See `HaloBox.c` + +void initialise_SFRD_spline(int Nbin, float zmin, float zmax, float Alpha_star, float Alpha_star_mini, float Fstar10, float Fstar7_MINI, + float mturn_a_const, bool minihalos); +double EvaluateSFRD(double redshift, double Mlim_Fstar); +double EvaluateSFRD_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI); + +void initialise_Nion_Ts_spline(int Nbin, float zmin, float zmax, float Alpha_star, float Alpha_star_mini, float Alpha_esc, float Fstar10, + float Fesc10, float Fstar7_MINI, float Fesc7_MINI, float mturn_a_const, bool minihalos); +double EvaluateNionTs(double redshift, double Mlim_Fstar, double Mlim_Fesc); +double EvaluateNionTs_MINI(double redshift, double log10_Mturn_LW_ave, double Mlim_Fstar_MINI, double Mlim_Fesc_MINI); + +void initialise_FgtrM_delta_table(double min_dens, double max_dens, double zpp, double growth_zpp, double smin_zpp, double smax_zpp); +double EvaluateFcoll_delta(double delta, double growthf, double sigma_min, double sigma_max); + +void init_FcollTable(double zmin, double zmax, bool x_ray); +double EvaluatedFcolldz(double delta, double redshift, double sigma_min, double sigma_max); + +void initialise_Nion_Conditional_spline(float z, float Mcrit_atom, float min_density, float max_density, + float Mmin, float Mmax, float Mcond, float log10Mturn_min, float log10Mturn_max, + float log10Mturn_min_MINI, float log10Mturn_max_MINI, float Alpha_star, + float Alpha_star_mini, float Alpha_esc, float Fstar10, float Fesc10, + float Mlim_Fstar, float Mlim_Fesc, float Fstar7_MINI, float Fesc7_MINI, + float Mlim_Fstar_MINI, float Mlim_Fesc_MINI, int method, int method_mini, + bool minihalos, bool prev); +double EvaluateNion_Conditional(double delta, double log10Mturn, double growthf, double M_min, double M_max, double M_cond, double sigma_max, + double Mlim_Fstar, double Mlim_Fesc, bool prev); +double EvaluateNion_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, + double Mturn_a, double Mlim_Fstar, double Mlim_Fesc, bool prev); + +void initialise_SFRD_Conditional_table(double min_density, double max_density, double growthf, + float Mcrit_atom, double Mmin, double Mmax, double Mcond, float Alpha_star, float Alpha_star_mini, + float Fstar10, float Fstar7_MINI, int method, int method_mini, bool minihalos); +double EvaluateSFRD_Conditional(double delta, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar); +double EvaluateSFRD_Conditional_MINI(double delta, double log10Mturn_m, double growthf, double M_min, double M_max, double M_cond, double sigma_max, double Mturn_a, double Mlim_Fstar); + +void initialise_dNdM_tables(double xmin, double xmax, double ymin, double ymax, double growth1, double param, bool from_catalog); +double EvaluateNhalo(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta); +double EvaluateMcoll(double condition, double growthf, double lnMmin, double lnMmax, double M_cond, double sigma, double delta); + +void initialise_dNdM_inverse_table(double xmin, double xmax, double lnM_min, double growth1, double param, bool from_catalog); +double EvaluateNhaloInv(double condition, double prob); + +void initialise_J_split_table(int Nbin, double umin, double umax, double gamma1); +double EvaluateJ(double u_res,double gamma1); + +void initialiseSigmaMInterpTable(float M_Min, float M_Max); +double EvaluateSigma(double lnM); +double EvaluatedSigmasqdm(double lnM); + +void InitialiseSigmaInverseTable(); +double EvaluateSigmaInverse(double sigma); + +void freeSigmaMInterpTable(); +void free_conditional_tables(); +void free_global_tables(); +void free_dNdM_tables(); + +#endif diff --git a/src/py21cmfast/src/interpolation.c b/src/py21cmfast/src/interpolation.c index b6aa96ca..3f215661 100644 --- a/src/py21cmfast/src/interpolation.c +++ b/src/py21cmfast/src/interpolation.c @@ -2,73 +2,39 @@ // We use regular grid tables since they are faster to evaluate (we always know which bin we are in) // So I'm making a general function for the 1D and 2D cases -struct RGTable1D{ - int n_bin; - double x_min; - double x_width; +#include +#include +#include +#include "interpolation.h" +#include "logger.h" - double *y_arr; - bool allocated; -}; - -struct RGTable2D{ - int nx_bin, ny_bin; - double x_min, y_min; - double x_width, y_width; - - double **z_arr; - - double saved_ll, saved_ul; //for future acceleration - bool allocated; -}; - -struct RGTable1D_f{ - int n_bin; - double x_min; - double x_width; - - float *y_arr; - bool allocated; -}; - -struct RGTable2D_f{ - int nx_bin, ny_bin; - double x_min, y_min; - double x_width, y_width; - - float **z_arr; - - double saved_ll, saved_ul; //for future acceleration - bool allocated; -}; - -void allocate_RGTable1D(int n_bin, struct RGTable1D * ptr){ +void allocate_RGTable1D(int n_bin, RGTable1D * ptr){ ptr->n_bin = n_bin; ptr->y_arr = calloc(n_bin,sizeof(double)); ptr->allocated = true; } -void allocate_RGTable1D_f(int n_bin, struct RGTable1D_f * ptr){ +void allocate_RGTable1D_f(int n_bin, RGTable1D_f * ptr){ ptr->n_bin = n_bin; ptr->y_arr = calloc(n_bin,sizeof(float)); ptr->allocated = true; } -void free_RGTable1D(struct RGTable1D * ptr){ +void free_RGTable1D(RGTable1D * ptr){ if(ptr->allocated){ free(ptr->y_arr); ptr->allocated = false; } } -void free_RGTable1D_f(struct RGTable1D_f * ptr){ +void free_RGTable1D_f(RGTable1D_f * ptr){ if(ptr->allocated){ free(ptr->y_arr); ptr->allocated = false; } } -void allocate_RGTable2D(int n_x, int n_y, struct RGTable2D * ptr){ +void allocate_RGTable2D(int n_x, int n_y, RGTable2D * ptr){ int i; ptr->nx_bin = n_x; ptr->ny_bin = n_y; @@ -80,7 +46,7 @@ void allocate_RGTable2D(int n_x, int n_y, struct RGTable2D * ptr){ ptr->allocated = true; } -void allocate_RGTable2D_f(int n_x, int n_y, struct RGTable2D_f * ptr){ +void allocate_RGTable2D_f(int n_x, int n_y, RGTable2D_f * ptr){ int i; ptr->nx_bin = n_x; ptr->ny_bin = n_y; @@ -92,7 +58,7 @@ void allocate_RGTable2D_f(int n_x, int n_y, struct RGTable2D_f * ptr){ ptr->allocated = true; } -void free_RGTable2D_f(struct RGTable2D_f * ptr){ +void free_RGTable2D_f(RGTable2D_f * ptr){ int i; if(ptr->allocated){ for(i=0;inx_bin;i++) @@ -102,7 +68,7 @@ void free_RGTable2D_f(struct RGTable2D_f * ptr){ } } -void free_RGTable2D(struct RGTable2D * ptr){ +void free_RGTable2D(RGTable2D * ptr){ int i; if(ptr->allocated){ for(i=0;inx_bin;i++) @@ -112,23 +78,20 @@ void free_RGTable2D(struct RGTable2D * ptr){ } } -double EvaluateRGTable1D(double x, struct RGTable1D *table){ +double EvaluateRGTable1D(double x, RGTable1D *table){ double x_min = table->x_min; double x_width = table->x_width; int idx = (int)floor((x - x_min)/x_width); double table_val = x_min + x_width*(double)idx; double interp_point = (x - table_val)/x_width; - // LOG_DEBUG("1D: x %.6e (min %.2e wid %.2e) -> idx %d -> tbl %.6e -> itp %.6e",x, x_min, x_width,idx,table_val,interp_point); //a + f(a-b) is one fewer operation but less precise double result = table->y_arr[idx]*(1-interp_point) + table->y_arr[idx+1]*(interp_point); - // LOG_DEBUG("-> result %.2e",result); - return result; } -double EvaluateRGTable2D(double x, double y, struct RGTable2D *table){ +double EvaluateRGTable2D(double x, double y, RGTable2D *table){ double x_min = table->x_min; double x_width = table->x_width; double y_min = table->y_min; @@ -144,21 +107,16 @@ double EvaluateRGTable2D(double x, double y, struct RGTable2D *table){ double left_edge, right_edge, result; - // LOG_ULTRA_DEBUG("2D Interp: val (%.2e,%.2e) min (%.2e,%.2e) wid (%.2e,%.2e)",x,y,x_min,y_min,x_width,y_width); - // LOG_ULTRA_DEBUG("2D Interp: idx (%d,%d) tbl (%.2e,%.2e) itp (%.2e,%.2e)",x_idx,y_idx,x_table,y_table,interp_point_x,interp_point_y); - // LOG_ULTRA_DEBUG("2D Interp: table cell corners (%.2e,%.2e,%.2e,%.2e)",table->z_arr[x_idx][y_idx],table->z_arr[x_idx][y_idx+1],table->z_arr[x_idx+1][y_idx],table->z_arr[x_idx+1][y_idx+1]); - left_edge = table->z_arr[x_idx][y_idx]*(1-interp_point_y) + table->z_arr[x_idx][y_idx+1]*(interp_point_y); right_edge = table->z_arr[x_idx+1][y_idx]*(1-interp_point_y) + table->z_arr[x_idx+1][y_idx+1]*(interp_point_y); result = left_edge*(1-interp_point_x) + right_edge*(interp_point_x); - // LOG_DEBUG("result %.6e",result); return result; } //some tables are floats but I still need to return doubles -double EvaluateRGTable1D_f(double x, struct RGTable1D_f *table){ +double EvaluateRGTable1D_f(double x, RGTable1D_f *table){ double x_min = table->x_min; double x_width = table->x_width; int idx = (int)floor((x - x_min)/x_width); @@ -168,7 +126,7 @@ double EvaluateRGTable1D_f(double x, struct RGTable1D_f *table){ return table->y_arr[idx]*(1-interp_point) + table->y_arr[idx+1]*(interp_point); } -double EvaluateRGTable2D_f(double x, double y, struct RGTable2D_f *table){ +double EvaluateRGTable2D_f(double x, double y, RGTable2D_f *table){ double x_min = table->x_min; double x_width = table->x_width; double y_min = table->y_min; @@ -184,15 +142,10 @@ double EvaluateRGTable2D_f(double x, double y, struct RGTable2D_f *table){ double left_edge, right_edge, result; - // LOG_DEBUG("2D Interp: val (%.2e,%.2e) min (%.2e,%.2e) wid (%.2e,%.2e)",x,y,x_min,y_min,x_width,y_width); - // LOG_DEBUG("2D Interp: idx (%d,%d) tbl (%.2e,%.2e) itp (%.2e,%.2e)",x_idx,y_idx,x_table,y_table,interp_point_x,interp_point_y); - // LOG_DEBUG("2D Interp: table corners (%.2e,%.2e,%.2e,%.2e)",z_arr[x_idx][y_idx],z_arr[x_idx][y_idx+1],z_arr[x_idx+1][y_idx],z_arr[x_idx+1][y_idx+1]); - left_edge = table->z_arr[x_idx][y_idx]*(1-interp_point_y) + table->z_arr[x_idx][y_idx+1]*(interp_point_y); right_edge = table->z_arr[x_idx+1][y_idx]*(1-interp_point_y) + table->z_arr[x_idx+1][y_idx+1]*(interp_point_y); result = left_edge*(1-interp_point_x) + right_edge*(interp_point_x); - // LOG_DEBUG("result %.6e",result); return result; } diff --git a/src/py21cmfast/src/interpolation.h b/src/py21cmfast/src/interpolation.h new file mode 100644 index 00000000..2fc9315a --- /dev/null +++ b/src/py21cmfast/src/interpolation.h @@ -0,0 +1,61 @@ +#ifndef _INTERPOLATION_H +#define _INTERPOLATION_H + +#include + +typedef struct RGTable1D{ + int n_bin; + double x_min; + double x_width; + + double *y_arr; + bool allocated; +}RGTable1D; + +typedef struct RGTable2D{ + int nx_bin, ny_bin; + double x_min, y_min; + double x_width, y_width; + + double **z_arr; + + double saved_ll, saved_ul; //for future acceleration + bool allocated; +}RGTable2D; + +typedef struct RGTable1D_f{ + int n_bin; + double x_min; + double x_width; + + float *y_arr; + bool allocated; +}RGTable1D_f; + +typedef struct RGTable2D_f{ + int nx_bin, ny_bin; + double x_min, y_min; + double x_width, y_width; + + float **z_arr; + + double saved_ll, saved_ul; //for future acceleration + bool allocated; +}RGTable2D_f; + +void allocate_RGTable1D(int n_bin, RGTable1D * ptr); +void allocate_RGTable1D_f(int n_bin, RGTable1D_f * ptr); +void allocate_RGTable2D(int n_x, int n_y, RGTable2D * ptr); +void allocate_RGTable2D_f(int n_x, int n_y, RGTable2D_f * ptr); + +void free_RGTable1D(RGTable1D * ptr); +void free_RGTable1D_f(RGTable1D_f * ptr); +void free_RGTable2D(RGTable2D * ptr); +void free_RGTable2D_f(RGTable2D_f * ptr); + +double EvaluateRGTable1D(double x, RGTable1D *table); +double EvaluateRGTable2D(double x, double y, RGTable2D *table); +double EvaluateRGTable1D_f(double x, RGTable1D_f *table); +double EvaluateRGTable2D_f(double x, double y, RGTable2D_f *table); + +#endif diff --git a/src/py21cmfast/src/photoncons.c b/src/py21cmfast/src/photoncons.c index 9666427e..c6de94d5 100644 --- a/src/py21cmfast/src/photoncons.c +++ b/src/py21cmfast/src/photoncons.c @@ -14,11 +14,31 @@ // or find z at a given Q -> z_at_Q(Q, &(z)). // 3) free memory allocation -> free_Q_value() +#include +#include +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "InputParameters.h" +#include "cosmology.h" +#include "hmf.h" +#include "interp_tables.h" +#include "debugging.h" + +#include "photoncons.h" + +bool photon_cons_allocated = false; //These globals hold values relevant for the photon conservation (z-shift) model -float calibrated_NF_min; -double *deltaz, *deltaz_smoothed, *NeutralFractions, *z_Q, *Q_value, *nf_vals, *z_vals; -int N_NFsamples,N_extrapolated, N_analytic, N_calibrated, N_deltaz; -double FinalNF_Estimate, FirstNF_Estimate; +static float calibrated_NF_min; +static double *deltaz, *deltaz_smoothed, *NeutralFractions, *z_Q, *Q_value, *nf_vals, *z_vals; +static int N_NFsamples,N_extrapolated, N_analytic, N_calibrated, N_deltaz; +static double FinalNF_Estimate, FirstNF_Estimate; static gsl_interp_accel *Q_at_z_spline_acc; static gsl_spline *Q_at_z_spline; @@ -40,8 +60,8 @@ void z_at_NFHist(double xHI_Hist, double *splined_value); void NFHist_at_z(double z, double *splined_value); // Set up interpolation table for the volume filling factor, Q, at a given redshift z and redshift at a given Q. -int InitialisePhotonCons(struct UserParams *user_params, struct CosmoParams *cosmo_params, - struct AstroParams *astro_params, struct FlagOptions *flag_options) +int InitialisePhotonCons(UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options) { /* @@ -50,8 +70,7 @@ int InitialisePhotonCons(struct UserParams *user_params, struct CosmoParams *cos int status; Try{ // this try wraps the whole function. - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); + Broadcast_struct_global_all(user_params,cosmo_params,astro_params,flag_options); init_ps(); // To solve differentail equation, uses Euler's method. // NOTE: @@ -65,7 +84,7 @@ int InitialisePhotonCons(struct UserParams *user_params, struct CosmoParams *cos double a_start = 0.03, a_end = 1./(1. + global_params.PhotonConsEndCalibz); // Scale factors of 0.03 and 0.17 correspond to redshifts of ~32 and ~5.0, respectively. double C_HII = 3., T_0 = 2e4; double reduce_ratio = 1.003; - double Q0,Q1,Nion0,Nion1,Trec,da,a,z0,z1,zi,dadt,ans,delta_a,zi_prev,Q1_prev; + double Q0,Q1,Nion0,Nion1,Trec,da,a,z0,z1,zi,dadt,delta_a,zi_prev,Q1_prev; double *z_arr,*Q_arr; int Nmax = 2000; // This is the number of step, enough with 'da = 2e-3'. If 'da' is reduced, this number should be checked. int cnt, nbin, i, istart; @@ -179,7 +198,7 @@ int InitialisePhotonCons(struct UserParams *user_params, struct CosmoParams *cos Q1 = Q0 + ((Nion0-Nion1)/2/delta_a)*da; // No Recombination } else { - dadt = Ho*sqrt(cosmo_params_ps->OMm/a + global_params.OMr/a/a + cosmo_params_ps->OMl*a*a); // da/dt = Ho*a*sqrt(OMm/a^3 + OMr/a^4 + OMl) + dadt = Ho*sqrt(cosmo_params->OMm/a + global_params.OMr/a/a + cosmo_params->OMl*a*a); // da/dt = Ho*a*sqrt(OMm/a^3 + OMr/a^4 + OMl) Trec = 0.93 * 1e9 * SperYR * pow(C_HII/3.,-1) * pow(T_0/2e4,0.7) * pow((1.+zi)/7.,-3); Q1 = Q0 + ((Nion0-Nion1)/2./delta_a - Q0/Trec/dadt)*da; } @@ -242,7 +261,7 @@ int InitialisePhotonCons(struct UserParams *user_params, struct CosmoParams *cos gsl_set_error_handler_off(); gsl_status = gsl_spline_init(Q_at_z_spline, z_Q, Q_value, nbin); - GSL_ERROR(gsl_status); + CATCH_GSL_ERROR(gsl_status); Zmin = z_Q[0]; Zmax = z_Q[nbin-1]; @@ -261,7 +280,7 @@ int InitialisePhotonCons(struct UserParams *user_params, struct CosmoParams *cos } gsl_status = gsl_spline_init(z_at_Q_spline, Q_z, z_value, nbin); - GSL_ERROR(gsl_status); + CATCH_GSL_ERROR(gsl_status); free(z_arr); free(Q_arr); @@ -326,7 +345,7 @@ void determine_deltaz_for_photoncons() { int i, j, increasing_val, counter, smoothing_int; double temp; - float z_cal, z_analytic, NF_sample, returned_value, NF_sample_min, gradient_analytic, z_analytic_at_endpoint, const_offset, z_analytic_2, smoothing_width; + float z_cal, z_analytic, NF_sample, NF_sample_min, gradient_analytic, z_analytic_at_endpoint, const_offset, z_analytic_2, smoothing_width; float bin_width, delta_NF, val1, val2, extrapolated_value; LOG_DEBUG("Determining deltaz for photon cons."); @@ -422,7 +441,7 @@ void determine_deltaz_for_photoncons() { LOG_ERROR("NF %.3f dz %.3f",NeutralFractions[i],deltaz[i]); } } - GSL_ERROR(gsl_status); + CATCH_GSL_ERROR(gsl_status); return; } @@ -632,19 +651,19 @@ void determine_deltaz_for_photoncons() { gsl_set_error_handler_off(); int gsl_status; gsl_status = gsl_spline_init(deltaz_spline_for_photoncons, NeutralFractions, deltaz, N_NFsamples + N_extrapolated + 1); - GSL_ERROR(gsl_status); + CATCH_GSL_ERROR(gsl_status); } -float adjust_redshifts_for_photoncons( - struct AstroParams *astro_params, struct FlagOptions *flag_options, float *redshift, +void adjust_redshifts_for_photoncons( + AstroParams *astro_params, FlagOptions *flag_options, float *redshift, float *stored_redshift, float *absolute_delta_z ) { - int i, new_counter; + int new_counter; double temp; - float required_NF, adjusted_redshift, future_z, gradient_extrapolation, const_extrapolation, temp_redshift, check_required_NF; + float required_NF, adjusted_redshift, temp_redshift, check_required_NF; LOG_DEBUG("Adjusting redshifts for photon cons."); @@ -897,14 +916,14 @@ void initialise_NFHistory_spline(double *redshifts, double *NF_estimate, int NSp gsl_set_error_handler_off(); int gsl_status; gsl_status = gsl_spline_init(NFHistory_spline, nf_vals, z_vals, (counter+1)); - GSL_ERROR(gsl_status); + CATCH_GSL_ERROR(gsl_status); z_NFHistory_spline_acc = gsl_interp_accel_alloc (); // z_NFHistory_spline = gsl_spline_alloc (gsl_interp_cspline, (counter+1)); z_NFHistory_spline = gsl_spline_alloc (gsl_interp_linear, (counter+1)); gsl_status = gsl_spline_init(z_NFHistory_spline, z_vals, nf_vals, (counter+1)); - GSL_ERROR(gsl_status); + CATCH_GSL_ERROR(gsl_status); } diff --git a/src/py21cmfast/src/photoncons.h b/src/py21cmfast/src/photoncons.h new file mode 100644 index 00000000..30f253ab --- /dev/null +++ b/src/py21cmfast/src/photoncons.h @@ -0,0 +1,32 @@ +#ifndef _PHOTONCONS_H +#define _PHOTONCONS_H + +#include +#include "InputParameters.h" + +//This is directly accessed in the wrapper currently +//TODO: remove this global declaration and make an internal checking function +extern bool photon_cons_allocated; + +int InitialisePhotonCons(UserParams *user_params, CosmoParams *cosmo_params, + AstroParams *astro_params, FlagOptions *flag_options); + +int PhotonCons_Calibration(double *z_estimate, double *xH_estimate, int NSpline); +int ComputeZstart_PhotonCons(double *zstart); + +void adjust_redshifts_for_photoncons( + AstroParams *astro_params, FlagOptions *flag_options, float *redshift, + float *stored_redshift, float *absolute_delta_z +); + +void determine_deltaz_for_photoncons(); +void FreePhotonConsMemory(); + +int ObtainPhotonConsData(double *z_at_Q_data, double *Q_data, int *Ndata_analytic, double *z_cal_data, double *nf_cal_data, int *Ndata_calibration, + double *PhotonCons_NFdata, double *PhotonCons_deltaz, int *Ndata_PhotonCons); + +//alpha photoncons functions +void set_alphacons_params(double norm, double slope); +double get_fesc_fit(double redshift); + +#endif diff --git a/src/py21cmfast/src/ps.c b/src/py21cmfast/src/ps.c deleted file mode 100644 index fb741a51..00000000 --- a/src/py21cmfast/src/ps.c +++ /dev/null @@ -1,2254 +0,0 @@ -/*** Some usefull math macros ***/ -#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) - -static double mnarg1,mnarg2; -#define FMAX(a,b) (mnarg1=(a),mnarg2=(b),(mnarg1) > (mnarg2) ?\ -(mnarg1) : (mnarg2)) - -static double mnarg1,mnarg2; -#define FMIN(a,b) (mnarg1=(a),mnarg2=(b),(mnarg1) < (mnarg2) ?\ -(mnarg1) : (mnarg2)) - -#define ERFC_NPTS (int) 75 -#define ERFC_PARAM_DELTA (float) 0.1 -static double log_erfc_table[ERFC_NPTS], erfc_params[ERFC_NPTS]; -static gsl_interp_accel *erfc_acc; -static gsl_spline *erfc_spline; - -#define NGaussLegendre 40 //defines the number of points in the Gauss-Legendre quadrature integration - -#define NMass 300 - -#define NGL_INT 100 // 100 - -#define NR_END 1 -#define FREE_ARG char* - -#define MM 7 -#define NSTACK 50 - -#define EPS2 3.0e-11 - -// Luv/SFR = 1 / 1.15 x 10^-28 [M_solar yr^-1/erg s^-1 Hz^-1] -// G. Sun and S. R. Furlanetto (2016) MNRAS, 417, 33 -#define Luv_over_SFR (double)(1./1.15/1e-28) - -#define delta_lnMhalo (double)(5e-6) -#define Mhalo_min (double)(1e6) -#define Mhalo_max (double)(1e16) - -#define MAX_DELTAC_FRAC (float)0.99 //max delta/deltac for the mass function integrals -#define JENKINS_a (0.73) //Jenkins+01, SMT has 0.707 -#define JENKINS_b (0.34) //Jenkins+01 fit from Barkana+01, SMT has 0.5 -#define JENKINS_c (0.81) //Jenkins+01 from from Barkana+01, SMT has 0.6 - -bool initialised_ComputeLF = false; - -gsl_interp_accel *LF_spline_acc; -gsl_spline *LF_spline; - -gsl_interp_accel *deriv_spline_acc; -gsl_spline *deriv_spline; - -struct CosmoParams *cosmo_params_ps; -struct UserParams *user_params_ps; -struct FlagOptions *flag_options_ps; - -//These globals hold values initialised in init_ps() and used throughout the rest of the file -double sigma_norm, theta_cmb, omhh, z_equality, y_d, sound_horizon, alpha_nu, f_nu, f_baryon, beta_c, d2fact, R_CUTOFF, DEL_CURR, SIG_CURR; - -double sigmaparam_FgtrM_bias(float z, float sigsmallR, float del_bias, float sig_bias); - -//Sigma interpolation tables are defined here instead of interp_tables.c since they are used in their construction -struct RGTable1D_f Sigma_InterpTable = {.allocated = false,}; -struct RGTable1D_f dSigmasqdm_InterpTable = {.allocated = false,}; - -//These arrays hold the points and weights for the Gauss-Legendre integration routine -//(JD) Since these were always malloc'd one at a time with fixed length ~100, I've changed them to fixed-length arrays -float xi_GL[NGL_INT+1], wi_GL[NGL_INT+1]; -float GL_limit[2] = {0}; - -//These globals are used for the LF calculation -double *lnMhalo_param, *Muv_param, *Mhalo_param; -double *log10phi, *M_uv_z, *M_h_z; -double *lnMhalo_param_MINI, *Muv_param_MINI, *Mhalo_param_MINI; -double *log10phi_MINI, *M_uv_z_MINI, *M_h_z_MINI; -double *deriv, *lnM_temp, *deriv_temp; - -void initialiseSigmaMInterpTable(float M_Min, float M_Max); -void freeSigmaMInterpTable(); - -double EvaluateSigma(double lnM); -double EvaluatedSigmasqdm(double lnM); - -//JBM: Exact integral for power-law indices non zero (for zero it's erfc) -double Fcollapprox (double numin, double beta); - -//Parameters used for gsl integral on the mass function -struct parameters_gsl_MF_integrals{ - //parameters for all MF integrals - double redshift; - double growthf; - int HMF; - - //Conditional parameters - double sigma_cond; - double delta; - - //SFR additions - double Mturn; - double f_star_norm; - double alpha_star; - double Mlim_star; - - //Nion additions - double f_esc_norm; - double alpha_esc; - double Mlim_esc; - - //Minihalo additions - double Mturn_upper; -}; - -unsigned long *lvector(long nl, long nh); -void free_lvector(unsigned long *v, long nl, long nh); - -float *vector(long nl, long nh); -void free_vector(float *v, long nl, long nh); - -void spline(float x[], float y[], int n, float yp1, float ypn, float y2[]); -void splint(float xa[], float ya[], float y2a[], int n, float x, float *y); - -void gauleg(float x1, float x2, float x[], float w[], int n); - -/***** FUNCTION PROTOTYPES *****/ -double init_ps(); /* initialize global variables, MUST CALL THIS FIRST!!! returns R_CUTOFF */ -void free_ps(); /* deallocates the gsl structures from init_ps */ -double sigma_z0(double M); //calculates sigma at z=0 (no dicke) -double power_in_k(double k); /* Returns the value of the linear power spectrum density (i.e. <|delta_k|^2>/V) at a given k mode at z=0 */ -double TFmdm(double k); //Eisenstein & Hu power spectrum transfer function -void TFset_parameters(); - -double TF_CLASS(double k, int flag_int, int flag_dv); //transfer function of matter (flag_dv=0) and relative velocities (flag_dv=1) fluctuations from CLASS -double power_in_vcb(double k); /* Returns the value of the DM-b relative velocity power spectrum density (i.e. <|delta_k|^2>/V) at a given k mode at z=0 */ - - -double FgtrM(double z, double M); -double FgtrM_wsigma(double z, double sig); -double FgtrM_st(double z, double M); -double FgtrM_Watson(double growthf, double M); -double FgtrM_Watson_z(double z, double growthf, double M); -double Fcoll_General(double z, double lnM_min, double lnM_max); - -float erfcc(float x); -double splined_erfc(double x); - -double M_J_WDM(); - -void Broadcast_struct_global_PS(struct UserParams *user_params, struct CosmoParams *cosmo_params){ - - cosmo_params_ps = cosmo_params; - user_params_ps = user_params; -} - - - -/* - this function reads the z=0 matter (CDM+baryons) and relative velocity transfer functions from CLASS (from a file) - flag_int = 0 to initialize interpolator, flag_int = -1 to free memory, flag_int = else to interpolate. - flag_dv = 0 to output density, flag_dv = 1 to output velocity. - similar to built-in function "double T_RECFAST(float z, int flag)" -*/ - -double TF_CLASS(double k, int flag_int, int flag_dv) -{ - static double kclass[CLASS_LENGTH], Tmclass[CLASS_LENGTH], Tvclass_vcb[CLASS_LENGTH]; - static gsl_interp_accel *acc_density, *acc_vcb; - static gsl_spline *spline_density, *spline_vcb; - float trash, currk, currTm, currTv; - double ans; - int i; - int gsl_status; - FILE *F; - - char filename[500]; - sprintf(filename,"%s/%s",global_params.external_table_path,CLASS_FILENAME); - - - if (flag_int == 0) { // Initialize vectors and read file - if (!(F = fopen(filename, "r"))) { - LOG_ERROR("Unable to open file: %s for reading.", filename); - Throw(IOError); - } - - int nscans; - for (i = 0; i < CLASS_LENGTH; i++) { - nscans = fscanf(F, "%e %e %e ", &currk, &currTm, &currTv); - if (nscans != 3) { - LOG_ERROR("Reading CLASS Transfer Function failed."); - Throw(IOError); - } - kclass[i] = currk; - Tmclass[i] = currTm; - Tvclass_vcb[i] = currTv; - if (i > 0 && kclass[i] <= kclass[i - 1]) { - LOG_WARNING("Tk table not ordered"); - LOG_WARNING("k=%.1le kprev=%.1le", kclass[i], kclass[i - 1]); - } - } - fclose(F); - - - LOG_SUPER_DEBUG("Read CLASS Transfer file"); - - gsl_set_error_handler_off(); - // Set up spline table for densities - acc_density = gsl_interp_accel_alloc (); - spline_density = gsl_spline_alloc (gsl_interp_cspline, CLASS_LENGTH); - gsl_status = gsl_spline_init(spline_density, kclass, Tmclass, CLASS_LENGTH); - GSL_ERROR(gsl_status); - - LOG_SUPER_DEBUG("Generated CLASS Density Spline."); - - //Set up spline table for velocities - acc_vcb = gsl_interp_accel_alloc (); - spline_vcb = gsl_spline_alloc (gsl_interp_cspline, CLASS_LENGTH); - gsl_status = gsl_spline_init(spline_vcb, kclass, Tvclass_vcb, CLASS_LENGTH); - GSL_ERROR(gsl_status); - - LOG_SUPER_DEBUG("Generated CLASS velocity Spline."); - return 0; - } - else if (flag_int == -1) { - gsl_spline_free (spline_density); - gsl_interp_accel_free(acc_density); - gsl_spline_free (spline_vcb); - gsl_interp_accel_free(acc_vcb); - return 0; - } - - - if (k > kclass[CLASS_LENGTH-1]) { // k>kmax - LOG_WARNING("Called TF_CLASS with k=%f, larger than kmax! Returning value at kmax.", k); - if(flag_dv == 0){ // output is density - return (Tmclass[CLASS_LENGTH]/kclass[CLASS_LENGTH-1]/kclass[CLASS_LENGTH-1]); - } - else if(flag_dv == 1){ // output is rel velocity - return (Tvclass_vcb[CLASS_LENGTH]/kclass[CLASS_LENGTH-1]/kclass[CLASS_LENGTH-1]); - } //we just set it to the last value, since sometimes it wants large k for R<R conversion). -// The sigma is evaluated at z=0, with the time evolution contained in the dicke(z) factor, -// i.e. sigma(M,z) = sigma_z0(m) * dicke(z) - -// normalized so that sigma_z0(M->8/h Mpc) = SIGMA8 in ../Parameter_files/COSMOLOGY.H - -// NOTE: volume is normalized to = 1, so this is equvalent to the mass standard deviation - -// M is in solar masses - -// References: Padmanabhan, pg. 210, eq. 5.107 -double dsigma_dk(double k, void *params){ - double p, w, T, gamma, q, aa, bb, cc, kR; - - // get the power spectrum.. choice of 5: - if (user_params_ps->POWER_SPECTRUM == 0){ // Eisenstein & Hu - T = TFmdm(k); - // check if we should cuttoff power spectrum according to Bode et al. 2000 transfer function - if (global_params.P_CUTOFF) T *= pow(1 + pow(BODE_e*k*R_CUTOFF, 2*BODE_v), -BODE_n/BODE_v); - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - } - else if (user_params_ps->POWER_SPECTRUM == 1){ // BBKS - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb/cosmo_params_ps->OMm)); - q = k / (cosmo_params_ps->hlittle*gamma); - T = (log(1.0+2.34*q)/(2.34*q)) * - pow( 1.0+3.89*q + pow(16.1*q, 2) + pow( 5.46*q, 3) + pow(6.71*q, 4), -0.25); - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - } - else if (user_params_ps->POWER_SPECTRUM == 2){ // Efstathiou,G., Bond,J.R., and White,S.D.M., MNRAS,258,1P (1992) - gamma = 0.25; - aa = 6.4/(cosmo_params_ps->hlittle*gamma); - bb = 3.0/(cosmo_params_ps->hlittle*gamma); - cc = 1.7/(cosmo_params_ps->hlittle*gamma); - p = pow(k, cosmo_params_ps->POWER_INDEX) / pow( 1+pow( aa*k + pow(bb*k, 1.5) + pow(cc*k,2), 1.13), 2.0/1.13 ); - } - else if (user_params_ps->POWER_SPECTRUM == 3){ // Peebles, pg. 626 - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb/cosmo_params_ps->OMm)); - aa = 8.0 / (cosmo_params_ps->hlittle*gamma); - bb = 4.7 / pow(cosmo_params_ps->hlittle*gamma, 2); - p = pow(k, cosmo_params_ps->POWER_INDEX) / pow(1 + aa*k + bb*k*k, 2); - } - else if (user_params_ps->POWER_SPECTRUM == 4){ // White, SDM and Frenk, CS, 1991, 379, 52 - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb/cosmo_params_ps->OMm)); - aa = 1.7/(cosmo_params_ps->hlittle*gamma); - bb = 9.0/pow(cosmo_params_ps->hlittle*gamma, 1.5); - cc = 1.0/pow(cosmo_params_ps->hlittle*gamma, 2); - p = pow(k, cosmo_params_ps->POWER_INDEX) * 19400.0 / pow(1 + aa*k + bb*pow(k, 1.5) + cc*k*k, 2); - } - else if (user_params_ps->POWER_SPECTRUM == 5){ // output of CLASS - T = TF_CLASS(k, 1, 0); //read from z=0 output of CLASS. Note, flag_int = 1 here always, since now we have to have initialized the interpolator for CLASS - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - if(user_params_ps->USE_RELATIVE_VELOCITIES) { //jbm:Add average relvel suppression - p *= 1.0 - A_VCB_PM*exp( -pow(log(k/KP_VCB_PM),2.0)/(2.0*SIGMAK_VCB_PM*SIGMAK_VCB_PM)); //for v=vrms - } - } - else{ - LOG_ERROR("No such power spectrum defined: %i. Output is bogus.", user_params_ps->POWER_SPECTRUM); - Throw(ValueError); - } - double Radius; - - Radius = *(double *)params; - - kR = k*Radius; - - if ( (global_params.FILTER == 0) || (sigma_norm < 0) ){ // top hat - if ( (kR) < 1.0e-4 ){ w = 1.0;} // w converges to 1 as (kR) -> 0 - else { w = 3.0 * (sin(kR)/pow(kR, 3) - cos(kR)/pow(kR, 2));} - } - else if (global_params.FILTER == 1){ // gaussian of width 1/R - w = pow(E, -kR*kR/2.0); - } - else { - LOG_ERROR("No such filter: %i. Output is bogus.", global_params.FILTER); - Throw(ValueError); - } - return k*k*p*w*w; -} -double sigma_z0(double M){ - - double result, error, lower_limit, upper_limit; - gsl_function F; - double rel_tol = FRACT_FLOAT_ERR*10; //<- relative tolerance - gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000); - double kstart, kend; - - double Radius; -// R = MtoR(M); - - Radius = MtoR(M); - // now lets do the integral for sigma and scale it with sigma_norm - - if(user_params_ps->POWER_SPECTRUM == 5){ - kstart = fmax(1.0e-99/Radius, KBOT_CLASS); - kend = fmin(350.0/Radius, KTOP_CLASS); - }//we establish a maximum k of KTOP_CLASS~1e3 Mpc-1 and a minimum at KBOT_CLASS,~1e-5 Mpc-1 since the CLASS transfer function has a max! - else{ - kstart = 1.0e-99/Radius; - kend = 350.0/Radius; - } - - lower_limit = kstart;//log(kstart); - upper_limit = kend;//log(kend); - - F.function = &dsigma_dk; - F.params = &Radius; - - int status; - - gsl_set_error_handler_off(); - - status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol,1000, GSL_INTEG_GAUSS61, w, &result, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); - LOG_ERROR("data: M=%e",M); - GSL_ERROR(status); - } - - gsl_integration_workspace_free (w); - - return sigma_norm * sqrt(result); -} - - -// FUNCTION TFmdm is the power spectrum transfer function from Eisenstein & Hu ApJ, 1999, 511, 5 -double TFmdm(double k){ - double q, gamma_eff, q_eff, TF_m, q_nu; - - q = k*pow(theta_cmb,2)/omhh; - gamma_eff=sqrt(alpha_nu) + (1.0-sqrt(alpha_nu))/(1.0+pow(0.43*k*sound_horizon, 4)); - q_eff = q/gamma_eff; - TF_m= log(E+1.84*beta_c*sqrt(alpha_nu)*q_eff); - TF_m /= TF_m + pow(q_eff,2) * (14.4 + 325.0/(1.0+60.5*pow(q_eff,1.11))); - q_nu = 3.92*q/sqrt(f_nu/N_nu); - TF_m *= 1.0 + (1.2*pow(f_nu,0.64)*pow(N_nu,0.3+0.6*f_nu)) / - (pow(q_nu,-1.6)+pow(q_nu,0.8)); - - return TF_m; -} - - -void TFset_parameters(){ - double z_drag, R_drag, R_equality, p_c, p_cb, f_c, f_cb, f_nub, k_equality; - - LOG_DEBUG("Setting Transfer Function parameters."); - - z_equality = 25000*omhh*pow(theta_cmb, -4) - 1.0; - k_equality = 0.0746*omhh/(theta_cmb*theta_cmb); - - z_drag = 0.313*pow(omhh,-0.419) * (1 + 0.607*pow(omhh, 0.674)); - z_drag = 1 + z_drag*pow(cosmo_params_ps->OMb*cosmo_params_ps->hlittle*cosmo_params_ps->hlittle, 0.238*pow(omhh, 0.223)); - z_drag *= 1291 * pow(omhh, 0.251) / (1 + 0.659*pow(omhh, 0.828)); - - y_d = (1 + z_equality) / (1.0 + z_drag); - - R_drag = 31.5 * cosmo_params_ps->OMb*cosmo_params_ps->hlittle*cosmo_params_ps->hlittle * pow(theta_cmb, -4) * 1000 / (1.0 + z_drag); - R_equality = 31.5 * cosmo_params_ps->OMb*cosmo_params_ps->hlittle*cosmo_params_ps->hlittle * pow(theta_cmb, -4) * 1000 / (1.0 + z_equality); - - sound_horizon = 2.0/3.0/k_equality * sqrt(6.0/R_equality) * - log( (sqrt(1+R_drag) + sqrt(R_drag+R_equality)) / (1.0 + sqrt(R_equality)) ); - - p_c = -(5 - sqrt(1 + 24*(1 - f_nu-f_baryon)))/4.0; - p_cb = -(5 - sqrt(1 + 24*(1 - f_nu)))/4.0; - f_c = 1 - f_nu - f_baryon; - f_cb = 1 - f_nu; - f_nub = f_nu+f_baryon; - - alpha_nu = (f_c/f_cb) * (2*(p_c+p_cb)+5)/(4*p_cb+5.0); - alpha_nu *= 1 - 0.553*f_nub+0.126*pow(f_nub,3); - alpha_nu /= 1-0.193*sqrt(f_nu)+0.169*f_nu; - alpha_nu *= pow(1+y_d, p_c-p_cb); - alpha_nu *= 1+ (p_cb-p_c)/2.0 * (1.0+1.0/(4.0*p_c+3.0)/(4.0*p_cb+7.0))/(1.0+y_d); - beta_c = 1.0/(1.0-0.949*f_nub); -} - - -// Returns the value of the linear power spectrum DENSITY (i.e. <|delta_k|^2>/V) -// at a given k mode linearly extrapolated to z=0 -double power_in_k(double k){ - double p, T, gamma, q, aa, bb, cc; - - // get the power spectrum.. choice of 5: - if (user_params_ps->POWER_SPECTRUM == 0){ // Eisenstein & Hu - T = TFmdm(k); - // check if we should cuttoff power spectrum according to Bode et al. 2000 transfer function - if (global_params.P_CUTOFF) T *= pow(1 + pow(BODE_e*k*R_CUTOFF, 2*BODE_v), -BODE_n/BODE_v); - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - //p = pow(k, POWER_INDEX - 0.05*log(k/0.05)) * T * T; //running, alpha=0.05 - } - else if (user_params_ps->POWER_SPECTRUM == 1){ // BBKS - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb/cosmo_params_ps->OMm)); - q = k / (cosmo_params_ps->hlittle*gamma); - T = (log(1.0+2.34*q)/(2.34*q)) * - pow( 1.0+3.89*q + pow(16.1*q, 2) + pow( 5.46*q, 3) + pow(6.71*q, 4), -0.25); - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - } - else if (user_params_ps->POWER_SPECTRUM == 2){ // Efstathiou,G., Bond,J.R., and White,S.D.M., MNRAS,258,1P (1992) - gamma = 0.25; - aa = 6.4/(cosmo_params_ps->hlittle*gamma); - bb = 3.0/(cosmo_params_ps->hlittle*gamma); - cc = 1.7/(cosmo_params_ps->hlittle*gamma); - p = pow(k, cosmo_params_ps->POWER_INDEX) / pow( 1+pow( aa*k + pow(bb*k, 1.5) + pow(cc*k,2), 1.13), 2.0/1.13 ); - } - else if (user_params_ps->POWER_SPECTRUM == 3){ // Peebles, pg. 626 - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb)/(cosmo_params_ps->OMm)); - aa = 8.0 / (cosmo_params_ps->hlittle*gamma); - bb = 4.7 / pow(cosmo_params_ps->hlittle*gamma, 2); - p = pow(k, cosmo_params_ps->POWER_INDEX) / pow(1 + aa*k + bb*k*k, 2); - } - else if (user_params_ps->POWER_SPECTRUM == 4){ // White, SDM and Frenk, CS, 1991, 379, 52 - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb/cosmo_params_ps->OMm)); - aa = 1.7/(cosmo_params_ps->hlittle*gamma); - bb = 9.0/pow(cosmo_params_ps->hlittle*gamma, 1.5); - cc = 1.0/pow(cosmo_params_ps->hlittle*gamma, 2); - p = pow(k, cosmo_params_ps->POWER_INDEX) * 19400.0 / pow(1 + aa*k + bb*pow(k, 1.5) + cc*k*k, 2); - } - else if (user_params_ps->POWER_SPECTRUM == 5){ // output of CLASS - T = TF_CLASS(k, 1, 0); //read from z=0 output of CLASS. Note, flag_int = 1 here always, since now we have to have initialized the interpolator for CLASS - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - if(user_params_ps->USE_RELATIVE_VELOCITIES) { //jbm:Add average relvel suppression - p *= 1.0 - A_VCB_PM*exp( -pow(log(k/KP_VCB_PM),2.0)/(2.0*SIGMAK_VCB_PM*SIGMAK_VCB_PM)); //for v=vrms - } - } - else{ - LOG_ERROR("No such power spectrum defined: %i. Output is bogus.", user_params_ps->POWER_SPECTRUM); - Throw(ValueError); - } - - - return p*TWOPI*PI*sigma_norm*sigma_norm; -} - - -/* - Returns the value of the linear power spectrum of the DM-b relative velocity - at kinematic decoupling (which we set at zkin=1010) -*/ -double power_in_vcb(double k){ - - double p, T, gamma, q, aa, bb, cc; - - //only works if using CLASS - if (user_params_ps->POWER_SPECTRUM == 5){ // CLASS - T = TF_CLASS(k, 1, 1); //read from CLASS file. flag_int=1 since we have initialized before, flag_vcb=1 for velocity - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - } - else{ - LOG_ERROR("Cannot get P_cb unless using CLASS: %i\n Set USE_RELATIVE_VELOCITIES 0 or use CLASS.\n", user_params_ps->POWER_SPECTRUM); - Throw(ValueError); - } - - return p*TWOPI*PI*sigma_norm*sigma_norm; -} - - -double init_ps(){ - double result, error, lower_limit, upper_limit; - gsl_function F; - double rel_tol = FRACT_FLOAT_ERR*10; //<- relative tolerance - gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000); - double kstart, kend; - - //we start the interpolator if using CLASS: - if (user_params_ps->POWER_SPECTRUM == 5){ - LOG_DEBUG("Setting CLASS Transfer Function inits."); - TF_CLASS(1.0, 0, 0); - } - - // Set cuttoff scale for WDM (eq. 4 in Barkana et al. 2001) in comoving Mpc - R_CUTOFF = 0.201*pow((cosmo_params_ps->OMm-cosmo_params_ps->OMb)*cosmo_params_ps->hlittle*cosmo_params_ps->hlittle/0.15, 0.15)*pow(global_params.g_x/1.5, -0.29)*pow(global_params.M_WDM, -1.15); - - omhh = cosmo_params_ps->OMm*cosmo_params_ps->hlittle*cosmo_params_ps->hlittle; - theta_cmb = T_cmb / 2.7; - - // Translate Parameters into forms GLOBALVARIABLES form - f_nu = global_params.OMn/cosmo_params_ps->OMm; - f_baryon = cosmo_params_ps->OMb/cosmo_params_ps->OMm; - if (f_nu < TINY) f_nu = 1e-10; - if (f_baryon < TINY) f_baryon = 1e-10; - - TFset_parameters(); - - sigma_norm = -1; - - double Radius_8; - Radius_8 = 8.0/cosmo_params_ps->hlittle; - - if(user_params_ps->POWER_SPECTRUM == 5){ - kstart = fmax(1.0e-99/Radius_8, KBOT_CLASS); - kend = fmin(350.0/Radius_8, KTOP_CLASS); - }//we establish a maximum k of KTOP_CLASS~1e3 Mpc-1 and a minimum at KBOT_CLASS,~1e-5 Mpc-1 since the CLASS transfer function has a max! - else{ - kstart = 1.0e-99/Radius_8; - kend = 350.0/Radius_8; - } - - lower_limit = kstart; - upper_limit = kend; - - LOG_DEBUG("Initializing Power Spectrum with lower_limit=%e, upper_limit=%e, rel_tol=%e, radius_8=%g", lower_limit,upper_limit, rel_tol, Radius_8); - - F.function = &dsigma_dk; - F.params = &Radius_8; - - int status; - - gsl_set_error_handler_off(); - - status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol, - 1000, GSL_INTEG_GAUSS61, w, &result, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); - GSL_ERROR(status); - } - - gsl_integration_workspace_free (w); - - LOG_DEBUG("Initialized Power Spectrum."); - - sigma_norm = cosmo_params_ps->SIGMA_8/sqrt(result); //takes care of volume factor - return R_CUTOFF; -} - - - - -//function to free arrays related to the power spectrum -void free_ps(){ - - //we free the PS interpolator if using CLASS: - if (user_params_ps->POWER_SPECTRUM == 5){ - TF_CLASS(1.0, -1, 0); - } - - return; -} - - - -/* - FUNCTION dsigmasqdm_z0(M) - returns d/dm (sigma^2) (see function sigma), in units of Msun^-1 - */ -double dsigmasq_dm(double k, void *params){ - double p, w, T, gamma, q, aa, bb, cc, dwdr, drdm, kR; - - // get the power spectrum.. choice of 5: - if (user_params_ps->POWER_SPECTRUM == 0){ // Eisenstein & Hu ApJ, 1999, 511, 5 - T = TFmdm(k); - // check if we should cuttoff power spectrum according to Bode et al. 2000 transfer function - if (global_params.P_CUTOFF) T *= pow(1 + pow(BODE_e*k*R_CUTOFF, 2*BODE_v), -BODE_n/BODE_v); - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - //p = pow(k, POWER_INDEX - 0.05*log(k/0.05)) * T * T; //running, alpha=0.05 - } - else if (user_params_ps->POWER_SPECTRUM == 1){ // BBKS - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb)/(cosmo_params_ps->OMm)); - q = k / (cosmo_params_ps->hlittle*gamma); - T = (log(1.0+2.34*q)/(2.34*q)) * - pow( 1.0+3.89*q + pow(16.1*q, 2) + pow( 5.46*q, 3) + pow(6.71*q, 4), -0.25); - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - } - else if (user_params_ps->POWER_SPECTRUM == 2){ // Efstathiou,G., Bond,J.R., and White,S.D.M., MNRAS,258,1P (1992) - gamma = 0.25; - aa = 6.4/(cosmo_params_ps->hlittle*gamma); - bb = 3.0/(cosmo_params_ps->hlittle*gamma); - cc = 1.7/(cosmo_params_ps->hlittle*gamma); - p = pow(k, cosmo_params_ps->POWER_INDEX) / pow( 1+pow( aa*k + pow(bb*k, 1.5) + pow(cc*k,2), 1.13), 2.0/1.13 ); - } - else if (user_params_ps->POWER_SPECTRUM == 3){ // Peebles, pg. 626 - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb)/(cosmo_params_ps->OMm)); - aa = 8.0 / (cosmo_params_ps->hlittle*gamma); - bb = 4.7 / (cosmo_params_ps->hlittle*gamma); - p = pow(k, cosmo_params_ps->POWER_INDEX) / pow(1 + aa*k + bb*k*k, 2); - } - else if (user_params_ps->POWER_SPECTRUM == 4){ // White, SDM and Frenk, CS, 1991, 379, 52 - gamma = cosmo_params_ps->OMm * cosmo_params_ps->hlittle * pow(E, -(cosmo_params_ps->OMb) - (cosmo_params_ps->OMb)/(cosmo_params_ps->OMm)); - aa = 1.7/(cosmo_params_ps->hlittle*gamma); - bb = 9.0/pow(cosmo_params_ps->hlittle*gamma, 1.5); - cc = 1.0/pow(cosmo_params_ps->hlittle*gamma, 2); - p = pow(k, cosmo_params_ps->POWER_INDEX) * 19400.0 / pow(1 + aa*k + pow(bb*k, 1.5) + cc*k*k, 2); - } - else if (user_params_ps->POWER_SPECTRUM == 5){ // JBM: CLASS - T = TF_CLASS(k, 1, 0); //read from z=0 output of CLASS - p = pow(k, cosmo_params_ps->POWER_INDEX) * T * T; - if(user_params_ps->USE_RELATIVE_VELOCITIES) { //jbm:Add average relvel suppression - p *= 1.0 - A_VCB_PM*exp( -pow(log(k/KP_VCB_PM),2.0)/(2.0*SIGMAK_VCB_PM*SIGMAK_VCB_PM)); //for v=vrms - } - } - else{ - LOG_ERROR("No such power spectrum defined: %i. Output is bogus.", user_params_ps->POWER_SPECTRUM); - Throw(ValueError); - } - - double Radius; - Radius = *(double *)params; - - // now get the value of the window function - kR = k * Radius; - if (global_params.FILTER == 0){ // top hat - if ( (kR) < 1.0e-4 ){ w = 1.0; }// w converges to 1 as (kR) -> 0 - else { w = 3.0 * (sin(kR)/pow(kR, 3) - cos(kR)/pow(kR, 2));} - - // now do d(w^2)/dm = 2 w dw/dr dr/dm - if ( (kR) < 1.0e-10 ){ dwdr = 0;} - else{ dwdr = 9*cos(kR)*k/pow(kR,3) + 3*sin(kR)*(1 - 3/(kR*kR))/(kR*Radius);} - //3*k*( 3*cos(kR)/pow(kR,3) + sin(kR)*(-3*pow(kR, -4) + 1/(kR*kR)) );} - // dwdr = -1e8 * k / (R*1e3); - drdm = 1.0 / (4.0*PI * cosmo_params_ps->OMm*RHOcrit * Radius*Radius); - } - else if (global_params.FILTER == 1){ // gaussian of width 1/R - w = pow(E, -kR*kR/2.0); - dwdr = - k*kR * w; - drdm = 1.0 / (pow(2*PI, 1.5) * cosmo_params_ps->OMm*RHOcrit * 3*Radius*Radius); - } - else { - LOG_ERROR("No such filter: %i. Output is bogus.", global_params.FILTER); - Throw(ValueError); - } - -// return k*k*p*2*w*dwdr*drdm * d2fact; - return k*k*p*2*w*dwdr*drdm; -} -double dsigmasqdm_z0(double M){ - double result, error, lower_limit, upper_limit; - gsl_function F; - double rel_tol = FRACT_FLOAT_ERR*10; //<- relative tolerance - gsl_integration_workspace * w - = gsl_integration_workspace_alloc (1000); - double kstart, kend; - - double Radius; -// R = MtoR(M); - Radius = MtoR(M); - - // now lets do the integral for sigma and scale it with sigma_norm - if(user_params_ps->POWER_SPECTRUM == 5){ - kstart = fmax(1.0e-99/Radius, KBOT_CLASS); - kend = fmin(350.0/Radius, KTOP_CLASS); - }//we establish a maximum k of KTOP_CLASS~1e3 Mpc-1 and a minimum at KBOT_CLASS,~1e-5 Mpc-1 since the CLASS transfer function has a max! - else{ - kstart = 1.0e-99/Radius; - kend = 350.0/Radius; - } - - lower_limit = kstart;//log(kstart); - upper_limit = kend;//log(kend); - - - if (user_params_ps->POWER_SPECTRUM == 5){ // for CLASS we do not need to renormalize the sigma integral. - d2fact=1.0; - } - else { - d2fact = M*10000/sigma_z0(M); - } - - - F.function = &dsigmasq_dm; - F.params = &Radius; - - int status; - - gsl_set_error_handler_off(); - - status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol,1000, GSL_INTEG_GAUSS61, w, &result, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); - LOG_ERROR("data: M=%e",M); - GSL_ERROR(status); - } - - gsl_integration_workspace_free (w); - -// return sigma_norm * sigma_norm * result /d2fact; - return sigma_norm * sigma_norm * result; -} - -////MASS FUNCTIONS BELOW////// - -/* sheth correction to delta crit */ -double sheth_delc_dexm(double del, double sig){ - return sqrt(SHETH_a)*del*(1. + global_params.SHETH_b*pow(sig*sig/(SHETH_a*del*del), global_params.SHETH_c)); -} - -/*DexM uses a fit to this barrier to acheive MF similar to ST, Here I use the fixed version for the sampler*/ -//NOTE: if I made this a table it would save a pow call per condition in the sampler -double sheth_delc_fixed(double del, double sig){ - return sqrt(JENKINS_a)*del*(1. + JENKINS_b*pow(sig*sig/(JENKINS_a*del*del), JENKINS_c)); -} - -//Get the relevant excursion set barrier density given the user-specified HMF -double get_delta_crit(int HMF, double sigma, double growthf){ - if(HMF==4) - return DELTAC_DELOS; - if(HMF==1) - return sheth_delc_fixed(Deltac/growthf,sigma)*growthf; - - return Deltac; -} - -/* -Unconditional Mass function from Delos 2023 (https://arxiv.org/pdf/2311.17986.pdf) -Matches well with N-bodies (M200), has a corresponding Conditional Mass Function (below) and -an excursion set method. Hence can be consistently used throughout the Halo Finder, Halo Sampler -And radiation. The mass functions are based off a constant barrier delta = 1.5 and a top-hat window function -*/ -double dNdlnM_Delos(double growthf, double lnM){ - double dfdnu,dsigmadm,sigma,sigma_inv,dfdM; - double nu; - //hardcoded for now - const double coeff_nu = 0.519; - const double index_nu = 0.582; - const double exp_factor = -0.469; - - sigma = EvaluateSigma(lnM); - sigma_inv = 1/sigma; - dsigmadm = EvaluatedSigmasqdm(lnM) * (0.5*sigma_inv); //d(s^2)/dm z0 to dsdm - - nu = DELTAC_DELOS*sigma_inv/growthf; - - dfdnu = coeff_nu*pow(nu,index_nu)*exp(exp_factor*nu*nu); - dfdM = dfdnu * fabs(dsigmadm) * sigma_inv; - - //NOTE: dfdM == constants*dNdlnM - return dfdM*cosmo_params_ps->OMm*RHOcrit; -} - -double dNdlnM_conditional_Delos(double growthf, double lnM, double delta_cond, double sigma_cond){ - double result,dfdnu,dsigmadm,sigma,sigdiff_inv,dfdM,sigma_inv; - double nu; - //hardcoded for now - const double coeff_nu = 0.519; - const double index_nu = 0.582; - const double exp_factor = -0.469; - - sigma = EvaluateSigma(lnM); - if(sigma < sigma_cond) return 0.; - dsigmadm = EvaluatedSigmasqdm(lnM) * 0.5; //d(s^2)/dm to s*dsdm - sigdiff_inv = sigma == sigma_cond ? 1e6 : 1/(sigma*sigma - sigma_cond*sigma_cond); - - nu = (DELTAC_DELOS - delta_cond)*sqrt(sigdiff_inv)/growthf; - - dfdnu = coeff_nu*pow(nu,index_nu)*exp(exp_factor*nu*nu); - dfdM = dfdnu * fabs(dsigmadm) * sigdiff_inv; - - //NOTE: like the other CMFs this is dNdlogM and leaves out - // the (cosmo_params_ps->OMm)*RHOcrit - //NOTE: dfdM == constants*dNdlnM - return dfdM; -} - -//Sheth Tormen 2002 fit for the CMF, while the moving barrier does not allow for a simple rescaling, it has been found -//That a taylor expansion of the barrier shape around the point of interest well approximates the simulations -double st_taylor_factor(double sig, double sig_cond, double growthf, double *zeroth_order){ - int i; - double a = JENKINS_a; - double alpha = JENKINS_c; //fixed instead of global_params.SHETH_c bc of DexM corrections - double beta = JENKINS_b; //fixed instead of global_params.SHETH_b - - double del = Deltac/growthf; - - double sigsq = sig*sig; - double sigsq_inv = 1./sigsq; - double sigcsq = sig_cond*sig_cond; - double sigdiff = sig == sig_cond ? 1e-6 : sigsq - sigcsq; - - // This array cumulatively builds the taylor series terms - // sigdiff^n / n! * df/dsigma (polynomial w alpha) - double t_array[6]; - t_array[0] = 1.; - for(i=1;i<6;i++) - t_array[i] = t_array[i-1] * (-sigdiff) / i * (alpha-i+1) * sigsq_inv; - - //Sum small to large - double result = 0.; - for(i=5;i>=0;i--){ - result += t_array[i]; - } - - double prefactor_1 = sqrt(a)*del; - double prefactor_2 = beta*pow(sigsq_inv*(a*del*del),-alpha); - - result = prefactor_1*(1 + prefactor_2*result); - *zeroth_order = prefactor_1*(1+prefactor_2); //0th order term gives the barrier for efficiency - return result; -} - -//CMF Corresponding to the Sheth Mo Tormen HMF (Sheth+ 2002) -double dNdM_conditional_ST(double growthf, double lnM, double delta_cond, double sigma_cond){ - double sigma1, dsigmasqdm, Barrier, factor, sigdiff_inv, result; - double delta_0 = delta_cond / growthf; - sigma1 = EvaluateSigma(lnM); - dsigmasqdm = EvaluatedSigmasqdm(lnM); - if(sigma1 < sigma_cond) return 0.; - - factor = st_taylor_factor(sigma1,sigma_cond,growthf,&Barrier) - delta_0; - - sigdiff_inv = sigma1 == sigma_cond ? 1e6 : 1/(sigma1*sigma1 - sigma_cond*sigma_cond); - - result = -dsigmasqdm*factor*pow(sigdiff_inv,1.5)*exp(-(Barrier - delta_0)*(Barrier - delta_0)*0.5*(sigdiff_inv))/sqrt(2.*PI); - return result; -} - -/* - FUNCTION dNdM_st(z, M) - Computes the Press_schechter mass function with Sheth-Torman correction for ellipsoidal collapse at - redshift z, and dark matter halo mass M (in solar masses). Moving barrier B(z,sigma) and sharp-k window functino - - Uses interpolated sigma and dsigmadm to be computed faster. Necessary for mass-dependent ionising efficiencies. - - The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: - comoving Mpc^-3 Msun^-1 - - Reference: Sheth, Mo, Torman 2001 - */ -double dNdlnM_st(double growthf, double lnM){ - double sigma, dsigmadm, nuhat; - - float MassBinLow; - int MassBin; - - sigma = EvaluateSigma(lnM); - dsigmadm = EvaluatedSigmasqdm(lnM); - - sigma = sigma * growthf; - dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); - - nuhat = sqrt(SHETH_a) * Deltac / sigma; - - return (-(cosmo_params_ps->OMm)*RHOcrit) * (dsigmadm/sigma) * sqrt(2./PI)*SHETH_A * (1+ pow(nuhat, -2*SHETH_p)) * nuhat * pow(E, -nuhat*nuhat/2.0); -} - -//Conditional Extended Press-Schechter Mass function, with constant barrier delta=1.682 and sharp-k window function -double dNdM_conditional_EPS(double growthf, double lnM, double delta_cond, double sigma_cond){ - double sigma1, dsigmasqdm, sigdiff_inv, del; - - sigma1 = EvaluateSigma(lnM); - dsigmasqdm = EvaluatedSigmasqdm(lnM); - - //limit setting - if(sigma1 < sigma_cond) return 0.; - sigdiff_inv = sigma1 == sigma_cond ? 1e6 : 1/(sigma1*sigma1 - sigma_cond*sigma_cond); - del = (Deltac - delta_cond)/growthf; - - return -del*dsigmasqdm*pow(sigdiff_inv, 1.5)*exp(-del*del*0.5*sigdiff_inv)/sqrt(2.*PI); -} - -/* - FUNCTION dNdM(growthf, M) - Computes the Press_schechter mass function at - redshift z (using the growth factor), and dark matter halo mass M (in solar masses). - - Uses interpolated sigma and dsigmadm to be computed faster. Necessary for mass-dependent ionising efficiencies. - - The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: - comoving Mpc^-3 Msun^-1 - - Reference: Padmanabhan, pg. 214 - */ -double dNdlnM_PS(double growthf, double lnM){ - double sigma, dsigmadm; - - sigma = EvaluateSigma(lnM); - dsigmadm = EvaluatedSigmasqdm(lnM); - - sigma = sigma * growthf; - dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); - return (-(cosmo_params_ps->OMm)*RHOcrit) * sqrt(2/PI) * (Deltac/(sigma*sigma)) * dsigmadm * exp(-(Deltac*Deltac)/(2*sigma*sigma)); -} - -//The below mass functions do not have a CMF given -/* - FUNCTION dNdM_WatsonFOF(z, M) - Computes the Press_schechter mass function with Warren et al. 2011 correction for ellipsoidal collapse at - redshift z, and dark matter halo mass M (in solar masses). - - The Universial FOF function (Eq. 12) of Watson et al. 2013 - - The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: - comoving Mpc^-3 Msun^-1 - - Reference: Watson et al. 2013 - */ -double dNdlnM_WatsonFOF(double growthf, double lnM){ - - double sigma, dsigmadm, f_sigma; - - sigma = EvaluateSigma(lnM); - dsigmadm = EvaluatedSigmasqdm(lnM); - - sigma = sigma * growthf; - dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); - - f_sigma = Watson_A * ( pow( Watson_beta/sigma, Watson_alpha) + 1. ) * exp( - Watson_gamma/(sigma*sigma) ); - - return (-(cosmo_params_ps->OMm)*RHOcrit) * (dsigmadm/sigma) * f_sigma; -} - -/* - FUNCTION dNdM_WatsonFOF_z(z, M) - Computes the Press_schechter mass function with Warren et al. 2011 correction for ellipsoidal collapse at - redshift z, and dark matter halo mass M (in solar masses). - - The Universial FOF function, with redshift evolution (Eq. 12 - 15) of Watson et al. 2013. - - The return value is the number density per unit mass of halos in the mass range M to M+dM in units of: - comoving Mpc^-3 Msun^-1 - - Reference: Watson et al. 2013 - */ -double dNdlnM_WatsonFOF_z(double z, double growthf, double lnM){ - double sigma, dsigmadm, A_z, alpha_z, beta_z, Omega_m_z, f_sigma; - - sigma = EvaluateSigma(lnM); - dsigmadm = EvaluatedSigmasqdm(lnM); - - sigma = sigma * growthf; - dsigmadm = dsigmadm * (growthf*growthf/(2.*sigma)); - - Omega_m_z = (cosmo_params_ps->OMm)*pow(1.+z,3.) / ( (cosmo_params_ps->OMl) + (cosmo_params_ps->OMm)*pow(1.+z,3.) + (global_params.OMr)*pow(1.+z,4.) ); - - A_z = Omega_m_z * ( Watson_A_z_1 * pow(1. + z, Watson_A_z_2 ) + Watson_A_z_3 ); - alpha_z = Omega_m_z * ( Watson_alpha_z_1 * pow(1.+z, Watson_alpha_z_2 ) + Watson_alpha_z_3 ); - beta_z = Omega_m_z * ( Watson_beta_z_1 * pow(1.+z, Watson_beta_z_2 ) + Watson_beta_z_3 ); - - f_sigma = A_z * ( pow(beta_z/sigma, alpha_z) + 1. ) * exp( - Watson_gamma_z/(sigma*sigma) ); - - return (-(cosmo_params_ps->OMm)*RHOcrit) * (dsigmadm/sigma) * f_sigma; -} - -///////MASS FUNCTION INTEGRANDS BELOW////// - -//gets the fraction (in units of 1/normalisation at 1e10) -double get_frac_limit(double M, double norm, double alpha, double limit, bool mini){ - double pivot = mini ? 1e7 : 1e10; - if ((alpha > 0. && M > limit) || (alpha < 0. && M < limit)) - return 1/norm; - - //if alpha is zero, this returns 1 as expected (note strict inequalities above) - return pow(M/pivot,alpha); -} - -double nion_fraction(double M, void *param_struct){ - struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; - double M_turn_lower = params.Mturn; - double f_starn = params.f_star_norm; - double a_star = params.alpha_star; - double f_escn = params.f_esc_norm; - double a_esc = params.alpha_esc; - double Mlim_star = params.Mlim_star; - double Mlim_esc = params.Mlim_esc; - - double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,false); - double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,false); - - return Fstar * Fesc * exp(-M_turn_lower/M); -} - -double nion_fraction_mini(double M, void *param_struct){ - struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; - double M_turn_lower = params.Mturn; - double M_turn_upper = params.Mturn_upper; - double f_starn = params.f_star_norm; - double a_star = params.alpha_star; - double f_escn = params.f_esc_norm; - double a_esc = params.alpha_esc; - double Mlim_star = params.Mlim_star; - double Mlim_esc = params.Mlim_esc; - - double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,true); - double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,true); - - return Fstar * Fesc * exp(-M_turn_lower/M - M/M_turn_upper); -} - -double conditional_mf(double growthf, double lnM, double delta, double sigma, int HMF){ - //dNdlnM = dfcoll/dM * M / M * constants - if(HMF==0) { - return dNdM_conditional_EPS(growthf,lnM,delta,sigma); - } - if(HMF==1) { - return dNdM_conditional_ST(growthf,lnM,delta,sigma); - } - if(HMF==4) { - return dNdlnM_conditional_Delos(growthf,lnM,delta,sigma); - } - //NOTE: Normalisation scaling is currently applied outside the integral, per condition - //This will be the rescaled EPS CMF, - return dNdM_conditional_EPS(growthf,lnM,delta,sigma); - -} - -double c_mf_integrand(double lnM, void *param_struct){ - struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; - double growthf = params.growthf; - double delta = params.delta; //the condition delta - double sigma2 = params.sigma_cond; - int HMF = params.HMF; - - return conditional_mf(growthf,lnM,delta,sigma2,HMF); -} - -double c_fcoll_integrand(double lnM, void *param_struct){ - return exp(lnM) * c_mf_integrand(lnM,param_struct); -} - -double c_nion_integrand(double lnM, void *param_struct){ - return nion_fraction(exp(lnM),param_struct) * exp(lnM) * c_mf_integrand(lnM,param_struct); -} - -//The reason this is separated from the above is the second exponent -double c_nion_integrand_mini(double lnM, void *param_struct){ - - return nion_fraction_mini(exp(lnM),param_struct) * exp(lnM) * c_mf_integrand(lnM,param_struct); -} - -double unconditional_mf(double growthf, double lnM, double z, int HMF){ - //most of the UMFs are defined with M, but we integrate over lnM - //NOTE: HMF > 4 or < 0 gets caught earlier, so unless some strange change is made this is fine - if(HMF==0) { - return dNdlnM_PS(growthf, lnM); - } - if(HMF==1) { - return dNdlnM_st(growthf, lnM); - } - if(HMF==2) { - return dNdlnM_WatsonFOF(growthf, lnM); - } - if(HMF==3) { - return dNdlnM_WatsonFOF_z(z, growthf, lnM); - } - if(HMF==4) { - return dNdlnM_Delos(growthf, lnM); - } - else{ - LOG_ERROR("Invalid HMF %d",HMF); - Throw(ValueError); - } -} - -double u_mf_integrand(double lnM, void *param_struct){ - struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; - double mf, m_factor; - double growthf = params.growthf; - double z = params.redshift; - int HMF = params.HMF; - - return unconditional_mf(growthf,lnM,z,HMF); -} - -double u_fcoll_integrand(double lnM, void *param_struct){ - return exp(lnM) * u_mf_integrand(lnM,param_struct); -} - -double u_nion_integrand(double lnM, void *param_struct){ - struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; - double M_turn = params.Mturn; - double f_starn = params.f_star_norm; - double a_star = params.alpha_star; - double f_escn = params.f_esc_norm; - double a_esc = params.alpha_esc; - double Mlim_star = params.Mlim_star; - double Mlim_esc = params.Mlim_esc; - - double M = exp(lnM); - - double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,false); - double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,false); - - return M * Fstar * Fesc * exp(-M_turn/M) * u_mf_integrand(lnM,param_struct); -} - -//The reason this is separated from the above is the second exponent -double u_nion_integrand_mini(double lnM, void *param_struct){ - struct parameters_gsl_MF_integrals params = *(struct parameters_gsl_MF_integrals *)param_struct; - double M_turn_lower = params.Mturn; - double M_turn_upper = params.Mturn_upper; - double f_starn = params.f_star_norm; - double a_star = params.alpha_star; - double f_escn = params.f_esc_norm; - double a_esc = params.alpha_esc; - double Mlim_star = params.Mlim_star; - double Mlim_esc = params.Mlim_esc; - - double M = exp(lnM); - - double Fstar = get_frac_limit(M,f_starn,a_star,Mlim_star,true); - double Fesc = get_frac_limit(M,f_escn,a_esc,Mlim_esc,true); - - return M * Fstar * Fesc * exp(-M_turn_lower/M - M/M_turn_upper) * u_mf_integrand(lnM,param_struct); -} - -///// INTEGRATION ROUTINES BELOW ///// - -//TODO: make type enum for clarity (but cffi doesn't seem to like enum in 21cmFAST.h) -//NOTE: SFR is obtained from nion with alpha_esc==0 and f_esc==1 -//Currently the scheme is to use negative numbers for conditionals, and (1,2,3,4) for (number,mass,n_ion,n_ion_mini) -double (*get_integrand_function(int type))(double,void*){ - if(type==1) - return &u_mf_integrand; //Unondtional mass function integral - if(type==2) - return &u_fcoll_integrand; //Unconditional collapsed fraction integral - if(type==3) - return &u_nion_integrand; //Unconditional N_ion integral (two power-laws and one exponential) - if(type==4) - return &u_nion_integrand_mini; //Unconditional N_ion minihalo integral (two power-laws and two exponentials) - if(type==-1) - return &c_mf_integrand; //Conditional mass function integral - if(type==-2) - return &c_fcoll_integrand; //Conditional collapsed fraction integral - if(type==-3) - return &c_nion_integrand; //Conditional N_ion integral (two power-laws and one exponential) - if(type==-4) - return &c_nion_integrand_mini; //Conditional N_ion minihalo integral (two power-laws and two exponentials) - - LOG_ERROR("Invalid type %d for MF integral"); - Throw(ValueError); -} - -//Integral of a CMF or UMF -//In future all MF integrals will go through here, simply selecting the integrand function from a switch -double IntegratedNdM_QAG(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type){ - double result, error, lower_limit, upper_limit; - gsl_function F; - // double rel_tol = FRACT_FLOAT_ERR*128; //<- relative tolerance - double rel_tol = 1e-3; //<- relative tolerance - int w_size = 1000; - gsl_integration_workspace * w - = gsl_integration_workspace_alloc (w_size); - - int status; - F.function = get_integrand_function(type); - F.params = ¶ms; - lower_limit = lnM_lo; - upper_limit = lnM_hi; - - gsl_set_error_handler_off(); - status = gsl_integration_qag (&F, lower_limit, upper_limit, 0, rel_tol, - w_size, GSL_INTEG_GAUSS61, w, &result, &error); - - if(status!=0) { - LOG_ERROR("gsl integration error occured!"); - LOG_ERROR("(function argument): lower_limit=%.3e (%.3e) upper_limit=%.3e (%.3e) rel_tol=%.3e result=%.3e error=%.3e",lower_limit,exp(lower_limit),upper_limit,exp(upper_limit),rel_tol,result,error); - LOG_ERROR("data: z=%.3e growthf=%.3e HMF=%d type=%d ",params.redshift,params.growthf,params.HMF,type); - LOG_ERROR("sigma=%.3e delta=%.3e",params.sigma_cond,params.delta); - LOG_ERROR("Mturn_lo=%.3e f*=%.3e a*=%.3e Mlim*=%.3e",params.Mturn,params.f_star_norm,params.alpha_star,params.Mlim_star); - LOG_ERROR("f_escn=%.3e a_esc=%.3e Mlim_esc=%.3e",params.f_esc_norm,params.alpha_esc,params.Mlim_esc); - LOG_ERROR("Mturn_hi %.3e",params.Mturn_upper); - GSL_ERROR(status); - } - - gsl_integration_workspace_free (w); - - return result; -} - -//calculates the weightings and the positions for any Gauss-Legendre quadrature. -void gauleg(float x1, float x2, float x[], float w[], int n) -//Given the lower and upper limits of integration x1 and x2, and given n, this routine returns arrays x[1..n] and w[1..n] of length n, -//containing the abscissas and weights of the Gauss- Legendre n-point quadrature formula. -{ - - int m,j,i; - double z1,z,xm,xl,pp,p3,p2,p1; - - m=(n+1)/2; - xm=0.5*(x2+x1); - xl=0.5*(x2-x1); - for (i=1;i<=m;i++) { - //High precision is a good idea for this routine. - //The roots are symmetric in the interval, so we only have to find half of them. - //Loop over the desired roots. - - z=cos(3.141592654*(i-0.25)/(n+0.5)); - - //Starting with the above approximation to the ith root, we enter the main loop of refinement by Newton’s method. - do { - p1=1.0; - p2=0.0; - for (j=1;j<=n;j++) { - //Loop up the recurrence relation to get the Legendre polynomial evaluated at z. - p3=p2; - p2=p1; - p1=((2.0*j-1.0)*z*p2-(j-1.0)*p3)/j; - } - //p1 is now the desired Legendre polynomial. We next compute pp, its derivative, by a standard relation involving also p2, - //the polynomial of one lower order. - pp=n*(z*p1-p2)/(z*z-1.0); - z1=z; - z=z1-p1/pp; - } while (fabs(z-z1) > EPS2); - x[i]=xm-xl*z; - x[n+1-i]=xm+xl*z; - w[i]=2.0*xl/((1.0-z*z)*pp*pp); - w[n+1-i]=w[i]; - } -} - -//Specific initialistion for the global arrays -void initialise_GL(int n, float lnM_Min, float lnM_Max){ - //don't redo if you don't have to - if(lnM_Min == GL_limit[0] && lnM_Max == GL_limit[1]) - return; - - gauleg(lnM_Min,lnM_Max,xi_GL,wi_GL,n); - GL_limit[0] = lnM_Min; - GL_limit[1] = lnM_Max; -} - -//actually perform the GL integration -//NOTE: that the lnM limits are not used -double IntegratedNdM_GL(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type){ - int i; - double integral = 0; - if((float)lnM_lo != (float)GL_limit[0] || (float)lnM_hi != (float)GL_limit[1]){ - LOG_ERROR("Integral limits [%.8e %.8e] do not match Gauss Legendre limits [%.8e %.8e]!",exp(lnM_lo),exp(lnM_hi),GL_limit[0],GL_limit[1]); - Throw(TableGenerationError); - } - - for(i=1; i<(NGL_INT+1); i++){ - integral += wi_GL[i]*(get_integrand_function(type))(xi_GL[i],¶ms); - } - - return integral; -} - -#include -//JBM: Integral of a power-law times exponential for EPS: \int dnu nu^beta * exp(-nu/2)/sqrt(nu) from numin to infty. -double Fcollapprox(double numin, double beta){ -//nu is deltacrit^2/sigma^2, corrected by delta(R) and sigma(R) - double gg = gsl_sf_gamma_inc(0.5+beta,0.5*numin); - return gg*pow(2,0.5+beta)*pow(2.0*PI,-0.5); -} - -//This takes into account the last approximation in Munoz+22, where erfc (beta=0) is used -//NOTE: even though nu_condition is defined in the unconditional (no sigma_cond), here it -// represents where nu_tilde == nu_condition (effectively a final pivot point) -//NOTE: This assumes numin < nucondition, otherise it fails -double Fcollapprox_condition(double numin, double nucondition, double beta){ - return (Fcollapprox(numin,beta) - Fcollapprox(nucondition,beta)) + Fcollapprox(nucondition,0.)*pow(nucondition,beta); -} - -//This routine assumes sharp cutoffs for each turnover rather than exponential, assumes a triple power-law form for sigma(M) -// and takes advantage of the fact that Gamma_inc(x,min) = integral_min^inf (t^(x-1)exp(-t)) dt which is satisfied for the HMF when the -// above approximations are made -//Originally written by JBM within the GL integration before it was separated here and generalised to the other integrals -double MFIntegral_Approx(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type){ - //variables used in the calculation - double lnM_higher, lnM_lower; - - double delta,sigma_c; - double index_base; - - if(params.HMF != 0){ - LOG_ERROR("Approximate Fcoll is currently only implemented for EPS"); - LOG_ERROR("Ensure parameter input specifically to this function has HMF==0"); - Throw(TableGenerationError); - } - double growthf = params.growthf; - if(type < 0){ - //we are a conditional mf - delta = params.delta; - sigma_c = params.sigma_cond; - } - else{ - //unconditional - delta = 0.; - sigma_c = 0.; - } - - double lnM_lo_limit = lnM_lo; - double lnM_hi_limit = lnM_hi; - //(Speed): by passing in log(M_turnover) i can avoid these 2 log calls - double lnMturn_l = log(params.Mturn); - double lnMturn_u = log(params.Mturn_upper); - //(Speed): LOG(MPIVOTn) can be pre-defined via macro - double lnMp1 = log(MPIVOT1); - double lnMp2 = log(MPIVOT2); - - //The below limit setting is done simply so that variables which do not conern particular integrals - // can be left undefined, rather than explicitly set to some value (0 or 1e20) - //Mass and number integrals set the lower cutoff to the integral limit - if(fabs(type) >= 3 && lnMturn_l > lnM_lo_limit) - lnM_lo_limit = lnMturn_l; - //non-minihalo integrals set the upper cutoff to the integral limit - if(fabs(type) == 4 && lnMturn_u < lnM_hi_limit) - lnM_hi_limit = lnMturn_u; - - //it is possible for the lower turnover (LW crit or reion feedback) - // to be higher than the upper limit (atomic limit) or the condition - if(lnM_lo_limit >= lnM_hi_limit || EvaluateSigma(lnM_lo_limit) <= sigma_c){ - return 0.; - } - - //n_ion or MINI - if(fabs(type) >= 3) - index_base = params.alpha_star + params.alpha_esc; - //fcoll - else if(fabs(type)==2) - index_base = 0.; - //nhalo - else - index_base = -1.; - - double delta_arg = pow((Deltac - delta)/growthf, 2); - double beta1 = index_base * AINDEX1 * 0.5; //exponent for Fcollapprox for nu>nupivot1 (large M) - double beta2 = index_base * AINDEX2 * 0.5; //exponent for Fcollapprox for nupivot2 nu_pivot2){ - fcoll += (Fcollapprox(nu_lo_limit,beta2))*pow(nu_pivot1_umf,-beta2); - } - else { - fcoll += (Fcollapprox(nu_pivot2,beta2))*pow(nu_pivot1_umf,-beta2); - fcoll += (Fcollapprox(nu_lo_limit,beta3)-Fcollapprox(nu_pivot2,beta3) )*pow(nu_pivot2_umf,-beta3); - } - } - } - else{ - if(nu_lo_limit >= nu_condition){ //fully in the flat part of sigma(nu), M^alpha is nu-independent. - // This is just an erfc, remembering that the conditional nu can be higher than the unconditional nu of the condition - return Fcollapprox(nu_lo_limit,0.); - } - - if(nu_lo_limit >= nu_pivot1){ - //We use the condition version wherever the nu range may intersect nu_condition (i.e beta1) - fcoll += Fcollapprox_condition(nu_lo_limit,nu_condition,beta1)*pow(nu_pivot1_umf,-beta1); - } - else{ - fcoll += Fcollapprox_condition(nu_pivot1,nu_condition,beta1)*pow(nu_pivot1_umf,-beta1); - if (nu_lo_limit > nu_pivot2){ - fcoll += (Fcollapprox(nu_lo_limit,beta2)-Fcollapprox(nu_pivot1,beta2))*pow(nu_pivot1_umf,-beta2); - } - else { - fcoll += (Fcollapprox(nu_pivot2,beta2)-Fcollapprox(nu_pivot1,beta2) )*pow(nu_pivot1_umf,-beta2); - fcoll += (Fcollapprox(nu_lo_limit,beta3)-Fcollapprox(nu_pivot2,beta3) )*pow(nu_pivot2_umf,-beta3); - } - } - } - - if (fcoll<=0.0){ - LOG_DEBUG("Negative fcoll? fc=%.1le\n",fcoll); - fcoll=1e-40; - } - return fcoll; -} - -double IntegratedNdM(double lnM_lo, double lnM_hi, struct parameters_gsl_MF_integrals params, int type, int method){ - if(method==0 || (method==1 && params.delta > global_params.CRIT_DENS_TRANSITION)) - return IntegratedNdM_QAG(lnM_lo, lnM_hi, params, type); - if(method==1) - return IntegratedNdM_GL(lnM_lo, lnM_hi, params, type); - if(method==2) - return MFIntegral_Approx(lnM_lo, lnM_hi, params, type); -} - -//Some wrappers over the integration functions for specific integrals// - -/* - FUNCTION FgtrM(z, M) - Computes the fraction of mass contained in haloes with mass > M at redshift z - */ -double FgtrM(double z, double M){ - double del, sig; - - del = Deltac/dicke(z); //regular spherical collapse delta - sig = sigma_z0(M); - - return splined_erfc(del / (sqrt(2)*sig)); -} - -/* - FUNCTION FgtrM_wsigma(z, sigma_z0(M)) - Computes the fraction of mass contained in haloes with mass > M at redshift z. - Requires sigma_z0(M) rather than M to make certain heating integrals faster - */ -double FgtrM_wsigma(double z, double sig){ - double del; - - del = Deltac/dicke(z); //regular spherical collapse delta - - return splined_erfc(del / (sqrt(2)*sig)); -} - -double Fcoll_General(double z, double lnM_min, double lnM_max){ - double lower_limit, upper_limit, growthf; - - growthf = dicke(z); - struct parameters_gsl_MF_integrals integral_params = { - .redshift = z, - .growthf = growthf, - .HMF = user_params_ps->HMF, - }; - return IntegratedNdM(lnM_min, lnM_max, integral_params, 2, 0) / (cosmo_params_ps->OMm*RHOcrit); -} - -double Nion_General(double z, double lnM_Min, double lnM_Max, double MassTurnover, double Alpha_star, double Alpha_esc, double Fstar10, - double Fesc10, double Mlim_Fstar, double Mlim_Fesc){ - struct parameters_gsl_MF_integrals params = { - .redshift = z, - .growthf = dicke(z), - .Mturn = MassTurnover, - .alpha_star = Alpha_star, - .alpha_esc = Alpha_esc, - .f_star_norm = Fstar10, - .f_esc_norm = Fesc10, - .Mlim_star = Mlim_Fstar, - .Mlim_esc = Mlim_Fesc, - .HMF = user_params_ps->HMF, - }; - return IntegratedNdM(lnM_Min,lnM_Max,params,3,0) / ((cosmo_params_ps->OMm)*RHOcrit); -} - -double Nion_General_MINI(double z, double lnM_Min, double lnM_Max, double MassTurnover, double MassTurnover_upper, double Alpha_star, - double Alpha_esc, double Fstar7_MINI, double Fesc7_MINI, double Mlim_Fstar, double Mlim_Fesc){ - struct parameters_gsl_MF_integrals params = { - .redshift = z, - .growthf = dicke(z), - .Mturn = MassTurnover, - .Mturn_upper = MassTurnover_upper, - .alpha_star = Alpha_star, - .alpha_esc = Alpha_esc, - .f_star_norm = Fstar7_MINI, - .f_esc_norm = Fesc7_MINI, - .Mlim_star = Mlim_Fstar, - .Mlim_esc = Mlim_Fesc, - .HMF = user_params_ps->HMF, - }; - return IntegratedNdM(lnM_Min,lnM_Max,params,4,0) / ((cosmo_params_ps->OMm)*RHOcrit); -} - -double Nhalo_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method){ - struct parameters_gsl_MF_integrals params = { - .growthf = growthf, - .HMF = user_params_ps->HMF, - .sigma_cond = sigma, - .delta = delta, - }; - - if(delta <= -1. || lnM1 >= log(M_cond)) - return 0.; - //return 1 halo AT THE CONDITION MASS if delta is exceeded - if(delta > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma,growthf)){ - if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) //this limit is not ideal, but covers floating point errors when we set lnM2 == log(M_cond) - return 1./M_cond; - else - return 0.; - } - - return IntegratedNdM(lnM1,lnM2,params,-1, method); -} - -double Mcoll_Conditional(double growthf, double lnM1, double lnM2, double M_cond, double sigma, double delta, int method){ - struct parameters_gsl_MF_integrals params = { - .growthf = growthf, - .HMF = user_params_ps->HMF, - .sigma_cond = sigma, - .delta = delta, - }; - - if(delta <= -1. || lnM1 >= log(M_cond)) - return 0.; - //return 100% of mass AT THE CONDITION MASS if delta is exceeded - if(delta > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma,growthf)){ - if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) //this limit is not ideal, but covers floating point errors when we set lnM2 == log(M_cond) - return 1.; - else - return 0.; - } - return IntegratedNdM(lnM1,lnM2,params,-2, method); -} - -double Nion_ConditionalM_MINI(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, - double MassTurnover_upper, double Alpha_star, double Alpha_esc, double Fstar7, - double Fesc7, double Mlim_Fstar, double Mlim_Fesc, int method){ - struct parameters_gsl_MF_integrals params = { - .growthf = growthf, - .Mturn = MassTurnover, - .Mturn_upper = MassTurnover_upper, - .alpha_star = Alpha_star, - .alpha_esc = Alpha_esc, - .f_star_norm = Fstar7, - .f_esc_norm = Fesc7, - .Mlim_star = Mlim_Fstar, - .Mlim_esc = Mlim_Fesc, - .HMF = user_params_ps->HMF, - .sigma_cond = sigma2, - .delta = delta2, - }; - - if(delta2 <= -1. || lnM1 >= log(M_cond)) - return 0.; - //return 1 halo at the condition mass if delta is exceeded - //NOTE: this will almost always be zero, due to the upper turover, - // however this replaces an integral so it won't be slow - if(delta2 > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma2,growthf)){ - if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) //this limit is not ideal, but covers floating point errors when we set lnM2 == log(M_cond) - return nion_fraction_mini(M_cond,¶ms); //NOTE: condition mass is used as if it were Lagrangian (no 1+delta) - else - return 0.; - } - - //If we don't have a corresponding CMF, use EPS and normalise - //NOTE: it's possible we may want to use another default - if(params.HMF != 0 && params.HMF != 1 && params.HMF != 4) - params.HMF = 0; - - return IntegratedNdM(lnM1,lnM2,params,-4,method); -} - -double Nion_ConditionalM(double growthf, double lnM1, double lnM2, double M_cond, double sigma2, double delta2, double MassTurnover, - double Alpha_star, double Alpha_esc, double Fstar10, double Fesc10, double Mlim_Fstar, - double Mlim_Fesc, int method){ - struct parameters_gsl_MF_integrals params = { - .growthf = growthf, - .Mturn = MassTurnover, - .alpha_star = Alpha_star, - .alpha_esc = Alpha_esc, - .f_star_norm = Fstar10, - .f_esc_norm = Fesc10, - .Mlim_star = Mlim_Fstar, - .Mlim_esc = Mlim_Fesc, - .HMF = user_params_ps->HMF, - .sigma_cond = sigma2, - .delta = delta2, - }; - - if(delta2 <= -1. || lnM1 >= log(M_cond)) - return 0.; - //return 1 halo at the condition mass if delta is exceeded - if(delta2 > MAX_DELTAC_FRAC*get_delta_crit(params.HMF,sigma2,growthf)){ - if(M_cond*(1-FRACT_FLOAT_ERR) <= exp(lnM2)) - return nion_fraction(M_cond,¶ms); //NOTE: condition mass is used as if it were Lagrangian (no 1+delta) - else - return 0.; - } - - //If we don't have a corresponding CMF, use EPS and normalise - //NOTE: it's possible we may want to use another default - if(params.HMF != 0 && params.HMF != 1 && params.HMF != 4) - params.HMF = 0; - - return IntegratedNdM(lnM1,lnM2,params,-3, method); -} - -/* returns the "effective Jeans mass" in Msun - corresponding to the gas analog of WDM ; eq. 10 in Barkana+ 2001 */ -double M_J_WDM(){ - double z_eq, fudge=60; - if (!(global_params.P_CUTOFF)) - return 0; - z_eq = 3600*(cosmo_params_ps->OMm-cosmo_params_ps->OMb)*cosmo_params_ps->hlittle*cosmo_params_ps->hlittle/0.15; - return fudge*3.06e8 * (1.5/global_params.g_x) * sqrt((cosmo_params_ps->OMm-cosmo_params_ps->OMb)*cosmo_params_ps->hlittle*cosmo_params_ps->hlittle/0.15) * pow(global_params.M_WDM, -4) * pow(z_eq/3000.0, 1.5); -} - -float erfcc(float x) -{ - double t,q,ans; - - q=fabs(x); - t=1.0/(1.0+0.5*q); - ans=t*exp(-q*q-1.2655122+t*(1.0000237+t*(0.374092+t*(0.0967842+ - t*(-0.1862881+t*(0.2788681+t*(-1.13520398+t*(1.4885159+ - t*(-0.82215223+t*0.17087277))))))))); - return x >= 0.0 ? ans : 2.0-ans; -} - -double splined_erfc(double x){ - if (x < 0){ - return 1.0; - } - - // TODO: This could be wrapped in a Try/Catch to try the fast way and if it doesn't - // work, use the slow way. - return erfcc(x); // the interpolation below doesn't seem to be stable in Ts.c - if (x > ERFC_PARAM_DELTA*(ERFC_NPTS-1)) - return erfcc(x); - else - return exp(gsl_spline_eval(erfc_spline, x, erfc_acc)); -} - - -void initialiseSigmaMInterpTable(float M_min, float M_max){ - int i; - - if(!Sigma_InterpTable.allocated) - allocate_RGTable1D_f(NMass,&Sigma_InterpTable); - if(!dSigmasqdm_InterpTable.allocated) - allocate_RGTable1D_f(NMass,&dSigmasqdm_InterpTable); - - Sigma_InterpTable.x_min = log(M_min); - Sigma_InterpTable.x_width = (log(M_max) - log(M_min))/(NMass-1.); - dSigmasqdm_InterpTable.x_min = log(M_min); - dSigmasqdm_InterpTable.x_width = (log(M_max) - log(M_min))/(NMass-1.); - -#pragma omp parallel private(i) num_threads(user_params_ps->N_THREADS) - { - float Mass; -#pragma omp for - for(i=0;i 0.99e30) // The lower boundary condition is set either to be "natural" - y2[1]=u[1]=0.0; - else { // or else to have a specified first derivative. - y2[1] = -0.5; - u[1]=(3.0/(x[2]-x[1]))*((y[2]-y[1])/(x[2]-x[1])-yp1); - } - for (i=2;i<=n-1;i++) { //This is the decomposition loop of the tridiagonal algorithm. - sig=(x[i]-x[i-1])/(x[i+1]-x[i-1]); //y2 and u are used for temporary - na = 1; - nb = 1; - check = 0; - while(((float)(x[i+na*1]-x[i-nb*1])==(float)0.0)) { - check = check + 1; - if(check%2==0) { - na = na + 1; - } - else { - nb = nb + 1; - } - sig=(x[i]-x[i-1])/(x[i+na*1]-x[i-nb*1]); - } - p=sig*y2[i-1]+2.0; //storage of the decomposed - y2[i]=(sig-1.0)/p; // factors. - u[i]=(y[i+1]-y[i])/(x[i+1]-x[i]) - (y[i]-y[i-1])/(x[i]-x[i-1]); - u[i]=(6.0*u[i]/(x[i+1]-x[i-1])-sig*u[i-1])/p; - - if(((float)(x[i+1]-x[i])==(float)0.0) || ((float)(x[i]-x[i-1])==(float)0.0)) { - na = 0; - nb = 0; - check = 0; - while((float)(x[i+na*1]-x[i-nb])==(float)(0.0) || ((float)(x[i+na]-x[i-nb*1])==(float)0.0)) { - check = check + 1; - if(check%2==0) { - na = na + 1; - } - else { - nb = nb + 1; - } - } - u[i]=(y[i+1]-y[i])/(x[i+na*1]-x[i-nb]) - (y[i]-y[i-1])/(x[i+na]-x[i-nb*1]); - - u[i]=(6.0*u[i]/(x[i+na*1]-x[i-nb*1])-sig*u[i-1])/p; - - } - } - if (ypn > 0.99e30) //The upper boundary condition is set either to be "natural" - qn=un=0.0; - else { //or else to have a specified first derivative. - qn=0.5; - un=(3.0/(x[n]-x[n-1]))*(ypn-(y[n]-y[n-1])/(x[n]-x[n-1])); - } - y2[n]=(un-qn*u[n-1])/(qn*y2[n-1]+1.0); - - for (k=n-1;k>=1;k--) { //This is the backsubstitution loop of the tridiagonal - y2[k]=y2[k]*y2[k+1]+u[k]; //algorithm. - } - free_vector(u,1,n-1); -} - - -void splint(float xa[], float ya[], float y2a[], int n, float x, float *y) -/*Given the arrays xa[1..n] and ya[1..n], which tabulate a function (with the xai's in order), - and given the array y2a[1..n], which is the output from spline above, and given a value of - x, this routine returns a cubic-spline interpolated value y.*/ -{ - void nrerror(char error_text[]); - int klo,khi,k; - float h,b,a; - klo=1; // We will find the right place in the table by means of - khi=n; //bisection. This is optimal if sequential calls to this - while (khi-klo > 1) { //routine are at random values of x. If sequential calls - k=(khi+klo) >> 1; //are in order, and closely spaced, one would do better - if (xa[k] > x) khi=k; //to store previous values of klo and khi and test if - else klo=k; //they remain appropriate on the next call. - } // klo and khi now bracket the input value of x. - h=xa[khi]-xa[klo]; - if (h == 0.0) nrerror("Bad xa input to routine splint"); //The xa's must be distinct. - a=(xa[khi]-x)/h; - b=(x-xa[klo])/h; //Cubic spline polynomial is now evaluated. - *y=a*ya[klo]+b*ya[khi]+((a*a*a-a)*y2a[klo]+(b*b*b-b)*y2a[khi])*(h*h)/6.0; -} - -unsigned long *lvector(long nl, long nh) -/* allocate an unsigned long vector with subscript range v[nl..nh] */ -{ - unsigned long *v; - v = (unsigned long *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(long))); - if(!v) nrerror("allocation failure in lvector()"); - return v - nl + NR_END; -} - -void free_lvector(unsigned long *v, long nl, long nh) -/* free an unsigned long vector allocated with lvector() */ -{ - free((FREE_ARG) (v+nl-NR_END)); -} - - -/* dnbiasdM */ -double dnbiasdM(double M, float z, double M_o, float del_o){ - double sigsq, del, sig_one, sig_o; - - if ((M_o-M) < TINY){ - LOG_ERROR("In function dnbiasdM: M must be less than M_o!\nAborting...\n"); - Throw(ValueError); - } - del = Deltac/dicke(z) - del_o; - if (del < 0){ - LOG_ERROR(" In function dnbiasdM: del_o must be less than del_1 = del_crit/dicke(z)!\nAborting...\n"); - Throw(ValueError); - } - - sig_o = sigma_z0(M_o); - sig_one = sigma_z0(M); - sigsq = sig_one*sig_one - sig_o*sig_o; - return -(RHOcrit*cosmo_params_ps->OMm)/M /sqrt(2*PI) *del*pow(sigsq,-1.5)*pow(E, -0.5*del*del/sigsq)*dsigmasqdm_z0(M); -} - -/* - calculates the fraction of mass contained in haloes with mass > M at redshift z, in regions with a linear overdensity of del_bias, and standard deviation sig_bias - */ - -//I wrote a version of FgtrM which takes the growth func instead of z for a bit of speed -double FgtrM_bias_fast(float growthf, float del_bias, float sig_small, float sig_large){ - double del, sig; - if (sig_large > sig_small){ // biased region is smaller that halo! - LOG_ERROR("Trying to compute FgtrM in region where M_min > M_max"); - Throw(ValueError); - } - //sometimes they are the same to float precision, where the M_condition ~ M_Min - if(sig_large == sig_small){ - return 0.; - } - // del = Deltac/growthf - del_bias; //NOTE HERE DELTA EXTRAPOLATED TO z=0 - sig = sqrt(sig_small*sig_small - sig_large*sig_large); - del = (Deltac - del_bias)/growthf; - - //if the density is above critical on this scale, it is collapsed - //NOTE: should we allow del < 0??? We would need to change dfcolldz to prevent zero dfcoll - // if(del < FRACT_FLOAT_ERR){ - // return 1.; - // } - return splined_erfc(del / (sqrt(2)*sig)); -} - -/* Uses sigma parameters instead of Mass for scale */ -double sigmaparam_FgtrM_bias(float z, float sigsmallR, float del_bias, float sig_bias){ - return FgtrM_bias_fast(dicke(z),del_bias,sigsmallR,sig_bias); -} - -double FgtrM_bias(double z, double M, double del_bias, double sig_bias){ - return sigmaparam_FgtrM_bias(z,EvaluateSigma(log(M)),del_bias,sig_bias); -} - -// Redshift derivative of the conditional collapsed fraction -float dfcoll_dz(float z, float sigma_min, float del_bias, float sig_bias) -{ - double dz,z1,z2; - double fc1,fc2,ans; - - dz = 0.001; - z1 = z + dz; - z2 = z - dz; - fc1 = sigmaparam_FgtrM_bias(z1, sigma_min, del_bias, sig_bias); - fc2 = sigmaparam_FgtrM_bias(z2, sigma_min, del_bias, sig_bias); - ans = (fc1 - fc2)/(2.0*dz); - return ans; -} - -/* redshift derivative of the growth function at z */ -double ddicke_dz(double z){ - float dz = 1e-10; - double omegaM_z, ddickdz, dick_0, x, x_0, domegaMdz; - - return (dicke(z+dz)-dicke(z))/dz; -} - -/* compute a mass limit where the stellar baryon fraction and the escape fraction exceed unity */ -//NOTE (JD): Why aren't we using 1e10 * pow(FRAC,-1/PL)? what am I missing here that makes the rootfind necessary -float Mass_limit (float logM, float PL, float FRAC) { - return FRAC*pow(pow(10.,logM)/1e10,PL); -} -void bisection(float *x, float xlow, float xup, int *iter){ - *x=(xlow + xup)/2.; - ++(*iter); -} - -float Mass_limit_bisection(float Mmin, float Mmax, float PL, float FRAC){ - int i, iter, max_iter=200; - float rel_tol=0.001; - float logMlow, logMupper, x, x1; - iter = 0; - logMlow = log10(Mmin); - logMupper = log10(Mmax); - - if (PL < 0.) { - if (Mass_limit(logMlow,PL,FRAC) <= 1.) { - return Mmin; - } - } - else if (PL > 0.) { - if (Mass_limit(logMupper,PL,FRAC) <= 1.) { - return Mmax; - } - } - else - return 0; - bisection(&x, logMlow, logMupper, &iter); - do { - if((Mass_limit(logMlow,PL,FRAC)-1.)*(Mass_limit(x,PL,FRAC)-1.) < 0.) - logMupper = x; - else - logMlow = x; - bisection(&x1, logMlow, logMupper, &iter); - if(fabs(x1-x) < rel_tol) { - return pow(10.,x1); - } - x = x1; - } - while(iter < max_iter); - - // Got to max_iter without finding a solution. - LOG_ERROR("Failed to find a mass limit to regulate stellar fraction/escape fraction is between 0 and 1."); - LOG_ERROR(" The solution does not converge or iterations are not sufficient."); -// Throw(ParameterError); - Throw(MassDepZetaError); - - return(0.0); -} - -int initialise_ComputeLF(int nbins, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, struct FlagOptions *flag_options) { - - Broadcast_struct_global_PS(user_params,cosmo_params); - Broadcast_struct_global_UF(user_params,cosmo_params); - - lnMhalo_param = calloc(nbins,sizeof(double)); - Muv_param = calloc(nbins,sizeof(double)); - Mhalo_param = calloc(nbins,sizeof(double)); - - LF_spline_acc = gsl_interp_accel_alloc(); - LF_spline = gsl_spline_alloc(gsl_interp_cspline, nbins); - - init_ps(); - - int status; - Try initialiseSigmaMInterpTable(0.999*Mhalo_min,1.001*Mhalo_max); - Catch(status) { - LOG_ERROR("\t...called from initialise_ComputeLF"); - return(status); - } - - initialised_ComputeLF = true; - return(0); -} - -void cleanup_ComputeLF(){ - free(lnMhalo_param); - free(Muv_param); - free(Mhalo_param); - gsl_spline_free (LF_spline); - gsl_interp_accel_free(LF_spline_acc); - freeSigmaMInterpTable(); - initialised_ComputeLF = 0; -} - -int ComputeLF(int nbins, struct UserParams *user_params, struct CosmoParams *cosmo_params, struct AstroParams *astro_params, - struct FlagOptions *flag_options, int component, int NUM_OF_REDSHIFT_FOR_LF, float *z_LF, float *M_TURNs, double *M_uv_z, double *M_h_z, double *log10phi) { - /* - This is an API-level function and thus returns an int status. - */ - int status; - Try{ // This try block covers the whole function. - // This NEEDS to be done every time, because the actual object passed in as - // user_params, cosmo_params etc. can change on each call, freeing up the memory. - initialise_ComputeLF(nbins, user_params,cosmo_params,astro_params,flag_options); - - int i,i_z; - int i_unity, i_smth, mf, nbins_smth=7; - double dlnMhalo, lnMhalo_i, SFRparam, Muv_1, Muv_2, dMuvdMhalo; - double Mhalo_i, lnMhalo_min, lnMhalo_max, lnMhalo_lo, lnMhalo_hi, dlnM, growthf; - double f_duty_upper, Mcrit_atom; - float Fstar, Fstar_temp; - double dndm; - int gsl_status; - - gsl_set_error_handler_off(); - if (astro_params->ALPHA_STAR < -0.5) - LOG_WARNING( - "ALPHA_STAR is %f, which is unphysical value given the observational LFs.\n"\ - "Also, when ALPHA_STAR < -.5, LFs may show a kink. It is recommended to set ALPHA_STAR > -0.5.", - astro_params->ALPHA_STAR - ); - - mf = user_params_ps->HMF; - - lnMhalo_min = log(Mhalo_min*0.999); - lnMhalo_max = log(Mhalo_max*1.001); - dlnMhalo = (lnMhalo_max - lnMhalo_min)/(double)(nbins - 1); - - for (i_z=0; i_zF_STAR10*pow(Mhalo_i/1e10,astro_params->ALPHA_STAR); - else - Fstar = astro_params->F_STAR7_MINI*pow(Mhalo_i/1e7,astro_params->ALPHA_STAR_MINI); - if (Fstar > 1.) Fstar = 1; - - if (i_unity < 0) { // Find the array number at which Fstar crosses unity. - if (astro_params->ALPHA_STAR > 0.) { - if ( (1.- Fstar) < FRACT_FLOAT_ERR ) i_unity = i; - } - else if (astro_params->ALPHA_STAR < 0. && i < nbins-1) { - if (component == 1) - Fstar_temp = astro_params->F_STAR10*pow( exp(lnMhalo_min + dlnMhalo*(double)(i+1))/1e10,astro_params->ALPHA_STAR); - else - Fstar_temp = astro_params->F_STAR7_MINI*pow( exp(lnMhalo_min + dlnMhalo*(double)(i+1))/1e7,astro_params->ALPHA_STAR_MINI); - if (Fstar_temp < 1. && (1.- Fstar) < FRACT_FLOAT_ERR) i_unity = i; - } - } - - // parametrization of SFR - SFRparam = Mhalo_i * cosmo_params->OMb/cosmo_params->OMm * (double)Fstar * (double)(hubble(z_LF[i_z])*SperYR/astro_params->t_STAR); // units of M_solar/year - - Muv_param[i] = 51.63 - 2.5*log10(SFRparam*Luv_over_SFR); // UV magnitude - // except if Muv value is nan or inf, but avoid error put the value as 10. - if ( isinf(Muv_param[i]) || isnan(Muv_param[i]) ) Muv_param[i] = 10.; - - M_uv_z[i + i_z*nbins] = Muv_param[i]; - } - - gsl_status = gsl_spline_init(LF_spline, lnMhalo_param, Muv_param, nbins); - GSL_ERROR(gsl_status); - - lnMhalo_lo = log(Mhalo_min); - lnMhalo_hi = log(Mhalo_max); - dlnM = (lnMhalo_hi - lnMhalo_lo)/(double)(nbins - 1); - - // There is a kink on LFs at which Fstar crosses unity. This kink is a numerical artefact caused by the derivate of dMuvdMhalo. - // Most of the cases the kink doesn't appear in magnitude ranges we are interested (e.g. -22 < Muv < -10). However, for some extreme - // parameters, it appears. To avoid this kink, we use the interpolation of the derivate in the range where the kink appears. - // 'i_unity' is the array number at which the kink appears. 'i_unity-3' and 'i_unity+12' are related to the range of interpolation, - // which is an arbitrary choice. - // NOTE: This method does NOT work in cases with ALPHA_STAR < -0.5. But, this parameter range is unphysical given that the - // observational LFs favour positive ALPHA_STAR in this model. - // i_smth = 0: calculates LFs without interpolation. - // i_smth = 1: calculates LFs using interpolation where Fstar crosses unity. - if (i_unity-3 < 0) i_smth = 0; - else if (i_unity+12 > nbins-1) i_smth = 0; - else i_smth = 1; - if (i_smth == 0) { - for (i=0; iUSE_INTERPOLATION_TABLES) { - return EvaluateRGTable1D_f(lnM, &Sigma_InterpTable); - } - return sigma_z0(exp(lnM)); -} - -double EvaluatedSigmasqdm(double lnM){ - //this may be slow, figure out why the dsigmadm table is in log10 - if(user_params_ps->USE_INTERPOLATION_TABLES){ - return -pow(10., EvaluateRGTable1D_f(lnM, &dSigmasqdm_InterpTable)); - } - return dsigmasqdm_z0(exp(lnM)); -} - -//set the minimum source mass for the integrals, If we have an exponential cutoff we go below the chosen mass by a factor of 50 -//NOTE: previously, with USE_MINI_HALOS, the sigma table was initialised with M_MIN_INTEGRAL/50, but then all integrals perofmed -// from M_MIN_INTEGRAL -double minimum_source_mass(double redshift, bool xray, struct AstroParams *astro_params, struct FlagOptions *flag_options){ - double Mmin,min_factor,mu_factor,t_vir_min; - if(flag_options->USE_MASS_DEPENDENT_ZETA && !flag_options->USE_MINI_HALOS) - min_factor = 50.; // small lower bound to cover far below the turnover - else - min_factor = 1.; //sharp cutoff - - // automatically false if !USE_MASS_DEPENDENT_ZETA - if(flag_options->USE_MINI_HALOS){ - Mmin = global_params.M_MIN_INTEGRAL; - } - // automatically true if USE_MASS_DEPENDENT_ZETA - else if(flag_options->M_MIN_in_Mass) { - //NOTE: previously this divided Mturn by 50 in spin temperature, but not in the ionised box - // which I think is a bug with M_MIN_in_Mass, since there is a sharp cutoff - Mmin = astro_params->M_TURN; - } - else { - //if the virial temp minimum is set below ionisation we need to set mu accordingly - t_vir_min = xray ? astro_params->X_RAY_Tvir_MIN : astro_params->ION_Tvir_MIN; - mu_factor = t_vir_min < 9.99999e3 ? 1.22 : 0.6; - Mmin = TtoM(redshift, t_vir_min, mu_factor); - } - - //This is mostly unused and needs to be tested - if(global_params.P_CUTOFF){ - Mmin = fmax(Mmin,M_J_WDM()); - } - - Mmin /= min_factor; - - return Mmin; -} diff --git a/src/py21cmfast/src/recombinations.c b/src/py21cmfast/src/recombinations.c index 198356ff..ccf0658e 100755 --- a/src/py21cmfast/src/recombinations.c +++ b/src/py21cmfast/src/recombinations.c @@ -1,3 +1,19 @@ +#include +#include +#include +#include +#include +#include + +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" +#include "Constants.h" +#include "InputParameters.h" +#include "thermochem.h" + +#include "recombinations.h" + #define A_NPTS (int) (60) /*Warning: the calculation of the MHR model parameters is valid only from redshift 2 to A_NPTS+2*/ static double A_table[A_NPTS], A_params[A_NPTS]; @@ -23,12 +39,7 @@ static double RR_table[RR_Z_NPTS][RR_lnGamma_NPTS], lnGamma_values[RR_lnGamma_NP static gsl_interp_accel *RR_acc[RR_Z_NPTS]; static gsl_spline *RR_spline[RR_Z_NPTS]; - -/*** FUNCTION PROTOTYPES ***/ -double splined_recombination_rate(double z_eff, double gamma12_bg); // assumes T=1e4 and case B - double recombination_rate(double z_eff, double gamma12_bg, double T4, int usecaseB); -void init_MHR(); /*initializes the lookup table for the PDF density integral in MHR00 model at redshift z*/ void free_MHR(); /* deallocates the gsl structures from init_MHR */ double Gamma_SS(double Gamma_bg, double Delta, double T_4, double z);//ionization rate w. self shielding double MHR_rr (double del, void *params); @@ -45,7 +56,6 @@ void init_A_MHR(); /*initializes the lookup table for the A paremeter in MHR00 m void init_C_MHR(); /*initializes the lookup table for the C paremeter in MHR00 model*/ void init_beta_MHR(); /*initializes the lookup table for the beta paremeter in MHR00 model*/ - double splined_recombination_rate(double z_eff, double gamma12_bg){ int z_ct = (int) (z_eff / RR_DEL_Z + 0.5); // round to nearest int double lnGamma = log(gamma12_bg); @@ -153,7 +163,7 @@ double MHR_rr (double lnD, void *params){ // temeperature T4 (in 1e4 K), and usecaseB rate coefficient // Assumes self-shielding according to Rahmati+ 2013 double recombination_rate(double z, double gamma12_bg, double T4, int usecaseB){ - double result, error, lower_limit, upper_limit, A, C_0, beta, avenH; + double result, error, lower_limit, upper_limit; gsl_function F; double rel_tol = 0.01; //<- relative tolerance gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000); @@ -176,7 +186,7 @@ double recombination_rate(double z, double gamma12_bg, double T4, int usecaseB){ LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); LOG_ERROR("data: z=%e gamma12_bg=%e T4=%e A_MHR(z)=%e",z,gamma12_bg,T4,A_MHR(z)); LOG_ERROR("data: C_MHR(z)=%e beta_MHR(z)=%e nH=%e usecaseB=%d\n",C_MHR(z),beta_MHR(z),No*pow( 1+z, 3),usecaseB); - GSL_ERROR(status); + CATCH_GSL_ERROR(status); } gsl_integration_workspace_free (w); @@ -215,7 +225,7 @@ double A_aux_integral(double z){ LOG_ERROR("gsl integration error occured!"); LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",lower_limit,upper_limit,rel_tol,result,error); LOG_ERROR("data: z=%e",z); - GSL_ERROR(status); + CATCH_GSL_ERROR(status); } gsl_integration_workspace_free (w); diff --git a/src/py21cmfast/src/recombinations.h b/src/py21cmfast/src/recombinations.h new file mode 100644 index 00000000..a2ffc7cf --- /dev/null +++ b/src/py21cmfast/src/recombinations.h @@ -0,0 +1,7 @@ +#ifndef _RECOMB_H +#define _RECOMB_H + +double splined_recombination_rate(double z_eff, double gamma12_bg); +void init_MHR(); /*initializes the lookup table for the PDF density integral in MHR00 model at redshift z*/ + +#endif diff --git a/src/py21cmfast/src/subcell_rsds.c b/src/py21cmfast/src/subcell_rsds.c index 454ff56c..9591946a 100644 --- a/src/py21cmfast/src/subcell_rsds.c +++ b/src/py21cmfast/src/subcell_rsds.c @@ -1,20 +1,30 @@ +#include +#include +#include +#include + +#include "Constants.h" + +#include "InputParameters.h" +#include "OutputStructs.h" +#include "indexing.h" + +#include "subcell_rsds.h" + double apply_subcell_rsds( - struct UserParams *user_params, - struct CosmoParams *cosmo_params, - struct FlagOptions *flag_options, - struct AstroParams *astro_params, - struct IonizedBox *ionized_box, - struct BrightnessTemp *box, + UserParams *user_params, + CosmoParams *cosmo_params, + FlagOptions *flag_options, + AstroParams *astro_params, + IonizedBox *ionized_box, + BrightnessTemp *box, float redshift, - struct TsBox *spin_temp, + TsBox *spin_temp, float T_rad, float *v, - float H -) { + float H){ - char wisdom_filename[500]; - int i, ii, j, k, n_x, n_y, n_z; - float k_x, k_y, k_z; + int i, ii, j, k; double ave; ave = 0.; @@ -28,7 +38,6 @@ double apply_subcell_rsds( delta_T_RSD_LOS[i] = (float *)calloc(user_params->HII_DIM,sizeof(float)); } - float gradient_component, min_gradient_component; float d1_low, d1_high, d2_low, d2_high; float x_val1, x_val2, subcell_displacement; float RSD_pos_new, RSD_pos_new_boundary_low,RSD_pos_new_boundary_high; diff --git a/src/py21cmfast/src/subcell_rsds.h b/src/py21cmfast/src/subcell_rsds.h new file mode 100644 index 00000000..409c5251 --- /dev/null +++ b/src/py21cmfast/src/subcell_rsds.h @@ -0,0 +1,21 @@ +#ifndef _SUBCELL_RSD_H +#define _SUBCELL_RSD_H + +#include "InputParameters.h" +#include "OutputStructs.h" + +double apply_subcell_rsds( + UserParams *user_params, + CosmoParams *cosmo_params, + FlagOptions *flag_options, + AstroParams *astro_params, + IonizedBox *ionized_box, + BrightnessTemp *box, + float redshift, + TsBox *spin_temp, + float T_rad, + float *v, + float H +); + +#endif diff --git a/src/py21cmfast/src/thermochem.c b/src/py21cmfast/src/thermochem.c new file mode 100644 index 00000000..29237f21 --- /dev/null +++ b/src/py21cmfast/src/thermochem.c @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include "cexcept.h" +#include "exceptions.h" +#include "logger.h" + +#include "Constants.h" +#include "InputParameters.h" +#include "OutputStructs.h" +#include "cosmology.h" + +#include "thermochem.h" + +float ComputeFullyIoinizedTemperature(float z_re, float z, float delta){ + // z_re: the redshift of reionization + // z: the current redshift + // delta:the density contrast + float result, delta_re; + // just be fully ionized + if (fabs(z - z_re) < 1e-4) + result = 1; + else{ + // linearly extrapolate to get density at reionization + delta_re = delta * (1. + z ) / (1. + z_re); + if (delta_re<=-1) delta_re=-1. + global_params.MIN_DENSITY_LOW_LIMIT; + // evolving ionized box eq. 6 of McQuinn 2015, ignored the dependency of density at ionization + if (delta<=-1) delta=-1. + global_params.MIN_DENSITY_LOW_LIMIT; + result = pow((1. + delta) / (1. + delta_re), 1.1333); + result *= pow((1. + z) / (1. + z_re), 3.4); + result *= expf(pow((1. + z)/7.1, 2.5) - pow((1. + z_re)/7.1, 2.5)); + } + result *= pow(global_params.T_RE, 1.7); + // 1e4 before helium reionization; double it after + result += pow(1e4 * ((1. + z)/4.), 1.7) * ( 1 + delta); + result = pow(result, 0.5882); + //LOG_DEBUG("z_re=%.4f, z=%.4f, delta=%e, Tk=%.f", z_re, z, delta, result); + return result; +} + +float ComputePartiallyIoinizedTemperature(float T_HI, float res_xH){ + if (res_xH<=0.) return global_params.T_RE; + if (res_xH>=1) return T_HI; + + return T_HI * res_xH + global_params.T_RE * (1. - res_xH); +} + +/* returns the case A hydrogen recombination coefficient (Abel et al. 1997) in cm^3 s^-1*/ +double alpha_A(double T){ + double logT, ans; + logT = log(T/(double)1.1604505e4); + ans = pow(E, -28.6130338 - 0.72411256*logT - 2.02604473e-2*pow(logT, 2) + - 2.38086188e-3*pow(logT, 3) - 3.21260521e-4*pow(logT, 4) + - 1.42150291e-5*pow(logT, 5) + 4.98910892e-6*pow(logT, 6) + + 5.75561414e-7*pow(logT, 7) - 1.85676704e-8*pow(logT, 8) + - 3.07113524e-9 * pow(logT, 9)); + return ans; +} + +/* returns the case B hydrogen recombination coefficient (Spitzer 1978) in cm^3 s^-1*/ +double alpha_B(double T){ + return alphaB_10k * pow (T/1.0e4, -0.75); +} + + +/* + Function NEUTRAL_FRACTION returns the hydrogen neutral fraction, chi, given: + hydrogen density (pcm^-3) + gas temperature (10^4 K) + ionization rate (1e-12 s^-1) + */ +double neutral_fraction(double density, double T4, double gamma, int usecaseB){ + double chi, b, alpha, corr_He = 1.0/(4.0/global_params.Y_He - 3); + + if (usecaseB) + alpha = alpha_B(T4*1e4); + else + alpha = alpha_A(T4*1e4); + + gamma *= 1e-12; + + // approximation chi << 1 + chi = (1+corr_He)*density * alpha / gamma; + if (chi < TINY){ return 0;} + if (chi < 1e-5) + return chi; + + // this code, while mathematically accurate, is numerically buggy for very small x_HI, so i will use valid approximation x_HI <<1 above when x_HI < 1e-5, and this otherwise... the two converge seemlessly + //get solutions of quadratic of chi (neutral fraction) + b = -2 - gamma / (density*(1+corr_He)*alpha); + chi = ( -b - sqrt(b*b - 4) ) / 2.0; //correct root + return chi; +} + +/* function HeI_ion_crosssec returns the HI ionization cross section at parameter frequency + (taken from Verner et al (1996) */ +double HeI_ion_crosssec(double nu){ + double x,y; + + if (nu < HeI_NUIONIZATION) + return 0; + + x = nu/NU_over_EV/13.61 - 0.4434; + y = sqrt(x*x + pow(2.136, 2)); + return 9.492e-16*((x-1)*(x-1) + 2.039*2.039) * + pow(y, (0.5 * 3.188 - 5.5)) + * pow(1.0 + sqrt(y/1.469), -3.188); +} + + +/* function HeII_ion_crosssec returns the HeII ionization cross section at parameter frequency + (taken from Osterbrock, pg. 14) */ +double HeII_ion_crosssec(double nu){ + double epsilon, Z = 2; + + if (nu < HeII_NUIONIZATION) + return 0; + + if (nu == HeII_NUIONIZATION) + nu+=TINY; + + epsilon = sqrt( nu/HeII_NUIONIZATION - 1); + return (6.3e-18)/Z/Z * pow(HeII_NUIONIZATION/nu, 4) + * pow(E, 4-(4*atan(epsilon)/epsilon)) / (1-pow(E, -2*PI/epsilon)); +} + + +/* function HI_ion_crosssec returns the HI ionization cross section at parameter frequency + (taken from Osterbrock, pg. 14) */ +double HI_ion_crosssec(double nu){ + double epsilon, Z = 1; + + if (nu < NUIONIZATION) + return 0; + + if (nu == NUIONIZATION) + nu+=TINY; + + epsilon = sqrt( nu/NUIONIZATION - 1); + return (6.3e-18)/Z/Z * pow(NUIONIZATION/nu, 4) + * pow(E, 4-(4*atan(epsilon)/epsilon)) / (1-pow(E, -2*PI/epsilon)); +} + + + +/* Return the thomspon scattering optical depth from zstart to zend through fully ionized IGM. + The hydrogen reionization history is given by the zarry and xHarry parameters, in increasing + redshift order of length len.*/ +typedef struct{ + float *z, *xH; + int len; +} tau_e_params; +double dtau_e_dz(double z, void *params){ + float xH, xi; + int i=1; + tau_e_params p = *(tau_e_params *)params; + + if ((p.len == 0) || !(p.z)) { + return (1+z)*(1+z)*drdz(z); + } + else{ + // find where we are in the redshift array + if (p.z[0]>z) // ionization fraction is 1 prior to start of array + return (1+z)*(1+z)*drdz(z); + while ( (i < p.len) && (p.z[i] < z) ) {i++;} + if (i == p.len) + return 0; + + // linearly interpolate in redshift + xH = p.xH[i-1] + (p.xH[i] - p.xH[i-1])/(p.z[i] - p.z[i-1]) * (z - p.z[i-1]); + xi = 1.0-xH; + if (xi<0){ + LOG_WARNING("in taue: funny business xi=%e, changing to 0.", xi); + xi=0; + } + if (xi>1){ + LOG_WARNING("in taue: funny business xi=%e, changing to 1", xi); + xi=1; + } + + return xi*(1+z)*(1+z)*drdz(z); + } +} +double tau_e(float zstart, float zend, float *zarry, float *xHarry, int len){ + double prehelium, posthelium, error; + gsl_function F; + double rel_tol = 1e-3; //<- relative tolerance + gsl_integration_workspace * w + = gsl_integration_workspace_alloc (1000); + tau_e_params p; + + if (zstart >= zend){ + LOG_ERROR("in tau_e: First parameter must be smaller than the second.\n"); + Throw(ValueError); + } + + F.function = &dtau_e_dz; + p.z = zarry; + p.xH = xHarry; + p.len = len; + F.params = &p; + if ((len > 0) && zarry) + zend = zarry[len-1] - FRACT_FLOAT_ERR; + + int status; + + gsl_set_error_handler_off(); + + if (zend > global_params.Zreion_HeII){// && (zstart < Zreion_HeII)){ + if (zstart < global_params.Zreion_HeII){ + status = gsl_integration_qag (&F, global_params.Zreion_HeII, zstart, 0, rel_tol, + 1000, GSL_INTEG_GAUSS61, w, &prehelium, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",global_params.Zreion_HeII,zstart,rel_tol,prehelium,error); + LOG_ERROR("data: zstart=%e zend=%e",zstart,zend); + CATCH_GSL_ERROR(status); + } + + status = gsl_integration_qag (&F, zend, global_params.Zreion_HeII, 0, rel_tol, + 1000, GSL_INTEG_GAUSS61, w, &posthelium, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",zend,global_params.Zreion_HeII,rel_tol,posthelium,error); + LOG_ERROR("data: zstart=%e zend=%e",zstart,zend); + CATCH_GSL_ERROR(status); + } + } + else{ + prehelium = 0; + status = gsl_integration_qag (&F, zend, zstart, 0, rel_tol, + 1000, GSL_INTEG_GAUSS61, w, &posthelium, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",zend,zstart,rel_tol,posthelium,error); + CATCH_GSL_ERROR(status); + } + } + } + else{ + posthelium = 0; + status = gsl_integration_qag (&F, zend, zstart, 0, rel_tol, + 1000, GSL_INTEG_GAUSS61, w, &prehelium, &error); + + if(status!=0) { + LOG_ERROR("gsl integration error occured!"); + LOG_ERROR("(function argument): lower_limit=%e upper_limit=%e rel_tol=%e result=%e error=%e",zend,zstart,rel_tol,prehelium,error); + CATCH_GSL_ERROR(status); + } + } + gsl_integration_workspace_free (w); + + return SIGMAT * ( (N_b0+He_No)*prehelium + N_b0*posthelium ); +} + +float ComputeTau(UserParams *user_params, CosmoParams *cosmo_params, int NPoints, float *redshifts, float *global_xHI) { + Broadcast_struct_global_noastro(user_params,cosmo_params); + + return tau_e(0, redshifts[NPoints-1], redshifts, global_xHI, NPoints); +} + +double atomic_cooling_threshold(float z){ + return TtoM(z, 1e4, 0.59); +} + +double molecular_cooling_threshold(float z){ + return TtoM(z, 600, 1.22); +} + +double lyman_werner_threshold(float z, float J_21_LW, float vcb, AstroParams *astro_params){ + // correction follows Schauer+20, fit jointly to LW feedback and relative velocities. They find weaker effect of LW feedback than before (Stacy+11, Greif+11, etc.) due to HII self shielding. + double mcrit_noLW = 3.314e7 * pow( 1.+z, -1.5);// this follows Visbal+15, which is taken as the optimal fit from Fialkov+12 which was calibrated with the simulations of Stacy+11 and Greif+11; + + double f_LW = 1.0 + astro_params->A_LW * pow(J_21_LW, astro_params->BETA_LW); + + double f_vcb = pow(1.0 + astro_params->A_VCB * vcb/SIGMAVCB, astro_params->BETA_VCB); + + // double mcrit_LW = mcrit_noLW * (1.0 + 10. * sqrt(J_21_LW)); //Eq. (12) in Schauer+20 + // return pow(10.0, log10(mcrit_LW) + 0.416 * vcb/SIGMAVCB ); //vcb and sigmacb in km/s, from Eq. (9) + + return (mcrit_noLW * f_LW * f_vcb); + +} + +double reionization_feedback(float z, float Gamma_halo_HII, float z_IN){ + if (z_IN<=1e-19) + return 1e-40; + return REION_SM13_M0 * pow(HALO_BIAS * Gamma_halo_HII, REION_SM13_A) * pow((1.+z)/10, REION_SM13_B) * + pow(1 - pow((1.+z)/(1.+z_IN), REION_SM13_C), REION_SM13_D); +} diff --git a/src/py21cmfast/src/thermochem.h b/src/py21cmfast/src/thermochem.h new file mode 100644 index 00000000..8b3156fc --- /dev/null +++ b/src/py21cmfast/src/thermochem.h @@ -0,0 +1,24 @@ +#ifndef _THERMOCHEM_H +#define _THERMOCHEM_H + +#include "InputParameters.h" + +float ComputeTau(UserParams *user_params, CosmoParams *cosmo_params, int Npoints, float *redshifts, float *global_xHI); +double molecular_cooling_threshold(float z); +double atomic_cooling_threshold(float z); +double lyman_werner_threshold(float z, float J_21_LW, float vcb, AstroParams *astro_params); +double reionization_feedback(float z, float Gamma_halo_HII, float z_IN); +float ComputeFullyIoinizedTemperature(float z_re, float z, float delta); +float ComputePartiallyIoinizedTemperature(float T_HI, float res_xH); + +/* returns the case A hydrogen recombination coefficient (Abel et al. 1997) in cm^3 s^-1*/ +double alpha_A(double T); +/* returns the case B hydrogen recombination coefficient (Spitzer 1978) in cm^3 s^-1*/ +double alpha_B(double T); + +double HeI_ion_crosssec(double nu); +double HeII_ion_crosssec(double nu); +double HI_ion_crosssec(double nu); +double neutral_fraction(double density, double T4, double gamma, int usecaseB); + +#endif diff --git a/testplots/tests.test_integration_features.py--test_power_spectra_coeval[minimize_mem].pdf b/testplots/tests.test_integration_features.py--test_power_spectra_coeval[minimize_mem].pdf deleted file mode 100644 index 5130a0c3..00000000 Binary files a/testplots/tests.test_integration_features.py--test_power_spectra_coeval[minimize_mem].pdf and /dev/null differ diff --git a/tests/test_c_interpolation_tables.py b/tests/test_c_interpolation_tables.py index f4f32ba4..ac6c588e 100644 --- a/tests/test_c_interpolation_tables.py +++ b/tests/test_c_interpolation_tables.py @@ -67,8 +67,7 @@ def test_sigma_table(name, plt): up = UserParams(opts["user_params"]) cp = CosmoParams(opts["cosmo_params"]) up.update(USE_INTERPOLATION_TABLES=True) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) + lib.Broadcast_struct_global_noastro(up(), cp()) lib.init_ps() lib.initialiseSigmaMInterpTable( @@ -115,9 +114,7 @@ def test_inverse_cmf_tables(name, plt): edges = np.logspace(7, 12, num=hist_size).astype("f4") edges_ln = np.log(edges) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) lib.init_ps() lib.initialiseSigmaMInterpTable( @@ -283,9 +280,7 @@ def test_Massfunc_conditional_tables(name, plt): edges = np.logspace(7, 12, num=hist_size).astype("f4") edges_ln = np.log(edges) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) lib.init_ps() lib.initialiseSigmaMInterpTable( @@ -487,9 +482,7 @@ def test_FgtrM_conditional_tables(name, R, plt): fo = FlagOptions(opts["flag_options"]) up.update(USE_INTERPOLATION_TABLES=True) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) hist_size = 1000 M_min = global_params.M_MIN_INTEGRAL @@ -526,9 +519,7 @@ def test_FgtrM_conditional_tables(name, R, plt): ) up.update(USE_INTERPOLATION_TABLES=False) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) fcoll_integrals = np.vectorize(lib.EvaluateFcoll_delta)( edges_d[:-1], growth_out, sigma_min, sigma_cond @@ -596,9 +587,7 @@ def test_SFRD_z_tables(name, plt): INHOMO_RECO=True, USE_TS_FLUCT=True, ) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) hist_size = 1000 M_min = global_params.M_MIN_INTEGRAL @@ -611,7 +600,7 @@ def test_SFRD_z_tables(name, plt): lib.init_ps() if up.INTEGRATION_METHOD_ATOMIC == 1 or up.INTEGRATION_METHOD_MINI == 1: - lib.initialise_GL(100, np.log(M_min), np.log(M_max)) + lib.initialise_GL(np.log(M_min), np.log(M_max)) Mlim_Fstar = 1e10 * (10**ap.F_STAR10) ** (-1.0 / ap.ALPHA_STAR) Mlim_Fstar_MINI = 1e7 * (10**ap.F_STAR7_MINI) ** (-1.0 / ap.ALPHA_STAR_MINI) @@ -727,9 +716,7 @@ def test_Nion_z_tables(name, plt): INHOMO_RECO=True, USE_TS_FLUCT=True, ) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) f10s = 10**ap.F_STAR10 f7s = 10**ap.F_STAR7_MINI @@ -745,7 +732,7 @@ def test_Nion_z_tables(name, plt): lib.init_ps() if up.INTEGRATION_METHOD_ATOMIC == 1 or up.INTEGRATION_METHOD_MINI == 1: - lib.initialise_GL(100, np.log(M_min), np.log(M_max)) + lib.initialise_GL(np.log(M_min), np.log(M_max)) Mlim_Fstar = 1e10 * (10**ap.F_STAR10) ** (-1.0 / ap.ALPHA_STAR) Mlim_Fesc = 1e10 * (10**ap.F_ESC10) ** (-1.0 / ap.ALPHA_ESC) @@ -885,9 +872,7 @@ def test_Nion_conditional_tables(name, R, mini, intmethod, plt): INHOMO_RECO=True, USE_TS_FLUCT=True, ) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) hist_size = 1000 M_min = global_params.M_MIN_INTEGRAL @@ -896,7 +881,7 @@ def test_Nion_conditional_tables(name, R, mini, intmethod, plt): lib.init_ps() if up.INTEGRATION_METHOD_ATOMIC == 1 or up.INTEGRATION_METHOD_MINI == 1: - lib.initialise_GL(100, np.log(M_min), np.log(M_max)) + lib.initialise_GL(np.log(M_min), np.log(M_max)) growth_out = lib.dicke(redshift) cond_mass = ( @@ -1073,9 +1058,7 @@ def test_SFRD_conditional_table(name, R, intmethod, plt): INHOMO_RECO=True, USE_TS_FLUCT=True, ) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) hist_size = 1000 M_min = global_params.M_MIN_INTEGRAL @@ -1084,7 +1067,7 @@ def test_SFRD_conditional_table(name, R, intmethod, plt): lib.init_ps() if up.INTEGRATION_METHOD_ATOMIC == 1 or up.INTEGRATION_METHOD_MINI == 1: - lib.initialise_GL(100, np.log(M_min), np.log(M_max)) + lib.initialise_GL(np.log(M_min), np.log(M_max)) growth_out = lib.dicke(redshift) cond_mass = ( @@ -1238,9 +1221,7 @@ def test_conditional_integral_methods(R, name, integrand, plt): if "sfr" in integrand: ap.update(F_ESC10=0.0, F_ESC7_MINI=0.0, ALPHA_ESC=0.0) # F_ESCX is in log10 - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) hist_size = 1000 M_min = global_params.M_MIN_INTEGRAL @@ -1249,7 +1230,7 @@ def test_conditional_integral_methods(R, name, integrand, plt): lib.init_ps() if up.INTEGRATION_METHOD_ATOMIC == 1 or up.INTEGRATION_METHOD_MINI == 1: - lib.initialise_GL(100, np.log(M_min), np.log(M_max)) + lib.initialise_GL(np.log(M_min), np.log(M_max)) growth_out = lib.dicke(redshift) cond_mass = ( @@ -1282,9 +1263,7 @@ def test_conditional_integral_methods(R, name, integrand, plt): continue up.update(INTEGRATION_METHOD_ATOMIC=method, INTEGRATION_METHOD_MINI=method) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) integrals.append( np.vectorize(lib.Nion_ConditionalM)( diff --git a/tests/test_halo_sampler.py b/tests/test_halo_sampler.py index 4de9410b..84f36a45 100644 --- a/tests/test_halo_sampler.py +++ b/tests/test_halo_sampler.py @@ -35,10 +35,7 @@ def test_sampler_from_catalog(name, mass, plt): ap = AstroParams(opts["astro_params"]) fo = FlagOptions(opts["flag_options"]) up.update(USE_INTERPOLATION_TABLES=True) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) - lib.Broadcast_struct_global_STOC(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) l10min = np.log10(up.SAMPLER_MIN_MASS) l10max = np.log10(mass) @@ -147,10 +144,7 @@ def test_sampler_from_grid(name, delta, plt): ap = AstroParams(opts["astro_params"]) fo = FlagOptions(opts["flag_options"]) up.update(USE_INTERPOLATION_TABLES=True) - lib.Broadcast_struct_global_PS(up(), cp()) - lib.Broadcast_struct_global_UF(up(), cp()) - lib.Broadcast_struct_global_IT(up(), cp(), ap(), fo()) - lib.Broadcast_struct_global_STOC(up(), cp(), ap(), fo()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) lib.init_ps() lib.initialiseSigmaMInterpTable( @@ -291,13 +285,15 @@ def test_halo_scaling_relations(): # NOTE: Not using upper turnover, this test should be extended fo = FlagOptions( USE_MINI_HALOS=True, + INHOMO_RECO=True, + USE_TS_FLUCT=True, USE_HALO_FIELD=True, FIXED_HALO_GRIDS=False, HALO_STOCHASTICITY=True, USE_UPPER_STELLAR_TURNOVER=False, ) - lib.Broadcast_struct_global_UF(up(), cp()) + lib.Broadcast_struct_global_all(up(), cp(), ap(), fo()) mturn_acg = np.maximum(lib.atomic_cooling_threshold(redshift), 10**ap.M_TURN) mturn_mcg = ( 10**ap.M_TURN diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 5f16d2bd..f6a82d11 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -299,7 +299,11 @@ def test_run_lf(): redshifts=[7, 8, 9], nbins=100, component=0, - flag_options={"USE_MINI_HALOS": True}, + flag_options={ + "USE_MINI_HALOS": True, + "INHOMO_RECO": True, + "USE_TS_FLUCT": True, + }, mturnovers=[7.0, 7.0, 7.0], mturnovers_mini=[5.0, 5.0, 5.0], ) @@ -465,7 +469,11 @@ def test_run_coeval_bad_inputs(): ): wrapper.run_coeval( redshift=6.0, - flag_options={"USE_MINI_HALOS": True}, + flag_options={ + "USE_MINI_HALOS": True, + "INHOMO_RECO": True, + "USE_TS_FLUCT": True, + }, use_interp_perturb_field=True, ) @@ -588,12 +596,24 @@ def test_lc_pass_redshift_deprecation(rectlcn, ic): def test_coeval_lowerz_than_photon_cons(ic): with pytest.raises(ValueError, match="You have passed a redshift"): wrapper.run_coeval( - init_box=ic, redshift=2.0, flag_options={"PHOTON_CONS_TYPE": 1} + init_box=ic, + redshift=2.0, + flag_options={ + "PHOTON_CONS_TYPE": 1, + "USE_HALO_FIELD": False, + "HALO_STOCHASTICITY": False, + }, ) def test_lc_lowerz_than_photon_cons(rectlcn, ic): with pytest.raises(ValueError, match="You have passed a redshift"): wrapper.run_lightcone( - init_box=ic, redshift=2.0, flag_options={"PHOTON_CONS_TYPE": 1} + init_box=ic, + redshift=2.0, + flag_options={ + "PHOTON_CONS_TYPE": 1, + "USE_HALO_FIELD": False, + "HALO_STOCHASTICITY": False, + }, )